1 /*
2 * This file is part of Licq, an instant messaging client for UNIX.
3 * Copyright (C) 2007-2013 Licq developers <licq-dev@googlegroups.com>
4 *
5 * Licq is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * Licq is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Licq; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "oscarservice.h"
21
22 #include <boost/scoped_array.hpp>
23 #include <cerrno>
24 #include <cstdio>
25 #include <cstdlib>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 #include <licq/buffer.h>
32 #include <licq/contactlist/usermanager.h>
33 #include <licq/byteorder.h>
34 #include <licq/daemon.h>
35 #include <licq/event.h>
36 #include <licq/plugin/pluginmanager.h>
37 #include <licq/pluginsignal.h>
38 #include <licq/proxy.h>
39 #include <licq/logging/log.h>
40
41 #include "gettext.h"
42 #include "icq.h"
43 #include "packet-srv.h"
44 #include "socket.h"
45 #include "user.h"
46
47 using namespace LicqIcq;
48 using Licq::gLog;
49 using Licq::gDaemon;
50 using std::string;
51
COscarService(unsigned short Fam)52 COscarService::COscarService(unsigned short Fam)
53 {
54 myFam = Fam;
55 mySocketDesc = -1;
56 myProxy = NULL;
57 myStatus = STATUS_UNINITIALIZED;
58 pthread_mutex_init(&mutex_sendqueue, NULL);
59 pthread_cond_init(&cond_sendqueue, NULL);
60 pthread_mutex_init(&mutex_status, NULL);
61 pthread_cond_init(&cond_status, NULL);
62 }
63
~COscarService()64 COscarService::~COscarService()
65 {
66 if (myProxy) delete myProxy;
67 }
68
ChangeStatus(EOscarServiceStatus s)69 void COscarService::ChangeStatus(EOscarServiceStatus s)
70 {
71 pthread_mutex_lock(&mutex_status);
72 myStatus = s;
73 pthread_cond_signal(&cond_status);
74 pthread_mutex_unlock(&mutex_status);
75 }
76
WaitForStatus(EOscarServiceStatus s)77 bool COscarService::WaitForStatus(EOscarServiceStatus s)
78 {
79 pthread_mutex_lock(&mutex_status);
80
81 struct timespec ts;
82 ts.tv_nsec = 0;
83 //wait for 120 seconds
84 ts.tv_sec = time(NULL) + 120;
85
86 if (pthread_cond_timedwait(&cond_status, &mutex_status, &ts) == ETIMEDOUT)
87 {
88 pthread_mutex_unlock(&mutex_status);
89 return false;
90 }
91 if (myStatus == s)
92 {
93 pthread_mutex_unlock(&mutex_status);
94 return true;
95 }
96
97 pthread_mutex_unlock(&mutex_status);
98 return false;
99 }
100
setConnectCredential(const string & server,unsigned short port,const string & cookie)101 void COscarService::setConnectCredential(const string& server,
102 unsigned short port, const string& cookie)
103 {
104 myServer = server;
105 myPort = port;
106 myCookie = cookie;
107 }
108
SendPacket(CPacket * p)109 bool COscarService::SendPacket(CPacket *p)
110 {
111 Licq::INetSocket* s = gSocketManager.FetchSocket(mySocketDesc);
112 if (s == NULL) return false;
113 Licq::Buffer* b = p->Finalize(s);
114 if (!s->send(*b))
115 {
116 gLog.warning(tr("Error sending event (FAM #%02X, Subtype #%02X, Sequence #%hu): %s."),
117 (unsigned short)((p->SNAC() >> 16) & 0xffff), (unsigned short)(p->SNAC() & 0xffff),
118 p->Sequence(), s->errorStr().c_str());
119 gSocketManager.DropSocket(s);
120 delete b;
121 return false;
122 }
123 gSocketManager.DropSocket(s);
124 delete b;
125 return true;
126 }
127
ClearQueue()128 void COscarService::ClearQueue()
129 {
130 pthread_mutex_lock(&mutex_sendqueue);
131 std::list<Licq::Event*>::iterator iter;
132 unsigned long i = mySendQueue.size();
133 for (iter = mySendQueue.begin(); i > 0; i--)
134 {
135 Licq::Event* e = *iter;
136 mySendQueue.erase(iter);
137 if (e != NULL)
138 {
139 gLog.info(tr("Event #%hu is still on the service 0x%02X queue!"), e->Sequence(), myFam);
140 delete e;
141 }
142 }
143 pthread_mutex_unlock(&mutex_sendqueue);
144 }
145
SendEvent(pthread_t caller,unsigned long eventId,const Licq::UserId & userId,unsigned short SubType,bool Request)146 void COscarService::SendEvent(pthread_t caller, unsigned long eventId,
147 const Licq::UserId& userId, unsigned short SubType, bool Request)
148 {
149 Licq::Event* e = new Licq::Event(caller, eventId, mySocketDesc, NULL, Licq::Event::ConnectServer, userId);
150 e->SetSubType(SubType);
151 if (Request)
152 gIcqProtocol.PushEvent(e);
153 else
154 e->SetNoAck(true);
155 pthread_mutex_lock(&mutex_sendqueue);
156 mySendQueue.push_back(e);
157 pthread_cond_signal(&cond_sendqueue);
158 pthread_mutex_unlock(&mutex_sendqueue);
159 }
160
SendBARTFam(Licq::Event * e)161 bool COscarService::SendBARTFam(Licq::Event* e)
162 {
163 switch (e->SubType())
164 {
165 case ICQ_SNACxBART_DOWNLOADxREQUEST:
166 {
167 CPU_RequestBuddyIcon* p;
168 {
169 UserReadGuard u(e->userId());
170 if (!u.isLocked())
171 return false;
172 p = new CPU_RequestBuddyIcon(u->accountId(),
173 u->buddyIconType(), u->buddyIconHashType(), u->buddyIconHash(), myFam);
174 gLog.info(tr("Requesting buddy icon for %s (#%hu/#%d)..."),
175 u->getAlias().c_str(), p->Sequence(), p->SubSequence());
176 }
177 e->AttachPacket(p);
178 return (SendPacket(p));
179 }
180
181 default:
182 gLog.warning(tr("Event with unsupported subtype (%02X) for FAM %02X failed."),
183 e->SubType(), myFam);
184 return false;
185 }
186
187 return false;
188 }
189
ProcessPacket(Buffer & packet)190 bool COscarService::ProcessPacket(Buffer& packet)
191 {
192 unsigned short Len;
193 unsigned short Sequence;
194 char startCode, Channel;
195
196 // read in the serveice header info
197 packet >> startCode;
198
199 if (startCode != 0x2a)
200 {
201 gLog.warning(tr("bad start code %d for packet in socket of service 0x%02X."),
202 startCode, myFam);
203 return false;
204 }
205
206 packet >> Channel
207 >> Sequence
208 >> Len;
209
210 Sequence = BSWAP_16(Sequence);
211 Len = BSWAP_16(Len);
212
213 switch (Channel)
214 {
215 case ICQ_CHNxNEW:
216 ProcessNewChannel(packet);
217 break;
218
219 case ICQ_CHNxDATA:
220 ProcessDataChannel(packet);
221 break;
222
223 case ICQ_CHNxCLOSE:
224 gLog.info(tr("Server send us request for close service 0x%02X."), myFam);
225 return false;
226 break;
227
228 default:
229 gLog.warning(tr("Packet from unhandled channel %02x for service 0x%02X."),
230 Channel, myFam);
231 break;
232 }
233
234 return true;
235 }
236
ProcessNewChannel(Buffer & packet)237 void COscarService::ProcessNewChannel(Buffer& packet)
238 {
239 unsigned long Version = packet.UnpackUnsignedLongBE();
240
241 if (Version != 0x00000001)
242 {
243 gLog.warning(tr("Packet with wrong version (0x%08lx) from new channel for service 0x%02X."),
244 Version, myFam);
245 }
246 }
247
ProcessDataChannel(Buffer & packet)248 void COscarService::ProcessDataChannel(Buffer& packet)
249 {
250 unsigned short Family, SubType, Flags;
251 unsigned long RequestId;
252
253 packet >> Family >> SubType >> Flags >> RequestId;
254 Family = BSWAP_16(Family);
255 SubType = BSWAP_16(SubType);
256 Flags = BSWAP_16(Flags);
257 RequestId = BSWAP_32(RequestId);
258
259 if (Flags & 0x8000) // version of the family that this SNAC, just ignore it
260 {
261 unsigned short len = packet.UnpackUnsignedShortBE();
262 packet.incDataPosRead(len);
263 }
264
265 switch (Family)
266 {
267 case ICQ_SNACxFAM_SERVICE:
268 ProcessServiceFam(packet, SubType, RequestId);
269 break;
270
271 case ICQ_SNACxFAM_BART:
272 if (myFam == ICQ_SNACxFAM_BART)
273 ProcessBARTFam(packet, SubType, RequestId);
274 else
275 gLog.warning(tr("Unsupported family %04hx on data channel of service %02X."),
276 Family, myFam);
277 break;
278
279 default:
280 gLog.warning(tr("Unknown or usupported family %04hx on data channel of service %02X."),
281 Family, myFam);
282 break;
283 }
284 }
285
ProcessServiceFam(Buffer & packet,unsigned short SubType,unsigned long RequestId)286 void COscarService::ProcessServiceFam(Buffer& packet, unsigned short SubType,
287 unsigned long RequestId)
288 {
289 switch (SubType)
290 {
291 case ICQ_SNACxSUB_ERROR:
292 {
293 unsigned short err = packet.UnpackUnsignedShortBE();
294 unsigned short suberr = 0;
295
296 packet.readTLV();
297 if (packet.getTLVLen(0x0008) == 2)
298 suberr = packet.UnpackUnsignedShortTLV(0x0008);
299 gLog.warning(tr("Error #%02x.%02x in control FAM request (%ld) for service 0x%02X."),
300 err, suberr, RequestId, myFam);
301 break;
302 }
303
304 case ICQ_SNACxSUB_READYxSERVER:
305 gLog.info(tr("Server says he's ready for service 0x%02X."), myFam);
306 ChangeStatus(STATUS_SRV_READY_RECV);
307 break;
308
309 case ICQ_SNACxSRV_ACKxIMxICQ:
310 gLog.info(tr("Server sent us channel capability list for service 0x%02X."), myFam);
311 ChangeStatus(STATUS_SRV_VER_RECV);
312 break;
313
314 case ICQ_SNACxSUB_RATE_INFO:
315 gLog.info(tr("Server sent us rate-limits information for service 0x%02X."), myFam);
316 ChangeStatus(STATUS_SRV_RATE_RECV);
317 break;
318
319 default:
320 gLog.warning(tr("Unknown or unsupported service FAM subtype 0x%02X for service 0x%02X."),
321 SubType, myFam);
322 break;
323 }
324 }
325
ProcessBARTFam(Buffer & packet,unsigned short SubType,unsigned long RequestId)326 void COscarService::ProcessBARTFam(Buffer& packet, unsigned short SubType,
327 unsigned long RequestId)
328 {
329 switch (SubType)
330 {
331 case ICQ_SNACxBART_ERROR:
332 {
333 unsigned short err = packet.UnpackUnsignedShortBE();
334 unsigned short suberr = 0;
335
336 packet.readTLV();
337 if (packet.getTLVLen(0x0008) == 2)
338 suberr = packet.UnpackUnsignedShortTLV(0x0008);
339 gLog.warning(tr("Error #%02x.%02x in BART request (%ld) for service 0x%02X."),
340 err, suberr, RequestId, myFam);
341
342 Licq::Event* e = gIcqProtocol.DoneServerEvent(RequestId, Licq::Event::ResultError);
343 if (e)
344 gIcqProtocol.ProcessDoneEvent(e);
345 break;
346 }
347
348 case ICQ_SNACxBART_DOWNLOADxREPLY:
349 {
350 string id = packet.unpackByteString();
351 UserWriteGuard u(Licq::UserId(gIcqProtocol.ownerId(), id));
352 if (!u.isLocked())
353 {
354 gLog.warning(tr("Buddy icon for unknown user (%s)."), id.c_str());
355 break;
356 }
357
358 unsigned short IconType = packet.UnpackUnsignedShortBE();
359 char HashType = packet.UnpackChar();
360 char HashLength = packet.UnpackChar();
361 switch (IconType)
362 {
363 case BART_TYPExBUDDY_ICON_SMALL:
364 case BART_TYPExBUDDY_ICON:
365 {
366 if ((HashType == 0 || HashType == 1) && HashLength > 0 && HashLength <= 16)
367 {
368 string hash = packet.unpackRawString(HashLength);
369 packet.UnpackChar(); // unknown (command ?)
370 packet.UnpackUnsignedShortBE(); // IconType once more
371 packet.UnpackChar(); // HashType once more
372 char HashLength2 = packet.UnpackChar(); // Hash once more
373 packet.incDataPosRead(HashLength2); // Hash once more
374 u->setOurBuddyIconHash(hash);
375
376 gLog.info(tr("Buddy icon reply for %s."), u->getAlias().c_str());
377 unsigned short IconLen = packet.UnpackUnsignedShortBE();
378 if (IconLen > 0) // do not create empty .pic files
379 {
380 int FD = open(u->pictureFileName().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 00664);
381 if (FD == -1)
382 {
383 gLog.error(tr("Unable to open picture file (%s): %s."),
384 u->pictureFileName().c_str(), strerror(errno));
385 break;
386 }
387
388 string icon = packet.unpackRawString(IconLen);
389 write(FD, icon.c_str(), IconLen);
390 close(FD);
391
392 u->SetEnableSave(false);
393 u->SetPicturePresent(true);
394 u->SetEnableSave(true);
395 }
396 u->save(Licq::User::SavePictureInfo);
397 Licq::gPluginManager.pushPluginSignal(new Licq::PluginSignal(
398 Licq::PluginSignal::SignalUser,
399 Licq::PluginSignal::UserPicture, u->id()));
400
401 Licq::Event* e = gIcqProtocol.DoneServerEvent(RequestId, Licq::Event::ResultSuccess);
402 if (e)
403 gIcqProtocol.ProcessDoneEvent(e);
404 }
405 else
406 {
407 gLog.warning(tr("Buddy icon reply for %s with wrong or unsupported hashtype (%d) or hashlength (%d)."),
408 u->getAlias().c_str(), HashType, HashLength);
409 Licq::Event* e = gIcqProtocol.DoneServerEvent(RequestId, Licq::Event::ResultFailed);
410 if (e)
411 gIcqProtocol.ProcessDoneEvent(e);
412 }
413 break;
414 }
415
416 default:
417 {
418 gLog.warning(tr("Buddy icon reply for %s with wrong or unsupported icontype (0x%02x)."),
419 u->getAlias().c_str(), IconType);
420 Licq::Event* e = gIcqProtocol.DoneServerEvent(RequestId, Licq::Event::ResultFailed);
421 if (e)
422 gIcqProtocol.ProcessDoneEvent(e);
423 break;
424 }
425 }
426 break;
427 }
428
429 default:
430 break;
431 }
432 }
433
Initialize()434 bool COscarService::Initialize()
435 {
436 ChangeStatus(STATUS_SERVICE_REQ_SENT);
437 gIcqProtocol.icqRequestService(myFam);
438
439 if (!WaitForStatus(STATUS_SERVICE_REQ_ACKED))
440 {
441 gLog.warning(tr("Give up waiting for redirect reply while initializing service 0x%02X."),
442 myFam);
443 ChangeStatus(STATUS_UNINITIALIZED);
444 return false;
445 }
446
447 ChangeStatus(STATUS_CONNECTED);
448 SrvSocket* s = new SrvSocket(gIcqProtocol.ownerId());
449 gLog.info(tr("Connecting to separate server for service 0x%02X."), myFam);
450 if (gIcqProtocol.GetProxy() == NULL)
451 {
452 if (myProxy != NULL)
453 {
454 delete myProxy;
455 myProxy = NULL;
456 }
457 }
458 else
459 {
460 if (myProxy == NULL)
461 myProxy = gDaemon.createProxy();
462 }
463 if (!s->connectTo(myServer, myPort, myProxy))
464 {
465 gLog.warning(tr("Can't establish service 0x%02X socket."), myFam);
466 ChangeStatus(STATUS_UNINITIALIZED);
467 return false;
468 }
469 mySocketDesc = s->Descriptor();
470 gSocketManager.AddSocket(s);
471 gSocketManager.DropSocket(s);
472 // Alert the select thread that there is a new socket
473 gIcqProtocol.myNewSocketPipe.putChar('S');
474
475 CPU_SendCookie* p1 = new CPU_SendCookie(myCookie, myFam);
476 gLog.info(tr("Sending cookie for service 0x%02X."), myFam);
477 if (!SendPacket(p1))
478 {
479 gLog.warning(tr("Can't send cookie while initializing service 0x%02X."), myFam);
480 ChangeStatus(STATUS_UNINITIALIZED);
481 return false;
482 }
483
484 if (!WaitForStatus(STATUS_SRV_READY_RECV))
485 {
486 gLog.warning(tr("Give up waiting for server ready packet while initializing service 0x%02X."),
487 myFam);
488 ChangeStatus(STATUS_UNINITIALIZED);
489 return false;
490 }
491
492 unsigned short VerArray[2][2] = {{ 0x0001, 0x0004 }, // Service FAM
493 { 0x0010, 0x0001 }}; // BART FAM
494 CPU_ImICQ *p2 = new CPU_ImICQ(VerArray, 2, myFam);
495 gLog.info(tr("Sending our families versions for service 0x%02X."), myFam);
496 if (!SendPacket(p2))
497 {
498 gLog.warning(tr("Can't send channel capability request while initializing service 0x%02X."),
499 myFam);
500 ChangeStatus(STATUS_UNINITIALIZED);
501 return false;
502 }
503
504 if (!WaitForStatus(STATUS_SRV_VER_RECV))
505 {
506 gLog.warning(tr("Give up waiting for channel capability list while initializing service 0x%02X."),
507 myFam);
508 ChangeStatus(STATUS_UNINITIALIZED);
509 return false;
510 }
511
512 CPU_GenericFamily *p3 = new CPU_GenericFamily(ICQ_SNACxFAM_SERVICE,
513 ICQ_SNACxSUB_REQ_RATE_INFO, myFam);
514 gLog.info(tr("Sending request of rate-limits for service 0x%02X."), myFam);
515 if (!SendPacket(p3))
516 {
517 gLog.warning(tr("Can't send request for rate-limits while initializing service 0x%02X."),
518 myFam);
519 ChangeStatus(STATUS_UNINITIALIZED);
520 return false;
521 }
522
523 if (!WaitForStatus(STATUS_SRV_RATE_RECV))
524 {
525 gLog.warning(tr("Give up waiting for rate-limits while initializing service 0x%02X."),
526 myFam);
527 ChangeStatus(STATUS_UNINITIALIZED);
528 return false;
529 }
530
531 CPU_RateAck *p4 = new CPU_RateAck(myFam);
532 gLog.info(tr("Sending ack for rate-limits for service 0x%02X."), myFam);
533 if (!SendPacket(p4))
534 {
535 gLog.warning(tr("Can't send rate-limits ack while initializing service 0x%02X."), myFam);
536 ChangeStatus(STATUS_UNINITIALIZED);
537 return false;
538 }
539 unsigned short VerArray2[2][4] = {{ 0x0001, 0x0004, 0x0110, 0x08e4 }, // Service FAM
540 { 0x0010, 0x0001, 0x0110, 0x08e4 }}; // BART FAM
541 CPU_ClientReady *p5 = new CPU_ClientReady(VerArray2, 2, myFam);
542 gLog.info(tr("Sending client ready for service 0x%02X."), myFam);
543 if (!SendPacket(p5))
544 {
545 gLog.warning(tr("Can't send client ready while initializing service 0x%02X."), myFam);
546 ChangeStatus(STATUS_UNINITIALIZED);
547 return false;
548 }
549
550 ChangeStatus(STATUS_READY);
551 return true;
552 }
553
OscarServiceSendQueue_tep(void * p)554 void* LicqIcq::OscarServiceSendQueue_tep(void *p)
555 {
556 pthread_detach(pthread_self());
557
558 COscarService *os = (COscarService *)p;
559
560 while (true)
561 {
562 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
563 pthread_mutex_lock(&os->mutex_sendqueue);
564 if (!os->mySendQueue.empty())
565 {
566 std::list<Licq::Event*>::iterator iter = os->mySendQueue.begin();
567 Licq::Event* e = *iter;
568 os->mySendQueue.erase(iter);
569 pthread_mutex_unlock(&os->mutex_sendqueue);
570
571 if (e->IsCancelled())
572 {
573 delete e;
574 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
575 pthread_testcancel();
576 continue;
577 }
578
579 if (gIcqProtocol.Status() != STATUS_ONLINE)
580 {
581 gLog.warning(tr("Can't send event for service 0x%02X because we are not online."),
582 os->myFam);
583 if (gIcqProtocol.DoneEvent(e, Licq::Event::ResultError) != NULL)
584 gIcqProtocol.ProcessDoneEvent(e);
585 else
586 delete e;
587 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
588 pthread_testcancel();
589 continue;
590 }
591
592 if (os->mySocketDesc == -1)
593 {
594 gLog.info(tr("Initializing socket for service 0x%02X."), os->myFam);
595 if (!os->Initialize())
596 {
597 gLog.warning(tr("Initialization of socket for service 0x%02X failed, failing event."),
598 os->myFam);
599 if (gIcqProtocol.DoneEvent(e, Licq::Event::ResultError) != NULL)
600 gIcqProtocol.ProcessDoneEvent(e);
601 else
602 delete e;
603 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
604 pthread_testcancel();
605 continue;
606 }
607 }
608
609 bool Sent;
610 switch (os->myFam)
611 {
612 case ICQ_SNACxFAM_BART:
613 Sent = os->SendBARTFam(e);
614 break;
615
616 default:
617 gLog.warning(tr("Event for unknown or unsupported service 0x%02X failed."),
618 os->myFam);
619 Sent = false;
620 break;
621 }
622
623 if (!Sent)
624 {
625 if (gIcqProtocol.DoneEvent(e, Licq::Event::ResultError) != NULL)
626 gIcqProtocol.ProcessDoneEvent(e);
627 else
628 delete e;
629 }
630
631 if (e->NoAck())
632 delete e;
633
634 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
635 pthread_testcancel();
636 continue;
637 }
638 else
639 {
640 pthread_cond_wait(&os->cond_sendqueue, &os->mutex_sendqueue);
641 pthread_mutex_unlock(&os->mutex_sendqueue);
642 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
643 pthread_testcancel();
644 }
645 }
646
647 pthread_exit(NULL);
648 }
649