/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-2009 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "..\..\core\commonheaders.h" #include #define DDEMESSAGETIMEOUT 1000 #define WNDCLASS_DDEMSGWINDOW _T("MirandaDdeMsgWindow") struct DdeMsgWindowData { int fAcked, fData; HWND hwndDde; }; static LRESULT CALLBACK DdeMessageWindow(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { struct DdeMsgWindowData *dat; ATOM hSzItem; HGLOBAL hDdeData; dat=(struct DdeMsgWindowData*)GetWindowLongPtr(hwnd, 0); switch(msg) { case WM_DDE_ACK: dat->fAcked=1; dat->hwndDde=(HWND)wParam; return 0; case WM_DDE_DATA: UnpackDDElParam(msg, lParam, (PUINT_PTR)&hDdeData, (PUINT_PTR)&hSzItem); dat->fData=1; if (hDdeData) { DDEDATA *data; int release; data=(DDEDATA*)GlobalLock(hDdeData); if (data->fAckReq) { DDEACK ack={0}; PostMessage((HWND)wParam, WM_DDE_ACK, (WPARAM)hwnd, PackDDElParam(WM_DDE_ACK, *(PUINT)&ack, (UINT)hSzItem)); } else GlobalDeleteAtom(hSzItem); release=data->fRelease; GlobalUnlock(hDdeData); if (release) GlobalFree(hDdeData); } else GlobalDeleteAtom(hSzItem); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } static int DoDdeRequest(const char *szItemName, HWND hwndDdeMsg) { ATOM hSzItemName; DWORD timeoutTick, thisTick; MSG msg; struct DdeMsgWindowData *dat=(struct DdeMsgWindowData*)GetWindowLongPtr(hwndDdeMsg, 0); hSzItemName=GlobalAddAtomA(szItemName); if (!PostMessage(dat->hwndDde, WM_DDE_REQUEST, (WPARAM)hwndDdeMsg, MAKELPARAM(CF_TEXT, hSzItemName))) { GlobalDeleteAtom(hSzItemName); return 1; } timeoutTick=GetTickCount()+5000; dat->fData=0; dat->fAcked=0; do { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } if (dat->fData || dat->fAcked) break; thisTick=GetTickCount(); if (thisTick>timeoutTick) break; } while (MsgWaitForMultipleObjects(0, NULL, FALSE, timeoutTick-thisTick, QS_ALLINPUT) == WAIT_OBJECT_0); if (!dat->fData) { GlobalDeleteAtom(hSzItemName); return 1; } return 0; } //see Q160957 and http://developer.netscape.com/docs/manuals/communicator/DDE/index.htm static int DdeOpenUrl(const char *szBrowser, char *szUrl, int newWindow, HWND hwndDdeMsg) { ATOM hSzBrowser, hSzTopic; DWORD_PTR dwResult; char *szItemName; struct DdeMsgWindowData *dat=(struct DdeMsgWindowData*)GetWindowLongPtr(hwndDdeMsg, 0); hSzBrowser=GlobalAddAtomA(szBrowser); hSzTopic=GlobalAddAtomA("WWW_OpenURL"); dat->fAcked=0; if (!SendMessageTimeout(HWND_BROADCAST, WM_DDE_INITIATE, (WPARAM)hwndDdeMsg, MAKELPARAM(hSzBrowser, hSzTopic), SMTO_ABORTIFHUNG|SMTO_NORMAL, DDEMESSAGETIMEOUT, &dwResult) || !dat->fAcked) { GlobalDeleteAtom(hSzTopic); GlobalDeleteAtom(hSzBrowser); return 1; } szItemName=(char*)mir_alloc(lstrlenA(szUrl)+7); wsprintfA(szItemName, "\"%s\", , %d", szUrl, newWindow?0:-1); if (DoDdeRequest(szItemName, hwndDdeMsg)) { mir_free(szItemName); GlobalDeleteAtom(hSzTopic); GlobalDeleteAtom(hSzBrowser); return 1; } PostMessage(dat->hwndDde, WM_DDE_TERMINATE, (WPARAM)hwndDdeMsg, 0); GlobalDeleteAtom(hSzTopic); GlobalDeleteAtom(hSzBrowser); mir_free(szItemName); return 0; } typedef struct { char *szUrl; int newWindow; } TOpenUrlInfo; static void OpenURLThread(void *arg) { TOpenUrlInfo *hUrlInfo = (TOpenUrlInfo*)arg; char *szResult; HWND hwndDdeMsg; struct DdeMsgWindowData msgWndData={0}; char *pszProtocol; HKEY hKey; char szSubkey[80]; char szCommandName[MAX_PATH]; DWORD dataLength; int success=0; if (!hUrlInfo->szUrl) return; hwndDdeMsg=CreateWindow(WNDCLASS_DDEMSGWINDOW, _T(""), 0, 0, 0, 0, 0, NULL, NULL, hMirandaInst, NULL); SetWindowLongPtr(hwndDdeMsg, 0, (LONG_PTR)&msgWndData); if (!_strnicmp(hUrlInfo->szUrl, "ftp:", 4) || !_strnicmp(hUrlInfo->szUrl, "ftp.", 4)) pszProtocol="ftp"; if (!_strnicmp(hUrlInfo->szUrl, "mailto:", 7)) pszProtocol="mailto"; if (!_strnicmp(hUrlInfo->szUrl, "news:", 5)) pszProtocol="news"; else pszProtocol="http"; wsprintfA(szSubkey, "%s\\shell\\open\\command", pszProtocol); if (RegOpenKeyExA(HKEY_CURRENT_USER, szSubkey, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS || RegOpenKeyExA(HKEY_CLASSES_ROOT, szSubkey, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) { dataLength=SIZEOF(szCommandName); if (RegQueryValueEx(hKey, NULL, NULL, NULL, (PBYTE)szCommandName, &dataLength) == ERROR_SUCCESS) { _strlwr(szCommandName); if (strstr(szCommandName, "mozilla") || strstr(szCommandName, "netscape")) success=(DdeOpenUrl("mozilla", hUrlInfo->szUrl, hUrlInfo->newWindow, hwndDdeMsg) == 0 || DdeOpenUrl("netscape", hUrlInfo->szUrl, hUrlInfo->newWindow, hwndDdeMsg) == 0); else if (strstr(szCommandName, "iexplore") || strstr(szCommandName, "msimn")) success=0 == DdeOpenUrl("iexplore", hUrlInfo->szUrl, hUrlInfo->newWindow, hwndDdeMsg); else if (strstr(szCommandName, "opera")) success=0 == DdeOpenUrl("opera", hUrlInfo->szUrl, hUrlInfo->newWindow, hwndDdeMsg); //opera's the default anyway } RegCloseKey(hKey); } DestroyWindow(hwndDdeMsg); if (success) return; //wack a protocol on it if ((isalpha(hUrlInfo->szUrl[0]) && hUrlInfo->szUrl[1] == ':') || hUrlInfo->szUrl[0] == '\\') { szResult=(char*)mir_alloc(lstrlenA(hUrlInfo->szUrl)+9); wsprintfA(szResult, "file:///%s", hUrlInfo->szUrl); } else { int i; for (i=0;isalpha(hUrlInfo->szUrl[i]);i++); if (hUrlInfo->szUrl[i] == ':') szResult=mir_strdup(hUrlInfo->szUrl); else { if (!_strnicmp(hUrlInfo->szUrl, "ftp.", 4)) { szResult=(char*)mir_alloc(lstrlenA(hUrlInfo->szUrl)+7); wsprintfA(szResult, "ftp://%s", hUrlInfo->szUrl); } else { szResult=(char*)mir_alloc(lstrlenA(hUrlInfo->szUrl)+8); wsprintfA(szResult, "http://%s", hUrlInfo->szUrl); } } } ShellExecuteA(NULL, "open", szResult, NULL, NULL, SW_SHOWDEFAULT); mir_free(szResult); mir_free(hUrlInfo->szUrl); mir_free(hUrlInfo); return; } static INT_PTR OpenURL(WPARAM wParam, LPARAM lParam) { TOpenUrlInfo *hUrlInfo = (TOpenUrlInfo*)mir_alloc(sizeof(TOpenUrlInfo)); hUrlInfo->szUrl = (char*)lParam?mir_strdup((char*)lParam):NULL; hUrlInfo->newWindow = (int)wParam; forkthread(OpenURLThread, 0, (void*)hUrlInfo); return 0; } int InitOpenUrl(void) { WNDCLASS wcl; wcl.lpfnWndProc=DdeMessageWindow; wcl.cbClsExtra=0; wcl.cbWndExtra=sizeof(void*); wcl.hInstance=hMirandaInst; wcl.hCursor=NULL; wcl.lpszClassName=WNDCLASS_DDEMSGWINDOW; wcl.hbrBackground=NULL; wcl.hIcon=NULL; wcl.lpszMenuName=NULL; wcl.style=0; RegisterClass(&wcl); CreateServiceFunction(MS_UTILS_OPENURL, OpenURL); return 0; }