2 qqnotifysocket.cpp - Notify Socket for the QQ Protocol
3 forked from msnnotifysocket.cpp
5 Copyright (c) 2006 by Hui Jin <blueangel.jin@gmail.com>
6 Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
7 Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
8 Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org>
9 Copyright (c) 2005 by Michaƫl Larouche <larouche@kde.org>
10 Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
12 Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
15 KMerlin (c) 2001 by Olaf Lueg <olueg@olsd.de>
17 *************************************************************************
19 * This program is free software; you can redistribute it and/or modify *
20 * it under the terms of the GNU General Public License as published by *
21 * the Free Software Foundation; either version 2 of the License, or *
22 * (at your option) any later version. *
24 *************************************************************************
28 #include <QHostAddress>
30 #include "kopetestatusmessage.h"
33 #include "qqnotifysocket.h"
34 #include "qqaccount.h"
36 QQNotifySocket::QQNotifySocket( QQAccount
*account
, const QString
&password
)
40 m_newstatus
= Kopete::OnlineStatus::Offline
;
41 Eva::ByteArray
pwd( password
.toAscii().data(), password
.size() );
42 m_passwordKey
= Eva::Packet::QQHash(pwd
);
43 pwd
.release(); // the data is handled in QT
44 m_loginMode
= Eva::NormalLogin
;
46 // FIXME: more error-checking.
47 m_qqId
= account
->accountId().toInt();
49 m_heartbeat
= new QTimer(this);
50 QObject::connect( m_heartbeat
, SIGNAL(timeout()), SLOT(heartbeat()) );
53 QQNotifySocket::~QQNotifySocket()
56 if( m_heartbeat
->isActive() )
63 void QQNotifySocket::doneConnect()
65 // setup the status first
66 QQSocket::doneConnect();
68 kDebug( 14140 ) << "Negotiating server protocol version";
70 sendPacket( Eva::login( m_qqId
, m_id
++, m_passwordKey
, m_token
, m_loginMode
) );
72 sendPacket( Eva::loginToken(m_qqId
, m_id
++) );
76 void QQNotifySocket::disconnect()
78 kDebug(14140) << "online status =" <<
79 onlineStatus() << endl
;
80 // FIXME: double check the logic, please.
81 if( m_disconnectReason
==Kopete::Account::Unknown
)
82 m_disconnectReason
=Kopete::Account::Manual
;
83 // sendGoodbye, shall we setup the status as well ?
84 if( onlineStatus() == Connected
)
87 // the socket is not connected yet, so I should force the signals
88 if ( onlineStatus() == Disconnected
|| onlineStatus() == Connecting
)
91 QQSocket::disconnect();
94 void QQNotifySocket::handleError( uint code
, uint id
)
98 // TODO: Add support for all of these!
102 QQSocket::handleError( code
, id
);
108 void QQNotifySocket::handleIncomingPacket( const QByteArray
& rawData
)
110 kDebug( 14140 ) << rawData
;
111 Eva::Packet
packet( rawData
.data(), rawData
.size() );
114 Eva::ByteArray
initKey((char*) Eva::Packet::getInitKey(), 16 );
117 kDebug( 14140 ) << "command = " << packet
.command();
118 switch( packet
.command() )
120 case Eva::Command::RequestLoginToken
:
121 text
= Eva::Packet::loginToken( packet
.body() );
124 case Eva::Command::Login
:
125 text
= Eva::Packet::decrypt( packet
.body(), m_passwordKey
);
126 if( text
.size() == 0 )
127 text
= Eva::Packet::decrypt( packet
.body(), initKey
);
131 text
= Eva::Packet::decrypt( packet
.body(), m_sessionKey
);
132 if ( text
.size() == 0 )
133 text
= Eva::Packet::decrypt( packet
.body(), m_passwordKey
);
136 kDebug( 14140 ) << "text = " << QByteArray( text
.c_str(), text
.size() );
139 switch( packet
.command() )
141 // FIXME: use table-driven pattern ?
142 case Eva::Command::Logout
:
143 case Eva::Command::Heartbeat
:
145 case Eva::Command::UpdateInfo
:
146 case Eva::Command::Search
:
147 case Eva::Command::UserInfo
:
149 // map std::map to QMap
150 std::map
<const char*, std::string
, Eva::ltstr
> dict
= Eva::Packet::contactDetail(text
);
151 QMap
<const char*, QByteArray
> qmap
;
153 QString id
= QString( dict
["qqId"].c_str() );
154 std::map
<const char*, std::string
, Eva::ltstr
>::const_iterator it
= dict
.begin();
156 for( ; it
!= dict
.end(); it
++ )
157 qmap
.insert( (*it
).first
, QByteArray((*it
).second
.c_str() ) );
159 emit
contactDetailReceived(id
, qmap
);
164 case Eva::Command::AddBuddy
:
165 case Eva::Command::RemoveBuddy
:
166 case Eva::Command::AuthInvite
:
168 case Eva::Command::ChangeStatus
:
169 if( Eva::Packet::replyCode(text
) == Eva::ChangeStatusOK
)
171 kDebug( 14140 ) << "ChangeStatus ok";
172 emit
statusChanged( m_newstatus
);
174 else // TODO: Debug me.
178 case Eva::Command::AckSysMsg
:
179 case Eva::Command::SendMsg
:
181 case Eva::Command::ReceiveMsg
:
183 Eva::MessageEnvelop
envelop(text
);
184 kDebug(14140) << "Received message from " << envelop
.sender
<< " to " << envelop
.receiver
<< " type=" << envelop
.type
;
185 kDebug(14140) << "seq = " << envelop
.sequence
<< " from " << envelop
.ip
<< ":" << envelop
.port
;
187 sendPacket( Eva::messageReply(m_qqId
, packet
.sequence(), m_sessionKey
, Eva::Packet::replyKey(text
) ));
188 Eva::ByteArray
body( text
.data() + sizeof(envelop
), text
.size() - sizeof(envelop
) );
191 // TODO: check whether this is a duplicated message
192 switch( envelop
.type
)
195 kDebug(14140) << "command 0x0010: " << QByteArray( body
.c_str(), body
.size() );
197 case Eva::RcvFromBuddy
:
199 Eva::MessageHeader
mh(body
);
200 kDebug(14140) << "message header:";
201 kDebug(14140) << "ver:" << mh
.version
<< " sender:" << mh
.sender
202 << " receiver:" << mh
.receiver
203 << " type:" << mh
.type
<< " seq:" << mh
.sequence
204 << " timestamp:" << mh
.timestamp
<< " avatar:" << mh
.avatar
207 if( mh
.receiver
!= m_qqId
)
209 kDebug(14140) << "receive other(" << mh
.receiver
<<")'s message";
213 // FIXME: replace the magic number!
214 // FIXME: the code stinks!
215 Eva::uchar
* p
= body
.data()+36;
216 bool hasFontStyle
= p
[3] != 0;
217 Eva::uchar replyType
= p
[8];
219 // clear compiler warnings
220 Q_UNUSED(hasFontStyle
);
223 Eva::ByteArray
msg(body
.size());
230 kDebug(14140) << "message received: " << msg
.data();
231 // FIXME: use a function to generate guid!
232 emit
messageReceived(mh
, msg
);
242 case Eva::Command::RemoveMe
:
245 case Eva::Command::RequestKey
:
247 char type
= text
.data()[0];
248 char reply
= text
.data()[1];
250 if( reply
== Eva::RequestKeyOK
)
252 // NOTE: the type of the key supports TransferKey only.
253 if( type
== Eva::TransferKey
)
255 m_transferKey
= Eva::Packet::transferKey( text
);
256 m_transferToken
= Eva::Packet::transferToken( text
);
257 kDebug( 14140 ) << "transferKey =" << QByteArray( m_transferKey
.c_str(), m_transferKey
.size());
258 kDebug( 14140 ) << "transferToken =" << QByteArray( m_transferToken
.c_str(), m_transferToken
.size());
265 case Eva::Command::GetCell
:
268 case Eva::Command::Login
:
269 switch( Eva::Packet::replyCode(text
) )
272 kDebug( 14140 ) << "Bingo! QQ:#" << m_qqId
<< " logged in!";
273 // show off some meta data :
274 m_sessionKey
= Eva::Packet::sessionKey(text
);
275 kDebug( 14140 ) << "sessionKey = " <<
276 QByteArray( m_sessionKey
.c_str(), m_sessionKey
.size() ) << endl
;
278 kDebug( 14140 ) << "remote IP: " << QHostAddress( Eva::Packet::remoteIP(text
) ).toString();
279 kDebug( 14140 ) << "remote port: " << Eva::Packet::remotePort(text
);
280 kDebug( 14140 ) << "local IP: " << QHostAddress( Eva::Packet::localIP(text
) ).toString();
281 kDebug( 14140 ) << "local port: " << Eva::Packet::localPort(text
);
282 kDebug( 14140 ) << "login time: " << Eva::Packet::loginTime(text
);
283 kDebug( 14140 ) << "last login from: " << QHostAddress( Eva::Packet::lastLoginFrom(text
) ).toString();
284 kDebug( 14140 ) << "last login time: " << Eva::Packet::lastLoginTime(text
);
286 // start the heartbeat
287 if( !m_heartbeat
->isActive() )
289 m_heartbeat
->setSingleShot(false);
290 m_heartbeat
->start(60000);
293 // FIXME: refactor me!
294 emit
newContactList();
295 // FIXME: We might login in as invisible as well.
296 m_newstatus
= Kopete::OnlineStatus::Online
;
297 sendPacket( Eva::statusUpdate( m_qqId
, m_id
++, m_sessionKey
, Eva::Online
) );
298 sendPacket( Eva::transferKey( m_qqId
, m_id
++, m_sessionKey
) );
300 // get the meta data for myself
301 contactDetail(m_qqId
);
303 // fetch the online contacts
304 sendListOnlineContacts();
310 case Eva::LoginRedirect
:
311 kDebug( 14140 ) << "Redirect to "
312 << QHostAddress(Eva::Packet::redirectedIP(text
)).toString()
313 << " : " << Eva::Packet::redirectedPort(text
) << endl
;
315 connect( QHostAddress( Eva::Packet::redirectedIP(text
) ).toString(), Eva::Packet::redirectedPort(text
) );
318 case Eva::LoginWrongPassword
:
319 kDebug( 14140 ) << "password is wrong. ";
322 case Eva::LoginMiscError
:
323 kDebug( 14140 ) << "unknown error. ";
327 kDebug( 14140 ) << "Bad, we are not supposed to be here !";
333 case Eva::Command::AllContacts
:
337 while( len < text.size() )
338 emit contactList( Eva::contactInfo( text.data(), len ) );
339 short pos = ntohs( Eva::type_cast<short> (text.data()) );
341 if( pos != Eva::ContactListEnd )
342 sendPacket( Eva::allContacts( m_qqId, m_id++, m_sessionKey, pos ) );
346 case Eva::Command::ContactsOnline
:
349 case Eva::Command::GetCell2
:
350 case Eva::Command::SIP
:
351 case Eva::Command::Test
:
353 case Eva::Command::GroupNames
:
357 case Eva::Command::UploadGroups
:
358 case Eva::Command::Memo
:
360 case Eva::Command::DownloadGroups
:
364 case Eva::Command::GetLevel
:
367 case Eva::Command::RequestLoginToken
:
369 kDebug( 14140 ) << "command = " << packet
.command() << ": token = " <<
370 QByteArray ( m_token
.c_str(), m_token
.size() ) << endl
;
371 sendPacket( Eva::login( m_qqId
, m_id
++, m_passwordKey
, m_token
, m_loginMode
) );
374 case Eva::Command::ExtraInfo
:
375 case Eva::Command::Signature
:
376 case Eva::Command::ReceiveSysMsg
:
378 case Eva::Command::ContactStausChanged
:
380 kDebug( 14140 ) << "contact status signal";
381 Eva::ContactStatus
cs(text
.data());
382 kDebug( 14140 ) << "contact status detail:";
383 kDebug( 14140 ) << "id = " << cs
.qqId
<< " status = " << cs
.status
;
384 emit
contactStatusChanged( cs
);
394 void QQNotifySocket::contactDetail(Eva::uint qqId
)
396 sendPacket( Eva::contactDetail( m_qqId
, m_id
++, m_sessionKey
, qqId
) );
399 void QQNotifySocket::sendTextMessage( const uint toId
, const QByteArray
& message
)
401 // Translate the message to Eva::ByteArray
402 // TODO: color and font
403 kDebug( 14140 ) << "Send the message: " << message
<< " from " << m_qqId
<< " to " << toId
;
404 // attach the ByteArray to QString:
405 // FIXME: Add an adapter to ByteArray
406 Eva::ByteArray
text( (char*)message
.data(), message
.size() );
409 Eva::ByteArray packet
= Eva::textMessage(m_qqId
, m_id
++, m_sessionKey
, toId
, m_transferKey
, text
);
410 QQSocket::sendPacket( QByteArray( packet
.c_str(), packet
.size()) );
414 void QQNotifySocket::heartbeat()
416 sendPacket( Eva::heartbeat( m_qqId
, m_id
++, m_sessionKey
));
419 void QQNotifySocket::sendListOnlineContacts(uint pos
)
421 sendPacket( Eva::onlineContacts( m_qqId
, m_id
++, m_sessionKey
, pos
) );
424 void QQNotifySocket::groupNames( const Eva::ByteArray
& text
)
427 std::list
< std::string
> l
= Eva::Packet::groupNames( text
);
428 for( std::list
<std::string
>::const_iterator it
= l
.begin(); it
!= l
.end(); it
++ )
429 ql
.append( QString( (*it
).c_str() ) );
432 emit
groupNames( ql
);
435 void QQNotifySocket::groupInfos( const Eva::ByteArray
& text
)
438 std::list
< Eva::GroupInfo
> gis
= Eva::Packet::groupInfos( text
);
439 // TODO: send it one by one.
440 for( std::list
< Eva::GroupInfo
>::const_iterator it
= gis
.begin();
441 it
!= gis
.end(); it
++ )
443 kDebug(14140) << "buddy: qqId = " << (*it
).qqId
<< " type = " << (*it
).type
444 << " groupId = " << (*it
).groupId
<< endl
;
445 emit
contactInGroup( (*it
).qqId
, (*it
).type
, (*it
).groupId
);
448 int next
= Eva::Packet::nextGroupId( text
);
450 sendDownloadGroups( next
);
453 void QQNotifySocket::doGetContactStatuses( const Eva::ByteArray
& text
)
456 Eva::uchar pos
= Eva::ContactListBegin
;
457 std::list
< Eva::ContactStatus
> css
= Eva::Packet::onlineContacts( text
, pos
);
458 for( std::list
< Eva::ContactStatus
>::const_iterator it
= css
.begin();
459 it
!= css
.end(); it
++ )
461 kDebug(14140) << "buddy: qqId = " << (*it
).qqId
<< " status = " << (*it
).status
;
462 emit
contactStatusChanged(*it
);
466 sendListOnlineContacts(pos
);
469 #include "qqnotifysocket.moc"
470 // vim: set noet ts=4 sts=4 sw=4: