1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 1998-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 "filetransfer.h"
21 
22 #include <cstdio>
23 #include <ctype.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <cerrno>
27 #include <sys/types.h>
28 #include <fcntl.h>
29 #include <string>
30 #include <string.h>
31 #include <sys/time.h>
32 
33 #include <licq/logging/log.h>
34 #include <licq/contactlist/owner.h>
35 #include <licq/daemon.h>
36 
37 #include "gettext.h"
38 #include "icq.h"
39 #include "packet-tcp.h"
40 #include "user.h"
41 
42 #define DEBUG_THREADS(x)
43 
44 const unsigned short FT_STATE_DISCONNECTED = 0;
45 const unsigned short FT_STATE_HANDSHAKE = 1;
46 const unsigned short FT_STATE_WAITxFORxCLIENTxINIT = 2;
47 const unsigned short FT_STATE_WAITxFORxSERVERxINIT = 3;
48 const unsigned short FT_STATE_WAITxFORxSTART = 4;
49 const unsigned short FT_STATE_WAITxFORxFILExINFO = 5;
50 const unsigned short FT_STATE_RECEIVINGxFILE = 6;
51 const unsigned short FT_STATE_SENDINGxFILE = 7;
52 const unsigned short FT_STATE_CONFIRMINGxFILE = 8;
53 
54 using namespace LicqIcq;
55 using Licq::IcqFileTransferEvent;
56 using Licq::Log;
57 using Licq::gDaemon;
58 using Licq::gLog;
59 using std::list;
60 using std::string;
61 
62 //=====FILE==================================================================
63 
64 //-----PacketFile---------------------------------------------------------------
CPacketFile()65 CPacketFile::CPacketFile()
66   : buffer(NULL)
67 {
68   // Empty
69 }
70 
~CPacketFile()71 CPacketFile::~CPacketFile()
72 {
73   delete buffer;
74 }
75 
76 //-----FileInitClient-----------------------------------------------------------
CPFile_InitClient(const string & localName,unsigned long _nNumFiles,unsigned long _nTotalSize)77 CPFile_InitClient::CPFile_InitClient(const string& localName,
78                                     unsigned long _nNumFiles,
79                                     unsigned long _nTotalSize)
80 {
81   m_nSize = 20 + localName.size();
82   InitBuffer();
83 
84   buffer->PackChar(0x00);
85   buffer->PackUnsignedLong(0);
86   buffer->PackUnsignedLong(_nNumFiles);
87   buffer->PackUnsignedLong(_nTotalSize);
88   buffer->PackUnsignedLong(0x64);
89   buffer->PackString(localName.c_str());
90 }
91 
~CPFile_InitClient()92 CPFile_InitClient::~CPFile_InitClient()
93 {
94   // Empty
95 }
96 
97 //-----FileInitServer-----------------------------------------------------------
CPFile_InitServer(const string & localName)98 CPFile_InitServer::CPFile_InitServer(const string& localName)
99 {
100   m_nSize = 8 + localName.size();
101   InitBuffer();
102 
103   buffer->PackChar(0x01);
104   buffer->PackUnsignedLong(0x64);
105   buffer->PackString(localName.c_str());
106 }
107 
~CPFile_InitServer()108 CPFile_InitServer::~CPFile_InitServer()
109 {
110   // Empty
111 }
112 
113 //-----FileBatch----------------------------------------------------------------
CPFile_Info(const string & fileName)114 CPFile_Info::CPFile_Info(const string& fileName)
115 {
116   m_bValid = true;
117   m_nError = 0;
118 
119   struct stat buf;
120 
121   // Remove any path from the filename
122   size_t lastSlash = fileName.rfind('/');
123   myFileName = (lastSlash == string::npos ? fileName : fileName.substr(lastSlash+1));
124 
125   if (stat(myFileName.c_str(), &buf) < 0)
126   {
127     m_bValid = false;
128     m_nError = errno;
129     return;
130   }
131   m_nFileSize = buf.st_size;
132 
133   m_nSize = myFileName.size() + 21;
134   InitBuffer();
135 
136   buffer->PackUnsignedShort(0x02);
137   // Add all the file names
138   buffer->PackString(myFileName.c_str());
139   // Add the empty file name
140   buffer->PackString("");
141   //Add the file length
142   buffer->PackUnsignedLong(m_nFileSize);
143   buffer->PackUnsignedLong(0x00);
144   buffer->PackUnsignedLong(0x64);
145 }
146 
147 
~CPFile_Info()148 CPFile_Info::~CPFile_Info()
149 {
150   // Empty
151 }
152 
153 
154 //-----FileStart----------------------------------------------------------------
CPFile_Start(unsigned long nFilePos,unsigned long nFile)155 CPFile_Start::CPFile_Start(unsigned long nFilePos, unsigned long nFile)
156 {
157   m_nSize = 17;
158   InitBuffer();
159 
160   buffer->PackChar(0x03);
161   buffer->PackUnsignedLong(nFilePos);
162   buffer->PackUnsignedLong(0x00);
163   buffer->PackUnsignedLong(0x64);
164   buffer->PackUnsignedLong(nFile);
165 }
166 
~CPFile_Start()167 CPFile_Start::~CPFile_Start()
168 {
169   // Empty
170 }
171 
172 //-----FileSpeed----------------------------------------------------------------
CPFile_SetSpeed(unsigned long nSpeed)173 CPFile_SetSpeed::CPFile_SetSpeed(unsigned long nSpeed)
174 {
175   m_nSize = 5;
176   InitBuffer();
177 
178   buffer->PackChar(0x05);
179   buffer->PackUnsignedLong(nSpeed);
180 }
181 
~CPFile_SetSpeed()182 CPFile_SetSpeed::~CPFile_SetSpeed()
183 {
184   // Empty
185 }
186 
187 //=====FileTransferManager===========================================================
IcqFileTransferEvent(unsigned char t,const string & fileName)188 IcqFileTransferEvent::IcqFileTransferEvent(unsigned char t, const string& fileName)
189 {
190   m_nCommand = t;
191   myFileName = fileName;
192 }
193 
~IcqFileTransferEvent()194 IcqFileTransferEvent::~IcqFileTransferEvent()
195 {
196   // Empty
197 }
198 
199 
200 FileTransferManagerList CFileTransferManager::ftmList;
201 
202 pthread_mutex_t CFileTransferManager::thread_cancel_mutex
203                                                    = PTHREAD_MUTEX_INITIALIZER;
204 
~IcqFileTransferManager()205 Licq::IcqFileTransferManager::~IcqFileTransferManager()
206 {
207   // Empty
208 }
209 
FileTransferManager(const Licq::UserId & userId)210 FileTransferManager::FileTransferManager(const Licq::UserId& userId)
211   : m_bThreadRunning(false)
212 {
213   myUserId = userId;
214   m_nSession = rand();
215 
216   {
217     Licq::OwnerReadGuard o(gIcqProtocol.ownerId());
218     myLocalName = o->getAlias();
219   }
220 
221   m_nCurrentFile = m_nBatchFiles = 0;
222   m_nFileSize = m_nBatchSize = m_nFilePos = m_nBatchPos = 0;
223   m_nBytesTransfered = m_nBatchBytesTransfered = 0;
224   m_nStartTime = m_nBatchStartTime = 0;
225   m_nFileDesc = -1;
226   m_nState = FT_STATE_DISCONNECTED;
227   m_bThreadCreated = false;
228 
229   myRemoteName = myUserId.accountId();
230 
231   ftmList.push_back(this);
232 }
233 
234 
235 //-----CFileTransferManager::StartFileTransferServer-----------------------------------------
StartFileTransferServer()236 bool CFileTransferManager::StartFileTransferServer()
237 {
238   if (gDaemon.StartTCPServer(&ftServer) == -1)
239   {
240     gLog.warning(tr("No more ports available, add more or close open chat/file sessions."));
241     return false;
242   }
243 
244   // Add the server to the sock manager
245   sockman.AddSocket(&ftServer);
246   sockman.DropSocket(&ftServer);
247 
248   return true;
249 }
250 
251 
252 
receiveFiles(const string & directory)253 bool CFileTransferManager::receiveFiles(const string& directory)
254 {
255   myIsReceiver = true;
256 
257   if (directory.empty())
258   {
259     myDirectory = Licq::gDaemon.baseDir() + myUserId.accountId();
260     if (access(Licq::gDaemon.baseDir().c_str(), F_OK) < 0 && mkdir(myDirectory.c_str(), 0700) == -1 &&
261         errno != EEXIST)
262     {
263       gLog.warning(tr("Unable to create directory %s for file transfer."), myDirectory.c_str());
264       myDirectory = Licq::gDaemon.baseDir();
265     }
266   }
267   else
268   {
269     myDirectory = directory;
270   }
271 
272   struct stat buf;
273   stat(myDirectory.c_str(), &buf);
274   if (!S_ISDIR(buf.st_mode))
275   {
276     gLog.warning(tr("Path %s is not a directory."), myDirectory.c_str());
277     return false;
278   }
279 
280   if (!StartFileTransferServer())
281   {
282     PushFileTransferEvent(Licq::FT_ERRORxBIND);
283     return false;
284   }
285 
286   // Create the socket manager thread
287   if (pthread_create(&thread_ft, NULL, &FileTransferManager_tep, this) == -1)
288   {
289     PushFileTransferEvent(Licq::FT_ERRORxRESOURCES);
290     return false;
291   }
292 
293   m_bThreadCreated = true;
294 
295   return true;
296 }
297 
298 
299 //-----CFileTransferManager::StartAsClient-------------------------------------------
sendFiles(const list<string> & pathNames,unsigned short nPort)300 void CFileTransferManager::sendFiles(const list<string>& pathNames, unsigned short nPort)
301 {
302   myIsReceiver = false;
303 
304   // Validate the pathnames
305   if (pathNames.empty())
306     return;
307 
308   struct stat buf;
309   list<string>::const_iterator iter;
310   for (iter = pathNames.begin(); iter != pathNames.end(); ++iter)
311   {
312     if (stat(iter->c_str(), &buf) == -1)
313     {
314       gLog.warning(tr("File Transfer: File access error %s: %s."),
315          iter->c_str(), strerror(errno));
316       continue;
317     }
318     myPathNames.push_back(*iter);
319     m_nBatchFiles++;
320     m_nBatchSize += buf.st_size;
321   }
322   myPathNameIter = myPathNames.begin();
323   myPathName = *myPathNameIter;
324   m_nPort = nPort;
325 
326   // start the server anyway, may need to do reverse connect
327   if (!StartFileTransferServer())
328   {
329     PushFileTransferEvent(Licq::FT_ERRORxBIND);
330     return;
331   }
332 
333   // Create the socket manager thread
334   if (pthread_create(&thread_ft, NULL, &FileTransferManager_tep, this) == -1)
335   {
336     PushFileTransferEvent(Licq::FT_ERRORxRESOURCES);
337     return;
338   }
339 
340   m_bThreadCreated = true;
341 }
342 
343 
344 //-----CFileTransferManager::ConnectToFileServer-----------------------------
ConnectToFileServer(unsigned short nPort)345 bool CFileTransferManager::ConnectToFileServer(unsigned short nPort)
346 {
347   bool bTryDirect;
348   bool bSendIntIp;
349   {
350     UserReadGuard u(myUserId);
351     if (!u.isLocked())
352       return false;
353 
354     bTryDirect = u->Version() <= 6 || u->directMode();
355     bSendIntIp = u->SendIntIp();
356   }
357 
358   bool bSuccess = false;
359   if (bTryDirect)
360   {
361     gLog.info(tr("File Transfer: Connecting to server."));
362     bSuccess = gIcqProtocol.openConnectionToUser(myUserId, &mySock, nPort);
363    }
364 
365   bool bResult = false;
366   if (!bSuccess)
367   {
368     unsigned long nIp;
369     {
370       Licq::OwnerReadGuard o(gIcqProtocol.ownerId());
371       nIp = bSendIntIp ? o->IntIp() : o->Ip();
372     }
373 
374     // try reverse connect
375     int nId = gIcqProtocol.requestReverseConnection(myUserId, 0, nIp, LocalPort(), nPort);
376 
377     if (nId != -1)
378     {
379       struct SFileReverseConnectInfo *r = new struct SFileReverseConnectInfo;
380       r->nId = nId;
381       r->m = this;
382       r->bTryDirect = !bTryDirect;
383       pthread_mutex_lock(&thread_cancel_mutex);
384       pthread_create(&m_tThread, NULL, FileWaitForSignal_tep, r);
385       m_bThreadRunning = true;
386       pthread_mutex_unlock(&thread_cancel_mutex);
387       bResult = true;
388     }
389   }
390   else
391     bResult = SendFileHandshake();
392 
393   return bResult;
394 }
395 
SendFileHandshake()396 bool CFileTransferManager::SendFileHandshake()
397 {
398   gLog.info(tr("File Transfer: Shaking hands."));
399 
400   // Send handshake packet:
401   unsigned short nVersion;
402   {
403     UserReadGuard u(myUserId);
404     nVersion = u->ConnectionVersion();
405   }
406   if (!IcqProtocol::handshake_Send(&mySock, myUserId, LocalPort(), nVersion, false))
407     return false;
408 
409   // Send init packet:
410   CPFile_InitClient p(myLocalName, m_nBatchFiles, m_nBatchSize);
411   if (!SendPacket(&p)) return false;
412 
413   gLog.info(tr("File Transfer: Waiting for server to respond."));
414 
415   m_nState = FT_STATE_WAITxFORxSERVERxINIT;
416 
417   sockman.AddSocket(&mySock);
418   sockman.DropSocket(&mySock);
419 
420   return true;
421 }
422 
423 
424 //-----CFileTransferManager::AcceptReverseConnection-------------------------
AcceptReverseConnection(Licq::TCPSocket * s)425 void CFileTransferManager::AcceptReverseConnection(Licq::TCPSocket* s)
426 {
427   if (mySock.Descriptor() != -1)
428   {
429     gLog.warning(tr("File Transfer: Attempted reverse connection when already connected."));
430     return;
431   }
432 
433   mySock.TransferConnectionFrom(*s);
434   sockman.AddSocket(&mySock);
435   sockman.DropSocket(&mySock);
436 
437   m_nState = FT_STATE_WAITxFORxCLIENTxINIT;
438 
439   // Reload socket info
440   myThreadPipe.putChar('R');
441 
442   gLog.info(tr("File Transfer: Received reverse connection."));
443 }
444 
445 
446 //-----CFileTransferManager::ProcessPacket-------------------------------------------
ProcessPacket()447 bool CFileTransferManager::ProcessPacket()
448 {
449   if (!mySock.RecvPacket())
450   {
451     if (mySock.Error() == 0)
452       gLog.info(tr("File Transfer: Remote end disconnected."));
453     else
454       gLog.warning(tr("File Transfer: Lost remote end: %s"), mySock.errorStr().c_str());
455     if (m_nState == FT_STATE_WAITxFORxFILExINFO)
456       m_nResult = Licq::FT_DONExBATCH;
457     else
458       m_nResult = Licq::FT_ERRORxCLOSED;
459     return false;
460   }
461 
462   if (!mySock.RecvBufferFull())
463     return true;
464   CBuffer& b = mySock.RecvBuffer();
465 
466   switch(m_nState)
467   {
468     // Server States
469 
470     case FT_STATE_HANDSHAKE:
471     {
472       CBuffer tmp(b); // we need to save a copy for later
473 
474       if (!gIcqProtocol.Handshake_Recv(&mySock, LocalPort(), false))
475         break;
476       gLog.info(tr("File Transfer: Received handshake."));
477 
478       unsigned long nId = 0;
479       if (mySock.Version() == 7 || mySock.Version() == 8)
480       {
481         CPacketTcp_Handshake_v7 hand(&tmp);
482         nId = hand.Id();
483       }
484 
485       if (nId != 0)
486       {
487         pthread_mutex_lock(&gIcqProtocol.mutex_reverseconnect);
488         std::list<CReverseConnectToUserData *>::iterator iter;
489         bool bFound = false;
490         for (iter = gIcqProtocol.m_lReverseConnect.begin();
491             iter != gIcqProtocol.m_lReverseConnect.end();  ++iter)
492         {
493           if ((*iter)->nId == nId && (*iter)->myIdString == myUserId.accountId())
494           {
495             bFound = true;
496             (*iter)->bSuccess = true;
497             (*iter)->bFinished = true;
498             pthread_cond_broadcast(&gIcqProtocol.cond_reverseconnect_done);
499             break;
500           }
501         }
502         pthread_mutex_unlock(&gIcqProtocol.mutex_reverseconnect);
503 
504         if (bFound)
505         {
506           // Send init packet:
507           CPFile_InitClient p(myLocalName, m_nBatchFiles, m_nBatchSize);
508           if (!SendPacket(&p))
509           {
510             m_nResult = Licq::FT_ERRORxCLOSED;
511             return false;
512           }
513 
514           gLog.info(tr("File Transfer: Waiting for server to respond."));
515 
516           m_nState = FT_STATE_WAITxFORxSERVERxINIT;
517           break;
518         }
519       }
520 
521       m_nState = FT_STATE_WAITxFORxCLIENTxINIT;
522       break;
523     }
524 
525     case FT_STATE_WAITxFORxCLIENTxINIT:
526     {
527       b.unpackUInt16LE(); // Packet length
528       unsigned char nCmd = b.UnpackChar();
529       if (nCmd == 0x05)
530       {
531         unsigned long nSpeed = b.UnpackUnsignedLong();
532         gLog.info(tr("File Transfer: Speed set to %ld%%."), nSpeed);
533         break;
534       }
535       if (nCmd != 0x00)
536       {
537         b.log(Log::Error, "File Transfer: Invalid client init packet");
538         m_nResult = Licq::FT_ERRORxHANDSHAKE;
539         return false;
540       }
541       b.UnpackUnsignedLong();
542       m_nBatchFiles = b.UnpackUnsignedLong();
543       m_nBatchSize = b.UnpackUnsignedLong();
544       m_nSpeed = b.UnpackUnsignedLong();
545       myRemoteName = b.unpackShortStringLE();
546 
547       m_nBatchStartTime = time(NULL);
548       m_nBatchBytesTransfered = m_nBatchPos = 0;
549 
550       PushFileTransferEvent(Licq::FT_STARTxBATCH);
551 
552       // Send speed response
553       CPFile_SetSpeed p1(100);
554       if (!SendPacket(&p1))
555       {
556         m_nResult = Licq::FT_ERRORxCLOSED;
557         return false;
558       }
559 
560       // Send response
561       CPFile_InitServer p(myLocalName);
562       if (!SendPacket(&p))
563       {
564         m_nResult = Licq::FT_ERRORxCLOSED;
565         return false;
566       }
567 
568       gLog.info(tr("File Transfer: Waiting for file info."));
569       m_nState = FT_STATE_WAITxFORxFILExINFO;
570       break;
571     }
572 
573     case FT_STATE_WAITxFORxFILExINFO:
574     {
575       b.unpackUInt16LE(); // Packet length
576       unsigned char nCmd = b.UnpackChar();
577       if (nCmd == 0x05)
578       {
579         unsigned long nSpeed = b.UnpackUnsignedLong();
580         gLog.info(tr("File Transfer: Speed set to %ld%%."), nSpeed);
581         break;
582       }
583       if (nCmd == 0x06 && b.getDataSize() == 3)
584       {
585         gLog.info(tr("File Transfer: Ignoring a possible erroneous packet."));
586         break;
587       }
588       if (nCmd != 0x02)
589       {
590         b.log(Log::Error, "File Transfer: Invalid file info packet");
591         m_nResult = Licq::FT_ERRORxHANDSHAKE;
592         return false;
593       }
594       b.UnpackChar();
595       myFileName = b.unpackShortStringLE();
596 
597       // Remove any preceeding path info from the filename for security
598       // reasons
599       size_t lastSlash = myFileName.rfind('/');
600       if (lastSlash != string::npos)
601         myFileName.erase(0, lastSlash+1);
602 
603       b.UnpackUnsignedShort(); // 0 length string...?
604       b.UnpackChar();
605       m_nFileSize = b.UnpackUnsignedLong();
606       b.UnpackUnsignedLong();
607       m_nSpeed = b.UnpackUnsignedLong();
608 
609       m_nBytesTransfered = 0;
610       m_nCurrentFile++;
611 
612       gLog.info(tr("File Transfer: Waiting for plugin to confirm file receive.\n"));
613 
614       m_nState = FT_STATE_CONFIRMINGxFILE;
615       PushFileTransferEvent(new IcqFileTransferEvent(Licq::FT_CONFIRMxFILE, myPathName));
616       break;
617     }
618 
619     case FT_STATE_CONFIRMINGxFILE:
620     {
621       // Still waiting for the plugin to confirm
622       gLog.warning(tr("File Transfer: Still waiting for the plugin to confirm file receive..."));
623       break;
624     }
625 
626     case FT_STATE_RECEIVINGxFILE:
627     {
628       // if this is the first call to this function...
629       if (m_nBytesTransfered == 0)
630       {
631         m_nStartTime = time(NULL);
632         m_nBatchPos += m_nFilePos;
633         gLog.info(tr("File Transfer: Receiving %s (%ld bytes)."),
634             myFileName.c_str(), m_nFileSize);
635         PushFileTransferEvent(new IcqFileTransferEvent(Licq::FT_STARTxFILE, myPathName));
636         gettimeofday(&tv_lastupdate, NULL);
637       }
638 
639       // Write the new data to the file and empty the buffer
640       b.unpackUInt16LE(); // Packet length
641       char nCmd = b.UnpackChar();
642       if (nCmd == 0x05)
643       {
644         unsigned long nSpeed = b.UnpackUnsignedLong();
645         gLog.info(tr("File Transfer: Speed set to %ld%%."), nSpeed);
646         break;
647       }
648 
649       if (nCmd != 0x06)
650       {
651         gLog.unknown(tr("File Transfer: Invalid data (%c) ignoring packet"),
652            nCmd);
653         break;
654       }
655 
656       errno = 0;
657       size_t nBytesWritten = write(m_nFileDesc, b.getDataPosRead(), b.getDataSize() - 3);
658       if (nBytesWritten != b.getDataSize() - 3)
659       {
660         gLog.error(tr("File Transfer: Write error: %s."),
661             errno == 0 ? "Disk full (?)" : strerror(errno));
662         m_nResult = Licq::FT_ERRORxFILE;
663         return false;
664       }
665 
666       m_nFilePos += nBytesWritten;
667       m_nBytesTransfered += nBytesWritten;
668       m_nBatchPos += nBytesWritten;
669       m_nBatchBytesTransfered += nBytesWritten;
670 
671       // Check if we need to send an update notification
672       if (m_nUpdatesEnabled)
673       {
674         struct timeval tv_now;
675         gettimeofday(&tv_now, NULL);
676         if (tv_now.tv_sec >= tv_lastupdate.tv_sec + m_nUpdatesEnabled)
677         {
678           PushFileTransferEvent(Licq::FT_UPDATE);
679           tv_lastupdate = tv_now;
680         }
681       }
682 
683       int nBytesLeft = m_nFileSize - m_nFilePos;
684       if (nBytesLeft > 0)
685         break;
686 
687       close(m_nFileDesc);
688       m_nFileDesc = -1;
689       if (nBytesLeft == 0) // File transfer done perfectly
690       {
691         gLog.info(tr("File Transfer: %s received."), myFileName.c_str());
692       }
693       else // nBytesLeft < 0
694       {
695         // Received too many bytes for the given size of the current file
696         gLog.warning(tr("File Transfer: %s received %d too many bytes."),
697             myFileName.c_str(), -nBytesLeft);
698       }
699       // Notify Plugin
700       PushFileTransferEvent(new IcqFileTransferEvent(Licq::FT_DONExFILE, myPathName));
701 
702       // Now wait for a disconnect or another file
703       m_nState = FT_STATE_WAITxFORxFILExINFO;
704       break;
705     }
706 
707 
708     // Client States
709 
710     case FT_STATE_WAITxFORxSERVERxINIT:
711     {
712       b.unpackUInt16LE(); // Packet length
713       char nCmd = b.UnpackChar();
714       if (nCmd == 0x05)
715       {
716         unsigned long nSpeed = b.UnpackUnsignedLong();
717         gLog.info(tr("File Transfer: Speed set to %ld%%."), nSpeed);
718         break;
719       }
720       if (nCmd != 0x01)
721       {
722         b.log(Log::Error, "File Transfer: Invalid server init packet");
723         m_nResult = Licq::FT_ERRORxHANDSHAKE;
724         return false;
725       }
726       m_nSpeed = b.UnpackUnsignedLong();
727       myRemoteName = b.unpackShortStringLE();
728 
729       // Send file info packet
730       CPFile_Info p(*myPathNameIter);
731       if (!p.IsValid())
732       {
733         gLog.warning(tr("File Transfer: Read error for %s:\n%s"),
734             myPathNameIter->c_str(), p.ErrorStr());
735         m_nResult = Licq::FT_ERRORxFILE;
736         return false;
737       }
738       if (!SendPacket(&p))
739       {
740         m_nResult = Licq::FT_ERRORxCLOSED;
741         return false;
742       }
743 
744       m_nFileSize = p.GetFileSize();
745       myFileName = p.fileName();
746 
747       m_nBatchStartTime = time(NULL);
748       m_nBatchBytesTransfered = m_nBatchPos = 0;
749 
750       PushFileTransferEvent(Licq::FT_STARTxBATCH);
751 
752       m_nState = FT_STATE_WAITxFORxSTART;
753       break;
754     }
755 
756     case FT_STATE_WAITxFORxSTART:
757     {
758       // contains the seek value
759       b.unpackUInt16LE(); // Packet length
760       char nCmd = b.UnpackChar();
761       if (nCmd == 0x05)
762       {
763         unsigned long nSpeed = b.UnpackUnsignedLong();
764         gLog.info(tr("File Transfer: Speed set to %ld%%."), nSpeed);
765         break;
766       }
767       if (nCmd != 0x03)
768       {
769         b.log(Log::Error, "File Transfer: Invalid start packet");
770         m_nResult = Licq::FT_ERRORxCLOSED;
771         return false;
772       }
773 
774       m_nBytesTransfered = 0;
775       m_nCurrentFile++;
776 
777       m_nFilePos = b.UnpackUnsignedLong();
778 
779       m_nFileDesc = open(myPathNameIter->c_str(), O_RDONLY);
780       if (m_nFileDesc == -1)
781       {
782         gLog.error(tr("File Transfer: Read error '%s': %s."),
783             myPathNameIter->c_str(), strerror(errno));
784         m_nResult = Licq::FT_ERRORxFILE;
785         return false;
786       }
787 
788       if (lseek(m_nFileDesc, m_nFilePos, SEEK_SET) == -1)
789       {
790         gLog.error(tr("File Transfer: Seek error '%s': %s."),
791             myFileName.c_str(), strerror(errno));
792         m_nResult = Licq::FT_ERRORxFILE;
793         return false;
794       }
795 
796       m_nState = FT_STATE_SENDINGxFILE;
797       break;
798     }
799 
800     case FT_STATE_SENDINGxFILE:
801     {
802       b.unpackUInt16LE(); // Packet length
803       char nCmd = b.UnpackChar();
804       if (nCmd == 0x05)
805       {
806         unsigned long nSpeed = b.UnpackUnsignedLong();
807         gLog.info(tr("File Transfer: Speed set to %ld%%."), nSpeed);
808         break;
809       }
810       b.log(Log::Unknown, tr("File Transfer: Unknown packet received during file send"));
811       break;
812     }
813 
814 
815     default:
816     {
817       gLog.error(tr("Internal error: FileTransferManager::ProcessPacket(), invalid state (%d)."),
818           m_nState);
819       break;
820     }
821 
822   } // switch
823 
824   mySock.ClearRecvBuffer();
825 
826   return true;
827 }
828 
829 // This function gives a callback opportunity for the plugin, just before
830 // the actual transfer begins
startReceivingFile(const string & fileName)831 bool CFileTransferManager::startReceivingFile(const string& fileName)
832 {
833   gLog.info(tr("File Transfer: Received plugin confirmation."));
834 
835   if (m_nState != FT_STATE_CONFIRMINGxFILE)
836   {
837      gLog.warning(tr("File Transfer: StartReceivingFile called without a pending confirmation."));
838      return false;
839   }
840 
841   // If a different filename was specified, use it
842   if (fileName.empty())
843     myFileName = fileName;
844 
845   // Get the local filename and set the file offset (for resume)
846   struct stat buf;
847   m_nFileDesc = -1;
848   myPathName = myDirectory + '/' + myFileName;
849   while (m_nFileDesc == -1)
850   {
851     if (stat(myPathName.c_str(), &buf) != -1)
852     {
853       if ((unsigned long)buf.st_size >= m_nFileSize)
854       {
855         char buf[20];
856         snprintf(buf, sizeof(buf), ".%lu", (unsigned long)time(NULL));
857         myPathName += buf;
858       }
859       m_nFileDesc = open(myPathName.c_str(), O_WRONLY | O_CREAT | O_APPEND, 00600);
860       m_nFilePos = buf.st_size;
861     }
862     else
863     {
864       m_nFileDesc = open(myPathName.c_str(), O_WRONLY | O_CREAT, 00600);
865       m_nFilePos = 0;
866     }
867     if (m_nFileDesc == -1)
868     {
869       gLog.error(tr("File Transfer: Unable to open %s for writing: %s."),
870           myPathName.c_str(), strerror(errno));
871       m_nResult = Licq::FT_ERRORxFILE;
872       return false;
873     }
874   }
875 
876   // Send response
877   CPFile_Start p(m_nFilePos, m_nCurrentFile);
878   if (!SendPacket(&p))
879   {
880     gLog.error(tr("File Transfer: Unable to send file receive start packet."));
881     m_nResult = Licq::FT_ERRORxCLOSED;
882     return false;
883   }
884 
885   m_nState = FT_STATE_RECEIVINGxFILE;
886   return true;
887 }
888 
889 //-----CFileTransferManager::SendFilePacket----------------------------------
SendFilePacket()890 bool CFileTransferManager::SendFilePacket()
891 {
892   static char pSendBuf[2048];
893 
894   if (m_nBytesTransfered == 0)
895   {
896     m_nStartTime = time(NULL);
897     m_nBatchPos += m_nFilePos;
898     gLog.info(tr("File Transfer: Sending %s (%ld bytes)."),
899         myPathName.c_str(), m_nFileSize);
900     PushFileTransferEvent(new IcqFileTransferEvent(Licq::FT_STARTxFILE, myPathName));
901     gettimeofday(&tv_lastupdate, NULL);
902   }
903 
904   int nBytesToSend = m_nFileSize - m_nFilePos;
905   if (nBytesToSend > 2048) nBytesToSend = 2048;
906   if (read(m_nFileDesc, pSendBuf, nBytesToSend) != nBytesToSend)
907   {
908     gLog.error(tr("File Transfer: Error reading from %s: %s."),
909         myPathName.c_str(), strerror(errno));
910     m_nResult = Licq::FT_ERRORxFILE;
911     return false;
912   }
913   CBuffer xSendBuf(nBytesToSend + 1);
914   xSendBuf.PackChar(0x06);
915   xSendBuf.Pack(pSendBuf, nBytesToSend);
916   if (!SendBuffer(&xSendBuf))
917   {
918     m_nResult = Licq::FT_ERRORxCLOSED;
919     return false;
920   }
921 
922   m_nFilePos += nBytesToSend;
923   m_nBytesTransfered += nBytesToSend;
924 
925   m_nBatchPos += nBytesToSend;
926   m_nBatchBytesTransfered += nBytesToSend;
927 
928   // Check if we need to send an update notification
929   if (m_nUpdatesEnabled)
930   {
931     struct timeval tv_now;
932     gettimeofday(&tv_now, NULL);
933     if (tv_now.tv_sec >= tv_lastupdate.tv_sec + m_nUpdatesEnabled)
934     {
935       PushFileTransferEvent(Licq::FT_UPDATE);
936       tv_lastupdate = tv_now;
937     }
938   }
939 
940   int nBytesLeft = m_nFileSize - m_nFilePos;
941   if (nBytesLeft > 0)
942   {
943     // More bytes to send so go away until the socket is free again
944     return true;
945   }
946 
947   // Only get here if we are done
948   close(m_nFileDesc);
949   m_nFileDesc = -1;
950 
951   if (nBytesLeft == 0)
952   {
953     gLog.info(tr("File Transfer: Sent %s."), myFileName.c_str());
954   }
955   else // nBytesLeft < 0
956   {
957     gLog.info(tr("File Transfer: Sent %s, %d too many bytes."),
958         myFileName.c_str(), -nBytesLeft);
959   }
960   PushFileTransferEvent(new IcqFileTransferEvent(Licq::FT_DONExFILE, myPathName));
961 
962   // Go to the next file, if no more then close connections
963   myPathNameIter++;
964   if (myPathNameIter == myPathNames.end())
965   {
966     m_nResult = Licq::FT_DONExBATCH;
967     return false;
968   }
969   else
970   {
971     // Send file info packet
972     CPFile_Info p(*myPathNameIter);
973     if (!p.IsValid())
974     {
975       gLog.warning(tr("File Transfer: Read error for %s: %s"),
976           myPathNameIter->c_str(), p.ErrorStr());
977       m_nResult = Licq::FT_ERRORxFILE;
978       return false;
979     }
980     if (!SendPacket(&p))
981     {
982       m_nResult = Licq::FT_ERRORxCLOSED;
983       return false;
984     }
985 
986     m_nFileSize = p.GetFileSize();
987     myFileName = p.fileName();
988     myPathName = *myPathNameIter;
989 
990     m_nState = FT_STATE_WAITxFORxSTART;
991   }
992 
993   return true;
994 }
995 
996 
997 
998 //-----CFileTransferManager::PopFileTransferEvent------------------------------
PopFileTransferEvent()999 IcqFileTransferEvent *CFileTransferManager::PopFileTransferEvent()
1000 {
1001   if (ftEvents.empty()) return NULL;
1002 
1003   IcqFileTransferEvent* e = ftEvents.front();
1004   ftEvents.pop_front();
1005 
1006   return e;
1007 }
1008 
1009 
1010 //-----CFileTransferManager::PushFileTransferEvent-----------------------------
PushFileTransferEvent(unsigned char t)1011 void CFileTransferManager::PushFileTransferEvent(unsigned char t)
1012 {
1013   PushFileTransferEvent(new IcqFileTransferEvent(t));
1014 }
1015 
PushFileTransferEvent(IcqFileTransferEvent * e)1016 void CFileTransferManager::PushFileTransferEvent(IcqFileTransferEvent *e)
1017 {
1018   ftEvents.push_back(e);
1019   myEventsPipe.putChar('*');
1020 }
1021 
1022 
LocalPort() const1023 unsigned short FileTransferManager::LocalPort() const
1024 {
1025   return ftServer.getLocalPort();
1026 }
1027 
Pipe()1028 int FileTransferManager::Pipe()
1029 {
1030   return myEventsPipe.getReadFd();
1031 }
1032 
1033 //-----CFileTransferManager::SendPacket----------------------------------------------
SendPacket(CPacket * p)1034 bool CFileTransferManager::SendPacket(CPacket *p)
1035 {
1036   return SendBuffer(p->getBuffer());
1037 }
1038 
1039 
1040 //-----CFileTransferManager::SendBuffer----------------------------------------------
SendBuffer(CBuffer * b)1041 bool CFileTransferManager::SendBuffer(CBuffer *b)
1042 {
1043   if (!mySock.send(*b))
1044   {
1045     gLog.warning(tr("File Transfer: Send error: %s"), mySock.errorStr().c_str());
1046     return false;
1047   }
1048   return true;
1049 }
1050 
1051 
ChangeSpeed(unsigned short nSpeed)1052 void CFileTransferManager::ChangeSpeed(unsigned short nSpeed)
1053 {
1054   if (nSpeed > 100)
1055   {
1056     gLog.warning(tr("Invalid file transfer speed: %d%%."), nSpeed);
1057     return;
1058   }
1059 
1060   //CPFile_ChangeSpeed p(nSpeed);
1061   //SendPacket(&p);
1062   m_nSpeed = nSpeed;
1063 }
1064 
1065 
1066 
1067 //----CFileTransferManager::CloseFileTransfer--------------------------------
CloseFileTransfer()1068 void CFileTransferManager::CloseFileTransfer()
1069 {
1070   // Close the thread
1071   myThreadPipe.putChar('X');
1072   if (m_bThreadCreated)
1073     pthread_join(thread_ft, NULL);
1074   m_bThreadCreated = false;
1075 
1076   CloseConnection();
1077 }
1078 
1079 
1080 //----CFileTransferManager::CloseConnection----------------------------------
CloseConnection()1081 void CFileTransferManager::CloseConnection()
1082 {
1083   sockman.CloseSocket(ftServer.Descriptor(), false, false);
1084   sockman.CloseSocket(mySock.Descriptor(), false, false);
1085   m_nState = FT_STATE_DISCONNECTED;
1086 
1087   if (m_nFileDesc != -1)
1088   {
1089     close(m_nFileDesc);
1090     m_nFileDesc = -1;
1091   }
1092 }
1093 
1094 
1095 
FileTransferManager_tep(void * arg)1096 void* LicqIcq::FileTransferManager_tep(void* arg)
1097 {
1098   CFileTransferManager *ftman = (CFileTransferManager *)arg;
1099 
1100   fd_set f_recv, f_send;
1101   struct timeval *tv;
1102   struct timeval tv_updates = { 2, 0 };
1103   int l, nSocketsAvailable, nCurrentSocket;
1104 
1105   if (!ftman->isReceiver())
1106   {
1107     if (!ftman->ConnectToFileServer(ftman->m_nPort))
1108     {
1109       ftman->PushFileTransferEvent(Licq::FT_ERRORxCONNECT);
1110       return NULL;
1111     }
1112   }
1113   else if (!ftman->isReceiver())
1114     return NULL;
1115 
1116   while (true)
1117   {
1118     f_recv = ftman->sockman.socketSet();
1119     l = ftman->sockman.LargestSocket() + 1;
1120 
1121     // Add the new socket pipe descriptor
1122     FD_SET(ftman->myThreadPipe.getReadFd(), &f_recv);
1123     if (ftman->myThreadPipe.getReadFd() >= l)
1124       l = ftman->myThreadPipe.getReadFd() + 1;
1125 
1126     // Set up the send descriptor
1127     FD_ZERO(&f_send);
1128     if (ftman->m_nState == FT_STATE_SENDINGxFILE)
1129     {
1130       FD_SET(ftman->mySock.Descriptor(), &f_send);
1131       // No need to check "l" as mySock is already in the read list
1132     }
1133 
1134     // Prepare max timeout if necessary
1135     if (ftman->m_nUpdatesEnabled &&
1136         (ftman->m_nState == FT_STATE_SENDINGxFILE ||
1137          ftman->m_nState == FT_STATE_RECEIVINGxFILE) )
1138     {
1139       tv_updates.tv_sec = ftman->m_nUpdatesEnabled;
1140       tv_updates.tv_usec = 0;
1141       tv = &tv_updates;
1142     }
1143     else
1144     {
1145       tv = NULL;
1146     }
1147 
1148     nSocketsAvailable = select(l, &f_recv, &f_send, NULL, tv);
1149 
1150     if (nSocketsAvailable == -1)
1151     {
1152       // Something is very wrong, most likely we've lost control of a file
1153       //   descriptor and select will continue to fail causing this thread to
1154       //   spin so better to just give up and exit.
1155 
1156       gLog.warning(tr("File Transfer: select failed, aborting thread: %s"), strerror(errno));
1157       pthread_exit(NULL);
1158     }
1159 
1160     // Check if we timed out
1161     if (tv != NULL && nSocketsAvailable == 0)
1162     {
1163       ftman->PushFileTransferEvent(Licq::FT_UPDATE);
1164       gettimeofday(&ftman->tv_lastupdate, NULL);
1165     }
1166 
1167     nCurrentSocket = 0;
1168     while (nSocketsAvailable > 0 && nCurrentSocket < l)
1169     {
1170       if (FD_ISSET(nCurrentSocket, &f_recv))
1171       {
1172         // New socket event ----------------------------------------------------
1173         if (nCurrentSocket == ftman->myThreadPipe.getReadFd())
1174         {
1175           char buf = ftman->myThreadPipe.getChar();
1176           if (buf == 'R')
1177           {
1178             DEBUG_THREADS("[FileTransferManager_tep] Reloading socket info.\n");
1179           }
1180           else if (buf == 'X')
1181           {
1182             DEBUG_THREADS("[FileTransferManager_tep] Exiting.\n");
1183             pthread_exit(NULL);
1184           }
1185         }
1186 
1187         // Connection on the server port ---------------------------------------
1188         else if (nCurrentSocket == ftman->ftServer.Descriptor())
1189         {
1190           if (ftman->mySock.Descriptor() != -1)
1191           {
1192             gLog.warning(tr("File Transfer: Receiving repeat incoming connection."));
1193 
1194             // Dump the extra connection to clear the listen socket queue
1195             Licq::TCPSocket ts;
1196             if (ftman->ftServer.RecvConnection(ts))
1197               ts.CloseConnection();
1198           }
1199           else
1200           {
1201             if (ftman->ftServer.RecvConnection(ftman->mySock))
1202             {
1203               ftman->sockman.AddSocket(&ftman->mySock);
1204               ftman->sockman.DropSocket(&ftman->mySock);
1205 
1206               ftman->m_nState = FT_STATE_HANDSHAKE;
1207               gLog.info(tr("File Transfer: Received connection."));
1208             }
1209             else
1210             {
1211               gLog.error(tr("File Transfer: Unable to receive new connection."));
1212             }
1213           }
1214         }
1215 
1216         // Message from connected socket----------------------------------------
1217         else if (nCurrentSocket == ftman->mySock.Descriptor())
1218         {
1219           ftman->mySock.Lock();
1220           bool ok = ftman->ProcessPacket();
1221           ftman->mySock.Unlock();
1222           if (!ok)
1223           {
1224             ftman->CloseConnection();
1225             ftman->PushFileTransferEvent(ftman->m_nResult);
1226           }
1227         }
1228 
1229         else
1230         {
1231           gLog.warning(tr("File Transfer: No such socket."));
1232         }
1233 
1234         nSocketsAvailable--;
1235       }
1236       else if (FD_ISSET(nCurrentSocket, &f_send))
1237       {
1238         if (nCurrentSocket == ftman->mySock.Descriptor())
1239         {
1240           ftman->mySock.Lock();
1241           bool ok = ftman->SendFilePacket();
1242           ftman->mySock.Unlock();
1243           if (!ok)
1244           {
1245             ftman->CloseConnection();
1246             ftman->PushFileTransferEvent(ftman->m_nResult);
1247           }
1248         }
1249         nSocketsAvailable--;
1250       }
1251 
1252       nCurrentSocket++;
1253     }
1254   }
1255   return NULL;
1256 }
1257 
FileWaitForSignal_tep(void * arg)1258 void* LicqIcq::FileWaitForSignal_tep(void* arg)
1259 {
1260   pthread_detach(pthread_self());
1261 
1262   unsigned short nPort;
1263   struct SFileReverseConnectInfo *rc = (struct SFileReverseConnectInfo *)arg;
1264   pthread_mutex_t *cancel_mutex = &CFileTransferManager::thread_cancel_mutex;
1265 
1266   pthread_mutex_lock(cancel_mutex);
1267   pthread_cleanup_push(FileWaitForSignal_cleanup, arg);
1268     pthread_testcancel();
1269   pthread_cleanup_pop(0);
1270   Licq::UserId userId = rc->m->userId();
1271   nPort = rc->m->m_nPort;
1272   pthread_mutex_unlock(cancel_mutex);
1273 
1274   pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
1275   gLog.info(tr("File Transfer: Waiting for reverse connection.\n"));
1276   bool bConnected = gIcqProtocol.waitForReverseConnection(rc->nId, userId);
1277   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1278 
1279   pthread_mutex_lock(cancel_mutex);
1280   pthread_cleanup_push(FileWaitForSignal_cleanup, arg);
1281     pthread_testcancel();
1282   pthread_cleanup_pop(0);
1283 
1284   if (bConnected || !rc->bTryDirect)
1285   {
1286     if (!bConnected)
1287       rc->m->PushFileTransferEvent(Licq::FT_ERRORxCONNECT);
1288 
1289     rc->m->m_bThreadRunning = false;
1290     pthread_mutex_unlock(cancel_mutex);
1291 
1292     delete rc;
1293     pthread_exit(NULL);
1294   }
1295 
1296   pthread_mutex_unlock(cancel_mutex);
1297 
1298   pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
1299   gLog.info(tr("File Transfer: Reverse connection failed, trying direct."));
1300   Licq::TCPSocket s;
1301   bConnected = gIcqProtocol.openConnectionToUser(userId, &s, nPort);
1302   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1303 
1304   pthread_mutex_lock(cancel_mutex);
1305   pthread_cleanup_push(FileWaitForSignal_cleanup, arg);
1306     pthread_testcancel();
1307   pthread_cleanup_pop(0);
1308 
1309   if (bConnected)
1310   {
1311     if (rc->m->mySock.Descriptor() != -1)
1312     {
1313       gLog.warning(tr("File Transfer: Attempted connection when already connected."));
1314     }
1315     else
1316     {
1317       rc->m->mySock.TransferConnectionFrom(s);
1318       bConnected = rc->m->SendFileHandshake();
1319       rc->m->myThreadPipe.putChar('R');
1320     }
1321   }
1322 
1323   if (!bConnected)
1324     rc->m->PushFileTransferEvent(Licq::FT_ERRORxCONNECT);
1325 
1326   rc->m->m_bThreadRunning = false;
1327 
1328   pthread_mutex_unlock(cancel_mutex);
1329   delete rc;
1330   pthread_exit(NULL);
1331 }
1332 
FileWaitForSignal_cleanup(void * arg)1333 void LicqIcq::FileWaitForSignal_cleanup(void* arg)
1334 {
1335   struct SFileReverseConnectInfo *rc = (struct SFileReverseConnectInfo *)arg;
1336   delete rc;
1337 
1338   pthread_mutex_unlock(&CFileTransferManager::thread_cancel_mutex);
1339 }
1340 
FindByPort(unsigned short p)1341 CFileTransferManager *CFileTransferManager::FindByPort(unsigned short p)
1342 {
1343   FileTransferManagerList::iterator iter;
1344   for (iter = ftmList.begin(); iter != ftmList.end(); ++iter)
1345   {
1346     if ( (*iter)->LocalPort() == p ) return *iter;
1347   }
1348   return NULL;
1349 }
1350 
1351 
~FileTransferManager()1352 FileTransferManager::~FileTransferManager()
1353 {
1354   // cancel the waiting thread first
1355   pthread_mutex_lock(&thread_cancel_mutex);
1356   if (m_bThreadRunning)
1357     pthread_cancel(m_tThread);
1358   pthread_mutex_unlock(&thread_cancel_mutex);
1359 
1360   CloseFileTransfer();
1361 
1362   // Delete any pending events
1363   IcqFileTransferEvent *e = NULL;
1364   while (ftEvents.size() > 0)
1365   {
1366     e = ftEvents.front();
1367     delete e;
1368     ftEvents.pop_front();
1369   }
1370 
1371   FileTransferManagerList::iterator fiter;
1372   for (fiter = ftmList.begin(); fiter != ftmList.end(); ++fiter)
1373   {
1374     if (*fiter == this) break;
1375   }
1376   if (fiter != ftmList.end()) ftmList.erase(fiter);
1377 }
1378 
1379