From 66526765714b08969548a414d3fa87dbe333242d Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Sat, 28 Jul 2012 19:25:08 +0000 Subject: "!Deprecated" folders moved from root directory to plugins git-svn-id: http://svn.miranda-ng.org/main/trunk@1230 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/!NotAdopted/Skype/skype.c | 3465 +++++++++++++++++++++++++++++++++++++ 1 file changed, 3465 insertions(+) create mode 100644 plugins/!NotAdopted/Skype/skype.c (limited to 'plugins/!NotAdopted/Skype/skype.c') diff --git a/plugins/!NotAdopted/Skype/skype.c b/plugins/!NotAdopted/Skype/skype.c new file mode 100644 index 0000000000..1aea600d3f --- /dev/null +++ b/plugins/!NotAdopted/Skype/skype.c @@ -0,0 +1,3465 @@ +/* + +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 "skype.h" +#include "debug.h" +#include "skypeapi.h" +#include "skypesvc.h" +#include "contacts.h" +#include "utf8.h" +#include "pthread.h" +#include "gchat.h" +#include "m_toptoolbar.h" +#include "voiceservice.h" +#include "msglist.h" +#include "memlist.h" +#include +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF +#endif +#ifdef _WIN64 +#pragma comment (lib, "bufferoverflowU.lib") +#endif + +#pragma warning (disable: 4706) // assignment within conditional expression + +struct MM_INTERFACE mmi; + +POPUPDATAT MessagePopup; + +// Exported Globals +HWND hSkypeWnd=NULL, g_hWnd=NULL, hSkypeWndSecondary=NULL, hForbiddenSkypeWnd = NULL; +HANDLE SkypeReady, SkypeMsgReceived, hInitChat=NULL, httbButton=NULL, FetchMessageEvent=NULL; +BOOL SkypeInitialized=FALSE, MirandaShuttingDown=FALSE, PopupServiceExists=FALSE; +BOOL UseSockets=FALSE, bSkypeOut=FALSE, bProtocolSet=FALSE, bIsImoproxy=FALSE; +char skype_path[MAX_PATH], protocol=2, *pszProxyCallout=NULL, g_szProtoName[_MAX_FNAME]="SKYPE"; +int SkypeStatus=ID_STATUS_OFFLINE, hSearchThread=-1, receivers=1; +long sendwatchers = 0, rcvwatchers = 0; +UINT ControlAPIAttach, ControlAPIDiscover; +LONG AttachStatus=-1; +HINSTANCE hInst; +HANDLE hProtocolAvatarsFolder; +char DefaultAvatarsFolder[MAX_PATH+1]; +DWORD mirandaVersion; +int hLangpack = 0; + +CRITICAL_SECTION RingAndEndcallMutex, QueryThreadMutex, TimeMutex; + +// Module Internal Globals +PLUGINLINK *pluginLink; +HANDLE MessagePumpReady; +HANDLE hChatEvent=NULL, hChatMenu=NULL; +HANDLE hEvInitChat=NULL, hBuddyAdded=NULL; +HANDLE hMenuAddSkypeContact=NULL; + +DWORD msgPumpThreadId = 0; +#ifdef SKYPEBUG_OFFLN +HANDLE GotUserstatus; +#endif + +BOOL bModulesLoaded=FALSE; +char *RequestedStatus=NULL; // To fix Skype-API Statusmode-bug +char cmdMessage[12]="MESSAGE", cmdPartner[8]="PARTNER"; // Compatibility commands + + + +// Direct assignment of user properties to a DB-Setting +static const settings_map m_settings[]= { + {"LANGUAGE", "Language1"}, + {"PROVINCE", "State"}, + {"CITY", "City"}, + {"PHONE_HOME", "Phone"}, + {"PHONE_OFFICE", "CompanyPhone"}, + {"PHONE_MOBILE", "Cellular"}, + {"HOMEPAGE", "Homepage"}, + {"ABOUT", "About"} + }; + +// Imported Globals +extern status_map status_codes[]; + +BOOL (WINAPI *MyEnableThemeDialogTexture)(HANDLE, DWORD) = 0; + +HMODULE hUxTheme = 0; + +// function pointers, use typedefs for casting to shut up the compiler when using GetProcAddress() + +typedef BOOL (WINAPI *PITA)(); +typedef HANDLE (WINAPI *POTD)(HWND, LPCWSTR); +typedef UINT (WINAPI *PDTB)(HANDLE, HDC, int, int, RECT *, RECT *); +typedef UINT (WINAPI *PCTD)(HANDLE); +typedef UINT (WINAPI *PDTT)(HANDLE, HDC, int, int, LPCWSTR, int, DWORD, DWORD, RECT *); + +PITA pfnIsThemeActive = 0; +POTD pfnOpenThemeData = 0; +PDTB pfnDrawThemeBackground = 0; +PCTD pfnCloseThemeData = 0; +PDTT pfnDrawThemeText = 0; + +#define FIXED_TAB_SIZE 100 // default value for fixed width tabs + +typedef struct { + char msgnum[16]; + BOOL getstatus; + BOOL bIsRead; + BOOL bDontMarkSeen; + BOOL QueryMsgDirection; + TYP_MSGLENTRY *pMsgEntry; +} fetchmsg_arg; + +typedef struct { + HANDLE hContact; + char szId[16]; +} msgsendwt_arg; + +/* + * visual styles support (XP+) + * returns 0 on failure + */ + +int InitVSApi() +{ + if((hUxTheme = LoadLibraryA("uxtheme.dll")) == 0) + return 0; + + pfnIsThemeActive = (PITA)GetProcAddress(hUxTheme, "IsThemeActive"); + pfnOpenThemeData = (POTD)GetProcAddress(hUxTheme, "OpenThemeData"); + pfnDrawThemeBackground = (PDTB)GetProcAddress(hUxTheme, "DrawThemeBackground"); + pfnCloseThemeData = (PCTD)GetProcAddress(hUxTheme, "CloseThemeData"); + pfnDrawThemeText = (PDTT)GetProcAddress(hUxTheme, "DrawThemeText"); + + MyEnableThemeDialogTexture = (BOOL (WINAPI *)(HANDLE, DWORD))GetProcAddress(hUxTheme, "EnableThemeDialogTexture"); + if(pfnIsThemeActive != 0 && pfnOpenThemeData != 0 && pfnDrawThemeBackground != 0 && pfnCloseThemeData != 0 && pfnDrawThemeText != 0) { + return 1; + } + return 0; +} + +/* + * unload uxtheme.dll + */ + +int FreeVSApi() +{ + if(hUxTheme != 0) + FreeLibrary(hUxTheme); + return 0; +} + +// Plugin Info +PLUGININFOEX pluginInfo = { + sizeof(PLUGININFOEX), + "Skype Protocol", + PLUGIN_MAKE_VERSION(0,0,0,52), + "Support for Skype network", + "leecher - tweety - jls17", + "leecher@dose.0wnz.at - tweety@user.berlios.de", + "© 2004-2011 leecher - tweety", + "http://developer.berlios.de/projects/mgoodies/", + UNICODE_AWARE, + 0, //doesn't replace anything built-in + { 0xa71f8335, 0x7b87, 0x4432, { 0xb8, 0xa3, 0x81, 0x47, 0x94, 0x31, 0xc6, 0xf5 } } // {A71F8335-7B87-4432-B8A3-81479431C6F5} +}; + +#define MAPDND 1 // Map Occupied to DND status and say that you support it +//#define MAPNA 1 // Map NA status to Away and say that you support it + +/* P R O G R A M */ + +void RegisterToDbeditorpp(void) +{ + // known modules list + if (ServiceExists("DBEditorpp/RegisterSingleModule")) + CallService("DBEditorpp/RegisterSingleModule", (WPARAM)SKYPE_PROTONAME, 0); +} + +void RegisterToUpdate(void) +{ + //Use for the Updater plugin + if(ServiceExists(MS_UPDATE_REGISTER)) + { + Update update = {0}; + char szVersion[16]; + + update.szComponentName = pluginInfo.shortName; + update.pbVersion = (BYTE *)CreateVersionStringPlugin((PLUGININFO *)&pluginInfo, szVersion); + update.cpbVersion = (DWORD)strlen((char *)update.pbVersion); + +#ifdef _WIN64 +#ifdef _UNICODE + update.szBetaUpdateURL = "http://dose.0wnz.at/miranda/Skype/Skype_protocol_unicode_x64.zip"; +#else + update.szBetaUpdateURL = "http://dose.0wnz.at/miranda/Skype/Skype_protocol_x64.zip"; +#endif + update.szBetaVersionURL = "http://dose.0wnz.at/miranda/Skype/"; + update.pbBetaVersionPrefix = (BYTE *)"SKYPE version "; + update.szUpdateURL = update.szBetaUpdateURL; // FIXME!! + update.szVersionURL = update.szBetaVersionURL; // FIXME + update.pbVersionPrefix = update.pbBetaVersionPrefix; //FIXME +#else /* _WIN64 */ +#ifdef _UNICODE + update.szBetaUpdateURL = "http://dose.0wnz.at/miranda/Skype/Skype_protocol_unicode.zip"; +#else + update.szBetaUpdateURL = "http://dose.0wnz.at/miranda/Skype/Skype_protocol.zip"; +#endif + update.szBetaVersionURL = "http://dose.0wnz.at/miranda/Skype/"; + update.pbBetaVersionPrefix = (BYTE *)"SKYPE version "; +#ifdef _UNICODE + update.szUpdateURL = update.szBetaUpdateURL; // FIXME!! + update.szVersionURL = update.szBetaVersionURL; // FIXME + update.pbVersionPrefix = update.pbBetaVersionPrefix; //FIXME +#else + update.szUpdateURL = "http://addons.miranda-im.org/feed.php?dlfile=3200"; + update.szVersionURL = "http://addons.miranda-im.org/details.php?action=viewfile&id=3200"; + update.pbVersionPrefix = (BYTE *)"Skype Protocol"; +#endif +#endif + + update.cpbVersionPrefix = (DWORD)strlen((char *)update.pbVersionPrefix); + update.cpbBetaVersionPrefix = (DWORD)strlen((char *)update.pbBetaVersionPrefix); + + CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update); + + } +} + +/* + * ShowMessage + * + * Shows a popup, if the popup plugin is enabled. + * mustShow: 1 -> If Popup-Plugin is not available/disabled, show Message + * in a Messagewindow + * If the Popup-Plugin is enabled, let the message stay on + * screen until someone clicks it away. + * 0 -> If Popup-Plugin is not available/disabled, skip message + * Returns 0 on success, -1 on failure + * + */ +int ShowMessage(int iconID, TCHAR *lpzText, int mustShow) { + + + + if (DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "SuppressErrors", 0)) return -1; + lpzText=TranslateTS(lpzText); + + if (bModulesLoaded && PopupServiceExists && ServiceExists(MS_POPUP_ADDPOPUPT) && DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UsePopup", 0) && !MirandaShuttingDown) { + BOOL showPopup, popupWindowColor; + unsigned int popupBackColor, popupTextColor; + int popupTimeSec; + + popupTimeSec = DBGetContactSettingDword(NULL, SKYPE_PROTONAME, "popupTimeSecErr", 4); + popupTextColor = DBGetContactSettingDword(NULL, SKYPE_PROTONAME, "popupTextColorErr", GetSysColor(COLOR_WINDOWTEXT)); + popupBackColor = DBGetContactSettingDword(NULL, SKYPE_PROTONAME, "popupBackColorErr", GetSysColor(COLOR_BTNFACE)); + popupWindowColor = ( 0 != DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "popupWindowColorErr", TRUE)); + showPopup = ( 0 != DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "showPopupErr", TRUE)); + + MessagePopup.lchContact = NULL; + MessagePopup.lchIcon = LoadIcon(hInst,MAKEINTRESOURCE(iconID)); + MessagePopup.colorBack = ! popupWindowColor ? popupBackColor : GetSysColor(COLOR_BTNFACE); + MessagePopup.colorText = ! popupWindowColor ? popupTextColor : GetSysColor(COLOR_WINDOWTEXT); + MessagePopup.iSeconds = popupTimeSec; + MessagePopup.PluginData = (void *)1; + + lstrcpy(MessagePopup.lptzText, lpzText); + +#ifdef _UNICODE + mbstowcs (MessagePopup.lptzContactName, SKYPE_PROTONAME, strlen(SKYPE_PROTONAME)+1); +#else + lstrcpy(MessagePopup.lptzContactName, SKYPE_PROTONAME); +#endif + + if(showPopup) + CallService(MS_POPUP_ADDPOPUPT,(WPARAM)&MessagePopup,0); + + return 0; + } + else { + + if (mustShow==1) MessageBox(NULL,lpzText,_T("Skype Protocol"), MB_OK | MB_ICONWARNING); + return 0; + } +} +#ifdef _UNICODE +int ShowMessageA(int iconID, char *lpzText, int mustShow) { + WCHAR *lpwText; + int iRet; + size_t len = mbstowcs (NULL, lpzText, strlen(lpzText)); + if (len == -1 || !(lpwText = calloc(len+1,sizeof(WCHAR)))) return -1; + mbstowcs (lpwText, lpzText, strlen(lpzText)); + iRet = ShowMessage(iconID, lpwText, mustShow); + free (lpwText); + return iRet; +} +#endif + +// processing Hooks + +int HookContactAdded(WPARAM wParam, LPARAM lParam) { + char *szProto; + + UNREFERENCED_PARAMETER(lParam); + + szProto = (char*)CallService( MS_PROTO_GETCONTACTBASEPROTO, wParam, 0 ); + if (szProto!=NULL && !strcmp(szProto, SKYPE_PROTONAME)) + add_contextmenu((HANDLE)wParam); + return 0; +} + +int HookContactDeleted(WPARAM wParam, LPARAM lParam) { + char *szProto; + + UNREFERENCED_PARAMETER(lParam); + + szProto = (char*)CallService( MS_PROTO_GETCONTACTBASEPROTO, wParam, 0 ); + if (szProto!=NULL && !strcmp(szProto, SKYPE_PROTONAME)) { + DBVARIANT dbv; + int retval; + + if (DBGetContactSettingString((HANDLE)wParam, SKYPE_PROTONAME, SKYPE_NAME, &dbv)) return 1; + retval=SkypeSend("SET USER %s BUDDYSTATUS 1", dbv.pszVal); + DBFreeVariant(&dbv); + if (retval) return 1; + } + return 0; +} + +void GetInfoThread(HANDLE hContact) { + DBVARIANT dbv; + int i; + char *ptr; + BOOL bSetNick = FALSE; + // All properties are already handled in the WndProc, so we just consume the + // messages here to do proper ERROR handling + // If you add something here, be sure to handle it in WndProc, but let it + // fall through there so that message gets added to the queue in order to be + // consumed by SkypeGet + char *pszProps[] = { + "BIRTHDAY", "COUNTRY", "SEX", "MOOD_TEXT", "TIMEZONE", "IS_VIDEO_CAPABLE"}; + + + LOG (("GetInfoThread started.")); + EnterCriticalSection (&QueryThreadMutex); + if (DBGetContactSettingString(hContact, SKYPE_PROTONAME, SKYPE_NAME, &dbv)) + { + LOG (("GetInfoThread terminated, cannot find Skype Name for contact %08X.", hContact)); + LeaveCriticalSection (&QueryThreadMutex); + return; + } + + if (ptr=SkypeGet ("USER", dbv.pszVal, "DISPLAYNAME")) { + // WndProc sets Nick accordingly + if (*ptr) bSetNick = TRUE; + free (ptr); + } + + if (ptr=SkypeGet ("USER", dbv.pszVal, "FULLNAME")) { + if (*ptr && !bSetNick && DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "ShowFullname", 1)) { + // No Displayname and FULLNAME requested + SkypeDBWriteContactSettingUTF8String(hContact, SKYPE_PROTONAME, "Nick", ptr); + bSetNick = TRUE; + } + free (ptr); + } + + if (!bSetNick) { + // Still no nick set, so use SKYPE Nickname + DBWriteContactSettingString(hContact, SKYPE_PROTONAME, "Nick", dbv.pszVal); + } + + + if (!bIsImoproxy) + { + for (i=0; i= 7 || bIsImoproxy) { + // Notify about the possibility of an avatar + ACKDATA ack = {0}; + ack.cbSize = sizeof( ACKDATA ); + ack.szModule = SKYPE_PROTONAME; + ack.hContact = hContact; + ack.type = ACKTYPE_AVATAR; + ack.result = ACKRESULT_STATUS; + + CallService( MS_PROTO_BROADCASTACK, 0, ( LPARAM )&ack ); + //if (ptr=SkypeGet ("USER", dbv.pszVal, "RICH_MOOD_TEXT")) free (ptr); + } + + if (!bIsImoproxy) + { + for (i=0; i=5 || bIsImoproxy) { + SkypeSend ("CREATE APPLICATION libpurple_typing"); + testfor ("CREATE APPLICATION libpurple_typing", 2000); + } + if (protocol>=5) { + SearchUsersWaitingMyAuthorization(NULL); + if (DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UseGroupchat", 0)) + SearchRecentChats(NULL); + } + SkypeSend("SEARCH MISSED%sS", cmdMessage); + + +#ifndef SKYPEBUG_OFFLN + if (SkypeSend("GET USERSTATUS")==-1) + { + LOG (("SkypeSystemInit thread stopped with failure.")); + Initializing=FALSE; + return; + } +#endif + SetTimer (g_hWnd, 1, PING_INTERVAL, NULL); + SkypeInitialized=TRUE; + Initializing=FALSE; + LOG (("SkypeSystemInit thread terminated gracefully.")); + return; +} + +void FirstLaunch(char *dummy) { + int counter=0; + + UNREFERENCED_PARAMETER(dummy); + + LOG (("FirstLaunch thread started.")); + if (!DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "StartSkype", 1) || ConnectToSkypeAPI(skype_path, FALSE)==-1) + { + int oldstatus=SkypeStatus; + + LOG(("OnModulesLoaded starting offline..")); + InterlockedExchange((long *)&SkypeStatus, ID_STATUS_OFFLINE); + ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldstatus, SkypeStatus); + } + if (AttachStatus==-1 || AttachStatus==SKYPECONTROLAPI_ATTACH_REFUSED || AttachStatus==SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE) { + LOG (("FirstLaunch thread stopped because of invalid Attachstatus.")); + return; + } + + // When you launch Skype and Attach is Successfull, it still takes some time + // until it becomes available for receiving messages. + // Let's probe this with PINGing + LOG(("CheckIfApiIsResponding Entering test loop")); + for ( ;; ) { + LOG(("Test #%d", counter)); + if (SkypeSend("PING")==-1) counter ++; else break; + if (counter>=20) { + OUTPUT(_T("Cannot reach Skype API, plugin disfunct.")); + LOG (("FirstLaunch thread stopped: cannot reach Skype API.")); + return; + } + Sleep(500); + } + LOG(("CheckIfApiIsResponding: Testing for PONG")); + testfor("PONG", 2000); // Flush PONG from MsgQueue + + pthread_create(( pThreadFunc )SkypeSystemInit, NULL); + LOG (("FirstLaunch thread terminated gracefully.")); +} + +int CreateTopToolbarButton(WPARAM wParam, LPARAM lParam) { + TTBButton ttb={0}; + + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + ttb.cbSize = sizeof(ttb); + ttb.dwFlags = TTBBF_VISIBLE|TTBBF_SHOWTOOLTIP|TTBBF_DRAWBORDER; + ttb.hbBitmapDown = ttb.hbBitmapUp = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_CALL)); + ttb.pszServiceDown = ttb.pszServiceUp = SKYPEOUT_CALL; + ttb.name=Translate("Do a SkypeOut-call"); + if ((int)(httbButton=(HANDLE)CallService(MS_TTB_ADDBUTTON, (WPARAM)&ttb, 0))==-1) httbButton=0; + return 0; +} + + +int OnModulesLoaded(WPARAM wParam, LPARAM lParam) { + bModulesLoaded=TRUE; + + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + PopupServiceExists = ServiceExists(MS_POPUP_ADDPOPUPEX); + + logoff_contacts(FALSE); + + HookEventsLoaded(); + RegisterToUpdate(); + RegisterToDbeditorpp(); + VoiceServiceModulesLoaded(); + GCInit(); + + add_contextmenu(NULL); + if ( ServiceExists( MS_GC_REGISTER )) + { + GCREGISTER gcr = {0}; + static COLORREF crCols[1] = {0}; + char szEvent[MAXMODULELABELLENGTH]; + + gcr.cbSize = sizeof( GCREGISTER ); + gcr.dwFlags = GC_CHANMGR | GC_TCHAR; // |GC_ACKMSG; // TODO: Not implemented yet + gcr.ptszModuleDispName = _T("Skype protocol"); + gcr.pszModule = SKYPE_PROTONAME; + if (CallService(MS_GC_REGISTER, 0, (LPARAM)&gcr)) + { + OUTPUT(_T("Unable to register with Groupchat module!")); + } + _snprintf (szEvent, sizeof(szEvent), "%s\\ChatInit", SKYPE_PROTONAME); + hInitChat = CreateHookableEvent(szEvent); + hEvInitChat = HookEvent(szEvent, ChatInit); + + hChatEvent = HookEvent(ME_GC_EVENT, GCEventHook); + hChatMenu = HookEvent(ME_GC_BUILDMENU, GCMenuHook); + CreateServiceFunction (SKYPE_CHATNEW, SkypeChatCreate); + CreateProtoService (PS_LEAVECHAT, GCOnLeaveChat); + CreateProtoService (PS_JOINCHAT, GCOnJoinChat); + } + // Try folder service first + hProtocolAvatarsFolder = NULL; + if (ServiceExists(MS_FOLDERS_REGISTER_PATH)) + { + char *tmpPath; + + if (!ServiceExists (MS_UTILS_REPLACEVARS) || !(tmpPath = Utils_ReplaceVars("%miranda_avatarcache%"))) + tmpPath = PROFILE_PATH; + mir_snprintf(DefaultAvatarsFolder, sizeof(DefaultAvatarsFolder), "%s\\%s", tmpPath, SKYPE_PROTONAME); + hProtocolAvatarsFolder = (HANDLE) FoldersRegisterCustomPath(SKYPE_PROTONAME, "Avatars Cache", DefaultAvatarsFolder); + } + + if (hProtocolAvatarsFolder == NULL) + { + // Use defaults + CallService(MS_DB_GETPROFILEPATH, (WPARAM) MAX_PATH, (LPARAM) DefaultAvatarsFolder); + mir_snprintf(DefaultAvatarsFolder, sizeof(DefaultAvatarsFolder), "%s\\%s", DefaultAvatarsFolder, SKYPE_PROTONAME); + CreateDirectoryA(DefaultAvatarsFolder, NULL); + } + + pthread_create(( pThreadFunc )FirstLaunch, NULL); + return 0; +} + +void FetchMessageThread(fetchmsg_arg *pargs) { + char *who=NULL, *type=NULL, *chat=NULL, *users=NULL, *msg=NULL, *status=NULL; + char *ptr, *msgptr, szPartnerHandle[32], szBuf[128]; + int direction=0, msglen = 0; + DWORD timestamp = 0, lwr=0; + CCSDATA ccs={0}; + PROTORECVEVENT pre={0}; + HANDLE hContact = NULL, hDbEvent, hChat = NULL; + DBEVENTINFO dbei={0}; + DBVARIANT dbv={0}; + fetchmsg_arg args; + BOOL bEmoted=FALSE, isGroupChat=FALSE, bHasPartList=FALSE; + BOOL bUseGroupChat = DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UseGroupchat", 0); + + if (!pargs) return; + args = *pargs; + free (pargs); + + sprintf (szPartnerHandle, "%s_HANDLE", cmdPartner); + pre.lParam = strtoul(args.msgnum, NULL, 10); + if (args.bIsRead) pre.flags |= PREF_CREATEREAD; + //pEvent = MsgList_FindMessage(pre.lParam); + + // Get Timestamp + if (!args.pMsgEntry || !args.pMsgEntry->tEdited) { + if (!(ptr=SkypeGet (cmdMessage, args.msgnum, "TIMESTAMP"))) return; + if (strncmp(ptr, "ERROR", 5)) timestamp=atol(ptr); + else timestamp=(DWORD)SkypeTime(NULL); + free(ptr); + } else timestamp=(DWORD)(args.pMsgEntry->tEdited); + + __try { + // Get Chatname (also to determine if we need to relay this to a groupchat) + if (!(chat=SkypeGetErr (cmdMessage, args.msgnum, "CHATNAME"))) __leave; + if (hChat = find_chatA(chat)) isGroupChat=TRUE; + + // Get chat status + if ((status=SkypeGetErr ("CHAT", chat, "STATUS")) && + !strcmp(status, "MULTI_SUBSCRIBED")) isGroupChat=TRUE; + + // Get chat type + if (!(type=SkypeGetErr (cmdMessage, args.msgnum, "TYPE"))) __leave; + bEmoted = strcmp(type, "EMOTED")==0; + if (strcmp(type, "MULTI_SUBSCRIBED")==0) isGroupChat=TRUE; + + // Group chat handling + if (isGroupChat && strcmp(type, "TEXT") && strcmp(type, "SAID") && strcmp(type, "UNKNOWN") && !bEmoted) { + if (bUseGroupChat) { + BOOL bAddedMembers = FALSE; + + if (!strcmp(type,"SAWMEMBERS") || !strcmp(type, "CREATEDCHATWITH")) + { + // We have a new Groupchat + LOG(("FetchMessageThread CHAT SAWMEMBERS")); + if (!hChat) ChatStart(chat, FALSE); + __leave; + } + if (!strcmp(type,"KICKED")) + { + GCDEST gcd = {0}; + GCEVENT gce = {0}; + CONTACTINFO ci = {0}; + + if (!hChat) __leave; + gcd.pszModule = SKYPE_PROTONAME; + gcd.ptszID = make_nonutf_tchar_string((const unsigned char*)chat); + gcd.iType = GC_EVENT_KICK; + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + gce.dwFlags = GCEF_ADDTOLOG | GC_TCHAR; + gce.time = timestamp; + + if (users=SkypeGetErr (cmdMessage, args.msgnum, "USERS")) { + ci.hContact = find_contact(users); + gce.ptszUID= make_nonutf_tchar_string((const unsigned char*)users); + if (who=SkypeGetErr (cmdMessage, args.msgnum, szPartnerHandle)) { + gce.ptszStatus= make_nonutf_tchar_string((const unsigned char*)who); + ci.cbSize = sizeof(ci); + ci.szProto = SKYPE_PROTONAME; + ci.dwFlag = CNF_DISPLAY | CNF_TCHAR; + if (ci.hContact && !CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&ci)) gce.ptszNick=ci.pszVal; + else gce.ptszNick=gce.ptszUID; + + CallService(MS_GC_EVENT, 0, (LPARAM)&gce); + RemChatContact (GetChat(gcd.ptszID), gce.ptszUID); + free_nonutf_tchar_string((void*)gce.ptszStatus); + if (ci.pszVal) miranda_sys_free (ci.pszVal); + } + free_nonutf_tchar_string((void*)gce.ptszUID); + } + free_nonutf_tchar_string((void*)gcd.ptszID); + __leave; + } + if (!strcmp(type,"SETROLE")) + { + GCDEST gcd = {0}; + GCEVENT gce = {0}; + CONTACTINFO ci = {0}; + gchat_contact *gcContact; + char *pszRole; + + // FROM_HANDLE - Wer hats gesetzt + // USERS - Wessen Rolle wurde gesetzt + // ROLE - Die neue Rolle + if (!hChat) __leave; + gcd.pszModule = SKYPE_PROTONAME; + gcd.ptszID = make_nonutf_tchar_string((const unsigned char*)chat); + gcd.iType = GC_EVENT_REMOVESTATUS; + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + gce.dwFlags = GCEF_ADDTOLOG | GC_TCHAR; + gce.time = timestamp; + + if (users=SkypeGetErr (cmdMessage, args.msgnum, "USERS")) { + gce.ptszUID= make_nonutf_tchar_string((const unsigned char*)users); + if (who=SkypeGetErr (cmdMessage, args.msgnum, szPartnerHandle)) { + ci.cbSize = sizeof(ci); + ci.szProto = SKYPE_PROTONAME; + ci.dwFlag = CNF_DISPLAY | CNF_TCHAR; + ci.hContact = find_contact(who); + if (ci.hContact && !CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&ci)) { + gce.ptszText=_tcsdup(ci.pszVal); + miranda_sys_free (ci.pszVal); + ci.pszVal = NULL; + } + else gce.ptszText=make_tchar_string((const unsigned char*)who); + + ci.hContact = find_contact(users); + if (ci.hContact && !CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&ci)) gce.ptszNick=ci.pszVal; + else gce.ptszNick=gce.ptszUID; + + if (gcContact = GetChatContact(GetChat(gcd.ptszID), gce.ptszUID)) + { + gce.ptszStatus = gcContact->szRole; + CallService(MS_GC_EVENT, 0, (LPARAM)&gce); + } + if (pszRole=SkypeGetErr (cmdMessage, args.msgnum, "ROLE")) { + gce.ptszStatus = make_nonutf_tchar_string((const unsigned char*)pszRole); + gcd.iType = GC_EVENT_ADDSTATUS; + CallService(MS_GC_EVENT, 0, (LPARAM)&gce); + free_nonutf_tchar_string((void*)gce.ptszStatus); + free (pszRole); + } + free((void*)gce.ptszText); + if (ci.pszVal) miranda_sys_free (ci.pszVal); + } + free_nonutf_tchar_string((void*)gce.ptszUID); + } + free_nonutf_tchar_string((void*)gcd.ptszID); + __leave; + } + if (!strcmp(type,"SETTOPIC")) + { + GCDEST gcd = {0}; + GCEVENT gce = {0}; + CONTACTINFO ci = {0}; + + LOG(("FetchMessageThread CHAT SETTOPIC")); + gcd.pszModule = SKYPE_PROTONAME; + gcd.ptszID = make_nonutf_tchar_string((const unsigned char*)chat); + gcd.iType = GC_EVENT_TOPIC; + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + gce.dwFlags = GCEF_ADDTOLOG | GC_TCHAR; + gce.time = timestamp; + if (who=SkypeGetErr (cmdMessage, args.msgnum, szPartnerHandle)) { + ci.hContact = find_contact(who); + gce.ptszUID = make_nonutf_tchar_string((const unsigned char*)who); + ci.cbSize = sizeof(ci); + ci.szProto = SKYPE_PROTONAME; + ci.dwFlag = CNF_DISPLAY | CNF_TCHAR; + if (ci.hContact && !CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&ci)) gce.ptszNick=ci.pszVal; + else gce.ptszNick=gce.ptszUID; + + if (ptr=SkypeGetErr (cmdMessage, args.msgnum, "BODY")) { + gce.ptszText = make_tchar_string((const unsigned char*)ptr); + CallService(MS_GC_EVENT, 0, (LPARAM)&gce); + free ((void*)gce.ptszText); + free (ptr); + } + free_nonutf_tchar_string ((void*)gce.ptszUID); + if (ci.pszVal) miranda_sys_free (ci.pszVal); + } + free_nonutf_tchar_string((void*)gcd.ptszID); + __leave; + } + if (!strcmp(type,"LEFT") || (bAddedMembers = strcmp(type,"ADDEDMEMBERS")==0)) + { + GCDEST gcd = {0}; + GCEVENT gce = {0}; + CONTACTINFO ci = {0}; + char *pszInvited = Translate("invited "); + + LOG(("FetchMessageThread CHAT LEFT or ADDEDMEMBERS")); + if (bAddedMembers) { + gcd.pszModule = SKYPE_PROTONAME; + gcd.ptszID = make_nonutf_tchar_string((const unsigned char*)chat); + gcd.iType = GC_EVENT_ACTION; + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + gce.dwFlags = GCEF_ADDTOLOG | GC_TCHAR; + gce.time = timestamp; + if (users=SkypeGetErr (cmdMessage, args.msgnum, "USERS")) { + // We assume that users buffer has enough room for "invited" string + memmove (users+strlen(pszInvited), users, strlen(users)+1); + memcpy (users, pszInvited, strlen(pszInvited)); + gce.ptszText= make_tchar_string((const unsigned char*)users); + if (who=SkypeGetErr (cmdMessage, args.msgnum, szPartnerHandle)) { + DBVARIANT dbv; + + if (DBGetContactSettingString(NULL, SKYPE_PROTONAME, SKYPE_NAME, &dbv)==0) { + gce.bIsMe = strcmp(who, dbv.pszVal)==0; + DBFreeVariant(&dbv); + } + if (!gce.bIsMe) ci.hContact = find_contact(who); + gce.ptszUID= make_nonutf_tchar_string((const unsigned char*)who); + ci.cbSize = sizeof(ci); + ci.szProto = SKYPE_PROTONAME; + ci.dwFlag = CNF_DISPLAY | CNF_TCHAR; + if (!CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&ci)) gce.ptszNick=ci.pszVal; + else gce.ptszNick=gce.ptszUID; + + CallService(MS_GC_EVENT, 0, (LPARAM)&gce); + free_nonutf_tchar_string((void*)gce.ptszUID); + if (ci.pszVal) miranda_sys_free (ci.pszVal); + } + if (gce.ptszText) free ((void*)gce.ptszText); + } + free_nonutf_tchar_string ((void*)gcd.ptszID); + } + if (!args.QueryMsgDirection) SkypeSend ("GET CHAT %s MEMBERS", chat); + __leave; + } + } + __leave; + } + + // Need to get the status? + if (args.getstatus) { + char *status; + + if (protocol<4) InterlockedIncrement (&rcvwatchers); + status=SkypeGetID(cmdMessage, args.msgnum, "STATUS"); + if (protocol<4) InterlockedDecrement (&rcvwatchers); + if (!status) __leave; + if (!strcmp(status, "SENT")) direction=DBEF_SENT; + free(status); + } + + // Who sent it? + if (!(who=SkypeGetErr (cmdMessage, args.msgnum, szPartnerHandle))) __leave; + + // Get contact handle + LOG(("FetchMessageThread Finding contact handle")); + DBGetContactSettingString(NULL, SKYPE_PROTONAME, SKYPE_NAME, &dbv); + if (dbv.pszVal && !strcmp (who, dbv.pszVal)) + { + char *pTok, *nextoken; + + // It's from me.. But to whom? + // CHATMESSAGE .. USERS doesn't return anything, so we have to query the CHAT-Object + if (!(ptr=SkypeGetErr ("CHAT", chat, "ACTIVEMEMBERS"))) { + DBFreeVariant (&dbv); + __leave; + } + + for (pTok = strtok_r (ptr, " ", &nextoken); pTok; pTok=strtok_r(NULL, " ", &nextoken)) { + if (strcmp (pTok, dbv.pszVal)) break; // Take the first dude in the list who is not me + } + + if (!pTok) { + free (ptr); + DBFreeVariant (&dbv); + __leave; // We failed + } + free (who); + who=memmove (ptr, pTok, strlen(pTok)+1); + direction = DBEF_SENT; + } + DBFreeVariant (&dbv); + + if (!(hContact=find_contact(who))) { + // Permanent adding of user obsolete, we use the BUDDYSTATUS now (bug #0000005) + ResetEvent(hBuddyAdded); + SkypeSend("GET USER %s BUDDYSTATUS", who); + WaitForSingleObject(hBuddyAdded, INFINITE); + if (!(hContact=find_contact(who))) { + // Arrgh, the user has been deleted from contact list. + // In this case, we add him temp. to receive the msg at least. + hContact=add_contact(who, PALF_TEMPORARY); + } + } + // Text which was sent (on edited msg, BODY may already be in queue, check) + sprintf (szBuf, "GET %s %s BODY", cmdMessage, args.msgnum); + if (!args.pMsgEntry || !args.pMsgEntry->tEdited || !(ptr=SkypeRcv(szBuf+4, 1000))) + { + if (SkypeSend(szBuf)==-1 || !(ptr=SkypeRcv(szBuf+4, INFINITE))) + __leave; + } + if (strncmp(ptr, "ERROR", 5)) { + msgptr = ptr+strlen(szBuf+4)+1; + bHasPartList = strncmp(msgptr,"tEdited) { + // Mark the message as edited + if (!*msgptr && args.pMsgEntry->hEvent != INVALID_HANDLE_VALUE) { + // Empty message and edited -> Delete event + if ((int)(hContact = (HANDLE)CallService (MS_DB_EVENT_GETCONTACT, (WPARAM)args.pMsgEntry->hEvent, 0)) != -1) { + CallService (MS_DB_EVENT_DELETE, (WPARAM)hContact, (LPARAM)args.pMsgEntry->hEvent); + free (ptr); + __leave; + } + } else { + msgptr-=9; + memcpy (msgptr, "[EDITED] ", 9); + } + } + if( bEmoted && !isGroupChat) { + CONTACTINFO ci = {0}; + int newlen; + char *pMsg, *pszUTFnick=NULL; + ci.cbSize = sizeof(ci); + ci.szProto = SKYPE_PROTONAME; + ci.dwFlag = CNF_DISPLAY | CNF_TCHAR; + if (ci.hContact = hContact) { + CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&ci); + if (ci.pszVal) { +#ifdef _UNICODE + pszUTFnick = (char*)make_utf8_string(ci.pszVal); +#else + utf8_encode (ci.pszVal, &pszUTFnick); +#endif + miranda_sys_free (ci.pszVal); + } + } + newlen = strlen(msgptr) + (pszUTFnick?strlen(pszUTFnick):0) + 9; + if (pMsg = malloc(newlen)) { + sprintf (pMsg, "** %s%s%s **", (pszUTFnick?pszUTFnick:""),(pszUTFnick?" ":""),(char*)msgptr); + free (ptr); + ptr = msgptr = pMsg; + } + if (pszUTFnick) free(pszUTFnick); + } + + if (mirandaVersion >= 0x070000 && // 0.7.0+ supports PREF_UTF flag, no need to decode UTF8 + !isGroupChat) { // I guess Groupchat doesn't support UTF8? + msg = ptr; + pre.flags |= PREF_UTF; + } else { // Older version has to decode either UTF8->ANSI or UTF8->UNICODE + // This could be replaced by mir_getUTFI - functions for Miranda 0.5+ builds, but we stay + // 0.4 compatible for backwards compatibility. Unfortunately this requires us to link with utf8.c +#ifdef _UNICODE + int wcLen; +#endif + + if (utf8_decode(msgptr, &msg)==-1) { + free(ptr); + __leave; + } +#ifdef _UNICODE + msglen = strlen(msg)+1; + msgptr = (char*)make_unicode_string ((const unsigned char*)msgptr); + wcLen = (_tcslen((TCHAR*)msgptr)+1)*sizeof(TCHAR); + msg=realloc(msg, msglen+wcLen); + memcpy (msg+msglen, msgptr, wcLen); + free(msgptr); + pre.flags |= PREF_UNICODE; +#endif + msgptr = msg; + free (ptr); + } + msglen = strlen(msgptr)+1; + } else { + free (ptr); + __leave; + } + // skype sends some xml statics after a call has finished. Check if thats the case and suppress it if necessary... + if ((DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "SuppressCallSummaryMessage", 1) && + bHasPartList) || msgptr[0]==0) __leave; + + if (isGroupChat && bUseGroupChat) { + GCDEST gcd = {0}; + GCEVENT gce = {0}; + DBVARIANT dbv = {0}; + CONTACTINFO ci = {0}; + + LOG(("FetchMessageThread This is a group chat message")); + if (!hChat) ChatStart(chat, FALSE); + gcd.pszModule = SKYPE_PROTONAME; + gcd.ptszID = make_nonutf_tchar_string((const unsigned char*)chat); + gcd.iType = bEmoted?GC_EVENT_ACTION:GC_EVENT_MESSAGE; + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + if ((gce.bIsMe = (direction&DBEF_SENT)?TRUE:FALSE) && + DBGetContactSettingString(NULL, SKYPE_PROTONAME, SKYPE_NAME, &dbv)==0) + { + free(who); + who = _strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + gce.ptszUID = make_nonutf_tchar_string((const unsigned char*)who); + ci.cbSize = sizeof(ci); + ci.szProto = SKYPE_PROTONAME; + ci.dwFlag = CNF_DISPLAY | CNF_TCHAR; + ci.hContact = !gce.bIsMe?hContact:NULL; + gce.ptszNick=gce.ptszUID; + if (!CallService(MS_CONTACT_GETCONTACTINFO,0,(LPARAM)&ci)) gce.ptszNick=ci.pszVal; + gce.time = timestamp>0?timestamp:(DWORD)SkypeTime(NULL); + gce.pszText = msgptr; + if (pre.flags & PREF_UNICODE) gce.pszText += msglen; + gce.dwFlags = GCEF_ADDTOLOG | GC_TCHAR; + CallService(MS_GC_EVENT, 0, (LPARAM)&gce); + MsgList_Add (pre.lParam, INVALID_HANDLE_VALUE); // Mark as groupchat + if (ci.pszVal) miranda_sys_free (ci.pszVal); + free_nonutf_tchar_string((void*)gce.ptszUID); + free_nonutf_tchar_string(gcd.ptszID); + + // Yes, we have successfully read the msg + if (!args.bDontMarkSeen) + SkypeSend("SET %s %s SEEN", cmdMessage, args.msgnum); + __leave; + } + + if (args.QueryMsgDirection || (direction&DBEF_SENT)) { + // Check if the timestamp is valid + dbei.cbSize=sizeof(dbei); + dbei.cbBlob=0; + if (hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDFIRST,(WPARAM)hContact,0)) { + CallService(MS_DB_EVENT_GET,(WPARAM)hDbEvent,(LPARAM)&dbei); + lwr=dbei.timestamp; + } + dbei.cbSize=sizeof(dbei); + dbei.cbBlob=0; + dbei.timestamp=0; + if (hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDLAST,(WPARAM)hContact,0)) + CallService(MS_DB_EVENT_GET,(WPARAM)hDbEvent,(LPARAM)&dbei); + LOG(("FetchMessageThread timestamp %ld between %ld and %ld", timestamp, lwr, dbei.timestamp)); + if (timestamp0?timestamp:(DWORD)SkypeTime(NULL); + dbei.flags=direction; + if (pre.flags & PREF_CREATEREAD) dbei.flags|=DBEF_READ; + if (pre.flags & PREF_UTF) dbei.flags|=DBEF_UTF; + dbei.eventType=EVENTTYPE_MESSAGE; + pme = MsgList_Add (pre.lParam, (HANDLE)CallServiceSync(MS_DB_EVENT_ADD, (WPARAM)(HANDLE)hContact, (LPARAM)&dbei)); + + // We could call MS_PROTO_CHAINSEND if we want to have MetaContact adding the history for us, + // however we all know that CCSDATA doesn't contain timestamp-information which is + // really bad on importing history for example, as all messages would be added with current + // timestamp. This would cause unreliable jumbled timestamps in metacontact, so we better do this + // ourself. + if (DBGetContactSettingByte(hContact, "MetaContacts", "IsSubcontact", 0)) + { + DWORD dwMetaLink = DBGetContactSettingDword(hContact, "MetaContacts", "MetaLink", MAXDWORD); + HANDLE hMetaContact; + + if (dwMetaLink != MAXDWORD && (hMetaContact = GetMetaHandle(dwMetaLink))) + { + dbei.szModule=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hMetaContact, 0); + pme->hMetaEvent = (HANDLE)CallServiceSync(MS_DB_EVENT_ADD, (WPARAM)(HANDLE)hMetaContact, (LPARAM)&dbei); + } + } + + if (!args.QueryMsgDirection && !args.bDontMarkSeen) + SkypeSend("SET %s %s SEEN", cmdMessage, args.msgnum); + } + } + + + if (!(direction&DBEF_SENT) && (!args.QueryMsgDirection || (args.QueryMsgDirection && timestamp>dbei.timestamp))) { + LOG(("FetchMessageThread Normal message add...")); + // Normal message received, process it + ccs.szProtoService = PSR_MESSAGE; + ccs.hContact = hContact; + ccs.wParam = 0; + ccs.lParam = (LPARAM)⪯ + pre.flags |= direction; + if(isGroupChat && DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "MarkGroupchatRead", 0)) + pre.flags |= PREF_CREATEREAD; + pre.timestamp = timestamp>0?timestamp:(DWORD)SkypeTime(NULL); + pre.szMessage = msgptr; + CallServiceSync(MS_PROTO_CHAINRECV, 0, (LPARAM) &ccs); + + // Yes, we have successfully read the msg + if (!args.bDontMarkSeen) SkypeSend("SET %s %s SEEN", cmdMessage, args.msgnum); + } + } + __finally { + if (status) free(status); + if (msg) free(msg); + if (users) free(users); + if (chat) free(chat); + if (type) free(type); + if (who) free (who); + } + +} + +void FetchMessageThreadSync(fetchmsg_arg *pargs) { + // Secure this thread with a mutex. + // This is needed to ensure that we get called after an old msg in the queue has + // been added so that MsgList_FindEntry will find it. + WaitForSingleObject (FetchMessageEvent, 30000); // Wait max. 30 sec. for previous message fetch to complete + if ((pargs->pMsgEntry = MsgList_FindMessage(strtoul(pargs->msgnum, NULL, 10))) && !pargs->pMsgEntry->tEdited) { + // Better don't do this, as we set the msg as read and with this code, we would + // mark messages not opened by user as read which isn't that good + /* + if (pargs->bIsRead && pMsgEvent->hEvent != INVALID_HANDLE_VALUE) + { + HANDLE hContact; + if ((int)(hContact = (HANDLE)CallService (MS_DB_EVENT_GETCONTACT, (WPARAM)pMsgEntry->hEvent, 0)) != -1) + CallService (MS_DB_EVENT_MARKREAD, (WPARAM)hContact, (LPARAM)hDBEvent); + } + */ + free (pargs); + } + else FetchMessageThread (pargs); + SetEvent (FetchMessageEvent); +} + +static int MsglCmpProc(const void *pstPElement,const void *pstPToFind) +{ + return strcmp ((char*)((fetchmsg_arg*)pstPElement)->pMsgEntry, (char*)((fetchmsg_arg*)pstPToFind)->pMsgEntry); +} + +void MessageListProcessingThread(char *str) { + char *token, *nextoken, *chat=NULL; + fetchmsg_arg *args; + TYP_LIST *hListMsgs = List_Init(32); + int i, nCount; + + // Frst we need to sort the message timestamps + for ((token=strtok_r(str, ", ", &nextoken)); token; token=strtok_r(NULL, ", ", &nextoken)) { + if (args=calloc(1, sizeof(fetchmsg_arg)+sizeof(DWORD))) { + strncpy (args->msgnum, token, sizeof(args->msgnum)); + args->getstatus=TRUE; + args->bIsRead=TRUE; + args->bDontMarkSeen=TRUE; + args->QueryMsgDirection=TRUE; + (char*)args->pMsgEntry = SkypeGet ("CHATMESSAGE", token, "TIMESTAMP"); // Bad abuse of pointer + if (!chat) chat=SkypeGet ("CHATMESSAGE", token, "CHATNAME"); + if (args->pMsgEntry) List_InsertSort (hListMsgs, MsglCmpProc, args); + else free(args); + } + } + for (i=0, nCount=List_Count(hListMsgs); ipMsgEntry); + args->pMsgEntry = NULL; + FetchMessageThreadSync (args); + } + if (chat) { + SkypeSend ("GET CHAT %s MEMBERS", chat); + free (chat); + } + List_Exit (hListMsgs); + free (str); +} + +char *GetCallerHandle(char *szSkypeMsg) { + return SkypeGet(szSkypeMsg, "PARTNER_HANDLE", ""); +} + + +HANDLE GetCallerContact(char *szSkypeMsg) { + char *szHandle; + HANDLE hContact=NULL; + + if (!(szHandle=GetCallerHandle(szSkypeMsg))) return NULL; + if (!(hContact=find_contact(szHandle))) { + // If it's a SkypeOut-contact, PARTNER_HANDLE = SkypeOUT number + DBVARIANT dbv; + int tCompareResult; + + for (hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);hContact != NULL;hContact=(HANDLE)CallService( MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0)) { + if (DBGetContactSettingString(hContact, SKYPE_PROTONAME, "SkypeOutNr", &dbv)) continue; + tCompareResult = strcmp(dbv.pszVal, szHandle); + DBFreeVariant(&dbv); + if (tCompareResult) continue; else break; + } + } + free(szHandle); + if (!hContact) {LOG(("GetCallerContact Not found!"));} + return hContact; +} + +HANDLE GetMetaHandle(DWORD dwId) { + HANDLE hContact; + char *szProto; + + for (hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);hContact != NULL;hContact=(HANDLE)CallService( MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0)) { + szProto = (char*)CallService( MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0 ); + if (szProto!=NULL && !strcmp(szProto, "MetaContacts") && + DBGetContactSettingDword(hContact, "MetaContacts", "MetaID", MAXDWORD)==dwId) + return hContact; + } + return 0; +} + +LRESULT CALLBACK InCallPopUpProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) +{ + switch(msg) + { + case WM_COMMAND: + break; + + case WM_CONTEXTMENU: + SendMessage(hwnd,UM_DESTROYPOPUP,0,0); + break; + case UM_FREEPLUGINDATA: + //Here we'd free our own data, if we had it. + return FALSE; + case UM_INITPOPUP: + break; + case UM_DESTROYPOPUP: + break; + case WM_NOTIFY: + default: + break; + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + +void RingThread(char *szSkypeMsg) { + HANDLE hContact; + DBEVENTINFO dbei={0}; + DBVARIANT dbv; + char *ptr = NULL; + + // We use a single critical section for the RingThread- and the EndCallThread-functions + // so that only one function is running at the same time. This is needed, because when + // a initated and unaccepted call (which is still ringing) is hangup/canceled, skype + // sends two messages. First "CALL xxx STATUS RINGING" .. second "CALL xx STATUS CANCELED". + // This starts two independend threads (first: RingThread; second: EndCallThread). Now + // the two message are processed in reverse order sometimes. This causes the EndCallThread to + // delete the contacts "CallId" property and after that the RingThread saves the contacts + // "CallId" again. After that its not possible to call this contact, because the plugin + // thinks that there is already a call going and the hangup-function isnt working, because + // skype doesnt accept status-changes for finished calls. The CriticalSection syncronizes + // the threads and the messages are processed in correct order. + // Not the best solution, but it works. + EnterCriticalSection (&RingAndEndcallMutex); + + LOG(("RingThread started.")); + if (protocol >= 5) SkypeSend ("MINIMIZE"); + if (hContact=GetCallerContact(szSkypeMsg)) { + // Make sure that an answering thread is not already in progress so that we don't get + // the 'Incoming call' event twice + if (!DBGetContactSettingString(hContact, SKYPE_PROTONAME, "CallId", &dbv)) { + DBFreeVariant(&dbv); + LOG(("RingThread terminated.")); + goto l_exitRT; + } + DBWriteContactSettingString(hContact, SKYPE_PROTONAME, "CallId", szSkypeMsg); + } + + if (!(ptr=SkypeGet(szSkypeMsg, "TYPE", ""))) { + LOG(("RingThread terminated.")); + goto l_exitRT;; + } + + if (!strncmp(ptr, "INCOMING", 8)) + NofifyVoiceService(hContact, szSkypeMsg, VOICE_STATE_RINGING); + else + NofifyVoiceService(hContact, szSkypeMsg, VOICE_STATE_CALLING); + + if (!strncmp(ptr, "INCOMING", 8)) { + if (!hContact) { + char *szHandle; + + if (szHandle=GetCallerHandle(szSkypeMsg)) { + if (!(hContact=add_contact(szHandle, PALF_TEMPORARY))) { + free(szHandle); + goto l_exitRT; + } + DBDeleteContactSetting(hContact, "CList", "Hidden"); + DBWriteContactSettingWord(hContact, SKYPE_PROTONAME, "Status", (WORD)SkypeStatusToMiranda("SKYPEOUT")); + DBWriteContactSettingString(hContact, SKYPE_PROTONAME, "SkypeOutNr", szHandle); + free(szHandle); + } else goto l_exitRT; + } + } + + if (HasVoiceService()) { + // Voice service will handle it + goto l_exitRT; + } + + dbei.cbSize=sizeof(dbei); + dbei.eventType=EVENTTYPE_CALL; + dbei.szModule=SKYPE_PROTONAME; + dbei.timestamp=(DWORD)SkypeTime(NULL); + dbei.pBlob=(unsigned char*)Translate("Phonecall"); + dbei.cbBlob=strlen((const char*)dbei.pBlob)+1; + if (!strncmp(ptr, "INCOMING", 8)) + { + CLISTEVENT cle={0}; + char toolTip[256]; + + if(PopupServiceExists) + { + BOOL showPopup, popupWindowColor; + unsigned int popupBackColor, popupTextColor; + int popupTimeSec; + POPUPDATAT InCallPopup; + TCHAR * lpzContactName = (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME,(WPARAM)hContact,GCDNF_TCHAR); + + popupTimeSec = DBGetContactSettingDword(NULL, SKYPE_PROTONAME, "popupTimeSec", 4); + popupTextColor = DBGetContactSettingDword(NULL, SKYPE_PROTONAME, "popupTextColor", GetSysColor(COLOR_WINDOWTEXT)); + popupBackColor = DBGetContactSettingDword(NULL, SKYPE_PROTONAME, "popupBackColor", GetSysColor(COLOR_BTNFACE)); + popupWindowColor = (0 != DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "popupWindowColor", TRUE)); + showPopup = (0 != DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "showPopup", TRUE)); + + InCallPopup.lchContact = hContact; + InCallPopup.lchIcon = LoadIcon(hInst,MAKEINTRESOURCE(IDI_CALL)); + InCallPopup.colorBack = ! popupWindowColor ? popupBackColor : GetSysColor(COLOR_BTNFACE); + InCallPopup.colorText = ! popupWindowColor ? popupTextColor : GetSysColor(COLOR_WINDOWTEXT); + InCallPopup.iSeconds = popupTimeSec; + InCallPopup.PluginWindowProc = (WNDPROC)InCallPopUpProc; + InCallPopup.PluginData = (void *)1; + + lstrcpy(InCallPopup.lptzText, TranslateT("Incoming Skype Call")); + + lstrcpy(InCallPopup.lptzContactName, lpzContactName); + + if(showPopup) + CallService(MS_POPUP_ADDPOPUPT,(WPARAM)&InCallPopup,0); + + } + cle.cbSize=sizeof(cle); + cle.hIcon=LoadIcon(hInst,MAKEINTRESOURCE(IDI_CALL)); + cle.pszService=SKYPE_ANSWERCALL; + dbei.flags=DBEF_READ; + cle.hContact=hContact; + cle.hDbEvent=(HANDLE)CallService(MS_DB_EVENT_ADD,(WPARAM)hContact,(LPARAM)&dbei); + _snprintf(toolTip,sizeof(toolTip),Translate("Incoming call from %s"),(char*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME,(WPARAM)hContact,0)); + cle.pszTooltip=toolTip; + CallServiceSync(MS_CLIST_ADDEVENT,0,(LPARAM)&cle); + } + else + { + dbei.flags=DBEF_SENT; + CallService(MS_DB_EVENT_ADD,(WPARAM)hContact,(LPARAM)&dbei); + } + +l_exitRT: + if (ptr) free (ptr); + free(szSkypeMsg); + LeaveCriticalSection (&RingAndEndcallMutex); +} + +void EndCallThread(char *szSkypeMsg) { + HANDLE hContact=NULL, hDbEvent; + DBEVENTINFO dbei={0}; + DBVARIANT dbv; + int tCompareResult; + + // We use a single critical section for the RingThread- and the EndCallThread-functions + // so that only one function is running at the same time. This is needed, because when + // a initated and unaccepted call (which is still ringing) is hangup/canceled, skype + // sends two messages. First "CALL xxx STATUS RINGING" .. second "CALL xx STATUS CANCELED". + // This starts two independend threads (first: RingThread; second: EndCallThread). Now + // the two message are processed in reverse order sometimes. This causes the EndCallThread to + // delete the contacts "CallId" property and after that the RingThread saves the contacts + // "CallId" again. After that its not possible to call this contact, because the plugin + // thinks that there is already a call going and the hangup-function isnt working, because + // skype doesnt accept status-changes for finished calls. The CriticalSection syncronizes + // the threads and the messages are processed in correct order. + // Not the best solution, but it works. + EnterCriticalSection (&RingAndEndcallMutex); + + LOG(("EndCallThread started.")); + if (szSkypeMsg) { + for (hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);hContact != NULL;hContact=(HANDLE)CallService( MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0)) { + if (DBGetContactSettingString(hContact, SKYPE_PROTONAME, "CallId", &dbv)) continue; + tCompareResult = strcmp(dbv.pszVal, szSkypeMsg); + DBFreeVariant(&dbv); + if (tCompareResult) continue; else break; + } + } + if (hContact) + { + NofifyVoiceService(hContact, szSkypeMsg, VOICE_STATE_ENDED); + + DBDeleteContactSetting(hContact, SKYPE_PROTONAME, "CallId"); + + if (!HasVoiceService()) { + dbei.cbSize=sizeof(dbei); + hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDFIRSTUNREAD,(WPARAM)hContact,0); + while(hDbEvent) { + dbei.cbBlob=0; + CallService(MS_DB_EVENT_GET,(WPARAM)hDbEvent,(LPARAM)&dbei); + if (!(dbei.flags&(DBEF_SENT|DBEF_READ)) && dbei.eventType==EVENTTYPE_CALL) { + CallService(MS_DB_EVENT_MARKREAD,(WPARAM)hContact,(LPARAM)hDbEvent); + CallService(MS_CLIST_REMOVEEVENT,(WPARAM)hContact,(LPARAM)hDbEvent); + } + if (dbei.pBlob) free(dbei.pBlob); + hDbEvent=(HANDLE)CallService(MS_DB_EVENT_FINDNEXT,(WPARAM)hDbEvent,0); + } + } + + if (!DBGetContactSettingString(hContact, SKYPE_PROTONAME, "SkypeOutNr", &dbv)) { + DBFreeVariant(&dbv); + if (!strcmp((char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0), SKYPE_PROTONAME) && + DBGetContactSettingByte(hContact, "CList", "NotOnList", 0) + ) + CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); + } + } + free(szSkypeMsg); + LeaveCriticalSection (&RingAndEndcallMutex); +} + +void HoldCallThread(char *szSkypeMsg) { + HANDLE hContact; + + LOG(("HoldCallThread started")); + if (!szSkypeMsg) { + LOG(("HoldCallThread terminated.")); + return; + } + if (hContact=GetCallerContact(szSkypeMsg)) { + DBWriteContactSettingByte(hContact, SKYPE_PROTONAME, "OnHold", 1); + NofifyVoiceService(hContact, szSkypeMsg, VOICE_STATE_ON_HOLD); + } + free(szSkypeMsg); + LOG(("HoldCallThread terminated gracefully")); +} + +void ResumeCallThread(char *szSkypeMsg) { + HANDLE hContact; + + LOG(("ResumeCallThread started")); + if (!szSkypeMsg) { + LOG(("ResumeCallThread terminated.")); + return; + } + if (hContact=GetCallerContact(szSkypeMsg)) { + DBDeleteContactSetting(hContact, SKYPE_PROTONAME, "OnHold"); + NofifyVoiceService(hContact, szSkypeMsg, VOICE_STATE_TALKING); + } + free(szSkypeMsg); + LOG(("ResumeCallThread terminated gracefully.")); +} + +int SetUserStatus(void) { + if (RequestedStatus && AttachStatus!=-1) { + if (SkypeSend("SET USERSTATUS %s", RequestedStatus)==-1) return 1; + } + return 0; +} + +void LaunchSkypeAndSetStatusThread(void *newStatus) { + +/* if (!DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UnloadOnOffline", 0)) { + logoff_contacts(); + return 1; + } +*/ + int oldStatus=SkypeStatus; + static BOOL bLaunching = FALSE; + + UNREFERENCED_PARAMETER(newStatus); + + if (bLaunching) return; + bLaunching = TRUE; + LOG (("LaunchSkypeAndSetStatusThread started.")); + InterlockedExchange((long *)&SkypeStatus, (int)ID_STATUS_CONNECTING); + ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, SkypeStatus); + + if (ConnectToSkypeAPI(skype_path, 1)!=-1) { + pthread_create(( pThreadFunc )SkypeSystemInit, NULL); + //InterlockedExchange((long *)&SkypeStatus, (int)newStatus); + //ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, SkypeStatus); + SetUserStatus(); + } + + LOG (("LaunchSkypeAndSetStatusThread terminated gracefully.")); + bLaunching = FALSE; +} + +LONG APIENTRY WndProc(HWND hWndDlg, UINT message, UINT wParam, LONG lParam) +{ + PCOPYDATASTRUCT CopyData; + char *ptr, *szSkypeMsg=NULL, *nick, *buf; + static char *onlinestatus=NULL; + static BOOL RestoreUserStatus=FALSE; + int sstat, oldstatus, flag; + HANDLE hContact; + fetchmsg_arg *args; + static int iReentranceCnt = 0; + + iReentranceCnt++; + switch (message) + { + case WM_COPYDATA: + LOG(("WM_COPYDATA start")); + if(hSkypeWnd==(HWND)wParam) { + char *pData; + CopyData=(PCOPYDATASTRUCT)lParam; + pData = (char*)CopyData->lpData; + while (*pData==' ') pData++; + szSkypeMsg=_strdup((char*)pData); + ReplyMessage(1); + LOG(("< %s", szSkypeMsg)); + + if (!strncmp(szSkypeMsg, "CONNSTATUS", 10)) { + if (!strncmp(szSkypeMsg+11, "LOGGEDOUT", 9)) { + SkypeInitialized=FALSE; + ResetEvent(SkypeReady); + AttachStatus=-1; + sstat=ID_STATUS_OFFLINE; + if (g_hWnd) KillTimer (g_hWnd, 1); + logoff_contacts(TRUE); + } else + sstat=SkypeStatusToMiranda(szSkypeMsg+11); + + if (sstat) { + oldstatus=SkypeStatus; + InterlockedExchange((long*)&SkypeStatus, sstat); + ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldstatus, SkypeStatus); + if (sstat!=ID_STATUS_OFFLINE) { + if (sstat!=ID_STATUS_CONNECTING && (oldstatus==ID_STATUS_OFFLINE || oldstatus==ID_STATUS_CONNECTING)) { + + SkypeInitialized=FALSE; + pthread_create(( pThreadFunc )SkypeSystemInit, NULL); + } + if (DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "KeepState", 0)) RestoreUserStatus=TRUE; + } + +// if (SkypeStatus==ID_STATUS_ONLINE) SkypeSend("SEARCH MISSEDMESSAGES"); + } +// break; + } + if (!strncmp(szSkypeMsg, "USERSTATUS", 10)) { +// if ((sstat=SkypeStatusToMiranda(szSkypeMsg+11)) && SkypeStatus!=ID_STATUS_CONNECTING) { + if ((sstat=SkypeStatusToMiranda(szSkypeMsg+11))) { + if (RestoreUserStatus && RequestedStatus) { + RestoreUserStatus=FALSE; + SkypeSend ("SET USERSTATUS %s", RequestedStatus); + } + oldstatus=SkypeStatus; + InterlockedExchange((long*)&SkypeStatus, sstat); + ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldstatus, sstat); +#ifdef SKYPEBUG_OFFLN + if ((oldstatus==ID_STATUS_OFFLINE || oldstatus==ID_STATUS_CONNECTING) && + SkypeStatus!=ID_STATUS_CONNECTING && SkypeStatus!=ID_STATUS_OFFLINE) + pthread_create(( pThreadFunc )SearchFriendsThread, NULL); +#endif + } +#ifdef SKYPEBUG_OFFLN + SetEvent(GotUserstatus); +#endif + break; + } + if (!strncmp(szSkypeMsg, "APPLICATION libpurple_typing", 28)) { + char *nextoken, *p; + + if (p=strtok_r(szSkypeMsg+29, " ", &nextoken)) + { + if (!strcmp (p, "STREAMS")) { + char *pStr; + + while (p=strtok_r(NULL, " ", &nextoken)) { + if (pStr = strchr(p, ':')) { + *pStr=0; + if (hContact=find_contact(p)) { + *pStr=':'; + DBWriteContactSettingString(hContact, SKYPE_PROTONAME, "Typing_Stream", p); + } + } + } + } + else if (!strcmp (p, "DATAGRAM")) { + if (p=strtok_r(NULL, " ", &nextoken)) { + char *pStr; + + if (pStr = strchr(p, ':')) { + *pStr=0; + if (hContact=find_contact(p)) { + *pStr=':'; + DBWriteContactSettingString(hContact, SKYPE_PROTONAME, "Typing_Stream", p); + + if (p=strtok_r(NULL, " ", &nextoken)) { + LPARAM lTyping = PROTOTYPE_CONTACTTYPING_OFF; + + if (!strcmp(p, "PURPLE_TYPING")) lTyping=PROTOTYPE_CONTACTTYPING_INFINITE; + CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact, lTyping); + break; + } + } + } + } + } + } + } + if (!strncmp(szSkypeMsg, "USER ", 5)) { + char *nextoken; + + buf=_strdup(szSkypeMsg+5); + nick=strtok_r(buf, " ", &nextoken); + ptr=strtok_r(NULL, " ", &nextoken); + + if (strcmp(ptr, "BUDDYSTATUS")) { + if (!strcmp(ptr, "RECEIVEDAUTHREQUEST")) { + pthread_create(( pThreadFunc )SearchUsersWaitingMyAuthorization, NULL); + free (buf); + break; + } + + if (!(hContact=find_contact(nick)) && strcmp(ptr, "FULLNAME")) { + SkypeSend("GET USER %s BUDDYSTATUS", nick); + free (buf); + break; + } + + if (!strcmp(ptr, "ONLINESTATUS")) { + if (SkypeStatus!=ID_STATUS_OFFLINE) + { + DBWriteContactSettingWord(hContact, SKYPE_PROTONAME, "Status", (WORD)SkypeStatusToMiranda(ptr+13)); + if((WORD)SkypeStatusToMiranda(ptr+13) != ID_STATUS_OFFLINE) + { + LOG(("WndProc Status is not offline so get user info")); + pthread_create(GetInfoThread, hContact); + } + } + } + + + /* We handle the following properties right here in the wndProc, in case that + * Skype protocol broadcasts them to us. + * + * However, we still let them be added to the Message queue im memory, as they + * may get consumed by GetInfoThread. + * This is necessary to have a proper error handling in case the property is + * not supported (i.e. imo2sproxy). + * + * If one of the property GETs returns an error, the error-message has to be + * removed from the message queue, as the error is the answer to the query. + * If we don't remove the ERRORs from the list, another consumer may see the ERROR + * as a reply to his query and process it. + * In case the SKYPE Protocol really broadcasts one of these messages without being + * requested by GetInfoThread (i.e. MOOD_TEXT), the garbage collector will take + * care of them and remove them after some time. + * This may not be the most efficient way, but ensures that we finally do proper + * error handling. + */ + if (!strcmp(ptr, "FULLNAME")) { + char *nm; + + if (nm = strtok_r(NULL, " ", &nextoken)) + { + SkypeDBWriteContactSettingUTF8String(hContact, SKYPE_PROTONAME, "FirstName", nm); + if (!(nm=strtok_r(NULL, "", &nextoken))) DBDeleteContactSetting(hContact, SKYPE_PROTONAME, "LastName"); + else + SkypeDBWriteContactSettingUTF8String(hContact, SKYPE_PROTONAME, "LastName", nm); + } + } else + if (!strcmp(ptr, "BIRTHDAY")) { + unsigned int y, m, d; + if (sscanf(ptr+9, "%04d%02d%02d", &y, &m, &d)==3) { + DBWriteContactSettingWord(hContact, SKYPE_PROTONAME, "BirthYear", (WORD)y); + DBWriteContactSettingByte(hContact, SKYPE_PROTONAME, "BirthMonth", (BYTE)m); + DBWriteContactSettingByte(hContact, SKYPE_PROTONAME, "BirthDay", (BYTE)d); + } else { + DBDeleteContactSetting(hContact, SKYPE_PROTONAME, "BirthYear"); + DBDeleteContactSetting(hContact, SKYPE_PROTONAME, "BirthMonth"); + DBDeleteContactSetting(hContact, SKYPE_PROTONAME, "BirthDay"); + } + } else + if (!strcmp(ptr, "COUNTRY")) { + if (ptr[8]) { + struct CountryListEntry *countries; + int countryCount, i; + + CallService(MS_UTILS_GETCOUNTRYLIST, (WPARAM)&countryCount, (LPARAM)&countries); + for (i=0; i= 86400 )?(256-((2*(atoi(ptr+9)-86400))/3600)):((-2*(atoi(ptr+9)-86400))/3600); + if (tms.tm_isdst == 1 && DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UseTimeZonePatch", 0)) + { + LOG(("WndProc: Using the TimeZonePatch")); + DBWriteContactSettingByte(hContact, "UserInfo", "Timezone", (BYTE)(tz+2)); + } + else + { + LOG(("WndProc: Not using the TimeZonePatch")); + DBWriteContactSettingByte(hContact, "UserInfo", "Timezone", (BYTE)(tz+0)); + } + } else { + LOG(("WndProc: Deleting the TimeZone in UserInfo Section")); + DBDeleteContactSetting(hContact, "UserInfo", "Timezone"); + } + } else + if (!strcmp(ptr, "IS_VIDEO_CAPABLE")){ + if (!_stricmp(ptr + 17, "True")) + DBWriteContactSettingString(hContact, SKYPE_PROTONAME, "MirVer", "Skype 2.0"); + else + DBWriteContactSettingString(hContact, SKYPE_PROTONAME, "MirVer", "Skype"); + } else + if (!strcmp(ptr, "RICH_MOOD_TEXT")) { + DBWriteContactSettingString(hContact, SKYPE_PROTONAME, "MirVer", "Skype 3.0"); + } else + if (!strcmp(ptr, "DISPLAYNAME")) { + // Skype Bug? -> If nickname isn't customised in the Skype-App, this won't return anything :-( + if (ptr[12]) + SkypeDBWriteContactSettingUTF8String(hContact, SKYPE_PROTONAME, "Nick", ptr+12); + } else // Other proerties that can be directly assigned to a DB-Value + { + int i; + char *pszProp; + + for (i=0; itEdited = atol(ptr+18); + } + bFetchMsg = TRUE; + } else bFetchMsg = (strncmp(ptr, " STATUS RE", 10) == 0 && !rcvwatchers) || + (strncmp(ptr, " STATUS SENT", 12) == 0 && !sendwatchers); + + if (bFetchMsg) { + // If new message is available, fetch it + ptr[0]=0; + if (!(args=(fetchmsg_arg *)calloc(1, sizeof(*args)))) break; + strncpy (args->msgnum, pMsgNum, sizeof(args->msgnum)); + args->getstatus=FALSE; + //args->bIsRead = strncmp(ptr+8, "READ", 4) == 0; + pthread_create(( pThreadFunc )FetchMessageThreadSync, args); + break; + } + } + } + if (!strncmp(szSkypeMsg, "ERROR 68", 8)) { + LOG(("We got a sync problem :( -> SendMessage() will try to recover...")); + break; + } + if (!strncmp(szSkypeMsg, "PROTOCOL ", 9)) { + if ((protocol=(char)atoi(szSkypeMsg+9))>=3) { + strcpy(cmdMessage, "CHATMESSAGE"); + strcpy(cmdPartner, "FROM"); + } + bProtocolSet = TRUE; + + if (protocol<5 && !hMenuAddSkypeContact && + DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "EnableMenu", 1)) + { + hMenuAddSkypeContact = add_mainmenu(); + } + } + LOG(("SkypeMsgAdd launched")); + SkypeMsgAdd(szSkypeMsg); + ReleaseSemaphore(SkypeMsgReceived, receivers, NULL); + } + break; + + case WM_TIMER: + if (iReentranceCnt>1) break; + if (!bIsImoproxy) SkypeSend("PING"); + SkypeMsgCollectGarbage(MAX_MSG_AGE); + MsgList_CollectGarbage(); + if (receivers>1) + { + LOG(("Watchdog WARNING: there are still %d receivers waiting for MSGs", receivers)); + } + break; + + case WM_CLOSE: + PostQuitMessage (0); + break; + case WM_DESTROY: + KillTimer (hWndDlg, 1); + break; + + default: + if(message==ControlAPIAttach) { + // Skype responds with Attach to the discover-message + if ((HWND)wParam == hForbiddenSkypeWnd) { + ResetEvent(SkypeReady); + break; + } + AttachStatus=lParam; + if (lParam==SKYPECONTROLAPI_ATTACH_SUCCESS) { + LOG (("AttachStatus success, got hWnd %08X", (HWND)wParam)); + + if (hSkypeWnd && (HWND)wParam!=hSkypeWnd && IsWindow(hSkypeWnd)) + hSkypeWndSecondary = (HWND)wParam; + else { + hSkypeWnd=(HWND)wParam; // Skype gave us the communication window handle + hSkypeWndSecondary = NULL; + } + } + if (AttachStatus!=SKYPECONTROLAPI_ATTACH_API_AVAILABLE && + AttachStatus!=SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE) + { + LOG(("Attaching: SkypeReady fired, Attachstatus is %d", AttachStatus)); + SetEvent(SkypeReady); + } + AttachStatus=lParam; + break; + } + --iReentranceCnt; + return (DefWindowProc(hWndDlg, message, wParam, lParam)); + } + LOG(("WM_COPYDATA exit (%08X)", message)); + if (szSkypeMsg) free(szSkypeMsg); + --iReentranceCnt; + return 1; +} + +void TellError(DWORD err) { + LPVOID lpMsgBuf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); + MessageBox( NULL, (TCHAR*)lpMsgBuf, _T("GetLastError"), MB_OK|MB_ICONINFORMATION ); + LocalFree( lpMsgBuf ); + return; +} + + +// SERVICES // +INT_PTR SkypeSetStatus(WPARAM wParam, LPARAM lParam) +{ + int oldStatus, iRet; + BOOL UseCustomCommand, UnloadOnOffline; + + UNREFERENCED_PARAMETER(lParam); + + if (MirandaShuttingDown) return 0; + LOG (("SkypeSetStatus enter")); + UseCustomCommand = DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UseCustomCommand", 0); + UnloadOnOffline = DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UnloadOnOffline", 0); + + //if (!SkypeInitialized && !DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UnloadOnOffline", 0)) return 0; + + // Workaround for Skype status-bug + if ((int)wParam==ID_STATUS_OFFLINE) logoff_contacts(TRUE); + if (SkypeStatus==(int)wParam) return 0; + oldStatus = SkypeStatus; + + if ((int)wParam==ID_STATUS_CONNECTING) return 0; +#ifdef MAPDND + if ((int)wParam==ID_STATUS_OCCUPIED || (int)wParam==ID_STATUS_ONTHEPHONE) wParam=ID_STATUS_DND; + if ((int)wParam==ID_STATUS_OUTTOLUNCH) wParam=ID_STATUS_NA; +#endif +#ifdef MAPNA + if ((int)wParam==ID_STATUS_NA) wParam = ID_STATUS_AWAY; +#endif + + RequestedStatus=MirandaStatusToSkype((int)wParam); + + /* + if (SkypeStatus != ID_STATUS_OFFLINE) + { + InterlockedExchange((long*)&SkypeStatus, (int)wParam); + ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, SkypeStatus); + } + */ + + if ((int)wParam==ID_STATUS_OFFLINE && UnloadOnOffline) + { + if(UseCustomCommand) + { + DBVARIANT dbv; + if(!DBGetContactSettingString(NULL,SKYPE_PROTONAME,"CommandLine",&dbv)) + { + CloseSkypeAPI(dbv.pszVal); + DBFreeVariant(&dbv); + } + } + else + { + CloseSkypeAPI(skype_path); + } + + } else if (AttachStatus==-1) + { + pthread_create(LaunchSkypeAndSetStatusThread, (void *)wParam); + return 0; + } + + iRet = SetUserStatus(); + LOG (("SkypeSetStatus exit")); + return iRet; +} + +int __stdcall SendBroadcast( HANDLE hContact, int type, int result, HANDLE hProcess, LPARAM lParam ) +{ + ACKDATA ack = {0}; + ack.cbSize = sizeof( ACKDATA ); + ack.szModule = SKYPE_PROTONAME; + ack.hContact = hContact; + ack.type = type; + ack.result = result; + ack.hProcess = hProcess; + ack.lParam = lParam; + return CallService( MS_PROTO_BROADCASTACK, 0, ( LPARAM )&ack ); +} + +static void __cdecl SkypeGetAwayMessageThread( HANDLE hContact ) +{ + DBVARIANT dbv; + if ( !DBGetContactSettingString( hContact, "CList", "StatusMsg", &dbv )) { + SendBroadcast( hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, ( HANDLE )1, ( LPARAM )dbv.pszVal ); + DBFreeVariant( &dbv ); + } + else SendBroadcast( hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, ( HANDLE )1, ( LPARAM )0 ); +} + +INT_PTR SkypeGetAwayMessage(WPARAM wParam,LPARAM lParam) +{ + CCSDATA* ccs = ( CCSDATA* )lParam; + + UNREFERENCED_PARAMETER(wParam); + + pthread_create( SkypeGetAwayMessageThread, ccs->hContact ); + return 1; +} + +#define POLYNOMIAL (0x488781ED) /* This is the CRC Poly */ +#define TOPBIT (1 << (WIDTH - 1)) /* MSB */ +#define WIDTH 32 + +static int GetFileHash(char* filename) +{ + HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + int remainder = 0, byte, bit; + char data[1024]; + DWORD dwRead; + + if(hFile == INVALID_HANDLE_VALUE) return 0; + + do + { + // Read file chunk + dwRead = 0; + ReadFile(hFile, data, 1024, &dwRead, NULL); + + /* loop through each byte of data */ + for (byte = 0; byte < (int) dwRead; ++byte) { + /* store the next byte into the remainder */ + remainder ^= (data[byte] << (WIDTH - 8)); + /* calculate for all 8 bits in the byte */ + for ( bit = 8; bit > 0; --bit) { + /* check if MSB of remainder is a one */ + if (remainder & TOPBIT) + remainder = (remainder << 1) ^ POLYNOMIAL; + else + remainder = (remainder << 1); + } + } + } while(dwRead == 1024); + + CloseHandle(hFile); + + return remainder; +} + +static int _GetFileSize(char* filename) +{ + HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + int size; + + if(hFile == INVALID_HANDLE_VALUE) + return 0; + size = GetFileSize(hFile, NULL); + CloseHandle(hFile); + return size; +} + +/* RetrieveUserAvatar + * + * Purpose: Get a user avatar from skype itself + * Params : param=(void *)(HANDLE)hContact + */ +void RetrieveUserAvatar(void *param) +{ + HANDLE hContact = (HANDLE) param, file; + PROTO_AVATAR_INFORMATION AI={0}; + ACKDATA ack = {0}; + DBVARIANT dbv; + char AvatarFile[MAX_PATH+1], AvatarTmpFile[MAX_PATH+10], *ptr, *pszTempFile; + + if (hContact == NULL) + return; + + // Mount default ack + ack.cbSize = sizeof( ACKDATA ); + ack.szModule = SKYPE_PROTONAME; + ack.hContact = hContact; + ack.type = ACKTYPE_AVATAR; + ack.result = ACKRESULT_FAILED; + + AI.cbSize = sizeof( AI ); + AI.hContact = hContact; + + // Get skype name + if (DBGetContactSettingString(hContact, SKYPE_PROTONAME, SKYPE_NAME, &dbv) == 0) + { + if (dbv.pszVal) + { + // Get filename + FoldersGetCustomPath(hProtocolAvatarsFolder, AvatarFile, sizeof(AvatarFile), DefaultAvatarsFolder); + mir_snprintf(AvatarTmpFile, sizeof(AvatarTmpFile), "AVATAR 1 %s\\%s_tmp.jpg", AvatarFile, dbv.pszVal); + pszTempFile = AvatarTmpFile+9; + mir_snprintf(AvatarFile, sizeof(AvatarFile), "%s\\%s.jpg", AvatarFile, dbv.pszVal); + + // Just to be sure + DeleteFileA(pszTempFile); + file = CreateFileA(pszTempFile, 0, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file != INVALID_HANDLE_VALUE) + { + CloseHandle(file); + if (ptr=SkypeGet ("USER", dbv.pszVal, AvatarTmpFile)) + { + if (strncmp(ptr, "ERROR", 5) && + GetFileAttributesA(pszTempFile) != INVALID_FILE_ATTRIBUTES) + { + ack.result = ACKRESULT_SUCCESS; + + // Is no avatar image? + if (!DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "ShowDefaultSkypeAvatar", 0) + && GetFileHash(pszTempFile) == 0x8d34e05d && _GetFileSize(pszTempFile) == 3751) + { + // Has no avatar + AI.format = PA_FORMAT_UNKNOWN; + ack.hProcess = (HANDLE)&AI; + DeleteFileA(AvatarFile); + } + else + { + // Got it + MoveFileExA(pszTempFile, AvatarFile, MOVEFILE_REPLACE_EXISTING); + AI.format = PA_FORMAT_JPEG; + strcpy(AI.filename, AvatarFile); + ack.hProcess = (HANDLE)&AI; + } + + } + free (ptr); + } + DeleteFileA(pszTempFile); + } + + } + DBFreeVariant(&dbv); + } + CallService( MS_PROTO_BROADCASTACK, 0, ( LPARAM )&ack ); +} + + +/* SkypeGetAvatarInfo + * + * Purpose: Set user avatar in profile + * Params : wParam=0 + * lParam=(LPARAM)(const char*)filename + * Returns: 0 - Success + * -1 - Failure + */ +INT_PTR SkypeGetAvatarInfo(WPARAM wParam,LPARAM lParam) +{ + + DBVARIANT dbv; + PROTO_AVATAR_INFORMATION* AI = ( PROTO_AVATAR_INFORMATION* )lParam; + if (AI->hContact == NULL) // User + { + if (!DBGetContactSettingString(NULL,SKYPE_PROTONAME, "AvatarFile", &dbv)) + { + lstrcpynA(AI->filename, dbv.pszVal, sizeof(AI->filename)); + DBFreeVariant(&dbv); + return GAIR_SUCCESS; + } + else + return GAIR_NOAVATAR; + } + else // Contact + { + DBVARIANT dbv; + char AvatarFile[MAX_PATH+1]; + + if (protocol < 7 && !bIsImoproxy) + return GAIR_NOAVATAR; + + if (wParam & GAIF_FORCE) + { + // Request anyway + pthread_create(RetrieveUserAvatar, (void *) AI->hContact); + return GAIR_WAITFOR; + } + + if (DBGetContactSettingString(AI->hContact, SKYPE_PROTONAME, SKYPE_NAME, &dbv)) + // No skype name ?? + return GAIR_NOAVATAR; + + if (dbv.pszVal == NULL) + { + // No skype name ?? + DBFreeVariant(&dbv); + return GAIR_NOAVATAR; + } + + // Get filename + FoldersGetCustomPath(hProtocolAvatarsFolder, AvatarFile, sizeof(AvatarFile), DefaultAvatarsFolder); + mir_snprintf(AvatarFile, sizeof(AvatarFile), "%s\\%s.jpg", AvatarFile, dbv.pszVal); + DBFreeVariant(&dbv); + + // Check if the file exists + if (GetFileAttributesA(AvatarFile) == INVALID_FILE_ATTRIBUTES) + return GAIR_NOAVATAR; + + // Return the avatar + AI->format = PA_FORMAT_JPEG; + strcpy(AI->filename, AvatarFile); + return GAIR_SUCCESS; + } +} + + +/* SkypeGetAvatarCaps + * + * Purpose: Query avatar caps for a protocol + * Params : wParam=One of AF_* + * lParam=Depends on wParam + * Returns: Depends on wParam + */ +INT_PTR SkypeGetAvatarCaps(WPARAM wParam, LPARAM lParam) +{ + switch(wParam) + { + case AF_MAXSIZE: + { + POINT *p = (POINT *) lParam; + if (p == NULL) + return -1; + + p->x = 96; + p->y = 96; + return 0; + } + case AF_PROPORTION: + { + return PIP_NONE; + } + case AF_FORMATSUPPORTED: + { + if (lParam == PA_FORMAT_PNG || lParam == PA_FORMAT_JPEG) + return TRUE; + else + return FALSE; + } + case AF_ENABLED: + { + return TRUE; + } + case AF_DONTNEEDDELAYS: + { + return FALSE; + } + } + return -1; +} + + +INT_PTR SkypeGetStatus(WPARAM wParam, LPARAM lParam) { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + return SkypeStatus; +} + +INT_PTR SkypeGetInfo(WPARAM wParam,LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + + UNREFERENCED_PARAMETER(wParam); + + pthread_create(GetInfoThread, ccs->hContact); + return 0; +} + +INT_PTR SkypeAddToList(WPARAM wParam, LPARAM lParam) { + PROTOSEARCHRESULT *psr=(PROTOSEARCHRESULT*)lParam; + + LOG(("SkypeAddToList Adding API function called")); + if (psr->cbSize!=sizeof(PROTOSEARCHRESULT) || !psr->nick) return 0; + LOG(("SkypeAddToList OK")); + return (INT_PTR)add_contact(psr->nick, wParam); +} + +INT_PTR SkypeBasicSearch(WPARAM wParam, LPARAM lParam) { + UNREFERENCED_PARAMETER(wParam); + + LOG(("SkypeBasicSearch %s", (char *)lParam)); + if (!SkypeInitialized) return 0; + return (hSearchThread=pthread_create(( pThreadFunc )BasicSearchThread, _strdup((char *)lParam))); +} + +void MessageSendWatchThread(msgsendwt_arg *arg) { + char *str, *err; + + // sendwatchers need to be incremented before starting this thread + LOG(("MessageSendWatchThread started.")); + str=SkypeRcvMsg(arg->szId, SkypeTime(NULL)-1, arg->hContact, DBGetContactSettingDword(NULL,"SRMsg","MessageTimeout",TIMEOUT_MSGSEND)+1000); + InterlockedDecrement (&sendwatchers); + if (str) + { + if (!DBGetContactSettingByte(arg->hContact, SKYPE_PROTONAME, "ChatRoom", 0)) { + if (err=GetSkypeErrorMsg(str)) { + ProtoBroadcastAck(SKYPE_PROTONAME, arg->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE) 1, (LPARAM)Translate(err)); + free(err); + free(str); + free(arg); + LOG(("MessageSendWatchThread terminated.")); + return; + } + ProtoBroadcastAck(SKYPE_PROTONAME, arg->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE) 1, 0); + } + free(str); + LOG(("MessageSendWatchThread terminated gracefully.")); + } + free (arg); +} + +INT_PTR SkypeSendMessage(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + DBVARIANT dbv; + BOOL sendok=TRUE; + char *msg = (char *) ccs->lParam, *utfmsg=NULL, *mymsgcmd=cmdMessage, szId[16]={0}; + static DWORD dwMsgNum = 0; + BYTE bIsChatroom = 0 != DBGetContactSettingByte(ccs->hContact, SKYPE_PROTONAME, "ChatRoom", 0); + + UNREFERENCED_PARAMETER(wParam); + + if (bIsChatroom) + { + if (DBGetContactSettingString(ccs->hContact, SKYPE_PROTONAME, "ChatRoomID", &dbv)) + return 0; + mymsgcmd="CHATMESSAGE"; + } + else + { + if (DBGetContactSettingString(ccs->hContact, SKYPE_PROTONAME, SKYPE_NAME, &dbv)) + return 0; + mymsgcmd="MESSAGE"; + } + + if (ccs->wParam & PREF_UTF) { + utfmsg = msg; + } else if (ccs->wParam & PREF_UNICODE) { + utfmsg = (char*)make_utf8_string((WCHAR*)(msg+strlen(msg)+1)); + } else { + if (utf8_encode(msg, &utfmsg)==-1) utfmsg=NULL; + } + if (protocol>=4) { + InterlockedIncrement ((LONG*)&dwMsgNum); + sprintf (szId, "#M%d ", dwMsgNum++); + } + InterlockedIncrement (&sendwatchers); + if (!utfmsg || SkypeSend("%s%s %s %s", szId, mymsgcmd, dbv.pszVal, utfmsg)) sendok=FALSE; + if (utfmsg && utfmsg!=msg) free(utfmsg); + DBFreeVariant(&dbv); + + if (sendok) { + msgsendwt_arg *psendarg = calloc(1, sizeof(msgsendwt_arg)); + + if (psendarg) { + psendarg->hContact = ccs->hContact; + strcpy (psendarg->szId, szId); + pthread_create(MessageSendWatchThread, psendarg); + } else InterlockedDecrement (&sendwatchers); + return 1; + } else InterlockedDecrement (&sendwatchers); + if (!bIsChatroom) + ProtoBroadcastAck(SKYPE_PROTONAME, ccs->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE) 1, (LPARAM)Translate("Connection to Skype lost")); + return 0; +} + +INT_PTR SkypeRecvMessage(WPARAM wParam, LPARAM lParam) +{ + DBEVENTINFO dbei={0}; + CCSDATA *ccs = (CCSDATA *) lParam; + PROTORECVEVENT *pre = (PROTORECVEVENT *) ccs->lParam; + + UNREFERENCED_PARAMETER(wParam); + + DBDeleteContactSetting(ccs->hContact, "CList", "Hidden"); + dbei.cbSize = sizeof(dbei); + dbei.szModule = SKYPE_PROTONAME; + dbei.timestamp = pre->timestamp; + if (pre->flags & PREF_CREATEREAD) dbei.flags|=DBEF_READ; + if (pre->flags & PREF_UTF) dbei.flags|=DBEF_UTF; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = strlen(pre->szMessage) + 1; + if (pre->flags & PREF_UNICODE) + dbei.cbBlob += sizeof( wchar_t )*( (DWORD)wcslen(( wchar_t* )&pre->szMessage[dbei.cbBlob] )+1 ); + dbei.pBlob = (PBYTE) pre->szMessage; + MsgList_Add (pre->lParam, (HANDLE)CallService(MS_DB_EVENT_ADD, (WPARAM)ccs->hContact, (LPARAM)&dbei)); + return 0; +} + +INT_PTR SkypeUserIsTyping(WPARAM wParam, LPARAM lParam) { + DBVARIANT dbv={0}; + HANDLE hContact = (HANDLE)wParam; + + if (protocol<5 && !bIsImoproxy) return 0; + if (DBGetContactSettingString(hContact, SKYPE_PROTONAME, "Typing_Stream", &dbv)) { + if (DBGetContactSettingString(hContact, SKYPE_PROTONAME, SKYPE_NAME, &dbv) == 0) { + char szCmd[256]; + _snprintf (szCmd, sizeof(szCmd), + "ALTER APPLICATION libpurple_typing CONNECT %s", dbv.pszVal); + SkypeSend (szCmd); + DBFreeVariant (&dbv); + testfor (szCmd, 2000); + // TODO: We should somehow cache the typing notify result and send it + // after we got a connection, but in the meantime this notification won't + // get sent on first run + } + return 0; + } + + SkypeSend ("ALTER APPLICATION libpurple_typing DATAGRAM %s %s", dbv.pszVal, + (lParam==PROTOTYPE_SELFTYPING_ON?"PURPLE_TYPING":"PURPLE_NOT_TYPING")); + DBFreeVariant(&dbv); + return 0; +} + + +INT_PTR SkypeSendAuthRequest(WPARAM wParam, LPARAM lParam) { + CCSDATA* ccs = (CCSDATA*)lParam; + DBVARIANT dbv; + int retval; + + UNREFERENCED_PARAMETER(wParam); + + if (!ccs->lParam || DBGetContactSettingString(ccs->hContact, SKYPE_PROTONAME, SKYPE_NAME, &dbv)) + return 1; + retval = SkypeSend("SET USER %s BUDDYSTATUS 2 %s", dbv.pszVal, (char *)ccs->lParam); + DBFreeVariant(&dbv); + if (retval) return 1; else return 0; +} + +INT_PTR SkypeRecvAuth(WPARAM wParam, LPARAM lParam) { + DBEVENTINFO dbei = {0}; + CCSDATA* ccs = (CCSDATA*)lParam; + PROTORECVEVENT* pre = (PROTORECVEVENT*)ccs->lParam; + + UNREFERENCED_PARAMETER(wParam); + + DBDeleteContactSetting(ccs->hContact, "CList", "Hidden"); + + dbei.cbSize = sizeof(dbei); + dbei.szModule = SKYPE_PROTONAME; + dbei.timestamp = pre->timestamp; + dbei.flags = ((pre->flags & PREF_CREATEREAD)?DBEF_READ:0); + dbei.eventType = EVENTTYPE_AUTHREQUEST; + dbei.cbBlob = pre->lParam; + dbei.pBlob = (PBYTE)pre->szMessage; + + CallService(MS_DB_EVENT_ADD, (WPARAM)NULL, (LPARAM)&dbei); + return 0; +} + +char *__skypeauth(WPARAM wParam) { + DBEVENTINFO dbei={0}; + + if (!SkypeInitialized) return NULL; + + dbei.cbSize = sizeof(dbei); + if ((dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, wParam, 0))==-1 || + !(dbei.pBlob = (unsigned char*)malloc(dbei.cbBlob))) + { return NULL; } + + if (CallService(MS_DB_EVENT_GET, wParam, (LPARAM)&dbei) || + dbei.eventType != EVENTTYPE_AUTHREQUEST || + strcmp(dbei.szModule, SKYPE_PROTONAME)) + { + free(dbei.pBlob); + return NULL; + } + return (char *)dbei.pBlob; +} + +INT_PTR SkypeAuthAllow(WPARAM wParam, LPARAM lParam) { + char *pBlob; + + UNREFERENCED_PARAMETER(lParam); + + if (pBlob=__skypeauth(wParam)) + { + int retval=SkypeSend("SET USER %s ISAUTHORIZED TRUE", pBlob+sizeof(DWORD)+sizeof(HANDLE)); + free(pBlob); + if (!retval) return 0; + } + return 1; +} + +INT_PTR SkypeAuthDeny(WPARAM wParam, LPARAM lParam) { + char *pBlob; + + UNREFERENCED_PARAMETER(lParam); + + if (pBlob=__skypeauth(wParam)) + { + int retval=SkypeSend("SET USER %s ISAUTHORIZED FALSE", pBlob+sizeof(DWORD)+sizeof(HANDLE)); + free(pBlob); + if (!retval) return 0; + } + return 1; +} + + +INT_PTR SkypeAddToListByEvent(WPARAM wParam, LPARAM lParam) { + char *pBlob; + + UNREFERENCED_PARAMETER(lParam); + + if (pBlob=__skypeauth(wParam)) + { + HANDLE hContact=add_contact(pBlob+sizeof(DWORD)+sizeof(HANDLE), LOWORD(wParam)); + free(pBlob); + if (hContact) return (int)hContact; + } + return 0; +} + +INT_PTR SkypeRegisterProxy(WPARAM wParam, LPARAM lParam) { + UNREFERENCED_PARAMETER(wParam); + + if (!lParam) { + free (pszProxyCallout); + pszProxyCallout = NULL; + } + pszProxyCallout = _strdup((char*)lParam); + bIsImoproxy = TRUE; + return 0; +} + + +void CleanupNicknames(char *dummy) { + HANDLE hContact; + char *szProto; + DBVARIANT dbv, dbv2; + + UNREFERENCED_PARAMETER(dummy); + + LOG(("CleanupNicknames Cleaning up...")); + for (hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);hContact != NULL;hContact=(HANDLE)CallService( MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0)) { + szProto = (char*)CallService( MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0 ); + if (szProto!=NULL && !strcmp(szProto, SKYPE_PROTONAME) && + DBGetContactSettingByte(hContact, SKYPE_PROTONAME, "ChatRoom", 0)==0) + { + if (DBGetContactSettingString(hContact, SKYPE_PROTONAME, SKYPE_NAME, &dbv)) continue; + if (DBGetContactSettingString(hContact, SKYPE_PROTONAME, "Nick", &dbv2)) { + DBFreeVariant(&dbv); + continue; + } + DBDeleteContactSetting(hContact, SKYPE_PROTONAME, "Nick"); + GetInfoThread(hContact); + DBFreeVariant(&dbv); + DBFreeVariant(&dbv2); + } + } + OUTPUT(_T("Cleanup finished.")); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// EnterBitmapFileName - enters a bitmap filename + +int __stdcall EnterBitmapFileName( char* szDest ) +{ + char szFilter[ 512 ]; + OPENFILENAMEA ofn = {0}; + *szDest = 0; + + CallService( MS_UTILS_GETBITMAPFILTERSTRINGS, sizeof szFilter, ( LPARAM )szFilter ); + ofn.lStructSize = sizeof( OPENFILENAME ); + ofn.lpstrFilter = szFilter; + ofn.lpstrFile = szDest; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.nMaxFile = MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + ofn.lpstrDefExt = "bmp"; + if ( !GetOpenFileNameA( &ofn )) + return 1; + + return ERROR_SUCCESS; +} + +int MirandaExit(WPARAM wParam, LPARAM lParam) { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + MirandaShuttingDown=TRUE; + return 0; +} + +int OkToExit(WPARAM wParam, LPARAM lParam) { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + +// logoff_contacts(); + MirandaShuttingDown=TRUE; + + // Trigger all semaphores and events just to be sure that there is no deadlock + ReleaseSemaphore(SkypeMsgReceived, receivers, NULL); + SetEvent (SkypeReady); + SetEvent (MessagePumpReady); +#ifdef SKYPEBUG_OFFLN + SetEvent(GotUserstatus); +#endif + SetEvent (hBuddyAdded); + + SkypeFlush (); + PostMessage (g_hWnd, WM_CLOSE, 0, 0); + return 0; +} + + +struct PLUGINDI { + char **szSettings; + int dwCount; +}; + +// Taken from pluginopts.c and modified +int EnumOldPluginName(const char *szSetting,LPARAM lParam) +{ + struct PLUGINDI *pdi=(struct PLUGINDI*)lParam; + if (pdi && lParam) { + pdi->szSettings=(char**)realloc(pdi->szSettings,(pdi->dwCount+1)*sizeof(char*)); + pdi->szSettings[pdi->dwCount++]=_strdup(szSetting); + } + return 0; +} + +// Are there any Skype users on list? +// 1 --> Yes +// 0 --> No +int AnySkypeusers(void) +{ + HANDLE hContact; + DBVARIANT dbv; + int tCompareResult; + + // already on list? + for (hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + hContact != NULL; + hContact=(HANDLE)CallService( MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0)) + { + // GETCONTACTBASEPROTO doesn't work on not loaded protocol, therefore get + // protocol from DB + if (DBGetContactSettingString(hContact, "Protocol", "p", &dbv)) continue; + tCompareResult = !strcmp(dbv.pszVal, SKYPE_PROTONAME); + DBFreeVariant(&dbv); + if (tCompareResult) return 1; + } + return 0; +} + + +void UpgradeName(char *OldName) +{ + DBCONTACTENUMSETTINGS cns; + DBCONTACTWRITESETTING cws; + DBVARIANT dbv; + HANDLE hContact=NULL; + struct PLUGINDI pdi; + + LOG(("Updating old database settings if there are any...")); + cns.pfnEnumProc=EnumOldPluginName; + cns.lParam=(LPARAM)&pdi; + cns.szModule=OldName; + cns.ofsSettings=0; + + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + + for ( ;; ) { + memset(&pdi,0,sizeof(pdi)); + CallService(MS_DB_CONTACT_ENUMSETTINGS,(WPARAM)hContact,(LPARAM)&cns); + // Upgrade Protocol settings to new string + if (pdi.szSettings) { + int i; + + LOG(("We're currently upgrading...")); + for (i=0;i 0 && !Miranda_Terminated()) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + UnregisterClass (WndClass.lpszClassName, hInst); + LOG (("Messagepump stopped.")); +} + +// DLL Stuff // + +__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirVersion) +{ + mirandaVersion = mirVersion; + + pluginInfo.cbSize = sizeof(PLUGININFO); + return (PLUGININFO*) &pluginInfo; +} + +__declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirVersion) +{ + mirandaVersion = mirVersion; + + return &pluginInfo; +} + +static const MUUID interfaces[] = {MUUID_SKYPE_CALL, MIID_LAST}; +__declspec(dllexport) const MUUID * MirandaPluginInterfaces(void) +{ + return interfaces; +} + + +BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) +{ + UNREFERENCED_PARAMETER(fdwReason); + UNREFERENCED_PARAMETER(lpvReserved); + + hInst = hinstDLL; + return TRUE; +} + + +int PreShutdown(WPARAM wParam, LPARAM lParam) { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + PostThreadMessage(msgPumpThreadId, WM_QUIT, 0, 0); + return 0; +} + +int __declspec(dllexport) Load(PLUGINLINK *link) +{ + PROTOCOLDESCRIPTOR pd; + DWORD Buffsize; + HKEY MyKey; + BOOL SkypeInstalled; + BOOL UseCustomCommand; + WSADATA wsaData; + char path[MAX_PATH]; + + pluginLink = link; + mir_getMMI( &mmi ); + //mir_getLP(&pluginInfo); + + GetModuleFileNameA( hInst, path, sizeof( path )); + _splitpath (path, NULL, NULL, SKYPE_PROTONAME, NULL); + CharUpperA( SKYPE_PROTONAME ); + + InitializeCriticalSection(&RingAndEndcallMutex); + InitializeCriticalSection(&QueryThreadMutex); + InitializeCriticalSection(&TimeMutex); + + +#ifdef _DEBUG + init_debug(); +#endif + + LOG(("Load: Skype Plugin loading...")); + + // We need to upgrade SKYPE_PROTOCOL internal name to Skype if not already done + if (!DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UpgradeDone", 0)) + UpgradeName("SKYPE_PROTOCOL"); + + // Initialisation of Skype MsgQueue must be done because of Cleanup in end and + // Mutex is also initialized here. + LOG(("SkypeMsgInit initializing Skype MSG-queue")); + if (SkypeMsgInit()==-1) { + OUTPUT(_T("Memory allocation error on startup.")); + return 0; + } + + // On first run on new profile, ask user, if he wants to enable the plugin in + // this profile + // --> Fixing Issue #0000006 from bugtracker. + if (!DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "FirstRun", 0)) { + DBWriteContactSettingByte(NULL, SKYPE_PROTONAME, "FirstRun", 1); + if (AnySkypeusers()==0) // First run, it seems :) + if (MessageBox(NULL, TranslateT("This seems to be the first time that you're running the Skype protocol plugin. Do you want to enable the protocol for this Miranda-Profile? (If you chose NO, you can always enable it in the plugin options later."), _T("Welcome!"), MB_ICONQUESTION|MB_YESNO)==IDNO) { + char path[MAX_PATH], *filename; + GetModuleFileNameA(hInst, path, sizeof(path)); + if (filename = strrchr(path,'\\')+1) + DBWriteContactSettingByte(NULL,"PluginDisable",filename,1); + return 0; + } + } + + + // Check if Skype is installed + SkypeInstalled=TRUE; + UseCustomCommand = (BYTE)DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UseCustomCommand", 0); + UseSockets = (BOOL)DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UseSkype2Socket", 0); + + if (!UseSockets && !UseCustomCommand) + { + if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Skype\\Phone"), 0, KEY_READ, &MyKey)!=ERROR_SUCCESS) + { + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Skype\\Phone"), 0, KEY_READ, &MyKey)!=ERROR_SUCCESS) + { + SkypeInstalled=FALSE; + } + } + + Buffsize=sizeof(skype_path); + + if (SkypeInstalled==FALSE || RegQueryValueExA(MyKey, "SkypePath", NULL, NULL, (unsigned char *)skype_path, &Buffsize)!=ERROR_SUCCESS) + { + //OUTPUT("Skype was not found installed :( \nMaybe you are using portable skype."); + RegCloseKey(MyKey); + skype_path[0]=0; + //return 0; + } + RegCloseKey(MyKey); + } + WSAStartup(MAKEWORD(2,2), &wsaData); + + // Start Skype connection + if (!(ControlAPIAttach=RegisterWindowMessage(_T("SkypeControlAPIAttach"))) || !(ControlAPIDiscover=RegisterWindowMessage(_T("SkypeControlAPIDiscover")))) + { + OUTPUT(_T("Cannot register Window message.")); + return 0; + } + + SkypeMsgReceived=CreateSemaphore(NULL, 0, MAX_MSGS, NULL); + if (!(SkypeReady=CreateEvent(NULL, TRUE, FALSE, NULL)) || + !(MessagePumpReady=CreateEvent(NULL, FALSE, FALSE, NULL)) || +#ifdef SKYPEBUG_OFFLN + !(GotUserstatus=CreateEvent(NULL, TRUE, FALSE, NULL)) || +#endif + !(hBuddyAdded=CreateEvent(NULL, FALSE, FALSE, NULL)) || + !(FetchMessageEvent=CreateEvent(NULL, FALSE, TRUE, NULL))) { + OUTPUT(_T("Unable to create Mutex!")); + return 0; + } + + /* Register the module */ + ZeroMemory(&pd, sizeof(pd)); + pd.cbSize = sizeof(pd); + pd.szName = SKYPE_PROTONAME; + pd.type = PROTOTYPE_PROTOCOL; + CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM)&pd); + + VoiceServiceInit(); + + CreateServices(); + HookEvents(); + InitVSApi(); + MsgList_Init(); + + HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown); + + // Startup Message-pump + pthread_create (( pThreadFunc )MsgPump, NULL); + WaitForSingleObject(MessagePumpReady, INFINITE); + return 0; +} + + + +int __declspec( dllexport ) Unload(void) +{ + BOOL UseCustomCommand = DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UseCustomCommand", 0); + BOOL Shutdown = DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "Shutdown", 0); + + LOG (("Unload started")); + + if ( Shutdown && ((skype_path && skype_path[0]) ||UseCustomCommand) ) { + + if(UseCustomCommand) + { + DBVARIANT dbv; + if(!DBGetContactSettingString(NULL,SKYPE_PROTONAME,"CommandLine",&dbv)) + { + char szAbsolutePath[MAX_PATH]; + + TranslateMirandaRelativePathToAbsolute(dbv.pszVal, szAbsolutePath, FALSE); + _spawnl(_P_NOWAIT, szAbsolutePath, szAbsolutePath, "/SHUTDOWN", NULL); + LOG (("Unload Sent /shutdown to %s", szAbsolutePath)); + DBFreeVariant(&dbv); + } + } + else + { + _spawnl(_P_NOWAIT, skype_path, skype_path, "/SHUTDOWN", NULL); + LOG (("Unload Sent /shutdown to %s", skype_path)); + } + + } + SkypeMsgCleanup(); + WSACleanup(); + FreeVSApi(); + UnhookEvents(); + UnhookEvent(hChatEvent); + UnhookEvent (hChatMenu); + UnhookEvent (hEvInitChat); + DestroyHookableEvent(hInitChat); + VoiceServiceExit(); + GCExit(); + MsgList_Exit(); + + CloseHandle(SkypeReady); + CloseHandle(SkypeMsgReceived); +#ifdef SKYPEBUG_OFFLN + CloseHandle(GotUserstatus); +#endif + CloseHandle(MessagePumpReady); + CloseHandle(hBuddyAdded); + CloseHandle(FetchMessageEvent); + + DeleteCriticalSection(&RingAndEndcallMutex); + DeleteCriticalSection(&QueryThreadMutex); + + SkypeRegisterProxy (0, 0); + LOG (("Unload: Shutdown complete")); +#ifdef _DEBUG + end_debug(); +#endif + DeleteCriticalSection(&TimeMutex); + return 0; +} + -- cgit v1.2.3