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); 
	} 
}