diff --git a/avdecoder.cpp b/avdecoder.cpp index bc643f1..db401f5 100644 --- a/avdecoder.cpp +++ b/avdecoder.cpp @@ -138,7 +138,6 @@ QByteArray AVDecoder::getAudioThumbnail(int width, int height) QImage img = QImage::fromData(QByteArray((const char *)pkt.data, pkt.size)); QImage result = img.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation); result.save(&imgbuffer, "JPEG"); - av_free_packet(&pkt); break; } } diff --git a/cmaclient.cpp b/cmaclient.cpp index 22c3a63..80f0f33 100644 --- a/cmaclient.cpp +++ b/cmaclient.cpp @@ -20,6 +20,7 @@ #include "cmaclient.h" #include "capability.h" #include "avdecoder.h" +#include "cmaevent.h" #include "utils.h" #include "QApplication" @@ -33,11 +34,9 @@ QMutex CmaClient::mutex; QMutex CmaClient::runner; QMutex CmaClient::eloop; -Database CmaClient::db; bool CmaClient::is_running = true; bool CmaClient::event_loop_enabled = true; -metadata_t CmaClient::g_thumbmeta = {0, 0, 0, NULL, NULL, 0, 0, 0, Thumbnail, {{18, 144, 80, 0, 1, 1.0f, 2}}, NULL}; CmaClient *CmaClient::this_object = NULL; CmaClient::CmaClient(QObject *parent) : @@ -59,18 +58,6 @@ void CmaClient::setRunning(bool state) is_running = state; } -bool CmaClient::isEventLoopEnabled() -{ - QMutexLocker locker(&eloop); - return event_loop_enabled; -} - -void CmaClient::setEventLoop(bool state) -{ - QMutexLocker locker(&eloop); - event_loop_enabled = state; -} - void CmaClient::connectUsb() { vita_device_t *vita; @@ -165,834 +152,30 @@ void CmaClient::enterEventLoop() qDebug("Starting event loop"); - setEventLoop(true); + event_loop_enabled = true; + while(isEventLoopEnabled()) { if(VitaMTP_Read_Event(device, &event) < 0) { qWarning("Error reading event from Vita."); - setEventLoop(false); break; } - - switch(event.Code) { - case PTP_EC_VITA_RequestSendNumOfObject: - vitaEventSendNumOfObject(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendObjectMetadata: - vitaEventSendObjectMetadata(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendObject: - vitaEventSendObject(&event, event.Param1); - break; - case PTP_EC_VITA_RequestCancelTask: // unimplemented - vitaEventCancelTask(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendHttpObjectFromURL: - vitaEventSendHttpObjectFromURL(&event, event.Param1); - break; - case PTP_EC_VITA_Unknown1: // unimplemented - vitaEventUnimplementated(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendObjectStatus: - vitaEventSendObjectStatus(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendObjectThumb: - vitaEventSendObjectThumb(&event, event.Param1); - break; - case PTP_EC_VITA_RequestDeleteObject: - vitaEventDeleteObject(&event, event.Param1); - break; - case PTP_EC_VITA_RequestGetSettingInfo: - vitaEventGetSettingInfo(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendHttpObjectPropFromURL: - vitaEventSendHttpObjectPropFromURL(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendPartOfObject: - vitaEventSendPartOfObject(&event, event.Param1); - break; - case PTP_EC_VITA_RequestOperateObject: - vitaEventOperateObject(&event, event.Param1); - break; - case PTP_EC_VITA_RequestGetPartOfObject: - vitaEventGetPartOfObject(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendStorageSize: - vitaEventSendStorageSize(&event, event.Param1); - break; - case PTP_EC_VITA_RequestCheckExistance: - vitaEventCheckExistance(&event, event.Param1); - break; - case PTP_EC_VITA_RequestGetTreatObject: - vitaEventGetTreatObject(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendCopyConfirmationInfo: - vitaEventSendCopyConfirmationInfo(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendObjectMetadataItems: - vitaEventSendObjectMetadataItems(&event, event.Param1); - break; - case PTP_EC_VITA_RequestSendNPAccountInfo: - vitaEventSendNPAccountInfo(&event, event.Param1); - break; - case PTP_EC_VITA_RequestTerminate: - vitaEventRequestTerminate(&event, event.Param1); - break; - default: - vitaEventUnimplementated(&event, event.Param1); - } - qDebug("Ended event, code: 0x%x, id: %d", event.Code, event.Param1); - } - qDebug("Finished event thread"); - emit deviceDisconnected(); -} - -quint16 CmaClient::processAllObjects(CMAObject *parent, quint32 handle) -{ - union { - unsigned char *fileData; - uint32_t *handles; - } data; - - metadata_t remote_meta; - unsigned int length; - - if(VitaMTP_GetObject(device, handle, &remote_meta, (void **)&data, &length) != PTP_RC_OK) { - qWarning("Cannot get object for handle %d", handle); - return PTP_RC_VITA_Invalid_Data; - } - - CMAObject *object = db.pathToObject(remote_meta.name, parent->metadata.ohfi); - - if(object) { - qDebug("Deleting %s", object->path.toStdString().c_str()); - removeRecursively(object->path); - db.remove(object); - } - - QDir dir(parent->path); - - if(remote_meta.dataType & Folder) { - if(!dir.mkpath(remote_meta.name)) { - qWarning("Cannot create directory: %s", remote_meta.name); - free(data.fileData); - free(remote_meta.name); - return PTP_RC_VITA_Failed_Operate_Object; - } - } else { - QFile file(dir.absoluteFilePath(remote_meta.name)); - - if(!file.open(QIODevice::WriteOnly)) { - qWarning("Cannot write to %s", remote_meta.name); - free(data.fileData); - free(remote_meta.name); - return PTP_RC_VITA_Invalid_Permission; - } else { - file.write((const char *)data.fileData, remote_meta.size); - } - } - - QFileInfo info(dir, remote_meta.name); - object = new CMAObject(parent); - object->initObject(info); - object->metadata.handle = remote_meta.handle; - db.append(parent->metadata.ohfi, object); - free(remote_meta.name); - - qDebug("Added object %s with OHFI %i to database", object->metadata.path, object->metadata.ohfi); - - if(remote_meta.dataType & Folder) { - for(unsigned int i = 0; i < length; i++) { - quint16 ret = processAllObjects(object, data.handles[i]); - - if(ret != PTP_RC_OK) { - qDebug("Deleteting object with OHFI %d", object->metadata.ohfi); - db.remove(object); - free(data.fileData); - return ret; - } - } - } - - free(data.fileData); - return PTP_RC_OK; -} - -void CmaClient::vitaEventGetTreatObject(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - treat_object_t treatObject; - - if(VitaMTP_GetTreatObject(device, eventId, &treatObject) != PTP_RC_OK) { - qWarning("Cannot get information on object to get"); - return; - } - - QMutexLocker locker(&db.mutex); - - CMAObject *parent = db.ohfiToObject(treatObject.ohfiParent); - - if(parent == NULL) { - qWarning("Cannot find parent OHFI %d", treatObject.ohfiParent); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); - return; - } - - VitaMTP_ReportResult(device, eventId, processAllObjects(parent, treatObject.handle)); -} - -void CmaClient::vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - copy_confirmation_info_t *info; - if(VitaMTP_SendCopyConfirmationInfoInit(device, eventId, &info) != PTP_RC_OK) { - qWarning("Error recieving initial information."); - return; - } - - QMutexLocker locker(&db.mutex); - - quint64 size = 0; - - for(quint32 i = 0; i < info->count; i++) { - CMAObject *object; - - if((object = db.ohfiToObject(info->ohfi[i])) == NULL) { - qWarning("Cannot find OHFI %d", info->ohfi[i]); - free(info); - return; - } - - size += object->metadata.size; - } - - if(VitaMTP_SendCopyConfirmationInfo(device, eventId, info, size) != PTP_RC_OK) { - qWarning("Error sending copy confirmation"); - } else { - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } - - free(info); -} - -void CmaClient::vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - quint32 ohfi; - if(VitaMTP_SendObjectMetadataItems(device, eventId, &ohfi) != PTP_RC_OK) { - qWarning("Cannot get OHFI for retreving metadata"); - return; - } - - QMutexLocker locker(&db.mutex); - - CMAObject *object = db.ohfiToObject(ohfi); - - if(object == NULL) { - qWarning("Cannot find OHFI %d in database", ohfi); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); - return; - } - - metadata_t *metadata = &object->metadata; - metadata->next_metadata = NULL; - qDebug("Sending metadata for OHFI %d (%s)", ohfi, metadata->path); - - if(VitaMTP_SendObjectMetadata(device, eventId, metadata) != PTP_RC_OK) { - qWarning("Error sending metadata"); - } else { - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + CmaEvent *vita_event = new CmaEvent(device, event); + connect(vita_event, SIGNAL(finishedEventLoop()), this, SLOT(finishEventLoop())); + connect(vita_event, SIGNAL(refreshDatabase()), this, SIGNAL(refreshDatabase())); + vita_event->start("cma_event"); } } -void CmaClient::vitaEventSendNPAccountInfo(vita_event_t *event, int eventId) +bool CmaClient::isEventLoopEnabled() { - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - // AFAIK, Sony hasn't even implemented this in their CMA - qWarning("Event 0x%x unimplemented!", event->Code); + QMutexLocker locker(&eloop); + return event_loop_enabled; } -void CmaClient::vitaEventRequestTerminate(vita_event_t *event, int eventId) +void CmaClient::finishEventLoop() { - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - //qWarning("Event 0x%x unimplemented!", event->Code); - setEventLoop(false); -} - -void CmaClient::vitaEventUnimplementated(vita_event_t *event, int eventId) -{ - qWarning("Unknown event not handled, code: 0x%x, id: %d", event->Code, eventId); - qWarning("Param1: 0x%08X, Param2: 0x%08X, Param3: 0x%08X", event->Param1, event->Param2, event->Param3); -} - -void CmaClient::vitaEventSendNumOfObject(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - QMutexLocker locker(&db.mutex); - - uint ohfi = event->Param2; - int items = db.filterObjects(ohfi, NULL); - - if(VitaMTP_SendNumOfObject(device, eventId, items) != PTP_RC_OK) { - qWarning("Error occured receiving object count for OHFI parent %d", ohfi); - } else { - qDebug("Returned count of %d objects for OHFI parent %d", items, ohfi); - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } -} - -void CmaClient::vitaEventSendObjectMetadata(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - browse_info_t browse; - - if(VitaMTP_GetBrowseInfo(device, eventId, &browse) != PTP_RC_OK) { - qWarning("GetBrowseInfo failed"); - return; - } - QMutexLocker locker(&db.mutex); - - metadata_t *meta; - int count = db.filterObjects(browse.ohfiParent, &meta); // if meta is null, will return empty XML - qDebug("Sending %i metadata filtered objects for OHFI %d", count, browse.ohfiParent); - - if(VitaMTP_SendObjectMetadata(device, eventId, meta) != PTP_RC_OK) { // send all objects with OHFI parent - qWarning("Sending metadata for OHFI parent %d failed", browse.ohfiParent); - } else { - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } -} - -void CmaClient::vitaEventSendObject(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - int ohfi = event->Param2; - - QMutexLocker locker(&db.mutex); - - qDebug("Searching object with OHFI %d", ohfi); - - Database::find_data iters; - if(!db.find(ohfi, iters)) { - qWarning("Failed to find OHFI %d", ohfi); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); - return; - } - - unsigned long len = 0; - CMAObject *object = *iters.it; - CMAObject *start = object; - uint parentHandle = event->Param3; - bool send_folder = object->metadata.dataType & Folder; - uint handle; - - do { - uchar *data = NULL; - len = object->metadata.size; - QFile file(object->path); - - // read the file to send if it's not a directory - // if it is a directory, data and len are not used by VitaMTP - if(object->metadata.dataType & File) { - if(!file.open(QIODevice::ReadOnly) || (data = file.map(0, file.size())) == NULL) { - qWarning("Failed to read %s", object->path.toStdString().c_str()); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object); - return; - } - } - - // get the PTP object ID for the parent to put the object - // we know the parent has to be before the current node - // the first time this is called, parentHandle is left untouched - - if(start != object) { - parentHandle = object->parent->metadata.handle; - } - - // send the data over - qDebug("Sending %s of %lu bytes to device", object->metadata.name, len); - qDebug("OHFI %d with handle 0x%08X", ohfi, parentHandle); - - if(VitaMTP_SendObject(device, &parentHandle, &handle, &object->metadata, data) != PTP_RC_OK) { - qWarning("Sending of %s failed.", object->metadata.name); - file.unmap(data); - return; - } - - object->metadata.handle = handle; - - if(object->metadata.dataType & File) { - file.unmap(data); - } - - // break early if only a file needs to be sent - if(!send_folder) { - break; - } - - object = *++iters.it; - - } while(iters.it != iters.end && object->metadata.ohfiParent >= OHFI_OFFSET); // get everything under this "folder" - - VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, handle); - - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Data); // TODO: Send thumbnail -} - -void CmaClient::vitaEventCancelTask(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - int eventIdToCancel = event->Param2; - VitaMTP_CancelTask(device, eventIdToCancel); - qWarning("Event CancelTask (0x%x) unimplemented!", event->Code); - setEventLoop(false); -} - -void CmaClient::vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - char *url; - if(VitaMTP_GetUrl(device, eventId, &url) != PTP_RC_OK) { - qWarning("Failed to recieve URL"); - return; - } - - QString urlpath = QSettings().value("urlPath").toString(); - QString basename = QFileInfo(QUrl(url).path()).fileName(); - QFile file(QDir(urlpath).absoluteFilePath(basename)); - - QByteArray data; - - if(!file.open(QIODevice::ReadOnly)) { - if(basename == "psp2-updatelist.xml") { - qDebug("Found request for update list. Sending cached data"); - QFile res(":/main/psp2-updatelist.xml"); - res.open(QIODevice::ReadOnly); - data = res.readAll(); - } else { - qWarning("Failed to download %s", url); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Download); - free(url); - return; - } - } else { - data = file.readAll(); - } - - qDebug("Sending %i bytes of data for HTTP request %s", data.size(), url); - - if(VitaMTP_SendHttpObjectFromURL(device, eventId, data.data(), data.size()) != PTP_RC_OK) { - qWarning("Failed to send HTTP object"); - } else { - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } - - free(url); -} - -void CmaClient::vitaEventSendObjectStatus(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - object_status_t objectstatus; - - if(VitaMTP_SendObjectStatus(device, eventId, &objectstatus) != PTP_RC_OK) { - qWarning("Failed to get information for object status."); - return; - } - - QMutexLocker locker(&db.mutex); - - CMAObject *object = db.pathToObject(objectstatus.title, objectstatus.ohfiRoot); - - if(object == NULL) { // not in database, don't return metadata - qDebug("Object %s not in database (OHFI: %i). Sending OK response for non-existence", objectstatus.title, objectstatus.ohfiRoot); - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } else { - metadata_t *metadata = &object->metadata; - metadata->next_metadata = NULL; - qDebug("Sending metadata for OHFI %d", object->metadata.ohfi); - - if(VitaMTP_SendObjectMetadata(device, eventId, metadata) != PTP_RC_OK) { - qWarning("Error sending metadata for %d", object->metadata.ohfi); - } else { - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } - } - - free(objectstatus.title); -} - -void CmaClient::vitaEventSendObjectThumb(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - QMutexLocker locker(&db.mutex); - - int ohfi = event->Param2; - CMAObject *object = db.ohfiToObject(ohfi); - - if(object == NULL) { - qWarning("Cannot find OHFI %d in database.", ohfi); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); - return; - } - - QByteArray data = getThumbnail(object->path, object->metadata.dataType, &g_thumbmeta); - - if(data.size() == 0) { - qWarning("Cannot find/read thumbnail for %s", object->path.toStdString().c_str()); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Data); - return; - } - - // workaround for the vitamtp locale bug - char *locale = strdup(setlocale(LC_ALL, NULL)); - setlocale(LC_ALL, "C"); - - if(VitaMTP_SendObjectThumb(device, eventId, (metadata_t *)&g_thumbmeta, (uchar *)data.data(), data.size()) != PTP_RC_OK) { - qWarning("Error sending thumbnail"); - } else { - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } - - // restore locale - setlocale(LC_ALL, locale); - free(locale); -} - -void CmaClient::vitaEventDeleteObject(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - QMutexLocker locker(&db.mutex); - - int ohfi = event->Param2; - CMAObject *object = db.ohfiToObject(ohfi); - - if(object == NULL) { - qWarning("OHFI %d not found", ohfi); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); - return; - } - - qDebug("Deleting %s, OHFI: %i", object->metadata.path, object->metadata.ohfi); - removeRecursively(object->path); - db.remove(object); - - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); -} - -void CmaClient::vitaEventGetSettingInfo(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - settings_info_t *settingsinfo; - if(VitaMTP_GetSettingInfo(device, eventId, &settingsinfo) != PTP_RC_OK) { - qWarning("Failed to get setting info from Vita."); - return; - } - - qDebug("Current account id: %s", settingsinfo->current_account.accountId); - - - if(QSettings().value("lastAccountId").toString() != settingsinfo->current_account.accountId) { - db.setUUID(settingsinfo->current_account.accountId); - // set the database to be updated ASAP - emit refreshDatabase(); - } - - // free all the information - VitaMTP_Data_Free_Settings(settingsinfo); - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); -} - -void CmaClient::vitaEventSendHttpObjectPropFromURL(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - char *url; - if(VitaMTP_GetUrl(device, eventId, &url) != PTP_RC_OK) { - qWarning("Failed to get URL"); - return; - } - - QString urlpath = QSettings().value("urlPath").toString(); - QString basename = QFileInfo(url).fileName(); - QFileInfo file(QDir(urlpath).absoluteFilePath(basename)); - - if(!file.exists()) { - qWarning("The file %s is not accesible", url); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Download); - free(url); - return; - } - - QString timestamp = file.lastModified().toString(); - - http_object_prop_t httpobjectprop; - httpobjectprop.timestamp = timestamp.toUtf8().data(); - httpobjectprop.timestamp_len = timestamp.toUtf8().size(); - - if(VitaMTP_SendHttpObjectPropFromURL(device, eventId, &httpobjectprop) != PTP_RC_OK) { - qWarning("Failed to send object properties"); - } else { - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } - - free(url); -} - -void CmaClient::vitaEventSendPartOfObject(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - send_part_init_t part_init; - - if(VitaMTP_SendPartOfObjectInit(device, eventId, &part_init) != PTP_RC_OK) { - qWarning("Cannot get information on object to send"); - return; - } - - QMutexLocker locker(&db.mutex); - - CMAObject *object = db.ohfiToObject(part_init.ohfi); - - if(object == NULL) { - qWarning("Cannot find object for OHFI %d", part_init.ohfi); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Context); - return; - } - - QFile file(object->path); - - if(!file.open(QIODevice::ReadOnly)) { - qWarning("Cannot read %s", object->path.toStdString().c_str()); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object); - return; - } else { - file.seek(part_init.offset); - QByteArray data = file.read(part_init.size); - qDebug("Sending %s at file offset %zu for %zu bytes", object->metadata.path, part_init.offset, part_init.size); - - if(VitaMTP_SendPartOfObject(device, eventId, (unsigned char *)data.data(), data.size()) != PTP_RC_OK) { - qWarning("Failed to send part of object OHFI %d", part_init.ohfi); - } else { - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } - } -} - -void CmaClient::vitaEventOperateObject(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - operate_object_t operateobject; - - if(VitaMTP_OperateObject(device, eventId, &operateobject) != PTP_RC_OK) { - qWarning("Cannot get information on object to operate"); - return; - } - - QMutexLocker locker(&db.mutex); - - CMAObject *root = db.ohfiToObject(operateobject.ohfi); - - // end for renaming only - if(root == NULL) { - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object); - return; - } - - switch(operateobject.cmd) { - case VITA_OPERATE_CREATE_FOLDER: { - qDebug("Operate command %d: Create folder %s", operateobject.cmd, operateobject.title); - - QDir dir(root->path); - if(!dir.mkdir(operateobject.title)) { - qWarning("Unable to create temporary folder: %s", operateobject.title); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object); - } else { - CMAObject *newobj = new CMAObject(root); - newobj->initObject(QFileInfo(dir, operateobject.title)); - db.append(operateobject.ohfi, newobj); - qDebug("Created folder %s with OHFI %d under parent %s", newobj->metadata.path, newobj->metadata.ohfi, root->metadata.path); - VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, newobj->metadata.ohfi); - } - break; - } - case VITA_OPERATE_CREATE_FILE: { - qDebug("Operate command %d: Create file %s", operateobject.cmd, operateobject.title); - - QFile file(root->path + QDir::separator() + operateobject.title); - if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - qWarning("Unable to create temporary file: %s", operateobject.title); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object); - } else { - CMAObject *newobj = new CMAObject(root); - newobj->initObject(file); - db.append(root->metadata.ohfi, newobj); - qDebug("Created file %s with OHFI %d under parent %s", newobj->metadata.path, newobj->metadata.ohfi, root->metadata.path); - VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, newobj->metadata.ohfi); - } - break; - } - case VITA_OPERATE_RENAME: { - qDebug("Operate command %d: Rename %s to %s", operateobject.cmd, root->metadata.name, operateobject.title); - - QString oldpath = root->path; - QString oldname = root->metadata.name; - - //rename the current object - root->rename(operateobject.title); - Database::find_data iters; - db.find(root->metadata.ohfi, iters); - - // rename the rest of the list only if has the renamed parent in some part of the chain - while(iters.it != iters.end) { - CMAObject *obj = *iters.it++; - - if(obj->hasParent(root)) { - obj->refreshPath(); - } - } - - // rename in filesystem - if(!QFile(oldpath).rename(root->path)) { - qWarning("Unable to rename %s to %s", oldname.toStdString().c_str(), operateobject.title); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object); - break; - } - - qDebug("Renamed OHFI %d from %s to %s", root->metadata.ohfi, oldname.toStdString().c_str(), root->metadata.name); - VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, root->metadata.ohfi); - break; - } - - default: - qWarning("Operate command %d: Not implemented", operateobject.cmd); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object); - break; - } - - free(operateobject.title); -} - -void CmaClient::vitaEventGetPartOfObject(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - unsigned char *data; - send_part_init_t part_init; - - if(VitaMTP_GetPartOfObject(device, eventId, &part_init, &data) != PTP_RC_OK) { - qWarning("Cannot get object from device"); - return; - } - - QMutexLocker locker(&db.mutex); - CMAObject *object = db.ohfiToObject(part_init.ohfi); - - if(object == NULL) { - qWarning("Cannot find OHFI %d", part_init.ohfi); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); - free(data); - return; - } - - qDebug("Receiving %s at offset %zu for %zu bytes", object->metadata.path, part_init.offset, part_init.size); - - QFile file(object->path); - if(!file.open(QIODevice::ReadWrite)) { - qWarning("Cannot write to file %s", object->path.toStdString().c_str()); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission); - } else { - file.seek(part_init.offset); - file.write((const char *)data, part_init.size); - object->updateObjectSize(part_init.size); - qDebug("Written %zu bytes to %s at offset %zu.", part_init.size, object->path.toStdString().c_str(), part_init.offset); - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } - - free(data); -} - -void CmaClient::vitaEventSendStorageSize(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - QMutexLocker locker(&db.mutex); - - int ohfi = event->Param2; - CMAObject *object = db.ohfiToObject(ohfi); - - if(object == NULL) { - qWarning("Error: Cannot find OHFI %d", ohfi); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); - return; - } else { - QFile file(object->path); - - if(!file.exists()) { - // create the directory if doesn't exist so the query don't fail - qDebug("Creating %s", object->path.toStdString().c_str()); - - if(!QDir(QDir::root()).mkpath(object->path)) { - qWarning("Create directory failed"); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission); - return; - } - } - } - - quint64 total; - quint64 free; - - if(!getDiskSpace(object->path, &free, &total)) { - qWarning("Cannot get disk space"); - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission); - return; - } - - qDebug("Storage stats for drive containing OHFI %d, free: %llu, total: %llu", ohfi, free, total); - - if(VitaMTP_SendStorageSize(device, eventId, total, free) != PTP_RC_OK) { - qWarning("Send storage size failed"); - } else { - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); - } -} - -void CmaClient::vitaEventCheckExistance(vita_event_t *event, int eventId) -{ - qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); - - int handle = event->Param2; - existance_object_t existance; - - if(VitaMTP_CheckExistance(device, handle, &existance) != PTP_RC_OK) { - qWarning("Cannot read information on object to be sent"); - return; - } - - QMutexLocker locker(&db.mutex); - - CMAObject *object = db.pathToObject(existance.name, 0); - - if(object == NULL) { - VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Different_Object); - } else { - VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_VITA_Same_Object, object->metadata.ohfi); - } - - VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + QMutexLocker locker(&eloop); + event_loop_enabled = false; } void CmaClient::close() @@ -1007,7 +190,7 @@ void CmaClient::close() void CmaClient::stop() { CmaClient::setRunning(false); - CmaClient::setEventLoop(false); + CmaClient::finishEventLoop(); } CmaClient::~CmaClient() diff --git a/cmaclient.h b/cmaclient.h index d9229b1..dbe9827 100644 --- a/cmaclient.h +++ b/cmaclient.h @@ -42,8 +42,6 @@ public: void launch(); - static Database db; - private: static bool isRunning(); static void setRunning(bool state); @@ -53,29 +51,6 @@ private: void processNewConnection(vita_device_t *device); - uint16_t processAllObjects(CMAObject *parent, uint32_t handle); - void vitaEventSendObject(vita_event_t *event, int eventId); - void vitaEventSendObjectMetadata(vita_event_t *event, int eventId); - void vitaEventSendNumOfObject(vita_event_t *event, int eventId); - void vitaEventCancelTask(vita_event_t *event, int eventId); - void vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId); - void vitaEventUnimplementated(vita_event_t *event, int eventId); - void vitaEventSendObjectStatus(vita_event_t *event, int eventId); - void vitaEventSendObjectThumb(vita_event_t *event, int eventId); - void vitaEventDeleteObject(vita_event_t *event, int eventId); - void vitaEventGetSettingInfo(vita_event_t *event, int eventId); - void vitaEventSendHttpObjectPropFromURL(vita_event_t *event, int eventId); - void vitaEventSendPartOfObject(vita_event_t *event, int eventId); - void vitaEventOperateObject(vita_event_t *event, int eventId); - void vitaEventGetPartOfObject(vita_event_t *event, int eventId); - void vitaEventSendStorageSize(vita_event_t *event, int eventId); - void vitaEventCheckExistance(vita_event_t *event, int eventId); - void vitaEventGetTreatObject(vita_event_t *event, int eventId); - void vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventId); - void vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId); - void vitaEventSendNPAccountInfo(vita_event_t *event, int eventId); - void vitaEventRequestTerminate(vita_event_t *event, int eventId); - static int deviceRegistered(const char *deviceid); static int generatePin(wireless_vita_info_t *info, int *p_err); @@ -84,7 +59,6 @@ private: vita_device_t *device; static bool event_loop_enabled; static bool is_running; - static metadata_t g_thumbmeta; static CmaClient *this_object; static QMutex mutex; static QMutex runner; @@ -105,6 +79,7 @@ public slots: private slots: void connectUsb(); void connectWireless(); + static void finishEventLoop(); }; #endif // CMACLIENT_H diff --git a/cmaevent.cpp b/cmaevent.cpp new file mode 100644 index 0000000..473ba0a --- /dev/null +++ b/cmaevent.cpp @@ -0,0 +1,839 @@ +#include "cmaevent.h" +#include "utils.h" + +#include +#include +#include +#include +#include + +Database CmaEvent::db; + +metadata_t CmaEvent::g_thumbmeta = {0, 0, 0, NULL, NULL, 0, 0, 0, Thumbnail, {{18, 144, 80, 0, 1, 1.0f, 2}}, NULL}; + +CmaEvent::CmaEvent(vita_device_t *s_device, vita_event_t s_event, QObject *parent) : + BaseWorker(parent), device(s_device), t_event(s_event) +{ +} + +void CmaEvent::process() +{ + qDebug() << "Starting event_thread:" << QThread::currentThreadId(); + switch(t_event.Code) { + case PTP_EC_VITA_RequestSendNumOfObject: + vitaEventSendNumOfObject(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendObjectMetadata: + vitaEventSendObjectMetadata(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendObject: + vitaEventSendObject(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestCancelTask: // unimplemented + vitaEventCancelTask(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendHttpObjectFromURL: + vitaEventSendHttpObjectFromURL(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_Unknown1: // unimplemented + vitaEventUnimplementated(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendObjectStatus: + vitaEventSendObjectStatus(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendObjectThumb: + vitaEventSendObjectThumb(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestDeleteObject: + vitaEventDeleteObject(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestGetSettingInfo: + vitaEventGetSettingInfo(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendHttpObjectPropFromURL: + vitaEventSendHttpObjectPropFromURL(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendPartOfObject: + vitaEventSendPartOfObject(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestOperateObject: + vitaEventOperateObject(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestGetPartOfObject: + vitaEventGetPartOfObject(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendStorageSize: + vitaEventSendStorageSize(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestCheckExistance: + vitaEventCheckExistance(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestGetTreatObject: + vitaEventGetTreatObject(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendCopyConfirmationInfo: + vitaEventSendCopyConfirmationInfo(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendObjectMetadataItems: + vitaEventSendObjectMetadataItems(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestSendNPAccountInfo: + vitaEventSendNPAccountInfo(&t_event, t_event.Param1); + break; + case PTP_EC_VITA_RequestTerminate: + vitaEventRequestTerminate(&t_event, t_event.Param1); + break; + default: + vitaEventUnimplementated(&t_event, t_event.Param1); + } + qDebug("Ended event, code: 0x%x, id: %d", t_event.Code, t_event.Param1); + + emit finished(); +} + +quint16 CmaEvent::processAllObjects(CMAObject *parent, quint32 handle) +{ + union { + unsigned char *fileData; + uint32_t *handles; + } data; + + metadata_t remote_meta; + unsigned int length; + + if(VitaMTP_GetObject(device, handle, &remote_meta, (void **)&data, &length) != PTP_RC_OK) { + qWarning("Cannot get object for handle %d", handle); + return PTP_RC_VITA_Invalid_Data; + } + + CMAObject *object = db.pathToObject(remote_meta.name, parent->metadata.ohfi); + + if(object) { + qDebug("Deleting %s", object->path.toStdString().c_str()); + removeRecursively(object->path); + db.remove(object); + } + + QDir dir(parent->path); + + if(remote_meta.dataType & Folder) { + if(!dir.mkpath(remote_meta.name)) { + qWarning("Cannot create directory: %s", remote_meta.name); + free(data.fileData); + free(remote_meta.name); + return PTP_RC_VITA_Failed_Operate_Object; + } + } else { + QFile file(dir.absoluteFilePath(remote_meta.name)); + + if(!file.open(QIODevice::WriteOnly)) { + qWarning("Cannot write to %s", remote_meta.name); + free(data.fileData); + free(remote_meta.name); + return PTP_RC_VITA_Invalid_Permission; + } else { + file.write((const char *)data.fileData, remote_meta.size); + } + } + + QFileInfo info(dir, remote_meta.name); + object = new CMAObject(parent); + object->initObject(info); + object->metadata.handle = remote_meta.handle; + db.append(parent->metadata.ohfi, object); + free(remote_meta.name); + + qDebug("Added object %s with OHFI %i to database", object->metadata.path, object->metadata.ohfi); + + if(remote_meta.dataType & Folder) { + for(unsigned int i = 0; i < length; i++) { + quint16 ret = processAllObjects(object, data.handles[i]); + + if(ret != PTP_RC_OK) { + qDebug("Deleteting object with OHFI %d", object->metadata.ohfi); + db.remove(object); + free(data.fileData); + return ret; + } + } + } + + free(data.fileData); + return PTP_RC_OK; +} + +void CmaEvent::vitaEventGetTreatObject(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + treat_object_t treatObject; + + if(VitaMTP_GetTreatObject(device, eventId, &treatObject) != PTP_RC_OK) { + qWarning("Cannot get information on object to get"); + return; + } + + QMutexLocker locker(&db.mutex); + + CMAObject *parent = db.ohfiToObject(treatObject.ohfiParent); + + if(parent == NULL) { + qWarning("Cannot find parent OHFI %d", treatObject.ohfiParent); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); + return; + } + + VitaMTP_ReportResult(device, eventId, processAllObjects(parent, treatObject.handle)); +} + +void CmaEvent::vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + copy_confirmation_info_t *info; + if(VitaMTP_SendCopyConfirmationInfoInit(device, eventId, &info) != PTP_RC_OK) { + qWarning("Error recieving initial information."); + return; + } + + QMutexLocker locker(&db.mutex); + + quint64 size = 0; + + for(quint32 i = 0; i < info->count; i++) { + CMAObject *object; + + if((object = db.ohfiToObject(info->ohfi[i])) == NULL) { + qWarning("Cannot find OHFI %d", info->ohfi[i]); + free(info); + return; + } + + size += object->metadata.size; + } + + if(VitaMTP_SendCopyConfirmationInfo(device, eventId, info, size) != PTP_RC_OK) { + qWarning("Error sending copy confirmation"); + } else { + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } + + free(info); +} + +void CmaEvent::vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + quint32 ohfi; + if(VitaMTP_SendObjectMetadataItems(device, eventId, &ohfi) != PTP_RC_OK) { + qWarning("Cannot get OHFI for retreving metadata"); + return; + } + + QMutexLocker locker(&db.mutex); + + CMAObject *object = db.ohfiToObject(ohfi); + + if(object == NULL) { + qWarning("Cannot find OHFI %d in database", ohfi); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); + return; + } + + metadata_t *metadata = &object->metadata; + metadata->next_metadata = NULL; + qDebug("Sending metadata for OHFI %d (%s)", ohfi, metadata->path); + + if(VitaMTP_SendObjectMetadata(device, eventId, metadata) != PTP_RC_OK) { + qWarning("Error sending metadata"); + } else { + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } +} + +void CmaEvent::vitaEventSendNPAccountInfo(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + // AFAIK, Sony hasn't even implemented this in their CMA + qWarning("Event 0x%x unimplemented!", event->Code); +} + +void CmaEvent::vitaEventRequestTerminate(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + //qWarning("Event 0x%x unimplemented!", event->Code); + emit finishedEventLoop(); +} + +void CmaEvent::vitaEventUnimplementated(vita_event_t *event, int eventId) +{ + qWarning("Unknown event not handled, code: 0x%x, id: %d", event->Code, eventId); + qWarning("Param1: 0x%08X, Param2: 0x%08X, Param3: 0x%08X", event->Param1, event->Param2, event->Param3); +} + +void CmaEvent::vitaEventSendNumOfObject(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + QMutexLocker locker(&db.mutex); + + uint ohfi = event->Param2; + int items = db.filterObjects(ohfi, NULL); + + if(VitaMTP_SendNumOfObject(device, eventId, items) != PTP_RC_OK) { + qWarning("Error occured receiving object count for OHFI parent %d", ohfi); + } else { + qDebug("Returned count of %d objects for OHFI parent %d", items, ohfi); + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } +} + +void CmaEvent::vitaEventSendObjectMetadata(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + browse_info_t browse; + + if(VitaMTP_GetBrowseInfo(device, eventId, &browse) != PTP_RC_OK) { + qWarning("GetBrowseInfo failed"); + return; + } + QMutexLocker locker(&db.mutex); + + metadata_t *meta; + int count = db.filterObjects(browse.ohfiParent, &meta); // if meta is null, will return empty XML + qDebug("Sending %i metadata filtered objects for OHFI %d", count, browse.ohfiParent); + + if(VitaMTP_SendObjectMetadata(device, eventId, meta) != PTP_RC_OK) { // send all objects with OHFI parent + qWarning("Sending metadata for OHFI parent %d failed", browse.ohfiParent); + } else { + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } +} + +void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + int ohfi = event->Param2; + + QMutexLocker locker(&db.mutex); + + qDebug("Searching object with OHFI %d", ohfi); + + Database::find_data iters; + if(!db.find(ohfi, iters)) { + qWarning("Failed to find OHFI %d", ohfi); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); + return; + } + + unsigned long len = 0; + CMAObject *object = *iters.it; + CMAObject *start = object; + uint parentHandle = event->Param3; + bool send_folder = object->metadata.dataType & Folder; + uint handle; + + do { + uchar *data = NULL; + len = object->metadata.size; + QFile file(object->path); + + // read the file to send if it's not a directory + // if it is a directory, data and len are not used by VitaMTP + if(object->metadata.dataType & File) { + if(!file.open(QIODevice::ReadOnly) || (data = file.map(0, file.size())) == NULL) { + qWarning("Failed to read %s", object->path.toStdString().c_str()); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object); + return; + } + } + + // get the PTP object ID for the parent to put the object + // we know the parent has to be before the current node + // the first time this is called, parentHandle is left untouched + + if(start != object) { + parentHandle = object->parent->metadata.handle; + } + + // send the data over + qDebug("Sending %s of %lu bytes to device", object->metadata.name, len); + qDebug("OHFI %d with handle 0x%08X", ohfi, parentHandle); + + if(VitaMTP_SendObject(device, &parentHandle, &handle, &object->metadata, data) != PTP_RC_OK) { + qWarning("Sending of %s failed.", object->metadata.name); + file.unmap(data); + return; + } + + object->metadata.handle = handle; + + if(object->metadata.dataType & File) { + file.unmap(data); + } + + // break early if only a file needs to be sent + if(!send_folder) { + break; + } + + object = *++iters.it; + + } while(iters.it != iters.end && object->metadata.ohfiParent >= OHFI_OFFSET); // get everything under this "folder" + + VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, handle); + + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Data); // TODO: Send thumbnail +} + +void CmaEvent::vitaEventCancelTask(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + int eventIdToCancel = event->Param2; + VitaMTP_CancelTask(device, eventIdToCancel); +} + +void CmaEvent::vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + char *url; + if(VitaMTP_GetUrl(device, eventId, &url) != PTP_RC_OK) { + qWarning("Failed to recieve URL"); + return; + } + + QString urlpath = QSettings().value("urlPath").toString(); + QString basename = QFileInfo(QUrl(url).path()).fileName(); + QFile file(QDir(urlpath).absoluteFilePath(basename)); + + QByteArray data; + + if(!file.open(QIODevice::ReadOnly)) { + if(basename == "psp2-updatelist.xml") { + qDebug("Found request for update list. Sending cached data"); + QFile res(":/main/psp2-updatelist.xml"); + res.open(QIODevice::ReadOnly); + data = res.readAll(); + } else { + qWarning("Failed to download %s", url); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Download); + free(url); + return; + } + } else { + data = file.readAll(); + } + + qDebug("Sending %i bytes of data for HTTP request %s", data.size(), url); + + if(VitaMTP_SendHttpObjectFromURL(device, eventId, data.data(), data.size()) != PTP_RC_OK) { + qWarning("Failed to send HTTP object"); + } else { + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } + + free(url); +} + +void CmaEvent::vitaEventSendObjectStatus(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + object_status_t objectstatus; + + if(VitaMTP_SendObjectStatus(device, eventId, &objectstatus) != PTP_RC_OK) { + qWarning("Failed to get information for object status."); + return; + } + + QMutexLocker locker(&db.mutex); + + CMAObject *object = db.pathToObject(objectstatus.title, objectstatus.ohfiRoot); + + if(object == NULL) { // not in database, don't return metadata + qDebug("Object %s not in database (OHFI: %i). Sending OK response for non-existence", objectstatus.title, objectstatus.ohfiRoot); + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } else { + metadata_t *metadata = &object->metadata; + metadata->next_metadata = NULL; + qDebug("Sending metadata for OHFI %d", object->metadata.ohfi); + + if(VitaMTP_SendObjectMetadata(device, eventId, metadata) != PTP_RC_OK) { + qWarning("Error sending metadata for %d", object->metadata.ohfi); + } else { + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } + } + + free(objectstatus.title); +} + +void CmaEvent::vitaEventSendObjectThumb(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + QMutexLocker locker(&db.mutex); + + int ohfi = event->Param2; + CMAObject *object = db.ohfiToObject(ohfi); + + if(object == NULL) { + qWarning("Cannot find OHFI %d in database.", ohfi); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); + return; + } + + QByteArray data = getThumbnail(object->path, object->metadata.dataType, &g_thumbmeta); + + if(data.size() == 0) { + qWarning("Cannot find/read thumbnail for %s", object->path.toStdString().c_str()); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Data); + return; + } + + // workaround for the vitamtp locale bug + char *locale = strdup(setlocale(LC_ALL, NULL)); + setlocale(LC_ALL, "C"); + + if(VitaMTP_SendObjectThumb(device, eventId, (metadata_t *)&g_thumbmeta, (uchar *)data.data(), data.size()) != PTP_RC_OK) { + qWarning("Error sending thumbnail"); + } else { + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } + + // restore locale + setlocale(LC_ALL, locale); + free(locale); +} + +void CmaEvent::vitaEventDeleteObject(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + QMutexLocker locker(&db.mutex); + + int ohfi = event->Param2; + CMAObject *object = db.ohfiToObject(ohfi); + + if(object == NULL) { + qWarning("OHFI %d not found", ohfi); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); + return; + } + + qDebug("Deleting %s, OHFI: %i", object->metadata.path, object->metadata.ohfi); + removeRecursively(object->path); + db.remove(object); + + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); +} + +void CmaEvent::vitaEventGetSettingInfo(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + settings_info_t *settingsinfo; + if(VitaMTP_GetSettingInfo(device, eventId, &settingsinfo) != PTP_RC_OK) { + qWarning("Failed to get setting info from Vita."); + return; + } + + qDebug("Current account id: %s", settingsinfo->current_account.accountId); + + + if(QSettings().value("lastAccountId").toString() != settingsinfo->current_account.accountId) { + db.setUUID(settingsinfo->current_account.accountId); + // set the database to be updated ASAP + emit refreshDatabase(); + } + + // free all the information + VitaMTP_Data_Free_Settings(settingsinfo); + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); +} + +void CmaEvent::vitaEventSendHttpObjectPropFromURL(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + char *url; + if(VitaMTP_GetUrl(device, eventId, &url) != PTP_RC_OK) { + qWarning("Failed to get URL"); + return; + } + + QString urlpath = QSettings().value("urlPath").toString(); + QString basename = QFileInfo(url).fileName(); + QFileInfo file(QDir(urlpath).absoluteFilePath(basename)); + + if(!file.exists()) { + qWarning("The file %s is not accesible", url); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Download); + free(url); + return; + } + + QString timestamp = file.lastModified().toString(); + + http_object_prop_t httpobjectprop; + httpobjectprop.timestamp = timestamp.toUtf8().data(); + httpobjectprop.timestamp_len = timestamp.toUtf8().size(); + + if(VitaMTP_SendHttpObjectPropFromURL(device, eventId, &httpobjectprop) != PTP_RC_OK) { + qWarning("Failed to send object properties"); + } else { + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } + + free(url); +} + +void CmaEvent::vitaEventSendPartOfObject(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + send_part_init_t part_init; + + if(VitaMTP_SendPartOfObjectInit(device, eventId, &part_init) != PTP_RC_OK) { + qWarning("Cannot get information on object to send"); + return; + } + + QMutexLocker locker(&db.mutex); + + CMAObject *object = db.ohfiToObject(part_init.ohfi); + + if(object == NULL) { + qWarning("Cannot find object for OHFI %d", part_init.ohfi); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Context); + return; + } + + QFile file(object->path); + + if(!file.open(QIODevice::ReadOnly)) { + qWarning("Cannot read %s", object->path.toStdString().c_str()); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object); + return; + } else { + file.seek(part_init.offset); + QByteArray data = file.read(part_init.size); + qDebug("Sending %s at file offset %zu for %zu bytes", object->metadata.path, part_init.offset, part_init.size); + + if(VitaMTP_SendPartOfObject(device, eventId, (unsigned char *)data.data(), data.size()) != PTP_RC_OK) { + qWarning("Failed to send part of object OHFI %d", part_init.ohfi); + } else { + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } + } +} + +void CmaEvent::vitaEventOperateObject(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + operate_object_t operateobject; + + if(VitaMTP_OperateObject(device, eventId, &operateobject) != PTP_RC_OK) { + qWarning("Cannot get information on object to operate"); + return; + } + + QMutexLocker locker(&db.mutex); + + CMAObject *root = db.ohfiToObject(operateobject.ohfi); + + // end for renaming only + if(root == NULL) { + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Not_Exist_Object); + return; + } + + switch(operateobject.cmd) { + case VITA_OPERATE_CREATE_FOLDER: { + qDebug("Operate command %d: Create folder %s", operateobject.cmd, operateobject.title); + + QDir dir(root->path); + if(!dir.mkdir(operateobject.title)) { + qWarning("Unable to create temporary folder: %s", operateobject.title); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object); + } else { + CMAObject *newobj = new CMAObject(root); + newobj->initObject(QFileInfo(dir, operateobject.title)); + db.append(operateobject.ohfi, newobj); + qDebug("Created folder %s with OHFI %d under parent %s", newobj->metadata.path, newobj->metadata.ohfi, root->metadata.path); + VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, newobj->metadata.ohfi); + } + break; + } + case VITA_OPERATE_CREATE_FILE: { + qDebug("Operate command %d: Create file %s", operateobject.cmd, operateobject.title); + + QFile file(root->path + QDir::separator() + operateobject.title); + if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + qWarning("Unable to create temporary file: %s", operateobject.title); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object); + } else { + CMAObject *newobj = new CMAObject(root); + newobj->initObject(file); + db.append(root->metadata.ohfi, newobj); + qDebug("Created file %s with OHFI %d under parent %s", newobj->metadata.path, newobj->metadata.ohfi, root->metadata.path); + VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, newobj->metadata.ohfi); + } + break; + } + case VITA_OPERATE_RENAME: { + qDebug("Operate command %d: Rename %s to %s", operateobject.cmd, root->metadata.name, operateobject.title); + + QString oldpath = root->path; + QString oldname = root->metadata.name; + + //rename the current object + root->rename(operateobject.title); + Database::find_data iters; + db.find(root->metadata.ohfi, iters); + + // rename the rest of the list only if has the renamed parent in some part of the chain + while(iters.it != iters.end) { + CMAObject *obj = *iters.it++; + + if(obj->hasParent(root)) { + obj->refreshPath(); + } + } + + // rename in filesystem + if(!QFile(oldpath).rename(root->path)) { + qWarning("Unable to rename %s to %s", oldname.toStdString().c_str(), operateobject.title); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object); + break; + } + + qDebug("Renamed OHFI %d from %s to %s", root->metadata.ohfi, oldname.toStdString().c_str(), root->metadata.name); + VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, root->metadata.ohfi); + break; + } + + default: + qWarning("Operate command %d: Not implemented", operateobject.cmd); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Failed_Operate_Object); + break; + } + + free(operateobject.title); +} + +void CmaEvent::vitaEventGetPartOfObject(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + unsigned char *data; + send_part_init_t part_init; + + if(VitaMTP_GetPartOfObject(device, eventId, &part_init, &data) != PTP_RC_OK) { + qWarning("Cannot get object from device"); + return; + } + + QMutexLocker locker(&db.mutex); + CMAObject *object = db.ohfiToObject(part_init.ohfi); + + if(object == NULL) { + qWarning("Cannot find OHFI %d", part_init.ohfi); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); + free(data); + return; + } + + qDebug("Receiving %s at offset %zu for %zu bytes", object->metadata.path, part_init.offset, part_init.size); + + QFile file(object->path); + if(!file.open(QIODevice::ReadWrite)) { + qWarning("Cannot write to file %s", object->path.toStdString().c_str()); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission); + } else { + file.seek(part_init.offset); + file.write((const char *)data, part_init.size); + object->updateObjectSize(part_init.size); + qDebug("Written %zu bytes to %s at offset %zu.", part_init.size, object->path.toStdString().c_str(), part_init.offset); + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } + + free(data); +} + +void CmaEvent::vitaEventSendStorageSize(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + QMutexLocker locker(&db.mutex); + + int ohfi = event->Param2; + CMAObject *object = db.ohfiToObject(ohfi); + + if(object == NULL) { + qWarning("Error: Cannot find OHFI %d", ohfi); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); + return; + } else { + QFile file(object->path); + + if(!file.exists()) { + // create the directory if doesn't exist so the query don't fail + qDebug("Creating %s", object->path.toStdString().c_str()); + + if(!QDir(QDir::root()).mkpath(object->path)) { + qWarning("Create directory failed"); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission); + return; + } + } + } + + quint64 total; + quint64 free; + + if(!getDiskSpace(object->path, &free, &total)) { + qWarning("Cannot get disk space"); + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Permission); + return; + } + + qDebug("Storage stats for drive containing OHFI %d, free: %llu, total: %llu", ohfi, free, total); + + if(VitaMTP_SendStorageSize(device, eventId, total, free) != PTP_RC_OK) { + qWarning("Send storage size failed"); + } else { + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); + } +} + +void CmaEvent::vitaEventCheckExistance(vita_event_t *event, int eventId) +{ + qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); + + int handle = event->Param2; + existance_object_t existance; + + if(VitaMTP_CheckExistance(device, handle, &existance) != PTP_RC_OK) { + qWarning("Cannot read information on object to be sent"); + return; + } + + QMutexLocker locker(&db.mutex); + + CMAObject *object = db.pathToObject(existance.name, 0); + + if(object == NULL) { + VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Different_Object); + } else { + VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_VITA_Same_Object, object->metadata.ohfi); + } + + VitaMTP_ReportResult(device, eventId, PTP_RC_OK); +} diff --git a/cmaevent.h b/cmaevent.h new file mode 100644 index 0000000..993226f --- /dev/null +++ b/cmaevent.h @@ -0,0 +1,59 @@ +#ifndef CMAEVENT_H +#define CMAEVENT_H + +#include "cmaobject.h" +#include "database.h" +#include "baseworker.h" + +#include + +#include + +class CmaEvent : public BaseWorker +{ + Q_OBJECT +public: + explicit CmaEvent(vita_device_t *s_device, vita_event_t s_event, QObject *parent = 0); + + static Database db; + +private: + uint16_t processAllObjects(CMAObject *parent, uint32_t handle); + void vitaEventSendObject(vita_event_t *event, int eventId); + void vitaEventSendObjectMetadata(vita_event_t *event, int eventId); + void vitaEventSendNumOfObject(vita_event_t *event, int eventId); + void vitaEventCancelTask(vita_event_t *event, int eventId); + void vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId); + void vitaEventUnimplementated(vita_event_t *event, int eventId); + void vitaEventSendObjectStatus(vita_event_t *event, int eventId); + void vitaEventSendObjectThumb(vita_event_t *event, int eventId); + void vitaEventDeleteObject(vita_event_t *event, int eventId); + void vitaEventGetSettingInfo(vita_event_t *event, int eventId); + void vitaEventSendHttpObjectPropFromURL(vita_event_t *event, int eventId); + void vitaEventSendPartOfObject(vita_event_t *event, int eventId); + void vitaEventOperateObject(vita_event_t *event, int eventId); + void vitaEventGetPartOfObject(vita_event_t *event, int eventId); + void vitaEventSendStorageSize(vita_event_t *event, int eventId); + void vitaEventCheckExistance(vita_event_t *event, int eventId); + void vitaEventGetTreatObject(vita_event_t *event, int eventId); + void vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventId); + void vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId); + void vitaEventSendNPAccountInfo(vita_event_t *event, int eventId); + void vitaEventRequestTerminate(vita_event_t *event, int eventId); + + vita_device_t *device; + vita_event_t t_event; + + static metadata_t g_thumbmeta; + +signals: + void finished(); + void finishedEventLoop(); + void refreshDatabase(); + +public slots: + void process(); + +}; + +#endif // CMAEVENT_H diff --git a/mainwidget.cpp b/mainwidget.cpp index fdb11c5..d465e57 100644 --- a/mainwidget.cpp +++ b/mainwidget.cpp @@ -19,6 +19,7 @@ #include "mainwidget.h" #include "cmaclient.h" +#include "cmaevent.h" #include "utils.h" #include @@ -100,10 +101,10 @@ void MainWidget::stopServer() void MainWidget::refreshDatabase() { - QMutexLocker locker(&CmaClient::db.mutex); + QMutexLocker locker(&CmaEvent::db.mutex); - CmaClient::db.destroy(); - int count = CmaClient::db.create(); + CmaEvent::db.destroy(); + int count = CmaEvent::db.create(); qDebug("Added %i entries to the database", count); } diff --git a/qcma.pro b/qcma.pro index aee4621..d345017 100644 --- a/qcma.pro +++ b/qcma.pro @@ -26,7 +26,8 @@ SOURCES += main.cpp \ sforeader.cpp \ cmaclient.cpp \ cmabroadcast.cpp \ - avdecoder.cpp + avdecoder.cpp \ + cmaevent.cpp HEADERS += \ capability.h \ @@ -41,7 +42,8 @@ HEADERS += \ sforeader.h \ cmaclient.h \ cmabroadcast.h \ - avdecoder.h + avdecoder.h \ + cmaevent.h CONFIG += link_pkgconfig PKGCONFIG += libvitamtp libavformat libavcodec libavutil libswscale