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