1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2004-2013 Licq developers <licq-dev@googlegroups.com>
4  *
5  * Licq is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * Licq is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Licq; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include <cctype>
21 #include <cstdio>
22 #include <cstring>
23 #include <list>
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include <string>
27 #include <unistd.h>
28 #include <vector>
29 
30 #include <licq/logging/log.h>
31 #include <licq/socket.h>
32 #include <licq/conversation.h>
33 #include <licq/event.h>
34 #include <licq/plugin/pluginmanager.h>
35 #include <licq/protocolsignal.h>
36 
37 #include "msn.h"
38 #include "msnpacket.h"
39 #include "owner.h"
40 #include "user.h"
41 
42 using namespace LicqMsn;
43 using Licq::gConvoManager;
44 using Licq::gLog;
45 using std::list;
46 using std::string;
47 
48 #ifndef HAVE_STRNDUP
49 
50 #include <cstdlib>
51 #include <cstring>
52 
strndup(const char * s,size_t n)53 char *strndup(const char *s, size_t n)
54 {
55   char *str;
56 
57   if (n < 1)
58     return NULL;
59 
60   str = (char *)malloc(n + 1);
61   if (!str)
62     return NULL;
63 
64   memset(str, '\0', n + 1);
65   strncpy(str, s, n);
66 
67   return str;
68 }
69 #endif // HAVE_STRNDUP
70 
71 
CMSN()72 CMSN::CMSN()
73   : myServerSocket(NULL),
74     mySslSocket(NULL),
75     m_vlPacketBucket(211),
76     myNextTimeoutId(1)
77 {
78   m_bWaitingPingReply = m_bCanPing = false;
79   m_pPacketBuf = 0;
80   m_pSSLPacket = 0;
81   myStatus = Licq::User::OfflineStatus;
82   myPassword = "";
83   m_nSessionStart = 0;
84 
85 }
86 
~CMSN()87 CMSN::~CMSN()
88 {
89   if (m_pPacketBuf)
90     delete m_pPacketBuf;
91 }
92 
defaultServerHost() const93 std::string CMSN::defaultServerHost() const
94 {
95   return "messenger.hotmail.com";
96 }
97 
defaultServerPort() const98 int CMSN::defaultServerPort() const
99 {
100   return 1863;
101 }
102 
StorePacket(SBuffer * _pBuf,int _nSock)103 void CMSN::StorePacket(SBuffer *_pBuf, int _nSock)
104 {
105   if (_pBuf->m_bStored == false)
106   {
107     BufferList &b = m_vlPacketBucket[HashValue(_nSock)];
108     b.push_front(_pBuf);
109   }
110 }
111 
RemovePacket(const Licq::UserId & userId,int _nSock,int nSize)112 void CMSN::RemovePacket(const Licq::UserId& userId, int _nSock, int nSize)
113 {
114   BufferList &b = m_vlPacketBucket[HashValue(_nSock)];
115   BufferList::iterator it;
116   SBuffer *pNewBuf = 0;
117   int nNewSize = 0;
118 
119   for (it = b.begin(); it != b.end(); it++)
120   {
121     if ((*it)->myUserId == userId)
122     {
123       // Found a packet that has part of another packet at the end
124       // so we have to save it and put it back on the queue.
125       if (nSize)
126       {
127 	nNewSize = (*it)->m_pBuf->getDataSize() - nSize;
128 	if (nNewSize)
129 	{
130 	  pNewBuf = new SBuffer;
131 	  pNewBuf->myUserId = userId;
132 	  pNewBuf->m_pBuf = new CMSNBuffer(nNewSize);
133 	  pNewBuf->m_pBuf->packRaw((*it)->m_pBuf->getDataStart()+nSize, nNewSize);
134 	  pNewBuf->m_bStored = true;
135 	}
136       }
137 
138       b.erase(it);
139       break;
140     }
141   }
142 
143   // Now add it here
144   if (pNewBuf)
145     b.push_front(pNewBuf);
146 }
147 
RetrievePacket(const Licq::UserId & userId,int _nSock)148 SBuffer *CMSN::RetrievePacket(const Licq::UserId& userId, int _nSock)
149 {
150   BufferList &b = m_vlPacketBucket[HashValue(_nSock)];
151   BufferList::iterator it;
152   for (it = b.begin(); it != b.end(); it++)
153   {
154     if ((*it)->myUserId == userId)
155       return *it;
156   }
157 
158   return 0;
159 }
160 
RetrieveEvent(unsigned long _nTag)161 Licq::Event *CMSN::RetrieveEvent(unsigned long _nTag)
162 {
163   Licq::Event *e = 0;
164 
165   list<Licq::Event*>::iterator it;
166   for (it = m_pEvents.begin(); it != m_pEvents.end(); it++)
167   {
168     if ((*it)->Sequence() == _nTag)
169     {
170       e = *it;
171       m_pEvents.erase(it);
172       break;
173     }
174   }
175 
176   return e;
177 }
178 
HandlePacket(Licq::TCPSocket * sock,CMSNBuffer & packet,const Licq::UserId & userId)179 void CMSN::HandlePacket(Licq::TCPSocket* sock, CMSNBuffer& packet, const Licq::UserId& userId)
180 {
181   int _nSocket = sock->Descriptor();
182   SBuffer* pBuf = RetrievePacket(userId, _nSocket);
183   bool bProcess = false;
184 
185   if (pBuf)
186     *(pBuf->m_pBuf) += packet;
187   else
188   {
189     pBuf = new SBuffer;
190     pBuf->m_pBuf = new CMSNBuffer(packet);
191     pBuf->myUserId = userId;
192     pBuf->m_bStored = false;
193   }
194 
195   do
196   {
197     char *szNeedle;
198     CMSNBuffer *pPart = 0;
199     unsigned long nFullSize = 0;
200     bProcess = false;
201 
202     if ((szNeedle = strstr((char *)pBuf->m_pBuf->getDataStart(),
203                            "\r\n")))
204     {
205       bool isMSG = (memcmp(pBuf->m_pBuf->getDataStart(), "MSG", 3) == 0);
206 
207       if (/*memcmp(pBuf->m_pBuf->getDataStart(), "MSG", 3) == 0*/ isMSG ||
208           memcmp(pBuf->m_pBuf->getDataStart(), "NOT", 3) == 0)
209       {
210         pBuf->m_pBuf->SkipParameter(); // MSG, NOT
211         if (isMSG)
212         {
213           pBuf->m_pBuf->SkipParameter(); // Hotmail
214           pBuf->m_pBuf->SkipParameter(); // Hotmail
215         }
216         string strSize = pBuf->m_pBuf->GetParameter();
217         int nSize = atoi(strSize.c_str());
218 
219         // Cut the packet instead of passing it all along
220         // we might receive 1 full packet and part of another.
221         if (nSize <= (pBuf->m_pBuf->getDataPosWrite() - pBuf->m_pBuf->getDataPosRead()))
222         {
223           nFullSize = nSize + pBuf->m_pBuf->getDataPosRead() - pBuf->m_pBuf->getDataStart() + 1;
224           if (pBuf->m_pBuf->getDataSize() > nFullSize)
225           {
226             // Hack to save the part and delete the rest
227             if (pBuf->m_bStored == false)
228             {
229               StorePacket(pBuf, _nSocket);
230               pBuf->m_bStored = true;
231             }
232 
233             // We have a packet, with part of another one at the end
234             pPart = new CMSNBuffer(nFullSize);
235             pPart->packRaw(pBuf->m_pBuf->getDataStart(), nFullSize);
236           }
237           bProcess = true;
238         }
239       }
240       else //if(memcmp(pBuf->m_pBuf->getDataStart(), "ACK", 3) == 0)
241       {
242         int nSize = szNeedle - pBuf->m_pBuf->getDataStart() +2;
243 
244         // Cut the packet instead of passing it all along
245         // we might receive 1 full packet and part of another.
246         if (nSize <= (pBuf->m_pBuf->getDataPosWrite() - pBuf->m_pBuf->getDataPosRead()))
247         {
248           nFullSize = nSize + pBuf->m_pBuf->getDataPosRead() - pBuf->m_pBuf->getDataStart();
249 
250           if (pBuf->m_pBuf->getDataSize() > nFullSize)
251           {
252             // Hack to save the part and delete the rest
253             if (pBuf->m_bStored == false)
254             {
255               StorePacket(pBuf, _nSocket);
256               pBuf->m_bStored = true;
257             }
258 
259             // We have a packet, with part of another one at the end
260             pPart = new CMSNBuffer(nFullSize);
261             pPart->packRaw(pBuf->m_pBuf->getDataStart(), nFullSize);
262           }
263           bProcess = true;
264         }
265       }
266 
267       if (!bProcess)
268       {
269         // Save it
270         StorePacket(pBuf, _nSocket);
271         pBuf->m_bStored = true;
272       }
273 
274       pBuf->m_pBuf->Reset();
275     }
276     else
277     {
278       // Need to save it, doens't have much data yet
279       StorePacket(pBuf, _nSocket);
280       pBuf->m_bStored = true;
281       bProcess = false;
282     }
283 
284     if (bProcess)
285     {
286       // Handle it, and then remove it from the queue
287       if (sock == myServerSocket)
288         ProcessServerPacket(pPart ? pPart : pBuf->m_pBuf);
289       else
290         ProcessSBPacket(userId, pPart ? pPart : pBuf->m_pBuf, sock);
291       RemovePacket(userId, _nSocket, nFullSize);
292       if (pPart)
293         delete pPart;
294       else
295         delete pBuf;
296       pBuf = RetrievePacket(userId, _nSocket);
297     }
298     else
299       pBuf = 0;
300 
301   } while (pBuf);
302 }
303 
Decode(const string & strIn)304 string CMSN::Decode(const string &strIn)
305 {
306   string strOut = "";
307 
308   for (unsigned int i = 0; i < strIn.length(); i++)
309   {
310     if (strIn[i] == '%')
311     {
312       char szByte[3];
313       szByte[0] = strIn[++i]; szByte[1] = strIn[++i]; szByte[2] = '\0';
314       strOut += strtol(szByte, NULL, 16);
315     }
316     else
317       strOut += strIn[i];
318   }
319 
320   return strOut;
321 }
322 
SocketToCID(int _nSocket)323 unsigned long CMSN::SocketToCID(int _nSocket)
324 {
325   Licq::Conversation* convo = gConvoManager.getFromSocket(_nSocket);
326   return (convo != NULL ? convo->id() : 0);
327 }
328 
Encode(const string & strIn)329 string CMSN::Encode(const string &strIn)
330 {
331   string strOut = "";
332 
333   for (unsigned int i = 0; i < strIn.length(); i++)
334   {
335     if (isalnum(strIn[i]))
336       strOut += strIn[i];
337     else
338     {
339       char szChar[4];
340       sprintf(szChar, "%%%02X", strIn[i]);
341       szChar[3] = '\0';
342       strOut += szChar;
343     }
344   }
345 
346   return strOut;
347 }
348 
run()349 int CMSN::run()
350 {
351   // Ping server every 60s
352   myMainLoop.addTimeout(60*1000, this, 0, false);
353 
354   myMainLoop.addRawFile(getReadPipe(), this);
355   myMainLoop.run();
356 
357   // Close out now
358   MSNLogoff();
359   return 0;
360 }
361 
rawFileEvent(int,int fd,int)362 void CMSN::rawFileEvent(int /*id*/, int fd, int /*revents*/)
363 {
364   char c;
365   read(fd, &c, 1);
366   switch (c)
367   {
368     case PipeSignal:
369     {
370       ProcessSignal(popSignal().get());
371       break;
372     }
373 
374     case PipeShutdown:
375       gLog.info("Exiting");
376       myMainLoop.quit();
377       break;
378   }
379 }
380 
ProcessSignal(const Licq::ProtocolSignal * s)381 void CMSN::ProcessSignal(const Licq::ProtocolSignal* s)
382 {
383   if (myServerSocket == NULL && s->signal() != Licq::ProtocolSignal::SignalLogon)
384     return;
385 
386   switch (s->signal())
387   {
388     case Licq::ProtocolSignal::SignalLogon:
389     {
390       if (myServerSocket == NULL)
391       {
392         const Licq::ProtoLogonSignal* sig =
393             dynamic_cast<const Licq::ProtoLogonSignal*>(s);
394         Logon(sig->userId(), sig->status());
395       }
396       break;
397     }
398     case Licq::ProtocolSignal::SignalChangeStatus:
399     {
400       const Licq::ProtoChangeStatusSignal* sig =
401           dynamic_cast<const Licq::ProtoChangeStatusSignal*>(s);
402       MSNChangeStatus(sig->status());
403       break;
404     }
405     case Licq::ProtocolSignal::SignalLogoff:
406     {
407       MSNLogoff();
408       break;
409     }
410     case Licq::ProtocolSignal::SignalAddUser:
411     {
412       const Licq::ProtoAddUserSignal* sig =
413           dynamic_cast<const Licq::ProtoAddUserSignal*>(s);
414       MSNAddUser(sig->userId());
415       break;
416     }
417     case Licq::ProtocolSignal::SignalRemoveUser:
418     {
419       const Licq::ProtoRemoveUserSignal* sig =
420           dynamic_cast<const Licq::ProtoRemoveUserSignal*>(s);
421       MSNRemoveUser(sig->userId());
422       break;
423     }
424     case Licq::ProtocolSignal::SignalRenameUser:
425     {
426       const Licq::ProtoRenameUserSignal* sig =
427           dynamic_cast<const Licq::ProtoRenameUserSignal*>(s);
428       MSNRenameUser(sig->userId());
429       break;
430     }
431     case Licq::ProtocolSignal::SignalNotifyTyping:
432     {
433       const Licq::ProtoTypingNotificationSignal* sig =
434           dynamic_cast<const Licq::ProtoTypingNotificationSignal*>(s);
435       sendIsTyping(sig->userId(), sig->active(), sig->convoId());
436       break;
437     }
438     case Licq::ProtocolSignal::SignalSendMessage:
439     {
440       const Licq::ProtoSendMessageSignal* sig =
441           dynamic_cast<const Licq::ProtoSendMessageSignal*>(s);
442       MSNSendMessage(sig->eventId(), sig->userId(), sig->message(), sig->callerThread(), sig->convoId());
443       break;
444     }
445     case Licq::ProtocolSignal::SignalGrantAuth:
446     {
447       const Licq::ProtoGrantAuthSignal* sig =
448           dynamic_cast<const Licq::ProtoGrantAuthSignal*>(s);
449       MSNGrantAuth(sig->userId());
450       Licq::gPluginManager.pushPluginEvent(new Licq::Event(s));
451       break;
452     }
453     case Licq::ProtocolSignal::SignalUpdateInfo:
454     {
455       string newAlias;
456       {
457         Licq::OwnerReadGuard o(s->userId());
458         if (!o.isLocked())
459           newAlias = o->getAlias();
460       }
461       MSNUpdateUser(newAlias);
462       Licq::gPluginManager.pushPluginEvent(new Licq::Event(s));
463       break;
464     }
465     case Licq::ProtocolSignal::SignalBlockUser:
466     {
467       const Licq::ProtoBlockUserSignal* sig =
468           dynamic_cast<const Licq::ProtoBlockUserSignal*>(s);
469       MSNBlockUser(sig->userId());
470       break;
471     }
472     case Licq::ProtocolSignal::SignalUnblockUser:
473     {
474       const Licq::ProtoUnblockUserSignal* sig =
475           dynamic_cast<const Licq::ProtoUnblockUserSignal*>(s);
476       MSNUnblockUser(sig->userId());
477       break;
478     }
479 
480     default:
481     {
482       /* Unsupported action, if it has an eventId, cancel it */
483       if (s->eventId() != 0)
484         Licq::gPluginManager.pushPluginEvent(
485             new Licq::Event(s, Licq::Event::ResultUnsupported));
486       break;
487     }
488   }
489 }
490 
socketEvent(int,Licq::INetSocket * inetSocket,int)491 void CMSN::socketEvent(int /*id*/, Licq::INetSocket* inetSocket, int /*revents*/)
492 {
493   Licq::TCPSocket* sock = dynamic_cast<Licq::TCPSocket*>(inetSocket);
494   assert(sock != NULL);
495 
496   CMSNBuffer packet;
497   bool recok = sock->receive(packet);
498 
499   if (sock == myServerSocket)
500   {
501     if (recok)
502       HandlePacket(myServerSocket, packet, myOwnerId);
503     else
504     {
505       // Time to reconnect
506       gLog.info("Disconnected from server, reconnecting");
507       sleep(1);
508       closeSocket(myServerSocket, false);
509       myServerSocket = NULL;
510       Logon(myOwnerId, myStatus);
511     }
512   }
513 
514   else if (sock == mySslSocket)
515   {
516     if (recok)
517       ProcessSSLServerPacket(packet);
518   }
519 
520   else
521   {
522     //SB socket
523     if (recok)
524       HandlePacket(sock, packet, sock->userId());
525     else
526     {
527       // Sometimes SB just drops connection without sending any BYE for the user(s) first
528       // This seems to happen when other user is offical client
529       killConversation(sock);
530       closeSocket(sock);
531     }
532   }
533 }
534 
closeSocket(Licq::TCPSocket * sock,bool clearUser)535 void CMSN::closeSocket(Licq::TCPSocket* sock, bool clearUser)
536 {
537   myMainLoop.removeSocket(sock);
538   sock->CloseConnection();
539 
540   if (clearUser)
541   {
542     Licq::UserWriteGuard u(sock->userId());
543     if (u.isLocked())
544     {
545       u->clearSocketDesc(sock);
546       if (u->OfflineOnDisconnect())
547         u->statusChanged(Licq::User::OfflineStatus);
548     }
549   }
550 
551   delete sock;
552 }
553 
timeoutEvent(int id)554 void CMSN::timeoutEvent(int id)
555 {
556   if (id == 0)
557     sendServerPing();
558   else
559     typingTimeout(id);
560 }
561 
WaitDataEvent(CMSNDataEvent * _pEvent)562 void CMSN::WaitDataEvent(CMSNDataEvent *_pEvent)
563 {
564   m_lMSNEvents.push_back(_pEvent);
565 }
566 
RemoveDataEvent(CMSNDataEvent * pData)567 bool CMSN::RemoveDataEvent(CMSNDataEvent *pData)
568 {
569   list<CMSNDataEvent *>::iterator it;
570   for (it = m_lMSNEvents.begin(); it != m_lMSNEvents.end(); it++)
571   {
572     if ((*it)->userId() == pData->userId() &&
573 	(*it)->getSocket() == pData->getSocket())
574     {
575       // Close the socket
576       int sockFd = pData->getSocket()->Descriptor();
577       closeSocket(pData->getSocket());
578 
579       Licq::Conversation* convo = gConvoManager.getFromSocket(sockFd);
580       if (convo != NULL)
581         gConvoManager.remove(convo->id());
582 
583       m_lMSNEvents.erase(it);
584       delete pData;
585       pData = 0;
586       break;
587     }
588   }
589 
590   return (pData == 0);
591 }
592 
FetchDataEvent(const Licq::UserId & userId,Licq::TCPSocket * sock)593 CMSNDataEvent* CMSN::FetchDataEvent(const Licq::UserId& userId, Licq::TCPSocket* sock)
594 {
595   CMSNDataEvent *pReturn = 0;
596   list<CMSNDataEvent *>::iterator it;
597   for (it = m_lMSNEvents.begin(); it != m_lMSNEvents.end(); it++)
598   {
599     if ((*it)->userId() == userId && (*it)->getSocket() == sock)
600     {
601       pReturn = *it;
602       break;
603     }
604   }
605 
606   if (!pReturn)
607   {
608     pReturn = FetchStartDataEvent(userId);
609     if (pReturn)
610       pReturn->setSocket(sock);
611   }
612 
613   return pReturn;
614 }
615 
FetchStartDataEvent(const Licq::UserId & userId)616 CMSNDataEvent* CMSN::FetchStartDataEvent(const Licq::UserId& userId)
617 {
618   CMSNDataEvent *pReturn = 0;
619   list<CMSNDataEvent *>::iterator it;
620   for (it = m_lMSNEvents.begin(); it != m_lMSNEvents.end(); it++)
621   {
622     if ((*it)->userId() == userId && (*it)->getSocket() == NULL)
623     {
624       pReturn = *it;
625       break;
626     }
627   }
628 
629   return pReturn;
630 }
631