LibIRCClient 1.10 Used by Probotic
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

1288 lines
30KB

  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 IS_DEBUG_ENABLED(s) ((s)->options & LIBIRC_OPTION_DEBUG)
  15. #include "portable.c"
  16. #include "sockets.c"
  17. #include "libircclient.h"
  18. #include "session.h"
  19. #include "utils.c"
  20. #include "errors.c"
  21. #include "colors.c"
  22. #include "dcc.c"
  23. #include "ssl.c"
  24. #ifdef _MSC_VER
  25. /*
  26. * The debugger of MSVC 2005 does not like strdup.
  27. * It complains about heap corruption when free is called.
  28. * Use _strdup instead.
  29. */
  30. #undef strdup
  31. #define strdup _strdup
  32. #endif
  33. #if defined (WIN32_DLL)
  34. static int winsock_refcount = 0;
  35. #endif
  36. irc_session_t * irc_create_session (irc_callbacks_t * callbacks)
  37. {
  38. irc_session_t * session;
  39. #if defined (WIN32_DLL)
  40. // From MSDN: The WSAStartup function typically leads to protocol-specific helper
  41. // DLLs being loaded. As a result, the WSAStartup function should not be called
  42. // from the DllMain function in a application DLL. This can potentially cause deadlocks.
  43. if ( winsock_refcount == 0 )
  44. {
  45. WORD wVersionRequested = MAKEWORD (1, 1);
  46. WSADATA wsaData;
  47. if ( WSAStartup (wVersionRequested, &wsaData) != 0 )
  48. return 0;
  49. winsock_refcount++;
  50. }
  51. #endif
  52. session = malloc (sizeof(irc_session_t));
  53. if ( !session )
  54. return 0;
  55. memset (session, 0, sizeof(irc_session_t));
  56. session->sock = -1;
  57. if ( libirc_mutex_init (&session->mutex_session)
  58. || libirc_mutex_init (&session->mutex_dcc) )
  59. {
  60. free (session);
  61. return 0;
  62. }
  63. session->dcc_last_id = 1;
  64. session->dcc_timeout = 60;
  65. memcpy (&session->callbacks, callbacks, sizeof(irc_callbacks_t));
  66. if ( !session->callbacks.event_ctcp_req )
  67. session->callbacks.event_ctcp_req = libirc_event_ctcp_internal;
  68. return session;
  69. }
  70. static void free_ircsession_strings (irc_session_t * session)
  71. {
  72. if ( session->realname )
  73. free (session->realname);
  74. if ( session->username )
  75. free (session->username);
  76. if ( session->nick )
  77. free (session->nick);
  78. if ( session->server )
  79. free (session->server);
  80. if ( session->server_password )
  81. free (session->server_password);
  82. session->realname = 0;
  83. session->username = 0;
  84. session->nick = 0;
  85. session->server = 0;
  86. session->server_password = 0;
  87. }
  88. void irc_destroy_session (irc_session_t * session)
  89. {
  90. free_ircsession_strings( session );
  91. // The CTCP VERSION must be freed only now
  92. if ( session->ctcp_version )
  93. free (session->ctcp_version);
  94. if ( session->sock >= 0 )
  95. socket_close (&session->sock);
  96. #if defined (ENABLE_THREADS)
  97. libirc_mutex_destroy (&session->mutex_session);
  98. #endif
  99. #if defined (ENABLE_SSL)
  100. if ( session->ssl )
  101. SSL_free( session->ssl );
  102. #endif
  103. /*
  104. * delete DCC data
  105. * libirc_remove_dcc_session removes the DCC session from the list.
  106. */
  107. while ( session->dcc_sessions )
  108. libirc_remove_dcc_session (session, session->dcc_sessions, 0);
  109. libirc_mutex_destroy (&session->mutex_dcc);
  110. free (session);
  111. #if defined (WIN32_DLL)
  112. if ( --winsock_refcount == 0 )
  113. WSACleanup();
  114. #endif
  115. }
  116. int irc_connect (irc_session_t * session,
  117. const char * server,
  118. unsigned short port,
  119. const char * server_password,
  120. const char * nick,
  121. const char * username,
  122. const char * realname)
  123. {
  124. struct sockaddr_in saddr;
  125. char * p;
  126. // Check and copy all the specified fields
  127. if ( !server || !nick )
  128. {
  129. session->lasterror = LIBIRC_ERR_INVAL;
  130. return 1;
  131. }
  132. if ( session->state != LIBIRC_STATE_INIT )
  133. {
  134. session->lasterror = LIBIRC_ERR_STATE;
  135. return 1;
  136. }
  137. // Free the strings if defined; may be the case when the session is reused after the connection fails
  138. free_ircsession_strings( session );
  139. // Handle the server # prefix (SSL)
  140. if ( server[0] == SSL_PREFIX )
  141. {
  142. #if defined (ENABLE_SSL)
  143. server++;
  144. session->flags |= SESSIONFL_SSL_CONNECTION;
  145. #else
  146. session->lasterror = LIBIRC_ERR_SSL_NOT_SUPPORTED;
  147. return 1;
  148. #endif
  149. }
  150. if ( username )
  151. session->username = strdup (username);
  152. if ( server_password )
  153. session->server_password = strdup (server_password);
  154. if ( realname )
  155. session->realname = strdup (realname);
  156. session->nick = strdup (nick);
  157. session->server = strdup (server);
  158. // If port number is zero and server contains the port, parse it
  159. if ( port == 0 && (p = strchr( session->server, ':' )) != 0 )
  160. {
  161. // Terminate the string and parse the port number
  162. *p++ = '\0';
  163. port = atoi( p );
  164. }
  165. // IPv4 address resolving
  166. memset( &saddr, 0, sizeof(saddr) );
  167. saddr.sin_family = AF_INET;
  168. saddr.sin_port = htons (port);
  169. saddr.sin_addr.s_addr = inet_addr( session->server );
  170. if ( saddr.sin_addr.s_addr == INADDR_NONE )
  171. {
  172. struct hostent *hp;
  173. #if defined HAVE_GETHOSTBYNAME_R
  174. int tmp_errno;
  175. struct hostent tmp_hostent;
  176. char buf[2048];
  177. if ( gethostbyname_r (session->server, &tmp_hostent, buf, sizeof(buf), &hp, &tmp_errno) )
  178. hp = 0;
  179. #else
  180. hp = gethostbyname (session->server);
  181. #endif // HAVE_GETHOSTBYNAME_R
  182. if ( !hp )
  183. {
  184. session->lasterror = LIBIRC_ERR_RESOLV;
  185. return 1;
  186. }
  187. memcpy (&saddr.sin_addr, hp->h_addr, (size_t) hp->h_length);
  188. }
  189. // create the IRC server socket
  190. if ( socket_create( PF_INET, SOCK_STREAM, &session->sock)
  191. || socket_make_nonblocking (&session->sock) )
  192. {
  193. session->lasterror = LIBIRC_ERR_SOCKET;
  194. return 1;
  195. }
  196. #if defined (ENABLE_SSL)
  197. // Init the SSL stuff
  198. if ( session->flags & SESSIONFL_SSL_CONNECTION )
  199. {
  200. int rc = ssl_init( session );
  201. if ( rc != 0 )
  202. {
  203. session->lasterror = rc;
  204. return 1;
  205. }
  206. }
  207. #endif
  208. // and connect to the IRC server
  209. if ( socket_connect (&session->sock, (struct sockaddr *) &saddr, sizeof(saddr)) )
  210. {
  211. session->lasterror = LIBIRC_ERR_CONNECT;
  212. return 1;
  213. }
  214. session->state = LIBIRC_STATE_CONNECTING;
  215. session->flags = SESSIONFL_USES_IPV6; // reset in case of reconnect
  216. return 0;
  217. }
  218. int irc_connect6 (irc_session_t * session,
  219. const char * server,
  220. unsigned short port,
  221. const char * server_password,
  222. const char * nick,
  223. const char * username,
  224. const char * realname)
  225. {
  226. #if defined (ENABLE_IPV6)
  227. struct sockaddr_in6 saddr;
  228. struct addrinfo ainfo, *res = NULL;
  229. char portStr[32], *p;
  230. #if defined (_WIN32)
  231. int addrlen = sizeof(saddr);
  232. HMODULE hWsock;
  233. getaddrinfo_ptr_t getaddrinfo_ptr;
  234. freeaddrinfo_ptr_t freeaddrinfo_ptr;
  235. int resolvesuccess = 0;
  236. #endif
  237. // Check and copy all the specified fields
  238. if ( !server || !nick )
  239. {
  240. session->lasterror = LIBIRC_ERR_INVAL;
  241. return 1;
  242. }
  243. if ( session->state != LIBIRC_STATE_INIT )
  244. {
  245. session->lasterror = LIBIRC_ERR_STATE;
  246. return 1;
  247. }
  248. // Free the strings if defined; may be the case when the session is reused after the connection fails
  249. free_ircsession_strings( session );
  250. // Handle the server # prefix (SSL)
  251. if ( server[0] == SSL_PREFIX )
  252. {
  253. #if defined (ENABLE_SSL)
  254. server++;
  255. session->flags |= SESSIONFL_SSL_CONNECTION;
  256. #else
  257. session->lasterror = LIBIRC_ERR_SSL_NOT_SUPPORTED;
  258. return 1;
  259. #endif
  260. }
  261. if ( username )
  262. session->username = strdup (username);
  263. if ( server_password )
  264. session->server_password = strdup (server_password);
  265. if ( realname )
  266. session->realname = strdup (realname);
  267. session->nick = strdup (nick);
  268. session->server = strdup (server);
  269. // If port number is zero and server contains the port, parse it
  270. if ( port == 0 && (p = strchr( session->server, ':' )) != 0 )
  271. {
  272. // Terminate the string and parse the port number
  273. *p++ = '\0';
  274. port = atoi( p );
  275. }
  276. memset( &saddr, 0, sizeof(saddr) );
  277. saddr.sin6_family = AF_INET6;
  278. saddr.sin6_port = htons (port);
  279. sprintf( portStr, "%u", (unsigned)port );
  280. #if defined (_WIN32)
  281. if ( WSAStringToAddressA( (LPSTR)session->server, AF_INET6, NULL, (struct sockaddr *)&saddr, &addrlen ) == SOCKET_ERROR )
  282. {
  283. hWsock = LoadLibraryA("ws2_32");
  284. if (hWsock)
  285. {
  286. /* Determine functions at runtime, because windows systems < XP do not
  287. * support getaddrinfo. */
  288. getaddrinfo_ptr = (getaddrinfo_ptr_t)GetProcAddress(hWsock, "getaddrinfo");
  289. freeaddrinfo_ptr = (freeaddrinfo_ptr_t)GetProcAddress(hWsock, "freeaddrinfo");
  290. if (getaddrinfo_ptr && freeaddrinfo_ptr)
  291. {
  292. memset(&ainfo, 0, sizeof(ainfo));
  293. ainfo.ai_family = AF_INET6;
  294. ainfo.ai_socktype = SOCK_STREAM;
  295. ainfo.ai_protocol = 0;
  296. if ( getaddrinfo_ptr(session->server, portStr, &ainfo, &res) == 0 && res )
  297. {
  298. resolvesuccess = 1;
  299. memcpy( &saddr, res->ai_addr, res->ai_addrlen );
  300. freeaddrinfo_ptr( res );
  301. }
  302. }
  303. FreeLibrary(hWsock);
  304. }
  305. if (!resolvesuccess)
  306. {
  307. session->lasterror = LIBIRC_ERR_RESOLV;
  308. return 1;
  309. }
  310. }
  311. #else
  312. if ( inet_pton( AF_INET6, session->server, (void*) &saddr.sin6_addr ) <= 0 )
  313. {
  314. memset( &ainfo, 0, sizeof(ainfo) );
  315. ainfo.ai_family = AF_INET6;
  316. ainfo.ai_socktype = SOCK_STREAM;
  317. ainfo.ai_protocol = 0;
  318. if ( getaddrinfo( session->server, portStr, &ainfo, &res ) || !res )
  319. {
  320. session->lasterror = LIBIRC_ERR_RESOLV;
  321. return 1;
  322. }
  323. memcpy( &saddr, res->ai_addr, res->ai_addrlen );
  324. freeaddrinfo( res );
  325. }
  326. #endif
  327. // create the IRC server socket
  328. if ( socket_create( PF_INET6, SOCK_STREAM, &session->sock)
  329. || socket_make_nonblocking (&session->sock) )
  330. {
  331. session->lasterror = LIBIRC_ERR_SOCKET;
  332. return 1;
  333. }
  334. #if defined (ENABLE_SSL)
  335. // Init the SSL stuff
  336. if ( session->flags & SESSIONFL_SSL_CONNECTION )
  337. {
  338. int rc = ssl_init( session );
  339. if ( rc != 0 )
  340. return rc;
  341. }
  342. #endif
  343. // and connect to the IRC server
  344. if ( socket_connect (&session->sock, (struct sockaddr *) &saddr, sizeof(saddr)) )
  345. {
  346. session->lasterror = LIBIRC_ERR_CONNECT;
  347. return 1;
  348. }
  349. session->state = LIBIRC_STATE_CONNECTING;
  350. session->flags = 0; // reset in case of reconnect
  351. return 0;
  352. #else
  353. session->lasterror = LIBIRC_ERR_NOIPV6;
  354. return 1;
  355. #endif
  356. }
  357. int irc_is_connected (irc_session_t * session)
  358. {
  359. return (session->state == LIBIRC_STATE_CONNECTED
  360. || session->state == LIBIRC_STATE_CONNECTING) ? 1 : 0;
  361. }
  362. int irc_run (irc_session_t * session)
  363. {
  364. if ( session->state != LIBIRC_STATE_CONNECTING )
  365. {
  366. session->lasterror = LIBIRC_ERR_STATE;
  367. return 1;
  368. }
  369. while ( irc_is_connected(session) )
  370. {
  371. struct timeval tv;
  372. fd_set in_set, out_set;
  373. int maxfd = 0;
  374. tv.tv_usec = 250000;
  375. tv.tv_sec = 0;
  376. // Init sets
  377. FD_ZERO (&in_set);
  378. FD_ZERO (&out_set);
  379. irc_add_select_descriptors (session, &in_set, &out_set, &maxfd);
  380. if ( select (maxfd + 1, &in_set, &out_set, 0, &tv) < 0 )
  381. {
  382. if ( socket_error() == EINTR )
  383. continue;
  384. session->lasterror = LIBIRC_ERR_TERMINATED;
  385. return 1;
  386. }
  387. if ( irc_process_select_descriptors (session, &in_set, &out_set) )
  388. return 1;
  389. }
  390. return 0;
  391. }
  392. int irc_add_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set, int * maxfd)
  393. {
  394. if ( session->sock < 0
  395. || session->state == LIBIRC_STATE_INIT
  396. || session->state == LIBIRC_STATE_DISCONNECTED )
  397. {
  398. session->lasterror = LIBIRC_ERR_STATE;
  399. return 1;
  400. }
  401. libirc_mutex_lock (&session->mutex_session);
  402. switch (session->state)
  403. {
  404. case LIBIRC_STATE_CONNECTING:
  405. // While connection, only out_set descriptor should be set
  406. libirc_add_to_set (session->sock, out_set, maxfd);
  407. break;
  408. case LIBIRC_STATE_CONNECTED:
  409. // Add input descriptor if there is space in input buffer
  410. if ( session->incoming_offset < (sizeof (session->incoming_buf) - 1)
  411. || (session->flags & SESSIONFL_SSL_WRITE_WANTS_READ) != 0 )
  412. libirc_add_to_set (session->sock, in_set, maxfd);
  413. // Add output descriptor if there is something in output buffer
  414. if ( libirc_findcrlf (session->outgoing_buf, session->outgoing_offset) > 0
  415. || (session->flags & SESSIONFL_SSL_READ_WANTS_WRITE) != 0 )
  416. libirc_add_to_set (session->sock, out_set, maxfd);
  417. break;
  418. }
  419. libirc_mutex_unlock (&session->mutex_session);
  420. libirc_dcc_add_descriptors (session, in_set, out_set, maxfd);
  421. return 0;
  422. }
  423. static void libirc_process_incoming_data (irc_session_t * session, size_t process_length)
  424. {
  425. #define MAX_PARAMS_ALLOWED 10
  426. char buf[2*512], *p, *s;
  427. const char * command = 0, *prefix = 0, *params[MAX_PARAMS_ALLOWED+1];
  428. int code = 0, paramindex = 0;
  429. char *buf_end = buf + process_length;
  430. if ( process_length > sizeof(buf) )
  431. abort(); // should be impossible
  432. memcpy (buf, session->incoming_buf, process_length);
  433. buf[process_length] = '\0';
  434. memset ((char *)params, 0, sizeof(params));
  435. p = buf;
  436. /*
  437. * From RFC 1459:
  438. * <message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
  439. * <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
  440. * <command> ::= <letter> { <letter> } | <number> <number> <number>
  441. * <SPACE> ::= ' ' { ' ' }
  442. * <params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
  443. * <middle> ::= <Any *non-empty* sequence of octets not including SPACE
  444. * or NUL or CR or LF, the first of which may not be ':'>
  445. * <trailing> ::= <Any, possibly *empty*, sequence of octets not including
  446. * NUL or CR or LF>
  447. */
  448. // Parse <prefix>
  449. if ( buf[0] == ':' )
  450. {
  451. while ( *p && *p != ' ')
  452. p++;
  453. *p++ = '\0';
  454. // we use buf+1 to skip the leading colon
  455. prefix = buf + 1;
  456. // If LIBIRC_OPTION_STRIPNICKS is set, we should 'clean up' nick
  457. // right here
  458. if ( session->options & LIBIRC_OPTION_STRIPNICKS )
  459. {
  460. for ( s = buf + 1; *s; s++ )
  461. {
  462. if ( *s == '@' || *s == '!' )
  463. {
  464. *s = '\0';
  465. break;
  466. }
  467. }
  468. }
  469. }
  470. // Parse <command>
  471. if ( isdigit (p[0]) && isdigit (p[1]) && isdigit (p[2]) )
  472. {
  473. p[3] = '\0';
  474. code = atoi (p);
  475. p += 4;
  476. }
  477. else
  478. {
  479. s = p;
  480. while ( *p && *p != ' ')
  481. p++;
  482. *p++ = '\0';
  483. command = s;
  484. }
  485. // Parse middle/params
  486. while ( *p && paramindex < MAX_PARAMS_ALLOWED )
  487. {
  488. // beginning from ':', this is the last param
  489. if ( *p == ':' )
  490. {
  491. params[paramindex++] = p + 1; // skip :
  492. break;
  493. }
  494. // Just a param
  495. for ( s = p; *p && *p != ' '; p++ )
  496. ;
  497. params[paramindex++] = s;
  498. if ( !*p )
  499. break;
  500. *p++ = '\0';
  501. }
  502. // Handle PING/PONG
  503. if ( command && !strncmp (command, "PING", buf_end - command) && params[0] )
  504. {
  505. irc_send_raw (session, "PONG %s", params[0]);
  506. return;
  507. }
  508. // and dump
  509. if ( code )
  510. {
  511. // We use SESSIONFL_MOTD_RECEIVED flag to check whether it is the first
  512. // RPL_ENDOFMOTD or ERR_NOMOTD after the connection.
  513. if ( (code == 1 || code == 376 || code == 422) && !(session->flags & SESSIONFL_MOTD_RECEIVED ) )
  514. {
  515. session->flags |= SESSIONFL_MOTD_RECEIVED;
  516. if ( session->callbacks.event_connect )
  517. (*session->callbacks.event_connect) (session, "CONNECT", prefix, params, paramindex);
  518. }
  519. if ( session->callbacks.event_numeric )
  520. (*session->callbacks.event_numeric) (session, code, prefix, params, paramindex);
  521. }
  522. else
  523. {
  524. if ( !strncmp (command, "NICK", buf_end - command) )
  525. {
  526. /*
  527. * If we're changed our nick, we should save it.
  528. */
  529. char nickbuf[256];
  530. irc_target_get_nick (prefix, nickbuf, sizeof(nickbuf));
  531. if ( !strncmp (nickbuf, session->nick, strlen(session->nick)) && paramindex > 0 )
  532. {
  533. free (session->nick);
  534. session->nick = strdup (params[0]);
  535. }
  536. if ( session->callbacks.event_nick )
  537. (*session->callbacks.event_nick) (session, command, prefix, params, paramindex);
  538. }
  539. else if ( !strncmp (command, "QUIT", buf_end - command) )
  540. {
  541. if ( session->callbacks.event_quit )
  542. (*session->callbacks.event_quit) (session, command, prefix, params, paramindex);
  543. }
  544. else if ( !strncmp (command, "JOIN", buf_end - command) )
  545. {
  546. if ( session->callbacks.event_join )
  547. (*session->callbacks.event_join) (session, command, prefix, params, paramindex);
  548. }
  549. else if ( !strncmp (command, "PART", buf_end - command) )
  550. {
  551. if ( session->callbacks.event_part )
  552. (*session->callbacks.event_part) (session, command, prefix, params, paramindex);
  553. }
  554. else if ( !strncmp (command, "MODE", buf_end - command) )
  555. {
  556. if ( paramindex > 0 && !strncmp (params[0], session->nick, strlen(session->nick)) )
  557. {
  558. params[0] = params[1];
  559. paramindex = 1;
  560. if ( session->callbacks.event_umode )
  561. (*session->callbacks.event_umode) (session, command, prefix, params, paramindex);
  562. }
  563. else
  564. {
  565. if ( session->callbacks.event_mode )
  566. (*session->callbacks.event_mode) (session, command, prefix, params, paramindex);
  567. }
  568. }
  569. else if ( !strncmp (command, "TOPIC", buf_end - command) )
  570. {
  571. if ( session->callbacks.event_topic )
  572. (*session->callbacks.event_topic) (session, command, prefix, params, paramindex);
  573. }
  574. else if ( !strncmp (command, "KICK", buf_end - command) )
  575. {
  576. if ( session->callbacks.event_kick )
  577. (*session->callbacks.event_kick) (session, command, prefix, params, paramindex);
  578. }
  579. else if ( !strncmp (command, "PRIVMSG", buf_end - command) )
  580. {
  581. if ( paramindex > 1 )
  582. {
  583. size_t msglen = strlen (params[1]);
  584. /*
  585. * Check for CTCP request (a CTCP message starts from 0x01
  586. * and ends by 0x01
  587. */
  588. if ( params[1][0] == 0x01 && params[1][msglen-1] == 0x01 )
  589. {
  590. char ctcp_buf[128];
  591. msglen -= 2;
  592. if ( msglen > sizeof(ctcp_buf) - 1 )
  593. msglen = sizeof(ctcp_buf) - 1;
  594. memcpy (ctcp_buf, params[1] + 1, msglen);
  595. ctcp_buf[msglen] = '\0';
  596. if ( !strncasecmp(ctcp_buf, "DCC ", 4) )
  597. libirc_dcc_request (session, prefix, ctcp_buf);
  598. else if ( !strncasecmp( ctcp_buf, "ACTION ", 7)
  599. && session->callbacks.event_ctcp_action )
  600. {
  601. params[1] = ctcp_buf + 7; // the length of "ACTION "
  602. paramindex = 2;
  603. (*session->callbacks.event_ctcp_action) (session, "ACTION", prefix, params, paramindex);
  604. }
  605. else
  606. {
  607. params[0] = ctcp_buf;
  608. paramindex = 1;
  609. if ( session->callbacks.event_ctcp_req )
  610. (*session->callbacks.event_ctcp_req) (session, "CTCP", prefix, params, paramindex);
  611. }
  612. }
  613. else if ( !strncasecmp (params[0], session->nick, strlen(session->nick) ) )
  614. {
  615. if ( session->callbacks.event_privmsg )
  616. (*session->callbacks.event_privmsg) (session, "PRIVMSG", prefix, params, paramindex);
  617. }
  618. else
  619. {
  620. if ( session->callbacks.event_channel )
  621. (*session->callbacks.event_channel) (session, "CHANNEL", prefix, params, paramindex);
  622. }
  623. }
  624. }
  625. else if ( !strncmp (command, "NOTICE", buf_end - command) )
  626. {
  627. size_t msglen = strlen (params[1]);
  628. /*
  629. * Check for CTCP request (a CTCP message starts from 0x01
  630. * and ends by 0x01
  631. */
  632. if ( paramindex > 1 && params[1][0] == 0x01 && params[1][msglen-1] == 0x01 )
  633. {
  634. char ctcp_buf[512];
  635. msglen -= 2;
  636. if ( msglen > sizeof(ctcp_buf) - 1 )
  637. msglen = sizeof(ctcp_buf) - 1;
  638. memcpy (ctcp_buf, params[1] + 1, msglen);
  639. ctcp_buf[msglen] = '\0';
  640. params[0] = ctcp_buf;
  641. paramindex = 1;
  642. if ( session->callbacks.event_ctcp_rep )
  643. (*session->callbacks.event_ctcp_rep) (session, "CTCP", prefix, params, paramindex);
  644. }
  645. else if ( !strncasecmp (params[0], session->nick, strlen(session->nick) ) )
  646. {
  647. if ( session->callbacks.event_notice )
  648. (*session->callbacks.event_notice) (session, command, prefix, params, paramindex);
  649. } else {
  650. if ( session->callbacks.event_channel_notice )
  651. (*session->callbacks.event_channel_notice) (session, command, prefix, params, paramindex);
  652. }
  653. }
  654. else if ( !strncmp (command, "INVITE", buf_end - command) )
  655. {
  656. if ( session->callbacks.event_invite )
  657. (*session->callbacks.event_invite) (session, command, prefix, params, paramindex);
  658. }
  659. else if ( !strncmp (command, "KILL", buf_end - command) )
  660. {
  661. ; /* ignore this event - not all servers generate this */
  662. }
  663. else
  664. {
  665. /*
  666. * The "unknown" event is triggered upon receipt of any number of
  667. * unclassifiable miscellaneous messages, which aren't handled by
  668. * the library.
  669. */
  670. if ( session->callbacks.event_unknown )
  671. (*session->callbacks.event_unknown) (session, command, prefix, params, paramindex);
  672. }
  673. }
  674. }
  675. int irc_process_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set)
  676. {
  677. char buf[256], hname[256];
  678. if ( session->sock < 0
  679. || session->state == LIBIRC_STATE_INIT
  680. || session->state == LIBIRC_STATE_DISCONNECTED )
  681. {
  682. session->lasterror = LIBIRC_ERR_STATE;
  683. return 1;
  684. }
  685. session->lasterror = 0;
  686. libirc_dcc_process_descriptors (session, in_set, out_set);
  687. // Handle "connection succeed" / "connection failed"
  688. if ( session->state == LIBIRC_STATE_CONNECTING )
  689. {
  690. // If the socket is not connected yet, wait longer - it is not an error
  691. if ( !FD_ISSET (session->sock, out_set) )
  692. return 0;
  693. // Now we have to determine whether the socket is connected
  694. // or the connect is failed
  695. struct sockaddr_storage saddr, laddr;
  696. socklen_t slen = sizeof(saddr);
  697. socklen_t llen = sizeof(laddr);
  698. if ( getsockname (session->sock, (struct sockaddr*)&laddr, &llen) < 0
  699. || getpeername (session->sock, (struct sockaddr*)&saddr, &slen) < 0 )
  700. {
  701. // connection failed
  702. session->lasterror = LIBIRC_ERR_CONNECT;
  703. session->state = LIBIRC_STATE_DISCONNECTED;
  704. return 1;
  705. }
  706. if (saddr.ss_family == AF_INET)
  707. memcpy (&session->local_addr, &((struct sockaddr_in *)&laddr)->sin_addr, sizeof(struct in_addr));
  708. else
  709. memcpy (&session->local_addr, &((struct sockaddr_in6 *)&laddr)->sin6_addr, sizeof(struct in6_addr));
  710. #if defined (ENABLE_DEBUG)
  711. if ( IS_DEBUG_ENABLED(session) )
  712. fprintf (stderr, "[DEBUG] Detected local address: %s\n", inet_ntoa(session->local_addr));
  713. #endif
  714. session->state = LIBIRC_STATE_CONNECTED;
  715. // Get the hostname
  716. if ( gethostname (hname, sizeof(hname)) < 0 )
  717. strcpy (hname, "unknown");
  718. // Prepare the data, which should be sent to the server
  719. if ( session->server_password )
  720. {
  721. snprintf (buf, sizeof(buf), "PASS %s", session->server_password);
  722. irc_send_raw (session, buf);
  723. }
  724. snprintf (buf, sizeof(buf), "NICK %s", session->nick);
  725. irc_send_raw (session, buf);
  726. /*
  727. * RFC 1459 states that "hostname and servername are normally
  728. * ignored by the IRC server when the USER command comes from
  729. * a directly connected client (for security reasons)", therefore
  730. * we don't need them.
  731. */
  732. snprintf (buf, sizeof(buf), "USER %s unknown unknown :%s",
  733. session->username ? session->username : "nobody",
  734. session->realname ? session->realname : "noname");
  735. irc_send_raw (session, buf);
  736. return 0;
  737. }
  738. if ( session->state != LIBIRC_STATE_CONNECTED )
  739. {
  740. session->lasterror = LIBIRC_ERR_STATE;
  741. return 1;
  742. }
  743. // Hey, we've got something to read!
  744. if ( FD_ISSET (session->sock, in_set) )
  745. {
  746. int offset, length = session_socket_read( session );
  747. if ( length < 0 )
  748. {
  749. if ( session->lasterror == 0 )
  750. session->lasterror = (length == 0 ? LIBIRC_ERR_CLOSED : LIBIRC_ERR_TERMINATED);
  751. session->state = LIBIRC_STATE_DISCONNECTED;
  752. return 1;
  753. }
  754. session->incoming_offset += length;
  755. // process the incoming data
  756. while ( (offset = libirc_findcrlf (session->incoming_buf, session->incoming_offset)) > 0 )
  757. {
  758. #if defined (ENABLE_DEBUG)
  759. if ( IS_DEBUG_ENABLED(session) )
  760. libirc_dump_data ("RECV", session->incoming_buf, offset);
  761. #endif
  762. // parse the string
  763. libirc_process_incoming_data (session, offset);
  764. offset = libirc_findcrlf_offset(session->incoming_buf, offset, session->incoming_offset);
  765. if ( session->incoming_offset - offset > 0 )
  766. memmove (session->incoming_buf, session->incoming_buf + offset, session->incoming_offset - offset);
  767. session->incoming_offset -= offset;
  768. }
  769. }
  770. // We can write a stored buffer
  771. if ( FD_ISSET (session->sock, out_set) )
  772. {
  773. int length;
  774. // Because outgoing_buf could be changed asynchronously, we should lock any change
  775. libirc_mutex_lock (&session->mutex_session);
  776. length = session_socket_write( session );
  777. if ( length < 0 )
  778. {
  779. if ( session->lasterror == 0 )
  780. session->lasterror = (length == 0 ? LIBIRC_ERR_CLOSED : LIBIRC_ERR_TERMINATED);
  781. session->state = LIBIRC_STATE_DISCONNECTED;
  782. libirc_mutex_unlock (&session->mutex_session);
  783. return 1;
  784. }
  785. #if defined (ENABLE_DEBUG)
  786. if ( IS_DEBUG_ENABLED(session) )
  787. libirc_dump_data ("SEND", session->outgoing_buf, length);
  788. #endif
  789. if ( length > 0 && session->outgoing_offset - length > 0 )
  790. memmove (session->outgoing_buf, session->outgoing_buf + length, session->outgoing_offset - length);
  791. session->outgoing_offset -= length;
  792. libirc_mutex_unlock (&session->mutex_session);
  793. }
  794. return 0;
  795. }
  796. int irc_send_raw (irc_session_t * session, const char * format, ...)
  797. {
  798. char buf[1024];
  799. va_list va_alist;
  800. if ( session->state != LIBIRC_STATE_CONNECTED )
  801. {
  802. session->lasterror = LIBIRC_ERR_STATE;
  803. return 1;
  804. }
  805. va_start (va_alist, format);
  806. vsnprintf (buf, sizeof(buf), format, va_alist);
  807. va_end (va_alist);
  808. libirc_mutex_lock (&session->mutex_session);
  809. if ( (strlen(buf) + 2) >= (sizeof(session->outgoing_buf) - session->outgoing_offset) )
  810. {
  811. libirc_mutex_unlock (&session->mutex_session);
  812. session->lasterror = LIBIRC_ERR_NOMEM;
  813. return 1;
  814. }
  815. strcpy (session->outgoing_buf + session->outgoing_offset, buf);
  816. session->outgoing_offset += strlen (buf);
  817. session->outgoing_buf[session->outgoing_offset++] = 0x0D;
  818. session->outgoing_buf[session->outgoing_offset++] = 0x0A;
  819. libirc_mutex_unlock (&session->mutex_session);
  820. return 0;
  821. }
  822. int irc_cmd_quit (irc_session_t * session, const char * reason)
  823. {
  824. return irc_send_raw (session, "QUIT :%s", reason ? reason : "quit");
  825. }
  826. int irc_cmd_join (irc_session_t * session, const char * channel, const char * key)
  827. {
  828. if ( !channel )
  829. {
  830. session->lasterror = LIBIRC_ERR_STATE;
  831. return 1;
  832. }
  833. if ( key )
  834. return irc_send_raw (session, "JOIN %s :%s", channel, key);
  835. else
  836. return irc_send_raw (session, "JOIN %s", channel);
  837. }
  838. int irc_cmd_part (irc_session_t * session, const char * channel)
  839. {
  840. if ( !channel )
  841. {
  842. session->lasterror = LIBIRC_ERR_STATE;
  843. return 1;
  844. }
  845. return irc_send_raw (session, "PART %s", channel);
  846. }
  847. int irc_cmd_topic (irc_session_t * session, const char * channel, const char * topic)
  848. {
  849. if ( !channel )
  850. {
  851. session->lasterror = LIBIRC_ERR_STATE;
  852. return 1;
  853. }
  854. if ( topic )
  855. return irc_send_raw (session, "TOPIC %s :%s", channel, topic);
  856. else
  857. return irc_send_raw (session, "TOPIC %s", channel);
  858. }
  859. int irc_cmd_names (irc_session_t * session, const char * channel)
  860. {
  861. if ( !channel )
  862. {
  863. session->lasterror = LIBIRC_ERR_STATE;
  864. return 1;
  865. }
  866. return irc_send_raw (session, "NAMES %s", channel);
  867. }
  868. int irc_cmd_list (irc_session_t * session, const char * channel)
  869. {
  870. if ( channel )
  871. return irc_send_raw (session, "LIST %s", channel);
  872. else
  873. return irc_send_raw (session, "LIST");
  874. }
  875. int irc_cmd_invite (irc_session_t * session, const char * nick, const char * channel)
  876. {
  877. if ( !channel || !nick )
  878. {
  879. session->lasterror = LIBIRC_ERR_STATE;
  880. return 1;
  881. }
  882. return irc_send_raw (session, "INVITE %s %s", nick, channel);
  883. }
  884. int irc_cmd_kick (irc_session_t * session, const char * nick, const char * channel, const char * comment)
  885. {
  886. if ( !channel || !nick )
  887. {
  888. session->lasterror = LIBIRC_ERR_STATE;
  889. return 1;
  890. }
  891. if ( comment )
  892. return irc_send_raw (session, "KICK %s %s :%s", channel, nick, comment);
  893. else
  894. return irc_send_raw (session, "KICK %s %s", channel, nick);
  895. }
  896. int irc_cmd_msg (irc_session_t * session, const char * nch, const char * text)
  897. {
  898. if ( !nch || !text )
  899. {
  900. session->lasterror = LIBIRC_ERR_STATE;
  901. return 1;
  902. }
  903. return irc_send_raw (session, "PRIVMSG %s :%s", nch, text);
  904. }
  905. int irc_cmd_notice (irc_session_t * session, const char * nch, const char * text)
  906. {
  907. if ( !nch || !text )
  908. {
  909. session->lasterror = LIBIRC_ERR_STATE;
  910. return 1;
  911. }
  912. return irc_send_raw (session, "NOTICE %s :%s", nch, text);
  913. }
  914. void irc_target_get_nick (const char * target, char *nick, size_t size)
  915. {
  916. char *p = strstr (target, "!");
  917. unsigned int len;
  918. if ( p )
  919. len = p - target;
  920. else
  921. len = strlen (target);
  922. if ( len > size-1 )
  923. len = size - 1;
  924. memcpy (nick, target, len);
  925. nick[len] = '\0';
  926. }
  927. void irc_target_get_host (const char * target, char *host, size_t size)
  928. {
  929. unsigned int len;
  930. const char *p = strstr (target, "!");
  931. if ( !p )
  932. p = target;
  933. len = strlen (p);
  934. if ( len > size-1 )
  935. len = size - 1;
  936. memcpy (host, p, len);
  937. host[len] = '\0';
  938. }
  939. int irc_cmd_ctcp_request (irc_session_t * session, const char * nick, const char * reply)
  940. {
  941. if ( !nick || !reply )
  942. {
  943. session->lasterror = LIBIRC_ERR_STATE;
  944. return 1;
  945. }
  946. return irc_send_raw (session, "PRIVMSG %s :\x01%s\x01", nick, reply);
  947. }
  948. int irc_cmd_ctcp_reply (irc_session_t * session, const char * nick, const char * reply)
  949. {
  950. if ( !nick || !reply )
  951. {
  952. session->lasterror = LIBIRC_ERR_STATE;
  953. return 1;
  954. }
  955. return irc_send_raw (session, "NOTICE %s :\x01%s\x01", nick, reply);
  956. }
  957. void irc_get_version (unsigned int * high, unsigned int * low)
  958. {
  959. *high = LIBIRC_VERSION_HIGH;
  960. *low = LIBIRC_VERSION_LOW;
  961. }
  962. void irc_set_ctx (irc_session_t * session, void * ctx)
  963. {
  964. session->ctx = ctx;
  965. }
  966. void * irc_get_ctx (irc_session_t * session)
  967. {
  968. return session->ctx;
  969. }
  970. void irc_set_ctcp_version (irc_session_t * session, const char * version)
  971. {
  972. if ( session->ctcp_version )
  973. free(session->ctcp_version);
  974. session->ctcp_version = strdup(version);
  975. }
  976. void irc_disconnect (irc_session_t * session)
  977. {
  978. if ( session->sock >= 0 )
  979. socket_close (&session->sock);
  980. session->sock = -1;
  981. session->state = LIBIRC_STATE_INIT;
  982. }
  983. int irc_cmd_me (irc_session_t * session, const char * nch, const char * text)
  984. {
  985. if ( !nch || !text )
  986. {
  987. session->lasterror = LIBIRC_ERR_STATE;
  988. return 1;
  989. }
  990. return irc_send_raw (session, "PRIVMSG %s :\x01" "ACTION %s\x01", nch, text);
  991. }
  992. void irc_option_set (irc_session_t * session, unsigned int option)
  993. {
  994. session->options |= option;
  995. }
  996. void irc_option_reset (irc_session_t * session, unsigned int option)
  997. {
  998. session->options &= ~option;
  999. }
  1000. int irc_cmd_channel_mode (irc_session_t * session, const char * channel, const char * mode)
  1001. {
  1002. if ( !channel )
  1003. {
  1004. session->lasterror = LIBIRC_ERR_INVAL;
  1005. return 1;
  1006. }
  1007. if ( mode )
  1008. return irc_send_raw (session, "MODE %s %s", channel, mode);
  1009. else
  1010. return irc_send_raw (session, "MODE %s", channel);
  1011. }
  1012. int irc_cmd_user_mode (irc_session_t * session, const char * mode)
  1013. {
  1014. if ( mode )
  1015. return irc_send_raw (session, "MODE %s %s", session->nick, mode);
  1016. else
  1017. return irc_send_raw (session, "MODE %s", session->nick);
  1018. }
  1019. int irc_cmd_nick (irc_session_t * session, const char * newnick)
  1020. {
  1021. if ( !newnick )
  1022. {
  1023. session->lasterror = LIBIRC_ERR_INVAL;
  1024. return 1;
  1025. }
  1026. return irc_send_raw (session, "NICK %s", newnick);
  1027. }
  1028. int irc_cmd_whois (irc_session_t * session, const char * nick)
  1029. {
  1030. if ( !nick )
  1031. {
  1032. session->lasterror = LIBIRC_ERR_INVAL;
  1033. return 1;
  1034. }
  1035. return irc_send_raw (session, "WHOIS %s %s", nick, nick);
  1036. }