status_t MSNManager::TypingNotification(const char *passport, uint16 typing) { LOG(kProtocolName, liDebug, "Typing notify to %s", passport); if ((fConnectionState != otOffline) && (fConnectionState != otConnecting)) { if (fNoticeCon == NULL) { LOG(kProtocolName, liDebug, "Can't send typing notify to %s, fNoticeCon is null", passport); return B_ERROR; } // Set up message Command *msg = new Command("MSG"); msg->AddParam("U"); BString format = "MIME-Version: 1.0\r\n" "Content-Type: text/x-msmsgscontrol\r\n" "TypingUser: "******"\r\n"; format << "\r\n\r\n"; msg->AddPayload(format.String(), format.Length()); // Find connection bool needSB = false; connectionlist::iterator it; for ( it=fConnections.begin(); it != fConnections.end(); it++ ) { MSNSBConnection * c = dynamic_cast<MSNSBConnection*>( *it ); if ( c != NULL && c->IsSingleChatWith( passport ) ) break; } Command *sbReq = NULL; if (it == fConnections.end()) { LOG(kProtocolName, liDebug, "Can't send typing notify to %s, no open connection: opening new", passport); sbReq = new Command("XFR"); sbReq->AddParam("SB"); // Request a SB connection; fNoticeCon->Send(sbReq, qsImmediate); needSB = true; }; if (needSB) { fWaitingSBs[sbReq->TransactionID()] = std::pair<BString,Command*>(passport, msg); } else { (*it)->Send(msg); }; return B_OK; }; LOG(kProtocolName, liHigh, "Error sending typing notification to %s", passport); return B_ERROR; };
status_t MSNManager::MessageUser(const char *passport, const char *message) { if ((fConnectionState != otOffline) && (fConnectionState != otConnecting)) { if (fNoticeCon == NULL) { LOG(kProtocolName, liDebug, "Could not message %s, fNoticeCon is null", passport); return B_ERROR; } // Set up message Command *msg = new Command("MSG"); msg->AddParam("N"); // Don't ack packet BString format = "MIME-Version: 1.0\r\n" "Content-Type: text/plain; charset=UTF-8\r\n" "X-MMS-IM-Format: FN=Arial; EF=I; CO=0; CS=0; PF=22\r\n\r\n"; format << message; msg->AddPayload(format.String(), format.Length()); // Find connection bool needSB = false; connectionlist::iterator it; for ( it=fConnections.begin(); it != fConnections.end(); it++ ) { MSNSBConnection * c = dynamic_cast<MSNSBConnection*>( *it ); if ( c != NULL && c->IsSingleChatWith( passport ) ) break; } Command *sbReq = NULL; if (it == fConnections.end()) { LOG(kProtocolName, liDebug, "Could not message %s, no open connection: opening new", passport); sbReq = new Command("XFR"); sbReq->AddParam("SB"); // Request a SB connection; fNoticeCon->Send(sbReq, qsImmediate); needSB = true; }; if (needSB) { fWaitingSBs[sbReq->TransactionID()] = std::pair<BString,Command*>(passport, msg); } else { (*it)->Send(msg); }; return B_OK; }; LOG(kProtocolName, liHigh, "Error sending message to %s", passport); return B_ERROR; };
status_t MSNConnection::HandleUSR( Command * command ) { LOG(kProtocolName, liDebug, "C %lX: Processing USR", this); status_t result = B_ERROR; if (strcmp(command->Param(0), "OK") == 0) { Progress("MSN Login", "MSN: Logged in!", 1.0); GoOnline(); BMessage statusChange(msnmsgOurStatusChanged); statusChange.AddInt8("status", fState); fManMsgr.SendMessage(&statusChange); /* * Depending on the server protocol, server location and the * account being used (non-hotmail/msn acc), the server will not * respond on the PRP MFN command and our connection will break. * Michael */ BString passport(fManager->Passport()); if ((B_ERROR != passport.FindFirst("@hotmail")) || (B_ERROR != passport.FindFirst("@msn"))) { LOG(kProtocolName, liDebug, "Sending PRP MSN command" ); Command *rea = new Command("PRP"); rea->AddParam("MFN"); rea->AddParam(fManager->DisplayName(), true); Send(rea); } else { LOG(kProtocolName, liDebug, "Not sending PRP MSN command!"); }; fManager->Handler()->ContactList(&fContacts); Command *adl = new Command("ADL"); std::list<BString> contacts; std::map<BString, std::list<BString> > domainUsers; fManager->Handler()->ContactList(&contacts); // Make our contact list into a list by domain for (std::list<BString>::iterator iIt = contacts.begin(); iIt != contacts.end(); iIt++) { BString user; BString domain; if (SplitEmail((*iIt).String(), user, domain) == B_OK) { std::map<BString, std::list<BString> >::iterator dIt = domainUsers.find(domain); if (dIt == domainUsers.end()) { std::list<BString> users; users.push_back(user); domainUsers[domain] = users; } else { dIt->second.push_back(user); }; }; }; BString payload = "<ml l=\"1\">"; // Package each contact by domain for (std::map<BString, std::list<BString> >::iterator dIt = domainUsers.begin(); dIt != domainUsers.end(); dIt++) { payload << "<d n=\"" << dIt->first << "\">"; for (std::list<BString>::iterator uIt = dIt->second.begin(); uIt != dIt->second.end(); uIt++) { payload << "<c n=\"" << (*uIt) << "\" l=\"3\" t=\"1\" />"; }; payload << "</d>"; }; payload << "</ml>"; adl->AddPayload(payload.String()); Send(adl); Command *chg = new Command("CHG"); chg->AddParam("NLN"); BString caps = ""; caps << kOurCaps; chg->AddParam( caps.String() ); Send(chg); result = B_OK; } else { BString URI = command->Param(2); BString nonce = command->Param(3); BString authResp = ""; BString authToken = ""; SSO *auth = new SSO(NULL, fManager->Passport(), fManager->Password(), URI.String(), nonce.String()); result = auth->Response(authToken, authResp); if (result == B_OK) { Command *reply = new Command("USR"); reply->AddParam("SSO"); reply->AddParam("S"); reply->AddParam(authToken.String()); reply->AddParam(authResp.String()); reply->Debug(); Send(reply); }; }; return result; }
int32 MSNConnection::Receiver(void *con) { LOG(kProtocolName, liLow, "C[r] %lX: Receiver init", con); MSNConnection *connection = reinterpret_cast<MSNConnection *>(con); const uint32 kSleep = 2000000; BMessenger **kMsgr = &connection->fSockMsgr; int32 socket = 0; if ( !(*kMsgr)->IsValid() ) { LOG(kProtocolName, liLow, "C[r] %lX: Messenger wasn't valid!", connection); return B_ERROR; } BMessage reply; socket = connection->fSock; struct fd_set read; struct fd_set error; int16 bytes = 0; int32 processed = 0; char buffer[1024]; BString commandBuff = ""; int32 kNewLineLen = strlen("\r\n"); LOG(kProtocolName, liLow, "C[r] %lX: Starting receiver loop", connection); while ((*kMsgr)->IsValid() == true) { FD_ZERO(&read); FD_ZERO(&error); FD_SET(socket, &read); FD_SET(socket, &error); memset(buffer, 0, sizeof(buffer)); if (select(socket + 1, &read, NULL, &error, NULL) > 0) { if (FD_ISSET(socket, &error)) { LOG(kProtocolName, liLow, "C[r] %lX: Got socket error", connection); snooze(kSleep); continue; }; if (FD_ISSET(socket, &read)) { bytes = recv(socket, buffer, sizeof(buffer), 0); if (bytes > 0) { commandBuff.Append(buffer, bytes); processed += bytes; } else { if ((*kMsgr)->IsValid() == false) return B_OK; LOG(kProtocolName, liLow, "C[r] %lX: Socket got less than 0 (or 0)", connection); perror("SOCKET ERROR"); BMessage msg(msnmsgCloseConnection); msg.AddPointer("connection", con); connection->fManMsgr.SendMessage(&msg); connection->fState = otOffline; return B_ERROR; }; }; }; int32 commandLen = commandBuff.FindFirst("\r\n"); while (commandLen > 0) { BString command = ""; int32 totalLen = commandBuff.Length(); commandBuff.MoveInto(command, 0, commandLen + kNewLineLen); processed = totalLen - (commandLen + kNewLineLen); Command *comm = new Command(""); comm->MakeObject(command.String()); int32 payloadLen = 0; if (comm->ExpectsPayload(&payloadLen) == true) { LOG(kProtocolName, liDebug, "C[r] %lX: Payload of %i, have %i bytes", connection, payloadLen, processed); while (processed < payloadLen) { FD_ZERO(&read); FD_ZERO(&error); FD_SET(socket, &read); FD_SET(socket, &error); memset(buffer, 0, sizeof(buffer)); if (select(socket + 1, &read, NULL, &error, NULL) > 0) { if (FD_ISSET(socket, &error)) { LOG(kProtocolName, liLow, "C[r] %lX: Got socket error", connection); snooze(kSleep); continue; }; if (FD_ISSET(socket, &read)) { bytes = recv(socket, buffer, min_c(payloadLen - processed, (int32)sizeof(buffer)), 0); if (bytes > 0) { commandBuff.Append(buffer, bytes); processed += bytes; } else { if ( (*kMsgr)->IsValid() == false) return B_OK; LOG(kProtocolName, liLow, "--C[r] %lX: Socket got less than 0 (or 0: %i)", connection, bytes); perror("SOCKET ERROR"); BMessage msg(msnmsgCloseConnection); msg.AddPointer("connection", con); connection->fManMsgr.SendMessage(&msg); connection->fState = otOffline; return B_ERROR; }; }; }; }; // add payload to message? comm->AddPayload( commandBuff.String(), payloadLen, false ); commandBuff.Remove( 0, payloadLen ); processed = commandBuff.Length(); }; BMessage dataReady(msnmsgDataReady); dataReady.AddPointer("command", comm); (*kMsgr)->SendMessage(&dataReady); // this line needed to process several commands at once if they're in the // buffer commandLen = commandBuff.FindFirst("\r\n"); }; }; return B_OK; };
status_t MSNConnection::HandleCHL( Command * command ) { LOG(kProtocolName, liDebug, "C %lX: Processing CHL", this); const char *challenge = command->Param(0); int i = 0; unsigned char buf[256]; char chlString[128]; long long high = 0; long long low = 0; long long temp = 0; long long key = 0; long long bskey = 0; int *chlStringArray = (int *)chlString; int *md5hash = (int *)buf; char hash1a[17]; char hash2a[17]; sprintf((char *)buf + 16, "%s%s", challenge, kClientCode); MD5(buf + 16, strlen((char *)buf + 16), buf); for (i = 0; i < 16; i++) { sprintf((char *)buf + 16 + i * 2,"%02x", buf[i]); }; for (i = 0; i < 4; i++) { md5hash[i] = md5hash[i] & 0x7FFFFFFF; }; i = (strlen(challenge) + strlen(kClientID) + 7) & 0xF8; sprintf(chlString,"%s%s00000000", challenge, kClientID); chlString[i] = 0; for (i = 0; i < (int)strlen(chlString) / 4; i += 2) { temp = chlStringArray[i]; temp = (0x0E79A9C1 * temp) % 0x7FFFFFFF; temp += high; temp = md5hash[0] * temp + md5hash[1]; temp = temp % 0x7FFFFFFF; high = chlStringArray[i + 1]; high = (high + temp) % 0x7FFFFFFF; high = md5hash[2] * high + md5hash[3]; high = high % 0x7FFFFFFF; low = low + high + temp; }; high = (high + md5hash[1]) % 0x7FFFFFFF; low = (low + md5hash[3]) % 0x7FFFFFFF; key = (low << 32) + high; for (i= 0; i < 8; i++) { bskey <<= 8; bskey += key & 255; key >>=8; }; strncpy((char *)hash1a, (char *)buf + 16, 16); strncpy((char *)hash2a, (char *)buf + 32, 16); hash1a[16] = '\0'; hash2a[16] = '\0'; sprintf((char *)buf, "%llx%llx", strtoull(hash1a,NULL,16) ^ bskey, strtoull(hash2a,NULL,16) ^ bskey); Command *reply = new Command("QRY"); reply->AddParam(kClientID); reply->AddPayload((char *)buf, 32); Send(reply); reply->Debug(); return B_OK; };