From c80bc292b555c6666930790c399f6fac6226c468 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Sat, 21 Jul 2012 10:11:53 +0000 Subject: Skype and IMO2sProxy added, not adapted yet git-svn-id: http://svn.miranda-ng.org/main/trunk@1091 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/IMO2sProxy/src/imo2skype/imo2skypeapi.c | 1959 +++++++++++++++++++++++ 1 file changed, 1959 insertions(+) create mode 100644 plugins/IMO2sProxy/src/imo2skype/imo2skypeapi.c (limited to 'plugins/IMO2sProxy/src/imo2skype/imo2skypeapi.c') diff --git a/plugins/IMO2sProxy/src/imo2skype/imo2skypeapi.c b/plugins/IMO2sProxy/src/imo2skype/imo2skypeapi.c new file mode 100644 index 0000000000..de0a9b4028 --- /dev/null +++ b/plugins/IMO2sProxy/src/imo2skype/imo2skypeapi.c @@ -0,0 +1,1959 @@ +/* Module: imo2skypeapi.c + Purpose: Simple wrapper for imo.im Webservice to SKYPE API to maintain compatibility with Skype-Plugins + Author: leecher + Date: 30.08.2009 +*/ + +#define VOICECALL_VERSION 1221873445 +#define IVC_VERSION "98a29c15e305a7af04634b03d5e1425d6c67806e" + +#include +#include +#include +#include +#include +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include "w32browser.h" +#define strcasecmp stricmp +#define strncasecmp strnicmp +#define thread_t HANDLE +#define vsnprintf _vsnprintf +#define GWL_PINST 0 +#define GWL_ORIGWPRC 1*sizeof(PVOID) +#else +#define thread_t pthread_t +#include +#include +#endif +#include "imo_skype.h" +#include "imo_request.h" +#include "fifo.h" +#include "memlist.h" +#include "buddylist.h" +#include "avatarlist.h" +#include "msgqueue.h" +#include "callqueue.h" +#include "imo2skypeapi.h" + +typedef struct +{ + char *pszImoStat; + char *pszSkypeStat; +} STATMAP; + +struct _tagIMOSAPI +{ + IMOSKYPE *hInst; + NICKENTRY myUser; + char *pszPass; + char *pszLogBuf; + char *pszClientName; + int cbBuf; + TYP_LIST *hBuddyList; + TYP_LIST *hAvatarList; + TYP_LIST *hMsgQueue; + TYP_LIST *hCallQueue; + int iProtocol; + int iLoginStat; + IMO2SCB Callback; + void *pUser; + FILE *fpLog; + thread_t hThread; + int bFriendsPending; + int iFlags; + int iShuttingDown; + volatile time_t tSetMoodText; + char *pszCmdID; + char *pszBuddiesToAdd; +}; + +static STATMAP m_stMap[] = +{ + {"available", "ONLINE"}, + {"available", "SKYPEME"}, + {"offline", "OFFLINE"}, + {"away", "AWAY"}, + {"busy", "DND"}, + {"busy", "NA"}, + {"invisible", "INVISIBLE"} +}; + +static int StartCallSWF (IMOSAPI *pInst, CALLENTRY *pCall); +static int StatusCallback (cJSON *pMsg, void *pUser); +static void DispatcherThread(void *pUser); +static int Dispatcher_Start(IMOSAPI *pInst); +static int Dispatcher_Stop(IMOSAPI *pInst); +static void Send(IMOSAPI *pInst, const char *pszMsg, ...); +static void HandleMessage(IMOSAPI *pInst, char *pszMsg); + + +// ----------------------------------------------------------------------------- +// Interface +// ----------------------------------------------------------------------------- + +IMOSAPI *Imo2S_Init(IMO2SCB Callback, void *pUser, int iFlags) +{ + IMOSAPI *pInst = calloc(1, sizeof(IMOSAPI)); + + if (!pInst) return NULL; + if (!(pInst->pszLogBuf = malloc(pInst->cbBuf=512)) || + !(pInst->hInst = ImoSkype_Init(StatusCallback, pInst)) || + !(pInst->hBuddyList = BuddyList_Init()) || + !(pInst->hAvatarList = AvatarList_Init()) || + !(pInst->hMsgQueue = MsgQueue_Init()) || +#ifdef WIN32 + ((iFlags & IMO2S_FLAG_ALLOWINTERACT) && W32Browser_Init(0)==-1) || +#endif + !(pInst->hCallQueue = CallQueue_Init())) + { + Imo2S_Exit(pInst); + return NULL; + } + pInst->Callback = Callback; + pInst->pUser = pUser; + pInst->iFlags = iFlags; + pInst->myUser.iBuddyStatus = 3; + strcpy (pInst->myUser.szStatus, "OFFLINE"); + pInst->iProtocol = 3; + return pInst; +} + +// ----------------------------------------------------------------------------- + +void Imo2S_SetLog (IMOSAPI *pInst, FILE *fpLog) +{ + pInst->fpLog = fpLog; +} + +// ----------------------------------------------------------------------------- + +void Imo2S_Exit (IMOSAPI *pInst) +{ + if (!pInst) return; + pInst->iShuttingDown = 1; + if (pInst->fpLog) fprintf (pInst->fpLog, "Imo2S_Exit()\n"); + if (pInst->iLoginStat == 1) Imo2S_Logout(pInst); + if (pInst->hInst) ImoSkype_Exit(pInst->hInst); + if (pInst->hBuddyList) BuddyList_Exit(pInst->hBuddyList); + if (pInst->hAvatarList) AvatarList_Exit(pInst->hAvatarList); + if (pInst->hMsgQueue) MsgQueue_Exit(pInst->hMsgQueue); + if (pInst->hCallQueue) CallQueue_Exit(pInst->hCallQueue); + if (pInst->pszPass) free (pInst->pszPass); + if (pInst->pszLogBuf) free(pInst->pszLogBuf); + if (pInst->pszClientName) free(pInst->pszClientName); +#ifdef WIN32 + if (pInst->iFlags & IMO2S_FLAG_ALLOWINTERACT) W32Browser_Exit(); +#endif + BuddyList_FreeEntry(&pInst->myUser, TRUE); + memset (pInst, 0, sizeof(IMOSAPI)); + free (pInst); +} + +// ----------------------------------------------------------------------------- + +int Imo2S_Login (IMOSAPI *pInst, char *pszUser, char *pszPass, char **ppszError) +{ + // In case this module is passing in the original values... + char *pszLocalUser, *pszLocalPass; + + if (pInst->fpLog) fprintf (pInst->fpLog, "Imo2S_Login(%s, ****)\n", pszUser); + if (pInst->iLoginStat == 1) return pInst->iLoginStat; + if (!pInst->hInst) pInst->hInst=ImoSkype_Init(StatusCallback, pInst); + pszLocalUser = strdup(pszUser); + if (pInst->myUser.pszUser) free (pInst->myUser.pszUser); + pInst->myUser.pszUser = pszLocalUser; + pszLocalPass = strdup(pszPass); + if (pInst->pszPass) free (pInst->pszPass); + pInst->pszPass = pszLocalPass; + Send(pInst, "CONNSTATUS CONNECTING"); + pInst->iLoginStat = ImoSkype_Login(pInst->hInst, pszLocalUser, pszLocalPass); + if (pInst->iLoginStat == 1) + { + ImoSkype_GetUnreadMsgs(pInst->hInst); + if (IMO_API_VERSION > 0) ImoSkype_GetAlpha(pInst->hInst); + Dispatcher_Start(pInst); + } + else + if (ppszError) *ppszError = ImoSkype_GetLastError(pInst->hInst); + return pInst->iLoginStat; +} + +// ----------------------------------------------------------------------------- + +void Imo2S_Logout(IMOSAPI *pInst) +{ + if (pInst->fpLog) fprintf (pInst->fpLog, "Imo2S_Logout()\n"); + Dispatcher_Stop(pInst); + if (ImoSkype_Logout(pInst->hInst) == 1) + { + pInst->iLoginStat = 0; + strcpy (pInst->myUser.szStatus, "OFFLINE"); + } + + + // If we relogin, user information won't be re-propagated if we + // reuse the same connection. Therefore also dispose the connection + // to imo.im service (wouldn't be necessary, but to ensure proper + // repropagation of contacts on login, we have to do it, sorry) + if (!pInst->iShuttingDown) + { + ImoSkype_Exit(pInst->hInst); + pInst->hInst = NULL; + } +} + +// ----------------------------------------------------------------------------- + +int Imo2S_Send (IMOSAPI *pInst, char *pszMsg) +{ + char *pszDup = strdup(pszMsg); + char *pszRealMsg = pszMsg; + + if (*pszRealMsg=='#') + { + char *p; + if (p = strchr (pszRealMsg, ' ')) pszRealMsg=p+1; + } + if (pInst->fpLog) fprintf (pInst->fpLog, "Imo2S_Send(%s)\n", pszMsg); + if (strlen(pszRealMsg)>15 && strncasecmp (pszRealMsg, "SET ", 4)== 0 && + (strncasecmp (pszRealMsg+4, "USERSTATUS", 10)==0 || + strncasecmp (pszRealMsg+4, "CONNSTATUS", 10)==0)) + { +// if (pInst->fpLog) fprintf (pInst->fpLog, "Imo2S_Send: iLoginStat = %d\n", +// pInst->iLoginStat); + if (pInst->iLoginStat == 0) + { + if (pInst->myUser.pszUser && pInst->pszPass && strncasecmp (pszRealMsg+15, "OFFLINE", 7)) + { + Imo2S_Login(pInst, pInst->myUser.pszUser, pInst->pszPass, NULL); + } + } + else + { + HandleMessage (pInst, pszDup); + if (strncasecmp (pszRealMsg+15, "OFFLINE", 7) == 0) + Imo2S_Logout(pInst); + free (pszDup); + return 0; + } + } + if (pInst->iLoginStat != 1) return -1; + HandleMessage(pInst, pszDup); + free (pszDup); + return 0; +} + +// ----------------------------------------------------------------------------- +// Static +// ----------------------------------------------------------------------------- + + +static int StatusCallback (cJSON *pMsg, void *pUser) +{ + char *pszName; + BOOL bAdded; + cJSON *pContent, *pProto; + IMOSAPI *pInst = (IMOSAPI*)pUser; + int m, iSize = cJSON_GetArraySize(pMsg); + + if (pInst->fpLog) + { + char *pszMsg = cJSON_Print(pMsg); + fprintf (pInst->fpLog, "Imo2S::StatusCallback():%s\n", pszMsg); + free(pszMsg); + } + + for (m=0; mvaluestring; + + if ((pProto = cJSON_GetObjectItem(pContent,"proto")) && strcasecmp (pProto->valuestring, "prpl-skype")) + continue; + + if (!strcmp(pszName, "recv_im")) + { + // I got a message! + cJSON *pEdata = cJSON_GetObjectItem(pContent,"edata"); + + if (pEdata) + { + MSGENTRY *pMsg; + + // imo.im sometimes seems to send information about messages you sent yourself. + // We have to ignore them. + if (strcmp(cJSON_GetObjectItem(pEdata, "buid")->valuestring, pInst->myUser.pszUser) && + (pMsg = MsgQueue_Insert(pInst->hMsgQueue, pEdata))) + { + if (pInst->iFlags & IMO2S_FLAG_CURRTIMESTAMP) time(&pMsg->timestamp); + Send(pInst, "%sMESSAGE %d STATUS %s", pInst->iProtocol>=3?"CHAT":"", + pMsg->hdr.uMsgNr, pMsg->szStatus); + } + } + } + else if (!strcmp(pszName, "signed_on")) + { + // I just signed on. + cJSON *pEdata = cJSON_GetObjectItem(pContent,"edata"); + char *pszAlias; + + Send(pInst, "CONNSTATUS ONLINE"); + if (strcmp(pInst->myUser.szStatus, "OFFLINE")==0) + strcpy (pInst->myUser.szStatus, "ONLINE"); + Send(pInst, "USERSTATUS %s", pInst->myUser.szStatus); + if (pEdata && (pszAlias = cJSON_GetObjectItem(pEdata, "alias")->valuestring)) + { + pInst->myUser.pszAlias = strdup(pszAlias); + } + Send(pInst, "CURRENTUSERHANDLE %s", cJSON_GetObjectItem(pContent, "uid")->valuestring); + } + else if (!strcmp(pszName, "buddy_icon")) + { + // Here are the Avatars for the buddies + //We have to track them in a seperate list as the buddies may get populated lateron + cJSON *pArray = cJSON_GetObjectItem(pContent,"edata"), *pItem; + int i, iCount; + + if (pArray) + { + for (i=0, iCount = cJSON_GetArraySize(pArray); ihAvatarList, pItem); + } + } + } + else if (!strcmp(pszName, "disconnect")) + { + // I got disconnected (wrong user/pass?) + cJSON *pEdata = cJSON_GetObjectItem(pContent,"edata"); + //char *pszMsg; + + Send(pInst, "CONNSTATUS OFFLINE"); + Send(pInst, "USERSTATUS OFFLINE"); + strcpy (pInst->myUser.szStatus, "OFFLINE"); + /* + if (pEdata && (pszMsg = cJSON_GetObjectItem(pEdata, "msg")->valuestring)) + { + if (strcmp(pszMsg, "uidpassword")==0) + { + fprintf (stderr, "Invalid username / password combination!\n"); + } + } + */ + } + else if ((bAdded = !strcmp(pszName, "buddy_added")) || !strcmp(pszName, "buddy_status")) + { + // Here comes the contact list + cJSON *pArray = cJSON_GetObjectItem(pContent,"edata"), *pItem; + char *pszLastBUID = NULL, *pszLastGroup = NULL; + int i, iCount; + + if (pArray) + { + TYP_LIST *hListMemToAdd = NULL; + + for (i=0, iCount = cJSON_GetArraySize(pArray); ivaluestring; + cJSON *pGroup = cJSON_GetObjectItem(pItem, "group"); + cJSON *pDisplay = cJSON_GetObjectItem(pItem, "display"); + + if (bAdded) + { + /* + if (BuddyList_Find (pInst->hBuddyList, cJSON_GetObjectItem(pItem, "buid")->valuestring)) + bAdded=FALSE; + else + */ + BuddyList_Insert(pInst->hBuddyList, pItem); + } + else BuddyList_SetStatus(pInst->hBuddyList, pItem); + + if ((!pGroup || strcmp(pGroup->valuestring, "Skype")==0 || + strcmp(pGroup->valuestring, "Offline")==0) && + !pDisplay) + { + // Normal user, not groupchat, so output this + sprintf (szQuery, "GET USER %s ONLINESTATUS", pszBUID); + HandleMessage (pInst, szQuery); + sprintf (szQuery, "GET USER %s MOOD_TEXT", pszBUID); + HandleMessage (pInst, szQuery); + } + else + { + // Groupchat created/added + MSGENTRY * pMsgEntry = NULL; + + if (bAdded && pInst->pszBuddiesToAdd) + { + // Process pending ADDs + char *pTok, szQuery[256]; + + for (pTok = strtok(pInst->pszBuddiesToAdd, ", "); pTok; pTok=strtok(NULL, ", ")) + ImoSkype_GroupInvite(pInst->hInst, pszBUID, pTok); + free (pInst->pszBuddiesToAdd); + pInst->pszBuddiesToAdd = NULL; + sprintf (szQuery, "GET CHAT %s STATUS", pszBUID); + HandleMessage (pInst, szQuery); + } + else + { + if (bAdded || !pDisplay) + { + char szQuery[256], szGroup[256]={0}; + NICKENTRY *pChat; + + if (pGroup) sprintf (szGroup, "%s;", pGroup->valuestring); + if (pGroup && (pChat=BuddyList_Find(pInst->hBuddyList, szGroup)) && + pChat->hGCMembers) + { + sprintf (szQuery, "GET CHAT %s MEMBERS", szGroup); + HandleMessage (pInst, szQuery); + } + else + { + if ((bAdded || !pChat) && (!pszLastBUID || strcmp(pszLastBUID, pszBUID)) && + (!pszLastGroup || (pGroup && strcmp(pszLastGroup, pGroup->valuestring)))) + { + if (!hListMemToAdd) hListMemToAdd=List_Init(0); + List_Push(hListMemToAdd, pItem); + pszLastBUID = pszBUID; + if (pGroup) pszLastGroup = pGroup->valuestring; + } + } + } + /* + else + { + pMsgEntry = MsgQueue_AddEvent (pInst->hMsgQueue, pszBUID, "SETTOPIC"); + if (pDisplay->valuestring) pMsgEntry->pszMessage = strdup(pDisplay->valuestring); + } + */ + if (pMsgEntry) + Send(pInst, "%sMESSAGE %d STATUS %s", pInst->iProtocol>=3?"CHAT":"", + pMsgEntry->hdr.uMsgNr, pMsgEntry->szStatus); + + } + } + } + } + if (hListMemToAdd) + { + int nCount; + + for (i=0, nCount=List_Count(hListMemToAdd); ihMsgQueue, cJSON_GetObjectItem(pItem, "buid")->valuestring, "ADDEDMEMBERS")) // MULTI_SUBSCRIBED + { + if (pInviter) pMsgEntry->pszAuthor = strdup(pInviter->valuestring); + Send(pInst, "%sMESSAGE %d STATUS %s", pInst->iProtocol>=3?"CHAT":"", + pMsgEntry->hdr.uMsgNr, pMsgEntry->szStatus); + } + } + + List_Exit(hListMemToAdd); + hListMemToAdd = NULL; + } + if (bAdded && pInst->bFriendsPending) + { + char szMsg[]="SEARCH FRIENDS"; + pInst->bFriendsPending = 0; + HandleMessage (pInst, szMsg); + } + } + } + else if (!strcmp(pszName, "buddy_removed")) + { + // Here comes the contact list + cJSON *pArray = cJSON_GetObjectItem(pContent,"edata"), *pItem; + int i, iCount; + + if (pArray) + { + for (i=0, iCount = cJSON_GetArraySize(pArray); ivaluestring)) + { + NICKENTRY *pNick = BuddyList_Find (pInst->hBuddyList, pszUser); + + if (pNick && pNick->pGroup) + { + MSGENTRY *pMsg = MsgQueue_AddEvent (pInst->hMsgQueue, pszUser, "KICKED"); + cJSON *pUid = cJSON_GetObjectItem(pContent,"uid"); + if (pUid) pMsg->pszAuthor = strdup(pUid->valuestring); + Send(pInst, "%sMESSAGE %d STATUS %s", pInst->iProtocol>=3?"CHAT":"", + pMsg->hdr.uMsgNr, pMsg->szStatus); + } + else + Send (pInst, "USER %s BUDDYSTATUS 1", pszUser); + if (pNick) + BuddyList_Remove (pInst->hBuddyList, pNick); + } + } + } + } + else + if (/*!strcmp(pszName, "recv") || */!strcmp(pszName, "streams_info")) + { + cJSON *pEdata = cJSON_GetObjectItem(pContent,"edata"), + *pType = cJSON_GetObjectItem(pContent,"type"), *pVal; + + if (pType && pEdata) + { + if (strcasecmp(pType->valuestring, "call")==0 || strcasecmp(pType->valuestring, "video")==0 || + strcasecmp(pType->valuestring, "av")==0) + { + // Rring, rrring... + int iDirection = ((pVal = cJSON_GetObjectItem(pEdata, "is_initiator")) && pVal->type == cJSON_True)?CALL_OUTGOING:CALL_INCOMING; + CALLENTRY *pCall = CallQueue_Insert (pInst->hCallQueue, pEdata, iDirection); + if (pCall) + { + Send (pInst, "CALL %d STATUS %s", pCall->hdr.uMsgNr, pCall->szStatus); + if ((pInst->iFlags & IMO2S_FLAG_ALLOWINTERACT) && iDirection == CALL_OUTGOING) + StartCallSWF (pInst, pCall); + } + } + } + } + else + if (!strcmp(pszName, "ended")) + { + cJSON *pEdata = cJSON_GetObjectItem(pContent,"edata"), + *pType = cJSON_GetObjectItem(pContent,"type"); + + if (pType && pEdata) + { + if (strcasecmp(pType->valuestring, "call")==0 || strcasecmp(pType->valuestring, "video")==0) + { + // No call ID, so just hangup all calls to this user + int i, nCount = List_Count(pInst->hCallQueue); + char *pszUser = cJSON_GetObjectItem(pEdata, "buid")->valuestring; + + for (i=0; ihCallQueue, i); + + if (!strcmp(pCall->pszUser, pszUser) && strcmp (pCall->szStatus, "FINISHED")) + { + char szQuery[256]; + + sprintf (szQuery, "SET CALL %d STATUS FINISHED", pCall->hdr.uMsgNr); + HandleMessage (pInst, szQuery); + } + } + } + } + } + else + if (!strcmp(pszName, "reflect")) // Status reflections. We may want to support more of them in the future + { + cJSON *pEdata = cJSON_GetObjectItem(pContent,"edata"), + *pType = cJSON_GetObjectItem(pContent,"type"), *pRname, *pValue; + + if (pEdata && pType && (pRname = cJSON_GetObjectItem(pEdata,"r_name"))) + { + if (strcasecmp(pType->valuestring, "account") == 0) + { + if (strcasecmp (pRname->valuestring, "set_status") == 0 && + (pValue = cJSON_GetObjectItem(pEdata,"primitive")) ) + { + unsigned int i; + + for (i=0; ivaluestring)) + { + strcpy (pInst->myUser.szStatus, m_stMap[i].pszSkypeStat); + Send (pInst, "USERSTATUS %s", pInst->myUser.szStatus); + break; + } + } + + // Just ensure that on autoaway we notify imo.im that we are still there + if (!strcmp (pValue->valuestring, "away")) + ImoSkype_KeepAlive(pInst->hInst); + + if (pValue = cJSON_GetObjectItem(pEdata,"status")) + { + if (pInst->myUser.pszStatusText) free (pInst->myUser.pszStatusText); + pInst->myUser.pszStatusText = NULL; + if (*pValue->valuestring) + pInst->myUser.pszStatusText = strdup(pValue->valuestring); + } + + } + } + else + if (strcasecmp(pType->valuestring, "conv") == 0) + { + if (strcasecmp (pRname->valuestring, "send_im") == 0) + { + MSGENTRY *pMsg; + + if (pMsg = MsgQueue_AddReflect(pInst->hMsgQueue, pEdata, pInst->hBuddyList)) + { + if (pInst->iFlags & IMO2S_FLAG_CURRTIMESTAMP) time(&pMsg->timestamp); + Send(pInst, "%sMESSAGE %d STATUS %s", pInst->iProtocol>=3?"CHAT":"", + pMsg->hdr.uMsgNr, pMsg->szStatus); + } + } + + } + } + } + else + if (!strcmp(pszName, "expired") && m>=iSize-1) + { + // Session expired, so we have to reconnect + Send(pInst, "CONNSTATUS OFFLINE"); + Send(pInst, "USERSTATUS OFFLINE"); + strcpy (pInst->myUser.szStatus, "OFFLINE"); + pInst->iLoginStat = 0; + Dispatcher_Stop(pInst); + if (pInst->myUser.pszUser && pInst->pszPass) + Imo2S_Login(pInst, pInst->myUser.pszUser, pInst->pszPass, NULL); + } + else + if (!strcmp(pszName, "ack")) + { + MSGENTRY *pMsg; + cJSON *pRqId = cJSON_GetObjectItem(pContent,"request_id"); + + if (pRqId && (pMsg = MsgQueue_FindByRqId(pInst->hMsgQueue, pRqId->valueint))) + { + cJSON *pResp, *pTS; + + strcpy (pMsg->szStatus, "SENT"); + if ((pResp = cJSON_GetObjectItem(pContent,"response")) && + (pTS = cJSON_GetObjectItem(pResp,"timestamp"))) + pMsg->timestamp = pTS->valueint; + Send (pInst, "MESSAGE %d STATUS %s", pMsg->hdr.uMsgNr, pMsg->szStatus); + } + } + else + { + char *pszMsg = cJSON_Print(pMsg); + fprintf (stderr, "%s\n\n", pszMsg); + free (pszMsg); + } + } + return 0; +} + +// ----------------------------------------------------------------------------- + +#ifdef WIN32 +// Set call status to finished when closing phone applet window just in case +// there is no callback by imo.im upon closure +static LRESULT CallWndFilter(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_DESTROY: + { + IMOSAPI *pInst = (IMOSAPI*)GetWindowLong (hWnd, GWL_PINST); + int i, nCount; + + if (!pInst) break; + for (i=0, nCount = List_Count(pInst->hCallQueue); ihCallQueue, i); + + if (pCall->hCallWnd == hWnd) + { + char szQuery[256]; + + sprintf (szQuery, "SET CALL %d STATUS FINISHED", pCall->hdr.uMsgNr); + HandleMessage (pInst, szQuery); + break; + } + } + break; + } + } + return CallWindowProc((WNDPROC)GetWindowLong (hWnd, GWL_ORIGWPRC), hWnd, message, wParam, lParam); +} +#endif + + +// ----------------------------------------------------------------------------- + +static int StartCallSWF (IMOSAPI *pInst, CALLENTRY *pCall) +{ + char szSWF[256], szFlashVars[1024], szID[18]; + char szHTML[2048]; + static unsigned int id=100; + + + /* The flash plugin basically opens rtmp://[host]/mchat in case of type=imo, otherwise rtmp://[host]/ivc + Maybe this can also be used with a SIP-phone or Asterisk? + */ +#ifndef WIN32 + FILE *fpTemp; + int iFound; + + sprintf (pCall->szCallFile, "xdg-open %s.html", tmpnam(NULL)); + if (!(fpTemp=fopen(pCall->szCallFile, "w"))) return -1; +#else + // Windows: + // Ensure that we are allowed to load the .swf from imo.im +#ifndef CSIDL_APPDATA +#define CSIDL_APPDATA 0x001a +#endif + static BOOL bInit = TRUE; + + if (bInit) + { + char szPath[MAX_PATH]={0}; + HMODULE hModule; + typedef HMODULE (__stdcall *SHGETFOLDERPATH)(HWND, int, HANDLE, DWORD, LPTSTR); + + if (hModule = LoadLibrary("SHFOLDER.DLL")) + { + SHGETFOLDERPATH fnShGetFolderPath = (SHGETFOLDERPATH)GetProcAddress(hModule, "SHGetFolderPathA"); + + if (fnShGetFolderPath && fnShGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, szPath)==S_OK) + { + strcat (szPath, "\\Macromedia\\Flash Player"); + if (GetFileAttributes (szPath) != 0xFFFFFFFF) + { + strcat (szPath, "\\#Security"); + _mkdir (szPath); + strcat (szPath, "\\FlashPlayerTrust"); + _mkdir (szPath); + strcat (szPath, "\\imo2sproxy.cfg"); + if (GetFileAttributes (szPath) == 0xFFFFFFFF) + { + FILE *fp = fopen (szPath, "w"); + + if (fp) + { + fputs ("https://imo.im/images/nchat.swf\nabout:blank\n", fp); + fclose (fp); + } + } + } + } + FreeLibrary(hModule); + } + bInit = FALSE; + } +#endif + sprintf (szSWF, "https://imo.im/images/nchat.swf?%s", IVC_VERSION); + ImoRq_CreateID (szID, 16); + //sprintf (szID, "imo%d", id++); + + if (*pCall->szSendStream) + sprintf (szFlashVars, + "type=skype&" + "send_stream=%s&" + "recv_stream=%s&" + "id=skype#%s&" + "host=video0.imo.im&" + "debug=conv net&" + "buddy_icon=https://imo.im/assets/main/icn_default.png&" + "setCookie=setCookie&" + "getCookie=getCookie&" + "removeCookie=removeCookie&" + "log=log&" + "init_callback=init_callback&" + "video_options_changed_cb=video_options_changed_cb&" + "options_changed_cb=options_changed_cb&" + "mic_changed_cb=mic_changed_cb&" + "cam_changed_cb=cam_changed_cb&" + "connection_status_cb=connection_status_cb", + pCall->szSendStream, pCall->szRecvStream, pCall->szSendStream); + else + return -1; + // sprintf (szFlashVars, "type=imo&conv=%s&role=%d&host=%s&id=%s", pCall->szConv, pCall->iRole, pCall->szIP, szID); + + strcat (szFlashVars, "&audio_only=true"); + // Currently not supported + sprintf (szHTML, "\n" + "" + "\n" + "\n" + "\n" + "\n" + "" + "\n" + "
\n" + "\n", + szSWF, szID, szSWF, szFlashVars); + +#ifdef WIN32 + //OutputDebugString (szHTML); + sprintf (szSWF, "Voicechat with %s", pCall->pszUser); + + /* + + { + FILE *fpOut = fopen("C:\\TEMP\\CALL.HTML", "w"); + + fputs (szHTML, fpOut); + fclose (fpOut); + system ("\"C:\\Programme\\Internet Explorer\\IEXPLORE.EXE\" C:\\TEMP\\CALL.HTML"); + } + */ + + if ((pCall->hCallWnd = W32Browser_ShowHTMLStr (szHTML,420, 280, szSWF))>0) + { + // Hook WndProc to handle WM_DESTROY so that we generate a CALL %d STATUS FINISHED on + // closing the chat window +#ifdef _WIN64 + SetWindowLongPtr ((HWND)pCall->hCallWnd, GWL_PINST, (LONG_PTR)pInst); + SetWindowLongPtr ((HWND)pCall->hCallWnd, GWL_ORIGWPRC, + GetWindowLongPtr ((HWND)pCall->hCallWnd, GWLP_WNDPROC)); + SetWindowLongPtr ((HWND)pCall->hCallWnd, GWLP_WNDPROC, (LONG_PTR)CallWndFilter); +#else + SetWindowLong ((HWND)pCall->hCallWnd, GWL_PINST, (LONG)pInst); + SetWindowLong ((HWND)pCall->hCallWnd, GWL_ORIGWPRC, GetWindowLong ((HWND)pCall->hCallWnd, GWL_WNDPROC)); + SetWindowLong((HWND)pCall->hCallWnd, GWL_WNDPROC, (LONG)CallWndFilter); +#endif + return 0; + } + return -1; +#else + fprintf (fpTemp, "%s", szHTML); + fclose (fpTemp); + iFound = system(pCall->szCallFile); + return (iFound == -1 || iFound == 127)?-1:0; +#endif +} + +// ----------------------------------------------------------------------------- + +static void DispatcherThread(void *pUser) +{ + IMOSAPI *pInst = (IMOSAPI*)pUser; + time_t t = 0, tcur; + + if (pInst->fpLog) fprintf (pInst->fpLog, "Imo2S::DispatcherThread() start\n"); + while (!pInst->iShuttingDown) + { +#if defined(WIN32) && defined(WIN32) + char szBuf[128]; + + sprintf (szBuf, "DispatcherThread %d loops.\n", GetCurrentThreadId()); + OutputDebugString (szBuf); +#endif + if (time(&tcur)>=t+300) + { + t=tcur; + //ImoSkype_Ping (pInst->hInst); + ImoSkype_KeepAlive(pInst->hInst); + } + + // Set status in case this is needed + if (pInst->tSetMoodText && tcur>=pInst->tSetMoodText) + { + int i; + + pInst->tSetMoodText = 0; + for (i=0; imyUser.szStatus)) + { + ImoSkype_SetStatus(pInst->hInst, m_stMap[i].pszImoStat, + pInst->myUser.pszStatusText?pInst->myUser.pszStatusText:""); + break; + } + } + } + ImoSkype_Poll(pInst->hInst); + } +} + +// ----------------------------------------------------------------------------- + +#ifdef WIN32 +static int Dispatcher_Start(IMOSAPI *pInst) +{ + DWORD ThreadID; + + if (pInst->fpLog) fprintf (pInst->fpLog, "Imo2S::Dispatcher_Start()\n"); + return (pInst->hThread=(thread_t)_beginthreadex(NULL, 0, + (unsigned(__stdcall *)(void*))DispatcherThread, pInst, 0, &ThreadID))!=0; + +} + +static int Dispatcher_Stop(IMOSAPI *pInst) +{ + int iRet, iOldShutdown; + + if (pInst->fpLog) + { + fprintf (pInst->fpLog, "Imo2S::Dispatcher_Stop()\n"); + fflush(pInst->fpLog); + } + + // Shutdown polling socket and wait some time if thread terminates + // gracefully, otherwise kill it + iOldShutdown = pInst->iShuttingDown; + pInst->iShuttingDown = 1; + ImoSkype_CancelPolling (pInst->hInst); + if (WaitForSingleObject (pInst->hThread, 2000) == WAIT_TIMEOUT) + iRet = TerminateThread (pInst->hThread, 0); + else iRet = 1; + pInst->iShuttingDown = iOldShutdown; + + if (iRet) + { + CloseHandle (pInst->hThread); + pInst->hThread = 0; + } + + if (pInst->fpLog) + { + fprintf (pInst->fpLog, "Imo2S::Dispatcher_Stop() done.\n"); + pInst->fpLog = NULL; + } + + return iRet; +} + +#else +static int Dispatcher_Start(IMOSAPI *pInst) +{ + if (pInst->fpLog) fprintf (pInst->fpLog, "Imo2S::Dispatcher_Start()\n"); + return pthread_create(&pInst->hThread, NULL, DispatcherThread, pInst)==0; +} + +static int Dispatcher_Stop(IMOSAPI *pInst) +{ + if (pInst->fpLog) fprintf (pInst->fpLog, "Imo2S::Dispatcher_Stop()\n"); + if (pthread_cancel(pInst->hThread)) + { + pInst->hThread=0; + return 1; + } + return 0; +} +#endif + +// ----------------------------------------------------------------------------- + +static void Send(IMOSAPI *pInst, const char *pszMsg, ...) +{ + va_list ap; + int iLen, iLenCmdID; + char *pszLogBuf = pInst->pszLogBuf; + int cbBuf = pInst->cbBuf; + + iLenCmdID = pInst->pszCmdID?strlen(pInst->pszCmdID)+1:0; + do + { + cbBuf = pInst->cbBuf - iLenCmdID; + pszLogBuf = pInst->pszLogBuf + iLenCmdID; + va_start(ap, pszMsg); + iLen = vsnprintf (pszLogBuf, cbBuf, pszMsg, ap); + va_end(ap); +#ifndef WIN32 + if (iLen>=cbBuf) iLen=-1; +#endif + if (iLen == -1) + { + char *pNewBuf; + + if (!(pNewBuf = realloc(pInst->pszLogBuf, pInst->cbBuf*2))) + { + break; + } + pInst->cbBuf*=2; + pInst->pszLogBuf = pNewBuf; + } + } while (iLen == -1); + if (pInst->pszCmdID && iLenCmdID>1) + { + memcpy (pInst->pszLogBuf, pInst->pszCmdID, iLenCmdID); + pInst->pszLogBuf[iLenCmdID-1]=' '; + } + +//printf ("%s\n", szBuf); + pInst->Callback(pInst->pszLogBuf, pInst->pUser); +} + +// ----------------------------------------------------------------------------- + +static void HandleMessage(IMOSAPI *pInst, char *pszMsg) +{ + char *pszCmd=strtok(pszMsg, " "); + + if (!pInst || !pszCmd || !pInst->hInst) return; + if (*pszCmd=='#') + { + // This is a PROTOCOL 4 feature, but we will support it just in case... + pInst->pszCmdID = pszCmd; + if (!(pszCmd=strtok(NULL, " "))) + { + pInst->pszCmdID = NULL; + return; + } + } + else pInst->pszCmdID = NULL; + + if (strcasecmp(pszCmd, "PROTOCOL") == 0) + { + if (pszCmd = strtok(NULL, " ")) + { + pInst->iProtocol = atoi(pszCmd); + if (pInst->iProtocol>3) pInst->iProtocol=3; + } + + Send (pInst, "PROTOCOL %d", pInst->iProtocol); + return; + } + else + if (strcasecmp(pszCmd, "PING") == 0) + { + Send (pInst, "PONG"); + return; + } + else + if (strcasecmp(pszCmd, "SEARCH") == 0) + { + TYP_FIFO *hFifo; + + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 2 Invalid command"); + return; + } + + if (strcasecmp(pszCmd, "FRIENDS") == 0) + { + unsigned int nCount =List_Count(pInst->hBuddyList); + + if (!nCount) + { + pInst->bFriendsPending = 1; + return; + } + if(hFifo=Fifo_Init(512)) + { + unsigned int i; + char *pszUsers; + + for (i=0; ihBuddyList, i); + if (!pEntry->pGroup && !pEntry->hGCMembers) + { + if (i>0) Fifo_AddString (hFifo, ", "); + Fifo_AddString (hFifo, pEntry->pszUser); + } + } + if (pszUsers = Fifo_Get(hFifo, NULL)) + Send (pInst, "USERS %s", pszUsers); + else + pInst->bFriendsPending = 1; + Fifo_Exit(hFifo); + } + } + else if (strcasecmp(pszCmd, "MISSEDMESSAGES") == 0) + { + unsigned int nCount=List_Count(pInst->hMsgQueue); + + if(nCount && (hFifo=Fifo_Init(512))) + { + unsigned int i, j=0; + + for (i=0; ihMsgQueue, i); + char szNr[32]; + + if (!strcmp(pEntry->szStatus, "RECEIVED")) + { + if (j>0) Fifo_AddString (hFifo, ", "); + sprintf (szNr, "%d", pEntry->hdr.uMsgNr); + Fifo_AddString (hFifo, szNr); + j++; + } + } + Send (pInst, "MESSAGES %s", Fifo_Get(hFifo, NULL)); + Fifo_Exit(hFifo); + } + } + else if (strcasecmp(pszCmd, "USERS") == 0) + { + // There is no possibility to search Skype users in imo.im + // therefore just return that the user exists even if it doesn't :( + Send (pInst, "USERS %s", pszCmd+6); + // We add the user as a temporary contact to our list so that the + // client can get the empty properties and set the buddystatus. + BuddyList_AddTemporaryUser (pInst->hBuddyList, pszCmd+6); + } + else if (strcasecmp(pszCmd, "USERSWAITINGMYAUTHORIZATION") == 0) + { + Send (pInst, "USERS"); + } + else if (strcasecmp(pszCmd, "ACTIVECALLS") == 0) + { + char szCalls[512]; + int i, nCount, iOffs=0; + + iOffs = sprintf (szCalls, "CALLS"); + for (i=0, nCount=List_Count(pInst->hCallQueue); ihCallQueue, i))->hdr.uMsgNr); + if (iOffs+6>=sizeof(szCalls)) break; + } + Send (pInst, szCalls); + } + else if (strcasecmp(pszCmd, "RECENTCHATS") == 0) + { + char szChats[2056]; + int i, nCount, iOffs=0, j=0; + + iOffs = sprintf (szChats, "CHATS "); + for (i=0, nCount=List_Count(pInst->hBuddyList); ihBuddyList, i); + + if (BuddyList_IsGroupchat(pChat)) + { + if (j) iOffs+=sprintf(&szChats[iOffs], ", "); + iOffs+=sprintf(&szChats[iOffs], "%s", pChat->pszUser); + if (iOffs+6>=sizeof(szChats)) break; + j++; + } + } + Send (pInst, szChats); + } + return; + } + else + if (strcasecmp(pszCmd, "GET") == 0) + { + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 7 Invalid property"); + return; + } + + if (strcasecmp(pszCmd, "USER") == 0) + { + NICKENTRY *pUser = NULL; + + if (pszCmd = strtok(NULL, " ")) + { + if (!strcasecmp (pszCmd, pInst->myUser.pszUser)) + pUser = &pInst->myUser; + else + pUser = BuddyList_Find(pInst->hBuddyList, pszCmd); + } + + if (!pUser) + { + Send (pInst, "ERROR 26 Invalid user handle"); + return; + } + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 10 Invalid property"); + return; + } + + if (!strcasecmp (pszCmd, "HANDLE")) + { + if (pUser->pszAlias) Send (pInst, "USER %s HANDLE %s", pUser->pszUser, pUser->pszAlias); + } + else if (!strcasecmp (pszCmd, "FULLNAME")) /* Workaround */ + Send (pInst, "USER %s FULLNAME %s", pUser->pszUser, pUser->pszAlias); + else if (!strcasecmp (pszCmd, "DISPLAYNAME")) + Send (pInst, "USER %s DISPLAYNAME %s", pUser->pszUser, pUser->pszAlias); + else if (!strcasecmp (pszCmd, "HASCALLEQUIPMENT")) + Send (pInst, "USER %s HASCALLEQUIPMENT TRUE", pUser->pszUser); + else if (!strcasecmp (pszCmd, "BUDDYSTATUS")) + Send (pInst, "USER %s BUDDYSTATUS %d", pUser->pszUser, pUser->iBuddyStatus); + else if (!strcasecmp (pszCmd, "ISAUTHORIZED")) + Send (pInst, "USER %s ISAUTHORIZED TRUE", pUser->pszUser); + else if (!strcasecmp (pszCmd, "MOOD_TEXT")) + Send (pInst, "USER %s MOOD_TEXT %s", pUser->pszUser, + pUser->pszStatusText?pUser->pszStatusText:""); + else if (!strcasecmp (pszCmd, "SEX")) + Send (pInst, "USER %s SEX UNKNOWN", pUser->pszUser); + else if (!strcasecmp (pszCmd, "BIRTHDAY")) + Send (pInst, "USER %s BIRTHDAY 0", pUser->pszUser); + else if (!strcasecmp (pszCmd, "ONLINESTATUS")) + { + unsigned int i; + + for (i=0; iszStatus)) + { + Send (pInst, "USER %s ONLINESTATUS %s", pUser->pszUser, m_stMap[i].pszSkypeStat); + break; + } + } + } + else if (!strcasecmp (pszCmd, "AVATAR")) + { + char *pszFile, *pAvatarBuf; + unsigned int dwLength; + AVATARENTRY *pAvatar; + FILE *fp; + + if (!(pszFile = strtok(NULL, " ")) || strcasecmp (pszFile, "1")) + { + Send (pInst, "ERROR 116 GET invalid ID"); + return; + } + if (!(pszFile = strtok(NULL, "\n"))) + { + Send (pInst, "ERROR 7 GET: invalid WHAT"); + return; + } + if (fp=fopen(pszFile, "r")) + { + fseek (fp, 0, SEEK_END); + if (ftell(fp)) + { + fclose(fp); + Send (pInst, "ERROR 124 GET Destination file is not empty"); + return; + } + fclose(fp); + } + if (!(pAvatar = AvatarList_Find(pInst->hAvatarList, pUser->pszUser)) || + !(pAvatarBuf = ImoSkype_GetAvatar (pInst->hInst, pAvatar->pszIcon, &dwLength))) + { + // FIXME: Normally we should return default avatar if pAvatar is NULL. + // Default avatar from imo.im is PNG, but JPEG is expected, so we + // return an error here + Send (pInst, "ERROR 122 GET Unable to load avatar"); + return; + } + if (!(fp=fopen(pszFile, +#ifdef WIN32 + "wb" +#else + "w" +#endif + ))) + { + Send (pInst, "ERROR 121 GET File path doesn't exist"); + return; + } + fwrite (pAvatarBuf, dwLength, 1, fp); + fclose (fp); + Send (pInst, "USER %s AVATAR 1 %s", pUser->pszUser, pszFile); + } + else + { + Send(pInst, "ERROR 10 Invalid propery"); + } + return; + } + else + if (strcasecmp(pszCmd, "CURRENTUSERHANDLE") == 0) + { + if (pInst->myUser.pszUser) + Send(pInst, "CURRENTUSERHANDLE %s", pInst->myUser.pszUser); + } + else + if (strcasecmp(pszCmd, "USERSTATUS") == 0) + { + Send(pInst, "USERSTATUS %s", pInst->myUser.szStatus); + } + else + if (strcasecmp(pszCmd, "MESSAGE") == 0 || strcasecmp(pszCmd, "CHATMESSAGE") == 0) + { + MSGENTRY *pEntry; + char *pszMessage = pszCmd; + + if (!(pszCmd = strtok(NULL, " ")) || !(pEntry = MsgQueue_Find(pInst->hMsgQueue, atol(pszCmd)))) + { + Send (pInst, "ERROR 14 Invalid message id"); + return; + } + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 10 Invalid property"); + return; + } + if (!strcasecmp (pszCmd, "TIMESTAMP")) + Send (pInst, "%s %d TIMESTAMP %ld", pszMessage, pEntry->hdr.uMsgNr, pEntry->timestamp); + else if (!strcasecmp (pszCmd, "PARTNER_HANDLE") || !strcasecmp (pszCmd, "FROM_HANDLE")) + { + char *pszUser = strdup(pEntry->pszAuthor?pEntry->pszAuthor:pEntry->pszUser), *p, *pszRealUser=pszUser; + + if (p=strtok(pszUser, ";")) + { + if (!(pszRealUser=strtok(NULL, ";"))) pszRealUser=p; + } + Send (pInst, "%s %d %s %s", pszMessage, pEntry->hdr.uMsgNr, pszCmd, pszRealUser); + free (pszUser); + } + else if (!strcasecmp (pszCmd, "PARTNER_DISPNAME")) + Send (pInst, "%s %d PARTNER_DISPNAME %s", pszMessage, pEntry->hdr.uMsgNr, pEntry->pszAlias); + else if (!strcasecmp (pszCmd, "TYPE")) + Send (pInst, "%s %d TYPE %s", pszMessage, pEntry->hdr.uMsgNr, pEntry->szType); + else if (!strcasecmp (pszCmd, "STATUS")) + Send (pInst, "%s %d STATUS %s", pszMessage, pEntry->hdr.uMsgNr, pEntry->szStatus); + else if (!strcasecmp (pszCmd, "FAILUREREASON")) + Send (pInst, "%s %d FAILUREREASON %s", pszMessage, pEntry->hdr.uMsgNr, pEntry->szFailure); + else if (!strcasecmp (pszCmd, "BODY")) + Send (pInst, "%s %d BODY %s", pszMessage, pEntry->hdr.uMsgNr, pEntry->pszMessage); + else if (!strcasecmp (pszCmd, "CHATNAME")) + { + if (pEntry->pszAuthor || !pEntry->pszMessage || strchr(pEntry->pszUser, ';')) // Groupchat + { + char *pszUser = strdup(pEntry->pszUser), *p; + + if (p=strchr(pszUser, ';')) p[1]=0; + Send (pInst, "%s %d CHATNAME %s", pszMessage, pEntry->hdr.uMsgNr, pszUser); + free (pszUser); + } + else + Send (pInst, "%s %d CHATNAME #%s/$%s", pszMessage, pEntry->hdr.uMsgNr, pInst->myUser.pszUser, pEntry->pszUser); + } + else if (!strcasecmp (pszCmd, "USERS")) + { + // On KICK, this is the user who GOT kicked + char *pszUser = strchr(pEntry->pszUser, ';'); + if (pszUser) pszUser++; else pszUser=pEntry->pszUser; + Send (pInst, "%s %d USERS %s", pszMessage, pEntry->hdr.uMsgNr, pszUser); + } + else + Send (pInst, "ERROR 10 Invalid property / not implemented"); + return; + } + else + if (strcasecmp(pszCmd, "PRIVILEGE") == 0) + { + if (!(pszCmd = strtok(NULL, " ")) || + (strcasecmp (pszCmd, "SKYPEOUT") && + strcasecmp (pszCmd, "SKYPEIN") && + strcasecmp (pszCmd, "VOICEMAIL"))) + { + Send (pInst, "ERROR 40 Unknown Privilege"); + return; + } + Send (pInst, "PRIVILEGE %s FALSE", pszCmd); + return; + } + else + if (strcasecmp(pszCmd, "CHAT") == 0) + { + char *pszChat; + NICKENTRY *pChat=NULL; + + // A $ sign is in the name of a dialog, otherwise it's a groupchat ID + if (!(pszChat = strtok(NULL, " ")) || + (!strchr(pszChat, '$') && !(pChat=BuddyList_Find(pInst->hBuddyList, pszChat)))) + { + Send (pInst, "ERROR 14 Invalid message id"); + return; + } + + + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 10 Invalid property"); + return; + } + + if (strcasecmp(pszCmd, "NAME") == 0) + Send (pInst, "CHAT %s NAME %s", pszChat, pszChat); + else if (strcasecmp(pszCmd, "STATUS") == 0) + // A $ sign is in the name of a dialog, otherwise it's a groupchat ID + Send (pInst, "CHAT %s STATUS %s", pszChat, pChat?"MULTI_SUBSCRIBED":"LEGACY_DIALOG"); + else if (strcasecmp(pszCmd, "ADDER") == 0) + Send (pInst, "CHAT %s ADDER %s", pszChat, pInst->myUser.pszUser); + else if (strcasecmp(pszCmd, "TYPE") == 0) + Send (pInst, "CHAT %s TYPE %s", pszChat, pChat?"MULTICHAT":"DIALOG"); + else if (strcasecmp(pszCmd, "MYROLE") == 0) + Send (pInst, "CHAT %s MYROLE USER", pszChat); + else if (strcasecmp(pszCmd, "MYSTATUS") == 0) + Send (pInst, "CHAT %s MYSTATUS SUBSCRIBED", pszChat); + else if ((strcasecmp(pszCmd, "TOPIC") == 0 || strcasecmp(pszCmd, "FRIENDLYNAME") == 0) && pChat) + Send (pInst, "CHAT %s %s %s", pszChat, pszCmd, pChat->pszDisplay); + else if ((strcasecmp(pszCmd, "ACTIVEMEMBERS") == 0 || strcasecmp(pszCmd, "MEMBERS") == 0) && pChat) + { + char szMembers[1024]={0}; + int i, nCount, iOffs=0; + + iOffs = sprintf (szMembers, "CHAT %s %s ", pszChat, pszCmd); + for (i=0, nCount=List_Count(pChat->hGCMembers); ihGCMembers, i); + char szUser[512], *pTok; + + if (i) iOffs+=sprintf(&szMembers[iOffs], " "); + strcpy (szUser, pMemb->pszUser); + pTok=strtok(szUser, ";"); + pTok=strtok(NULL, ";"); + iOffs+=sprintf(&szMembers[iOffs], "%s", pTok); + if (iOffs+6>=sizeof(szMembers)) break; + } + Send (pInst, szMembers); + } + else + Send(pInst, "ERROR 7 Invalid property / not implemented"); + return; + } + else + if (strcasecmp(pszCmd, "CALL") == 0) + { + CALLENTRY *pEntry; + + if (!(pszCmd = strtok(NULL, " ")) || !(pEntry = CallQueue_Find(pInst->hCallQueue, atol(pszCmd)))) + { + Send (pInst, "ERROR 11 Invalid call id"); + return; + } + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 10 Invalid property"); + return; + } + if (!strcasecmp (pszCmd, "TIMESTAMP")) + Send (pInst, "CALL %d TIMESTAMP %ld", pEntry->hdr.uMsgNr, pEntry->timestamp); + else if (!strcasecmp (pszCmd, "PARTNER_HANDLE")) + Send (pInst, "CALL %d PARTNER_HANDLE %s", pEntry->hdr.uMsgNr, pEntry->pszUser); + else if (!strcasecmp (pszCmd, "PARTNER_DISPNAME")) + { + NICKENTRY *pNick = BuddyList_Find (pInst->hBuddyList, pEntry->pszUser); + + if (pNick) + Send (pInst, "CALL %d PARTNER_DISPNAME %s", pEntry->hdr.uMsgNr, pNick->pszAlias); + } + else if (!strcasecmp (pszCmd, "CONF_ID")) + Send (pInst, "CALL %d CONF_ID 0", pEntry->hdr.uMsgNr); + else if (!strcasecmp (pszCmd, "TYPE")) + Send (pInst, "CALL %d TYPE %s", pEntry->hdr.uMsgNr, pEntry->iDirection==CALL_INCOMING?"INCOMING_P2P":"OUTGOING_P2P"); + else if (!strcasecmp (pszCmd, "STATUS")) + Send (pInst, "CALL %d STATUS %s", pEntry->hdr.uMsgNr, pEntry->szStatus); + else if (!strcasecmp (pszCmd, "VIDEO_STATUS")) + Send (pInst, "CALL %d VIDEO_STATUS VIDEO_NONE", pEntry->hdr.uMsgNr); + else if (!strcasecmp (pszCmd, "VIDEO_SEND_STATUS") || !strcasecmp (pszCmd, "VIDEO_RECEIVE_STATUS")) + Send (pInst, "CALL %d %s NOT_AVAILABLE", pEntry->hdr.uMsgNr, pszCmd); + else if (!strcasecmp (pszCmd, "FAILUREREASON")) + Send (pInst, "CALL %d FAILUREREASON UNKNOWN", pEntry->hdr.uMsgNr); + else if (!strcasecmp (pszCmd, "DURATION")) + Send (pInst, "CALL %d DURATION 0", pEntry->hdr.uMsgNr); + else if (!strcasecmp (pszCmd, "CONF_PARTICIPANTS_COUNT")) + Send (pInst, "CALL %d CONF_PARTICIPANTS_COUNT 0", pEntry->hdr.uMsgNr); + else + Send (pInst, "ERROR 10 Invalid property / not implemented"); + return; + } + else + if (strcasecmp(pszCmd, "SKYPEVERSION") == 0) + { + Send (pInst, "SKYPEVERSION 3.8.0.188"); // Fake + } + else + { + Send(pInst, "ERROR 7 Invalid property / not implemented"); + } + return; + } + else + if (strcasecmp(pszCmd, "SET") == 0) + { + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 7 Invalid property"); + return; + } + + if (strcasecmp(pszCmd, "USER") == 0) + { + char *pszUser; + NICKENTRY *pUser = NULL; + + if (!(pszUser = strtok(NULL, " "))) + { + Send (pInst, "ERROR 26 Invalid user handle"); + return; + } + + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 7 Invalid property"); + return; + } + + pUser = BuddyList_Find(pInst->hBuddyList, pszUser); + + if (strcasecmp(pszCmd, "BUDDYSTATUS") == 0) + { + int iStatus = -1; + + if (pszCmd = strtok(NULL, " ")) + iStatus = atoi(pszCmd); + + if (!pUser && iStatus < 2) + { + Send (pInst, "ERROR 26 Invalid user handle"); + return; + } + if (iStatus == 2 || (iStatus > 2 && !pUser) || iStatus != pUser->iBuddyStatus) + { + switch (iStatus) + { + case 1: + if (ImoSkype_DelBuddy (pInst->hInst, pUser->pszUser, + strcmp(pUser->szStatus, "OFFLINE")?"Skype":"Offline") == 1) + pUser->iBuddyStatus = iStatus; + break; + case 2: + case 3: + ImoSkype_AddBuddy (pInst->hInst, pszUser); + return; + default: + Send (pInst, "ERROR 518 Invalid status given for BUDDYSTATUS"); + return; + } + } + Send (pInst, "USER %s BUDDYSTATUS %d", pUser->pszUser, pUser->iBuddyStatus); + return; + } + else if (!pUser) + { + Send (pInst, "ERROR 26 Invalid user handle"); + return; + } + else + { + // ISAUTHORIZED + Send (pInst, "ERROR 7 Not implemented"); + } + } + else + if (strcasecmp(pszCmd, "USERSTATUS") == 0) + { + unsigned int i; + + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 28 Unknown userstatus"); + return; + } + + if (strcasecmp(pInst->myUser.szStatus, pszCmd)) + { + for (i=0; itSetMoodText = 0; + if (ImoSkype_SetStatus(pInst->hInst, m_stMap[i].pszImoStat, + pInst->myUser.pszStatusText?pInst->myUser.pszStatusText:"")>0) + strcpy (pInst->myUser.szStatus, pszCmd); + Send (pInst, "USERSTATUS %s", pInst->myUser.szStatus); + break; + } + } + if (i==sizeof(m_stMap)/sizeof(m_stMap[0])) + Send (pInst, "ERROR 28 Unknown userstatus"); + } else Send (pInst, "USERSTATUS %s", pInst->myUser.szStatus); + return; + } + else + if (strcasecmp(pszCmd, "MESSAGE") == 0 || strcasecmp(pszCmd, "CHATMESSAGE") == 0) + { + MSGENTRY *pEntry; + char *pszMessage = pszCmd; + + if (!(pszCmd = strtok(NULL, " ")) || !(pEntry = MsgQueue_Find(pInst->hMsgQueue, atol(pszCmd)))) + { + Send (pInst, "ERROR 14 Invalid message id"); + return; + } + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 10 Invalid property"); + return; + } + if (!strcasecmp (pszCmd, "SEEN")) + { + strcpy (pEntry->szStatus, "READ"); + Send (pInst, "%s %d STATUS %s", pszMessage, pEntry->hdr.uMsgNr, pEntry->szStatus); + } + else + Send (pInst, "ERROR 10 Invalid property / not implemented"); + return; + } + else + if (strcasecmp(pszCmd, "CALL") == 0) + { + CALLENTRY *pEntry; + + if (!(pszCmd = strtok(NULL, " ")) || !(pEntry = CallQueue_Find(pInst->hCallQueue, atol(pszCmd)))) + { + Send (pInst, "ERROR 11 Invalid call id"); + return; + } + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 10 Invalid property"); + return; + } + if (!strcasecmp (pszCmd, "STATUS")) + { + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 21 Unknown/disallowed call prop"); + return; + } + strcpy (pEntry->szStatus, pszCmd); + Send (pInst, "CALL %d STATUS %s", pEntry->hdr.uMsgNr, pEntry->szStatus); + + // {RINGING, INPROGRESS, ONHOLD, FINISHED} + if (pInst->iFlags & IMO2S_FLAG_ALLOWINTERACT) + { + if (strcasecmp (pEntry->szStatus, "INPROGRESS") == 0 && pEntry->iDirection == CALL_INCOMING) + { + StartCallSWF (pInst, pEntry); + } + else + if (strcasecmp (pEntry->szStatus, "FINISHED") == 0 && pEntry->hCallWnd) + { + if (*pEntry->szCallFile) + { + unlink(pEntry->szCallFile); + *pEntry->szCallFile=0; + } +#ifdef WIN32 + if (pEntry->hCallWnd) + { + W32Browser_CloseWindow (pEntry->hCallWnd); + pEntry->hCallWnd = NULL; + } +#endif + + // On incoming call, hang up + } + } + + // Currently we don't support calls, just hang up + /* FIXME: Hangup! */ + return; + } + return; + } + else + if (strcasecmp(pszCmd, "PROFILE") == 0) + { + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 10 Invalid property"); + return; + } + + if (!strcasecmp (pszCmd, "MOOD_TEXT")) + { + if (!pInst->myUser.pszStatusText || + strcasecmp(pInst->myUser.pszStatusText, pszCmd+10)) + { + time_t t; + // Delay setting of MOOD_TEXT, as next command in chain may change online status + // and we want to prevent double calls + // The polling-thread will take care of this event + pInst->tSetMoodText = time(&t)+15; + if (pInst->myUser.pszStatusText) free (pInst->myUser.pszStatusText); + pInst->myUser.pszStatusText = strdup (pszCmd+10); + } + } + else + { + Send (pInst, "ERROR 552 Invalid property"); + return; + } + } + else + { + Send (pInst, "ERROR 7 Invalid property"); + return; + } + } + else + if (strcasecmp(pszCmd, "MESSAGE") == 0 || strcasecmp(pszCmd, "CHATMESSAGE") == 0) + { + NICKENTRY *pUser; + unsigned int uMsgId; + MSGENTRY *pMsg; + + if (!(pszCmd = strtok(NULL, " ")) || !(pUser = BuddyList_Find(pInst->hBuddyList, pszCmd))) + { + Send (pInst, "ERROR 26 Invalid user handle"); + return; + } + pszCmd+=strlen(pszCmd)+1; + if (!*pszCmd) + { + Send (pInst, "ERROR 43 Cannot send empty message"); + return; + } + if (!(pMsg = MsgQueue_AddSent (pInst->hMsgQueue, pUser->pszUser, pUser->pszAlias, pszCmd, &uMsgId))) + { + Send (pInst, "ERROR 9901 Internal error"); + return; + } + if (IMO_API_VERSION == 0) Send (pInst, "MESSAGE %d STATUS SENDING", uMsgId); + if (ImoSkype_SendMessage(pInst->hInst, pUser->pszUser, pszCmd, &pMsg->uRqId)>0) + { + if (IMO_API_VERSION == 0) strcpy (pMsg->szStatus, "SENT"); + } + else + { + strcpy (pMsg->szStatus, "FAILED"); + strncpy (pMsg->szFailure, ImoSkype_GetLastError(pInst->hInst), sizeof(pMsg->szFailure)); + } + Send (pInst, "MESSAGE %d STATUS %s", uMsgId, pMsg->szStatus); + } + else + if (strcasecmp(pszCmd, "CHAT") == 0) + { + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 536 CREATE: no object or type given"); + return; + } + if (strcasecmp(pszCmd, "CREATE") == 0) + { + char szName[128]; // No name given here, so make up a name + time_t t; + + time(&t); + sprintf (szName, "%d", t); + if (ImoSkype_CreateSharedGroup(pInst->hInst, szName)<1) return; + + // Now buddy_added should be called which gives us the ID of the new groupchat + // where we can then add members to. However this is not known here yet, so feed + // list with users to add to instance and let it be processed by buddy_added handler + if (pszCmd = strtok(NULL, "\n")) + { + if (pInst->pszBuddiesToAdd) free(pInst->pszBuddiesToAdd); + pInst->pszBuddiesToAdd = strdup(pszCmd); + } + } + else + Send (pInst, "ERROR 503 CHAT: Invalid or unknown action"); + return; + } + else + if (strcasecmp(pszCmd, "CALL") == 0) + { + NICKENTRY *pUser; + + if (!(pszCmd = strtok(NULL, " ")) || !(pUser = BuddyList_Find(pInst->hBuddyList, pszCmd))) + { + Send (pInst, "ERROR 26 Invalid user handle"); + return; + } + + ImoSkype_StartVoiceCall (pInst->hInst, pUser->pszUser); + return; + } + else + if (strcasecmp(pszCmd, "OPEN") == 0) + { + return; + } + else + if (strcasecmp(pszCmd, "CREATE") == 0) + { + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 536 CREATE: no object or type given"); + return; + } + if (strcasecmp(pszCmd, "APPLICATION") == 0) + { + if (!(pszCmd = strtok(NULL, " ")) || strcasecmp(pszCmd, "libpurple_typing")) + Send (pInst, "ERROR 540 CREATE APPLICATION: Missing or invalid name"); + else + Send (pInst, "CREATE APPLICATION libpurple_typing"); + } + else + Send (pInst, "ERROR 537 CREATE: Unknown object type given"); + return; + } + else + if (strcasecmp(pszCmd, "DELETE") == 0) + { + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 538 DELETE: no object or type given"); + return; + } + if (strcasecmp(pszCmd, "APPLICATION") == 0) + { + if (!(pszCmd = strtok(NULL, " ")) || strcasecmp(pszCmd, "libpurple_typing")) + Send (pInst, "ERROR 542 DELETE APPLICATION: Missing or invalid application name"); + else + Send (pInst, "DELETE APPLICATION libpurple_typing"); + } + else + Send (pInst, "ERROR 539 DELETE: Unknown object type given"); + return; + } + else + if (strcasecmp(pszCmd, "ALTER") == 0) + { + if (!(pszCmd = strtok(NULL, " "))) + Send (pInst, "ERROR 526 ALTER: no object type given"); + else + { + if (strcasecmp(pszCmd, "APPLICATION") == 0) + { + if (!(pszCmd = strtok(NULL, " ")) || strcasecmp(pszCmd, "libpurple_typing") || + !(pszCmd = strtok(NULL, " "))) + Send (pInst, "ERROR 545 ALTER: missing or invalid action"); + else + { + NICKENTRY *pUser; + + if (strcasecmp (pszCmd, "CONNECT") == 0) + { + if (!(pszCmd = strtok(NULL, " ")) || !(pUser = BuddyList_Find(pInst->hBuddyList, pszCmd))) + Send (pInst, "ERROR 547 ALTER APPLICATION CONNECT: Invalid user handle"); + else + { + Send (pInst, "ALTER APPLICATION libpurple_typing CONNECT %s", pszCmd); + Send (pInst, "APPLICATION CONNECTING %s", pszCmd); + Send (pInst, "APPLICATION libpurple_typing STREAMS %s:1", pszCmd); + // FIXME: Shouldn't we enumerate all STREAMS here? dunno... + } + } else + if (strcasecmp (pszCmd, "DATAGRAM") == 0) + { + char *pSep; + + if (!(pszCmd = strtok(NULL, " ")) || !(pSep = strchr(pszCmd, ':'))) + Send (pInst, "ERROR 551 ALTER APPLICATION DATAGRAM: Missing or invalid stream identifier"); + else + { + *pSep=0; + if (!(pUser = BuddyList_Find(pInst->hBuddyList, pszCmd))) + Send (pInst, "ERROR 551 ALTER APPLICATION DATAGRAM: Missing or invalid stream identifier"); + else + { + *pSep=':'; + if (!(pszCmd = strtok(NULL, " "))) + Send (pInst, "ERROR 541 APPLICATION: Operation failed"); + else + { + if (!strcmp (pszCmd, "PURPLE_TYPING")) + ImoSkype_Typing (pInst->hInst, pUser->pszUser, "typing"); + else if (!strcmp (pszCmd, "PURPLE_TYPED")) + ImoSkype_Typing (pInst->hInst, pUser->pszUser, "typed"); + else if (!strcmp (pszCmd, "PURPLE_NOT_TYPING")) + ImoSkype_Typing (pInst->hInst, pUser->pszUser, "not_typing"); + } + } + } + } + } + + } + else + if (strcasecmp(pszCmd, "CHAT") == 0) + { + char *pszChat; + NICKENTRY *pChat; + + if (!(pszChat = strtok(NULL, " ")) || + !(pChat=BuddyList_Find(pInst->hBuddyList, pszChat))) + { + Send (pInst, "ERROR 14 Invalid message id"); + return; + } + + + if (!(pszCmd = strtok(NULL, " "))) + { + Send (pInst, "ERROR 503 CHAT: Invalid or unknown action"); + return; + } + + if (strcasecmp (pszCmd, "ADDMEMBERS") == 0) + { + while(pszCmd = strtok(NULL, ", ")) + { + ImoSkype_GroupInvite (pInst->hInst, pChat->pszUser, pszCmd); + } + } + else if (strcasecmp (pszCmd, "KICK") == 0) + { + while(pszCmd = strtok(NULL, ", ")) + { + ImoSkype_GroupKick (pInst->hInst, pChat->pszUser, pszCmd); + } + } + else if (strcasecmp (pszCmd, "SETTOPIC") == 0) + { + if(pszCmd = strtok(NULL, "")) + { + ImoSkype_GroupTopic (pInst->hInst, pChat->pszUser, pszCmd); + } + } + else if (strcasecmp (pszCmd, "LEAVE") == 0) + { + if(pszCmd = strtok(NULL, "")) + { + ImoSkype_GroupLeave (pInst->hInst, pChat->pszUser); + } + } + else Send (pInst, "ERROR 503 CHAT: Invalid or unknown action"); + } else + Send (pInst, "ERROR 527 ALTER: unknown object type given"); + } + return; + } + else + if (strcasecmp(pszCmd, "NAME") == 0) + { + if (pszCmd = strtok(NULL, " ")) + { + if (pInst->pszClientName) free(pInst->pszClientName); + pInst->pszClientName = strdup(pszCmd); + } + Send (pInst, "OK"); + return; + } + else + { + Send (pInst, "ERROR 2 Not Implemented"); + } +} + +// ----------------------------------------------------------------------------- + -- cgit v1.2.3