LibIRCClient 1.10 Used by Probotic
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
Repozitorijs ir arhivēts. Tam var aplūkot failus un to var klonēt, bet nevar iesūtīt jaunas izmaiņas, kā arī atvērt jaunas problēmas/izmaiņu pieprasījumus.

898 rindas
24KB

  1. /*
  2. * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
  3. *
  4. * This library is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU Lesser General Public License as published by
  6. * the Free Software Foundation; either version 3 of the License, or (at your
  7. * option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  12. * License for more details.
  13. */
  14. #define LIBIRC_DCC_CHAT 1
  15. #define LIBIRC_DCC_SENDFILE 2
  16. #define LIBIRC_DCC_RECVFILE 3
  17. static irc_dcc_session_t * libirc_find_dcc_session (irc_session_t * session, irc_dcc_t dccid, int lock_list)
  18. {
  19. irc_dcc_session_t * s, *found = 0;
  20. if ( lock_list )
  21. libirc_mutex_lock (&session->mutex_dcc);
  22. for ( s = session->dcc_sessions; s; s = s->next )
  23. {
  24. if ( s->id == dccid )
  25. {
  26. found = s;
  27. break;
  28. }
  29. }
  30. if ( found == 0 && lock_list )
  31. libirc_mutex_unlock (&session->mutex_dcc);
  32. return found;
  33. }
  34. static void libirc_dcc_destroy_nolock (irc_session_t * session, irc_dcc_t dccid)
  35. {
  36. irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 0);
  37. if ( dcc )
  38. {
  39. if ( dcc->sock >= 0 )
  40. socket_close (&dcc->sock);
  41. dcc->state = LIBIRC_STATE_REMOVED;
  42. }
  43. }
  44. static void libirc_remove_dcc_session (irc_session_t * session, irc_dcc_session_t * dcc, int lock_list)
  45. {
  46. if ( dcc->sock >= 0 )
  47. socket_close (&dcc->sock);
  48. if ( dcc->dccsend_file_fp )
  49. fclose (dcc->dccsend_file_fp);
  50. dcc->dccsend_file_fp = 0;
  51. libirc_mutex_destroy (&dcc->mutex_outbuf);
  52. if ( lock_list )
  53. libirc_mutex_lock (&session->mutex_dcc);
  54. if ( session->dcc_sessions != dcc )
  55. {
  56. irc_dcc_session_t * s;
  57. for ( s = session->dcc_sessions; s; s = s->next )
  58. {
  59. if ( s->next == dcc )
  60. {
  61. s->next = dcc->next;
  62. break;
  63. }
  64. }
  65. }
  66. else
  67. session->dcc_sessions = dcc->next;
  68. if ( lock_list )
  69. libirc_mutex_unlock (&session->mutex_dcc);
  70. free (dcc);
  71. }
  72. static void libirc_dcc_add_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set, int * maxfd)
  73. {
  74. irc_dcc_session_t * dcc, *dcc_next;
  75. time_t now = time (0);
  76. libirc_mutex_lock (&ircsession->mutex_dcc);
  77. // Preprocessing DCC list:
  78. // - ask DCC send callbacks for data;
  79. // - remove unused DCC structures
  80. for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc_next )
  81. {
  82. dcc_next = dcc->next;
  83. // Remove timed-out sessions
  84. if ( (dcc->state == LIBIRC_STATE_CONNECTING
  85. || dcc->state == LIBIRC_STATE_INIT
  86. || dcc->state == LIBIRC_STATE_LISTENING)
  87. && now - dcc->timeout > ircsession->dcc_timeout )
  88. {
  89. // Inform the caller about DCC timeout.
  90. // Do not inform when state is LIBIRC_STATE_INIT - session
  91. // was initiated from someone else, and callbacks aren't set yet.
  92. if ( dcc->state != LIBIRC_STATE_INIT )
  93. {
  94. libirc_mutex_unlock (&ircsession->mutex_dcc);
  95. if ( dcc->cb )
  96. (*dcc->cb)(ircsession, dcc->id, LIBIRC_ERR_TIMEOUT, dcc->ctx, 0, 0);
  97. libirc_mutex_lock (&ircsession->mutex_dcc);
  98. }
  99. libirc_remove_dcc_session (ircsession, dcc, 0);
  100. }
  101. /*
  102. * If we're sending file, and the output buffer is empty, we need
  103. * to provide some data.
  104. */
  105. if ( dcc->state == LIBIRC_STATE_CONNECTED
  106. && dcc->dccmode == LIBIRC_DCC_SENDFILE
  107. && dcc->dccsend_file_fp
  108. && dcc->outgoing_offset == 0 )
  109. {
  110. int len = fread (dcc->outgoing_buf, 1, sizeof (dcc->outgoing_buf), dcc->dccsend_file_fp);
  111. if ( len <= 0 )
  112. {
  113. int err = (len < 0 ? LIBIRC_ERR_READ : 0);
  114. libirc_mutex_unlock (&ircsession->mutex_dcc);
  115. (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
  116. libirc_mutex_lock (&ircsession->mutex_dcc);
  117. libirc_dcc_destroy_nolock (ircsession, dcc->id);
  118. }
  119. else
  120. dcc->outgoing_offset = len;
  121. }
  122. // Clean up unused sessions
  123. if ( dcc->state == LIBIRC_STATE_REMOVED )
  124. libirc_remove_dcc_session (ircsession, dcc, 0);
  125. }
  126. for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next )
  127. {
  128. switch (dcc->state)
  129. {
  130. case LIBIRC_STATE_LISTENING:
  131. // While listening, only in_set descriptor should be set
  132. libirc_add_to_set (dcc->sock, in_set, maxfd);
  133. break;
  134. case LIBIRC_STATE_CONNECTING:
  135. // While connection, only out_set descriptor should be set
  136. libirc_add_to_set (dcc->sock, out_set, maxfd);
  137. break;
  138. case LIBIRC_STATE_CONNECTED:
  139. // Add input descriptor if there is space in input buffer
  140. // and it is DCC chat (during DCC send, there is nothing to recv)
  141. if ( dcc->incoming_offset < sizeof(dcc->incoming_buf) - 1 )
  142. libirc_add_to_set (dcc->sock, in_set, maxfd);
  143. // Add output descriptor if there is something in output buffer
  144. libirc_mutex_lock (&dcc->mutex_outbuf);
  145. if ( dcc->outgoing_offset > 0 )
  146. libirc_add_to_set (dcc->sock, out_set, maxfd);
  147. libirc_mutex_unlock (&dcc->mutex_outbuf);
  148. break;
  149. case LIBIRC_STATE_CONFIRM_SIZE:
  150. /*
  151. * If we're receiving file, then WE should confirm the transferred
  152. * part (so we have to sent data). But if we're sending the file,
  153. * then RECEIVER should confirm the packet, so we have to receive
  154. * data.
  155. *
  156. * We don't need to LOCK_DCC_OUTBUF - during file transfer, buffers
  157. * can't change asynchronously.
  158. */
  159. if ( dcc->dccmode == LIBIRC_DCC_RECVFILE && dcc->outgoing_offset > 0 )
  160. libirc_add_to_set (dcc->sock, out_set, maxfd);
  161. if ( dcc->dccmode == LIBIRC_DCC_SENDFILE && dcc->incoming_offset < 4 )
  162. libirc_add_to_set (dcc->sock, in_set, maxfd);
  163. }
  164. }
  165. libirc_mutex_unlock (&ircsession->mutex_dcc);
  166. }
  167. static void libirc_dcc_process_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set)
  168. {
  169. irc_dcc_session_t * dcc;
  170. /*
  171. * We need to use such a complex scheme here, because on every callback
  172. * a number of DCC sessions could be destroyed.
  173. */
  174. libirc_mutex_lock (&ircsession->mutex_dcc);
  175. for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next )
  176. {
  177. if ( dcc->state == LIBIRC_STATE_LISTENING
  178. && FD_ISSET (dcc->sock, in_set) )
  179. {
  180. socklen_t len = sizeof(dcc->remote_addr);
  181. #if defined(_WIN32)
  182. SOCKET nsock, err = 0;
  183. #else
  184. int nsock, err = 0;
  185. #endif
  186. // New connection is available; accept it.
  187. if ( socket_accept (&dcc->sock, &nsock, (struct sockaddr *) &dcc->remote_addr, &len) )
  188. err = LIBIRC_ERR_ACCEPT;
  189. // On success, change the active socket and change the state
  190. if ( err == 0 )
  191. {
  192. // close the listen socket, and replace it by a newly
  193. // accepted
  194. socket_close (&dcc->sock);
  195. dcc->sock = nsock;
  196. dcc->state = LIBIRC_STATE_CONNECTED;
  197. }
  198. // If this is DCC chat, inform the caller about accept()
  199. // success or failure.
  200. // Otherwise (DCC send) there is no reason.
  201. if ( dcc->dccmode == LIBIRC_DCC_CHAT )
  202. {
  203. libirc_mutex_unlock (&ircsession->mutex_dcc);
  204. (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
  205. libirc_mutex_lock (&ircsession->mutex_dcc);
  206. }
  207. if ( err )
  208. libirc_dcc_destroy_nolock (ircsession, dcc->id);
  209. }
  210. if ( dcc->state == LIBIRC_STATE_CONNECTING
  211. && FD_ISSET (dcc->sock, out_set) )
  212. {
  213. // Now we have to determine whether the socket is connected
  214. // or the connect is failed
  215. struct sockaddr_in saddr;
  216. socklen_t slen = sizeof(saddr);
  217. int err = 0;
  218. if ( getpeername (dcc->sock, (struct sockaddr*)&saddr, &slen) < 0 )
  219. err = LIBIRC_ERR_CONNECT;
  220. // On success, change the state
  221. if ( err == 0 )
  222. dcc->state = LIBIRC_STATE_CONNECTED;
  223. // If this is DCC chat, inform the caller about connect()
  224. // success or failure.
  225. // Otherwise (DCC send) there is no reason.
  226. if ( dcc->dccmode == LIBIRC_DCC_CHAT )
  227. {
  228. libirc_mutex_unlock (&ircsession->mutex_dcc);
  229. (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
  230. libirc_mutex_lock (&ircsession->mutex_dcc);
  231. }
  232. if ( err )
  233. libirc_dcc_destroy_nolock (ircsession, dcc->id);
  234. }
  235. if ( dcc->state == LIBIRC_STATE_CONNECTED
  236. || dcc->state == LIBIRC_STATE_CONFIRM_SIZE )
  237. {
  238. if ( FD_ISSET (dcc->sock, in_set) )
  239. {
  240. int length, offset = 0, err = 0;
  241. unsigned int amount = sizeof (dcc->incoming_buf) - dcc->incoming_offset;
  242. length = socket_recv (&dcc->sock, dcc->incoming_buf + dcc->incoming_offset, amount);
  243. if ( length < 0 )
  244. {
  245. err = LIBIRC_ERR_READ;
  246. }
  247. else if ( length == 0 )
  248. {
  249. err = LIBIRC_ERR_CLOSED;
  250. if ( dcc->dccsend_file_fp )
  251. {
  252. fclose (dcc->dccsend_file_fp);
  253. dcc->dccsend_file_fp = 0;
  254. }
  255. }
  256. else
  257. {
  258. dcc->incoming_offset += length;
  259. if ( dcc->dccmode != LIBIRC_DCC_CHAT )
  260. offset = dcc->incoming_offset;
  261. else
  262. offset = libirc_findcrorlf (dcc->incoming_buf, dcc->incoming_offset);
  263. /*
  264. * In LIBIRC_STATE_CONFIRM_SIZE state we don't call any
  265. * callbacks (except there is an error). We just receive
  266. * the data, and compare it with the amount sent.
  267. */
  268. if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE )
  269. {
  270. if ( dcc->dccmode != LIBIRC_DCC_SENDFILE )
  271. abort();
  272. if ( dcc->incoming_offset == 4 )
  273. {
  274. // The order is big-endian
  275. const unsigned char * bptr = (const unsigned char *) dcc->incoming_buf;
  276. unsigned int received_size = (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3];
  277. // Sent size confirmed
  278. if ( dcc->file_confirm_offset == received_size )
  279. {
  280. dcc->state = LIBIRC_STATE_CONNECTED;
  281. dcc->incoming_offset = 0;
  282. }
  283. else
  284. err = LIBIRC_ERR_WRITE;
  285. }
  286. }
  287. else
  288. {
  289. /*
  290. * If it is DCC_CHAT, we send a 0-terminated string
  291. * (which is smaller than offset). Otherwise we send
  292. * a full buffer.
  293. */
  294. libirc_mutex_unlock (&ircsession->mutex_dcc);
  295. if ( dcc->dccmode != LIBIRC_DCC_CHAT )
  296. {
  297. if ( dcc->dccmode != LIBIRC_DCC_RECVFILE )
  298. abort();
  299. (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, offset);
  300. /*
  301. * If the session is not terminated in callback,
  302. * put the sent amount into the sent_packet_size_net_byteorder
  303. */
  304. if ( dcc->state != LIBIRC_STATE_REMOVED )
  305. {
  306. dcc->state = LIBIRC_STATE_CONFIRM_SIZE;
  307. dcc->file_confirm_offset += offset;
  308. // Store as big endian
  309. dcc->outgoing_buf[0] = (char) dcc->file_confirm_offset >> 24;
  310. dcc->outgoing_buf[1] = (char) dcc->file_confirm_offset >> 16;
  311. dcc->outgoing_buf[2] = (char) dcc->file_confirm_offset >> 8;
  312. dcc->outgoing_buf[3] = (char) dcc->file_confirm_offset;
  313. dcc->outgoing_offset = 4;
  314. }
  315. }
  316. else
  317. (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, strlen(dcc->incoming_buf));
  318. libirc_mutex_lock (&ircsession->mutex_dcc);
  319. if ( dcc->incoming_offset - offset > 0 )
  320. memmove (dcc->incoming_buf, dcc->incoming_buf + offset, dcc->incoming_offset - offset);
  321. dcc->incoming_offset -= offset;
  322. }
  323. }
  324. /*
  325. * If error arises somewhere above, we inform the caller
  326. * of failure, and destroy this session.
  327. */
  328. if ( err )
  329. {
  330. libirc_mutex_unlock (&ircsession->mutex_dcc);
  331. (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
  332. libirc_mutex_lock (&ircsession->mutex_dcc);
  333. libirc_dcc_destroy_nolock (ircsession, dcc->id);
  334. }
  335. }
  336. /*
  337. * Session might be closed (with sock = -1) after the in_set
  338. * processing, so before out_set processing we should check
  339. * for this case
  340. */
  341. if ( dcc->state == LIBIRC_STATE_REMOVED )
  342. continue;
  343. /*
  344. * Write bit set - we can send() something, and it won't block.
  345. */
  346. if ( FD_ISSET (dcc->sock, out_set) )
  347. {
  348. int length, offset, err = 0;
  349. /*
  350. * Because in some cases outgoing_buf could be changed
  351. * asynchronously (by another thread), we should lock
  352. * it.
  353. */
  354. libirc_mutex_lock (&dcc->mutex_outbuf);
  355. offset = dcc->outgoing_offset;
  356. if ( offset > 0 )
  357. {
  358. length = socket_send (&dcc->sock, dcc->outgoing_buf, offset);
  359. if ( length < 0 )
  360. err = LIBIRC_ERR_WRITE;
  361. else if ( length == 0 )
  362. err = LIBIRC_ERR_CLOSED;
  363. else
  364. {
  365. /*
  366. * If this was DCC_SENDFILE, and we just sent a packet,
  367. * change the state to wait for confirmation (and store
  368. * sent packet size)
  369. */
  370. if ( dcc->state == LIBIRC_STATE_CONNECTED
  371. && dcc->dccmode == LIBIRC_DCC_SENDFILE )
  372. {
  373. dcc->file_confirm_offset += offset;
  374. dcc->state = LIBIRC_STATE_CONFIRM_SIZE;
  375. libirc_mutex_unlock (&ircsession->mutex_dcc);
  376. libirc_mutex_unlock (&dcc->mutex_outbuf);
  377. (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, offset);
  378. libirc_mutex_lock (&ircsession->mutex_dcc);
  379. libirc_mutex_lock (&dcc->mutex_outbuf);
  380. }
  381. if ( dcc->outgoing_offset - length > 0 )
  382. memmove (dcc->outgoing_buf, dcc->outgoing_buf + length, dcc->outgoing_offset - length);
  383. dcc->outgoing_offset -= length;
  384. /*
  385. * If we just sent the confirmation data, change state
  386. * back.
  387. */
  388. if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE
  389. && dcc->dccmode == LIBIRC_DCC_RECVFILE
  390. && dcc->outgoing_offset == 0 )
  391. {
  392. /*
  393. * If the file is already received, we should inform
  394. * the caller, and close the session.
  395. */
  396. if ( dcc->received_file_size == dcc->file_confirm_offset )
  397. {
  398. libirc_mutex_unlock (&ircsession->mutex_dcc);
  399. libirc_mutex_unlock (&dcc->mutex_outbuf);
  400. (*dcc->cb)(ircsession, dcc->id, 0, dcc->ctx, 0, 0);
  401. libirc_dcc_destroy_nolock (ircsession, dcc->id);
  402. }
  403. else
  404. {
  405. /* Continue to receive the file */
  406. dcc->state = LIBIRC_STATE_CONNECTED;
  407. }
  408. }
  409. }
  410. }
  411. libirc_mutex_unlock (&dcc->mutex_outbuf);
  412. /*
  413. * If error arises somewhere above, we inform the caller
  414. * of failure, and destroy this session.
  415. */
  416. if ( err )
  417. {
  418. libirc_mutex_unlock (&ircsession->mutex_dcc);
  419. (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0);
  420. libirc_mutex_lock (&ircsession->mutex_dcc);
  421. libirc_dcc_destroy_nolock (ircsession, dcc->id);
  422. }
  423. }
  424. }
  425. }
  426. libirc_mutex_unlock (&ircsession->mutex_dcc);
  427. }
  428. static int libirc_new_dcc_session (irc_session_t * session, unsigned long ip, unsigned short port, int dccmode, void * ctx, irc_dcc_session_t ** pdcc)
  429. {
  430. irc_dcc_session_t * dcc = malloc (sizeof(irc_dcc_session_t));
  431. if ( !dcc )
  432. return LIBIRC_ERR_NOMEM;
  433. // setup
  434. memset (dcc, 0, sizeof(irc_dcc_session_t));
  435. dcc->dccsend_file_fp = 0;
  436. if ( libirc_mutex_init (&dcc->mutex_outbuf) )
  437. goto cleanup_exit_error;
  438. if ( socket_create (PF_INET, SOCK_STREAM, &dcc->sock) )
  439. goto cleanup_exit_error;
  440. if ( !ip )
  441. {
  442. unsigned long arg = 1;
  443. setsockopt (dcc->sock, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, sizeof(arg));
  444. #if defined (ENABLE_IPV6)
  445. if ( session->flags & SESSIONFL_USES_IPV6 )
  446. {
  447. struct sockaddr_in6 saddr6;
  448. memset (&saddr6, 0, sizeof(saddr6));
  449. saddr6.sin6_family = AF_INET6;
  450. memcpy (&saddr6.sin6_addr, &session->local_addr6, sizeof(session->local_addr6));
  451. saddr6.sin6_port = htons (0);
  452. if ( bind (dcc->sock, (struct sockaddr *) &saddr6, sizeof(saddr6)) < 0 )
  453. goto cleanup_exit_error;
  454. }
  455. else
  456. #endif
  457. {
  458. struct sockaddr_in saddr;
  459. memset (&saddr, 0, sizeof(saddr));
  460. saddr.sin_family = AF_INET;
  461. memcpy (&saddr.sin_addr, &session->local_addr, sizeof(session->local_addr));
  462. saddr.sin_port = htons (0);
  463. if ( bind (dcc->sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0 )
  464. goto cleanup_exit_error;
  465. }
  466. if ( listen (dcc->sock, 5) < 0 )
  467. goto cleanup_exit_error;
  468. dcc->state = LIBIRC_STATE_LISTENING;
  469. }
  470. else
  471. {
  472. // make socket non-blocking, so connect() call won't block
  473. if ( socket_make_nonblocking (&dcc->sock) )
  474. goto cleanup_exit_error;
  475. memset (&dcc->remote_addr, 0, sizeof(dcc->remote_addr));
  476. dcc->remote_addr.sin_family = AF_INET;
  477. dcc->remote_addr.sin_addr.s_addr = htonl (ip); // what idiot came up with idea to send IP address in host-byteorder?
  478. dcc->remote_addr.sin_port = htons(port);
  479. dcc->state = LIBIRC_STATE_INIT;
  480. }
  481. dcc->dccmode = dccmode;
  482. dcc->ctx = ctx;
  483. time (&dcc->timeout);
  484. // and store it
  485. libirc_mutex_lock (&session->mutex_dcc);
  486. dcc->id = session->dcc_last_id++;
  487. dcc->next = session->dcc_sessions;
  488. session->dcc_sessions = dcc;
  489. libirc_mutex_unlock (&session->mutex_dcc);
  490. *pdcc = dcc;
  491. return 0;
  492. cleanup_exit_error:
  493. if ( dcc->sock >= 0 )
  494. socket_close (&dcc->sock);
  495. free (dcc);
  496. return LIBIRC_ERR_SOCKET;
  497. }
  498. int irc_dcc_destroy (irc_session_t * session, irc_dcc_t dccid)
  499. {
  500. // This function doesn't actually destroy the session; it just changes
  501. // its state to "removed" and closes the socket. The memory is actually
  502. // freed after the processing loop.
  503. irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1);
  504. if ( !dcc )
  505. return 1;
  506. if ( dcc->sock >= 0 )
  507. socket_close (&dcc->sock);
  508. dcc->state = LIBIRC_STATE_REMOVED;
  509. libirc_mutex_unlock (&session->mutex_dcc);
  510. return 0;
  511. }
  512. int irc_dcc_chat (irc_session_t * session, void * ctx, const char * nick, irc_dcc_callback_t callback, irc_dcc_t * dccid)
  513. {
  514. struct sockaddr_in saddr;
  515. socklen_t len = sizeof(saddr);
  516. char cmdbuf[128], notbuf[128];
  517. irc_dcc_session_t * dcc;
  518. int err;
  519. if ( session->state != LIBIRC_STATE_CONNECTED )
  520. {
  521. session->lasterror = LIBIRC_ERR_STATE;
  522. return 1;
  523. }
  524. err = libirc_new_dcc_session (session, 0, 0, LIBIRC_DCC_CHAT, ctx, &dcc);
  525. if ( err )
  526. {
  527. session->lasterror = err;
  528. return 1;
  529. }
  530. if ( getsockname (dcc->sock, (struct sockaddr*) &saddr, &len) < 0 )
  531. {
  532. session->lasterror = LIBIRC_ERR_SOCKET;
  533. libirc_remove_dcc_session (session, dcc, 1);
  534. return 1;
  535. }
  536. sprintf (notbuf, "DCC Chat (%s)", inet_ntoa (saddr.sin_addr));
  537. sprintf (cmdbuf, "DCC CHAT chat %lu %u", (unsigned long) ntohl (saddr.sin_addr.s_addr), ntohs (saddr.sin_port));
  538. if ( irc_cmd_notice (session, nick, notbuf)
  539. || irc_cmd_ctcp_request (session, nick, cmdbuf) )
  540. {
  541. libirc_remove_dcc_session (session, dcc, 1);
  542. return 1;
  543. }
  544. *dccid = dcc->id;
  545. dcc->cb = callback;
  546. dcc->dccmode = LIBIRC_DCC_CHAT;
  547. return 0;
  548. }
  549. int irc_dcc_msg (irc_session_t * session, irc_dcc_t dccid, const char * text)
  550. {
  551. irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1);
  552. if ( !dcc )
  553. return 1;
  554. if ( dcc->dccmode != LIBIRC_DCC_CHAT )
  555. {
  556. session->lasterror = LIBIRC_ERR_INVAL;
  557. libirc_mutex_unlock (&session->mutex_dcc);
  558. return 1;
  559. }
  560. if ( (strlen(text) + 2) >= (sizeof(dcc->outgoing_buf) - dcc->outgoing_offset) )
  561. {
  562. session->lasterror = LIBIRC_ERR_NOMEM;
  563. libirc_mutex_unlock (&session->mutex_dcc);
  564. return 1;
  565. }
  566. libirc_mutex_lock (&dcc->mutex_outbuf);
  567. strcpy (dcc->outgoing_buf + dcc->outgoing_offset, text);
  568. dcc->outgoing_offset += strlen (text);
  569. dcc->outgoing_buf[dcc->outgoing_offset++] = 0x0D;
  570. dcc->outgoing_buf[dcc->outgoing_offset++] = 0x0A;
  571. libirc_mutex_unlock (&dcc->mutex_outbuf);
  572. libirc_mutex_unlock (&session->mutex_dcc);
  573. return 0;
  574. }
  575. static void libirc_dcc_request (irc_session_t * session, const char * nick, const char * req)
  576. {
  577. char filenamebuf[256];
  578. unsigned long ip, size;
  579. unsigned short port;
  580. if ( sscanf (req, "DCC CHAT chat %lu %hu", &ip, &port) == 2 )
  581. {
  582. if ( session->callbacks.event_dcc_chat_req )
  583. {
  584. irc_dcc_session_t * dcc;
  585. int err = libirc_new_dcc_session (session, ip, port, LIBIRC_DCC_CHAT, 0, &dcc);
  586. if ( err )
  587. {
  588. session->lasterror = err;
  589. return;
  590. }
  591. (*session->callbacks.event_dcc_chat_req) (session,
  592. nick,
  593. inet_ntoa (dcc->remote_addr.sin_addr),
  594. dcc->id);
  595. }
  596. return;
  597. }
  598. else if ( sscanf (req, "DCC SEND %s %lu %hu %lu", filenamebuf, &ip, &port, &size) == 4 )
  599. {
  600. if ( session->callbacks.event_dcc_send_req )
  601. {
  602. irc_dcc_session_t * dcc;
  603. int err = libirc_new_dcc_session (session, ip, port, LIBIRC_DCC_RECVFILE, 0, &dcc);
  604. if ( err )
  605. {
  606. session->lasterror = err;
  607. return;
  608. }
  609. (*session->callbacks.event_dcc_send_req) (session,
  610. nick,
  611. inet_ntoa (dcc->remote_addr.sin_addr),
  612. filenamebuf,
  613. size,
  614. dcc->id);
  615. dcc->received_file_size = size;
  616. }
  617. return;
  618. }
  619. #if defined (ENABLE_DEBUG)
  620. fprintf (stderr, "BUG: Unhandled DCC message: %s\n", req);
  621. abort();
  622. #endif
  623. }
  624. int irc_dcc_accept (irc_session_t * session, irc_dcc_t dccid, void * ctx, irc_dcc_callback_t callback)
  625. {
  626. irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1);
  627. if ( !dcc )
  628. return 1;
  629. if ( dcc->state != LIBIRC_STATE_INIT )
  630. {
  631. session->lasterror = LIBIRC_ERR_STATE;
  632. libirc_mutex_unlock (&session->mutex_dcc);
  633. return 1;
  634. }
  635. dcc->cb = callback;
  636. dcc->ctx = ctx;
  637. // Initiate the connect
  638. if ( socket_connect (&dcc->sock, (struct sockaddr *) &dcc->remote_addr, sizeof(dcc->remote_addr)) )
  639. {
  640. libirc_dcc_destroy_nolock (session, dccid);
  641. libirc_mutex_unlock (&session->mutex_dcc);
  642. session->lasterror = LIBIRC_ERR_CONNECT;
  643. return 1;
  644. }
  645. dcc->state = LIBIRC_STATE_CONNECTING;
  646. libirc_mutex_unlock (&session->mutex_dcc);
  647. return 0;
  648. }
  649. int irc_dcc_decline (irc_session_t * session, irc_dcc_t dccid)
  650. {
  651. irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1);
  652. if ( !dcc )
  653. return 1;
  654. if ( dcc->state != LIBIRC_STATE_INIT )
  655. {
  656. session->lasterror = LIBIRC_ERR_STATE;
  657. libirc_mutex_unlock (&session->mutex_dcc);
  658. return 1;
  659. }
  660. libirc_dcc_destroy_nolock (session, dccid);
  661. libirc_mutex_unlock (&session->mutex_dcc);
  662. return 0;
  663. }
  664. int irc_dcc_sendfile (irc_session_t * session, void * ctx, const char * nick, const char * filename, irc_dcc_callback_t callback, irc_dcc_t * dccid)
  665. {
  666. struct sockaddr_in saddr;
  667. socklen_t len = sizeof(saddr);
  668. char cmdbuf[128], notbuf[128];
  669. irc_dcc_session_t * dcc;
  670. const char * p;
  671. int err;
  672. long filesize;
  673. if ( !session || !dccid || !filename || !callback )
  674. {
  675. session->lasterror = LIBIRC_ERR_INVAL;
  676. return 1;
  677. }
  678. if ( session->state != LIBIRC_STATE_CONNECTED )
  679. {
  680. session->lasterror = LIBIRC_ERR_STATE;
  681. return 1;
  682. }
  683. if ( (err = libirc_new_dcc_session (session, 0, 0, LIBIRC_DCC_SENDFILE, ctx, &dcc)) != 0 )
  684. {
  685. session->lasterror = err;
  686. return 1;
  687. }
  688. if ( (dcc->dccsend_file_fp = fopen (filename, "rb")) == 0 )
  689. {
  690. libirc_remove_dcc_session (session, dcc, 1);
  691. session->lasterror = LIBIRC_ERR_OPENFILE;
  692. return 1;
  693. }
  694. /* Get file length */
  695. if ( fseek (dcc->dccsend_file_fp, 0, SEEK_END)
  696. || (filesize = ftell (dcc->dccsend_file_fp)) == -1
  697. || fseek (dcc->dccsend_file_fp, 0, SEEK_SET) )
  698. {
  699. libirc_remove_dcc_session (session, dcc, 1);
  700. session->lasterror = LIBIRC_ERR_NODCCSEND;
  701. return 1;
  702. }
  703. if ( getsockname (dcc->sock, (struct sockaddr*) &saddr, &len) < 0 )
  704. {
  705. libirc_remove_dcc_session (session, dcc, 1);
  706. session->lasterror = LIBIRC_ERR_SOCKET;
  707. return 1;
  708. }
  709. // Remove path from the filename
  710. if ( (p = strrchr (filename, '\\')) == 0
  711. && (p = strrchr (filename, '/')) == 0 )
  712. p = filename;
  713. else
  714. p++; // skip directory slash
  715. sprintf (notbuf, "DCC Send %s (%s)", p, inet_ntoa (saddr.sin_addr));
  716. sprintf (cmdbuf, "DCC SEND %s %lu %u %ld", p, (unsigned long) ntohl (saddr.sin_addr.s_addr), ntohs (saddr.sin_port), filesize);
  717. if ( irc_cmd_notice (session, nick, notbuf)
  718. || irc_cmd_ctcp_request (session, nick, cmdbuf) )
  719. {
  720. libirc_remove_dcc_session (session, dcc, 1);
  721. return 1;
  722. }
  723. *dccid = dcc->id;
  724. dcc->cb = callback;
  725. return 0;
  726. }