From f04d64869f3b1de54fb343f28f955584780001b8 Mon Sep 17 00:00:00 2001 From: mataes2007 Date: Sat, 26 Nov 2011 15:41:10 +0000 Subject: Project folders rename part 3 git-svn-id: http://miranda-plugins.googlecode.com/svn/trunk@215 e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb --- Skype/skypeapi.c | 1876 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1876 insertions(+) create mode 100644 Skype/skypeapi.c (limited to 'Skype/skypeapi.c') diff --git a/Skype/skypeapi.c b/Skype/skypeapi.c new file mode 100644 index 0000000..0e15a49 --- /dev/null +++ b/Skype/skypeapi.c @@ -0,0 +1,1876 @@ +/* + * SkypeAPI - All more or less important functions that deal with Skype + */ + +#include "skype.h" +#include "skypeapi.h" +#include "utf8.h" +#include "debug.h" +#include "contacts.h" +#include "skypeproxy.h" +#include "pthread.h" +#include "gchat.h" +#pragma warning (push) +#pragma warning (disable: 4100) // unreferenced formal parameter +#include "../../include/m_utils.h" +#include "../../include/m_langpack.h" +#pragma warning (push) +#include "m_toptoolbar.h" + +#pragma warning (disable: 4706) // assignment within conditional expression + +// Imported Globals +extern HWND hSkypeWnd, g_hWnd; +extern BOOL SkypeInitialized, UseSockets, MirandaShuttingDown, bIsImoproxy; +extern int SkypeStatus, receivers; +extern HANDLE SkypeReady, SkypeMsgReceived, httbButton; +extern UINT ControlAPIAttach, ControlAPIDiscover; +extern LONG AttachStatus; +extern HINSTANCE hInst; +extern PLUGININFO pluginInfo; +extern HANDLE hProtocolAvatarsFolder, hHookSkypeApiRcv; +extern char DefaultAvatarsFolder[MAX_PATH+1], *pszProxyCallout, protocol, g_szProtoName[]; + +// -> Skype Message Queue functions // + +struct MsgQueue *SkypeMsgs=NULL; + +status_map status_codes[] = { + {ID_STATUS_AWAY, "AWAY"}, + {ID_STATUS_NA, "NA"}, + {ID_STATUS_DND, "DND"}, + {ID_STATUS_ONLINE, "ONLINE"}, + {ID_STATUS_FREECHAT, "SKYPEME"}, // Unfortunately Skype API tells us userstatus ONLINE, if we are free for chat + {ID_STATUS_OFFLINE, "OFFLINE"}, + {ID_STATUS_INVISIBLE, "INVISIBLE"}, + {ID_STATUS_CONNECTING, "CONNECTING"}, + {0, NULL} +}; + +//status_map + + +CRITICAL_SECTION MsgQueueMutex, ConnectMutex, SendMutex; +BOOL rcvThreadRunning=FALSE; BOOL isConnecting = FALSE; +SOCKET ClientSocket=INVALID_SOCKET; + +static char *m_szSendBuf = NULL; +static DWORD m_iBufSize = 0; + + +static int _ConnectToSkypeAPI(char *path, BOOL bStart); + + +/* SkypeReceivedMessage + * + * Purpose: Hook to be called when a message is received, if some caller is + * using our internal I/O services. + * Params : wParam - Not used + * lParam - COPYDATASTRUCT like in WM_COPYDATA + * Returns: Result from SendMessage + */ +INT_PTR SkypeReceivedAPIMessage(WPARAM wParam, LPARAM lParam) { + return SendMessage(g_hWnd, WM_COPYDATA, (WPARAM)hSkypeWnd, lParam); +} + +/* + * Skype via Socket --> Skype2Socket connection + */ + +void rcvThread(char *dummy) { + unsigned int length; + char *buf; + COPYDATASTRUCT CopyData; + int rcv; + + if (!UseSockets) return; + rcvThreadRunning=TRUE; + for ( ;; ) { + if (ClientSocket==INVALID_SOCKET) { + rcvThreadRunning=FALSE; + return; + } + LOG(("rcvThread Receiving from socket..")); + if ((rcv=recv(ClientSocket, (char *)&length, sizeof(length), 0))==SOCKET_ERROR || rcv==0) { + rcvThreadRunning=FALSE; + if (rcv==SOCKET_ERROR) {LOG(("rcvThread Socket error"));} + else {LOG(("rcvThread lost connection, graceful shutdown"));} + return; + } + LOG(("rcvThread Received length, recieving message..")); + buf=(char *)calloc(1, length+1); + if ((rcv = recv(ClientSocket, buf, length, 0))==SOCKET_ERROR || rcv==0) { + rcvThreadRunning=FALSE; + if (rcv==SOCKET_ERROR) {LOG(("rcvThread Socket error"));} + else {LOG(("rcvThread lost connection, graceful shutdown"));} + free(buf); + return; + } + LOG(("Received message: %s", buf)); + + CopyData.dwData=0; + CopyData.lpData=buf; + CopyData.cbData=(DWORD)strlen(buf)+1; + if (!SendMessage(g_hWnd, WM_COPYDATA, (WPARAM)hSkypeWnd, (LPARAM)&CopyData)) + { + LOG(("SendMessage failed: %08X", GetLastError())); + } + free(buf); + } +} + +/** + * Purpose: Retrieves class name from window + * + * Note: This is some sort of hack to return static local variable, + * but it works :) + */ +const TCHAR* getClassName(HWND wnd) +{ + static TCHAR className[256]; + + *className=0; + GetClassName(wnd, &className[0], sizeof(className)/sizeof(className[0])); + return className; +} + +/** + * Purpose: Finds a window + * + * Note: This function relies on Skype window placement. + * It should work for Skype 3.x + */ +HWND findWindow(HWND parent, const TCHAR* childClassName) +{ + // Get child window + // This window is not combo box or edit box + HWND wnd = GetWindow(parent, GW_CHILD); + while(wnd != NULL && _tcscmp(getClassName(wnd), childClassName) != 0) + wnd = GetWindow(wnd, GW_HWNDNEXT); + + return wnd; +} + +static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) +{ + DWORD dwPID; + const TCHAR *lpszClassName; + + + GetWindowThreadProcessId(hWnd,&dwPID); + if (lParam != 0 && dwPID != (DWORD)lParam) return TRUE; + lpszClassName = getClassName(hWnd); + if(_tcscmp(lpszClassName, _T("tSkMainForm.UnicodeClass")) == 0 || + _tcscmp(lpszClassName, _T("TLoginForm.UnicodeClass")) == 0) + { + HWND loginControl = GetWindow(hWnd, GW_CHILD); + + LOG(("setUserNamePasswordThread: Skype window found!")); + + // Sleep for some time, while Skype is loading + // It loads slowly :( + //Sleep(5000); + LOG (("TLoginControl = %S", getClassName(loginControl))); + + // Check for login control + if(_tcscmp(getClassName(loginControl), _T("TLoginControl")) == 0) + { + // Find user name window + HWND userName = findWindow(loginControl, _T("TNavigableTntComboBox.UnicodeClass")); + HWND password = findWindow(loginControl, _T("TNavigableTntEdit.UnicodeClass")); + + LOG (("userName=%08X; password=%08X", userName, password)); + if (userName && password) + { + // Set user name and password + DBVARIANT dbv; + + if(!DBGetContactSettingWString(NULL,SKYPE_PROTONAME,"LoginUserName",&dbv)) + { + SendMessageW(userName, WM_SETTEXT, 0, (LPARAM)dbv.pwszVal); + DBFreeVariant(&dbv); + } + + if(!DBGetContactSettingWString(NULL,SKYPE_PROTONAME,"LoginPassword",&dbv)) + { + SendMessageW(password, WM_SETTEXT, 0, (LPARAM)dbv.pwszVal); + DBFreeVariant(&dbv); + SendMessageW(password, WM_CHAR, 13, 0); + } + + + SendMessageW(hWnd, + WM_COMMAND, + 0x4a8, // sign-in button; WARNING: This ID can change during newer Skype versions + (LPARAM)findWindow(loginControl, _T("TTntButton.UnicodeClass"))); + } + return FALSE; + } + + } + return TRUE; +} + +DWORD WINAPI setUserNamePasswordThread(LPVOID lpDummy) +{ + DWORD dwPid = (DWORD)lpDummy; + HANDLE mutex = CreateMutex(NULL, TRUE, _T("setUserNamePasswordMutex")); + + // Check double entrance + if(GetLastError() == ERROR_ALREADY_EXISTS) + return 0; + + WaitForSingleObject(SkypeReady, 5000); + EnumWindows (EnumWindowsProc, dwPid); + + ReleaseMutex(mutex); + CloseHandle(mutex); + return 0; +} + +/** + * Purpose: Finds Skype window and sets user name and password. + * + * Note: This function relies on Skype window placement. + * It should work for Skype 3.x + */ +void setUserNamePassword(int dwPid) +{ + DWORD threadId; + CreateThread(NULL, 0, &setUserNamePasswordThread, (LPVOID)dwPid, 0, &threadId); + + // Give time to thread + Sleep(100); +} + +/* + * Skype Messagequeue - Implemented as a linked list + */ + +/* SkypeMsgInit + * + * Purpose: Initializes the Skype Message queue and API + * Returns: 0 - Success + * -1 - Memory allocation failure + */ +int SkypeMsgInit(void) { + + if ((SkypeMsgs=(struct MsgQueue*)malloc(sizeof(struct MsgQueue)))==NULL) return -1; + SkypeMsgs->message=NULL; + SkypeMsgs->tAdded=SkypeMsgs->tReceived=0; + SkypeMsgs->next=NULL; + InitializeCriticalSection(&MsgQueueMutex); + InitializeCriticalSection(&ConnectMutex); + InitializeCriticalSection(&SendMutex); + m_szSendBuf = malloc(m_iBufSize=512); + return 0; +} + +/* SkypeMsgAdd + * + * Purpose: Add Message to linked list + * Params : msg - Message to add to queue + * Returns: 0 - Success + * -1 - Memory allocation failure + */ +int SkypeMsgAdd(char *msg) { + struct MsgQueue *ptr; + + EnterCriticalSection(&MsgQueueMutex); + ptr=SkypeMsgs; + if (!ptr) { + LeaveCriticalSection(&MsgQueueMutex); + return -1; + } + while (ptr->next!=NULL) ptr=ptr->next; + if ((ptr->next=(struct MsgQueue*)malloc(sizeof(struct MsgQueue)))==NULL) { + LeaveCriticalSection(&MsgQueueMutex); + return -1; + } + ptr=ptr->next; + ptr->message = _strdup(msg); // Don't forget to free! + ptr->tAdded = SkypeTime(NULL); + SkypeTime(&ptr->tReceived); + ptr->next=NULL; + LOG (("SkypeMsgAdd (%s) @%lu/%ld", msg, ptr->tReceived, ptr->tAdded)); + LeaveCriticalSection(&MsgQueueMutex); + return 0; +} + +/* SkypeMsgCleanup + * + * Purpose: Clean up the whole MESSagequeue - free() all + */ +void SkypeMsgCleanup(void) { + struct MsgQueue *ptr; + int i; + + LOG(("SkypeMsgCleanup Cleaning up message queue..")); + if (receivers>1) + { + LOG (("SkypeMsgCleanup Releasing %d receivers", receivers)); + for (i=0;imessage=NULL; //First message is free()d by user + while (SkypeMsgs) { + ptr=SkypeMsgs; + if (ptr->message) free(ptr->message); + SkypeMsgs=ptr->next; + free(ptr); + } + LeaveCriticalSection(&MsgQueueMutex); + LeaveCriticalSection(&ConnectMutex); + LeaveCriticalSection(&SendMutex); + DeleteCriticalSection(&MsgQueueMutex); + DeleteCriticalSection(&ConnectMutex); + DeleteCriticalSection(&SendMutex); + if (m_szSendBuf) + { + free (m_szSendBuf); + m_szSendBuf = NULL; + m_iBufSize = 0; + } + LOG(("SkypeMsgCleanup Done.")); +} + +/* SkypeMsgGet + * + * Purpose: Fetch next message from message queue + * Returns: The next message + * Warning: Don't forget to free() return value! + */ +char *SkypeMsgGet(void) { + struct MsgQueue *ptr; + char *msg; + + if (!SkypeMsgs || !((ptr=SkypeMsgs)->next)) return NULL; + EnterCriticalSection(&MsgQueueMutex); + msg=ptr->next->message; + SkypeMsgs=ptr->next; + free(ptr); + LeaveCriticalSection(&MsgQueueMutex); + return msg; +} + +// Message sending routine, for internal use by SkypeSend +static int __sendMsg(char *szMsg) { + COPYDATASTRUCT CopyData; + LRESULT SendResult; + int oldstatus; + unsigned int length=(unsigned int)strlen(szMsg); + + LOG(("> %s", szMsg)); + + // Fake PING-PONG, as PING-PONG is not supported by Skype2Socket + if ((UseSockets || bIsImoproxy) && !strcmp(szMsg, "PING")) { + CopyData.dwData=0; + CopyData.lpData="PONG"; + CopyData.cbData=5; + SendMessage(g_hWnd, WM_COPYDATA, (WPARAM)hSkypeWnd, (LPARAM)&CopyData); + return 0; + } + + if (UseSockets) { + BOOL res; + if (ClientSocket==INVALID_SOCKET) return -1; + + EnterCriticalSection(&SendMutex); + res = send(ClientSocket, (char *)&length, sizeof(length), 0)==SOCKET_ERROR + || send(ClientSocket, szMsg, length, 0)==SOCKET_ERROR; + LeaveCriticalSection(&SendMutex); + if (res) + SendResult = 0; + else + return 0; + } else { + CopyData.dwData=0; + CopyData.lpData=szMsg; + CopyData.cbData=(DWORD)strlen(szMsg)+1; + + // Internal comm channel + if (pszProxyCallout) + return CallService (pszProxyCallout, 0, (LPARAM)&CopyData); + + // If this didn't work, proceed with normal Skype API + if (!hSkypeWnd) + { + LOG(("SkypeSend: DAMN! No Skype window handle! :(")); + } + SendResult=SendMessage(hSkypeWnd, WM_COPYDATA, (WPARAM)g_hWnd, (LPARAM)&CopyData); + LOG(("SkypeSend: SendMessage returned %d", SendResult)); + } + if (!SendResult) + { + SkypeInitialized=FALSE; + AttachStatus=-1; + ResetEvent(SkypeReady); + if (g_hWnd) KillTimer (g_hWnd, 1); + if (SkypeStatus!=ID_STATUS_OFFLINE) + { + // Go offline + logoff_contacts(FALSE); + oldstatus=SkypeStatus; + InterlockedExchange((long *)&SkypeStatus, (int)ID_STATUS_OFFLINE); + ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldstatus, SkypeStatus); + } + // Reconnect to Skype + ResetEvent(SkypeReady); + pthread_create(LaunchSkypeAndSetStatusThread, (void *)ID_STATUS_ONLINE); + return -1; +// SendMessageTimeout(HWND_BROADCAST, ControlAPIDiscover, (WPARAM)g_hWnd, 0, SMTO_ABORTIFHUNG, 3000, NULL); + } + return 0; +} + +/* SkypeSend + * + * Purpose: Sends the specified message to the Skype API. + * If it fails, try to reconnect zu the Skype API + * Params: use like sprintf without first param (dest. buffer) + * Returns: 0 - Success + * -1 - Failure + */ +int SkypeSend(char *szFmt, ...) { + char *pNewBuf; + va_list ap; + size_t iLen; + + // 0.0.0.17+ - Build message-String from supplied parameter list + // so the user doesn't have to care about memory allocation any more. + // 0.0.0.47+ - No more restrictions apply to the format string. + // The temporary buffer remains allocated during the session and gets + // dynamically expanded when needed. This makes sense, as this function + // is used very often and therefore it is faster to not allocate + // memory on every send. + if (!m_szSendBuf && !(m_szSendBuf=malloc(m_iBufSize=512))) return -1; + do + { + va_start(ap, szFmt); + iLen = _vsnprintf(m_szSendBuf, m_iBufSize, szFmt, ap); + va_end(ap); + if (iLen == -1) + { + if (!(pNewBuf = (char*)realloc (m_szSendBuf, m_iBufSize*2))) + { + iLen = strlen (m_szSendBuf); + break; + } + m_szSendBuf = pNewBuf; + m_iBufSize*=2; + } + } while (iLen == -1); + + return __sendMsg(m_szSendBuf); +} + +/* SkypeRcvTime + * + * Purpose: Wait, until either the message "what" is received or maxwait-Time has passed + * or there was an error and return it + * Params : what - Wait for this string-part at the beginning of a received string + * If the first character of the string is NULL, the rest after the NULL + * character will be searched in the entire received message-string. + * You can tokenize the string by using NULL characters. + * You HAVE TO end the string with a extra \0, otherwise the tokenizer + * will run amok in memory! + * st - The message timestamp must be newer or equal to st. + * Set to 0, if you do not need this and want the first message of this + * kind in the queue. + * maxwait - Wait this time before returning, if nothing was received, + * can be INFINITE + * Returns: The received message containing "what" or a ERROR-Message or NULL if + * time is up and nothing was received + * Warning: Don't forget to free() return value! + */ +char *SkypeRcvTime(char *what, time_t st, DWORD maxwait) { + char *msg, *token=NULL; + struct MsgQueue *ptr, *ptr_; + int j; + DWORD dwWaitStat; + BOOL bChatMsg = FALSE, bIsChatMsg = FALSE; + + LOG (("SkypeRcv - Requesting answer: %s", what)); + if (what) bChatMsg = strncmp(what, "CHATMESSAGE", 11)==0; + do { + EnterCriticalSection(&MsgQueueMutex); + // First, search for the requested message. On second run, also accept an ERROR + for (j=0; j<2; j++) + { + ptr_=SkypeMsgs; + ptr=ptr_->next; + while (ptr!=NULL) { + if (what && what[0]==0) { + // Tokenizer syntax active + token=what+1; + while (*token) { + if (!strstr (ptr->message, token)) { + token=NULL; + break; + } + token+=strlen(token)+1; + } + } + + //if (j==1) {LOG(("SkypeRcv compare %s (%lu) -- %s (%lu)", ptr->message, ptr->tReceived, what, st));} + if ((st == 0 || ptr->tReceived >= st) && + (what==NULL || token || (what[0] && !strncmp(ptr->message, what, strlen(what))) || + (bIsChatMsg = (j==1 && bChatMsg && !strncmp(ptr->message, what+4, strlen(what+4)))) || + (j==1 && !strncmp(ptr->message, "ERROR", 5)))) + { + msg=ptr->message; + ptr_->next=ptr->next; + LOG(("next; + } + } + LeaveCriticalSection(&MsgQueueMutex); + InterlockedIncrement ((long *)&receivers); //receivers++; + dwWaitStat = WaitForSingleObject(SkypeMsgReceived, maxwait); + if (receivers>1) InterlockedDecrement ((long *)&receivers); // receivers--; + if (receivers>1) {LOG (("SkypeRcv: %d receivers still waiting", receivers));} + + } while(dwWaitStat == WAIT_OBJECT_0 && !MirandaShuttingDown); + InterlockedDecrement ((long *)&receivers); + LOG(("next; + while (ptr!=NULL) { + //LOG (("SkypeRcvMsg - msg: %s -- %s", ptr->message, what)); + pCurMsg = ptr->message; + bIsError = FALSE; + if (*what && !strncmp(pCurMsg, what, iLenWhat)) { + // Now we received a MESSAGE with an identifier. So this one is definitely for us + // However the status can be SENDING instead of SENT and next message with this number + // isn't using the ID anymore, so we have to save the ID as new identifier for message recognition + pCurMsg+=iLenWhat; + if ((pMsg = strchr (pCurMsg, ' ')) && (pMsg=strchr (pMsg+1, ' '))) + strncpy (msgid, pCurMsg, pMsg-pCurMsg); + else if (strncmp (pCurMsg, "ERROR", 5) == 0) bIsError = TRUE; + } + + if ((*msgid && strncmp (pCurMsg, msgid, strlen(msgid)) == 0) || + (!*what && ptr->tReceived >= st && + (strncmp(pCurMsg, "MESSAGE", 7) == 0 || strncmp(pCurMsg, "CHATMESSAGE", 11) == 0 ) + ) || bIsError || + (ptr->tReceived >= st && ptr->tReceived <=st+1 && + (bIsError=(strncmp(pCurMsg, "ERROR 26", 8)==0 || strncmp(pCurMsg, "ERROR 43", 8)==0)) + ) ) + { + bProcess = bIsError; + if (!bIsError) { + if ((pMsg = strchr (pCurMsg, ' ')) && (pMsg=strchr (pMsg+1, ' '))) { + pMsg++; + if (strncmp (pMsg, "STATUS ", 7) == 0) { + pMsg+=7; + if (strcmp (pMsg, "SENDING") == 0) { + // Remove dat shit + ptr_->next=ptr->next; + free (ptr->message); + free (ptr); + ptr=ptr_->next; + continue; + } + bProcess = (strcmp (pMsg, "SENT") == 0 || strcmp (pMsg, "QUEUED") == 0 || + strcmp (pMsg, "FAILED") == 0 || strcmp (pMsg, "IGNORED") == 0 || + strcmp (pMsg, "SENDING") == 0); + } + } + } + if (bProcess) { + msg=ptr->message; + ptr_->next=ptr->next; + LOG(("next; + + } + LeaveCriticalSection(&MsgQueueMutex); + InterlockedIncrement ((long *)&receivers); //receivers++; + dwWaitStat = WaitForSingleObject(SkypeMsgReceived, maxwait); + if (receivers>1) InterlockedDecrement ((long *)&receivers); // receivers--; + if (receivers>1) {LOG (("SkypeRcvMsg: %d receivers still waiting", receivers));} + + } while(dwWaitStat == WAIT_OBJECT_0 && !MirandaShuttingDown); + InterlockedDecrement ((long *)&receivers); + LOG((" Answer %s", str, ptr)); + return ptr; +} + +char *SkypeGetID(char *szWhat, char *szWho, char *szProperty) { + char szID[16]={0}; + static DWORD dwId = 0; + + if (protocol>=4 || bIsImoproxy) sprintf (szID, "#G%d ", dwId++); + return __SkypeGet (szID, szWhat, szWho, szProperty); +} + +char *SkypeGet(char *szWhat, char *szWho, char *szProperty) { + return __SkypeGet ("", szWhat, szWho, szProperty); +} + +#ifdef _UNICODE +WCHAR *SkypeGetW(char *szWhat, WCHAR *szWho, char *szProperty) { + char *ptszWho = (char*)make_utf8_string(szWho); + char *pRet = SkypeGet (szWhat, ptszWho, szProperty); + free (ptszWho); + if (pRet) { + WCHAR *ptr = make_unicode_string((const unsigned char*)pRet); + free (pRet); + return ptr; + } + return NULL; +} +#endif + +char *SkypeGetErr(char *szWhat, char *szWho, char *szProperty) { + char *ret = SkypeGet(szWhat, szWho, szProperty); + if (ret && !strncmp(ret, "ERROR", 5)) { + free (ret); + return NULL; + } + return ret; +} + +#ifdef _UNICODE +WCHAR *SkypeGetErrW(char *szWhat, TCHAR *szWho, char *szProperty) { + WCHAR *ret = SkypeGetW(szWhat, szWho, szProperty); + if (ret && !_tcsncmp(ret, _T("ERROR"), 5)) { + free (ret); + return NULL; + } + return ret; +} +#endif + + +/* SkypeGetProfile + * + * Issues a SET PROFILE szProperty szValue and waits until the answer is received + * Returns the answer or NULL on failure + * BEWARE: Don't forget to free() return value! + * + * For example: SkypeGetProfile("FULLNAME", "Tweety"); +*/ +char *SkypeGetProfile(char *szProperty) { + return SkypeGet ("PROFILE", "", szProperty); +} + +/* SkypeSetProfile + * + * +*/ +int SkypeSetProfile(char *szProperty, char *szValue) { + return SkypeSend("SET PROFILE %s %s", szProperty, szValue); +} + +/* SkypeMsgCollectGarbage + * + * Purpose: Runs the garbage collector on the Skype Message-Queue to throw out old + * messages which may unnecessarily eat up memory. + * Params : age - Time in seconds. Messages older than this value will be + * thrown out. + * Returns: 0 - No messages were thrown out + * >0 - n messages were thrown out + */ +int SkypeMsgCollectGarbage(time_t age) { + struct MsgQueue *ptr, *ptr_; + int i=0; + + EnterCriticalSection(&MsgQueueMutex); + ptr_=SkypeMsgs; + ptr=ptr_->next; + while (ptr!=NULL) { + if (ptr->tAdded && SkypeTime(NULL)-ptr->tAdded>age) { + LOG(("GarbageCollector throwing out message: %s", ptr->message)); + free(ptr->message); + ptr_->next=ptr->next; + free(ptr); + ptr=ptr_; + i++; + } + ptr_=ptr; + ptr=ptr_->next; + } + LeaveCriticalSection(&MsgQueueMutex); + return i; +} + + +/* SkypeCall + * + * Purpose: Give a Skype call to the given User in wParam + * or hangs up existing call + * (hangUp is moved over to SkypeCallHangup) + * Params : wParam - Handle to the User to be called + * lParam - Can be NULL + * Returns: 0 - Success + * -1 - Failure + */ +INT_PTR SkypeCall(WPARAM wParam, LPARAM lParam) { + DBVARIANT dbv; + char *msg=0; + int res; + + if (!DBGetContactSettingString((HANDLE)wParam, SKYPE_PROTONAME, "CallId", &dbv)) { + res = -1; // no direct return, because dbv needs to be freed + } else { + if (DBGetContactSettingString((HANDLE)wParam, SKYPE_PROTONAME, SKYPE_NAME, &dbv)) return -1; + msg=(char *)malloc(strlen(dbv.pszVal)+6); + strcpy(msg, "CALL "); + strcat(msg, dbv.pszVal); + res=SkypeSend(msg); + } + DBFreeVariant(&dbv); + free(msg); + return res; +} + +/* SkypeCallHangup + * + * Prupose: Hangs up the existing call to the given User + * in wParam. + * + * Params : wParam - Handle to the User to be called + * lParam - Can be NULL + * + * Returns: 0 - Success + * -1 - Failure + * + */ +INT_PTR SkypeCallHangup(WPARAM wParam, LPARAM lParam) +{ + DBVARIANT dbv; + char *msg=0; + int res = -1; + + if (!DBGetContactSettingString((HANDLE)wParam, SKYPE_PROTONAME, "CallId", &dbv)) { + msg=(char *)malloc(strlen(dbv.pszVal)+21); + sprintf(msg, "SET %s STATUS FINISHED", dbv.pszVal); + //sprintf(msg, "ALTER CALL %s HANGUP", dbv.pszVal); + res=SkypeSend(msg); +#if _DEBUG + DBDeleteContactSetting((HANDLE)wParam, SKYPE_PROTONAME, "CallId"); +#endif + //} else { + // if (DBGetContactSetting((HANDLE)wParam, SKYPE_PROTONAME, SKYPE_NAME, &dbv)) return -1; + // msg=(char *)malloc(strlen(dbv.pszVal)+6); + // strcpy(msg, "CALL "); + // strcat(msg, dbv.pszVal); + // res=SkypeSend(msg); + } + DBFreeVariant(&dbv); + free(msg); + return res; +} + +/* FixNumber + * + * Purpose: Eliminates all non-numeric chars from the given phonenumber + * Params : p - Pointer to the buffer with the number + */ +void FixNumber(char *p) { + unsigned int i; + + for (i=0;i<=strlen(p);i++) + if ((p[i]<'0' || p[i]>'9')) + if (p[i]) { + memmove(p+i, p+i+1, strlen(p+i)); + i--; + } else break; +} + + +/* DialDlgProc + * + * Purpose: Dialog procedure for the Dial-Dialog + */ +static BOOL CALLBACK DialDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + static HANDLE hContact; + static unsigned int entries=0; + BOOL TempAdded=FALSE; + char number[64], *msg, *ptr=NULL; + + switch (uMsg){ + case WM_INITDIALOG: + hContact=(HANDLE)lParam; + Utils_RestoreWindowPosition(hwndDlg, NULL, SKYPE_PROTONAME, "DIALdlg"); + TranslateDialogDefault(hwndDlg); + + if (lParam) { + DBVARIANT dbv; + BOOL bDialNow=TRUE; + + if (!DBGetContactSetting(hContact,"UserInfo","MyPhone1",&dbv)) { + int j; + char idstr[16]; + + // Multiple phone numbers, select one + bDialNow=FALSE; + DBFreeVariant(&dbv); + for(j=0;;j++) { + sprintf(idstr,"MyPhone%d",j); + if(DBGetContactSettingString(hContact,"UserInfo",idstr,&dbv)) break; + FixNumber(dbv.pszVal+1); // Leave + alone + SendDlgItemMessage(hwndDlg,IDC_NUMBER,CB_ADDSTRING,0,(LPARAM)dbv.pszVal); + DBFreeVariant(&dbv); + } + } + if (DBGetContactSettingString(hContact,SKYPE_PROTONAME,"SkypeOutNr",&dbv)) { + DBGetContactSettingString(hContact,"UserInfo","MyPhone0",&dbv); + FixNumber(dbv.pszVal+1); + } + SetDlgItemTextA(hwndDlg, IDC_NUMBER, dbv.pszVal); + DBFreeVariant(&dbv); + if (bDialNow) PostMessage(hwndDlg, WM_COMMAND, IDDIAL, 0); + } else { + DBVARIANT dbv; + char number[64]; + + for (entries=0;entriesMAX_ENTRIES) entries=MAX_ENTRIES; + for (i=entries;i>0;i--) { + sprintf(buf, "LastNumber%d", i-1); + if (!DBGetContactSettingString(NULL, SKYPE_PROTONAME, buf, &dbv)) { + sprintf(buf, "LastNumber%d", i); + DBWriteContactSettingString(NULL, SKYPE_PROTONAME, buf, dbv.pszVal); + DBFreeVariant(&dbv); + } else break; + } + DBWriteContactSettingString(NULL, SKYPE_PROTONAME, "LastNumber0", number); + } + TempAdded=TRUE; + } + if (!DBWriteContactSettingString(hContact, SKYPE_PROTONAME, "SkypeOutNr", number)) { + msg=(char *)malloc(strlen(number)+6); + strcpy(msg, "CALL "); + strcat(msg, number); + if (SkypeSend(msg) || (ptr=SkypeRcv("ERROR", 500))) { + DBDeleteContactSetting(hContact, SKYPE_PROTONAME, "SkypeOutNr"); + if (ptr) { + OUTPUTA(ptr); + free(ptr); + } + if (TempAdded) CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); + } + free(msg); + } + case IDCANCEL: + DestroyWindow(hwndDlg); + break; + } + break; + case WM_DESTROY: + Utils_SaveWindowPosition(hwndDlg, NULL, SKYPE_PROTONAME, "DIALdlg"); + if (httbButton) CallService(MS_TTB_SETBUTTONSTATE, (WPARAM)httbButton, TTBST_RELEASED); + break; + } + return FALSE; +} + +/* CallstatDlgProc + * + * Purpose: Dialog procedure for the CallStatus Dialog + */ +static BOOL CALLBACK CallstatDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + static int selected; + static DBVARIANT dbv, dbv2={0}; + + switch (uMsg){ + case WM_INITDIALOG: + { + HANDLE hContact; + char *szProto; + + if (!DBGetContactSettingString((HANDLE)lParam, SKYPE_PROTONAME, "CallId", &dbv)) { + + // Check, if another call is in progress + 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) && hContact!=(HANDLE)lParam && + DBGetContactSettingByte(hContact, SKYPE_PROTONAME, "ChatRoom", 0) == 0 && + !DBGetContactSettingString(hContact, SKYPE_PROTONAME, "CallId", &dbv2)) + { + if (DBGetContactSettingByte(hContact, SKYPE_PROTONAME, "OnHold", 0)) { + DBFreeVariant(&dbv2); + continue; + } else break; + } + } + + if (dbv2.pszVal) + { + char buf[256], buf2[256]; + char *szOtherCaller=(char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME,(WPARAM)hContact,0); + + Utils_RestoreWindowPosition(hwndDlg, NULL, SKYPE_PROTONAME, "CALLSTATdlg"); + TranslateDialogDefault(hwndDlg); + SendMessage(hwndDlg, WM_COMMAND, IDC_JOIN, 0); + + GetWindowTextA(hwndDlg, buf, sizeof(buf)); + _snprintf(buf2, sizeof(buf), buf, CallService(MS_CLIST_GETCONTACTDISPLAYNAME,(WPARAM)lParam,0)); + SetWindowTextA(hwndDlg, buf2); + + GetDlgItemTextA(hwndDlg, IDC_JOIN, buf, sizeof(buf)); + _snprintf(buf2, sizeof(buf), buf, szOtherCaller); + SetDlgItemTextA(hwndDlg, IDC_JOIN, buf2); + + GetDlgItemTextA(hwndDlg, IDC_HOLD, buf, sizeof(buf)); + _snprintf(buf2, sizeof(buf), buf, szOtherCaller); + SetDlgItemTextA(hwndDlg, IDC_HOLD, buf2); + + return TRUE; + } + + // No other call in progress, no need for this Dlg., just answer the call + SkypeSend("SET %s STATUS INPROGRESS", dbv.pszVal); + testfor ("ERROR", 200); + DBFreeVariant(&dbv); + } + DestroyWindow(hwndDlg); + break; + } + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_JOIN: + case IDC_HOLD: + case IDC_HANGUP: + CheckRadioButton(hwndDlg, IDC_JOIN, IDC_HANGUP, (selected=LOWORD(wParam))); + break; + case IDOK: + { + char *szIdCall2; + + switch (selected) { + case IDC_JOIN: + if (szIdCall2=strchr(dbv2.pszVal, ' ')) + SkypeSend("SET %s JOIN_CONFERENCE%s", dbv.pszVal, szIdCall2); + break; + case IDC_HOLD: + SkypeSend("SET %s STATUS ONHOLD", dbv2.pszVal); + SkypeSend("SET %s STATUS INPROGRESS", dbv.pszVal); + break; + case IDC_HANGUP: + SkypeSend("SET %s STATUS FINISHED", dbv.pszVal); + break; + } + + DBFreeVariant(&dbv); + DBFreeVariant(&dbv2); + DestroyWindow(hwndDlg); + break; + } + } + break; + case WM_DESTROY: + Utils_SaveWindowPosition(hwndDlg, NULL, SKYPE_PROTONAME, "CALLSTATdlg"); + break; + } + return FALSE; +} + + +/* SkypeOutCallErrorCheck + * + * Purpose: Checks, if an error has occured after call and + * if so, hangs up the call + * This procedure is a seperate thread to not block the core + * while waiting for "ERROR" + * Params : szCallId - ID of the call + */ +void SkypeOutCallErrorCheck(char *szCallId) { + if (testfor("ERROR", 500)) EndCallThread(szCallId); +} + +/* SkypeOutCall + * + * Purpose: Give a SkypeOut call to the given User in wParam + * or hangs up existing call + * The user's record is searched for Phone-number entries. + * If there is more than 1 entry, the Dial-Dialog is shown + * Params : wParam - Handle to the User to be called + * If NULL, the dial-dialog is shown + * Returns: 0 - Success + * -1 - Failure + */ +INT_PTR SkypeOutCall(WPARAM wParam, LPARAM lParam) { + DBVARIANT dbv; + int res = -1; + + if (wParam && !DBGetContactSettingString((HANDLE)wParam, SKYPE_PROTONAME, "CallId", &dbv)) { + res=SkypeSend("SET %s STATUS FINISHED", dbv.pszVal); + pthread_create(( pThreadFunc )SkypeOutCallErrorCheck, _strdup(dbv.pszVal)); + DBFreeVariant(&dbv); + } else if (!CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_DIAL), NULL, DialDlgProc, (LPARAM)wParam)) return -1; + return res; +} + +/* SkypeHoldCall + * + * Purpose: Put the call to the User given in wParam on Hold or Resumes it + * Params : wParam - Handle to the User + * Returns: 0 - Success + * -1 - Failure + */ +INT_PTR SkypeHoldCall(WPARAM wParam, LPARAM lParam) { + DBVARIANT dbv; + int retval; + + LOG(("SkypeHoldCall started")); + if (!wParam || DBGetContactSettingString((HANDLE)wParam, SKYPE_PROTONAME, "CallId", &dbv)) + return -1; + retval = SkypeSend ("SET %s STATUS %s", dbv.pszVal, + DBGetContactSettingByte((HANDLE)wParam, SKYPE_PROTONAME, "OnHold", 0)?"INPROGRESS":"ONHOLD"); + DBFreeVariant(&dbv); + return retval; +} + +/* SkypeAnswerCall + * + * Purpose: Answer a Skype-call when a user double-clicks on + * The incoming-call-Symbol. Works for both, Skype and SkypeOut-calls + * Params : wParam - Not used + * lParam - CLISTEVENT* + * Returns: 0 - Success + * -1 - Failure + */ +INT_PTR SkypeAnswerCall(WPARAM wParam, LPARAM lParam) { + + LOG(("SkypeAnswerCall started")); + CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_CALLSTAT), NULL, CallstatDlgProc, (LPARAM)((CLISTEVENT*)lParam)->hContact); + return 0; +} +/* SkypeSetNick + * + * Purpose: Set Full Name in profile + * Params : wParam=0 + * lParam=(LPARAM)(const char*)Nick text + * Returns: 0 - Success + * -1 - Failure + */ +INT_PTR SkypeSetNick(WPARAM wParam, LPARAM lParam) { + int retval = -1; + char *Nick = NULL; + + if (wParam & SMNN_UNICODE) + { + DBWriteContactSettingWString(0, SKYPE_PROTONAME, "Nick", (WCHAR*)lParam); + if (AttachStatus == SKYPECONTROLAPI_ATTACH_SUCCESS && + !(Nick = (char*)make_utf8_string((WCHAR*)lParam))) return -1; + } + else + { + DBWriteContactSettingString(0, SKYPE_PROTONAME, "Nick", (char*)lParam); + if(AttachStatus == SKYPECONTROLAPI_ATTACH_SUCCESS && + utf8_encode((const char *)lParam, &Nick) == -1 ) return -1; + } + if(AttachStatus == SKYPECONTROLAPI_ATTACH_SUCCESS) + retval = SkypeSend("SET PROFILE FULLNAME %s", Nick); + if (Nick) free (Nick); + + return retval; + +} +/* SkypeSetAwayMessage + * + * Purpose: Set Mood message in profile + * Params : wParam=status mode + * lParam=(LPARAM)(const char*)message text + * Returns: 0 - Success + * -1 - Failure + */ +INT_PTR SkypeSetAwayMessage(WPARAM wParam, LPARAM lParam) { + int retval = -1; + char *Mood = NULL; + + if (!lParam) lParam=(LPARAM)""; + if(utf8_encode((const char *)lParam, &Mood) == -1 ) return -1; + DBWriteContactSettingString(NULL, SKYPE_PROTONAME, "MoodText", (const char *)lParam); + + if(AttachStatus == SKYPECONTROLAPI_ATTACH_SUCCESS) + retval = SkypeSend("SET PROFILE MOOD_TEXT %s", Mood); + free (Mood); + + return retval; +} +INT_PTR SkypeSetAwayMessageW(WPARAM wParam, LPARAM lParam) { + int retval = -1; + char *Mood = NULL; + + if (!lParam) lParam=(LPARAM)""; + if (!(Mood = (char*)make_utf8_string((WCHAR*)lParam))) return -1; + DBWriteContactSettingWString(NULL, SKYPE_PROTONAME, "MoodText", (WCHAR*)lParam); + + if(AttachStatus == SKYPECONTROLAPI_ATTACH_SUCCESS) + retval = SkypeSend("SET PROFILE MOOD_TEXT %s", Mood); + free (Mood); + + return retval; +} + +/* SkypeSetAvatar + * + * Purpose: Set user avatar in profile + * Params : wParam=0 + * lParam=(LPARAM)(const char*)filename + * Returns: 0 - Success + * -1 - Failure + */ +INT_PTR SkypeSetAvatar(WPARAM wParam, LPARAM lParam) { + char *filename = (char *) lParam, *ext; + char AvatarFile[MAX_PATH+1], OldAvatarFile[1024]; + char *ptr = NULL; + int ret; + char command[500]; + DBVARIANT dbv = {0}; + BOOL hasOldAvatar = (DBGetContactSettingString(NULL, SKYPE_PROTONAME, "AvatarFile", &dbv) == 0 && dbv.type == DBVT_ASCIIZ); + size_t len; + + if (AttachStatus != SKYPECONTROLAPI_ATTACH_SUCCESS) + return -3; + + if (filename == NULL) + return -1; + len = strlen(filename); + if (len < 4) + return -1; + + ext = &filename[len-4]; + if (_stricmp(ext, ".jpg")==0 || _stricmp(ext-1, ".jpeg")==0) + ext = "jpg"; + else if (_stricmp(ext, ".png")==0) + ext = "png"; + else + return -2; + + FoldersGetCustomPath(hProtocolAvatarsFolder, AvatarFile, sizeof(AvatarFile), DefaultAvatarsFolder); + mir_snprintf(AvatarFile, sizeof(AvatarFile), "%s\\%s avatar.%s", AvatarFile, SKYPE_PROTONAME, ext); + + // Backup old file + if (hasOldAvatar) + { + strncpy(OldAvatarFile, dbv.pszVal, sizeof(OldAvatarFile)-4); + OldAvatarFile[sizeof(OldAvatarFile)-5] = '\0'; + strcat(OldAvatarFile, "_old"); + DeleteFileA(OldAvatarFile); + if (!MoveFileA(dbv.pszVal, OldAvatarFile)) + { + DBFreeVariant(&dbv); + return -3; + } + } + + // Copy new file + if (!CopyFileA(filename, AvatarFile, FALSE)) + { + if (hasOldAvatar) + { + MoveFileA(OldAvatarFile, dbv.pszVal); + DBFreeVariant(&dbv); + } + return -3; + } + + // Try to set with skype + mir_snprintf(command, sizeof(command), "SET AVATAR 1 %s", AvatarFile); + if (SkypeSend(command) || (ptr = SkypeRcv(command+4, INFINITE)) == NULL || !strncmp(ptr, "ERROR", 5)) + { + DeleteFileA(AvatarFile); + + if (hasOldAvatar) + MoveFileA(OldAvatarFile, dbv.pszVal); + + ret = -4; + } + else + { + if (hasOldAvatar) + DeleteFileA(OldAvatarFile); + + DBWriteContactSettingString(NULL, SKYPE_PROTONAME, "AvatarFile", AvatarFile); + + ret = 0; + } + + if (ptr != NULL) + free(ptr); + + if (hasOldAvatar) + DBFreeVariant(&dbv); + + return ret; +} + + +/* SkypeSendFile + * + * Purpose: Opens the Skype-dialog to send a file + * Params : wParam - Handle to the User + * lParam - Not used + * Returns: 0 - Success + * -1 - Failure + */ +INT_PTR SkypeSendFile(WPARAM wParam, LPARAM lParam) { + DBVARIANT dbv; + int retval; + + if (!wParam || DBGetContactSettingString((HANDLE)wParam, SKYPE_PROTONAME, SKYPE_NAME, &dbv)) + return -1; + retval=SkypeSend("OPEN FILETRANSFER %s", dbv.pszVal); + DBFreeVariant(&dbv); + return retval; +} + +/* SkypeChatCreate + * + * Purpose: Creates a groupchat with the user + * Params : wParam - Handle to the User + * lParam - Not used + * Returns: 0 - Success + * -1 - Failure + */ +INT_PTR SkypeChatCreate(WPARAM wParam, LPARAM lParam) { + DBVARIANT dbv; + HANDLE hContact=(HANDLE)wParam; + char *ptr, *ptr2; + + if (!hContact || DBGetContactSettingString(hContact, SKYPE_PROTONAME, SKYPE_NAME, &dbv)) + return -1; + // Flush old messages + while (testfor("\0CHAT \0 STATUS \0", 0)); + if (SkypeSend("CHAT CREATE %s", dbv.pszVal) || !(ptr=SkypeRcv ("\0CHAT \0 STATUS \0", INFINITE))) + { + DBFreeVariant(&dbv); + return -1; + } + DBFreeVariant(&dbv); + if (ptr2=strstr (ptr, "STATUS")) { + *(ptr2-1)=0; + ChatStart (ptr+5, FALSE); + } + free(ptr); + return 0; +} + +/* SkypeAdduserDlg + * + * Purpose: Show Skype's Add user Dialog + */ +INT_PTR SkypeAdduserDlg(WPARAM wParam, LPARAM lParam) { + SkypeSend("OPEN ADDAFRIEND"); + return 0; +} + +/* SkypeFlush + * + * Purpose: Flush the Skype Message-List + */ +void SkypeFlush(void) { + char *ptr; + + while ((ptr=SkypeRcv(NULL, 0))!=NULL) free(ptr); +} + +/* SkypeStatusToMiranda + * + * Purpose: Converts the specified Skype-Status mode to the corresponding Miranda-Status mode + * Params : s - Skype Status + * Returns: The correct Status + * 0 - Nothing found + */ +int SkypeStatusToMiranda(char *s) { + int i; + if (!strcmp("SKYPEOUT", s)) return DBGetContactSettingDword(NULL, SKYPE_PROTONAME, "SkypeOutStatusMode", ID_STATUS_ONTHEPHONE); + for(i=0; status_codes[i].szStat; i++) + if (!strcmp(status_codes[i].szStat, s)) + return status_codes[i].id; + return 0; +} + +/* MirandaStatusToSkype + * + * Purpose: Converts the specified Miranda-Status mode to the corresponding Skype-Status mode + * Params : id - Miranda Status + * Returns: The correct Status + * NULL - Nothing found + */ +char *MirandaStatusToSkype(int id) { + int i; + if (DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "NoSkype3Stats", 0)) { + switch (id) + { + case ID_STATUS_NA: return "AWAY"; + case ID_STATUS_FREECHAT: return "ONLINE"; + } + } + for(i=0; status_codes[i].szStat; i++) + if (status_codes[i].id==id) + return status_codes[i].szStat; + return NULL; +} + +/* GetSkypeErrorMsg + * + * Purpose: Get a human-readable Error-Message for the supplied Skype Error-Message + * Params : str - Skype Error-Message string + * Returns: Human-readable Error Message or NULL, if nothing was found + * Warning: Don't forget to free() return value + */ +char *GetSkypeErrorMsg(char *str) { + char *pos, *reason, *msg; + + LOG (("GetSkypeErrorMsg received error: %s", str)); + if (!strncmp(str, "ERROR", 5)) { + reason=_strdup(str); + return reason; + } + if ((pos=strstr(str, "FAILURE")) ) { + switch(atoi(pos+14)) { + case MISC_ERROR: msg="Misc. Error"; break; + case USER_NOT_FOUND: msg="User does not exist, check username"; break; + case USER_NOT_ONLINE: msg="Trying to send IM to an user, who is not online"; break; + case USER_BLOCKED: msg="IM blocked by recipient"; break; + case TYPE_UNSUPPORTED: msg="Type unsupported"; break; + case SENDER_NOT_FRIEND: msg="Sending IM message to user, who has not added you to friendslist and has chosen 'only people in my friendslist can start IM'"; break; + case SENDER_NOT_AUTHORIZED: msg="Sending IM message to user, who has not authorized you and has chosen 'only people whom I have authorized can start IM'"; break; + default: msg="Unknown error"; + } + reason=(char *)malloc(strlen(pos)+strlen(msg)+3); + sprintf (reason, "%s: %s", pos, msg); + return reason; + } + return NULL; +} + +/* testfor + * + * Purpose: Wait, until the given Message-Fragment is received from Skype within + * the given amount of time + * Params : see SkypeRcv + * Returns: TRUE - Message was received within the given amount of time + * FALSE- nope, sorry + */ +BOOL testfor(char *what, DWORD maxwait) { + char *res; + + if ((res=SkypeRcv(what, maxwait))==NULL) return FALSE; + free(res); + return TRUE; +} + +char SendSkypeproxyCommand(char command) { + int length=0; + char reply=0; + BOOL res; + + EnterCriticalSection(&SendMutex); + res = send(ClientSocket, (char *)&length, sizeof(length), 0)==SOCKET_ERROR + || send(ClientSocket, (char *)&command, sizeof(command), 0)==SOCKET_ERROR + || recv(ClientSocket, (char *)&reply, sizeof(reply), 0)==SOCKET_ERROR; + LeaveCriticalSection(&SendMutex); + if (res) + return -1; + else + return reply; +} + +/* ConnectToSkypeAPI + * + * Purpose: Establish a connection to the Skype API + * Params : path - Path to the Skype application + * iStart - Need to start skype for status change. + * 1 = Normal start if Skype not running + * 2 = Forced startp code execution no matter what + * Returns: 0 - Connecting succeeded + * -1 - Something went wrong + */ +int ConnectToSkypeAPI(char *path, int iStart) { + static int iRet = -1; // last request result + static volatile long newRequest = TRUE; + + InterlockedExchange(&newRequest, TRUE); // place new request + EnterCriticalSection(&ConnectMutex); // Prevent reentrance + if (iRet == -1 || newRequest) + { + iRet = _ConnectToSkypeAPI(path, iStart); + InterlockedExchange(&newRequest, FALSE); // every thread which is waiting for connect mutex will get our result as well.. but subsequent calls will set this value to true and call _Connect again + } + LeaveCriticalSection(&ConnectMutex); + return iRet; +} + +void TranslateMirandaRelativePathToAbsolute(LPCSTR cszPath, LPSTR szAbsolutePath, BOOL fQuoteSpaces) { + *szAbsolutePath = 0; + CallService (MS_UTILS_PATHTOABSOLUTE, (WPARAM)(*cszPath ? cszPath : ".\\"), (LPARAM)szAbsolutePath); + if(fQuoteSpaces && strchr((LPCSTR)szAbsolutePath, ' ')){ + memmove (szAbsolutePath+1, szAbsolutePath, strlen(szAbsolutePath)+1); + *szAbsolutePath='"'; + strcat (szAbsolutePath, "\""); + } + + TRACEA(szAbsolutePath); +} + +static int my_spawnv(const char *cmdname, const char *const *argv, PROCESS_INFORMATION *pi) +{ + int i, iLen=0; + char *CommandLine; + STARTUPINFOA si={0}; + BOOL bRet; + + memset (pi, 0, sizeof(PROCESS_INFORMATION)); + for (i=0; argv[i]; i++) iLen+=strlen(argv[i])+1; + if (!(CommandLine = calloc(1, iLen))) return -1; + for (i=0; argv[i]; i++) { + if (i) strcat (CommandLine, " "); + strcat (CommandLine, argv[i]); + } + si.cb = sizeof(si); + + bRet = CreateProcessA( cmdname,CommandLine,NULL,NULL,TRUE,0,NULL,NULL,&si,pi); + free(CommandLine); + if (!bRet) return -1; + return (DWORD)pi->hProcess; +} + +static int _ConnectToSkypeAPI(char *path, int iStart) { + BOOL SkypeLaunched=FALSE; + BOOL UseCustomCommand = DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "UseCustomCommand", 0); + int counter=0, i, j, maxattempts=DBGetContactSettingWord(NULL, SKYPE_PROTONAME, "ConnectionAttempts", 10); + char *args[7], *pFree = NULL; + char *SkypeOptions[]={"/notray", "/nosplash", "/minimized", "/removable", "/datapath:", "/secondary"}; + const int SkypeDefaults[]={0, 1, 1, 0, 0}; + + char szAbsolutePath[MAX_PATH]; + + LOG(("ConnectToSkypeAPI started.")); + if (UseSockets) + { + SOCKADDR_IN service; + DBVARIANT dbv; + long inet; + struct hostent *hp; + + LOG(("ConnectToSkypeAPI: Connecting to Skype2socket socket...")); + if ((ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET) return -1; + + if (!DBGetContactSettingString(NULL, SKYPE_PROTONAME, "Host", &dbv)) { + if ((inet=inet_addr(dbv.pszVal))==-1) { + if (hp=gethostbyname(dbv.pszVal)) + memcpy(&inet, hp->h_addr, sizeof(inet)); + else { + OUTPUT(_T("Cannot resolve host!")); + DBFreeVariant(&dbv); + return -1; + } + } + DBFreeVariant(&dbv); + } else { + OUTPUT(_T("Cannot find valid host to connect to.")); + return -1; + } + + service.sin_family = AF_INET; + service.sin_addr.s_addr = inet; + service.sin_port = htons((unsigned short)DBGetContactSettingWord(NULL, SKYPE_PROTONAME, "Port", 1401)); + + if ( connect( ClientSocket, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR) return -1; + + if (DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "RequiresPassword", 0) && !DBGetContactSettingString(NULL, SKYPE_PROTONAME, "Password", &dbv)) + { + char reply=0; + + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal)+1, (LPARAM)dbv.pszVal); + if ((reply=SendSkypeproxyCommand(AUTHENTICATE))==-1) + { + DBFreeVariant(&dbv); + return -1; + } + if (!reply) { + OUTPUT(_T("Authentication is not supported/needed for this Skypeproxy server. It will be disabled.")); + DBWriteContactSettingByte(NULL, SKYPE_PROTONAME, "RequiresPassword", 0); + } else { + unsigned int length=(unsigned int)strlen(dbv.pszVal); + BOOL res; + EnterCriticalSection(&SendMutex); + res = send(ClientSocket, (char *)&length, sizeof(length), 0)==SOCKET_ERROR + || send(ClientSocket, dbv.pszVal, length, 0)==SOCKET_ERROR + || recv(ClientSocket, (char *)&reply, sizeof(reply), 0)==SOCKET_ERROR; + LeaveCriticalSection(&SendMutex); + if (res) + { + DBFreeVariant(&dbv); + return -1; + } + if (!reply) + { + OUTPUT(_T("Authentication failed for this server, connection was not successful. Verify that your password is correct!")); + DBFreeVariant(&dbv); + return -1; + } + } + DBFreeVariant(&dbv); + } + else + { + char reply=0; + + if ((reply=SendSkypeproxyCommand(CAPABILITIES))==-1) return -1; + if (reply&USE_AUTHENTICATION) { + OUTPUT(_T("The server you specified requires authentication, but you have not supplied a password for it. Check the Skype plugin settings and try again.")); + return -1; + } + } + + + if (!rcvThreadRunning) + if(_beginthread(( pThreadFunc )rcvThread, 0, NULL)==-1) return -1; + + AttachStatus=SKYPECONTROLAPI_ATTACH_SUCCESS; + return 0; + } + + if (pszProxyCallout) + { + if (SkypeSend("SET USERSTATUS ONLINE")==-1) + { + AttachStatus=SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE; + return -1; + } + for ( ;; ) { + char *ptr = SkypeRcv ("CONNSTATUS", INFINITE); + if (strcmp (ptr+11, "CONNECTING")) + { + free (ptr); + break; + } + free (ptr); + } + + AttachStatus=SKYPECONTROLAPI_ATTACH_SUCCESS; + return 0; + } + + do + { + int retval; + /* To initiate communication, Client should broadcast windows message + ('SkypeControlAPIDiscover') to all windows in the system, specifying its own + window handle in wParam parameter. + */ + if (iStart != 2 || counter) + { + LOG(("ConnectToSkypeAPI sending discover message.. hWnd=%08X", (long)g_hWnd)); + retval=SendMessageTimeout(HWND_BROADCAST, ControlAPIDiscover, (WPARAM)g_hWnd, 0, SMTO_ABORTIFHUNG, 3000, NULL); + LOG(("ConnectToSkypeAPI sent discover message returning %d", retval)); + } + + /* In response, Skype responds with + message 'SkypeControlAPIAttach' to the handle specified, and indicates + connection status + SkypeReady is set if there is an answer by Skype other than API_AVAILABLE. + If there is no answer after 3 seconds, launch Skype as it's propably + not running. + */ + if (iStart == 2 || (WaitForSingleObject(SkypeReady, 3000)==WAIT_TIMEOUT && AttachStatus!=SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION)) + { + if (iStart != 2 && g_hWnd==NULL) + { + LOG(("ConnectToSkypeAPI: hWnd of SkypeDispatchWindow not yet set..")); + continue; + } + if ((iStart == 2 || !SkypeLaunched) && (path || UseCustomCommand)) + { + static PROCESS_INFORMATION pi={0}; + DWORD dwExitStatus = 0; + + if ((!pi.hProcess || !GetExitCodeProcess(pi.hProcess, &dwExitStatus) || dwExitStatus != STILL_ACTIVE) && + (DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "StartSkype", 1) || iStart)) + { + LOG(("ConnectToSkypeAPI Starting Skype, as it's not running")); + + j=1; + for (i=0; i=maxattempts && AttachStatus==-1) + { + int oldstatus=SkypeStatus; + InterlockedExchange((long *)&SkypeStatus, (int)ID_STATUS_OFFLINE); + ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldstatus, SkypeStatus); + OUTPUT(_T("ERROR: Skype not running / too old / working!")); + return -1; + } + } + } + LOG(("Attachstatus %d", AttachStatus)); + } while (AttachStatus==SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE || AttachStatus==SKYPECONTROLAPI_ATTACH_API_AVAILABLE || AttachStatus==-1); + + while (AttachStatus==SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION) Sleep(1000); + LOG(("Attachstatus %d", AttachStatus)); + if (AttachStatus!=SKYPECONTROLAPI_ATTACH_SUCCESS) { + int oldstatus; + + switch(AttachStatus) { + case SKYPECONTROLAPI_ATTACH_REFUSED: + OUTPUT(_T("Skype refused the connection :(")); + break; + case SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE: + OUTPUT(_T("The Skype API is not available")); + break; + default: + LOG(("ERROR: AttachStatus: %d", AttachStatus)); + OUTPUT(_T("Wheee, Skype won't let me use the API. :(")); + } + oldstatus=SkypeStatus; + InterlockedExchange((long *)&SkypeStatus, (int)ID_STATUS_OFFLINE); + ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldstatus, SkypeStatus); + return -1; + } + + return 0; +} + +/* CloseSkypeAPI + * Purpose: Closes existing api connection + * Params: path - Path to the Skype application; could be NULL when using proxy + * Returns: always 0 + */ +int CloseSkypeAPI(char *skypePath) +{ + char szAbsolutePath[MAX_PATH]; + + logoff_contacts(TRUE); + if (UseSockets) + { + if (ClientSocket != INVALID_SOCKET) + { + closesocket(ClientSocket); + ClientSocket = INVALID_SOCKET; + } + } + else { + if (!pszProxyCallout) + { + if (AttachStatus!=-1) + { + // it was crashing when the skype-network-proxy is used (imo2sproxy for imo.im) and skype-path is empty + // now, with the "UseSockets" check and the skypePath[0] != 0 check its fixed + if (skypePath != NULL && skypePath[0] != 0) { + TranslateMirandaRelativePathToAbsolute(skypePath, szAbsolutePath, FALSE); + _spawnl(_P_NOWAIT, szAbsolutePath, szAbsolutePath, "/SHUTDOWN", NULL); + } + } + } + } + SkypeInitialized=FALSE; + ResetEvent(SkypeReady); + AttachStatus=-1; + if (g_hWnd) KillTimer (g_hWnd, 1); + return 0; +} +/* ConnectToSkypeAPI + * + * Purpose: Establish a connection to the Skype API + * Params : path - Path to the Skype application + * Returns: 0 - Connecting succeeded + * -1 - Something went wrong + */ +//int __connectAPI(char *path) { +// int retval; +// +// EnterCriticalSection(&ConnectMutex); +// if (AttachStatus!=-1) { +// LeaveCriticalSection(&ConnectMutex); +// return -1; +// } +// InterlockedExchange((long *)&SkypeStatus, ID_STATUS_CONNECTING); +// ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) ID_STATUS_OFFLINE, SkypeStatus); +// retval=__connectAPI(path); +// if (retval==-1) { +// logoff_contacts(); +// InterlockedExchange((long *)&SkypeStatus, ID_STATUS_OFFLINE); +// ProtoBroadcastAck(SKYPE_PROTONAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) ID_STATUS_CONNECTING, SkypeStatus); +// } +// LeaveCriticalSection(&ConnectMutex); +// return retval; +//} -- cgit v1.2.3