www.pudn.com > fanccMSNr.src.rar > ChatterModel.cpp
// TODO: Remove this #include and TRACE() #include "../stdafx.h" #include#include #include #include using namespace boost; #include "MessengerModel.hpp" namespace poral { ChatterModel::ChatterModel() { transactionID=0; messageDispatcher=NULL; status=NOT_CONNECTED; viewRequested=false; } ChatterModel::~ChatterModel() { if(messageDispatcher!=NULL) { delete messageDispatcher; } } void ChatterModel::connect(MessengerModel &messengerModel, const Message &inMessage) { status=ACCEPTING_THE_INVITATION; // Initialize variables this->messengerModel=&messengerModel; if(messageDispatcher==NULL) { messageDispatcher=messengerModel.getMessageDispatcher()->newInstance(); messageDispatcher->addMessageListener(*this); } initSessionID=inMessage.getArgument(0); const string &ipWithPort=inMessage.getArgument(1); initHash=inMessage.getArgument(3); const string &inviter=inMessage.getArgument(4); screenName=inMessage.getArgument(5); typedef tokenizer > Tokenizer; char_separator sep(":"); Tokenizer tokens(ipWithPort, sep); string serverIP= *(tokens.begin()); messageDispatcher->connect(serverIP, 1863); // wait for connected() notification } void ChatterModel::connect(MessengerModel &messengerModel, const Message &inMessage, const string &companionID) { // Initialize variables status=ATTEMPTING_TO_CONNECT; companion.ID=companionID; this->messengerModel=&messengerModel; if(messageDispatcher==NULL) { messageDispatcher=messengerModel.getMessageDispatcher()->newInstance(); messageDispatcher->addMessageListener(*this); } const string &ipWithPort=inMessage.getArgument(2); initHash=inMessage.getArgument(4); typedef tokenizer > Tokenizer; char_separator sep(":"); Tokenizer tokens(ipWithPort, sep); serverIP= *(tokens.begin()); messageDispatcher->connect(serverIP, 1863); if(!viewRequested) { // Is anybody out there? listen to ME! this->messengerModel->fireChatterViewRequested(*this); viewRequested=true; } // wait for connected() notification } void ChatterModel::reconnect() { status=ATTEMPTING_TO_CONNECT; messageDispatcher->connect(serverIP, 1863); // wait for connected() notification } void ChatterModel::connected() { Message outMessage; if(status==ACCEPTING_THE_INVITATION) { outMessage.setName("ANS"); outMessage.addArgument(getTransactionID()); outMessage.addArgument(messengerModel->getMy().ID); outMessage.addArgument(initHash); outMessage.addArgument(initSessionID); } else { status=WAITING_FOR_COMPANION; outMessage.setName("USR"); outMessage.addArgument(getTransactionID()); outMessage.addArgument(messengerModel->getMy().ID); outMessage.addArgument(initHash); } messageDispatcher->sendMessage(outMessage); } void ChatterModel::messageArrived(const Message &inMessage) { const string &name=inMessage.getName(); if(name=="ANS") { // Not yet support MANY_TO_MANY_CHAT; setStatus(ONE_TO_ONE_CHAT); } if(name=="BYE") { // Not yet support MANY_TO_MANY_CHAT; setStatus(CONNECTED_ALONE); } if(name=="IRO") { // TODO: Reform this to be able to handle multiple attender; companion.ID=inMessage.getArgument(3); companion.screenName=inMessage.getArgument(4); } if(name=="USR") { callCompanion(companion.ID); } if(name=="JOI") { companion.ID=inMessage.getArgument(0); companion.screenName=inMessage.getArgument(1); // Not yet support MANY_TO_MANY_CHAT; setStatus(ONE_TO_ONE_CHAT); } // MIME Messages if(name=="MSG") { try { const MIMEMessage &inMIME= dynamic_cast (inMessage); MIMEMessageArrived(inMIME); } catch(bad_cast) { // do noting } } } void ChatterModel::MIMEMessageArrived(const MIMEMessage &inMessage) { if(inMessage.contentType.find("text/x-msmsgscontrol")!=string::npos) { Properties::const_iterator i; i=inMessage.properties.find("TypingUser"); if(i!=inMessage.properties.end()) { const string &ID=(*i).second; fireTyping(ID); } } else if(inMessage.contentType.find("text/plain")!=string::npos) { fireChatterViewRequested(); fireMessageArrived(inMessage); } else if(inMessage.contentType.find("text/x-msmsgsinvite")!=string::npos) { InvitationMessage invMessage=inMessage; fireChatterViewRequested(); Properties::iterator i; i=invMessage.properties.find("Invitation-Command"); if(i==invMessage.properties.end()) { return; } const string &invitationCommand=(*i).second; if(invitationCommand=="INVITE") { fileTransferModel.status=FileTransferModel::ATTEMPT_TO_RECEIVE; i=invMessage.properties.find("Application-FileSize"); if(i==invMessage.properties.end()) { return; } const string &fileSizeStr=(*i).second; i=invMessage.properties.find("Invitation-Cookie"); if(i==invMessage.properties.end()) { return; } const string &cookie=(*i).second; i=invMessage.properties.find("Application-File"); if(i==invMessage.properties.end()) { return; } const string &filename=(*i).second; size_t fileSize; istrstream fileSizeStream(fileSizeStr.data()); fileSizeStream >> fileSize; fireFileTransferInvited(filename, fileSize, cookie); } else if(invitationCommand=="ACCEPT") { if(fileTransferModel.status==FileTransferModel::ATTEMPT_TO_RECEIVE) { Properties::iterator i; i=invMessage.properties.find("IP-Address"); if(i==invMessage.properties.end()) { return; } const string &ip=(*i).second; i=invMessage.properties.find("Port"); if(i==invMessage.properties.end()) { return; } const string &portStr=(*i).second; int port; istrstream portStream(portStr.data()); portStream >> port; i=invMessage.properties.find("AuthCookie"); if(i==invMessage.properties.end()) { return; } const string &authCookie=(*i).second; fileTransferModel.connect(*this, ip, port, authCookie, filename); } else if(fileTransferModel.status==FileTransferModel::ATTEMPT_TO_SEND) { const string authCookie=(format("%d") % fileTransferModel.newAuthCookie).str(); fileTransferModel.newAuthCookie++; int port=fileTransferModel.listen(*this, 6891, authCookie, fileTransferModel.filename); string address; messageDispatcher->getAddress(address); string &portString = (format("%d") % port).str(); TRACE("port==%d\n", port); InvitationMessage invMessage; invMessage.properties.insert(Property("Invitation-Command", "ACCEPT")); invMessage.properties.insert(Property("Invitation-Cookie", fileTransferModel.cookie)); invMessage.properties.insert(Property("IP-Address", address)); invMessage.properties.insert(Property("Port", portString)); invMessage.properties.insert(Property("AuthCookie", authCookie)); invMessage.properties.insert(Property("Launch-Application", "FALSE")); sendInvitationMessage(invMessage); } } else if(invitationCommand=="CANCEL") { fileTransferModel.status=FileTransferModel::NOT_CONNECTED; i=invMessage.properties.find("Invitation-Cookie"); if(i==invMessage.properties.end()) { return; } const string &cookie=(*i).second; fireFileTransferRejected(cookie); } } } void ChatterModel::sendInvitationMessage(const InvitationMessage &invMessage) { MIMEMessage outMessage; outMessage.addArgument(getTransactionID()); outMessage.addArgument("A"); invMessage.toMIMEMessage(outMessage); messageDispatcher->sendMessage(outMessage); } // file transfer related methods void ChatterModel::acceptFileTransfer(const string &cookie, const string &filename) { this->filename=filename; InvitationMessage invMessage; invMessage.properties.insert(Property("Invitation-Command", "ACCEPT")); invMessage.properties.insert(Property("Invitation-Cookie", cookie)); invMessage.properties.insert(Property("Launch-Application", "FALSE")); sendInvitationMessage(invMessage); } void ChatterModel::rejectFileTransfer(const string &cookie) { fileTransferModel.status=FileTransferModel::NOT_CONNECTED; InvitationMessage invMessage; invMessage.properties.insert(Property("Invitation-Command", "CANCEL")); invMessage.properties.insert(Property("Invitation-Cookie", cookie)); invMessage.properties.insert(Property("Cancel-Code", "REJECT")); sendInvitationMessage(invMessage); } void ChatterModel::inviteFileTransfer(const string &filename) { fileTransferModel.status=FileTransferModel::ATTEMPT_TO_SEND; fileTransferModel.filename=filename; // Does not care about cookie for now fileTransferModel.cookie=(format("%d") % fileTransferModel.newCookie).str(); fileTransferModel.newCookie++; struct stat fileInfo; stat(filename.data(), &fileInfo); fileTransferModel.length=fileInfo.st_size; const string fileSizeStr=(format("%d") % (int)(fileTransferModel.length)).str(); InvitationMessage invMessage; invMessage.properties.insert(Property("Application-Name", "File Transfer")); invMessage.properties.insert(Property("Application-GUID", "{5D3E02AB-6190-11d3-BBBB-00C04F795683}")); invMessage.properties.insert(Property("Invitation-Command", "INVITE")); invMessage.properties.insert(Property("Invitation-Cookie", fileTransferModel.cookie)); invMessage.properties.insert(Property("Application-File", filename)); invMessage.properties.insert(Property("Application-FileSize", fileSizeStr)); sendInvitationMessage(invMessage); } void ChatterModel::disconnect(ChatterListener::Event event, bool closed) { } void ChatterModel::failed() { status=NOT_CONNECTED; } void ChatterModel::addChatterListener(ChatterListener &listener) { listeners.push_back(&listener); } void ChatterModel::removeChatterListener(const ChatterListener &listener) { list ::iterator location=find(listeners.begin(), listeners.end(), &listener); listeners.erase(location); } void ChatterModel::fireChatterViewRequested() { if(!viewRequested) { messengerModel->fireChatterViewRequested(*this); viewRequested=true; } } void ChatterModel::fireTyping(const string &user) { list ::iterator listener; for(listener=listeners.begin(); listener!=listeners.end(); listener++) { (*listener)->typing(user); } } void ChatterModel::fireMessageArrived(const ChatMessage &inMessage) { list ::iterator listener; for(listener=listeners.begin(); listener!=listeners.end(); listener++) { (*listener)->messageArrived(inMessage); } } void ChatterModel::fireChatterUpdate(ChatterListener::Event event) { list ::iterator listener; for(listener=listeners.begin(); listener!=listeners.end(); listener++) { (*listener)->chatterUpdate(*this, event); } } void ChatterModel::fireFileTransferInvited(const string &filename, size_t size, const string &cookie) { list ::iterator listener; for(listener=listeners.begin(); listener!=listeners.end(); listener++) { (*listener)->fileTransferInvited(filename, size, cookie); } } void ChatterModel::fireFileTransferRejected(const string &cookie) { list ::iterator listener; for(listener=listeners.begin(); listener!=listeners.end(); listener++) { (*listener)->fileTransferRejected(cookie); } } void ChatterModel::setStatus(Status status) { if(status==ONE_TO_ONE_CHAT) { sendBufferedMessages(); } this->status=status; } void ChatterModel::sendMessage(const string &message) { messageBuffer.push(message); if(status==NOT_CONNECTED) { reconnect(); } if(status==CONNECTED_ALONE) { setStatus(WAITING_FOR_COMPANION); callCompanion(companion.ID); } if(status==ONE_TO_ONE_CHAT) { sendBufferedMessages(); } } void ChatterModel::sendBufferedMessages() { while(messageBuffer.size()!=0) { sendMessageNow(messageBuffer.front()); messageBuffer.pop(); } } void ChatterModel::callCompanion(const string &companionID) { Message outMessage; outMessage.setName("CAL"); outMessage.addArgument(getTransactionID()); outMessage.addArgument(companionID); messageDispatcher->sendMessage(outMessage); } void ChatterModel::sendMessageNow(const string &message) { MIMEMessage outMessage; outMessage.setName("MSG"); outMessage.addArgument(getTransactionID()); outMessage.addArgument("A"); outMessage.version="1.0"; outMessage.contentType="text/plain; charset=UTF-8"; outMessage.properties.insert(Property("X-MMS-IM-Format", "FN=%EA%B5%B4%EB%A6%BC; EF=; CO=0; CS=81; PF=0")); outMessage.body=message; messageDispatcher->sendMessage(outMessage); } }