www.pudn.com > fanccMSNr.src.rar > MessengerModel.cpp


#include "MessengerModel.hpp" 
#include "ChatterModel.hpp" 
#include "md5/md5.h" 
 
#include  
#include  
 
#include  
#include  
 
using namespace boost; 
 
 
// TODO: Remove this #include and TRACE() 
#include "../stdafx.h" 
 
 
namespace poral { 
 
	namespace { 
		/** 
		* Gets MD5 digest string by hexadicimal presentation 
		*/ 
		void md5Digest(string &hexDigest, const string &input) 
		{ 
			md5_state_t md5; 
			md5_byte_t digest[16]; 
 
			md5_init(&md5); 
			md5_append(&md5, (const md5_byte_t *)input.data(), (int)input.length()); 
			md5_finish(&md5, digest); 
 
			char hex_output[16*2 + 1]; 
			for (int i = 0; i < 16; ++i) { 
				sprintf(hex_output + (i * 2), "%02x", digest[i]); 
			} 
			hexDigest=hex_output; 
		} 
 
		string EncodeUTF8(CString strScr) 
		{ 
			CString strTemp; 
			strScr.TrimLeft("\""); 
			strScr.TrimRight("\""); 
			CString strEncodingUrl; 
			WCHAR Buffer[1000]; 
			int cchWideChar=1000,iLen; 
			iLen = MultiByteToWideChar(CP_ACP,NULL,strScr,-1,Buffer,cchWideChar); 
			if(0 == iLen) 
			{ 
				TRACE("MultiByteToWideChar Error:" + strScr); 
				return ""; 
			} 
			char cBuffer[2000]; 
			iLen = WideCharToMultiByte(65001,NULL,Buffer,-1,cBuffer,2000,NULL,NULL); 
			if(0 == iLen) 
			{ 
				TRACE("WideCharToMultiByte Error"); 
				return ""; 
			} 
			for(int i=0;i PassportUrls; 
	bool GetLoginServerAddres() 
	{ 
		//建立http连接 
		CInternetSession* pSession = NULL; 
		CHttpConnection* pConnection = NULL; 
		CHttpFile* pHttpFile = NULL; 
		try 
		{ 
			pSession = new CInternetSession(NULL, 1, INTERNET_OPEN_TYPE_PRECONFIG); 
			CString strServer; 
			strServer.Format("nexus.passport.com"); 
			CString strObject; 
			strObject.Format("/rdr/pprdr.asp"); 
			INTERNET_PORT nPort = 80; 
			pConnection = pSession->GetHttpConnection(strServer,nPort); 
			pHttpFile = pConnection->OpenRequest( 
				CHttpConnection::HTTP_VERB_GET, 
				strObject, 
				strServer, 
				1, 
				NULL, 
				NULL, 
				INTERNET_FLAG_EXISTING_CONNECT); 
			pHttpFile->SendRequest(); 
			DWORD dwHttpStatus; 
			if( NULL != pHttpFile ) 
			{ 
				pHttpFile->QueryInfoStatusCode(dwHttpStatus); 
				//访问http文件成功 
				CString strReciveData; 
				if(dwHttpStatus == HTTP_STATUS_OK) 
				{ 
					//Returns the response or request headers from the HTTP server. 
					pHttpFile->QueryInfo( 
						HTTP_QUERY_RAW_HEADERS_CRLF, 
						strReciveData, 
						NULL); 
					int pos = strReciveData.Find("PassportURLs"); 
					if( -1 != pos) 
					{ 
						strReciveData = strReciveData.Mid(pos); 
						pos = strReciveData.Find(","); 
						int iIndex = 0; 
						CString strPara; 
						string passportUrls; 
						while ( -1 != pos ) 
						{ 
							strPara = strReciveData.Left(pos); 
							strPara = strPara.Mid(strPara.Find("=") + 1); 
							passportUrls = strPara.GetBuffer(strPara.GetLength()); 
							TRACE("PassportUrls:  %s\n",passportUrls.c_str()); 
							PassportUrls[iIndex] = passportUrls; 
							iIndex++; 
							strReciveData = strReciveData.Mid(pos+1); 
							pos = strReciveData.Find(","); 
						} 
						//保存最后一个passportUrls ----  ConfigVersion 
						strPara = strReciveData.Left(strReciveData.Find("Content-Length")); 
						passportUrls = strPara.Mid(strPara.Find("=")); 
						PassportUrls[iIndex] = passportUrls; 
					} 
					return true; 
				} 
			} 
		} 
		catch (...) { 
			TRACE("GetLoginServerAddres Error.....\n"); 
		} 
		if (pHttpFile != NULL)  
		{ 
			pHttpFile->Close(); 
			delete pHttpFile;  
		}   
		if (pConnection != NULL)  
		{ 
			pConnection->Close(); 
			delete pConnection;  
		}  
		if (pSession != NULL)  
		{ 
			pSession->Close(); 
			delete pSession;  
		} 
		return false; 
	} 
 
		string GetClientTicket(string userID, string password, string ChallengeString) 
		{ 
			// First get a valid login address for the initial server 
			if(GetLoginServerAddres()) 
			{ 
				// On the position of DALOGIN is a valid URL, for login 
				string uri = "https://" + PassportUrls[DALOGIN]; 
 
				//CInternetSession session("EWork-MSN",PRE_CONFIG_INTERNET_ACCESS); 
				CHttpConnection* pServer = NULL; 
				CHttpFile* pFile = NULL; 
				CString strServerName(""),strObject(""); 
				INTERNET_PORT nPort = 443; 
				DWORD dwServiceType; 
 
				//建立http连接 
				CInternetSession session; 
				try 
				{ 
					while(true) 
					{ 
						if (!AfxParseURL(uri.c_str(), dwServiceType, strServerName, strObject, nPort)) 
						{ 
							return "0"; 
						} 
						/*strServerName.Format("loginnet.passport.com"); 
						strObject.Format("/login2.srf");*/ 
						//session = new CInternetSession(NULL, 1, INTERNET_OPEN_TYPE_PRECONFIG); 
						pServer = session.GetHttpConnection(strServerName, nPort); 
						pFile = pServer->OpenRequest( 
							CHttpConnection::HTTP_VERB_GET, 
							strObject, 
							strServerName, 
							1, 
							NULL, 
							"HTTP/1.0", 
							INTERNET_FLAG_NO_AUTO_REDIRECT | INTERNET_FLAG_SECURE 
							); 
						//userID编码 
						CString strUserName = userID.c_str(); 
						strUserName.Replace("@","%40"); 
						CString strAccount; 
						strAccount.Format("sign-in=%s,pwd=%s,%s \r\n", 
							strUserName, 
							password.c_str(), 
							ChallengeString.c_str()); 
						CString strHeader; 
						strHeader.Format("Authorization: Passport1.4 OrgVerb=GET,OrgURL=http://messenger.msn.com,"); 
						strHeader.Replace("http://messenger.msn.com","http%3A%2F%2Fmessenger%2Emsn%2Ecom"); 
						string strHeaderUTF8 = strHeader.GetBuffer(); 
						CString strAuthorization; 
						strAuthorization.Format("%s%s",strHeaderUTF8.c_str(),strAccount); 
						 
						pFile->AddRequestHeaders("Accept: */* \r\n");						 
 
						pFile->AddRequestHeaders(strAuthorization); 
 
 
						pFile->SendRequest(); 
						DWORD dwRet; 
						pFile->QueryInfoStatusCode(dwRet); 
						if (dwRet == HTTP_STATUS_OK)  
						{ 
							CString strNewLocation(""); 
							pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, strNewLocation); 
							int pos = strNewLocation.Find("Authentication-Info"); 
							if( -1 != pos) 
							{ 
								strNewLocation = strNewLocation.Mid(pos); 
								int iStart = -1; 
								int iEnd = -1; 
								iStart = strNewLocation.Find("'"); 
								iEnd = strNewLocation.Mid(iStart+1).Find("'"); 
								if( -1 != iStart && -1 != iEnd ) 
								{ 
									TRACE("GetTicket:   %s\n",strNewLocation.Mid(iStart+1,iEnd)); 
									string strTicket = strNewLocation.Mid(iStart+1,iEnd).GetBuffer(); 
									return strTicket; 
								} 
								else 
								{ 
									return "401"; 
								} 
							} 
						} 
						else 
						{ 
							// 是否重定向 
							if (dwRet == HTTP_STATUS_MOVED || 
								dwRet == HTTP_STATUS_REDIRECT || 
								dwRet == HTTP_STATUS_REDIRECT_METHOD) 
 
							{ 
								CString strNewLocation(""); 
								pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, strNewLocation); 
								int pos = strNewLocation.Find("Location"); 
								strNewLocation = strNewLocation.Mid(pos+8); 
								pos = strNewLocation.Find("\r\n"); 
								if( -1 != pos ) 
								{ 
									uri = strNewLocation.Left(pos).GetBuffer(); 
								} 
								else 
								{ 
									return "0"; 
								} 
							} 
							else 
							{ 
								return "0"; 
							} 
						} 
					}					 
				} 
				catch(CInternetException* pEx) 
				{ 
					TCHAR   szCause[255]; 
					CString strFormatted; 
 
					pEx->GetErrorMessage(szCause, 255); 
 
					// (in real life, it's probably more 
					// appropriate to read this from 
					//  a string resource so it would be easy to 
					// localize) 
 
					strFormatted = _T("ERROR: "); 
					strFormatted += szCause; 
					TRACE("GetTicket Error...%s.....\n",strFormatted); 
				} 
				if (pFile != NULL)  
				{ 
					pFile->Close(); 
					delete pFile;  
				}   
				if (pServer != NULL)  
				{ 
					pServer->Close(); 
					delete pServer;  
				}  
				session.Close(); 
				return "0"; 
			} 
			return "0"; 
		} 
	} 
 
	MessengerModel::~MessengerModel() { 
	} 
	void MessengerModel::login(const string &ID, const string &password) { 
		this->my.ID=ID; 
		this->password=password; 
 
		status=CONNECTING; 
		fireMessengerUpdate(MessengerListener::CONNECTING); 
 
		// Removing message listener if it already listen. See disconnect(). 
		messageDispatcher->removeMessageListener(*this); 
		messageDispatcher->addMessageListener(*this); 
		messageDispatcher->connect("messenger.hotmail.com", 1863); 
		// wait for conneced() notification 
	} 
	void MessengerModel::startChatWith(const string &companionID) { 
		this->companionID=companionID; 
 
		// Sends XFR message 
		Message outMessage; 
		outMessage.setName("XFR"); 
		outMessage.addArgument(getTransactionID()); 
		outMessage.addArgument("SB"); 
 
		messageDispatcher->sendMessage(outMessage); 
		// wait for XFR SB reply 
	} 
 
	void MessengerModel::cancelLogin() { 
		disconnect(MessengerListener::CONNECTION_CLOSED); 
	} 
 
	void MessengerModel::logout() { 
		disconnect(MessengerListener::CONNECTION_CLOSED); 
	} 
 
	void MessengerModel::disconnect(MessengerListener::Event event, bool closed) { 
		// CAUTION: Can't remove listener when iterator traverses! 
		// For example, when this function called by messageArrived() called by CMessageDispatcher::OnReceive(), 
		// removing MessageListener modifies CMessageDispatcher::listeners' iterators and finally causes access violation 
		// by overiterated iterator. 
		// Removing a message listener should done in login(). 
 
		//messageDispatcher->removeMessageListener(*this); 
		if(!closed) { 
			messageDispatcher->close(); 
		} 
		users.erase(users.begin(), users.end()); 
		status=NOT_CONNECTED; 
		fireMessengerUpdate(event); 
	} 
 
	// Called when first connected. 
	// Send VER message. 
	void MessengerModel::connected() { 
		Message outMessage; 
 
		outMessage.setName("VER"); 
		outMessage.addArgument(getTransactionID()); 
		outMessage.addArgument("MSNP9"); 
		/*outMessage.addArgument("MSNP7"); 
		outMessage.addArgument("MSNP6"); 
		outMessage.addArgument("MSNP5"); 
		outMessage.addArgument("MSNP4");*/ 
		outMessage.addArgument("CVR0"); 
 
		messageDispatcher->sendMessage(outMessage); 
	} 
 
	void MessengerModel::failed() { 
		disconnect(MessengerListener::CONNECTION_FAILED, true); 
	} 
 
	void MessengerModel::setStatus(const string &status) { 
		Message outMessage; 
 
		outMessage.setName("CHG"); 
		outMessage.addArgument(getTransactionID()); 
		outMessage.addArgument(status); 
 
		messageDispatcher->sendMessage(outMessage); 
	} 
 
	void MessengerModel::setScreenName(const string &screenName) { 
		Message outMessage; 
 
		outMessage.setName("REA"); 
		outMessage.addArgument(getTransactionID()); 
		outMessage.addArgument(my.ID); 
		outMessage.addArgument(screenName); 
 
		messageDispatcher->sendMessage(outMessage); 
	} 
 
	void MessengerModel::blockUser(bool block, const string &userID) { 
		Message outMessage; 
		 
		outMessage.setName("REM"); 
		outMessage.addArgument(getTransactionID()); 
		if(block) { 
			outMessage.addArgument("AL"); 
		} else { 
			outMessage.addArgument("BL"); 
		} 
		outMessage.addArgument(userID); 
		messageDispatcher->sendMessage(outMessage); 
 
 
		Message addMessage; 
 
		addMessage.setName("ADD"); 
		addMessage.addArgument(getTransactionID()); 
		if(block) { 
			addMessage.addArgument("BL"); 
		} else { 
			addMessage.addArgument("AL"); 
		} 
		addMessage.addArgument(userID); 
		addMessage.addArgument("pseudo_screenName");	// It works! 
 
		messageDispatcher->sendMessage(addMessage); 
		// wait for add message 
	} 
 
	void MessengerModel::removeUser(const string &userID) { 
		vector::iterator userIterator=findUser(userID); 
		if(userIterator==users.end()) { 
			// Does noting 
			return; 
		} 
		User &user=(*userIterator); 
		if(!user.blocked) { 
			// Remove user from AL list first, if it unblocked. 
			blockUser(true, userID); 
		} 
 
		// Then remove it from FL list 
		Message flremMessage; 
 
		flremMessage.setName("REM"); 
		flremMessage.addArgument(getTransactionID()); 
		flremMessage.addArgument("FL"); 
		flremMessage.addArgument(user.ID); 
		flremMessage.addArgument(user.group); 
		messageDispatcher->sendMessage(flremMessage); 
		// Wait for REM reply 
	} 
 
	void MessengerModel::addUser(const string &userID) { 
		Message outMessage; 
		outMessage.setName("ADD"); 
		outMessage.addArgument(getTransactionID()); 
		outMessage.addArgument("FL"); 
		outMessage.addArgument(userID); 
		outMessage.addArgument(userID); 
		outMessage.addArgument(0); 
 
		messageDispatcher->sendMessage(outMessage); 
 
		// add user to model 
		addUser("FLN", userID, userID); 
 
		// Unblock the user. 
		blockUser(false, userID); 
	} 
 
 
	void MessengerModel::synchronizeUser() { 
		Message outMessage; 
		outMessage.setName("SYN"); 
		outMessage.addArgument(getTransactionID()); 
		outMessage.addArgument("0"); 
 
		messageDispatcher->sendMessage(outMessage); 
	} 
 
	void MessengerModel::messageArrived(const Message &inMessage) { 
		const string &name=inMessage.getName(); 
		Message outMessage; 
		 
		if(name=="VER") { 
			/*outMessage.setName("INF"); 
			outMessage.addArgument(getTransactionID()); 
			messageDispatcher->sendMessage(outMessage);*/ 
 
			outMessage.setName("CVR"); 
			outMessage.addArgument(getTransactionID()); 
			outMessage.addArgument("0x0409"); 
			outMessage.addArgument("win"); 
			outMessage.addArgument("4.10"); 
			outMessage.addArgument("i386"); 
			outMessage.addArgument("MSNMSGR"); 
			outMessage.addArgument("5.0.0544"); 
			outMessage.addArgument("MSMSGS"); 
			outMessage.addArgument(my.ID); 
			messageDispatcher->sendMessage(outMessage); 
		} 
		if(name=="CVR") { 
			outMessage.setName("USR"); 
			outMessage.addArgument(getTransactionID()); 
			outMessage.addArgument("TWN"); 
			outMessage.addArgument("I"); 
			outMessage.addArgument(my.ID); 
			messageDispatcher->sendMessage(outMessage); 
		} 
		// server information 
		if(name=="XFR") { 
			const string &serverType = inMessage.getArgument(1); 
			// connect with notification server 
			if(serverType=="NS") { 
				const string &ipWithPort=inMessage.getArgument(2); 
 
				typedef tokenizer > tokenizer; 
				char_separator sep(":"); 
				tokenizer tokens(ipWithPort, sep); 
				string notificationServer= *(tokens.begin()); 
				 
				messageDispatcher->close(); 
				messageDispatcher->connect(notificationServer, 1863); 
			} 
			// connect with switchboard server 
			if(serverType=="SB") { 
				ChatterModel *newChatter=NULL; 
				list::iterator chatter=findChatter(companionID); 
				if(chatter!=chatters.end()) { 
					newChatter = &(*chatter); 
				} else { 
					newChatter = &createChatterModel(); 
				} 
 
				newChatter->connect(*this, inMessage, companionID); 
			} 
		} 
		if(name=="USR") { 
			const string &type=inMessage.getArgument(1); 
 
			if(type=="TWN") { 
				string challenge=inMessage.getArgument(3); 
				CString challengeUTF8 = challenge.c_str(); 
				challengeUTF8.Replace("http://messenger.msn.com","http%3A%2F%2Fmessenger%2Emsn%2Ecom"); 
				challenge = challengeUTF8.GetBuffer(); 
				string clientTicket = GetClientTicket(my.ID,password,challenge); 
				if(clientTicket == "401") 
				{ 
					AfxMessageBox("username or password error!"); 
					return; 
				} 
				else if( clientTicket == "0" ) 
				{ 
					AfxMessageBox("unknown error occured!"); 
					return; 
				} 
				/*challenge.append(password);				 
				string digest; 
				md5Digest(digest, challenge); 
				outMessage.setName("USR"); 
				outMessage.addArgument(getTransactionID()); 
				outMessage.addArgument("MD5"); 
				outMessage.addArgument("S"); 
				outMessage.addArgument(digest); 
				messageDispatcher->sendMessage(outMessage);*/ 
				outMessage.setName("USR"); 
				outMessage.addArgument(getTransactionID()); 
				outMessage.addArgument("TWN"); 
				outMessage.addArgument("S"); 
				outMessage.addArgument(clientTicket); 
				messageDispatcher->sendMessage(outMessage); 
			} 
			// finally logged in 
			if(type=="OK") { 
				my.screenName=inMessage.getArgument(3); 
 
				status=CONNECTED; 
				fireMessengerUpdate(MessengerListener::CONNECTED); 
 
				synchronizeUser(); 
				setStatus("NLN"); 
			} 
		} 
		// user list 
		if(name=="LST") { 
			const string &type=inMessage.getArgument(1); 
			const string &iList=inMessage.getArgument(3); 
			const string &nList=inMessage.getArgument(4); 
			// argument 5 does not exists when nList=="0" 
			// const string &ID=inMessage.getArgument(5); 
 
			if(type=="FL" && nList!="0") { 
				const string &ID=inMessage.getArgument(5); 
				const string &screenName=inMessage.getArgument(6); 
				const string &group=inMessage.getArgument(7); 
 
				User &newUser=addUser("FLN", ID, screenName); 
				newUser.group=group; 
 
				// fires update signal when listing has completed. 
				if(iList==nList) { 
					fireUserUpdate(newUser, MessengerListener::UserEvent::ADDED); 
				} 
			} 
 
			// blocked users 
			if(type=="BL" && nList!="0") { 
				const string &ID=inMessage.getArgument(5); 
				const string &screenName=inMessage.getArgument(6); 
				vector::iterator i=findUser(ID); 
				if(i!=users.end()) { 
					User &blockedUser=(*i); 
					blockedUser.blocked=true; 
 
					// fires update signal when listing has completed. 
					if(iList==nList) { 
						fireUserUpdate(blockedUser, MessengerListener::UserEvent::BLOCKED); 
					} 
				} // end of if(user found) 
			} 
		} 
		// status changed 
		if(name=="CHG") { 
			my.status=inMessage.getArgument(1); 
 
			fireMessengerUpdate(MessengerListener::MY_STATUS_CHANGED); 
		} 
		// screen name changed 
		if(name=="REA") { 
			my.screenName=inMessage.getArgument(3); 
			fireMessengerUpdate(MessengerListener::MY_SCREEN_NAME_CHANGED); 
		} 
		// added to the list 
		if(name=="ADD") { 
 
			const string &listName=inMessage.getArgument(1); 
			const string &userID=inMessage.getArgument(3); 
			vector::iterator i=findUser(userID); 
			if(i!=users.end()) { 
				User &user=(*i); 
 
				if(listName=="AL") { 
					user.blocked=false; 
					fireUserUpdate(user, MessengerListener::UserEvent::UNBLOCKED); 
				} 
				else if(listName=="BL") { 
					user.blocked=true; 
					fireUserUpdate(user, MessengerListener::UserEvent::BLOCKED); 
				} 
			}  
			// user not exists in FL 
			else { 
				// Someone adds you 
				if(listName=="RL") { 
					User newUser; 
					newUser.ID=userID; 
					newUser.screenName=inMessage.getArgument(4); 
					fireUserUpdate(newUser, MessengerListener::UserEvent::ADDS_YOU); 
				} 
			} 
		} 
		// removed from the list 
		if(name=="REM") { 
			const string &listName=inMessage.getArgument(1); 
			if(listName=="FL") { 
				// remove the user from this->users 
				const string &userID=inMessage.getArgument(3); 
				vector::iterator userIterator=findUser(userID); 
				if(userIterator!=users.end()) { 
					User &user=(*userIterator); 
					users.erase(userIterator); 
					fireUserUpdate(user, MessengerListener::UserEvent::REMOVED); 
				} 
			} 
		} 
 
		// challenge 
		if(name=="CHL") { 
			string challenge=inMessage.getArgument(1); 
			challenge.append("Q1P7W2E4J9R8U3S5"); 
			string digest; 
			md5Digest(digest, challenge); 
			digest.insert(0, "\n"); 
 
			outMessage.setName("QRY"); 
			outMessage.addArgument(getTransactionID()); 
			outMessage.addArgument("msmsgs@msnmsgr.com"); 
			outMessage.addArgument("32"); 
			outMessage.addArgument(digest); 
			outMessage.setNewLine(false); 
			messageDispatcher->sendMessage(outMessage); 
		} 
		// user information income 
		if(name=="ILN") { 
			const string &status=inMessage.getArgument(1); 
			const string &ID=inMessage.getArgument(2); 
			const string &screenName=inMessage.getArgument(3); 
 
			User &newUser=addUser(status, ID, screenName); 
			fireUserUpdate(newUser, MessengerListener::UserEvent::LOGIN); 
		} 
		// user status changed 
		if(name=="NLN") { 
			const string &status=inMessage.getArgument(0); 
			const string &ID=inMessage.getArgument(1); 
			const string &screenName=inMessage.getArgument(2); 
 
			User &newUser=addUser(status, ID, screenName); 
			fireUserUpdate(newUser, MessengerListener::UserEvent::STATUS_CHANGED); 
		} 
 
		// user logged out 
		if(name=="FLN") { 
			const string &ID=inMessage.getArgument(0); 
 
			vector::iterator i=findUser(ID); 
			if(i!=users.end()) { 
				User &user=*i; 
				user.ID=ID; 
				user.status="FLN"; 
 
				fireUserUpdate(user, MessengerListener::UserEvent::LOGOUT); 
			} 
		} 
 
		// MIME Messages 
		if(name=="MSG") { 
			try { 
				const MIMEMessage &inMIME= dynamic_cast(inMessage); 
				MIMEMessageArrived(inMIME); 
			} catch(bad_cast) { 
				// do noting 
			} 
		} 
		// Invited to switchboard 
		if(name=="RNG") { 
			const string &companionID=inMessage.getArgument(4); 
			ChatterModel *newChatter=NULL; 
			list::iterator chatter=findChatter(companionID); 
			if(chatter!=chatters.end()) { 
				newChatter = &(*chatter); 
			} else { 
				newChatter = &createChatterModel(); 
			} 
 
			newChatter->connect(*this, inMessage); 
		} 
 
		// connection reset by peer 
		if(name=="OUT") { 
			const string &cause=inMessage.getArgument(0); 
			if(cause=="OTH") { 
				disconnect(MessengerListener::CONNECTION_CLOSED_BY_OTHERS); 
			} 
			if(cause=="SSD") { 
				disconnect(MessengerListener::CONNECTION_CLOSED_BY_SERVER); 
			} 
		} 
 
		// Errors 
		if(name=="911") { 
			disconnect(MessengerListener::AUTH_FAILED); 
		} 
	} 
 
	User &MessengerModel::addUser(const string &status, const string &ID, const string &screenName) { 
		vector::iterator i=findUser(ID); 
		User *newUser; 
		if(i!=users.end()) { 
			newUser=&(*i); 
			newUser->status=status; 
			newUser->ID=ID; 
			newUser->screenName=screenName; 
		} else { 
			// secure new object entry 
			User temporary; 
			users.push_back(temporary); 
			newUser=&users.at(users.size()-1); 
			// assign to new entry 
			newUser->status=status; 
			newUser->ID=ID; 
			newUser->screenName=screenName; 
		} 
		return *newUser; 
	} 
 
 
	ChatterModel &MessengerModel::createChatterModel() { 
		// TODO: reform this to dynamic allocation. 
		ChatterModel temp; 
		chatters.push_back(temp); 
		return *(chatters.rbegin()); 
	} 
 
	void MessengerModel::removeChatterModel(const ChatterModel &model) { 
		chatters.remove(model); 
		TRACE("chatter removed. size==%d\n", chatters.size()); 
	} 
 
	void MessengerModel::MIMEMessageArrived(const MIMEMessage &inMessage) { 
		Message outMessage; 
 
		if(inMessage.contentType.find("text/x-msmsgsprofile")!=string::npos) { 
			// TODO: process message 
		} 
	} 
 
 
	void MessengerModel::addMessengerListener(MessengerListener &listener) { 
		listeners.push_back(&listener); 
 
	} 
	void MessengerModel::removeMessengerListener(const MessengerListener &listener) { 
		list::iterator location=find(listeners.begin(), listeners.end(), &listener); 
		if(location!=listeners.end()) { 
			listeners.erase(location); 
		} 
	} 
	void MessengerModel::fireMessengerUpdate(MessengerListener::Event event) { 
		list::iterator i; 
		for( i=listeners.begin(); i!=listeners.end(); i++) { 
			(*i)->messengerUpdate(*this, event); 
		} 
	} 
	void MessengerModel::fireUserUpdate(const User &user, MessengerListener::UserEvent event) { 
		list::iterator i; 
		for( i=listeners.begin(); i!=listeners.end(); i++) { 
			(*i)->userUpdate(user, event); 
		} 
	} 
 
	void MessengerModel::fireChatterViewRequested(ChatterModel &model) { 
		list::iterator i; 
		for( i=listeners.begin(); i!=listeners.end(); i++) { 
			(*i)->chatterViewRequested(model); 
		} 
	} 
 
	namespace { 
		const string *idToFind; 
 
		bool findUserById(const User &user) { 
			if(user.ID==(*idToFind)) { 
				return true; 
			} 
			return false; 
		} 
	} 
 
	vector::iterator MessengerModel::findUser(const string &ID) { 
		idToFind=&ID; 
		return find_if(users.begin(), users.end(), findUserById); 
	} 
 
	namespace { 
		const string *chatterToFind; 
 
		bool findChatterByCompanion(const ChatterModel &chatter) { 
			if(chatter.getCompanion().ID==(*chatterToFind)) { 
				return true; 
			} 
			return false; 
		} 
	} 
 
	list::iterator MessengerModel::findChatter(const string &companionID) { 
		chatterToFind=&companionID; 
		return find_if(chatters.begin(), chatters.end(), findChatterByCompanion); 
	} 
 
}