From d1cb574222bac3b31988d00481ccf7e49d8692f6 Mon Sep 17 00:00:00 2001 From: Kirill Volinsky Date: Sat, 19 May 2012 20:07:57 +0000 Subject: added AssocMgr git-svn-id: http://svn.miranda-ng.org/main/trunk@86 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/AssocMgr/AssocMgr.sln | 23 + plugins/AssocMgr/Info_Src.txt | 30 + plugins/AssocMgr/License_Appendix.txt | 64 ++ plugins/AssocMgr/assoclist.c | 1104 ++++++++++++++++++++ plugins/AssocMgr/assoclist.h | 33 + plugins/AssocMgr/assocmgr.vcxproj | 162 +++ plugins/AssocMgr/assocmgr.vcxproj.filters | 153 +++ plugins/AssocMgr/common.h | 55 + plugins/AssocMgr/dde.c | 293 ++++++ plugins/AssocMgr/dde.h | 30 + plugins/AssocMgr/docs/AssocMgr-Developer.txt | 47 + plugins/AssocMgr/docs/AssocMgr-License.txt | 278 +++++ plugins/AssocMgr/docs/AssocMgr-Readme.txt | 159 +++ plugins/AssocMgr/docs/AssocMgr-Translation.txt | 86 ++ plugins/AssocMgr/main.c | 175 ++++ plugins/AssocMgr/reg.c | 1298 ++++++++++++++++++++++++ plugins/AssocMgr/reg.h | 51 + plugins/AssocMgr/res/emptyfile.ico | Bin 0 -> 10134 bytes plugins/AssocMgr/res/miranda.ico | Bin 0 -> 22486 bytes plugins/AssocMgr/res/mirandafile.ico | Bin 0 -> 10134 bytes plugins/AssocMgr/resource.h | 24 + plugins/AssocMgr/resource.rc | 117 +++ plugins/AssocMgr/test.c | 869 ++++++++++++++++ plugins/AssocMgr/test.h | 24 + plugins/AssocMgr/utils.c | 199 ++++ plugins/AssocMgr/utils.h | 42 + plugins/AssocMgr/version.h | 40 + plugins/AssocMgr/version.rc | 51 + 28 files changed, 5407 insertions(+) create mode 100644 plugins/AssocMgr/AssocMgr.sln create mode 100644 plugins/AssocMgr/Info_Src.txt create mode 100644 plugins/AssocMgr/License_Appendix.txt create mode 100644 plugins/AssocMgr/assoclist.c create mode 100644 plugins/AssocMgr/assoclist.h create mode 100644 plugins/AssocMgr/assocmgr.vcxproj create mode 100644 plugins/AssocMgr/assocmgr.vcxproj.filters create mode 100644 plugins/AssocMgr/common.h create mode 100644 plugins/AssocMgr/dde.c create mode 100644 plugins/AssocMgr/dde.h create mode 100644 plugins/AssocMgr/docs/AssocMgr-Developer.txt create mode 100644 plugins/AssocMgr/docs/AssocMgr-License.txt create mode 100644 plugins/AssocMgr/docs/AssocMgr-Readme.txt create mode 100644 plugins/AssocMgr/docs/AssocMgr-Translation.txt create mode 100644 plugins/AssocMgr/main.c create mode 100644 plugins/AssocMgr/reg.c create mode 100644 plugins/AssocMgr/reg.h create mode 100644 plugins/AssocMgr/res/emptyfile.ico create mode 100644 plugins/AssocMgr/res/miranda.ico create mode 100644 plugins/AssocMgr/res/mirandafile.ico create mode 100644 plugins/AssocMgr/resource.h create mode 100644 plugins/AssocMgr/resource.rc create mode 100644 plugins/AssocMgr/test.c create mode 100644 plugins/AssocMgr/test.h create mode 100644 plugins/AssocMgr/utils.c create mode 100644 plugins/AssocMgr/utils.h create mode 100644 plugins/AssocMgr/version.h create mode 100644 plugins/AssocMgr/version.rc diff --git a/plugins/AssocMgr/AssocMgr.sln b/plugins/AssocMgr/AssocMgr.sln new file mode 100644 index 0000000000..b6c81c1a2f --- /dev/null +++ b/plugins/AssocMgr/AssocMgr.sln @@ -0,0 +1,23 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssocMgr", "assocmgr.vcxproj", "{065889B7-478C-8E11-E93B-54A6D5A21564}" +EndProject +Global + GlobalSection(DPCodeReviewSolutionGUID) = preSolution + DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000} + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {065889B7-478C-8E11-E93B-54A6D5A21564}.Debug|Win32.ActiveCfg = Debug|Win32 + {065889B7-478C-8E11-E93B-54A6D5A21564}.Debug|Win32.Build.0 = Debug|Win32 + {065889B7-478C-8E11-E93B-54A6D5A21564}.Release|Win32.ActiveCfg = Release|Win32 + {065889B7-478C-8E11-E93B-54A6D5A21564}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/plugins/AssocMgr/Info_Src.txt b/plugins/AssocMgr/Info_Src.txt new file mode 100644 index 0000000000..655c2f66f7 --- /dev/null +++ b/plugins/AssocMgr/Info_Src.txt @@ -0,0 +1,30 @@ + +File Association Manager 0.1.1.0 for Miranda IM 0.6+ +------------------------------------------------------------------------ + Source Code + +Reminder: +'File Association Manager' is released under the terms of the +GNU General Public License. +See 'AssocMgr-License.txt' for more details. +'File Association Manager' is copyright 2005-2007 by H. Herkenrath. + +Please notify me of any changes that improve +'File Association Manager' or add new features. +If you have any questions on the code, feel free +to contact me at my email address. + + H. Herkenrath (hrathh at users.sourceforge.net) + + +Notes +------------------------------------------------------------------------ +The following files need to be changed to bump the version number: + +Info_Src.txt (1 place) +version.h (4 places) +m_assocmgr.h (1 place) +m_assocmgr.inc (1 place) +docs\AssocMgr-Readme.txt (3 places) +docs\AssocMgr-Translation.txt (2 places) +docs\AssocMgr-Developer.txt (1 place) diff --git a/plugins/AssocMgr/License_Appendix.txt b/plugins/AssocMgr/License_Appendix.txt new file mode 100644 index 0000000000..c590c3f5fb --- /dev/null +++ b/plugins/AssocMgr/License_Appendix.txt @@ -0,0 +1,64 @@ + +Excecpt of GNU General Public License (Appendix): + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/plugins/AssocMgr/assoclist.c b/plugins/AssocMgr/assoclist.c new file mode 100644 index 0000000000..71e6c7c34f --- /dev/null +++ b/plugins/AssocMgr/assoclist.c @@ -0,0 +1,1104 @@ +/* + +'File Association Manager'-Plugin for Miranda IM + +Copyright (C) 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 (AssocMgr-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "common.h" + +/* Options */ +extern HINSTANCE hInst; +static HANDLE hHookOptInit; +/* Services */ +static HANDLE hServiceAddFile,hServiceRemoveFile,hServiceAddUrl,hServiceRemoveUrl; + +/************************* Assoc List *****************************/ + +typedef struct { + char *pszClassName; /* class name as used in registry and db */ + TCHAR *pszDescription; + HINSTANCE hInstance; /* allowed to be NULL for miranda32.exe */ + WORD nIconResID; + char *pszService; + WORD flags; /* set of FTDF_* and UTDF_* flags */ + char *pszFileExt; /* file type: NULL for url type*/ + char *pszMimeType; /* file type: allowed to be NULL */ + TCHAR *pszVerbDesc; /* file type: allowed to be NULL */ +} ASSOCDATA; + +static ASSOCDATA *pAssocList; /* protected by csAssocList */ +static int nAssocListCount; /* protected by csAssocList */ +static CRITICAL_SECTION csAssocList; + +/************************* Assoc Enabled **************************/ + +static BOOL IsAssocEnabled(const ASSOCDATA *assoc) +{ + char szSetting[MAXMODULELABELLENGTH]; + mir_snprintf(szSetting,sizeof(szSetting),"enabled_%s",assoc->pszClassName); + return DBGetContactSettingByte(NULL,"AssocMgr",szSetting,(BYTE)!(assoc->flags&FTDF_DEFAULTDISABLED))!=0; +} + +static void SetAssocEnabled(const ASSOCDATA *assoc,BOOL fEnabled) +{ + char szSetting[MAXMODULELABELLENGTH]; + TCHAR szDLL[MAX_PATH],szBuf[MAX_PATH]; + mir_snprintf(szSetting,sizeof(szSetting),"enabled_%s",assoc->pszClassName); + DBWriteContactSettingByte(NULL,"AssocMgr",szSetting,(BYTE)fEnabled); + /* dll name for uninstall */ + if(assoc->hInstance!=NULL && assoc->hInstance!=hInst && assoc->hInstance!=GetModuleHandle(NULL)) + if(GetModuleFileName(assoc->hInstance,szBuf,SIZEOF(szBuf))) + if(CallService(MS_UTILS_PATHTORELATIVET,(WPARAM)szBuf,(LPARAM)szDLL)) { + mir_snprintf(szSetting,sizeof(szSetting),"module_%s",assoc->pszClassName); + DBWriteContactSettingTString(NULL,"AssocMgr",szSetting,szDLL); + } +} + +static void DeleteAssocEnabledSetting(const ASSOCDATA *assoc) +{ + char szSetting[MAXMODULELABELLENGTH]; + mir_snprintf(szSetting,sizeof(szSetting),"enabled_%s",assoc->pszClassName); + DBDeleteContactSetting(NULL,"AssocMgr",szSetting); + /* dll name for uninstall */ + mir_snprintf(szSetting,sizeof(szSetting),"module_%s",assoc->pszClassName); + DBDeleteContactSetting(NULL,"AssocMgr",szSetting); +} + +void CleanupAssocEnabledSettings(void) +{ + int nSettingsCount; + char **ppszSettings,*pszSuffix; + DBVARIANT dbv; + int i; + HANDLE hFile; + TCHAR szDLL[MAX_PATH]; + char szSetting[MAXMODULELABELLENGTH]; + + /* delete old enabled_* settings if associated plugin no longer present */ + if(EnumDbPrefixSettings("AssocMgr","enabled_",&ppszSettings,&nSettingsCount)) { + EnterCriticalSection(&csAssocList); + for(i=0;ipszClassName=mir_strdup(assoc->pszClassName); + assoc2->pszDescription=mir_tstrdup(assoc->pszDescription); + assoc2->hInstance=assoc->hInstance; + assoc2->nIconResID=assoc->nIconResID; + assoc2->pszService=mir_strdup(assoc->pszService); + assoc2->flags=assoc->flags; + assoc2->pszFileExt=mir_strdup(assoc->pszFileExt); + assoc2->pszMimeType=mir_strdup(assoc->pszMimeType); + assoc2->pszVerbDesc=mir_tstrdup(assoc->pszVerbDesc); + if(assoc2->pszClassName==NULL || assoc2->pszDescription==NULL || + (assoc2->pszFileExt==NULL && assoc->pszFileExt!=NULL)) { + mir_free(assoc2->pszClassName); /* does NULL check */ + mir_free(assoc2->pszDescription); /* does NULL check */ + mir_free(assoc2->pszService); /* does NULL check */ + mir_free(assoc2->pszFileExt); /* does NULL check */ + mir_free(assoc2->pszMimeType); /* does NULL check */ + mir_free(assoc2->pszVerbDesc); /* does NULL check */ + mir_free(assoc2); + return NULL; + } + return assoc2; +} + +// this function assumes it has got the csAssocList mutex +// this function assumes CoInitialize() has been called before +static int ReplaceImageListAssocIcon(HIMAGELIST himl,const ASSOCDATA *assoc,int iPrevIndex) +{ + HICON hIcon=NULL; + int index; + if(himl==NULL) return -1; + + /* load icon */ + hIcon=LoadRegClassSmallIcon(assoc->pszClassName); + if(hIcon==NULL) { + SHFILEINFOA sfi; + if(SHGetFileInfoA((assoc->pszFileExt!=NULL)?assoc->pszFileExt:"",FILE_ATTRIBUTE_NORMAL,&sfi,sizeof(sfi),SHGFI_ICON|SHGFI_SMALLICON|SHGFI_USEFILEATTRIBUTES)) + hIcon=sfi.hIcon; /* WinXP: this icon is not updated until the process exits */ + } + /* add icon */ + if(hIcon==NULL) return -1; + index=ImageList_ReplaceIcon(himl,iPrevIndex,hIcon); + DestroyIcon(hIcon); + return index; +} + +// the return value does not need to be freed +// this function assumes it has got the csAssocList mutex +static TCHAR* GetAssocTypeDesc(const ASSOCDATA *assoc) +{ + static TCHAR szDesc[32]; + if(assoc->pszFileExt==NULL) mir_sntprintf(szDesc,SIZEOF(szDesc),TranslateT("%hs:"),assoc->pszClassName); + else mir_sntprintf(szDesc,SIZEOF(szDesc),TranslateT("%hs files"),assoc->pszFileExt); + return szDesc; +} + +// this function assumes it has got the csAssocList mutex +static BOOL IsAssocRegistered(const ASSOCDATA *assoc) +{ + BOOL fSuccess=FALSE,fIsUrl,fUseMainCmdLine; + TCHAR *pszRunCmd; + + fIsUrl=(assoc->pszFileExt==NULL); + fUseMainCmdLine=(assoc->pszService==NULL); + + /* class */ + pszRunCmd=MakeRunCommand(fUseMainCmdLine,!fUseMainCmdLine); + if(pszRunCmd!=NULL) + fSuccess=IsRegClass(assoc->pszClassName,pszRunCmd); + mir_free(pszRunCmd); /* does NULL check */ + /* file ext */ + if(!fIsUrl) + fSuccess=IsRegFileExt(assoc->pszFileExt,assoc->pszClassName); + + return fSuccess; +} + +// this function assumes it has got the csAssocList mutex +// call GetLastError() on error to get more error details +static BOOL EnsureAssocRegistered(const ASSOCDATA *assoc) +{ + BOOL fSuccess=FALSE,fIsUrl,fUseMainCmdLine; + TCHAR *pszIconLoc,*pszRunCmd,*pszDdeCmd,*pszAppFileName; + + fIsUrl=(assoc->pszFileExt==NULL); + fUseMainCmdLine=(assoc->pszService==NULL); + + pszRunCmd=MakeRunCommand(fUseMainCmdLine,!fUseMainCmdLine); + if(pszRunCmd!=NULL) { + fSuccess=TRUE; /* tentatively */ + /* do not overwrite user customized settings */ + if(!IsRegClass(assoc->pszClassName,pszRunCmd)) { + /* class icon */ + if(!assoc->nIconResID && fIsUrl) pszIconLoc=MakeIconLocation(NULL,0); /* miranda logo */ + else if(!assoc->nIconResID) pszIconLoc=MakeIconLocation(hInst,IDI_MIRANDAFILE); /* generic file */ + else pszIconLoc=MakeIconLocation(assoc->hInstance,assoc->nIconResID); + /* register class */ + if(fUseMainCmdLine) pszDdeCmd=NULL; + else pszDdeCmd=fIsUrl?DDEURLCMD:DDEFILECMD; + fSuccess=AddRegClass(assoc->pszClassName,assoc->pszDescription,pszIconLoc,_T(MIRANDANAME),pszRunCmd,pszDdeCmd,DDEAPP,DDETOPIC,assoc->pszVerbDesc,assoc->flags&FTDF_BROWSERAUTOOPEN,fIsUrl,assoc->flags&FTDF_ISSHORTCUT); + mir_free(pszIconLoc); /* does NULL check */ + /* file type */ + if(fSuccess && !fIsUrl) { + /* register mime type */ + if(assoc->pszMimeType!=NULL) + if(AddRegMimeType(assoc->pszMimeType,assoc->pszFileExt,assoc->pszDescription)) + RememberMimeTypeAdded(assoc->pszMimeType,assoc->pszFileExt,TRUE); + /* register file ext */ + fSuccess=AddRegFileExt(assoc->pszFileExt,assoc->pszClassName,assoc->pszMimeType,assoc->flags&FTDF_ISTEXT); + /* register open-with */ + pszAppFileName=MakeAppFileName(fUseMainCmdLine); + if(pszAppFileName!=NULL) + AddRegOpenWithExtEntry(pszAppFileName,assoc->pszFileExt,assoc->pszDescription); + mir_free(pszAppFileName); /* does NULL check */ + } + } + mir_free(pszRunCmd); + } + else SetLastError(ERROR_OUTOFMEMORY); + return fSuccess; +} + +// this function assumes it has got the csAssocList mutex +// call GetLastError() on error to get more error details +static BOOL UnregisterAssoc(const ASSOCDATA *assoc) +{ + BOOL fIsUrl,fUseMainCmdLine; + TCHAR *pszAppFileName,*pszRunCmd; + + fIsUrl=(assoc->pszFileExt==NULL); + fUseMainCmdLine=(assoc->pszService==NULL); + + /* class might have been registered by another instance */ + pszRunCmd=MakeRunCommand(fUseMainCmdLine,!fUseMainCmdLine); + if(pszRunCmd!=NULL && !IsRegClass(assoc->pszClassName,pszRunCmd)) { + mir_free(pszRunCmd); + return TRUE; /* succeed anyway */ + } + mir_free(pszRunCmd); /* does NULL check */ + + /* file type */ + if(!fIsUrl) { + /* file extension */ + RemoveRegFileExt(assoc->pszFileExt,assoc->pszClassName); + /* mime type */ + if(assoc->pszMimeType!=NULL) + if(WasMimeTypeAdded(assoc->pszMimeType)) { + RemoveRegMimeType(assoc->pszMimeType,assoc->pszFileExt); + RememberMimeTypeAdded(assoc->pszMimeType,assoc->pszFileExt,FALSE); + } + /* open-with entry */ + pszAppFileName=MakeAppFileName(fUseMainCmdLine); + if(pszAppFileName!=NULL) + RemoveRegOpenWithExtEntry(pszAppFileName,assoc->pszFileExt); + mir_free(pszAppFileName); /* does NULL check */ + } + return RemoveRegClass(assoc->pszClassName); +} + +/************************* Assoc List Workers *********************/ + +/* this structure represents the head of both + * FILETYPEDESC and URLTYPEDESC structures. + * the head is identical for both structures. */ +typedef struct { + int cbSize; /* either sizeof(FILETYPEDESC) or sizeof(URLTYPEDESC) */ + const void *pszDescription; + HINSTANCE hInstance; + UINT nIconResID; + const char *pszService; + DWORD flags; +} TYPEDESCHEAD; + +// ownership of pszClassName, pszFileExt, pszVerbDesc and pszMimeType is transfered +// to the storage list on success +static BOOL AddNewAssocItem_Worker(char *pszClassName,const TYPEDESCHEAD *tdh,char *pszFileExt,TCHAR *pszVerbDesc,char *pszMimeType) +{ + ASSOCDATA *pAssocListBuf,*assoc; + int index; + + /* is already in list? */ + EnterCriticalSection(&csAssocList); + index=FindAssocItem(pszClassName); + if(index!=-1) return FALSE; + + /* resize storage array */ + pAssocListBuf=(ASSOCDATA*)mir_realloc(pAssocList,(nAssocListCount+1)*sizeof(ASSOCDATA)); + if(pAssocListBuf==NULL) { + LeaveCriticalSection(&csAssocList); + return FALSE; + } + pAssocList=pAssocListBuf; + + /* init new item */ + assoc=&pAssocList[nAssocListCount]; + assoc->pszClassName=pszClassName; /* no dup here */ + assoc->pszDescription=s2t(tdh->pszDescription,tdh->flags&FTDF_UNICODE,TRUE); /* does NULL check */ + assoc->hInstance=tdh->hInstance; /* hInstance is allowed to be NULL for miranda32.exe */ + assoc->nIconResID=(WORD)tdh->nIconResID; /* default icon selected later on */ + assoc->pszService=mir_strdup(tdh->pszService); /* does NULL check */ + assoc->flags=(WORD)tdh->flags; + assoc->pszFileExt=pszFileExt; + assoc->pszMimeType=pszMimeType; + assoc->pszVerbDesc=pszVerbDesc; + + /* error check */ + if(assoc->pszDescription==NULL || (assoc->pszService==NULL && tdh->pszService!=NULL)) { + mir_free(assoc->pszService); /* does NULL check */ + mir_free(assoc->pszDescription); /* does NULL check */ + LeaveCriticalSection(&csAssocList); + return FALSE; + } + + /* add registry keys */ + if(IsAssocEnabled(assoc)) + EnsureAssocRegistered(assoc); + + ++nAssocListCount; + NotifyAssocChange(FALSE); + return TRUE; +} + +// ownership of pszClassName is *not* transferd to storage list +static BOOL RemoveAssocItem_Worker(const char *pszClassName) +{ + ASSOCDATA *pAssocListBuf,*assoc; + int index; + + /* find index */ + EnterCriticalSection(&csAssocList); + index=FindAssocItem(pszClassName); + if(index==-1) { + LeaveCriticalSection(&csAssocList); + return FALSE; + } + assoc=&pAssocList[index]; + + /* delete registry keys and db setting */ + UnregisterAssoc(assoc); + if(assoc->pszMimeType!=NULL) + RememberMimeTypeAdded(assoc->pszMimeType,assoc->pszFileExt,FALSE); + DeleteAssocEnabledSetting(assoc); + + /* free memory */ + mir_free(assoc->pszClassName); + mir_free(assoc->pszDescription); + mir_free(assoc->pszService); + mir_free(assoc->pszFileExt); /* does NULL check */ + mir_free(assoc->pszVerbDesc); /* does NULL check */ + mir_free(assoc->pszMimeType); /* does NULL check */ + + /* resize storage array */ + if((index+1)cbSizepszFileExt==NULL || ftd->pszFileExt[0]!='.') + return 2; + + pszFileExt=mir_strdup(ftd->pszFileExt); + pszClassName=MakeFileClassName(ftd->pszFileExt); + if(pszFileExt!=NULL && pszClassName!=NULL) { + pszVerbDesc=s2t(ftd->ptszVerbDesc,ftd->flags&FTDF_UNICODE,TRUE); /* does NULL check */ + pszMimeType=mir_strdup(ftd->pszMimeType); /* does NULL check */ + if(AddNewAssocItem_Worker(pszClassName,(TYPEDESCHEAD*)ftd,pszFileExt,pszVerbDesc,pszMimeType)) + /* no need to free pszClassName, pszFileExt,pszVerbDesc and pszMimeType, + * as their ownership got transfered to storage list */ + return 0; + } + mir_free(pszClassName); /* does NULL check */ + mir_free(pszFileExt); /* does NULL check */ + return 3; +} + +static int ServiceRemoveFileType(WPARAM wParam,LPARAM lParam) +{ + char *pszClassName; + UNREFERENCED_PARAMETER(wParam); + + if((char*)lParam==NULL) return 2; + pszClassName=MakeFileClassName((char*)lParam); + if(pszClassName!=NULL) + if(RemoveAssocItem_Worker(pszClassName)) { + mir_free(pszClassName); + return 0; + } + mir_free(pszClassName); /* does NULL check */ + return 3; +} + +static int ServiceAddNewUrlType(WPARAM wParam,LPARAM lParam) +{ + const URLTYPEDESC *utd=(URLTYPEDESC*)lParam; + char *pszClassName; + UNREFERENCED_PARAMETER(wParam); + + if(utd->cbSizepszService==NULL) + return 2; + if(utd->pszProtoPrefix==NULL || utd->pszProtoPrefix[lstrlenA(utd->pszProtoPrefix)-1]!=':') + return 2; + + pszClassName=MakeUrlClassName(utd->pszProtoPrefix); + if(pszClassName!=NULL) + if(AddNewAssocItem_Worker(pszClassName,(TYPEDESCHEAD*)utd,NULL,NULL,NULL)) + /* no need to free pszClassName, as its + * ownership got transfered to storage list */ + return 0; + mir_free(pszClassName); /* does NULL check */ + return 3; +} + +static int ServiceRemoveUrlType(WPARAM wParam,LPARAM lParam) +{ + char *pszClassName; + UNREFERENCED_PARAMETER(wParam); + + if((char*)lParam==NULL) return 2; + pszClassName=MakeUrlClassName((char*)lParam); + if(pszClassName!=NULL) + if(RemoveAssocItem_Worker(pszClassName)) { + mir_free(pszClassName); + return 0; + } + mir_free(pszClassName); /* does NULL check */ + return 3; +} + +/************************* Open Handler ***************************/ + +static BOOL InvokeHandler_Worker(const char *pszClassName,const TCHAR *pszParam,int *res) +{ + void *pvParam; + char *pszService; + int index; + ASSOCDATA *assoc; + + /* find it in list */ + EnterCriticalSection(&csAssocList); + index=FindAssocItem(pszClassName); + if(index==-1) { + LeaveCriticalSection(&csAssocList); + return FALSE; + } + assoc=&pAssocList[index]; + /* no service specified? correct registry to use main commandline */ + if(assoc->pszService==NULL) { + EnsureAssocRegistered(assoc); + NotifyAssocChange(FALSE); + /* try main command line */ + if((int)ShellExecute(NULL,NULL,pszParam,NULL,NULL,SW_SHOWNORMAL)>=32) + *res=0; /* success */ + return TRUE; + } + /* get params */ + pszService=mir_strdup(assoc->pszService); + pvParam=t2s(pszParam,assoc->flags&FTDF_UNICODE,FALSE); + LeaveCriticalSection(&csAssocList); + + /* call service */ + if(pszService!=NULL && pvParam!=NULL) + *res=CallService(pszService,0,(LPARAM)pvParam); + mir_free(pszService); /* does NULL check */ + mir_free(pvParam); /* does NULL check */ + return TRUE; +} + +int InvokeFileHandler(const TCHAR *pszFileName) +{ + char *pszClassName,*pszFileExt; + TCHAR *p; + int res=CALLSERVICE_NOTFOUND; + + /* find extension */ + p=_tcsrchr(pszFileName,_T('.')); + if(p!=NULL) { + pszFileExt=t2a(p); + if(pszFileExt!=NULL) { + /* class name */ + pszClassName=MakeFileClassName(pszFileExt); + if(pszClassName!=NULL) + if(!InvokeHandler_Worker(pszClassName,pszFileName,&res)) { + /* correct registry on error (no longer in list) */ + RemoveRegFileExt(pszFileExt,pszClassName); + RemoveRegClass(pszClassName); + } + mir_free(pszClassName); /* does NULL check */ + mir_free(pszFileExt); + } + } + return res; +} + +int InvokeUrlHandler(const TCHAR *pszUrl) +{ + char *pszClassName,*pszProtoPrefix,*p; + int res=CALLSERVICE_NOTFOUND; + + /* find prefix */ + pszProtoPrefix=t2a(pszUrl); + if(pszProtoPrefix!=NULL) { + p=strchr(pszProtoPrefix,_T(':')); + if(p!=NULL) { + *(++p)=0; /* remove trailing : */ + /* class name */ + pszClassName=MakeUrlClassName(pszProtoPrefix); + if(pszClassName!=NULL) + if(!InvokeHandler_Worker(pszClassName,pszUrl,&res)) + /* correct registry on error (no longer in list) */ + RemoveRegClass(pszClassName); + mir_free(pszClassName); /* does NULL check */ + } + mir_free(pszProtoPrefix); + } + return res; +} + +/************************* Options ********************************/ + +static int CALLBACK ListViewSortDesc(LPARAM lParam1,LPARAM lParam2,LPARAM lParamSort) +{ + int cmp; + if(((ASSOCDATA*)lParam1)->pszFileExt!=NULL && ((ASSOCDATA*)lParam2)->pszFileExt!=NULL) + cmp=CompareStringA((LCID)lParamSort,0,((ASSOCDATA*)lParam1)->pszFileExt,-1,((ASSOCDATA*)lParam2)->pszFileExt,-1); + else if(((ASSOCDATA*)lParam1)->pszFileExt==((ASSOCDATA*)lParam2)->pszFileExt) /* both NULL */ + cmp=CompareStringA((LCID)lParamSort,0,((ASSOCDATA*)lParam1)->pszClassName,-1,((ASSOCDATA*)lParam2)->pszClassName,-1); + else /* different types, incomparable */ + cmp=(((ASSOCDATA*)lParam1)->pszFileExt==NULL)?CSTR_LESS_THAN:CSTR_GREATER_THAN; + if(cmp==CSTR_EQUAL) + cmp=CompareString((LCID)lParamSort,0,((ASSOCDATA*)lParam1)->pszDescription,-1,((ASSOCDATA*)lParam2)->pszDescription,-1); + if(cmp!=0) cmp-=2; /* maintain CRT conventions */ + return cmp; +} + +#define M_REFRESH_ICONS (WM_APP+1) +static int CALLBACK AssocListOptDlgProc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam) +{ + switch(msg) { + case WM_INITDIALOG: + { HWND hwndList; + HIMAGELIST himl; + LVITEM lvi; + ASSOCDATA *assoc; + int i; + TranslateDialogDefault(hwndDlg); + CoInitialize(NULL); + hwndList=GetDlgItem(hwndDlg,IDC_ASSOCLIST); +#if defined(_UNICODE) + ListView_SetUnicodeFormat(hwndList,TRUE); +#endif + SendDlgItemMessage(hwndDlg,IDC_HEADERTEXT,WM_SETFONT,SendMessage(GetParent(hwndDlg),PSM_GETBOLDFONT,0,0),0); + /* checkboxes won't show up on Win95 without IE3+ or 4.70 (plugin opts uses the same) */ + ListView_SetExtendedListViewStyle(hwndList,LVS_EX_CHECKBOXES|LVS_EX_FULLROWSELECT|LVS_EX_LABELTIP); + /* columns */ + { LVCOLUMN lvc; + lvc.mask=LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM; + lvc.pszText=TranslateT("Type"); + lvc.cx=170; + ListView_InsertColumn(hwndList,lvc.iSubItem=0,&lvc); + lvc.pszText=TranslateT("Description"); + ListView_InsertColumn(hwndList,lvc.iSubItem=1,&lvc); + } + /* create image storage */ + EnterCriticalSection(&csAssocList); + { HDC hdc; + hdc=GetDC(hwndList); + if(hdc!=NULL) { /* BITSPIXEL is compatible with ILC_COLOR flags */ + himl=ImageList_Create(GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),GetDeviceCaps(hdc,BITSPIXEL)|ILC_MASK,nAssocListCount,0); + ReleaseDC(hwndList,hdc); + } else himl=NULL; + } + ListView_SetImageList(hwndList,himl,LVSIL_SMALL); /* autodestroyed */ + /* enum assoc list */ + lvi.iSubItem=0; + lvi.mask=LVIF_TEXT|LVIF_PARAM|LVIF_IMAGE; + for(i=0;ipszDescription); + ListView_SetCheckState(hwndList,lvi.iItem,IsAssocEnabled(assoc) && IsAssocRegistered(assoc)); + } + } + /* sort items (before moving to groups) */ + ListView_SortItems(hwndList,ListViewSortDesc,(LPARAM)CallService(MS_LANGPACK_GETLOCALE,0,0)); + /* groups */ + if(ListView_EnableGroupView(hwndList,TRUE)==1) { /* returns 0 on pre WinXP or if commctls6 are disabled */ + LVGROUP lvg; + int iItem; + /* dummy item for group */ + lvi.iItem=ListView_GetItemCount(hwndList)-1; + lvi.iSubItem=0; + lvi.mask=LVIF_PARAM|LVIF_IMAGE; + lvi.iImage=-1; + lvi.lParam=0; + /* insert groups */ + lvg.cbSize=sizeof(lvg); + lvg.mask=LVGF_HEADER|LVGF_GROUPID; + lvg.iGroupId=2; + lvg.pszHeader=(WCHAR*)TranslateW(L"URLs on Websites"); + lvi.iItem=ListView_InsertItem(hwndList,&lvi); + if(lvi.iItem!=-1) { + ListView_InsertGroup(hwndList,lvi.iItem,&lvg); + lvg.iGroupId=1; + lvg.pszHeader=(WCHAR*)TranslateW(L"File Types"); + iItem=lvi.iItem=ListView_InsertItem(hwndList,&lvi); + if(lvi.iItem!=-1) + ListView_InsertGroup(hwndList,lvi.iItem,&lvg); + else ListView_DeleteItem(hwndList,iItem); + } + /* move to group */ + lvi.iSubItem=0; + lvi.mask=LVIF_PARAM|LVIF_GROUPID; + for(lvi.iItem=0;ListView_GetItem(hwndList,&lvi);++lvi.iItem) { + assoc=(ASSOCDATA*)lvi.lParam; + if(assoc==NULL) continue; /* groups */ + lvi.iGroupId=(assoc->pszFileExt==NULL)+1; + ListView_SetItem(hwndList,&lvi); + } + } + LeaveCriticalSection(&csAssocList); + lvi.iItem=ListView_GetTopIndex(hwndList); + ListView_SetItemState(hwndList,lvi.iItem,LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED); + ListView_SetColumnWidth(hwndList,1,LVSCW_AUTOSIZE_USEHEADER); /* size to fit window */ + /* only while running */ + CheckDlgButton(hwndDlg,IDC_ONLYWHILERUNNING,(BOOL)DBGetContactSettingByte(NULL,"AssocMgr","OnlyWhileRunning",SETTING_ONLYWHILERUNNING_DEFAULT)); + /* autostart */ + { TCHAR *pszRunCmd; + pszRunCmd=MakeRunCommand(TRUE,TRUE); + if(pszRunCmd!=NULL) { + CheckDlgButton(hwndDlg,IDC_AUTOSTART,IsRegRunEntry(_T("MirandaIM"),pszRunCmd)); + mir_free(pszRunCmd); + } + } + return TRUE; + } + case WM_SETTINGCHANGE: + case M_REFRESH_ICONS: + { LVITEM lvi; + HIMAGELIST himl; + ASSOCDATA *assoc; + HWND hwndList; + hwndList=GetDlgItem(hwndDlg,IDC_ASSOCLIST); + himl=ListView_GetImageList(hwndList,LVSIL_SMALL); + /* enum items */ + lvi.iSubItem=0; + lvi.mask=LVIF_PARAM|LVIF_IMAGE; + for(lvi.iItem=0;ListView_GetItem(hwndList,&lvi);++lvi.iItem) { + assoc=(ASSOCDATA*)lvi.lParam; + if(assoc==NULL) continue; /* groups */ + lvi.iImage=ReplaceImageListAssocIcon(himl,assoc,lvi.iImage); + ListView_SetItem(hwndList,&lvi); + } + if(lvi.iItem) { /* ListView_Update() blinks */ + ListView_RedrawItems(hwndList,0,lvi.iItem-1); + UpdateWindow(hwndList); + } + return TRUE; + } + case WM_CTLCOLORSTATIC: + /* use same text color for header as for group boxes (WinXP+) */ + if(GetDlgCtrlID((HWND)lParam)==IDC_HEADERTEXT) { + HMODULE hUxThemeDLL; + HBRUSH hBrush; + hUxThemeDLL=LoadLibraryA("UXTHEME"); /* all ascii, already loaded */ + lParam=(LPARAM)GetDlgItem(hwndDlg,IDC_MISCLABEL); + hBrush=(HBRUSH)SendMessage(hwndDlg,msg,wParam,lParam); + if(hUxThemeDLL!=NULL) { + HTHEME (WINAPI *pfnGetWindowTheme)(HWND); + HRESULT (WINAPI *pfnGetThemeColor)(HTHEME,int,int,int,COLORREF*); + COLORREF clr; + *(PROC*)&pfnGetWindowTheme=GetProcAddress(hUxThemeDLL,"GetWindowTheme"); + *(PROC*)&pfnGetThemeColor=GetProcAddress(hUxThemeDLL,"GetThemeColor"); + if(pfnGetWindowTheme!=NULL && pfnGetThemeColor!=NULL) { + HTHEME hTheme; + hTheme=pfnGetWindowTheme((HWND)lParam); + if(hTheme!=NULL) + if(!pfnGetThemeColor(hTheme,BP_GROUPBOX,GBS_NORMAL,TMT_TEXTCOLOR,&clr)) { + SetBkMode((HDC)wParam,TRANSPARENT); + SetTextColor((HDC)wParam,clr); + } + } + FreeLibrary(hUxThemeDLL); + } + return (BOOL)hBrush; + } + break; + case WM_NCDESTROY: + CoUninitialize(); + return TRUE; + case WM_COMMAND: + /* enable apply */ + PostMessage(GetParent(hwndDlg),PSM_CHANGED,0,0); + break; +#if defined(_UNICODE) + case WM_NOTIFYFORMAT: + SetWindowLong(hwndDlg,DWL_MSGRESULT,NFR_UNICODE); + return TRUE; +#endif + case WM_NOTIFY: + { NMHDR *nmhdr=(NMHDR*)lParam; + switch(nmhdr->idFrom) { + case IDC_ASSOCLIST: + switch(nmhdr->code) { + case LVN_DELETEITEM: /* also called on WM_DESTROY */ + { LVITEM lvi; + lvi.mask=LVIF_PARAM; + lvi.iSubItem=0; + lvi.iItem=((NMLISTVIEW*)lParam)->iItem; + /* free memory */ + if(ListView_GetItem(nmhdr->hwndFrom,&lvi)) + mir_free((ASSOCDATA*)lvi.lParam); /* does NULL check */ + return TRUE; + } + case LVN_ITEMCHANGED: + /* enable apply (not while loading) */ + if(IsWindowVisible(nmhdr->hwndFrom)) + PostMessage(GetParent(hwndDlg),PSM_CHANGED,0,0); + return TRUE; + case LVN_KEYDOWN: + /* workaround for WinXP (ListView with groups): + * eat keyboard navigation that goes beyond the first item in list + * as it would scroll out of scope in this case + * bug should not be present using WinVista and higher */ + switch(((NMLVKEYDOWN*)lParam)->wVKey) { + case VK_UP: + { LVITEM lvi; + lvi.iSubItem=0; + lvi.mask=LVIF_PARAM; + lvi.iItem=ListView_GetNextItem(nmhdr->hwndFrom,-1,LVNI_FOCUSED); + lvi.iItem=ListView_GetNextItem(nmhdr->hwndFrom,lvi.iItem,LVNI_ABOVE); + if(lvi.iItem!=-1) + if(ListView_GetItem(nmhdr->hwndFrom,&lvi)) + if((ASSOCDATA*)lvi.lParam==NULL) /* groups */ + lvi.iItem=-1; + if(lvi.iItem==-1) { + SetWindowLong(hwndDlg,DWL_MSGRESULT,TRUE); /* eat it */ + return TRUE; + } + break; + } + case VK_PRIOR: + { LVITEM lvi; + lvi.iSubItem=0; + lvi.mask=LVIF_PARAM; + lvi.iItem=ListView_GetNextItem(nmhdr->hwndFrom,-1,LVNI_FOCUSED); + lvi.iItem-=ListView_GetCountPerPage(nmhdr->hwndFrom); + if(lvi.iItem>=0) + if(ListView_GetItem(nmhdr->hwndFrom,&lvi)) + if((ASSOCDATA*)lvi.lParam==NULL) /* groups */ + lvi.iItem=-1; + if(lvi.iItem<0) { + ListView_SetItemState(nmhdr->hwndFrom,0,LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED); + SetWindowLong(hwndDlg,DWL_MSGRESULT,TRUE); /* eat it */ + return TRUE; + } + break; + } + } + break; + } + break; + case 0: + switch(nmhdr->code) { + case PSN_APPLY: + { HWND hwndList; + LVITEM lvi; + BOOL fEnabled,fRegFailed=FALSE; + ASSOCDATA *assoc; + + /* only while running */ + DBWriteContactSettingByte(NULL,"AssocMgr","OnlyWhileRunning",(BYTE)(IsDlgButtonChecked(hwndDlg,IDC_ONLYWHILERUNNING)!=0)); + + /* save enabled assoc items */ + hwndList=GetDlgItem(hwndDlg,IDC_ASSOCLIST); + lvi.iSubItem=0; + lvi.mask=LVIF_PARAM; + EnterCriticalSection(&csAssocList); + for(lvi.iItem=0;ListView_GetItem(hwndList,&lvi);++lvi.iItem) { + assoc=(ASSOCDATA*)lvi.lParam; + if(assoc==NULL) continue; /* groups */ + fEnabled=ListView_GetCheckState(hwndList,lvi.iItem); + SetAssocEnabled(assoc,fEnabled); + + /* re-register registery keys */ + if(fEnabled?!EnsureAssocRegistered(assoc):!UnregisterAssoc(assoc)) { + char *pszErr; + pszErr=GetWinErrorDescription(GetLastError()); + ShowInfoMessage(NIIF_ERROR,Translate("File Association Error"),Translate("There was an error writing to the registry to modify the file/url associations.\nReason: %s"),(pszErr!=NULL)?pszErr:Translate("Unknown")); + mir_free(pszErr); /* does NULL check */ + fRegFailed=TRUE; /* just show one time */ + } + } + LeaveCriticalSection(&csAssocList); + NotifyAssocChange(TRUE); + PostMessage(hwndDlg,M_REFRESH_ICONS,0,0); + if(fRegFailed) { + } + /* autostart */ + { TCHAR *pszRunCmd; + pszRunCmd=MakeRunCommand(TRUE,TRUE); + fRegFailed=FALSE; + if(pszRunCmd!=NULL) { + fEnabled=IsDlgButtonChecked(hwndDlg,IDC_AUTOSTART); + if(fEnabled?!AddRegRunEntry(_T("MirandaIM"),pszRunCmd):!RemoveRegRunEntry(_T("MirandaIM"),pszRunCmd)) { + char *pszErr; + pszErr=GetWinErrorDescription(GetLastError()); + ShowInfoMessage(NIIF_ERROR,Translate("Autostart Error"),Translate("There was an error writing to the registry to modify the autostart list.\n\nReason: %s"),(pszErr!=NULL)?pszErr:Translate("Unknown")); + mir_free(pszErr); /* does NULL check */ + fRegFailed=TRUE; /* just show one time */ + } + mir_free(pszRunCmd); + } + } + return TRUE; + } + } /* code */ + break; + } /* idFrom */ + break; + } /* WM_NOTIFY */ + } + return FALSE; +} + +static int AssocListOptInit(WPARAM wParam,LPARAM lParam) +{ + OPTIONSDIALOGPAGE odp; + UNREFERENCED_PARAMETER(lParam); + ZeroMemory(&odp,sizeof(odp)); + odp.cbSize=sizeof(odp); + odp.hInstance=hInst; + odp.pszTemplate=MAKEINTRESOURCEA(IDD_OPT_ASSOCLIST); + odp.position=900000100; /* network opts = 900000000 */ + odp.ptszGroup=_T("Services"); /* autotranslated */ + odp.ptszTitle=_T("Associations"); /* autotranslated */ + odp.flags=ODPF_BOLDGROUPS|ODPF_EXPERTONLY|ODPF_TCHAR; + odp.pfnDlgProc=AssocListOptDlgProc; + CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp); + return 0; +} + +/************************* Misc ***********************************/ + +void InitAssocList(void) +{ + /* Options */ + INITCOMMONCONTROLSEX icc; + icc.dwSize=sizeof(icc); + icc.dwICC=ICC_LISTVIEW_CLASSES; + InitCommonControlsEx(&icc); + hHookOptInit=HookEvent(ME_OPT_INITIALISE,AssocListOptInit); + + /* Assoc List */ + pAssocList=NULL; + nAssocListCount=0; + InitializeCriticalSection(&csAssocList); + + /* Services */ + hServiceAddFile=CreateServiceFunction(MS_ASSOCMGR_ADDNEWFILETYPE,ServiceAddNewFileType); + hServiceRemoveFile=CreateServiceFunction(MS_ASSOCMGR_REMOVEFILETYPE,ServiceRemoveFileType); + hServiceAddUrl=CreateServiceFunction(MS_ASSOCMGR_ADDNEWURLTYPE,ServiceAddNewUrlType); + hServiceRemoveUrl=CreateServiceFunction(MS_ASSOCMGR_REMOVEURLTYPE,ServiceRemoveUrlType); + + /* Notify Shell */ + nNotifyTimerID=0; + InitializeCriticalSection(&csNotifyTimer); + + /* register open-with app */ + { TCHAR *pszAppFileName,*pszIconLoc,*pszRunCmd; + pszIconLoc=MakeIconLocation(NULL,0); + + // miranda32.exe + pszAppFileName=MakeAppFileName(TRUE); + pszRunCmd=MakeRunCommand(TRUE,FALSE); + if(pszAppFileName!=NULL && pszRunCmd!=NULL) + AddRegOpenWith(pszAppFileName,FALSE,_T(MIRANDANAME),pszIconLoc,pszRunCmd,NULL,NULL,NULL); + mir_free(pszRunCmd); /* does NULL check */ + mir_free(pszAppFileName); /* does NULL check */ + // assocmgr.dll + pszAppFileName=MakeAppFileName(FALSE); + pszRunCmd=MakeRunCommand(FALSE,TRUE); + if(pszAppFileName!=NULL && pszRunCmd!=NULL) + AddRegOpenWith(pszAppFileName,TRUE,_T(MIRANDANAME),pszIconLoc,pszRunCmd,DDEFILECMD,DDEAPP,DDETOPIC); + mir_free(pszRunCmd); /* does NULL check */ + mir_free(pszAppFileName); /* does NULL check */ + + mir_free(pszIconLoc); /* does NULL check */ + } + + /* default items */ + { FILETYPEDESC ftd; + ftd.cbSize=sizeof(FILETYPEDESC); + ftd.pszFileExt=".dat"; + ftd.pszMimeType=NULL; + ftd.ptszDescription=TranslateT("Miranda IM Database"); + ftd.hInstance=hInst; + ftd.nIconResID=IDI_MIRANDAFILE; + ftd.ptszVerbDesc=NULL; + ftd.pszService=NULL; + ftd.flags=FTDF_DEFAULTDISABLED|FTDF_TCHAR; + ServiceAddNewFileType(0,(LPARAM)&ftd); + } +} + +void UninitAssocList(void) +{ + BYTE fOnlyWhileRunning; + ASSOCDATA *assoc; + int i; + + /* Options */ + UnhookEvent(hHookOptInit); + + /* Services */ + DestroyServiceFunction(hServiceAddFile); + DestroyServiceFunction(hServiceRemoveFile); + DestroyServiceFunction(hServiceAddUrl); + DestroyServiceFunction(hServiceRemoveUrl); + + /* Assoc List */ + fOnlyWhileRunning=DBGetContactSettingByte(NULL,"AssocMgr","OnlyWhileRunning",SETTING_ONLYWHILERUNNING_DEFAULT); + for(i=0;ipszClassName); + mir_free(assoc->pszDescription); + mir_free(assoc->pszService); + mir_free(assoc->pszFileExt); /* does NULL check */ + mir_free(assoc->pszVerbDesc); /* does NULL check */ + mir_free(assoc->pszMimeType); /* does NULL check */ + } + mir_free(pAssocList); + DeleteCriticalSection(&csAssocList); + + /* Notify Shell */ + if(fOnlyWhileRunning && nAssocListCount) + NotifyAssocChange(TRUE); + DeleteCriticalSection(&csNotifyTimer); + + /* unregister open-with app */ + if(fOnlyWhileRunning) { + TCHAR *pszAppFileName; + // miranda32.exe + pszAppFileName=MakeAppFileName(TRUE); + if(pszAppFileName!=NULL) + RemoveRegOpenWith(pszAppFileName); + pszAppFileName=MakeAppFileName(FALSE); + // assocmgr.dll + if(pszAppFileName!=NULL) + RemoveRegOpenWith(pszAppFileName); + mir_free(pszAppFileName); /* does NULL check */ + } +} \ No newline at end of file diff --git a/plugins/AssocMgr/assoclist.h b/plugins/AssocMgr/assoclist.h new file mode 100644 index 0000000000..8e8d5cc5da --- /dev/null +++ b/plugins/AssocMgr/assoclist.h @@ -0,0 +1,33 @@ +/* + +'File Association Manager'-Plugin for Miranda IM + +Copyright (C) 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 (AssocMgr-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Assoc Enabled */ +void CleanupAssocEnabledSettings(void); +/* Mime Reg */ +void CleanupMimeTypeAddedSettings(void); +/* Assoc List Utils */ +BOOL IsRegisteredAssocItem(const char *pszClassName); +/* Open Handler */ +int InvokeFileHandler(const TCHAR *pszFileName); +int InvokeUrlHandler(const TCHAR *pszUrl); +/* Misc */ +void InitAssocList(void); +void UninitAssocList(void); diff --git a/plugins/AssocMgr/assocmgr.vcxproj b/plugins/AssocMgr/assocmgr.vcxproj new file mode 100644 index 0000000000..780e11cdbf --- /dev/null +++ b/plugins/AssocMgr/assocmgr.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + AssocMgr + + + + DynamicLibrary + Unicode + true + + + DynamicLibrary + Unicode + + + + + + + + + + + + + + + $(SolutionDir)$(Configuration)/Plugins\ + $(SolutionDir)$(Configuration)/Obj/$(ProjectName)\ + true + + + $(SolutionDir)$(Configuration)/Plugins\ + $(SolutionDir)$(Configuration)/Obj/$(ProjectName)\ + true + + + + MultiThreadedDebug + false + Disabled + Level3 + true + EditAndContinue + ../../include;../ExternalAPI;%(AdditionalIncludeDirectories) + _DEBUG;UNICODE;WIN32;_WINDOWS;_USRDLL;STRICT;ASSOCMGR_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + + + true + _DEBUG;%(PreprocessorDefinitions) + .\temp/Debug/Unicode\assocmgr.tlb + true + Win32 + + + ..\..\include\msapi + _DEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + + true + true + Windows + $(IntDir)$(TargetName).lib + 0x24300000 + comctl32.lib;%(AdditionalDependencies) + false + + + + + MultiThreaded + OnlyExplicitInline + true + true + Full + Level3 + ../../include;../ExternalAPI;%(AdditionalIncludeDirectories) + NDEBUG;UNICODE;WIN32;_WINDOWS;_USRDLL;STRICT;ASSOCMGR_EXPORTS;%(PreprocessorDefinitions) + + + Size + + + true + NDEBUG;%(PreprocessorDefinitions) + .\temp/Release/Unicode\assocmgr.tlb + false + Win32 + + + ..\..\include\msapi + NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + + true + true + Windows + $(IntDir)$(TargetName).lib + 0x24300000 + comctl32.lib;%(AdditionalDependencies) + + + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/AssocMgr/assocmgr.vcxproj.filters b/plugins/AssocMgr/assocmgr.vcxproj.filters new file mode 100644 index 0000000000..7cf75682ed --- /dev/null +++ b/plugins/AssocMgr/assocmgr.vcxproj.filters @@ -0,0 +1,153 @@ + + + + + {deefcd93-f7b0-4946-a73e-cdbbc1d23aac} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {390c6322-88b0-42e4-8b84-73e5345e945b} + h;hpp;hxx;hm;inl + + + {db2d32fd-f303-490e-bc40-ca67fd6fee73} + ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + {6f8e30e0-bc03-46e7-b039-e0fbd019afb2} + txt + + + {cc674aae-3e65-4f1c-959f-c95acfe7111a} + *.h + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Resource Files + + + Resource Files + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + SDK + + + + + Resource Files + + + + + Resource Files + + + Resource Files + + + Documentation + + + Documentation + + + Documentation + + + Documentation + + + Documentation + + + Documentation + + + Documentation + + + \ No newline at end of file diff --git a/plugins/AssocMgr/common.h b/plugins/AssocMgr/common.h new file mode 100644 index 0000000000..912f3fd9d0 --- /dev/null +++ b/plugins/AssocMgr/common.h @@ -0,0 +1,55 @@ +/* + +'File Association Manager'-Plugin for Miranda IM + +Copyright (C) 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 (AssocMgr-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#define _WIN32_WINNT 0x0600 +#define __RPCASYNC_H__ /* VC6 has a mistake in there (warning) */ +#include +#pragma warning(disable:4201) /* nonstandard extension used : nameless struct/union */ +#pragma warning(disable:4115) /* V6 has a mistake in there (warning) */ +#include /* for SHChangeNotify() */ +#pragma warning(default:4115) +#include +#pragma warning(default:4201) +#include +#include /* for mir_snprintf() */ +#include +#include + +#define MIRANDA_VER 0x0A00 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "win2k.h" + +#define ASSOCMGR_NOHELPERFUNCTIONS +#include "m_assocmgr.h" +#include "utils.h" +#include "reg.h" +#include "assoclist.h" +#include "dde.h" +#include "test.h" +#include "resource.h" diff --git a/plugins/AssocMgr/dde.c b/plugins/AssocMgr/dde.c new file mode 100644 index 0000000000..7be9f1fbcf --- /dev/null +++ b/plugins/AssocMgr/dde.c @@ -0,0 +1,293 @@ +/* + +'File Association Manager'-Plugin for Miranda IM + +Copyright (C) 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 (AssocMgr-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "common.h" + +/* Conversation */ +extern HINSTANCE hInst; +static HWND hwndDdeMsg; +/* Misc */ +static HANDLE hHookModulesLoaded,hHookPreShutdown; + +/************************* Open Handler ***************************/ + +// pszFilePath needs to be allocated using mir_alloc() +static void __stdcall FileActionAsync(TCHAR *pszFilePath) +{ + /* invoke main handler */ + switch(InvokeFileHandler(pszFilePath)) { /* pszFilePath is always a long path name */ + case 0: /* success */ break; + case CALLSERVICE_NOTFOUND: + ShowInfoMessage(NIIF_ERROR,Translate("Miranda IM could not open file"),Translate("Miranda IM was not able to open \""TCHAR_STR_PARAM"\".\n\nThere is no registered handler for this file type."),pszFilePath); + break; + default: + ShowInfoMessage(NIIF_ERROR,Translate("Miranda IM could not open file"),Translate("Miranda IM was not able to open \""TCHAR_STR_PARAM"\".\n\nThe file could not be processed."),pszFilePath); + } + mir_free(pszFilePath); /* async param */ +} + +// pszUrl needs to be allocated using mir_alloc() +static void __stdcall UrlActionAsync(TCHAR *pszUrl) +{ + /* invoke main handler */ + switch(InvokeUrlHandler(pszUrl)) { + case 0: /* success */ break; + case CALLSERVICE_NOTFOUND: + ShowInfoMessage(NIIF_ERROR,Translate("Miranda IM could not open URL"),Translate("Miranda IM was not able to open \""TCHAR_STR_PARAM"\".\n\nThere is no registered handler for this URL type."),pszUrl); + break; + default: + ShowInfoMessage(NIIF_ERROR,Translate("Miranda IM could not open URL"),Translate("Miranda IM was not able to open \""TCHAR_STR_PARAM"\".\n\nThe given URL is invalid and can not be parsed."),pszUrl); + } + mir_free(pszUrl); /* async param */ +} + +/************************* Conversation ***************************/ + +#define DDEMESSAGETIMEOUT 30000 +#define WNDCLASS_DDEMSGWINDOW _T("MirandaDdeMsgWindow") + +// returned pointer points into a substring of ppszString +// returns an empty string if the string does not have enough arguments +static TCHAR* GetExecuteParam(TCHAR **ppszString) +{ + TCHAR *pszParam,*p; + BOOL fQuoted; + + fQuoted=(**ppszString==_T('"')); + pszParam=*ppszString; + if(fQuoted) pszParam++; + p=_tcschr(pszParam,(TCHAR)(fQuoted?_T('"'):_T(','))); + if(p!=NULL) { + *(p++)=0; + if(fQuoted && *p==_T(',')) p++; + } else p=&pszParam[lstrlen(pszParam)]; + *ppszString=p; + return pszParam; +} + +static LRESULT CALLBACK DdeMessageWindow(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) +{ + switch(msg) { + case WM_DDE_INITIATE: + { ATOM hSzApp,hSzTopic; + hSzApp=LOWORD(lParam); /* no UnpackDDElParam() here */ + hSzTopic=HIWORD(lParam); + if((hSzApp==GlobalFindAtom(DDEAPP) && hSzTopic==GlobalFindAtom(DDETOPIC)) || !hSzApp) { + hSzApp=GlobalAddAtom(DDEAPP); + hSzTopic=GlobalAddAtom(DDETOPIC); + if(hSzApp && hSzTopic) + /* PackDDElParam() only for posted msgs */ + SendMessage((HWND)wParam,WM_DDE_ACK,(WPARAM)hwnd,MAKELPARAM(hSzApp,hSzTopic)); + if(hSzApp) GlobalDeleteAtom(hSzApp); + if(hSzTopic) GlobalDeleteAtom(hSzTopic); + } + return 0; + } + case WM_DDE_EXECUTE: /* posted message */ + { HGLOBAL hCommand; + TCHAR *pszCommand; + DDEACK ack; + ZeroMemory(&ack,sizeof(ack)); + if(UnpackDDElParam(msg,lParam,NULL,(PUINT)&hCommand)) { + #if defined(_UNICODE) + /* ANSI execute command can't happen for shell */ + if(IsWindowUnicode((HWND)wParam)) { + #endif + pszCommand=GlobalLock(hCommand); + if(pszCommand!=NULL) { + TCHAR *pszAction,*pszArg; + pszAction=GetExecuteParam(&pszCommand); + pszArg=mir_tstrdup(GetExecuteParam(&pszCommand)); + if(pszArg!=NULL) { + /* we are inside miranda here, we make it async so the shell does + * not timeout regardless what the plugins try to do. */ + if(!lstrcmpi(pszAction,_T("file"))) + ack.fAck=(short)(CallFunctionAsync(FileActionAsync,pszArg)!=0); + else if(!lstrcmpi(pszAction,_T("url"))) + ack.fAck=(short)(CallFunctionAsync(UrlActionAsync,pszArg)!=0); + if(!ack.fAck) mir_free(pszArg); /* otherwise freed by asyncproc */ + } + GlobalUnlock(hCommand); + } + #if defined(_UNICODE) + } + #endif + lParam=ReuseDDElParam(lParam,msg,WM_DDE_ACK,*(PUINT)&ack,(UINT)hCommand); + if(!PostMessage((HWND)wParam,WM_DDE_ACK,(WPARAM)hwnd,lParam)) { + GlobalFree(hCommand); + FreeDDElParam(WM_DDE_ACK,lParam); + } + } + return 0; + } + case WM_DDE_TERMINATE: + PostMessage((HWND)wParam,msg,(WPARAM)hwnd,0); /* ack reply */ + return 0; + case WM_DDE_REQUEST: + case WM_DDE_ADVISE: + case WM_DDE_UNADVISE: + case WM_DDE_POKE: + /* fail safely for those to avoid memory leak in lParam */ + { ATOM hSzItem; + DDEACK ack; + ZeroMemory(&ack,sizeof(ack)); + if(UnpackDDElParam(msg,lParam,NULL,(PUINT)&hSzItem)) { + lParam=ReuseDDElParam(lParam,msg,WM_DDE_ACK,*(PUINT)&ack,(UINT)hSzItem); + if(!PostMessage((HWND)wParam,WM_DDE_ACK,(WPARAM)hwnd,lParam)) { + if(hSzItem) GlobalDeleteAtom(hSzItem); + FreeDDElParam(WM_DDE_ACK,lParam); + } + } + return 0; + } + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + +// CloseHandle() the return value +static HANDLE StartupMainProcess(TCHAR *pszDatabasePath) +{ + TCHAR *p,szPath[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFO si; + + /* we are inside RunDll32 here */ + if(!GetModuleFileName(hInst,szPath,SIZEOF(szPath))) return NULL; + p=_tcsrchr(szPath,_T('\\')); + if(p!=NULL) { *p=0; p=_tcsrchr(szPath,_T('\\')); } + if(p==NULL) return NULL; + lstrcpy(++p,_T("miranda32.exe")); + + /* inherit startup data from RunDll32 process */ + GetStartupInfo(&si); + if(!CreateProcess(szPath,pszDatabasePath,NULL,NULL,TRUE,GetPriorityClass(GetCurrentProcess()),NULL,NULL,&si,&pi)) + return NULL; + CloseHandle(pi.hThread); + return pi.hProcess; +} + +#ifdef __cplusplus +extern "C" { +#endif + +// entry point for RunDll32, this is also WaitForDDEW +__declspec(dllexport) void CALLBACK WaitForDDE(HWND hwnd,HINSTANCE hinstExe,TCHAR *pszCmdLine,int nCmdShow) +{ + HANDLE pHandles[2]; + DWORD dwTick; + UNREFERENCED_PARAMETER(hinstExe); + UNREFERENCED_PARAMETER(nCmdShow); /* obeys nCmdShow using GetStartupInfo() */ + UNREFERENCED_PARAMETER(hwnd); + + /* wait for dde window to be available (avoiding race condition) */ + pHandles[0]=CreateEvent(NULL,TRUE,FALSE,WNDCLASS_DDEMSGWINDOW); + if(pHandles[0]!=NULL) { + pHandles[1]=StartupMainProcess(pszCmdLine); /* obeys nCmdShow using GetStartupInfo() */ + if(pHandles[1]!=NULL) { + dwTick=GetTickCount(); + /* either process terminated or dde window created */ + if(WaitForMultipleObjects(SIZEOF(pHandles),pHandles,FALSE,DDEMESSAGETIMEOUT)==WAIT_OBJECT_0) { + dwTick=GetTickCount()-dwTick; + if(dwTick 'Settings' -> 'Linker' -> 'Output' -> 'Base Address' + +H. Herkenrath (hrathh at users.sourceforge.net) diff --git a/plugins/AssocMgr/docs/AssocMgr-License.txt b/plugins/AssocMgr/docs/AssocMgr-License.txt new file mode 100644 index 0000000000..a726a52df1 --- /dev/null +++ b/plugins/AssocMgr/docs/AssocMgr-License.txt @@ -0,0 +1,278 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. diff --git a/plugins/AssocMgr/docs/AssocMgr-Readme.txt b/plugins/AssocMgr/docs/AssocMgr-Readme.txt new file mode 100644 index 0000000000..7695790522 --- /dev/null +++ b/plugins/AssocMgr/docs/AssocMgr-Readme.txt @@ -0,0 +1,159 @@ + +File Association Manager 0.1.1.0 +------------------------------------------------------------------------ + Plugin for Miranda IM 0.6 and + + + Plugin Info: ---------------------------- + | Version: 0.1.1.0 + | Filename: assocmgr.dll + | Author: H. Herkenrath (hrathh at users.sourceforge.net) + | Description: Handles file types and URLs like aim, + | ymsgr, xmpp, wpmsg, gg, tlen. + + Contents: ------------------------------- + | Features, Requirements, Usage, Installation, Browsers, + | Bugs and Wishes, To-Do List, Version History, + | Thanks, Translation, License and Copyright + +Features +---------------------- + + Allows file types and URLs to be opened using Miranda IM and plugins + + If Miranda IM is is not running when an associated file or URL is opened + it gets invoked automatically + + You can add contacts easily from websites and forums + + Adds an option to start Miranda IM automatically with your Computer + + Offers the possibility to associate the file types only while running + + Provides register services for other plugins (very easy to use) + + Supports an unlimited amount of URLs or file types to be registered + + Full Unicode support + + All registry keys are cleanly generated and can completely be removed + + Keeps a backup of overwritten file extensions and restores it + + Backup gets restored only if it is still valid + + Auto-cleanup for old database settings of uninstalled plugins + + Automatic installation of all files, just unzip into Plugins directory + + Supported plugins: Database Editor++ + +Requirements +---------------------- + -> Miranda IM 0.6+: + Miranda IM is needed in version 0.6 or later. + +Usage +---------------------- + The registered associations of 'File Association Manager' can be + configured here: 'Options' -> 'Services' -> 'Associations' + You need to have other plugins installed that actually + offer URL protocols or file type associations. + This plugin only offers the registration service, it does not + add any file associations on its own. + +Installation +---------------------- + Find 'miranda32.exe' on your computer. + + Just copy all the contents of the zip-file as they are into the 'Plugins' directory + in the Miranda IM folder. + 'File Association Manager' will detect the files and move them into the appropriate + directories on it's first run. + + You can also do all the installation by hand, if you want to: + + Main Plugin: Copy the file 'assocmgr.dll' into the 'Plugins' subdirectory + in the Miranda IM folder. + + The Unicode version of the plugin will only work on Windows NT/2000/XP, + Windows Server 2003, Windows Vista or later with an installed + Unicode version of Miranda IM. + To use it on Windows 95/98/Me, please download the ANSI version of the plugin. + + Documentation: The txt-files should be moved along with the SDK-zip into the + 'Docs' directory in the Miranda IM folder. + + That's it! + +Bugs and Wishes +---------------------- + Feel free to mail me your wishes about 'File Association Manager' and tell + me all the bugs you may find. + My email address is: hrathh at users.sourceforge.net + +To-Do List (random ideas) +---------------------- + - draw a better mirandafile icon...anyone? + - outsource UrlDecode() function into MS_NETLIB_URLDECODE (MS_NETLIB_URLENCODE already exits) + add nice wrapper Netlib_UrlDecode() as this needs to be called in each urlparser service (substrings) + - test using different browsers + - remove demo purpose code (after some time) + - dreaming of an installer.dll plugin that registers on + mir-files to unzip them...anyone? + +Version History +---------------------- + 0.1.1.0 - keeps a backup of overwritten file extensions and restores it + - backup only is restored if it is still valid + - auto-cleanup for old database settings of uninstalled plugins + - workaround for WinXP bug causing an scrolling glitch + on ListView controls with groups + - added FTDF_ISSHORTCUT flag for file types + - list gets sorted using langpack locale + - fixes for Win9x/NT4 + - minor improvements + 0.1.0.3 - really fixed crash on options + - minor fixes + 0.1.0.2 - fixed crash on options + 0.1.0.1 - added v0.8 support + - minor improvements + 0.1.0.0 - Initial release + +Thanks +---------------------- + * To Jacek_FH who wrote the ProtoLink plugin which first + summed up all link handling, but was not extensible. + * To egoDust who wrote the unfinished icqwm plugin to + handle icq-files + * To rainwater for the aim links part of the AIM TOC protocol + * To Sergey Gershovich (Jazzy) who wrote the Autorun plugin + +Translation +---------------------- + Translation strings for language pack maintainers can be found + in 'AssocMgr-Translation.txt' of the SDK package. + +License and Copyright +---------------------- + 'File Association Manager'" is released under the terms of the + GNU General Public License. + See "AssocMgr-License.txt" for more details. + + 'File Association Manager' is copyright 2005-2007 by H. Herkenrath. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +H. Herkenrath (hrathh at users.sourceforge.net) diff --git a/plugins/AssocMgr/docs/AssocMgr-Translation.txt b/plugins/AssocMgr/docs/AssocMgr-Translation.txt new file mode 100644 index 0000000000..46e992516a --- /dev/null +++ b/plugins/AssocMgr/docs/AssocMgr-Translation.txt @@ -0,0 +1,86 @@ + +File Association Manager 0.1.1.0 for Miranda IM 0.6+ +------------------------------------------------------------------------ + Translator Information + + Contents: ------------------------------- + | General Info, German Strings + + +General Info +----------------------------- + 'File Association Manager' can be translated with the Miranda IM + language files. + + Put the following strings in a file called 'langpack_.txt' + in the Miranda IM directory and translate them into your + language. + If you need more info on Miranda IM language files visit: + http://miranda.svn.sourceforge.net/viewvc/*checkout*/miranda/trunk/miranda/i18n/readme.txt + +German Strings +----------------------------- + +; --- Plugin: File Association Manager 0.1.1.0 (German) --- +; Translation by hrathh +; Please report any mistakes or missing strings in here. + +[Handles file type associations and URLs like aim, ymsgr, xmpp, wpmsg, gg, tlen.] +Verwaltet Verknüpfungen mit Dateien und URLs wie aim, ymsgr, xmpp, wpmsg, gg, tlen. +[File Association Manager Plugin] +Verknüpfungsmanager-Plugin +[The File Association Manager Plugin can not be loaded. It requires Miranda IM %s or later.] +Das Verknüpfungsmanager-Plugin kann nicht geladen werden. Es benötigt Miranda IM %s oder höher. + +; Options +;[Services] +[Associations] +Verknüpfungen +[Registered associations for Miranda IM:] +Verknüpfungen, die mit Miranda IM assoziiert sind: +[Type] +Typ +[%hs files] +%hs-Dateien +;[%hs:] +[Description] +Beschreibung +[URLs on Websites] +URLs auf Websiten +[File Types] +Dateitypen +[Miscellaneous] +Sonstiges +[&Start Miranda IM automatically when the Computer starts (using current profile)] +Miranda IM automatisch bei &Systemstart ausführen (mit dem aktuellen Profil) +[Only associate with Miranda IM while it is &running] +Nur mit Miranda IM verknüpfen solange das Programm &ausgeführt wird + +; Errors +[File Association Error] +Verknüpfungsproblem +[There was an error writing to the registry to modify the file/url associations.\nReason: %s] +Es trat ein Fehler auf beim Schreiben in die Systemregistrierung. Die Datei- bzw. URL-Verknüpfungen konnten nicht verandert werden.\nGrund: %s +[Unknown] +Unbekannt +[Autostart Error] +Autostart-Fehler +[There was an error writing to the registry to modify the autostart list.\n\nReason: %s] +Es trat ein Fehler auf beim Schreiben in die Systemregistrierung. Der Autostarteintrag konnte nicht verandert werden.\n\nGrund: %s +[Miranda IM could not open the file] +Miranda IM konnte die angebene Datei nicht öffnen +[Miranda IM was not able to open "%s".\n\nThere is no registered handler for this file type.] +Die Datei "%s" konnte von Miranda IM nicht geöffnet werden.\n\nFür den Dateityp ist kein Handler definiert. +[Miranda IM was not able to open "%s".\n\nThe file could not be processed.] +Die Datei "%s" konnte von Miranda IM nicht geöffnet werden.\n\nBeim Verarbeiten der Datei trat ein Fehler auf. +[Miranda IM could not open URL] +Miranda IM konnte die angegebene URL nicht öffnen +[Miranda IM was not able to open "%s".\n\nThere is no registered handler for this URL type.] +Die URL "%s" konnte von Miranda IM nicht geöffnet werden.\n\nFur den URL-Typ ist kein Handler definiert. +[Miranda IM was not able to open "%s".\n\nThe given URL is invalid and can not be parsed.] +Die Datei "%s" konnte von Miranda IM nicht geöffnet werden.\n\nDie URL is ungültig und kann nicht ausgewertet werden. + +; --- + + +H. Herkenrath (hrathh at users.sourceforge.net) diff --git a/plugins/AssocMgr/main.c b/plugins/AssocMgr/main.c new file mode 100644 index 0000000000..60d6a8b5a0 --- /dev/null +++ b/plugins/AssocMgr/main.c @@ -0,0 +1,175 @@ +/* + +'File Association Manager'-Plugin for Miranda IM + +Copyright (C) 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 (AssocMgr-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "common.h" +#include "version.h" + +HINSTANCE hInst; +PLUGINLINK *pluginLink; +struct MM_INTERFACE mmi; +struct UTF8_INTERFACE utfi; +static HANDLE hHookModulesLoaded; +int hLangpack; + +static char szPluginInfoEmail[]=PLUGIN_EMAIL; +PLUGININFOEX pluginInfo={ + sizeof(PLUGININFOEX), + "File Association Manager", + PLUGIN_VERSION, +#if defined(_DEBUG) + "Development build not intended for release. ("__DATE__")", /* autotranslated */ +#else + "Handles file type associations and URLs like aim, ymsgr, xmpp, wpmsg, gg, tlen.", /* autotranslated */ +#endif + "H. Herkenrath", + szPluginInfoEmail, /* @ will be set later */ + "© 2005-2007 H. Herkenrath", + PLUGIN_WEBSITE, + UNICODE_AWARE, + 0, +#if defined(_UNICODE) + // {52685CD7-0EC7-44c1-A1A6-381612418202} + {0x52685cd7,0xec7,0x44c1,{0xa1,0xa6,0x38,0x16,0x12,0x41,0x82,0x2}}, +#else + // {48692828-D4BA-43b5-BF81-44F384811569} + {0x48692828,0xd4ba,0x43b5,{0xbf,0x81,0x44,0xf3,0x84,0x81,0x15,0x69}} +#endif +}; +static const MUUID interfaces[]={MIID_ASSOCMGR,MIID_AUTORUN,MIID_LAST}; + +BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,void *pReserved) +{ + UNREFERENCED_PARAMETER(pReserved); + if(fdwReason==DLL_PROCESS_ATTACH) { + /* Do not call this from a DLL that is linked to the static C run-time library (CRT). + * The static CRT requires DLL_THREAD_ATTACH and DLL_THREAD_DETATCH notifications + * to function properly. */ + DisableThreadLibraryCalls(hInst=hinstDLL); + } + return TRUE; +} + +static void InstallFile(const TCHAR *pszFileName,const TCHAR *pszDestSubDir) +{ + TCHAR szFileFrom[MAX_PATH+1],szFileTo[MAX_PATH+1],*p; + HANDLE hFile; + + if(!GetModuleFileName(hInst,szFileFrom,SIZEOF(szFileFrom)-lstrlen(pszFileName))) + return; + p=_tcsrchr(szFileFrom,_T('\\')); + if(p!=NULL) *(++p)=0; + lstrcat(szFileFrom,pszFileName); /* buffer safe */ + + hFile=CreateFile(szFileFrom,0,FILE_SHARE_READ,0,OPEN_EXISTING,0,0); + if(hFile==INVALID_HANDLE_VALUE) return; + CloseHandle(hFile); + + if(!GetModuleFileNameWorkaround(NULL,szFileTo,SIZEOF(szFileTo)-lstrlen(pszDestSubDir)-lstrlen(pszFileName))) + return; + p=_tcsrchr(szFileTo,_T('\\')); + if(p!=NULL) *(++p)=0; + lstrcat(szFileTo,pszDestSubDir); /* buffer safe */ + CreateDirectory(szFileTo,NULL); + lstrcat(szFileTo,pszFileName); /* buffer safe */ + + if(!MoveFile(szFileFrom,szFileTo) && GetLastError()==ERROR_ALREADY_EXISTS) { + DeleteFile(szFileTo); + MoveFile(szFileFrom,szFileTo); + } +} + +static int AssocMgrModulesLoaded(WPARAM wParam,LPARAM lParam) +{ + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + if(ServiceExists("DBEditorpp/RegisterSingleModule")) + CallService("DBEditorpp/RegisterSingleModule",(WPARAM)"AssocMgr",0); + InitTest(); + return 0; +} + +#ifdef __cplusplus +extern "C" { +#endif + +__declspec(dllexport) const PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion) +{ + if(mirandaVersionsizeof(TCHAR)) { + pszVal=(TCHAR*)mir_alloc(dwSize+sizeof(TCHAR)); + if(pszVal!=NULL) { + /* get value */ + if(!RegQueryValueEx(hKey,pszValName,NULL,&dwType,(BYTE*)pszVal,&dwSize)) { + pszVal[dwSize/sizeof(TCHAR)]=0; + if(dwType==REG_EXPAND_SZ) { + dwSize=MAX_PATH; + pszVal2=(TCHAR*)mir_alloc(dwSize*sizeof(TCHAR)); + if(ExpandEnvironmentStrings(pszVal,pszVal2,dwSize)) { + mir_free(pszVal); + return pszVal2; + } + mir_free(pszVal2); + } else if(dwType==REG_SZ) + return pszVal; + } + mir_free(pszVal); + } + } + return NULL; +} + +// hKey must have been opened with KEY_QUERY_VALUE access right +static BOOL IsRegStrValue(HKEY hKey,const TCHAR *pszValName,const TCHAR *pszCmpVal) +{ + BOOL fSame=FALSE; + TCHAR *pszVal; + pszVal=GetRegStrValue(hKey,pszValName); + if(pszVal!=NULL) { + fSame=!lstrcmp(pszVal,pszCmpVal); + mir_free(pszVal); + } + return fSame; +} + +// hKey must have been opened with KEY_QUERY_VALUE access right +static BOOL IsRegStrValueA(HKEY hKey,const TCHAR *pszValName,const char *pszCmpVal) +{ + BOOL fSame=FALSE; + TCHAR *pszVal; + char *pszValA; + pszVal=GetRegStrValue(hKey,pszValName); + if(pszVal!=NULL) { + pszValA=t2a(pszVal); + if(pszValA!=NULL) + fSame=!lstrcmpA(pszValA,pszCmpVal); + mir_free(pszValA); /* does NULL check */ + mir_free(pszVal); + } + return fSame; +} + +/************************* Backup to DB ***************************/ + +#define REGF_ANSI 0x80000000 /* this bit is set in dwType for ANSI registry data */ + +// pData must always be Unicode data, registry supports Unicode even on Win95 +static void WriteDbBackupData(const char *pszSetting,DWORD dwType,BYTE *pData,DWORD cbData) +{ + DBCONTACTWRITESETTING dbcws; + dbcws.szModule="AssocMgr"; + dbcws.szSetting=pszSetting; + dbcws.value.type=DBVT_BLOB; + dbcws.value.cpbVal=(WORD)(cbData+sizeof(DWORD)); + dbcws.value.pbVal=(BYTE*)mir_alloc(cbData+sizeof(DWORD)); + if(dbcws.value.pbVal==NULL) return; + *(DWORD*)dbcws.value.pbVal=dwType; + CopyMemory(dbcws.value.pbVal+sizeof(DWORD),pData,cbData); + CallService(MS_DB_CONTACT_WRITESETTING,0,(LPARAM)&dbcws); + mir_free(dbcws.value.pbVal); +} + +// mir_free() the value returned in ppData +static BOOL ReadDbBackupData(const char *pszSetting,DWORD *pdwType,BYTE **ppData,DWORD *pcbData) +{ + DBCONTACTGETSETTING dbcgs; + DBVARIANT dbv; + dbcgs.szModule="AssocMgr"; + dbcgs.szSetting=pszSetting; + dbcgs.pValue=&dbv; + if(!CallService(MS_DB_CONTACT_GETSETTING,0,(LPARAM)&dbcgs)) { + if(dbv.type==DBVT_BLOB && dbv.cpbVal>=sizeof(DWORD)) { + *pdwType=*(DWORD*)dbv.pbVal; + *ppData=dbv.pbVal; + *pcbData=dbv.cpbVal-sizeof(DWORD); + MoveMemory(*ppData,*ppData+sizeof(DWORD),*pcbData); + return TRUE; + } + CallService(MS_DB_CONTACT_FREEVARIANT,0,(LPARAM)&dbv); + } + return FALSE; +} + +struct BackupRegTreeParam { + char **ppszDbPrefix; + DWORD *pdwDbPrefixSize; + int level; +}; + +static void BackupRegTree_Worker(HKEY hKey,const char *pszSubKey,struct BackupRegTreeParam *param) +{ + LONG res; + DWORD nMaxSubKeyLen,nMaxValNameLen,nMaxValSize; + DWORD index,cchName,dwType,cbData; + BYTE *pData; + char *pszName; + register TCHAR *ptszName; + DWORD nDbPrefixLen; + if((res=RegOpenKeyExA(hKey,pszSubKey,0,KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS,&hKey))==ERROR_SUCCESS) { + if((res=RegQueryInfoKey(hKey,NULL,NULL,NULL,NULL,&nMaxSubKeyLen,NULL,NULL,&nMaxValNameLen,&nMaxValSize,NULL,NULL))==ERROR_SUCCESS) { + if(nMaxSubKeyLen>nMaxValNameLen) nMaxValNameLen=nMaxSubKeyLen; + /* prepare buffer */ + nDbPrefixLen=(DWORD)lstrlenA(*param->ppszDbPrefix)+lstrlenA(pszSubKey)+1; + cchName=nDbPrefixLen+nMaxValNameLen+3; + if(cchName>*param->pdwDbPrefixSize) { + pszName=(char*)mir_realloc(*param->ppszDbPrefix,cchName); + if(pszName==NULL) return; + *param->ppszDbPrefix=pszName; + *param->pdwDbPrefixSize=cchName; + } + lstrcatA(lstrcatA(*param->ppszDbPrefix,pszSubKey),"\\"); /* buffer safe */ + /* enum values */ + pszName=(char*)mir_alloc(nMaxValNameLen+1); + if(nMaxValSize==0) nMaxValSize=1; + pData=(BYTE*)mir_alloc(nMaxValSize); + if(pszName!=NULL && pData!=NULL) { + index=0; + while(!res) { + cchName=nMaxValNameLen+1; + cbData=nMaxValSize; + if((res=RegEnumValueA(hKey,index++,pszName,&cchName,NULL,NULL,NULL,NULL))==ERROR_SUCCESS) { + (*param->ppszDbPrefix)[nDbPrefixLen]=0; + lstrcatA(*param->ppszDbPrefix,pszName); /* buffer safe */ + ptszName=a2t(pszName); + if(ptszName!=NULL) { + if(!RegQueryValueEx(hKey,ptszName,NULL,&dwType,pData,&cbData)) { + #ifdef _UNICODE + WriteDbBackupData(*param->ppszDbPrefix,dwType,pData,cbData); + #else + if(!(dwType®F_ANSI)) /* sanity check, never happens */ + WriteDbBackupData(*param->ppszDbPrefix,dwType®F_ANSI,pData,cbData); + #endif + } + mir_free(ptszName); + } + } + } + if(res==ERROR_NO_MORE_ITEMS) res=ERROR_SUCCESS; + } + mir_free(pData); /* does NULL check */ + /* enum subkeys */ + if(param->level<32 && pszName!=NULL) { + ++param->level; /* can be max 32 levels deep (after prefix), restriction of RegCreateKeyEx() */ + index=0; + while(!res) { + cchName=nMaxSubKeyLen+1; + if((res=RegEnumKeyExA(hKey,index++,pszName,&cchName,NULL,NULL,NULL,NULL))==ERROR_SUCCESS) { + (*param->ppszDbPrefix)[nDbPrefixLen]=0; + BackupRegTree_Worker(hKey,pszName,param); /* recursion */ + } + } + } + if(res==ERROR_NO_MORE_ITEMS) res=ERROR_SUCCESS; + mir_free(pszName); /* does NULL check */ + } + RegCloseKey(hKey); + } +} + +static void BackupRegTree(HKEY hKey,const char *pszSubKey,const char *pszDbPrefix) +{ + struct BackupRegTreeParam param; + DWORD dwDbPrefixSize; + param.level=0; + param.pdwDbPrefixSize=&dwDbPrefixSize; + param.ppszDbPrefix=(char**)&pszDbPrefix; + pszDbPrefix=mir_strdup(pszDbPrefix); + if(pszDbPrefix!=NULL) { + dwDbPrefixSize=lstrlenA(pszDbPrefix)+1; + BackupRegTree_Worker(hKey,pszSubKey,¶m); + mir_free((char*)pszDbPrefix); + } +} + +static LONG RestoreRegTree(HKEY hKey,const char *pszSubKey,const char *pszDbPrefix) +{ + char **ppszSettings,*pszSuffix,*pszPrefixWithSubKey; + int nSettingsCount,i,nDbPrefixLen,nPrefixWithSubKeyLen; + char *pslash=NULL,*pnext,*pkeys; + char *pszValName; + WCHAR *pwszValName; + HKEY hSubKey; + DWORD dwType,cbData; + BYTE *pData; + LONG res; + + nDbPrefixLen=lstrlenA(pszDbPrefix); + nPrefixWithSubKeyLen=nDbPrefixLen+lstrlenA(pszSubKey)+1; + pszPrefixWithSubKey=(char*)mir_alloc(nPrefixWithSubKeyLen+1); + if(pszPrefixWithSubKey==NULL) return ERROR_OUTOFMEMORY; + lstrcatA(lstrcatA(lstrcpyA(pszPrefixWithSubKey,pszDbPrefix),pszSubKey),"\\"); /* buffer safe */ + res=ERROR_NO_MORE_ITEMS; + if(pszPrefixWithSubKey!=NULL) { + if(EnumDbPrefixSettings("AssocMgr",pszPrefixWithSubKey,&ppszSettings,&nSettingsCount)) { + for(i=0;i + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Englisch (GB) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE MOVEABLE PURE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE MOVEABLE PURE +BEGIN + "#include \r\n" + "\0" +END + +3 TEXTINCLUDE MOVEABLE PURE +BEGIN + "#include ""version.rc""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPT_ASSOCLIST DIALOGEX 0, 0, 301, 227 +STYLE DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Registered associations for Miranda IM:", + IDC_HEADERTEXT,3,0,295,8,SS_NOPREFIX + CONTROL "",IDC_ASSOCLIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | + WS_BORDER | WS_TABSTOP,3,10,295,169 + GROUPBOX "Miscellaneous",IDC_MISCLABEL,3,183,295,41,WS_GROUP + CONTROL "&Start Miranda IM automatically when the Computer starts (using current profile)", + IDC_AUTOSTART,"Button",BS_AUTOCHECKBOX | BS_TOP | + WS_TABSTOP,10,195,284,10 + CONTROL "Only associate with Miranda IM while it is &running", + IDC_ONLYWHILERUNNING,"Button",BS_AUTOCHECKBOX | BS_TOP | + WS_TABSTOP,10,208,284,10 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MIRANDA ICON DISCARDABLE "res/miranda.ico" +IDI_MIRANDAFILE ICON DISCARDABLE "res/mirandafile.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_OPT_ASSOCLIST, DIALOG + BEGIN + VERTGUIDE, 3 + VERTGUIDE, 10 + VERTGUIDE, 294 + VERTGUIDE, 298 + END +END +#endif // APSTUDIO_INVOKED + +#endif // Englisch (GB) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/AssocMgr/test.c b/plugins/AssocMgr/test.c new file mode 100644 index 0000000000..0da578d4ec --- /dev/null +++ b/plugins/AssocMgr/test.c @@ -0,0 +1,869 @@ +/* + +'File Association Manager'-Plugin for Miranda IM + +Copyright (C) 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 (AssocMgr-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +// -- Includes +#include "common.h" + +/* +#define AIM_SUPPORT_TEST +#define ICQ_SUPPORT_TEST +#define YAHOO_SUPPORT_TEST +#define MSN_SUPPORT_TEST +#define GG_SUPPORT_TEST +#define JABBER_SUPPORT_TEST +*/ + +// ----------------------------------------- + +extern HINSTANCE hInst; + +/* a copy of those in m_assocmgr.h, as we did not #include those helpers */ +__inline static int AssocMgr_AddNewFileTypeT(const char *ext,const char *mime,const TCHAR *desc,const TCHAR *verb,HINSTANCE hinst,UINT iconid,const char *service,DWORD flags) +{ + FILETYPEDESC ftd; + ftd.cbSize=sizeof(FILETYPEDESC); + ftd.pszFileExt=ext; + ftd.pszMimeType=mime; + ftd.ptszDescription=desc; + ftd.hInstance=hinst; + ftd.nIconResID=iconid; + ftd.ptszVerbDesc=verb; + ftd.pszService=service; + ftd.flags=flags|FTDF_TCHAR; + return CallService(MS_ASSOCMGR_ADDNEWFILETYPE,0,(LPARAM)&ftd); +} + +static int __inline AssocMgr_AddNewUrlType(const char *prefix,const char *desc,HINSTANCE hinst,UINT iconid,const char *service,DWORD flags) +{ + URLTYPEDESC utd; + utd.cbSize=sizeof(URLTYPEDESC); + utd.pszProtoPrefix=prefix; + utd.pszDescription=desc; + utd.hInstance=hinst; + utd.nIconResID=iconid; + utd.pszService=service; + utd.flags=flags&~UTDF_UNICODE; + return CallService(MS_ASSOCMGR_ADDNEWURLTYPE,0,(LPARAM)&utd); +} + +// ----------------------------------------- + +#ifdef AIM_SUPPORT_TEST + +#define AIM_PROTOCOL_NAME "AIM" +#define IDI_AOL 28 +#define MOD_KEY_CL "CList" +#define AIM_KEY_NL "NotOnList" +struct oscar_data { HINSTANCE hInstance; } static conn; +static __inline HANDLE find_contact(const char *nick) { nick; return NULL; } +static __inline HANDLE add_contact(const char *nick) { nick; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); } +static __inline void aim_gchat_joinrequest(const char *room, int exchange) { room; exchange; MessageBoxA(NULL,"Join group chat!",room,MB_OK); return; } +#include +#include +#include + +#include "m_assocmgr.h" +static HANDLE hHookModulesLoaded; +static HANDLE hServiceParseLink; + +static int ServiceParseAimLink(WPARAM wParam,LPARAM lParam) +{ + char *arg=(char*)lParam; + UNREFERENCED_PARAMETER(wParam); + if(arg==NULL) return 1; /* sanity check */ + /* skip leading prefix */ + arg=strchr(arg,':'); + if(arg==NULL) return 1; /* parse failed */ + for(++arg;*arg=='/';++arg); + /* + add user: aim:addbuddy?screenname=NICK&groupname=GROUP + send message: aim:goim?screenname=NICK&message=MSG + open chatroom: aim:gochat?roomname=ROOM&exchange=NUM + */ + /* add a contact to the list */ + if(!_strnicmp(arg,"addbuddy?",9)) { + char *tok,*sn=NULL,*group=NULL; + ADDCONTACTSTRUCT acs; + PROTOSEARCHRESULT psr; + if(*(arg+=9)==0) return 1; /* parse failed */ + tok=strtok(arg,"&"); /* first token */ + while(tok!=NULL) { + if(!_strnicmp(tok,"screenname=",11) && *(tok+11)!=0) + sn=Netlib_UrlDecode(tok+11); + if(!_strnicmp(tok,"groupname=",10) && *(tok+10)!=0) + group=Netlib_UrlDecode(tok+10); /* group is currently ignored */ + tok=strtok(NULL,"&"); /* next token */ + } + if(sn==NULL) return 1; /* parse failed */ + if(find_contact(sn)==NULL) { /* does not yet check if sn is current user */ + acs.handleType=HANDLE_SEARCHRESULT; + acs.szProto=AIM_PROTOCOL_NAME; + acs.psr=&psr; + ZeroMemory(&psr,sizeof(PROTOSEARCHRESULT)); + psr.cbSize=sizeof(PROTOSEARCHRESULT); + psr.nick=sn; + CallService(MS_ADDCONTACT_SHOW,(WPARAM)NULL,(LPARAM)&acs); + } + return 0; + } + /* send a message to a contact */ + else if(!_strnicmp(arg,"goim?",5)) { + char *tok,*sn=NULL,*msg=NULL; + HANDLE hContact; + if(*(arg+=5)==0) return 1; /* parse failed */ + tok=strtok(arg,"&"); /* first token */ + while(tok!=NULL) { + if(!_strnicmp(tok,"screenname=",11) && *(tok+11)!=0) + sn=Netlib_UrlDecode(tok+11); + if(!_strnicmp(tok,"message=",8) && *(tok+8)!=0) + msg=Netlib_UrlDecode(tok+8); + tok=strtok(NULL,"&"); /* next token */ + } + if(sn==NULL) return 1; /* parse failed */ + if(ServiceExists(MS_MSG_SENDMESSAGE)) { + hContact=find_contact(sn); + if(hContact==NULL) { + hContact=add_contact(sn); /* does not yet check if sn is current user */ + if(hContact!=NULL) + DBWriteContactSettingByte(hContact,MOD_KEY_CL,AIM_KEY_NL,1); + } + if(hContact!=NULL) + CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)msg); + } + return 0; + } + /* open a chatroom */ + else if(!_strnicmp(arg,"gochat?",7)) { + char *tok,*rm=NULL; + int exchange=0; + if(*(arg+=7)==0) return 1; /* parse failed */ + tok=strtok(arg,"&"); /* first token */ + while(tok!=NULL) { + if(!_strnicmp(tok,"roomname=",9) && *(tok+9)!=0) + rm=Netlib_UrlDecode(tok+9); + if(!_strnicmp(tok,"exchange=",9)) + exchange=atoi(Netlib_UrlDecode(tok+9)); + tok=strtok(NULL,"&"); /* next token */ + } + if(rm==NULL || exchange<=0) return 1; /* parse failed */ + aim_gchat_joinrequest(rm,exchange); + return 0; + } + return 1; /* parse failed */ +} + +static int AimLinksModulesLoaded(WPARAM wParam,LPARAM lParam) +{ + char service_name[MAXMODULELABELLENGTH]; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + mir_snprintf(service_name,sizeof(service_name),"%s%s",AIM_PROTOCOL_NAME,"ParseAimLink"); + /* or "AOL Instant Messenger Links" */ + AssocMgr_AddNewUrlType("aim:",Translate("AIM Link Protocol"),conn.hInstance,IDI_AOL,service_name,0); + return 0; +} + +void aim_links_init() +{ + char service_name[MAXMODULELABELLENGTH]; + //LOG(LOG_DEBUG,"Links: init"); + mir_snprintf(service_name,sizeof(service_name),"%s%s",AIM_PROTOCOL_NAME,"ParseAimLink"); + hServiceParseLink=CreateServiceFunction(service_name,ServiceParseAimLink); + hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,AimLinksModulesLoaded); +} + +void aim_links_destroy() +{ + //LOG(LOG_DEBUG,"Links: destroy"); + UnhookEvent(hHookModulesLoaded); + DestroyServiceFunction(hServiceParseLink); +} +#endif + +// ----------------------------------------- + +#ifdef ICQ_SUPPORT_TEST + +#define gpszICQProtoName "ICQ" +#define IDI_ICQ 101 +#define hInst GetModuleHandleA("ICQ") +static __inline HANDLE HContactFromUIN(DWORD dwUIN,int *Added) { dwUIN; Added; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); } +static __inline HANDLE ICQFindFirstContact(void) { return NULL; } +static __inline HANDLE ICQFindNextContact(HANDLE hContact) { hContact; return NULL; } +static __inline void AddToCache(HANDLE hContact,DWORD dwUin) { hContact; dwUin; } +static __inline DWORD ICQGetContactSettingUIN(HANDLE hContact) { hContact; return 0; } +static __inline HANDLE HandleFromCacheByUin(DWORD dwUin) { dwUin; return NULL; } +#include +#include +#include +typedef struct { + PROTOSEARCHRESULT hdr; + DWORD uin; + BYTE auth; + char* uid; +} ICQSEARCHRESULT; + +#include "m_assocmgr.h" +static HANDLE hHookModulesLoaded; +static HANDLE hServiceOpenFile; + +static void TrimString(char *str) +{ + int len,start; + len=lstrlenA(str); + while(str[0]!='\0' && (unsigned char)str[len-1]<=' ') str[--len]=0; + for(start=0;str[start] && (unsigned char)str[start]<=' ';++start); + MoveMemory(str,str+start,len-start+1); +} + +static BOOL IsEmpty(const char *str) +{ + int i; + for(i=0;str[i]!='\0';i++) + if(str[i]!=' ' && str[i]!='\r' && str[i]!='\n') + return FALSE; + return TRUE; +} + +#define ICQFILE_MESSAGEUSER 1 +#define ICQFILE_ADDUSER 2 +typedef struct { + int type; + char uin[64]; + char email[128]; + char nick[128]; + char firstName[64]; + char lastName[64]; +} ICQFILEINFO; + +static HANDLE IsHContactFromUIN(DWORD uin) +{ + HANDLE hContact; + DWORD dwUin; + hContact=HandleFromCacheByUin(uin); + if(hContact!=NULL) return hContact; + hContact=ICQFindFirstContact(); + while(hContact!=NULL) { + dwUin=ICQGetContactSettingUIN(hContact); + if(dwUin==uin) { + AddToCache(hContact,dwUin); + return hContact; + } + hContact=ICQFindNextContact(hContact); + } + return NULL; +} + +static void AddIcqUser(ICQFILEINFO *info) +{ + ADDCONTACTSTRUCT acs; + ICQSEARCHRESULT psr; + DWORD uin; + /* check that uin does not belong to current user */ + uin=atoi(info->uin); + if(uin!=0 && uin!=ICQGetContactSettingUIN(NULL)) { + /* not yet present? */ + if(IsHContactFromUIN(uin)==NULL) { + acs.handleType=HANDLE_SEARCHRESULT; + acs.szProto=gpszICQProtoName; + acs.psr=(PROTOSEARCHRESULT*)&psr; + ZeroMemory(&psr,sizeof(ICQSEARCHRESULT)); + psr.hdr.cbSize=sizeof(ICQSEARCHRESULT); + if(info->nick[0]) psr.hdr.nick=info->nick; + else psr.hdr.nick=info->uin; + if(info->email[0]) psr.hdr.email=info->email; + if(info->firstName[0]) psr.hdr.firstName=info->firstName; + if(info->lastName[0]) psr.hdr.lastName=info->lastName; + psr.uin=uin; + psr.auth=1; /* authentication needed flag */ + psr.uid=NULL; /* icq contact */ + CallService(MS_ADDCONTACT_SHOW,(WPARAM)NULL,(LPARAM)&acs); + } + } +} + +static void MessageIcqUser(ICQFILEINFO *info) +{ + HANDLE hContact; + if(ServiceExists(MS_MSG_SENDMESSAGE)) { + hContact=HContactFromUIN(atoi(info->uin),NULL); /* adds the contact if needed */ + if(hContact!=NULL) + CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)NULL); + } +} + +static int IcqOpenFile(WPARAM wParam,LPARAM lParam) +{ + TCHAR *pszFile=(TCHAR*)lParam; /* TCHAR is specified on AssocMgr_AddNewFileTypeT() */ + FILE *fp; + ICQFILEINFO info; + char line[4096],*sep; + UNREFERENCED_PARAMETER(wParam); + /* + send message: + [ICQ Message User] + UIN=1234567 + + add user: + (all the .uin files I've seen only have UIN= in them) + [ICQ User] + UIN=1234567 + Email= + NickName= + FirstName= + LastName= + */ + if(pszFile==NULL) return 1; /* sanity check */ + fp=_tfopen(pszFile,_T("rt")); + if(fp==NULL) return 1; /* open failed */ + info.type=0; + while(!feof(fp)) { + if(fgets(line,sizeof(line),fp)==NULL) break; + TrimString(line); + if(IsEmpty(line)) continue; + if(line[0]=='[') { + ZeroMemory(&info,sizeof(info)); + if(!lstrcmpiA(line,"[ICQ Message User]")) + info.type=ICQFILE_MESSAGEUSER; + else if(!lstrcmpiA(line,"[ICQ User]")) + info.type=ICQFILE_ADDUSER; + continue; + } + if(info.type==0) continue; + sep=strchr(line,'='); + if(sep==NULL) { info.type=0; break; } /* format error */ + *(sep++)='\0'; + if(!lstrcmpA("UIN",line)) lstrcpynA(info.uin,sep,sizeof(info.uin)); /* buffer safe */ + else if(!lstrcmpA("Email",line)) lstrcpynA(info.email,sep,sizeof(info.email)); /* buffer safe */ + else if(!lstrcmpA("NickName",line)) lstrcpynA(info.nick,sep,sizeof(info.nick)); /* buffer safe */ + else if(!lstrcmpA("FirstName",line)) lstrcpynA(info.firstName,sep,sizeof(info.firstName)); /* buffer safe */ + else if(!lstrcmpA("LastName",line)) lstrcpynA(info.lastName,sep,sizeof(info.lastName)); /* buffer safe */ + } + fclose(fp); + switch(info.type) { + case ICQFILE_MESSAGEUSER: MessageIcqUser(&info); return 0; + case ICQFILE_ADDUSER: AddIcqUser(&info); return 0; + default: return 1; /* open failed */ + } +} + +static int IcqFilesModulesLoaded(WPARAM wParam,LPARAM lParam) +{ + char szService[MAX_PATH+32]; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + strcat(strcpy(szService,gpszICQProtoName),"OpenFile"); + /* .icq files are not used, just by the ProtoLink plugin */ + //AssocMgr_AddNewFileTypeT(".icq","application/x-icq",TranslateT("ICQ Link Shortcut"),TranslateT("&Add to Contact List..."),hInst,IDI_ICQ,szService,FTDF_BROWSERAUTOOPEN|FTDF_ISTEXT|FTDF_ISSHORTCUT|FTDF_DEFAULTDISABLED); + AssocMgr_AddNewFileTypeT(".uin","application/x-icq",TranslateT("ICQ Link Shortcut"),TranslateT("&Add to Contact List..."),hInst,IDI_ICQ,szService,FTDF_BROWSERAUTOOPEN|FTDF_ISTEXT|FTDF_ISSHORTCUT); + return 0; +} + +void InitIcqFiles(void) +{ + char szService[MAX_PATH+32]; + strcat(strcpy(szService,gpszICQProtoName),"OpenFile"); + hServiceOpenFile=CreateServiceFunction(szService,IcqOpenFile); + hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,IcqFilesModulesLoaded); +} + +void UninitIcqFiles(void) +{ + UnhookEvent(hHookModulesLoaded); + DestroyServiceFunction(hServiceOpenFile); +} + +#undef hInst +#endif + +// ----------------------------------------- + +#ifdef YAHOO_SUPPORT_TEST + +#define yahooProtocolName "YAHOO" +#define IDI_YAHOO 10101 +#define hinstance GetModuleHandleA("YAHOO") +static __inline HANDLE getbuddyH(const char *yahoo_id) { yahoo_id; return NULL; } +static __inline HANDLE add_buddy(const char *yahoo_id,const char *yahoo_name,DWORD flags) { yahoo_id; yahoo_name; flags; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); } +#include +#include +#include + +#include "m_assocmgr.h" +static HANDLE hHookModulesLoaded; +static HANDLE hServiceParseYmsgrLink; + +static int ServiceParseYmsgrLink(WPARAM wParam,LPARAM lParam) +{ + char *arg=(char*)lParam; + UNREFERENCED_PARAMETER(wParam); + if(arg==NULL) return 1; /* sanity check */ + /* + add user: ymsgr:addfriend?ID + send message: ymsgr:sendim?ID&m=MESSAGE + add chatroom: ymsgr:chat?ROOM + */ + /* skip leading prefix */ + arg=strchr(arg,':'); + if(arg==NULL) return 1; /* parse failed */ + for(++arg;*arg=='/';++arg); + /* add a contact to the list */ + if(!_strnicmp(arg,"addfriend?",10)) { + char *tok,*id=NULL; + ADDCONTACTSTRUCT acs; + PROTOSEARCHRESULT psr; + if(*(arg+=10)==0) return 1; /* parse failed */ + tok=strtok(arg,"&"); /* first token */ + if(tok!=NULL) id=Netlib_UrlDecode(tok); + if(id==NULL || *id==0) return 1; /* parse failed */ + if(getbuddyH(id)==NULL) { /* does not yet check if id is current user */ + acs.handleType=HANDLE_SEARCHRESULT; + acs.szProto=yahooProtocolName; + acs.psr=&psr; + ZeroMemory(&psr,sizeof(PROTOSEARCHRESULT)); + psr.cbSize=sizeof(PROTOSEARCHRESULT); + psr.nick=id; + CallService(MS_ADDCONTACT_SHOW,(WPARAM)NULL,(LPARAM)&acs); + } + return 0; + } + /* send a message to a contact */ + else if(!_strnicmp(arg,"sendim?",7)) { + char *tok,*id=NULL,*msg=NULL; + HANDLE hContact; + if(*(arg+=7)==0) return 1; /* parse failed */ + tok=strtok(arg,"&"); /* first token */ + if(tok!=NULL) id=tok; + while(tok!=NULL) { + if(!_strnicmp(tok,"m=",2) && *(tok+2)!=0) + msg=Netlib_UrlDecode(tok+2); + tok=strtok(NULL,"&"); /* next token */ + } + if(id==NULL || *id==0) return 1; /* parse failed */ + if(ServiceExists(MS_MSG_SENDMESSAGE)) { /* does not yet check if sn is current user */ + hContact=add_buddy(id,id,PALF_TEMPORARY); /* ensure contact is on list */ + if(hContact!=NULL) + CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)msg); + } + return 0; + } + /* open a chatroom */ + else if(!_strnicmp(arg,"chat?",5)) { + char *tok,*rm=NULL; + if(*(arg+=5)==0) return 1; /* parse failed */ + tok=strtok(arg,"&"); /* first token */ + if(tok!=NULL) rm=Netlib_UrlDecode(tok); + if(rm==NULL) return 1; /* parse failed */ + /* not yet implemented (rm contains name of chatroom)*/ + return 0; + } + return 1; /* parse failed */ +} + +static int YmsgrLinksModulesLoaded(WPARAM wParam,LPARAM lParam) +{ + char szService[MAXMODULELABELLENGTH]; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + mir_snprintf(szService,sizeof(szService),"%s%s",yahooProtocolName,"ParseYmsgrLink"); + AssocMgr_AddNewUrlType("ymsgr:",Translate("Yahoo Link Protocol"),hinstance,IDI_YAHOO,szService,0); + return 0; +} + +void YmsgrLinksInit(void) +{ + char szService[MAXMODULELABELLENGTH]; + mir_snprintf(szService,sizeof(szService),"%s%s",yahooProtocolName,"ParseYmsgrLink"); + hServiceParseYmsgrLink=CreateServiceFunction(szService,ServiceParseYmsgrLink); + hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,YmsgrLinksModulesLoaded); +} + +void YmsgrLinksUninit(void) +{ + UnhookEvent(hHookModulesLoaded); + DestroyServiceFunction(hServiceParseYmsgrLink); +} + +#undef hinstance +#endif + +// ----------------------------------------- + +#ifdef MSN_SUPPORT_TEST + +#define msnProtocolName "MSN" +#define IDI_MSN 101 +#define hInst GetModuleHandleA("MSN") +static __inline HANDLE MSN_HContactFromEmailT(const char *msnEmail) { msnEmail; return NULL; } +static __inline HANDLE MSN_HContactFromEmail(const char *msnEmail,const char *msnNick,int addIfNeeded,int temporary) { msnEmail; msnNick; addIfNeeded; temporary; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); } +#include +#include +#include + +#include "m_assocmgr.h" +static HANDLE hHookModulesLoaded; +static HANDLE hServiceParseMsnimLink; + +static int ServiceParseMsnimLink(WPARAM wParam,LPARAM lParam) +{ + char *arg=(char*)lParam; + UNREFERENCED_PARAMETER(wParam); + if(arg==NULL) return 1; /* sanity check */ + /* + add user: msnim:add?contact=netpassport@emailaddress.com + send message: msnim:chat?contact=netpassport@emailaddress.com + voice chat: msnim:voice?contact=netpassport@emailaddress.com + video chat: msnim:video?contact=netpassport@emailaddress.com + */ + /* skip leading prefix */ + arg=strchr(arg,':'); + if(arg==NULL) return 1; /* parse failed */ + for(++arg;*arg=='/';++arg); + /* add a contact to the list */ + if(!_strnicmp(arg,"add?",4)) { + char *tok,*email=NULL; + ADDCONTACTSTRUCT acs; + PROTOSEARCHRESULT psr; + if(*(arg+=4)==0) return 1; /* parse failed */ + tok=strtok(arg,"&"); /* first token */ + while(tok!=NULL) { + if(!_strnicmp(tok,"contact=",8) && *(tok+11)!=0) + email=Netlib_UrlDecode(tok+11); + tok=strtok(NULL,"&"); /* next token */ + } + if(email==NULL || *email==0) return 1; /* parse failed */ + if(MSN_HContactFromEmailT(email)==NULL) { /* does not yet check if email is current user */ + acs.handleType=HANDLE_SEARCHRESULT; + acs.szProto=AIM_PROTOCOL_NAME; + acs.psr=&psr; + ZeroMemory(&psr,sizeof(PROTOSEARCHRESULT)); + psr.cbSize=sizeof(PROTOSEARCHRESULT); + psr.nick=email; + psr.email=email; + CallService(MS_ADDCONTACT_SHOW,(WPARAM)NULL,(LPARAM)&acs); + } + return 0; + } + /* send a message to a contact */ + /* "voice" and "video" not yet implemented, perform same action as "chat" */ + else if(!_strnicmp(arg,"chat?",5) || !_strnicmp(arg,"voice?",6) || !_strnicmp(arg,"video?",6)) { + char *tok,*email=NULL; + HANDLE hContact; + if(*(arg+=5)==0) return 1; /* parse failed */ + if(*arg=='?' && *(++arg)==0) return 1; /* for "voice?" and "video?" */ + tok=strtok(arg,"&"); /* first token */ + while(tok!=NULL) { + if(!_strnicmp(tok,"contact=",8) && *(tok+11)!=0) + email=Netlib_UrlDecode(tok+11); + tok=strtok(NULL,"&"); /* next token */ + } + if(email==NULL || *email==0) return 1; /* parse failed */ + if(ServiceExists(MS_MSG_SENDMESSAGE)) { + hContact=MSN_HContactFromEmail(email,email,TRUE,TRUE); /* does not yet check if email is current user */ + if(hContact!=NULL) + CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)NULL); + } + return 0; + } + return 1; /* parse failed */ +} + +static int MsnLinksModulesLoaded(WPARAM wParam,LPARAM lParam) +{ + char szService[MAXMODULELABELLENGTH]; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + mir_snprintf(szService,sizeof(szService),"%s%s",msnProtocolName,"ParseMsnimLink"); + AssocMgr_AddNewUrlType("msnim:",Translate("MSN Link Protocol"),hInst,IDI_MSN,szService,0); + return 0; +} + +int LoadMsnLinks(void) +{ + char szService[MAXMODULELABELLENGTH]; + mir_snprintf(szService,sizeof(szService),"%s%s",msnProtocolName,"ParseMsnimLink"); + hServiceParseMsnimLink=CreateServiceFunction(szService,ServiceParseMsnimLink); + hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,MsnLinksModulesLoaded); + return 0; +} + +void UnloadMsnLinks(void) +{ + UnhookEvent(hHookModulesLoaded); + DestroyServiceFunction(hServiceParseMsnimLink); +} + +#undef hInst +#endif + +// ----------------------------------------- + +#ifdef GG_SUPPORT_TEST + +#define GG_PROTO "GG" +#define IDI_GG 251 +#define hInstance GetModuleHandleA("GG") +typedef DWORD uin_t; +static __inline HANDLE gg_getcontact(uin_t uin,int create,int inlist,char *szNick) { uin; create; inlist; szNick; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); } +#include +#include +#include + +#include "m_assocmgr.h" +static HANDLE hHookModulesLoaded; +static HANDLE hServiceParseLink; + +static int ServiceParseLink(WPARAM wParam,LPARAM lParam) +{ + char *arg=(char*)lParam; + UNREFERENCED_PARAMETER(wParam); + if(arg==NULL) return 1; /* sanity check */ + /* send message: gg:UID */ + /* skip leading prefix */ + arg=strchr(arg,':'); + if(arg==NULL) return 1; /* parse failed */ + for(++arg;*arg=='/';++arg); + /* send a message to a contact */ + { HANDLE hContact; + if(ServiceExists(MS_MSG_SENDMESSAGE)) { + hContact=gg_getcontact(atoi(arg),TRUE,FALSE,arg); + if(hContact!=NULL) + CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)NULL); + } + } + return 0; +} + +static int LinksModulesLoaded(WPARAM wParam,LPARAM lParam) +{ + char szService[MAXMODULELABELLENGTH]; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + mir_snprintf(szService,sizeof(szService),"%s%s",GG_PROTO,"ParseMsnimLink"); + AssocMgr_AddNewUrlType("gg:",Translate("Gadu-Gadu Link Protocol"),hInstance,IDI_GG,szService,0); + return 0; +} + +void gg_registerlinks(void) +{ + char szService[MAXMODULELABELLENGTH]; + mir_snprintf(szService,sizeof(szService),"%s%s",GG_PROTO,"ParseLink"); + hServiceParseLink=CreateServiceFunction(szService,ServiceParseLink); + hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,LinksModulesLoaded); +} + +void gg_unregisterlinks(void) +{ + UnhookEvent(hHookModulesLoaded); + DestroyServiceFunction(hServiceParseLink); +} + +#undef hInstance +#endif + +// ----------------------------------------- + +#ifdef JABBER_SUPPORT_TEST + +#define jabberProtoName "JABBER" +#define IDI_JABBER 102 +#define hInst GetModuleHandleA("JABBER") +static __inline HANDLE JabberHContactFromJID(const char *jid) { jid; return NULL; } +static __inline HANDLE JabberDBCreateContact(char *jid,char *nick,BOOL temporary,BOOL stripResource) { jid; nick; temporary; stripResource; return (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0); } +#include +#include +#include + +#include "m_assocmgr.h" +static HANDLE hHookModulesLoaded; +static HANDLE hServiceParseXmppURI; + +static int ServiceParseXmppURI(WPARAM wParam,LPARAM lParam) +{ + char *arg=(char*)lParam; + char *jid; + UNREFERENCED_PARAMETER(wParam); + if(arg==NULL) return 1; /* sanity check */ + /* skip leading prefix */ + arg=strchr(arg,':'); + if(arg==NULL) return 1; /* parse failed */ + for(++arg;*arg=='/';++arg); + /* + complete specification: http://www.xmpp.org/extensions/xep-0147.html + send message: xmpp:JID?message;subject=TEXT&body=TEXT + add user: xmpp:JID?roster + remove user: xmpp:JID?remove + */ + /* user id */ + arg=strchr(jid=arg,'?'); + if(arg==NULL) arg+=lstrlenA(arg); /* points to terminating nul */ + else *(arg++)=0; + if(*jid==0) return 1; /* parse failed */ + /* send a message to a contact */ + else if(*arg==0 || (!_strnicmp(arg,"message",7) && (*(arg+7)==';' || *(arg+7)==0))) { + char *tok,*subj=NULL,*body=NULL; + HANDLE hContact; + char msg[1024]; + arg+=7; + while(*arg==';') ++arg; + tok=strtok(arg,"&"); /* first token */ + while(tok!=NULL) { + if(!_strnicmp(tok,"subject=",8) && *(tok+8)!=0) + subj=Netlib_UrlDecode(tok+8); + if(!_strnicmp(tok,"body=",5) && *(tok+5)!=0) + body=Netlib_UrlDecode(tok+5); + tok=strtok(NULL,"&"); /* next token */ + } + if(ServiceExists(MS_MSG_SENDMESSAGE)) { + hContact=JabberDBCreateContact(jid,jid,TRUE,FALSE); + if(subj!=NULL && body!=NULL) { + mir_snprintf(msg,sizeof(msg),"%.128s %s",subj,body); + body=msg; + } else if(body==NULL) body=subj; + if(hContact!=NULL) + CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,(LPARAM)body); + } + return 0; + } + /* add user to contact list */ + else if(!_strnicmp(arg,"roster",6) && (*(arg+6)==';' || *(arg+6)==0)) { + ADDCONTACTSTRUCT acs; + PROTOSEARCHRESULT psr; + if(JabberHContactFromJID(jid)==NULL) { /* does not yet check if jid belongs to current user */ + acs.handleType=HANDLE_SEARCHRESULT; + acs.szProto=jabberProtoName; + acs.psr=&psr; + ZeroMemory(&psr,sizeof(PROTOSEARCHRESULT)); + psr.cbSize=sizeof(PROTOSEARCHRESULT); + psr.nick=jid; + CallService(MS_ADDCONTACT_SHOW,(WPARAM)NULL,(LPARAM)&acs); + } + return 0; + } + /* remove user from contact list */ + else if(!_strnicmp(arg,"remove",6) && (*(arg+6)==';' || *(arg+6)==0)) { + HANDLE hContact; + hContact=JabberHContactFromJID(jid); + if(hContact==NULL) /* not yet implemented: show standard miranda dialog here */ + CallService(MS_DB_CONTACT_DELETE,(WPARAM)hContact,0); + return 0; + } + /* add user subscription */ + else if(!_strnicmp(arg,"subscribe",9) && (*(arg+9)==';' || *(arg+9)==0)) { + /* not yet implemented */ + return 0; + } + /* remove user subscription */ + else if(!_strnicmp(arg,"unsubscribe",11) && (*(arg+11)==';' || *(arg+11)==0)) { + /* not yet implemented */ + return 0; + } + return 1; /* parse failed */ +} + +static int JabberLinksModulesLoaded(WPARAM wParam,LPARAM lParam) +{ + char szService[MAXMODULELABELLENGTH]; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + mir_snprintf(szService,sizeof(szService),"%s%s",jabberProtoName,"ParseXmppURI"); + AssocMgr_AddNewUrlType("xmpp:",Translate("Jabber Link Protocol"),hInst,IDI_JABBER,szService,0); + return 0; +} + +int JabberLinksInit() +{ + char szService[MAXMODULELABELLENGTH]; + mir_snprintf(szService,sizeof(szService),"%s%s",jabberProtoName,"ParseXmppURI"); + hServiceParseXmppURI=CreateServiceFunction(szService,ServiceParseXmppURI); + hHookModulesLoaded=HookEvent(ME_SYSTEM_MODULESLOADED,JabberLinksModulesLoaded); + return 0; +} + +int JabberLinksUninit() +{ + UnhookEvent(hHookModulesLoaded); + DestroyServiceFunction(hServiceParseXmppURI); + return 0; +} + +#undef hInst +#endif + +// ----------------------------------------- + +/* +static HANDLE hServiceTest; +static int TestingService(WPARAM wParam,LPARAM lParam) +{ + UNREFERENCED_PARAMETER(wParam); + MessageBoxEx(NULL,(TCHAR*)lParam,_T("Testing Service"),MB_OK|MB_SETFOREGROUND|MB_TOPMOST|MB_TASKMODAL,LANGIDFROMLCID((LCID)CallService(MS_LANGPACK_GETLOCALE,0,0))); + return 0; +} +*/ + +void InitTest(void) +{ + #ifdef AIM_SUPPORT_TEST + conn.hInstance=GetModuleHandleA("AIM"); + aim_links_init(); + #endif + #ifdef ICQ_SUPPORT_TEST + InitIcqFiles(); + #endif + #ifdef YAHOO_SUPPORT_TEST + YmsgrLinksInit(); + #endif + #ifdef MSN_SUPPORT_TEST + LoadMsnLinks(); + #endif + #ifdef GG_SUPPORT_TEST + gg_registerlinks(); + #endif + #ifdef JABBER_SUPPORT_TEST + JabberLinksInit(); + #endif + //hServiceTest=CreateServiceFunction("AssocMgr/TestingService",TestingService); + //AssocMgr_AddNewFileTypeT(".mir",NULL,TranslateT("Miranda Installer Package (demo purpose)"),TranslateT("&Install"),hInst,IDI_MIRANDAFILE,"AssocMgr/TestingService",0); +} + +void UninitTest(void) +{ + #ifdef AIM_SUPPORT_TEST + aim_links_destroy(); + #endif + #ifdef ICQ_SUPPORT_TEST + UninitIcqFiles(); + #endif + #ifdef YAHOO_SUPPORT_TEST + YmsgrLinksUninit(); + #endif + #ifdef MSN_SUPPORT_TEST + UnloadMsnLinks(); + #endif + #ifdef GG_SUPPORT_TEST + gg_unregisterlinks(); + #endif + #ifdef JABBER_SUPPORT_TEST + JabberLinksUninit(); + #endif + //DestroyServiceFunction(hServiceTest); +} diff --git a/plugins/AssocMgr/test.h b/plugins/AssocMgr/test.h new file mode 100644 index 0000000000..638dea164b --- /dev/null +++ b/plugins/AssocMgr/test.h @@ -0,0 +1,24 @@ +/* + +'File Association Manager'-Plugin for Miranda IM + +Copyright (C) 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 (AssocMgr-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Misc */ +void InitTest(void); +void UninitTest(void); diff --git a/plugins/AssocMgr/utils.c b/plugins/AssocMgr/utils.c new file mode 100644 index 0000000000..d5ae8b7327 --- /dev/null +++ b/plugins/AssocMgr/utils.c @@ -0,0 +1,199 @@ +/* + +'File Association Manager'-Plugin for Miranda IM + +Copyright (C) 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 (AssocMgr-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "common.h" + +DWORD WINAPI GetModuleFileNameWorkaround(HMODULE hModule,TCHAR *pszFileName,DWORD nSize) +{ + /* GetModuleFileName() commonly crashes when hModule=NULL and + * miranda32.exe was compiled without version resource */ + if(hModule==NULL) hModule=GetModuleHandle(NULL); + if(hModule==NULL) return 0; + return GetModuleFileName(hModule,pszFileName,nSize); +} + +/************************* String Conv ****************************/ + +// mir_free() the return value +WCHAR* a2u(const char *pszAnsi,BOOL fMirCp) +{ + UINT codepage,cch; + WCHAR *psz; + + if(pszAnsi==NULL) return NULL; + codepage=fMirCp?CallService(MS_LANGPACK_GETCODEPAGE,0,0):CP_ACP; + cch=MultiByteToWideChar(codepage,0,pszAnsi,-1,NULL,0); + if(!cch) return NULL; + + psz=(WCHAR*)mir_alloc(cch*sizeof(WCHAR)); + if(psz!=NULL && !MultiByteToWideChar(codepage,0,pszAnsi,-1,psz,cch)) { + mir_free(psz); + return NULL; + } + return psz; +} + +// mir_free() the return value +char* u2a(const WCHAR *pszUnicode,BOOL fMirCp) +{ + UINT codepage,cch; + char *psz; + DWORD flags; + + if(pszUnicode==NULL) return NULL; + codepage=fMirCp?CallService(MS_LANGPACK_GETCODEPAGE,0,0):CP_ACP; + /* without WC_COMPOSITECHECK some characters might get out strange (see MS blog) */ + cch=WideCharToMultiByte(codepage,flags=WC_COMPOSITECHECK,pszUnicode,-1,NULL,0,NULL,NULL); + if(!cch) cch=WideCharToMultiByte(codepage,flags=0,pszUnicode,-1,NULL,0,NULL,NULL); + if(!cch) return NULL; + + psz=(char*)mir_alloc(cch); + if(psz!=NULL && !WideCharToMultiByte(codepage,flags,pszUnicode,-1,psz,cch,NULL,NULL)){ + mir_free(psz); + return NULL; + } + return psz; +} + +// mir_free() the return value +TCHAR* s2t(const void *pszStr,DWORD fUnicode,BOOL fMirCp) +{ +#if defined(_UNICODE) + if(fUnicode) return mir_wstrdup((WCHAR*)pszStr); + return a2u((char*)pszStr,fMirCp); +#else + if(fUnicode) return u2a((WCHAR*)pszStr,fMirCp); + return mir_strdup((char*)pszStr); +#endif +} + +// mir_free() the return value +void* t2s(const TCHAR *pszStr,DWORD fUnicode,BOOL fMirCp) +{ +#if defined(_UNICODE) + if(!fUnicode) return (void*)u2a(pszStr,fMirCp); + return (void*)mir_wstrdup(pszStr); +#else + if(fUnicode) return (void*)a2u(pszStr,fMirCp); + return (void*)mir_strdup(pszStr); +#endif +} + +/************************* Database *******************************/ + +struct EnumPrefixSettingsParams { + char **settings; + int nSettingsCount; + const char *pszPrefix; + int nPrefixLen; +}; + +static int EnumPrefixSettingsProc(const char *pszSetting,LPARAM lParam) +{ + struct EnumPrefixSettingsParams *param=(struct EnumPrefixSettingsParams*)lParam; + if(!strncmp(pszSetting,param->pszPrefix,param->nPrefixLen)) { + char **buf; + /* resize storage array */ + buf=mir_realloc(param->settings,(param->nSettingsCount+1)*sizeof(char*)); + if(buf!=NULL) { + param->settings=buf; + buf[param->nSettingsCount]=mir_strdup(pszSetting); + if(buf[param->nSettingsCount]!=NULL) ++param->nSettingsCount; + } + } + return 0; +} + +// mir_free() the returned pSettings after use +BOOL EnumDbPrefixSettings(const char *pszModule,const char *pszSettingPrefix,char ***pSettings,int *pnSettingsCount) +{ + DBCONTACTENUMSETTINGS dbces; + struct EnumPrefixSettingsParams param; + dbces.szModule=pszModule; + dbces.pfnEnumProc=EnumPrefixSettingsProc; + dbces.lParam=(LPARAM)¶m; + param.settings=NULL; + param.nSettingsCount=0; + param.pszPrefix=pszSettingPrefix; + param.nPrefixLen=lstrlenA(pszSettingPrefix); + CallService(MS_DB_CONTACT_ENUMSETTINGS,0,(LPARAM)&dbces); + *pnSettingsCount=param.nSettingsCount; + *pSettings=param.settings; + return param.nSettingsCount!=0; +} + +/************************* 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); +} + +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); +} + +// LocalFree() the return value +char* GetWinErrorDescription(DWORD dwLastError) +{ + char *buf=NULL; + DWORD flags=FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM; + if(!FormatMessageA(flags,NULL,dwLastError,LANGIDFROMLCID((LCID)CallService(MS_LANGPACK_GETLOCALE,0,0)),(char*)&buf,0,NULL)) + if(GetLastError()==ERROR_RESOURCE_LANG_NOT_FOUND) + FormatMessageA(flags,NULL,dwLastError,0,(char*)&buf,0,NULL); + return buf; +} diff --git a/plugins/AssocMgr/utils.h b/plugins/AssocMgr/utils.h new file mode 100644 index 0000000000..0c2682c4a8 --- /dev/null +++ b/plugins/AssocMgr/utils.h @@ -0,0 +1,42 @@ +/* + +'File Association Manager'-Plugin for Miranda IM + +Copyright (C) 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 (AssocMgr-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +DWORD WINAPI GetModuleFileNameWorkaround(HMODULE hModule,TCHAR *pszFileName,DWORD nSize); + +/* String Conv */ +WCHAR* a2u(const char *pszAnsi,BOOL fMirCp); +char* u2a(const WCHAR *pszUnicode,BOOL fMirCp); +TCHAR* s2t(const void *pszStr,DWORD fUnicode,BOOL fMirCp); +void* t2s(const TCHAR *pszStr,DWORD fUnicode,BOOL fMirCp); +#ifdef _UNICODE + #define t2a(s) u2a(s,FALSE) + #define a2t(s) a2u(s,FALSE) +#else + #define t2a(s) mir_strdup(s) + #define a2t(s) mir_strdup(s) +#endif + +/* Database */ +BOOL EnumDbPrefixSettings(const char *pszModule,const char *pszSettingPrefix,char ***pSettings,int *pnSettingsCount); + +/* Error Output */ +void ShowInfoMessage(BYTE flags,const char *pszTitle,const char *pszTextFmt,...); +char* GetWinErrorDescription(DWORD dwLastError); diff --git a/plugins/AssocMgr/version.h b/plugins/AssocMgr/version.h new file mode 100644 index 0000000000..ce9485a55f --- /dev/null +++ b/plugins/AssocMgr/version.h @@ -0,0 +1,40 @@ +/* + +'File Association Manager'-Plugin for Miranda IM + +Copyright (C) 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 (AssocMgr-License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#define NEEDED_MIRANDA_VERSION PLUGIN_MAKE_VERSION(0,6,0,0) +#define NEEDED_MIRANDA_VERSION_STR "0.6" +#define PLUGIN_VERSION PLUGIN_MAKE_VERSION(0,1,1,0) +#define FILE_VERSION 0,1,1,0 + +#if defined(_DEBUG) + #define FILE_VERSION_STR "0.1.1.1 alpha" +#else + #define FILE_VERSION_STR "0.1.1.0" +#endif + +#define PLUGIN_EMAIL "hrathh users.sourceforge.net" +#define PLUGIN_EMAIL_ATT_POS 7 /* position of the @-sign in the email adress above */ + +#if defined(_UNICODE) + #define PLUGIN_WEBSITE "http://addons.miranda-im.org/details.php?action=viewfile&id=3458" +#else + #define PLUGIN_WEBSITE "http://addons.miranda-im.org/details.php?action=viewfile&id=3457" +#endif diff --git a/plugins/AssocMgr/version.rc b/plugins/AssocMgr/version.rc new file mode 100644 index 0000000000..4234d8d7ec --- /dev/null +++ b/plugins/AssocMgr/version.rc @@ -0,0 +1,51 @@ +#ifndef _MAC + +#include "version.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION FILE_VERSION + PRODUCTVERSION FILE_VERSION + FILEFLAGSMASK 0x0L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Comments", "Licensed under the terms of the GNU General Public License" + VALUE "FileDescription", "File Association Manager Plugin for Miranda IM" + VALUE "FileVersion", FILE_VERSION_STR +#ifdef _UNICODE + VALUE "InternalName", "AssocMgr (Unicode)" +#else + VALUE "InternalName", "AssocMgr" +#endif + VALUE "LegalCopyright", "Copyright © 2005-2007 H. Herkenrath" + VALUE "OriginalFilename", "assocmgr.dll" + VALUE "ProductName", "File Association Manager" + VALUE "ProductVersion", FILE_VERSION_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + +#endif // !_MAC + + + + -- cgit v1.2.3