1 /*
2  * notificationserver.cpp
3  * libmsn
4  *
5  * Created by Mark Rowe on Mon Mar 22 2004.
6  * Copyright (c) 2004 Mark Rowe. All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 #include <msn/notificationserver.h>
24 #include <msn/errorcodes.h>
25 #include <msn/externals.h>
26 #include <msn/md5.h>
27 #include <msn/util.h>
28 #include <curl/curl.h>
29 #include <algorithm>
30 #include <cctype>
31 #include <cassert>
32 
33 #ifndef WIN32
34 #include <unistd.h>
35 #else
36 #include <io.h>
37 #endif
38 
39 #include <stdio.h>
40 #include <cstring>
41 
42 #ifndef curl_free
43 #define curl_free free
44 #endif
45 
46 namespace MSN
47 {
48 
49     static size_t msn_handle_curl_write(void *ptr, size_t size, size_t nmemb, void  *stream);
50     static size_t msn_handle_curl_header(void *ptr, size_t size, size_t nmemb, void *stream) ;
51 
NotificationServerConnection(NotificationServerConnection::AuthData & auth_,Callbacks & cb_)52     NotificationServerConnection::NotificationServerConnection(NotificationServerConnection::AuthData & auth_, Callbacks & cb_)
53         : Connection(), auth(auth_), externalCallbacks(cb_), _connectionState(NS_DISCONNECTED), _nextPing(50), commandHandlers()
54     {
55         registerCommandHandlers();
56     }
57 
NotificationServerConnection(Passport username_,std::string password_,Callbacks & cb_)58     NotificationServerConnection::NotificationServerConnection(Passport username_, std::string password_, Callbacks & cb_)
59         : Connection(), auth(username_, password_), externalCallbacks(cb_), _connectionState(NS_DISCONNECTED), commandHandlers()
60     {
61         registerCommandHandlers();
62     }
63 
NotificationServerConnection(Callbacks & cb_)64     NotificationServerConnection::NotificationServerConnection(Callbacks & cb_) : Connection(), auth(Passport(), ""), externalCallbacks(cb_), _connectionState(NS_DISCONNECTED), commandHandlers()
65     {
66         registerCommandHandlers();
67     }
68 
~NotificationServerConnection()69     NotificationServerConnection::~NotificationServerConnection()
70     {
71         if (this->connectionState() != NS_DISCONNECTED)
72             this->disconnect();
73     }
74 
connectionWithSocket(int fd)75     Connection *NotificationServerConnection::connectionWithSocket(int fd)
76     {
77         this->assertConnectionStateIsNot(NS_DISCONNECTED);
78         std::list<SwitchboardServerConnection *> & list = _switchboardConnections;
79         std::list<SwitchboardServerConnection *>::iterator i = list.begin();
80 
81         if (this->sock == fd)
82             return this;
83 
84         for (; i != list.end(); i++)
85         {
86             Connection *c = (*i)->connectionWithSocket(fd);
87             if (c)
88                 return c;
89         }
90         return NULL;
91     }
92 
switchboardWithOnlyUser(Passport username)93     SwitchboardServerConnection *NotificationServerConnection::switchboardWithOnlyUser(Passport username)
94     {
95         if (this->connectionState() >= NS_CONNECTED)
96         {
97             std::list<SwitchboardServerConnection *> & list = _switchboardConnections;
98             std::list<SwitchboardServerConnection *>::iterator i = list.begin();
99 
100             for (; i != list.end(); i++)
101             {
102                 if ((*i)->users.size() == 1 &&
103                     *((*i)->users.begin()) == username)
104                     return *i;
105             }
106         }
107         return NULL;
108     }
109 
switchboardConnections()110     const std::list<SwitchboardServerConnection *> & NotificationServerConnection::switchboardConnections()
111     {
112         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
113         return _switchboardConnections;
114     }
115 
addSwitchboardConnection(SwitchboardServerConnection * c)116     void NotificationServerConnection::addSwitchboardConnection(SwitchboardServerConnection *c)
117     {
118         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
119         _switchboardConnections.push_back(c);
120     }
121 
removeSwitchboardConnection(SwitchboardServerConnection * c)122     void NotificationServerConnection::removeSwitchboardConnection(SwitchboardServerConnection *c)
123     {
124         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
125         _switchboardConnections.remove(c);
126     }
127 
addCallback(NotificationServerCallback callback,int trid,void * data)128     void NotificationServerConnection::addCallback(NotificationServerCallback callback,
129                                                    int trid, void *data)
130     {
131         this->assertConnectionStateIsAtLeast(NS_CONNECTING);
132         this->callbacks[trid] = std::make_pair(callback, data);
133     }
134 
removeCallback(int trid)135     void NotificationServerConnection::removeCallback(int trid)
136     {
137         this->assertConnectionStateIsAtLeast(NS_CONNECTING);
138         this->callbacks.erase(trid);
139     }
140 
registerCommandHandlers()141     void NotificationServerConnection::registerCommandHandlers()
142     {
143         if (commandHandlers.size() == 0)
144         {
145             commandHandlers["OUT"] = &NotificationServerConnection::handle_OUT;
146             commandHandlers["ADD"] = &NotificationServerConnection::handle_ADD;
147             commandHandlers["REM"] = &NotificationServerConnection::handle_REM;
148             commandHandlers["BLP"] = &NotificationServerConnection::handle_BLP;
149             commandHandlers["GTC"] = &NotificationServerConnection::handle_GTC;
150             commandHandlers["REA"] = &NotificationServerConnection::handle_REA;
151             commandHandlers["CHG"] = &NotificationServerConnection::handle_CHG;
152             commandHandlers["CHL"] = &NotificationServerConnection::handle_CHL;
153             commandHandlers["ILN"] = &NotificationServerConnection::handle_ILN;
154             commandHandlers["NLN"] = &NotificationServerConnection::handle_NLN;
155             commandHandlers["NOT"] = &NotificationServerConnection::handle_NOT;
156             commandHandlers["FLN"] = &NotificationServerConnection::handle_FLN;
157             commandHandlers["MSG"] = &NotificationServerConnection::handle_MSG;
158             commandHandlers["ADG"] = &NotificationServerConnection::handle_ADG;
159             commandHandlers["RMG"] = &NotificationServerConnection::handle_RMG;
160             commandHandlers["REG"] = &NotificationServerConnection::handle_REG;
161         }
162     }
163 
dispatchCommand(std::vector<std::string> & args)164     void NotificationServerConnection::dispatchCommand(std::vector<std::string> & args)
165     {
166         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
167         std::map<std::string, void (NotificationServerConnection::*)(std::vector<std::string> &)>::iterator i = commandHandlers.find(args[0]);
168         if (i != commandHandlers.end())
169             (this->*commandHandlers[args[0]])(args);
170     }
171 
handle_OUT(std::vector<std::string> & args)172     void NotificationServerConnection::handle_OUT(std::vector<std::string> & args)
173     {
174         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
175         if (args.size() > 1)
176         {
177             if (args[1] == "OTH")
178             {
179                 this->myNotificationServer()->externalCallbacks.showError(this, "You have logged onto MSN twice at once. Your MSN session will now terminate.");
180             }
181             else if (args[1] == "SSD")
182             {
183                 this->myNotificationServer()->externalCallbacks.showError(this, "This MSN server is going down for maintenance. Your MSN session will now terminate.");
184             } else {
185                 this->myNotificationServer()->externalCallbacks.showError(this, (std::string("The MSN server has terminated the connection with an unknown reason code. Please report this code: ") +
186                                       args[1]).c_str());
187             }
188         }
189         this->disconnect();
190     }
191 
handle_ADD(std::vector<std::string> & args)192     void NotificationServerConnection::handle_ADD(std::vector<std::string> & args)
193     {
194         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
195         if (args[2] == "RL")
196         {
197             this->myNotificationServer()->externalCallbacks.gotNewReverseListEntry(this, args[4], decodeURL(args[5]));
198         }
199         else
200         {
201             int groupID = -1;
202             if (args.size() > 5)
203                 groupID = decimalFromString(args[5]);
204 
205             this->myNotificationServer()->externalCallbacks.addedListEntry(this, args[2], args[4], groupID);
206         }
207         this->myNotificationServer()->externalCallbacks.gotLatestListSerial(this, decimalFromString(args[3]));
208     }
209 
handle_REM(std::vector<std::string> & args)210     void NotificationServerConnection::handle_REM(std::vector<std::string> & args)
211     {
212         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
213         int groupID = -1;
214         if (args.size() > 4)
215             groupID = decimalFromString(args[4]);
216 
217         this->myNotificationServer()->externalCallbacks.removedListEntry(this, args[2], args[4], groupID);
218         this->myNotificationServer()->externalCallbacks.gotLatestListSerial(this, decimalFromString(args[3]));
219     }
220 
handle_BLP(std::vector<std::string> & args)221     void NotificationServerConnection::handle_BLP(std::vector<std::string> & args)
222     {
223         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
224         this->myNotificationServer()->externalCallbacks.gotBLP(this, args[3][0]);
225         this->myNotificationServer()->externalCallbacks.gotLatestListSerial(this, decimalFromString(args[3]));
226     }
227 
handle_GTC(std::vector<std::string> & args)228     void NotificationServerConnection::handle_GTC(std::vector<std::string> & args)
229     {
230         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
231         this->myNotificationServer()->externalCallbacks.gotGTC(this, args[3][0]);
232         this->myNotificationServer()->externalCallbacks.gotLatestListSerial(this, decimalFromString(args[3]));
233     }
234 
handle_REA(std::vector<std::string> & args)235     void NotificationServerConnection::handle_REA(std::vector<std::string> & args)
236     {
237         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
238         this->myNotificationServer()->externalCallbacks.gotFriendlyName(this, decodeURL(args[4]));
239         this->myNotificationServer()->externalCallbacks.gotLatestListSerial(this, decimalFromString(args[2]));
240     }
241 
handle_CHG(std::vector<std::string> & args)242     void NotificationServerConnection::handle_CHG(std::vector<std::string> & args)
243     {
244         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
245         this->myNotificationServer()->externalCallbacks.changedStatus(this, buddyStatusFromString(args[2]));
246     }
247 
handle_CHL(std::vector<std::string> & args)248     void NotificationServerConnection::handle_CHL(std::vector<std::string> & args)
249     {
250         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
251         md5_state_t state;
252         md5_byte_t digest[16];
253         int a;
254 
255         md5_init(&state);
256         md5_append(&state, (md5_byte_t *)(args[2].c_str()), (int) args[2].size());
257         md5_append(&state, (md5_byte_t *)"VT6PX?UQTM4WM%YR", 16);
258         md5_finish(&state, digest);
259 
260         std::ostringstream buf_;
261         buf_ << "QRY " << this->trID++ << " PROD0038W!61ZTF9 32\r\n";
262         if (write(buf_) != buf_.str().size())
263             return;
264 
265 
266         char hexdigest[3];
267         for (a = 0; a < 16; a++)
268         {
269             sprintf(hexdigest, "%02x", digest[a]);
270             write(std::string(hexdigest, 2), false);
271         }
272     }
273 
handle_ILN(std::vector<std::string> & args)274     void NotificationServerConnection::handle_ILN(std::vector<std::string> & args)
275     {
276         this->assertConnectionStateIs(NS_CONNECTED);
277         this->myNotificationServer()->externalCallbacks.buddyChangedStatus(this, args[3], "", buddyStatusFromString(args[2]));
278     }
279 
handle_NLN(std::vector<std::string> & args)280     void NotificationServerConnection::handle_NLN(std::vector<std::string> & args)
281     {
282         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
283         this->myNotificationServer()->externalCallbacks.buddyChangedStatus(this, args[2], "", buddyStatusFromString(args[1]));
284     }
285 
handle_NOT(std::vector<std::string> & args)286     void NotificationServerConnection::handle_NOT(std::vector<std::string> & args)
287     {
288         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
289         int notlen = decimalFromString(args[1]);
290         this->readBuffer = this->readBuffer.substr(notlen);
291     }
292 
handle_FLN(std::vector<std::string> & args)293     void NotificationServerConnection::handle_FLN(std::vector<std::string> & args)
294     {
295         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
296         this->myNotificationServer()->externalCallbacks.buddyOffline(this, args[1]);
297     }
298 
handle_MSG(std::vector<std::string> & args)299     void NotificationServerConnection::handle_MSG(std::vector<std::string> & args)
300     {
301         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
302         Connection::handle_MSG(args);
303     }
304 
handle_RNG(std::vector<std::string> & args)305     void NotificationServerConnection::handle_RNG(std::vector<std::string> & args)
306     {
307         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
308         SwitchboardServerConnection::AuthData auth = SwitchboardServerConnection::AuthData(this->auth.username,
309                                                                                            args[1],
310                                                                                            args[4]);
311         SwitchboardServerConnection *newSBconn = new SwitchboardServerConnection(auth, *this);
312         this->addSwitchboardConnection(newSBconn);
313         std::pair<std::string, int> server_address = splitServerAddress(args[2]);
314         newSBconn->connect(server_address.first, server_address.second);
315     }
316 
handle_ADG(std::vector<std::string> & args)317     void NotificationServerConnection::handle_ADG(std::vector<std::string> & args)
318     {
319         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
320         this->myNotificationServer()->externalCallbacks.addedGroup(this, decodeURL(args[3]), decimalFromString(args[4]));
321         this->myNotificationServer()->externalCallbacks.gotLatestListSerial(this, decimalFromString(args[2]));
322     }
323 
handle_RMG(std::vector<std::string> & args)324     void NotificationServerConnection::handle_RMG(std::vector<std::string> & args)
325     {
326         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
327         this->myNotificationServer()->externalCallbacks.removedGroup(this, decimalFromString(args[3]));
328         this->myNotificationServer()->externalCallbacks.gotLatestListSerial(this, decimalFromString(args[2]));
329     }
330 
handle_REG(std::vector<std::string> & args)331     void NotificationServerConnection::handle_REG(std::vector<std::string> & args)
332     {
333         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
334         this->myNotificationServer()->externalCallbacks.renamedGroup(this, decimalFromString(args[3]), decodeURL(args[4]));
335         this->myNotificationServer()->externalCallbacks.gotLatestListSerial(this, decimalFromString(args[2]));
336     }
337 
338 
setState(BuddyStatus state)339     void NotificationServerConnection::setState(BuddyStatus state)
340     {
341         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
342         std::ostringstream buf_;
343         buf_ << "CHG " << this->trID++ << " " << buddyStatusToString(state) << " 0\r\n";
344         write(buf_);
345     }
346 
setBLP(char setting)347     void NotificationServerConnection::setBLP(char setting)
348     {
349         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
350         std::ostringstream buf_;
351         buf_ << "BLP " << this->trID++ << " " << setting << "L\r\n";
352         write(buf_);
353     }
354 
setGTC(char setting)355     void NotificationServerConnection::setGTC(char setting)
356     {
357         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
358         std::ostringstream buf_;
359         buf_ << "GTC " << this->trID++ << " " << setting << "\r\n";
360         write(buf_);
361     }
362 
setFriendlyName(std::string friendlyName)363     void NotificationServerConnection::setFriendlyName(std::string friendlyName) throw (std::runtime_error)
364     {
365         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
366         if (friendlyName.size() > 387)
367             throw std::runtime_error("Friendly name too long!");
368 
369         std::ostringstream buf_;
370         buf_ << "REA " << this->trID++ << " " << this->auth.username << " " << encodeURL(friendlyName) << "\r\n";
371         write(buf_);
372     }
373 
addToList(std::string list,Passport buddyName)374     void NotificationServerConnection::addToList(std::string list, Passport buddyName)
375     {
376         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
377         std::ostringstream buf_;
378         buf_ << "ADD " << this->trID++ << " " << list << " " << buddyName << " " << buddyName << "\r\n";
379         write(buf_);
380     }
381 
removeFromList(std::string list,Passport buddyName)382     void NotificationServerConnection::removeFromList(std::string list, Passport buddyName)
383     {
384         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
385         std::ostringstream buf_;
386         buf_ << "REM " << this->trID++ << " " << list << " " << buddyName << "\r\n";
387         write(buf_);
388     }
389 
addToGroup(Passport buddyName,int groupID)390     void NotificationServerConnection::addToGroup(Passport buddyName, int groupID)
391     {
392         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
393         std::ostringstream buf_;
394         buf_ << "ADD " << this->trID++ << " " << "FL" << " " << buddyName << " " << buddyName <<  " " << groupID << "\r\n";
395         write(buf_);
396     }
397 
removeFromGroup(Passport buddyName,int groupID)398     void NotificationServerConnection::removeFromGroup(Passport buddyName, int groupID)
399     {
400         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
401         std::ostringstream buf_;
402         buf_ << "REM " << this->trID++ << " " << "FL" << " " << buddyName;
403         if( groupID > 0 ) {
404             buf_ << " " << groupID;
405         }
406         buf_ << "\r\n";
407         write(buf_);
408     }
409 
addGroup(std::string groupName)410     void NotificationServerConnection::addGroup(std::string groupName)
411     {
412         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
413         std::ostringstream buf_;
414         buf_ << "ADG " << this->trID++ << " " << encodeURL(groupName) << " " << 0 << "\r\n";
415         write(buf_);
416     }
417 
removeGroup(int groupID)418     void NotificationServerConnection::removeGroup(int groupID)
419     {
420         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
421         std::ostringstream buf_;
422         buf_ << "RMG " << this->trID++ << " " << groupID << "\r\n";
423         write(buf_);
424     }
425 
renameGroup(int groupID,std::string newGroupName)426     void NotificationServerConnection::renameGroup(int groupID, std::string newGroupName)
427     {
428         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
429         std::ostringstream buf_;
430         buf_ << "REG " << this->trID++ << " " << groupID << " " << encodeURL(newGroupName) << " " << 0 << "\r\n";
431         write(buf_);
432     }
433 
434 
synchronizeLists(int version)435     void NotificationServerConnection::synchronizeLists(int version)
436     {
437         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
438         this->assertConnectionStateIsNot(NS_SYNCHRONISING);
439         ListSyncInfo *info = new ListSyncInfo(version);
440 
441         std::ostringstream buf_;
442         buf_ << "SYN " << this->trID << " " << version << "\r\n";
443         if (write(buf_) != buf_.str().size())
444             return;
445 
446         this->addCallback(&NotificationServerConnection::callback_SyncData, this->trID, (void *)info);
447         this->synctrid = this->trID++;
448         this->setConnectionState(NS_SYNCHRONISING);
449     }
450 
sendPing()451     void NotificationServerConnection::sendPing()
452     {
453         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
454         write("PNG\r\n");
455     }
456 
requestSwitchboardConnection(const void * tag)457     void NotificationServerConnection::requestSwitchboardConnection(const void *tag)
458     {
459         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
460         SwitchboardServerConnection::AuthData *auth = new SwitchboardServerConnection::AuthData(this->auth.username, tag);
461         std::ostringstream buf_;
462         buf_ << "XFR " << this->trID << " SB\r\n";
463         if (write(buf_) != buf_.str().size())
464             return;
465 
466         this->addCallback(&NotificationServerConnection::callback_TransferToSwitchboard, this->trID++, (void *)auth);
467     }
468 
469     template <class _Tp>
470     class _sameUserName
471     {
472         Buddy buddy;
473 public:
_sameUserName(const _Tp & __u)474         _sameUserName(const _Tp &__u) : buddy(__u) {};
operator ()(const _Tp & __x)475         bool operator()(const _Tp &__x) { return __x.userName == buddy.userName; }
476     };
477 
checkReverseList(ListSyncInfo * info)478     void NotificationServerConnection::checkReverseList(ListSyncInfo *info)
479     {
480         std::list<Buddy> & flist = info->reverseList;
481         std::list<Buddy> & alist = info->allowList;
482         std::list<Buddy> & blist = info->blockList;
483         std::list<Buddy>::iterator flist_i;
484         std::list<Buddy>::iterator alist_i;
485         std::list<Buddy>::iterator blist_i;
486 
487         for (flist_i = flist.begin(); flist_i != flist.end(); flist_i++)
488         {
489             if (std::count_if(alist.begin(), alist.end(), _sameUserName<Buddy>(*flist_i)) == 0 &&
490                 std::count_if(blist.begin(), blist.end(), _sameUserName<Buddy>(*flist_i)) == 0)
491             {
492                 this->myNotificationServer()->externalCallbacks.gotNewReverseListEntry(this, (*flist_i).userName, (*flist_i).friendlyName);
493             }
494         }
495     }
496 
socketConnectionCompleted()497     void NotificationServerConnection::socketConnectionCompleted()
498     {
499         this->assertConnectionStateIs(NS_CONNECTING);
500         this->setConnectionState(NS_CONNECTED);
501 
502         Connection::socketConnectionCompleted();
503 
504         // If an error occurs in Connection::socketConnectionCompleted, we
505         // will be disconnected before we get here.
506         if (this->connectionState() != NS_DISCONNECTED)
507         {
508             this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock);
509             this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 1, 0);
510         }
511     }
512 
connect(const std::string & hostname,unsigned int port)513     void NotificationServerConnection::connect(const std::string & hostname, unsigned int port)
514     {
515         this->assertConnectionStateIs(NS_DISCONNECTED);
516         connectinfo *info = new connectinfo(this->auth.username, this->auth.password);
517 
518         if ((this->sock = this->myNotificationServer()->externalCallbacks.connectToServer(hostname, port, &this->connected)) == -1)
519         {
520             this->myNotificationServer()->externalCallbacks.showError(this, "Could not connect to MSN server");
521             this->myNotificationServer()->externalCallbacks.closingConnection(this);
522             return;
523         }
524         this->setConnectionState(NS_CONNECTING);
525         this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 0, 1);
526         if (this->connected)
527             this->socketConnectionCompleted();
528 
529         std::ostringstream buf_;
530         buf_ << "VER " << this->trID << " MSNP8\r\n";
531         if (this->write(buf_) != buf_.str().size())
532             return;
533 
534         this->addCallback(&NotificationServerConnection::callback_NegotiateCVR, this->trID++, (void *)info);
535     }
536 
connect(const std::string & hostname,unsigned int port,const Passport & username,const std::string & password)537     void NotificationServerConnection::connect(const std::string & hostname, unsigned int port, const Passport & username, const std::string & password)
538     {
539         this->auth.username = username;
540         this->auth.password = password;
541         this->connect(hostname, port);
542     }
543 
disconnect()544     void NotificationServerConnection::disconnect()
545     {
546         this->assertConnectionStateIsNot(NS_DISCONNECTED);
547 
548         std::list<SwitchboardServerConnection *> list = _switchboardConnections;
549         std::list<SwitchboardServerConnection *>::iterator i = list.begin();
550         for (; i != list.end(); i++)
551         {
552             delete *i;
553         }
554 
555         this->callbacks.clear();
556 
557         this->setConnectionState(NS_DISCONNECTED);
558         this->myNotificationServer()->externalCallbacks.closingConnection(this);
559         Connection::disconnect();
560     }
561 
disconnectForTransfer()562     void NotificationServerConnection::disconnectForTransfer()
563     {
564         this->assertConnectionStateIsNot(NS_DISCONNECTED);
565         this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock);
566         ::close(this->sock);
567         this->setConnectionState(NS_DISCONNECTED);
568     }
569 
handleIncomingData()570     void NotificationServerConnection::handleIncomingData()
571     {
572         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
573         while (this->isWholeLineAvailable())
574         {
575             std::vector<std::string> args = this->getLine();
576             if ((args.size() >= 4 && args[0] == "MSG") ||
577                 (args.size() >= 2 && (args[0] == "NOT" || args[0] == "IPG")))
578             {
579                 int dataLength;
580                 if (args[0] == "MSG")
581                     dataLength = decimalFromString(args[3]);
582                 else
583                     dataLength = decimalFromString(args[1]);
584 
585                 if (this->readBuffer.find("\r\n", 0) + 2 + dataLength > this->readBuffer.size()) {
586                     this->myNotificationServer()->externalCallbacks.showError(this, "Could not read buffer: too small");
587                     return;
588                 }
589             }
590             this->readBuffer = this->readBuffer.substr(this->readBuffer.find("\r\n", 0) + 2);
591             int trid = 0;
592 
593             if (args.size() >= 6 && args[0] == "XFR" && args[2] == "NS")
594             {
595                 // XFR TrID NS NotificationServerIP:Port 0 ThisServerIP:Port
596                 //  0    1  2             3              4        5
597                 this->callbacks.clear(); // delete the callback data
598 
599                 this->disconnectForTransfer();
600 
601                 std::pair<std::string, int> server_address = splitServerAddress(args[3]);
602                 this->connect(server_address.first, server_address.second);
603                 return;
604             }
605 
606             if (args.size() >= 7 && args[0] == "RNG")
607             {
608                 // RNG SessionID SwitchboardServerIP:Port CKI AuthString InvitingUser InvitingDisplayName
609                 // 0      1                 2              3       4           5             6
610                 this->handle_RNG(args);
611                 return;
612             }
613 
614             if (args.size() >= 2 && args[0] == "QNG")
615             {
616                 // QNG
617                 //  0
618 
619                 this->_nextPing = 50;
620                 return;
621             }
622 
623             if ((args.size() >= 4 && (args[0] == "LST" || args[0] == "LSG")) ||
624                 (args.size() >= 2 && (args[0] == "GTC" || args[0] == "BLP")) ||
625                 (args.size() >= 3 && args[0] == "BPR")
626                 )
627             {
628                 // LST UserName FriendlyName UserLists [GroupNumbers]
629                 //  0      1          2          3           4
630                 //
631                 // or
632                 // (GTC|BLP) [TrID] [ListVersion] Setting
633                 //     0        1        2          4
634 
635                 if (this->synctrid)
636                 {
637                     trid = this->synctrid;
638                 }
639                 else
640                 {
641                     trid = decimalFromString(args[1]);
642                 }
643             }
644             else if (args.size() > 1)
645             {
646                 try
647                 {
648                     trid = decimalFromString(args[1]);
649                 }
650                 catch (...)
651                 {
652                 }
653             }
654 
655             if (!this->callbacks.empty() && trid >= 0)
656             {
657                 if (this->callbacks.find(trid) != this->callbacks.end())
658                 {
659                     (this->*(this->callbacks[trid].first))(args, trid, this->callbacks[trid].second);
660                     continue;
661                 }
662             }
663 
664             if (isdigit(args[0][0]))
665                 this->showError(decimalFromString(args[0]));
666             else
667                 this->dispatchCommand(args);
668         }
669     }
670 
callback_SyncData(std::vector<std::string> & args,int trid,void * data)671     void NotificationServerConnection::callback_SyncData(std::vector<std::string> & args, int trid, void *data) throw (std::runtime_error)
672     {
673         this->assertConnectionStateIs(NS_SYNCHRONISING);
674         ListSyncInfo *info = static_cast<ListSyncInfo *>(data);
675 
676         if (args[0] == "SYN")
677         {
678             if (info->listVersion == decimalFromString(args[2]))
679             {
680                 delete info;
681                 info = NULL;
682                 this->removeCallback(trid);
683                 this->myNotificationServer()->externalCallbacks.gotBuddyListInfo(this, NULL);
684                 this->setConnectionState(NS_CONNECTED);
685                 return;
686             }
687             else
688             {
689                 info->listVersion = decimalFromString(args[2]);
690                 info->usersRemaining = decimalFromString(args[3]);
691                 info->groupsRemaining = decimalFromString(args[4]);
692                 this->myNotificationServer()->externalCallbacks.gotLatestListSerial(this, info->listVersion);
693             }
694         }
695         else if (args[0] == "LST")
696         {
697             int list = decimalFromString(args[3]);
698             if ((list & ListSyncInfo::LST_FL) == ListSyncInfo::LST_FL)
699             {
700                 info->forwardList.push_back(Buddy(args[1], decodeURL(args[2])));
701                 std::vector<std::string> groups = splitString(args[4], ",");
702                 std::vector<std::string>::iterator i = groups.begin();
703                 for (; i != groups.end(); i++)
704                 {
705                     int group = atoi(i->c_str());
706                     info->groups[group].buddies.push_back(&(info->forwardList.back()));
707                     info->forwardList.back().groups.push_back(&info->groups[group]);
708                 }
709             }
710             if ((list & ListSyncInfo::LST_RL) == ListSyncInfo::LST_RL)
711             {
712                 info->reverseList.push_back(Buddy(args[1], decodeURL(args[2])));
713             }
714             if ((list & ListSyncInfo::LST_AL) == ListSyncInfo::LST_AL)
715             {
716                 info->allowList.push_back(Buddy(args[1], decodeURL(args[2])));
717             }
718             if ((list & ListSyncInfo::LST_BL) == ListSyncInfo::LST_BL)
719             {
720                 info->blockList.push_back(Buddy(args[1], decodeURL(args[2])));
721             }
722             info->usersRemaining--;
723             if (info->usersRemaining == 0)
724             {
725                 info->progress |= ListSyncInfo::LST_FL | ListSyncInfo::LST_RL | ListSyncInfo::LST_AL | ListSyncInfo::LST_BL;
726             }
727         }
728         else if (args[0] == "GTC")
729         {
730             info->reverseListPrompting = args[1][0];
731             info->progress |= ListSyncInfo::COMPLETE_GTC;
732             this->myNotificationServer()->externalCallbacks.gotGTC(this, info->reverseListPrompting);
733         }
734         else if (args[0] == "BLP")
735         {
736             info->privacySetting = args[1][0];
737             info->progress |= ListSyncInfo::COMPLETE_BLP;
738             this->myNotificationServer()->externalCallbacks.gotBLP(this, info->privacySetting);
739         }
740         else if (args[0] == "LSG")
741         {
742             int groupID = decimalFromString(args[1]);
743             Group g(groupID, decodeURL(args[2]));
744             info->groups[groupID] = g;
745 			info->groupsRemaining--;
746             if (info->groupsRemaining == 0 && info->usersRemaining == 0)
747             {
748                 info->progress |= ListSyncInfo::LST_FL | ListSyncInfo::LST_RL | ListSyncInfo::LST_AL | ListSyncInfo::LST_BL;
749             }
750         }
751         else if (args[0] == "BPR")
752         {
753             bool enabled;
754             if (decodeURL(args[2])[0] == 'Y')
755                 enabled = true;
756             else
757                 enabled = false;
758             info->forwardList.back().phoneNumbers.push_back(Buddy::PhoneNumber(args[1],
759                                                                                decodeURL(args[2]),
760                                                                                enabled));
761         }
762         else
763             throw std::runtime_error("Unexpected sync data");
764 
765         if (info->progress == (ListSyncInfo::LST_FL | ListSyncInfo::LST_RL |
766                                ListSyncInfo::LST_AL | ListSyncInfo::LST_BL |
767                                ListSyncInfo::COMPLETE_BLP | ListSyncInfo::COMPLETE_GTC))
768         {
769             this->removeCallback(trid);
770             this->checkReverseList(info);
771             this->myNotificationServer()->externalCallbacks.gotBuddyListInfo(this, info);
772             this->synctrid = 0;
773             delete info;
774             this->setConnectionState(NS_CONNECTED);
775         }
776         else if (info->progress > 63 || info->progress < 0)
777             throw std::runtime_error("Corrupt sync progress!");
778     }
779 
780 
callback_NegotiateCVR(std::vector<std::string> & args,int trid,void * data)781     void NotificationServerConnection::callback_NegotiateCVR(std::vector<std::string> & args, int trid, void *data)
782     {
783         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
784         connectinfo * info = (connectinfo *) data;
785         this->removeCallback(trid);
786 
787         if (args.size() >= 3 && args[0] != "VER" || args[2] != "MSNP8") // if either *differs*...
788         {
789             this->myNotificationServer()->externalCallbacks.showError(NULL, "Protocol negotiation failed");
790             delete info;
791             this->disconnect();
792             return;
793         }
794 
795         std::ostringstream buf_;
796         buf_ << "CVR " << this->trID << " 0x0409 winnt 5.2 i386 MSNMSGR 7.5.0324 MSMSGS " << info->username << "\r\n";
797         if (this->write(buf_) != buf_.str().size())
798             return;
799         this->addCallback(&NotificationServerConnection::callback_RequestUSR, this->trID++, (void *) data);
800     }
801 
callback_TransferToSwitchboard(std::vector<std::string> & args,int trid,void * data)802     void NotificationServerConnection::callback_TransferToSwitchboard(std::vector<std::string> & args, int trid, void *data)
803     {
804         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
805         SwitchboardServerConnection::AuthData *auth = static_cast<SwitchboardServerConnection::AuthData *>(data);
806         this->removeCallback(trid);
807 
808         if (args[0] != "XFR")
809         {
810             this->showError(decimalFromString(args[0]));
811             this->disconnect();
812             delete auth;
813             return;
814         }
815 
816         auth->cookie = args[5];
817         auth->sessionID = "";
818 
819         SwitchboardServerConnection *newconn = new SwitchboardServerConnection(*auth, *this);
820 
821         this->addSwitchboardConnection(newconn);
822         std::pair<std::string, int> server_address = splitServerAddress(args[3]);
823         newconn->connect(server_address.first, server_address.second);
824 
825         delete auth;
826     }
827 
callback_RequestUSR(std::vector<std::string> & args,int trid,void * data)828     void NotificationServerConnection::callback_RequestUSR(std::vector<std::string> & args, int trid, void *data)
829     {
830         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
831         connectinfo *info = (connectinfo *)data;
832         this->removeCallback(trid);
833 
834         if (args.size() > 1 && args[0] != "CVR") // if*differs*...
835         {
836             this->myNotificationServer()->externalCallbacks.showError(NULL, "Protocol negotiation failed");
837             delete info;
838             this->disconnect();
839             return;
840         }
841 
842         std::ostringstream buf_;
843         buf_ << "USR " << this->trID << " TWN I " << info->username << "\r\n";
844         if (this->write(buf_) != buf_.str().size())
845             return;
846 
847         this->addCallback(&NotificationServerConnection::callback_PassportAuthentication, this->trID++, (void *) data);
848     }
849 
callback_PassportAuthentication(std::vector<std::string> & args,int trid,void * data)850     void NotificationServerConnection::callback_PassportAuthentication(std::vector<std::string> & args, int trid, void * data)
851     {
852         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
853         connectinfo * info;
854 
855         CURL *curl;
856         CURLcode ret;
857         curl_slist *slist = NULL;
858         std::string auth;
859         char *uname, *pword;
860         std::string proxy;
861 
862         info=(connectinfo *)data;
863         this->removeCallback(trid);
864 
865         if (isdigit(args[0][0]))
866         {
867             this->showError(decimalFromString(args[0]));
868             delete info;
869             this->disconnect();
870             return;
871         }
872 
873         if (args.size() >= 4 && args[4].empty()) {
874             this->disconnect();
875             delete info;
876             return;
877         }
878 
879         /* Now to fire off some HTTPS login */
880         /* args[4] contains the most interesting part we need to send on */
881         curl = curl_easy_init();
882         if (curl == NULL) {
883             this->disconnect();
884             delete info;
885             return;
886         }
887 
888         proxy = this->myNotificationServer()->externalCallbacks.getSecureHTTPProxy();
889         if (! proxy.empty())
890             ret = curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
891         else
892             ret = CURLE_OK;
893 
894         if (ret == CURLE_OK)
895             ret = curl_easy_setopt(curl, CURLOPT_URL, "https://login.passport.com/login2.srf");
896 
897         curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
898         uname = curl_escape(const_cast<char *>(info->username.c_str()), 0);
899         pword = curl_escape(const_cast<char *>(info->password.c_str()), 0);
900         auth = std::string("Authorization: Passport1.4 OrgVerb=GET,OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom,sign-in=") + uname + ",pwd=" + pword + ","+ args[4];
901         curl_free(uname);
902         curl_free(pword);
903         slist = curl_slist_append(slist, auth.c_str());
904 
905         if (ret == CURLE_OK)
906             ret = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
907         if (ret == CURLE_OK)
908             ret = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
909         if (ret == CURLE_OK)
910             ret = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
911         /*if (ret == CURLE_OK)
912             ret = curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1);*/
913         /*  if (ret == CURLE_OK)
914             ret = curl_easy_setopt(curl, CURLOPT_USERAGENT, "msnlib/1.0 (Proteus)");*/
915         if (ret == CURLE_OK)
916             ret = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &msn_handle_curl_write);
917         /*if (ret == CURLE_OK)
918             ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, info);*/
919         if (ret == CURLE_OK)
920             ret = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &msn_handle_curl_header);
921         if (ret == CURLE_OK)
922             ret = curl_easy_setopt(curl, CURLOPT_WRITEHEADER, info);
923 
924         if (ret == CURLE_OK)
925             ret = curl_easy_perform(curl);
926 
927         curl_easy_cleanup(curl);
928         curl_slist_free_all(slist);
929 
930         /* ok, if auth succeeded info->cookie is now the cookie to pass back */
931         if (info->cookie.empty())
932         {
933             // No authentication cookie /usually/ means that authentication failed.
934             this->showError(911);
935 
936             this->disconnect();
937             delete info;
938             return;
939         }
940 
941         std::ostringstream buf_;
942         buf_ << "USR " << this->trID << " TWN S " << info->cookie << "\r\n";
943         if (this->write(buf_) != buf_.str().size())
944             return;
945         this->addCallback(&NotificationServerConnection::callback_AuthenticationComplete, this->trID++, (void *) data);
946     }
947 
callback_AuthenticationComplete(std::vector<std::string> & args,int trid,void * data)948     void NotificationServerConnection::callback_AuthenticationComplete(std::vector<std::string> & args, int trid, void * data)
949     {
950         this->assertConnectionStateIsAtLeast(NS_CONNECTED);
951         connectinfo * info = (connectinfo *) data;
952         this->removeCallback(trid);
953 
954         if (isdigit(args[0][0]))
955         {
956             this->showError(decimalFromString(args[0]));
957             delete info;
958             this->disconnect();
959             return;
960         }
961 
962         this->myNotificationServer()->externalCallbacks.gotFriendlyName(this, decodeURL(args[4]));
963 
964         delete info;
965 
966         this->myNotificationServer()->externalCallbacks.gotNewConnection(this);
967     }
968 
969 
msn_handle_curl_write(void * ptr,size_t size,size_t nmemb,void * stream)970     static size_t msn_handle_curl_write(void *ptr, size_t size, size_t nmemb, void  *stream) {
971         return size * nmemb;
972     }
973 
msn_handle_curl_header(void * ptr,size_t size,size_t nmemb,void * stream)974     static size_t msn_handle_curl_header(void *ptr, size_t size, size_t nmemb, void *stream)
975     {
976         connectinfo * info;
977         std::string cookiedata;
978 
979         info = (connectinfo *)stream;
980 
981         if ((size * nmemb) < strlen("Authentication-Info:"))
982             return (size * nmemb);
983 
984         std::string headers_ = std::string((char *)ptr, size * nmemb);
985         Message::Headers headers = Message::Headers(headers_);
986         cookiedata = headers["Authentication-Info:"];
987 
988         if (! cookiedata.empty())
989         {
990             size_t pos = cookiedata.find(",from-PP='");
991             if (pos == std::string::npos) {
992                 info->cookie = "";
993             } else {
994                 info->cookie = cookiedata.substr(pos + strlen(",from-PP='"));
995                 pos = info->cookie.find("'");
996                 if (pos != std::string::npos)
997                     info->cookie = info->cookie.substr(0, pos);
998             }
999         }
1000 
1001         return (size * nmemb);
1002     }
1003 }
1004