Chat-O-Matic/libs/libmsn/notificationserver.cpp

1739 lines
67 KiB
C++
Raw Normal View History

/*
* notificationserver.cpp
* libmsn
*
* Created by Mark Rowe on Mon Mar 22 2004.
* Refactored by Tiago Salem Herrmann on 08/2007.
* Copyright (c) 2004 Mark Rowe. All rights reserved.
* Copyright (c) 2007 Tiago Salem Herrmann. All rights reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "notificationserver.h"
#include "errorcodes.h"
#include "externals.h"
#include "md5.h"
#include "util.h"
#include "soap.h"
#include <algorithm>
#include <cctype>
#include <cassert>
#ifndef WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#else
#include <io.h>
#endif
#include <stdio.h>
#include <string.h>
#include <map>
#include "xmlParser.h"
namespace MSN
{
std::map<std::string, void (NotificationServerConnection::*)(std::vector<std::string> &)> NotificationServerConnection::commandHandlers;
std::map<std::string, void (NotificationServerConnection::*)(std::vector<std::string> &, std::string, std::string)> NotificationServerConnection::messageHandlers;
NotificationServerConnection::NotificationServerConnection(Passport username_, std::string password_, Callbacks & cb_)
: Connection(), auth(username_, password_), myPassport(username_), m_clientId(0), externalCallbacks(cb_), _connectionState(NS_DISCONNECTED), generatingLockkey(false), removingOIM(false), bplSetting('B')
{
msnobj.setCreator(username_);
registerHandlers();
}
NotificationServerConnection::~NotificationServerConnection()
{
if (this->connectionState() != NS_DISCONNECTED)
this->disconnect();
}
Connection *NotificationServerConnection::connectionWithSocket(void *sock)
{
if (this->sock == sock)
return this;
std::vector<SwitchboardServerConnection *> & list = _switchboardConnections;
std::vector<SwitchboardServerConnection *>::iterator i = list.begin();
for (; i != list.end(); i++)
{
Connection *c = (*i)->connectionWithSocket(sock);
if (c)
return c;
}
std::vector<Soap *> & list2 = _SoapConnections;
std::vector<Soap *>::iterator d = list2.begin();
for (; d != list2.end(); d++)
{
if((*d)->sock == sock )
return (*d);
}
return NULL;
}
SwitchboardServerConnection *NotificationServerConnection::switchboardWithOnlyUser(Passport username)
{
if (this->connectionState() >= NS_CONNECTED)
{
std::vector<SwitchboardServerConnection *> & list = _switchboardConnections;
std::vector<SwitchboardServerConnection *>::iterator i = list.begin();
for (; i != list.end(); i++)
{
if ((*i)->users.size() == 1 &&
*((*i)->users.begin()) == username)
return *i;
}
}
return NULL;
}
const std::vector<SwitchboardServerConnection *> & NotificationServerConnection::switchboardConnections()
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
return _switchboardConnections;
}
void NotificationServerConnection::addSwitchboardConnection(SwitchboardServerConnection *c)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
_switchboardConnections.push_back(c);
}
void NotificationServerConnection::addSoapConnection(Soap *s)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
_SoapConnections.push_back(s);
}
void NotificationServerConnection::removeSwitchboardConnection(SwitchboardServerConnection *c)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
std::vector<SwitchboardServerConnection *>::iterator it;
for(it = _switchboardConnections.begin(); it != _switchboardConnections.end(); it++)
{
if((*it) == c)
{
_switchboardConnections.erase(it);
return;
}
}
}
void NotificationServerConnection::removeSoapConnection(Soap *s)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
std::vector<Soap *>::iterator it;
for(it = _SoapConnections.begin(); it != _SoapConnections.end(); it++)
{
if((*it) == s)
{
_SoapConnections.erase(it);
return;
}
}
}
void NotificationServerConnection::addCallback(NotificationServerCallback callback,
int trid, void *data)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTING);
this->callbacks[trid] = std::make_pair(callback, data);
}
void NotificationServerConnection::removeCallback(int trid)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTING);
this->callbacks.erase(trid);
}
void NotificationServerConnection::registerHandlers()
{
if (commandHandlers.size() == 0)
{
commandHandlers["OUT"] = &NotificationServerConnection::handle_OUT;
commandHandlers["RML"] = &NotificationServerConnection::handle_RML;
commandHandlers["BLP"] = &NotificationServerConnection::handle_BLP;
commandHandlers["CHG"] = &NotificationServerConnection::handle_CHG;
commandHandlers["CHL"] = &NotificationServerConnection::handle_CHL;
commandHandlers["ILN"] = &NotificationServerConnection::handle_ILN;
commandHandlers["NLN"] = &NotificationServerConnection::handle_NLN;
commandHandlers["FLN"] = &NotificationServerConnection::handle_FLN;
commandHandlers["MSG"] = &NotificationServerConnection::handle_MSG;
commandHandlers["PRP"] = &NotificationServerConnection::handle_PRP;
commandHandlers["UBX"] = &NotificationServerConnection::handle_UBX;
commandHandlers["GCF"] = &NotificationServerConnection::handle_GCF;
commandHandlers["ADL"] = &NotificationServerConnection::handle_ADL;
commandHandlers["UBN"] = &NotificationServerConnection::handle_UBN;
commandHandlers["FQY"] = &NotificationServerConnection::handle_FQY;
}
if (messageHandlers.size() == 0)
{
messageHandlers["text/x-msmsgsinitialemailnotification"] = &NotificationServerConnection::message_initial_email_notification;
messageHandlers["text/x-msmsgsinitialmdatanotification"] = &NotificationServerConnection::message_initialmdatanotification;
messageHandlers["text/x-msmsgsemailnotification"] = &NotificationServerConnection::message_email_notification;
messageHandlers["text/x-msmsgsprofile"] = &NotificationServerConnection::message_msmsgsprofile;
messageHandlers["text/x-msmsgsoimnotification"] = &NotificationServerConnection::message_oimnotification;
}
}
void NotificationServerConnection::dispatchCommand(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
std::map<std::string, void (NotificationServerConnection::*)(std::vector<std::string> &)>::iterator i = commandHandlers.find(args[0]);
if (i != commandHandlers.end())
(this->*commandHandlers[args[0]])(args);
}
void NotificationServerConnection::disconnectNS()
{
std::ostringstream buf_;
buf_ << "OUT\r\n";
if (this->write(buf_) != buf_.str().size())
return;
disconnect();
}
void NotificationServerConnection::handle_OUT(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
if (args.size() > 1)
{
if (args[1] == "OTH")
{
this->myNotificationServer()->externalCallbacks.showError(this, "You have logged onto MSN twice at once. Your MSN session will now terminate.");
}
else if (args[1] == "SSD")
{
this->myNotificationServer()->externalCallbacks.showError(this, "This MSN server is going down for maintenance. Your MSN session will now terminate.");
} else {
this->myNotificationServer()->externalCallbacks.showError(this, (std::string("The MSN server has terminated the connection with an unknown reason code. Please report this code: ") +
args[1]).c_str());
}
}
this->disconnect();
}
void NotificationServerConnection::handle_RML(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
int msglen;
std::string msg;
if(args[2] != "OK" && args[2] != "OK")
return; // TODO - raise an error
msglen = decimalFromString(args[2]);
msg = this->readBuffer.substr(0, msglen);
this->readBuffer = this->readBuffer.substr(msglen);
XMLNode rml_data = XMLNode::parseString( msg.c_str() );
int nDomains = rml_data.nChildNode("d");
for(int i=0; i<nDomains; i++)
{
XMLNode domain = rml_data.getChildNode("d", i);
std::string domain_name = domain.getAttribute("n",0);
int nContacts = domain.nChildNode("c");
for(int j=0; j< nContacts; j++)
{
XMLNode contact = domain.getChildNode("c",j);
std::string contact_name = contact.getAttribute("n",0);
MSN::ContactList list_number = (MSN::ContactList) decimalFromString(contact.getAttribute("l",0));
// type is 1 for normal contact, 4 for mobile phone
// TODO - use this value
//int type = decimalFromString(contact.getAttribute("t",0));
MSN::Passport passport(contact_name+"@"+domain_name);
this->myNotificationServer()->externalCallbacks.removedListEntry(this, list_number, passport);
}
}
}
void NotificationServerConnection::handle_BLP(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
this->myNotificationServer()->externalCallbacks.gotBLP(this, args[3][0]);
}
void NotificationServerConnection::handle_CHG(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
this->myNotificationServer()->externalCallbacks.changedStatus(this, buddyStatusFromString(args[2]));
}
void NotificationServerConnection::handle_CHL(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
std::ostringstream buf_;
buf_ << "QRY " << this->trID++ << " " << szClientID << " 32\r\n";
if (write(buf_) != buf_.str().size())
return;
char b[33];
memset(&b,0,33);
DoMSNP11Challenge(args[2].c_str(),b);
// send the md5
std::string a(b);
write(a, false);
}
void NotificationServerConnection::handle_ILN(std::vector<std::string> & args)
{
this->assertConnectionStateIs(NS_CONNECTED);
if(args.size() > 7)
this->myNotificationServer()->externalCallbacks.buddyChangedStatus(this, args[3], decodeURL(args[5]), buddyStatusFromString(args[2]), decimalFromString(args[6]), decodeURL(args[7]));
else
this->myNotificationServer()->externalCallbacks.buddyChangedStatus(this, args[3], decodeURL(args[5]), buddyStatusFromString(args[2]), decimalFromString(args[6]), "");
}
void NotificationServerConnection::handle_NLN(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
if(args.size() > 6)
this->myNotificationServer()->externalCallbacks.buddyChangedStatus(this, args[2], decodeURL(args[4]), buddyStatusFromString(args[1]), decimalFromString(args[5]), decodeURL(args[6]));
else
this->myNotificationServer()->externalCallbacks.buddyChangedStatus(this, args[2], decodeURL(args[4]), buddyStatusFromString(args[1]), decimalFromString(args[5]), "");
}
void NotificationServerConnection::handle_FLN(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
this->myNotificationServer()->externalCallbacks.buddyOffline(this, args[1]);
}
void NotificationServerConnection::handle_UBN(std::vector<std::string> & args)
{
// TODO - UBN is a way to exchange data through notification server
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
int msglen;
std::string msg;
msglen = decimalFromString(args[3]);
msg = this->readBuffer.substr(0, msglen);
this->readBuffer = this->readBuffer.substr(msglen);
}
void NotificationServerConnection::handle_FQY(std::vector<std::string> & args)
{
// TODO - I dont know what it means yet
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
int msglen;
std::string msg;
msglen = decimalFromString(args[2]);
msg = this->readBuffer.substr(0, msglen);
this->readBuffer = this->readBuffer.substr(msglen);
}
void NotificationServerConnection::callback_URL(std::vector<std::string> & args, int trid, void *data)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
char b[33];
MSN::hotmailInfo info;
info.rru = args[2];
info.url = args[3];
info.id = args[4];
info.sl = toStr(time(NULL) - decimalFromString(login_time));
info.MSPAuth = MSPAuth;
info.sid = sid;
info.kv = kv;
// calculate creds
std::string creds_tmp = MSPAuth + info.sl + this->auth.password;
memset(&b,0,33);
md5_state_t state;
md5_byte_t digest[16];
int di;
md5_init(&state);
md5_append(&state, (const md5_byte_t *)creds_tmp.c_str(), creds_tmp.size());
md5_finish(&state, digest);
// convert to string
for (di = 0; di < 16; ++di)
sprintf(&b[2*di], "%02x", digest[di]);
std::string creds(b);
info.creds = creds;
this->myNotificationServer()->externalCallbacks.gotInboxUrl(this, info);
}
void NotificationServerConnection::handle_MSG(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
int msglen;
std::string msg;
std::string mime;
std::string body;
size_t tmp;
msglen = decimalFromString(args[3]);
msg = this->readBuffer.substr(0, msglen);
this->readBuffer = this->readBuffer.substr(msglen);
body = msg.substr(msg.find("\r\n\r\n") + 4);
mime = msg.substr(0, msg.size() - body.size());
std::string contentType;
Message::Headers headers = Message::Headers(mime);
contentType = headers["Content-Type"];
if ((tmp = contentType.find("; charset")) != std::string::npos)
contentType = contentType.substr(0, tmp);
std::map<std::string, void (NotificationServerConnection::*)(std::vector<std::string> &, std::string, std::string)>::iterator i = messageHandlers.find(contentType);
if (i != messageHandlers.end())
(this->*(messageHandlers[contentType]))(args, mime, body);
}
void NotificationServerConnection::message_initial_email_notification(std::vector<std::string> & args, std::string mime, std::string body)
{
std::string unreadInbox;
std::string unreadFolder;
int unreadInboxCount = 0, unreadFolderCount = 0;
// Initial email notifications body is a set of MIME headers
Message::Headers headers = Message::Headers(body);
unreadInbox = headers["Inbox-Unread"];
unreadFolder = headers["Folders-Unread"];
if (! unreadInbox.empty())
unreadInboxCount = decimalFromString(unreadInbox);
if (! unreadFolder.empty())
unreadFolderCount = decimalFromString(unreadFolder);
// this->myNotificationServer()->externalCallbacks.gotInitialEmailNotification(this, unreadInboxCount, unreadFolderCount);
}
void NotificationServerConnection::message_email_notification(std::vector<std::string> & args, std::string mime, std::string body)
{
// New email notifications body is a set of MIME headers
Message::Headers headers = Message::Headers(body);
std::string from = headers["From-Addr"];
std::string subject = headers["Subject"];
this->myNotificationServer()->externalCallbacks.gotNewEmailNotification(this, from, subject);
}
void NotificationServerConnection::message_msmsgsprofile(std::vector<std::string> & args, std::string mime, std::string body)
{
direct_connection=false;
Message::Headers headers = Message::Headers(mime);
server_reported_ip = headers["ClientIP"];
server_reported_port = headers["ClientPort"];
login_time = headers["LoginTime"];
MSPAuth = headers["MSPAuth"];
sid = headers["sid"];
kv = headers["kv"];
if (login_time.empty()) //IN MSNP9 there is no logintime it seems, so set it manualy
{
time_t actualTime;
std::stringstream os;
time(&actualTime);
os << actualTime;
login_time = os.str();
}
this->myNotificationServer()->externalCallbacks.gotNewConnection(this);
// TODO - test portability to windows and mac, probably solved by ifdefs,
// or with an external callback to user application
// search on local machine the ip reported by the server
/* int s = socket (PF_INET, SOCK_STREAM, 0);
for (int i=1;;i++)
{
struct ifreq ifr;
struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
char *ip;
ifr.ifr_ifindex = i;
if (ioctl (s, SIOCGIFNAME, &ifr) < 0)
break;
if (ioctl (s, SIOCGIFADDR, &ifr) < 0)
continue;
ip = inet_ntoa (sin->sin_addr);
std::string ip2(ip);
if(ip2==server_reported_ip)
direct_connection=true;
}
close (s);
*/
}
void NotificationServerConnection::message_initialmdatanotification(std::vector<std::string> & args, std::string mime, std::string body)
{
Message::Headers headers = Message::Headers(body);
std::string maildata = headers["Mail-Data"];
XMLNode domTree = XMLNode::parseString( maildata.c_str() );
//Mail-Data: <MD><E><I>2</I><IU>1</IU><O>1</O><OU>0</OU></E><Q><QTM>409600</QTM><QNM>204800</QNM></Q></MD>
//Mail-Data: <MD><E><I>3</I><IU>2</IU><O>2</O><OU>0</OU></E><Q><QTM>409600</QTM><QNM>204800</QNM></Q></MD>
//Mail-Data: <MD><E><I>3</I><IU>1</IU><O>2</O><OU>0</OU></E><Q><QTM>409600</QTM><QNM>204800</QNM></Q></MD>
//empty inbox
//Mail-Data: <MD><E><I>0</I><IU>0</IU><O>5</O><OU>0</OU></E><Q><QTM>409600</QTM><QNM>204800</QNM></Q></MD>
int emails = domTree.nChildNode("E");
if (emails)
{
XMLNode curr_element = domTree.getChildNode("E",0);
int inbox_msgs = decimalFromString(curr_element.getChildNode("I").getText());
int inbox_unread = decimalFromString(curr_element.getChildNode("IU").getText());
int other_folders = decimalFromString(curr_element.getChildNode("O").getText());
int other_folders_unread = decimalFromString(curr_element.getChildNode("OU").getText());
this->myNotificationServer()->externalCallbacks. gotInitialEmailNotification(this, inbox_msgs, inbox_unread, other_folders, other_folders_unread);
}
// try to get OIM information
message_oimnotification(args, mime, body);
}
void NotificationServerConnection::message_oimnotification(std::vector<std::string> & args, std::string mime, std::string body)
{
Message::Headers headers = Message::Headers(body);
std::string maildata = headers["Mail-Data"];
if(maildata == "too-large")
{
// more than 25 OIM's
// request OIM list through soap
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->getMailData();
return;
}
// process maildata
gotMailData(maildata);
}
void NotificationServerConnection::gotSoapMailData(Soap & soapConnection, std::string maildata)
{
gotMailData(maildata);
}
void NotificationServerConnection::gotMailData(std::string maildata)
{
std::vector<eachOIM> messages;
XMLNode domTree = XMLNode::parseString( maildata.c_str() );
int oims = domTree.nChildNode("M");
if (oims)
{
for(int i=0;i<oims;i++)
{
eachOIM temp_oim;
XMLNode curr_element = domTree.getChildNode("M",i);
temp_oim.from = curr_element.getChildNode("E").getText();
temp_oim.id = curr_element.getChildNode("I").getText();
temp_oim.fromFN = curr_element.getChildNode("N").getText();
std::vector<std::string> friendlyName;
// if we do not have '?', it is just the email
if(temp_oim.fromFN.find("?") != std::string::npos)
{
friendlyName=splitString(temp_oim.fromFN,"?");
// TODO - handle the encoding (friendlyName[1])
if(friendlyName[2]=="B")
{
temp_oim.fromFN=b64_decode(friendlyName[3].c_str());
}
if(friendlyName[2]=="Q")
{
// Quoted-Printable, is similar to URL encoding,
// but uses "=" instead of "%".
std::string change = friendlyName[3];
// changes the = by %
for(unsigned int a=0;a<change.length();a++)
if(change[a]=='=') change[a]='%';
temp_oim.fromFN=decodeURL(change);
}
}
messages.push_back(temp_oim);
}
this->myNotificationServer()->externalCallbacks.gotOIMList(this, messages);
}
domTree.deleteNodeContent('Y');
}
void NotificationServerConnection::handle_RNG(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
SwitchboardServerConnection::AuthData auth = SwitchboardServerConnection::AuthData(this->auth.username,
args[1],
args[4]);
SwitchboardServerConnection *newSBconn = new SwitchboardServerConnection(auth, *this);
this->addSwitchboardConnection(newSBconn);
std::pair<std::string, int> server_address = splitServerAddress(args[2]);
newSBconn->connect(server_address.first, server_address.second);
}
void NotificationServerConnection::handle_PRP(std::vector<std::string> & args)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
if(this->_connectionState == NS_SYNCHRONISING)
{
this->myNotificationServer()->externalCallbacks.gotFriendlyName(this, decodeURL(args[3]));
this->myDisplayName = decodeURL(args[3]);
this->myNotificationServer()->externalCallbacks.connectionReady(this);
// the initial process ends here
this->setConnectionState(NS_CONNECTED);
return;
}
if ( args[2] == "MFN") //when you set manually your FriendlyName
{
this->myNotificationServer()->externalCallbacks.gotFriendlyName(this, decodeURL(args[3]));
this->myDisplayName = decodeURL(args[3]);
}// TODO - Implement other PRP commands: MBE WWE
}
void NotificationServerConnection::handle_GCF(std::vector<std::string> & args)
{
int msglen;
std::string msg;
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
// we do not use it so far. throw it away
msglen = decimalFromString(args[2]);
msg = this->readBuffer.substr(0, msglen);
this->readBuffer = this->readBuffer.substr(msglen);
}
void NotificationServerConnection::handle_ADL(std::vector<std::string> & args)
{
int msglen;
std::string msg;
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
if(args[2] =="OK" && (this->_connectionState == NS_SYNCHRONISING))
{
if(adl_packets.empty())
{
// no more adl packets to send!
// now you need the change the nickname and set the status
// to complete the initial connection process
// it is not possible to use setFriendlyName
// because we cannot send soap requests at this time
std::ostringstream buf_;
if(this->myDisplayName.empty())
this->myDisplayName = myPassport;
if(server_email_verified != "0")
{
// our email is verified
buf_ << "PRP " << this->trID++ << " MFN " << encodeURL(this->myDisplayName) << "\r\n";
write(buf_);
}
else
{
// not verified, so we cant change our displayName
this->myNotificationServer()->externalCallbacks.connectionReady(this);
// the initial process ends here
this->setConnectionState(NS_CONNECTED);
}
return;
}
else
{
// send each adl packet at a time
std::string adl_payload = adl_packets.front();
adl_packets.pop_front();
std::ostringstream buf_;
buf_ << "ADL " << this->trID++ << " " << adl_payload.length() << "\r\n";
buf_ << adl_payload;
if (write(buf_) != buf_.str().size())
return;
}
}
// I reach here when ADL has payload
msglen = decimalFromString(args[2]);
msg = this->readBuffer.substr(0, msglen);
this->readBuffer = this->readBuffer.substr(msglen);
// ADL 0 70
// <ml><d n="domain.com"><c n="foo.bar" t="1" l="8" f="fName" /></d></ml>
//
XMLNode adl_data = XMLNode::parseString( msg.c_str() );
int nDomains = adl_data.nChildNode("d");
for(int i=0; i<nDomains; i++)
{
XMLNode domain = adl_data.getChildNode("d",i);
std::string domain_name = domain.getAttribute("n",0);
int nContacts = domain.nChildNode("c");
for(int j=0; j< nContacts; j++)
{
XMLNode contact = domain.getChildNode("c",j);
std::string contact_name = contact.getAttribute("n",0);
std::string fname = contact.getAttribute("f",0);
MSN::ContactList list_number = (MSN::ContactList) decimalFromString(contact.getAttribute("l",0));
// type is 1 for wlm contacts, 4 for mobile phone, 32 for yahoo? (or any kind of email contact)
int type = decimalFromString(contact.getAttribute("t",0));
if(type == 32)
return;
MSN::Passport passport(contact_name+"@"+domain_name);
this->myNotificationServer()->externalCallbacks.addedListEntry(this, list_number, passport, fname);
}
}
}
void NotificationServerConnection::handle_UBX(std::vector<std::string> & args)
{
int msglen;
personalInfo pInfo;
std::string msg,media,psm;
MSN::Passport fromPassport = args[1];
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
msglen = decimalFromString(args[3]);
msg = this->readBuffer.substr(0, msglen);
this->readBuffer = this->readBuffer.substr(msglen);
// some buggy clients send no data in UBX command
if( msg.length() < 10 ) return;
XMLNode ubx_data = XMLNode::parseString( msg.c_str() );
const char *a = ubx_data.getChildNode("PSM").getText();
if(a)
{
psm = a;
pInfo.PSM = psm;
}
const char *m = ubx_data.getChildNode("CurrentMedia").getText();
if(m)
{
media = m;
// the splitString will drop the first NULL position.
// we need it to keep the order of the following fields.
std::vector<std::string> media1 = splitString(media, "\\0");
if(media1.size()>=4) // at least 4 fields. Type, Enabled, format, data
{ // if we have some field, so ...
int i=0;
if (media.find("\\0")==0)
{ // if starts with \0 there is no
// App field. It is optional.
pInfo.mediaApp = "";
}
else
{
pInfo.mediaApp = media1[i++];
}
pInfo.mediaType = media1[i++];
pInfo.mediaIsEnabled = decimalFromString(media1[i++]);
if(pInfo.mediaIsEnabled)
{
pInfo.mediaFormat = media1[i++];
for(unsigned int b=i; b < media1.size(); b++)
{
pInfo.mediaLines.push_back(media1[i++]);
}
}
}
}
this->myNotificationServer()->externalCallbacks.buddyChangedPersonalInfo(this, fromPassport, pInfo);
}
void NotificationServerConnection::setState(BuddyStatus state, uint clientID)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
std::ostringstream buf_;
std::string xml;
if(msnobj.getMSNObjectXMLByType(3,xml))
buf_ << "CHG " << this->trID++ << " " <<
buddyStatusToString(state) << " "<< unsignedToStr(clientID) << " " << encodeURL(xml) << "\r\n";
else
buf_ << "CHG " << this->trID++ << " " << buddyStatusToString(state) << " " << unsignedToStr(clientID) << "\r\n";
write(buf_);
}
void NotificationServerConnection::setBLP(char setting)
{
if (setting != 'A' || setting != 'B')
return;
if(this->_connectionState == NS_CONNECTED)
{
std::ostringstream buf_;
this->bplSetting = setting;
buf_ << "BLP " << this->trID++ << " " << setting << "L\r\n";
write(buf_);
}
else
{
this->bplSetting = setting;
}
}
void NotificationServerConnection::setFriendlyName(std::string friendlyName, bool updateServer) throw (std::runtime_error)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
if(friendlyName.empty())
return;
if (friendlyName.size() > 387)
throw std::runtime_error("Friendly name too long!");
if(updateServer)
{
// update nickname on server
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->changeDisplayName(friendlyName);
}
else
{
this->myDisplayName = friendlyName;
std::ostringstream buf_;
buf_ << "PRP " << this->trID++ << " MFN " << encodeURL(friendlyName) << "\r\n";
write(buf_);
}
}
void NotificationServerConnection::setPersonalStatus(personalInfo pInfo)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
std::string tempMedia;
XMLNode data = XMLNode::createXMLTopNode("Data");
XMLNode PSM = XMLNode::createXMLTopNode("PSM");
XMLNode CurrentMedia = XMLNode::createXMLTopNode("CurrentMedia");
XMLNode MachineGuid = XMLNode::createXMLTopNode("MachineGuid");
PSM.addText( pInfo.PSM.c_str() );
if(pInfo.mediaIsEnabled)
{
tempMedia = pInfo.mediaApp +"\\0"+
pInfo.mediaType+"\\0"+
toStr(pInfo.mediaIsEnabled)+"\\0"+
pInfo.mediaFormat +"\\0";
std::vector<std::string>::iterator i = pInfo.mediaLines.begin();
for(;i!=pInfo.mediaLines.end();i++)
{
tempMedia+=(*i);
tempMedia+="\\0";
}
}
CurrentMedia.addText( tempMedia.c_str() );
data.addChild(PSM);
data.addChild(CurrentMedia);
char *payload1=data.createXMLString(false);
std::string payload(payload1);
free(payload1);
std::ostringstream buf_;
buf_ << "UUX " << this->trID++ << " " << payload.length() << "\r\n";
buf_ << payload;
write(buf_);
}
void NotificationServerConnection::blockContact(Passport buddyName)
{
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->removeContactFromList(buddyName,LST_AL);
soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->addContactToList(buddyName,LST_BL);
}
void NotificationServerConnection::unblockContact(Passport buddyName)
{
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->removeContactFromList(buddyName,LST_BL);
soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->addContactToList(buddyName,LST_AL);
}
void NotificationServerConnection::addToAddressBook(Passport buddyName, std::string displayName)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->addContactToAddressBook(buddyName, displayName);
}
void NotificationServerConnection::enableContactOnAddressBook(std::string contactId, std::string passport)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->enableContactOnAddressBook(contactId, passport, this->myDisplayName);
}
void NotificationServerConnection::disableContactOnAddressBook(std::string contactId, std::string passport)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->disableContactFromAddressBook(contactId,passport);
}
void NotificationServerConnection::delFromAddressBook(std::string contactId, std::string passport)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
std::vector<std::string> passport2 = splitString(passport, "@");
std::string user = passport2[0];
std::string domain = passport2[1];
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->delContactFromAddressBook(contactId,passport);
}
void NotificationServerConnection::addToList(MSN::ContactList list, Passport buddyName)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->addContactToList(buddyName,list);
}
void NotificationServerConnection::removeFromList(MSN::ContactList list, Passport buddyName)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->removeContactFromList(buddyName,list);
}
void NotificationServerConnection::addToGroup(std::string groupId, std::string contactId)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->addContactToGroup(groupId, contactId);
}
void NotificationServerConnection::removeFromGroup(std::string groupId, std::string contactId)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->delContactFromGroup(groupId, contactId);
}
void NotificationServerConnection::addGroup(std::string groupName)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->addGroup(groupName);
}
void NotificationServerConnection::removeGroup(std::string groupID)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->delGroup(groupID);
}
void NotificationServerConnection::renameGroup(std::string groupID, std::string newGroupName)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->renameGroup(groupID, newGroupName);
}
void NotificationServerConnection::synchronizeContactList(std::string lastChange)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
this->assertConnectionStateIsNot(NS_SYNCHRONISING);
// we are synchronizing through soap requests now
this->setConnectionState(NS_SYNCHRONISING);
listInfo = new ListSyncInfo(lastChange);
if(!listInfo)
return; // TODO - raise an error
if(!lastChange.length()) lastChange = "0";
listInfo->lastChange = lastChange;
Soap *soapConnection;
soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->getLists(listInfo);
}
void NotificationServerConnection::gotLists(Soap &soapConnection)
{
if(!listInfo)
return; // TODO - raise an error
Soap *soapConnection1;
soapConnection1 = new Soap(*this, this->sitesToAuthList);
// ask for Address book
soapConnection1->getAddressBook(this->listInfo);
}
void NotificationServerConnection::gotAddressBook(Soap &soapConnection)
{
// TODO - sorry, I dont have choice. I need this here because the initial setFriendlyName
// is called by handle_ADL(), which does not have access to info variable.
this->myDisplayName = listInfo->myDisplayName;
std::ostringstream buf_;
// TODO - see what is the user choice: BL or AL
// A value of 'AL' indicates that users that are neither on the client's Allow List or Buddy List will be allowed to see the client's online status and open a switchboard session with the client. A value of 'BL' indicates that these users will see the client as offline and will not be allowed to open a switchboard session.
// http://msnpiki.msnfanatic.com/index.php/Command:BLP
buf_ << "BLP " << this->trID << " " << this->bplSetting << "L\r\n";
if (write(buf_) != buf_.str().size())
return;
this->addCallback(&NotificationServerConnection::callback_initialBPL, this->trID++, (void *)NULL);
}
void NotificationServerConnection::callback_initialBPL(std::vector<std::string> & args, int trid, void *data)
{
this->assertConnectionStateIs(NS_SYNCHRONISING);
this->removeCallback(trid);
this->myNotificationServer()->externalCallbacks.gotBuddyListInfo(this, this->listInfo);
delete this->listInfo;
}
void NotificationServerConnection::completeConnection(std::map<std::string, int > & allContacts, void *info)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
// FIXME - handle the errors
std::map<std::string, std::vector<std::string> > domains;
std::string tempADL;
// contains which number is. (i.e. 1 for FL, 2 for AL, 4 for BL) or the sum
std::map<std::string, int> tempList;
std::map<std::string,int>::iterator i;
for (i = allContacts.begin(); i != allContacts.end(); i++)
{
std::vector<std::string> parts = splitString((*i).first,"@");
if(tempList[(*i).first]==0)
domains[parts[1]].push_back(parts[0]);
// it does not allow LST_AL and LST_BL, as the server will refuse with a 241 error
int privacyListMask = MSN::LST_AL | MSN::LST_BL;
if ( ((*i).second & privacyListMask) == privacyListMask )
tempList[(*i).first] = (*i).second & ~MSN::LST_AL;
else
tempList[(*i).first] = (*i).second;
}
// deleting buddy information
std::map<std::string, MSN::Buddy *>::iterator d = listInfo->contactList.begin();
for(d; d != listInfo->contactList.end(); d++)
{
delete (*d).second;
}
// adding domains and users to xml:
// The max payload of ADL command is about 7500 bytes.
// so the code below should do that.
// not using xmlParser due to the complex algorithm
// TODO - What to do when there are no contacts?
std::map<std::string, std::vector<std::string> >::iterator cur = domains.begin();
tempADL = "";
// for each domain
for(; cur != domains.end(); cur++)
{
do
{
tempADL += "<d n=\"" + (*cur).first + "\">";
// for each user
while(domains[(*cur).first].size()!=0)
{
std::string a((*cur).second[0]+"@"+(*cur).first);
tempADL += "<c n=\"" + (*cur).second[0] + "\" l=\""+toStr(tempList[a]) +"\" t=\"1\"/>";
(*cur).second.erase((*cur).second.begin());
if (tempADL.length()>7400)
break;
}
tempADL += "</d>";
if (tempADL.length()>7400)
{
adl_packets.push_back("<ml l=\"1\">" + tempADL + "</ml>" );
tempADL = "";
}
} while(domains[(*cur).first].size()!=0);
}
adl_packets.push_back("<ml l=\"1\">" + tempADL + "</ml>" );
// send the first one
std::string adl_payload = adl_packets.front();
adl_packets.pop_front();
std::ostringstream buf_;
buf_ << "ADL " << this->trID++ << " " << adl_payload.length() << "\r\n";
buf_ << adl_payload;
if (write(buf_) != buf_.str().size())
return;
}
void NotificationServerConnection::sendPing()
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
std::string a("PNG\r\n");
write(a);
}
void NotificationServerConnection::requestSwitchboardConnection(const void *tag)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
SwitchboardServerConnection::AuthData *auth = new SwitchboardServerConnection::AuthData(this->auth.username, tag);
std::ostringstream buf_;
buf_ << "XFR " << this->trID << " SB\r\n";
if (write(buf_) != buf_.str().size())
return;
this->addCallback(&NotificationServerConnection::callback_TransferToSwitchboard, this->trID++, (void *)auth);
}
template <class _Tp>
class _sameUserName
{
Buddy buddy;
public:
_sameUserName(const _Tp &__u) : buddy(__u) {};
bool operator()(const _Tp &__x) { return __x.userName == buddy.userName; }
};
void NotificationServerConnection::socketConnectionCompleted()
{
this->assertConnectionStateIs(NS_CONNECTING);
this->setConnectionState(NS_CONNECTED);
Connection::socketConnectionCompleted();
// If an error occurs in Connection::socketConnectionCompleted, we
// will be disconnected before we get here.
if (this->connectionState() != NS_DISCONNECTED)
{
this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock);
this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 1, 0, false);
}
}
void NotificationServerConnection::connect(const std::string & hostname, unsigned int port)
{
this->assertConnectionStateIs(NS_DISCONNECTED);
connectinfo *info = new connectinfo(this->auth.username, this->auth.password);
this->info = info;
if ((this->sock = this->myNotificationServer()->externalCallbacks.connectToServer(hostname, port, &this->connected)) == NULL)
{
this->myNotificationServer()->externalCallbacks.showError(this, "Could not connect to MSN server");
this->myNotificationServer()->externalCallbacks.closingConnection(this);
return;
}
this->setConnectionState(NS_CONNECTING);
this->myNotificationServer()->externalCallbacks.registerSocket(this->sock, 0, 1, false);
if (this->connected)
this->socketConnectionCompleted();
std::ostringstream buf_;
buf_ << "VER " << this->trID << " MSNP15 CVR0\r\n";
if (this->write(buf_) != buf_.str().size())
return;
this->addCallback(&NotificationServerConnection::callback_NegotiateCVR, this->trID++, (void *)info);
}
void NotificationServerConnection::connect(const std::string & hostname, unsigned int port, const Passport & username, const std::string & password)
{
this->auth.username = username;
this->auth.password = password;
this->connect(hostname, port);
}
void NotificationServerConnection::disconnect()
{
if (this->connectionState() == NS_DISCONNECTED)
return;
std::vector<SwitchboardServerConnection *> list = _switchboardConnections;
std::vector<SwitchboardServerConnection *>::iterator i = list.begin();
for (; i != list.end(); ++i)
{
delete *i;
}
std::vector<Soap *> list2 = _SoapConnections;
std::vector<Soap *>::iterator d = list2.begin();
for (; d != list2.end(); ++d)
{
delete *d;
}
this->callbacks.clear();
this->sitesToAuthList.erase(sitesToAuthList.begin(), sitesToAuthList.end());
SentQueuedOIMs.erase(SentQueuedOIMs.begin(), SentQueuedOIMs.end());
this->setConnectionState(NS_DISCONNECTED);
this->myNotificationServer()->externalCallbacks.closingConnection(this);
Connection::disconnect();
}
void NotificationServerConnection::disconnectForTransfer()
{
this->assertConnectionStateIsNot(NS_DISCONNECTED);
this->myNotificationServer()->externalCallbacks.unregisterSocket(this->sock);
this->myNotificationServer()->externalCallbacks.closeSocket(this->sock);
this->setConnectionState(NS_DISCONNECTED);
}
void NotificationServerConnection::handleIncomingData()
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
while (this->isWholeLineAvailable())
{
std::vector<std::string> args = this->getLine();
if(!args.size()) continue;
if (args[0] == "MSG" || args[0] == "NOT" ||
args[0] == "IPG" || args[0] == "GCF" ||
args[0] == "UBX" || args[0] == "ADL" ||
args[0] == "RML")
{
int dataLength;
if (args[0] == "MSG" || args[0] == "UBX")
dataLength = decimalFromString(args[3]);
else if(args[0] == "GCF" || args[0] == "ADL" || args[0] == "RML")
dataLength = decimalFromString(args[2]);
else
dataLength = decimalFromString(args[1]);
if (this->readBuffer.find("\r\n") + 2 + dataLength > this->readBuffer.size())
return;
}
this->readBuffer = this->readBuffer.substr(this->readBuffer.find("\r\n") + 2);
int trid = 0;
if (args.size() >= 6 && args[0] == "XFR" && args[2] == "NS")
{
// XFR TrID NS NotificationServerIP:Port 0 ThisServerIP:Port
// 0 1 2 3 4 5
this->callbacks.clear(); // delete the callback data
this->disconnectForTransfer();
std::pair<std::string, int> server_address = splitServerAddress(args[3]);
this->connect(server_address.first, server_address.second);
return;
}
if (args.size() >= 7 && args[0] == "RNG")
{
// RNG SessionID SwitchboardServerIP:Port CKI AuthString InvitingUser InvitingDisplayName
// 0 1 2 3 4 5 6
this->handle_RNG(args);
return;
}
if (args.size() >= 2 && args[0] == "QNG")
{
// QNG seconds
// 0 1
// ping response, ignore
return;
}
if ((args.size() >= 3 && args[0] == "LST" ) ||
(args.size() >= 2 && (args[0] == "GTC" )) ||
(args.size() >= 3 && (args[0] == "BPR" || args[0] == "LSG" ))
)
{
// LST N=UserName F=FriendlyName C=GUID param groupID
// 0 1 2 3 4 5
//
// or
// (GTC|BLP) [TrID] [ListVersion] Setting
// 0 1 2 4
if (this->synctrid)
{
trid = this->synctrid;
}
else
{
trid = decimalFromString(args[1]);
}
}
else if (args.size() > 1)
{
try
{
trid = decimalFromString(args[1]);
}
catch (...)
{
}
}
if (!this->callbacks.empty() && trid >= 0)
{
if (this->callbacks.find(trid) != this->callbacks.end())
{
(this->*(this->callbacks[trid].first))(args, trid, this->callbacks[trid].second);
continue;
}
}
if (isdigit(args[0][0]))
this->showError(decimalFromString(args[0]));
else
this->dispatchCommand(args);
}
}
void NotificationServerConnection::callback_NegotiateCVR(std::vector<std::string> & args, int trid, void *data)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
connectinfo * info = (connectinfo *) data;
this->removeCallback(trid);
if (args.size() >= 3 && args[0] != "VER" || args[2] != "MSNP15") // if either *differs*...
{
this->myNotificationServer()->externalCallbacks.showError(NULL, "Protocol negotiation failed");
this->disconnect();
return;
}
std::ostringstream buf_;
buf_ << "CVR " << this->trID << " 0x0409 winnt 5.1 i386 MSG80BETA 8.1.0178.00 MSMSGS " << info->username << "\r\n";
if (this->write(buf_) != buf_.str().size())
return;
this->addCallback(&NotificationServerConnection::callback_RequestUSR, this->trID++, (void *) data);
}
void NotificationServerConnection::callback_TransferToSwitchboard(std::vector<std::string> & args, int trid, void *data)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
SwitchboardServerConnection::AuthData *auth = static_cast<SwitchboardServerConnection::AuthData *>(data);
this->removeCallback(trid);
if (args[0] != "XFR")
{
this->showError(decimalFromString(args[0]));
this->disconnect();
delete auth;
return;
}
auth->cookie = args[5];
auth->sessionID = "";
SwitchboardServerConnection *newconn = new SwitchboardServerConnection(*auth, *this);
this->addSwitchboardConnection(newconn);
std::pair<std::string, int> server_address = splitServerAddress(args[3]);
newconn->connect(server_address.first, server_address.second);
delete auth;
}
void NotificationServerConnection::callback_RequestUSR(std::vector<std::string> & args, int trid, void *data)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
connectinfo *info = (connectinfo *)data;
this->removeCallback(trid);
if (args.size() > 1 && args[0] != "CVR") // if*differs*...
{
this->myNotificationServer()->externalCallbacks.showError(NULL, "Protocol negotiation failed");
this->disconnect();
return;
}
std::ostringstream buf_;
buf_ << "USR " << this->trID << " SSO I " << info->username << "\r\n";
if (this->write(buf_) != buf_.str().size())
return;
this->addCallback(&NotificationServerConnection::callback_PassportAuthentication, this->trID++, (void *) data);
}
void NotificationServerConnection::callback_PassportAuthentication(std::vector<std::string> & args, int trid, void * data)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
connectinfo * info;
info=(connectinfo *)data;
this->removeCallback(trid);
if (isdigit(args[0][0]))
{
this->showError(decimalFromString(args[0]));
this->disconnect();
return;
}
if (args.size() >= 4 && args[4].empty()) {
this->disconnect();
return;
}
this->myNotificationServer()->externalCallbacks.getSecureHTTPProxy();
Soap *soapConnection = new Soap(*this);
this->mdi = args[5];
soapConnection->setMBI(args[4]);
soapConnection->getTickets(info->username,info->password,args[4]);
delete info;
info=NULL;
}
void NotificationServerConnection::gotTickets(Soap & soapConnection, std::vector<MSN::Soap::sitesToAuth> sitesToAuthList)
{
std::ostringstream buf_;
this->sitesToAuthList = sitesToAuthList;
std::string token = sitesToAuthList[1].BinarySecurityToken;
std::string binarysecret = sitesToAuthList[1].BinarySecret;
this->token = token;
buf_ << "USR " << this->trID << " SSO S " << token << " " << mdi_encrypt(binarysecret, mdi) << "\r\n";
if (this->write(buf_) != buf_.str().size())
return;
this->addCallback(&NotificationServerConnection::callback_AuthenticationComplete, this->trID++, NULL);
}
void NotificationServerConnection::callback_AuthenticationComplete(std::vector<std::string> & args, int trid, void * data)
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
this->removeCallback(trid);
if (isdigit(args[0][0]))
{
this->showError(decimalFromString(args[0]));
this->disconnect();
return;
}
server_email_verified = args[4];
}
void NotificationServerConnection::get_oim(std::string id, bool markAsRead)
{
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->getOIM(id,markAsRead);
}
void NotificationServerConnection::delete_oim(std::string id)
{
if(this->removingOIM)
{
DeletedQueuedOIMs.push_back(id);
return;
}
this->removingOIM = true;
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->deleteOIM(id);
}
void NotificationServerConnection::send_oim(Soap::OIM oim)
{
// do not generate two lockkeys at the same time
if(this->generatingLockkey)
{
SentQueuedOIMs.push_back(oim);
return;
}
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
SentQueuedOIMs.push_back(oim);
this->generatingLockkey=true;
soapConnection->generateLockkey(oim);
}
void NotificationServerConnection::gotOIM(Soap & soapConnection, bool success, std::string id, std::string message)
{
this->myNotificationServer()->externalCallbacks.gotOIM(this, success, id, message);
}
void NotificationServerConnection::gotOIMLockkey(Soap & soapConnection, std::string lockkey)
{
this->lockkey = lockkey;
this->generatingLockkey = false;
if(this->lockkey.empty())
{
std::vector<Soap::OIM>::iterator i = SentQueuedOIMs.begin();
for(; i != SentQueuedOIMs.end(); i++)
{
this->myNotificationServer()->externalCallbacks.gotOIMSendConfirmation(this, false, (*i).id);
}
SentQueuedOIMs.erase(SentQueuedOIMs.begin(), SentQueuedOIMs.end());
return;
}
sendQueuedOIMs();
}
void NotificationServerConnection::gotOIMDeleteConfirmation(Soap & soapConnection, std::string id, bool deleted)
{
this->myNotificationServer()->externalCallbacks.gotOIMDeleteConfirmation(this, deleted, id);
if(this->DeletedQueuedOIMs.empty())
{
removingOIM = false;
return;
}
else
{
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->deleteOIM(DeletedQueuedOIMs.back());
DeletedQueuedOIMs.pop_back();
}
}
void NotificationServerConnection::gotOIMSendConfirmation(Soap & soapConnection, int id, bool sent)
{
if(!sent)
this->lockkey.clear();
this->myNotificationServer()->externalCallbacks.gotOIMSendConfirmation(this, sent, id);
}
void NotificationServerConnection::sendQueuedOIMs()
{
std::vector<Soap::OIM>::iterator i = SentQueuedOIMs.begin();
for(; i != SentQueuedOIMs.end(); i++)
{
Soap *soapConnection = new Soap(*this, this->sitesToAuthList);
soapConnection->sendOIM((*i), this->lockkey);
}
SentQueuedOIMs.erase(SentQueuedOIMs.begin(), SentQueuedOIMs.end());
}
bool NotificationServerConnection::change_DisplayPicture(std::string filename)
{
msnobj.delMSNObjectByType(3);
if(!filename.empty())
msnobj.addMSNObject(filename,3);
return true;
}
void NotificationServerConnection::gotChangeDisplayNameConfirmation(Soap & soapConnection, std::string displayName, bool changed)
{
if(changed)
{
this->myDisplayName = displayName;
// server update OK, now change to the current session
std::ostringstream buf_;
buf_ << "PRP " << this->trID++ << " MFN " << encodeURL(displayName) << "\r\n";
write(buf_);
}
// TODO - raise an error if not changed
}
void NotificationServerConnection::gotAddContactToGroupConfirmation(Soap & soapConnection, bool added, std::string newVersion, std::string groupId, std::string contactId)
{
this->myNotificationServer()->externalCallbacks.addedContactToGroup(this, added, groupId, contactId);
}
void NotificationServerConnection::gotDelContactFromGroupConfirmation(Soap & soapConnection, bool removed, std::string newVersion, std::string groupId, std::string contactId)
{
this->myNotificationServer()->externalCallbacks.removedContactFromGroup(this, removed, groupId, contactId);
}
void NotificationServerConnection::gotAddGroupConfirmation(Soap & soapConnection, bool added, std::string newVersion, std::string groupName, std::string groupId)
{
this->myNotificationServer()->externalCallbacks.addedGroup(this, added, groupName, groupId);
}
void NotificationServerConnection::gotDelGroupConfirmation(Soap & soapConnection, bool removed, std::string newVersion, std::string groupId)
{
this->myNotificationServer()->externalCallbacks.removedGroup(this, removed, groupId);
}
void NotificationServerConnection::gotRenameGroupConfirmation(Soap & soapConnection, bool renamed, std::string newVersion, std::string newGroupName, std::string groupId)
{
this->myNotificationServer()->externalCallbacks.renamedGroup(this, renamed, newGroupName, groupId);
}
void NotificationServerConnection::gotAddContactToAddressBookConfirmation(Soap & soapConnection, bool added, std::string newVersion, std::string passport, std::string displayName, std::string guid)
{
this->myNotificationServer()->externalCallbacks.addedContactToAddressBook(this, added, passport, displayName, guid);
if(added)
{
std::vector<std::string> passport2 = splitString(passport, "@");
std::string user = passport2[0];
std::string domain = passport2[1];
// TODO - use xmlParser
std::string payload3("<ml><d n=\"" + domain + "\"><c n=\"" + user+ "\" l=\"2\" t=\"1\"/></d></ml>");
std::ostringstream buf_3;
buf_3 << "ADL " << this->trID++ << " " << payload3.length() << "\r\n";
buf_3 << payload3;
write(buf_3);
std::string payload2("<ml><d n=\"" + domain + "\"><c n=\"" + user+ "\" l=\"1\" t=\"1\"/></d></ml>");
std::ostringstream buf_2;
buf_2 << "ADL " << this->trID++ << " " << payload2.length() << "\r\n";
buf_2 << payload2;
write(buf_2);
// the official client sends FQY
std::string payload4("<ml l=\"2\"><d n=\"" + domain + "\"><c n=\"" + user+ "\"/></d></ml>");
std::ostringstream buf_4;
buf_4 << "FQY " << this->trID++ << " " << payload4.length() << "\r\n";
buf_4 << payload4;
write(buf_4);
}
}
void NotificationServerConnection::gotDelContactFromAddressBookConfirmation(Soap & soapConnection, bool removed, std::string newVersion, std::string contactId, std::string passport)
{
this->myNotificationServer()->externalCallbacks.removedContactFromAddressBook(this, removed, contactId, passport);
if(removed)
{
std::vector<std::string> passport2 = splitString(passport, "@");
std::string user = passport2[0];
std::string domain = passport2[1];
// TODO - use xmlParser
std::string payload2("<ml><d n=\"" + domain + "\"><c n=\"" + user+ "\" l=\"1\" t=\"1\"/></d></ml>");
std::ostringstream buf_2;
buf_2 << "RML " << this->trID++ << " " << payload2.length() << "\r\n";
buf_2 << payload2;
write(buf_2);
}
}
void NotificationServerConnection::gotEnableContactOnAddressBookConfirmation(Soap & soapConnection, bool disabled, std::string newVersion, std::string contactId, std::string passport)
{
this->myNotificationServer()->externalCallbacks.enabledContactOnAddressBook(this, disabled, contactId, passport);
if(disabled)
{
std::vector<std::string> passport2 = splitString(passport, "@");
std::string user = passport2[0];
std::string domain = passport2[1];
// TODO - use xmlParser
std::string payload3("<ml><d n=\"" + domain + "\"><c n=\"" + user+ "\" l=\"1\" t=\"1\"/></d></ml>");
std::ostringstream buf_3;
buf_3 << "ADL " << this->trID++ << " " << payload3.length() << "\r\n";
buf_3 << payload3;
write(buf_3);
}
}
void NotificationServerConnection::gotDisableContactOnAddressBookConfirmation(Soap & soapConnection, bool disabled, std::string newVersion, std::string contactId, std::string passport)
{
this->myNotificationServer()->externalCallbacks.disabledContactOnAddressBook(this, disabled, contactId);
if(disabled)
{
std::vector<std::string> passport2 = splitString(passport, "@");
std::string user = passport2[0];
std::string domain = passport2[1];
// TODO - use xmlParser
std::string payload2("<ml><d n=\"" + domain + "\"><c n=\"" + user+ "\" l=\"1\" t=\"1\"/></d></ml>");
std::ostringstream buf_2;
buf_2 << "RML " << this->trID++ << " " << payload2.length() << "\r\n";
buf_2 << payload2;
write(buf_2);
}
}
void NotificationServerConnection::gotAddContactToListConfirmation(Soap & soapConnection, bool added, std::string newVersion, std::string passport, MSN::ContactList list)
{
if(added)
{
std::vector<std::string> passport2 = splitString(passport, "@");
std::string user = passport2[0];
std::string domain = passport2[1];
// TODO - use xmlParser
std::string payload2("<ml><d n=\"" + domain + "\"><c n=\"" + user+ "\" l=\""+toStr(list)+"\" t=\"1\"/></d></ml>");
std::ostringstream buf_2;
buf_2 << "ADL " << this->trID++ << " " << payload2.length() << "\r\n";
buf_2 << payload2;
write(buf_2);
this->myNotificationServer()->externalCallbacks.addedListEntry(this, list, passport, "");
}
}
void NotificationServerConnection::gotDelContactFromListConfirmation(Soap & soapConnection, bool deleted, std::string newVersion, std::string passport, MSN::ContactList list)
{
if(deleted)
{
std::vector<std::string> passport2 = splitString(passport, "@");
std::string user = passport2[0];
std::string domain = passport2[1];
// TODO - use XMLParser
std::string payload("<ml><d n=\""+ domain + "\"><c n=\"" + user + "\" l=\""+toStr(list)+"\" t=\"1\"/></d></ml>");
std::ostringstream buf_;
buf_ << "RML " << this->trID++ << " " << payload.length() << "\r\n";
buf_ << payload;
write(buf_);
this->myNotificationServer()->externalCallbacks.removedListEntry(this, list, passport);
}
}
void NotificationServerConnection::setCapabilities(uint m_clientId)
{
this->m_clientId = m_clientId;
}
void NotificationServerConnection::getInboxUrl()
{
this->assertConnectionStateIsAtLeast(NS_CONNECTED);
std::ostringstream buf_;
buf_ << "URL " << this->trID << " INBOX\r\n";
write(buf_);
this->addCallback(&NotificationServerConnection::callback_URL, this->trID++, NULL);
}
}