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