/* Kopete Yahoo Protocol Handles logging into to the Yahoo service Copyright (c) 2004 Duncan Mac-Vicar P. Copyright (c) 2005-2006 André Duffeck Copyright 2009 Matt Rogers Kopete (c) 2002-2009 by the Kopete developers ************************************************************************* * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * ************************************************************************* */ #include "logintask.h" #include "transfer.h" #include "ymsgtransfer.h" #include "yahootypes.h" #include "client.h" #include #include #include #include #include extern "C" { #include "libyahoo.h" } LoginTask::LoginTask(Task* parent) : Task(parent) { kdDebug(YAHOO_RAW_DEBUG) ; mState = InitialState; } LoginTask::~LoginTask() { } bool LoginTask::take(Transfer* transfer) { /* Yahoo login task has various stages 1 .- Initial State 1.1 .- OnGo is called 1.2 .- SendVerify() - send a service verify ack 2.- SentVerify 2.1 - take(), get a useless transfer, sendAuth is called 3.- SentAuth 2.2 - take(), get a transfer with login and challenge string sendAuthResp is called. 2.3 - Need to decode and send a transfer back 4.- SentAuthResp */ if ( !forMe( transfer ) ) return false; YMSGTransfer *t = static_cast(transfer); if ( t->service() == Yahoo::ServicePing) { emit buddyListReady(); return true; } switch (mState) { case (InitialState): client()->notifyError( "Error in login procedure.", "take called while in initial state", Client::Debug ); return false; break; case (SentVerify): sendAuth( t ); return true; break; case (SentAuth): sendAuthResp( t ); return true; break; case (SentAuthResp): parseCookies( t ); handleAuthResp( t ); // Throw transfer to the next task as it contains further data return false; break; default: return false; break; } } bool LoginTask::forMe(const Transfer* transfer) const { const YMSGTransfer *t = 0L; t = dynamic_cast(transfer); if (!t) return false; if ( t->service() == Yahoo::ServicePing) return true; switch (mState) { case (InitialState): //there shouldn't be a incoming transfer for this task at this state return false; break; case (SentVerify): if ( t->service() == Yahoo::ServiceVerify ) return true; break; case (SentAuth): if ( t->service() == Yahoo::ServiceAuth ) return true; break; case (SentAuthResp ): if ( t->service() == Yahoo::ServiceList || t->service() == Yahoo::ServiceAuthResp ) return true; default: return false; break; } return false; } void LoginTask::onGo() { kdDebug(YAHOO_RAW_DEBUG) ; /* initial state, we have to send a ServiceVerify */ if (mState == InitialState) sendVerify(); else client()->notifyError( "Error in login procedure.", "onGo called while not in initial state", Client::Debug ); } void LoginTask::reset() { mState = InitialState; } void LoginTask::sendVerify() { /* send a ServiceVerify */ kdDebug(YAHOO_RAW_DEBUG) ; YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceVerify); send( t ); mState = SentVerify; } void LoginTask::sendAuth(YMSGTransfer* transfer) { kdDebug(YAHOO_RAW_DEBUG) ; // transfer is the verify ack transfer, no useful data in it. Q_UNUSED(transfer); /* got ServiceVerify ACK, send a ServiceAuth with username */ kdDebug(YAHOO_RAW_DEBUG) ; YMSGTransfer *t = new YMSGTransfer( Yahoo::ServiceAuth ); t->setParam( 1 , client()->userId().local8Bit() ); send(t); mState = SentAuth; } void LoginTask::sendAuthResp(YMSGTransfer* t) { kdDebug(YAHOO_RAW_DEBUG) ; TQString sn = t->firstParam( 1 ); TQString seed = t->firstParam( 94 ); m_challengeString = seed; TQString version_s = t->firstParam( 13 ); m_sessionID = t->id(); int version = version_s.toInt(); switch (version) { case 0: case 1: case 2: kdDebug(YAHOO_RAW_DEBUG) << "Using version 16 authorization" << endl; sendAuthSixteenStage1(sn, seed); break; default: kdDebug(YAHOO_RAW_DEBUG) << "Unknown authentication method used!" << "Attempting current authentication anyways" << endl; sendAuthSixteenStage1(sn, seed); break; } mState = SentAuthResp; emit haveSessionID( m_sessionID ); } void LoginTask::sendAuthSixteenStage1(const TQString& sn, const TQString& seed) { const TQString YahooTokenUrl = "https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login=%1&passwd=%2&chal=%3"; kdDebug(YAHOO_RAW_DEBUG) << "seed:" << seed << endl; m_stage1Data= TQString(); /* construct a URL from the seed and request tokens */ TQByteArray encodedUrl; TQString fullUrl = YahooTokenUrl.arg(sn, client()->password(), seed); KURL tokenUrl(fullUrl); TDEIO::Job* job = TDEIO::get(tokenUrl, true, false); connect(job, TQT_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), this, TQT_SLOT(handleAuthSixteenStage1Data(TDEIO::Job*, const TQByteArray&))); connect(job, TQT_SIGNAL(result(TDEIO::Job*)), this, TQT_SLOT(handleAuthSixteenStage1Result(TDEIO::Job*))); } void LoginTask::handleAuthSixteenStage1Data(TDEIO::Job* job, const TQByteArray& data) { kdDebug(YAHOO_RAW_DEBUG) << "data:" << data << endl; m_stage1Data.append(data); } void LoginTask::handleAuthSixteenStage1Result(TDEIO::Job* job) { int responseNumber = -1; TQString token; int error = job->error(); kdDebug(YAHOO_RAW_DEBUG) << "error:" << error << endl; if (error == 0) { TQStringList responses = TQStringList::split("\r\n", m_stage1Data); responseNumber = responses[0].toInt(); if (responses.count() >= 3) { token = responses[1]; token.remove("ymsgr="); kdDebug(YAHOO_RAW_DEBUG) << "response is:" << responseNumber << endl; kdDebug(YAHOO_RAW_DEBUG) << "token is:" << token << endl; } if (responseNumber != 0) { switch(responseNumber) { case -1: /* error in the received stream */ emit loginResponse(Yahoo::LoginSock, TQString()); kdDebug(YAHOO_RAW_DEBUG) << "unknown error logging in" << endl; break; case 1212: /* password incorrect */ emit loginResponse(Yahoo::LoginPasswd, TQString()); kdDebug(YAHOO_RAW_DEBUG) << "password incorrect" << endl; break; case 1213: /* security lock */ emit loginResponse(Yahoo::LoginLock, TQString()); break; case 1235: /* username does not exist */ emit loginResponse(Yahoo::LoginUname, TQString()); kdDebug(YAHOO_RAW_DEBUG) << "user does not exist" << endl; break; case 1214: case 1236: emit loginResponse(Yahoo::LoginVerify, TQString()); break; case 100: /* username or password missing */ /*FIXME handle this */ break; default: /* FIXME unknown error. handle it! */ break; } } else { /* start stage 2 here */ sendAuthSixteenStage2(token); } } } void LoginTask::sendAuthSixteenStage2(const TQString& token) { const TQString YahooLoginUrl = "https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=%1"; kdDebug(YAHOO_RAW_DEBUG) << "token:" << token << endl; m_stage2Data = TQString(); TQString fullUrl = YahooLoginUrl.arg(token); KURL loginUrl(fullUrl); TDEIO::Job* job = TDEIO::get(loginUrl, true, false); connect(job, TQT_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), this, TQT_SLOT(handleAuthSixteenStage2Data(TDEIO::Job*, const TQByteArray&))); connect(job, TQT_SIGNAL(result(TDEIO::Job*)), this, TQT_SLOT(handleAuthSixteenStage2Result(TDEIO::Job*))); } void LoginTask::handleAuthSixteenStage2Data(TDEIO::Job*, const TQByteArray& data) { kdDebug(YAHOO_RAW_DEBUG) << "data:" << data << endl; m_stage2Data.append(data); } void LoginTask::handleAuthSixteenStage2Result(TDEIO::Job* job) { TQString crumb; int responseNumber = -1; int error = job->error(); kdDebug(YAHOO_RAW_DEBUG) << "error:" << error << endl; if (error == 0) { TQStringList responses = TQStringList::split("\r\n", m_stage2Data); kdDebug(YAHOO_RAW_DEBUG) << responses << endl; responseNumber = responses[0].toInt(); if (responseNumber == 0) { crumb = responses[1]; crumb.remove("crumb="); m_yCookie = responses[2].remove(0,2); /* remove Y= */ m_tCookie = responses[3].remove(0,2); /* remove T= */ } if (responseNumber != 0) { switch(responseNumber) { case -1: emit loginResponse(Yahoo::LoginSock, TQString()); break; case 100: emit loginResponse(Yahoo::LoginSock, TQString()); break; default: /* try to login anyways */ break; } } else { TQString cryptString = crumb; cryptString.append(m_challengeString); sendAuthSixteenStage3(cryptString); } } } void LoginTask::sendAuthSixteenStage3(const TQString& cryptString) { kdDebug(YAHOO_RAW_DEBUG) << " with crypt string" << cryptString << endl; //TQByteArray cryptStringHash = TQCryptographicHash::hash( cryptString.toAscii(), // TQCryptographicHash::Md5 ); //cryptStringHash = cryptStringHash.toBase64(); TQString cryptStringHash = KMD5( cryptString.ascii() ).base64Digest(); cryptStringHash = cryptStringHash.replace('+', '.'); cryptStringHash = cryptStringHash.replace('/', '_'); cryptStringHash = cryptStringHash.replace('=', '-'); YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceAuthResp, m_stateOnConnect); t->setId( m_sessionID ); t->setParam( 1, client()->userId().local8Bit()); t->setParam( 0 , client()->userId().local8Bit()); t->setParam( 277, m_yCookie.local8Bit() ); t->setParam( 278, m_tCookie.local8Bit() ); t->setParam( 307, cryptStringHash.local8Bit() ); t->setParam( 244, 2097087 ); t->setParam( 2 , client()->userId().local8Bit()); t->setParam( 2, 1 ); // Both parameter 2s wind up in the packet t->setParam( 135, YMSG_PROGRAM_VERSION_STRING ); send(t); } void LoginTask::sendAuthResp_pre_0x0b(const TQString &/*sn*/, const TQString &/*seed*/) { kdDebug(YAHOO_RAW_DEBUG) ; } void LoginTask::handleAuthResp(YMSGTransfer *t) { kdDebug(YAHOO_RAW_DEBUG) ; switch( t->service() ) { case( Yahoo::ServiceList ): kdDebug(YAHOO_RAW_DEBUG) << "Emitting Signal" << endl; emit loginResponse( Yahoo::LoginOk, TQString() ); break; case( Yahoo::ServiceAuthResp ): kdDebug(YAHOO_RAW_DEBUG) << "Emitting Signal" << endl; emit loginResponse( t->firstParam( 66 ).toInt(), t->firstParam( 20 ) ); break; default: break; } mState = InitialState; } void LoginTask::setStateOnConnect( Yahoo::Status status ) { m_stateOnConnect = status; } void LoginTask::parseCookies( YMSGTransfer *t ) { kdDebug(YAHOO_RAW_DEBUG) ; for( int i = 0; i < t->paramCount( 59 ); ++i) { TQString cookie; cookie = t->nthParam( 59, i ); if( cookie.startsWith( "Y" ) ) { m_yCookie = getcookie( cookie.latin1() ); m_loginCookie = getlcookie( cookie.latin1() ); } else if( cookie.startsWith( "T" ) ) { m_tCookie = getcookie( cookie.latin1() ); } else if( cookie.startsWith( "C" ) ) { m_cCookie = getcookie( cookie.latin1() ); } } if( !m_yCookie.isEmpty() && !m_tCookie.isEmpty()) emit haveCookies(); } void LoginTask::setVerificationWord( const TQString &word ) { m_verificationWord = word; } const TQString& LoginTask::yCookie() { return m_yCookie; } const TQString& LoginTask::tCookie() { return m_tCookie; } const TQString& LoginTask::cCookie() { return m_cCookie; } const TQString& LoginTask::loginCookie() { return m_loginCookie; } #include "logintask.moc"