summaryrefslogtreecommitdiff
path: root/plugins/!NotAdopted/Skype/skypeapi.c
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-07-28 19:25:08 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-07-28 19:25:08 +0000
commit66526765714b08969548a414d3fa87dbe333242d (patch)
treed3fc2027140f97edfdfc5023e70ff8b4db920c11 /plugins/!NotAdopted/Skype/skypeapi.c
parent3ccd712341ed9e76252bd595c5a797d6c8ea8992 (diff)
"!Deprecated" folders moved from root directory to plugins
git-svn-id: http://svn.miranda-ng.org/main/trunk@1230 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/!NotAdopted/Skype/skypeapi.c')
-rw-r--r--plugins/!NotAdopted/Skype/skypeapi.c1699
1 files changed, 1699 insertions, 0 deletions
diff --git a/plugins/!NotAdopted/Skype/skypeapi.c b/plugins/!NotAdopted/Skype/skypeapi.c
new file mode 100644
index 0000000000..2a6eb1b76e
--- /dev/null
+++ b/plugins/!NotAdopted/Skype/skypeapi.c
@@ -0,0 +1,1699 @@
+/*
+ * 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"
+#include "alogon.h"
+#include "msgq.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 //
+
+static TYP_MSGQ SkypeMsgs, SkypeSendQueue;
+
+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
+
+
+static CRITICAL_SECTION ConnectMutex;
+static BOOL rcvThreadRunning=FALSE, isConnecting = FALSE;
+static SOCKET ClientSocket=INVALID_SOCKET;
+static HANDLE SkypeMsgToSend=NULL;
+
+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);
+ }
+}
+
+void sendThread(char *dummy) {
+ COPYDATASTRUCT CopyData;
+ LRESULT SendResult;
+ int oldstatus;
+ unsigned int length;
+ char *szMsg;
+
+ while (SkypeMsgToSend) {
+ if (WaitForSingleObject(SkypeMsgToSend, INFINITE) != WAIT_OBJECT_0) return;
+ if (!(szMsg = MsgQ_Get(&SkypeSendQueue))) continue;
+ length=(unsigned int)strlen(szMsg);
+
+ if (UseSockets) {
+ if (send(ClientSocket, (char *)&length, sizeof(length), 0) != SOCKET_ERROR &&
+ send(ClientSocket, szMsg, length, 0) != SOCKET_ERROR) {
+ free (szMsg);
+ continue;
+ }
+ SendResult = 0;
+ } else {
+ CopyData.dwData=0;
+ CopyData.lpData=szMsg;
+ CopyData.cbData=length+1;
+
+ // Internal comm channel
+ if (pszProxyCallout) {
+ CallService (pszProxyCallout, 0, (LPARAM)&CopyData);
+ free(szMsg);
+ continue;
+ }
+
+ // 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));
+ free(szMsg);
+ }
+ 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);
+ WaitForSingleObject (SkypeReady, 10000);
+ // SendMessageTimeout(HWND_BROADCAST, ControlAPIDiscover, (WPARAM)g_hWnd, 0, SMTO_ABORTIFHUNG, 3000, NULL);
+ }
+ }
+}
+
+
+/*
+ * 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) {
+
+ MsgQ_Init(&SkypeMsgs);
+ MsgQ_Init(&SkypeSendQueue);
+ InitializeCriticalSection(&ConnectMutex);
+ if (SkypeMsgToSend=CreateSemaphore(NULL, 0, MAX_MSGS, NULL)) {
+ if (m_szSendBuf = malloc(m_iBufSize=512)) {
+ if (_beginthread(( pThreadFunc )sendThread, 0, NULL)!=-1)
+ return 0;
+ free(m_szSendBuf);
+ }
+ CloseHandle (SkypeMsgToSend);
+ }
+ return -1;
+}
+
+/* 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) {
+ return MsgQ_Add(&SkypeMsgs, msg)?0:-1;
+}
+
+/* SkypeMsgCleanup
+ *
+ * Purpose: Clean up the whole MESSagequeue - free() all
+ */
+void SkypeMsgCleanup(void) {
+ int i;
+
+ LOG(("SkypeMsgCleanup Cleaning up message queue.."));
+ if (receivers>1)
+ {
+ LOG (("SkypeMsgCleanup Releasing %d receivers", receivers));
+ for (i=0;i<receivers; i++)
+ {
+ SkypeMsgAdd ("ERROR Semaphore was blocked");
+ }
+ ReleaseSemaphore (SkypeMsgReceived, receivers, NULL);
+ }
+
+ EnterCriticalSection(&ConnectMutex);
+ MsgQ_Exit(&SkypeMsgs);
+ LeaveCriticalSection(&ConnectMutex);
+ DeleteCriticalSection(&ConnectMutex);
+ CloseHandle(SkypeMsgToSend);
+ SkypeMsgToSend=NULL;
+ MsgQ_Exit(&SkypeSendQueue);
+ 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) {
+ return MsgQ_Get(&SkypeMsgs);
+}
+
+// Message sending routine, for internal use by SkypeSend
+static int __sendMsg(char *szMsg) {
+ COPYDATASTRUCT CopyData;
+
+ 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 && ClientSocket==INVALID_SOCKET) return -1;
+ if (!MsgQ_Add(&SkypeSendQueue, szMsg) || !ReleaseSemaphore(SkypeMsgToSend, 1, NULL))
+ return -1;
+ 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;
+ 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(&SkypeMsgs.cs);
+ // First, search for the requested message. On second run, also accept an ERROR
+ for (j=0; j<2; j++)
+ {
+ for (ptr=SkypeMsgs.l.tqh_first; ptr; ptr=ptr->l.tqe_next) {
+ 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=MsgQ_RemoveMsg(&SkypeMsgs, ptr);
+ LOG(("<SkypeRcv: %s", msg));
+ if (bIsChatMsg) {
+ msg=realloc(msg, strlen(msg)+5);
+ memmove (msg+4, msg, strlen(msg)+1);
+ memcpy (msg, "CHAT", 4);
+
+ // This may be a sign that protocol negotiation failed, so we can try to send
+ // our supported protocol version again, just in case... (Skype API bug?)
+ //SkypeSend(SKYPE_PROTO);
+ }
+ LeaveCriticalSection(&SkypeMsgs.cs);
+ return msg;
+ }
+ }
+ }
+ LeaveCriticalSection(&SkypeMsgs.cs);
+ 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(("<SkypeRcv: (empty)"));
+ return NULL;
+}
+char *SkypeRcv(char *what, DWORD maxwait) {
+ return SkypeRcvTime(what, 0, maxwait);
+}
+
+char *SkypeRcvMsg(char *what, time_t st, HANDLE hContact, DWORD maxwait) {
+ char *msg, msgid[32]={0}, *pMsg, *pCurMsg;
+ struct MsgQueue *ptr;
+ int iLenWhat = strlen(what);
+ DWORD dwWaitStat;
+ BOOL bIsError, bProcess;
+
+ LOG (("SkypeRcvMsg - Requesting answer: %s ", what));
+ do {
+ EnterCriticalSection(&SkypeMsgs.cs);
+ ptr=SkypeMsgs.l.tqh_first;
+ while(ptr) {
+ //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 &&
+ DBGetContactSettingWord(hContact, SKYPE_PROTONAME, "Status", ID_STATUS_OFFLINE)!=ID_STATUS_OFFLINE) {
+ // Remove dat shit
+ struct MsgQueue *ptr_=ptr->l.tqe_next;
+
+ free(MsgQ_RemoveMsg(&SkypeMsgs, ptr));
+ ptr=ptr_;
+ 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=MsgQ_RemoveMsg(&SkypeMsgs, ptr);
+ LOG(("<SkypeRcv: %s", msg));
+ LeaveCriticalSection(&SkypeMsgs.cs);
+ return msg;
+ }
+ }
+ ptr=ptr->l.tqe_next;
+ }
+ LeaveCriticalSection(&SkypeMsgs.cs);
+ 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(("<SkypeRcvMsg: (empty)"));
+ return NULL;
+}
+
+/*
+ Introduced in 0.0.0.17
+
+ Issues a GET szWhat szWho szProperty and waits until the answer is received
+ Returns the answer or NULL on failure
+ BEWARE: Don't forget to free() return value!
+
+ For example: SkypeGet("USER", dbv.pszVal, "FULLNAME");
+*/
+static char *__SkypeGet(char *szID, char *szWhat, char *szWho, char *szProperty) {
+ char *str, *ptr;
+ size_t len, len_id;
+ time_t st = 0;
+
+ st = *szID?0:SkypeTime(NULL);
+ str=(char *)_alloca((len=strlen(szWhat)+strlen(szWho)+strlen(szProperty)+(*szWho?2:1)+(len_id=strlen(szID)))+5);
+ sprintf(str, "%sGET %s%s%s %s", szID, szWhat, *szWho?" ":"", szWho, szProperty);
+ if (__sendMsg(str)) return NULL;
+ if (*szProperty) len++;
+ if (*szID) {
+ sprintf(str, "%s%s%s%s %s", szID, szWhat, *szWho?" ":"", szWho, szProperty);
+ ptr = SkypeRcvTime(str, st, INFINITE);
+ } else ptr = SkypeRcvTime(str+4, st, INFINITE);
+ if (ptr && strncmp (ptr+len_id, "ERROR", 5)) memmove(ptr, ptr+len, strlen(ptr)-len+1);
+ LOG(("SkypeGet - Request %s -> 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) {
+ return MsgQ_CollectGarbage(&SkypeMsgs, age);
+}
+
+
+/* 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
+ */
+static 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;entries<MAX_ENTRIES;entries++) {
+ sprintf(number, "LastNumber%d", entries);
+ if (!DBGetContactSettingTString(NULL, SKYPE_PROTONAME, number, &dbv)) {
+ SendDlgItemMessage(hwndDlg,IDC_NUMBER,CB_ADDSTRING,0,(LPARAM)dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } else break ;
+ }
+ }
+ SetFocus(GetDlgItem(hwndDlg, IDC_NUMBER));
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDDIAL:
+ EnableWindow(GetDlgItem(hwndDlg, IDDIAL), FALSE);
+ GetDlgItemTextA(hwndDlg, IDC_NUMBER, number, sizeof(number));
+ if (!strncmp(number, "00", 2)) {
+ memmove(number, number+1, sizeof(number)-1);
+ number[0]='+';
+ number[sizeof(number)]=0;
+ }
+ if (!hContact) {
+ if (!(hContact=add_contact(number, PALF_TEMPORARY))) {
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ DBDeleteContactSetting(hContact, "CList", "Hidden");
+ DBWriteContactSettingWord(hContact, SKYPE_PROTONAME, "Status", (WORD)SkypeStatusToMiranda("SKYPEOUT"));
+ if (SendDlgItemMessage(hwndDlg,IDC_NUMBER,CB_FINDSTRING,0,(LPARAM)number)==CB_ERR) {
+ int i;
+ char buf[64];
+ DBVARIANT dbv;
+
+ if (entries>MAX_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;
+
+ 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;
+ 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,FALSE,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;
+ 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;
+ 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<sizeof(SkypeOptions)/sizeof(SkypeOptions[0]); i++)
+ if (DBGetContactSettingByte(NULL, SKYPE_PROTONAME, SkypeOptions[i]+1, SkypeDefaults[i])) {
+ DBVARIANT dbv;
+
+ switch (i)
+ {
+ case 4:
+ if(!DBGetContactSettingString(NULL,SKYPE_PROTONAME,"datapath",&dbv))
+ {
+ int paramSize;
+ TranslateMirandaRelativePathToAbsolute(dbv.pszVal, szAbsolutePath, TRUE);
+ paramSize = strlen(SkypeOptions[i]) + strlen(szAbsolutePath);
+ pFree = args[j] = malloc(paramSize + 1);
+ sprintf(args[j],"%s%s",SkypeOptions[i],szAbsolutePath);
+ DBFreeVariant(&dbv);
+ }
+ break;
+ case 2:
+ args[j++]="/legacylogin";
+ default:
+ args[j]=SkypeOptions[i];
+ break;
+ }
+ LOG(("Using Skype parameter: %s", args[j]));
+ //MessageBox(NULL,"Using Skype parameter: ",args[j],0);
+ j++;
+ }
+ args[j]=NULL;
+
+ if(UseCustomCommand)
+ {
+ DBVARIANT dbv;
+
+ if(!DBGetContactSettingString(NULL,SKYPE_PROTONAME,"CommandLine",&dbv))
+ {
+ TranslateMirandaRelativePathToAbsolute(dbv.pszVal, szAbsolutePath, FALSE);
+ args[0] = (LPSTR)szAbsolutePath;
+ LOG(("ConnectToSkypeAPI: Launch skype using command line"));
+ if (!*szAbsolutePath || my_spawnv(szAbsolutePath, args, &pi) == -1) {
+ LOG(("ConnectToSkypeAPI: Failed to launch skype!"));
+ } else {
+ WaitForInputIdle((HANDLE)pi.hProcess, 5000);
+ setUserNamePassword(pi.dwProcessId);
+ }
+ DBFreeVariant(&dbv);
+ }
+ }
+ else
+ {
+ args[0]=path;
+ LOG(("ConnectToSkypeAPI: Launch skype"));
+ /*for(int i=0;i<j;i++)
+ {
+ if(args[i] != NULL)
+ LOG("ConnectToSkypeAPI", args[i]);
+ }*/
+
+ // if there is no skype installed and no custom command line, then exit .. else it crashes
+ if (args[0] == NULL || strlen(args[0])==0)
+ {
+ return -1;
+ }
+ if (my_spawnv(path, args, &pi) != -1) {
+ WaitForInputIdle((HANDLE)pi.hProcess, 5000);
+ setUserNamePassword(pi.dwProcessId);
+ }
+ }
+ if (pFree) free(pFree);
+ }
+ ResetEvent(SkypeReady);
+ SkypeLaunched=TRUE;
+ LOG(("ConnectToSkypeAPI: Skype process started."));
+ // Skype launching iniciated, keep sending Discover messages until it responds.
+ continue;
+ }
+ else
+ {
+ LOG(("ConnectToSkypeAPI: Check if Skype was launchable.."));
+ if (DBGetContactSettingByte(NULL, SKYPE_PROTONAME, "StartSkype", 1) && !(path || UseCustomCommand)) return -1;
+ LOG(("Trying to attach: #%d", counter));
+ counter++;
+ if (counter>=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;
+//}