MtpServer.cpp revision d32114950770a6e9361e0869a41e5a03a7acab42
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <sys/types.h>
20#include <sys/ioctl.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <errno.h>
24#include <sys/stat.h>
25#include <dirent.h>
26
27#include <cutils/properties.h>
28
29#include "MtpDebug.h"
30#include "MtpDatabase.h"
31#include "MtpProperty.h"
32#include "MtpServer.h"
33#include "MtpStorage.h"
34#include "MtpStringBuffer.h"
35
36#include <linux/usb/f_mtp.h>
37
38namespace android {
39
40static const MtpOperationCode kSupportedOperationCodes[] = {
41    MTP_OPERATION_GET_DEVICE_INFO,
42    MTP_OPERATION_OPEN_SESSION,
43    MTP_OPERATION_CLOSE_SESSION,
44    MTP_OPERATION_GET_STORAGE_IDS,
45    MTP_OPERATION_GET_STORAGE_INFO,
46    MTP_OPERATION_GET_NUM_OBJECTS,
47    MTP_OPERATION_GET_OBJECT_HANDLES,
48    MTP_OPERATION_GET_OBJECT_INFO,
49    MTP_OPERATION_GET_OBJECT,
50//    MTP_OPERATION_GET_THUMB,
51    MTP_OPERATION_DELETE_OBJECT,
52    MTP_OPERATION_SEND_OBJECT_INFO,
53    MTP_OPERATION_SEND_OBJECT,
54//    MTP_OPERATION_INITIATE_CAPTURE,
55//    MTP_OPERATION_FORMAT_STORE,
56//    MTP_OPERATION_RESET_DEVICE,
57//    MTP_OPERATION_SELF_TEST,
58//    MTP_OPERATION_SET_OBJECT_PROTECTION,
59//    MTP_OPERATION_POWER_DOWN,
60    MTP_OPERATION_GET_DEVICE_PROP_DESC,
61    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
62    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
63    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
64//    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
65//    MTP_OPERATION_MOVE_OBJECT,
66//    MTP_OPERATION_COPY_OBJECT,
67//    MTP_OPERATION_GET_PARTIAL_OBJECT,
68//    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
69    MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
70    MTP_OPERATION_GET_OBJECT_PROP_DESC,
71//    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
72//    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
73    MTP_OPERATION_GET_OBJECT_REFERENCES,
74    MTP_OPERATION_SET_OBJECT_REFERENCES,
75//    MTP_OPERATION_SKIP,
76};
77
78static const MtpEventCode kSupportedEventCodes[] = {
79    MTP_EVENT_OBJECT_ADDED,
80    MTP_EVENT_OBJECT_REMOVED,
81};
82
83MtpServer::MtpServer(int fd, MtpDatabase* database,
84                    int fileGroup, int filePerm, int directoryPerm)
85    :   mFD(fd),
86        mDatabase(database),
87        mFileGroup(fileGroup),
88        mFilePermission(filePerm),
89        mDirectoryPermission(directoryPerm),
90        mSessionID(0),
91        mSessionOpen(false),
92        mSendObjectHandle(kInvalidObjectHandle),
93        mSendObjectFormat(0),
94        mSendObjectFileSize(0)
95{
96}
97
98MtpServer::~MtpServer() {
99}
100
101void MtpServer::addStorage(const char* filePath) {
102    int index = mStorages.size() + 1;
103    index |= index << 16;   // set high and low part to our index
104    MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
105    addStorage(storage);
106}
107
108MtpStorage* MtpServer::getStorage(MtpStorageID id) {
109    for (int i = 0; i < mStorages.size(); i++) {
110        MtpStorage* storage =  mStorages[i];
111        if (storage->getStorageID() == id)
112            return storage;
113    }
114    return NULL;
115}
116
117void MtpServer::run() {
118    int fd = mFD;
119
120    LOGV("MtpServer::run fd: %d\n", fd);
121
122    while (1) {
123        int ret = mRequest.read(fd);
124        if (ret < 0) {
125            LOGE("request read returned %d, errno: %d", ret, errno);
126            if (errno == ECANCELED) {
127                // return to top of loop and wait for next command
128                continue;
129            }
130            break;
131        }
132        MtpOperationCode operation = mRequest.getOperationCode();
133        MtpTransactionID transaction = mRequest.getTransactionID();
134
135        LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
136        mRequest.dump();
137
138        // FIXME need to generalize this
139        bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
140                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
141                    || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
142                    || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
143        if (dataIn) {
144            int ret = mData.read(fd);
145            if (ret < 0) {
146                LOGE("data read returned %d, errno: %d", ret, errno);
147                if (errno == ECANCELED) {
148                    // return to top of loop and wait for next command
149                    continue;
150                }
151                break;
152            }
153            LOGV("received data:");
154            mData.dump();
155        } else {
156            mData.reset();
157        }
158
159        if (handleRequest()) {
160            if (!dataIn && mData.hasData()) {
161                mData.setOperationCode(operation);
162                mData.setTransactionID(transaction);
163                LOGV("sending data:");
164                ret = mData.write(fd);
165                if (ret < 0) {
166                    LOGE("request write returned %d, errno: %d", ret, errno);
167                    if (errno == ECANCELED) {
168                        // return to top of loop and wait for next command
169                        continue;
170                    }
171                    break;
172                }
173            }
174
175            mResponse.setTransactionID(transaction);
176            LOGV("sending response %04X", mResponse.getResponseCode());
177            ret = mResponse.write(fd);
178            if (ret < 0) {
179                LOGE("request write returned %d, errno: %d", ret, errno);
180                if (errno == ECANCELED) {
181                    // return to top of loop and wait for next command
182                    continue;
183                }
184                break;
185            }
186        } else {
187            LOGV("skipping response\n");
188        }
189    }
190
191    if (mSessionOpen)
192        mDatabase->sessionEnded();
193}
194
195void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
196    if (mSessionOpen) {
197        LOGD("sendObjectAdded %d\n", handle);
198        mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
199        mEvent.setTransactionID(mRequest.getTransactionID());
200        mEvent.setParameter(1, handle);
201        int ret = mEvent.write(mFD);
202        LOGD("mEvent.write returned %d\n", ret);
203    }
204}
205
206void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
207    if (mSessionOpen) {
208        LOGD("sendObjectRemoved %d\n", handle);
209        mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
210        mEvent.setTransactionID(mRequest.getTransactionID());
211        mEvent.setParameter(1, handle);
212        int ret = mEvent.write(mFD);
213        LOGD("mEvent.write returned %d\n", ret);
214    }
215}
216
217bool MtpServer::handleRequest() {
218    MtpOperationCode operation = mRequest.getOperationCode();
219    MtpResponseCode response;
220
221    mResponse.reset();
222
223    if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
224        // FIXME - need to delete mSendObjectHandle from the database
225        LOGE("expected SendObject after SendObjectInfo");
226        mSendObjectHandle = kInvalidObjectHandle;
227    }
228
229    switch (operation) {
230        case MTP_OPERATION_GET_DEVICE_INFO:
231            response = doGetDeviceInfo();
232            break;
233        case MTP_OPERATION_OPEN_SESSION:
234            response = doOpenSession();
235            break;
236        case MTP_OPERATION_CLOSE_SESSION:
237            response = doCloseSession();
238            break;
239        case MTP_OPERATION_GET_STORAGE_IDS:
240            response = doGetStorageIDs();
241            break;
242         case MTP_OPERATION_GET_STORAGE_INFO:
243            response = doGetStorageInfo();
244            break;
245        case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
246            response = doGetObjectPropsSupported();
247            break;
248        case MTP_OPERATION_GET_OBJECT_HANDLES:
249            response = doGetObjectHandles();
250            break;
251        case MTP_OPERATION_GET_NUM_OBJECTS:
252            response = doGetNumObjects();
253            break;
254        case MTP_OPERATION_GET_OBJECT_REFERENCES:
255            response = doGetObjectReferences();
256            break;
257        case MTP_OPERATION_SET_OBJECT_REFERENCES:
258            response = doSetObjectReferences();
259            break;
260        case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
261            response = doGetObjectPropValue();
262            break;
263        case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
264            response = doSetObjectPropValue();
265            break;
266        case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
267            response = doGetDevicePropValue();
268            break;
269        case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
270            response = doSetDevicePropValue();
271            break;
272        case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
273            response = doResetDevicePropValue();
274            break;
275        case MTP_OPERATION_GET_OBJECT_INFO:
276            response = doGetObjectInfo();
277            break;
278        case MTP_OPERATION_GET_OBJECT:
279            response = doGetObject();
280            break;
281        case MTP_OPERATION_SEND_OBJECT_INFO:
282            response = doSendObjectInfo();
283            break;
284        case MTP_OPERATION_SEND_OBJECT:
285            response = doSendObject();
286            break;
287        case MTP_OPERATION_DELETE_OBJECT:
288            response = doDeleteObject();
289            break;
290        case MTP_OPERATION_GET_OBJECT_PROP_DESC:
291            response = doGetObjectPropDesc();
292            break;
293        case MTP_OPERATION_GET_DEVICE_PROP_DESC:
294            response = doGetDevicePropDesc();
295            break;
296        default:
297            response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
298            break;
299    }
300
301    if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
302        return false;
303    mResponse.setResponseCode(response);
304    return true;
305}
306
307MtpResponseCode MtpServer::doGetDeviceInfo() {
308    MtpStringBuffer   string;
309    char prop_value[PROPERTY_VALUE_MAX];
310
311    MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
312    MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
313    MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
314
315    // fill in device info
316    mData.putUInt16(MTP_STANDARD_VERSION);
317    mData.putUInt32(6); // MTP Vendor Extension ID
318    mData.putUInt16(MTP_STANDARD_VERSION);
319    string.set("microsoft.com: 1.0;");
320    mData.putString(string); // MTP Extensions
321    mData.putUInt16(0); //Functional Mode
322    mData.putAUInt16(kSupportedOperationCodes,
323            sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
324    mData.putAUInt16(kSupportedEventCodes,
325            sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
326    mData.putAUInt16(deviceProperties); // Device Properties Supported
327    mData.putAUInt16(captureFormats); // Capture Formats
328    mData.putAUInt16(playbackFormats);  // Playback Formats
329    // FIXME
330    string.set("Google, Inc.");
331    mData.putString(string);   // Manufacturer
332
333    property_get("ro.product.model", prop_value, "MTP Device");
334    string.set(prop_value);
335    mData.putString(string);   // Model
336    string.set("1.0");
337    mData.putString(string);   // Device Version
338
339    property_get("ro.serialno", prop_value, "????????");
340    string.set(prop_value);
341    mData.putString(string);   // Serial Number
342
343    delete playbackFormats;
344    delete captureFormats;
345    delete deviceProperties;
346
347    return MTP_RESPONSE_OK;
348}
349
350MtpResponseCode MtpServer::doOpenSession() {
351    if (mSessionOpen) {
352        mResponse.setParameter(1, mSessionID);
353        return MTP_RESPONSE_SESSION_ALREADY_OPEN;
354    }
355    mSessionID = mRequest.getParameter(1);
356    mSessionOpen = true;
357
358    mDatabase->sessionStarted();
359
360    return MTP_RESPONSE_OK;
361}
362
363MtpResponseCode MtpServer::doCloseSession() {
364    if (!mSessionOpen)
365        return MTP_RESPONSE_SESSION_NOT_OPEN;
366    mSessionID = 0;
367    mSessionOpen = false;
368    mDatabase->sessionEnded();
369    return MTP_RESPONSE_OK;
370}
371
372MtpResponseCode MtpServer::doGetStorageIDs() {
373    if (!mSessionOpen)
374        return MTP_RESPONSE_SESSION_NOT_OPEN;
375
376    int count = mStorages.size();
377    mData.putUInt32(count);
378    for (int i = 0; i < count; i++)
379        mData.putUInt32(mStorages[i]->getStorageID());
380
381    return MTP_RESPONSE_OK;
382}
383
384MtpResponseCode MtpServer::doGetStorageInfo() {
385    MtpStringBuffer   string;
386
387    if (!mSessionOpen)
388        return MTP_RESPONSE_SESSION_NOT_OPEN;
389    MtpStorageID id = mRequest.getParameter(1);
390    MtpStorage* storage = getStorage(id);
391    if (!storage)
392        return MTP_RESPONSE_INVALID_STORAGE_ID;
393
394    mData.putUInt16(storage->getType());
395    mData.putUInt16(storage->getFileSystemType());
396    mData.putUInt16(storage->getAccessCapability());
397    mData.putUInt64(storage->getMaxCapacity());
398    mData.putUInt64(storage->getFreeSpace());
399    mData.putUInt32(1024*1024*1024); // Free Space in Objects
400    string.set(storage->getDescription());
401    mData.putString(string);
402    mData.putEmptyString();   // Volume Identifier
403
404    return MTP_RESPONSE_OK;
405}
406
407MtpResponseCode MtpServer::doGetObjectPropsSupported() {
408    if (!mSessionOpen)
409        return MTP_RESPONSE_SESSION_NOT_OPEN;
410    MtpObjectFormat format = mRequest.getParameter(1);
411    MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
412    mData.putAUInt16(properties);
413    delete properties;
414    return MTP_RESPONSE_OK;
415}
416
417MtpResponseCode MtpServer::doGetObjectHandles() {
418    if (!mSessionOpen)
419        return MTP_RESPONSE_SESSION_NOT_OPEN;
420    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
421    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
422    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
423                                                            // 0x00000000 for all objects?
424    if (parent == 0xFFFFFFFF)
425        parent = 0;
426
427    MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
428    mData.putAUInt32(handles);
429    delete handles;
430    return MTP_RESPONSE_OK;
431}
432
433MtpResponseCode MtpServer::doGetNumObjects() {
434    if (!mSessionOpen)
435        return MTP_RESPONSE_SESSION_NOT_OPEN;
436    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
437    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
438    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
439                                                            // 0x00000000 for all objects?
440    if (parent == 0xFFFFFFFF)
441        parent = 0;
442
443    int count = mDatabase->getNumObjects(storageID, format, parent);
444    if (count >= 0) {
445        mResponse.setParameter(1, count);
446        return MTP_RESPONSE_OK;
447    } else {
448        mResponse.setParameter(1, 0);
449        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
450    }
451}
452
453MtpResponseCode MtpServer::doGetObjectReferences() {
454    if (!mSessionOpen)
455        return MTP_RESPONSE_SESSION_NOT_OPEN;
456    MtpStorageID handle = mRequest.getParameter(1);
457
458    // FIXME - check for invalid object handle
459    MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
460    if (handles) {
461        mData.putAUInt32(handles);
462        delete handles;
463    } else {
464        mData.putEmptyArray();
465    }
466    return MTP_RESPONSE_OK;
467}
468
469MtpResponseCode MtpServer::doSetObjectReferences() {
470    if (!mSessionOpen)
471        return MTP_RESPONSE_SESSION_NOT_OPEN;
472    MtpStorageID handle = mRequest.getParameter(1);
473    MtpObjectHandleList* references = mData.getAUInt32();
474    MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
475    delete references;
476    return result;
477}
478
479MtpResponseCode MtpServer::doGetObjectPropValue() {
480    MtpObjectHandle handle = mRequest.getParameter(1);
481    MtpObjectProperty property = mRequest.getParameter(2);
482    LOGD("GetObjectPropValue %d %s\n", handle,
483            MtpDebug::getObjectPropCodeName(property));
484
485    return mDatabase->getObjectPropertyValue(handle, property, mData);
486}
487
488MtpResponseCode MtpServer::doSetObjectPropValue() {
489    MtpObjectHandle handle = mRequest.getParameter(1);
490    MtpObjectProperty property = mRequest.getParameter(2);
491    LOGD("SetObjectPropValue %d %s\n", handle,
492            MtpDebug::getObjectPropCodeName(property));
493
494    return mDatabase->setObjectPropertyValue(handle, property, mData);
495}
496
497MtpResponseCode MtpServer::doGetDevicePropValue() {
498    MtpDeviceProperty property = mRequest.getParameter(1);
499    LOGD("GetDevicePropValue %s\n",
500            MtpDebug::getDevicePropCodeName(property));
501
502    return mDatabase->getDevicePropertyValue(property, mData);
503}
504
505MtpResponseCode MtpServer::doSetDevicePropValue() {
506    MtpDeviceProperty property = mRequest.getParameter(1);
507    LOGD("SetDevicePropValue %s\n",
508            MtpDebug::getDevicePropCodeName(property));
509
510    return mDatabase->setDevicePropertyValue(property, mData);
511}
512
513MtpResponseCode MtpServer::doResetDevicePropValue() {
514    MtpDeviceProperty property = mRequest.getParameter(1);
515    LOGD("ResetDevicePropValue %s\n",
516            MtpDebug::getDevicePropCodeName(property));
517
518    return mDatabase->resetDeviceProperty(property);
519}
520
521MtpResponseCode MtpServer::doGetObjectInfo() {
522    MtpObjectHandle handle = mRequest.getParameter(1);
523    return mDatabase->getObjectInfo(handle, mData);
524}
525
526MtpResponseCode MtpServer::doGetObject() {
527    MtpObjectHandle handle = mRequest.getParameter(1);
528    MtpString pathBuf;
529    int64_t fileLength;
530    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
531    if (result != MTP_RESPONSE_OK)
532        return result;
533
534    const char* filePath = (const char *)pathBuf;
535    mtp_file_range  mfr;
536    mfr.fd = open(filePath, O_RDONLY);
537    if (mfr.fd < 0) {
538        return MTP_RESPONSE_GENERAL_ERROR;
539    }
540    mfr.offset = 0;
541    mfr.length = fileLength;
542
543    // send data header
544    mData.setOperationCode(mRequest.getOperationCode());
545    mData.setTransactionID(mRequest.getTransactionID());
546    mData.writeDataHeader(mFD, fileLength);
547
548    // then transfer the file
549    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
550    close(mfr.fd);
551    if (ret < 0) {
552        if (errno == ECANCELED)
553            return MTP_RESPONSE_TRANSACTION_CANCELLED;
554        else
555            return MTP_RESPONSE_GENERAL_ERROR;
556    }
557    return MTP_RESPONSE_OK;
558}
559
560MtpResponseCode MtpServer::doSendObjectInfo() {
561    MtpString path;
562    MtpStorageID storageID = mRequest.getParameter(1);
563    MtpStorage* storage = getStorage(storageID);
564    MtpObjectHandle parent = mRequest.getParameter(2);
565    if (!storage)
566        return MTP_RESPONSE_INVALID_STORAGE_ID;
567
568    // special case the root
569    if (parent == MTP_PARENT_ROOT) {
570        path = storage->getPath();
571        parent = 0;
572    } else {
573        int64_t dummy;
574        int result = mDatabase->getObjectFilePath(parent, path, dummy);
575        if (result != MTP_RESPONSE_OK)
576            return result;
577    }
578
579    // read only the fields we need
580    mData.getUInt32();  // storage ID
581    MtpObjectFormat format = mData.getUInt16();
582    mData.getUInt16();  // protection status
583    mSendObjectFileSize = mData.getUInt32();
584    mData.getUInt16();  // thumb format
585    mData.getUInt32();  // thumb compressed size
586    mData.getUInt32();  // thumb pix width
587    mData.getUInt32();  // thumb pix height
588    mData.getUInt32();  // image pix width
589    mData.getUInt32();  // image pix height
590    mData.getUInt32();  // image bit depth
591    mData.getUInt32();  // parent
592    uint16_t associationType = mData.getUInt16();
593    uint32_t associationDesc = mData.getUInt32();   // association desc
594    mData.getUInt32();  // sequence number
595    MtpStringBuffer name, created, modified;
596    mData.getString(name);    // file name
597    mData.getString(created);      // date created
598    mData.getString(modified);     // date modified
599    // keywords follow
600
601    time_t modifiedTime;
602    if (!parseDateTime(modified, modifiedTime))
603        modifiedTime = 0;
604
605    if (path[path.size() - 1] != '/')
606        path += "/";
607    path += (const char *)name;
608
609    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
610            format, parent, storageID, mSendObjectFileSize, modifiedTime);
611    if (handle == kInvalidObjectHandle) {
612        return MTP_RESPONSE_GENERAL_ERROR;
613    }
614
615  if (format == MTP_FORMAT_ASSOCIATION) {
616        mode_t mask = umask(0);
617        int ret = mkdir((const char *)path, mDirectoryPermission);
618        umask(mask);
619        if (ret && ret != -EEXIST)
620            return MTP_RESPONSE_GENERAL_ERROR;
621        chown((const char *)path, getuid(), mFileGroup);
622    } else {
623        mSendObjectFilePath = path;
624        // save the handle for the SendObject call, which should follow
625        mSendObjectHandle = handle;
626        mSendObjectFormat = format;
627    }
628
629    mResponse.setParameter(1, storageID);
630    mResponse.setParameter(2, parent);
631    mResponse.setParameter(3, handle);
632
633    return MTP_RESPONSE_OK;
634}
635
636MtpResponseCode MtpServer::doSendObject() {
637    MtpResponseCode result = MTP_RESPONSE_OK;
638    mode_t mask;
639    int ret;
640
641    if (mSendObjectHandle == kInvalidObjectHandle) {
642        LOGE("Expected SendObjectInfo before SendObject");
643        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
644        goto done;
645    }
646
647    // read the header
648    ret = mData.readDataHeader(mFD);
649    // FIXME - check for errors here.
650
651    // reset so we don't attempt to send this back
652    mData.reset();
653
654    mtp_file_range  mfr;
655    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
656    if (mfr.fd < 0) {
657        result = MTP_RESPONSE_GENERAL_ERROR;
658        goto done;
659    }
660    fchown(mfr.fd, getuid(), mFileGroup);
661    // set permissions
662    mask = umask(0);
663    fchmod(mfr.fd, mFilePermission);
664    umask(mask);
665
666    mfr.offset = 0;
667    mfr.length = mSendObjectFileSize;
668
669    // transfer the file
670    ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
671    close(mfr.fd);
672
673    LOGV("MTP_RECEIVE_FILE returned %d", ret);
674
675    if (ret < 0) {
676        unlink(mSendObjectFilePath);
677        if (errno == ECANCELED)
678            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
679        else
680            result = MTP_RESPONSE_GENERAL_ERROR;
681    }
682
683done:
684    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
685            result == MTP_RESPONSE_OK);
686    mSendObjectHandle = kInvalidObjectHandle;
687    mSendObjectFormat = 0;
688    return result;
689}
690
691static void deleteRecursive(const char* path) {
692    char pathbuf[PATH_MAX];
693    int pathLength = strlen(path);
694    if (pathLength >= sizeof(pathbuf) - 1) {
695        LOGE("path too long: %s\n", path);
696    }
697    strcpy(pathbuf, path);
698    if (pathbuf[pathLength - 1] != '/') {
699        pathbuf[pathLength++] = '/';
700    }
701    char* fileSpot = pathbuf + pathLength;
702    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
703
704    DIR* dir = opendir(path);
705    if (!dir) {
706        LOGE("opendir %s failed: %s", path, strerror(errno));
707        return;
708    }
709
710    struct dirent* entry;
711    while ((entry = readdir(dir))) {
712        const char* name = entry->d_name;
713
714        // ignore "." and ".."
715        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
716            continue;
717        }
718
719        int nameLength = strlen(name);
720        if (nameLength > pathRemaining) {
721            LOGE("path %s/%s too long\n", path, name);
722            continue;
723        }
724        strcpy(fileSpot, name);
725
726        int type = entry->d_type;
727        if (entry->d_type == DT_DIR) {
728            deleteRecursive(pathbuf);
729            rmdir(pathbuf);
730        } else {
731            unlink(pathbuf);
732        }
733    }
734}
735
736static void deletePath(const char* path) {
737    struct stat statbuf;
738    if (stat(path, &statbuf) == 0) {
739        if (S_ISDIR(statbuf.st_mode)) {
740            deleteRecursive(path);
741            rmdir(path);
742        } else {
743            unlink(path);
744        }
745    } else {
746        LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
747    }
748}
749
750MtpResponseCode MtpServer::doDeleteObject() {
751    MtpObjectHandle handle = mRequest.getParameter(1);
752    MtpObjectFormat format = mRequest.getParameter(2);
753    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
754    // FIXME - implement deleting objects by format
755
756    MtpString filePath;
757    int64_t fileLength;
758    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
759    if (result == MTP_RESPONSE_OK) {
760        LOGV("deleting %s", (const char *)filePath);
761        deletePath((const char *)filePath);
762        return mDatabase->deleteFile(handle);
763    } else {
764        return result;
765    }
766}
767
768MtpResponseCode MtpServer::doGetObjectPropDesc() {
769    MtpObjectProperty propCode = mRequest.getParameter(1);
770    MtpObjectFormat format = mRequest.getParameter(2);
771    LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
772                                        MtpDebug::getFormatCodeName(format));
773    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
774    if (!property)
775        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
776    property->write(mData);
777    delete property;
778    return MTP_RESPONSE_OK;
779}
780
781MtpResponseCode MtpServer::doGetDevicePropDesc() {
782    MtpDeviceProperty propCode = mRequest.getParameter(1);
783    LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
784    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
785    if (!property)
786        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
787    property->write(mData);
788    delete property;
789    return MTP_RESPONSE_OK;
790}
791
792}  // namespace android
793