/* static */ int DnDURIObject::RebaseURIPath(RTCString &strPathAbs, const RTCString &strBaseOld /* = "" */, const RTCString &strBaseNew /* = "" */) { char *pszPath = RTUriFilePath(strPathAbs.c_str()); if (!pszPath) /* No URI? */ pszPath = RTStrDup(strPathAbs.c_str()); int rc; if (pszPath) { const char *pszPathStart = pszPath; const char *pszBaseOld = strBaseOld.c_str(); if ( pszBaseOld && RTPathStartsWith(pszPath, pszBaseOld)) { pszPathStart += strlen(pszBaseOld); } rc = VINF_SUCCESS; if (RT_SUCCESS(rc)) { char *pszPathNew = RTPathJoinA(strBaseNew.c_str(), pszPathStart); if (pszPathNew) { char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */, pszPathNew /* pszPath */, NULL /* pszQuery */, NULL /* pszFragment */); if (pszPathURI) { LogFlowFunc(("Rebasing \"%s\" to \"%s\"\n", strPathAbs.c_str(), pszPathURI)); strPathAbs = RTCString(pszPathURI) + "\r\n"; RTStrFree(pszPathURI); } else rc = VERR_INVALID_PARAMETER; RTStrFree(pszPathNew); } else rc = VERR_NO_MEMORY; } RTStrFree(pszPath); } else rc = VERR_NO_MEMORY; return rc; }
int DnDURIList::AppendURIPath(const char *pszURI, uint32_t fFlags) { AssertPtrReturn(pszURI, VERR_INVALID_POINTER); /** @todo Check for string termination? */ #ifdef DEBUG_andy LogFlowFunc(("pszPath=%s, fFlags=0x%x\n", pszURI, fFlags)); #endif int rc = VINF_SUCCESS; /* Query the path component of a file URI. If this hasn't a * file scheme NULL is returned. */ char *pszFilePath = RTUriFilePath(pszURI, URI_FILE_FORMAT_AUTO); if (pszFilePath) { /* Add the path to our internal file list (recursive in * the case of a directory). */ size_t cbPathLen = RTPathStripTrailingSlash(pszFilePath); if (cbPathLen) { char *pszFileName = RTPathFilename(pszFilePath); if (pszFileName) { Assert(pszFileName >= pszFilePath); size_t cbBase = (fFlags & DNDURILIST_FLAGS_ABSOLUTE_PATHS) ? 0 /* Use start of path as root. */ : pszFileName - pszFilePath; char *pszRoot = &pszFilePath[cbBase]; m_lstRoot.append(pszRoot); #ifdef DEBUG_andy LogFlowFunc(("pszFilePath=%s, pszFileName=%s, pszRoot=%s\n", pszFilePath, pszFileName, pszRoot)); #endif rc = appendPathRecursive(pszFilePath, cbBase, fFlags); } else rc = VERR_NOT_FOUND; } else rc = VERR_INVALID_PARAMETER; RTStrFree(pszFilePath); } else rc = VERR_INVALID_PARAMETER; LogFlowFuncLeaveRC(rc); return rc; }
int DnDURIList::RootFromURIData(const void *pvData, size_t cbData, uint32_t fFlags) { Assert(fFlags == 0); RT_NOREF1(fFlags); AssertPtrReturn(pvData, VERR_INVALID_POINTER); AssertReturn(cbData, VERR_INVALID_PARAMETER); if (!RTStrIsValidEncoding(static_cast<const char *>(pvData))) return VERR_INVALID_PARAMETER; RTCList<RTCString> lstURI = RTCString(static_cast<const char *>(pvData), cbData - 1).split("\r\n"); if (lstURI.isEmpty()) return VINF_SUCCESS; int rc = VINF_SUCCESS; for (size_t i = 0; i < lstURI.size(); ++i) { /* Query the path component of a file URI. If this hasn't a * file scheme, NULL is returned. */ const char *pszURI = lstURI.at(i).c_str(); char *pszFilePath = RTUriFilePath(pszURI); #ifdef DEBUG_andy LogFlowFunc(("pszURI=%s, pszFilePath=%s\n", pszURI, pszFilePath)); #endif if (pszFilePath) { rc = DnDPathSanitize(pszFilePath, strlen(pszFilePath)); if (RT_SUCCESS(rc)) { m_lstRoot.append(pszFilePath); m_cTotal++; } RTStrFree(pszFilePath); } else rc = VERR_INVALID_PARAMETER; if (RT_FAILURE(rc)) break; } return rc; }
static int vbglR3DnDHGProcessURIMessages(uint32_t uClientId, uint32_t *puScreenId, char *pszFormat, uint32_t cbFormat, uint32_t *pcbFormatRecv, void **ppvData, uint32_t cbData, size_t *pcbDataRecv) { /* Make a string list out of the uri data. */ RTCList<RTCString> uriList = RTCString(static_cast<char*>(*ppvData), *pcbDataRecv - 1).split("\r\n"); if (uriList.isEmpty()) return VINF_SUCCESS; uint32_t cbTmpData = _1M * 10; void *pvTmpData = RTMemAlloc(cbTmpData); if (!pvTmpData) return VERR_NO_MEMORY; /* Create and query the drop target directory. */ char pszDropDir[RTPATH_MAX]; int rc = vbglR3DnDCreateDropDir(pszDropDir, sizeof(pszDropDir)); if (RT_FAILURE(rc)) { RTMemFree(pvTmpData); return rc; } /* Patch the old drop data with the new drop directory, so the drop target * can find the files. */ RTCList<RTCString> guestUriList; for (size_t i = 0; i < uriList.size(); ++i) { const RTCString &strUri = uriList.at(i); /* Query the path component of a file URI. If this hasn't a * file scheme, null is returned. */ if (char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO)) { RTCString strFullPath = RTCString().printf("%s%c%s", pszDropDir, RTPATH_SLASH, pszFilePath); char *pszNewUri = RTUriFileCreate(strFullPath.c_str()); if (pszNewUri) { guestUriList.append(pszNewUri); RTStrFree(pszNewUri); } } else guestUriList.append(strUri); } /* Cleanup the old data and write the new data back to the event. */ RTMemFree(*ppvData); RTCString newData = RTCString::join(guestUriList, "\r\n") + "\r\n"; *ppvData = RTStrDupN(newData.c_str(), newData.length()); *pcbDataRecv = newData.length() + 1; /* Lists for holding created files & directories in the case of a * rollback. */ RTCList<RTCString> guestDirList; RTCList<RTCString> guestFileList; char pszPathname[RTPATH_MAX]; uint32_t cbPathname = 0; bool fLoop = true; do { uint32_t uNextMsg; uint32_t cNextParms; rc = vbglR3DnDQueryNextHostMessageType(uClientId, &uNextMsg, &cNextParms, false); DO(("%Rrc - %d\n", rc , uNextMsg)); if (RT_SUCCESS(rc)) { switch(uNextMsg) { case DragAndDropSvc::HOST_DND_HG_SND_DIR: { uint32_t fMode = 0; rc = vbglR3DnDHGProcessSendDirMessage(uClientId, pszPathname, sizeof(pszPathname), &cbPathname, &fMode); if (RT_SUCCESS(rc)) { DO(("Got drop dir: %s - %o - %Rrc\n", pszPathname, fMode, rc)); char *pszNewDir = RTPathJoinA(pszDropDir, pszPathname); rc = RTDirCreate(pszNewDir, (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU, 0); if (!guestDirList.contains(pszNewDir)) guestDirList.append(pszNewDir); } break; } case DragAndDropSvc::HOST_DND_HG_SND_FILE: { uint32_t cbDataRecv; uint32_t fMode = 0; rc = vbglR3DnDHGProcessSendFileMessage(uClientId, pszPathname, sizeof(pszPathname), &cbPathname, pvTmpData, cbTmpData, &cbDataRecv, &fMode); if (RT_SUCCESS(rc)) { char *pszNewFile = RTPathJoinA(pszDropDir, pszPathname); DO(("Got drop file: %s - %d - %o - %Rrc\n", pszPathname, cbDataRecv, fMode, rc)); RTFILE hFile; rc = RTFileOpen(&hFile, pszNewFile, RTFILE_O_WRITE | RTFILE_O_APPEND | RTFILE_O_DENY_ALL | RTFILE_O_OPEN_CREATE); if (RT_SUCCESS(rc)) { rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, NULL); if (RT_SUCCESS(rc)) { rc = RTFileWrite(hFile, pvTmpData, cbDataRecv, 0); /* Valid UNIX mode? */ if ( RT_SUCCESS(rc) && (fMode & RTFS_UNIX_MASK)) rc = RTFileSetMode(hFile, (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR); } RTFileClose(hFile); if (!guestFileList.contains(pszNewFile)) guestFileList.append(pszNewFile); } } break; } case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL: { rc = vbglR3DnDHGProcessCancelMessage(uClientId); if (RT_SUCCESS(rc)) rc = VERR_CANCELLED; /* Break out of the loop. */ } default: fLoop = false; break; } } else { if (rc == VERR_NO_DATA) rc = VINF_SUCCESS; break; } }while(fLoop); RTMemFree(pvTmpData); /* Cleanup on failure or if the user has canceled. */ if (RT_FAILURE(rc)) { /* Remove any stuff created. */ for (size_t i = 0; i < guestFileList.size(); ++i) RTFileDelete(guestFileList.at(i).c_str()); for (size_t i = 0; i < guestDirList.size(); ++i) RTDirRemove(guestDirList.at(i).c_str()); RTDirRemove(pszDropDir); } return rc; }
/** * Retrieves the data stored in this object and store the result in * pMedium. * * @return IPRT status code. * @return HRESULT * @param pFormatEtc * @param pMedium */ STDMETHODIMP UIDnDDataObject::GetData(LPFORMATETC pFormatEtc, LPSTGMEDIUM pMedium) { AssertPtrReturn(pFormatEtc, DV_E_FORMATETC); AssertPtrReturn(pMedium, DV_E_FORMATETC); HRESULT hr = DV_E_FORMATETC; LPFORMATETC pThisFormat = NULL; LPSTGMEDIUM pThisMedium = NULL; LogFlowThisFunc(("\n")); /* Format supported? */ ULONG lIndex; if ( LookupFormatEtc(pFormatEtc, &lIndex) && lIndex < m_cFormats) /* Paranoia. */ { pThisMedium = &m_pStgMedium[lIndex]; AssertPtr(pThisMedium); pThisFormat = &m_pFormatEtc[lIndex]; AssertPtr(pThisFormat); LogFlowThisFunc(("pThisMedium=%p, pThisFormat=%p\n", pThisMedium, pThisFormat)); LogFlowThisFunc(("mStatus=%RU32\n", m_enmStatus)); switch (m_enmStatus) { case DnDDataObjectStatus_Dropping: { #if 0 LogRel3(("DnD: Dropping\n")); LogFlowFunc(("Waiting for event ...\n")); int rc2 = RTSemEventWait(m_SemEvent, RT_INDEFINITE_WAIT); LogFlowFunc(("rc=%Rrc, mStatus=%RU32\n", rc2, m_enmStatus)); #endif break; } case DnDDataObjectStatus_Dropped: { LogRel3(("DnD: Dropped\n")); LogRel3(("DnD: cfFormat=%RI16, sFormat=%s, tyMed=%RU32, dwAspect=%RU32\n", pThisFormat->cfFormat, UIDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat), pThisFormat->tymed, pThisFormat->dwAspect)); LogRel3(("DnD: Got strFormat=%s, pvData=%p, cbData=%RU32\n", m_strFormat.toUtf8().constData(), m_pvData, m_cbData)); QVariant::Type vaType; QString strMIMEType; if ( (pFormatEtc->tymed & TYMED_HGLOBAL) && (pFormatEtc->dwAspect == DVASPECT_CONTENT) && ( pFormatEtc->cfFormat == CF_TEXT || pFormatEtc->cfFormat == CF_UNICODETEXT) ) { strMIMEType = "text/plain"; /** @todo Indicate UTF8 encoding? */ vaType = QVariant::String; } else if ( (pFormatEtc->tymed & TYMED_HGLOBAL) && (pFormatEtc->dwAspect == DVASPECT_CONTENT) && (pFormatEtc->cfFormat == CF_HDROP)) { strMIMEType = "text/uri-list"; vaType = QVariant::StringList; } #if 0 /* More formats; not needed right now. */ else if ( (pFormatEtc->tymed & TYMED_ISTREAM) && (pFormatEtc->dwAspect == DVASPECT_CONTENT) && (pFormatEtc->cfFormat == CF_FILECONTENTS)) { } else if ( (pFormatEtc->tymed & TYMED_HGLOBAL) && (pFormatEtc->dwAspect == DVASPECT_CONTENT) && (pFormatEtc->cfFormat == CF_FILEDESCRIPTOR)) { } else if ( (pFormatEtc->tymed & TYMED_HGLOBAL) && (pFormatEtc->cfFormat == CF_PREFERREDDROPEFFECT)) { HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, sizeof(DWORD)); DWORD *pdwEffect = (DWORD *)GlobalLock(hData); AssertPtr(pdwEffect); *pdwEffect = DROPEFFECT_COPY; GlobalUnlock(hData); pMedium->hGlobal = hData; pMedium->tymed = TYMED_HGLOBAL; } #endif LogRel3(("DnD: strMIMEType=%s, vaType=%ld\n", strMIMEType.toUtf8().constData(), vaType)); int rc; if (!m_fDataRetrieved) { if (m_pDnDHandler) { rc = m_pDnDHandler->retrieveData(Qt::CopyAction, strMIMEType, vaType, m_vaData); } else rc = VERR_NOT_FOUND; m_fDataRetrieved = true; LogFlowFunc(("Retrieving data ended with %Rrc\n", rc)); } else /* Data already been retrieved. */ rc = VINF_SUCCESS; if ( RT_SUCCESS(rc) && m_vaData.isValid()) { if ( strMIMEType.startsWith("text/uri-list") /* One item. */ && ( m_vaData.canConvert(QVariant::String) /* Multiple items. */ || m_vaData.canConvert(QVariant::StringList)) ) { QStringList lstFilesURI = m_vaData.toStringList(); QStringList lstFiles; for (size_t i = 0; i < lstFilesURI.size(); i++) { char *pszFilePath = RTUriFilePath(lstFilesURI.at(i).toUtf8().constData()); if (pszFilePath) { lstFiles.append(pszFilePath); RTStrFree(pszFilePath); } else /* Unable to parse -- refuse entire request. */ { lstFiles.clear(); rc = VERR_INVALID_PARAMETER; break; } } size_t cFiles = lstFiles.size(); LogFlowThisFunc(("Files (%zu)\n", cFiles)); if ( RT_SUCCESS(rc) && cFiles) { size_t cchFiles = 0; /* Number of characters. */ for (size_t i = 0; i < cFiles; i++) { const char *pszFile = lstFiles.at(i).toUtf8().constData(); cchFiles += strlen(pszFile); cchFiles += 1; /* Terminating '\0'. */ LogFlowThisFunc(("\tFile: %s (cchFiles=%zu)\n", pszFile, cchFiles)); } /* List termination with '\0'. */ cchFiles++; size_t cbBuf = sizeof(DROPFILES) + (cchFiles * sizeof(RTUTF16)); DROPFILES *pDropFiles = (DROPFILES *)RTMemAllocZ(cbBuf); if (pDropFiles) { /* Put the files list right after our DROPFILES structure. */ pDropFiles->pFiles = sizeof(DROPFILES); /* Offset to file list. */ pDropFiles->fWide = 1; /* We use Unicode. Always. */ uint8_t *pCurFile = (uint8_t *)pDropFiles + pDropFiles->pFiles; AssertPtr(pCurFile); LogFlowThisFunc(("Encoded:\n")); for (size_t i = 0; i < cFiles; i++) { const char *pszFile = lstFiles.at(i).toUtf8().constData(); Assert(strlen(pszFile)); size_t cchCurFile; PRTUTF16 pwszFile; rc = RTStrToUtf16(pszFile, &pwszFile); if (RT_SUCCESS(rc)) { cchCurFile = RTUtf16Len(pwszFile); Assert(cchCurFile); memcpy(pCurFile, pwszFile, cchCurFile * sizeof(RTUTF16)); RTUtf16Free(pwszFile); } else break; pCurFile += cchCurFile * sizeof(RTUTF16); /* Terminate current file name. */ *pCurFile = L'\0'; pCurFile += sizeof(RTUTF16); LogFlowThisFunc(("\t#%zu: cchCurFile=%zu\n", i, cchCurFile)); } if (RT_SUCCESS(rc)) { *pCurFile = L'\0'; /* Final list terminator. */ /* * Fill out the medium structure we're going to report back. */ pMedium->tymed = TYMED_HGLOBAL; pMedium->pUnkForRelease = NULL; pMedium->hGlobal = GlobalAlloc( GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, cbBuf); if (pMedium->hGlobal) { LPVOID pvMem = GlobalLock(pMedium->hGlobal); if (pvMem) { memcpy(pvMem, pDropFiles, cbBuf); GlobalUnlock(pMedium->hGlobal); hr = S_OK; } else rc = VERR_ACCESS_DENIED; } else rc = VERR_NO_MEMORY; LogFlowThisFunc(("Copying to TYMED_HGLOBAL (%zu bytes): %Rrc\n", cbBuf, rc)); } RTMemFree(pDropFiles); } else rc = VERR_NO_MEMORY; if (RT_FAILURE(rc)) LogFlowThisFunc(("Failed with %Rrc\n", rc)); } } else if ( strMIMEType.startsWith("text/plain") && m_vaData.canConvert(QVariant::String)) { const bool fUnicode = pFormatEtc->cfFormat == CF_UNICODETEXT; const size_t cbCh = fUnicode ? sizeof(WCHAR) : sizeof(char); QString strText = m_vaData.toString(); size_t cbSrc = strText.length() * cbCh; Assert(cbSrc); LPCVOID pvSrc = fUnicode ? (void *)strText.unicode() : (void *)strText.toUtf8().constData(); AssertPtr(pvSrc); LogFlowFunc(("pvSrc=0x%p, cbSrc=%zu, cbCh=%zu, fUnicode=%RTbool\n", pvSrc, cbSrc, cbCh, fUnicode)); pMedium->tymed = TYMED_HGLOBAL; pMedium->pUnkForRelease = NULL; pMedium->hGlobal = GlobalAlloc(GHND | GMEM_SHARE, cbSrc); if (pMedium->hGlobal) { LPVOID pvDst = GlobalLock(pMedium->hGlobal); if (pvDst) { memcpy(pvDst, pvSrc, cbSrc); GlobalUnlock(pMedium->hGlobal); } else rc = VERR_ACCESS_DENIED; hr = S_OK; } else hr = VERR_NO_MEMORY; } else LogRel2(("DnD: MIME type '%s' not supported\n", strMIMEType.toUtf8().constData())); LogFlowThisFunc(("Handling formats ended with rc=%Rrc\n", rc)); } break; } default: break; } } /* * Fallback in error case. */ if (FAILED(hr)) { if (pThisMedium) { switch (pThisMedium->tymed) { case TYMED_HGLOBAL: pMedium->hGlobal = (HGLOBAL)OleDuplicateData(pThisMedium->hGlobal, pThisFormat->cfFormat, 0 /* Flags */); break; default: break; } } if (pFormatEtc) pMedium->tymed = pFormatEtc->tymed; pMedium->pUnkForRelease = NULL; } LogFlowThisFunc(("Returning hr=%Rhrc\n", hr)); return hr; }
/* * This class is a meta message class. It doesn't consist of any own message * data, but handle the meta info, the data itself as well any files or * directories which have to be transfered to the guest. */ DnDHGSendDataMessage::DnDHGSendDataMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], PFNDNDPROGRESS pfnProgressCallback, void *pvProgressUser) : m_cbAll(0) , m_cbTransfered(0) , m_pfnProgressCallback(pfnProgressCallback) , m_pvProgressUser(pvProgressUser) { RTCString strNewUris; /* Check the format for any uri type. */ if (hasFileUrls(static_cast<const char*>(paParms[1].u.pointer.addr), paParms[1].u.pointer.size)) { DO(("old data '%s'\n", (char*)paParms[3].u.pointer.addr)); /* The list is separated by newline (Even if only one file is * listed). */ RTCList<RTCString> oldUriList = RTCString(static_cast<const char*>(paParms[3].u.pointer.addr), paParms[3].u.pointer.size).split("\r\n"); if (!oldUriList.isEmpty()) { RTCList<RTCString> newUriList; for (size_t i = 0; i < oldUriList.size(); ++i) { const RTCString &strUri = oldUriList.at(i); /* Query the path component of a file URI. If this hasn't a * file scheme null is returned. */ if (char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO)) { /* Add the path to our internal file list (recursive in * the case of a directory). */ if (char *pszFilename = RTPathFilename(pszFilePath)) { char *pszNewUri = RTUriFileCreate(pszFilename); if (pszNewUri) { newUriList.append(pszNewUri); RTStrFree(pszNewUri); buildFileTree(pszFilePath, pszFilename - pszFilePath); } } RTStrFree(pszFilePath); } else newUriList.append(strUri); } /* We have to change the actual DnD data. Remove any host paths and * just decode the filename into the new data. The guest tools will * add the correct path again, before sending the DnD drop event to * some window. */ strNewUris = RTCString::join(newUriList, "\r\n") + "\r\n"; /* Remark: We don't delete the old pointer here, cause this is done * by the caller. We just use the RTString data, which has the * scope of this ctor. This is enough cause the data is copied in * the DnDHGSendDataMessagePrivate anyway. */ paParms[3].u.pointer.addr = (void*)strNewUris.c_str(); paParms[3].u.pointer.size = strNewUris.length() + 1; paParms[4].u.uint32 = strNewUris.length() + 1; } } /* Add the size of the data to the todo list. */ m_cbAll += paParms[4].u.uint32; /* The first message is the meta info for the data and the data itself. */ m_pNextPathMsg = new DnDHGSendDataMessagePrivate(uMsg, cParms, paParms, &DnDHGSendDataMessage::progressCallback, this); DO(("new data '%s'\n", (char*)paParms[3].u.pointer.addr)); DO(("cbAll: %u\n", m_cbAll)); DO(("cbData: %u\n", paParms[4].u.uint32)); for (size_t i = 0; i < m_uriList.size(); ++i) DO(("file: %s : %s - %o - %ld\n", m_uriList.at(i).m_strHostPath.c_str(), m_uriList.at(i).m_strGuestPath.c_str(), m_uriList.at(i).m_fMode, m_uriList.at(i).m_cbSize)); }