/* '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