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 ¶m1)
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 ¶m1, const FXString ¶m2, const FXString ¶m3, const FXString ¶m4)
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