summaryrefslogtreecommitdiff
path: root/protocols/JabberG/src/jabber_iq.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/JabberG/src/jabber_iq.cpp')
-rw-r--r--protocols/JabberG/src/jabber_iq.cpp375
1 files changed, 375 insertions, 0 deletions
diff --git a/protocols/JabberG/src/jabber_iq.cpp b/protocols/JabberG/src/jabber_iq.cpp
new file mode 100644
index 0000000000..43084114c2
--- /dev/null
+++ b/protocols/JabberG/src/jabber_iq.cpp
@@ -0,0 +1,375 @@
+/*
+
+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 "jabber_iq.h"
+#include "jabber_caps.h"
+#include "jabber_privacy.h"
+#include "jabber_ibb.h"
+#include "jabber_rc.h"
+
+void CJabberProto::IqInit()
+{
+ InitializeCriticalSection( &m_csIqList );
+ m_ppIqList = NULL;
+ m_nIqCount = 0;
+ m_nIqAlloced = 0;
+}
+
+void CJabberProto::IqUninit()
+{
+ if ( m_ppIqList ) mir_free( m_ppIqList );
+ m_ppIqList = NULL;
+ m_nIqCount = 0;
+ m_nIqAlloced = 0;
+ DeleteCriticalSection( &m_csIqList );
+}
+
+void CJabberProto::IqRemove( int index )
+{
+ EnterCriticalSection( &m_csIqList );
+ if ( index>=0 && index<m_nIqCount ) {
+ memmove( m_ppIqList+index, m_ppIqList+index+1, sizeof( JABBER_IQ_FUNC )*( m_nIqCount-index-1 ));
+ m_nIqCount--;
+ }
+ LeaveCriticalSection( &m_csIqList );
+}
+
+void CJabberProto::IqExpire()
+{
+ int i;
+ time_t expire;
+
+ EnterCriticalSection( &m_csIqList );
+ expire = time( NULL ) - 120; // 2 minute
+ i = 0;
+ while ( i < m_nIqCount ) {
+ if ( m_ppIqList[i].requestTime < expire )
+ IqRemove( i );
+ else
+ i++;
+ }
+ LeaveCriticalSection( &m_csIqList );
+}
+
+JABBER_IQ_PFUNC CJabberProto::JabberIqFetchFunc( int iqId )
+{
+ int i;
+ JABBER_IQ_PFUNC res;
+
+ EnterCriticalSection( &m_csIqList );
+ IqExpire();
+#ifdef _DEBUG
+ for ( i=0; i<m_nIqCount; i++ )
+ Log( " %04d : %02d : 0x%x", m_ppIqList[i].iqId, m_ppIqList[i].procId, m_ppIqList[i].func );
+#endif
+ for ( i=0; i<m_nIqCount && m_ppIqList[i].iqId!=iqId; i++ );
+ if ( i < m_nIqCount ) {
+ res = m_ppIqList[i].func;
+ IqRemove( i );
+ }
+ else {
+ res = ( JABBER_IQ_PFUNC ) NULL;
+ }
+ LeaveCriticalSection( &m_csIqList );
+ return res;
+}
+
+void CJabberProto::IqAdd( unsigned int iqId, JABBER_IQ_PROCID procId, JABBER_IQ_PFUNC func )
+{
+ int i;
+
+ EnterCriticalSection( &m_csIqList );
+ Log( "IqAdd id=%d, proc=%d, func=0x%x", iqId, procId, func );
+ if ( procId == IQ_PROC_NONE )
+ i = m_nIqCount;
+ else
+ for ( i=0; i<m_nIqCount && m_ppIqList[i].procId!=procId; i++ );
+
+ if ( i>=m_nIqCount && m_nIqCount>=m_nIqAlloced ) {
+ m_nIqAlloced = m_nIqCount + 8;
+ m_ppIqList = ( JABBER_IQ_FUNC * )mir_realloc( m_ppIqList, sizeof( JABBER_IQ_FUNC )*m_nIqAlloced );
+ }
+
+ if ( m_ppIqList != NULL ) {
+ m_ppIqList[i].iqId = iqId;
+ m_ppIqList[i].procId = procId;
+ m_ppIqList[i].func = func;
+ m_ppIqList[i].requestTime = time( NULL );
+ if ( i == m_nIqCount ) m_nIqCount++;
+ }
+ LeaveCriticalSection( &m_csIqList );
+}
+
+BOOL CJabberIqManager::FillPermanentHandlers()
+{
+ // Google Shared Status (http://code.google.com/apis/talk/jep_extensions/shared_status.html)
+ AddPermanentHandler( &CJabberProto::OnIqSetGoogleSharedStatus, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_TO | JABBER_IQ_PARSE_ID_STR, _T("google:shared-status"), FALSE, _T("query"));
+
+ // version requests (XEP-0092)
+ AddPermanentHandler( &CJabberProto::OnIqRequestVersion, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_VERSION), FALSE, _T("query"));
+
+ // last activity (XEP-0012)
+ AddPermanentHandler( &CJabberProto::OnIqRequestLastActivity, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_LAST_ACTIVITY), FALSE, _T("query"));
+
+ // ping requests (XEP-0199)
+ AddPermanentHandler( &CJabberProto::OnIqRequestPing, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_PING), FALSE, _T("ping"));
+
+ // entity time (XEP-0202)
+ AddPermanentHandler( &CJabberProto::OnIqRequestTime, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_ENTITY_TIME), FALSE, _T("time"));
+
+ // entity time (XEP-0090)
+ AddPermanentHandler( &CJabberProto::OnIqProcessIqOldTime, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_ENTITY_TIME_OLD), FALSE, _T("query"));
+
+ // old avatars support (deprecated XEP-0008)
+ AddPermanentHandler( &CJabberProto::OnIqRequestAvatar, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_AVATAR), FALSE, _T("query"));
+
+ // privacy lists (XEP-0016)
+ AddPermanentHandler( &CJabberProto::OnIqRequestPrivacyLists, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR, _T(JABBER_FEAT_PRIVACY_LISTS), FALSE, _T("query"));
+
+ // in band bytestreams (XEP-0047)
+ AddPermanentHandler( &CJabberProto::OnFtHandleIbbIq, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_CHILD_TAG_NODE | JABBER_IQ_PARSE_CHILD_TAG_NAME | JABBER_IQ_PARSE_CHILD_TAG_XMLNS, _T(JABBER_FEAT_IBB), FALSE, NULL);
+
+ // socks5-bytestreams (XEP-0065)
+ AddPermanentHandler( &CJabberProto::FtHandleBytestreamRequest, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_BYTESTREAMS), FALSE, _T("query"));
+
+ // session initiation (XEP-0095)
+ AddPermanentHandler( &CJabberProto::OnSiRequest, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_SI), FALSE, _T("si"));
+
+ // roster push requests
+ AddPermanentHandler( &CJabberProto::OnRosterPushRequest, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_IQ_ROSTER), FALSE, _T("query"));
+
+ // OOB file transfers
+ AddPermanentHandler( &CJabberProto::OnIqRequestOOB, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_HCONTACT | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_OOB), FALSE, _T("query"));
+
+ // disco#items requests (XEP-0030, XEP-0050)
+ AddPermanentHandler( &CJabberProto::OnHandleDiscoItemsRequest, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_TO | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_DISCO_ITEMS), FALSE, _T("query"));
+
+ // disco#info requests (XEP-0030, XEP-0050, XEP-0115)
+ AddPermanentHandler( &CJabberProto::OnHandleDiscoInfoRequest, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_TO | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_DISCO_INFO), FALSE, _T("query"));
+
+ // ad-hoc commands (XEP-0050) for remote controlling (XEP-0146)
+ AddPermanentHandler( &CJabberProto::HandleAdhocCommandRequest, JABBER_IQ_TYPE_SET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_TO | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_COMMANDS), FALSE, _T("command"));
+
+ // http auth (XEP-0070)
+ AddPermanentHandler( &CJabberProto::OnIqHttpAuth, JABBER_IQ_TYPE_GET, JABBER_IQ_PARSE_FROM | JABBER_IQ_PARSE_ID_STR | JABBER_IQ_PARSE_CHILD_TAG_NODE, _T(JABBER_FEAT_HTTP_AUTH), FALSE, _T("confirm"));
+
+ return TRUE;
+}
+
+BOOL CJabberIqManager::Start()
+{
+ if ( m_hExpirerThread || m_bExpirerThreadShutdownRequest )
+ return FALSE;
+
+ m_hExpirerThread = ppro->JForkThreadEx( &CJabberProto::ExpirerThread, this );
+ if ( !m_hExpirerThread )
+ return FALSE;
+
+ return TRUE;
+}
+
+void __cdecl CJabberProto::ExpirerThread( void* pParam )
+{
+ CJabberIqManager *pManager = ( CJabberIqManager * )pParam;
+ pManager->ExpirerThread();
+}
+
+void CJabberIqManager::ExpirerThread()
+{
+ while (!m_bExpirerThreadShutdownRequest)
+ {
+ Lock();
+ CJabberIqInfo* pInfo = DetachExpired();
+ Unlock();
+ if (!pInfo)
+ {
+ for (int i = 0; !m_bExpirerThreadShutdownRequest && (i < 10); i++)
+ Sleep(50);
+
+ // -1 thread :)
+ ppro->m_adhocManager.ExpireSessions();
+ continue;
+ }
+ ExpireInfo( pInfo );
+ delete pInfo;
+ }
+
+ if ( !m_bExpirerThreadShutdownRequest ) {
+ CloseHandle( m_hExpirerThread );
+ m_hExpirerThread = NULL;
+ }
+}
+
+void CJabberIqManager::ExpireInfo( CJabberIqInfo* pInfo, void*)
+{
+ if ( !pInfo )
+ return;
+
+ if ( pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_FROM )
+ pInfo->m_szFrom = pInfo->m_szReceiver;
+ if (( pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_HCONTACT ) && ( pInfo->m_szFrom ))
+ pInfo->m_hContact = ppro->HContactFromJID( pInfo->m_szFrom , 3);
+
+ ppro->Log( "Expiring iq id %d, sent to " TCHAR_STR_PARAM, pInfo->m_nIqId, pInfo->m_szReceiver ? pInfo->m_szReceiver : _T("server"));
+
+ pInfo->m_nIqType = JABBER_IQ_TYPE_FAIL;
+ (ppro->*(pInfo->m_pHandler))( NULL, pInfo );
+}
+
+CJabberIqInfo* CJabberIqManager::AddHandler(JABBER_IQ_HANDLER pHandler, int nIqType, const TCHAR *szReceiver, DWORD dwParamsToParse, int nIqId, void *pUserData, int iPriority)
+{
+ CJabberIqInfo* pInfo = new CJabberIqInfo();
+ if (!pInfo)
+ return NULL;
+
+ pInfo->m_pHandler = pHandler;
+ if (nIqId == -1)
+ nIqId = ppro->SerialNext();
+ pInfo->m_nIqId = nIqId;
+ pInfo->m_nIqType = nIqType;
+ pInfo->m_dwParamsToParse = dwParamsToParse;
+ pInfo->m_pUserData = pUserData;
+ pInfo->m_dwRequestTime = GetTickCount();
+ pInfo->m_dwTimeout = JABBER_DEFAULT_IQ_REQUEST_TIMEOUT;
+ pInfo->m_iPriority = iPriority;
+ pInfo->SetReceiver(szReceiver);
+
+ InsertIq(pInfo);
+
+ return pInfo;
+}
+
+BOOL CJabberIqManager::HandleIq(int nIqId, HXML pNode )
+{
+ if (nIqId == -1 || pNode == NULL)
+ return FALSE;
+
+ const TCHAR *szType = xmlGetAttrValue( pNode, _T("type"));
+ if ( !szType )
+ return FALSE;
+
+ int nIqType = JABBER_IQ_TYPE_FAIL;
+ if (!_tcsicmp(szType, _T("result")))
+ nIqType = JABBER_IQ_TYPE_RESULT;
+ else if (!_tcsicmp(szType, _T("error")))
+ nIqType = JABBER_IQ_TYPE_ERROR;
+ else
+ return FALSE;
+
+ Lock();
+ CJabberIqInfo* pInfo = DetachInfo(nIqId);
+ Unlock();
+ if (pInfo)
+ {
+ pInfo->m_nIqType = nIqType;
+ if (nIqType == JABBER_IQ_TYPE_RESULT) {
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_CHILD_TAG_NODE)
+ pInfo->m_pChildNode = xmlGetChild( pNode , 0 );
+
+ if (pInfo->m_pChildNode && (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_CHILD_TAG_NAME))
+ pInfo->m_szChildTagName = ( TCHAR* )xmlGetName( pInfo->m_pChildNode );
+ if (pInfo->m_pChildNode && (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_CHILD_TAG_XMLNS))
+ pInfo->m_szChildTagXmlns = ( TCHAR* )xmlGetAttrValue( pNode, _T("xmlns"));
+ }
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_TO)
+ pInfo->m_szTo = ( TCHAR* )xmlGetAttrValue( pNode, _T("to"));
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_FROM)
+ pInfo->m_szFrom = ( TCHAR* )xmlGetAttrValue( pNode, _T("from"));
+ if (pInfo->m_szFrom && (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_HCONTACT))
+ pInfo->m_hContact = ppro->HContactFromJID( pInfo->m_szFrom, 3 );
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_ID_STR)
+ pInfo->m_szId = ( TCHAR* )xmlGetAttrValue( pNode, _T("id"));
+
+ (ppro->*(pInfo->m_pHandler))(pNode, pInfo);
+ delete pInfo;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL CJabberIqManager::HandleIqPermanent( HXML pNode )
+{
+ BOOL bStopHandling = FALSE;
+ Lock();
+ CJabberIqPermanentInfo *pInfo = m_pPermanentHandlers;
+ while ( pInfo ) {
+ // have to get all data here, in the loop, because there's always possibility that previous handler modified it
+ const TCHAR *szType = xmlGetAttrValue( pNode, _T("type"));
+ if ( !szType )
+ break;
+
+ CJabberIqInfo iqInfo;
+
+ iqInfo.m_nIqType = JABBER_IQ_TYPE_FAIL;
+ if ( !_tcsicmp( szType, _T("get")))
+ iqInfo.m_nIqType = JABBER_IQ_TYPE_GET;
+ else if ( !_tcsicmp( szType, _T("set")))
+ iqInfo.m_nIqType = JABBER_IQ_TYPE_SET;
+ else
+ break;
+
+ if ( pInfo->m_nIqTypes & iqInfo.m_nIqType )
+ {
+ HXML pFirstChild = xmlGetChild( pNode , 0 );
+ if ( !pFirstChild || !xmlGetName( pFirstChild ))
+ break;
+
+ const TCHAR *szTagName = xmlGetName( pFirstChild );
+ const TCHAR *szXmlns = xmlGetAttrValue( pFirstChild, _T("xmlns"));
+
+ if ( (!pInfo->m_szXmlns || ( szXmlns && !_tcscmp( pInfo->m_szXmlns, szXmlns ))) &&
+ ( !pInfo->m_szTag || !_tcscmp( pInfo->m_szTag, szTagName ))) {
+ // node suits handler criteria, call the handler
+ iqInfo.m_pChildNode = pFirstChild;
+ iqInfo.m_szChildTagName = ( TCHAR* )szTagName;
+ iqInfo.m_szChildTagXmlns = ( TCHAR* )szXmlns;
+ iqInfo.m_szId = ( TCHAR* )xmlGetAttrValue( pNode, _T("id"));
+ iqInfo.m_pUserData = pInfo->m_pUserData;
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_TO)
+ iqInfo.m_szTo = ( TCHAR* )xmlGetAttrValue( pNode, _T("to"));
+
+ if (pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_FROM)
+ iqInfo.m_szFrom = ( TCHAR* )xmlGetAttrValue( pNode, _T("from"));
+
+ if ((pInfo->m_dwParamsToParse & JABBER_IQ_PARSE_HCONTACT) && (iqInfo.m_szFrom))
+ iqInfo.m_hContact = ppro->HContactFromJID( iqInfo.m_szFrom, 3 );
+
+ ppro->Log( "Handling iq id " TCHAR_STR_PARAM ", type " TCHAR_STR_PARAM ", from " TCHAR_STR_PARAM, iqInfo.m_szId, szType, iqInfo.m_szFrom );
+ if ((ppro->*(pInfo->m_pHandler))(pNode, &iqInfo)) {
+ bStopHandling = TRUE;
+ break;
+ }
+ }
+ }
+
+ pInfo = pInfo->m_pNext;
+ }
+ Unlock();
+
+ return bStopHandling;
+}