/* Jabber Protocol Plugin for Miranda IM Copyright (C) 2002-04 Santithorn Bunchua Copyright (C) 2005-12 George Hazan Copyright (C) 2007 Maxim Mluhov 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 "jabber.h" #include #include #include #include #include "jabber_list.h" #include "jabber_iq.h" #include "jabber_caps.h" #include "m_file.h" #include "m_addcontact.h" #include "jabber_disco.h" #include "m_proto_listeningto.h" ///////////////////////////////////////////////////////////////////////////////////////// // GetMyAwayMsg - obtain the current away message INT_PTR __cdecl CJabberProto::GetMyAwayMsg(WPARAM wParam, LPARAM lParam) { TCHAR *szStatus = NULL; INT_PTR nRetVal = 0; EnterCriticalSection(&m_csModeMsgMutex); switch (wParam ? (int)wParam : m_iStatus) { case ID_STATUS_ONLINE: szStatus = m_modeMsgs.szOnline; break; case ID_STATUS_AWAY: case ID_STATUS_ONTHEPHONE: case ID_STATUS_OUTTOLUNCH: szStatus = m_modeMsgs.szAway; break; case ID_STATUS_NA: szStatus = m_modeMsgs.szNa; break; case ID_STATUS_DND: case ID_STATUS_OCCUPIED: szStatus = m_modeMsgs.szDnd; break; case ID_STATUS_FREECHAT: szStatus = m_modeMsgs.szFreechat; break; default: // Should not reach here break; } if (szStatus) nRetVal = (lParam & SGMA_UNICODE) ? (INT_PTR)mir_t2u(szStatus) : (INT_PTR)mir_t2a(szStatus); LeaveCriticalSection(&m_csModeMsgMutex); return nRetVal; } ///////////////////////////////////////////////////////////////////////////////////////// // JabberGetAvatar - retrieves the file name of my own avatar INT_PTR __cdecl CJabberProto::JabberGetAvatar(WPARAM wParam, LPARAM lParam) { TCHAR* buf = (TCHAR*)wParam; int size = (int)lParam; if (buf == NULL || size <= 0) return -1; if ( !m_options.EnableAvatars) return -2; GetAvatarFileName(NULL, buf, size); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // JabberGetAvatarCaps - returns directives how to process avatars INT_PTR __cdecl CJabberProto::JabberGetAvatarCaps(WPARAM wParam, LPARAM lParam) { switch(wParam) { case AF_MAXSIZE: { POINT* size = (POINT*)lParam; if (size) size->x = size->y = 96; } return 0; case AF_PROPORTION: return PIP_NONE; case AF_FORMATSUPPORTED: // Jabber supports avatars of virtually all formats return 1; case AF_ENABLED: return m_options.EnableAvatars; } return -1; } ///////////////////////////////////////////////////////////////////////////////////////// // JabberGetAvatarInfo - retrieves the avatar info INT_PTR __cdecl CJabberProto::JabberGetAvatarInfo(WPARAM wParam, LPARAM lParam) { if ( !m_options.EnableAvatars) return GAIR_NOAVATAR; PROTO_AVATAR_INFORMATIONT* AI = (PROTO_AVATAR_INFORMATIONT*)lParam; char szHashValue[ MAX_PATH ]; if (JGetStaticString("AvatarHash", AI->hContact, szHashValue, sizeof szHashValue)) { Log("No avatar"); return GAIR_NOAVATAR; } TCHAR tszFileName[ MAX_PATH ]; GetAvatarFileName(AI->hContact, tszFileName, SIZEOF(tszFileName)); _tcsncpy(AI->filename, tszFileName, SIZEOF(AI->filename)); AI->format = (AI->hContact == NULL) ? PA_FORMAT_PNG : JGetByte(AI->hContact, "AvatarType", 0); if (::_taccess(AI->filename, 0) == 0) { char szSavedHash[ 256 ]; if ( !JGetStaticString("AvatarSaved", AI->hContact, szSavedHash, sizeof szSavedHash)) { if ( !strcmp(szSavedHash, szHashValue)) { Log("Avatar is Ok: %s == %s", szSavedHash, szHashValue); return GAIR_SUCCESS; } } } if ((wParam & GAIF_FORCE) != 0 && AI->hContact != NULL && m_bJabberOnline) { DBVARIANT dbv; if ( !JGetStringT(AI->hContact, "jid", &dbv)) { JABBER_LIST_ITEM* item = ListGetItemPtr(LIST_ROSTER, dbv.ptszVal); if (item != NULL) { BOOL isXVcard = JGetByte(AI->hContact, "AvatarXVcard", 0); TCHAR szJid[ JABBER_MAX_JID_LEN ]; if (item->resourceCount != NULL && !isXVcard) { TCHAR *bestResName = ListGetBestClientResourceNamePtr(dbv.ptszVal); mir_sntprintf(szJid, SIZEOF(szJid), bestResName?_T("%s/%s"):_T("%s"), dbv.ptszVal, bestResName); } else lstrcpyn(szJid, dbv.ptszVal, SIZEOF(szJid)); Log("Rereading %s for " TCHAR_STR_PARAM, isXVcard ? JABBER_FEAT_VCARD_TEMP : JABBER_FEAT_AVATAR, szJid); int iqId = SerialNext(); if (isXVcard) IqAdd(iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultGetVCardAvatar); else IqAdd(iqId, IQ_PROC_NONE, &CJabberProto::OnIqResultGetClientAvatar); XmlNodeIq iq(_T("get"), iqId, szJid); if (isXVcard) iq << XCHILDNS(_T("vCard"), _T(JABBER_FEAT_VCARD_TEMP)); else iq << XQUERY(isXVcard ? _T("") : _T(JABBER_FEAT_AVATAR)); m_ThreadInfo->send(iq); db_free(&dbv); return GAIR_WAITFOR; } db_free(&dbv); } } Log("No avatar"); return GAIR_NOAVATAR; } //////////////////////////////////////////////////////////////////////////////////////// // JabberGetEventTextChatStates - retrieves a chat state description from an event INT_PTR __cdecl CJabberProto::OnGetEventTextChatStates(WPARAM, LPARAM lParam) { DBEVENTGETTEXT *pdbEvent = (DBEVENTGETTEXT *)lParam; INT_PTR nRetVal = 0; if (pdbEvent->dbei->cbBlob > 0) { if (pdbEvent->dbei->pBlob[0] == JABBER_DB_EVENT_CHATSTATES_GONE) { if (pdbEvent->datatype == DBVT_WCHAR) nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("closed chat session"))); else if (pdbEvent->datatype == DBVT_ASCIIZ) nRetVal = (INT_PTR)mir_strdup(Translate("closed chat session")); } } return nRetVal; } //////////////////////////////////////////////////////////////////////////////////////// // OnGetEventTextPresence - retrieves presence state description from an event INT_PTR __cdecl CJabberProto::OnGetEventTextPresence(WPARAM, LPARAM lParam) { DBEVENTGETTEXT *pdbEvent = (DBEVENTGETTEXT *)lParam; INT_PTR nRetVal = 0; if (pdbEvent->dbei->cbBlob > 0) { switch (pdbEvent->dbei->pBlob[0]) { case JABBER_DB_EVENT_PRESENCE_SUBSCRIBE: { if (pdbEvent->datatype == DBVT_WCHAR) nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("sent subscription request"))); else if (pdbEvent->datatype == DBVT_ASCIIZ) nRetVal = (INT_PTR)mir_strdup(Translate("sent subscription request")); break; } case JABBER_DB_EVENT_PRESENCE_SUBSCRIBED: { if (pdbEvent->datatype == DBVT_WCHAR) nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("approved subscription request"))); else if (pdbEvent->datatype == DBVT_ASCIIZ) nRetVal = (INT_PTR)mir_strdup(Translate("approved subscription request")); break; } case JABBER_DB_EVENT_PRESENCE_UNSUBSCRIBE: { if (pdbEvent->datatype == DBVT_WCHAR) nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("declined subscription"))); else if (pdbEvent->datatype == DBVT_ASCIIZ) nRetVal = (INT_PTR)mir_strdup(Translate("declined subscription")); break; } case JABBER_DB_EVENT_PRESENCE_UNSUBSCRIBED: { if (pdbEvent->datatype == DBVT_WCHAR) nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("declined subscription"))); else if (pdbEvent->datatype == DBVT_ASCIIZ) nRetVal = (INT_PTR)mir_strdup(Translate("declined subscription")); break; } case JABBER_DB_EVENT_PRESENCE_ERROR: { if (pdbEvent->datatype == DBVT_WCHAR) nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("sent error presence"))); else if (pdbEvent->datatype == DBVT_ASCIIZ) nRetVal = (INT_PTR)mir_strdup(Translate("sent error presence")); break; } default: { if (pdbEvent->datatype == DBVT_WCHAR) nRetVal = (INT_PTR)mir_tstrdup(TranslateTS(_T("sent unknown presence type"))); else if (pdbEvent->datatype == DBVT_ASCIIZ) nRetVal = (INT_PTR)mir_strdup(Translate("sent unknown presence type")); break; } } } return nRetVal; } //////////////////////////////////////////////////////////////////////////////////////// // JabberSetAvatar - sets an avatar without UI INT_PTR __cdecl CJabberProto::JabberSetAvatar(WPARAM, LPARAM lParam) { TCHAR* tszFileName = (TCHAR*)lParam; if (m_bJabberOnline) { SetServerVcard(TRUE, tszFileName); SendPresence(m_iDesiredStatus, false); } else if (tszFileName == NULL || tszFileName[0] == 0) { // Remove avatar TCHAR tFileName[ MAX_PATH ]; GetAvatarFileName(NULL, tFileName, MAX_PATH); DeleteFile(tFileName); JDeleteSetting(NULL, "AvatarSaved"); JDeleteSetting(NULL, "AvatarHash"); } else { int fileIn = _topen(tszFileName, O_RDWR | O_BINARY, S_IREAD | S_IWRITE); if (fileIn == -1) { mir_free(tszFileName); return 1; } long dwPngSize = _filelength(fileIn); char* pResult = new char[ dwPngSize ]; if (pResult == NULL) { _close(fileIn); mir_free(tszFileName); return 2; } _read(fileIn, pResult, dwPngSize); _close(fileIn); mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE]; mir_sha1_ctx sha1ctx; mir_sha1_init(&sha1ctx); mir_sha1_append(&sha1ctx, (mir_sha1_byte_t*)pResult, dwPngSize); mir_sha1_finish(&sha1ctx, digest); TCHAR tFileName[ MAX_PATH ]; GetAvatarFileName(NULL, tFileName, MAX_PATH); DeleteFile(tFileName); char buf[MIR_SHA1_HASH_SIZE*2+1]; for (int i=0; isend((char*)lParam); } ///////////////////////////////////////////////////////////////////////////////////////// // "/GCGetToolTipText" - gets tooltip text static const TCHAR * JabberEnum2AffilationStr[]={ _T("None"), _T("Outcast"), _T("Member"), _T("Admin"), _T("Owner") }; static const TCHAR * JabberEnum2RoleStr[]={ _T("None"), _T("Visitor"), _T("Participant"), _T("Moderator") }; //FIXME Table conversion fast but is not safe static const TCHAR * JabberEnum2StatusStr[]= { _T("Offline"), _T("Online"), _T("Away"), _T("DND"), _T("NA"), _T("Occupied"), _T("Free for chat"), _T("Invisible"), _T("On the phone"), _T("Out to lunch"), _T("Idle") }; static void appendString(bool bIsTipper, const TCHAR *tszTitle, const TCHAR *tszValue, TCHAR* buf, size_t bufSize) { if (*buf) { const TCHAR *szSeparator = (bIsTipper) ? _T("\n") : ((IsWinVerMEPlus()) ? _T("\r\n") : _T(" | ")); _tcsncat(buf, szSeparator, bufSize); } size_t len = _tcslen(buf); buf += len; bufSize -= len; if (bIsTipper) mir_sntprintf(buf, bufSize, _T("%s%s%s%s"), _T(""), TranslateTS(tszTitle), _T("\t"), tszValue); else { TCHAR* p = TranslateTS(tszTitle); mir_sntprintf(buf, bufSize, _T("%s%s\t%s"), p, _tcslen(p)<=7 ? _T("\t") : _T(""), tszValue); } } INT_PTR __cdecl CJabberProto::JabberGCGetToolTipText(WPARAM wParam, LPARAM lParam) { if ( !wParam || !lParam) return 0; //room global tooltip not supported yet JABBER_LIST_ITEM* item = ListGetItemPtr(LIST_CHATROOM, (TCHAR*)wParam); if (item == NULL) return 0; //no room found JABBER_RESOURCE_STATUS * info = NULL; for (int i=0; i < item->resourceCount; i++) { JABBER_RESOURCE_STATUS& p = item->resource[i]; if ( !lstrcmp(p.resourceName, (TCHAR*)lParam)) { info = &p; break; } } if (info == NULL) return 0; //no info found // ok process info output will be: // JID: real@jid/resource or // Nick: Nickname // Status: StatusText // Role: Moderator // Affiliation: Affiliation TCHAR outBuf[2048]; outBuf[0]=_T('\0'); bool bIsTipper = db_get_b(NULL, "Tab_SRMsg", "adv_TipperTooltip", 0) && ServiceExists("mToolTip/HideTip"); //JID: if (_tcschr(info->resourceName, _T('@')) != NULL) appendString(bIsTipper, _T("JID:"), info->resourceName, outBuf, SIZEOF(outBuf)); else if (lParam) { //or simple nick appendString(bIsTipper, _T("Nick:"), (TCHAR*) lParam, outBuf, SIZEOF(outBuf)); } // status if (info->status >= ID_STATUS_OFFLINE && info->status <= ID_STATUS_IDLE ) appendString(bIsTipper, _T("Status:"), TranslateTS(JabberEnum2StatusStr[info->status-ID_STATUS_OFFLINE]), outBuf, SIZEOF(outBuf)); // status text if (info->statusMessage) appendString(bIsTipper, _T("Status text:"), info->statusMessage, outBuf, SIZEOF(outBuf)); // Role appendString(bIsTipper, _T("Role:"), TranslateTS(JabberEnum2RoleStr[info->role]), outBuf, SIZEOF(outBuf)); // Affiliation appendString(bIsTipper, _T("Affiliation:"), TranslateTS(JabberEnum2AffilationStr[info->affiliation]), outBuf, SIZEOF(outBuf)); // real jid if (info->szRealJid) appendString(bIsTipper, _T("Real JID:"), info->szRealJid, outBuf, SIZEOF(outBuf)); return (INT_PTR)(outBuf[0] == 0 ? NULL : mir_tstrdup(outBuf)); } // File Association Manager plugin support INT_PTR __cdecl CJabberProto::JabberServiceParseXmppURI(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); TCHAR *arg = (TCHAR *)lParam; if (arg == NULL) return 1; TCHAR szUri[ 1024 ]; mir_sntprintf(szUri, SIZEOF(szUri), _T("%s"), arg); TCHAR *szJid = szUri; // skip leading prefix szJid = _tcschr(szJid, _T(':')); if (szJid == NULL) return 1; // skip // for (++szJid; *szJid == _T('/'); ++szJid); // empty jid? if ( !*szJid) return 1; // command code TCHAR *szCommand = szJid; szCommand = _tcschr(szCommand, _T('?')); if (szCommand) *(szCommand++) = 0; // parameters TCHAR *szSecondParam = szCommand ? _tcschr(szCommand, _T(';')) : NULL; if (szSecondParam) *(szSecondParam++) = 0; // TCHAR *szThirdParam = szSecondParam ? _tcschr(szSecondParam, _T(';')) : NULL; // if (szThirdParam) // *(szThirdParam++) = 0; // no command or message command if ( !szCommand || (szCommand && !_tcsicmp(szCommand, _T("message")))) { // message if (ServiceExists(MS_MSG_SENDMESSAGE)) { HANDLE hContact = HContactFromJID(szJid, TRUE); TCHAR *szMsgBody = NULL; if ( !hContact) hContact = DBCreateContact(szJid, szJid, TRUE, TRUE); if ( !hContact) return 1; if (szSecondParam) { //there are parameters to message szMsgBody = _tcsstr(szSecondParam, _T("body=")); if (szMsgBody) { szMsgBody += 5; TCHAR* szDelim = _tcschr(szMsgBody, _T(';')); if (szDelim) szDelim = 0; JabberHttpUrlDecode(szMsgBody); } } CallService(MS_MSG_SENDMESSAGE "W",(WPARAM)hContact, (LPARAM)szMsgBody); return 0; } return 1; } else if ( !_tcsicmp(szCommand, _T("roster"))) { if ( !HContactFromJID(szJid)) { JABBER_SEARCH_RESULT jsr = { 0 }; jsr.hdr.cbSize = sizeof(JABBER_SEARCH_RESULT); jsr.hdr.flags = PSR_TCHAR; jsr.hdr.nick = szJid; jsr.hdr.id = szJid; _tcsncpy(jsr.jid, szJid, SIZEOF(jsr.jid) - 1); ADDCONTACTSTRUCT acs; acs.handleType = HANDLE_SEARCHRESULT; acs.szProto = m_szModuleName; acs.psr = &jsr.hdr; CallService(MS_ADDCONTACT_SHOW, 0, (LPARAM)&acs); } return 0; } else if ( !_tcsicmp(szCommand, _T("join"))) { // chat join invitation GroupchatJoinRoomByJid(NULL, szJid); return 0; } else if ( !_tcsicmp(szCommand, _T("disco"))) { // service discovery request OnMenuHandleServiceDiscovery(0, (LPARAM)szJid); return 0; } else if ( !_tcsicmp(szCommand, _T("command"))) { // ad-hoc commands if (szSecondParam) { if ( !_tcsnicmp(szSecondParam, _T("node="), 5)) { szSecondParam += 5; if ( !*szSecondParam) szSecondParam = NULL; } else szSecondParam = NULL; } CJabberAdhocStartupParams* pStartupParams = new CJabberAdhocStartupParams(this, szJid, szSecondParam); ContactMenuRunCommands(0, (LPARAM)pStartupParams); return 0; } else if ( !_tcsicmp(szCommand, _T("sendfile"))) { // send file if (ServiceExists(MS_FILE_SENDFILE)) { HANDLE hContact = HContactFromJID(szJid, TRUE); if ( !hContact) hContact = DBCreateContact(szJid, szJid, TRUE, TRUE); if ( !hContact) return 1; CallService(MS_FILE_SENDFILE, (WPARAM)hContact, (LPARAM)NULL); return 0; } return 1; } return 1; /* parse failed */ } // XEP-0224 support (Attention/Nudge) INT_PTR __cdecl CJabberProto::JabberSendNudge(WPARAM wParam, LPARAM) { if ( !m_bJabberOnline) return 0; HANDLE hContact = (HANDLE)wParam; DBVARIANT dbv; if ( !JGetStringT(hContact, "jid", &dbv)) { TCHAR tszJid[ JABBER_MAX_JID_LEN ]; TCHAR *szResource = ListGetBestClientResourceNamePtr(dbv.ptszVal); if (szResource) mir_sntprintf(tszJid, SIZEOF(tszJid), _T("%s/%s"), dbv.ptszVal, szResource); else mir_sntprintf(tszJid, SIZEOF(tszJid), _T("%s"), dbv.ptszVal); db_free(&dbv); JabberCapsBits jcb = GetResourceCapabilites(tszJid, FALSE); m_ThreadInfo->send( XmlNode(_T("message")) << XATTR(_T("type"), _T("headline")) << XATTR(_T("to"), tszJid) << XCHILDNS(_T("attention"), jcb & JABBER_CAPS_ATTENTION ? _T(JABBER_FEAT_ATTENTION) : _T(JABBER_FEAT_ATTENTION_0))); } return 0; } BOOL CJabberProto::SendHttpAuthReply(CJabberHttpAuthParams *pParams, BOOL bAuthorized) { if ( !m_bJabberOnline || !pParams || !m_ThreadInfo) return FALSE; if (pParams->m_nType == CJabberHttpAuthParams::IQ) { XmlNodeIq iq(bAuthorized ? _T("result") : _T("error"), pParams->m_szIqId, pParams->m_szFrom); if ( !bAuthorized) { iq << XCHILDNS(_T("confirm"), _T(JABBER_FEAT_HTTP_AUTH)) << XATTR(_T("id"), pParams->m_szId) << XATTR(_T("method"), pParams->m_szMethod) << XATTR(_T("url"), pParams->m_szUrl); iq << XCHILD(_T("error")) << XATTRI(_T("code"), 401) << XATTR(_T("type"), _T("auth")) << XCHILDNS(_T("not-authorized"), _T("urn:ietf:params:xml:xmpp-stanzas")); } m_ThreadInfo->send(iq); } else if (pParams->m_nType == CJabberHttpAuthParams::MSG) { XmlNode msg(_T("message")); msg << XATTR(_T("to"), pParams->m_szFrom); if ( !bAuthorized) msg << XATTR(_T("type"), _T("error")); if (pParams->m_szThreadId) msg << XCHILD(_T("thread"), pParams->m_szThreadId); msg << XCHILDNS(_T("confirm"), _T(JABBER_FEAT_HTTP_AUTH)) << XATTR(_T("id"), pParams->m_szId) << XATTR(_T("method"), pParams->m_szMethod) << XATTR(_T("url"), pParams->m_szUrl); if ( !bAuthorized) msg << XCHILD(_T("error")) << XATTRI(_T("code"), 401) << XATTR(_T("type"), _T("auth")) << XCHILDNS(_T("not-authorized"), _T("urn:ietf:params:xml:xmpp-stanzas")); m_ThreadInfo->send(msg); } else return FALSE; return TRUE; } class CJabberDlgHttpAuth: public CJabberDlgBase { typedef CJabberDlgBase CSuper; public: CJabberDlgHttpAuth(CJabberProto *proto, HWND hwndParent, CJabberHttpAuthParams *pParams): CSuper(proto, IDD_HTTP_AUTH, hwndParent, true), m_txtInfo(this, IDC_EDIT_HTTP_AUTH_INFO), m_btnAuth(this, IDOK), m_btnDeny(this, IDCANCEL), m_pParams(pParams) { m_btnAuth.OnClick = Callback(this, &CJabberDlgHttpAuth::btnAuth_OnClick); m_btnDeny.OnClick = Callback(this, &CJabberDlgHttpAuth::btnDeny_OnClick); } void OnInitDialog() { CSuper::OnInitDialog(); WindowSetIcon(m_hwnd, m_proto, "openid"); SetDlgItemText(m_hwnd, IDC_TXT_URL, m_pParams->m_szUrl); SetDlgItemText(m_hwnd, IDC_TXT_FROM, m_pParams->m_szFrom); SetDlgItemText(m_hwnd, IDC_TXT_ID, m_pParams->m_szId); SetDlgItemText(m_hwnd, IDC_TXT_METHOD, m_pParams->m_szMethod); } BOOL SendReply(BOOL bAuthorized) { BOOL bRetVal = m_proto->SendHttpAuthReply(m_pParams, bAuthorized); m_pParams->Free(); mir_free(m_pParams); m_pParams = NULL; return bRetVal; } void btnAuth_OnClick(CCtrlButton*) { SendReply(TRUE); Close(); } void btnDeny_OnClick(CCtrlButton*) { SendReply(FALSE); Close(); } UI_MESSAGE_MAP(CJabberDlgHttpAuth, CSuper); UI_MESSAGE(WM_CTLCOLORSTATIC, OnCtlColorStatic); UI_MESSAGE_MAP_END(); INT_PTR OnCtlColorStatic(UINT, WPARAM, LPARAM) { return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); } private: CCtrlEdit m_txtInfo; CCtrlButton m_btnAuth; CCtrlButton m_btnDeny; CJabberHttpAuthParams *m_pParams; }; // XEP-0070 support (http auth) INT_PTR __cdecl CJabberProto::OnHttpAuthRequest(WPARAM wParam, LPARAM lParam) { CLISTEVENT *pCle = (CLISTEVENT *)lParam; CJabberHttpAuthParams *pParams = (CJabberHttpAuthParams *)pCle->lParam; if ( !pParams) return 0; CJabberDlgHttpAuth *pDlg = new CJabberDlgHttpAuth(this, (HWND)wParam, pParams); if ( !pDlg) { pParams->Free(); mir_free(pParams); return 0; } pDlg->Show(); return 0; } // Jabber API functions INT_PTR __cdecl CJabberProto::JabberGetApi(WPARAM wParam, LPARAM lParam) { IJabberInterface **ji = (IJabberInterface**)lParam; if ( !ji) return -1; *ji = &m_JabberApi; return 0; } DWORD CJabberInterface::GetFlags() const { return JIF_UNICODE; } int CJabberInterface::GetVersion() const { return 1; } DWORD CJabberInterface::GetJabberVersion() const { return __VERSION_DWORD; } IJabberSysInterface *CJabberInterface::Sys() const { return &m_psProto->m_JabberSysApi; } IJabberNetInterface *CJabberInterface::Net() const { return &m_psProto->m_JabberNetApi; } int CJabberSysInterface::GetVersion() const { return 1; } int CJabberSysInterface::CompareJIDs(LPCTSTR jid1, LPCTSTR jid2) { if ( !jid1 || !jid2) return 0; return JabberCompareJids(jid1, jid2); } HANDLE CJabberSysInterface::ContactFromJID(LPCTSTR jid) { if ( !jid) return NULL; return m_psProto->HContactFromJID(jid); } LPTSTR CJabberSysInterface::ContactToJID(HANDLE hContact) { return m_psProto->JGetStringT(hContact, m_psProto->JGetByte(hContact, "ChatRoom", 0) ? "ChatRoomID" : "jid"); } LPTSTR CJabberSysInterface::GetBestResourceName(LPCTSTR jid) { if (jid == NULL) return NULL; LPCTSTR p = _tcschr(jid, '/'); if (p == NULL) { m_psProto->ListLock(); // make sure we allow access to the list only after making mir_tstrdup() of resource name LPTSTR res = mir_tstrdup(m_psProto->ListGetBestClientResourceNamePtr(jid)); m_psProto->ListUnlock(); return res; } return mir_tstrdup(jid); } LPTSTR CJabberSysInterface::GetResourceList(LPCTSTR jid) { if ( !jid) return NULL; m_psProto->ListLock(); JABBER_LIST_ITEM *item = NULL; if ((item = m_psProto->ListGetItemPtr(LIST_VCARD_TEMP, jid)) == NULL) item = m_psProto->ListGetItemPtr(LIST_ROSTER, jid); if (item == NULL) { m_psProto->ListUnlock(); return NULL; } if (item->resource == NULL) { m_psProto->ListUnlock(); return NULL; } int i; int iLen = 1; // 1 for extra zero terminator at the end of the string // calculate total necessary string length for (i=0; iresourceCount; i++) { iLen += lstrlen(item->resource[i].resourceName) + 1; } // allocate memory and fill it LPTSTR str = (LPTSTR)mir_alloc(iLen * sizeof(TCHAR)); LPTSTR p = str; for (i=0; iresourceCount; i++) { lstrcpy(p, item->resource[i].resourceName); p += lstrlen(item->resource[i].resourceName) + 1; } *p = 0; // extra zero terminator m_psProto->ListUnlock(); return str; } char *CJabberSysInterface::GetModuleName() const { return m_psProto->m_szModuleName; } int CJabberNetInterface::GetVersion() const { return 1; } unsigned int CJabberNetInterface::SerialNext() { return m_psProto->SerialNext(); } int CJabberNetInterface::SendXmlNode(HXML node) { return m_psProto->m_ThreadInfo->send(node); } typedef struct { JABBER_HANDLER_FUNC Func; void *pUserData; } sHandlerData; void CJabberProto::ExternalTempIqHandler(HXML node, CJabberIqInfo *pInfo) { sHandlerData *d = (sHandlerData*)pInfo->GetUserData(); d->Func(&m_JabberApi, node, d->pUserData); free(d); // free IqHandlerData allocated in CJabberNetInterface::AddIqHandler below } BOOL CJabberProto::ExternalIqHandler(HXML node, CJabberIqInfo *pInfo) { sHandlerData *d = (sHandlerData*)pInfo->GetUserData(); return d->Func(&m_JabberApi, node, d->pUserData); } BOOL CJabberProto::ExternalMessageHandler(HXML node, ThreadData *pThreadData, CJabberMessageInfo* pInfo) { sHandlerData *d = (sHandlerData*)pInfo->GetUserData(); return d->Func(&m_JabberApi, node, d->pUserData); } BOOL CJabberProto::ExternalPresenceHandler(HXML node, ThreadData *pThreadData, CJabberPresenceInfo* pInfo) { sHandlerData *d = (sHandlerData*)pInfo->GetUserData(); return d->Func(&m_JabberApi, node, d->pUserData); } BOOL CJabberProto::ExternalSendHandler(HXML node, ThreadData *pThreadData, CJabberSendInfo* pInfo) { sHandlerData *d = (sHandlerData*)pInfo->GetUserData(); return d->Func(&m_JabberApi, node, d->pUserData); } HJHANDLER CJabberNetInterface::AddPresenceHandler(JABBER_HANDLER_FUNC Func, void *pUserData, int iPriority) { sHandlerData *d = (sHandlerData*)malloc(sizeof(sHandlerData)); d->Func = Func; d->pUserData = pUserData; return (HJHANDLER)m_psProto->m_presenceManager.AddPermanentHandler(&CJabberProto::ExternalPresenceHandler, d, free, iPriority); } HJHANDLER CJabberNetInterface::AddMessageHandler(JABBER_HANDLER_FUNC Func, int iMsgTypes, LPCTSTR szXmlns, LPCTSTR szTag, void *pUserData, int iPriority) { sHandlerData *d = (sHandlerData*)malloc(sizeof(sHandlerData)); d->Func = Func; d->pUserData = pUserData; return (HJHANDLER)m_psProto->m_messageManager.AddPermanentHandler(&CJabberProto::ExternalMessageHandler, iMsgTypes, 0, szXmlns, FALSE, szTag, d, free, iPriority); } HJHANDLER CJabberNetInterface::AddIqHandler(JABBER_HANDLER_FUNC Func, int iIqTypes, LPCTSTR szXmlns, LPCTSTR szTag, void *pUserData, int iPriority) { sHandlerData *d = (sHandlerData*)malloc(sizeof(sHandlerData)); d->Func = Func; d->pUserData = pUserData; return (HJHANDLER)m_psProto->m_iqManager.AddPermanentHandler(&CJabberProto::ExternalIqHandler, iIqTypes, 0, szXmlns, FALSE, szTag, d, free, iPriority); } HJHANDLER CJabberNetInterface::AddTemporaryIqHandler(JABBER_HANDLER_FUNC Func, int iIqTypes, int iIqId, void *pUserData, DWORD dwTimeout, int iPriority) { sHandlerData *d = (sHandlerData*)malloc(sizeof(sHandlerData)); d->Func = Func; d->pUserData = pUserData; CJabberIqInfo* pInfo = m_psProto->m_iqManager.AddHandler(&CJabberProto::ExternalTempIqHandler, iIqTypes, NULL, 0, iIqId, d, iPriority); if (pInfo && dwTimeout > 0) pInfo->SetTimeout(dwTimeout); return (HJHANDLER)pInfo; } HJHANDLER CJabberNetInterface::AddSendHandler(JABBER_HANDLER_FUNC Func, void *pUserData, int iPriority) { sHandlerData *d = (sHandlerData*)malloc(sizeof(sHandlerData)); d->Func = Func; d->pUserData = pUserData; return (HJHANDLER)m_psProto->m_sendManager.AddPermanentHandler(&CJabberProto::ExternalSendHandler, d, free, iPriority); } int CJabberNetInterface::RemoveHandler(HJHANDLER hHandler) { return m_psProto->m_sendManager.DeletePermanentHandler((CJabberSendPermanentInfo*)hHandler) || m_psProto->m_presenceManager.DeletePermanentHandler((CJabberPresencePermanentInfo*)hHandler) || m_psProto->m_messageManager.DeletePermanentHandler((CJabberMessagePermanentInfo*)hHandler) || m_psProto->m_iqManager.DeletePermanentHandler((CJabberIqPermanentInfo*)hHandler) || m_psProto->m_iqManager.DeleteHandler((CJabberIqInfo*)hHandler); } JabberFeatCapPairDynamic *CJabberNetInterface::FindFeature(LPCTSTR szFeature) { int i; for (i = 0; i < m_psProto->m_lstJabberFeatCapPairsDynamic.getCount(); i++) if ( !lstrcmp(m_psProto->m_lstJabberFeatCapPairsDynamic[i]->szFeature, szFeature)) return m_psProto->m_lstJabberFeatCapPairsDynamic[i]; return NULL; } int CJabberNetInterface::RegisterFeature(LPCTSTR szFeature, LPCTSTR szDescription) { if ( !szFeature) { return false; } // check for this feature in core features, and return false if it's present, to prevent re-registering a core feature int i; for (i = 0; g_JabberFeatCapPairs[i].szFeature; i++) { if ( !lstrcmp(g_JabberFeatCapPairs[i].szFeature, szFeature)) { return false; } } m_psProto->ListLock(); JabberFeatCapPairDynamic *fcp = FindFeature(szFeature); if ( !fcp) { // if the feature is not registered yet, allocate new bit for it JabberCapsBits jcb = JABBER_CAPS_OTHER_SPECIAL; // set all bits not included in g_JabberFeatCapPairs // set all bits occupied by g_JabberFeatCapPairs for (i = 0; g_JabberFeatCapPairs[i].szFeature; i++) jcb |= g_JabberFeatCapPairs[i].jcbCap; // set all bits already occupied by external plugins for (i = 0; i < m_psProto->m_lstJabberFeatCapPairsDynamic.getCount(); i++) jcb |= m_psProto->m_lstJabberFeatCapPairsDynamic[i]->jcbCap; // Now get first zero bit. The line below is a fast way to do it. If there are no zero bits, it returns 0. jcb = (~jcb) & (JabberCapsBits)(-(__int64)(~jcb)); if ( !jcb) { // no more free bits m_psProto->ListUnlock(); return false; } LPTSTR szExt = mir_tstrdup(szFeature); LPTSTR pSrc, pDst; for (pSrc = szExt, pDst = szExt; *pSrc; pSrc++) { // remove unnecessary symbols from szFeature to make the string shorter, and use it as szExt if (_tcschr(_T("bcdfghjklmnpqrstvwxz0123456789"), *pSrc)) { *pDst++ = *pSrc; } } *pDst = 0; m_psProto->m_clientCapsManager.SetClientCaps(_T(JABBER_CAPS_MIRANDA_NODE), szExt, jcb); fcp = new JabberFeatCapPairDynamic(); fcp->szExt = szExt; // will be deallocated along with other values of JabberFeatCapPairDynamic in CJabberProto destructor fcp->szFeature = mir_tstrdup(szFeature); fcp->szDescription = szDescription ? mir_tstrdup(szDescription) : NULL; fcp->jcbCap = jcb; m_psProto->m_lstJabberFeatCapPairsDynamic.insert(fcp); } else if (szDescription) { // update description if (fcp->szDescription) mir_free(fcp->szDescription); fcp->szDescription = mir_tstrdup(szDescription); } m_psProto->ListUnlock(); return true; } int CJabberNetInterface::AddFeatures(LPCTSTR szFeatures) { if ( !szFeatures) { return false; } m_psProto->ListLock(); BOOL ret = true; LPCTSTR szFeat = szFeatures; while (szFeat[0]) { JabberFeatCapPairDynamic *fcp = FindFeature(szFeat); // if someone is trying to add one of core features, RegisterFeature() will return false, so we don't have to perform this check here if ( !fcp) { // if the feature is not registered yet if ( !RegisterFeature(szFeat, NULL)) { ret = false; } else { fcp = FindFeature(szFeat); // update fcp after RegisterFeature() } } if (fcp) { m_psProto->m_uEnabledFeatCapsDynamic |= fcp->jcbCap; } else { ret = false; } szFeat += lstrlen(szFeat) + 1; } m_psProto->ListUnlock(); if (m_psProto->m_bJabberOnline) { m_psProto->SendPresence(m_psProto->m_iStatus, true); } return ret; } int CJabberNetInterface::RemoveFeatures(LPCTSTR szFeatures) { if ( !szFeatures) { return false; } m_psProto->ListLock(); BOOL ret = true; LPCTSTR szFeat = szFeatures; while (szFeat[0]) { JabberFeatCapPairDynamic *fcp = FindFeature(szFeat); if (fcp) { m_psProto->m_uEnabledFeatCapsDynamic &= ~fcp->jcbCap; } else { ret = false; // indicate that there was an error removing at least one of the specified features } szFeat += lstrlen(szFeat) + 1; } m_psProto->ListUnlock(); if (m_psProto->m_bJabberOnline) { m_psProto->SendPresence(m_psProto->m_iStatus, true); } return ret; } LPTSTR CJabberNetInterface::GetResourceFeatures(LPCTSTR jid) { JabberCapsBits jcb = m_psProto->GetResourceCapabilites(jid, true); if ( !(jcb & JABBER_RESOURCE_CAPS_ERROR)) { m_psProto->ListLock(); // contents of m_lstJabberFeatCapPairsDynamic must not change from the moment we calculate total length and to the moment when we fill the string int i; int iLen = 1; // 1 for extra zero terminator at the end of the string // calculate total necessary string length for (i = 0; g_JabberFeatCapPairs[i].szFeature; i++) { if (jcb & g_JabberFeatCapPairs[i].jcbCap) { iLen += lstrlen(g_JabberFeatCapPairs[i].szFeature) + 1; } } for (i = 0; i < m_psProto->m_lstJabberFeatCapPairsDynamic.getCount(); i++) { if (jcb & m_psProto->m_lstJabberFeatCapPairsDynamic[i]->jcbCap) { iLen += lstrlen(m_psProto->m_lstJabberFeatCapPairsDynamic[i]->szFeature) + 1; } } // allocate memory and fill it LPTSTR str = (LPTSTR)mir_alloc(iLen * sizeof(TCHAR)); LPTSTR p = str; for (i = 0; g_JabberFeatCapPairs[i].szFeature; i++) { if (jcb & g_JabberFeatCapPairs[i].jcbCap) { lstrcpy(p, g_JabberFeatCapPairs[i].szFeature); p += lstrlen(g_JabberFeatCapPairs[i].szFeature) + 1; } } for (i = 0; i < m_psProto->m_lstJabberFeatCapPairsDynamic.getCount(); i++) { if (jcb & m_psProto->m_lstJabberFeatCapPairsDynamic[i]->jcbCap) { lstrcpy(p, m_psProto->m_lstJabberFeatCapPairsDynamic[i]->szFeature); p += lstrlen(m_psProto->m_lstJabberFeatCapPairsDynamic[i]->szFeature) + 1; } } *p = 0; // extra zero terminator m_psProto->ListUnlock(); return str; } return NULL; }