/** * Asynchronous pluggable protocol for Applications * * Copyright (C) Microsoft Corporation, 2000 */ ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// #include "precomp.h" #include "app.h" #include // for _snprintf char * stristr(char *pszMain, char *pszSub) { char * pszCur = pszMain; char ch = (char) tolower(*pszSub); int cb = strlen(pszSub) - 1; // -1 to ignore leading character for (;;) { while (tolower(*pszCur) != ch && *pszCur) pszCur++; if (!*pszCur) return NULL; if (_strnicmp(pszCur + 1, pszSub + 1, cb) == 0) return pszCur; pszCur++; } } /** * Return last Win32 error as an HRESULT. */ HRESULT GetLastWin32Error() { // Win 95 can return 0, even when there's an error. DWORD dw = GetLastError(); return dw ? HRESULT_FROM_WIN32(dw) : E_FAIL; } HRESULT RunCommand(WCHAR *cmdLine) { HRESULT hr = S_OK; STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); ZeroMemory(&pi, sizeof(pi)); si.cb = sizeof(si); if(!CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { hr = GetLastWin32Error(); goto exit; } if(WaitForSingleObject(pi.hProcess, 180000L) == WAIT_TIMEOUT) { hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); goto exit; } exit: if(pi.hProcess) CloseHandle(pi.hProcess); if(pi.hThread) CloseHandle(pi.hThread); return hr; } #define ToHex(val) val <= 9 ? val + '0': val - 10 + 'A' DWORD ConvertToHex(WCHAR* strForm, BYTE* byteForm, DWORD dwSize) { DWORD i = 0; DWORD j = 0; for(i = 0; i < dwSize; i++) { strForm[j++] = ToHex((0xf & byteForm[i])); strForm[j++] = ToHex((0xf & (byteForm[i] >> 4))); } strForm[j] = L'\0'; return j; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // {B8BF3C7E-4DB6-4fdb-9CD3-13D2CE728CA8}, by guidgen VS7 CLSID CLSID_AppProtocol = { 0xb8bf3c7e, 0x4db6, 0x4fdb, { 0x9c, 0xd3, 0x13, 0xd2, 0xce, 0x72, 0x8c, 0xa8 } }; BOOL g_fStarted = FALSE; AppProtocolFactory g_AppProtocolFactory; IInternetSecurityManager* g_pSecurityMgr = NULL; ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Class AppProtocol ctor AppProtocol::AppProtocol(IUnknown * pUnkOuter) : m_refs (1), m_pProtocolSink (NULL), // m_cookie (NULL), m_fullUri (NULL), m_uriPath (NULL), m_queryString (NULL), m_appOrigin (NULL), m_appRoot (NULL), m_appRootTranslated (NULL), m_extraHeaders (NULL), m_inputDataSize (0), m_inputData (NULL), m_pInputRead (NULL), m_pOutputRead (NULL), m_pOutputWrite (NULL), m_started (FALSE), m_aborted (FALSE), m_done (FALSE), m_responseMimeType (NULL), m_cbOutput (0), m_extraHeadersUpr (NULL), m_strResponseHeader (NULL), m_postedMimeType (NULL), m_verb (NULL), m_localStoreFilePath (NULL), m_appType (APPTYPE_IE), m_status (STATUS_CLEAR) { m_pUnkOuter = (pUnkOuter ? pUnkOuter : (IUnknown *)(IPrivateUnknown *)this); InitializeCriticalSection(&m_csOutputWriter); } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// void AppProtocol::Cleanup() { ClearInterface(&m_pOutputRead); ClearInterface(&m_pOutputWrite); ClearInterface(&m_pProtocolSink); ClearInterface(&m_pInputRead); m_appType = APPTYPE_IE; m_status = STATUS_CLEAR; m_done = TRUE; m_aborted = FALSE; //??? if (m_bindinfo.cbSize) { ReleaseBindInfo(&m_bindinfo); ZeroMemory(&m_bindinfo, sizeof(m_bindinfo)); } } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// void AppProtocol::FreeStrings() { MemClearFn((void **)&m_fullUri); m_uriPath = NULL; m_queryString = NULL; MemClearFn((void **)&m_appOrigin) ; m_appRoot = NULL; MemClearFn((void **)&m_appRootTranslated); if(m_extraHeaders) { CoTaskMemFree(m_extraHeaders); m_extraHeaders = NULL; } // MemClearFn((void **)&m_cookie); MemClearFn((void **)&m_extraHeadersUpr); MemClearFn((void **)&m_strResponseHeader); MemClearFn((void **)&m_verb); MemClearFn((void **)&m_postedMimeType); MemClearFn((void **)&m_responseMimeType); MemClearFn((void **)&m_localStoreFilePath); } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// AppProtocol::~AppProtocol() { FreeStrings(); Cleanup(); DeleteCriticalSection(&m_csOutputWriter); if (g_pSecurityMgr != NULL) { g_pSecurityMgr->Release(); g_pSecurityMgr = NULL; } } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Private QI ULONG AppProtocol::PrivateAddRef() { return ++m_refs; } ULONG AppProtocol::PrivateRelease() { if (--m_refs > 0) return m_refs; delete this; return 0; } HRESULT AppProtocol::PrivateQueryInterface( REFIID iid, void** ppv) { *ppv = NULL; if (iid == IID_IInternetProtocol || iid == IID_IInternetProtocolRoot) { *ppv = (IInternetProtocol *)this; } else if (iid == IID_IUnknown) { *ppv = (IUnknown *)(IPrivateUnknown *)this; } else if (iid == IID_IWinInetHttpInfo) { *ppv = (IWinInetHttpInfo *)this; } else { return E_NOINTERFACE; } ((IUnknown *)*ppv)->AddRef(); return S_OK; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Public (delegated) QI ULONG AppProtocol::AddRef() { m_pUnkOuter->AddRef(); return PrivateAddRef(); } ULONG AppProtocol::Release() { m_pUnkOuter->Release(); return PrivateRelease(); } HRESULT AppProtocol::QueryInterface( REFIID iid, void ** ppv) { return m_pUnkOuter->QueryInterface(iid, ppv); } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT AppProtocol::Start( LPCWSTR url, IInternetProtocolSink * pProtocolSink, IInternetBindInfo * pBindInfo, DWORD grfSTI, DWORD ) { HRESULT hr = S_OK; WCHAR * Strings[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; DWORD cStrings = sizeof(Strings) / sizeof(Strings[0]); // DWORD cookieSize = 0; IServiceProvider * pServiceProvider = NULL; IHttpNegotiate * pHttpNegotiate = NULL; FreeStrings(); ReplaceInterface(&m_pProtocolSink, pProtocolSink); // ????? if (grfSTI & PI_PARSE_URL) goto exit; if (pProtocolSink == NULL) { hr = E_INVALIDARG; goto exit; } // get bindinfo m_bindinfo.cbSize = sizeof(BINDINFO); if (pBindInfo != NULL) { if (FAILED(hr = pBindInfo->GetBindInfo(&m_bindf, &m_bindinfo))) goto exit; } if (m_bindinfo.dwCodePage == 0) m_bindinfo.dwCodePage = CP_ACP; // extract root, uri path and query string from url if (FAILED(hr = ParseUrl(url))) goto exit; // get to headers in MSHtml if (FAILED(hr = pBindInfo->QueryInterface(IID_IServiceProvider, (void **) &pServiceProvider))) goto exit; if(pServiceProvider != NULL) { if (FAILED(hr = pServiceProvider->QueryService(IID_IHttpNegotiate, IID_IHttpNegotiate, (void **) &pHttpNegotiate))) goto exit; if(pHttpNegotiate != NULL) { hr = pHttpNegotiate->BeginningTransaction(url, NULL, 0, &m_extraHeaders); pHttpNegotiate->Release(); pHttpNegotiate = NULL; if (FAILED(hr)) goto exit; } pServiceProvider->Release(); pServiceProvider = NULL; } // determine verb switch (m_bindinfo.dwBindVerb) { case BINDVERB_GET: m_verb = DuplicateString(L"GET"); break; case BINDVERB_POST: m_verb = DuplicateString(L"POST"); break; case BINDVERB_PUT: m_verb = DuplicateString(L"PUT"); break; default: if (m_bindinfo.szCustomVerb != NULL && m_bindinfo.dwBindVerb == BINDVERB_CUSTOM) { m_verb = DuplicateString(m_bindinfo.szCustomVerb); } else { m_verb = DuplicateString(L"GET"); } break; } // get mime type of posted data from binding hr = pBindInfo->GetBindString(BINDSTRING_POST_DATA_MIME, Strings, cStrings, &cStrings); if(hr == S_OK && cStrings) { DWORD i; m_postedMimeType = DuplicateString(Strings[0]); if (m_postedMimeType == NULL) { hr = E_OUTOFMEMORY; goto exit; } for(i = 0; i < cStrings; i++) CoTaskMemFree(Strings[i]); } // don't fail if we failed to get bind string, hr = S_OK; // retrieve cookie /* cookieSize = 0; if(g_pInternetGetCookieW(m_fullUri, NULL, NULL, &cookieSize) && cookieSize) { m_cookie = (WCHAR *)MemAlloc(cookieSize + sizeof(WCHAR)); if (m_cookie == NULL) { hr = E_OUTOFMEMORY; goto exit; } g_pInternetGetCookieW(m_fullUri, NULL, m_cookie, &cookieSize); } */ // Input data if (m_bindinfo.stgmedData.tymed == TYMED_HGLOBAL) { m_inputDataSize = m_bindinfo.cbstgmedData; m_inputData = (BYTE *)m_bindinfo.stgmedData.hGlobal; } else if (m_bindinfo.stgmedData.tymed == TYMED_ISTREAM) { STATSTG statstg; ReplaceInterface(&m_pInputRead, m_bindinfo.stgmedData.pstm); if(m_pInputRead) { hr = m_pInputRead->Stat(&statstg, STATFLAG_NONAME); if(hr == S_OK) m_inputDataSize = statstg.cbSize.LowPart; else m_inputDataSize = (DWORD)-1; } } if (FAILED(hr = CreateStreamOnHGlobal(NULL, TRUE, &m_pOutputWrite))) goto exit; if (FAILED(hr = m_pOutputWrite->Clone(&m_pOutputRead))) goto exit; PROTOCOLDATA protData; protData.dwState = 1; protData.grfFlags = PI_FORCE_ASYNC; protData.pData = NULL; protData.cbData = 0; pProtocolSink->Switch(&protData); //*** hr = E_PENDING; exit: if (pHttpNegotiate) { pHttpNegotiate->Release(); pHttpNegotiate = NULL; } if (pServiceProvider) { pServiceProvider->Release(); pServiceProvider = NULL; } return hr; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT AppProtocol::Continue( PROTOCOLDATA * pProtData) { HRESULT hr = S_OK; // CHAR appDomainPath[MAX_PATH] = ""; WCHAR appRootTranslated[MAX_PATH]; if(pProtData->dwState != 1) { hr = E_FAIL; goto exit; } // step 1: "install" the specified file appRootTranslated[0] = L'\0'; hr = SetupAndInstall(m_fullUri, appRootTranslated); if (FAILED(hr)) goto exit; if (m_localStoreFilePath == NULL) { hr = E_FAIL; goto exit; } m_appRootTranslated = DuplicateString(appRootTranslated); if (m_appRootTranslated == NULL) { hr = E_OUTOFMEMORY; goto exit; } // step 2: check error status if ((m_status & STATUS_OFFLINE_MODE) && (m_status & STATUS_NOT_IN_CACHE)) { char buffer[100 + MAX_PATH]; SendHeaders(HTTP_RESPONSEOK); _snprintf(buffer, 100+MAX_PATH, "Error: In offline mode and file not found in application store.\r\nLocal path expected- %ws", m_localStoreFilePath); WriteBytes((BYTE*) buffer, lstrlenA(buffer)+1); hr = Finish(); goto exit; } // step 3: process different file types if (m_appType == APPTYPE_BYMANIFEST) { char buffer[512 + MAX_PATH]; SendHeaders(HTTP_RESPONSEOK); // ???? ignore if excess buffer len // do this before as m_localStoreFilePath might be overwritten _snprintf(buffer, 512 + MAX_PATH, "Application installed and executed with manifest file - %ws\r\nSource - %ws", m_localStoreFilePath, m_fullUri); if (FAILED(hr = ProcessAppManifest())) goto exit; WriteBytes((BYTE *) buffer, lstrlenA(buffer)+1); //???? includes last '\0' hr = Finish(); } else if (m_appType == APPTYPE_ASM) { char buffer[512 + MAX_PATH]; SendHeaders(HTTP_RESPONSEOK); // ???? ignore if excess buffer len _snprintf(buffer, 512+MAX_PATH, "Assembly Executed - %ws\r\nSource - %ws", m_localStoreFilePath, m_fullUri); WriteBytes((BYTE*) buffer, lstrlenA(buffer)+1); //???? includes last '\0' hr = Finish(); } else if (m_appType == APPTYPE_IE) { DWORD dwLength; BYTE buffer[512]; char header[60]; HANDLE hFile; int len; WCHAR* p; // set mime type // assume lower cases len = lstrlen(m_localStoreFilePath); p = m_localStoreFilePath + len - 5; if ((p[1] == L'.' && p[2] == L'h' && p[3] == L't' && p[4] == L'm') || (p[0] == L'.' && p[1] == L'h' && p[2] == L't' && p[3] == L'm' && p[4] == L'l')) //L".htm" ".html" sprintf(header, "%s\r\nContent-Type: text/html\r\n", HTTP_RESPONSEOK); else if (p[1] == L'.' && p[2] == L'g' && p[3] == L'i' && p[4] == L'f') //L".gif" sprintf(header, "%s\r\nContent-Type: image/gif\r\n", HTTP_RESPONSEOK); else if ((p[1] == L'.' && p[2] == L'j' && p[3] == L'p' && p[4] == L'g') || (p[0] == L'.' && p[1] == L'j' && p[2] == L'p' && p[3] == L'e' && p[4] == L'g')) //L".jpg" ".jpeg" sprintf(header, "%s\r\nContent-Type: image/jpeg\r\n", HTTP_RESPONSEOK); else sprintf(header, "%s", HTTP_RESPONSEOK); SendHeaders(header); hFile = CreateFile(m_localStoreFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { hr = GetLastWin32Error(); goto exit; } ZeroMemory(buffer, sizeof(buffer)); while ( ReadFile (hFile, buffer, 512, &dwLength, NULL) && dwLength ) { WriteBytes(buffer, dwLength); } if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); hr = Finish(); } exit: return hr; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT AppProtocol::Finish() { HRESULT hr = S_OK; if (m_done == FALSE) { m_done = TRUE; m_pProtocolSink->ReportData(BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE, 0, m_cbOutput); if (m_aborted == FALSE) m_pProtocolSink->ReportResult(S_OK, 0, NULL); } return hr; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT AppProtocol::Abort( HRESULT hrReason, DWORD ) { HRESULT hr = S_OK; m_aborted = TRUE; if (m_pProtocolSink != NULL) { hr = m_pProtocolSink->ReportResult(hrReason, 0, 0); if (FAILED(hr)) goto exit; } exit: return hr; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT AppProtocol::Terminate( DWORD ) { Cleanup(); return S_OK; } HRESULT AppProtocol::Suspend() { return E_NOTIMPL; } HRESULT AppProtocol::Resume() { return E_NOTIMPL; } HRESULT AppProtocol::Read( void *pv, ULONG cb, ULONG *pcbRead) { HRESULT hr; hr = m_pOutputRead->Read(pv, cb, pcbRead); // We must only return S_FALSE when we have hit the absolute end of the stream // If we think there is more data coming down the wire, then we return E_PENDING // here. Even if we return S_OK and no data, UrlMON will still think we've hit // the end of the stream. // if (S_OK == hr && 0 == *pcbRead) //**** if (S_OK == hr && (0 == *pcbRead || cb > *pcbRead)) { hr = m_done ? S_FALSE : E_PENDING; } return hr; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT AppProtocol::Seek( LARGE_INTEGER offset, DWORD origin, ULARGE_INTEGER *pPos) { return m_pOutputRead->Seek(offset, origin, pPos); } HRESULT AppProtocol::LockRequest( DWORD ) { return S_OK; } HRESULT AppProtocol::UnlockRequest() { return S_OK; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT AppProtocol::SendHeaders( LPSTR headers) { HRESULT hr = S_OK; // DWORD flags = 0; DWORD dwLength = 0; LPSTR mime = NULL; LPSTR tail = NULL; int iHLen = 0; if(headers == NULL) { hr = E_UNEXPECTED; goto exit; } if (m_strResponseHeader != NULL) MemFree(m_strResponseHeader); iHLen = strlen(headers); m_strResponseHeader = (WCHAR *) MemAllocClear(iHLen*sizeof(WCHAR) + 512); if (m_strResponseHeader != NULL) { wcscpy(m_strResponseHeader, L"Server: Microsoft.Net-App/1.0\r\nDate:"); // Microsoft-IIS/App 1.0 WCHAR szTemp[100]; szTemp[0] = NULL; GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, NULL, NULL, szTemp, 100); wcscat(m_strResponseHeader, szTemp); wcscat(m_strResponseHeader, L" "); GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL, szTemp, 100); wcscat(m_strResponseHeader, szTemp); wcscat(m_strResponseHeader, L"\r\n"); int iLen = wcslen(m_strResponseHeader); MultiByteToWideChar(CP_ACP, 0, headers, -1, &m_strResponseHeader[iLen], iHLen + 256 - iLen - 1); } mime = stristr(headers, "Content-Type:"); if(mime) { mime += 13; while(*mime && isspace(*mime)) mime++; tail = mime; while(*tail && *tail != '\r') tail++; dwLength = tail - mime; if(dwLength) { if (m_responseMimeType) MemFree(m_responseMimeType); m_responseMimeType = (WCHAR *) MemAlloc((dwLength + 1) * sizeof(WCHAR)); if (m_responseMimeType == NULL) { hr = E_OUTOFMEMORY; goto exit; } MultiByteToWideChar(CP_ACP, 0, mime, dwLength, m_responseMimeType, dwLength); m_responseMimeType[dwLength] = L'\0'; } } if (m_responseMimeType && *m_responseMimeType) m_pProtocolSink->ReportProgress(BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE, m_responseMimeType); else m_pProtocolSink->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, L"text/plain"); //proper default not L"text/html"); //**** SaveCookie(headers); exit: return hr; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// /*HRESULT AppProtocol::SaveCookie( LPSTR header) { HRESULT hr = S_OK; LPSTR cookie, tail; WCHAR *cookieBody = NULL; int bodyLength; for(cookie = stristr(header, "Set-Cookie:"); cookie != NULL; cookie = (*tail ? stristr(tail, "Set-Cookie:") : NULL)) //???? added () around ? : { cookie += 11; while(*cookie && isspace(*cookie)) cookie++; tail = cookie; while(*tail && *tail != '\r') tail++; bodyLength = tail - cookie; if(bodyLength) { cookieBody = (WCHAR *)MemAlloc(sizeof(WCHAR) *(bodyLength + 1)); if (cookieBody == NULL) { hr = E_OUTOFMEMORY; goto exit; } MultiByteToWideChar(CP_ACP, 0, cookie, bodyLength, cookieBody, bodyLength); cookieBody[bodyLength] = '\0'; if(!g_pInternetSetCookieW(m_cookiePath, NULL, cookieBody)) { hr = GetLastWin32Error(); goto exit; } } } exit: if (cookieBody) MemFree(cookieBody); return hr; }*/ ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT AppProtocol::WriteBytes( BYTE * buffer, DWORD dwLength) { HRESULT hr = S_OK; DWORD flags = 0; EnterCriticalSection(&m_csOutputWriter); if (!m_pOutputWrite) { hr = E_UNEXPECTED; } if (FAILED(hr = m_pOutputWrite->Write(buffer, dwLength, &dwLength))) goto exit; m_cbOutput += dwLength; if (!m_started) { m_started = TRUE; flags |= BSCF_FIRSTDATANOTIFICATION; } else //**** if (m_done) { flags |= BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE; } else //**** flags |= BSCF_INTERMEDIATEDATANOTIFICATION; if (FAILED(hr = m_pProtocolSink->ReportData(flags, dwLength, m_cbOutput))) goto exit; exit: LeaveCriticalSection(&m_csOutputWriter); return hr; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// /*int AppProtocol::GetKnownRequestHeader ( LPCWSTR szHeader, LPWSTR buf, int size) { if (szHeader == NULL || szHeader[0] == NULL || wcslen(szHeader) > 256) return 0; LPCWSTR szReturn = NULL; LPCWSTR szStart = NULL; int iLen = 0; HRESULT hr = S_OK; // int iter = 0; WCHAR szHeadUpr[260] = L""; wcscpy(szHeadUpr, szHeader); _wcsupr(szHeadUpr); if (wcscmp(szHeadUpr, SZ_HTTP_COOKIE) == 0) { szReturn = m_cookie; iLen = (m_cookie ? wcslen(m_cookie) : 0); goto exit; } if (m_extraHeadersUpr == NULL && m_extraHeaders != NULL) { m_extraHeadersUpr = DuplicateString(m_extraHeaders); if (m_extraHeadersUpr == NULL) { hr = E_OUTOFMEMORY; goto exit; } _wcsupr(m_extraHeadersUpr); } if (m_extraHeadersUpr == NULL) goto exit; szStart = wcsstr(m_extraHeadersUpr, szHeadUpr); if (szStart == NULL) goto exit; iLen = wcslen(szHeadUpr); szReturn = &(m_extraHeaders[iLen+1]); while(iswspace(*szReturn)) szReturn++; for (iLen = 0; szReturn[iLen] != L'\r' && szReturn[iLen] != NULL; iLen++); exit: if (szReturn == NULL || iLen == 0) return 0; if (iLen >= size) return -(iLen + 1); buf[iLen] = NULL; memcpy(buf, szReturn, iLen*sizeof(WCHAR)); return iLen; } */ ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // TODO: make it real, netclasses, manifest and all that // // ???? construct filename, create directory HRESULT AppProtocol::SetupAndInstall( LPTSTR url, /* *not* ok to modify */ LPTSTR path /* out */) { HRESULT hr = S_OK; // WCHAR *installFromPath = NULL; // full path - app://www.microsoft.com/app5/app.manifest WCHAR *origin = NULL; // src - www.microsoft.com/app5 WCHAR *p, *q; int i, j; // skip app:// part origin = DuplicateString(url + lstrlen(PROTOCOL_SCHEME)); if (origin == NULL) { hr = E_OUTOFMEMORY; goto exit; } // installFromPath = (WCHAR *) MemAlloc((lstrlen(origin/*url*/) + lstrlen(HTTP_SCHEME)/*MAX_PATH*/) * sizeof(WCHAR)); // if (installFromPath == NULL) // { // hr = E_OUTOFMEMORY; // goto exit; // } //??????????????????????? WCHAR installFromPath[MAX_URL_LENGTH]; lstrcpy(installFromPath, HTTP_SCHEME); lstrcat(installFromPath, origin); // assume file name is given (it's now passed in) - not myweb.cab // better? - check if last contain ".", if so assume has filename, else attach app.manifest as filename // lstrcat(installFromPath, L"/MyWeb.cab"); //????????? if no filename it will get index.html, if exists! // remove the filename, if specified // ?????????? this assume all filename has a '.' p = wcsrchr(origin, L'/'); q = wcsrchr(origin, L'.'); if (p && q && /*((p - origin) > 6) &&*/ (p < q)) *p = L'\0'; // else - no filename -> do something to installFromPath? or no '/' after "app://" in url else { hr = E_INVALIDARG; goto exit; } // install to if(GetEnvironmentVariable(L"ProgramFiles", path, MAX_PATH-lstrlen(STORE_PATH)-1) == 0) //???????? { // hr = CO_E_PATHTOOLONG;//E_FAIL; goto exit; } // ???????? lstrcat(path, STORE_PATH); if(!CreateDirectory(path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { hr = GetLastWin32Error(); goto exit; } lstrcat(path, L"\\"); i = lstrlen(path); j = 0; while(origin[j]) { if(origin[j] == L'/') { path[i] = L'\0'; if(!CreateDirectory(path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { hr = GetLastWin32Error(); goto exit; } path[i] = L'\\'; } else path[i] = origin[j]; i++; j++; } path[i] = L'\0'; if(!CreateDirectory(path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { hr = GetLastWin32Error(); goto exit; } // grab the files hr = InstallInternetFile(installFromPath, path); exit: // if(installFromPath) MemFree(installFromPath); if(origin) MemFree(origin); return hr; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // given the url like MyWeb://www.site.com/app/something/else?querystring=aa // sets // m_uriPath to /app/something/else // m_queryString to querystring=aa // m_appRoot to /app or // /app/something or // /app/something/else, // depending on which was successfully mapped to // m_appRootTranslated to, for instance, c:\program files\site app\ // not set till SetupAndInstall() is called // m_appOrigin to www.site.com // m_fullUri to myweb://www.site.com/app/something/else // // if application was not installed, attempts to install it // // ???? setup strings, find out if needs install HRESULT AppProtocol::ParseUrl(LPCTSTR url) { HRESULT hr = S_OK; WCHAR slash = L'/'; WCHAR *p, *base; int cchFullUri = 0; // WCHAR appRootTranslated[MAX_PATH]; // copy the cookie path into member variable m_fullUri = DuplicateString(url); if (m_fullUri == NULL) { hr = E_OUTOFMEMORY; goto exit; } // locate, store and strip query string, if any p = wcschr(m_fullUri, L'?'); if(p != NULL && p[1] != L'\0') { m_queryString = p + 1; *p = L'\0'; } // ????? to be moved // assume lower cases // need to find from end, skip ?querystring, use m_fullUri instead? cchFullUri = lstrlen(m_fullUri); m_appType = APPTYPE_IE; // ????? BUGBUG assume >= than 9 characters if (cchFullUri >= 9) { p = m_fullUri + cchFullUri - 5; /* if (p[0] != L'.' && p[1] != L'.') // error? no extension m_appType = APPTYPE_IE; else*/ if (p[1] == L'.' && (p[2] == L'e' && p[3] == L'x' && p[4] == L'e') || (p[2] == L'd' && p[3] == L'l' && p[4] == L'l')) //L".exe" ".dll" m_appType = APPTYPE_ASM; // (check asm or not?) else if (p[0] == L'.' && p[1] == L'a' && p[2] == L's' && p[3] == L'p' && p[4] == L'x') //L".aspx" m_appType = APPTYPE_MYWEB; else if (wcsncmp(p = m_fullUri + cchFullUri - 9, L".manifest", 9) == 0) m_appType = APPTYPE_BYMANIFEST; //else //default or others // m_appType = APPTYPE_IE; } // skip through protocol:// p = wcschr(m_fullUri, L':'); if(p == NULL || p[1] != slash || p[2] != slash || p[3] == L'\0') { hr = E_INVALIDARG; } // copy full origin path m_appOrigin = DuplicateString(p + 3); if (m_appOrigin == NULL) { hr = E_OUTOFMEMORY; goto exit; } // point to the end of site base = wcschr(m_appOrigin, slash); if(base == NULL) { hr = E_INVALIDARG; goto exit; } // appRootTranslated[0] = L'\0'; // tear off one segment at a time from the end while((p = wcsrchr(m_appOrigin, slash)) != base) { *p = L'\0'; // if(GetAppBaseDir(m_appOrigin, appRootTranslated) == S_OK) // { // break; // } } // moved to Continue() // if(appRootTranslated[0] == L'\0') // { // WCHAR * appPath = DuplicateString(m_fullUri); // WCHAR * p; // application not installed, install it /* if (appPath == NULL) { hr = E_OUTOFMEMORY; goto exit; } */ // ???? why? - this will remove the last - supposingly the filename? // p = wcsrchr(appPath, L'/'); // if(p) *p = NULL; // hr = SetupAndInstall(m_fullUri/*appPath*/, appRootTranslated); // if (FAILED(hr)) // goto exit; // if(appPath) MemFree(appPath); // } /* m_appRootTranslated = DuplicateString(appRootTranslated); if (m_appRootTranslated == NULL) { hr = E_OUTOFMEMORY; goto exit; } */ m_appRoot = wcschr(m_appOrigin, slash); // ???? this should be ok /* if(m_appRoot == NULL) { hr = E_INVALIDARG; goto exit; } */ if (m_appRoot) m_uriPath = wcsstr(m_fullUri, m_appRoot); else // ???? this should be ok m_uriPath = NULL; exit: return hr; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // Note: appRoot is assumed to have enough space (MAX_PATH) // /*HRESULT AppProtocol::GetAppBaseDir(LPCTSTR base, LPTSTR appRoot) { HKEY hKey = NULL; HRESULT hr = E_FAIL; HKEY hSubKey = NULL; appRoot[0] = L'\0'; if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, MYWEBS_KEY, 0, KEY_READ, &hKey) != ERROR_SUCCESS) { hr = GetLastWin32Error(); goto exit; } if(RegOpenKeyEx(hKey, base, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS) { DWORD dwType, cb = MAX_PATH * sizeof(WCHAR); if(RegQueryValueEx(hSubKey, MYWEBS_APPROOT, NULL, &dwType, (LPBYTE)appRoot, &cb) == ERROR_SUCCESS) { dwType = GetFileAttributes(appRoot); if (dwType == (DWORD) -1 || (dwType & FILE_ATTRIBUTE_DIRECTORY) == 0) appRoot[0] = NULL; else hr = S_OK; } RegCloseKey(hSubKey); } RegCloseKey(hKey); //exit: return hr; }*/ ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // ???????called only from managed code? WCHAR * AppProtocol::MapString( int key) { /* switch(key) { case IEWR_URIPATH: return m_uriPath; case IEWR_QUERYSTRING: return m_queryString; case IEWR_VERB: return m_verb; case IEWR_APPPATH: return m_appRoot; case IEWR_APPPATHTRANSLATED: return m_appRootTranslated; default:*/ if (key) key=key; // ??????? return NULL; // } } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // ????????called only from managed code? int AppProtocol::GetStringLength( int key) { WCHAR *targetString = MapString(key); return targetString ? lstrlen(targetString) + 1 : 0; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// int AppProtocol::GetString( int key, WCHAR * buf, int size) { WCHAR *targetString = MapString(key); int len; if(targetString == NULL) return 0; len = lstrlen(targetString); if(len >= size) return 0; lstrcpy(buf, targetString); return len + 1; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// /*int AppProtocol::MapPath( WCHAR * virtualPath, WCHAR * physicalPath, int length) { int requiredLength = lstrlen(m_appRootTranslated) + 2; int rootLength = lstrlen(m_appRoot); int i = 0; if(virtualPath && virtualPath[0] != '\0') { requiredLength += lstrlen(virtualPath) - rootLength; } if(requiredLength <= 0) return 0; if(requiredLength > length) return - requiredLength; while(m_appRootTranslated[i]) { physicalPath[i] = m_appRootTranslated[i]; i++; } if(virtualPath && virtualPath[0] != L'\0') { if(_memicmp(virtualPath, m_appRoot, sizeof(WCHAR) * rootLength)) return 0; virtualPath += rootLength; if(*virtualPath && *virtualPath != L'/') physicalPath[i++] = L'\\'; while(*virtualPath) { if(*virtualPath == L'/') physicalPath[i++] = L'\\'; else physicalPath[i++] = *virtualPath; virtualPath++; } } physicalPath[i] = L'\0'; return 1; } */ ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // ????? download file from url into path HRESULT AppProtocol::InstallInternetFile( LPTSTR url, LPTSTR path) { HRESULT hr = S_OK; HINTERNET hInternet = NULL; HINTERNET hTransfer = NULL; HANDLE hFile = INVALID_HANDLE_VALUE; WCHAR * file = NULL; DWORD bytesRead = 0; DWORD bytesWritten = 0; WCHAR szFile[MAX_PATH]; BYTE buffer[4096]; BOOL bNeedDownload = TRUE; // CabHandlerInfo cabInfo; // ????????? TO BE MOVED! // check offline mode DWORD dwState = 0; DWORD dwSize = sizeof(DWORD); BOOL bRemoveEmptyFile = FALSE; if(g_pInternetQueryOption(NULL, INTERNET_OPTION_CONNECTED_STATE, &dwState, &dwSize)) { if(dwState & INTERNET_STATE_DISCONNECTED_BY_USER) m_status |= STATUS_OFFLINE_MODE; } //////////////////////////////////////////////////////////// // Step 1: Construct the file name // ???? this assumes there is a filename after the last '/' ZeroMemory(szFile, sizeof(szFile)); wcsncpy(szFile, path, MAX_PATH - 2); lstrcat(szFile, L"\\"); file = wcsrchr(url, L'/'); if (file != NULL && wcslen(szFile) + wcslen(file) < MAX_PATH) wcscat(szFile, file + 1); else { hr = CO_E_PATHTOOLONG;//E_FAIL; goto exit; } MemClearFn((void **)&m_localStoreFilePath); m_localStoreFilePath = DuplicateString(szFile); //////////////////////////////////////////////////////////// // Step 2: Create the file // need write access, will open (and replace/overwrite) exiting file // ??? FILE_SHARE_READ? but we might write to if outdated... hFile = CreateFile(szFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { hr = GetLastWin32Error(); goto exit; } // ????????? TO BE MOVED! if (GetLastError() == ERROR_ALREADY_EXISTS) { FILETIME ftLastModified; #define FTTICKSPERDAY (60*60*24*(LONGLONG) 10000000) // == 864000000000 if (GetFileTime(hFile, NULL, NULL, &ftLastModified)) { SYSTEMTIME sSysT; FILETIME ft; GetSystemTime(&sSysT); SystemTimeToFileTime(&sSysT, &ft); ULARGE_INTEGER ulInt = * (ULARGE_INTEGER *) &ft; ulInt.QuadPart -= FTTICKSPERDAY * 3; // 3 days ft = * (FILETIME *) &ulInt; if (CompareFileTime(&ftLastModified, &ft) != -1) // reuse file, just execute it bNeedDownload = FALSE; } } else m_status |= STATUS_NOT_IN_CACHE; if ((m_status & STATUS_OFFLINE_MODE) && (m_status & STATUS_NOT_IN_CACHE)) { // hr = E_FAIL; // ???? file not found - if uncomment this line, generic IE error page will display instead bRemoveEmptyFile = TRUE; goto exit; } if (bNeedDownload && !(m_status & STATUS_OFFLINE_MODE)) { //////////////////////////////////////////////////////////// // Step 3: Copy the files over the internet hInternet = g_pInternetOpen(L"App", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if(hInternet == NULL) { hr = GetLastWin32Error(); bRemoveEmptyFile = TRUE; goto exit; } // use own caching instead hTransfer = g_pInternetOpenUrl(hInternet, url, NULL, 0, INTERNET_FLAG_NO_CACHE_WRITE, 0); if(hTransfer == NULL) { hr = GetLastWin32Error(); bRemoveEmptyFile = TRUE; goto exit; } // need to check if there's any error, eg. not found (404)... // synchronous download while(g_pInternetReadFile(hTransfer, buffer, sizeof(buffer), &bytesRead) && bytesRead != 0) { if ( !WriteFile(hFile, buffer, bytesRead, &bytesWritten, NULL) || bytesWritten != bytesRead ) { hr = GetLastWin32Error(); bRemoveEmptyFile = TRUE; goto exit; } } } // ensure file/internet handles are closed before further processing is done if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); if (hInternet != NULL) g_pInternetCloseHandle(hInternet); if (hTransfer != NULL) g_pInternetCloseHandle(hTransfer); hInternet = NULL; hTransfer = NULL; hFile = INVALID_HANDLE_VALUE; // ????????? TO BE MOVED! // process - run all .exe // lower case? end of string? if (/*m_appType == APPTYPE_ASM)*/wcsstr(file, L".exe")) { //#define MAX_SIZE_SECURITY_ID 0x200 DWORD dwZone; DWORD dwSize = MAX_SIZE_SECURITY_ID; BYTE byUniqueID[MAX_SIZE_SECURITY_ID]; WCHAR wzUniqueID[MAX_SIZE_SECURITY_ID * 2 + 1]; WCHAR wzCmdLine[1025]; //IInternetSecurityManager::ProcessUrlAction // lazy init if (g_pSecurityMgr == NULL) { hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER, IID_IInternetSecurityManager, (void**)&g_pSecurityMgr); //???? should this fails at all if security info/manager is undef? if (FAILED(hr)) { g_pSecurityMgr = NULL; goto exit; } } // m_fullUri -> this should translate correctly in IInternetProtocolInfo->ParseUrl() if (SUCCEEDED(hr = g_pSecurityMgr->MapUrlToZone(m_fullUri, &dwZone, 0))) { // check space in site? - assume checked in GetSecurityId // should site be just www.abc.com or www.abc.com/app2/appmain.exe? if (FAILED(hr) || FAILED(hr = g_pSecurityMgr->GetSecurityId(m_fullUri, byUniqueID, &dwSize, 0))) goto exit; ConvertToHex(wzUniqueID, byUniqueID, dwSize); } else { goto exit; } // ???? any space in url? start a new proc for that asm, not hang the browser /* C:\\Documents and Settings\\felixybc.NTDEV\\Desktop\\work\\conexec\\Debug\\*/ if (_snwprintf(wzCmdLine, 1025, L"conexec.exe \"%s\" +3 %d %s %s", szFile, dwZone, wzUniqueID, url) < 0) { hr = CO_E_PATHTOOLONG; goto exit; } RunCommand(wzCmdLine); } exit: if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); if (hInternet != NULL) g_pInternetCloseHandle(hInternet); if (hTransfer != NULL) g_pInternetCloseHandle(hTransfer); hInternet = NULL; hTransfer = NULL; hFile = INVALID_HANDLE_VALUE; // remove the file so that it will not be mistaken next time this runs // ignore if error deleting the file so the hr is not overwritten if (bRemoveEmptyFile) DeleteFile(szFile); return hr; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT AppProtocol::QueryOption(DWORD, LPVOID, DWORD*) { return E_NOTIMPL; } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// HRESULT AppProtocol::QueryInfo( DWORD dwOption, LPVOID pBuffer, LPDWORD pcbBuf, LPDWORD , LPDWORD ) { if (pcbBuf == NULL) return E_FAIL; HRESULT hr = S_OK; LPCWSTR szHeader = NULL; DWORD dwOpt = (dwOption & HTTP_QUERY_HEADER_MASK); DWORD dwLen = (m_strResponseHeader ? wcslen(m_strResponseHeader) : 0); switch(dwOpt) { case HTTP_QUERY_RAW_HEADERS_CRLF: case HTTP_QUERY_RAW_HEADERS: if (m_strResponseHeader == NULL) return E_FAIL; if (*pcbBuf < dwLen + 1) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); } else { if (dwOpt == HTTP_QUERY_RAW_HEADERS_CRLF) { wcscpy((WCHAR *) pBuffer, m_strResponseHeader); } else { DWORD iPos = 0; for(DWORD iter=0; iter_pFileList = new FILEINFOLIST; pCurrent = pAppInfo->_pFileList; } else { pCurrent->_pNext = new FILEINFOLIST; pCurrent = pCurrent->_pNext; } pCurrent->_pNext = NULL; _snwprintf(pCurrent->_wzFilename, MAX_PATH, L"%S", token); // worry about total path len later //no need m_pProtocolSink->ReportProgress(BINDSTATUS_BEGINDOWNLOADCOMPONENTS, L"downloading application files"); token = strtok( NULL, seps ); if (!_stricmp(token, "hash")) { _snwprintf(pCurrent->_wzHash, 33, L"%S", token); // ???? should i reuse the file handle instead? //* if (IsFileCorrupted(m_localStoreFilePath, token)) Integrity //* ... } else fSkipNextToken = TRUE; } } else if (!_stricmp(token, "newhref")) { // note, different handling... because '/' is one of seps for (int i = 0; i < MAX_URL_LENGTH; i++) { if (*(token+8+i) == '<') { // BUGBUG: 8 == strlen("newhref>") *(token+8+i) = '\0'; _snwprintf(pAppInfo->_wzNewRef, i, L"%S", token+8); // BUGBUG? is this going to work? token = strtok( token+i+9, seps); // now token == "newhref" && *(token-1) == '/' } } // BUGBUG: ignoring > MAX_URL_LENGTH case here... may mess up later if the URL contain a "keyword" // may be ok... } //else // ignore others for now // Get next token... if(!fSkipNextToken) token = strtok( NULL, seps ); else fSkipNextToken = FALSE; } }