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