/* Miranda SmileyAdd Plugin Copyright (C) 2007 - 2011 Boris Krasnovskiy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "stdafx.h" HNETLIBUSER hNetlibUser; static HANDLE hFolderCache = 0, hFolderPacks = 0; struct QueueElem { CMStringW url; CMStringW fname; bool needext; QueueElem(const CMStringW &purl, const CMStringW &pfname, bool ne) : url(purl), fname(pfname), needext(ne) { } }; static mir_cs csQueue; static LIST dlQueue(10); static wchar_t g_wszCachePath[MAX_PATH]; static bool threadRunning; bool InternetDownloadFile(const char *szUrl, char *szDest, HNETLIBCONN &hHttpDwnl) { int result = 0xBADBAD; char *szRedirUrl = nullptr; NETLIBHTTPREQUEST nlhr = {}; // initialize the netlib request nlhr.cbSize = sizeof(nlhr); nlhr.requestType = REQUEST_GET; nlhr.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_PERSISTENT | NLHRF_REDIRECT; nlhr.szUrl = (char*)szUrl; nlhr.nlc = hHttpDwnl; // change the header so the plugin is pretended to be IE 6 + WinXP nlhr.headersCount = 2; nlhr.headers = (NETLIBHTTPHEADER*)alloca(sizeof(NETLIBHTTPHEADER)*nlhr.headersCount); nlhr.headers[0].szName = "User-Agent"; nlhr.headers[0].szValue = NETLIB_USER_AGENT; nlhr.headers[1].szName = "Connection"; nlhr.headers[1].szValue = "close"; while (result == 0xBADBAD) { // download the page NLHR_PTR nlhrReply(Netlib_HttpTransaction(hNetlibUser, &nlhr)); if (nlhrReply) { hHttpDwnl = nlhrReply->nlc; // if the recieved code is 200 OK if (nlhrReply->resultCode == 200) { char *delim = strrchr(szDest, '\\'); if (delim) *delim = '\0'; CreateDirectoryTree(szDest); if (delim) *delim = '\\'; int res = -1; int fh = _open(szDest, _O_BINARY | _O_WRONLY | _O_CREAT, _S_IREAD | _S_IWRITE); if (fh != -1) { res = _write(fh, nlhrReply->pData, nlhrReply->dataLength); _close(fh); } if (res < 0) remove(szDest); else result = 0; } // if the recieved code is 302 Moved, Found, etc // workaround for url forwarding else if (nlhrReply->resultCode == 302 || nlhrReply->resultCode == 301 || nlhrReply->resultCode == 307) { // page moved // get the url for the new location and save it to szInfo // look for the reply header "Location" if (auto *pszUrl = Netlib_GetHeader(nlhrReply, "Location")) { size_t rlen = 0; if (pszUrl[0] == '/') { const char *szPref = strstr(szUrl, "://"); szPref = szPref ? szPref + 3 : szUrl; const char *szPath = strchr(szPref, '/'); rlen = szPath != nullptr ? szPath - szUrl : mir_strlen(szUrl); } szRedirUrl = (char *)mir_realloc(szRedirUrl, rlen + mir_strlen(pszUrl) * 3 + 1); strncpy(szRedirUrl, szUrl, rlen); mir_strcpy(szRedirUrl + rlen, pszUrl); nlhr.szUrl = szRedirUrl; } } else result = 1; } else { hHttpDwnl = nullptr; result = 1; } } mir_free(szRedirUrl); return result == 0; } void __cdecl SmileyDownloadThread(void*) { Thread_SetName("SmileyAdd: SmileyDownloadThread"); bool needext = false; HNETLIBCONN hHttpDwnl = nullptr; while (!Miranda_IsTerminated()) { QueueElem *pItem = nullptr; { mir_cslock lck(csQueue); if (dlQueue.getCount()) { pItem = dlQueue[0]; dlQueue.remove(int(0)); } } if (pItem == nullptr) { SleepEx(3000, TRUE); continue; } if (_waccess(pItem->fname.c_str(), 0) != 0) { InternetDownloadFile(_T2A(pItem->url.c_str()), _T2A(pItem->fname.c_str()), hHttpDwnl); CMStringW fname(pItem->fname); if (pItem->needext) { fname += ProtoGetAvatarExtension(ProtoGetAvatarFileFormat(fname)); needext = true; } _wrename(pItem->fname.c_str(), fname.c_str()); } delete pItem; } for (auto &it : dlQueue) delete it; dlQueue.destroy(); Netlib_CloseHandle(hHttpDwnl); threadRunning = false; if (!Miranda_IsTerminated()) { if (needext) CallServiceSync(MS_SMILEYADD_RELOAD, 0, 0); else NotifyEventHooks(g_hevOptionsChanged, 0, 0); } } bool GetSmileyFile(CMStringW &url, const CMStringW &packstr) { uint8_t hash[MIR_SHA1_HASH_SIZE]; mir_sha1_hash((uint8_t*)url.c_str(), url.GetLength() * sizeof(wchar_t), hash); wchar_t wszHash[MIR_SHA1_HASH_SIZE * 2 + 1]; bin2hexW(hash, sizeof(hash), wszHash); CMStringW filename; filename.AppendFormat(L"%s\\%s\\", g_wszCachePath, packstr.c_str()); int pathpos = filename.GetLength(); MRegexp16 urlsplit(L".*/(.*)"); urlsplit.match(url); CMStringW urlFileName = urlsplit.getGroup(1); int iExtIdx = urlFileName.ReverseFind('.'); if (iExtIdx != -1) { CMStringW wszExt = urlFileName.Mid(iExtIdx); if (ProtoGetAvatarFormat(wszExt) == PA_FORMAT_UNKNOWN) iExtIdx = -1; else filename += urlFileName; } if (iExtIdx == -1) { filename += wszHash; filename += L".*"; } _wfinddata_t c_file; INT_PTR hFile = _wfindfirst(filename, &c_file); if (hFile > -1) { _findclose(hFile); filename.Truncate(pathpos); filename += c_file.name; url = filename; return false; } if (iExtIdx == -1) { filename.Truncate(pathpos); filename += wszHash; filename += L"."; } { mir_cslock lck(csQueue); dlQueue.insert(new QueueElem(url, filename, iExtIdx == -1)); } if (!threadRunning) { threadRunning = true; mir_forkthread(SmileyDownloadThread); } url = filename; return false; } int FolderChanged(WPARAM, LPARAM) { FoldersGetCustomPathW(hFolderCache, g_wszCachePath, MAX_PATH, L""); FoldersGetCustomPathW(hFolderPacks, g_plugin.wszDefaultPath, MAX_PATH, L"%miranda_path%"); size_t len = mir_wstrlen(g_plugin.wszDefaultPath); if (len && g_plugin.wszDefaultPath[len - 1] != '\\') mir_wstrcpy(g_plugin.wszDefaultPath + len, L"\\"); return 0; } void DownloadInit(void) { NETLIBUSER nlu = {}; nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_NOHTTPSOPTION; nlu.szSettingsModule = MODULENAME; nlu.szDescriptiveName.a = MODULENAME; hNetlibUser = Netlib_RegisterUser(&nlu); hFolderPacks = FoldersRegisterCustomPathW(LPGEN("SmileyAdd"), LPGEN("Smiley packs' folder"), L"%miranda_path%"); if (hFolderPacks) { hFolderCache = FoldersRegisterCustomPathW(LPGEN("SmileyAdd"), LPGEN("Smiley cache"), MIRANDA_USERDATAW L"\\SmileyCache"); FolderChanged(0, 0); HookEvent(ME_FOLDERS_PATH_CHANGED, FolderChanged); } else { wcsncpy_s(g_wszCachePath, VARSW(L"%miranda_userdata%\\SmileyCache"), _TRUNCATE); wcsncpy_s(g_plugin.wszDefaultPath, VARSW(L"%miranda_path%\\"), _TRUNCATE); } } void DownloadClose(void) { Netlib_CloseHandle(hNetlibUser); }