/* * ispersis.cpp - IPersist, IPersistFile, and IPersistStream implementations for * URL class. */ #include "priv.h" #include "ishcut.h" #include "resource.h" // Need to flush the file to prevent win95 from barfing after stuff is written in VOID FlushFile(LPCTSTR pszFile) { if (!g_fRunningOnNT) { WritePrivateProfileString(NULL, NULL, NULL, pszFile); } } // save object to file STDMETHODIMP Intshcut::SaveToFile(LPCTSTR pszFile, BOOL bRemember) { HRESULT hres = InitProp(); if (SUCCEEDED(hres)) { m_pprop->SetFileName(pszFile); hres = m_pprop->Commit(STGC_DEFAULT); // Remember file if requested if (SUCCEEDED(hres)) { if (bRemember) { Dirty(FALSE); if ( !Str_SetPtr(&m_pszFile, pszFile) ) hres = E_OUTOFMEMORY; #ifdef DEBUG Dump(); #endif } SHChangeNotify(SHCNE_UPDATEITEM, (SHCNF_PATH | SHCNF_FLUSHNOWAIT), pszFile, NULL); } if (!bRemember) m_pprop->SetFileName(m_pszFile); } if(pszFile && (S_OK == hres)) FlushFile(pszFile); return hres; } STDMETHODIMP Intshcut::LoadFromFile(LPCTSTR pszFile) { HRESULT hres; if (Str_SetPtr(&m_pszFile, pszFile)) { hres = InitProp(); #ifdef DEBUG Dump(); #endif } else { hres = E_OUTOFMEMORY; } return hres; } STDMETHODIMP Intshcut::LoadFromAsyncFileNow() { HRESULT hres = S_OK; if (m_pszFileToLoad) { hres = LoadFromFile(m_pszFileToLoad); Str_SetPtr(&m_pszFileToLoad, NULL); } return hres; } STDMETHODIMP Intshcut::GetCurFile(LPTSTR pszFile, UINT cchLen) { HRESULT hr; if (m_pszFile) { hr = StringCchCopy(pszFile, cchLen, m_pszFile); } else hr = S_FALSE; return hr; } STDMETHODIMP Intshcut::Dirty(BOOL bDirty) { HRESULT hres; ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut)); if (bDirty) { if (IsFlagClear(m_dwFlags, ISF_DIRTY)) TraceMsg(TF_INTSHCUT, "Intshcut now dirty."); SetFlag(m_dwFlags, ISF_DIRTY); } else { if (IsFlagSet(m_dwFlags, ISF_DIRTY)) TraceMsg(TF_INTSHCUT, "Intshcut now clean."); ClearFlag(m_dwFlags, ISF_DIRTY); } hres = S_OK; ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut)); return hres; } // IPersist::GetClassID method for Intshcut STDMETHODIMP Intshcut::GetClassID(CLSID *pclsid) { ASSERT(IS_VALID_WRITE_PTR(pclsid, CLSID)); *pclsid = CLSID_InternetShortcut; return S_OK; } // IPersistFile::IsDirty handler for Intshcut STDMETHODIMP Intshcut::IsDirty(void) { HRESULT hres = LoadFromAsyncFileNow(); if(SUCCEEDED(hres)) { hres = InitProp(); if (SUCCEEDED(hres)) { if (IsFlagSet(m_dwFlags, ISF_DIRTY) || S_OK == m_pprop->IsDirty()) hres = S_OK; else hres = S_FALSE; } } return hres; } // Helper function to save off Trident specific stuff STDMETHODIMP Intshcut::_SaveOffPersistentDataFromSite() { IOleCommandTarget *pcmdt = NULL; HRESULT hr = S_OK; if (_punkSite) { if(S_OK == _CreateTemporaryBackingFile()) { ASSERT(m_pszTempFileName); hr = _punkSite->QueryInterface(IID_IOleCommandTarget, (LPVOID *)&pcmdt); if((S_OK == hr)) { ASSERT(pcmdt); VARIANT varIn = {0}; varIn.vt = VT_UNKNOWN; varIn.punkVal = (LPUNKNOWN)(SAFECAST(this, IUniformResourceLocator *)); // Tell the site to save off it's persistent stuff hr = pcmdt->Exec(&CGID_ShortCut, CMDID_INTSHORTCUTCREATE, 0, &varIn, NULL); pcmdt->Release(); } FlushFile(m_pszTempFileName); } } return hr; } // IPersistFile::Save handler for Intshcut STDMETHODIMP Intshcut::Save(LPCOLESTR pwszFile, BOOL bRemember) { HRESULT hres = LoadFromAsyncFileNow(); if (SUCCEEDED(hres)) { TCHAR szFile[MAX_PATH]; if (pwszFile) SHUnicodeToTChar(pwszFile, szFile, SIZECHARS(szFile)); else if (m_pszFile) hres = StringCchCopy(szFile, ARRAYSIZE(szFile), m_pszFile); else hres = E_FAIL; if (FAILED(hres)) { return hres; } // Perhaps there is a site which wants to save off stuff ? // However, the site may end up calling via intefaces hres = _SaveOffPersistentDataFromSite(); if ((S_OK == hres) && (m_pszTempFileName) && (StrCmp(m_pszTempFileName, szFile) != 0)) { // Copy contents of the temp file to the destination // if they are different files EVAL(CopyFile(m_pszTempFileName, szFile, FALSE)); } // Then save off in memory stuff to this file hres = SaveToFile(szFile, bRemember); } return hres; } STDMETHODIMP Intshcut::SaveCompleted(LPCOLESTR pwszFile) { return S_OK; } // IPersistFile::Load() STDMETHODIMP Intshcut::Load(LPCOLESTR pwszFile, DWORD dwMode) { HRESULT hres; if (m_pszFile || m_pszFileToLoad) { hres = E_FAIL; // can't ::Load twice } else { if (m_fMustLoadSync) hres = LoadFromFile(pwszFile); else { if (Str_SetPtr(&m_pszFileToLoad, pwszFile)) hres = S_OK; else hres = E_OUTOFMEMORY; } } return hres; } // IPersistFile::GetCurFile method for Intshcut STDMETHODIMP Intshcut::GetCurFile(WCHAR **ppwszFile) { HRESULT hr; TCHAR szTempFile[MAX_PATH]; ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut)); ASSERT(IS_VALID_WRITE_PTR(ppwszFile, LPOLESTR)); hr = LoadFromAsyncFileNow(); if (FAILED(hr)) return hr; if (m_pszFile) { hr = StringCchCopy(szTempFile, ARRAYSIZE(szTempFile), m_pszFile); } else { hr = StringCchCopy(szTempFile, ARRAYSIZE(szTempFile), TEXT("*.url")); if (SUCCEEDED(hr)) { // This code path returns S_FALSE on success hr = S_FALSE; } } HRESULT hrTemp = SHStrDup(szTempFile, ppwszFile); if (FAILED(hrTemp)) hr = hrTemp; ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut)); return(hr); } // IPersistStream::Load method for Intshcut STDMETHODIMP Intshcut::Load(IStream *pstm) { // to implement this: // save stream to temp.ini // IPersistFile::Load() from that // delete temp file return E_NOTIMPL; } // IPersistStream::Save method for Intshcut STDMETHODIMP Intshcut::Save(IStream *pstm, BOOL bClearDirty) { HRESULT hr = InitProp(); if (SUCCEEDED(hr)) { TCHAR szURL[INTERNET_MAX_URL_LENGTH]; hr = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL)); if (SUCCEEDED(hr)) { LPSTR pszContents; hr = CreateURLFileContents(szURL, &pszContents); if (SUCCEEDED(hr)) { ASSERT(hr == lstrlenA(pszContents)); hr = pstm->Write(pszContents, hr + 1, NULL); GlobalFree(pszContents); pszContents = NULL; } else { hr = E_OUTOFMEMORY; } } } return hr; } // IPersistStream::GetSizeMax method for Intshcut STDMETHODIMP Intshcut::GetSizeMax(PULARGE_INTEGER puliSize) { puliSize->LowPart = 0; puliSize->HighPart = 0; HRESULT hr = InitProp(); if (SUCCEEDED(hr)) { puliSize->LowPart = GetFileContentsAndSize(NULL); hr = S_OK; } return hr; } STDMETHODIMP Intshcut::_SetTempFileName(TCHAR *pszTempFileName) { ASSERT(NULL == m_pszTempFileName); if (m_pszTempFileName) DeleteFile(m_pszTempFileName); Str_SetPtr(&m_pszTempFileName, pszTempFileName); return (m_pszTempFileName ? S_OK : E_OUTOFMEMORY); } STDMETHODIMP Intshcut::_CreateTemporaryBackingFile() { HRESULT hres = E_FAIL; if (m_pszTempFileName) return S_OK; TCHAR szTempFileName[MAX_PATH]; TCHAR szDirectory[MAX_PATH]; DWORD dwRet = GetTempPath(ARRAYSIZE(szDirectory), szDirectory); if ((FALSE == dwRet) || (FALSE == PathFileExists(szDirectory))) { szDirectory[0] = TEXT('\\'); szDirectory[1] = TEXT('\0'); dwRet = TRUE; } dwRet = GetTempFileName(szDirectory, TEXT("www"), 0, szTempFileName); if (dwRet) { hres = _SetTempFileName(szTempFileName); // Now copy over the current file from which this was loaded and then save off // any changes if (S_OK == hres) { if (m_pszFile) { EVAL(CopyFile(m_pszFile, m_pszTempFileName, FALSE)); SaveToFile(m_pszTempFileName, FALSE); // this flushes the file } } } return hres; } // Calculate the size of the contents to be transferred in a block. STDMETHODIMP_(DWORD) Intshcut::GetFileContentsAndSize(LPSTR *ppszBuf) { DWORD cbSize = 0; // this is in bytes, not characters TCHAR szURL[INTERNET_MAX_URL_LENGTH]; BOOL fSuccess = FALSE; HRESULT hres; ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut)); ASSERT(m_pprop); if (ppszBuf) *ppszBuf = NULL; // Create a temporary backing File here and save off everything that needs to be // saved off there and use that to satisfy this request if (S_OK == _CreateTemporaryBackingFile()) { ASSERT(m_pszTempFileName); WCHAR wszTemp[MAX_PATH]; SHTCharToUnicode(m_pszTempFileName, wszTemp, ARRAYSIZE(wszTemp)); hres = Save(wszTemp, FALSE); // So our temp file is now up to date // Just copy the file HANDLE hFile = CreateFile(m_pszTempFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD dwTemp = 0; cbSize = GetFileSize(hFile, &dwTemp); if (ppszBuf) { if (0xFFFFFFFF != cbSize) { ASSERT(0 == dwTemp); *ppszBuf = (LPSTR)LocalAlloc(LPTR, cbSize); if (*ppszBuf) { dwTemp = 0; if(ReadFile(hFile, *ppszBuf, cbSize, &dwTemp, NULL)) { ASSERT(cbSize >= dwTemp); fSuccess = TRUE; } } } } else { fSuccess = TRUE; // Just want the size - not contents } CloseHandle(hFile); } if (FALSE == fSuccess) { cbSize = 0; if(ppszBuf && (*ppszBuf)) { LocalFree(*ppszBuf); *ppszBuf = NULL; } } } if (FALSE == fSuccess) { // if you couldn't read the file, then perhaps atleast this will work ? HRESULT hr = InitProp(); if (SUCCEEDED(hr) && SUCCEEDED(m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL)))) { hr = CreateURLFileContents(szURL, ppszBuf); // IEUNIX-This function should return the strlen not including the // null characters as this causes the shortcut file having a null // character causing a crash in the execution of the link. // Fortunately, that's what CreateURLFileContents returns cbSize = SUCCEEDED(hr) ? hr : 0; } } ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut)); return cbSize; } #ifndef WC_NO_BEST_FIT_CHARS #define WC_NO_BEST_FIT_CHARS 0x00000400 #endif // transfer the URL data in URL clipboard STDMETHODIMP Intshcut::TransferUniformResourceLocator(FORMATETC *pfmtetc, STGMEDIUM *pstgmed) { HRESULT hr; ASSERT(pfmtetc->dwAspect == DVASPECT_CONTENT); ASSERT(pfmtetc->lindex == -1); if (pfmtetc->tymed & TYMED_HGLOBAL) { TCHAR szURL[INTERNET_MAX_URL_LENGTH]; ASSERT(m_pprop); hr = InitProp(); if (SUCCEEDED(hr)) { hr = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL)); if (SUCCEEDED(hr)) { int cch = lstrlen(szURL) + 1; int cb = (cch-1) * 9 + 1; // the biggest result is an utf8 escaped version of the string // utf8 encoding can blow the size up to 3 times // escaping can blow each byte up to 3 times LPSTR pszURL = (LPSTR)GlobalAlloc(GPTR, cb); if (pszURL) { if (pfmtetc->cfFormat == CF_UNICODETEXT || pfmtetc->cfFormat == g_cfURLW) { StrCpyN((LPWSTR)pszURL, szURL, cch); } else { BOOL bUsedDefaultChar = FALSE; DWORD dwFlags = 0; if (IsOS(OS_WIN2000ORGREATER) || IsOS(OS_WIN98ORGREATER)) { dwFlags |= WC_NO_BEST_FIT_CHARS; } int wcResult = WideCharToMultiByte(CP_ACP, dwFlags, szURL, cch, pszURL, cb, NULL, &bUsedDefaultChar); if ((0 == wcResult) || bUsedDefaultChar) { // the string is weird and can't be converted back to unicode // we're going to utf8-escaped encode it ConvertToUtf8Escaped(szURL, ARRAYSIZE(szURL)); SHUnicodeToAnsi(szURL, pszURL, cb); } } pstgmed->tymed = TYMED_HGLOBAL; pstgmed->hGlobal = pszURL; } } } } else hr = DV_E_TYMED; return hr; } // transfer the URL data in text STDMETHODIMP Intshcut::TransferText(FORMATETC *pfmtetc, STGMEDIUM *pstgmed) { return TransferUniformResourceLocator(pfmtetc, pstgmed); } // assumes the current seek pos in the stream is at the start BOOL GetStreamMimeAndExt(LPCWSTR pszURL, IStream *pstm, LPTSTR pszMime, UINT cchMime, LPTSTR pszExt, UINT cchExt) { BYTE buf[256]; ULONG cbRead; pstm->Read(buf, SIZEOF(buf), &cbRead); WCHAR *pwszMimeOut; if (SUCCEEDED(FindMimeFromData(NULL, pszURL, buf, cbRead, NULL, 0, &pwszMimeOut, 0))) { TCHAR szMimeTemp[MAX_PATH]; if (pszMime == NULL) { pszMime = szMimeTemp; cchMime = ARRAYSIZE(szMimeTemp); } SHUnicodeToTChar(pwszMimeOut, pszMime, cchMime); CoTaskMemFree(pwszMimeOut); if (pszExt) MIME_GetExtension(pszMime, pszExt, cchExt); } // const LARGE_INTEGER c_li0 = {0, 0}; pstm->Seek(c_li0, STREAM_SEEK_SET, NULL); return TRUE; } // pszName is assumed to be MAX_PATH STDMETHODIMP Intshcut::GetDocumentName(LPTSTR pszName) { GetDescription(pszName, MAX_PATH); WCHAR *pszURL; if (S_OK == GetURLW(&pszURL)) { IStream *pstm; if (SUCCEEDED(URLOpenBlockingStreamW(NULL, pszURL, &pstm, 0, NULL))) { TCHAR szExt[MAX_PATH]; GetStreamMimeAndExt(pszURL, pstm, NULL, 0, szExt, ARRAYSIZE(szExt)); PathRenameExtension(pszName, szExt); pstm->Release(); } SHFree(pszURL); } return S_OK; } // transfer URL data in file-group-descriptor clipboard format. STDMETHODIMP Intshcut::TransferFileGroupDescriptorA(FORMATETC *pfmtetc, STGMEDIUM *pstgmed) { HRESULT hr; if (pfmtetc->dwAspect != DVASPECT_COPY && pfmtetc->dwAspect != DVASPECT_LINK && pfmtetc->dwAspect != DVASPECT_CONTENT) { hr = DV_E_DVASPECT; } else if (pfmtetc->tymed & TYMED_HGLOBAL) { FILEGROUPDESCRIPTORA * pfgd = (FILEGROUPDESCRIPTORA *)GlobalAlloc(GPTR, SIZEOF(FILEGROUPDESCRIPTORA)); if (pfgd) { FILEDESCRIPTORA * pfd = &(pfgd->fgd[0]); TCHAR szTemp[MAX_PATH]; if (pfmtetc->dwAspect == DVASPECT_COPY) { pfd->dwFlags = FD_FILESIZE; GetDocumentName(szTemp); } else { pfd->dwFlags = FD_FILESIZE | FD_LINKUI; GetDescription(szTemp, ARRAYSIZE(szTemp)); } SHTCharToAnsi(PathFindFileName(szTemp), pfd->cFileName, SIZECHARS(pfd->cFileName)); pfd->nFileSizeHigh = 0; pfd->nFileSizeLow = GetFileContentsAndSize(NULL); pfgd->cItems = 1; pstgmed->tymed = TYMED_HGLOBAL; pstgmed->hGlobal = pfgd; hr = S_OK; } else hr = E_OUTOFMEMORY; } else hr = DV_E_TYMED; return hr; } // transfer URL data in file-group-descriptor clipboard format. STDMETHODIMP Intshcut::TransferFileGroupDescriptorW(FORMATETC *pfmtetc, STGMEDIUM *pstgmed) { HRESULT hr; if (pfmtetc->dwAspect != DVASPECT_COPY && pfmtetc->dwAspect != DVASPECT_LINK && pfmtetc->dwAspect != DVASPECT_CONTENT) { hr = DV_E_DVASPECT; } else if (pfmtetc->tymed & TYMED_HGLOBAL) { FILEGROUPDESCRIPTORW * pfgd = (FILEGROUPDESCRIPTORW *)GlobalAlloc(GPTR, SIZEOF(FILEGROUPDESCRIPTORW)); if (pfgd) { FILEDESCRIPTORW * pfd = &(pfgd->fgd[0]); TCHAR szTemp[MAX_PATH]; if (pfmtetc->dwAspect == DVASPECT_COPY) { pfd->dwFlags = FD_FILESIZE; GetDocumentName(szTemp); } else { pfd->dwFlags = FD_FILESIZE | FD_LINKUI; GetDescription(szTemp, ARRAYSIZE(szTemp)); } SHTCharToUnicode(PathFindFileName(szTemp), pfd->cFileName, SIZECHARS(pfd->cFileName)); pfd->nFileSizeHigh = 0; pfd->nFileSizeLow = GetFileContentsAndSize(NULL); pfgd->cItems = 1; pstgmed->tymed = TYMED_HGLOBAL; pstgmed->hGlobal = pfgd; hr = S_OK; } else hr = E_OUTOFMEMORY; } else hr = DV_E_TYMED; return hr; } #if defined(BIG_ENDIAN) && defined(BYTE_ORDER) #if BYTE_ORDER != BIG_ENDIAN #undef BIG_ENDIAN #endif #endif #ifdef BIG_ENDIAN #define BOM 0xfffe #else #define BOM 0xfeff #endif STDMETHODIMP Intshcut::GetDocumentStream(IStream **ppstm) { *ppstm = NULL; WCHAR *pszURL; HRESULT hres = GetURLW(&pszURL); if (S_OK == hres) { IStream *pstm; hres = URLOpenBlockingStreamW(NULL, pszURL, &pstm, 0, NULL); if (SUCCEEDED(hres)) { TCHAR szMime[80]; if (GetStreamMimeAndExt(pszURL, pstm, szMime, ARRAYSIZE(szMime), NULL, 0) && StrCmpI(szMime, TEXT("text/html")) == 0) { IStream *aStreams[2]; if(m_uiCodePage == 1200) // Unicode { WCHAR wzBaseTag[INTERNET_MAX_URL_LENGTH + 20]; wnsprintfW(wzBaseTag, ARRAYSIZE(wzBaseTag), TEXT("%wc\n"), (WCHAR)BOM, pszURL); aStreams[0] = SHCreateMemStream((BYTE *)wzBaseTag, lstrlenW(wzBaseTag) * SIZEOF(wzBaseTag[0])); } else { CHAR szURL[INTERNET_MAX_URL_LENGTH], szBaseTag[INTERNET_MAX_URL_LENGTH + 20]; SHUnicodeToAnsi(pszURL, szURL, ARRAYSIZE(szURL)); wnsprintfA(szBaseTag, ARRAYSIZE(szBaseTag), "\n", szURL); // NOTE: this is an ANSI stream aStreams[0] = SHCreateMemStream((BYTE *)szBaseTag, lstrlenA(szBaseTag) * SIZEOF(szBaseTag[0])); } if (aStreams[0]) { aStreams[1] = pstm; hres = SHCreateStreamWrapperCP(aStreams, ARRAYSIZE(aStreams), STGM_READ, m_uiCodePage, ppstm); aStreams[0]->Release(); } else hres = E_OUTOFMEMORY; pstm->Release(); } else *ppstm = pstm; } SHFree(pszURL); } else hres = E_FAIL; return hres; } // transfer URL data in file-contents clipboard format. STDMETHODIMP Intshcut::TransferFileContents(FORMATETC *pfmtetc, STGMEDIUM *pstgmed) { HRESULT hr; if (pfmtetc->lindex != 0) return DV_E_LINDEX; if ((pfmtetc->dwAspect == DVASPECT_CONTENT || pfmtetc->dwAspect == DVASPECT_LINK) && (pfmtetc->tymed & TYMED_HGLOBAL)) { LPSTR pszFileContents; DWORD cbSize = GetFileContentsAndSize(&pszFileContents); if (pszFileContents) { pstgmed->tymed = TYMED_HGLOBAL; pstgmed->hGlobal = pszFileContents; hr = S_OK; } else hr = E_OUTOFMEMORY; } else if ((pfmtetc->dwAspect == DVASPECT_COPY) && (pfmtetc->tymed & TYMED_ISTREAM)) { hr = GetDocumentStream(&pstgmed->pstm); if (SUCCEEDED(hr)) { pstgmed->tymed = TYMED_ISTREAM; hr = S_OK; } } else hr = DV_E_TYMED; return hr; } #ifdef DEBUG STDMETHODIMP_(void) Intshcut::Dump(void) { ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut)); #define INDENT_STRING " " if (IsFlagSet(g_dwDumpFlags, DF_INTSHCUT)) { TraceMsg(TF_ALWAYS, "%sm_dwFlags = %#08lx", INDENT_STRING, m_dwFlags); TraceMsg(TF_ALWAYS, "%sm_pszFile = \"%s\"", INDENT_STRING, Dbg_SafeStr(m_pszFile)); if (m_pprop) m_pprop->Dump(); } } #endif // DEBUG