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