From 59e53cb6cad99051eb3e46b919fd78ab7493061d Mon Sep 17 00:00:00 2001 From: mataes2007 Date: Wed, 23 Nov 2011 18:16:39 +0000 Subject: added Help plugin git-svn-id: http://miranda-plugins.googlecode.com/svn/trunk@201 e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb --- Help/update.c | 1082 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1082 insertions(+) create mode 100644 Help/update.c (limited to 'Help/update.c') 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 +#include +#define _WIN32_WINNT 0x0500 +#define __RPCASYNC_H__ /* header shows warnings in VS6 */ +#include +#include +#define MIRANDA_VER 0x0600 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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;iheadersCount;++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,"","",&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,"","",&pBlock,&blockLength)) + break; + --nRemaining; + ZeroMemory(&upd,sizeof(upd)); + upd.pszTitle=GetTagValue(pBlock,blockLength,"",""); + upd.id=(WORD)atoi(GetTagValue(pBlock,blockLength,"","")); + if(!upd.id) continue; /* never happens */ + upd.pszVersion=GetTagValue(pBlock,blockLength,"",""); + upd.pszSubCategory=GetTagValue(pBlock,blockLength,"",""); + + /* parse time */ + pszUpdated=GetTagValue(pBlock,blockLength,"",""); + 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(ageresultCode==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;indexfDownloadAll && 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 -- cgit v1.2.3