1 /*
2  *      dccengine.cpp
3  *
4  *      Copyright 2010 David Vachulka <arch_dvx@users.sourceforge.net>
5  *
6  *      This program is free software; you can redistribute it and/or modify
7  *      it under the terms of the GNU General Public License as published by
8  *      the Free Software Foundation; either version 2 of the License, or
9  *      (at your option) any later version.
10  *
11  *      This program is distributed in the hope that it will be useful,
12  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *      GNU General Public License for more details.
15  *
16  *      You should have received a copy of the GNU General Public License
17  *      along with this program; if not, write to the Free Software
18  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *      MA 02110-1301, USA.
20  */
21 
22 #include "dccengine.h"
23 #include "i18n.h"
24 
25 FXDEFMAP(DccEngine) DccEngineMap[] = {
26     FXMAPFUNC(SEL_TIMEOUT,          DccEngine_CTIME,     DccEngine::onCloseTimeout),
27     FXMAPFUNC(SEL_TIMEOUT,          DccEngine_PTIME,     DccEngine::onPositionTimeout),
28     FXMAPFUNC(SOCKET_CANREAD,       DccEngine_SOCKET,    DccEngine::onSocketCanRead),
29     FXMAPFUNC(SOCKET_CONNECTED,     DccEngine_SOCKET,    DccEngine::onSocketConnected),
30     FXMAPFUNC(SOCKET_DISCONNECTED,  DccEngine_SOCKET,    DccEngine::onSocketDisconnected),
31     FXMAPFUNC(SOCKET_ERR,           DccEngine_SOCKET,    DccEngine::onSocketError),
32     FXMAPFUNC(SOCKET_STARTACCEPT,   DccEngine_SOCKET,    DccEngine::onSocketStartAccept),
33     FXMAPFUNC(SOCKET_LISTEN,        DccEngine_SOCKET,    DccEngine::onSocketListen),
34     FXMAPFUNC(SOCKET_WRITTEN,       DccEngine_SOCKET,    DccEngine::onSocketWritten)
35 };
36 
FXIMPLEMENT(DccEngine,FXObject,DccEngineMap,ARRAYNUMBER (DccEngineMap))37 FXIMPLEMENT(DccEngine, FXObject, DccEngineMap, ARRAYNUMBER(DccEngineMap))
38 
39 DccEngine::DccEngine(FXApp* app, FXObject* tgt, DccFile file, IrcEngine* engine)
40         : m_application(app), m_target(tgt), m_file(file), m_engine(engine)
41 {
42     m_connected = FALSE;
43     m_socket = new dxSocket(app, this, DccEngine_SOCKET);
44     m_positionChanged = FALSE;
45     m_lastChange = 0;
46     m_dataAmount = 0;
47     m_dccTimeout = dxUtils.getIntIniEntry("SETTINGS", "dccTimeout", 66);
48 }
49 
~DccEngine()50 DccEngine::~DccEngine()
51 {
52     m_connected = FALSE;
53     closeFile();
54     m_socket->disconnect();
55     delete m_socket;
56 }
57 
58 //for offered connectin by me
onCloseTimeout(FXObject *,FXSelector,void *)59 long DccEngine::onCloseTimeout(FXObject*, FXSelector, void*)
60 {
61     m_connected = FALSE;
62     m_file.canceled = TRUE;
63     updateDcc();
64     m_socket->disconnect();
65     sendEvent(IRC_DISCONNECT, _("Connection closed. Client didn't connect in given timeout"), "", "", "");
66     return 1;
67 }
68 
69 //fired current position
onPositionTimeout(FXObject *,FXSelector,void *)70 long DccEngine::onPositionTimeout(FXObject*, FXSelector, void*)
71 {
72     updateDcc();
73     if(m_file.currentPosition < m_file.size && m_connected)
74         m_application->addTimeout(this, DccEngine_PTIME, 1000);
75     return 1;
76 }
77 
onSocketCanRead(FXObject *,FXSelector,void *)78 long DccEngine::onSocketCanRead(FXObject*, FXSelector, void*)
79 {
80     readData();
81     return 1;
82 }
83 
onSocketWritten(FXObject *,FXSelector,void * ptr)84 long DccEngine::onSocketWritten(FXObject*, FXSelector, void *ptr)
85 {
86     if(m_file.type == DCC_OUT || m_file.type == DCC_POUT)
87     {
88         m_dataAmount += (FXint)(FXival)ptr;
89         m_file.currentPosition += (FXint)(FXival)ptr;
90         m_positionChanged = TRUE;
91     }
92     return 1;
93 }
94 
onSocketConnected(FXObject *,FXSelector,void *)95 long DccEngine::onSocketConnected(FXObject*, FXSelector, void*)
96 {
97     if(m_file.type == DCC_IN)
98     {
99         if(m_file.currentPosition)
100         {
101             m_receivedFile.open(FXString(m_file.path+".part").text(), std::ios_base::binary|std::ios_base::ate|std::ios_base::app);
102         }
103         else
104             m_receivedFile.open(FXString(m_file.path+".part").text(), std::ios_base::binary);
105         m_file.started = TRUE;
106     }
107     if(m_file.type == DCC_POUT)
108     {
109         m_sentFile.open(m_file.path.text(), std::ios_base::binary);
110         m_file.started = TRUE;
111         if(m_file.currentPosition) m_sentFile.seekg(m_file.currentPosition, std::ios::beg);
112         writeData();
113     }
114     m_connected = TRUE;
115     m_application->addTimeout(this, DccEngine_PTIME, 1000);
116     return 1;
117 }
118 
onSocketDisconnected(FXObject *,FXSelector,void *)119 long DccEngine::onSocketDisconnected(FXObject*, FXSelector, void*)
120 {
121     m_connected = FALSE;
122     m_application->removeTimeout(this, DccEngine_CTIME);
123     closeFile();
124     return 1;
125 }
126 
onSocketError(FXObject *,FXSelector,void * ptr)127 long DccEngine::onSocketError(FXObject*, FXSelector, void *ptr)
128 {
129     m_connected = FALSE;
130     m_application->removeTimeout(this, DccEngine_CTIME);
131     closeFile();
132     SocketError *err = NULL;
133     if(ptr) err = *((SocketError**)ptr);
134     if(err)
135     {
136         if(!err->errorStr.empty())
137             sendEvent(IRC_ERROR, err->errorStr);
138         switch(err->errorType){
139             case UNABLEINIT:
140             {
141                 sendEvent(IRC_ERROR, _("Unable to initiliaze socket"));
142                 break;
143             }
144             case BADHOST:
145             {
146                 sendEvent(IRC_ERROR, FXStringFormat(_("Bad host: %s"), m_file.ip.text()));
147                 break;
148             }
149             case UNABLECREATE:
150             {
151                 sendEvent(IRC_ERROR, _("Unable to create socket"));
152                 break;
153             }
154             case UNABLECONNECT:
155             {
156                 sendEvent(IRC_ERROR, FXStringFormat(_("Unable to connect to: %s"), m_file.ip.text()));
157                 break;
158             }
159             case SSLUNCREATE:
160             {
161                 sendEvent(IRC_ERROR, _("SSL creation error"));
162                 break;
163             }
164             case SSLCONNECTERROR:
165             {
166                 sendEvent(IRC_ERROR, _("SSL connect error"));
167                 break;
168             }
169             case UNABLEBIND:
170             {
171                 sendEvent(IRC_ERROR, _("Unable to bind socket"));
172                 break;
173             }
174             case UNABLELISTEN:
175             {
176                 sendEvent(IRC_ERROR, _("Unable to listen"));
177                 break;
178             }
179             case UNABLEACCEPT:
180             {
181                 sendEvent(IRC_ERROR, _("Unable to accept connection"));
182                 break;
183             }
184             case UNABLESEND:
185             {
186                 sendEvent(IRC_ERROR, _("Unable to send data"));
187                 break;
188             }
189             case UNABLEREAD:
190             {
191                 sendEvent(IRC_ERROR, FXStringFormat(_("Error in reading data from %s"), m_file.ip.text()));
192                 break;
193             }
194             case SSLZERO:
195             {
196                 sendEvent(IRC_ERROR, _("SSL_read() returns zero - closing socket"));
197                 break;
198             }
199             case SSLUNABLEREAD:
200             {
201                 sendEvent(IRC_ERROR, _("SSL read problem"));
202                 break;
203             }
204             case SSLABNORMAL:
205             {
206                 sendEvent(IRC_ERROR, _("Abnormal value from SSL read"));
207                 break;
208             }
209             case UNABLEREADBUFFER:
210             {
211                 sendEvent(IRC_ERROR, _("Unable read data from buffer"));
212                 break;
213             }
214             default:
215                break;
216         }
217         delete err;
218     }
219     return 1;
220 }
221 
onSocketListen(FXObject *,FXSelector,void *)222 long DccEngine::onSocketListen(FXObject*, FXSelector, void*)
223 {
224     m_application->removeTimeout(this, DccEngine_CTIME);
225     if(m_file.type == DCC_OUT)
226     {
227         m_sentFile.open(m_file.path.text(), std::ios_base::binary);
228         m_file.started = TRUE;
229         if(m_file.currentPosition) m_sentFile.seekg(m_file.currentPosition, std::ios::beg);
230         writeData();
231     }
232     if(m_file.type == DCC_PIN)
233     {
234         m_receivedFile.open(FXString(m_file.path+".part").text(), std::ios_base::binary);
235         m_file.started = TRUE;
236     }
237     m_connected = TRUE;
238     m_application->addTimeout(this, DccEngine_PTIME, 1000);
239     return 1;
240 }
241 
onSocketStartAccept(FXObject *,FXSelector,void *)242 long DccEngine::onSocketStartAccept(FXObject*, FXSelector, void*)
243 {
244     if(m_file.type == DCC_OUT)
245     {
246         m_file.port = m_socket->getPort();
247         m_engine->sendCtcp(m_file.nick, "DCC SEND "+dxutils::removeSpaces(m_file.path.rafter(PATHSEP))+" "+m_socket->stringIPToBinary(m_file.ip)+" "+FXStringVal(m_file.port)+" "+FXStringVal(m_file.size));
248     }
249     else if(m_file.type == DCC_PIN)
250     {
251         m_file.port = m_socket->getPort();
252         m_engine->sendCtcp(m_file.nick, "DCC SEND "+dxutils::removeSpaces(m_file.path.rafter(PATHSEP))+" "+m_socket->stringIPToBinary(m_file.ip)+" "+FXStringVal(m_file.port)+" "+FXStringVal(m_file.size)+" "+FXStringVal(m_file.token));
253     }
254     m_application->addTimeout(this, DccEngine_CTIME, m_dccTimeout*1000);
255     return 1;
256 }
257 
sendEvent(IrcEventType eventType,const FXString & param1)258 void DccEngine::sendEvent(IrcEventType eventType, const FXString &param1)
259 {
260     IrcEvent ev;
261     ev.eventType = eventType;
262     ev.param1 = param1;
263     ev.param2 = "";
264     ev.param3 = "";
265     ev.param4 = "";
266     ev.dccFile = m_file;
267     ev.time = FXSystem::now();
268     m_target->handle(this, FXSEL(SEL_COMMAND, DccEngine_DCC), &ev);
269 }
270 
sendEvent(IrcEventType eventType,const FXString & param1,const FXString & param2,const FXString & param3,const FXString & param4)271 void DccEngine::sendEvent(IrcEventType eventType, const FXString &param1, const FXString &param2, const FXString &param3, const FXString &param4)
272 {
273     IrcEvent ev;
274     ev.eventType = eventType;
275     ev.param1 = param1;
276     ev.param2 = param2;
277     ev.param3 = param3;
278     ev.param4 = param4;
279     ev.dccFile = m_file;
280     ev.time = FXSystem::now();
281     m_target->handle(this, FXSEL(SEL_COMMAND, DccEngine_DCC), &ev);
282 }
283 
updateDcc()284 void DccEngine::updateDcc()
285 {
286     if(m_positionChanged)
287     {
288         m_file.speed = m_dataAmount - m_file.speed;
289         m_lastChange = FXSystem::now();
290         m_dataAmount = m_file.speed;
291     }
292     else
293     {
294         if(FXSystem::now() != m_lastChange) m_file.speed = m_dataAmount/(FXSystem::now()-m_lastChange);
295         else m_file.speed = 0;
296     }
297     IrcEvent ev;
298     ev.eventType = IRC_DCCPOSITION;
299     ev.param1 = "";
300     ev.param2 = "";
301     ev.param3 = "";
302     ev.param4 = "";
303     ev.dccFile = m_file;
304     ev.time = FXSystem::now();
305     m_target->handle(this, FXSEL(SEL_COMMAND, DccEngine_DCC), &ev);
306     m_positionChanged = FALSE;
307 }
308 
startConnection()309 void DccEngine::startConnection()
310 {
311     if(m_connected)
312         return;
313     FXbool listen;
314     if(m_file.type == DCC_OUT || m_file.type == DCC_PIN)
315     {
316         listen = TRUE;
317         FXString address = dxUtils.getStringIniEntry("SETTINGS", "dccIP");
318         if(address.empty()) address = m_engine->getMyUserHost();
319         if(address.empty()) address = m_engine->getLocalIP();
320         if(address.empty())
321         {
322             m_file.canceled = TRUE;
323             return;
324         }
325         m_file.ip = address;
326     }
327     else
328         listen = FALSE;
329     m_socket->setIsSSL(FALSE);
330     m_socket->setHost(m_file.ip, m_file.port);
331     m_socket->setTarget(this);
332     if(listen) m_socket->listenOn();
333     else m_socket->connectTo();
334 }
335 
disconnect()336 void DccEngine::disconnect()
337 {
338     m_connected = FALSE;
339     closeFile();
340     m_socket->disconnect();
341 }
342 
readData()343 void DccEngine::readData()
344 {
345     if(m_file.type == DCC_IN || m_file.type == DCC_PIN)
346     {
347         FXint read = m_socket->getBytesAvailable();
348         if(read<=0) read = 4096;
349         FXchar *buffer = new FXchar[read];
350         FXint size = 0;
351         size = m_socket->read(buffer, read);
352         if(size >= 0)
353         {
354             m_file.currentPosition += size;
355             if(m_receivedFile.good())
356                 m_receivedFile.write(buffer, size);
357             FXlong pos = htonl(m_file.currentPosition);
358             m_socket->write(reinterpret_cast<char *>(&pos), 4);
359         }
360         if(m_file.currentPosition >= m_file.size)
361             closeFile();
362         m_positionChanged = TRUE;
363         m_dataAmount += size;
364         delete []buffer;
365     }
366     else
367     {
368         while(m_socket->getBytesAvailable())
369         {
370             FXulong pos;
371             m_socket->read(reinterpret_cast<FXchar*>(&pos), 4);
372             pos = ntohl(pos);
373             m_file.finishedPosition = pos;
374         }
375         if(m_file.finishedPosition >= m_file.size)
376         {
377             closeFile();
378             m_socket->disconnect();
379         }
380         writeData();
381     }
382 }
383 
writeData()384 void DccEngine::writeData()
385 {
386     if(m_file.type == DCC_IN || m_file.type == DCC_PIN)
387         return;
388     if(m_file.currentPosition >= m_file.size)
389         return;
390     FXchar buf[4096];
391     if(m_sentFile.good())
392         m_sentFile.read(buf, 4096);
393     FXuint readedChars = m_sentFile.gcount();
394     m_socket->write(buf, FXMIN(readedChars, m_file.size-m_file.currentPosition));
395 }
396 
closeFile()397 void DccEngine::closeFile()
398 {
399     if(m_file.type == DCC_IN || m_file.type == DCC_PIN)
400     {
401         while(m_socket->getBytesAvailable())
402         {
403             FXchar buffer[4096];
404             FXint size = 0;
405             size = m_socket->read(buffer, 4096);
406             if(size < 0)
407                 break;
408             m_file.currentPosition += size;
409             if(m_receivedFile.good())
410                 m_receivedFile.write(buffer, size);
411             FXlong pos = htonl(m_file.currentPosition);
412             if(m_socket->isConnected()) m_socket->write(reinterpret_cast<char *>(&pos), 4);
413         }
414         m_receivedFile.close();
415         if(m_file.currentPosition < m_file.size)
416             m_file.canceled = TRUE;
417         else
418         {
419             FXFile::rename(FXString(m_file.path+".part"), m_file.path);
420             m_file.finished = TRUE;
421         }
422     }
423     if(m_file.type == DCC_POUT || m_file.type == DCC_OUT)
424     {
425         m_sentFile.close();
426         while(m_socket->getBytesAvailable())
427         {
428             FXulong pos;
429             m_socket->read(reinterpret_cast<FXchar*>(&pos), 4);
430             pos = ntohl(pos);
431             m_file.finishedPosition = pos;
432         }
433         if(m_file.finishedPosition < m_file.size)
434             m_file.canceled = TRUE;
435         else
436             m_file.finished = TRUE;
437     }
438 }
439 
440 //change position in file mainly for resumed file
setDccPosition(FXulong position)441 FXbool DccEngine::setDccPosition(FXulong position)
442 {
443     if(position >= m_file.size) return FALSE;
444     m_file.currentPosition = position;
445     m_file.speed = 0;
446     m_file.finishedPosition = position;
447     m_file.canceled = FALSE;
448     return TRUE;
449 }
450 
451 //check dccfile for resume
isForResume(const FXString & nick,const FXString & name,FXint port)452 FXbool DccEngine::isForResume(const FXString& nick, const FXString& name, FXint port)
453 {
454     return m_file.nick == nick && FXPath::name(m_file.path) == name
455                 && m_file.port == port;
456 }
457 
458 //check dccfile for resume
isForResume(FXint token)459 FXbool DccEngine::isForResume(FXint token)
460 {
461     return m_file.token == token;
462 }
463