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