// ---------------------------------------------------------------------------80 // ICQ plugin for Miranda Instant Messenger // ________________________________________ // // Copyright © 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede // Copyright © 2001-2002 Jon Keating, Richard Hughes // Copyright © 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater // Copyright © 2004-2010 Joe Kucera // // 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // ----------------------------------------------------------------------------- // DESCRIPTION: // // Internal Xtraz API // // ----------------------------------------------------------------------------- #include "icqoscar.h" void CIcqProto::handleXtrazNotify(DWORD dwUin, DWORD dwMID, DWORD dwMID2, WORD wCookie, char* szMsg, int nMsgLen, BOOL bThruDC) { char *szNotify = strstrnull(szMsg, ""); char *szQuery = strstrnull(szMsg, ""); HANDLE hContact = HContactFromUIN(dwUin, NULL); if (hContact) // user sent us xtraz, he supports it SetContactCapabilities(hContact, CAPF_XTRAZ); if (szNotify && szQuery) { // valid request char *szWork, *szEnd; int nNotifyLen, nQueryLen; szNotify += 8; szQuery += 7; szEnd = strstrnull(szMsg, ""); if (!szEnd) szEnd = szMsg + nMsgLen; nNotifyLen = (szEnd - szNotify); szEnd = strstrnull(szMsg, ""); if (!szEnd) szEnd = szNotify; szNotify = DemangleXml(szNotify, nNotifyLen); nQueryLen = (szEnd - szQuery); szQuery = DemangleXml(szQuery, nQueryLen); szWork = strstrnull(szQuery, ""); szEnd = strstrnull(szQuery, ""); #ifdef _DEBUG debugLogA("Query: %s", szQuery); debugLogA("Notify: %s", szNotify); #endif if (szWork && szEnd) { // this is our plugin szWork += 10; *szEnd = '\0'; if (!stricmpnull(szWork, "srvMng") && strstrnull(szNotify, "AwayStat")) { char *szSender = strstrnull(szNotify, ""); char *szEndSend = strstrnull(szNotify, ""); if (szSender && szEndSend) { szSender += 10; *szEndSend = '\0'; if ((DWORD)atoi(szSender) == dwUin) { BYTE dwXId = m_bXStatusEnabled ? getContactXStatus(NULL) : 0; if (dwXId && validateStatusMessageRequest(hContact, MTYPE_SCRIPT_NOTIFY)) { // apply privacy rules NotifyEventHooks(m_modeMsgsEvent, (WPARAM)MTYPE_SCRIPT_NOTIFY, (LPARAM)dwUin); char *tmp = getSettingStringUtf(NULL, DBSETTING_XSTATUS_NAME, ""); char *szXName = MangleXml(tmp, strlennull(tmp)); SAFE_FREE(&tmp); tmp = getSettingStringUtf(NULL, DBSETTING_XSTATUS_MSG, ""); char *szXMsg = MangleXml(tmp, strlennull(tmp)); SAFE_FREE(&tmp); int nResponseLen = 212 + strlennull(szXName) + strlennull(szXMsg) + UINMAXLEN + 2; char *szResponse = (char*)_alloca(nResponseLen + 1); // send response mir_snprintf(szResponse, nResponseLen, "" "cAwaySrv" "" "" "%d" "%d" "%s" "%s", m_dwLocalUIN, dwXId, szXName, szXMsg); SAFE_FREE(&szXName); SAFE_FREE(&szXMsg); struct rates_xstatus_response : public rates_queue_item { protected: virtual rates_queue_item* copyItem(rates_queue_item *aDest = NULL) { rates_xstatus_response *pDest = (rates_xstatus_response*)aDest; if (!pDest) pDest = new rates_xstatus_response(ppro, wGroup); pDest->bThruDC = bThruDC; pDest->dwMsgID1 = dwMsgID1; pDest->dwMsgID2 = dwMsgID2; pDest->wCookie = wCookie; pDest->szResponse = null_strdup(szResponse); return rates_queue_item::copyItem(pDest); }; public: rates_xstatus_response(CIcqProto *ppro, WORD wGroup) : rates_queue_item(ppro, wGroup), szResponse(NULL) {}; virtual ~rates_xstatus_response() { if (bCreated) SAFE_FREE(&szResponse); }; virtual void execute() { ppro->SendXtrazNotifyResponse(dwUin, dwMsgID1, dwMsgID2, wCookie, szResponse, strlennull(szResponse), bThruDC); }; BOOL bThruDC; DWORD dwMsgID1; DWORD dwMsgID2; WORD wCookie; char *szResponse; }; m_ratesMutex->Enter(); WORD wGroup = m_rates->getGroupFromSNAC(ICQ_MSG_FAMILY, ICQ_MSG_RESPONSE); m_ratesMutex->Leave(); rates_xstatus_response rr(this, wGroup); rr.hContact = hContact; rr.dwUin = dwUin; rr.bThruDC = bThruDC; rr.dwMsgID1 = dwMID; rr.dwMsgID2 = dwMID2; rr.wCookie = wCookie; rr.szResponse = szResponse; handleRateItem(&rr, RQT_RESPONSE, 0, !bThruDC); } else if (dwXId) debugLogA("Privacy: Ignoring XStatus request"); else debugLogA("Error: We are not in XStatus, skipping"); } else debugLogA("Error: Invalid sender information"); } else debugLogA("Error: Missing sender information"); } else debugLogA("Error: Unknown plugin \"%s\" in Xtraz message", szWork); } else debugLogA("Error: Missing PluginID in Xtraz message"); SAFE_FREE(&szNotify); SAFE_FREE(&szQuery); } else debugLogA("Error: Invalid Xtraz Notify message"); } void CIcqProto::handleXtrazNotifyResponse(DWORD dwUin, HANDLE hContact, WORD wCookie, char* szMsg, int nMsgLen) { char *szMem, *szRes, *szEnd; int nResLen; #ifdef _DEBUG debugLogA("Received Xtraz Notify Response"); #endif szRes = strstrnull(szMsg, ""); szEnd = strstrnull(szMsg, ""); if (szRes && szEnd) { // valid response char *szNode, *szWork; szRes += 5; nResLen = szEnd - szRes; szMem = szRes = DemangleXml(szRes, nResLen); #ifdef _DEBUG debugLogA("Response: %s", szRes); #endif ProtoBroadcastAck(hContact, ICQACKTYPE_XTRAZNOTIFY_RESPONSE, ACKRESULT_SUCCESS, (HANDLE)wCookie, (LPARAM)szRes); NextVal: szNode = strstrnull(szRes, ""); else szEnd = NULL; if (szNode && szEnd) { *(szEnd - 1) = '\0'; szNode += 13; //one more than the length of the string to skip ' or " too szWork = szEnd + 1; if (!stricmpnull(szNode, "cAwaySrv")) { int bChanged = FALSE; *szEnd = ' '; szNode = strstrnull(szWork, ""); szEnd = strstrnull(szWork, ""); if (szNode && szEnd) { szNode += 7; *szEnd = '\0'; if (atoi(szNode) != getContactXStatus(hContact)) { // this is strange - but go on debugLogA("Warning: XStatusIds do not match!"); } *szEnd = ' '; } szNode = strstrnull(szWork, ""); szEnd = strstrnull(szWork, ""); if (szNode && szEnd) { // we got XStatus title, save it char *szXName, *szOldXName; szNode += 7; *szEnd = '\0'; szXName = DemangleXml(szNode, strlennull(szNode)); // check if the name changed szOldXName = getSettingStringUtf(hContact, DBSETTING_XSTATUS_NAME, NULL); if (strcmpnull(szOldXName, szXName)) bChanged = TRUE; SAFE_FREE(&szOldXName); db_set_utf(hContact, m_szModuleName, DBSETTING_XSTATUS_NAME, szXName); SAFE_FREE(&szXName); *szEnd = ' '; } szNode = strstrnull(szWork, ""); szEnd = strstrnull(szWork, ""); if (szNode && szEnd) { // we got XStatus mode msg, save it char *szXMsg, *szOldXMsg; szNode += 6; *szEnd = '\0'; szXMsg = DemangleXml(szNode, strlennull(szNode)); // check if the decription changed szOldXMsg = getSettingStringUtf(hContact, DBSETTING_XSTATUS_NAME, NULL); if (strcmpnull(szOldXMsg, szXMsg)) bChanged = TRUE; SAFE_FREE(&szOldXMsg); db_set_utf(hContact, m_szModuleName, DBSETTING_XSTATUS_MSG, szXMsg); SAFE_FREE(&szXMsg); } ProtoBroadcastAck(hContact, ICQACKTYPE_XSTATUS_RESPONSE, ACKRESULT_SUCCESS, (HANDLE)wCookie, 0); } else { char *szSrvEnd = strstrnull(szEnd, ""); if (szSrvEnd && strstrnull(szSrvEnd, ""); szEnd = strstrnull(szData, ""); if (szPid && szEnd) { szPid += 5; return DemangleXml(szPid, szEnd - szPid); } return NULL; } void CIcqProto::handleXtrazInvitation(DWORD dwUin, DWORD dwMID, DWORD dwMID2, WORD wCookie, char* szMsg, int nMsgLen, BOOL bThruDC) { HANDLE hContact = HContactFromUIN(dwUin, NULL); if (hContact) // user sent us xtraz, he supports it SetContactCapabilities(hContact, CAPF_XTRAZ); char *szPluginID = getXmlPidItem(szMsg, nMsgLen); if (!strcmpnull(szPluginID, "ICQChatRecv")) // it is a invitation to multi-user chat ; else NetLog_Uni(bThruDC, "Error: Unknown plugin \"%s\" in Xtraz message", szPluginID); SAFE_FREE(&szPluginID); } void CIcqProto::handleXtrazData(DWORD dwUin, DWORD dwMID, DWORD dwMID2, WORD wCookie, char* szMsg, int nMsgLen, BOOL bThruDC) { HANDLE hContact; char* szPluginID; hContact = HContactFromUIN(dwUin, NULL); if (hContact) // user sent us xtraz, he supports it SetContactCapabilities(hContact, CAPF_XTRAZ); szPluginID = getXmlPidItem(szMsg, nMsgLen); if (!strcmpnull(szPluginID, "viewCard")) { // it is a greeting card char *szWork, *szEnd, *szUrl, *szNum; szWork = strstrnull(szMsg, ""); szEnd = strstrnull(szMsg, ""); if (szWork && szEnd) { int nDataLen = szEnd - szWork; szUrl = (char*)_alloca(nDataLen); memcpy(szUrl, szWork + 5, nDataLen); szUrl[nDataLen - 5] = '\0'; if (!_strnicmp(szUrl, "view_", 5)) { szNum = szUrl + 5; szWork = strstrnull(szUrl, ".html"); if (szWork) { strcpy(szWork, ".php"); strcat(szWork, szWork + 5); } while (szWork = strstrnull(szUrl, "&")) // unescape & code strcpy(szWork + 1, szWork + 5); szWork = (char*)SAFE_MALLOC(nDataLen + MAX_PATH); ICQTranslateUtfStatic(LPGEN("Greeting card:"), szWork, MAX_PATH); strcat(szWork, "\r\nhttp://www.icq.com/friendship/pages/view_page_"); strcat(szWork, szNum); // Create message to notify user PROTORECVEVENT pre = { 0 }; pre.timestamp = time(NULL); pre.szMessage = szWork; pre.flags = PREF_UTF; int bAdded; ProtoChainRecvMsg(HContactFromUIN(dwUin, &bAdded), &pre); SAFE_FREE(&szWork); } else NetLog_Uni(bThruDC, "Error: Non-standard greeting card message"); } else NetLog_Uni(bThruDC, "Error: Malformed greeting card message"); } else NetLog_Uni(bThruDC, "Error: Unknown plugin \"%s\" in Xtraz message", szPluginID); SAFE_FREE(&szPluginID); } // Functions really sending Xtraz stuff DWORD CIcqProto::SendXtrazNotifyRequest(HANDLE hContact, char* szQuery, char* szNotify, int bForced) { char *szQueryBody; char *szNotifyBody; DWORD dwUin; int nBodyLen; char *szBody; DWORD dwCookie; if (getContactUid(hContact, &dwUin, NULL)) return 0; // Invalid contact if (!CheckContactCapabilities(hContact, CAPF_XTRAZ) && !bForced) return 0; // Contact does not support xtraz, do not send anything szQueryBody = MangleXml(szQuery, strlennull(szQuery)); szNotifyBody = MangleXml(szNotify, strlennull(szNotify)); nBodyLen = strlennull(szQueryBody) + strlennull(szNotifyBody) + 41; szBody = (char*)_alloca(nBodyLen); nBodyLen = mir_snprintf(szBody, nBodyLen, "%s%s", szQueryBody, szNotifyBody); SAFE_FREE((void**)&szQueryBody); SAFE_FREE((void**)&szNotifyBody); // Set up the ack type cookie_message_data *pCookieData = CreateMessageCookie(MTYPE_SCRIPT_NOTIFY, ACKTYPE_CLIENT); dwCookie = AllocateCookie(CKT_MESSAGE, 0, hContact, (void*)pCookieData); // have we a open DC, send through that if (m_bDCMsgEnabled && IsDirectConnectionOpen(hContact, DIRECTCONN_STANDARD, 0)) icq_sendXtrazRequestDirect(hContact, dwCookie, szBody, nBodyLen, MTYPE_SCRIPT_NOTIFY); else icq_sendXtrazRequestServ(dwUin, dwCookie, szBody, nBodyLen, pCookieData); return dwCookie; } void CIcqProto::SendXtrazNotifyResponse(DWORD dwUin, DWORD dwMID, DWORD dwMID2, WORD wCookie, char* szResponse, int nResponseLen, BOOL bThruDC) { char *szResBody = MangleXml(szResponse, nResponseLen); int nBodyLen = strlennull(szResBody) + 21; char *szBody = (char*)_alloca(nBodyLen); HANDLE hContact = HContactFromUIN(dwUin, NULL); if (hContact != INVALID_HANDLE_VALUE && !CheckContactCapabilities(hContact, CAPF_XTRAZ)) { SAFE_FREE(&szResBody); return; // Contact does not support xtraz, do not send anything } nBodyLen = mir_snprintf(szBody, nBodyLen, "%s", szResBody); SAFE_FREE(&szResBody); // Was request received thru DC and have we a open DC, send through that if (bThruDC && IsDirectConnectionOpen(hContact, DIRECTCONN_STANDARD, 0)) icq_sendXtrazResponseDirect(hContact, wCookie, szBody, nBodyLen, MTYPE_SCRIPT_NOTIFY); else icq_sendXtrazResponseServ(dwUin, dwMID, dwMID2, wCookie, szBody, nBodyLen, MTYPE_SCRIPT_NOTIFY); }