/* Jabber Protocol Plugin for Miranda IM Tlen Protocol Plugin for Miranda NG Copyright (C) 2002-2004 Santithorn Bunchua Copyright (C) 2004-2007 Piotr Piastucki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "tlen.h" #include #include #include #include #include "resource.h" #include "tlen_list.h" #include "tlen_iq.h" #include "tlen_p2p_old.h" #include "tlen_avatar.h" #include "tlen_file.h" DWORD_PTR TlenProtocol::GetCaps(int type, HANDLE hContact) { if (type == PFLAGNUM_1) return PF1_IM|PF1_AUTHREQ|PF1_SERVERCLIST|PF1_MODEMSG|PF1_BASICSEARCH|PF1_SEARCHBYEMAIL|PF1_EXTSEARCH|PF1_EXTSEARCHUI|PF1_SEARCHBYNAME|PF1_FILE;//|PF1_VISLIST|PF1_INVISLIST; if (type == PFLAGNUM_2) return PF2_ONLINE|PF2_INVISIBLE|PF2_SHORTAWAY|PF2_LONGAWAY|PF2_HEAVYDND|PF2_FREECHAT; if (type == PFLAGNUM_3) return PF2_ONLINE|PF2_INVISIBLE|PF2_SHORTAWAY|PF2_LONGAWAY|PF2_HEAVYDND|PF2_FREECHAT; if (type == PFLAGNUM_4) return PF4_FORCEAUTH|PF4_NOCUSTOMAUTH|PF4_SUPPORTTYPING|PF4_AVATARS|PF4_IMSENDOFFLINE|PF4_OFFLINEFILES; if (type == PFLAG_UNIQUEIDTEXT) return (DWORD_PTR) Translate("Tlen login"); if (type == PFLAG_UNIQUEIDSETTING) return (DWORD_PTR) "jid"; return 0; } INT_PTR TlenProtocol::GetName(WPARAM wParam, LPARAM lParam) { strncpy((char*)lParam, m_szModuleName, wParam); return 0; } int TlenRunSearch(TlenProtocol *proto) { int iqId = 0; if (!proto->isOnline) return 0; if (proto->searchQuery != NULL && proto->searchIndex < 10) { iqId = proto->searchID; TlenIqAdd(proto, iqId, IQ_PROC_GETSEARCH, TlenIqResultSearch); if (proto->searchIndex == 0) { TlenSend(proto, "%s", iqId, proto->searchQuery); } else { TlenSend(proto, "%s%d", iqId, proto->searchQuery, proto->searchIndex * TLEN_MAX_SEARCH_RESULTS_PER_PAGE); } proto->searchIndex ++; } return iqId; } void TlenResetSearchQuery(TlenProtocol *proto) { if (proto->searchQuery != NULL) { mir_free(proto->searchQuery); proto->searchQuery = NULL; } proto->searchQueryLen = 0; proto->searchIndex = 0; proto->searchID = TlenSerialNext(proto); } HANDLE TlenProtocol::SearchBasic(const PROTOCHAR* id) { char *jid; int iqId = 0; if (!isOnline) return 0; if (id == NULL) return 0; char* id_A = mir_t2a(id); if ((jid=TlenTextEncode(id_A)) != NULL) { searchJID = mir_strdup(id_A); TlenResetSearchQuery(this); TlenStringAppend(&searchQuery, &searchQueryLen, "%s", jid); iqId = TlenRunSearch(this); mir_free(jid); } mir_free(id_A); return (HANDLE)iqId; } HANDLE TlenProtocol::SearchByEmail(const PROTOCHAR* email) { char *emailEnc; int iqId = 0; if (!isOnline) return 0; if (email == NULL) return 0; char* email_A = mir_t2a(email); if ((emailEnc=TlenTextEncode(email_A)) != NULL) { TlenResetSearchQuery(this); TlenStringAppend(&searchQuery, &searchQueryLen, "%s", emailEnc); iqId = TlenRunSearch(this); mir_free(emailEnc); } mir_free(email_A); return (HANDLE)iqId; } HANDLE TlenProtocol::SearchByName(const PROTOCHAR* nickT, const PROTOCHAR* firstNameT, const PROTOCHAR* lastNameT) { char* nick = mir_t2a(nickT); char* firstName = mir_t2a(firstNameT); char* lastName = mir_t2a(lastNameT); char *p; int iqId = 0; if (!isOnline) return 0; TlenResetSearchQuery(this); if (nick != NULL && nick[0] != '\0') { if ((p=TlenTextEncode(nick)) != NULL) { TlenStringAppend(&searchQuery, &searchQueryLen, "%s", p); mir_free(p); } } if (firstName != NULL && firstName[0] != '\0') { if ((p=TlenTextEncode(firstName)) != NULL) { TlenStringAppend(&searchQuery, &searchQueryLen, "%s", p); mir_free(p); } } if (lastName != NULL && lastName[0] != '\0') { if ((p=TlenTextEncode(lastName)) != NULL) { TlenStringAppend(&searchQuery, &searchQueryLen, "%s", p); mir_free(p); } } iqId = TlenRunSearch(this); return (HANDLE)iqId; } HWND TlenProtocol::CreateExtendedSearchUI(HWND owner) { return (HWND) CreateDialog(hInst, MAKEINTRESOURCE(IDD_ADVSEARCH), owner, TlenAdvSearchDlgProc); } HWND TlenProtocol::SearchAdvanced(HWND owner) { int iqId; if (!isOnline) return 0; TlenResetSearchQuery(this); iqId = TlenSerialNext(this); if ((searchQuery = TlenAdvSearchCreateQuery(owner, iqId)) != NULL) { iqId = TlenRunSearch(this); } return (HWND)iqId; } static HANDLE AddToListByJID(TlenProtocol *proto, const char *newJid, DWORD flags) { HANDLE hContact; char *jid, *nick; if ((hContact=TlenHContactFromJID(proto, newJid)) == NULL) { // not already there: add jid = mir_strdup(newJid); _strlwr(jid); hContact = (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0); CallService(MS_PROTO_ADDTOCONTACT, (WPARAM) hContact, (LPARAM) proto->m_szModuleName); db_set_s(hContact, proto->m_szModuleName, "jid", jid); if ((nick=TlenNickFromJID(newJid)) == NULL) nick = mir_strdup(newJid); db_set_s(hContact, "CList", "MyHandle", nick); mir_free(nick); mir_free(jid); // Note that by removing or disable the "NotOnList" will trigger // the plugin to add a particular contact to the roster list. // See DBSettingChanged hook at the bottom part of this source file. // But the add module will delete "NotOnList". So we will not do it here. // Also because we need "MyHandle" and "Group" info, which are set after // PS_ADDTOLIST is called but before the add dialog issue deletion of // "NotOnList". // If temporary add, "NotOnList" won't be deleted, and that's expected. db_set_b(hContact, "CList", "NotOnList", 1); if (flags & PALF_TEMPORARY) db_set_b(hContact, "CList", "Hidden", 1); } else { // already exist // Set up a dummy "NotOnList" when adding permanently only if (!(flags&PALF_TEMPORARY)) db_set_b(hContact, "CList", "NotOnList", 1); } return hContact; } HANDLE TlenProtocol::AddToList(int flags, PROTOSEARCHRESULT *psr) { HANDLE hContact; TLEN_SEARCH_RESULT *jsr = (TLEN_SEARCH_RESULT*)psr; if (jsr->hdr.cbSize != sizeof(TLEN_SEARCH_RESULT)) return (int) NULL; hContact = AddToListByJID(this, jsr->jid, flags); // wParam is flag e.g. PALF_TEMPORARY return hContact; } HANDLE TlenProtocol::AddToListByEvent( int flags, int iContact, HANDLE hDbEvent ) { DBEVENTINFO dbei = { sizeof(dbei) }; if ((dbei.cbBlob = db_event_getBlobSize(hDbEvent)) == (DWORD)(-1)) return (HANDLE) NULL; if ((dbei.pBlob=(PBYTE) mir_alloc(dbei.cbBlob)) == NULL) return (HANDLE) NULL; if (db_event_get(hDbEvent, &dbei)) { mir_free(dbei.pBlob); return (HANDLE) NULL; } if (strcmp(dbei.szModule, m_szModuleName)) { mir_free(dbei.pBlob); return (HANDLE) NULL; } /* // EVENTTYPE_CONTACTS is when adding from when we receive contact list (not used in Tlen) // EVENTTYPE_ADDED is when adding from when we receive "You are added" (also not used in Tlen) // Tlen will only handle the case of EVENTTYPE_AUTHREQUEST // EVENTTYPE_AUTHREQUEST is when adding from the authorization request dialog */ if (dbei.eventType != EVENTTYPE_AUTHREQUEST) { mir_free(dbei.pBlob); return (HANDLE) NULL; } char *nick = (char *)dbei.pBlob + sizeof(DWORD)*2; char *firstName = nick + strlen(nick) + 1; char *lastName = firstName + strlen(firstName) + 1; char *jid = lastName + strlen(lastName) + 1; HANDLE hContact = (HANDLE) AddToListByJID(this, jid, flags); mir_free(dbei.pBlob); return hContact; } int TlenProtocol::Authorize(HANDLE hDbEvent) { if (!isOnline) return 1; DBEVENTINFO dbei = { sizeof(dbei) }; if ((dbei.cbBlob = db_event_getBlobSize(hDbEvent)) == (DWORD)-1) return 1; if ((dbei.pBlob=(PBYTE) mir_alloc(dbei.cbBlob)) == NULL) return 1; if (db_event_get(hDbEvent, &dbei)){ mir_free(dbei.pBlob); return 1; } if (dbei.eventType != EVENTTYPE_AUTHREQUEST) { mir_free(dbei.pBlob); return 1; } if (strcmp(dbei.szModule, m_szModuleName)) { mir_free(dbei.pBlob); return 1; } char *nick = (char *)dbei.pBlob + sizeof(DWORD)*2; char *firstName = nick + strlen(nick) + 1; char *lastName = firstName + strlen(firstName) + 1; char *jid = lastName + strlen(lastName) + 1; TlenSend(this, "", jid); // Automatically add this user to my roster if option is enabled if (db_get_b(NULL, m_szModuleName, "AutoAdd", TRUE) == TRUE) { HANDLE hContact; TLEN_LIST_ITEM *item; if ((item=TlenListGetItemPtr(this, LIST_ROSTER, jid)) == NULL || (item->subscription != SUB_BOTH && item->subscription != SUB_TO)) { debugLogA("Try adding contact automatically jid=%s", jid); if ((hContact=AddToListByJID(this, jid, 0)) != NULL) { // Trigger actual add by removing the "NotOnList" added by AddToListByJID() // See AddToListByJID() and TlenDbSettingChanged(). db_unset(hContact, "CList", "NotOnList"); } } } mir_free(dbei.pBlob); return 0; } int TlenProtocol::AuthDeny(HANDLE hDbEvent, const PROTOCHAR* szReason) { if (!isOnline) return 1; DBEVENTINFO dbei = { sizeof(dbei) }; if ((dbei.cbBlob = db_event_getBlobSize(hDbEvent)) == (DWORD)(-1)) return 1; if ((dbei.pBlob = (PBYTE) mir_alloc(dbei.cbBlob)) == NULL) return 1; if (db_event_get(hDbEvent, &dbei)) { mir_free(dbei.pBlob); return 1; } if (dbei.eventType != EVENTTYPE_AUTHREQUEST) { mir_free(dbei.pBlob); return 1; } if (strcmp(dbei.szModule, m_szModuleName)) { mir_free(dbei.pBlob); return 1; } char *nick = (char *)dbei.pBlob + sizeof(DWORD)*2; char *firstName = nick + strlen(nick) + 1; char *lastName = firstName + strlen(firstName) + 1; char *jid = lastName + strlen(lastName) + 1; TlenSend(this, "", jid); TlenSend(this, "", jid); mir_free(dbei.pBlob); return 0; } static void TlenConnect(TlenProtocol *proto, int initialStatus) { if (!proto->isConnected) { ThreadData *thread; int oldStatus; thread = (ThreadData *) mir_alloc(sizeof(ThreadData)); memset(thread, 0, sizeof(ThreadData)); thread->proto = proto; proto->m_iDesiredStatus = initialStatus; oldStatus = proto->m_iStatus; proto->m_iStatus = ID_STATUS_CONNECTING; ProtoBroadcastAck(proto->m_szModuleName, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, proto->m_iStatus); thread->hThread = (HANDLE) forkthread((void (__cdecl *)(void*))TlenServerThread, 0, thread); } } int TlenProtocol::SetStatus(int iNewStatus) { int oldStatus; HANDLE s; m_iDesiredStatus = iNewStatus; if (iNewStatus == ID_STATUS_OFFLINE) { if (threadData) { if (isConnected) { TlenSendPresence(this, ID_STATUS_OFFLINE); } // TODO bug? s = proto; s = threadData->s; threadData = NULL; if (isConnected) { Sleep(200); // TlenSend(s, ""); // Force closing connection isConnected = FALSE; isOnline = FALSE; Netlib_CloseHandle(s); } } else { if (m_iStatus != ID_STATUS_OFFLINE) { oldStatus = m_iStatus; m_iStatus = ID_STATUS_OFFLINE; ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus); } } } else if (iNewStatus != m_iStatus) { if (!isConnected) TlenConnect(this, iNewStatus); else { // change status oldStatus = m_iStatus; // send presence update TlenSendPresence(this, iNewStatus); ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, m_iStatus); } } return 0; } INT_PTR TlenProtocol::GetStatus(WPARAM wParam, LPARAM lParam) { return m_iStatus; } int TlenProtocol::SetAwayMsg(int iStatus, const PROTOCHAR* msg) { char **szMsg; char *newModeMsg; debugLogA("SetAwayMsg called, wParam=%d lParam=%s", iStatus, msg); newModeMsg = TlenTextEncode(mir_t2a(msg)); //TODO TCHAR EnterCriticalSection(&modeMsgMutex); switch (iStatus) { case ID_STATUS_ONLINE: szMsg = &modeMsgs.szOnline; break; case ID_STATUS_AWAY: case ID_STATUS_ONTHEPHONE: case ID_STATUS_OUTTOLUNCH: szMsg = &modeMsgs.szAway; break; case ID_STATUS_NA: szMsg = &modeMsgs.szNa; break; case ID_STATUS_DND: case ID_STATUS_OCCUPIED: szMsg = &modeMsgs.szDnd; break; case ID_STATUS_FREECHAT: szMsg = &modeMsgs.szFreechat; break; case ID_STATUS_INVISIBLE: szMsg = &modeMsgs.szInvisible; break; default: LeaveCriticalSection(&modeMsgMutex); return 1; } if ((*szMsg == NULL && newModeMsg == NULL) || (*szMsg != NULL && newModeMsg != NULL && !strcmp(*szMsg, newModeMsg))) { // Message is the same, no update needed if (newModeMsg != NULL) mir_free(newModeMsg); } else { // Update with the new mode message if (*szMsg != NULL) mir_free(*szMsg); *szMsg = newModeMsg; // Send a presence update if needed if (iStatus == m_iStatus) { TlenSendPresence(this, m_iStatus); } } LeaveCriticalSection(&modeMsgMutex); return 0; } int TlenProtocol::GetInfo(HANDLE hContact, int infoType) { DBVARIANT dbv; int iqId; char *nick, *pNick; if (!isOnline) return 1; if (hContact == NULL) { iqId = TlenSerialNext(this); TlenIqAdd(this, iqId, IQ_PROC_NONE, TlenIqResultVcard); TlenSend(this, "", iqId); } else { if (db_get(hContact, m_szModuleName, "jid", &dbv)) return 1; if ((nick=TlenNickFromJID(dbv.pszVal)) != NULL) { if ((pNick=TlenTextEncode(nick)) != NULL) { iqId = TlenSerialNext(this); TlenIqAdd(this, iqId, IQ_PROC_NONE, TlenIqResultVcard); TlenSend(this, "%s", iqId, pNick); mir_free(pNick); } mir_free(nick); } db_free(&dbv); } return 0; } int TlenProtocol::SetApparentMode(HANDLE hContact, int mode) { DBVARIANT dbv; int oldMode; char *jid; if (!isOnline) return 0; if (!db_get_b(NULL, m_szModuleName, "VisibilitySupport", FALSE)) return 0; if (mode != 0 && mode != ID_STATUS_ONLINE && mode != ID_STATUS_OFFLINE) return 1; oldMode = db_get_w(hContact, m_szModuleName, "ApparentMode", 0); if ((int) mode == oldMode) return 1; db_set_w(hContact, m_szModuleName, "ApparentMode", (WORD) mode); if (!db_get(hContact, m_szModuleName, "jid", &dbv)) { jid = dbv.pszVal; switch (mode) { case ID_STATUS_ONLINE: if (m_iStatus == ID_STATUS_INVISIBLE || oldMode == ID_STATUS_OFFLINE) TlenSend(this, "available", jid); break; case ID_STATUS_OFFLINE: if (m_iStatus != ID_STATUS_INVISIBLE || oldMode == ID_STATUS_ONLINE) TlenSend(this, "", jid); break; case 0: if (oldMode == ID_STATUS_ONLINE && m_iStatus == ID_STATUS_INVISIBLE) TlenSend(this, "", jid); else if (oldMode == ID_STATUS_OFFLINE && m_iStatus != ID_STATUS_INVISIBLE) TlenSend(this, "available", jid); break; } db_free(&dbv); } return 0; } ///////////////////////////////////////////////////////////////////////////////////////// struct SENDACKTHREADDATA { __inline SENDACKTHREADDATA(TlenProtocol *_ppro, HANDLE _hContact, int _msgid=0) : proto(_ppro), hContact(_hContact), msgid(_msgid) {} TlenProtocol *proto; HANDLE hContact; int msgid; }; static void __cdecl TlenSendMessageAckThread(void *ptr) { SENDACKTHREADDATA *data = (SENDACKTHREADDATA *)ptr; SleepEx(10, TRUE); ProtoBroadcastAck(data->proto->m_szModuleName, data->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)data->msgid, 0); delete data; } static void __cdecl TlenSendMessageFailedThread(void *ptr) { SENDACKTHREADDATA *data = (SENDACKTHREADDATA *)ptr; SleepEx(10, TRUE); ProtoBroadcastAck(data->proto->m_szModuleName, data->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)data->msgid, 0); delete data; } static void __cdecl TlenGetAwayMsgThread(void *ptr) { DBVARIANT dbv; TLEN_LIST_ITEM *item; SENDACKTHREADDATA *data = (SENDACKTHREADDATA *)ptr; if (!db_get(data->hContact, data->proto->m_szModuleName, "jid", &dbv)) { if ((item=TlenListGetItemPtr(data->proto, LIST_ROSTER, dbv.pszVal)) != NULL) { db_free(&dbv); ProtoBroadcastAck(data->proto->m_szModuleName, data->hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, item->statusMessage==NULL ? (LPARAM)NULL : (LPARAM)(TCHAR*)_A2T(item->statusMessage)); return; } else db_free(&dbv); } ProtoBroadcastAck(data->proto->m_szModuleName, data->hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)(TCHAR*)TEXT("")); delete data; } INT_PTR TlenProtocol::SendAlert(WPARAM wParam, LPARAM lParam) { HANDLE hContact = ( HANDLE )wParam; DBVARIANT dbv; if (isOnline && !db_get(hContact, m_szModuleName, "jid", &dbv)) { TlenSend(this, "", dbv.pszVal); db_free(&dbv); } return 0; } int TlenProtocol::SendMsg(HANDLE hContact, int flags, const char* msgRAW) { DBVARIANT dbv; char *msgEnc; TLEN_LIST_ITEM *item; char msgType[16]; if (!isOnline || db_get(hContact, m_szModuleName, "jid", &dbv)) { forkthread(TlenSendMessageFailedThread, 0, new SENDACKTHREADDATA(this, hContact, 2)); return 2; } char* msg; if (flags & PREF_UNICODE) msg = mir_u2a((wchar_t*)&msgRAW[strlen(msgRAW) + 1]); else if (flags & PREF_UTF) msg = mir_utf8decodeA(msgRAW); else msg = mir_strdup(msgRAW); int id = TlenSerialNext(this); if (!strcmp(msg, "")) { TlenSend(this, "", dbv.pszVal); forkthread(TlenSendMessageAckThread, 0, new SENDACKTHREADDATA(this, hContact, id)); } else if (!strcmp(msg, "")) { TlenSend(this, "", dbv.pszVal, "pic", 0x757f044, id); forkthread(TlenSendMessageAckThread, 0, new SENDACKTHREADDATA(this, hContact, id)); } else { if ((msgEnc=TlenTextEncode(msg)) != NULL) { if (TlenListExist(this, LIST_CHATROOM, dbv.pszVal) && strchr(dbv.pszVal, '/') == NULL) strcpy(msgType, "groupchat"); else if (db_get_b(hContact, m_szModuleName, "bChat", FALSE)) strcpy(msgType, "privchat"); else strcpy(msgType, "chat"); if (!strcmp(msgType, "groupchat") || db_get_b(NULL, m_szModuleName, "MsgAck", FALSE) == FALSE) { SENDACKTHREADDATA *tdata = (SENDACKTHREADDATA*) mir_alloc(sizeof(SENDACKTHREADDATA)); tdata->proto = this; tdata->hContact = hContact; if (!strcmp(msgType, "groupchat")) TlenSend(this, "%s", dbv.pszVal, msgType, msgEnc); else if (!strcmp(msgType, "privchat")) TlenSend(this, "%s", dbv.pszVal, msgEnc); else TlenSend(this, "%s", dbv.pszVal, msgType, id, msgEnc); forkthread(TlenSendMessageAckThread, 0, new SENDACKTHREADDATA(this, hContact, id)); } else { if ((item=TlenListGetItemPtr(this, LIST_ROSTER, dbv.pszVal)) != NULL) item->idMsgAckPending = id; TlenSend(this, "%s", dbv.pszVal, msgType, id, msgEnc); } } mir_free(msgEnc); } mir_free(msg); db_free(&dbv); return id; } ///////////////////////////////////////////////////////////////////////////////////////// // TlenGetAvatarInfo - retrieves the avatar info INT_PTR TlenProtocol::GetAvatarInfo(WPARAM wParam, LPARAM lParam) { BOOL downloadingAvatar = FALSE; char *avatarHash = NULL; TLEN_LIST_ITEM *item = NULL; DBVARIANT dbv; PROTO_AVATAR_INFORMATIONT* AI = ( PROTO_AVATAR_INFORMATIONT* )lParam; if (!tlenOptions.enableAvatars) return GAIR_NOAVATAR; if (AI->hContact != NULL) { if (!db_get(AI->hContact, m_szModuleName, "jid", &dbv)) { item = TlenListGetItemPtr(this, LIST_ROSTER, dbv.pszVal); db_free(&dbv); if (item != NULL) { downloadingAvatar = item->newAvatarDownloading; avatarHash = item->avatarHash; } } } else if (threadData != NULL) avatarHash = threadData->avatarHash; if ((avatarHash == NULL || avatarHash[0] == '\0') && !downloadingAvatar) return GAIR_NOAVATAR; if (avatarHash != NULL && !downloadingAvatar) { TlenGetAvatarFileName(this, item, AI->filename, sizeof(AI->filename)); AI->format = ( AI->hContact == NULL ) ? threadData->avatarFormat : item->avatarFormat; return GAIR_SUCCESS; } /* get avatar */ if (( wParam & GAIF_FORCE ) != 0 && AI->hContact != NULL && isOnline) return GAIR_WAITFOR; return GAIR_NOAVATAR; } HANDLE TlenProtocol::GetAwayMsg(HANDLE hContact) { SENDACKTHREADDATA *tdata = new SENDACKTHREADDATA(this, hContact, 0); forkthread((void (__cdecl *)(void*))TlenGetAwayMsgThread, 0, (void*)tdata); return (HANDLE)1; } int TlenProtocol::RecvAwayMsg(HANDLE hContact, int mode, PROTORECVEVENT* evt) { return 0; } HANDLE TlenProtocol::FileAllow(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath) { TLEN_FILE_TRANSFER *ft; TLEN_LIST_ITEM *item; char *nick; if (!isOnline) return 0; ft = (TLEN_FILE_TRANSFER *) hTransfer; ft->szSavePath = mir_strdup(mir_t2a(szPath)); //TODO convert to PROTOCHAR* if ((item=TlenListAdd(this, LIST_FILE, ft->iqId)) != NULL) { item->ft = ft; } nick = TlenNickFromJID(ft->jid); if (ft->newP2P) { TlenSend(this, "", ft->jid, ft->jid, ft->iqId); } else { TlenSend(this, "", nick, ft->iqId); } mir_free(nick); return (HANDLE)hTransfer; } int TlenProtocol::FileDeny(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason) { TLEN_FILE_TRANSFER *ft; char *nick; if (!isOnline) return 1; ft = (TLEN_FILE_TRANSFER *) hTransfer; nick = TlenNickFromJID(ft->jid); if (ft->newP2P) { TlenSend(this, "", ft->iqId, nick);\ } else { TlenSend(this, "", ft->iqId, nick);\ } mir_free(nick); TlenP2PFreeFileTransfer(ft); return 0; } int TlenProtocol::FileResume(HANDLE hTransfer, int* action, const PROTOCHAR** szFilename) { return 0; } int TlenProtocol::FileCancel(HANDLE hContact, HANDLE hTransfer) { TLEN_FILE_TRANSFER *ft = (TLEN_FILE_TRANSFER *) hTransfer; debugLogA("Invoking FileCancel()"); if (ft->s != NULL) { ft->state = FT_ERROR; Netlib_CloseHandle(ft->s); ft->s = NULL; if (ft->hFileEvent != NULL) { HANDLE hEvent = ft->hFileEvent; ft->hFileEvent = NULL; SetEvent(hEvent); } } else { TlenP2PFreeFileTransfer(ft); } return 0; } HANDLE TlenProtocol::SendFile(HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles) { TLEN_FILE_TRANSFER *ft; int i, j; struct _stat statbuf; DBVARIANT dbv; char *nick, *p, idStr[10]; TLEN_LIST_ITEM *item; int id; if (!isOnline) return 0; // if (db_get_w(ccs->hContact, m_szModuleName, "Status", ID_STATUS_OFFLINE) == ID_STATUS_OFFLINE) return 0; if (db_get(hContact, m_szModuleName, "jid", &dbv)) return 0; ft = TlenFileCreateFT(this, dbv.pszVal); for (ft->fileCount=0; ppszFiles[ft->fileCount]; ft->fileCount++); ft->files = (char **) mir_alloc(sizeof(char *) * ft->fileCount); ft->filesSize = (long *) mir_alloc(sizeof(long) * ft->fileCount); ft->allFileTotalSize = 0; for (i=j=0; ifileCount; i++) { char* ppszFiles_i_A = mir_t2a(ppszFiles[i]); if (_stat(ppszFiles_i_A, &statbuf)) debugLogA("'%s' is an invalid filename", ppszFiles[i]); else { ft->filesSize[j] = statbuf.st_size; ft->files[j++] = mir_strdup(ppszFiles_i_A); ft->allFileTotalSize += statbuf.st_size; } mir_free(ppszFiles_i_A); } ft->fileCount = j; ft->szDescription = mir_t2a(szDescription); ft->hContact = hContact; ft->currentFile = 0; db_free(&dbv); id = TlenSerialNext(this); mir_snprintf(idStr, sizeof(idStr), "%d", id); if ((item=TlenListAdd(this, LIST_FILE, idStr)) != NULL) { ft->iqId = mir_strdup(idStr); nick = TlenNickFromJID(ft->jid); item->ft = ft; if (tlenOptions.useNewP2P) { TlenSend(this, "", ft->jid, ft->jid, idStr, ft->fileCount, ft->allFileTotalSize, ft->fileCount); ft->newP2P = TRUE; } else { if (ft->fileCount == 1) { char* ppszFiles_0_A = mir_t2a(ppszFiles[0]); if ((p=strrchr(ppszFiles_0_A, '\\')) != NULL) { p++; } else { p = ppszFiles_0_A; } p = TlenTextEncode(p); TlenSend(this, "", nick, p, idStr, ft->allFileTotalSize); mir_free(ppszFiles[0]); mir_free(p); } else { TlenSend(this, "", nick, idStr, ft->fileCount, ft->allFileTotalSize); } } mir_free(nick); } return (HANDLE) ft; } int TlenProtocol::SendContacts(HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList){ return 0; } int TlenProtocol::SendUrl(HANDLE hContact, int flags, const char* urlt){ return 0; } int TlenProtocol::RecvMsg(HANDLE hContact, PROTORECVEVENT* evt) { return Proto_RecvMessage(hContact, evt); } int TlenProtocol::RecvFile(HANDLE hContact, PROTOFILEEVENT* evt) { return Proto_RecvFile(hContact, evt); } int TlenProtocol::RecvUrl(HANDLE hContact, PROTORECVEVENT*) { return 0; } static char* settingToChar( DBCONTACTWRITESETTING* cws ) { switch( cws->value.type ) { case DBVT_ASCIIZ: return mir_strdup( cws->value.pszVal ); case DBVT_UTF8: return mir_utf8decode(mir_strdup(cws->value.pszVal), NULL); } return NULL; } int TlenProtocol::TlenDbSettingChanged(WPARAM wParam, LPARAM lParam) { DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam; // no action for hContact == NULL or when offline if ((HANDLE) wParam == NULL) return 0; if (!isConnected) return 0; if (!strcmp(cws->szModule, "CList")) { HANDLE hContact; DBVARIANT dbv; TLEN_LIST_ITEM *item; char *szProto, *nick, *jid, *group; hContact = (HANDLE) wParam; szProto = GetContactProto(hContact); if (szProto == NULL || strcmp(szProto, m_szModuleName)) return 0; // A contact's group is changed if (!strcmp(cws->szSetting, "Group")) { if (!db_get(hContact, m_szModuleName, "jid", &dbv)) { if ((item=TlenListGetItemPtr(this, LIST_ROSTER, dbv.pszVal)) != NULL) { db_free(&dbv); if (!db_get(hContact, "CList", "MyHandle", &dbv)) { nick = TlenTextEncode(dbv.pszVal); db_free(&dbv); } else if (!db_get(hContact, this->m_szModuleName, "Nick", &dbv)) { nick = TlenTextEncode(dbv.pszVal); db_free(&dbv); } else nick = TlenNickFromJID(item->jid); if (nick != NULL) { // Note: we need to compare with item->group to prevent infinite loop if (cws->value.type == DBVT_DELETED && item->group != NULL) { debugLogA("Group set to nothing"); TlenSend(this, "", nick, item->jid); } else if (cws->value.pszVal != NULL) { char *newGroup = settingToChar(cws); if (item->group == NULL || strcmp(newGroup, item->group)) { debugLogA("Group set to %s", newGroup); if ((group=TlenGroupEncode(newGroup)) != NULL) { TlenSend(this, "%s", nick, item->jid, group); mir_free(group); } } mir_free(newGroup); } mir_free(nick); } } else { db_free(&dbv); } } } // A contact is renamed else if (!strcmp(cws->szSetting, "MyHandle")) { char *newNick; // hContact = (HANDLE) wParam; // szProto = GetContactProto(hContact); // if (szProto == NULL || strcmp(szProto, proto->m_szModuleName)) return 0; if (!db_get(hContact, m_szModuleName, "jid", &dbv)) { jid = dbv.pszVal; if ((item=TlenListGetItemPtr(this, LIST_ROSTER, dbv.pszVal)) != NULL) { if (cws->value.type == DBVT_DELETED) { newNick = mir_strdup((char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_NOMYHANDLE)); } else if (cws->value.pszVal != NULL) { newNick = settingToChar(cws); } else { newNick = NULL; } // Note: we need to compare with item->nick to prevent infinite loop if (newNick != NULL && (item->nick == NULL || (item->nick != NULL && strcmp(item->nick, newNick)))) { if ((nick=TlenTextEncode(newNick)) != NULL) { debugLogA("Nick set to %s", newNick); if (item->group != NULL && (group=TlenGroupEncode(item->group)) != NULL) { TlenSend(this, "%s", nick, jid, group); mir_free(group); } else { TlenSend(this, "", nick, jid); } mir_free(nick); } } if (newNick != NULL) mir_free(newNick); } db_free(&dbv); } } // A temporary contact has been added permanently else if (!strcmp(cws->szSetting, "NotOnList")) { char *jid, *nick, *pGroup; if (cws->value.type==DBVT_DELETED || (cws->value.type==DBVT_BYTE && cws->value.bVal==0)) { if (!db_get(hContact, m_szModuleName, "jid", &dbv)) { jid = mir_strdup(dbv.pszVal); db_free(&dbv); debugLogA("Add %s permanently to list", jid); if (!db_get(hContact, "CList", "MyHandle", &dbv)) { nick = TlenTextEncode(dbv.pszVal); //Utf8Encode db_free(&dbv); } else { nick = TlenNickFromJID(jid); } if (nick != NULL) { debugLogA("jid=%s nick=%s", jid, nick); if (!db_get(hContact, "CList", "Group", &dbv)) { if ((pGroup=TlenGroupEncode(dbv.pszVal)) != NULL) { TlenSend(this, "%s", nick, jid, pGroup); TlenSend(this, "", jid); mir_free(pGroup); } db_free(&dbv); } else { TlenSend(this, "", nick, jid); TlenSend(this, "", jid); } mir_free(nick); db_unset(hContact, "CList", "Hidden"); } mir_free(jid); } } } } return 0; } int TlenProtocol::TlenContactDeleted(WPARAM wParam, LPARAM lParam) { if (!isOnline) // should never happen return 0; char *szProto = GetContactProto((HANDLE)wParam); if (szProto == NULL || strcmp(szProto, m_szModuleName)) return 0; DBVARIANT dbv; if (!db_get((HANDLE) wParam, m_szModuleName, "jid", &dbv)) { char *jid, *p, *q; jid = dbv.pszVal; if ((p=strchr(jid, '@')) != NULL) { if ((q=strchr(p, '/')) != NULL) *q = '\0'; } // Remove from roster, server also handles the presence unsubscription process. if (TlenListExist(this, LIST_ROSTER, jid)) TlenSend(this, "", jid); db_free(&dbv); } return 0; } int TlenProtocol::UserIsTyping(HANDLE hContact, int type) { DBVARIANT dbv; TLEN_LIST_ITEM *item; if (!isOnline) return 0; if (!db_get(hContact, m_szModuleName, "jid", &dbv)) { if ((item=TlenListGetItemPtr(this, LIST_ROSTER, dbv.pszVal)) != NULL /*&& item->wantComposingEvent == TRUE*/) { switch (type) { case PROTOTYPE_SELFTYPING_OFF: TlenSend(this, "", dbv.pszVal); break; case PROTOTYPE_SELFTYPING_ON: TlenSend(this, "", dbv.pszVal); break; } } db_free(&dbv); } return 0; } INT_PTR TlenProtocol::GetMyAvatar(WPARAM wParam, LPARAM lParam) { TCHAR* buf = (TCHAR*)wParam; int size = ( int )lParam; if ( buf == NULL || size <= 0 ) return -1; TlenGetAvatarFileName(this, NULL, buf, size); return 0; } static INT_PTR CALLBACK TlenChangeAvatarDlgProc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { switch ( msg ) { case WM_INITDIALOG: TranslateDialogDefault( hwndDlg ); { HICON hIcon = GetIcolibIcon(IDI_TLEN); SendMessage(hwndDlg, WM_SETICON, (WPARAM) ICON_BIG, (LPARAM) hIcon); ReleaseIcolibIcon(hIcon); } CheckDlgButton(hwndDlg, IDC_PUBLICAVATAR, TRUE); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: { int result = LOWORD(wParam); if (IsDlgButtonChecked(hwndDlg, IDC_PUBLICAVATAR)) { result |= 0x10000; } EndDialog(hwndDlg, result); } return TRUE; } break; } return 0; } INT_PTR TlenProtocol::SetMyAvatar(WPARAM wParam, LPARAM lParam) { TCHAR* szFileName = ( TCHAR* )lParam; TCHAR tFileName[ MAX_PATH ]; if (!isOnline) return 1; if (szFileName != NULL) { int result = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_USER_CHANGEAVATAR), NULL, TlenChangeAvatarDlgProc, (LPARAM) NULL); TlenGetAvatarFileName(this, NULL, tFileName, MAX_PATH); if ( CopyFile( szFileName, tFileName, FALSE ) == FALSE ) return 1; char* tFileNameA = mir_t2a(tFileName); //TODO - drop io.h int fileIn = open( tFileNameA, O_RDWR | O_BINARY, S_IREAD | S_IWRITE ); if ( fileIn != -1 ) { long dwPngSize = filelength(fileIn); BYTE* pResult = (BYTE *)mir_alloc(dwPngSize); if (pResult != NULL) { read( fileIn, pResult, dwPngSize ); close( fileIn ); TlenUploadAvatar(this, pResult, dwPngSize, (result & 0x10000) != 0); mir_free(pResult); } } mir_free(tFileName); mir_free(tFileNameA); } else TlenRemoveAvatar(this); return 0; } INT_PTR TlenProtocol::GetAvatarCaps(WPARAM wParam, LPARAM lParam) { switch (wParam) { case AF_MAXSIZE: { POINT* size = (POINT*)lParam; if ( size ) size->x = size->y = 64; } return 0; case AF_PROPORTION: return PIP_SQUARE; case AF_FORMATSUPPORTED: return (lParam == PA_FORMAT_PNG) ? 1 : 0; case AF_ENABLED: return (tlenOptions.enableAvatars && isOnline) ? 1 : 0; case AF_DONTNEEDDELAYS: return 1; case AF_MAXFILESIZE: return 10 * 1024; case AF_FETCHIFCONTACTOFFLINE: return 1; } return 0; } int TlenProtocol::OnEvent(PROTOEVENTTYPE iEventType, WPARAM wParam, LPARAM lParam) { //TlenProtocol *proto = (TlenProtocol *)ptr; switch( iEventType ) { case EV_PROTO_ONLOAD: return OnModulesLoaded(0, 0); case EV_PROTO_ONOPTIONS: return OptionsInit(wParam, lParam); case EV_PROTO_ONEXIT: return PreShutdown(0, 0); case EV_PROTO_ONRENAME: { CLISTMENUITEM mi = { sizeof(mi) }; mi.flags = CMIM_NAME | CMIF_TCHAR; mi.ptszName = m_tszUserName; Menu_ModifyItem(hMenuRoot, &mi); /* FIXME: Rename network user as well */ } } return 1; } // PSS_ADDED int TlenProtocol::AuthRecv(HANDLE hContact, PROTORECVEVENT* evt) { return 1; } // PSS_AUTHREQUEST int TlenProtocol::AuthRequest(HANDLE hContact, const PROTOCHAR* szMessage) { return 1; } HANDLE TlenProtocol::ChangeInfo(int iInfoType, void* pInfoData) { return NULL; } int TlenProtocol::RecvContacts(HANDLE hContact, PROTORECVEVENT* evt) { return 1; } extern INT_PTR CALLBACK TlenAccMgrUIDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); INT_PTR TlenProtocol::AccMgrUI(WPARAM wParam, LPARAM lParam) { return (INT_PTR)CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_ACCMGRUI), (HWND)lParam, TlenAccMgrUIDlgProc, (LPARAM)this); } void TlenInitServicesVTbl(TlenProtocol *proto) { proto->CreateProtoService(PS_GETNAME, &TlenProtocol::GetName); proto->CreateProtoService(PS_GETAVATARINFO, &TlenProtocol::GetAvatarInfo); proto->CreateProtoService(PS_SEND_NUDGE, &TlenProtocol::SendAlert); proto->CreateProtoService(PS_GETAVATARCAPS, &TlenProtocol::GetAvatarCaps); proto->CreateProtoService(PS_SETMYAVATART, &TlenProtocol::SetMyAvatar); proto->CreateProtoService(PS_GETMYAVATART, &TlenProtocol::GetMyAvatar); proto->CreateProtoService(PS_GETSTATUS, &TlenProtocol::GetStatus); proto->CreateProtoService(PS_CREATEACCMGRUI, &TlenProtocol::AccMgrUI); } TlenProtocol::TlenProtocol( const char *aProtoName, const TCHAR *aUserName) : PROTO(aProtoName, aUserName) { TlenInitServicesVTbl(this); InitializeCriticalSection(&modeMsgMutex); InitializeCriticalSection(&csSend); hTlenNudge = CreateProtoEvent("/Nudge"); HookProtoEvent(ME_OPT_INITIALISE, &TlenProtocol::OptionsInit); HookProtoEvent(ME_DB_CONTACT_SETTINGCHANGED, &TlenProtocol::TlenDbSettingChanged); HookProtoEvent(ME_DB_CONTACT_DELETED, &TlenProtocol::TlenContactDeleted); HookProtoEvent(ME_CLIST_PREBUILDCONTACTMENU, &TlenProtocol::PrebuildContactMenu); HookProtoEvent(ME_SYSTEM_PRESHUTDOWN, &TlenProtocol::PreShutdown); DBVARIANT dbv; if (!db_get(NULL, m_szModuleName, "LoginServer", &dbv)) db_free(&dbv); else db_set_s(NULL, m_szModuleName, "LoginServer", "tlen.pl"); if (!db_get(NULL, m_szModuleName, "ManualHost", &dbv)) db_free(&dbv); else db_set_s(NULL, m_szModuleName, "ManualHost", "s1.tlen.pl"); TlenLoadOptions(this); TlenWsInit(this); TlenSerialInit(this); TlenIqInit(this); TlenListInit(this); initMenuItems(); } TlenProtocol::~TlenProtocol() { uninitMenuItems(this); TlenVoiceCancelAll(this); TlenFileCancelAll(this); if (hTlenNudge) DestroyHookableEvent(hTlenNudge); TlenListUninit(this); TlenIqUninit(this); TlenSerialUninit(this); DeleteCriticalSection(&modeMsgMutex); DeleteCriticalSection(&csSend); TlenWsUninit(this); mir_free(modeMsgs.szOnline); mir_free(modeMsgs.szAway); mir_free(modeMsgs.szNa); mir_free(modeMsgs.szDnd); mir_free(modeMsgs.szFreechat); mir_free(modeMsgs.szInvisible); }