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