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
23 #include "UserInfoBlock.h"
29 #include "DirectClient.h"
30 #include "FileTransferClient.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"
45 using std::ostringstream
;
51 static const char* const Status_text
[] =
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.
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() )
79 * Constructor for creating the Client object. Use this when the
80 * uin/password are available at time of creation, to save having
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() )
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.
106 delete [] m_cookie_data
;
107 Disconnect(DisconnectedEvent::REQUESTED
);
109 delete m_message_handler
;
110 delete m_serverSocket
;
111 delete m_listenServer
;
116 delete m_cookiecache
;
123 m_authorizerHostname
= "login.icq.com";
124 m_authorizerPort
= 5190;
125 m_bosOverridePort
= false;
130 m_state
= NOT_CONNECTED
;
132 m_cookie_data
= NULL
;
135 m_self
->setStatus(STATUS_OFFLINE
, false);
137 m_status_wanted
= STATUS_OFFLINE
;
138 m_invisible_wanted
= false;
142 m_use_portrange
= false;
146 m_use_typing_notif
= 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;
204 void Client::ConnectAuthorizer(State state
) {
205 SignalLog(LogEvent::INFO
, "Client connecting");
208 * all sorts of SocketExceptions can be thrown
210 * - sockets not being created
211 * - DNS lookup failures
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
230 ostr
<< "Failed to connect to Authorizer: " << e
.what();
231 SignalLog(LogEvent::ERROR
, ostr
.str());
232 SignalDisconnect(DisconnectedEvent::FAILED_LOWLEVEL
);
236 SignalAddSocket( m_serverSocket
->getSocketHandle(), SocketEvent::WRITE
);
238 // randomize sequence number
240 m_client_seq_num
= (unsigned short)(0x7fff*(rand()/(RAND_MAX
+1.0)));
241 m_requestid
= (unsigned int)(0x7fffffff*(rand()/(RAND_MAX
+1.0)));
246 void Client::DisconnectAuthorizer() {
247 SignalRemoveSocket( m_serverSocket
->getSocketHandle() );
248 m_serverSocket
->Disconnect();
249 m_state
= NOT_CONNECTED
;
252 void Client::ConnectBOS() {
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
) {
263 ostr
<< "Failed to connect to BOS server: " << e
.what();
264 SignalLog(LogEvent::ERROR
, ostr
.str());
265 SignalDisconnect(DisconnectedEvent::FAILED_LOWLEVEL
);
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() );
320 SignalLog(LogEvent::WARN
, "Trying to disconnect unknown filedescriptor");
324 // ------------------ Signal Dispatchers -----------------
325 void Client::SignalConnect() {
326 m_state
= BOS_LOGGED_IN
;
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);
359 void Client::SignalAddSocket(int fd
, SocketEvent::Mode m
)
361 AddSocketHandleEvent
ev( fd
, m
);
365 void Client::SignalRemoveSocket(int fd
)
367 RemoveSocketHandleEvent
ev(fd
);
371 void Client::SignalMessage(MessageSNAC
*snac
)
374 ICQSubType
*st
= snac
->getICQSubType();
376 if (st
== NULL
) return;
378 if (st
->getType() == MSG_Type_FT
)
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
);
393 //FIX ME!!! old ACK could arrive
394 m_cookiecache
->insert(snac
->getICBMCookie(),
395 m_message_handler
->handleIncomingFT(static_cast<FTICQSubType
*>(st
), false));
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();
415 case MSG_Type_Normal
:
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
);
431 SignalLog(LogEvent::WARN
, "Received ACK for unknown message");
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
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);
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
);
487 if (!ev
->isFinished())
489 ev
->getContact()->setDirect(false);
490 // attempt to deliver via server instead
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
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
)
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);
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
);
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
);
570 m_reqidcache
->remove( reqid
);
577 throw ParseException("Request ID cached value is not for an SMS Message");
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
);
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())
625 m_reqidcache
->remove( snac
->RequestID() );
631 SignalLog(LogEvent::WARN
, "Request ID cached value is not for a Search request");
637 if ( m_contact_tree
.exists( snac
->getUIN() ) )
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
);
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())
689 m_reqidcache
->remove( snac
->RequestID() );
695 SignalLog(LogEvent::WARN
, "Request ID cached value is not for a Search request");
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();
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();
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();
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
);
863 m_reqidcache
->remove( snac
->RequestID() );
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())
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();
905 throw ParseException("Request ID cached value is not for a User Info request");
911 throw ParseException("Received a UserInfo response for unknown request id");
916 void Client::SignalUINResponse(UINResponseSNAC
*snac
)
918 unsigned int uin
= snac
->getUIN();
923 void Client::SignalUINRequestError()
925 NewUINEvent
e(0,false);
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(),
939 void Client::SignalLog(LogEvent::LogType type
, const string
& msg
)
941 LogEvent
ev(type
,msg
);
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
);
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
);
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
)
1002 void Client::dc_socket_cb(SocketEvent
*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() );
1034 ostr
<< "Received Buddy Online for "
1036 << " (" << c
->getUIN() << ") " << Status_text
[old_st
]
1037 << "->" << Status_text
[ c
->getStatus() ] << " from server";
1038 SignalLog(LogEvent::INFO
, ostr
.str() );
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);
1054 ostr
<< "Received Buddy Offline for "
1056 << " (" << c
->getUIN() << ") from server";
1057 SignalLog(LogEvent::INFO
, ostr
.str() );
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
)
1070 b
<< (unsigned char) 42;
1073 Buffer::marker mk
= b
.getAutoSizeShortMarker();
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());
1090 void Client::FLAPwrapSNACandSend(const OutSNAC
& snac
)
1093 FLAPwrapSNAC(b
, snac
);
1097 void Client::SendAuthReq() {
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");
1116 SignalLog(LogEvent::INFO
, "Sending Authorisation Request");
1120 void Client::SendNewUINReq()
1125 mk
= FLAPHeader(b
, 0x01, NextSeqNum());
1126 b
<< (unsigned int)0x00000001;
1130 SignalLog(LogEvent::INFO
, "Sending New UIN Request");
1131 FLAPwrapSNACandSend( UINRequestSNAC(m_password
) );
1134 void Client::SendCookie() {
1136 Buffer::marker mk
= FLAPHeader(b
,0x01,NextSeqNum());
1138 b
<< (unsigned int)0x00000001;
1140 b
<< CookieTLV(m_cookie_data
, m_cookie_length
);
1143 SignalLog(LogEvent::INFO
, "Sending Login Cookie");
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() {
1180 // startup listening server at this point, so we
1181 // know the listening port and ip
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
);
1187 m_listenServer
->StartServer();
1189 SignalAddSocket( m_listenServer
->getSocketHandle(), SocketEvent::READ
);
1191 ostr
<< "Server listening on " << IPtoString( m_serverSocket
->getLocalIP() ) << ":" << m_listenServer
->getPort();
1192 SignalLog(LogEvent::INFO
, ostr
.str());
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");
1222 m_last_server_ping
= time(NULL
);
1225 void Client::SendRequestSBL()
1229 FLAPwrapSNAC( b
, SBLRequestRightsSNAC() );
1230 FLAPwrapSNAC( b
, SBLRequestListSNAC() );
1232 SignalLog(LogEvent::INFO
, "Sending Request Server-based list");
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
) {
1260 ostr
<< "Sending packet to Server" << endl
<< b
;
1261 SignalLog(LogEvent::PACKET
, ostr
.str());
1262 m_serverSocket
->Send(b
);
1263 } catch(SocketException e
) {
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() {
1276 while (m_serverSocket
->connected()) {
1277 if (!m_serverSocket
->Recv(*m_recv
)) break;
1280 } catch(SocketException e
) {
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()) {
1302 *m_recv
>> start_byte
;
1303 if (start_byte
!= 42) {
1305 SignalLog(LogEvent::WARN
, "Invalid Start Byte on FLAP");
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;
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
1325 m_recv
->chopOffBuffer( sb
, data_len
+6 );
1329 ostr
<< "Received packet from Server" << endl
<< sb
;
1330 SignalLog(LogEvent::PACKET
, ostr
.str());
1341 ParseCh1(sb
,seq_num
);
1344 ParseCh2(sb
,seq_num
);
1347 ParseCh3(sb
,seq_num
);
1350 ParseCh4(sb
,seq_num
);
1353 ostr
<< "FLAP on unrecognised channel 0x" << std::hex
<< (int)channel
;
1354 SignalLog(LogEvent::WARN
, ostr
.str());
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
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
) {
1384 SignalLog(LogEvent::INFO
, "Connection Acknowledge from server");
1385 m_state
= AUTH_AWAITING_AUTH_REPLY
;
1386 } else if (m_state
== UIN_AWAITING_CONN_ACK
) {
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
1401 m_state
= BOS_AWAITING_LOGIN_REPLY
;
1404 SignalLog(LogEvent::WARN
, "Unknown packet received on channel 0x01");
1409 void Client::ParseCh2(Buffer
& b
, unsigned short seq_num
)
1414 snac
= ParseSNAC(b
);
1416 catch(ParseException e
)
1419 ostr
<< "Problem parsing SNAC: " << e
.what();
1420 SignalLog(LogEvent::WARN
, ostr
.str());
1424 switch(snac
->Family())
1428 switch(snac
->Subtype())
1430 case SNAC_GEN_ServerReady
:
1431 SignalLog(LogEvent::INFO
, "Received Server Ready from server");
1434 case SNAC_GEN_RateInfo
:
1435 SignalLog(LogEvent::INFO
, "Received Rate Information from server");
1437 SendPersonalInfoRequest();
1438 SendAddICBMParameter();
1440 if (m_fetch_sbl
) SendRequestSBL();
1443 case SNAC_GEN_CapAck
:
1444 SignalLog(LogEvent::INFO
, "Received Capabilities Ack from server");
1445 SendRateInfoRequest();
1447 case SNAC_GEN_UserInfo
:
1448 SignalLog(LogEvent::INFO
, "Received User Info from server");
1449 HandleUserInfoSNAC(static_cast<UserInfoSNAC
*>(snac
));
1452 SignalLog(LogEvent::INFO
, "Received MOTD from server");
1454 case SNAC_GEN_RateInfoChange
:
1455 SignalLog(LogEvent::INFO
, "Received Rate Info Change from server");
1456 SignalRateInfoChange(static_cast<RateInfoChangeSNAC
*>(snac
));
1462 switch(snac
->Subtype())
1464 case SNAC_BUD_Online
:
1465 SignalUserOnline(static_cast<BuddyOnlineSNAC
*>(snac
));
1467 case SNAC_BUD_Offline
:
1468 SignalUserOffline(static_cast<BuddyOfflineSNAC
*>(snac
));
1474 switch(snac
->Subtype())
1476 case SNAC_MSG_Message
:
1477 SignalLog(LogEvent::INFO
, "Received Message from server");
1478 SignalMessage(static_cast<MessageSNAC
*>(snac
));
1480 case SNAC_MSG_MessageACK
:
1481 SignalLog(LogEvent::INFO
, "Received Message ACK from server");
1482 SignalMessageACK(static_cast<MessageACKSNAC
*>(snac
));
1484 case SNAC_MSG_OfflineUser
:
1485 SignalLog(LogEvent::INFO
, "Received Message to Offline User from server");
1486 SignalMessageOfflineUser(static_cast<MessageOfflineUserSNAC
*>(snac
));
1492 switch(snac
->Subtype())
1494 case SNAC_SRV_Response
:
1495 SignalLog(LogEvent::INFO
, "Received Server Response from server");
1496 SignalSrvResponse(static_cast<SrvResponseSNAC
*>(snac
));
1502 switch(snac
->Subtype())
1504 case SNAC_UIN_Response
:
1505 SignalLog(LogEvent::INFO
, "Received UIN Response from server");
1506 SignalUINResponse(static_cast<UINResponseSNAC
*>(snac
));
1508 case SNAC_UIN_RequestError
:
1509 SignalLog(LogEvent::ERROR
, "Received UIN Request Error from server");
1510 SignalUINRequestError();
1516 switch(snac
->Subtype())
1518 case SNAC_SBL_Rights_Reply
:
1519 SignalLog(LogEvent::INFO
, "Server-based contact list rights granted\n");
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();
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
)
1538 case SBLEditACKSNAC::Success
:
1539 SignalLog(LogEvent::INFO
, "Server-based contact list modification succeeded\n");
1540 //updresults.push_back(ServerBasedContactEvent::Success);
1542 case SBLEditACKSNAC::Failed
:
1543 SignalLog(LogEvent::INFO
, "Server-based contact list modification failed\n");
1544 //updresults.push_back(ServerBasedContactEvent::Failed);
1546 case SBLEditACKSNAC::AuthRequired
:
1547 SignalLog(LogEvent::INFO
, "Failed, authentification is required to add a user\n");
1548 //updresults.push_back(ServerBasedContactEvent::AuthRequired);
1550 case SBLEditACKSNAC::AlreadyExists
:
1551 SignalLog(LogEvent::INFO
, "Already exists on the server-based contact list\n");
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());
1581 ct->setServerBased(ev->getType() == ServerBasedContactEvent::Upload);
1589 ev->setUploadResults(updresults);
1590 server_based_contact_list.emit(ev);
1593 m_reqidcache->remove( snac->RequestID() );
1595 SignalLog(LogEvent::WARN, "Request ID cached value is not for a server-based contacts upload request");
1598 SignalLog(LogEvent::WARN, "SBL Edit acknowledge from server for a non-existent upload");
1604 } // switch(SBL Subtype)
1609 if (dynamic_cast<RawSNAC
*>(snac
))
1612 ostr
<< "Unknown SNAC packet received - Family: 0x" << std::hex
<< snac
->Family()
1613 << " Subtype: 0x" << snac
->Subtype();
1614 SignalLog(LogEvent::WARN
, ostr
.str());
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
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
]);
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();
1664 DisconnectedEvent::Reason st
;
1666 if (tlvlist
.exists(TLV_ErrorCode
)) {
1667 ErrorCodeTLV
*t
= static_cast<ErrorCodeTLV
*>(tlvlist
[TLV_ErrorCode
]);
1669 ostr
<< "Error logging in Error Code: " << t
->Value();
1670 SignalLog(LogEvent::ERROR
, ostr
.str());
1671 switch(t
->Value()) {
1673 st
= DisconnectedEvent::FAILED_BADUSERNAME
;
1676 st
= DisconnectedEvent::FAILED_TURBOING
;
1679 st
= DisconnectedEvent::FAILED_BADPASSWORD
;
1682 st
= DisconnectedEvent::FAILED_MISMATCH_PASSWD
;
1685 st
= DisconnectedEvent::FAILED_TURBOING
;
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
;
1694 st
= DisconnectedEvent::REQUESTED
;
1696 DisconnectAuthorizer();
1697 SignalDisconnect(st
); // signal client (error)
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()) {
1711 st
= DisconnectedEvent::FAILED_DUALLOGIN
;
1714 st
= DisconnectedEvent::FAILED_UNKNOWN
;
1718 SignalLog(LogEvent::WARN
, "Unknown packet received on channel 4, disconnecting");
1719 st
= DisconnectedEvent::FAILED_UNKNOWN
;
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.
1741 time_t now
= time(NULL
);
1742 if (now
> m_last_server_ping
+ 60)
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)
1791 m_serverSocket
->FinishNonBlockingConnect();
1792 } catch(SocketException e
) {
1793 // signal connection failure
1795 ostr
<< "Failed on non-blocking connect: " << e
.what();
1796 SignalLog(LogEvent::ERROR
, ostr
.str());
1797 Disconnect(DisconnectedEvent::FAILED_LOWLEVEL
);
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
)) {
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) )
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");
1845 SignalLog(LogEvent::INFO
, "Incoming filetransfer connection on already open connection.");
1849 * File descriptor is a direct connection we have open to someone
1854 if (m_dccache
->exists(fd
))
1856 dc
= (*m_dccache
)[fd
];
1858 else if(m_smtp
->getfd() == fd
)
1862 else if (m_ftcache
->exists(fd
))
1864 dc
= (*m_ftcache
)[fd
];
1868 SignalLog(LogEvent::ERROR
, "Problem: Unassociated socket");
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)
1878 sock
->FinishNonBlockingConnect();
1879 } catch(SocketException e
) {
1880 // signal connection failure
1882 ostr
<< "Failed on non-blocking connect for direct connection: " << e
.what();
1883 SignalLog(LogEvent::ERROR
, ostr
.str());
1884 DisconnectDirectConn( fd
);
1888 SignalRemoveSocket(fd
);
1889 // no longer select on write
1891 SignalAddSocket(fd
, SocketEvent::READ
);
1892 // select on read now
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
)) {
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
1926 dc
->SendEvent(NULL
);
1927 } catch(DisconnectedException e
) {
1928 // tear down connection
1929 SignalLog(LogEvent::WARN
, e
.what());
1930 DisconnectDirectConn( fd
);
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.
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
);
1994 case MessageEvent::Email
:
1995 m_smtp
->SendEvent(ev
);
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;
2013 DirectClient
* Client::ConnectDirect(const ContactRef
& c
)
2015 DirectClient
*dc
= m_dccache
->getByContact(c
);
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
) ;
2035 } catch(DisconnectedException e
) {
2036 SignalLog(LogEvent::WARN
, e
.what());
2039 } catch(SocketException e
) {
2040 SignalLog(LogEvent::WARN
, e
.what());
2044 SignalLog(LogEvent::WARN
, "Uncaught exception");
2048 (*m_dccache
)[ dc
->getfd() ] = 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
);
2072 SendViaServerNormal(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
);
2088 ev
->setFinished(true);
2089 ev
->setDelivered(false);
2090 ev
->setDirect(false);
2091 ev
->setDeliveryFailureReason(MessageEvent::Failed_ClientNotCapable
);
2092 messageack
.emit(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
);
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
)
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
);
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
);
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
);
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
);
2200 void Client::PingServer()
2203 Buffer::marker mk
= FLAPHeader(b
,0x05,NextSeqNum());
2208 void Client::uploadSelfDetails()
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
) ) );
2251 void Client::fetchServerBasedContactList()
2254 if (m_state
== BOS_LOGGED_IN
) SendRequestSBL();
2257 void Client::fetchServerBasedContactList(int)
2262 void Client::uploadServerBasedContact(const ContactRef
& c
)
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 );
2279 void Client::updateServerBasedContact(const ContactRef
& c
)
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() );
2293 void Client::uploadServerBasedGroup(const ContactTree::Group
& gp
)
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() );
2310 void Client::uploadServerBasedContactList()
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() );
2333 void Client::removeServerBasedContactList()
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() );
2354 void Client::removeServerBasedContact(const ContactRef
& c
)
2358 FLAPwrapSNAC( b
, SBLBeginEditSNAC() );
2359 FLAPwrapSNAC( b
, SBLRemoveEntrySNAC(c
) );
2360 FLAPwrapSNAC( b
, SBLCommitEditSNAC() );
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
);
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)
2394 * - when going from invisible to visible
2396 * - send the Add to Invisible list (or better named Set in this case)
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
) );
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
) {
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
)
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
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
) );
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
) );
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()
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
];
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
) {
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
);
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
;
2783 case RANGE_60_ABOVE
:
2788 min_age
= max_age
= 0;
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
,
2796 ssnac
.setRequestID( reqid
);
2798 SignalLog(LogEvent::INFO
, "Sending full whitepage search");
2799 FLAPwrapSNACandSend( ssnac
);
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
);
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
);
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
);
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();
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
))
2880 ev
->setError("I/O error trying to resolve given filename.");
2881 ev
->setState(FileTransferEvent::ERROR
);
2882 filetransfer_update_signal
.emit(ev
);
2885 ev
->setState(FileTransferEvent::WAIT_RESPONS
);
2886 filetransfer_update_signal
.emit(ev
);
2887 ev
->setDirect(ev
->getContact()->getDirect());
2893 FileTransferClient
*ftc
= new FileTransferClient(m_self
,
2899 ftc
->logger
.connect( this, &Client::dc_log_cb
) ;
2900 ftc
->socket
.connect( this, &Client::dc_socket_cb
) ;
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
);
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
);
2926 SignalLog(LogEvent::WARN
, "Uncaught exception");
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());
2957 dc
= ConnectDirect(ev
->getContact());
2960 ev
->setState(FileTransferEvent::ERROR
);
2961 ev
->setError("Couldn't open an Direct connection to target");
2962 filetransfer_update_signal
.emit(ev
);
2966 if (ev
->getState() == FileTransferEvent::ACCEPTED
) {
2967 FileTransferClient
*ftc
= new FileTransferClient(m_self
,
2971 SignalAddSocket(ftc
->getlistenfd(), SocketEvent::READ
);
2972 ev
->setPort(ftc
->getlistenPort());
2974 (*m_ftcache
)[ftc
->getlistenfd()] = ftc
;
2978 SignalLog(LogEvent::INFO
, "Sending FileTransfer ACK direct");
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
);
2989 ev
->setState(FileTransferEvent::NOT_CONNECTED
);
2993 MessageSNAC
*snac
= new MessageSNAC();
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
,
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
);
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
)
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
:
3036 case FileTransferEvent::WAIT_RESPONS
:
3037 if (ev
->isDirect()) {
3038 // Through Direct Connection
3039 DirectClient
*dc
= m_dccache
->getByContact(ev
->getContact());
3041 dc
= ConnectDirect(ev
->getContact());
3044 ev
->setState(FileTransferEvent::ERROR
);
3045 ev
->setError("Couldn't send filetransfer cancel to target direct");
3046 filetransfer_update_signal
.emit(ev
);
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");
3059 SignalLog(LogEvent::INFO
, "Sending FT Cancel through server not implemented yet.");
3060 //Don't know how to do this
3063 case FileTransferEvent::CLOSE
:
3064 case FileTransferEvent::ACCEPTED
:
3065 case FileTransferEvent::TIMEOUT
:
3066 FileTransferClient
*ftc
= m_ftcache
->getByEvent(ev
);
3068 DisconnectDirectConn(ftc
->getfd());
3073 ev
->setState(FileTransferEvent::NOT_CONNECTED
);
3074 filetransfer_update_signal
.emit(ev
);
3083 unsigned int Client::getUIN() const
3085 return m_self
->getUIN();
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
);
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
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
) {
3191 * get the port that will be used on the BOS server.
3193 * @return the port number
3195 unsigned short Client::getBOSServerPort() const {
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
) {
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
3239 * set whether to make outgoing direct connections
3241 * @d whether to make outgoing direct connections
3243 void Client::setUseOutDC(bool d
) {
3248 * get whether to make outgoing direct connections
3250 * @return whether to make outgoing direct connections
3252 bool Client::getUseOutDC() const
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
) {
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
) {
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
) {
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
;
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
;
3356 m_translator
= new NULLTranslator();