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