summaryrefslogtreecommitdiff
path: root/Help/update.c
diff options
context:
space:
mode:
authormataes2007 <mataes2007@e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb>2011-11-23 18:16:39 +0000
committermataes2007 <mataes2007@e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb>2011-11-23 18:16:39 +0000
commit59e53cb6cad99051eb3e46b919fd78ab7493061d (patch)
treece1aad2ae099649dc8a07c219c83a829545b313f /Help/update.c
parent72c858d136d42fccd422620542b9c73808f16ae8 (diff)
added Help plugin
git-svn-id: http://miranda-plugins.googlecode.com/svn/trunk@201 e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb
Diffstat (limited to 'Help/update.c')
-rw-r--r--Help/update.c1082
1 files changed, 1082 insertions, 0 deletions
diff --git a/Help/update.c b/Help/update.c
new file mode 100644
index 0000000..4dafe61
--- /dev/null
+++ b/Help/update.c
@@ -0,0 +1,1082 @@
+/*
+Miranda IM Help Plugin
+Copyright (C) 2002 Richard Hughes, 2005-2007 H. Herkenrath
+
+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; either version 2
+of the License, or (at your option) any later version.
+
+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 (Help-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <tchar.h>
+#include <stdio.h>
+#define _WIN32_WINNT 0x0500
+#define __RPCASYNC_H__ /* header shows warnings in VS6 */
+#include <windows.h>
+#include <win2k.h>
+#define MIRANDA_VER 0x0600
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_database.h>
+#include <m_utils.h>
+#include <m_langpack.h>
+#include <m_netlib.h>
+#include <m_skin.h>
+#include <m_icolib.h>
+#include <m_clist.h>
+#include "m_help.h"
+#include "help.h"
+#include "unzip.h"
+#include "resource.h"
+#include "version.h"
+
+/* Lang Dlg */
+static HANDLE hServiceShowLangDialog;
+/* Misc */
+extern HINSTANCE hInst;
+static HANDLE hHookModulesLoaded,hHookPreShutdown;
+static HANDLE hNetlibUser;
+
+/************************* Download Update ****************************/
+
+static BOOL FillUserAgent(char *pszUserAgent,int nSize)
+{
+ char *pspace,szMirandaVer[32];
+ if(!CallService(MS_SYSTEM_GETVERSIONTEXT,SIZEOF(szMirandaVer),(LPARAM)szMirandaVer)) {
+ pspace=strchr(szMirandaVer,' ');
+ if(pspace) {
+ *pspace++='\0';
+ mir_snprintf(pszUserAgent,nSize,"Miranda/%s (%s) HelpPlugin/%s",szMirandaVer,pspace,USERAGENT_VERSION);
+ }
+ else mir_snprintf(pszUserAgent,nSize,"Miranda/%s HelpPlugin/%s",szMirandaVer,USERAGENT_VERSION);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void EnsurePackHeader(HANDLE hFile,const char *pszFileVersionHeader,const char *pszTitle,const char *pszVersion)
+{
+ DWORD dataSize,read;
+ BYTE *pData,*pBuf;
+ int headerLength,infoLength;
+
+ /* file size */
+ dataSize=GetFileSize(hFile,NULL);
+ if(dataSize==INVALID_FILE_SIZE) return;
+ headerLength=lstrlenA(pszFileVersionHeader);
+ pData=(BYTE*)mir_alloc(dataSize);
+ infoLength=lstrlenA(pszTitle)+lstrlenA(pszVersion)+27;
+ pBuf=(BYTE*)mir_alloc(infoLength);
+ if(pData!=NULL && pBuf!=NULL) {
+ /* compare header */
+ Netlib_Logf(hNetlibUser,"Ensure pack header");
+ SetFilePointer(hFile,0,NULL,FILE_BEGIN);
+ read=0;
+ if(ReadFile(hFile,pData,headerLength,&read,NULL)) {
+ if(!strncmp(pszFileVersionHeader,(char*)pData,read)) {
+ /* read all data */
+ read=0;
+ if(ReadFile(hFile,pData,dataSize-headerLength,&read,NULL)) {
+ dataSize=read;
+ /* prepend header */
+ SetFilePointer(hFile,headerLength,NULL,FILE_BEGIN);
+ wsprintfA((char*)pBuf,"\r\nX-FLName: %s\r\nX-Version: %s",pszTitle,pszVersion); /* buffer safe */
+ WriteFile(hFile,pBuf,infoLength-1,&read,NULL);
+ /* write it back */
+ if(!WriteFile(hFile,pData,dataSize,&read,NULL))
+ Netlib_Logf(hNetlibUser,"Write pack failed (%u)",GetLastError());
+ } else Netlib_Logf(hNetlibUser,"Read pack failed (%u)",GetLastError());
+ } else Netlib_Logf(hNetlibUser,"Invalid pack file format");
+ } else Netlib_Logf(hNetlibUser,"Read pack failed (%u)",GetLastError());
+ }
+ mir_free(pData); /* does NULL check */
+ mir_free(pBuf); /* does NULL check */
+}
+
+static void CleanupFileName(TCHAR *pszFileName,const char *pszVersion)
+{
+ TCHAR *p,*p2;
+ int len;
+ TCHAR szVersion[16];
+#if defined(_UNICODE)
+ szVersion[0]=_T('\0');
+ MultiByteToWideChar(CP_ACP,0,pszVersion,-1,szVersion,SIZEOF(szVersion));
+#else
+ lstrcpyn(szVersion,pszVersion,SIZEOF(szVersion));
+#endif
+ /* disallow version indicator in filename */
+ p=_tcsstr(pszFileName,szVersion);
+ if(p!=NULL) {
+ len=lstrlen(szVersion);
+ p2=CharPrev(pszFileName,p);
+ if(*p2=='v') p2=CharPrev(pszFileName,p2);
+ if(p2==pszFileName || (*p2!='-' && *p2!='_')) p2=p;
+ MoveMemory(p2,p+len,(lstrlen(p+len)+1)*sizeof(TCHAR));
+ }
+ /* lower case */
+ CharLower(pszFileName);
+ /* replace any '-' by '_' */
+ for(p=pszFileName;*p!='\0';p=CharNext(p)) {
+ if(*p=='-') *p='_';
+ else if(*p==' ') *p='_';
+ }
+ /* disallow digits */
+ for(p=pszFileName;*p!='\0';p=CharNext(p))
+ if(isdigit(*p)) {
+ p2=CharPrev(pszFileName,p);
+ if(*p2=='-' || *p2=='_')
+ p=p2;
+ *p='.'; /* handle together with dots */
+ break;
+ }
+ /* disallow any dots except extension */
+ p=_tcschr(pszFileName,'.');
+ p2=_tcsrchr(pszFileName,'.');
+ if(p!=NULL && p2!=NULL)
+ MoveMemory(p,p2,(lstrlen(p2)+1)*sizeof(TCHAR));
+}
+
+// pszFileName is allowed to be NULL, uses obtained file name in this case
+static BOOL DownloadPack(WORD id,const TCHAR *pszFilePattern,BOOL fEnabledPacks,const TCHAR *pszFileName,const char *pszFileVersionHeader,const char *pszTitle,const char *pszVersion)
+{
+ TCHAR *pszFilePatternPrefix,*pszFilePatternExt;
+ NETLIBHTTPREQUEST req,*resp;
+ NETLIBHTTPHEADER headers[1];
+ char szUrl[64],szUserAgent[128];
+ BOOL fSuccess=FALSE;
+ TCHAR *pszContentFileName,*pszExt;
+ TCHAR szFileName[MAX_PATH];
+ HANDLE hFile;
+ DWORD written;
+ int i;
+#if defined(_UNICODE)
+ TCHAR szContentDisp[MAX_PATH];
+#endif
+
+ /* split file pattern */
+ pszFilePatternPrefix=lstrcpy((TCHAR*)_alloca(lstrlen(pszFilePattern)+1),pszFilePattern);
+ pszFilePatternExt=_tcschr(pszFilePatternPrefix,_T('*')); /* can't fail, static input */
+ *(pszFilePatternExt++)='\0';
+
+ ZeroMemory(&req,sizeof(req));
+ req.cbSize=sizeof(req);
+ req.requestType=REQUEST_GET;
+ mir_snprintf(szUrl,sizeof(szUrl),"http://addons.miranda-im.org/feed.php?dlfile=%u",id);
+ req.szUrl=szUrl;
+ req.flags=NLHRF_HTTP11|NLHRF_NODUMP;
+ headers[0].szName="User-Agent";
+ headers[0].szValue=szUserAgent;
+ if(FillUserAgent(szUserAgent,SIZEOF(szUserAgent)))
+ req.headersCount=1;
+ req.headers=headers;
+
+ Netlib_Logf(hNetlibUser,"Download pack: %s (ver:%s)",szUrl,pszVersion);
+ resp=(NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION,(WPARAM)hNetlibUser,(LPARAM)&req);
+ if(resp!=NULL) {
+ /* get content filename */
+ pszContentFileName=pszExt=NULL;
+ for(i=0;i<resp->headersCount;++i)
+ if(!lstrcmpA(resp->headers[i].szName,"Content-Disposition")) {
+#if defined(_UNICODE)
+ szContentDisp[0]=_T('\0');
+ MultiByteToWideChar(CP_ACP,0,resp->headers[i].szValue,-1,szContentDisp,SIZEOF(szContentDisp));
+ pszContentFileName=_tcsrchr(szContentDisp,_T('='));
+ pszExt=_tcsrchr(szContentDisp,_T('.'));
+#else
+ pszContentFileName=_tcsrchr(resp->headers[i].szValue,_T('='));
+ pszExt=_tcsrchr(resp->headers[i].szValue,_T('.'));
+#endif
+ break;
+ }
+ /* valid result? */
+ if(resp->resultCode==200 && pszContentFileName!=NULL && pszExt!=NULL && resp->dataLength) {
+ ++pszContentFileName;
+ /* txt-file, save directly */
+ if(!lstrcmpi(pszExt,pszFilePatternExt)) {
+ Netlib_Logf(hNetlibUser,"txt-data ("TCHAR_STR_PARAM")",pszContentFileName);
+ /* proper file name */
+ if(pszFileName!=NULL) pszContentFileName=(TCHAR*)pszFileName;
+ else CleanupFileName(pszContentFileName,pszVersion);
+ /* write file */
+ if(GetPackPath(szFileName,SIZEOF(szFileName),fEnabledPacks,pszContentFileName)) {
+ hFile=CreateFile(szFileName,GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
+ if(hFile!=INVALID_HANDLE_VALUE) {
+ Netlib_Logf(hNetlibUser,"Output: "TCHAR_STR_PARAM,szFileName);
+ fSuccess=WriteFile(hFile,resp->pData,resp->dataLength,&written,NULL);
+ if(fSuccess) EnsurePackHeader(hFile,pszFileVersionHeader,pszTitle,pszVersion);
+ CloseHandle(hFile);
+ /* remove duplicate */
+ if(fEnabledPacks && GetPackPath(szFileName,SIZEOF(szFileName),FALSE,pszContentFileName))
+ DeleteFile(szFileName);
+ }
+ if(!fSuccess) Netlib_Logf(hNetlibUser,"Write failed (%u)",GetLastError());
+ }
+ else Netlib_Logf(hNetlibUser,"Write failed");
+ }
+ /* zip-file, unzip txt-file in it */
+ else {
+ HZIP hz;
+ ZIPENTRY ze;
+ int index=0;
+ BOOL fFound=FALSE;
+ /* open zip */
+ Netlib_Logf(hNetlibUser,"zip-data ("TCHAR_STR_PARAM")",pszContentFileName);
+ hz=OpenZipMem(resp->pData,resp->dataLength,NULL);
+ if(hz!=NULL) {
+ Netlib_Logf(hNetlibUser,"Search "TCHAR_STR_PARAM" in zip",pszFilePattern);
+ /* enum pack file in zip */
+ while(!FindZipItem(hz,pszFilePattern,TRUE,&index,&ze)) {
+ /* skip some confusingly named files */
+ if(_tcsstr(ze.name,_T("readme"))==NULL && _tcsstr(ze.name,_T("history"))==NULL) {
+ fFound=TRUE;
+ Netlib_Logf(hNetlibUser,"Found "TCHAR_STR_PARAM" in zip",ze.name);
+ /* proper file name */
+ if(pszFileName!=NULL) pszContentFileName=(TCHAR*)pszFileName;
+ else CleanupFileName(pszContentFileName=ze.name,pszVersion);
+ /* open file */
+ if(GetPackPath(szFileName,SIZEOF(szFileName),fEnabledPacks,pszContentFileName)) {
+ hFile=CreateFile(szFileName,GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
+ if(hFile!=INVALID_HANDLE_VALUE) {
+ Netlib_Logf(hNetlibUser,"Output: "TCHAR_STR_PARAM,szFileName);
+ /* unzip */
+ fSuccess=!UnzipItemHandle(hz,ze.index,hFile);
+ if(fSuccess) EnsurePackHeader(hFile,pszFileVersionHeader,pszTitle,pszVersion);
+ else Netlib_Logf(hNetlibUser,"Invalid zip-item");
+ CloseHandle(hFile);
+ /* remove duplicate */
+ if(fEnabledPacks && GetPackPath(szFileName,SIZEOF(szFileName),FALSE,pszContentFileName))
+ DeleteFile(szFileName);
+ } else Netlib_Logf(hNetlibUser,"Write failed (%u)",GetLastError());
+ } else Netlib_Logf(hNetlibUser,"Write failed (%u)",GetLastError());
+ }
+ ++index; /* skip current */
+ }
+ CloseZip(hz);
+ if(!fFound) Netlib_Logf(hNetlibUser,"No match found");
+ }
+ else Netlib_Logf(hNetlibUser,"Invalid zip-data");
+ }
+ }
+ else Netlib_Logf(hNetlibUser,"No acceptable response from HTTP request");
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT,0,(LPARAM)resp);
+ }
+ else Netlib_Logf(hNetlibUser,"No response from HTTP request");
+
+ return fSuccess;
+}
+
+/************************* Enum Updates *******************************/
+
+#define CACHETIME 23*60*60 // refresh cache every 23 hours (seconds)
+
+typedef struct {
+ char *pszTitle;
+ WORD id;
+ char *pszVersion;
+ char *pszSubCategory;
+ FILETIME ftFileDate;
+} UPDATE_INFO;
+typedef BOOL (CALLBACK *ENUM_UPDATES_CALLBACK)(UPDATE_INFO *upd,WORD nRemaining,WPARAM wParam,LPARAM lParam);
+
+// similar to strstr but allowes the searched buffer to contain zeros
+static BYTE* FindStrInBlobI(const BYTE *pData,const BYTE *pLast,const char *pszStr)
+{
+ register BYTE *p;
+ int i;
+ for(p=(BYTE*)pData;p<=pLast;++p) {
+ for(i=0;pszStr[i] && (p+i)<=pLast;++i)
+ if(_tolower(*(p+i))!=_tolower(pszStr[i]))
+ break;
+ if(pszStr[i]=='\0') return (BYTE*)p;
+ }
+ return NULL;
+}
+
+// pszBeginTag is allowed to be NULL
+// returned value points into the buffer, returns an empty string on error
+static char* GetTagValue(const BYTE *pBlock,int blockLength,const char *pszBeginTag,const char *pszEndTag)
+{
+ register BYTE *p,*pLast,*pVal;
+ pLast=(BYTE*)pBlock+blockLength-1;
+ /* begin tag */
+ if(pszBeginTag!=NULL) {
+ pVal=FindStrInBlobI(pBlock,pLast,pszBeginTag);
+ if(pVal==NULL) return ""; /* static */
+ pVal+=lstrlenA(pszBeginTag);
+ } else pVal=(BYTE*)pBlock;
+ /* end tag */
+ p=FindStrInBlobI(pBlock,pLast,pszEndTag);
+ if(p==NULL) return ""; /* static */
+ *p='\0';
+ return (char*)pVal;
+}
+
+// pBlock and blockLength must be set to the previous block before call
+static BOOL GetNextBlock(const BYTE *pData,int dataLength,const char *pszBlockBeginTag,const char *pszBlockEndTag,BYTE **ppBlock,int *blockLength)
+{
+ register BYTE *p,*pLast;
+ pLast=(BYTE*)pData+dataLength-1;
+ /* begin tag */
+ p=FindStrInBlobI((*ppBlock)+(*blockLength),pLast,pszBlockBeginTag);
+ if(p==NULL) return FALSE;
+ *ppBlock=p+lstrlenA(pszBlockBeginTag);
+ /* end tag */
+ p=FindStrInBlobI(p,pLast,pszBlockEndTag);
+ if(p==NULL) return FALSE;
+ *blockLength=p-(*ppBlock);
+ return TRUE;
+}
+
+// ppsz points just after first occurence of chr on return
+static WORD ParseWord(char **ppsz,int chr)
+{
+ register char *p1,*p2;
+ p1=strchr(*ppsz,chr);
+ if(p1==NULL) return 0;
+ *p1='\0';
+ p2=*ppsz;
+ *ppsz=p1+1;
+ return (WORD)atoi(p2);
+}
+
+static void EnumUpdates_Worker(BYTE *pData,int dataLength,BOOL *pbSuccess,BOOL fAllowRefetch,ENUM_UPDATES_CALLBACK callback,WPARAM wParam,LPARAM lParam)
+{
+ UPDATE_INFO upd;
+ WORD nRemaining;
+ SYSTEMTIME st;
+ BYTE *pBlock;
+ int blockLength;
+ char *pszUpdated;
+
+ /* count items */
+ pBlock=pData;
+ blockLength=0;
+ for(nRemaining=0;GetNextBlock(pData,dataLength,"<item>","</item>",&pBlock,&blockLength);++nRemaining);
+ if(fAllowRefetch) Netlib_Logf(hNetlibUser,"Data sets: %u",nRemaining);
+
+ /* parse data */
+ pBlock=pData;
+ blockLength=0;
+ ZeroMemory(&st,sizeof(st)); /* init unused members */
+ for(;;) {
+ if(!GetNextBlock(pData,dataLength,"<item>","</item>",&pBlock,&blockLength))
+ break;
+ --nRemaining;
+ ZeroMemory(&upd,sizeof(upd));
+ upd.pszTitle=GetTagValue(pBlock,blockLength,"<title>","</title>");
+ upd.id=(WORD)atoi(GetTagValue(pBlock,blockLength,"<id>","</id>"));
+ if(!upd.id) continue; /* never happens */
+ upd.pszVersion=GetTagValue(pBlock,blockLength,"<version>","</version>");
+ upd.pszSubCategory=GetTagValue(pBlock,blockLength,"<subcategory>","</subcategory>");
+
+ /* parse time */
+ pszUpdated=GetTagValue(pBlock,blockLength,"<updated>","</updated>");
+ st.wYear=ParseWord(&pszUpdated,'-'); /* time is formated as follows: */
+ st.wMonth=ParseWord(&pszUpdated,'-'); /* yyyy-mm-dd hh:mm:ss */
+ st.wDay=ParseWord(&pszUpdated,' ');
+ st.wHour=ParseWord(&pszUpdated,':');
+ st.wMinute=ParseWord(&pszUpdated,':');
+ st.wSecond=(WORD)atoi(pszUpdated);
+ SystemTimeToFileTime(&st,&upd.ftFileDate);
+
+ /* callback */
+ *pbSuccess=callback(&upd,nRemaining,wParam,lParam);
+ if(!*pbSuccess) break;
+ }
+}
+
+static BOOL EnumUpdates(ENUM_UPDATES_CALLBACK callback,const TCHAR *pszDataFile,BOOL fAllowRefetch,WPARAM wParam,LPARAM lParam)
+{
+ TCHAR szCacheFile[MAX_PATH],*p;
+ HANDLE hFile;
+ BOOL fSuccess=FALSE;
+
+ if(fAllowRefetch) Netlib_Logf(hNetlibUser,"Enumerate data sets");
+
+ /* read from cache */
+ if(GetTempPath(SIZEOF(szCacheFile)-lstrlen(pszDataFile),szCacheFile)) {
+ BYTE *pData;
+ int dataLength;
+ DWORD read,age;
+ FILETIME ftLastWrite,ft;
+ /* ensure last subdirectory exists */
+ CreateDirectory(szCacheFile,NULL);
+ /* file name */
+ p=_tcsrchr(szCacheFile,_T('\\'));
+ if(p!=NULL && *p!=_T('\\')) lstrcat(p,_T("\\")); /* buffer safe */
+ lstrcat(szCacheFile,pszDataFile); /* buffer safe */
+ /* try to open file */
+ hFile=CreateFile(szCacheFile,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
+ if(fAllowRefetch) Netlib_Logf(hNetlibUser,"Try to read from cache ("TCHAR_STR_PARAM")...",szCacheFile);
+ if(hFile!=INVALID_HANDLE_VALUE) {
+ /* analyze file age */
+ /* last-write file time resolution is acceptable
+ * on file systems for our calculation */
+ GetSystemTimeAsFileTime(&ft);
+ if(GetFileTime(hFile,NULL,NULL,&ftLastWrite)) {
+ ft.dwHighDateTime-=ftLastWrite.dwHighDateTime;
+ ft.dwLowDateTime-=ftLastWrite.dwLowDateTime;
+ /* calc elapsed seconds */
+ age=(DWORD)((((ULONGLONG)ft.dwHighDateTime)<<32)/10000000)+(ft.dwLowDateTime/10000000);
+ if(age<CACHETIME) {
+ /* file size */
+ dataLength=GetFileSize(hFile,NULL); /* low size is enough as our http transact can't handle more */
+ if(dataLength==INVALID_FILE_SIZE) dataLength=0;
+ pData=(BYTE*)mir_alloc(dataLength+1);
+ /* load all data */
+ read=0;
+ if(pData!=NULL && ReadFile(hFile,pData,dataLength,&read,NULL)) {
+ CloseHandle(hFile);
+ /* enum cached data */
+ if(fAllowRefetch) Netlib_Logf(hNetlibUser,"Cache valid (age:%usec, threshold:%usec)",age,CACHETIME);
+ EnumUpdates_Worker(pData,read,&fSuccess,fAllowRefetch,callback,wParam,lParam);
+ mir_free(pData);
+ return TRUE; /* done */
+ }
+ else Netlib_Logf(hNetlibUser,"Cache access failed (%u)",GetLastError());
+ mir_free(pData); /* does NULL check */
+ }
+ else Netlib_Logf(hNetlibUser,"Cache is too old (age: %u sec, threshold: %u sec)",age,CACHETIME);
+ }
+ CloseHandle(hFile);
+ }
+ else Netlib_Logf(hNetlibUser,"Cache not available");
+ }
+ else {
+ Netlib_Logf(hNetlibUser,"Cache access failed (%u)",GetLastError());
+ szCacheFile[0]=_T('\0');
+ }
+
+ /* download again */
+ if(fAllowRefetch) {
+ NETLIBHTTPREQUEST req,*resp;
+ NETLIBHTTPHEADER headers[1];
+ DWORD written;
+ char szUrl[64],szUserAgent[128];
+
+ ZeroMemory(&req,sizeof(req));
+ req.cbSize=sizeof(req);
+ req.requestType=REQUEST_GET;
+ mir_snprintf(szUrl,sizeof(szUrl),"http://addons.miranda-im.org/backend/"TCHAR_STR_PARAM,pszDataFile);
+ req.szUrl=szUrl;
+ req.flags=NLHRF_HTTP11|NLHRF_DUMPASTEXT;
+ headers[0].szName="User-Agent";
+ headers[0].szValue=szUserAgent;
+ if(FillUserAgent(szUserAgent,SIZEOF(szUserAgent)))
+ req.headersCount=1;
+ req.headers=headers;
+
+ Netlib_Logf(hNetlibUser,"Downloading data sets: "TCHAR_STR_PARAM,pszDataFile);
+ resp=(NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION,(WPARAM)hNetlibUser,(LPARAM)&req);
+ if(resp!=NULL) {
+ if(resp->resultCode==200 && resp->dataLength) {
+ /* keep cache */
+ hFile=CreateFile(szCacheFile,GENERIC_WRITE,FILE_SHARE_READ,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
+ if(hFile!=INVALID_HANDLE_VALUE) {
+ if(WriteFile(hFile,resp->pData,resp->dataLength,&written,NULL))
+ Netlib_Logf(hNetlibUser,"Cache output: "TCHAR_STR_PARAM,szCacheFile);
+ CloseHandle(hFile);
+ }
+ else Netlib_Logf(hNetlibUser,"Cache output failed (%u)",GetLastError());
+ EnumUpdates_Worker((BYTE*)resp->pData,resp->dataLength,&fSuccess,fAllowRefetch,callback,wParam,lParam);
+ }
+ else Netlib_Logf(hNetlibUser,"No acceptable response from HTTP request");
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT,0,(LPARAM)resp);
+ }
+ else Netlib_Logf(hNetlibUser,"No response from HTTP request");
+ }
+
+ return fSuccess;
+}
+
+/************************* Lang Dlg ***********************************/
+
+static HWND hwndLangDlg;
+
+#define UPDATEANIMFRAMES 20
+#define M_STARTANIM (WM_APP+1)
+#define M_STOPANIM (WM_APP+2)
+#define M_FINISH (WM_APP+3)
+
+struct DownloadSelectionParams {
+ HWND hwndDlg;
+ HWND hwndCombo;
+ BOOL fDownloadAll;
+};
+
+static BOOL CALLBACK DownloadLatestEnumProc(UPDATE_INFO *upd,WORD nRemaining,WPARAM wParam,LPARAM lParam)
+{
+ UPDATE_INFO *updLast=(UPDATE_INFO*)lParam;
+ /* find latest */
+ if(!lstrcmpA(upd->pszSubCategory,updLast->pszSubCategory))
+ if(!updLast->id || CompareFileTime(&upd->ftFileDate,&updLast->ftFileDate)>0)
+ CopyMemory(updLast,upd,sizeof(UPDATE_INFO));
+ /* download */
+ if(!nRemaining && updLast->id)
+ if(!DownloadPack(updLast->id,_T("helppack_*.txt"),(BOOL)wParam,NULL,"Miranda Help Pack Version 1",updLast->pszTitle,updLast->pszVersion)) {
+ updLast->id=0;
+ if((BOOL)wParam) return FALSE; /* stop it */
+ }
+ return TRUE;
+}
+
+static void DownloadSelectionThread(struct DownloadSelectionParams *arg)
+{
+ UPDATE_INFO updLast;
+ int index,sel,count;
+ int fSuccess=0;
+
+ SendMessage(arg->hwndDlg,M_STARTANIM,0,0);
+ sel=SendMessage(arg->hwndCombo,CB_GETCURSEL,0,0);
+ updLast.pszTitle=NULL;
+
+ /* disable all */
+ CorrectPacks(_T("helppack_*.txt"),_T("helppack_english.txt"),TRUE);
+
+ /* enum subcategories */
+ count=SendMessage(arg->hwndCombo,CB_GETCOUNT,0,0);
+ for(index=0;index<count;++index) {
+ if(!arg->fDownloadAll && sel!=index) continue;
+ /* get subcategory */
+ ZeroMemory(&updLast,sizeof(UPDATE_INFO));
+ updLast.pszSubCategory=(char*)SendMessage(arg->hwndCombo,CB_GETITEMDATA,index,0);
+ if(updLast.pszSubCategory==NULL || (int)updLast.pszSubCategory==CB_ERR) {
+ if(!arg->fDownloadAll) fSuccess=-1;
+ continue;
+ }
+ /* show status */
+ if(arg->fDownloadAll) SendMessage(arg->hwndCombo,CB_SETCURSEL,index,0);
+ /* download latest */
+ EnumUpdates(DownloadLatestEnumProc,_T("category_localisation.xml"),FALSE,sel==index,(LPARAM)&updLast);
+ if(updLast.id) fSuccess=1;
+ if(!IsWindowVisible(arg->hwndDlg)) break; /* canceled? */
+ if(!arg->fDownloadAll) break;
+ }
+
+ if(arg->fDownloadAll) SendMessage(arg->hwndCombo,CB_SETCURSEL,sel,0);
+
+ /* ensure one is enabled */
+ CorrectPacks(_T("helppack_*.txt"),_T("helppack_english.txt"),FALSE);
+
+ ReloadLangOptList();
+ SendMessage(arg->hwndDlg,M_STOPANIM,0,0);
+ PostMessage(arg->hwndDlg,M_FINISH,fSuccess,0);
+
+ mir_free(arg);
+}
+
+struct PopulateComboBoxParams {
+ HWND hwndDlg;
+ HWND hwndCombo;
+ HWND hwndOk;
+};
+
+static BOOL CALLBACK PopulateComboBoxEnumProc(UPDATE_INFO *upd,WORD nRemaining,WPARAM wParam,LPARAM lParam)
+{
+ struct PopulateComboBoxParams *arg=(struct PopulateComboBoxParams*)wParam;
+ int index;
+ char *pszParam;
+ UNREFERENCED_PARAMETER(nRemaining);
+
+ /* stop enum if canceled */
+ if(!IsWindowVisible(arg->hwndDlg)) return FALSE;
+ /* skip 'Others' section */
+ if(!lstrcmpA(upd->pszSubCategory,"Others")) return TRUE;
+
+ /* find out if already in list */
+ for(index=SendMessage(arg->hwndCombo,CB_GETCOUNT,0,0);index!=CB_ERR;--index) {
+ pszParam=(char*)SendMessage(arg->hwndCombo,CB_GETITEMDATA,index,0);
+ if((int)pszParam==CB_ERR) continue;
+ if(!lstrcmpA(upd->pszSubCategory,pszParam))
+ return TRUE; /* already there */
+ }
+ /* insert new item */
+ index=SendMessageA(arg->hwndCombo,CB_ADDSTRING,0,(LPARAM)Translate(upd->pszSubCategory));
+ if(index!=CB_ERR) {
+ SendMessage(arg->hwndCombo,CB_SETITEMDATA,index,(LPARAM)mir_strdup(upd->pszSubCategory));
+ /* select current UI language */
+ if(strstr(upd->pszSubCategory,(char*)lParam)!=NULL)
+ SendMessage(arg->hwndCombo,CB_SETCURSEL,index,0);
+ }
+ return TRUE;
+}
+
+static void PopulateComboBoxThread(struct PopulateComboBoxParams *arg)
+{
+ char szCurrentLang[64];
+
+ /* get current UI language name */
+ SendMessage(arg->hwndDlg,M_STARTANIM,0,0);
+ if(!GetLocaleInfoA(LOCALE_USER_DEFAULT,LOCALE_SENGLANGUAGE,szCurrentLang,SIZEOF(szCurrentLang)))
+ szCurrentLang[0]='\0';
+ /* enum all language sections */
+ EnumUpdates(PopulateComboBoxEnumProc,_T("category_localisation.xml"),TRUE,(WPARAM)arg,(LPARAM)szCurrentLang);
+
+ /* enable dialog */
+ SendMessage(arg->hwndDlg,M_STOPANIM,0,0);
+ EnableWindow(arg->hwndOk,TRUE);
+ /* close dialog (if canceled) */
+ if(!IsWindowVisible(arg->hwndDlg))
+ PostMessage(arg->hwndDlg,M_FINISH,(WPARAM)-1,0);
+
+ mir_free(arg);
+}
+
+static BOOL CALLBACK DownloadLangDlgProc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ { HWND hwndCombo;
+ /* init dialog */
+ hwndLangDlg=hwndDlg;
+ TranslateDialogDefault(hwndDlg);
+ SendMessage(hwndDlg,WM_SETICON,ICON_SMALL,(LPARAM)LoadSkinnedIcon(SKINICON_OTHER_MIRANDA));
+ /* init combo box */
+ hwndCombo=GetDlgItem(hwndDlg,IDC_LANGCOMBO);
+ SendMessage(hwndCombo,CB_SETLOCALE,(LCID)CallService(MS_LANGPACK_GETLOCALE,0,0),0); /* for sort order */
+ SendMessage(hwndCombo,CB_SETEXTENDEDUI,TRUE,0);
+ SendMessage(hwndCombo,CB_INITSTORAGE,32,32*16); /* speed up */
+ if(SendMessageA(hwndCombo,CB_ADDSTRING,0,(LPARAM)Translate("English (default)"))!=CB_ERR) {
+ SendMessage(hwndCombo,CB_SETCURSEL,0,0);
+ SendMessage(hwndCombo,CB_SETITEMDATA,0,(LPARAM)NULL);
+ }
+ /* enum all language sections */
+ { struct PopulateComboBoxParams *arg;
+ arg=(struct PopulateComboBoxParams *)mir_alloc(sizeof(struct PopulateComboBoxParams));
+ if(arg!=NULL) {
+ arg->hwndDlg=hwndDlg;
+ arg->hwndCombo=hwndCombo;
+ arg->hwndOk=GetDlgItem(hwndDlg,IDOK);
+ if(mir_forkthread(PopulateComboBoxThread,(void*)arg)==(DWORD)-1)
+ mir_free(arg);
+ }
+ }
+ return TRUE;
+ }
+ case WM_DESTROY:
+ { int index;
+ char *pszParam;
+ HWND hwnd;
+ /* release icon */
+ hwndLangDlg=NULL;
+ CallService(MS_SKIN2_RELEASEICON,(WPARAM)SendMessage(hwndDlg,WM_SETICON,ICON_SMALL,(LPARAM)NULL),0);
+ /* free memory (WM_DELETEITEM does not fit) */
+ hwnd=GetDlgItem(hwndDlg,IDC_LANGCOMBO);
+ for(index=SendMessage(hwnd,CB_GETCOUNT,0,0);index!=CB_ERR;--index) {
+ pszParam=(char*)SendMessage(hwnd,CB_GETITEMDATA,index,0);
+ if((int)pszParam==CB_ERR) continue;
+ SendMessage(hwnd,CB_DELETESTRING,index,0);
+ mir_free(pszParam); /* does NULL check */
+ }
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ /* disable */
+ EnableWindow(GetDlgItem(hwndDlg,IDOK),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_LANGCOMBO),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_DOWNLOADALL),FALSE);
+ /* download */
+ { struct DownloadSelectionParams *arg;
+ arg=(struct DownloadSelectionParams*)mir_alloc(sizeof(struct DownloadSelectionParams));
+ arg->hwndDlg=hwndDlg;
+ arg->hwndCombo=GetDlgItem(hwndDlg,IDC_LANGCOMBO);
+ arg->fDownloadAll=IsDlgButtonChecked(hwndDlg,IDC_DOWNLOADALL);
+ if(arg!=NULL && mir_forkthread(DownloadSelectionThread,arg)!=(DWORD)-1)
+ return TRUE;
+ else mir_free(arg); /* does NULL check */
+ }
+ // else fall through
+ case IDCANCEL: /* this is WM_CLOSE, too */
+ if(!IsWindowEnabled(GetDlgItem(hwndDlg,IDOK)))
+ ShowWindow(hwndDlg,SW_HIDE); /* worker thread will handle this */
+ else
+ PostMessage(hwndDlg,M_FINISH,(WPARAM)-1,0);
+ return TRUE;
+ }
+ break;
+ case M_FINISH:
+ if(wParam!=-1 && IsWindowVisible(hwndDlg)) {
+ MSGBOXPARAMS mbp;
+ mbp.cbSize=sizeof(mbp);
+ mbp.hwndOwner=hwndDlg; /* we get destroyed along with parent */
+ mbp.lpszCaption=TranslateT("Help Pack Download finished");
+ if((BOOL)wParam) {
+ mbp.dwStyle=MB_ICONINFORMATION;
+ mbp.lpszText=TranslateT("The download succeeded!");
+ } else {
+ mbp.dwStyle=MB_ICONERROR;
+ mbp.lpszText=TranslateT("The download failed!\n\nThe help pack could not be downloaded or extracted.");
+ }
+ mbp.dwStyle|=MB_OK;
+ mbp.dwLanguageId=LANGIDFROMLCID((LCID)CallService(MS_LANGPACK_GETLOCALE,0,0));
+ MessageBoxIndirect(&mbp);
+ }
+ DestroyWindow(hwndDlg); /* we must be in window thread to do this */
+ return TRUE;
+ case M_STARTANIM:
+ SetTimer(hwndDlg,1,100,NULL);
+ // fall through
+ case WM_TIMER:
+ { TCHAR str[128];
+ int updateAnimFrame;
+ HWND hwnd;
+ hwnd=GetDlgItem(hwndDlg,IDC_LOADING);
+ if(msg==M_STARTANIM) updateAnimFrame=0;
+ else updateAnimFrame=GetWindowLong(hwnd,GWL_USERDATA);
+ mir_sntprintf(str,SIZEOF(str),_T("%.*s%s%.*s"),updateAnimFrame%10,_T("........."),TranslateT("downloading"),updateAnimFrame%10,_T("........."));
+ SetWindowText(hwnd,str);
+ if(++updateAnimFrame==UPDATEANIMFRAMES) updateAnimFrame=0;
+ SetWindowLong(hwnd,GWL_USERDATA,updateAnimFrame);
+ if(msg==M_STARTANIM) ShowWindow(hwnd,SW_SHOW);
+ return TRUE;
+ }
+ case WM_CTLCOLORSTATIC:
+ switch(GetDlgCtrlID((HWND)lParam)) {
+ case IDC_LOADING:
+ { COLORREF textCol,bgCol,newCol;
+ int ratio,updateAnimFrame;
+ updateAnimFrame=GetWindowLong(GetDlgItem(hwndDlg,IDC_LOADING),GWL_USERDATA);
+ textCol=GetSysColor(COLOR_BTNTEXT);
+ bgCol=GetSysColor(COLOR_3DFACE);
+ ratio=abs(UPDATEANIMFRAMES/2-updateAnimFrame)*510/UPDATEANIMFRAMES;
+ newCol=RGB(GetRValue(bgCol)+(GetRValue(textCol)-GetRValue(bgCol))*ratio/256,
+ GetGValue(bgCol)+(GetGValue(textCol)-GetGValue(bgCol))*ratio/256,
+ GetBValue(bgCol)+(GetBValue(textCol)-GetBValue(bgCol))*ratio/256);
+ SetTextColor((HDC)wParam,newCol);
+ SetBkColor((HDC)wParam,GetSysColor(COLOR_3DFACE));
+ return (BOOL)GetSysColorBrush(COLOR_3DFACE);
+ }
+ }
+ break;
+ case M_STOPANIM:
+ KillTimer(hwndDlg,1);
+ ShowWindow(GetDlgItem(hwndDlg,IDC_LOADING),SW_HIDE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int ServiceShowLangDialog(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ if(Miranda_Terminated()) return 2; /* after pre-shutdown */
+ if(hwndLangDlg!=NULL && IsWindowVisible(hwndLangDlg)) {
+ SetForegroundWindow(hwndLangDlg);
+ return 0;
+ }
+ /* we can't allow a parent here as our window would get destroyed alongside with it */
+ return !CreateDialog(hInst,MAKEINTRESOURCE(IDD_DOWNLOADLANG),NULL,DownloadLangDlgProc);
+}
+
+/************************* Notify Dlg *********************************/
+
+static HWND hwndNotifyDlg;
+
+struct NotifyDlgParams {
+ const TCHAR *pszLanguage;
+ const FILETIME *ftCurrentVersion;
+ const FILETIME *ftNewVersion;
+};
+
+static BOOL CALLBACK UpdateNotifyDlgProc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ { struct NotifyDlgParams *arg=(struct NotifyDlgParams*)lParam;
+ SYSTEMTIME stFileDate;
+ TCHAR szDate[128];
+ LCID locale;
+ hwndNotifyDlg=hwndDlg;
+ TranslateDialogDefault(hwndDlg);
+ /* show update */
+ SendMessage(hwndDlg,WM_SETICON,ICON_SMALL,(LPARAM)LoadSkinnedIcon(SKINICON_OTHER_MIRANDA));
+ SetDlgItemText(hwndDlg,IDC_LANGUAGE,TranslateTS(arg->pszLanguage));
+ locale=(LCID)CallService(MS_LANGPACK_GETLOCALE,0,0);
+ szDate[0]=_T('\0');
+ if(FileTimeToSystemTime(arg->ftCurrentVersion,&stFileDate))
+ GetDateFormat(locale,DATE_SHORTDATE,&stFileDate,NULL,szDate,SIZEOF(szDate));
+ SetDlgItemText(hwndDlg,IDC_CURRENTVERSION,szDate);
+ szDate[0]=_T('\0');
+ if(FileTimeToSystemTime(arg->ftNewVersion,&stFileDate))
+ GetDateFormat(locale,DATE_SHORTDATE,&stFileDate,NULL,szDate,SIZEOF(szDate));
+ SetDlgItemText(hwndDlg,IDC_NEWVERSION,szDate);
+ /* make labels use bold font */
+ { HFONT hFont;
+ LOGFONT lf;
+ hFont=(HFONT)SendDlgItemMessage(hwndDlg,IDC_NEWVERSION,WM_GETFONT,0,0);
+ if(GetObject(hFont,sizeof(lf),&lf)) {
+ lf.lfWeight=FW_BOLD;
+ hFont=CreateFontIndirect(&lf);
+ } else hFont=NULL;
+ SendDlgItemMessage(hwndDlg,IDC_NEWVERSION,WM_SETFONT,(WPARAM)hFont,0);
+ SendDlgItemMessage(hwndDlg,IDC_NEWVERSIONLABEL,WM_SETFONT,(WPARAM)hFont,0);
+ SendDlgItemMessage(hwndDlg,IDC_LANGUAGE,WM_SETFONT,(WPARAM)hFont,0);
+ SendDlgItemMessage(hwndDlg,IDC_LANGUAGELABEL,WM_SETFONT,(WPARAM)hFont,0);
+ SetWindowLong(hwndDlg,DWL_USER,(LONG)hFont);
+ }
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL: /* this is WM_CLOSE, too */
+ EndDialog(hwndDlg,LOWORD(wParam));
+ return TRUE;
+ }
+ break;
+ case WM_DESTROY:
+ hwndNotifyDlg=NULL;
+ IcoLib_ReleaseIcon((HICON)SendMessage(hwndDlg,WM_SETICON,ICON_SMALL,(LPARAM)NULL));
+ return TRUE;
+ case WM_NCDESTROY:
+ { HFONT hFont;
+ hFont=(HFONT)GetWindowLong(hwndDlg,DWL_USER);
+ if(hFont!=NULL) DeleteObject(hFont);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/************************* Marked Versions ****************************/
+
+static void SetVersionMarked(WORD id,const char *pszVersion,BOOL fMarked)
+{
+ char szSetting[18];
+ wsprintfA(szSetting,"NoVerNotify_%u",id);
+ if(fMarked) DBWriteContactSettingString(NULL,"HelpPlugin",szSetting,pszVersion);
+ else DBDeleteContactSetting(NULL,"HelpPlugin",szSetting);
+}
+
+static BOOL IsVersionMarked(WORD id,const char *pszVersion)
+{
+ DBVARIANT dbv;
+ BOOL fMarked=FALSE;
+ char szSetting[18];
+ wsprintfA(szSetting,"NoVerNotify_%u",id);
+ if(!DBGetContactSettingString(NULL,"HelpPlugin",szSetting,&dbv)) {
+ fMarked=!lstrcmpA(dbv.pszVal,pszVersion);
+ mir_free(dbv.pszVal);
+ }
+ return fMarked;
+}
+
+/************************* Error Output ***************************/
+
+static void MessageBoxIndirectFree(MSGBOXPARAMSA *mbp)
+{
+ MessageBoxIndirectA(mbp);
+ mir_free((char*)mbp->lpszCaption); /* does NULL check */
+ mir_free((char*)mbp->lpszText); /* does NULL check */
+ mir_free(mbp);
+}
+
+static void ShowInfoMessage(BYTE flags,const char *pszTitle,const char *pszTextFmt,...)
+{
+ char szText[256]; /* max for systray */
+ MSGBOXPARAMSA *mbp;
+
+ va_list va;
+ va_start(va,pszTextFmt);
+ mir_vsnprintf(szText,SIZEOF(szText),pszTextFmt,va);
+ va_end(va);
+
+ if(ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) {
+ MIRANDASYSTRAYNOTIFY msn;
+ msn.cbSize=sizeof(msn);
+ msn.szProto=NULL;
+ msn.szInfoTitle=(char*)pszTitle;
+ msn.szInfo=(char*)szText;
+ msn.uTimeout=30000; /* max timeout */
+ msn.dwInfoFlags=flags;
+ if(!CallServiceSync(MS_CLIST_SYSTRAY_NOTIFY,0,(LPARAM)&msn))
+ return; /* success */
+ }
+
+ mbp=(MSGBOXPARAMSA*)mir_calloc(sizeof(*mbp));
+ if(mbp==NULL) return;
+ mbp->cbSize=sizeof(*mbp);
+ mbp->lpszCaption=mir_strdup(pszTitle);
+ mbp->lpszText=mir_strdup(szText);
+ mbp->dwStyle=MB_OK|MB_SETFOREGROUND|MB_TASKMODAL;
+ mbp->dwLanguageId=LANGIDFROMLCID((LCID)CallService(MS_LANGPACK_GETLOCALE,0,0));
+ switch(flags&NIIF_ICON_MASK) {
+ case NIIF_INFO: mbp->dwStyle|=MB_ICONINFORMATION; break;
+ case NIIF_WARNING: mbp->dwStyle|=MB_ICONWARNING; break;
+ case NIIF_ERROR: mbp->dwStyle|=MB_ICONERROR;
+ }
+ mir_forkthread(MessageBoxIndirectFree,mbp);
+}
+
+/************************* Update Check ************************************/
+
+#define MINCHECKTIME 60*60 // check no more than once an hour (seconds)
+#define REALCHECKTIME 24*60*60 // check once every 24 hours (seconds)
+#define FIRSTCHECKTIME 15 // first check 15 seconds after startup (seconds)
+
+static UINT idUpdateTimer;
+static DWORD idUpdateThread;
+
+static BOOL CALLBACK FindCurrentPackEnumProc(HELPPACK_INFO *pack,WPARAM wParam,LPARAM lParam)
+{
+ UPDATE_INFO *upd=(UPDATE_INFO*)wParam;
+ if(!lstrcmpiA(upd->pszTitle,pack->szFLName)) {
+ CopyMemory((HELPPACK_INFO*)lParam,pack,sizeof(HELPPACK_INFO));
+ return FALSE; /* stop if found */
+ }
+ return TRUE;
+}
+
+static BOOL CALLBACK FindLatestUpdEnumProc(UPDATE_INFO *upd,WORD nRemaining,WPARAM wParam,LPARAM lParam)
+{
+ UPDATE_INFO *updFind=(UPDATE_INFO*)lParam;
+ UNREFERENCED_PARAMETER(nRemaining);
+
+ /* is there any later item? */
+ if(!lstrcmpiA(upd->pszTitle,updFind->pszTitle))
+ if(CompareFileTime(&upd->ftFileDate,&updFind->ftFileDate)>0) {
+ *(BOOL*)wParam=TRUE;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL CALLBACK UpdateCheckEnumProc(UPDATE_INFO *upd,WORD nRemaining,WPARAM wParam,LPARAM lParam)
+{
+ HELPPACK_INFO pack;
+ UNREFERENCED_PARAMETER(nRemaining);
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ /* search if installed */
+ pack.szFLName[0]='\0';
+ EnumPacks(FindCurrentPackEnumProc,_T("helppack_*.txt"),"Miranda Help Pack Version 1",(WPARAM)upd,(LPARAM)&pack);
+ if(Miranda_Terminated()) return FALSE;
+ if(pack.szVersion[0] && pack.szFLName[0]) {
+ BOOL fHasNewerWithSameName;
+ Netlib_Logf(hNetlibUser,TCHAR_STR_PARAM" (name:\"%s\", ver:%s)",pack.szFileName,pack.szFLName,pack.szVersion);
+ /* skip earlier items with that same name (if any) */
+ fHasNewerWithSameName=FALSE; /* tentatively */
+ EnumUpdates(FindLatestUpdEnumProc,_T("category_localisation.xml"),FALSE,(WPARAM)&fHasNewerWithSameName,(LPARAM)upd);
+ if(Miranda_Terminated()) return FALSE;
+ if(fHasNewerWithSameName)
+ Netlib_Logf(hNetlibUser,"Skip old duplicate (name:\"%s\", ver:%s, cat:\"%s\")",upd->pszTitle,upd->pszVersion,upd->pszSubCategory);
+ /* cheap version check, allows for literals */
+ else if(lstrcmpiA(pack.szVersion,upd->pszVersion)) {
+ Netlib_Logf(hNetlibUser,"New version found (name:\"%s\", ver:%s, cat:\"%s\")",upd->pszTitle,upd->pszVersion,upd->pszSubCategory);
+ /* do not notify again when canceled once */
+ if(!IsVersionMarked(upd->id,upd->pszVersion)) {
+ /* show notify dlg */
+ struct NotifyDlgParams arg;
+ BOOL fSuccess=FALSE;
+ arg.pszLanguage=pack.szLanguage;
+ arg.ftCurrentVersion=&pack.ftFileDate;
+ arg.ftNewVersion=&upd->ftFileDate;
+ if(Miranda_Terminated()) return FALSE;
+ if(DialogBoxParam(hInst,MAKEINTRESOURCE(IDD_UPDATENOTIFY),0,UpdateNotifyDlgProc,(LPARAM)&arg)==IDOK) {
+ fSuccess=DownloadPack(upd->id,_T("helppack_*.txt"),pack.flags&HPF_ENABLED,pack.szFileName,"Miranda Help Pack Version 1",upd->pszTitle,upd->pszVersion);
+ if(fSuccess) ShowInfoMessage(NIIF_INFO,Translate("Help Pack Update succeeded"),Translate("The help pack \"%s\" has been sucessfully downloaded and installed."),TranslateTS(pack.szLanguage));
+ else ShowInfoMessage(NIIF_ERROR,Translate("Help Pack Update failed"),Translate("The help pack \"%s\" could not be downloaded or extracted."),TranslateTS(pack.szLanguage));
+ }
+ /* delete/add marked setting */
+ SetVersionMarked(upd->id,upd->pszVersion,!fSuccess);
+ }
+ else Netlib_Logf(hNetlibUser,"Already notified");
+ }
+ else Netlib_Logf(hNetlibUser,"Same version found (name:\"%s\", ver:%s, cat:\"%s\")",upd->pszTitle,upd->pszVersion,upd->pszSubCategory);
+ }
+ return !Miranda_Terminated();
+}
+
+static void UpdateCheckThread(void *unused)
+{
+ UNREFERENCED_PARAMETER(unused);
+ /* only if any pack installed */
+ if(EnumPacks(NULL,_T("helppack_*.txt"),"Miranda Help Pack Version 1",0,0))
+ EnumUpdates(UpdateCheckEnumProc,_T("category_localisation.xml"),TRUE,0,0);
+ else Netlib_Logf(hNetlibUser,"No "TCHAR_STR_PARAM" file present",_T("helppack_*.txt"));
+}
+
+static void CALLBACK UpdateCheckTimer(HWND hwnd,UINT msg,UINT idTimer,DWORD dwTime)
+{
+ UNREFERENCED_PARAMETER(msg);
+ UNREFERENCED_PARAMETER(dwTime);
+ if(idUpdateThread==-1 && DBGetContactSettingByte(NULL,"HelpPlugin","EnableHelpUpdates",SETTING_ENABLEHELPUPDATES_DEFAULT)) {
+ Netlib_Logf(hNetlibUser,"Running update check");
+ idUpdateThread=mir_forkthread(UpdateCheckThread,(void*)dwTime);
+ }
+ idUpdateTimer=SetTimer(hwnd,idTimer,1000*MINCHECKTIME,UpdateCheckTimer);
+}
+
+/************************* Misc ***************************************/
+
+static int UpdateModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ NETLIBUSER nlu;
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ /* Shared */
+ /* netlib module is not available before this hook */
+ ZeroMemory(&nlu,sizeof(nlu));
+ nlu.cbSize=sizeof(nlu);
+ nlu.flags=NUF_OUTGOING|NUF_HTTPCONNS;
+ nlu.szSettingsModule="HelpPlugin";
+ nlu.szDescriptiveName=Translate("Help Pack Update HTTP connection");
+ hNetlibUser=(HANDLE)CallService(MS_NETLIB_REGISTERUSER,0,(LPARAM)&nlu);
+ /* Update Check */
+ idUpdateThread=(DWORD)-1;
+ idUpdateTimer=SetTimer(NULL,idUpdateTimer,1000*FIRSTCHECKTIME,UpdateCheckTimer);
+ /* First Run */
+// disabled temporarily
+// if(!DBGetContactSettingByte(NULL,"HelpPlugin","FirstRun",0))
+// if(!ServiceShowLangDialog(0,0))
+// DBWriteContactSettingByte(NULL,"HelpPlugin","FirstRun",1);
+ return 0;
+}
+
+static int UpdatePreShutdown(WPARAM wParam,LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+ /* Notify Dlg (let the notify thread go unwind) */
+ if(hwndNotifyDlg!=NULL) SendMessage(hwndNotifyDlg,WM_CLOSE,0,0);
+ /* Lang Dlg (stop any running download enum) */
+ if(hwndLangDlg!=NULL) SendMessage(hwndLangDlg,WM_CLOSE,0,0);
+ /* Update Check (stop timer messages) */
+ if(idUpdateTimer) KillTimer(NULL,idUpdateTimer);
+ return 0;
+}
+
+void InitUpdate(void)
+{
+ /* Update Check */
+ idUpdateTimer=0;
+ /* Notify Dlg */
+ hwndNotifyDlg=NULL;
+ /* Lang Dlg */
+ hwndLangDlg=NULL;
+ hServiceShowLangDialog=CreateServiceFunction(MS_HELP_SHOWLANGDIALOG,ServiceShowLangDialog);
+ /* Misc */
+ hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,UpdateModulesLoaded);
+ hHookPreShutdown=HookEvent(ME_SYSTEM_PRESHUTDOWN,UpdatePreShutdown);
+}
+
+void UninitUpdate(void)
+{
+ /* Misc */
+ UnhookEvent(hHookModulesLoaded);
+ UnhookEvent(hHookPreShutdown);
+ /* Lang Dlg */
+ DestroyServiceFunction(hServiceShowLangDialog);
+} \ No newline at end of file