#include "common.h" #include "socket.h" void unzip_mem(char* buf, int len, TCHAR* dest); bool GetFile(char *url, TCHAR *temp_folder, char *plugin_name, char *version, bool dlls_only, int recurse_count /*=0*/) { if(recurse_count > MAX_REDIRECT_RECURSE) { NLog("GetFile: error, too many redirects"); return false; } TCHAR save_file[MAX_PATH]; if(url == 0 || temp_folder == 0 || plugin_name == 0) return false; // ensure temp_folder exists if(!CreatePath(options.temp_folder)) { NLogF("GetFile: error creating temp folder, code %u", GetLastError()); return false; } // ensure zip_folder exists, if necessary if(options.save_zips && !CreatePath(options.zip_folder)) { NLogF("GetFile: error creating zip folder, code %u", GetLastError()); return false; } TCHAR *temp_str = GetTString(plugin_name); mir_sntprintf(save_file, SIZEOF(save_file), _T("%s\\%s"), temp_folder, temp_str); mir_free(temp_str); if (version) { temp_str = GetTString(version); _tcscat(save_file, _T("_")); _tcscat(save_file, temp_str); mir_free(temp_str); } // copt extension from url char *ext = strrchr(url, '.'); if(ext && *ext && strcmp(ext, ".dll") == 0) { _tcscat(save_file, _T(".dll")); } else { // default to zip extension (e.g. miranda fl) _tcscat(save_file, _T(".zip")); ext = ".zip"; } // replace version text in URL char tmp_url[1024]; if (version != NULL) { char *p; size_t pos = 0; size_t version_len = strlen(version); while ((p = strstr(url, "%VERSION%")) != NULL && (p - url + version_len < sizeof(tmp_url) - 1)) { strncpy(&tmp_url[pos], url, p - url); pos += p - url; strcpy(&tmp_url[pos], version); pos += version_len; url += p - url + 9; // 9 == strlen("%VERSION%") } if (strlen(url) < sizeof(tmp_url) - 1) { strcpy(&tmp_url[pos], url); pos += strlen(url); } tmp_url[pos] = 0; url = tmp_url; } NETLIBHTTPREQUEST req = {0}; req.cbSize = sizeof(req); req.requestType = REQUEST_GET; req.szUrl = url; req.flags = NLHRF_NODUMP | NLHRF_HTTP11; req.nlc = hNetlibHttp; if (CallService(MS_SYSTEM_GETVERSION, 0, 0) >= PLUGIN_MAKE_VERSION(0,9,0,5)) req.flags |= NLHRF_PERSISTENT | NLHRF_REDIRECT; NETLIBHTTPREQUEST *resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUser, (LPARAM)&req); if (resp) { hNetlibHttp = resp->nlc; if (resp->resultCode == 200) { if (ext && *ext && _stricmp(ext, ".zip") == 0) { if (!options.no_unzip) unzip_mem(resp->pData, resp->dataLength, temp_folder); if (options.save_zips) { TCHAR save_archive[MAX_PATH]; mir_sntprintf(save_archive, SIZEOF(save_archive), _T("%s%s"), options.zip_folder, _tcsrchr(save_file, '\\')); HANDLE hSaveFile = CreateFile(save_archive, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (hSaveFile != INVALID_HANDLE_VALUE) { unsigned long bytes_written; WriteFile(hSaveFile, resp->pData, resp->dataLength, &bytes_written, NULL); CloseHandle(hSaveFile); } else NLogF("GetFile: error creating file, code %u", GetLastError()); } if(dlls_only) { NLog("Deleting non-dlls"); DeleteNonDlls(temp_folder); } } else { HANDLE hSaveFile = CreateFile(save_file, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (hSaveFile != INVALID_HANDLE_VALUE) { unsigned long bytes_written; WriteFile(hSaveFile, resp->pData, resp->dataLength, &bytes_written, NULL); CloseHandle(hSaveFile); } else NLogF("GetFile: error creating file, code %u", GetLastError()); } CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); return true; } else if (resp->resultCode >= 300 && resp->resultCode < 400) { // get new location bool ret = false; for (int i = 0; i < resp->headersCount; i++) { if (_stricmp(resp->headers[i].szName, "Location") == 0) { ret = GetFile(resp->headers[i].szValue, temp_folder, plugin_name, version, dlls_only, recurse_count + 1); break; } } CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); return ret; } else { TCHAR buff[1024], *tmp; mir_sntprintf(buff, SIZEOF(buff), TranslateT("Failed to download \"%s\" - Invalid response, code %d"), (tmp = mir_a2t(plugin_name)), resp->resultCode); ShowError(buff); mir_free(tmp); } CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); } else { hNetlibHttp = NULL; int err = GetLastError(); if (err && !Miranda_Terminated()) { TCHAR buff[1024], *tmp; int len = mir_sntprintf(buff, SIZEOF(buff), TranslateT("Failed to download \"%s\": "), (tmp = mir_a2t(plugin_name))); FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, buff + len, 512 - len, 0); ShowError(buff); mir_free(tmp); } } return false; } char *CheckVersionURL(char *url, BYTE *pbPrefixBytes, int cpbPrefixBytes, BYTE *pbVersionBytes, int cpbVersionBytes) { if (url == 0 || pbPrefixBytes == 0 || cpbPrefixBytes == 0 || pbVersionBytes == 0 || cpbVersionBytes == 0) return 0; char *ret = NULL; NETLIBHTTPREQUEST req = {0}; req.cbSize = sizeof(req); req.requestType = REQUEST_GET; req.szUrl = url; req.flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11; req.nlc = hNetlibHttp; if (CallService(MS_SYSTEM_GETVERSION, 0, 0) >= PLUGIN_MAKE_VERSION(0,9,0,5)) req.flags |= NLHRF_PERSISTENT | NLHRF_REDIRECT; NETLIBHTTPREQUEST *resp = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUser, (LPARAM)&req); if (resp) { hNetlibHttp = resp->nlc; if (resp->resultCode == 200) { // find the location of the prefix char* ver = (char*)memmem(resp->pData, resp->dataLength, pbPrefixBytes, cpbPrefixBytes); if (ver) { int len = resp->dataLength - ((ver += cpbPrefixBytes) - resp->pData); if (cpbVersionBytes <= len && memcmp(ver, pbVersionBytes, cpbVersionBytes) == 0) { // same version as current CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); return 0; } else { DWORD ver_current, ver_potential; char *buff = (char *)alloca(cpbVersionBytes + 1); memcpy(buff, (char *)pbVersionBytes, cpbVersionBytes); buff[cpbVersionBytes] = 0; // this is safe because pData finishes with a zero always (according to m_netlib.h docs) if (VersionFromString(buff, &ver_current) && VersionFromString(ver, &ver_potential)) { switch(options.ver_req) { case VR_MAJOR: ver_current &= 0xFF000000; ver_potential &= 0xFF000000; break; case VR_MINOR: ver_current &= 0xFFFF0000; ver_potential &= 0xFFFF0000; break; case VR_RELEASE: ver_current &= 0xFFFFFF00; ver_potential &= 0xFFFFFF00; break; case VR_BUILD: break; } // we can covert the versions to DWORDS, so compare... if (ver_current < ver_potential) { char buff2[16]; CreateVersionString(ver_potential, buff2); ret = _strdup(buff2); } } else // invalid version(s), but different from current - assume it's an update ret = _strdup(Translate("Yes")); } } } else if (resp->resultCode == 302) // redirect { // get new location for (int i = 0; i < resp->headersCount; i++) { if(_stricmp(resp->headers[i].szName, "Location") == 0) { ret = CheckVersionURL(resp->headers[i].szValue, pbPrefixBytes, cpbPrefixBytes, pbVersionBytes, cpbVersionBytes); break; } } } else NLogF("CheckVersionURL: error, http result code %d", resp->resultCode); CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); } else { hNetlibHttp = NULL; int err = GetLastError(); if (err) NLogF("CheckVersionURL: error code %d", err); } return ret; } char *UpdateRequired(UpdateInternal &update_internal, bool *beta) { // determine whether update is required char *ret = NULL, *ret_beta = NULL; if (options.use_xml_backend && update_internal.file_id != -1) { const char * res; if (update_internal.cat == MC_UNKNOWN) { if (XMLDataAvailable(MC_PLUGINS) && (res = FindVersion(update_internal.file_id, update_internal.update.pbVersion, update_internal.update.cpbVersion, MC_PLUGINS))) { update_internal.cat = MC_PLUGINS; if (strcmp(res, "same")) ret = _strdup(res); } else if (XMLDataAvailable(MC_LOCALIZATION) && (res = FindVersion(update_internal.file_id, update_internal.update.pbVersion, update_internal.update.cpbVersion, MC_LOCALIZATION))) { update_internal.cat = MC_LOCALIZATION; if (strcmp(res, "same")) ret = _strdup(res); } } else { res = FindVersion(update_internal.file_id, update_internal.update.pbVersion, update_internal.update.cpbVersion, update_internal.cat); if (res && strcmp(res, "same")) ret = _strdup(res); } } else { ret = CheckVersionURL(update_internal.update.szVersionURL, update_internal.update.pbVersionPrefix, update_internal.update.cpbVersionPrefix, update_internal.update.pbVersion, update_internal.update.cpbVersion); } if (update_internal.update_options.use_beta) { ret_beta = CheckVersionURL(update_internal.update.szBetaVersionURL, update_internal.update.pbBetaVersionPrefix, update_internal.update.cpbBetaVersionPrefix, update_internal.update.pbVersion, update_internal.update.cpbVersion); } if (ret && !ret_beta) { if(beta) *beta = false; return ret; } else if (!ret && ret_beta) { if (beta) *beta = true; return ret_beta; } else if (ret && ret_beta) { // find highest version of ret and ret_beta DWORD vRet, vRetBeta; VersionFromString(ret, &vRet); VersionFromString(ret_beta, &vRetBeta); if (vRetBeta > vRet) { free(ret); if (beta) *beta = true; return ret_beta; } else { free(ret_beta); if (beta) *beta = false; return ret; } } return 0; }