adding missing includes; trivial fix - comparison with string literal. by
[centerim.git] / libicq2000 / src / Client.cpp
blob3bb4d36710dbaf355102f2d5eb96743d7b1e00d6
1 /*
2 * libICQ2000 Client
4 * Copyright (C) 2001-2003 Barnaby Gray <barnaby@beedesign.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "TLV.h"
23 #include "UserInfoBlock.h"
25 #include "Client.h"
26 #include "buffer.h"
27 #include "socket.h"
28 #include "SNAC.h"
29 #include "DirectClient.h"
30 #include "FileTransferClient.h"
31 #include "DCCache.h"
32 #include "FTCache.h"
33 #include "MessageHandler.h"
34 #include "RequestIDCache.h"
35 #include "ICBMCookieCache.h"
36 #include "SMTPClient.h"
37 #include "Translator.h"
39 #include "sstream_fix.h"
41 #include <vector>
42 #include <iostream>
44 using std::string;
45 using std::ostringstream;
46 using std::endl;
47 using std::vector;
49 namespace ICQ2000
51 static const char* const Status_text[] =
52 { "Online",
53 "Away",
54 "N/A",
55 "Occupied",
56 "DND",
57 "Free for chat",
58 "Offline"
61 /**
62 * Constructor for creating the Client object. Use this when
63 * uin/password are unavailable at time of creation, they can
64 * always be set later.
66 Client::Client()
67 : m_self( new Contact(0) ), m_translator( new NULLTranslator() ),
68 m_message_handler( new MessageHandler(m_self, &m_contact_tree, m_translator) ),
69 m_serverSocket( new TCPSocket() ), m_listenServer( new TCPServer() ),
70 m_smtp( new SMTPClient( m_self, "localhost", 25 ) ),
71 m_dccache( new DCCache() ), m_reqidcache( new RequestIDCache() ),
72 m_cookiecache( new ICBMCookieCache() ),
73 m_recv( new Buffer() ), m_ftcache( new FTCache() )
75 Init();
78 /**
79 * Constructor for creating the Client object. Use this when the
80 * uin/password are available at time of creation, to save having
81 * to set them later.
83 * @param uin the owner's uin
84 * @param password the owner's password
86 Client::Client(const unsigned int uin, const string& password)
87 : m_self( new Contact(uin) ), m_password(password), m_translator( new NULLTranslator() ),
88 m_message_handler( new MessageHandler( m_self, &m_contact_tree, m_translator ) ),
89 m_serverSocket( new TCPSocket() ), m_listenServer( new TCPServer() ),
90 m_smtp( new SMTPClient( m_self, "localhost", 25 ) ),
91 m_dccache( new DCCache() ), m_reqidcache( new RequestIDCache() ),
92 m_cookiecache( new ICBMCookieCache() ),
93 m_recv( new Buffer() ), m_ftcache( new FTCache() )
95 Init();
98 /**
99 * Destructor for the Client object. This will free up all
100 * resources used by Client, including any Contact objects. It also
101 * automatically disconnects if you haven't done so already.
103 Client::~Client()
105 if (m_cookie_data)
106 delete [] m_cookie_data;
107 Disconnect(DisconnectedEvent::REQUESTED);
109 delete m_message_handler;
110 delete m_serverSocket;
111 delete m_listenServer;
112 delete m_smtp;
113 delete m_dccache;
114 delete m_ftcache;
115 delete m_reqidcache;
116 delete m_cookiecache;
117 delete m_recv;
118 delete m_translator;
121 void Client::Init()
123 m_authorizerHostname = "login.icq.com";
124 m_authorizerPort = 5190;
125 m_bosOverridePort = false;
127 m_in_dc = true;
128 m_out_dc = true;
130 m_state = NOT_CONNECTED;
132 m_cookie_data = NULL;
133 m_cookie_length = 0;
135 m_self->setStatus(STATUS_OFFLINE, false);
137 m_status_wanted = STATUS_OFFLINE;
138 m_invisible_wanted = false;
139 m_web_aware = false;
141 m_ext_ip = 0;
142 m_use_portrange = false;
143 m_lower_port = 0;
144 m_upper_port = 0;
146 m_use_typing_notif = false;
148 m_fetch_sbl = false;
150 m_cookiecache->setDefaultTimeout(30);
151 // 30 seconds is hopefully enough for even the slowest connections
152 m_cookiecache->expired.connect( this,&Client::ICBMCookieCache_expired_cb) ;
154 m_dccache->setDefaultTimeout(30);
155 // set timeout on direct connections to 30 seconds
156 // this will be increased once they are established
157 m_dccache->expired.connect( this,&Client::dccache_expired_cb) ;
159 m_ftcache->setDefaultTimeout(30);
160 // set timeout on direct connections to 30 seconds
161 // this will be increased once they are established
162 m_ftcache->expired.connect( this,&Client::ftcache_expired_cb) ;
164 m_reqidcache->expired.connect( this, &Client::reqidcache_expired_cb) ;
166 m_smtp->logger.connect( this, &Client::dc_log_cb) ;
167 m_smtp->messageack.connect( this, &Client::dc_messageack_cb) ;
168 m_smtp->socket.connect( this, &Client::dc_socket_cb) ;
170 /* contact list callbacks */
171 m_contact_tree.contactlist_signal.connect( this, &Client::contactlist_cb) ;
173 /* contact callbacks */
174 m_contact_tree.contact_status_change_signal.connect( contact_status_change_signal );
175 m_contact_tree.contact_userinfo_change_signal.connect( contact_userinfo_change_signal );
177 /* visible, invisible lists callbacks */
178 // m_visible_list.contactlist_signal.connect( this, &Client::visiblelist_cb) ;
179 // m_invisible_list.contactlist_signal.connect( this, &Client::invisiblelist_cb) ;
181 /* self contact callbacks */
182 m_self->status_change_signal.connect( self_contact_status_change_signal );
183 m_self->userinfo_change_signal.connect( self_contact_userinfo_change_signal );
185 /* message handler callbacks */
186 m_message_handler->messaged.connect( messaged );
187 m_message_handler->messageack.connect( messageack );
188 m_message_handler->want_auto_resp.connect( want_auto_resp );
189 m_message_handler->logger.connect( logger );
190 m_message_handler->filetransfer_incoming_signal.connect( filetransfer_incoming_signal );
191 m_message_handler->filetransfer_update_signal.connect( filetransfer_update_signal );
194 unsigned short Client::NextSeqNum() {
195 m_client_seq_num = ++m_client_seq_num & 0x7fff;
196 return m_client_seq_num;
199 unsigned int Client::NextRequestID() {
200 m_requestid = ++m_requestid & 0x7fffffff;
201 return m_requestid;
204 void Client::ConnectAuthorizer(State state) {
205 SignalLog(LogEvent::INFO, "Client connecting");
206 try {
208 * all sorts of SocketExceptions can be thrown
209 * here - for
210 * - sockets not being created
211 * - DNS lookup failures
215 ostringstream ostr;
216 ostr << "Looking up host name of authorizer: " << m_authorizerHostname.c_str();
217 SignalLog(LogEvent::INFO, ostr.str());
219 m_serverSocket->setRemoteHost(m_authorizerHostname.c_str());
220 m_serverSocket->setRemotePort(m_authorizerPort);
221 m_serverSocket->setBindHost(m_client_bind_host.c_str());
223 m_serverSocket->setBlocking(false);
225 SignalLog(LogEvent::INFO, "Establishing TCP connection to authorizer");
226 m_serverSocket->Connect();
227 } catch(SocketException e) {
228 // signal connection failure
229 ostringstream ostr;
230 ostr << "Failed to connect to Authorizer: " << e.what();
231 SignalLog(LogEvent::ERROR, ostr.str());
232 SignalDisconnect(DisconnectedEvent::FAILED_LOWLEVEL);
233 return;
236 SignalAddSocket( m_serverSocket->getSocketHandle(), SocketEvent::WRITE );
238 // randomize sequence number
239 srand(time(0));
240 m_client_seq_num = (unsigned short)(0x7fff*(rand()/(RAND_MAX+1.0)));
241 m_requestid = (unsigned int)(0x7fffffff*(rand()/(RAND_MAX+1.0)));
243 m_state = state;
246 void Client::DisconnectAuthorizer() {
247 SignalRemoveSocket( m_serverSocket->getSocketHandle() );
248 m_serverSocket->Disconnect();
249 m_state = NOT_CONNECTED;
252 void Client::ConnectBOS() {
253 try {
254 m_serverSocket->setRemoteHost(m_bosHostname.c_str());
255 m_serverSocket->setRemotePort(m_bosPort);
256 m_serverSocket->setBindHost(m_client_bind_host.c_str());
258 SignalLog(LogEvent::INFO, "Establishing TCP Connection to BOS Server");
259 m_serverSocket->setBlocking(false);
260 m_serverSocket->Connect();
261 } catch(SocketException e) {
262 ostringstream ostr;
263 ostr << "Failed to connect to BOS server: " << e.what();
264 SignalLog(LogEvent::ERROR, ostr.str());
265 SignalDisconnect(DisconnectedEvent::FAILED_LOWLEVEL);
266 return;
269 SignalAddSocket( m_serverSocket->getSocketHandle(), SocketEvent::WRITE );
271 m_state = BOS_AWAITING_CONN_ACK;
274 void Client::DisconnectBOS()
276 m_state = NOT_CONNECTED;
278 SignalRemoveSocket( m_serverSocket->getSocketHandle() );
279 m_serverSocket->Disconnect();
280 if (m_listenServer->isStarted()) {
281 SignalRemoveSocket( m_listenServer->getSocketHandle() );
282 m_listenServer->Disconnect();
284 DisconnectDirectConns();
287 void Client::DisconnectDirectConns()
289 m_dccache->removeAll();
290 m_ftcache->removeAll();
293 void Client::DisconnectDirectConn(int fd)
295 if (m_dccache->exists(fd))
297 m_dccache->remove(fd);
299 else if (m_ftcache->exists(fd))
301 FileTransferEvent *fev = (*m_ftcache)[fd]->getEvent();
302 m_ftcache->remove(fd);
303 SignalLog(LogEvent::WARN, "Disconnecting filetransfer");
305 if ((fev->getState() != FileTransferEvent::COMPLETE) &&
306 (fev->getState() != FileTransferEvent::CLOSE) &&
307 (fev->getState() != FileTransferEvent::ERROR))
309 fev->setState(FileTransferEvent::CANCELLED);
312 filetransfer_update_signal.emit(fev);
314 else if (m_smtp->getfd() == fd)
316 SignalRemoveSocket( m_smtp->getfd() );
318 else
320 SignalLog(LogEvent::WARN, "Trying to disconnect unknown filedescriptor");
324 // ------------------ Signal Dispatchers -----------------
325 void Client::SignalConnect() {
326 m_state = BOS_LOGGED_IN;
327 ConnectedEvent ev;
328 connected.emit(&ev);
331 void Client::SignalDisconnect(DisconnectedEvent::Reason r) {
332 DisconnectedEvent ev(r);
333 disconnected.emit(&ev);
335 if (m_self->getStatus() != STATUS_OFFLINE) {
336 m_self->setStatus(STATUS_OFFLINE, false);
339 // ensure all contacts return to Offline
340 ContactTree::iterator curr = m_contact_tree.begin();
341 while(curr != m_contact_tree.end())
343 ContactTree::Group::iterator gcurr = (*curr).begin();
345 while (gcurr != (*curr).end())
347 Status old_st = (*gcurr)->getStatus();
349 if ( old_st != STATUS_OFFLINE )
350 (*gcurr)->setStatus(STATUS_OFFLINE, false);
352 ++gcurr;
355 ++curr;
359 void Client::SignalAddSocket(int fd, SocketEvent::Mode m)
361 AddSocketHandleEvent ev( fd, m );
362 socket.emit(&ev);
365 void Client::SignalRemoveSocket(int fd)
367 RemoveSocketHandleEvent ev(fd);
368 socket.emit(&ev);
371 void Client::SignalMessage(MessageSNAC *snac)
373 ContactRef contact;
374 ICQSubType *st = snac->getICQSubType();
376 if (st == NULL) return;
378 if (st->getType() == MSG_Type_FT)
380 ostringstream ostr;
381 ostr << "File transfer through server";
382 SignalLog(LogEvent::INFO, ostr.str() );
383 ICBMCookie c = snac->getICBMCookie();
384 if ( m_cookiecache->exists( c ) )
386 MessageEvent *ev = (*m_cookiecache)[c];
387 ev->setDirect(false);
388 m_message_handler->handleIncomingACK( static_cast<FileTransferEvent*>(ev), static_cast<FTICQSubType*>(st) );
389 m_cookiecache->remove(c);
391 else
393 //FIX ME!!! old ACK could arrive
394 m_cookiecache->insert(snac->getICBMCookie(),
395 m_message_handler->handleIncomingFT(static_cast<FTICQSubType*>(st), false));
398 else
400 bool ack = m_message_handler->handleIncoming( st );
401 if (ack) SendAdvancedACK(snac);
406 void Client::SignalMessageACK(MessageACKSNAC *snac)
408 UINICQSubType *st = snac->getICQSubType();
410 if (st == NULL) return;
412 unsigned char type = st->getType();
413 switch(type)
415 case MSG_Type_Normal:
416 case MSG_Type_URL:
417 case MSG_Type_FT:
418 case MSG_Type_AutoReq_Away:
419 case MSG_Type_AutoReq_Occ:
420 case MSG_Type_AutoReq_NA:
421 case MSG_Type_AutoReq_DND:
422 case MSG_Type_AutoReq_FFC:
424 ICBMCookie c = snac->getICBMCookie();
425 if ( m_cookiecache->exists( c ) ) {
426 MessageEvent *ev = (*m_cookiecache)[c];
427 ev->setDirect(false);
428 m_message_handler->handleIncomingACK( ev, st );
429 m_cookiecache->remove(c);
430 } else {
431 SignalLog(LogEvent::WARN, "Received ACK for unknown message");
435 break;
437 default:
438 SignalLog(LogEvent::WARN, "Received ACK for unknown message type");
444 void Client::SignalMessageOfflineUser(MessageOfflineUserSNAC *snac)
447 * Mmm.. it'd be nice to use this as an ack for messages but it's
448 * not consistently sent for all messages through the server
449 * doesn't seem to be sent for normal (non-advanced) messages to
450 * online users.
452 ICBMCookie c = snac->getICBMCookie();
454 if ( m_cookiecache->exists( c ) )
456 /* indicate sending through server */
457 MessageEvent *ev = (*m_cookiecache)[c];
458 if (ev->getType() == MessageEvent::FileTransfer)
459 SignalLog( LogEvent::INFO, "FileTransfer request received by server ACK");
461 ev->setFinished(false);
462 ev->setDelivered(false);
463 ev->setDirect(false);
464 messageack.emit(ev);
467 else
469 SignalLog(LogEvent::WARN, "Received Offline ACK for unknown message");
473 void Client::dc_messageack_cb(MessageEvent *ev)
476 if (ev->getType() == MessageEvent::FileTransfer)
478 FileTransferEvent *fev = static_cast<FileTransferEvent*>(ev);
479 fev->setFinished(true);
480 fev->setState(FileTransferEvent::TIMEOUT);
481 filetransfer_update_signal.emit(fev);
483 else
485 messageack.emit(ev);
487 if (!ev->isFinished())
489 ev->getContact()->setDirect(false);
490 // attempt to deliver via server instead
491 SendViaServer(ev);
496 void Client::ftc_messageack_cb(MessageEvent *ev)
498 if (ev->getType() == MessageEvent::FileTransfer)
500 FileTransferEvent *fev = static_cast<FileTransferEvent*>(ev);
501 filetransfer_update_signal.emit(fev);
505 void Client::SignalSrvResponse(SrvResponseSNAC *snac)
507 if (snac->getType() == SrvResponseSNAC::OfflineMessagesComplete)
509 /* We are now meant to ACK this to say
510 * the we have got the offline messages
511 * and the server can dispose of storing
512 * them
514 SendOfflineMessagesACK();
517 else if (snac->getType() == SrvResponseSNAC::OfflineMessage)
519 // wow.. this is so much simpler now :-)
520 m_message_handler->handleIncoming(snac->getICQSubType(), snac->getTime());
523 else if (snac->getType() == SrvResponseSNAC::SMS_Error)
525 // mmm
527 else if (snac->getType() == SrvResponseSNAC::SMS_Response)
529 unsigned int reqid = snac->RequestID();
531 if ( m_reqidcache->exists( reqid ) )
533 RequestIDCacheValue *v = (*m_reqidcache)[ reqid ];
535 if ( v->getType() == RequestIDCacheValue::SMSMessage )
537 SMSEventCacheValue *uv = static_cast<SMSEventCacheValue*>(v);
538 SMSMessageEvent *ev = uv->getEvent();
540 if (snac->deliverable())
542 ev->setFinished(true);
543 ev->setDelivered(true);
544 ev->setDirect(false);
545 messageack.emit(ev);
546 m_reqidcache->remove( reqid );
548 else if (snac->smtp_deliverable())
550 // todo - konst have volunteered :-)
551 // yeah I did.. <konst> ;)
553 ev->setSMTPFrom(snac->getSMTPFrom());
554 ev->setSMTPTo(snac->getSMTPTo());
555 ev->setSMTPSubject(snac->getSMTPSubject());
557 m_smtp->SendEvent(ev);
560 else
562 if (snac->getErrorParam() != "DUPLEX RESPONSE")
564 // ignore DUPLEX RESPONSE since I always get that
565 ev->setFinished(true);
566 ev->setDelivered(false);
567 ev->setDirect(false);
568 ev->setDeliveryFailureReason(MessageEvent::Failed);
569 messageack.emit(ev);
570 m_reqidcache->remove( reqid );
575 else
577 throw ParseException("Request ID cached value is not for an SMS Message");
580 else
582 throw ParseException("Received an SMS response for unknown request id");
586 else if (snac->getType() == SrvResponseSNAC::SimpleUserInfo)
588 if ( m_reqidcache->exists( snac->RequestID() ) )
590 RequestIDCacheValue *v = (*m_reqidcache)[ snac->RequestID() ];
592 if ( v->getType() == RequestIDCacheValue::Search )
594 SearchCacheValue *sv = static_cast<SearchCacheValue*>(v);
596 SearchResultEvent *ev = sv->getEvent();
597 if (snac->isEmptyContact())
599 ev->setLastContactAdded( NULL );
601 else
603 ContactRef c = new Contact( snac->getUIN() );
604 c->setAlias(snac->getAlias());
605 c->setFirstName(snac->getFirstName());
606 c->setLastName(snac->getLastName());
607 c->setEmail(snac->getEmail());
608 c->setStatus(snac->getStatus(), false);
609 c->setAuthReq(snac->getAuthReq());
610 ContactList& cl = ev->getContactList();
611 ev->setLastContactAdded( cl.add(c) );
613 if (snac->isLastInSearch())
614 ev->setNumberMoreResults( snac->getNumberMoreResults() );
618 if (snac->isLastInSearch()) ev->setFinished(true);
620 search_result.emit(ev);
622 if (ev->isFinished())
624 delete ev;
625 m_reqidcache->remove( snac->RequestID() );
629 else
631 SignalLog(LogEvent::WARN, "Request ID cached value is not for a Search request");
635 else
637 if ( m_contact_tree.exists( snac->getUIN() ) )
639 // update Contact
640 ContactRef c = m_contact_tree[ snac->getUIN() ];
641 c->setAlias( snac->getAlias() );
642 c->setEmail( snac->getEmail() );
643 c->setFirstName( snac->getFirstName() );
644 c->setLastName( snac->getLastName() );
645 c->setAuthReq(snac->getAuthReq());
650 else if (snac->getType() == SrvResponseSNAC::SearchSimpleUserInfo)
652 if ( m_reqidcache->exists( snac->RequestID() ) )
654 RequestIDCacheValue *v = (*m_reqidcache)[ snac->RequestID() ];
656 if ( v->getType() == RequestIDCacheValue::Search )
658 SearchCacheValue *sv = static_cast<SearchCacheValue*>(v);
660 SearchResultEvent *ev = sv->getEvent();
661 if (snac->isEmptyContact())
663 ev->setLastContactAdded( NULL );
665 else
667 ContactRef c = new Contact( snac->getUIN() );
668 c->setAlias(snac->getAlias());
669 c->setFirstName(snac->getFirstName());
670 c->setLastName(snac->getLastName());
671 c->setEmail(snac->getEmail());
672 c->setStatus(snac->getStatus(), false);
673 c->setAuthReq(snac->getAuthReq());
674 ContactList& cl = ev->getContactList();
675 ev->setLastContactAdded( cl.add(c) );
677 if (snac->isLastInSearch())
678 ev->setNumberMoreResults( snac->getNumberMoreResults() );
682 if (snac->isLastInSearch()) ev->setFinished(true);
684 search_result.emit(ev);
686 if (ev->isFinished())
688 delete ev;
689 m_reqidcache->remove( snac->RequestID() );
693 else
695 SignalLog(LogEvent::WARN, "Request ID cached value is not for a Search request");
699 else
701 SignalLog(LogEvent::WARN, "Received a Search Result for unknown request id");
705 else if (snac->getType() == SrvResponseSNAC::RMainHomeInfo)
709 ContactRef c = getUserInfoCacheContact( snac->RequestID() );
710 ICQ2000::Contact::MainHomeInfo& imh = snac->getMainHomeInfo();
711 ICQ2000::Contact::MainHomeInfo omh;
712 omh.alias = m_translator->server_to_client( imh.alias, ENCODING_CONTACT_LOCALE, c );
713 omh.firstname = m_translator->server_to_client( imh.firstname, ENCODING_CONTACT_LOCALE, c );
714 omh.lastname = m_translator->server_to_client( imh.lastname, ENCODING_CONTACT_LOCALE, c );
715 omh.email = m_translator->server_to_client( imh.email, ENCODING_CONTACT_LOCALE, c );
716 omh.city = m_translator->server_to_client( imh.city, ENCODING_CONTACT_LOCALE, c );
717 omh.state = m_translator->server_to_client( imh.state, ENCODING_CONTACT_LOCALE, c );
718 omh.phone = m_translator->server_to_client( imh.phone, ENCODING_CONTACT_LOCALE, c );
719 omh.fax = m_translator->server_to_client( imh.fax, ENCODING_CONTACT_LOCALE, c );
720 omh.street = m_translator->server_to_client( imh.street, ENCODING_CONTACT_LOCALE, c );
721 omh.zip = m_translator->server_to_client( imh.zip, ENCODING_CONTACT_LOCALE, c );
722 omh.setMobileNo( imh.getMobileNo() );
723 omh.country = imh.country;
724 omh.timezone = imh.timezone;
725 c->setMainHomeInfo( omh );
727 catch(ParseException e)
729 SignalLog(LogEvent::WARN, e.what());
733 else if (snac->getType() == SrvResponseSNAC::RHomepageInfo)
737 ContactRef c = getUserInfoCacheContact( snac->RequestID() );
738 m_translator->server_to_client_inplace( snac->getHomepageInfo().homepage, ENCODING_CONTACT_LOCALE, c );
739 c->setHomepageInfo( snac->getHomepageInfo() );
741 catch(ParseException e)
743 SignalLog(LogEvent::WARN, e.what());
747 else if (snac->getType() == SrvResponseSNAC::RWorkInfo)
751 ContactRef c = getUserInfoCacheContact( snac->RequestID() );
753 ICQ2000::Contact::WorkInfo& imh = snac->getWorkInfo();
754 ICQ2000::Contact::WorkInfo omh;
755 omh.city = m_translator->server_to_client( imh.city, ENCODING_CONTACT_LOCALE, c );
756 omh.state = m_translator->server_to_client( imh.state, ENCODING_CONTACT_LOCALE, c );
757 omh.street = m_translator->server_to_client( imh.street, ENCODING_CONTACT_LOCALE, c );
758 omh.zip = m_translator->server_to_client( imh.zip, ENCODING_CONTACT_LOCALE, c );
759 omh.company_name = m_translator->server_to_client( imh.company_name, ENCODING_CONTACT_LOCALE, c );
760 omh.company_dept = m_translator->server_to_client( imh.company_dept, ENCODING_CONTACT_LOCALE, c );
761 omh.company_position = m_translator->server_to_client( imh.company_position, ENCODING_CONTACT_LOCALE, c );
762 omh.company_web = m_translator->server_to_client( imh.company_web, ENCODING_CONTACT_LOCALE, c );
764 c->setWorkInfo( omh );
766 catch(ParseException e)
768 SignalLog(LogEvent::WARN, e.what());
772 else if (snac->getType() == SrvResponseSNAC::RBackgroundInfo)
776 ContactRef c = getUserInfoCacheContact( snac->RequestID() );
778 for (Contact::BackgroundInfo::SchoolList::iterator iter = snac->getBackgroundInfo().schools.begin();
779 iter != snac->getBackgroundInfo().schools.end();
780 ++iter )
781 m_translator->server_to_client_inplace( iter->second, ENCODING_CONTACT_LOCALE, c );
783 c->setBackgroundInfo( snac->getBackgroundInfo() );
785 catch(ParseException e)
787 SignalLog(LogEvent::WARN, e.what());
791 else if (snac->getType() == SrvResponseSNAC::RInterestInfo)
795 ContactRef c = getUserInfoCacheContact( snac->RequestID() );
797 for (Contact::PersonalInterestInfo::InterestList::iterator iter = snac->getPersonalInterestInfo().interests.begin();
798 iter != snac->getPersonalInterestInfo().interests.end();
799 ++iter )
800 m_translator->server_to_client_inplace( iter->second, ENCODING_CONTACT_LOCALE, c );
802 c->setInterestInfo( snac->getPersonalInterestInfo() );
804 catch(ParseException e)
806 SignalLog(LogEvent::WARN, e.what());
810 else if (snac->getType() == SrvResponseSNAC::REmailInfo)
814 ContactRef c = getUserInfoCacheContact( snac->RequestID() );
816 for (Contact::EmailInfo::EmailList::iterator iter = snac->getEmailInfo().emails.begin();
817 iter != snac->getEmailInfo().emails.end();
818 ++iter )
819 m_translator->server_to_client_inplace( *iter, ENCODING_CONTACT_LOCALE, c );
821 c->setEmailInfo( snac->getEmailInfo() );
822 c->userinfo_change_emit();
824 catch(ParseException e)
826 SignalLog(LogEvent::WARN, e.what());
830 else if (snac->getType() == SrvResponseSNAC::RAboutInfo)
834 ContactRef c = getUserInfoCacheContact( snac->RequestID() );
835 c->setAboutInfo( m_translator->server_to_client( snac->getAboutInfo(), ENCODING_CONTACT_LOCALE, c ) );
837 catch(ParseException e)
839 SignalLog(LogEvent::WARN, e.what());
843 else if (snac->getType() == SrvResponseSNAC::RandomChatFound)
845 if ( m_reqidcache->exists( snac->RequestID() ) )
847 RequestIDCacheValue *v = (*m_reqidcache)[ snac->RequestID() ];
849 if ( v->getType() == RequestIDCacheValue::Search )
851 SearchCacheValue *sv = static_cast<SearchCacheValue*>(v);
853 SearchResultEvent *ev = sv->getEvent();
855 ContactRef c = new Contact( snac->getUIN() );
856 ContactList& cl = ev->getContactList();
857 ev->setLastContactAdded( cl.add(c) );
858 ev->setFinished(true);
860 search_result.emit(ev);
862 delete ev;
863 m_reqidcache->remove( snac->RequestID() );
865 } else {
866 SignalLog(LogEvent::WARN, "Request ID cached value is not for a Search request");
873 void Client::mergeSBL(ContactTree& tree)
875 SBLReceivedEvent ev(tree);
876 sbl_received.emit(&ev);
878 ContactTree::iterator curr = tree.begin();
879 while (curr != tree.end())
881 ContactTree::Group::iterator gcurr = (*curr).begin();
883 while (gcurr != (*curr).end())
885 ++gcurr;
887 ++curr;
892 ContactRef Client::getUserInfoCacheContact(unsigned int reqid)
894 if ( m_reqidcache->exists( reqid ) )
896 RequestIDCacheValue *v = (*m_reqidcache)[ reqid ];
898 if ( v->getType() == RequestIDCacheValue::UserInfo )
900 UserInfoCacheValue *uv = static_cast<UserInfoCacheValue*>(v);
901 return uv->getContact();
903 else
905 throw ParseException("Request ID cached value is not for a User Info request");
909 else
911 throw ParseException("Received a UserInfo response for unknown request id");
916 void Client::SignalUINResponse(UINResponseSNAC *snac)
918 unsigned int uin = snac->getUIN();
919 NewUINEvent e(uin);
920 newuin.emit(&e);
923 void Client::SignalUINRequestError()
925 NewUINEvent e(0,false);
926 newuin.emit(&e);
929 void Client::SignalRateInfoChange(RateInfoChangeSNAC *snac)
931 RateInfoChangeEvent e(snac->getCode(), snac->getRateClass(),
932 snac->getWindowSize(), snac->getClear(),
933 snac->getAlert(), snac->getLimit(),
934 snac->getDisconnect(), snac->getCurrentAvg(),
935 snac->getMaxAvg());
936 rate.emit(&e);
939 void Client::SignalLog(LogEvent::LogType type, const string& msg)
941 LogEvent ev(type,msg);
942 logger.emit(&ev);
945 void Client::ICBMCookieCache_expired_cb(MessageEvent *ev)
947 if (ev->getType() == MessageEvent::FileTransfer)
949 FileTransferEvent *fev = static_cast<FileTransferEvent*>(ev);
950 fev->setState(FileTransferEvent::TIMEOUT);
951 filetransfer_update_signal.emit(fev);
952 return;
955 SignalLog(LogEvent::WARN, "Message timeout without receiving ACK, sending offline");
957 SendViaServerNormal(ev);
958 /* downgrade Contact's capabilities, so we don't
959 attempt to send it as advanced again */
960 ev->getContact()->set_capabilities(Capabilities());
963 void Client::reqidcache_expired_cb(RequestIDCacheValue* v)
965 if ( v->getType() == RequestIDCacheValue::Search )
967 SearchCacheValue *sv = static_cast<SearchCacheValue*>(v);
969 SearchResultEvent *ev = sv->getEvent();
970 ev->setLastContactAdded( NULL );
971 ev->setExpired(true);
972 ev->setFinished(true);
973 search_result.emit(ev);
974 delete ev;
980 void Client::dccache_expired_cb(DirectClient *dc)
982 SignalLog(LogEvent::WARN, "Direct connection timeout reached");
985 void Client::ftcache_expired_cb(FileTransferClient *ftc)
987 SignalLog(LogEvent::WARN, "FileTransfer connection timeout reached");
990 void Client::dc_connected_cb(SocketClient *dc)
992 m_dccache->setTimeout(dc->getfd(), 600);
993 // once we are properly connected a direct
994 // connection will only timeout after 10 mins
997 void Client::dc_log_cb(LogEvent *ev)
999 logger.emit(ev);
1002 void Client::dc_socket_cb(SocketEvent *ev)
1004 socket.emit(ev);
1007 void Client::SignalUserOnline(BuddyOnlineSNAC *snac)
1009 const UserInfoBlock& userinfo = snac->getUserInfo();
1011 if (m_contact_tree.exists(userinfo.getUIN()))
1013 ContactRef c = m_contact_tree[userinfo.getUIN()];
1014 Status old_st = c->getStatus();
1016 // Birthday Flag set?
1017 if (userinfo.getBirthday()) c->setBirthday(true);
1019 c->setDirect(true); // reset flags when a user goes online
1020 c->setStatus( Contact::MapICQStatusToStatus(userinfo.getStatus()),
1021 Contact::MapICQStatusToInvisible(userinfo.getStatus()) );
1023 if ( userinfo.getExtIP() != 0 ) c->setExtIP( userinfo.getExtIP() );
1024 if ( userinfo.getLanIP() != 0 ) c->setLanIP( userinfo.getLanIP() );
1025 if ( userinfo.getLanPort() != 0 ) c->setLanPort( userinfo.getLanPort() );
1026 if ( userinfo.getTCPVersion() != 0 ) c->setTCPVersion( userinfo.getTCPVersion() );
1027 if ( userinfo.getDCCookie() != 0 ) c->setDCCookie( userinfo.getDCCookie() );
1029 c->set_signon_time( userinfo.getSignonDate() );
1030 if (userinfo.contains_capabilities())
1031 c->set_capabilities( userinfo.get_capabilities() );
1033 ostringstream ostr;
1034 ostr << "Received Buddy Online for "
1035 << c->getAlias()
1036 << " (" << c->getUIN() << ") " << Status_text[old_st]
1037 << "->" << Status_text[ c->getStatus() ] << " from server";
1038 SignalLog(LogEvent::INFO, ostr.str() );
1040 } else {
1041 ostringstream ostr;
1042 ostr << "Received Status change for user not on contact list: " << userinfo.getUIN();
1043 SignalLog(LogEvent::WARN, ostr.str());
1047 void Client::SignalUserOffline(BuddyOfflineSNAC *snac) {
1048 const UserInfoBlock& userinfo = snac->getUserInfo();
1049 if (m_contact_tree.exists(userinfo.getUIN())) {
1050 ContactRef c = m_contact_tree[userinfo.getUIN()];
1051 c->setStatus(STATUS_OFFLINE, false);
1053 ostringstream ostr;
1054 ostr << "Received Buddy Offline for "
1055 << c->getAlias()
1056 << " (" << c->getUIN() << ") from server";
1057 SignalLog(LogEvent::INFO, ostr.str() );
1058 } else {
1059 ostringstream ostr;
1060 ostr << "Received Status change for user not on contact list: " << userinfo.getUIN();
1061 SignalLog(LogEvent::WARN, ostr.str());
1065 // ------------------ Outgoing packets -------------------
1067 Buffer::marker FLAPHeader(Buffer& b, unsigned char channel, unsigned short seq)
1069 b.setBigEndian();
1070 b << (unsigned char) 42;
1071 b << channel;
1072 b << seq;
1073 Buffer::marker mk = b.getAutoSizeShortMarker();
1074 return mk;
1077 void FLAPFooter(Buffer& b, Buffer::marker& mk)
1079 b.setAutoSizeMarker(mk);
1083 void Client::FLAPwrapSNAC(Buffer& b, const OutSNAC& snac)
1085 Buffer::marker mk = FLAPHeader(b, 0x02, NextSeqNum());
1086 b << snac;
1087 FLAPFooter(b,mk);
1090 void Client::FLAPwrapSNACandSend(const OutSNAC& snac)
1092 Buffer b;
1093 FLAPwrapSNAC(b, snac);
1094 Send(b);
1097 void Client::SendAuthReq() {
1098 Buffer b;
1099 Buffer::marker mk = FLAPHeader(b, 0x01, NextSeqNum());
1101 b << (unsigned int)0x00000001;
1103 b << ScreenNameTLV(m_self->getStringUIN())
1104 << PasswordTLV(m_password)
1105 << ClientProfileTLV("ICQ Inc. - Product of ICQ (TM).2000b.4.63.1.3279.85")
1106 << ClientTypeTLV(266)
1107 << ClientVersionMajorTLV(4)
1108 << ClientVersionMinorTLV(63)
1109 << ClientICQNumberTLV(1)
1110 << ClientBuildMajorTLV(3279)
1111 << ClientBuildMinorTLV(85)
1112 << LanguageTLV("en")
1113 << CountryCodeTLV("us");
1115 FLAPFooter(b,mk);
1116 SignalLog(LogEvent::INFO, "Sending Authorisation Request");
1117 Send(b);
1120 void Client::SendNewUINReq()
1122 Buffer b;
1123 Buffer::marker mk;
1125 mk = FLAPHeader(b, 0x01, NextSeqNum());
1126 b << (unsigned int)0x00000001;
1127 FLAPFooter(b,mk);
1128 Send(b);
1130 SignalLog(LogEvent::INFO, "Sending New UIN Request");
1131 FLAPwrapSNACandSend( UINRequestSNAC(m_password) );
1134 void Client::SendCookie() {
1135 Buffer b;
1136 Buffer::marker mk = FLAPHeader(b,0x01,NextSeqNum());
1138 b << (unsigned int)0x00000001;
1140 b << CookieTLV(m_cookie_data, m_cookie_length);
1142 FLAPFooter(b,mk);
1143 SignalLog(LogEvent::INFO, "Sending Login Cookie");
1144 Send(b);
1147 void Client::SendCapabilities() {
1148 SignalLog(LogEvent::INFO, "Sending Capabilities");
1149 FLAPwrapSNACandSend( CapabilitiesSNAC() );
1152 void Client::SendSetUserInfo() {
1153 SignalLog(LogEvent::INFO, "Sending Set User Info");
1154 FLAPwrapSNACandSend( SetUserInfoSNAC() );
1157 void Client::SendRateInfoRequest() {
1158 SignalLog(LogEvent::INFO, "Sending Rate Info Request");
1159 FLAPwrapSNACandSend( RequestRateInfoSNAC() );
1162 void Client::SendRateInfoAck() {
1163 SignalLog(LogEvent::INFO, "Sending Rate Info Ack");
1164 FLAPwrapSNACandSend( RateInfoAckSNAC() );
1167 void Client::SendPersonalInfoRequest() {
1168 SignalLog(LogEvent::INFO, "Sending Personal Info Request");
1169 FLAPwrapSNACandSend( PersonalInfoRequestSNAC() );
1172 void Client::SendAddICBMParameter() {
1173 SignalLog(LogEvent::INFO, "Sending Add ICBM Parameter");
1174 FLAPwrapSNACandSend( MsgAddICBMParameterSNAC(m_use_typing_notif) );
1177 void Client::SendLogin() {
1178 Buffer b;
1180 // startup listening server at this point, so we
1181 // know the listening port and ip
1182 if (m_in_dc) {
1183 m_listenServer->setBindHost(m_client_bind_host.c_str());
1184 if (m_use_portrange) {
1185 m_listenServer->StartServer(m_lower_port, m_upper_port);
1186 } else {
1187 m_listenServer->StartServer();
1189 SignalAddSocket( m_listenServer->getSocketHandle(), SocketEvent::READ );
1190 ostringstream ostr;
1191 ostr << "Server listening on " << IPtoString( m_serverSocket->getLocalIP() ) << ":" << m_listenServer->getPort();
1192 SignalLog(LogEvent::INFO, ostr.str());
1193 } else {
1194 SignalLog(LogEvent::INFO, "Not starting listening server, incoming Direct connections disabled");
1197 if (!m_contact_tree.empty())
1198 FLAPwrapSNAC(b, AddBuddySNAC(m_contact_tree) );
1199 /* hack - for the moment still send older style buddy list */
1201 if (m_invisible_wanted)
1202 FLAPwrapSNAC(b, AddVisibleSNAC(m_visible_list) );
1204 SetStatusSNAC sss(Contact::MapStatusToICQStatus(m_status_wanted, m_invisible_wanted), m_web_aware);
1206 sss.setSendExtra(true);
1207 sss.setIP( m_serverSocket->getLocalIP() );
1208 sss.setPort( (m_in_dc ? m_listenServer->getPort() : 0) );
1209 FLAPwrapSNAC( b, sss );
1211 if (!m_invisible_wanted)
1212 FLAPwrapSNAC(b, AddInvisibleSNAC(m_invisible_list) );
1214 FLAPwrapSNAC( b, ClientReadySNAC() );
1216 FLAPwrapSNAC( b, SrvRequestOfflineSNAC(m_self->getUIN()) );
1218 SignalLog(LogEvent::INFO, "Sending Status, Client Ready and Offline Messages Request");
1219 Send(b);
1221 SignalConnect();
1222 m_last_server_ping = time(NULL);
1225 void Client::SendRequestSBL()
1227 Buffer b;
1229 FLAPwrapSNAC( b, SBLRequestRightsSNAC() );
1230 FLAPwrapSNAC( b, SBLRequestListSNAC() );
1232 SignalLog(LogEvent::INFO, "Sending Request Server-based list");
1233 Send(b);
1236 void Client::SendSBLReceivedACK()
1238 FLAPwrapSNACandSend( SBLListACKSNAC() );
1241 void Client::SendOfflineMessagesACK()
1243 SignalLog(LogEvent::INFO, "Sending Offline Messages ACK");
1244 FLAPwrapSNACandSend( SrvAckOfflineSNAC(m_self->getUIN()) );
1247 void Client::SendAdvancedACK(MessageSNAC *snac)
1249 ICQSubType *st = snac->getICQSubType();
1250 if (st == NULL || dynamic_cast<UINICQSubType*>(st) == NULL ) return;
1251 UINICQSubType *ust = dynamic_cast<UINICQSubType*>(snac->grabICQSubType());
1253 SignalLog(LogEvent::INFO, "Sending Advanced Message ACK");
1254 FLAPwrapSNACandSend( MessageACKSNAC( snac->getICBMCookie(), ust ) );
1257 void Client::Send(Buffer& b) {
1258 try {
1259 ostringstream ostr;
1260 ostr << "Sending packet to Server" << endl << b;
1261 SignalLog(LogEvent::PACKET, ostr.str());
1262 m_serverSocket->Send(b);
1263 } catch(SocketException e) {
1264 ostringstream ostr;
1265 ostr << "Failed to send: " << e.what();
1266 SignalLog(LogEvent::ERROR, ostr.str());
1267 Disconnect(DisconnectedEvent::FAILED_LOWLEVEL);
1271 // ------------------ Incoming packets -------------------
1273 void Client::RecvFromServer() {
1275 try {
1276 while (m_serverSocket->connected()) {
1277 if (!m_serverSocket->Recv(*m_recv)) break;
1278 Parse();
1280 } catch(SocketException e) {
1281 ostringstream ostr;
1282 ostr << "Failed on recv: " << e.what();
1283 SignalLog(LogEvent::ERROR, ostr.str());
1284 Disconnect(DisconnectedEvent::FAILED_LOWLEVEL);
1288 void Client::Parse() {
1290 // -- FLAP header --
1292 unsigned char start_byte, channel;
1293 unsigned short seq_num, data_len;
1295 // process FLAP(s) in packet
1297 if (m_recv->empty()) return;
1299 while (!m_recv->empty()) {
1300 m_recv->setPos(0);
1302 *m_recv >> start_byte;
1303 if (start_byte != 42) {
1304 m_recv->clear();
1305 SignalLog(LogEvent::WARN, "Invalid Start Byte on FLAP");
1306 return;
1309 /* if we don't have at least six bytes we don't have enough
1310 * info to determine if we have the whole of the FLAP
1312 if (m_recv->remains() < 5) return;
1314 *m_recv >> channel;
1315 *m_recv >> seq_num; // check sequence number - todo
1317 *m_recv >> data_len;
1318 if (m_recv->remains() < data_len) return; // waiting for more of the FLAP
1320 /* Copy into another Buffer which is passed
1321 * onto the separate parse code that way
1322 * multiple FLAPs in one packet are split up
1324 Buffer sb;
1325 m_recv->chopOffBuffer( sb, data_len+6 );
1328 ostringstream ostr;
1329 ostr << "Received packet from Server" << endl << sb;
1330 SignalLog(LogEvent::PACKET, ostr.str());
1333 sb.advance(6);
1335 // -- FLAP body --
1337 ostringstream ostr;
1339 switch(channel) {
1340 case 1:
1341 ParseCh1(sb,seq_num);
1342 break;
1343 case 2:
1344 ParseCh2(sb,seq_num);
1345 break;
1346 case 3:
1347 ParseCh3(sb,seq_num);
1348 break;
1349 case 4:
1350 ParseCh4(sb,seq_num);
1351 break;
1352 default:
1353 ostr << "FLAP on unrecognised channel 0x" << std::hex << (int)channel;
1354 SignalLog(LogEvent::WARN, ostr.str());
1355 break;
1358 if (sb.beforeEnd()) {
1359 /* we assert that parsing code eats uses all data
1360 * in the FLAP - seems useful to know when they aren't
1361 * as it probably means they are faulty
1363 ostringstream ostr;
1364 ostr << "Buffer pointer not at end after parsing FLAP was: 0x" << std::hex << sb.pos()
1365 << " should be: 0x" << sb.size();
1366 SignalLog(LogEvent::WARN, ostr.str());
1373 void Client::ParseCh1(Buffer& b, unsigned short seq_num) {
1375 if (b.remains() == 4 && (m_state == AUTH_AWAITING_CONN_ACK ||
1376 m_state == UIN_AWAITING_CONN_ACK)) {
1378 // Connection Acknowledge - first packet from server on connection
1379 unsigned int unknown;
1380 b >> unknown; // always 0x0001
1382 if (m_state == AUTH_AWAITING_CONN_ACK) {
1383 SendAuthReq();
1384 SignalLog(LogEvent::INFO, "Connection Acknowledge from server");
1385 m_state = AUTH_AWAITING_AUTH_REPLY;
1386 } else if (m_state == UIN_AWAITING_CONN_ACK) {
1387 SendNewUINReq();
1388 SignalLog(LogEvent::INFO, "Connection Acknowledge from server");
1389 m_state = UIN_AWAITING_UIN_REPLY;
1392 } else if (b.remains() == 4 && m_state == BOS_AWAITING_CONN_ACK) {
1394 SignalLog(LogEvent::INFO, "Connection Acknowledge from server");
1396 // Connection Ack, send the cookie
1397 unsigned int unknown;
1398 b >> unknown; // always 0x0001
1400 SendCookie();
1401 m_state = BOS_AWAITING_LOGIN_REPLY;
1403 } else {
1404 SignalLog(LogEvent::WARN, "Unknown packet received on channel 0x01");
1409 void Client::ParseCh2(Buffer& b, unsigned short seq_num)
1411 InSNAC *snac;
1414 snac = ParseSNAC(b);
1416 catch(ParseException e)
1418 ostringstream ostr;
1419 ostr << "Problem parsing SNAC: " << e.what();
1420 SignalLog(LogEvent::WARN, ostr.str());
1421 return;
1424 switch(snac->Family())
1427 case SNAC_FAM_GEN:
1428 switch(snac->Subtype())
1430 case SNAC_GEN_ServerReady:
1431 SignalLog(LogEvent::INFO, "Received Server Ready from server");
1432 SendCapabilities();
1433 break;
1434 case SNAC_GEN_RateInfo:
1435 SignalLog(LogEvent::INFO, "Received Rate Information from server");
1436 SendRateInfoAck();
1437 SendPersonalInfoRequest();
1438 SendAddICBMParameter();
1439 SendSetUserInfo();
1440 if (m_fetch_sbl) SendRequestSBL();
1441 SendLogin();
1442 break;
1443 case SNAC_GEN_CapAck:
1444 SignalLog(LogEvent::INFO, "Received Capabilities Ack from server");
1445 SendRateInfoRequest();
1446 break;
1447 case SNAC_GEN_UserInfo:
1448 SignalLog(LogEvent::INFO, "Received User Info from server");
1449 HandleUserInfoSNAC(static_cast<UserInfoSNAC*>(snac));
1450 break;
1451 case SNAC_GEN_MOTD:
1452 SignalLog(LogEvent::INFO, "Received MOTD from server");
1453 break;
1454 case SNAC_GEN_RateInfoChange:
1455 SignalLog(LogEvent::INFO, "Received Rate Info Change from server");
1456 SignalRateInfoChange(static_cast<RateInfoChangeSNAC*>(snac));
1457 break;
1459 break;
1461 case SNAC_FAM_BUD:
1462 switch(snac->Subtype())
1464 case SNAC_BUD_Online:
1465 SignalUserOnline(static_cast<BuddyOnlineSNAC*>(snac));
1466 break;
1467 case SNAC_BUD_Offline:
1468 SignalUserOffline(static_cast<BuddyOfflineSNAC*>(snac));
1469 break;
1471 break;
1473 case SNAC_FAM_MSG:
1474 switch(snac->Subtype())
1476 case SNAC_MSG_Message:
1477 SignalLog(LogEvent::INFO, "Received Message from server");
1478 SignalMessage(static_cast<MessageSNAC*>(snac));
1479 break;
1480 case SNAC_MSG_MessageACK:
1481 SignalLog(LogEvent::INFO, "Received Message ACK from server");
1482 SignalMessageACK(static_cast<MessageACKSNAC*>(snac));
1483 break;
1484 case SNAC_MSG_OfflineUser:
1485 SignalLog(LogEvent::INFO, "Received Message to Offline User from server");
1486 SignalMessageOfflineUser(static_cast<MessageOfflineUserSNAC*>(snac));
1487 break;
1489 break;
1491 case SNAC_FAM_SRV:
1492 switch(snac->Subtype())
1494 case SNAC_SRV_Response:
1495 SignalLog(LogEvent::INFO, "Received Server Response from server");
1496 SignalSrvResponse(static_cast<SrvResponseSNAC*>(snac));
1497 break;
1499 break;
1501 case SNAC_FAM_UIN:
1502 switch(snac->Subtype())
1504 case SNAC_UIN_Response:
1505 SignalLog(LogEvent::INFO, "Received UIN Response from server");
1506 SignalUINResponse(static_cast<UINResponseSNAC*>(snac));
1507 break;
1508 case SNAC_UIN_RequestError:
1509 SignalLog(LogEvent::ERROR, "Received UIN Request Error from server");
1510 SignalUINRequestError();
1511 break;
1513 break;
1515 case SNAC_FAM_SBL:
1516 switch(snac->Subtype())
1518 case SNAC_SBL_Rights_Reply:
1519 SignalLog(LogEvent::INFO, "Server-based contact list rights granted\n");
1520 break;
1522 case SNAC_SBL_List_From_Server:
1524 SignalLog(LogEvent::INFO, "Received server-based list from server\n");
1525 SBLListSNAC *sbs = static_cast<SBLListSNAC*>(snac);
1526 mergeSBL( sbs->getContactTree() );
1527 // SendSBLReceivedACK();
1528 break;
1531 case SNAC_SBL_Edit_ACK:
1533 vector<SBLEditACKSNAC::Result> r = static_cast<SBLEditACKSNAC*>(snac)->getResults();
1534 vector<SBLEditACKSNAC::Result>::iterator ir;
1536 for(ir = r.begin(); ir != r.end(); ++ir)
1537 switch( *ir ) {
1538 case SBLEditACKSNAC::Success:
1539 SignalLog(LogEvent::INFO, "Server-based contact list modification succeeded\n");
1540 //updresults.push_back(ServerBasedContactEvent::Success);
1541 break;
1542 case SBLEditACKSNAC::Failed:
1543 SignalLog(LogEvent::INFO, "Server-based contact list modification failed\n");
1544 //updresults.push_back(ServerBasedContactEvent::Failed);
1545 break;
1546 case SBLEditACKSNAC::AuthRequired:
1547 SignalLog(LogEvent::INFO, "Failed, authentification is required to add a user\n");
1548 //updresults.push_back(ServerBasedContactEvent::AuthRequired);
1549 break;
1550 case SBLEditACKSNAC::AlreadyExists:
1551 SignalLog(LogEvent::INFO, "Already exists on the server-based contact list\n");
1552 break;
1556 ** haven't yet decided the best way to recode this for groups and contacts **
1558 vector<ServerBasedContactEvent::UploadResult> updresults;
1559 vector<ServerBasedContactEvent::UploadResult>::iterator iur;
1561 if(!updresults.empty()) {
1562 if( m_reqidcache->exists( snac->RequestID() ) ) {
1563 RequestIDCacheValue *v = (*m_reqidcache)[ snac->RequestID() ];
1565 if ( v->getType() == RequestIDCacheValue::ServerBasedContact ) {
1566 ServerBasedContactCacheValue *sv = static_cast<ServerBasedContactCacheValue*>(v);
1567 ServerBasedContactEvent *ev = sv->getEvent();
1569 ContactList &cl = ev->getContactList();
1570 ContactList::iterator ic = cl.begin();
1572 iur = updresults.begin();
1574 while(iur != updresults.end() && ic != cl.end()) {
1575 if(*iur == ServerBasedContactEvent::Success
1576 || *iur == ServerBasedContactEvent::AuthRequired) {
1577 (*ic)->setServerBased(ev->getType() == ServerBasedContactEvent::Upload);
1579 ContactRef ct = getContact((*ic)->getUIN());
1580 if(ct.get()) {
1581 ct->setServerBased(ev->getType() == ServerBasedContactEvent::Upload);
1585 ++iur;
1586 ++ic;
1589 ev->setUploadResults(updresults);
1590 server_based_contact_list.emit(ev);
1592 delete ev;
1593 m_reqidcache->remove( snac->RequestID() );
1594 } else {
1595 SignalLog(LogEvent::WARN, "Request ID cached value is not for a server-based contacts upload request");
1597 } else {
1598 SignalLog(LogEvent::WARN, "SBL Edit acknowledge from server for a non-existent upload");
1602 break;
1604 } // switch(SBL Subtype)
1605 break;
1607 } // switch(Family)
1609 if (dynamic_cast<RawSNAC*>(snac))
1611 ostringstream ostr;
1612 ostr << "Unknown SNAC packet received - Family: 0x" << std::hex << snac->Family()
1613 << " Subtype: 0x" << snac->Subtype();
1614 SignalLog(LogEvent::WARN, ostr.str());
1617 delete snac;
1621 void Client::ParseCh3(Buffer& b, unsigned short seq_num)
1623 SignalLog(LogEvent::INFO, "Received packet on channel 0x03");
1626 void Client::ParseCh4(Buffer& b, unsigned short seq_num)
1628 if (m_state == AUTH_AWAITING_AUTH_REPLY || m_state == UIN_AWAITING_UIN_REPLY)
1630 // An Authorisation Reply / Error
1631 TLVList tlvlist;
1632 tlvlist.Parse(b, TLV_ParseMode_Channel04, (short unsigned int)-1);
1634 if (tlvlist.exists(TLV_Cookie) && tlvlist.exists(TLV_Redirect)) {
1636 RedirectTLV *r = static_cast<RedirectTLV*>(tlvlist[TLV_Redirect]);
1637 ostringstream ostr;
1638 ostr << "Redirected to: " << r->getHost();
1639 if (r->getPort() != 0) ostr << " port: " << std::dec << r->getPort();
1640 SignalLog(LogEvent::INFO, ostr.str());
1642 m_bosHostname = r->getHost();
1643 if (!m_bosOverridePort) {
1644 if (r->getPort() != 0) m_bosPort = r->getPort();
1645 else m_bosPort = m_authorizerPort;
1648 // Got our cookie - yum yum :-)
1649 CookieTLV *t = static_cast<CookieTLV*>(tlvlist[TLV_Cookie]);
1650 m_cookie_length = t->Length();
1652 if (m_cookie_data) delete [] m_cookie_data;
1653 m_cookie_data = new unsigned char[m_cookie_length];
1655 memcpy(m_cookie_data, t->Value(), m_cookie_length);
1657 SignalLog(LogEvent::INFO, "Authorisation accepted");
1659 DisconnectAuthorizer();
1660 ConnectBOS();
1662 } else {
1663 // Problemo
1664 DisconnectedEvent::Reason st;
1666 if (tlvlist.exists(TLV_ErrorCode)) {
1667 ErrorCodeTLV *t = static_cast<ErrorCodeTLV*>(tlvlist[TLV_ErrorCode]);
1668 ostringstream ostr;
1669 ostr << "Error logging in Error Code: " << t->Value();
1670 SignalLog(LogEvent::ERROR, ostr.str());
1671 switch(t->Value()) {
1672 case 0x01:
1673 st = DisconnectedEvent::FAILED_BADUSERNAME;
1674 break;
1675 case 0x02:
1676 st = DisconnectedEvent::FAILED_TURBOING;
1677 break;
1678 case 0x03:
1679 st = DisconnectedEvent::FAILED_BADPASSWORD;
1680 break;
1681 case 0x05:
1682 st = DisconnectedEvent::FAILED_MISMATCH_PASSWD;
1683 break;
1684 case 0x18:
1685 st = DisconnectedEvent::FAILED_TURBOING;
1686 break;
1687 default:
1688 st = DisconnectedEvent::FAILED_UNKNOWN;
1690 } else if (m_state == AUTH_AWAITING_AUTH_REPLY) {
1691 SignalLog(LogEvent::ERROR, "Error logging in, no error code given(?!)");
1692 st = DisconnectedEvent::FAILED_UNKNOWN;
1693 } else {
1694 st = DisconnectedEvent::REQUESTED;
1696 DisconnectAuthorizer();
1697 SignalDisconnect(st); // signal client (error)
1700 } else {
1702 TLVList tlvlist;
1703 tlvlist.Parse(b, TLV_ParseMode_Channel04, (short unsigned int)-1);
1705 DisconnectedEvent::Reason st;
1707 if (tlvlist.exists(TLV_DisconnectReason)) {
1708 DisconnectReasonTLV *t = static_cast<DisconnectReasonTLV*>(tlvlist[TLV_DisconnectReason]);
1709 switch(t->Value()) {
1710 case 0x0001:
1711 st = DisconnectedEvent::FAILED_DUALLOGIN;
1712 break;
1713 default:
1714 st = DisconnectedEvent::FAILED_UNKNOWN;
1717 } else {
1718 SignalLog(LogEvent::WARN, "Unknown packet received on channel 4, disconnecting");
1719 st = DisconnectedEvent::FAILED_UNKNOWN;
1721 DisconnectBOS();
1722 SignalDisconnect(st); // signal client (error)
1727 // -----------------------------------------------------
1730 * Perform any regular time dependant tasks.
1732 * Poll must be called regularly (at least every 60 seconds) but I
1733 * recommended 5 seconds, so timeouts work with good granularity.
1734 * It is not related to the socket callback and socket listening.
1735 * The client must call this Poll fairly regularly, to ensure that
1736 * timeouts on message sending works correctly, and that the server
1737 * is pinged once every 60 seconds.
1739 void Client::Poll()
1741 time_t now = time(NULL);
1742 if (now > m_last_server_ping + 60)
1744 PingServer();
1745 m_last_server_ping = now;
1749 m_reqidcache->clearoutPoll();
1750 m_cookiecache->clearoutPoll();
1751 m_dccache->clearoutPoll();
1752 m_dccache->clearoutMessagesPoll();
1753 m_ftcache->clearoutMessagesPoll();
1754 m_smtp->clearoutMessagesPoll();
1758 * Callback from client to tell library the socket is ready. The
1759 * client must call this method when select says that the file
1760 * descriptor is available for mode (read, write or exception).
1761 * The client will know what socket descriptors to select on, and
1762 * with what mode from the SocketEvent's it receives. It is worth
1763 * looking at the shell.cpp example in the examples directory and
1764 * fully understanding how it works with select.
1766 * @param fd the socket descriptor
1767 * @param m the mode that the socket is available on
1769 void Client::socket_cb(int fd, SocketEvent::Mode m) {
1771 if ( fd == m_serverSocket->getSocketHandle() ) {
1773 * File descriptor is the socket we have open to server
1777 if (m & SocketEvent::WRITE) SignalLog(LogEvent::INFO, "socket_cb for write");
1778 if (m & SocketEvent::READ) SignalLog(LogEvent::INFO, "socket_cb for read");
1779 if (m & SocketEvent::EXCEPTION) SignalLog(LogEvent::INFO, "socket_cb for exception");
1781 if (m_serverSocket->getState() == TCPSocket::NOT_CONNECTED) SignalLog(LogEvent::INFO, "server socket in state NOT_CONNECTED");
1782 if (m_serverSocket->getState() == TCPSocket::NONBLOCKING_CONNECT) SignalLog(LogEvent::INFO, "server socket in state NONBLOCKING_CONNECT");
1783 if (m_serverSocket->getState() == TCPSocket::CONNECTED) SignalLog(LogEvent::INFO, "server socket in state CONNECTED");
1786 if (m_serverSocket->getState() == TCPSocket::NONBLOCKING_CONNECT
1787 && (m & SocketEvent::WRITE)) {
1788 // the non-blocking connect has completed (good/bad)
1790 try {
1791 m_serverSocket->FinishNonBlockingConnect();
1792 } catch(SocketException e) {
1793 // signal connection failure
1794 ostringstream ostr;
1795 ostr << "Failed on non-blocking connect: " << e.what();
1796 SignalLog(LogEvent::ERROR, ostr.str());
1797 Disconnect(DisconnectedEvent::FAILED_LOWLEVEL);
1798 return;
1801 SignalLog(LogEvent::INFO, "Connection established");
1803 SignalRemoveSocket(fd);
1804 // no longer select on write
1806 SignalAddSocket(fd, SocketEvent::READ);
1807 // select on read now
1809 } else if (m_serverSocket->getState() == TCPSocket::CONNECTED && (m & SocketEvent::READ)) {
1810 RecvFromServer();
1811 } else {
1812 SignalLog(LogEvent::ERROR, "Server socket in inconsistent state!");
1813 Disconnect(DisconnectedEvent::FAILED_LOWLEVEL);
1816 } else if ( m_in_dc && fd == m_listenServer->getSocketHandle() ) {
1818 * File descriptor is the listening socket - someone is connected in
1821 TCPSocket *sock = m_listenServer->Accept();
1822 DirectClient *dc = new DirectClient(m_self, sock, m_message_handler, &m_contact_tree,
1823 m_ext_ip, m_listenServer->getPort() );
1824 (*m_dccache)[ sock->getSocketHandle() ] = dc;
1825 dc->logger.connect( this, &Client::dc_log_cb );
1826 dc->messageack.connect( this, &Client::dc_messageack_cb );
1827 dc->connected.connect( this, &Client::dc_connected_cb );
1828 dc->socket.connect( this, &Client::dc_socket_cb );
1829 SignalAddSocket( sock->getSocketHandle(), SocketEvent::READ );
1831 } else if ( m_ftcache->exists_listenfd( fd ) ) {
1833 FileTransferClient *ftc = (*m_ftcache)[fd];
1834 if ( (ftc->getSocket()==0) )
1836 ftc->setSocket();
1837 ftc->logger.connect( this, &Client::dc_log_cb );
1838 ftc->messageack.connect( this, &Client::ftc_messageack_cb );
1839 ftc->socket.connect( this, &Client::dc_socket_cb );
1840 m_ftcache->remove_and_not_delete(fd);
1841 (*m_ftcache)[ftc->getfd()] = ftc;
1842 SignalAddSocket( ftc->getfd(), SocketEvent::READ );
1843 SignalLog(LogEvent::INFO, "Incoming filetransfer connection");
1844 } else {
1845 SignalLog(LogEvent::INFO, "Incoming filetransfer connection on already open connection.");
1847 } else {
1849 * File descriptor is a direct connection we have open to someone
1853 SocketClient *dc;
1854 if (m_dccache->exists(fd))
1856 dc = (*m_dccache)[fd];
1858 else if(m_smtp->getfd() == fd)
1860 dc = m_smtp;
1862 else if (m_ftcache->exists(fd))
1864 dc = (*m_ftcache)[fd];
1866 else
1868 SignalLog(LogEvent::ERROR, "Problem: Unassociated socket");
1869 return;
1872 TCPSocket *sock = dc->getSocket();
1873 if (sock->getState() == TCPSocket::NONBLOCKING_CONNECT
1874 && (m & SocketEvent::WRITE)) {
1875 // the non-blocking connect has completed (good/bad)
1877 try {
1878 sock->FinishNonBlockingConnect();
1879 } catch(SocketException e) {
1880 // signal connection failure
1881 ostringstream ostr;
1882 ostr << "Failed on non-blocking connect for direct connection: " << e.what();
1883 SignalLog(LogEvent::ERROR, ostr.str());
1884 DisconnectDirectConn( fd );
1885 return;
1888 SignalRemoveSocket(fd);
1889 // no longer select on write
1891 SignalAddSocket(fd, SocketEvent::READ);
1892 // select on read now
1894 try {
1895 dc->FinishNonBlockingConnect();
1896 } catch(DisconnectedException e) {
1897 // first Send on socket could have failed
1898 SignalLog(LogEvent::WARN, e.what());
1899 DisconnectDirectConn( fd );
1902 if (dynamic_cast<FileTransferClient*>(dc) != NULL)
1904 SignalRemoveSocket(fd);
1905 // no longer select on read
1907 SignalAddSocket(fd, SocketEvent::WRITE);
1908 // select on write now
1911 } else if (sock->getState() == TCPSocket::CONNECTED && (m & SocketEvent::READ)) {
1912 try {
1913 dc->Recv();
1914 } catch(DisconnectedException e) {
1915 // tear down connection
1916 SignalLog(LogEvent::WARN, e.what());
1917 DisconnectDirectConn( fd );
1920 else if (sock->getState() == TCPSocket::CONNECTED && (m & SocketEvent::WRITE))
1922 // Should maybe make sure only FileTransferClients enter here..
1923 // Rate could be controlled here or in FileTransferClient
1924 try {
1925 dc->Recv();
1926 dc->SendEvent(NULL);
1927 } catch(DisconnectedException e) {
1928 // tear down connection
1929 SignalLog(LogEvent::WARN, e.what());
1930 DisconnectDirectConn( fd );
1933 } else {
1934 SignalLog(LogEvent::ERROR, "Direct Connection socket in inconsistent state!");
1935 DisconnectDirectConn( fd );
1942 * Register a new UIN.
1944 void Client::RegisterUIN() {
1945 if (m_state == NOT_CONNECTED)
1946 ConnectAuthorizer(UIN_AWAITING_CONN_ACK);
1950 * Boolean to determine if you are connected or not.
1952 * @return connected
1954 bool Client::isConnected() const {
1955 return (m_state == BOS_LOGGED_IN);
1958 void Client::HandleUserInfoSNAC(UserInfoSNAC *snac) {
1959 // this should only be personal info
1960 const UserInfoBlock &ub = snac->getUserInfo();
1961 if (ub.getUIN() == m_self->getUIN()) {
1962 // currently only interested in our external IP
1963 // - we might be behind NAT
1964 if (ub.getExtIP() != 0) m_ext_ip = ub.getExtIP();
1966 // Check for status change
1967 Status newstat = Contact::MapICQStatusToStatus( ub.getStatus() );
1968 bool newinvis = Contact::MapICQStatusToInvisible( ub.getStatus() );
1969 m_self->setStatus(newstat, newinvis);
1974 * Used for sending a message event from the client. The Client
1975 * should create the specific MessageEvent by dynamically
1976 * allocating it with new. The library will take care of deleting
1977 * it when appropriate. The MessageEvent will persist whilst the
1978 * message has not be confirmed as delivered or failed yet. Exactly
1979 * the same MessageEvent is signalled back in the messageack signal
1980 * callback, so a client could use pointer equality comparison to
1981 * match messages it has sent up to their acks.
1983 void Client::SendEvent(MessageEvent *ev) {
1984 switch (ev->getType()) {
1986 case MessageEvent::Normal:
1987 case MessageEvent::URL:
1988 case MessageEvent::AwayMessage:
1989 case MessageEvent::FileTransfer:
1990 case MessageEvent::Contacts:
1991 if (!SendDirect(ev)) SendViaServer(ev);
1992 break;
1994 case MessageEvent::Email:
1995 m_smtp->SendEvent(ev);
1996 break;
1998 default:
1999 SendViaServer(ev);
2000 break;
2004 bool Client::SendDirect(MessageEvent *ev) {
2005 ContactRef c = ev->getContact();
2006 if (!c->getDirect()) return false;
2007 DirectClient *dc = ConnectDirect(c);
2008 if (dc == NULL) return false;
2009 dc->SendEvent(ev);
2010 return true;
2013 DirectClient* Client::ConnectDirect(const ContactRef& c)
2015 DirectClient *dc = m_dccache->getByContact(c);
2016 if (dc == NULL) {
2017 if (!m_out_dc) return NULL;
2019 * If their external IP != internal IP then it's
2020 * only worth trying if their external IP == my external IP
2021 * (when we are behind the same masq box)
2023 if ( c->getExtIP() != c->getLanIP() && m_ext_ip != c->getExtIP() ) return NULL;
2024 if ( c->getLanIP() == 0 ) return NULL;
2025 SignalLog(LogEvent::INFO, "Establishing direct connection");
2026 dc = new DirectClient(m_self, c, m_message_handler,
2027 m_ext_ip, (m_in_dc ? m_listenServer->getPort() : 0) );
2028 dc->logger.connect( this, &Client::dc_log_cb) ;
2029 dc->messageack.connect( this, &Client::dc_messageack_cb) ;
2030 dc->connected.connect( this, &Client::dc_connected_cb ) ;
2031 dc->socket.connect( this, &Client::dc_socket_cb) ;
2033 try {
2034 dc->Connect();
2035 } catch(DisconnectedException e) {
2036 SignalLog(LogEvent::WARN, e.what());
2037 delete dc;
2038 return NULL;
2039 } catch(SocketException e) {
2040 SignalLog(LogEvent::WARN, e.what());
2041 delete dc;
2042 return NULL;
2043 } catch(...) {
2044 SignalLog(LogEvent::WARN, "Uncaught exception");
2045 return NULL;
2048 (*m_dccache)[ dc->getfd() ] = dc;
2051 return dc;
2054 void Client::SendViaServer(MessageEvent *ev)
2056 ContactRef c = ev->getContact();
2058 if (ev->getType() == MessageEvent::Normal
2059 || ev->getType() == MessageEvent::URL
2060 || ev->getType() == MessageEvent::Contacts)
2063 * Normal messages and URL messages sent via the server
2064 * can be sent as advanced for ICQ2000 users online, in which
2065 * case they can be ACKed, otherwise there is no way of
2066 * knowing if it's received
2069 if (c->get_accept_adv_msgs())
2070 SendViaServerAdvanced(ev);
2071 else {
2072 SendViaServerNormal(ev);
2073 delete ev;
2076 } else if (ev->getType() == MessageEvent::AwayMessage) {
2079 * Away message requests sent via the server only
2080 * work for ICQ2000 clients online, otherwise there
2081 * is no way of getting the away message, so we signal the ack
2082 * to the client as non-delivered
2085 if (c->get_accept_adv_msgs())
2086 SendViaServerAdvanced(ev);
2087 else {
2088 ev->setFinished(true);
2089 ev->setDelivered(false);
2090 ev->setDirect(false);
2091 ev->setDeliveryFailureReason(MessageEvent::Failed_ClientNotCapable);
2092 messageack.emit(ev);
2093 delete ev;
2096 } else if (ev->getType() == MessageEvent::AuthReq
2097 || ev->getType() == MessageEvent::AuthAck
2098 || ev->getType() == MessageEvent::UserAdd)
2102 * This seems the sure way of sending authorisation messages and
2103 * user added me notices. They can't be sent direct.
2105 SendViaServerNormal(ev);
2106 delete ev;
2109 else if (ev->getType() == MessageEvent::SMS)
2112 * SMS Messages are sent via a completely different mechanism.
2115 SMSMessageEvent *sv = static_cast<SMSMessageEvent*>(ev);
2116 SrvSendSNAC ssnac(sv->getMessage(), c->getNormalisedMobileNo(), m_self->getUIN(), "", sv->getRcpt());
2118 unsigned int reqid = NextRequestID();
2119 m_reqidcache->insert( reqid, new SMSEventCacheValue( sv ) );
2120 ssnac.setRequestID( reqid );
2122 FLAPwrapSNACandSend( ssnac );
2125 else if (ev->getType() == MessageEvent::FileTransfer)
2127 ostringstream ostr;
2128 ostr << "Sending FileTransfer via Server ";
2129 SignalLog(LogEvent::INFO, ostr.str() );
2130 SendViaServerAdvanced(ev);
2135 void Client::SendViaServerAdvanced(MessageEvent *ev)
2137 if (m_state == NOT_CONNECTED) {
2138 ev->setFinished(true);
2139 ev->setDelivered(false);
2140 ev->setDirect(false);
2141 ev->setDeliveryFailureReason(MessageEvent::Failed_NotConnected);
2142 messageack.emit(ev);
2143 delete ev;
2144 return;
2147 ContactRef c = ev->getContact();
2148 UINICQSubType *ist = m_message_handler->handleOutgoing(ev);
2149 ist->setAdvanced(true);
2151 MsgSendSNAC msnac(ist);
2152 msnac.setAdvanced(true);
2153 msnac.setSeqNum( c->nextSeqNum() );
2154 ICBMCookie ck = m_cookiecache->generateUnique();
2155 msnac.setICBMCookie( ck );
2157 m_cookiecache->insert( ck, ev );
2159 msnac.set_capabilities( c->get_capabilities() );
2161 FLAPwrapSNACandSend( msnac );
2163 delete ist;
2166 void Client::SendViaServerNormal(MessageEvent *ev)
2168 if (m_state == NOT_CONNECTED) {
2169 ev->setFinished(true);
2170 ev->setDelivered(false);
2171 ev->setDirect(false);
2172 ev->setDeliveryFailureReason(MessageEvent::Failed_NotConnected);
2173 messageack.emit(ev);
2174 return;
2177 ContactRef c = ev->getContact();
2178 UINICQSubType *ist = m_message_handler->handleOutgoing(ev);
2179 ist->setAdvanced(false);
2181 MsgSendSNAC msnac(ist);
2182 msnac.setAdvanced(false);
2184 FLAPwrapSNACandSend( msnac );
2186 ev->setFinished(true);
2187 ev->setDelivered(true);
2188 ev->setDirect(false);
2189 ICQMessageEvent *cev;
2190 if ((cev = dynamic_cast<ICQMessageEvent*>(ev)) != NULL) cev->setOfflineMessage(true);
2192 if (ev->getType() == MessageEvent::AuthReq) {
2193 ev->getContact()->setAuthAwait(true);
2196 messageack.emit(ev);
2197 delete ist;
2200 void Client::PingServer()
2202 Buffer b;
2203 Buffer::marker mk = FLAPHeader(b,0x05,NextSeqNum());
2204 FLAPFooter(b,mk);
2205 Send(b);
2208 void Client::uploadSelfDetails()
2210 Buffer b;
2212 ICQ2000::Contact::MainHomeInfo& imh = m_self->getMainHomeInfo();
2213 ICQ2000::Contact::MainHomeInfo omh;
2214 omh.alias = m_translator->client_to_server( imh.alias, ENCODING_CONTACT_LOCALE, m_self );
2215 omh.firstname = m_translator->client_to_server( imh.firstname, ENCODING_CONTACT_LOCALE, m_self );
2216 omh.lastname = m_translator->client_to_server( imh.lastname, ENCODING_CONTACT_LOCALE, m_self );
2217 omh.email = m_translator->client_to_server( imh.email, ENCODING_CONTACT_LOCALE, m_self );
2218 omh.city = m_translator->client_to_server( imh.city, ENCODING_CONTACT_LOCALE, m_self );
2219 omh.state = m_translator->client_to_server( imh.state, ENCODING_CONTACT_LOCALE, m_self );
2220 omh.phone = m_translator->client_to_server( imh.phone, ENCODING_CONTACT_LOCALE, m_self );
2221 omh.fax = m_translator->client_to_server( imh.fax, ENCODING_CONTACT_LOCALE, m_self );
2222 omh.street = m_translator->client_to_server( imh.street, ENCODING_CONTACT_LOCALE, m_self );
2223 omh.zip = m_translator->client_to_server( imh.zip, ENCODING_CONTACT_LOCALE, m_self );
2224 omh.setMobileNo( imh.getMobileNo() );
2225 omh.country = imh.country;
2226 omh.timezone = imh.timezone;
2228 ICQ2000::Contact::HomepageInfo ohp = m_self->getHomepageInfo();
2229 m_translator->client_to_server_inplace( ohp.homepage, ENCODING_CONTACT_LOCALE, m_self );
2231 ICQ2000::Contact::WorkInfo& iw = m_self->getWorkInfo();
2232 ICQ2000::Contact::WorkInfo ow;
2233 ow.city = m_translator->client_to_server( iw.city, ENCODING_CONTACT_LOCALE, m_self );
2234 ow.state = m_translator->client_to_server( iw.state, ENCODING_CONTACT_LOCALE, m_self );
2235 ow.street = m_translator->client_to_server( iw.street, ENCODING_CONTACT_LOCALE, m_self );
2236 ow.zip = m_translator->client_to_server( iw.zip, ENCODING_CONTACT_LOCALE, m_self );
2237 ow.company_name = m_translator->client_to_server( iw.company_name, ENCODING_CONTACT_LOCALE, m_self );
2238 ow.company_dept = m_translator->client_to_server( iw.company_dept, ENCODING_CONTACT_LOCALE, m_self );
2239 ow.company_position = m_translator->client_to_server( iw.company_position, ENCODING_CONTACT_LOCALE, m_self );
2240 ow.company_web = m_translator->client_to_server( iw.company_web, ENCODING_CONTACT_LOCALE, m_self );
2242 FLAPwrapSNAC( b, SrvUpdateMainHomeInfo(m_self->getUIN(), omh) );
2243 FLAPwrapSNAC( b, SrvUpdateWorkInfo(m_self->getUIN(), ow) );
2244 FLAPwrapSNAC( b, SrvUpdateHomepageInfo(m_self->getUIN(), ohp) );
2245 FLAPwrapSNAC( b, SrvUpdateAboutInfo(m_self->getUIN(),
2246 m_translator->client_to_server( m_self->getAboutInfo(), ENCODING_CONTACT_LOCALE, m_self ) ) );
2248 Send(b);
2251 void Client::fetchServerBasedContactList()
2253 m_fetch_sbl = true;
2254 if (m_state == BOS_LOGGED_IN) SendRequestSBL();
2257 void Client::fetchServerBasedContactList(int)
2259 // TODO!
2262 void Client::uploadServerBasedContact(const ContactRef& c)
2264 Buffer b;
2266 FLAPwrapSNAC( b, SBLRequestRightsSNAC() );
2267 FLAPwrapSNAC( b, SBLBeginEditSNAC() );
2268 ContactTree::Group &g = m_contact_tree.lookup_group_containing_contact(c);
2269 FLAPwrapSNAC( b, SBLAddEntrySNAC(g.get_label(), g.get_id()) );
2270 FLAPwrapSNAC( b, SBLAddEntrySNAC(c) );
2271 FLAPwrapSNAC( b, SBLCommitEditSNAC() );
2273 // TODO ContactTree!
2274 // SBLAddEntrySNAC ssnac(l);
2275 // ssnac.setRequestID( reqid );
2276 Send(b);
2279 void Client::updateServerBasedContact(const ContactRef& c)
2281 Buffer b;
2283 FLAPwrapSNAC( b, SBLRequestRightsSNAC() );
2284 FLAPwrapSNAC( b, SBLBeginEditSNAC() );
2285 ContactTree::Group &g = m_contact_tree.lookup_group_containing_contact(c);
2286 FLAPwrapSNAC( b, SBLAddEntrySNAC(g.get_label(), g.get_id()) );
2287 FLAPwrapSNAC( b, SBLUpdateEntrySNAC(c) );
2288 FLAPwrapSNAC( b, SBLCommitEditSNAC() );
2290 Send(b);
2293 void Client::uploadServerBasedGroup(const ContactTree::Group& gp)
2295 Buffer b;
2297 FLAPwrapSNAC( b, SBLRequestRightsSNAC() );
2298 FLAPwrapSNAC( b, SBLBeginEditSNAC() );
2300 std::vector<unsigned short> ids;
2301 for(ContactTree::Group::const_iterator ic = gp.begin(); ic != gp.end(); ++ic)
2302 ids.push_back((*ic)->getServerSideID());
2304 FLAPwrapSNAC( b, SBLUpdateEntrySNAC(gp.get_label(), gp.get_id(), ids) );
2305 FLAPwrapSNAC( b, SBLCommitEditSNAC() );
2307 Send(b);
2310 void Client::uploadServerBasedContactList()
2312 Buffer b;
2314 FLAPwrapSNAC( b, SBLBeginEditSNAC() );
2317 ** needs recoding for groups **
2318 ServerBasedContactEvent *ev = new ServerBasedContactEvent(ServerBasedContactEvent::Upload, m_contact_tree );
2319 unsigned int reqid = NextRequestID();
2320 m_reqidcache->insert( reqid, new ServerBasedContactCacheValue( ev ) );
2323 // TODO ContactTree!
2324 //SBLAddEntrySNAC ssnac( m_contact_tree );
2325 // ssnac.setRequestID( reqid );
2326 // FLAPwrapSNAC( b, ssnac );
2328 FLAPwrapSNAC( b, SBLCommitEditSNAC() );
2330 Send(b);
2333 void Client::removeServerBasedContactList()
2335 Buffer b;
2337 FLAPwrapSNAC( b, SBLBeginEditSNAC() );
2340 ** needs recoding for groups **
2341 ServerBasedContactEvent *ev = new ServerBasedContactEvent(ServerBasedContactEvent::Remove, l);
2342 unsigned int reqid = NextRequestID();
2343 m_reqidcache->insert( reqid, new ServerBasedContactCacheValue( ev ) );
2345 SBLRemoveEntrySNAC ssnac(l);
2346 ssnac.setRequestID( reqid );
2347 FLAPwrapSNAC( b, ssnac );
2349 FLAPwrapSNAC( b, SBLCommitEditSNAC() );
2351 Send(b);
2354 void Client::removeServerBasedContact(const ContactRef& c)
2356 Buffer b;
2358 FLAPwrapSNAC( b, SBLBeginEditSNAC() );
2359 FLAPwrapSNAC( b, SBLRemoveEntrySNAC(c) );
2360 FLAPwrapSNAC( b, SBLCommitEditSNAC() );
2362 Send(b);
2366 * Set your status. This is used to set your status, as well as to
2367 * connect and disconnect from the network. When you wish to
2368 * connect to the ICQ network, set status to something other than
2369 * STATUS_OFFLINE and connecting will be initiated. When you wish
2370 * to disconnect set the status to STATUS_OFFLINE and disconnection
2371 * will be initiated.
2373 * @param st the status
2374 * @param inv whether to be invisible or not
2376 void Client::setStatus(const Status st, bool inv)
2378 m_status_wanted = st;
2379 m_invisible_wanted = inv;
2381 if (st == STATUS_OFFLINE) {
2382 if (m_state != NOT_CONNECTED)
2383 Disconnect(DisconnectedEvent::REQUESTED);
2385 return;
2388 if (m_state == BOS_LOGGED_IN) {
2390 * The correct sequence of events are:
2391 * - when going from visible to invisible
2392 * - send the Add to Visible list (or better named Set in this case)
2393 * - send set Status
2394 * - when going from invisible to visible
2395 * - send set Status
2396 * - send the Add to Invisible list (or better named Set in this case)
2399 Buffer b;
2401 if (!m_self->isInvisible() && inv) {
2402 // visible -> invisible
2403 FLAPwrapSNAC( b, AddVisibleSNAC(m_visible_list) );
2406 FLAPwrapSNAC( b, SetStatusSNAC(Contact::MapStatusToICQStatus(st, inv), m_web_aware) );
2408 if (m_self->isInvisible() && !inv) {
2409 // invisible -> visible
2410 FLAPwrapSNAC( b, AddInvisibleSNAC(m_invisible_list) );
2413 Send(b);
2415 } else {
2416 // We'll set this as the initial status upon connecting
2417 m_status_wanted = st;
2418 m_invisible_wanted = inv;
2420 // start connecting if not already
2421 if (m_state == NOT_CONNECTED) {
2422 ConnectingEvent ev;
2423 connecting.emit(&ev);
2424 ConnectAuthorizer(AUTH_AWAITING_CONN_ACK);
2430 * Set your status, without effecting invisibility (your last
2431 * requested invisible state will be used).
2433 * @param st the status
2435 void Client::setStatus(const Status st)
2437 setStatus(st, m_invisible_wanted);
2441 * Set your invisibility, without effecting status (your last
2442 * requested status will be used).
2444 * @param inv invisibility
2446 void Client::setInvisible(bool inv)
2448 setStatus(m_status_wanted, inv);
2451 void Client::setWebAware(bool wa)
2453 if (m_web_aware!=wa)
2455 m_web_aware = wa;
2456 if (m_self->getStatus() != STATUS_OFFLINE)
2457 setStatus(m_status_wanted, m_invisible_wanted);
2461 void Client::setRandomChatGroup(unsigned short group) {
2462 if(m_random_group != group && m_state != NOT_CONNECTED) {
2463 m_random_group = group;
2465 unsigned int reqid = NextRequestID();
2466 SrvSetRandomChatGroup ssnac( m_self->getUIN(), group );
2468 SignalLog(LogEvent::INFO, "Setting random chat group");
2469 FLAPwrapSNACandSend( ssnac );
2474 * Get your current status.
2476 * @return your current status
2478 Status Client::getStatus() const {
2479 return m_self->getStatus();
2483 * Get your invisible status
2484 * @return Invisible boolean
2487 bool Client::getInvisible() const
2489 return m_self->isInvisible();
2493 * Get the last requested status
2495 Status Client::getStatusWanted() const
2497 return m_status_wanted;
2501 * Get the last invisibility status wanted
2503 bool Client::getInvisibleWanted() const
2505 return m_invisible_wanted;
2508 bool Client::getWebAware() const
2510 return m_web_aware;
2513 void Client::contactlist_cb(ContactListEvent *ev)
2515 if (ev->getType() == ContactListEvent::UserAdded)
2517 UserAddedEvent *cev = static_cast<UserAddedEvent*>(ev);
2518 ContactRef c = cev->getContact();
2519 if (c->isICQContact() && m_state == BOS_LOGGED_IN)
2521 FLAPwrapSNACandSend( AddBuddySNAC(c) );
2523 // fetch detailed userinfo from server
2524 fetchDetailContactInfo(c);
2528 else if (ev->getType() == ContactListEvent::UserRemoved)
2530 UserRemovedEvent *cev = static_cast<UserRemovedEvent*>(ev);
2531 ContactRef c = cev->getContact();
2532 if (c->isICQContact() && m_state == BOS_LOGGED_IN)
2534 FLAPwrapSNACandSend( RemoveBuddySNAC(c) );
2537 // remove all direct connections for that contact
2538 m_dccache->removeContact(c);
2541 else if (ev->getType() == ContactListEvent::GroupAdded)
2544 else if (ev->getType() == ContactListEvent::GroupRemoved)
2547 else if (ev->getType() == ContactListEvent::CompleteUpdate)
2551 // re-emit on the Client signal
2552 contactlist.emit(ev);
2555 void Client::visiblelist_cb(ContactListEvent *ev)
2557 if (ev->getType() == ContactListEvent::UserAdded) {
2558 UserAddedEvent *cev = static_cast<UserAddedEvent*>(ev);
2559 ContactRef c = cev->getContact();
2561 if (c->isICQContact() && m_state == BOS_LOGGED_IN && m_self->isInvisible()) {
2562 FLAPwrapSNACandSend( AddVisibleSNAC(c) );
2566 } else {
2567 UserRemovedEvent *cev = static_cast<UserRemovedEvent*>(ev);
2568 ContactRef c = cev->getContact();
2570 if (c->isICQContact() && m_state == BOS_LOGGED_IN && m_self->isInvisible()) {
2571 FLAPwrapSNACandSend( RemoveVisibleSNAC(c) );
2579 void Client::invisiblelist_cb(ContactListEvent *ev)
2581 if (ev->getType() == ContactListEvent::UserAdded) {
2582 UserAddedEvent *cev = static_cast<UserAddedEvent*>(ev);
2583 ContactRef c = cev->getContact();
2585 if (c->isICQContact() && m_state == BOS_LOGGED_IN && !m_self->isInvisible()) {
2586 FLAPwrapSNACandSend( AddInvisibleSNAC(c) );
2590 } else {
2591 UserRemovedEvent *cev = static_cast<UserRemovedEvent*>(ev);
2592 ContactRef c = cev->getContact();
2594 if (c->isICQContact() && m_state == BOS_LOGGED_IN && !m_self->isInvisible()) {
2595 FLAPwrapSNACandSend( RemoveInvisibleSNAC(c) );
2603 * Add a contact to your visible list.
2605 * @param c the contact passed as a reference counted object (ref_ptr<Contact> or ContactRef).
2607 void Client::addVisible(ContactRef c) {
2609 if (!m_visible_list.exists(c->getUIN())) {
2610 m_visible_list.add(c);
2616 * Remove a contact from your visible list.
2618 * @param uin the uin of the contact to be removed
2620 void Client::removeVisible(const unsigned int uin) {
2621 if (m_visible_list.exists(uin)) {
2622 m_visible_list.remove(uin);
2627 * Add a contact to your invisible list.
2629 * @param c the contact passed as a reference counted object (ref_ptr<Contact> or ContactRef).
2631 void Client::addInvisible(ContactRef c) {
2633 if (!m_invisible_list.exists(c->getUIN())) {
2634 m_invisible_list.add(c);
2640 * Remove a contact from your invisible list.
2642 * @param uin the uin of the contact to be removed
2644 void Client::removeInvisible(const unsigned int uin)
2646 if (m_invisible_list.exists(uin)) {
2647 m_invisible_list.remove(uin);
2651 ContactRef Client::getSelfContact()
2653 return m_self;
2657 * Get the Contact object for a given uin.
2659 * @param uin the uin
2660 * @return a pointer to the Contact object. NULL if no Contact with
2661 * that uin exists on your list.
2663 ContactRef Client::getContact(const unsigned int uin) {
2664 if (m_contact_tree.exists(uin)) {
2665 return m_contact_tree[uin];
2666 } else {
2667 return NULL;
2672 * Get the ContactTree object used for the main library.
2674 * @return a reference to the ContactTree
2676 ContactTree& Client::getContactTree()
2678 return m_contact_tree;
2682 * Request the simple contact information for a Contact. This
2683 * consists of the contact alias, firstname, lastname and
2684 * email. When the server has replied with the details the library
2685 * will signal a user info changed for this contact.
2687 * @param c contact to fetch info for
2688 * @see ContactListEvent
2690 void Client::fetchSimpleContactInfo(ContactRef c) {
2691 Buffer b;
2693 if ( !c->isICQContact() ) return;
2695 SignalLog(LogEvent::INFO, "Sending request Simple Userinfo Request");
2696 FLAPwrapSNACandSend( SrvRequestSimpleUserInfo( m_self->getUIN(), c->getUIN() ) );
2700 * Request the detailed contact information for a Contact. When the
2701 * server has replied with the details the library will signal a
2702 * user info changed for this contact.
2704 * @param c contact to fetch info for
2705 * @see ContactListEvent
2707 void Client::fetchDetailContactInfo(ContactRef c) {
2708 if ( !c->isICQContact() ) return;
2710 SignalLog(LogEvent::INFO, "Sending request Detailed Userinfo Request");
2712 unsigned int reqid = NextRequestID();
2713 m_reqidcache->insert( reqid, new UserInfoCacheValue(c) );
2714 SrvRequestDetailUserInfo ssnac( m_self->getUIN(), c->getUIN() );
2715 ssnac.setRequestID( reqid );
2716 FLAPwrapSNACandSend( ssnac );
2719 void Client::fetchSelfSimpleContactInfo()
2721 fetchSimpleContactInfo(m_self);
2724 void Client::fetchSelfDetailContactInfo()
2726 fetchDetailContactInfo(m_self);
2729 SearchResultEvent* Client::searchForContacts
2730 (const string& nickname, const string& firstname,
2731 const string& lastname)
2733 SearchResultEvent *ev = new SearchResultEvent( SearchResultEvent::ShortWhitepage );
2735 unsigned int reqid = NextRequestID();
2736 m_reqidcache->insert( reqid, new SearchCacheValue( ev ) );
2738 SrvRequestShortWP ssnac( m_self->getUIN(), nickname, firstname, lastname );
2739 ssnac.setRequestID( reqid );
2741 SignalLog(LogEvent::INFO, "Sending short whitepage search");
2742 FLAPwrapSNACandSend( ssnac );
2744 return ev;
2747 SearchResultEvent* Client::searchForContacts
2748 (const string& nickname, const string& firstname,
2749 const string& lastname, const string& email,
2750 AgeRange age, Sex sex, unsigned char language, const string& city,
2751 const string& state, unsigned short country,
2752 const string& company_name, const string& department,
2753 const string& position, bool only_online)
2755 SearchResultEvent *ev = new SearchResultEvent( SearchResultEvent::FullWhitepage );
2757 unsigned int reqid = NextRequestID();
2758 m_reqidcache->insert( reqid, new SearchCacheValue( ev ) );
2760 unsigned short min_age, max_age;
2762 switch(age) {
2763 case RANGE_18_22:
2764 min_age = 18;
2765 max_age = 22;
2766 break;
2767 case RANGE_23_29:
2768 min_age = 23;
2769 max_age = 29;
2770 break;
2771 case RANGE_30_39:
2772 min_age = 30;
2773 max_age = 39;
2774 break;
2775 case RANGE_40_49:
2776 min_age = 40;
2777 max_age = 49;
2778 break;
2779 case RANGE_50_59:
2780 min_age = 50;
2781 max_age = 59;
2782 break;
2783 case RANGE_60_ABOVE:
2784 min_age = 60;
2785 max_age = 0x2710;
2786 break;
2787 default:
2788 min_age = max_age = 0;
2789 break;
2792 SrvRequestFullWP ssnac( m_self->getUIN(), nickname, firstname, lastname, email,
2793 min_age, max_age, (unsigned char)sex, language, city, state,
2794 country, company_name, department, position,
2795 only_online);
2796 ssnac.setRequestID( reqid );
2798 SignalLog(LogEvent::INFO, "Sending full whitepage search");
2799 FLAPwrapSNACandSend( ssnac );
2801 return ev;
2804 SearchResultEvent* Client::searchForContacts(unsigned int uin)
2806 SearchResultEvent *ev = new SearchResultEvent( SearchResultEvent::UIN );
2808 unsigned int reqid = NextRequestID();
2809 m_reqidcache->insert( reqid, new SearchCacheValue( ev ) );
2811 SrvRequestSimpleUserInfo ssnac( m_self->getUIN(), uin );
2812 ssnac.setRequestID( reqid );
2814 SignalLog(LogEvent::INFO, "Sending simple user info request");
2815 FLAPwrapSNACandSend( ssnac );
2817 return ev;
2820 SearchResultEvent* Client::searchForContacts(const string& keyword)
2822 SearchResultEvent *ev = new SearchResultEvent( SearchResultEvent::Keyword );
2823 unsigned int reqid = NextRequestID();
2824 m_reqidcache->insert( reqid, new SearchCacheValue( ev ) );
2826 SrvRequestKeywordSearch ssnac( m_self->getUIN(), keyword );
2827 ssnac.setRequestID( reqid );
2829 SignalLog(LogEvent::INFO, "Sending contact keyword search request");
2830 FLAPwrapSNACandSend( ssnac );
2832 return ev;
2835 SearchResultEvent* Client::searchForContacts(RandomChatGroup group)
2837 SearchResultEvent *ev = new SearchResultEvent( SearchResultEvent::RandomChat );
2838 unsigned int reqid = NextRequestID();
2839 m_reqidcache->insert( reqid, new SearchCacheValue( ev ) );
2841 SrvRequestRandomChat ssnac( m_self->getUIN(), group );
2842 ssnac.setRequestID( reqid );
2844 SignalLog(LogEvent::INFO, "Sending contact random chat search request");
2845 FLAPwrapSNACandSend( ssnac );
2847 return ev;
2850 void Client::Disconnect(DisconnectedEvent::Reason r) {
2851 if (m_state == NOT_CONNECTED) return;
2853 SignalLog(LogEvent::INFO, "Client disconnecting");
2855 if (m_state == AUTH_AWAITING_CONN_ACK || m_state == AUTH_AWAITING_AUTH_REPLY
2856 || m_state == UIN_AWAITING_CONN_ACK || m_state == UIN_AWAITING_UIN_REPLY) {
2857 DisconnectAuthorizer();
2858 } else {
2859 DisconnectBOS();
2862 SignalDisconnect(r);
2866 * Call this method when you want to initiate a file transfer to a
2867 * remote client. The File Transfer object must have the message,
2868 * description and list of files that will be transfered all set up
2869 * before passing here.
2871 * @param ev the FileTransferEvent
2873 void Client::SendFileTransfer(FileTransferEvent *ev)
2875 if (ev->getState() == FileTransferEvent::NOT_CONNECTED)
2877 if (!FileTransferClient::SetupFileTransfer(ev))
2879 // TODO - enum!
2880 ev->setError("I/O error trying to resolve given filename.");
2881 ev->setState(FileTransferEvent::ERROR);
2882 filetransfer_update_signal.emit(ev);
2883 return;
2884 } else {
2885 ev->setState(FileTransferEvent::WAIT_RESPONS);
2886 filetransfer_update_signal.emit(ev);
2887 ev->setDirect(ev->getContact()->getDirect());
2888 SendEvent(ev);
2889 return;
2893 FileTransferClient *ftc = new FileTransferClient(m_self,
2894 ev->getContact(),
2895 m_message_handler,
2896 m_ext_ip,
2897 ev);
2899 ftc->logger.connect( this, &Client::dc_log_cb) ;
2900 ftc->socket.connect( this, &Client::dc_socket_cb) ;
2904 ftc->Connect();
2906 catch(DisconnectedException e)
2908 SignalLog(LogEvent::WARN, e.what());
2909 ev->setError("ERROR while trying to connect");
2910 ev->setState(FileTransferEvent::ERROR);
2911 filetransfer_update_signal.emit(ev);
2912 delete ftc;
2913 return;
2915 catch(SocketException e)
2917 SignalLog(LogEvent::WARN, e.what());
2918 ev->setError("ERROR while trying to connect");
2919 ev->setState(FileTransferEvent::ERROR);
2920 filetransfer_update_signal.emit(ev);
2921 delete ftc;
2922 return;
2924 catch(...)
2926 SignalLog(LogEvent::WARN, "Uncaught exception");
2927 return;
2930 (*m_ftcache)[ ftc->getfd() ] = ftc;
2934 ftc->SendEvent(NULL);
2935 ev->setState(FileTransferEvent::SEND);
2936 filetransfer_update_signal.emit(ev);
2938 catch(DisconnectedException e)
2940 SignalLog(LogEvent::WARN, e.what());
2941 DisconnectDirectConn(ftc->getfd());
2946 * Call this method after an incoming request has been signalled,
2947 * with accept/refuse (and a message) indicated.
2949 * @param ev the FileTransferEvent
2950 * @see filetransfer_incoming_signal
2952 void Client::SendFileTransferACK(FileTransferEvent *ev)
2954 if (ev->isDirect()) {
2955 DirectClient *dc = m_dccache->getByContact(ev->getContact());
2956 if (dc == NULL)
2957 dc = ConnectDirect(ev->getContact());
2959 if (dc == NULL) {
2960 ev->setState(FileTransferEvent::ERROR);
2961 ev->setError("Couldn't open an Direct connection to target");
2962 filetransfer_update_signal.emit(ev);
2963 return;
2966 if (ev->getState() == FileTransferEvent::ACCEPTED) {
2967 FileTransferClient *ftc = new FileTransferClient(m_self,
2968 m_message_handler,
2969 &m_contact_tree,
2970 m_ext_ip, ev);
2971 SignalAddSocket(ftc->getlistenfd(), SocketEvent::READ );
2972 ev->setPort(ftc->getlistenPort());
2974 (*m_ftcache)[ftc->getlistenfd()] = ftc;
2977 try {
2978 SignalLog(LogEvent::INFO, "Sending FileTransfer ACK direct");
2979 dc->SendFTACK(ev);
2980 } catch(DisconnectedException e) {
2981 // tear down connection
2982 SignalLog(LogEvent::WARN, e.what());
2983 DisconnectDirectConn( dc->getfd() );
2986 if (ev->getState() == FileTransferEvent::ACCEPTED)
2987 ev->setState(FileTransferEvent::RECEIVE);
2988 else
2989 ev->setState(FileTransferEvent::NOT_CONNECTED);
2991 } else {
2992 // ughh.
2993 MessageSNAC *snac = new MessageSNAC();
2994 ICBMCookie cookie;
2995 cookie.generate();
2996 snac->setICBMCookie(cookie);
2997 FTICQSubType *fst = new FTICQSubType(ev->getMessage(),
2998 ev->getDescription(),
2999 ev->getTotalSize());
3000 if (ev->getState() == FileTransferEvent::ACCEPTED) {
3001 FileTransferClient *ftc =
3002 new FileTransferClient(m_self,
3003 m_message_handler,
3004 &m_contact_tree,
3005 m_ext_ip, ev);
3006 SignalAddSocket(ftc->getlistenfd(), SocketEvent::READ );
3007 ev->setPort(ftc->getlistenPort());
3009 fst->setPort(ev->getPort());
3010 fst->setRevPort(ev->getPort());
3012 (*m_ftcache)[ftc->getlistenfd()] = ftc;
3013 ev->setState(FileTransferEvent::RECEIVE);
3014 } else {
3015 ev->setState(FileTransferEvent::NOT_CONNECTED);
3018 snac->setICQSubType(fst);
3020 SignalLog(LogEvent::INFO, "Sending FileTransfer ACK through server");
3021 //Don't know if it should be advanced???
3022 SendAdvancedACK(snac);
3024 filetransfer_update_signal.emit(ev);
3027 void Client::CancelFileTransfer(FileTransferEvent *ev)
3029 if (ev != NULL) {
3030 switch (ev->getState()) {
3031 case FileTransferEvent::NOT_CONNECTED: // Do nothing.
3032 case FileTransferEvent::ERROR: // FileTransferClient is
3033 case FileTransferEvent::CANCELLED: // already deleted.
3034 case FileTransferEvent::COMPLETE:
3035 break;
3036 case FileTransferEvent::WAIT_RESPONS:
3037 if (ev->isDirect()) {
3038 // Through Direct Connection
3039 DirectClient *dc = m_dccache->getByContact(ev->getContact());
3040 if (dc == NULL)
3041 dc = ConnectDirect(ev->getContact());
3043 if (dc == NULL) {
3044 ev->setState(FileTransferEvent::ERROR);
3045 ev->setError("Couldn't send filetransfer cancel to target direct");
3046 filetransfer_update_signal.emit(ev);
3047 return;
3049 try {
3050 dc->SendFTCancel(ev);
3051 } catch(DisconnectedException e) {
3052 // tear down connection
3053 SignalLog(LogEvent::WARN, e.what());
3054 DisconnectDirectConn( dc->getfd() );
3056 SignalLog(LogEvent::INFO, "Sending FT Cancel through direct connection");
3057 } else {
3058 // Through Server
3059 SignalLog(LogEvent::INFO, "Sending FT Cancel through server not implemented yet.");
3060 //Don't know how to do this
3062 break;
3063 case FileTransferEvent::CLOSE:
3064 case FileTransferEvent::ACCEPTED:
3065 case FileTransferEvent::TIMEOUT:
3066 FileTransferClient *ftc = m_ftcache->getByEvent(ev);
3067 if (ftc != NULL)
3068 DisconnectDirectConn(ftc->getfd());
3069 break;
3073 ev->setState(FileTransferEvent::NOT_CONNECTED);
3074 filetransfer_update_signal.emit(ev);
3080 * Get your uin.
3081 * @return your UIN
3083 unsigned int Client::getUIN() const
3085 return m_self->getUIN();
3089 * Set your uin.
3090 * Use to set what the uin you would like to log in as, before connecting.
3091 * @param uin your UIN
3093 void Client::setUIN(unsigned int uin)
3095 m_self->setUIN(uin);
3098 /**
3099 * Set the password to use at login.
3100 * @param password your password
3102 void Client::setPassword(const string& password)
3104 m_password = password;
3108 * Get the password you set for login
3109 * @return your password
3111 string Client::getPassword() const
3113 return m_password;
3117 * set the hostname of the login server.
3118 * You needn't touch this normally, it will default automatically to login.icq.com.
3120 * @param host The host name of the server
3122 void Client::setLoginServerHost(const string& host) {
3123 m_authorizerHostname = host;
3127 * get the hostname for the currently set login server.
3129 * @return the hostname
3131 string Client::getLoginServerHost() const {
3132 return m_authorizerHostname;
3136 * set the port on the login server to connect to
3138 * @param port the port number
3140 void Client::setLoginServerPort(const unsigned short& port) {
3141 m_authorizerPort = port;
3145 * get the currently set port on the login server.
3147 * @return the port number
3149 unsigned short Client::getLoginServerPort() const {
3150 return m_authorizerPort;
3154 * set whether to override the port used to connect to the BOS
3155 * server. If you would like to ignore the port that the login
3156 * server tells you to connect to on the BOS server and instead use
3157 * your own, set this to true and call setBOSServerPort with the
3158 * port you would like to use. This method is largely unnecessary,
3159 * if you set a different login port - for example to get through
3160 * firewalls that block 5190, the login server will accept it fine
3161 * and in the redirect message doesn't specify a port, so the
3162 * library will default to using the same one as it used to connect
3163 * to the login server anyway.
3165 * @param b override redirect port
3167 void Client::setBOSServerOverridePort(const bool& b) {
3168 m_bosOverridePort = b;
3172 * get whether the BOS redirect port will be overridden.
3174 * @return override redirect port
3176 bool Client::getBOSServerOverridePort() const {
3177 return m_bosOverridePort;
3181 * set the port to use to connect to the BOS server. This will only
3182 * be used if you also called setBOSServerOverridePort(true).
3184 * @param port the port number
3186 void Client::setBOSServerPort(const unsigned short& port) {
3187 m_bosPort = port;
3191 * get the port that will be used on the BOS server.
3193 * @return the port number
3195 unsigned short Client::getBOSServerPort() const {
3196 return m_bosPort;
3199 void Client::setSMTPServerHost(const string& host) {
3200 m_smtp->setServerHost(host);
3203 string Client::getSMTPServerHost() const {
3204 return m_smtp->getServerHost();
3207 void Client::setSMTPServerPort(unsigned short port) {
3208 m_smtp->setServerPort(port);
3211 unsigned short Client::getSMTPServerPort() const {
3212 return m_smtp->getServerPort();
3216 * set whether to accept incoming direct connections
3218 * @d whether to accept incoming direct connections
3220 void Client::setAcceptInDC(bool d) {
3221 m_in_dc = d;
3222 if (!m_in_dc && m_listenServer->isStarted()) {
3223 SignalRemoveSocket( m_listenServer->getSocketHandle() );
3224 m_listenServer->Disconnect();
3229 * get whether to accept incoming direct connections
3231 * @return whether to accept incoming direct connections
3233 bool Client::getAcceptInDC() const
3235 return m_in_dc;
3239 * set whether to make outgoing direct connections
3241 * @d whether to make outgoing direct connections
3243 void Client::setUseOutDC(bool d) {
3244 m_out_dc = d;
3248 * get whether to make outgoing direct connections
3250 * @return whether to make outgoing direct connections
3252 bool Client::getUseOutDC() const
3254 return m_out_dc;
3257 /**
3258 * set the upper bound of the portrange for incoming connections (esp. behind a firewall)
3259 * you have to restart the TCPServer(s) for this to take effect
3261 * @param upper upper bound
3263 void Client::setPortRangeUpperBound(unsigned short upper) {
3264 m_upper_port=upper;
3267 /**
3268 * set the lower bound of the portrange for incoming connections (esp. behind a firewall)
3269 * you have to restart the TCPServer(s) for this to take effect
3271 * @param lower lower bound
3273 void Client::setPortRangeLowerBound(unsigned short lower) {
3274 m_lower_port=lower;
3278 * get upper bound of the portrange used for incoming connections
3280 * @return upper bound
3282 unsigned short Client::getPortRangeUpperBound() const
3284 return m_upper_port;
3288 * get lower bound of the portrange used for incoming connections
3290 * @return lower bound
3292 unsigned short Client::getPortRangeLowerBound() const
3294 return m_lower_port;
3298 * set whether a portrange should be used for incoming connections
3300 * @param b whether to use a portrange
3302 void Client::setUsePortRange(bool b) {
3303 m_use_portrange=b;
3307 * get whether a portrange should be used for incoming connections
3309 * @return whether to use a portrange
3311 bool Client::getUsePortRange() const
3313 return m_use_portrange;
3316 void Client::setClientBindHost(const std::string& host)
3318 m_client_bind_host = host;
3319 m_smtp->setClientBindHost(host);
3322 std::string Client::getClientBindHost() const
3324 return m_client_bind_host;
3327 /**
3328 * set whether to enable the flag at login which indicates you'd
3329 * like to send and receive typing notifications
3331 * @param b whether to enable typing notifications
3333 void Client::setTypingNotifications(bool b)
3335 m_use_typing_notif = b;
3339 * set the translator class to use in character set translations.
3340 * Memory management of the object is assumed to be passed onto
3341 * libicq2000, which will destroy it on destruction of the Client
3342 * object, or on assignment of a different translator. Passing NULL
3343 * for translator will disable any character set translation.
3345 * @param t the translator
3347 void Client::set_translator(Translator * t)
3349 if (m_translator != NULL)
3351 delete m_translator;
3354 if (t == NULL)
3356 m_translator = new NULLTranslator();
3358 else
3360 m_translator = t;