/*
 * msntest.cpp
 * libmsn
 *
 * Created by Meredydd Luff.
 * Refactored by Tiago Salem Herrmann
 * Copyright (c) 2004 Meredydd Luff. 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 <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/ssl.h>

#include <msn/msn.h>
#include <string>
#include <iostream>

class Callbacks : public MSN::Callbacks
{
    
    virtual void registerSocket(void *s, int read, int write, bool isSSL);

    virtual void unregisterSocket(void *s);
    
    virtual void closeSocket(void *s);
    
    virtual void showError(MSN::Connection * conn, std::string msg);
    
    virtual void buddyChangedStatus(MSN::NotificationServerConnection * conn, MSN::Passport buddy, std::string friendlyname, MSN::BuddyStatus state, unsigned int clientID, std::string msnobject);

    virtual void buddyOffline(MSN::NotificationServerConnection * conn, MSN::Passport buddy);
    
    virtual void log(int writing, const char* buf);
   
    virtual void buddyChangedPersonalInfo(MSN::NotificationServerConnection * conn, MSN::Passport fromPassport, MSN::personalInfo);

    virtual void gotFriendlyName(MSN::NotificationServerConnection * conn, std::string friendlyname);

    virtual void gotBuddyListInfo(MSN::NotificationServerConnection * conn, MSN::ListSyncInfo * data);

    virtual void gotLatestListSerial(MSN::NotificationServerConnection * conn, std::string lastChange);

    virtual void gotGTC(MSN::NotificationServerConnection * conn, char c);

    virtual void gotBLP(MSN::NotificationServerConnection * conn, char c);
    
    virtual void addedListEntry(MSN::NotificationServerConnection * conn, MSN::ContactList list, MSN::Passport buddy, std::string friendlyname);
    
    virtual void removedListEntry(MSN::NotificationServerConnection * conn, MSN::ContactList list, MSN::Passport buddy);
    
    virtual void addedGroup(MSN::NotificationServerConnection * conn, bool added, std::string groupName, std::string groupID);

    virtual void removedGroup(MSN::NotificationServerConnection * conn, bool removed, std::string groupID);
    
    virtual void renamedGroup(MSN::NotificationServerConnection * conn, bool renamed, std::string newGroupName, std::string groupID);

    virtual void addedContactToGroup(MSN::NotificationServerConnection * conn, bool added, std::string groupId, std::string contactId);
    
    virtual void removedContactFromGroup(MSN::NotificationServerConnection * conn, bool removed, std::string groupId, std::string contactId);

    virtual void addedContactToAddressBook(MSN::NotificationServerConnection * conn, bool added, std::string passport, std::string displayName, std::string guid);

    virtual void removedContactFromAddressBook(MSN::NotificationServerConnection * conn, bool removed, std::string contactId, std::string passport);

    virtual void enabledContactOnAddressBook(MSN::NotificationServerConnection * conn, bool enabled, std::string contactId, std::string passport);
    virtual void disabledContactOnAddressBook(MSN::NotificationServerConnection * conn, bool disabled, std::string contactId);

    virtual void gotSwitchboard(MSN::SwitchboardServerConnection * conn, const void * tag);
    
    virtual void buddyJoinedConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname, int is_initial);
    
    virtual void buddyLeftConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy);
    
    virtual void gotInstantMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname, MSN::Message * msg);

    virtual void gotMessageSentACK(MSN::SwitchboardServerConnection * conn, int trID);

    virtual void gotEmoticonNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string alias, std::string msnobject);
    
    virtual void failedSendingMessage(MSN::Connection * conn);
    
    virtual void buddyTyping(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname);

    virtual void gotNudge(MSN::SwitchboardServerConnection * conn, MSN::Passport from);

    virtual void gotVoiceClipNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport from, std::string msnobject);
    
    virtual void gotWinkNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport from, std::string msnobject);

    virtual void gotInk(MSN::SwitchboardServerConnection * conn, MSN::Passport from, std::string image);
    
    virtual void gotVoiceClipFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string file);
    
    virtual void gotEmoticonFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string alias, std::string file);

    virtual void gotWinkFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string file);

    virtual void gotActionMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string message);
    
    virtual void gotInitialEmailNotification(MSN::NotificationServerConnection * conn, int msgs_inbox, int unread_inbox, int msgs_folders, int unread_folders);
    
    virtual void gotNewEmailNotification(MSN::NotificationServerConnection * conn, std::string from, std::string subject);
    
    virtual void fileTransferProgress(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, long long unsigned transferred, long long unsigned total);
    
    virtual void fileTransferFailed(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, MSN::fileTransferError error);
    
    virtual void fileTransferSucceeded(MSN::SwitchboardServerConnection * conn, unsigned int sessionID);

    virtual void fileTransferInviteResponse(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, bool response);
    
    virtual void gotNewConnection(MSN::Connection * conn);
    
    virtual void gotOIMList(MSN::NotificationServerConnection * conn, std::vector<MSN::eachOIM> OIMs);

    virtual void gotOIM(MSN::NotificationServerConnection * conn, bool success, std::string id, std::string message);
   
    virtual void gotOIMSendConfirmation(MSN::NotificationServerConnection * conn, bool success, int id);

    virtual void gotOIMDeleteConfirmation(MSN::NotificationServerConnection * conn, bool success, std::string id);

    virtual void gotContactDisplayPicture(MSN::SwitchboardServerConnection * conn, MSN::Passport passport, std::string filename );

    virtual void closingConnection(MSN::Connection * conn);
    
    virtual void changedStatus(MSN::NotificationServerConnection * conn, MSN::BuddyStatus state);
    
    virtual void * connectToServer(std::string server, int port, bool *connected, bool isSSL);
    
    virtual void connectionReady(MSN::Connection * conn);

    virtual void askFileTransfer(MSN::SwitchboardServerConnection *conn, MSN::fileTransferInvite ft);

    virtual int listenOnPort(int port);
    
    virtual std::string getOurIP();

    virtual int getSocketFileDescriptor (void *sock);

    virtual size_t getDataFromSocket (void *sock, char *data, size_t size);

    virtual size_t writeDataToSocket (void *sock, char *data, size_t size);

    virtual std::string getSecureHTTPProxy();
    virtual void gotInboxUrl (MSN::NotificationServerConnection *conn, MSN::hotmailInfo info);
};

struct pollfd mySockets[21];
struct ssl {
    bool isSSL;
    bool isConnected;
    SSL *ssl;
    SSL_CTX *ctx;
} mySocketsSsl[21];

void handle_command(MSN::NotificationServerConnection &);
int countsocks(void);
std::string myFriendlyName;
std::string myUsername;
//std::string lastObject;

// should be random
unsigned int sessionID = 123456;

int main()
{
    for (int i = 1; i < 20; i++)
    {
        mySockets[i].fd = -1;
        mySockets[i].events = POLLIN;
        mySockets[i].revents = 0;
        mySocketsSsl[i].isSSL = false;
        mySocketsSsl[i].isConnected = false;
        mySocketsSsl[i].ctx = NULL;
        mySocketsSsl[i].ssl = NULL;
    }

    mySockets[0].fd = 0;
    mySockets[0].events = POLLIN;
    mySockets[0].revents = 0;
    
    Callbacks cb;
    MSN::Passport uname;
    char *pass = NULL;
    while (1)
    {
        fprintf(stderr, "Enter your login name: ");
        fflush(stdout);
        try
        {
            std::cin >> uname;
            myUsername = uname;
            break;
        }
        catch (MSN::InvalidPassport & e)
        {
            std::cout << e.what() << std::endl;
        }
    }
    
    pass = getpass("Enter your password: ");
    fprintf(stderr, "Connecting to the MSN Messenger service...\n");
    
    MSN::NotificationServerConnection mainConnection(uname, pass, cb);
    mainConnection.connect("messenger.hotmail.com", 1863);
    fprintf(stderr, "> ");
    fflush(stderr);
    while (1)
    {
        poll(mySockets, 20, -1);
        for (int i = 1; i < 20; i++)
        {
            if (mySockets[i].fd == -1)
                break; 
            if (mySockets[i].revents & POLLHUP) 
            {
                mySockets[i].revents = 0; 
                continue; 
            }
            if (mySockets[i].revents & (POLLIN | POLLOUT | POLLPRI))
            {
                MSN::Connection *c;
                
                // Retrieve the connection associated with the
                // socket's file handle on which the event has
                // occurred.
                c = mainConnection.connectionWithSocket((void*)mySockets[i].fd);
                
                // if this is a libmsn socket
                if (c != NULL)
                {
                    // If we aren't connected yet, a socket event means that
                    // our connection attempt has completed.
                    if(mySocketsSsl[i].isSSL && !mySocketsSsl[i].isConnected)
                    {
                        BIO *bio_socket_new;

#if OPENSSL_VERSION_NUMBER >= 0x10000000L
			const SSL_METHOD *meth=NULL;
#else
			SSL_METHOD *meth=NULL;
#endif

                        meth=SSLv23_client_method();
                        SSLeay_add_ssl_algorithms();
                        mySocketsSsl[i].ctx = SSL_CTX_new(meth);
                        mySocketsSsl[i].ssl = SSL_new(mySocketsSsl[i].ctx);
                        bio_socket_new = BIO_new_socket(mySockets[i].fd, BIO_CLOSE);
                        if(!mySocketsSsl[i].ssl)
                            break;
                        BIO_set_nbio(bio_socket_new, 0);
                        SSL_set_bio(mySocketsSsl[i].ssl, bio_socket_new, bio_socket_new);
                        SSL_set_mode(mySocketsSsl[i].ssl, SSL_MODE_AUTO_RETRY);

                        // TODO - fix-me - not async and buggy
                        int ret = SSL_connect(mySocketsSsl[i].ssl);
                        mySocketsSsl[i].isConnected = true;
                    }
                    if (c->isConnected() == false)
                        c->socketConnectionCompleted();

                    // If this event is due to new data becoming available 
                    if (mySockets[i].revents & POLLIN)
                    {
                        if(mySocketsSsl[i].isSSL && mySocketsSsl[i].isConnected)
                        {
                             if(SSL_want_read(mySocketsSsl[i].ssl))
                             {
                                 mySockets[i].revents = 0;
                                 continue;
                             }
                        }
                        c->dataArrivedOnSocket();
                    }
                   
                    // If this event is due to the socket becoming writable
                    if (mySockets[i].revents & POLLOUT)
                    {
                        c->socketIsWritable();
                    }
                }
            }

            if (mySockets[i].revents & (POLLERR | POLLNVAL))
            {
                printf("Dud socket (%d)! Code %x (ERR=%x, INVAL=%x)\n", mySockets[i].fd, mySockets[i].revents, POLLERR, POLLNVAL);
                
                MSN::Connection *c;
                
                // Retrieve the connection associated with the
                // socket's file handle on which the event has
                // occurred.
                c = mainConnection.connectionWithSocket((void*)mySockets[i].fd);
 
                // if this is a libmsn socket
                if (c != NULL)
                {
                    // Delete the connection.  This will cause the resources
                    // that are being used to be freed.
                    delete c;
                }
                
                mySockets[i].fd = -1;
                mySockets[i].revents = 0;
                continue;
            }
        }

        if (mySockets[0].revents & POLLIN)
        {
            handle_command(mainConnection);
            mySockets[0].revents = 0;
        }
    }
}

void handle_command(MSN::NotificationServerConnection & mainConnection)
{
    char command[40];
    
    if (scanf(" %s", command) == EOF)
    {
        printf("\n");
        exit(0);
    }
    
    if (!strcmp(command, "quit"))
    {
        exit(0);
    } else if (!strcmp(command, "sendoim")) {
        char rcpt[80];
        char msg[1024];
        
        scanf(" %s", rcpt);
        
        fgets(msg, 1024, stdin);
        
        msg[strlen(msg)-1] = '\0';
        
        const std::string rcpt_ = rcpt;
        const std::string msg_ = msg;

        MSN::Soap::OIM oim;
        oim.myFname = myFriendlyName;
        oim.toUsername = rcpt;
        oim.message = msg;
        oim.myUsername = myUsername; 
        oim.id = 1; 

        mainConnection.send_oim(oim);
    } else if (!strcmp(command, "addtogroup")) {
        char gid[80];
        char uid[80];
        
        scanf(" %s", gid);
        
        fgets(uid, 80, stdin);
        
        uid[strlen(uid)-1] = '\0';
        
        const std::string uid_ = uid;
        const std::string gid_ = gid;

        mainConnection.addToGroup(gid,uid);
    } else if (!strcmp(command, "delfromgroup")) {
        char gid[80];
        char uid[80];
        
        scanf(" %s", gid);
        
        fgets(uid, 80, stdin);
        
        uid[strlen(uid)-1] = '\0';
        
        const std::string uid_ = uid;
        const std::string gid_ = gid;

        mainConnection.removeFromGroup(gid,uid);
    } else if (!strcmp(command, "renamegroup")) {
        char gid[80];
        char name[80];
        
        scanf(" %s", gid);
        
        fgets(name, 80, stdin);
        
        name[strlen(name)-1] = '\0';
        
        const std::string name_ = name;
        const std::string gid_ = gid;

        mainConnection.renameGroup(gid,name);
    } else if (!strcmp(command, "getoim")) {
        char id[80];
        
        scanf(" %s", id);
        
        const std::string _id = id;
        mainConnection.get_oim(id,false);
    } else if (!strcmp(command, "addgroup")) {
        char gname[80];
        
        scanf(" %s", gname);
        
        const std::string _gname = gname;
        mainConnection.addGroup(_gname);
    } else if (!strcmp(command, "delgroup")) {
        char gname[80];
        
        scanf(" %s", gname);
        
        const std::string _gname = gname;
        mainConnection.removeGroup(_gname);
    } else if (!strcmp(command, "deloim")) {
        char id[80];
        
        scanf(" %s", id);
        
        const std::string _id = id;
        mainConnection.delete_oim(id);

    } else if (!strcmp(command, "msg")) {
        char rcpt[80];
        char msg[1024];
        
        scanf(" %s", rcpt);
        
        fgets(msg, 1024, stdin);
        
        msg[strlen(msg)-1] = '\0';
        
        const std::string rcpt_ = rcpt;
        const std::string msg_ = msg;
        const std::pair<std::string, std::string> *ctx = new std::pair<std::string, std::string>(rcpt_, msg_);
        mainConnection.requestSwitchboardConnection(ctx);
    } else if (!strcmp(command, "status")) {
        char state[10];
        
        scanf(" %s", state);
        mainConnection.change_DisplayPicture("/tmp/global-photo.png");
        uint clientid=0;

        clientid += MSN::MSNC7;
        clientid += MSN::MSNC6;
        clientid += MSN::MSNC5;
        clientid += MSN::MSNC4;
        clientid += MSN::MSNC3;
        clientid += MSN::MSNC2;
        clientid += MSN::MSNC1;
        clientid += MSN::SupportWinks;
        clientid += MSN::VoiceClips;
        clientid += MSN::InkGifSupport;
        clientid += MSN::SIPInvitations;
        clientid += MSN::SupportMultiPacketMessaging;

        mainConnection.setState(MSN::buddyStatusFromString(state), clientid);
    } else if (!strcmp(command, "friendlyname")) {
        char fn[256];
        
        fgets(fn, 256, stdin);

        fn[strlen(fn)-1] = '\0';
        
        mainConnection.setFriendlyName(fn);
    } else if (!strcmp(command, "addtolist")) {
        MSN::ContactList list;
        char user[128];
        
        scanf(" %d %s", (int*)&list, user);
        
        mainConnection.addToList(list, user);
    } else if (!strcmp(command, "add")) {
        char user[128];
        char nick[128];
        
        scanf(" %s %s", user, nick);
        
        mainConnection.addToAddressBook(user, nick);
    } else if (!strcmp(command, "block")) {
        char user[128];
        
        scanf(" %s", user);
        
        mainConnection.blockContact(user);
    } else if (!strcmp(command, "unblock")) {
        char user[128];
        
        scanf(" %s", user);
        
        mainConnection.unblockContact(user);
    } else if (!strcmp(command, "delfromlist")) {
        MSN::ContactList list;
        char user[128];
        
        scanf(" %d %s", (int*)&list, user);
        
        mainConnection.removeFromList(list, user);
    } else if (!strcmp(command, "del")) {
        char contactid[128];
        char passport[128];
        
        scanf(" %s %s", contactid, passport);
        
        mainConnection.delFromAddressBook(contactid, passport);
    } else if (!strcmp(command, "enable")) {
        char contactid[128];
        char passport[128];
        
        scanf(" %s %s", contactid, passport);
        
        mainConnection.enableContactOnAddressBook(contactid,passport);
    } else if (!strcmp(command, "disable")) {
        char contactid[128];
        char passport[128];
        
        scanf(" %s %s", contactid, passport);
        
        mainConnection.disableContactOnAddressBook(contactid,passport);
    } else if (!strcmp(command, "reconnect")) {
        if (mainConnection.connectionState() != MSN::NotificationServerConnection::NS_DISCONNECTED)
            mainConnection.disconnect();

        mainConnection.connect("messenger.hotmail.com", 1863);
    } else if (!strcmp(command, "disconnect")) {
        mainConnection.disconnectNS();
    } else if (!strcmp(command, "inboxurl")) {
        mainConnection.getInboxUrl();
    } else {
        fprintf(stderr, "\nBad command \"%s\"", command);
    }
    
    fprintf(stderr, "\n> ");
    fflush(stderr);
}

int countsocks(void)
{
    int retval = 0;
    
    for (int a = 0; a < 20; a++)
    {
        if (mySockets[a].fd == -1) 
            break; 
        
        retval++;
    }
    return retval;
}

void Callbacks::registerSocket(void *s, int reading, int writing, bool isSSL)
{
    for (int a = 1; a < 20; a++)
    {
        if (mySockets[a].fd == -1 || mySockets[a].fd == getSocketFileDescriptor(s))
        {
            if(mySockets[a].fd == -1)
                mySockets[a].events = 0;
            if (reading)
                mySockets[a].events |= POLLIN;
 
            if (writing)
                mySockets[a].events |= POLLOUT;

            mySockets[a].fd = getSocketFileDescriptor(s);
            if(isSSL)
                mySocketsSsl[a].isSSL = true;
            return;
        }
    }
}

int
Callbacks::getSocketFileDescriptor (void *sock)
{
    long a = (long)sock;
    return (int)a;
}

void Callbacks::closeSocket(void *s)
{
    for (int a = 1; a < 20; a++)
    {
        if (mySockets[a].fd == getSocketFileDescriptor(s))
        {
            close(getSocketFileDescriptor(s));
            if(mySocketsSsl[a].isSSL)
            {
                if(mySocketsSsl[a].ssl)
                {
                    if(mySocketsSsl[a].ssl) SSL_set_shutdown(mySocketsSsl[a].ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
                    if(mySocketsSsl[a].ssl) SSL_free(mySocketsSsl[a].ssl);
                    if(mySocketsSsl[a].ctx) SSL_CTX_free(mySocketsSsl[a].ctx);
                    mySocketsSsl[a].ssl=NULL;
                    mySocketsSsl[a].ctx=NULL;
                }
            }
            for (int b = a; b < 19; b++)
            {
                mySockets[b].fd = mySockets[b + 1].fd;
                mySockets[b].revents = mySockets[b + 1].revents;
                mySockets[b].events = mySockets[b + 1].events;
                mySocketsSsl[b].isSSL = mySocketsSsl[b + 1].isSSL;
                mySocketsSsl[b].isConnected = mySocketsSsl[b + 1].isConnected;
                mySocketsSsl[b].ssl = mySocketsSsl[b + 1].ssl;
                mySocketsSsl[b].ctx = mySocketsSsl[b + 1].ctx;
            }
            mySockets[19].fd = -1;
            mySocketsSsl[19].isConnected = false;
            mySocketsSsl[19].isSSL = false;
            mySocketsSsl[19].ssl = NULL;
            mySocketsSsl[19].ctx = NULL;
        }
    }
}

void Callbacks::unregisterSocket(void *s)
{
    for (int a = 1; a < 20; a++)
    {
        if (mySockets[a].fd == getSocketFileDescriptor(s))
        {
            mySockets[a].events = 0;
        }
    }
}

void Callbacks::gotFriendlyName(MSN::NotificationServerConnection * conn, std::string friendlyname)
{
    myFriendlyName = friendlyname.c_str();
    printf("Your friendlyname is now: %s\n", friendlyname.c_str());
}

void Callbacks::gotBuddyListInfo(MSN::NotificationServerConnection * conn, MSN::ListSyncInfo * info)
{
    // IMPORTANT
    // Here you need to fill a vector with all your contacts
    // both received by the server and previous ones.
    // Next pass this vector to the function completeConnection()
    // if you dont call completeConnection(), the service will
    // not work.
    std::map<std::string, MSN::Buddy *>::iterator i = info->contactList.begin();
    std::map<std::string, int> allContacts;
        
    for (; i != info->contactList.end(); i++)
    {
        MSN::Buddy *contact = (*i).second;
        if(contact->lists & MSN::LST_AB ) // only if it is the address book
        {
            allContacts[contact->userName.c_str()]=0;
            allContacts[contact->userName.c_str()] |= MSN::LST_AB;
            printf("-AB %s (%s)\n    Id: %s\n", contact->friendlyName.c_str(), contact->userName.c_str(), contact->properties["contactId"].c_str());

            if(contact->properties["isMessengerUser"]=="false")
                printf("    Not Messenger User\n");

            std::list<MSN::Buddy::PhoneNumber>::iterator pns = contact->phoneNumbers.begin();
            std::list<MSN::Group *>::iterator g = contact->groups.begin();

            for (; g != contact->groups.end(); g++)
            {
                printf("    G: %s\n", (*g)->name.c_str());
            }
    
            for (; pns != contact->phoneNumbers.end(); pns++)
            {
                printf("    %s: %s (%d)\n", (*pns).title.c_str(), (*pns).number.c_str(), (*pns).enabled);
            }
        }
        if(contact->lists & MSN::LST_AL )
        {
            allContacts[contact->userName.c_str()] |= MSN::LST_AL;
            printf("-AL %s \n", contact->userName.c_str());
        }

        if(contact->lists & MSN::LST_BL )
        {
            allContacts[contact->userName.c_str()] |= MSN::LST_BL;
            printf("-BL %s \n", contact->userName.c_str());
        }

        if(contact->lists & MSN::LST_RL )
        {
            printf("-RL %s \n", contact->userName.c_str());
        }
        if(contact->lists & MSN::LST_PL )
        {
            printf("-PL %s \n", contact->userName.c_str());
        }
    }
    printf("Available Groups:\n");
    std::map<std::string, MSN::Group>::iterator g = info->groups.begin();

    for (; g != info->groups.end(); g++)
    {
        printf("    %s: %s\n", (*g).second.groupID.c_str(), (*g).second.name.c_str());
    }

    std::map<std::string, int>::iterator b = allContacts.begin();

    // this will send the ADL command to the server
    // It is necessary. Dont forget to add *all* your contacts to allContacts,
    // (both Forward, allow and block lists) or you probably will 
    // loose someone.
    // A contact cannot be present both on allow and block lists or the
    // server will return an error, so you need to let your application
    // choose the better list to put it in.
    conn->completeConnection(allContacts,info);
}

void Callbacks::gotLatestListSerial(MSN::NotificationServerConnection * conn, std::string lastChange)
{
    // The application needs to track this number to not ask for the whole contact
    // list every login
    printf("The latest change number is: %s\n", lastChange.c_str());
}

void Callbacks::gotGTC(MSN::NotificationServerConnection * conn, char c)
{
    printf("Your GTC value is now %c\n", c);
}

void Callbacks::gotOIMDeleteConfirmation(MSN::NotificationServerConnection * conn, bool success, std::string id)
{
    if(success)
        std::cout << "OIM "<< id << " removed sucessfully." << std::endl;
    else
        std::cout << "OIM "<< id << " not removed sucessfully." << std::endl;
                
}

void Callbacks::gotOIMSendConfirmation(MSN::NotificationServerConnection * conn, bool success, int id)
{
    if(success)
        std::cout << "OIM " << id << " sent sucessfully." << std::endl;
    else
        std::cout << "OIM " << id << " not sent sucessfully." << std::endl;
}

void Callbacks::gotOIM(MSN::NotificationServerConnection * conn, bool success, std::string id, std::string message)
{
    if(success)
        std::cout << "ID: " << id << std::endl << "\t" << message << std::endl;
    else
        std::cout << "Error retreiving OIM " << id << std::endl;
}

void Callbacks::gotOIMList(MSN::NotificationServerConnection * conn, std::vector<MSN::eachOIM> OIMs)
{
    if(OIMs.size()==0)
    {
        printf("No Offline messages\n");
        return;
    }
    std::vector<MSN::eachOIM>::iterator i = OIMs.begin();
    for(; i<OIMs.end();i++)
    {
        printf("Offline message from: %s\n\t - Friendly Name: %s\n\t - Id: %s\n",(*i).from.c_str(), (*i).fromFN.c_str(), (*i).id.c_str());
    }
}

void Callbacks::connectionReady(MSN::Connection * conn)
{
    printf("The connection is ready. You can change your status now!\n");
}

void Callbacks::gotBLP(MSN::NotificationServerConnection * conn, char c)
{
    printf("Your BLP value is now %cL\n", c);
}

void Callbacks::addedListEntry(MSN::NotificationServerConnection * conn, MSN::ContactList list, MSN::Passport username, std::string friendlyname)
{
    if(list == MSN::LST_RL)
        // after adding the user you need to delete it from the pending list.
        // it will be added automatically by the msn service
        printf("%s is now on your list %d. FriendlyName: %s\n", username.c_str(), list, friendlyname.c_str());
    else
        // on normal lists you'll never receive the contacts displayname
        // it is not needed anyway
        printf("%s is now on your list %d\n", username.c_str(), list);
}

void Callbacks::removedListEntry(MSN::NotificationServerConnection * conn, MSN::ContactList list, MSN::Passport username)
{
    // list is a number which matches with MSN::ContactList enum on util.h
    printf("%s has been removed from list %d\n", username.c_str(), list);
}

void Callbacks::addedGroup(MSN::NotificationServerConnection * conn, bool added, std::string groupName, std::string groupID)
{
    if(added)
        printf("A group named %s (%s) was added\n", groupName.c_str(), groupID.c_str());
    else
        printf("Group (%s) was NOT added\n", groupName.c_str());
}

void Callbacks::removedGroup(MSN::NotificationServerConnection * conn, bool removed, std::string groupID)
{
    if(removed)
        printf("A group with ID %s was removed\n", groupID.c_str());
    else
    printf("Group (%s) was NOT removed\n", groupID.c_str());
}

void Callbacks::renamedGroup(MSN::NotificationServerConnection * conn, bool renamed, std::string newGroupName, std::string groupID)
{
    if(renamed)
        printf("A group with ID %s was renamed to %s\n", groupID.c_str(), newGroupName.c_str());
    else
        printf("A group with ID %s was NOT renamed to %s\n", groupID.c_str(), newGroupName.c_str());
}

void Callbacks::showError(MSN::Connection * conn, std::string msg)
{
    printf("MSN: Error: %s\n", msg.c_str());
}

void Callbacks::buddyChangedStatus(MSN::NotificationServerConnection * conn, MSN::Passport buddy, std::string friendlyname, MSN::BuddyStatus status, unsigned int clientID, std::string msnobject)
{
    printf("%s (%s) is now %s\n", friendlyname.c_str(), buddy.c_str(), MSN::buddyStatusToString(status).c_str());
    if (clientID & MSN::SupportWebcam)
        printf("\t Supports Webcam\n");
    if (clientID & MSN::VoiceClips)
        printf("\t Supports VoiceClips\n");
    if (clientID & MSN::SupportDirectIM)
        printf("\t Supports DirectIM\n");
    if (clientID & MSN::SharingFolders)
        printf("\t Supports SharingFolders\n");
    if (clientID & MSN::MSNC1)
        printf("\t Supports MSNC1\n");
    if (clientID & MSN::MSNC2)
        printf("\t Supports MSNC2\n");
    if (clientID & MSN::MSNC3)
        printf("\t Supports MSNC3\n");
    if (clientID & MSN::MSNC4)
        printf("\t Supports MSNC4\n");
    if (clientID & MSN::MSNC5)
        printf("\t Supports MSNC5\n");
    if (clientID & MSN::MSNC6)
        printf("\t Supports MSNC6\n");
    if (clientID & MSN::MSNC7)
        printf("\t Supports MSNC7\n");
    if (clientID & MSN::InkGifSupport)
        printf("\t Supports Ink Gif\n");
    if (clientID & MSN::InkIsfSupport)
        printf("\t Supports Ink Isf\n");

//  lastObject = msnobject;
}

void Callbacks::buddyOffline(MSN::NotificationServerConnection * conn, MSN::Passport buddy)
{
    printf("%s is now offline\n", buddy.c_str());
}

void Callbacks::gotSwitchboard(MSN::SwitchboardServerConnection * conn, const void * tag)
{
    printf("Got switchboard connection\n");
    if (tag)
    {
        const std::pair<std::string, std::string> *ctx = static_cast<const std::pair<std::string, std::string> *>(tag);
        conn->inviteUser(ctx->first);
    }
}

void Callbacks::buddyJoinedConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string friendlyname, int is_initial)
{
    printf("%s (%s) is now in the session\n", friendlyname.c_str(), username.c_str());
    if (conn->auth.tag)
    {
        const std::pair<std::string, std::string> *ctx = static_cast<const std::pair<std::string, std::string> *>(conn->auth.tag);
    // Example of sending a custom emoticon
//    conn->sendEmoticon("(EMOTICON)", filename);

    int trid = conn->sendMessage(ctx->second);
    std::cout << "Message " << trid << " queued" << std::endl;
        delete ctx;
        conn->auth.tag = NULL;

        //Example of sending a file
//    MSN::fileTransferInvite ft;
//    ft.filename = "/tmp/filetosend.txt";
//    ft.friendlyname = "filetosend2.txt";
//    ft.sessionId = sessionID++;
//    ft.type = MSN::FILE_TRANSFER_WITHOUT_PREVIEW;
//    conn->sendFile(ft);

//    conn->sendNudge();
//    conn->sendAction("Action message here");

    // Exemple of requesting a display picture.
//    std::string filename2("/tmp/displayPicture.bin"+MSN::toStr(sessionID));
    // lastObject is the msnobject received on each contact status change
    // you should generate a random sessionID
//    conn->requestDisplayPicture(sessionID++, filename2, lastObject);

    // Example of sending a voice clip
//    conn->myNotificationServer()->msnobj.addMSNObject("/tmp/voiceclip.wav",11);
//    std::string obj;
//    conn->myNotificationServer()->msnobj.getMSNObjectXML("/tmp/voiceclip.wav", 11, obj);
//    conn->sendVoiceClip(obj);
    // exemple of sending an ink
//    std::string ink("base64 data here...");
//    conn->sendInk(ink);
    }
}

void Callbacks::buddyLeftConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport username)
{
    printf("%s has now left the session\n", username.c_str());
}

void Callbacks::gotInstantMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string friendlyname, MSN::Message * msg)
{
    printf("--- Message from %s (%s) in font %s:\n%s\n", friendlyname.c_str(), username.c_str(), msg->getFontName().c_str(), msg->getBody().c_str());
}

void Callbacks::gotEmoticonNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string alias, std::string msnobject)
{
    std::string filename2("/tmp/emoticon.bin"+MSN::toStr(sessionID));
    printf("--- Emoticon '%s' from %s -> %s\n", alias.c_str(), username.c_str(), msnobject.c_str());
    conn->requestEmoticon(sessionID++, filename2, msnobject, alias);
}

void Callbacks::failedSendingMessage(MSN::Connection * conn)
{
    printf("**************************************************\n");
    printf("ERROR:  Your last message failed to send correctly\n");
    printf("**************************************************\n");
}

void Callbacks::buddyTyping(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string friendlyname)
{
    printf("\t%s (%s) is typing...\n", friendlyname.c_str(), username.c_str());

}

void Callbacks::gotNudge(MSN::SwitchboardServerConnection * conn, MSN::Passport username)
{
    printf("\t%s sent you a nudge ...\n", username.c_str());
}

void Callbacks::gotVoiceClipNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string msnobject)
{
    printf("\t%s sent you a voice clip...\n", username.c_str());
    std::string filename2("/tmp/voiceclip.bin"+MSN::toStr(sessionID));
    conn->requestVoiceClip(sessionID++, filename2, msnobject);
}

void Callbacks::gotWinkNotification(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string msnobject)
{
    printf("\t%s sent you a Wink...\n", username.c_str());
    std::string filename2("/tmp/wink.bin"+MSN::toStr(sessionID));
    // you should generate a random sessionID number
    conn->requestWink(sessionID++, filename2, msnobject);
}

void Callbacks::gotInk(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string image)
{
    // image variable is the base64 encoded gif file
    printf("\t%s sent you an Ink...\n", username.c_str());
//  std::string filename2("/tmp/ink.bin"+MSN::toStr(sessionID));
    // you should generate a random sessionID number
//  conn->requestFile(sessionID++, filename2, msnobject);
}

void Callbacks::gotContactDisplayPicture(MSN::SwitchboardServerConnection * conn, MSN::Passport passport, std::string filename )
{
    printf("Received display picture from %s. File: %s\n", passport.c_str(), filename.c_str());
}

void Callbacks::gotActionMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport username, std::string message)
{
    printf("\t%s sent you an action message: %s\n", username.c_str(), message.c_str());
}

void Callbacks::gotInitialEmailNotification(MSN::NotificationServerConnection * conn, int msgs_inbox, int unread_inbox, int msgs_folders, int unread_folders)
{
    if (unread_inbox > 0) 
        printf("You have %d new messages in your Inbox. Total: %d\n", unread_inbox, msgs_inbox); 
    
    if (unread_folders > 0) 
        printf("You have %d new messages in other folders. Total: %d\n", unread_folders, msgs_folders); 
}

void Callbacks::gotNewEmailNotification(MSN::NotificationServerConnection * conn, std::string from, std::string subject)
{
    printf("New e-mail has arrived from %s.\nSubject: %s\n", from.c_str(), subject.c_str());
}

void Callbacks::fileTransferInviteResponse(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, bool response)
{
    if(response)
            printf("Session accepted %d: \n", sessionID);
    else
            printf("Session not accepted %d: \n", sessionID);
}

void Callbacks::fileTransferProgress(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, unsigned long long transferred, unsigned long long total)
{
    printf("File transfer: session %d\t(%llu/%llu bytes sent/received)\n", sessionID, transferred, total);
}

void Callbacks::fileTransferFailed(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, MSN::fileTransferError error)
{
    if( error == MSN::FILE_TRANSFER_ERROR_USER_CANCELED)
        printf("The other user canceled the file transfer failed. Session: %d\n", sessionID);
    if( error == MSN::FILE_TRANSFER_ERROR_UNKNOWN)
        printf("File transfer failed. Session: %d\n", sessionID);
}

void Callbacks::fileTransferSucceeded(MSN::SwitchboardServerConnection * conn, unsigned int sessionID)
{
    printf("File transfer successfully completed. session: %d\n", sessionID);
}

void Callbacks::gotNewConnection(MSN::Connection * conn)
{
    if (dynamic_cast<MSN::NotificationServerConnection *>(conn))
        dynamic_cast<MSN::NotificationServerConnection *>(conn)->synchronizeContactList();
}

void Callbacks::buddyChangedPersonalInfo(MSN::NotificationServerConnection * conn, MSN::Passport fromPassport, MSN::personalInfo pInfo) 
{
    // MSN::personalInfo shows all the data you can grab from the contact
    printf("User %s Personal Message: %s\n", fromPassport.c_str(),pInfo.PSM.c_str());
}

void Callbacks::closingConnection(MSN::Connection * conn)
{
    printf("Closed connection with socket %d\n", conn->sock);
}

void Callbacks::changedStatus(MSN::NotificationServerConnection * conn, MSN::BuddyStatus state)
{
    printf("Your state is now: %s\n", MSN::buddyStatusToString(state).c_str());

    MSN::personalInfo pInfo;
    pInfo.PSM="my personal message";
    pInfo.mediaType="Music";
    pInfo.mediaIsEnabled=1;
    pInfo.mediaFormat="{0} - {1}";
    pInfo.mediaLines.push_back("Artist");
    pInfo.mediaLines.push_back("Song");
    conn->setPersonalStatus(pInfo);
}

void * Callbacks::connectToServer(std::string hostname, int port, bool *connected, bool isSSL)
{
    struct sockaddr_in sa;
    struct hostent     *hp;
    int s;
    
    if ((hp = gethostbyname(hostname.c_str())) == NULL) {
        errno = ECONNREFUSED;                       
        return (void*)-1;
    }
    
    memset(&sa,0,sizeof(sa));
    memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length);     /* set address */
    sa.sin_family = hp->h_addrtype;
    sa.sin_port = htons((u_short)port);
    
    if ((s = socket(hp->h_addrtype,SOCK_STREAM,0)) < 0)     /* get socket */
        return (void*)-1;

    int oldfdArgs = fcntl(s, F_GETFL, 0);
    fcntl(s, F_SETFL, oldfdArgs | O_NONBLOCK);
    
    if (connect(s,(struct sockaddr *)&sa,sizeof sa) < 0)
    {
        if (errno != EINPROGRESS)
        {
            close(s);
            return (void*)-1;
        }
        *connected = false;
    }
    else
        *connected = true;
    return (void*)s;
}

int Callbacks::listenOnPort(int port)
{
    int s;
    struct sockaddr_in addr;
    
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        return -1;
    }
    
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    
    if (bind(s, (sockaddr *)(&addr), sizeof(addr)) < 0 || listen(s, 1) < 0)
    {
        close(s);
        return -1;
    }
    
    return s;
}

std::string Callbacks::getOurIP(void)
{
    struct hostent * hn;
    char buf2[1024];
    
    gethostname(buf2,1024);
    hn = gethostbyname(buf2);
    
    return inet_ntoa( *((struct in_addr*)hn->h_addr));
}

void Callbacks::log(int i, const char *s)
{
    
}

std::string Callbacks::getSecureHTTPProxy()
{
    return "";
}

void Callbacks::gotMessageSentACK(MSN::SwitchboardServerConnection * conn, int trID)
{
    std::cout << "Message " << trID << " delivered" << std::endl;
}

void Callbacks::askFileTransfer(MSN::SwitchboardServerConnection * conn, MSN::fileTransferInvite ft)
{
    std::string filename2("/tmp/"+ft.filename);
    switch(ft.type)
    {
        case MSN::FILE_TRANSFER_BACKGROUND_SHARING:
            printf("User %s wants to share with you a background file named %s. Size: %llu. Accepting..\n", ft.userPassport.c_str(), ft.filename.c_str(), ft.filesize);
            break;
        case MSN::FILE_TRANSFER_BACKGROUND_SHARING_CUSTOM:
            printf("User %s wants to share with you a *custom background file named %s. Size: %llu. Accepting..\n", ft.userPassport.c_str(), ft.filename.c_str(), ft.filesize);
            break;
        case MSN::FILE_TRANSFER_WITH_PREVIEW:
            printf("User %s wants to send you a file *with preview named %s. Size: %llu. Accepting..\n", ft.userPassport.c_str(), ft.filename.c_str(), ft.filesize);
            // ft.preview has the base64 encoded png file
            break;
        case MSN::FILE_TRANSFER_WITHOUT_PREVIEW:
            printf("User %s wants to send you a file *without preview named %s. Size: %llu. Accepting..\n", ft.userPassport.c_str(), ft.filename.c_str(), ft.filesize);
            break;
        default:
            printf("Unknown filetransfer type from %s..\n", ft.userPassport.c_str());

    }
    conn->fileTransferResponse(ft.sessionId, filename2, true);
}

void Callbacks::addedContactToGroup(MSN::NotificationServerConnection * conn, bool added, std::string groupId, std::string contactId)
{
    if(added)
        printf("User Id (%s) added to group Id (%s)\n", contactId.c_str(),groupId.c_str());
    else
        printf("User Id (%s) NOT added to group Id (%s)\n", contactId.c_str(),groupId.c_str());
}

void Callbacks::removedContactFromGroup(MSN::NotificationServerConnection * conn, bool removed, std::string groupId, std::string contactId)
{
    if(removed)
        printf("User Id (%s) removed from group Id (%s)\n", contactId.c_str(),groupId.c_str());
    else
        printf("User Id (%s) NOT removed from group Id (%s)\n", contactId.c_str(),groupId.c_str());
}

void Callbacks::addedContactToAddressBook(MSN::NotificationServerConnection * conn, bool added, std::string passport, std::string displayName, std::string guid)
{
    if(added)
        printf("User (%s - %s) added to AddressBook. Guid (%s)\n", passport.c_str(),displayName.c_str(), guid.c_str());
    else
        printf("User (%s - %s) NOT added to AddressBook.\n", passport.c_str(),displayName.c_str());
}

void Callbacks::removedContactFromAddressBook(MSN::NotificationServerConnection * conn, bool removed, std::string contactId, std::string passport)
{
    if(removed)
        printf("User %s removed from AddressBook. Guid (%s)\n", passport.c_str(), contactId.c_str());
    else
        printf("User %s NOT removed from AddressBook. Guid (%s)\n", passport.c_str(), contactId.c_str());
}

void Callbacks::enabledContactOnAddressBook(MSN::NotificationServerConnection * conn, bool enabled, std::string contactId, std::string passport)
{
    // this is used to enable a contact previously disabled from msn, but not fully removed
    if(enabled)
        printf("User (%s) enabled on AddressBook. Guid (%s)\n", passport.c_str(), contactId.c_str());
    else
        printf("User (%s) NOT enabled on AddressBook. Guid (%s)\n", passport.c_str(), contactId.c_str());
}

void Callbacks::disabledContactOnAddressBook(MSN::NotificationServerConnection * conn, bool disabled, std::string contactId)
{
    // this is used when you have disabled this user from msn, but not deleted from hotmail
    // I suggest to delete the contact instead of disable, since I haven't tested this too much yet
    if(disabled)
        printf("User disabled on AddressBook. Guid (%s)\n", contactId.c_str());
    else
        printf("User NOT disabled on AddressBook. Guid (%s)\n", contactId.c_str());
}

size_t Callbacks::getDataFromSocket (void *sock, char *data, size_t size)
{
    int fd;
    int idx=0;
    bool ssl_done = false;
    int amountRead = 0;
    int newAmountRead = 0;
    for (int i = 1; i < 20; i++)
    {
        if(mySockets[i].fd == getSocketFileDescriptor(sock))
        {
            idx = i;
            break;
        }
    }
    if(!idx)
        return 0;

    if(!mySocketsSsl[idx].isSSL)
    {
        int newWritten = ::recv(getSocketFileDescriptor(sock), data, size, 0);
        if (errno == EAGAIN)
           return -1;
        return newWritten;
    }

    if (!mySocketsSsl[idx].isConnected)
        return 0;

    while(!ssl_done)
    {
        if(!mySocketsSsl[idx].ssl) break;
        newAmountRead = SSL_read(mySocketsSsl[idx].ssl, data, size);
        switch(SSL_get_error(mySocketsSsl[idx].ssl,newAmountRead))
        {
            case SSL_ERROR_NONE:
                return newAmountRead;
            case SSL_ERROR_ZERO_RETURN:
                return 0;
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE:
                ssl_done=false;
                break;
            case SSL_ERROR_SSL:
                return 0;
                break;
            case SSL_ERROR_SYSCALL:
                return 0;
                break;
            default:
                return newAmountRead;
        }
    }
    return 0;
}

size_t Callbacks::writeDataToSocket (void *sock, char *data, size_t size)
{
    size_t written = 0;
    int idx;
    for (int i = 1; i < 20; i++)
    {
        if(mySockets[i].fd == getSocketFileDescriptor(sock))
        {
            idx = i;
            break;
        }
    }

    while (written < size)
    {
        int newWritten;
        if (mySocketsSsl[idx].isSSL)
        {
            if(!mySocketsSsl[idx].isConnected)
                return 0;
            newWritten = SSL_write(mySocketsSsl[idx].ssl, data, (int) (size - written));
            int error = SSL_get_error(mySocketsSsl[idx].ssl,newWritten);
            switch(error)
            {
                case SSL_ERROR_NONE:
                    written += newWritten;
                    data+=newWritten;
                    break;
                case SSL_ERROR_WANT_WRITE:
                case SSL_ERROR_WANT_READ:
                    continue;
                case SSL_ERROR_ZERO_RETURN:
                    break;
                case SSL_ERROR_SYSCALL:
                default:
                    break;
            }
        }
        else
        {
            newWritten = ::send(getSocketFileDescriptor(sock), data, (int)(size - written), 0);

            if (newWritten <= 0)
            {
                if (errno == EAGAIN)
                    continue;
                else
                    break;
            }
            written += newWritten;
            data+=newWritten;
        }
    }
    if (written != size)
    {
        // TODO: return an error
        showError(NULL, "Error on socket");
        unregisterSocket(sock);
        closeSocket(sock);
        return written;
    }
    return written;
}

void Callbacks::gotVoiceClipFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string file)
{
    printf("--- Voice clip file '%s'\n", file.c_str());

}

void Callbacks::gotEmoticonFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string alias, std::string file)
{
    printf("--- Voice emoticon file '%s' for '%s'\n", file.c_str(), alias.c_str());
}

void Callbacks::gotWinkFile(MSN::SwitchboardServerConnection * conn, unsigned int sessionID, std::string file)
{
    printf("--- Voice Wink file '%s'\n", file.c_str());
}

void Callbacks::gotInboxUrl(MSN::NotificationServerConnection *conn, MSN::hotmailInfo info)
{
    std::vector<std::string>::const_iterator i;

    std::cout << "--- Inbox URL data :" << std::endl;
    std::cout << "sid: " << info.sid << std::endl;
    std::cout << "kv: " << info.kv << std::endl;
    std::cout << "id: " << info.id << std::endl;
    std::cout << "sl: " << info.sl << std::endl;
    std::cout << "rru: " << info.rru << std::endl;
    std::cout << "auth: " << info.MSPAuth << std::endl;
    std::cout << "creds: " << info.creds << std::endl;
}
