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 "msn.h"
21 #include "msnpacket.h"
22 #include <licq/logging/log.h>
23 
24 #include <boost/foreach.hpp>
25 #include <cassert>
26 #include <cstdio>
27 #include <string>
28 #include <list>
29 #include <unistd.h>
30 #include <vector>
31 
32 #include <licq/contactlist/usermanager.h>
33 #include <licq/crypto.h>
34 #include <licq/daemon.h>
35 #include <licq/event.h>
36 #include <licq/logging/logservice.h>
37 #include <licq/oneventmanager.h>
38 #include <licq/plugin/pluginmanager.h>
39 #include <licq/pluginsignal.h>
40 #include <licq/socket.h>
41 #include <licq/userevents.h>
42 
43 #include "owner.h"
44 #include "user.h"
45 
46 using namespace LicqMsn;
47 using Licq::OnEventData;
48 using Licq::UserId;
49 using Licq::gLog;
50 using Licq::gOnEventManager;
51 using Licq::gUserManager;
52 using std::string;
53 
ProcessServerPacket(CMSNBuffer * packet)54 void CMSN::ProcessServerPacket(CMSNBuffer *packet)
55 {
56   CMSNPacket *pReply;
57 
58 //while (!m_pPacketBuf->End())
59   {
60     pReply = 0;
61     string strCmd = packet->unpackRawString(3);
62 
63     if (strCmd == "VER")
64     {
65       // Don't really care about this packet's data.
66       pReply = new CPS_MSNClientVersion(myOwnerId.accountId());
67     }
68     else if (strCmd == "CVR")
69     {
70       // Don't really care about this packet's data.
71       pReply = new CPS_MSNUser(myOwnerId.accountId());
72     }
73     else if (strCmd == "XFR")
74     {
75       //Time to transfer to a new server
76       packet->SkipParameter(); // Seq
77       string strServType = packet->GetParameter();
78       string strServer = packet->GetParameter();
79 
80       if (strServType == "SB")
81       {
82         packet->SkipParameter(); // 'CKI'
83         string strCookie = packet->GetParameter();
84 
85         MSNSBConnectStart(strServer, strCookie);
86       }
87       else
88       {
89         size_t sep = strServer.rfind(':');
90         string host;
91         int port = 0;
92         if (sep != string::npos)
93         {
94           host = strServer.substr(0, sep);
95           port = atoi(strServer.substr(sep+1).c_str());
96         }
97 
98         closeSocket(myServerSocket, false);
99         myServerSocket = NULL;
100 
101         // Make the new connection
102         Logon(myOwnerId, myStatus, host.c_str(), port);
103       }
104     }
105     else if (strCmd == "USR")
106     {
107       packet->SkipParameter(); // Seq
108       string strType = packet->GetParameter();
109 
110       if (strType == "OK")
111       {
112         packet->SkipParameter(); // email account
113         string strNick = packet->GetParameter();
114         string strDecodedNick = Decode(strNick);
115         gLog.info("%s logged in", strDecodedNick.c_str());
116 
117         // Set our alias here
118         unsigned long listVersion;
119         {
120           OwnerWriteGuard o(myOwnerId);
121           o->setAlias(strDecodedNick);
122           listVersion = o->listVersion();
123         }
124 
125         // This cookie doesn't work anymore now that we are online
126         myCookie.clear();
127 
128         pReply = new CPS_MSNSync(listVersion);
129       }
130       else
131       {
132         packet->SkipParameter(); // "S"
133         myCookie = packet->GetParameter();
134 
135         // Make an SSL connection to authenticate
136         MSNAuthenticate();
137       }
138     }
139     else if (strCmd == "CHL")
140     {
141       packet->SkipParameter(); // Seq
142       string strHash = packet->GetParameter();
143 
144       pReply = new CPS_MSNChallenge(strHash);
145     }
146     else if (strCmd == "SYN")
147     {
148       packet->SkipParameter();
149       string strVersion = packet->GetParameter();
150       unsigned long newListVersion = atol(strVersion.c_str());
151       {
152         OwnerWriteGuard o(myOwnerId);
153         o->setListVersion(newListVersion);
154       }
155 
156       MSNChangeStatus(myStatus);
157 
158       // Send our local list now
159       //Licq::UserListGuard userList(myOwnerId);
160       //BOOST_FOREACH(const Licq::User* user, **userList)
161       //{
162       //  pReply = new CPS_MSNAddUser(user->accountId());
163       //  SendPacket(pReply);
164       //}
165     }
166     else if (strCmd == "LST")
167     {
168       // Add user
169       string strUser = packet->GetParameter();
170       UserId userId(myOwnerId, strUser);
171       string strNick = packet->GetParameter();
172       string strLists = packet->GetParameter();
173       string strUserLists;
174 
175       if (userId == myOwnerId)
176         return;
177 
178       int nLists = atoi(strLists.c_str());
179       if (nLists & FLAG_CONTACT_LIST)
180         strUserLists = packet->GetParameter();
181 
182       if (nLists & FLAG_CONTACT_LIST)
183         gUserManager.addUser(userId, true, false);
184 
185       Licq::UserWriteGuard u(userId);
186       if (u.isLocked())
187       {
188         u->SetEnableSave(false);
189         u->setUserEncoding("UTF-8");
190         u->SetInvisibleList(nLists & FLAG_BLOCK_LIST);
191 
192         if (!u->KeepAliasOnUpdate())
193         {
194           string strDecodedNick = Decode(strNick);
195           u->setAlias(strDecodedNick);
196           Licq::gPluginManager.pushPluginSignal(new Licq::PluginSignal(
197               Licq::PluginSignal::SignalUser,
198               Licq::PluginSignal::UserBasic, u->id()));
199         }
200         u->setUserInfoString("Email1", strUser);
201         string strURL = "http://members.msn.com/"+strUser;
202         u->setUserInfoString("Homepage", strURL);
203         u->SetNewUser(false);
204         u->SetEnableSave(true);
205         u->save(Licq::User::SaveLicqInfo);
206         Licq::gPluginManager.pushPluginSignal(new Licq::PluginSignal(
207             Licq::PluginSignal::SignalUser,
208             Licq::PluginSignal::UserInfo, u->id()));
209       }
210     }
211     else if (strCmd == "LSG")
212     {
213       // Add group
214     }
215     else if (strCmd == "ADD")
216     {
217       packet->SkipParameter(); // What's this?
218       string strList = packet->GetParameter();
219       string strVersion = packet->GetParameter();
220       string strUser = packet->GetParameter();
221       UserId userId(myOwnerId, strUser);
222       string strNick = packet->GetParameter();
223 
224       unsigned long newListVersion = atol(strVersion.c_str());
225       {
226         OwnerWriteGuard o(myOwnerId);
227         o->setListVersion(newListVersion);
228       }
229 
230       if (strList == "RL")
231       {
232         gLog.info("Authorization request from %s", strUser.c_str());
233 
234         Licq::UserEvent* e = new Licq::EventAuthRequest(userId,
235             strNick.c_str(), "", "", "", "", time(0), 0);
236 
237         Licq::OwnerWriteGuard o(myOwnerId);
238         if (Licq::gDaemon.addUserEvent(*o, e))
239         {
240           e->AddToHistory(*o, true);
241           gOnEventManager.performOnEvent(OnEventData::OnEventSysMsg, *o);
242         }
243       }
244       else
245       {
246         gLog.info("Added %s to contact list", strUser.c_str());
247 
248         Licq::UserWriteGuard u(userId);
249         if (u.isLocked())
250         {
251           if (!u->KeepAliasOnUpdate())
252           {
253             string strDecodedNick = Decode(strNick);
254             u->setAlias(strDecodedNick);
255           }
256           Licq::gPluginManager.pushPluginSignal(new Licq::PluginSignal(
257               Licq::PluginSignal::SignalUser,
258               Licq::PluginSignal::UserBasic, u->id()));
259         }
260       }
261     }
262     else if (strCmd == "REM")
263     {
264       packet->SkipParameter(); // seq
265       packet->SkipParameter(); // list
266       string strVersion = packet->GetParameter();
267       string strUser = packet->GetParameter();
268 
269       unsigned long newListVersion = atol(strVersion.c_str());
270       {
271         OwnerWriteGuard o(myOwnerId);
272         o->setListVersion(newListVersion);
273       }
274 
275       gLog.info("Removed %s from contact list", strUser.c_str());
276     }
277     else if (strCmd == "REA")
278     {
279       packet->SkipParameter(); // seq
280       string strVersion = packet->GetParameter();
281       string strUser = packet->GetParameter();
282       string strNick = packet->GetParameter();
283 
284       unsigned long newListVersion = atol(strVersion.c_str());
285       {
286         OwnerWriteGuard o(myOwnerId);
287         o->setListVersion(newListVersion);
288       }
289 
290       if (strUser == myOwnerId.accountId())
291       {
292         Licq::OwnerWriteGuard o(myOwnerId);
293         string strDecodedNick = Decode(strNick);
294         o->setAlias(strDecodedNick);
295       }
296 
297       gLog.info("%s renamed successfully", strUser.c_str());
298     }
299     else if (strCmd == "CHG")
300     {
301       packet->SkipParameter(); // seq
302       string strStatus = packet->GetParameter();
303       unsigned status;
304 
305       if (strStatus == "NLN")
306         status = User::OnlineStatus;
307       else if (strStatus == "BSY")
308         status = User::OnlineStatus | User::OccupiedStatus;
309       else if (strStatus == "HDN")
310         status = User::OnlineStatus | User::InvisibleStatus;
311       else if (strStatus == "IDL")
312         status = User::OnlineStatus | User::IdleStatus;
313       else
314         status = User::OnlineStatus | User::AwayStatus;
315 
316       gLog.info("Server says we are now: %s",
317           User::statusToString(status, true, false).c_str());
318       myStatus = status;
319 
320       Licq::OwnerWriteGuard o(myOwnerId);
321       if (o.isLocked())
322         o->statusChanged(status);
323     }
324     else if (strCmd == "ILN" || strCmd == "NLN")
325     {
326       if (strCmd == "ILN")
327         packet->SkipParameter(); // seq
328       string strStatus = packet->GetParameter();
329       Licq::UserId userId(myOwnerId, packet->GetParameter());
330       string strNick = packet->GetParameter();
331       string strClientId = packet->GetParameter();
332       string strMSNObject = packet->GetParameter();
333       string strDecodedObject = strMSNObject.size() ? Decode(strMSNObject) :"";
334 
335       unsigned status;
336       if (strStatus == "NLN")
337         status = User::OnlineStatus;
338       else if (strStatus == "BSY")
339         status = User::OnlineStatus | User::OccupiedStatus;
340       else if (strStatus == "IDL")
341         status = User::OnlineStatus | User::IdleStatus;
342       else
343         status = User::OnlineStatus | User::AwayStatus;
344 
345       UserWriteGuard u(userId);
346       if (u.isLocked())
347       {
348         u->SetSendServer(true); // no direct connections
349 
350         if (!u->KeepAliasOnUpdate())
351         {
352           string strDecodedNick = Decode(strNick);
353           u->setAlias(strDecodedNick);
354           Licq::gPluginManager.pushPluginSignal(new Licq::PluginSignal(
355               Licq::PluginSignal::SignalUser,
356               Licq::PluginSignal::UserBasic, u->id()));
357         }
358 
359 	// Get the display picture here, so it can be shown with the notify
360         if (strDecodedObject != u->pictureObject())
361         {
362           // TODO: User shouldn't be updated until the new picture is actually received
363           u->setPictureObject(strDecodedObject);
364 	  if (strDecodedObject.size())
365             MSNGetDisplayPicture(u->id(), strDecodedObject);
366 	}
367 
368         gLog.info("%s changed status (%s)", u->getAlias().c_str(), strStatus.c_str());
369         u->statusChanged(status);
370       }
371     }
372     else if (strCmd == "FLN")
373     {
374       UserId userId(myOwnerId, packet->GetParameter());
375 
376       {
377         Licq::UserWriteGuard u(userId);
378         if (u.isLocked())
379         {
380           gLog.info("%s logged off", u->getAlias().c_str());
381           u->statusChanged(User::OfflineStatus);
382         }
383       }
384 
385       // Do we have a connection attempt to this user?
386       StartList::iterator it;
387       for (it = m_lStart.begin(); it != m_lStart.end(); it++)
388       {
389         if (*it && userId == (*it)->userId)
390         {
391           gLog.info("Removing connection attempt to %s", userId.toString().c_str());
392 //          SStartMessage *pStart = (*it);
393           m_lStart.erase(it);
394           break;
395         }
396       }
397     }
398     else if (strCmd == "RNG")
399     {
400       string strSessionID = packet->GetParameter();
401       string strServer = packet->GetParameter();
402       packet->SkipParameter(); // 'CKI'
403       string strCookie = packet->GetParameter();
404       Licq::UserId userId(myOwnerId, packet->GetParameter());
405 
406       MSNSBConnectAnswer(strServer, strSessionID, strCookie, userId);
407     }
408     else if (strCmd == "MSG")
409     {
410       packet->SkipParameter(); // 'Hotmail'
411       packet->SkipParameter(); // 'Hotmail' again
412       packet->SkipParameter(); // size
413       packet->SkipRN(); // Skip \r\n
414       packet->ParseHeaders();
415 
416       string strType = packet->GetValue("Content-Type");
417 
418       if (strType.find("text/x-msmsgsprofile") != string::npos)
419       {
420         m_strMSPAuth = packet->GetValue("MSPAuth");
421         m_strSID = packet->GetValue("sid");
422         m_strKV = packet->GetValue("kv");
423         m_nSessionStart = time(0);
424 
425         // We might have another packet attached
426         //packet->SkipRN();
427       }
428       else if (strType.find("text/x-msmsgsinitialemailnotification") != string::npos)
429       {
430         // Email alert when we sign in
431 
432         // Get the next part..
433         packet->SkipRN();
434         packet->ParseHeaders();
435       }
436       else if (strType.find("text/x-msmsgsemailnotification") != string::npos)
437       {
438         // Email we get while signed in
439 
440         // Get the next part..
441         packet->SkipRN();
442         packet->ParseHeaders();
443 
444         string strFrom = packet->GetValue("From");
445         string strFromAddr = packet->GetValue("From-Addr");
446         string strSubject = packet->GetValue("Subject");
447 
448         const string toHash = m_strMSPAuth + "9" + myPassword;
449         const string hexDigest = Licq::Md5::hashToHexString(toHash);
450 
451         gLog.info("New email from %s (%s)", strFrom.c_str(), strFromAddr.c_str());
452         Licq::EventEmailAlert* pEmailAlert = new Licq::EventEmailAlert(
453             strFrom, myOwnerId.accountId(), strFromAddr, strSubject, time(0),
454             m_strMSPAuth, m_strSID, m_strKV, packet->GetValue("id"),
455             packet->GetValue("Post-URL"), packet->GetValue("Message-URL"),
456             hexDigest, m_nSessionStart);
457 
458         Licq::OwnerWriteGuard o(myOwnerId);
459         if (Licq::gDaemon.addUserEvent(*o, pEmailAlert))
460         {
461           pEmailAlert->AddToHistory(*o, true);
462           gOnEventManager.performOnEvent(OnEventData::OnEventSysMsg, *o);
463         }
464       }
465     }
466     else if (strCmd == "QNG")
467     {
468       m_bWaitingPingReply = false;
469     }
470     else if (strCmd == "913")
471     {
472       unsigned long nSeq = packet->GetParameterUnsignedLong();
473 
474       // Search pStart for this sequence, mark it as an error, send the
475       // signals to the daemon and remove these item from the list.
476       SStartMessage *pStart = 0;
477       StartList::iterator it;
478       for (it = m_lStart.begin(); it != m_lStart.end(); it++)
479       {
480         if ((*it)->m_nSeq == nSeq)
481         {
482           gLog.error("Cannot send messages while invisible");
483           pStart = *it;
484           pStart->m_pEvent->m_eResult = Licq::Event::ResultFailed;
485           Licq::gPluginManager.pushPluginEvent(pStart->m_pEvent);
486           m_lStart.erase(it);
487           break;
488         }
489       }
490     }
491     else if (strCmd == "GTC")
492     {
493     }
494     else if (strCmd == "BLP")
495     {
496     }
497     else if (strCmd == "PRP")
498     {
499     }
500     else if (strCmd == "QRY")
501     {
502       m_bCanPing = true;
503     }
504     else if (strCmd == "NOT")
505     {
506       // For the moment, skip the notification... consider it spam from MSN
507       unsigned long nSize = packet->GetParameterUnsignedLong(); // size
508       packet->SkipRN(); // Skip \r\n
509       packet->Skip(nSize);
510     }
511     else
512     {
513       gLog.warning("Unhandled command (%s)", strCmd.c_str());
514     }
515 
516     if (pReply)
517       SendPacket(pReply);
518   }
519 }
520 
SendPacket(CMSNPacket * p)521 void CMSN::SendPacket(CMSNPacket *p)
522 {
523   assert(myServerSocket != NULL);
524   if (!myServerSocket->send(*p->getBuffer()))
525     MSNLogoff(true);
526 
527   delete p;
528 }
529 
Logon(const Licq::UserId & ownerId,unsigned status,string host,int port)530 void CMSN::Logon(const Licq::UserId& ownerId, unsigned status, string host, int port)
531 {
532   if (status == User::OfflineStatus)
533     return;
534 
535   myOwnerId = ownerId;
536 
537   {
538     Licq::OwnerReadGuard o(myOwnerId);
539     if (!o.isLocked())
540     {
541       gLog.error("No owner set");
542       return;
543     }
544     myPassword = o->password();
545     if (host.empty())
546       host = o->serverHost();
547     if (port == 0)
548       port = o->serverPort();
549   }
550 
551   if (host.empty())
552     host = defaultServerHost();
553   if (port <= 0)
554     port = defaultServerPort();
555 
556   myServerSocket = new Licq::TCPSocket(myOwnerId);
557   gLog.info("Server found at %s:%d", host.c_str(), port);
558 
559   if (!myServerSocket->connectTo(host, port))
560   {
561     gLog.info("Connect failed to %s", host.c_str());
562     delete myServerSocket;
563     myServerSocket = NULL;
564     return;
565   }
566 
567   myMainLoop.addSocket(myServerSocket, this);
568 
569   CMSNPacket *pHello = new CPS_MSNVersion();
570   SendPacket(pHello);
571   myStatus = status;
572 }
573 
MSNChangeStatus(unsigned status)574 void CMSN::MSNChangeStatus(unsigned status)
575 {
576   string msnStatus;
577   if (status & User::InvisibleStatus)
578   {
579     msnStatus = "HDN";
580     status = User::OnlineStatus | User::InvisibleStatus;
581   }
582   else if (status & User::FreeForChatStatus || status == User::OnlineStatus)
583   {
584     msnStatus = "NLN";
585     status = User::OnlineStatus;
586   }
587   else if (status & (User::OccupiedStatus | User::DoNotDisturbStatus))
588   {
589     msnStatus = "BSY";
590     status = User::OnlineStatus | User::OccupiedStatus;
591   }
592   else
593   {
594     msnStatus = "AWY";
595     status = User::OnlineStatus | User::AwayStatus;
596   }
597 
598   CMSNPacket* pSend = new CPS_MSNChangeStatus(msnStatus);
599   SendPacket(pSend);
600   myStatus = status;
601 }
602 
MSNLogoff(bool bDisconnected)603 void CMSN::MSNLogoff(bool bDisconnected)
604 {
605   if (myServerSocket == NULL)
606     return;
607 
608   if (!bDisconnected)
609   {
610     CMSNPacket *pSend = new CPS_MSNLogoff();
611     SendPacket(pSend);
612   }
613 
614   myStatus = User::OfflineStatus;
615 
616   // Don't try to send any more pings
617   m_bCanPing = false;
618 
619   // Close the server socket
620   closeSocket(myServerSocket, false);
621   myServerSocket = NULL;
622 
623   // Close user sockets and update the daemon
624   {
625     Licq::UserListGuard userList(myOwnerId);
626     BOOST_FOREACH(Licq::User* user, **userList)
627     {
628       UserWriteGuard u(dynamic_cast<User*>(user));
629       if (u->normalSocketDesc() != NULL)
630       {
631         closeSocket(u->normalSocketDesc(), false);
632         u->clearAllSocketDesc();
633       }
634       if (u->isOnline())
635         u->statusChanged(User::OfflineStatus);
636     }
637   }
638 
639   Licq::OwnerWriteGuard o(myOwnerId);
640   if (o.isLocked())
641     o->statusChanged(Licq::User::OfflineStatus);
642 }
643 
MSNAddUser(const UserId & userId)644 void CMSN::MSNAddUser(const UserId& userId)
645 {
646   {
647     Licq::UserWriteGuard u(userId);
648     if (u.isLocked())
649     {
650       u->SetEnableSave(false);
651       u->setUserEncoding("UTF-8");
652       u->SetEnableSave(true);
653       u->save(Licq::User::SaveLicqInfo);
654     }
655   }
656 
657   CMSNPacket* pSend = new CPS_MSNAddUser(userId.accountId(), CONTACT_LIST);
658   SendPacket(pSend);
659 }
660 
MSNRemoveUser(const UserId & userId)661 void CMSN::MSNRemoveUser(const UserId& userId)
662 {
663   CMSNPacket* pSend = new CPS_MSNRemoveUser(userId.accountId(), CONTACT_LIST);
664   SendPacket(pSend);
665   Licq::gUserManager.removeLocalUser(userId);
666 }
667 
MSNRenameUser(const UserId & userId)668 void CMSN::MSNRenameUser(const UserId& userId)
669 {
670   string strNick;
671   {
672     Licq::UserReadGuard u(userId);
673     if (!u.isLocked())
674       return;
675     strNick = u->getAlias();
676   }
677 
678   CMSNPacket* pSend = new CPS_MSNRenameUser(userId.accountId(), Encode(strNick));
679   SendPacket(pSend);
680 }
681 
MSNGrantAuth(const UserId & userId)682 void CMSN::MSNGrantAuth(const UserId& userId)
683 {
684   CMSNPacket* pSend = new CPS_MSNAddUser(userId.accountId(), ALLOW_LIST);
685   SendPacket(pSend);
686 }
687 
MSNUpdateUser(const string & alias)688 void CMSN::MSNUpdateUser(const string& alias)
689 {
690   CMSNPacket* pSend = new CPS_MSNRenameUser(myOwnerId.accountId(), Encode(alias));
691   SendPacket(pSend);
692 }
693 
MSNBlockUser(const UserId & userId)694 void CMSN::MSNBlockUser(const UserId& userId)
695 {
696   {
697     Licq::UserWriteGuard u(userId);
698     if (!u.isLocked())
699       return;
700     u->SetInvisibleList(true);
701   }
702 
703   CMSNPacket* pRem = new CPS_MSNRemoveUser(userId.accountId(), ALLOW_LIST);
704   gLog.info("Removing user %s from the allow list", userId.toString().c_str());
705   SendPacket(pRem);
706   CMSNPacket* pAdd = new CPS_MSNAddUser(userId.accountId(), BLOCK_LIST);
707   gLog.info("Adding user %s to the block list", userId.toString().c_str());
708   SendPacket(pAdd);
709 }
710 
MSNUnblockUser(const UserId & userId)711 void CMSN::MSNUnblockUser(const UserId& userId)
712 {
713   {
714     Licq::UserWriteGuard u(userId);
715     if (!u.isLocked())
716       return;
717     u->SetInvisibleList(false);
718   }
719 
720   CMSNPacket* pRem = new CPS_MSNRemoveUser(userId.accountId(), BLOCK_LIST);
721   gLog.info("Removing user %s from the block list", userId.toString().c_str());
722   SendPacket(pRem);
723   CMSNPacket* pAdd = new CPS_MSNAddUser(userId.accountId(), ALLOW_LIST);
724   gLog.info("Adding user %s to the allow list", userId.toString().c_str());
725   SendPacket(pAdd);
726 }
727 
MSNGetDisplayPicture(const Licq::UserId & userId,const string & strMSNObject)728 void CMSN::MSNGetDisplayPicture(const Licq::UserId& userId, const string &strMSNObject)
729 {
730   // If we are invisible, this will result in an error, so don't allow it
731   if (myStatus & User::InvisibleStatus)
732     return;
733 
734   CMSNPacket* pGetMSNDP = new CPS_MSNInvitation(userId.accountId(), myOwnerId.accountId(), strMSNObject);
735   CMSNP2PPacket *p = (CMSNP2PPacket *)(pGetMSNDP);
736   CMSNDataEvent *pDataResponse = new CMSNDataEvent(MSN_DP_EVENT,
737       p->SessionId(), p->BaseId(), userId, myOwnerId, p->CallGUID(), this);
738   WaitDataEvent(pDataResponse);
739   gLog.info("Requesting %s's display picture", userId.toString().c_str());
740   MSNSendInvitation(userId, pGetMSNDP);
741 }
742 
sendServerPing()743 void CMSN::sendServerPing()
744 {
745   if (m_bWaitingPingReply)
746   {
747     gLog.info("Ping timeout, reconnecting...");
748     m_bWaitingPingReply = false;
749     unsigned status = myStatus;
750     MSNLogoff();
751     Logon(myOwnerId, status);
752   }
753   else if (m_bCanPing)
754   {
755     CMSNPacket* pSend = new CPS_MSNPing();
756     SendPacket(pSend);
757 
758     m_bWaitingPingReply = true;
759   }
760 }
761