diff options
author | sje <sje@4f64403b-2f21-0410-a795-97e2b3489a10> | 2006-11-01 14:48:34 +0000 |
---|---|---|
committer | sje <sje@4f64403b-2f21-0410-a795-97e2b3489a10> | 2006-11-01 14:48:34 +0000 |
commit | d123e0ce94bf90b2adb0a4000930eb467e293226 (patch) | |
tree | d414dea59908105c4d2f256199a610e0a69c8690 /updater/xmldata.cpp | |
parent | a13e82647294da4add976a24335fec50d7bfe905 (diff) |
git-svn-id: https://server.scottellis.com.au/svn/mim_plugs@16 4f64403b-2f21-0410-a795-97e2b3489a10
Diffstat (limited to 'updater/xmldata.cpp')
-rw-r--r-- | updater/xmldata.cpp | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/updater/xmldata.cpp b/updater/xmldata.cpp new file mode 100644 index 0000000..132fd7a --- /dev/null +++ b/updater/xmldata.cpp @@ -0,0 +1,512 @@ +#include "common.h"
+#include "xmldata.h"
+
+const char *category_files[] = { "category_plugins", "category_localisation" };
+
+BYTE *pData[NUM_CATEGORIES] = {0};
+int dataLength[NUM_CATEGORIES] = {0};
+TiXmlDocument *doc[NUM_CATEGORIES] = {0};
+
+bool XMLDataAvailable(const Category cat) {
+ return (pData[cat] && dataLength[cat]);
+}
+
+void FreeXMLData(const Category cat) {
+ if(pData[cat]) {
+ free(pData[cat]);
+ pData[cat] = 0;
+ }
+ if(doc[cat]) {
+ delete doc[cat];
+ doc[cat] = 0;
+ }
+ dataLength[cat] = 0;
+}
+
+bool OldXMLDataExists(const Category cat) {
+ TCHAR xml_data_filename[MAX_PATH];
+ TCHAR *ts;
+ _tcscpy(xml_data_filename, options.data_folder);
+ _tcscat(xml_data_filename, _T("\\"));
+ _tcscat(xml_data_filename, ts = GetTString(category_files[cat]));
+ _tcscat(xml_data_filename, _T(".xml"));
+ free(ts);
+
+ HANDLE hDataFile = CreateFile(xml_data_filename, 0, 0, 0, OPEN_EXISTING, 0, 0);
+ if(hDataFile != INVALID_HANDLE_VALUE) {
+ CloseHandle(hDataFile);
+ return true;
+ }
+
+ return false;
+}
+
+// return age of file in hours
+const ULARGE_INTEGER mult = { 600000000, 0}; // number of 100 microsecond blocks in a minute
+long OldXMLDataAge(const Category cat) {
+ TCHAR xml_data_filename[MAX_PATH];
+ TCHAR *ts;
+ _tcscpy(xml_data_filename, options.data_folder);
+ _tcscat(xml_data_filename, _T("\\"));
+ _tcscat(xml_data_filename, ts = GetTString(category_files[cat]));
+ _tcscat(xml_data_filename, _T(".xml"));
+ free(ts);
+
+ HANDLE hDataFile = CreateFile(xml_data_filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+ if(hDataFile != INVALID_HANDLE_VALUE) {
+ FILETIME ft_then, ft_now;
+ GetFileTime(hDataFile, 0, 0, &ft_then);
+ CloseHandle(hDataFile);
+
+ SYSTEMTIME now;
+ GetSystemTime(&now);
+ SystemTimeToFileTime(&now, &ft_now);
+
+ ULARGE_INTEGER uli_now, uli_then, diff;
+ uli_now.HighPart = ft_now.dwHighDateTime;
+ uli_now.LowPart = ft_now.dwLowDateTime;
+ uli_then.HighPart = ft_then.dwHighDateTime;
+ uli_then.LowPart = ft_then.dwLowDateTime;
+
+ diff.QuadPart = uli_now.QuadPart - uli_then.QuadPart;
+
+ long minutes = (long)(diff.QuadPart / mult.QuadPart); // rounded down
+
+ // convert to hours (add 30 so we round up properly)
+ return (minutes + 30) / 60;
+ }
+
+ return -1;
+}
+
+bool LoadOldXMLData(const Category cat, bool update_age) {
+ TCHAR xml_data_filename[MAX_PATH];
+ TCHAR *ts;
+ _tcscpy(xml_data_filename, options.data_folder);
+ _tcscat(xml_data_filename, _T("\\"));
+ _tcscat(xml_data_filename, ts = GetTString(category_files[cat]));
+ _tcscat(xml_data_filename, _T(".xml"));
+ free(ts);
+
+ if(pData[cat]) free(pData[cat]);
+ pData[cat] = 0;
+ dataLength[cat] = 0;
+
+ // load
+ HANDLE hDataFile = CreateFile(xml_data_filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+ if(hDataFile != INVALID_HANDLE_VALUE) {
+ dataLength[cat] = GetFileSize(hDataFile, 0);
+ if(dataLength[cat]) {
+ unsigned long bytes_read;
+ pData[cat] = (BYTE *)malloc(dataLength[cat]);
+ if(ReadFile(hDataFile, pData[cat], dataLength[cat], &bytes_read, 0))
+ dataLength[cat] = bytes_read;
+ else {
+ free(pData[cat]);
+ pData[cat] = 0;
+ dataLength[cat] = 0;
+ }
+
+ }
+
+ if(update_age) {
+ FILETIME ft_now;
+ SYSTEMTIME now;
+ GetSystemTime(&now);
+ SystemTimeToFileTime(&now, &ft_now);
+ SetFileTime(hDataFile, 0, 0, &ft_now);
+ }
+
+ CloseHandle(hDataFile);
+ }
+
+ if(pData[cat] && dataLength[cat]) {
+ doc[cat] = new TiXmlDocument;
+ doc[cat]->Parse((char *)pData[cat], 0, TIXML_DEFAULT_ENCODING);
+ if(doc[cat]->Error()) {
+ FreeXMLData(cat);
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SaveXMLData(const Category cat) {
+ TCHAR xml_data_filename[MAX_PATH];
+ TCHAR *ts;
+ _tcscpy(xml_data_filename, options.data_folder);
+ _tcscat(xml_data_filename, _T("\\"));
+ _tcscat(xml_data_filename, ts = GetTString(category_files[cat]));
+ _tcscat(xml_data_filename, _T(".xml"));
+ free(ts);
+
+ if(!CreatePath(options.data_folder)) {
+ return false;
+ }
+
+ // save data
+ if(pData[cat] && dataLength[cat]) {
+ HANDLE hDataFile = CreateFile(xml_data_filename, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
+ if(hDataFile != INVALID_HANDLE_VALUE) {
+ unsigned long bytes_written;
+ WriteFile(hDataFile, pData[cat], dataLength[cat], &bytes_written, 0);
+ CloseHandle(hDataFile);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool bz2_decompress_xml(char *in_data, int in_data_length, BYTE **pDat, int *data_length) {
+ const int BLOCKSIZE = 1024 * 100;
+
+ bz_stream bzs = {0};
+
+ switch(BZ2_bzDecompressInit(&bzs, 0, 0)) {
+ case BZ_CONFIG_ERROR:
+ //MessageBox(0, "Configuration Error", "BZ2 Decompres Init", MB_OK | MB_ICONERROR);
+ ShowError(TranslateT("BZ2 Decompression, configuration error"));
+ return false;
+ case BZ_PARAM_ERROR:
+ //MessageBox(0, "Parameters Error", "BZ2 Decompres Init", MB_OK | MB_ICONERROR);
+ ShowError(TranslateT("BZ2 Decompression, parameter error"));
+ return false;
+ case BZ_MEM_ERROR:
+ //MessageBox(0, "Memory Error", "BZ2 Decompres Init", MB_OK | MB_ICONERROR);
+ ShowError(TranslateT("DB2 Decompression, memory error"));
+ return false;
+ }
+
+ bzs.avail_in = in_data_length;
+ bzs.next_in = in_data;
+
+ bzs.avail_out = BLOCKSIZE;
+ *pDat = (BYTE *)malloc(bzs.avail_out + 1); // allocate 100k (at present, xml data is about 87k) (1 byte extra for a terminating 0 for safety)
+ bzs.next_out = (char *)*pDat;
+
+ int blocknum = 0;
+ int ret;
+ while((ret = BZ2_bzDecompress(&bzs)) == BZ_OK && bzs.avail_in > 0) {
+ if(bzs.avail_out == 0) {
+ blocknum++;
+ *pDat = (BYTE *)realloc(*pDat, (blocknum + 1) * BLOCKSIZE + 1);
+ bzs.next_out = (char *)(*pDat + (blocknum * BLOCKSIZE));
+ bzs.avail_out = BLOCKSIZE;
+ }
+ }
+
+ BZ2_bzDecompressEnd(&bzs);
+
+ if(ret != BZ_STREAM_END) {
+// char msg[512];
+// sprintf(msg, "Error decompressing, code: %d", ret);
+// MessageBox(0, msg, "Error Decompressing BZ2 XML data", MB_OK);
+ free(*pDat);
+ *pDat = 0;
+ *data_length = 0;
+ return false;
+ }
+
+ *data_length = bzs.total_out_lo32; // assume it's not too massive!
+ (*pDat)[*data_length] = 0; // for safety - last char shouldn't matter to us
+
+ //char msg[256];
+ //sprintf(msg, "Bytes decompressed: %d", data_length);
+ //MessageBox(0, msg, "msg", MB_OK);
+
+ return true;
+}
+
+bool UpdateXMLData(const Category cat, const char *redirect_url /*= 0*/, int recurse_count /*=0*/) {
+
+ if(recurse_count > MAX_REDIRECT_RECURSE) {
+ PUShowMessage(Translate("Updater: Error getting data - too many redirects"), SM_WARNING);
+ return false;
+ }
+
+ NETLIBHTTPREQUEST req = {0};
+ NETLIBHTTPHEADER etag_hdr = {0};
+
+ if(OldXMLDataExists(cat)) {
+ // ensure backend not checked more than once every MIN_XMLDATA_AGE hours
+ long age = OldXMLDataAge(cat);
+ if(age >= 0 && age < MIN_XMLDATA_AGE) { // get it only if our file is at least 8 hours old
+#ifdef DEBUG_HTTP_POPUPS
+ char buff[512];
+ sprintf(buff, "XML Data is recent (%d hours old) - not downloading, using local copy", age);
+ PUShowMessage(buff, SM_NOTIFY);
+#endif
+
+ return LoadOldXMLData(cat, false);
+ }
+
+ // add ETag header for conditional get
+ DBCONTACTGETSETTING cgs;
+ DBVARIANT dbv;
+ cgs.szModule = MODULE;
+ char buff[256];
+ strcpy(buff, "DataETag_");
+ strcat(buff, category_files[cat]);
+ cgs.szSetting = buff;
+ cgs.pValue = &dbv;
+ if(!CallService(MS_DB_CONTACT_GETSETTING, 0, (LPARAM)&cgs)) {
+ req.headersCount = 1;
+ req.headers = &etag_hdr;
+ etag_hdr.szName = "If-None-Match";
+ etag_hdr.szValue = _strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ req.cbSize = sizeof(req);
+ req.requestType = REQUEST_GET;
+ char URL[MAX_PATH];
+ if(!redirect_url) {
+ strcpy(URL, MIM_BACKEND_URL_PREFIX);
+ strcat(URL, category_files[cat]);
+ strcat(URL, ".bz2");
+ } else {
+ strcpy(URL, redirect_url);
+ }
+ req.szUrl = URL;
+ //req.flags = NLHRF_DUMPASTEXT; //NLHRF_SMARTREMOVEHOST | NLHRF_SMARTAUTHHEADER;
+
+ NETLIBHTTPREQUEST *resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUser, (LPARAM)&req);
+ if(etag_hdr.szValue) free(etag_hdr.szValue);
+
+ if(!resp) {
+ int err = GetLastError();
+ if(err) {
+ TCHAR buff[512];
+ _stprintf(buff, TranslateT("Failed to download XML data: "));
+ int len = _tcslen(buff);
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, buff + len, 512 - len, 0);
+ ShowError(buff);
+ //MessageBox(0, buff + len, Translate("Updater: Error Downloading XML Data"), MB_OK | MB_ICONWARNING);
+ char *ts = GetAString(buff);
+ NLog(ts);
+ free(ts);
+ } else {
+ ShowError(TranslateT("Failed to download XML data - Response is NULL"));
+ //MessageBox(0, "Error downloading XML data...\nResponse is NULL"), Translate("Updater Error"), MB_OK | MB_ICONWARNING);
+ NLog("Failed to download XML data - Response is NULL");
+ }
+ return LoadOldXMLData(cat, false);
+
+ } else if(resp->resultCode == 304) { // 'Not Modified' response
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ resp = 0;
+#ifdef DEBUG_HTTP_POPUPS
+ PUShowMessage("XML Data unchanged - using local copy", SM_NOTIFY);
+#endif
+ // mark data as current
+ return LoadOldXMLData(cat, true);
+ } else if(resp->resultCode >= 300 && resp->resultCode < 400) { // redirect response
+ // get new location
+ bool ret = false;
+ for(int i = 0; i < resp->headersCount; i++) {
+ //MessageBox(0, resp->headers[i].szValue, resp->headers[i].szName, MB_OK);
+ if(strcmp(resp->headers[i].szName, "Location") == 0) {
+ ret = UpdateXMLData(cat, resp->headers[i].szValue, recurse_count + 1);
+ break;
+ }
+ }
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ resp = 0;
+ if(!ret) return LoadOldXMLData(cat, false);
+
+ return ret;
+
+ } else if(resp->resultCode != 200) {
+ TCHAR buff[512];
+ _stprintf(buff, TranslateT("Failed to download XML data - Invalid response, code %d"), resp->resultCode);
+ ShowError(buff);
+
+ char *ts = GetAString(buff);
+ NLog(ts);
+ free(ts);
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ resp = 0;
+ return LoadOldXMLData(cat, false);
+ }
+
+ // resp->resultCode == 200
+
+ if(!bz2_decompress_xml(resp->pData, resp->dataLength, &pData[cat], &dataLength[cat])) {
+ ShowError(TranslateT("Failed to decompress XML data"));
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ resp = 0;
+ return LoadOldXMLData(cat, false);
+ }
+
+ // store date header and data for response 'Not Modified' (304) above
+ for(int i = 0; i < resp->headersCount; i++) {
+ //MessageBox(0, resp->headers[i].szValue, resp->headers[i].szName, MB_OK);
+ if(strcmp(resp->headers[i].szName, "ETag") == 0) {
+ //MessageBox(0, resp->headers[i].szValue, "Storing ETag", MB_OK);
+ char buff[256];
+ strcpy(buff, "DataETag_");
+ strcat(buff, category_files[cat]);
+ DBWriteContactSettingString(0, MODULE, buff, resp->headers[i].szValue);
+ }
+ }
+
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ resp = 0;
+
+ doc[cat] = new TiXmlDocument;
+ doc[cat]->Parse((char *)pData[cat], 0, TIXML_DEFAULT_ENCODING);
+ if(doc[cat]->Error()) {
+ FreeXMLData(cat);
+ return false;
+ }
+
+ SaveXMLData(cat);
+
+ return true;
+}
+
+bool GetXMLData(BYTE **pDat, int &dataLen, const Category cat) {
+ if(pData[cat] && dataLength[cat]) {
+ *pDat = pData[cat];
+ dataLen = dataLength[cat];
+ return true;
+ } else
+ return false;
+}
+
+bool VersionLess(char *current, char *potential) {
+ DWORD dwCur, dwPot;
+ if(VersionFromString(current, &dwCur) && VersionFromString(potential, &dwPot)) {
+ switch(options.ver_req) {
+ case VR_MAJOR:
+ dwCur &= 0xFF000000;
+ dwPot &= 0xFF000000;
+ break;
+ case VR_MINOR:
+ dwCur &= 0xFFFF0000;
+ dwPot &= 0xFFFF0000;
+ break;
+ case VR_RELEASE:
+ dwCur &= 0xFFFFFF00;
+ dwPot &= 0xFFFFFF00;
+ break;
+ case VR_BUILD:
+ break;
+ }
+ return dwCur < dwPot;
+ }
+ return false;
+}
+
+char *FindVersion(int file_id, BYTE *pbVersionBytes, int cpbVersionBytes, const Category cat) {
+
+ if(!doc[cat]) return 0;
+
+ char version_string[128];
+ strncpy(version_string, (char *)pbVersionBytes, cpbVersionBytes);
+ version_string[cpbVersionBytes] = 0;
+
+ char *version;
+ int id = -1;
+ TiXmlElement *root = doc[cat]->RootElement();
+ for(TiXmlElement *el = root->FirstChildElement()->FirstChildElement(); el; el = el->NextSiblingElement()) {
+ if(strcmp(el->Value(), "item") == 0) {
+ version = 0;
+ for(TiXmlElement *el2 = el->FirstChildElement()->NextSiblingElement(); el2; el2 = el2->NextSiblingElement()) {
+ if(strcmp(el2->Value(), "id") == 0) {
+ id = atoi(el2->FirstChild()->Value());
+ }
+ if(strcmp(el2->Value(), "version") == 0) {
+ version = _strdup(el2->FirstChild()->Value());
+ }
+ }
+ if(id == file_id && version) {
+ if(strncmp(version, (char *)pbVersionBytes, cpbVersionBytes) == 0) {
+ free(version);
+ return _strdup("same");
+ } else {
+ if(VersionLess(version_string, version)) {
+ return version;
+ } else {
+ free(version);
+ return _strdup("same");
+ }
+ }
+ } else
+ if(version) free(version);
+ }
+ }
+
+ return 0;
+}
+
+int FindFileID(const char *name, const Category cat, UpdateList *update_list) {
+
+ if(!doc[cat]) return -1;
+
+ // ignore case in name
+ char *lowname = _strdup(name); strlwr(lowname);
+
+ const char *fl_name;
+ char *namelwr;
+ int id = -1;
+
+ TiXmlElement *root = doc[cat]->RootElement();
+ for(TiXmlElement *el = root->FirstChildElement("channel")->FirstChildElement("item"); el; el = el->NextSiblingElement("item")) {
+ if(strcmp(el->Value(), "item") == 0) {
+ fl_name = el->FirstChildElement("title")->FirstChild()->Value();
+ namelwr = _strdup(fl_name); strlwr(namelwr);
+ if(strcmp(lowname, namelwr) == 0) {
+ for(TiXmlElement *el2 = el->FirstChildElement()->NextSiblingElement(); el2; el2 = el2->NextSiblingElement()) {
+ if(strcmp(el2->Value(), "id") == 0) {
+ id = atoi(el2->FirstChild()->Value());
+ } else if(strcmp(el2->Value(), "subcategory") == 0 && strcmp(el2->FirstChild()->Value(), "Archived") == 0) {
+ id = -1; // this is an archived release with the same name - skip
+ break;
+ }
+ }
+ }
+ free(namelwr);
+ if(id != -1) break; // we found a non-archived release
+ }
+ }
+
+ free(lowname);
+
+ if(id == -1 && update_list) {
+ // couldn't find it in xml file - check if a plugin gave us a file id for a different shortName
+ for(update_list->reset(); update_list->current(); update_list->next()) {
+ if(update_list->current()->file_id != -1 && strcmp(update_list->current()->update.szComponentName, name) == 0) {
+ id = update_list->current()->file_id;
+ break;
+ }
+ }
+ }
+
+ return id;
+}
+
+void UpdateFLIDs(UpdateList &update_list) {
+ for(update_list.reset(); update_list.current(); update_list.next()) {
+ if(update_list.current()->file_id == -1 && update_list.current()->update.szUpdateURL && strcmp(update_list.current()->update.szUpdateURL, UPDATER_AUTOREGISTER) == 0) {
+ int file_id = FindFileID(update_list.current()->update.szComponentName, MC_PLUGINS, 0);
+ if(file_id == -1)
+ file_id = FindFileID(update_list.current()->update.szComponentName, MC_LOCALIZATION, 0);
+ if(file_id != -1) {
+ update_list.current()->file_id = file_id;
+ char *buff = (char *)safe_alloc(strlen(MIM_DOWNLOAD_URL_PREFIX) + 9);
+ sprintf(buff, MIM_DOWNLOAD_URL_PREFIX "%d", file_id);
+ update_list.current()->update.szUpdateURL = buff;
+ update_list.current()->shortName = safe_strdup(update_list.current()->update.szComponentName);
+
+ if(update_list.current()->update.szBetaVersionURL) {
+ update_list.current()->update_options.fixed = false;
+ LoadUpdateOptions(update_list.current()->update.szComponentName, &update_list.current()->update_options);
+ }
+ }
+ }
+ }
+}
|