1 /*
2 firetalk.c - FireTalk wrapper definitions
3 Copyright (C) 2000 Ian Gulliver
4 Copyright 2002-2006 Daniel Reed <n@ml.org>
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of version 2 of the GNU General Public License as
8 published by the Free Software Foundation.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 #include <assert.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/time.h>
25 #include <unistd.h>
26 #include <strings.h>
27 #include <netinet/in.h>
28 #include <sys/socket.h>
29 #include <arpa/inet.h>
30 #include <time.h>
31 #include <sys/stat.h>
32 #include <netdb.h>
33 #include <errno.h>
34 #include <signal.h>
35 #include <fcntl.h>
36 
37 #define FIRETALK
38 
39 #include "firetalk-int.h"
40 #include "firetalk.h"
41 
42 typedef void (*ptrtotoc)(void *, ...);
43 typedef void (*sighandler_t)(int);
44 
45 /* Global variables */
46 fte_t	firetalkerror;
47 static struct s_firetalk_handle *handle_head = NULL;
48 
49 static const firetalk_protocol_t **firetalk_protocols = NULL;
50 static int FP_MAX = 0;
51 
firetalk_register_protocol(const firetalk_protocol_t * const proto)52 fte_t	firetalk_register_protocol(const firetalk_protocol_t *const proto) {
53 	const firetalk_protocol_t **ptr;
54 
55 	if (proto == NULL)
56 		abort();
57 
58 	ptr = realloc(firetalk_protocols, sizeof(*firetalk_protocols)*(FP_MAX+1));
59 	if (ptr == NULL)
60 		return(FE_UNKNOWN);
61 	firetalk_protocols = ptr;
62 	firetalk_protocols[FP_MAX++] = proto;
63 	return(FE_SUCCESS);
64 }
65 
firetalk_register_default_protocols(void)66 static void firetalk_register_default_protocols(void) {
67 	extern const firetalk_protocol_t
68 		firetalk_protocol_irc,
69 		firetalk_protocol_toc2;
70 
71 	if (firetalk_register_protocol(&firetalk_protocol_irc) != FE_SUCCESS)
72 		abort();
73 	if (firetalk_register_protocol(&firetalk_protocol_toc2) != FE_SUCCESS)
74 		abort();
75 }
76 
firetalk_find_protocol(const char * strprotocol)77 int	firetalk_find_protocol(const char *strprotocol) {
78 	static int registered_defaults = 0;
79 	int	i;
80 
81 	if (strprotocol == NULL)
82 		abort();
83 
84 	for (i = 0; i < FP_MAX; i++)
85 		if (strcasecmp(strprotocol, firetalk_protocols[i]->strprotocol) == 0)
86 			return(i);
87 	if (!registered_defaults) {
88 		registered_defaults = 1;
89 		firetalk_register_default_protocols();
90 		for (i = 0; i < FP_MAX; i++)
91 			if (strcasecmp(strprotocol, firetalk_protocols[i]->strprotocol) == 0)
92 				return(i);
93 	}
94 	return(-1);
95 }
96 
97 /* Internal function definitions */
98 
99 /* firetalk_find_by_toc searches the firetalk handle list for the toc handle passed, and returns the firetalk handle */
firetalk_find_handle(client_t c)100 firetalk_t firetalk_find_handle(client_t c) {
101 	struct s_firetalk_handle *iter;
102 
103 	for (iter = handle_head; iter != NULL; iter = iter->next)
104 		if (iter->handle == c)
105 			return(iter);
106 	abort();
107 }
108 
firetalk_find_clientstruct(void * clientstruct)109 firetalk_t firetalk_find_clientstruct(void *clientstruct) {
110 	struct s_firetalk_handle *iter;
111 
112 	for (iter = handle_head; iter != NULL; iter = iter->next)
113 		if (iter->clientstruct == clientstruct)
114 			return(iter);
115 	return(NULL);
116 }
117 
118 #define DEBUG
119 
120 #ifdef DEBUG
121 # define VERIFYCONN \
122 	do { \
123 		if (firetalk_check_handle(conn) != FE_SUCCESS) \
124 			abort(); \
125 	} while(0)
126 
firetalk_check_handle(firetalk_t c)127 static fte_t firetalk_check_handle(firetalk_t c) {
128 	struct s_firetalk_handle *iter;
129 
130 	for (iter = handle_head; iter != NULL; iter = iter->next)
131 		if (iter == c)
132 			return(FE_SUCCESS);
133 	return(FE_BADHANDLE);
134 }
135 #else
136 # define VERIFYCONN \
137 	do { \
138 	} while(0)
139 #endif
140 
firetalk_parse_subcode_args(char * string)141 static char **firetalk_parse_subcode_args(char *string) {
142 	static char *args[256];
143 	int i,n;
144 	size_t l;
145 
146 	l = strlen(string);
147 	args[0] = string;
148 	n = 1;
149 	for (i = 0; (size_t) i < l && n < 255; i++) {
150 		if (string[i] == ' ') {
151 			string[i++] = '\0';
152 			args[n++] = &string[i];
153 		}
154 	}
155 	args[n] = NULL;
156 	return(args);
157 }
158 
firetalk_debase64_char(const char c)159 static unsigned char firetalk_debase64_char(const char c) {
160 	if ((c >= 'A') && (c <= 'Z'))
161 		return((unsigned char)(c - 'A'));
162 	if ((c >= 'a') && (c <= 'z'))
163 		return((unsigned char)(26 + (c - 'a')));
164 	if ((c >= '0') && (c <= '9'))
165 		return((unsigned char)(52 + (c - '0')));
166 	if (c == '+')
167 		return((unsigned char)62);
168 	if (c == '/')
169 		return((unsigned char)63);
170 	return((unsigned char)0);
171 }
172 
firetalk_debase64(const char * const str)173 const char *firetalk_debase64(const char *const str) {
174         static unsigned char out[256];
175 	int	s, o, len = strlen(str);
176 
177 	for (o = s = 0; (s <= (len - 3)) && (o < (sizeof(out)-1)); s += 4, o += 3) {
178 		out[o]   = (firetalk_debase64_char(str[s])   << 2) | (firetalk_debase64_char(str[s+1]) >> 4);
179 		out[o+1] = (firetalk_debase64_char(str[s+1]) << 4) | (firetalk_debase64_char(str[s+2]) >> 2);
180 		out[o+2] = (firetalk_debase64_char(str[s+2]) << 6) |  firetalk_debase64_char(str[s+3]);
181 	}
182 	out[o] = 0;
183 	return((char *)out);
184 }
185 
186 
firetalk_im_internal_add_deny(firetalk_t conn,const char * const nickname)187 fte_t	firetalk_im_internal_add_deny(firetalk_t conn, const char *const nickname) {
188 	struct s_firetalk_deny *iter;
189 
190 	VERIFYCONN;
191 
192 	for (iter = conn->deny_head; iter != NULL; iter = iter->next)
193 		if (firetalk_protocols[conn->protocol]->comparenicks(iter->nickname, nickname) == FE_SUCCESS)
194 			break; /* not an error, user is in buddy list */
195 
196 	if (iter == NULL) {
197 		iter = conn->deny_head;
198 		conn->deny_head = calloc(1, sizeof(struct s_firetalk_deny));
199 		if (conn->deny_head == NULL)
200 			abort();
201 		conn->deny_head->next = iter;
202 		conn->deny_head->nickname = strdup(nickname);
203 		if (conn->deny_head->nickname == NULL)
204 			abort();
205 	}
206 
207 	firetalk_callback_im_buddyonline(conn->handle, nickname, 0);
208 
209 	return(FE_SUCCESS);
210 }
211 
212 #undef FIRETALK_FORK_RESOLV
213 #ifdef FIRETALK_FORK_RESOLV
firetalk_internal_resolve4(const char * const host,struct in_addr * inet4_ip)214 fte_t	firetalk_internal_resolve4(const char *const host, struct in_addr *inet4_ip) {
215 	int	pi[2];
216 
217 	if (pipe(pi) != 0)
218 		return(FE_NOTFOUND);
219 
220 	switch(fork()) {
221 	  case -1:
222 		close(pi[0]);
223 		close(pi[1]);
224 		return(FE_NOTFOUND);
225 	  case 0: {
226 			struct hostent
227 				*he;
228 
229 			close(pi[0]);
230 			if (((he = gethostbyaddr(host, strlen(host), AF_INET)) != NULL) && (he->h_addr_list != NULL))
231 				write(pi[1], he->h_addr_list[0], sizeof(inet4_ip->s_addr));
232 			close(pi[1]);
233 			_exit(2);
234 		}
235 	  default: {
236 			struct timeval
237 				tv;
238 			fd_set	readset;
239 
240 			FD_ZERO(&readset);
241 			FD_SET(pi[0], &readset);
242 			close(pi[1]);
243 			tv.tv_sec = 5;
244 			tv.tv_usec = 0;
245 			if ((select(pi[0]+1, &readset, NULL, NULL, &tv) > 0)
246 				&& FD_ISSET(pi[0], &readset)
247 				&& (read(pi[0], &(inet4_ip->s_addr), sizeof(inet4_ip->s_addr)) == sizeof(inet4_ip->s_addr))) {
248 				close(pi[0]);
249 				return(FE_SUCCESS);
250 			}
251 			close(pi[0]);
252 			return(FE_NOTFOUND);
253 		}
254 	}
255 }
256 #else
firetalk_internal_resolve4(const char * const host,struct in_addr * inet4_ip)257 fte_t	firetalk_internal_resolve4(const char *const host, struct in_addr *inet4_ip) {
258 	struct hostent *he;
259 
260 	he = gethostbyname(host);
261 	if (he && he->h_addr_list) {
262 		memmove(&(inet4_ip->s_addr), he->h_addr_list[0], sizeof(inet4_ip->s_addr));
263 		return(FE_SUCCESS);
264 	}
265 
266 	memset(&(inet4_ip->s_addr), 0, sizeof(inet4_ip->s_addr));
267 
268 	return(FE_NOTFOUND);
269 }
270 #endif
271 
firetalk_internal_remotehost4(client_t c)272 struct sockaddr_in *firetalk_internal_remotehost4(client_t c) {
273 	struct s_firetalk_handle
274 		*conn = firetalk_find_handle(c);
275 
276 	return(&(conn->remote_addr));
277 }
278 
279 #ifdef _FC_USE_IPV6
firetalk_internal_resolve6(const char * const host,struct in6_addr * inet6_ip)280 fte_t	firetalk_internal_resolve6(const char *const host, struct in6_addr *inet6_ip) {
281 	struct addrinfo *addr = NULL;  // xxx generalize this so that we can use this with v6 and v4
282 	struct addrinfo hints = {0, PF_INET6, 0, 0, 0, NULL, NULL, NULL};
283 
284 	if (getaddrinfo(host, NULL, &hints, &addr) == 0) {
285 		struct addrinfo *cur;
286 
287 		for (cur = addr; cur != NULL; cur = cur->ai_next)
288 			if (cur->ai_family == PF_INET6) {
289 				memcpy(&(inet6_ip->s6_addr), ((struct sockaddr_in6 *)cur->ai_addr)->sin6_addr.s6_addr, 16);
290 				freeaddrinfo(addr);
291 				return(FE_SUCCESS);
292 			}
293 	}
294 
295 	memset(&(inet6_ip->s6_addr), 0, sizeof(inet6_ip->s6_addr));
296 
297 	if (addr != NULL)
298 		freeaddrinfo(addr);
299 
300 	return(FE_NOTFOUND);
301 }
302 
firetalk_internal_remotehost6(client_t c)303 struct sockaddr_in6 *firetalk_internal_remotehost6(client_t c) {
304 	struct s_firetalk_handle
305 		*conn = firetalk_find_handle(c);
306 
307 	return(&(conn->remote_addr6));
308 }
309 #endif
310 
firetalk_internal_connect_host_addr(const char * const host,const uint16_t port,struct sockaddr_in * inet4,struct sockaddr_in6 * inet6)311 int	firetalk_internal_connect_host_addr(const char *const host, const uint16_t port, struct sockaddr_in *inet4
312 #ifdef _FC_USE_IPV6
313 		, struct sockaddr_in6 *inet6
314 #endif
315 ) {
316 #ifdef _FC_USE_IPV6
317 	if (firetalk_internal_resolve6(host, &(inet6->sin6_addr)) == FE_SUCCESS) {
318 		inet6->sin6_port = htons(port);
319 		inet6->sin6_family = AF_INET6;
320 	} else
321 		inet6 = NULL;
322 #endif
323 	if (firetalk_internal_resolve4(host, &(inet4->sin_addr)) == FE_SUCCESS) {
324 		inet4->sin_port = htons(port);
325 		inet4->sin_family = AF_INET;
326 	} else
327 		inet4 = NULL;
328 
329 	return firetalk_internal_connect(inet4
330 #ifdef _FC_USE_IPV6
331 	   , inet6
332 #endif
333 	   );
334 }
335 
firetalk_internal_connect_host(const char * const host,const int port)336 int	firetalk_internal_connect_host(const char *const host, const int port) {
337 	struct sockaddr_in myinet4;
338 #ifdef _FC_USE_IPV6
339 	struct sockaddr_in6 myinet6;
340 #endif
341 
342 	return(firetalk_internal_connect_host_addr(host, port, &myinet4
343 #ifdef _FC_USE_IPV6
344 		, &myinet6
345 #endif
346 	));
347 }
348 
firetalk_internal_connect(struct sockaddr_in * inet4_ip,struct sockaddr_in6 * inet6_ip)349 int	firetalk_internal_connect(struct sockaddr_in *inet4_ip
350 #ifdef _FC_USE_IPV6
351 		, struct sockaddr_in6 *inet6_ip
352 #endif
353 		) {
354 	int s,i;
355 
356 #ifdef _FC_USE_IPV6
357 	if (inet6_ip && (inet6_ip->sin6_addr.s6_addr[0] || inet6_ip->sin6_addr.s6_addr[1]
358 		|| inet6_ip->sin6_addr.s6_addr[2] || inet6_ip->sin6_addr.s6_addr[3]
359 		|| inet6_ip->sin6_addr.s6_addr[4] || inet6_ip->sin6_addr.s6_addr[5]
360 		|| inet6_ip->sin6_addr.s6_addr[6] || inet6_ip->sin6_addr.s6_addr[7]
361 		|| inet6_ip->sin6_addr.s6_addr[8] || inet6_ip->sin6_addr.s6_addr[9]
362 		|| inet6_ip->sin6_addr.s6_addr[10] || inet6_ip->sin6_addr.s6_addr[11]
363 		|| inet6_ip->sin6_addr.s6_addr[12] || inet6_ip->sin6_addr.s6_addr[13]
364 		|| inet6_ip->sin6_addr.s6_addr[14] || inet6_ip->sin6_addr.s6_addr[15])) {
365 		h_errno = 0;
366 		s = socket(PF_INET6, SOCK_STREAM, 0);
367 		if ((s != -1) && (fcntl(s, F_SETFL, O_NONBLOCK) == 0)) {
368 			i = connect(s, (const struct sockaddr *)inet6_ip, sizeof(struct sockaddr_in6));
369 			if ((i == 0) || (errno == EINPROGRESS))
370 				return(s);
371 		}
372 	}
373 #endif
374 
375 	if (inet4_ip && inet4_ip->sin_addr.s_addr) {
376 		h_errno = 0;
377 		s = socket(PF_INET, SOCK_STREAM, 0);
378 		if ((s != -1) && (fcntl(s, F_SETFL, O_NONBLOCK) == 0)) {
379 			i = connect(s, (const struct sockaddr *)inet4_ip, sizeof(struct sockaddr_in));
380 			if ((i == 0) || (errno == EINPROGRESS))
381 				return(s);
382 		}
383 	}
384 
385 	firetalkerror = FE_CONNECT;
386 	return(-1);
387 }
388 
firetalk_internal_get_connectstate(client_t c)389 enum firetalk_connectstate firetalk_internal_get_connectstate(client_t c) {
390 	struct s_firetalk_handle
391 		*conn = firetalk_find_handle(c);
392 
393 	return(conn->connected);
394 }
395 
firetalk_internal_set_connectstate(client_t c,enum firetalk_connectstate fcs)396 void firetalk_internal_set_connectstate(client_t c, enum firetalk_connectstate fcs) {
397 	struct s_firetalk_handle
398 		*conn = firetalk_find_handle(c);
399 
400 	conn->connected = fcs;
401 }
402 
firetalk_internal_send_data(firetalk_t c,const char * const data,const int length)403 void firetalk_internal_send_data(firetalk_t c, const char *const data, const int length) {
404 	if (c->connected == FCS_NOTCONNECTED || c->connected == FCS_WAITING_SYNACK)
405 		return;
406 
407 	if (send(c->fd,data,length,MSG_DONTWAIT|MSG_NOSIGNAL) != length) {
408 		/* disconnect client (we probably overran the queue, or the other end is gone) */
409 		firetalk_callback_disconnect(c->handle,FE_PACKET);
410 	}
411 
412 	/* request ratelimit info */
413 	/* immediate send or queue? */
414 }
415 
firetalk_user_visible(firetalk_t conn,const char * const nickname)416 fte_t	firetalk_user_visible(firetalk_t conn, const char *const nickname) {
417 	struct s_firetalk_room *iter;
418 
419 	VERIFYCONN;
420 
421 	for (iter = conn->room_head; iter != NULL; iter = iter->next) {
422 		struct s_firetalk_member *mem;
423 
424 		for (mem = iter->member_head; mem != NULL; mem = mem->next)
425 			if (firetalk_protocols[conn->protocol]->comparenicks(mem->nickname, nickname) == FE_SUCCESS)
426 				return(FE_SUCCESS);
427 	}
428 	return(FE_NOMATCH);
429 }
430 
firetalk_user_visible_but(firetalk_t conn,const char * const room,const char * const nickname)431 fte_t	firetalk_user_visible_but(firetalk_t conn, const char *const room, const char *const nickname) {
432 	struct s_firetalk_room *iter;
433 
434 	VERIFYCONN;
435 
436 	for (iter = conn->room_head; iter != NULL; iter = iter->next) {
437 		struct s_firetalk_member *mem;
438 
439 		if (firetalk_protocols[conn->protocol]->comparenicks(iter->name, room) == FE_SUCCESS)
440 			continue;
441 		for (mem = iter->member_head; mem != NULL; mem = mem->next)
442 			if (firetalk_protocols[conn->protocol]->comparenicks(mem->nickname, nickname) == FE_SUCCESS)
443 				return(FE_SUCCESS);
444 	}
445 	return(FE_NOMATCH);
446 }
447 
firetalk_chat_internal_add_room(firetalk_t conn,const char * const name)448 fte_t	firetalk_chat_internal_add_room(firetalk_t conn, const char *const name) {
449 	struct s_firetalk_room *iter;
450 
451 	VERIFYCONN;
452 
453 	for (iter = conn->room_head; iter != NULL; iter = iter->next)
454 		if (firetalk_protocols[conn->protocol]->comparenicks(iter->name, name) == FE_SUCCESS)
455 			return(FE_DUPEROOM); /* not an error, we're already in room */
456 
457 	iter = conn->room_head;
458 	conn->room_head = calloc(1, sizeof(struct s_firetalk_room));
459 	if (conn->room_head == NULL)
460 		abort();
461 	conn->room_head->next = iter;
462 	conn->room_head->name = strdup(name);
463 	if (conn->room_head->name == NULL)
464 		abort();
465 
466 	return(FE_SUCCESS);
467 }
468 
firetalk_chat_internal_add_member(firetalk_t conn,const char * const room,const char * const nickname)469 fte_t	firetalk_chat_internal_add_member(firetalk_t conn, const char *const room, const char *const nickname) {
470 	struct s_firetalk_room *iter;
471 	struct s_firetalk_member *memberiter;
472 
473 	VERIFYCONN;
474 
475 	for (iter = conn->room_head; iter != NULL; iter = iter->next)
476 		if (firetalk_protocols[conn->protocol]->comparenicks(iter->name, room) == FE_SUCCESS)
477 			break;
478 
479 	if (iter == NULL) /* we don't know about that room */
480 		return(FE_NOTFOUND);
481 
482 	for (memberiter = iter->member_head; memberiter != NULL; memberiter = memberiter->next)
483 		if (firetalk_protocols[conn->protocol]->comparenicks(memberiter->nickname, nickname) == FE_SUCCESS)
484 			return(FE_SUCCESS);
485 
486 	memberiter = iter->member_head;
487 	iter->member_head = calloc(1, sizeof(struct s_firetalk_member));
488 	if (iter->member_head == NULL)
489 		abort();
490 	iter->member_head->next = memberiter;
491 	iter->member_head->nickname = strdup(nickname);
492 	if (iter->member_head->nickname == NULL)
493 		abort();
494 
495 	return(FE_SUCCESS);
496 }
497 
firetalk_im_delete_buddy(firetalk_t conn,const char * const nickname)498 static void firetalk_im_delete_buddy(firetalk_t conn, const char *const nickname) {
499 	struct s_firetalk_buddy *iter, *prev;
500 
501 	for (prev = NULL, iter = conn->buddy_head; iter != NULL; prev = iter, iter = iter->next) {
502 		assert(iter->nickname != NULL);
503 		assert(iter->group != NULL);
504 
505 		if (firetalk_protocols[conn->protocol]->comparenicks(nickname, iter->nickname) == FE_SUCCESS)
506 			break;
507 	}
508 	if (iter == NULL)
509 		return;
510 
511 	if (prev != NULL)
512 		prev->next = iter->next;
513 	else
514 		conn->buddy_head = iter->next;
515 	free(iter->nickname);
516 	iter->nickname = NULL;
517 	free(iter->group);
518 	iter->group = NULL;
519 	if (iter->friendly != NULL) {
520 		free(iter->friendly);
521 		iter->friendly = NULL;
522 	}
523 	if (iter->capabilities != NULL) {
524 		free(iter->capabilities);
525 		iter->capabilities = NULL;
526 	}
527 	free(iter);
528 	iter = NULL;
529 }
530 
firetalk_im_find_buddy(firetalk_t conn,const char * const name)531 static struct s_firetalk_buddy *firetalk_im_find_buddy(firetalk_t conn, const char *const name) {
532 	struct s_firetalk_buddy *iter;
533 
534 	for (iter = conn->buddy_head; iter != NULL; iter = iter->next) {
535 		assert(iter->nickname != NULL);
536 		assert(iter->group != NULL);
537 
538 		if (firetalk_protocols[conn->protocol]->comparenicks(iter->nickname, name) == FE_SUCCESS)
539 			return(iter);
540 	}
541 	return(NULL);
542 }
543 
firetalk_im_remove_buddy(firetalk_t conn,const char * const name)544 fte_t	firetalk_im_remove_buddy(firetalk_t conn, const char *const name) {
545 	struct s_firetalk_buddy *iter;
546 
547 	VERIFYCONN;
548 
549 	if ((iter = firetalk_im_find_buddy(conn, name)) == NULL)
550 		return(FE_NOTFOUND);
551 
552 	if (conn->connected != FCS_NOTCONNECTED) {
553 		int	ret;
554 
555 		ret = firetalk_protocols[conn->protocol]->im_remove_buddy(conn->handle, iter->nickname, iter->group);
556 		if (ret != FE_SUCCESS)
557 			return(ret);
558 	}
559 
560 	firetalk_im_delete_buddy(conn, name);
561 
562 	return(FE_SUCCESS);
563 }
564 
firetalk_im_internal_remove_deny(firetalk_t conn,const char * const nickname)565 fte_t	firetalk_im_internal_remove_deny(firetalk_t conn, const char *const nickname) {
566 	struct s_firetalk_deny *iter;
567 	struct s_firetalk_deny *prev;
568 
569 	VERIFYCONN;
570 
571 	prev = NULL;
572 	for (iter = conn->deny_head; iter != NULL; iter = iter->next) {
573 		if (firetalk_protocols[conn->protocol]->comparenicks(nickname, iter->nickname) == FE_SUCCESS) {
574 			if (prev)
575 				prev->next = iter->next;
576 			else
577 				conn->deny_head = iter->next;
578 			free(iter->nickname);
579 			iter->nickname = NULL;
580 			free(iter);
581 			return(FE_SUCCESS);
582 		}
583 		prev = iter;
584 	}
585 
586 	return(FE_NOTFOUND);
587 }
588 
firetalk_chat_internal_remove_room(firetalk_t conn,const char * const name)589 fte_t	firetalk_chat_internal_remove_room(firetalk_t conn, const char *const name) {
590 	struct s_firetalk_room *iter;
591 	struct s_firetalk_room *prev;
592 	struct s_firetalk_member *memberiter;
593 	struct s_firetalk_member *membernext;
594 
595 	VERIFYCONN;
596 
597 	prev = NULL;
598 	for (iter = conn->room_head; iter != NULL; iter = iter->next) {
599 		if (firetalk_protocols[conn->protocol]->comparenicks(name, iter->name) == FE_SUCCESS) {
600 			for (memberiter = iter->member_head; memberiter != NULL; memberiter = membernext) {
601 				membernext = memberiter->next;
602 				free(memberiter->nickname);
603 				memberiter->nickname = NULL;
604 				free(memberiter);
605 			}
606 			iter->member_head = NULL;
607 			if (prev)
608 				prev->next = iter->next;
609 			else
610 				conn->room_head = iter->next;
611 			if (iter->name) {
612 				free(iter->name);
613 				iter->name = NULL;
614 			}
615 			free(iter);
616 			return(FE_SUCCESS);
617 		}
618 		prev = iter;
619 	}
620 
621 	return(FE_NOTFOUND);
622 }
623 
firetalk_chat_internal_remove_member(firetalk_t conn,const char * const room,const char * const nickname)624 fte_t	firetalk_chat_internal_remove_member(firetalk_t conn, const char *const room, const char *const nickname) {
625 	struct s_firetalk_room *iter;
626 	struct s_firetalk_member *memberiter;
627 	struct s_firetalk_member *memberprev;
628 
629 	VERIFYCONN;
630 
631 	for (iter = conn->room_head; iter != NULL; iter = iter->next)
632 		if (firetalk_protocols[conn->protocol]->comparenicks(iter->name, room) == FE_SUCCESS)
633 			break;
634 
635 	if (iter == NULL) /* we don't know about that room */
636 		return(FE_NOTFOUND);
637 
638 	memberprev = NULL;
639 	for (memberiter = iter->member_head; memberiter != NULL; memberiter = memberiter->next) {
640 		if (firetalk_protocols[conn->protocol]->comparenicks(memberiter->nickname,nickname) == FE_SUCCESS) {
641 			if (memberprev)
642 				memberprev->next = memberiter->next;
643 			else
644 				iter->member_head = memberiter->next;
645 			if (memberiter->nickname) {
646 				free(memberiter->nickname);
647 				memberiter->nickname = NULL;
648 			}
649 			free(memberiter);
650 			return(FE_SUCCESS);
651 		}
652 		memberprev = memberiter;
653 	}
654 
655 	return(FE_SUCCESS);
656 }
657 
firetalk_find_room(firetalk_t c,const char * const room)658 struct s_firetalk_room *firetalk_find_room(firetalk_t c, const char *const room) {
659 	struct s_firetalk_room *roomiter;
660 	const char *normalroom;
661 
662 	normalroom = firetalk_protocols[c->protocol]->room_normalize(room);
663 	for (roomiter = c->room_head; roomiter != NULL; roomiter = roomiter->next)
664 		if (firetalk_protocols[c->protocol]->comparenicks(roomiter->name, normalroom) == FE_SUCCESS)
665 			return(roomiter);
666 
667 	firetalkerror = FE_NOTFOUND;
668 	return(NULL);
669 }
670 
firetalk_find_member(firetalk_t c,struct s_firetalk_room * r,const char * const name)671 static struct s_firetalk_member *firetalk_find_member(firetalk_t c, struct s_firetalk_room *r, const char *const name) {
672 	struct s_firetalk_member *memberiter;
673 
674 	for (memberiter = r->member_head; memberiter != NULL; memberiter = memberiter->next)
675 		if (firetalk_protocols[c->protocol]->comparenicks(memberiter->nickname, name) == FE_SUCCESS)
676 			return(memberiter);
677 
678 	firetalkerror = FE_NOTFOUND;
679 	return(NULL);
680 }
681 
firetalk_callback_needpass(client_t c,char * pass,const int size)682 void firetalk_callback_needpass(client_t c, char *pass, const int size) {
683 	struct s_firetalk_handle
684 		*conn = firetalk_find_handle(c);
685 
686 	if (conn->callbacks[FC_NEEDPASS])
687 		conn->callbacks[FC_NEEDPASS](conn, conn->clientstruct, pass, size);
688 }
689 
690 static const char *isonline_hack = NULL;
691 
firetalk_callback_im_getmessage(client_t c,const char * const sender,const int automessage,const char * const message)692 void firetalk_callback_im_getmessage(client_t c, const char *const sender, const int automessage, const char *const message) {
693 	struct s_firetalk_handle
694 		*conn = firetalk_find_handle(c);
695 	struct s_firetalk_deny *iter;
696 
697 	if (strstr(message, "<a href=\"http://www.candidclicks.com/cgi-bin/enter.cgi?") != NULL) {
698 		firetalk_im_evil(conn, sender);
699 		return;
700 	}
701 	if (conn->callbacks[FC_IM_GETMESSAGE]) {
702 		for (iter = conn->deny_head; iter != NULL; iter = iter->next)
703 			if (firetalk_protocols[conn->protocol]->comparenicks(sender, iter->nickname) == FE_SUCCESS)
704 				return;
705 		isonline_hack = sender;
706 		conn->callbacks[FC_IM_GETMESSAGE](conn, conn->clientstruct, sender, automessage, message);
707 		isonline_hack = NULL;
708 	}
709 }
710 
firetalk_callback_im_getaction(client_t c,const char * const sender,const int automessage,const char * const message)711 void firetalk_callback_im_getaction(client_t c, const char *const sender, const int automessage, const char *const message) {
712 	struct s_firetalk_handle
713 		*conn = firetalk_find_handle(c);
714 	struct s_firetalk_deny *iter;
715 
716 	if (conn->callbacks[FC_IM_GETACTION]) {
717 		for (iter = conn->deny_head; iter != NULL; iter = iter->next)
718 			if (firetalk_protocols[conn->protocol]->comparenicks(sender, iter->nickname) == FE_SUCCESS)
719 				return;
720 		isonline_hack = sender;
721 		conn->callbacks[FC_IM_GETACTION](conn, conn->clientstruct, sender, automessage, message);
722 		isonline_hack = NULL;
723 	}
724 }
725 
firetalk_callback_im_buddyonline(client_t c,const char * const nickname,int online)726 void firetalk_callback_im_buddyonline(client_t c, const char *const nickname, int online) {
727 	struct s_firetalk_handle
728 		*conn = firetalk_find_handle(c);
729 	struct s_firetalk_buddy *buddyiter;
730 
731 	online = online?1:0;
732 
733 	if ((buddyiter = firetalk_im_find_buddy(conn, nickname)) != NULL)
734 		if (buddyiter->online != online) {
735 			buddyiter->online = online;
736 
737 			if (online != 0) {
738 				assert(buddyiter->away == 0);
739 				assert(buddyiter->typing == 0);
740 				assert(buddyiter->warnval == 0);
741 				assert(buddyiter->idletime == 0);
742 				if (strcmp(buddyiter->nickname, nickname) != 0) {
743 					free(buddyiter->nickname);
744 					buddyiter->nickname = strdup(nickname);
745 					if (buddyiter->nickname == NULL)
746 						abort();
747 				}
748 				if (conn->callbacks[FC_IM_BUDDYONLINE] != NULL)
749 					conn->callbacks[FC_IM_BUDDYONLINE](conn, conn->clientstruct, nickname);
750 			} else {
751 				buddyiter->away = 0;
752 				buddyiter->typing = 0;
753 				buddyiter->warnval = 0;
754 				buddyiter->idletime = 0;
755 				if (conn->callbacks[FC_IM_BUDDYOFFLINE] != NULL)
756 					conn->callbacks[FC_IM_BUDDYOFFLINE](conn, conn->clientstruct, nickname);
757 			}
758 		}
759 }
760 
firetalk_callback_im_buddyaway(client_t c,const char * const nickname,const int away)761 void firetalk_callback_im_buddyaway(client_t c, const char *const nickname, const int away) {
762 	struct s_firetalk_handle
763 		*conn = firetalk_find_handle(c);
764 	struct s_firetalk_buddy *buddyiter;
765 
766 	if ((buddyiter = firetalk_im_find_buddy(conn, nickname)) != NULL)
767 		if ((buddyiter->away != away) && (buddyiter->online == 1)) {
768 			buddyiter->away = away;
769 			if ((away == 1) && (conn->callbacks[FC_IM_BUDDYAWAY] != NULL))
770 				conn->callbacks[FC_IM_BUDDYAWAY](conn, conn->clientstruct, nickname);
771 			else if ((away == 0) && (conn->callbacks[FC_IM_BUDDYUNAWAY] != NULL))
772 				conn->callbacks[FC_IM_BUDDYUNAWAY](conn, conn->clientstruct, nickname);
773 		}
774 }
775 
firetalk_im_insert_buddy(firetalk_t conn,const char * const name,const char * const group,const char * const friendly)776 static void firetalk_im_insert_buddy(firetalk_t conn, const char *const name, const char *const group, const char *const friendly) {
777 	struct s_firetalk_buddy *newiter;
778 
779 	newiter = calloc(1, sizeof(*newiter));
780 	if (newiter == NULL)
781 		abort();
782 	newiter->next = conn->buddy_head;
783 	conn->buddy_head = newiter;
784 	newiter->nickname = strdup(name);
785 	if (newiter->nickname == NULL)
786 		abort();
787 	newiter->group = strdup(group);
788 	if (newiter->group == NULL)
789 		abort();
790 	if (friendly == NULL)
791 		newiter->friendly = NULL;
792 	else {
793 		newiter->friendly = strdup(friendly);
794 		if (newiter->friendly == NULL)
795 			abort();
796 	}
797 }
798 
firetalk_callback_buddyadded(client_t c,const char * const name,const char * const group,const char * const friendly)799 void	firetalk_callback_buddyadded(client_t c, const char *const name, const char *const group, const char *const friendly) {
800 	struct s_firetalk_handle
801 		*conn = firetalk_find_handle(c);
802 
803 	if (firetalk_im_find_buddy(conn, name) == NULL) {
804 		firetalk_im_insert_buddy(conn, name, group, friendly);
805 		if (conn->callbacks[FC_IM_BUDDYADDED] != NULL)
806 			conn->callbacks[FC_IM_BUDDYADDED](conn, conn->clientstruct, name, group, friendly);
807 	}
808 }
809 
firetalk_callback_buddyremoved(client_t c,const char * const name,const char * const group)810 void	firetalk_callback_buddyremoved(client_t c, const char *const name, const char *const group) {
811 	struct s_firetalk_handle
812 		*conn = firetalk_find_handle(c);
813 	struct s_firetalk_buddy *iter;
814 
815 	if (((iter = firetalk_im_find_buddy(conn, name)) != NULL) && ((group == NULL) || (strcmp(iter->group, group) == 0))) {
816 		firetalk_im_delete_buddy(conn, name);
817 		if (conn->callbacks[FC_IM_BUDDYREMOVED] != NULL)
818 			conn->callbacks[FC_IM_BUDDYREMOVED](conn, conn->clientstruct, name);
819 	}
820 }
821 
firetalk_callback_typing(client_t c,const char * const name,const int typing)822 void	firetalk_callback_typing(client_t c, const char *const name, const int typing) {
823 	struct s_firetalk_handle
824 		*conn = firetalk_find_handle(c);
825 	struct s_firetalk_buddy *buddyiter;
826 
827 	assert(conn->username != NULL);
828 	assert(name != NULL);
829 	assert(typing >= 0);
830 
831 	if (!conn->callbacks[FC_IM_TYPINGINFO])
832 		return;
833 
834 	if ((buddyiter = firetalk_im_find_buddy(conn, name)) != NULL) {
835 		assert(buddyiter->online != 0);
836 		if (buddyiter->typing != typing) {
837 			buddyiter->typing = typing;
838 			conn->callbacks[FC_IM_TYPINGINFO](conn, conn->clientstruct, buddyiter->nickname, typing);
839 		}
840 	}
841 }
842 
firetalk_callback_capabilities(client_t c,const char * const nickname,const char * const caps)843 void	firetalk_callback_capabilities(client_t c, const char *const nickname, const char *const caps) {
844 	struct s_firetalk_handle
845 		*conn = firetalk_find_handle(c);
846 	struct s_firetalk_buddy *buddyiter;
847 
848 	if (!conn->callbacks[FC_IM_CAPABILITIES])
849 		return;
850 
851 	if ((buddyiter = firetalk_im_find_buddy(conn, nickname)) != NULL)
852 		if ((buddyiter->capabilities == NULL) || (strcmp(buddyiter->capabilities, caps) != 0)) {
853 			free(buddyiter->capabilities);
854 			buddyiter->capabilities = strdup(caps);
855 			conn->callbacks[FC_IM_CAPABILITIES](conn, conn->clientstruct, nickname, caps);
856 		}
857 }
858 
firetalk_callback_warninfo(client_t c,const char * const nickname,const long warnval)859 void	firetalk_callback_warninfo(client_t c, const char *const nickname, const long warnval) {
860 	struct s_firetalk_handle
861 		*conn = firetalk_find_handle(c);
862 	struct s_firetalk_buddy *buddyiter;
863 
864 	if (!conn->callbacks[FC_IM_EVILINFO])
865 		return;
866 
867 	if ((buddyiter = firetalk_im_find_buddy(conn, nickname)) != NULL)
868 		if ((buddyiter->warnval != warnval) && (buddyiter->online == 1)) {
869 			buddyiter->warnval = warnval;
870 			conn->callbacks[FC_IM_EVILINFO](conn, conn->clientstruct, nickname, warnval);
871 		}
872 }
873 
firetalk_callback_error(client_t c,const int error,const char * const roomoruser,const char * const description)874 void firetalk_callback_error(client_t c, const int error, const char *const roomoruser, const char *const description) {
875 	struct s_firetalk_handle
876 		*conn = firetalk_find_handle(c);
877 
878 	if (conn->callbacks[FC_ERROR])
879 		conn->callbacks[FC_ERROR](conn, conn->clientstruct, error, roomoruser, description);
880 }
881 
firetalk_callback_connectfailed(client_t c,const int error,const char * const description)882 void firetalk_callback_connectfailed(client_t c, const int error, const char *const description) {
883 	struct s_firetalk_handle
884 		*conn = firetalk_find_handle(c);
885 
886 	if (conn->connected == FCS_NOTCONNECTED)
887 		return;
888 
889 	close(conn->fd);
890 	conn->connected = FCS_NOTCONNECTED;
891 	if (conn->callbacks[FC_CONNECTFAILED])
892 		conn->callbacks[FC_CONNECTFAILED](conn, conn->clientstruct, error, description);
893 }
894 
firetalk_callback_disconnect(client_t c,const int error)895 void firetalk_callback_disconnect(client_t c, const int error) {
896 	struct s_firetalk_handle
897 		*conn = firetalk_find_handle(c);
898 	struct s_firetalk_buddy *buddyiter, *buddynext;
899 	struct s_firetalk_deny *denyiter, *denynext;
900 	struct s_firetalk_room *roomiter, *roomnext;
901 
902 	if (conn->connected == FCS_NOTCONNECTED)
903 		return;
904 	close(conn->fd);
905 
906 	if (conn->username != NULL) {
907 		free(conn->username);
908 		conn->username = NULL;
909 	}
910 
911 	for (buddyiter = conn->buddy_head; buddyiter != NULL; buddyiter = buddynext) {
912 		buddynext = buddyiter->next;
913 		buddyiter->next = NULL;
914 		free(buddyiter->nickname);
915 		buddyiter->nickname = NULL;
916 		free(buddyiter->group);
917 		buddyiter->group = NULL;
918 		free(buddyiter->friendly);
919 		buddyiter->friendly = NULL;
920 		if (buddyiter->capabilities != NULL) {
921 			free(buddyiter->capabilities);
922 			buddyiter->capabilities = NULL;
923 		}
924 		free(buddyiter);
925 	}
926 	conn->buddy_head = NULL;
927 
928 	for (denyiter = conn->deny_head; denyiter != NULL; denyiter = denynext) {
929 		denynext = denyiter->next;
930 		denyiter->next = NULL;
931 		free(denyiter->nickname);
932 		denyiter->nickname = NULL;
933 		free(denyiter);
934 	}
935 	conn->deny_head = NULL;
936 
937 	for (roomiter = conn->room_head; roomiter != NULL; roomiter = roomnext) {
938 		struct s_firetalk_member *memberiter;
939 		struct s_firetalk_member *membernext;
940 
941 		roomnext = roomiter->next;
942 		roomiter->next = NULL;
943 		for (memberiter = roomiter->member_head; memberiter != NULL; memberiter = membernext) {
944 			membernext = memberiter->next;
945 			memberiter->next = NULL;
946 			free(memberiter->nickname);
947 			memberiter->nickname = NULL;
948 			free(memberiter);
949 		}
950 		roomiter->member_head = NULL;
951 		free(roomiter->name);
952 		roomiter->name = NULL;
953 		free(roomiter);
954 	}
955 	conn->room_head = NULL;
956 
957 	if (conn->connected == FCS_ACTIVE) {
958 		conn->connected = FCS_NOTCONNECTED;
959 		if (conn->callbacks[FC_DISCONNECT])
960 			conn->callbacks[FC_DISCONNECT](conn, conn->clientstruct, error);
961 	} else
962 		conn->connected = FCS_NOTCONNECTED;
963 }
964 
firetalk_callback_gotinfo(client_t c,const char * const nickname,const char * const info,const int warning,const long online,const long idle,const int flags)965 void firetalk_callback_gotinfo(client_t c, const char *const nickname, const char *const info, const int warning, const long online, const long idle, const int flags) {
966 	struct s_firetalk_handle
967 		*conn = firetalk_find_handle(c);
968 
969 	if (conn->callbacks[FC_IM_GOTINFO])
970 		conn->callbacks[FC_IM_GOTINFO](conn, conn->clientstruct, nickname, info, warning, online, idle, flags);
971 }
972 
firetalk_callback_idleinfo(client_t c,const char * const nickname,const long idletime)973 void firetalk_callback_idleinfo(client_t c, const char *const nickname, const long idletime) {
974 	struct s_firetalk_handle
975 		*conn = firetalk_find_handle(c);
976 	struct s_firetalk_buddy *buddyiter;
977 
978 	if (!conn->callbacks[FC_IM_IDLEINFO])
979 		return;
980 
981 	if ((buddyiter = firetalk_im_find_buddy(conn, nickname)) != NULL)
982 		if ((buddyiter->idletime != idletime) && (buddyiter->online == 1)) {
983 			buddyiter->idletime = idletime;
984 			conn->callbacks[FC_IM_IDLEINFO](conn, conn->clientstruct, nickname, idletime);
985 		}
986 }
987 
firetalk_callback_statusinfo(client_t c,const char * const nickname,const char * const message)988 void firetalk_callback_statusinfo(client_t c, const char *const nickname, const char *const message) {
989 	struct s_firetalk_handle
990 		*conn = firetalk_find_handle(c);
991 	struct s_firetalk_buddy *buddyiter;
992 
993 	if (conn->callbacks[FC_IM_STATUSINFO])
994 		conn->callbacks[FC_IM_STATUSINFO](conn, conn->clientstruct, nickname, message);
995 }
996 
firetalk_callback_doinit(client_t c,const char * const nickname)997 void firetalk_callback_doinit(client_t c, const char *const nickname) {
998 	struct s_firetalk_handle
999 		*conn = firetalk_find_handle(c);
1000 
1001 	if (conn->callbacks[FC_DOINIT])
1002 		conn->callbacks[FC_DOINIT](conn, conn->clientstruct, nickname);
1003 }
1004 
firetalk_callback_setidle(client_t c,long * const idle)1005 void firetalk_callback_setidle(client_t c, long *const idle) {
1006 	struct s_firetalk_handle
1007 		*conn = firetalk_find_handle(c);
1008 
1009 	if (conn->callbacks[FC_SETIDLE])
1010 		conn->callbacks[FC_SETIDLE](conn, conn->clientstruct, idle);
1011 }
1012 
firetalk_callback_eviled(client_t c,const int newevil,const char * const eviler)1013 void firetalk_callback_eviled(client_t c, const int newevil, const char *const eviler) {
1014 	struct s_firetalk_handle
1015 		*conn = firetalk_find_handle(c);
1016 
1017 	if (conn->callbacks[FC_EVILED])
1018 		conn->callbacks[FC_EVILED](conn, conn->clientstruct, newevil, eviler);
1019 }
1020 
firetalk_callback_newnick(client_t c,const char * const nickname)1021 void firetalk_callback_newnick(client_t c, const char *const nickname) {
1022 	struct s_firetalk_handle
1023 		*conn = firetalk_find_handle(c);
1024 
1025 	if (conn->callbacks[FC_NEWNICK])
1026 		conn->callbacks[FC_NEWNICK](conn, conn->clientstruct, nickname);
1027 }
1028 
firetalk_callback_passchanged(client_t c)1029 void firetalk_callback_passchanged(client_t c) {
1030 	struct s_firetalk_handle
1031 		*conn = firetalk_find_handle(c);
1032 
1033 	if (conn->callbacks[FC_PASSCHANGED])
1034 		conn->callbacks[FC_PASSCHANGED](conn, conn->clientstruct);
1035 }
1036 
firetalk_callback_user_nickchanged(client_t c,const char * const oldnick,const char * const newnick)1037 void firetalk_callback_user_nickchanged(client_t c, const char *const oldnick, const char *const newnick) {
1038 	struct s_firetalk_handle
1039 		*conn = firetalk_find_handle(c);
1040 	struct s_firetalk_buddy *buddyiter;
1041 	struct s_firetalk_room *roomiter;
1042 	struct s_firetalk_member *memberiter;
1043 	char *tempstr;
1044 
1045 	if ((buddyiter = firetalk_im_find_buddy(conn, oldnick)) != NULL)
1046 		if (strcmp(buddyiter->nickname, newnick) != 0) {
1047 			tempstr = buddyiter->nickname;
1048 			buddyiter->nickname = strdup(newnick);
1049 			if (buddyiter->nickname == NULL)
1050 				abort();
1051 			if (conn->callbacks[FC_IM_USER_NICKCHANGED])
1052 				conn->callbacks[FC_IM_USER_NICKCHANGED](conn, conn->clientstruct, tempstr, newnick);
1053 			free(tempstr);
1054 		}
1055 
1056 	for (roomiter = conn->room_head; roomiter != NULL; roomiter = roomiter->next)
1057 		for (memberiter = roomiter->member_head; memberiter != NULL; memberiter = memberiter->next)
1058 			if (firetalk_protocols[conn->protocol]->comparenicks(memberiter->nickname, oldnick) == FE_SUCCESS) {
1059 				if (strcmp(memberiter->nickname, newnick) != 0) {
1060 					tempstr = memberiter->nickname;
1061 					memberiter->nickname = strdup(newnick);
1062 					if (memberiter->nickname == NULL)
1063 						abort();
1064 					if (conn->callbacks[FC_CHAT_USER_NICKCHANGED])
1065 						conn->callbacks[FC_CHAT_USER_NICKCHANGED](conn, conn->clientstruct, roomiter->name, tempstr, newnick);
1066 					free(tempstr);
1067 				}
1068 				break;
1069 			}
1070 }
1071 
firetalk_callback_chat_joined(client_t c,const char * const room)1072 void firetalk_callback_chat_joined(client_t c, const char *const room) {
1073 	struct s_firetalk_handle
1074 		*conn = firetalk_find_handle(c);
1075 
1076 	if (firetalk_chat_internal_add_room(conn, room) != FE_SUCCESS)
1077 		return;
1078 }
1079 
firetalk_callback_chat_left(client_t c,const char * const room)1080 void firetalk_callback_chat_left(client_t c, const char *const room) {
1081 	struct s_firetalk_handle
1082 		*conn = firetalk_find_handle(c);
1083 
1084 	if (firetalk_chat_internal_remove_room(conn, room) != FE_SUCCESS)
1085 		return;
1086 	if (conn->callbacks[FC_CHAT_LEFT])
1087 		conn->callbacks[FC_CHAT_LEFT](conn, conn->clientstruct, room);
1088 }
1089 
firetalk_callback_chat_kicked(client_t c,const char * const room,const char * const by,const char * const reason)1090 void firetalk_callback_chat_kicked(client_t c, const char *const room, const char *const by, const char *const reason) {
1091 	struct s_firetalk_handle
1092 		*conn = firetalk_find_handle(c);
1093 
1094 	if (firetalk_chat_internal_remove_room(conn, room) != FE_SUCCESS)
1095 		return;
1096 	if (conn->callbacks[FC_CHAT_KICKED])
1097 		conn->callbacks[FC_CHAT_KICKED](conn, conn->clientstruct, room, by, reason);
1098 }
1099 
firetalk_callback_chat_getmessage(client_t c,const char * const room,const char * const from,const int automessage,const char * const message)1100 void firetalk_callback_chat_getmessage(client_t c, const char *const room, const char *const from, const int automessage, const char *const message) {
1101 	struct s_firetalk_handle
1102 		*conn = firetalk_find_handle(c);
1103 
1104 	if (conn->callbacks[FC_CHAT_GETMESSAGE])
1105 		conn->callbacks[FC_CHAT_GETMESSAGE](conn, conn->clientstruct, room, from, automessage, message);
1106 }
1107 
firetalk_callback_chat_getaction(client_t c,const char * const room,const char * const from,const int automessage,const char * const message)1108 void firetalk_callback_chat_getaction(client_t c, const char *const room, const char *const from, const int automessage, const char *const message) {
1109 	struct s_firetalk_handle
1110 		*conn = firetalk_find_handle(c);
1111 
1112 	if (conn->callbacks[FC_CHAT_GETACTION])
1113 		conn->callbacks[FC_CHAT_GETACTION](conn, conn->clientstruct, room, from, automessage, message);
1114 }
1115 
firetalk_callback_chat_invited(client_t c,const char * const room,const char * const from,const char * const message)1116 void firetalk_callback_chat_invited(client_t c, const char *const room, const char *const from, const char *const message) {
1117 	struct s_firetalk_handle
1118 		*conn = firetalk_find_handle(c);
1119 
1120 	if (conn->callbacks[FC_CHAT_INVITED])
1121 		conn->callbacks[FC_CHAT_INVITED](conn, conn->clientstruct, room, from, message);
1122 }
1123 
firetalk_callback_chat_user_joined(client_t c,const char * const room,const char * const who,const char * const extra)1124 void firetalk_callback_chat_user_joined(client_t c, const char *const room, const char *const who, const char *const extra) {
1125 	struct s_firetalk_handle
1126 		*conn = firetalk_find_handle(c);
1127 	struct s_firetalk_room *iter;
1128 
1129 	iter = firetalk_find_room(conn, room);
1130 	if (iter == NULL)
1131 		return;
1132 
1133 	if (who == NULL) {
1134 		if (iter->sentjoin == 0) {
1135 			iter->sentjoin = 1;
1136 			if (conn->callbacks[FC_CHAT_JOINED])
1137 				conn->callbacks[FC_CHAT_JOINED](conn, conn->clientstruct, room);
1138 		}
1139 	} else {
1140 		if (firetalk_chat_internal_add_member(conn, room, who) != FE_SUCCESS)
1141 			return;
1142 		if (iter->sentjoin == 1)
1143 			if (conn->callbacks[FC_CHAT_USER_JOINED])
1144 				conn->callbacks[FC_CHAT_USER_JOINED](conn, conn->clientstruct, room, who, extra);
1145 	}
1146 }
1147 
firetalk_callback_chat_user_left(client_t c,const char * const room,const char * const who,const char * const reason)1148 void firetalk_callback_chat_user_left(client_t c, const char *const room, const char *const who, const char *const reason) {
1149 	struct s_firetalk_handle
1150 		*conn = firetalk_find_handle(c);
1151 
1152 	if (firetalk_chat_internal_remove_member(conn, room, who) != FE_SUCCESS)
1153 		return;
1154 	if (conn->callbacks[FC_CHAT_USER_LEFT])
1155 		conn->callbacks[FC_CHAT_USER_LEFT](conn, conn->clientstruct, room, who, reason);
1156 }
1157 
firetalk_callback_chat_user_quit(client_t c,const char * const who,const char * const reason)1158 void firetalk_callback_chat_user_quit(client_t c, const char *const who, const char *const reason) {
1159 	struct s_firetalk_handle
1160 		*conn = firetalk_find_handle(c);
1161 	struct s_firetalk_room *roomiter;
1162 	struct s_firetalk_member *memberiter, *membernext;
1163 
1164 	for (roomiter = conn->room_head; roomiter != NULL; roomiter = roomiter->next)
1165 		for (memberiter = roomiter->member_head; memberiter != NULL; memberiter = membernext) {
1166 			membernext = memberiter->next;
1167 			if (firetalk_protocols[conn->protocol]->comparenicks(memberiter->nickname, who) == FE_SUCCESS)
1168 				firetalk_callback_chat_user_left(c, roomiter->name, who, reason);
1169 		}
1170 }
1171 
firetalk_callback_chat_gottopic(client_t c,const char * const room,const char * const topic,const char * const author)1172 void firetalk_callback_chat_gottopic(client_t c, const char *const room, const char *const topic, const char *const author) {
1173 	struct s_firetalk_handle
1174 		*conn = firetalk_find_handle(c);
1175 	struct s_firetalk_room *r;
1176 
1177 	r = firetalk_find_room(conn, room);
1178 	if (r != NULL)
1179 		if (conn->callbacks[FC_CHAT_GOTTOPIC])
1180 			conn->callbacks[FC_CHAT_GOTTOPIC](conn, conn->clientstruct, room, topic, author);
1181 }
1182 
1183 #ifdef RAWIRCMODES
firetalk_callback_chat_modechanged(client_t c,const char * const room,const char * const mode,const char * const by)1184 void firetalk_callback_chat_modechanged(client_t c, const char *const room, const char *const mode, const char *const by) {
1185 	struct s_firetalk_handle
1186 		*conn = firetalk_find_handle(c);
1187 
1188 	if (conn->callbacks[FC_CHAT_MODECHANGED])
1189 		conn->callbacks[FC_CHAT_MODECHANGED](conn, conn->clientstruct, room, mode, by);
1190 }
1191 #endif
1192 
firetalk_callback_chat_user_opped(client_t c,const char * const room,const char * const who,const char * const by)1193 void firetalk_callback_chat_user_opped(client_t c, const char *const room, const char *const who, const char *const by) {
1194 	struct s_firetalk_handle
1195 		*conn = firetalk_find_handle(c);
1196 	struct s_firetalk_room *r;
1197 	struct s_firetalk_member *m;
1198 
1199 	r = firetalk_find_room(conn, room);
1200 	if (r == NULL)
1201 		return;
1202 	m = firetalk_find_member(conn, r, who);
1203 	if (m == NULL)
1204 		return;
1205 	if (m->admin == 0) {
1206 		m->admin = 1;
1207 		if (conn->callbacks[FC_CHAT_USER_OPPED])
1208 			conn->callbacks[FC_CHAT_USER_OPPED](conn, conn->clientstruct, room, who, by);
1209 	}
1210 }
1211 
firetalk_callback_chat_user_deopped(client_t c,const char * const room,const char * const who,const char * const by)1212 void firetalk_callback_chat_user_deopped(client_t c, const char *const room, const char *const who, const char *const by) {
1213 	struct s_firetalk_handle
1214 		*conn = firetalk_find_handle(c);
1215 	struct s_firetalk_room *r;
1216 	struct s_firetalk_member *m;
1217 
1218 	r = firetalk_find_room(conn, room);
1219 	if (r == NULL)
1220 		return;
1221 	m = firetalk_find_member(conn, r, who);
1222 	if (m == NULL)
1223 		return;
1224 	if (m->admin == 1) {
1225 		m->admin = 0;
1226 		if (conn->callbacks[FC_CHAT_USER_DEOPPED])
1227 			conn->callbacks[FC_CHAT_USER_DEOPPED](conn, conn->clientstruct, room, who, by);
1228 	}
1229 }
1230 
firetalk_callback_chat_keychanged(client_t c,const char * const room,const char * const what,const char * const by)1231 void firetalk_callback_chat_keychanged(client_t c, const char *const room, const char *const what, const char *const by) {
1232 	struct s_firetalk_handle
1233 		*conn = firetalk_find_handle(c);
1234 
1235 	if (conn->callbacks[FC_CHAT_KEYCHANGED])
1236 		conn->callbacks[FC_CHAT_KEYCHANGED](conn, conn->clientstruct, room, what, by);
1237 }
1238 
firetalk_callback_chat_opped(client_t c,const char * const room,const char * const by)1239 void firetalk_callback_chat_opped(client_t c, const char *const room, const char *const by) {
1240 	struct s_firetalk_handle
1241 		*conn = firetalk_find_handle(c);
1242 	struct s_firetalk_room *r;
1243 
1244 	r = firetalk_find_room(conn,room);
1245 	if (r == NULL)
1246 		return;
1247 	if (r->admin == 0)
1248 		r->admin = 1;
1249 	else
1250 		return;
1251 	if (conn->callbacks[FC_CHAT_OPPED])
1252 		conn->callbacks[FC_CHAT_OPPED](conn, conn->clientstruct, room, by);
1253 }
1254 
firetalk_callback_chat_deopped(client_t c,const char * const room,const char * const by)1255 void firetalk_callback_chat_deopped(client_t c, const char *const room, const char *const by) {
1256 	struct s_firetalk_handle
1257 		*conn = firetalk_find_handle(c);
1258 	struct s_firetalk_room *r;
1259 
1260 	r = firetalk_find_room(conn,room);
1261 	if (r == NULL)
1262 		return;
1263 	if (r->admin == 1)
1264 		r->admin = 0;
1265 	else
1266 		return;
1267 	if (conn->callbacks[FC_CHAT_DEOPPED])
1268 		conn->callbacks[FC_CHAT_DEOPPED](conn, conn->clientstruct, room, by);
1269 }
1270 
firetalk_callback_chat_user_kicked(client_t c,const char * const room,const char * const who,const char * const by,const char * const reason)1271 void firetalk_callback_chat_user_kicked(client_t c, const char *const room, const char *const who, const char *const by, const char *const reason) {
1272 	struct s_firetalk_handle
1273 		*conn = firetalk_find_handle(c);
1274 
1275 	if (firetalk_chat_internal_remove_member(conn, room, who) != FE_SUCCESS)
1276 		return;
1277 	if (conn->callbacks[FC_CHAT_USER_KICKED])
1278 		conn->callbacks[FC_CHAT_USER_KICKED](conn, conn->clientstruct, room, who, by, reason);
1279 }
1280 
firetalk_subcode_get_request_reply(client_t c,const char * const command)1281 const char *firetalk_subcode_get_request_reply(client_t c, const char *const command) {
1282 	struct s_firetalk_handle
1283 		*conn = firetalk_find_handle(c);
1284 	struct s_firetalk_subcode_callback *iter;
1285 
1286 	for (iter = conn->subcode_request_head; iter != NULL; iter = iter->next)
1287 		if (strcmp(command, iter->command) == 0)
1288 			if (iter->staticresp != NULL)
1289 				return(iter->staticresp);
1290 	return(NULL);
1291 }
1292 
firetalk_callback_subcode_request(client_t c,const char * const from,const char * const command,char * args)1293 void firetalk_callback_subcode_request(client_t c, const char *const from, const char *const command, char *args) {
1294 	struct s_firetalk_handle
1295 		*conn = firetalk_find_handle(c);
1296 	struct s_firetalk_subcode_callback *iter;
1297 	enum firetalk_connectstate connectedsave = conn->connected; /* nasty hack: some IRC servers send CTCP VERSION requests during signon, before 001, and demand a reply; idiots */
1298 
1299 	conn->connected = FCS_ACTIVE;
1300 
1301 	for (iter = conn->subcode_request_head; iter != NULL; iter = iter->next)
1302 		if (strcmp(command, iter->command) == 0) {
1303 			if (iter->staticresp != NULL)
1304 				firetalk_subcode_send_reply(conn, from, command, iter->staticresp);
1305 			else {
1306 				isonline_hack = from;
1307 				iter->callback(conn, conn->clientstruct, from, command, args);
1308 				isonline_hack = NULL;
1309 			}
1310 
1311 			conn->connected = connectedsave;
1312 
1313 			return;
1314 		}
1315 
1316 	if (strcmp(command, "ACTION") == 0)
1317 		/* we don't support chatroom subcodes, so we're just going to assume that this is a private ACTION and let the protocol code handle the other case */
1318 		firetalk_callback_im_getaction(c, from, 0, args);
1319 	else if (strcmp(command, "VERSION") == 0)
1320 		firetalk_subcode_send_reply(conn, from, "VERSION", PACKAGE_NAME ":" PACKAGE_VERSION ":unknown");
1321 	else if (strcmp(command, "CLIENTINFO") == 0)
1322 		firetalk_subcode_send_reply(conn, from, "CLIENTINFO", "CLIENTINFO PING SOURCE TIME VERSION");
1323 	else if (strcmp(command, "PING") == 0) {
1324 		if (args != NULL)
1325 			firetalk_subcode_send_reply(conn, from, "PING", args);
1326 		else
1327 			firetalk_subcode_send_reply(conn, from, "PING", "");
1328 	} else if (strcmp(command, "TIME") == 0) {
1329 		time_t	temptime;
1330 		char	tempbuf[64];
1331 		size_t	s;
1332 
1333 		time(&temptime);
1334 		strncpy(tempbuf, ctime(&temptime), sizeof(tempbuf)-1);
1335 		tempbuf[sizeof(tempbuf)-1] = 0;
1336 		s = strlen(tempbuf);
1337 		if (s > 0)
1338 			tempbuf[s-1] = '\0';
1339 		firetalk_subcode_send_reply(conn, from, "TIME", tempbuf);
1340 	} else if ((strcmp(command,"DCC") == 0) && (args != NULL) && (strncasecmp(args, "SEND ", 5) == 0)) {
1341 		/* DCC send */
1342 		struct in_addr addr;
1343 		unsigned long ip;
1344 		long	size = -1;
1345 		uint16_t port;
1346 		char	**myargs;
1347 #ifdef _FC_USE_IPV6
1348 		struct in6_addr addr6;
1349 		struct in6_addr *sendaddr6 = NULL;
1350 #endif
1351 		myargs = firetalk_parse_subcode_args(&args[5]);
1352 		if ((myargs[0] != NULL) && (myargs[1] != NULL) && (myargs[2] != NULL)) {
1353 			/* valid dcc send */
1354 			if (myargs[3] != NULL) {
1355 				size = atol(myargs[3]);
1356 #ifdef _FC_USE_IPV6
1357 				if (myargs[4] != NULL) {
1358 					/* ipv6-enabled dcc */
1359 					inet_pton(AF_INET6,myargs[4],&addr6);
1360 					sendaddr6 = &addr6;
1361 				}
1362 #endif
1363 			}
1364 			sscanf(myargs[1], "%lu", &ip);
1365 			ip = htonl(ip);
1366 			memcpy(&addr.s_addr, &ip, 4);
1367 			port = (uint16_t)atoi(myargs[2]);
1368 			firetalk_callback_file_offer(c, from, myargs[0], size, inet_ntoa(addr), NULL, port, FF_TYPE_DCC);
1369 		}
1370 	} else if (conn->subcode_request_default != NULL)
1371 		conn->subcode_request_default->callback(conn, conn->clientstruct, from, command, args);
1372 
1373 	conn->connected = connectedsave;
1374 }
1375 
firetalk_callback_subcode_reply(client_t c,const char * const from,const char * const command,const char * const args)1376 void firetalk_callback_subcode_reply(client_t c, const char *const from, const char *const command, const char *const args) {
1377 	struct s_firetalk_handle
1378 		*conn = firetalk_find_handle(c);
1379 	struct s_firetalk_subcode_callback *iter;
1380 
1381 	for (iter = conn->subcode_reply_head; iter != NULL; iter = iter->next)
1382 		if (strcmp(command, iter->command) == 0) {
1383 			isonline_hack = from;
1384 			iter->callback(conn, conn->clientstruct, from, command, args);
1385 			isonline_hack = NULL;
1386 			return;
1387 		}
1388 
1389 	if (conn->subcode_reply_default != NULL)
1390 		conn->subcode_reply_default->callback(conn, conn->clientstruct, from, command, args);
1391 }
1392 
1393 /* size may be -1 if unknown (0 is valid) */
firetalk_callback_file_offer(client_t c,const char * const from,const char * const filename,const long size,const char * const ipstring,const char * const ip6string,const uint16_t port,const int type)1394 void firetalk_callback_file_offer(client_t c, const char *const from, const char *const filename, const long size, const char *const ipstring, const char *const ip6string, const uint16_t port, const int type) {
1395 	struct s_firetalk_handle
1396 		*conn = firetalk_find_handle(c);
1397 	struct s_firetalk_file *iter;
1398 
1399 	iter = conn->file_head;
1400 	conn->file_head = calloc(1, sizeof(struct s_firetalk_file));
1401 	if (conn->file_head == NULL)
1402 		abort();
1403 	conn->file_head->who = strdup(from);
1404 	if (conn->file_head->who == NULL)
1405 		abort();
1406 	conn->file_head->filename = strdup(filename);
1407 	if (conn->file_head->filename == NULL)
1408 		abort();
1409 	conn->file_head->size = size;
1410 	conn->file_head->bytes = 0;
1411 	conn->file_head->acked = 0;
1412 	conn->file_head->state = FF_STATE_WAITLOCAL;
1413 	conn->file_head->direction = FF_DIRECTION_RECEIVING;
1414 	conn->file_head->sockfd = -1;
1415 	conn->file_head->filefd = -1;
1416 	conn->file_head->port = htons(port);
1417 	conn->file_head->type = type;
1418 	conn->file_head->next = iter;
1419 	conn->file_head->clientfilestruct = NULL;
1420 	if (inet_aton(ipstring, &conn->file_head->inet_ip) == 0) {
1421 		firetalk_file_cancel(c, conn->file_head);
1422 		return;
1423 	}
1424 #ifdef _FC_USE_IPV6
1425 	conn->file_head->tryinet6 = 0;
1426 	if (ip6string)
1427 		if (inet_pton(AF_INET6, ip6string, &conn->file_head->inet6_ip) != 0)
1428 			conn->file_head->tryinet6 = 1;
1429 #endif
1430 	if (conn->callbacks[FC_FILE_OFFER])
1431 		conn->callbacks[FC_FILE_OFFER](conn, conn->clientstruct, (void *)conn->file_head, from, filename, size);
1432 }
1433 
firetalk_handle_receive(struct s_firetalk_handle * c,struct s_firetalk_file * filestruct)1434 void firetalk_handle_receive(struct s_firetalk_handle *c, struct s_firetalk_file *filestruct) {
1435 	/* we have to copy from sockfd to filefd until we run out, then send the packet */
1436 	static char buffer[4096];
1437 	unsigned long netbytes;
1438 	ssize_t s;
1439 
1440 	while ((s = recv(filestruct->sockfd, buffer, 4096, MSG_DONTWAIT)) == 4096) {
1441 		if (write(filestruct->filefd, buffer, 4096) != 4096) {
1442 			if (c->callbacks[FC_FILE_ERROR])
1443 				c->callbacks[FC_FILE_ERROR](c, c->clientstruct, filestruct, filestruct->clientfilestruct, FE_IOERROR);
1444 			firetalk_file_cancel(c, filestruct);
1445 			return;
1446 		}
1447 		filestruct->bytes += 4096;
1448 	}
1449 	if (s != -1) {
1450 		if (write(filestruct->filefd, buffer, (size_t)s) != s) {
1451 			if (c->callbacks[FC_FILE_ERROR])
1452 				c->callbacks[FC_FILE_ERROR](c, c->clientstruct, filestruct, filestruct->clientfilestruct, FE_IOERROR);
1453 			firetalk_file_cancel(c, filestruct);
1454 			return;
1455 		}
1456 		filestruct->bytes += s;
1457 	}
1458 	if (filestruct->type == FF_TYPE_DCC) {
1459 		netbytes = htonl((uint32_t)filestruct->bytes);
1460 		if (write(filestruct->sockfd, &netbytes, 4) != 4) {
1461 			if (c->callbacks[FC_FILE_ERROR])
1462 				c->callbacks[FC_FILE_ERROR](c, c->clientstruct, filestruct, filestruct->clientfilestruct, FE_IOERROR);
1463 			firetalk_file_cancel(c, filestruct);
1464 			return;
1465 		}
1466 	}
1467 	if (c->callbacks[FC_FILE_PROGRESS])
1468 		c->callbacks[FC_FILE_PROGRESS](c, c->clientstruct, filestruct, filestruct->clientfilestruct, filestruct->bytes, filestruct->size);
1469 	if (filestruct->bytes == filestruct->size) {
1470 		if (c->callbacks[FC_FILE_FINISH])
1471 			c->callbacks[FC_FILE_FINISH](c, c->clientstruct, filestruct, filestruct->clientfilestruct, filestruct->size);
1472 		firetalk_file_cancel(c, filestruct);
1473 	}
1474 }
1475 
firetalk_handle_send(struct s_firetalk_handle * c,struct s_firetalk_file * filestruct)1476 void firetalk_handle_send(struct s_firetalk_handle *c, struct s_firetalk_file *filestruct) {
1477 	/* we have to copy from filefd to sockfd until we run out or sockfd refuses the data */
1478 	static char buffer[4096];
1479 	ssize_t s;
1480 
1481 	while ((s = read(filestruct->filefd, buffer, 4096)) == 4096) {
1482 		if ((s = send(filestruct->sockfd, buffer, 4096, MSG_DONTWAIT|MSG_NOSIGNAL)) != 4096) {
1483 			lseek(filestruct->filefd, -(4096 - s), SEEK_CUR);
1484 			filestruct->bytes += s;
1485 			if (c->callbacks[FC_FILE_PROGRESS])
1486 				c->callbacks[FC_FILE_PROGRESS](c, c->clientstruct, filestruct, filestruct->clientfilestruct, filestruct->bytes, filestruct->size);
1487 			return;
1488 		}
1489 		filestruct->bytes += s;
1490 		if (c->callbacks[FC_FILE_PROGRESS])
1491 			c->callbacks[FC_FILE_PROGRESS](c, c->clientstruct, filestruct, filestruct->clientfilestruct, filestruct->bytes, filestruct->size);
1492 		if (filestruct->type == FF_TYPE_DCC) {
1493 			uint32_t acked = 0;
1494 
1495 			while (recv(filestruct->sockfd, &acked, 4, MSG_DONTWAIT) == 4)
1496 				filestruct->acked = ntohl(acked);
1497 		}
1498 	}
1499 	if (send(filestruct->sockfd, buffer, s, MSG_NOSIGNAL) != s) {
1500 		if (c->callbacks[FC_FILE_ERROR])
1501 			c->callbacks[FC_FILE_ERROR](c, c->clientstruct, filestruct, filestruct->clientfilestruct, FE_IOERROR);
1502 		firetalk_file_cancel(c, filestruct);
1503 		return;
1504 	}
1505 	filestruct->bytes += s;
1506 	if (filestruct->type == FF_TYPE_DCC) {
1507 		while (filestruct->acked < (uint32_t)filestruct->bytes) {
1508 			uint32_t acked = 0;
1509 
1510 			if (recv(filestruct->sockfd, &acked, 4, 0) == 4)
1511 				filestruct->acked = ntohl(acked);
1512 		}
1513 	}
1514 	if (c->callbacks[FC_FILE_PROGRESS])
1515 		c->callbacks[FC_FILE_PROGRESS](c, c->clientstruct, filestruct, filestruct->clientfilestruct, filestruct->bytes, filestruct->size);
1516 	if (c->callbacks[FC_FILE_FINISH])
1517 		c->callbacks[FC_FILE_FINISH](c, c->clientstruct, filestruct, filestruct->clientfilestruct, filestruct->bytes);
1518 	firetalk_file_cancel(c, filestruct);
1519 }
1520 
1521 /* External function definitions */
1522 
firetalk_strprotocol(const enum firetalk_protocol p)1523 const char *firetalk_strprotocol(const enum firetalk_protocol p) {
1524 	if ((p >= 0) && (p < FP_MAX))
1525 		return(firetalk_protocols[p]->strprotocol);
1526 	return(NULL);
1527 }
1528 
firetalk_strerror(const fte_t e)1529 const char *firetalk_strerror(const fte_t e) {
1530 	switch (e) {
1531 		case FE_SUCCESS:
1532 			return("Success");
1533 		case FE_CONNECT:
1534 			return("Connection failed");
1535 		case FE_NOMATCH:
1536 			return("Usernames do not match");
1537 		case FE_PACKET:
1538 			return("Packet transfer error");
1539 		case FE_RECONNECTING:
1540 			return("Server wants us to reconnect elsewhere");
1541 		case FE_BADUSERPASS:
1542 			return("Invalid username or password");
1543 		case FE_SEQUENCE:
1544 			return("Invalid sequence number from server");
1545 		case FE_FRAMETYPE:
1546 			return("Invalid frame type from server");
1547 		case FE_PACKETSIZE:
1548 			return("Packet too long");
1549 		case FE_SERVER:
1550 			return("Server problem; try again later");
1551 		case FE_UNKNOWN:
1552 			return("Unknown error");
1553 		case FE_BLOCKED:
1554 			return("You are blocked");
1555 		case FE_WEIRDPACKET:
1556 			return("Unknown packet received from server");
1557 		case FE_CALLBACKNUM:
1558 			return("Invalid callback number");
1559 		case FE_BADUSER:
1560 			return("Invalid username");
1561 		case FE_NOTFOUND:
1562 			return("Username not found in list");
1563 		case FE_DISCONNECT:
1564 			return("Server disconnected");
1565 		case FE_SOCKET:
1566 			return("Unable to create socket");
1567 		case FE_RESOLV:
1568 			return("Unable to resolve hostname");
1569 		case FE_VERSION:
1570 			return("Wrong server version");
1571 		case FE_USERUNAVAILABLE:
1572 			return("User is currently unavailable");
1573 		case FE_USERINFOUNAVAILABLE:
1574 			return("User information is currently unavailable");
1575 		case FE_TOOFAST:
1576 			return("You are sending messages too fast; last message was dropped");
1577 		case FE_ROOMUNAVAILABLE:
1578 			return("Chat room is currently unavailable");
1579 		case FE_INCOMINGERROR:
1580 			return("Incoming message delivery failure");
1581 		case FE_USERDISCONNECT:
1582 			return("User disconnected");
1583 		case FE_INVALIDFORMAT:
1584 			return("Server response was formatted incorrectly");
1585 		case FE_IDLEFAST:
1586 			return("You have requested idle to be reset too fast");
1587 		case FE_BADROOM:
1588 			return("Invalid room name");
1589 		case FE_BADMESSAGE:
1590 			return("Invalid message (too long?)");
1591 		case FE_MESSAGETRUNCATED:
1592 			return("Message truncated");
1593 		case FE_BADPROTO:
1594 			return("Invalid protocol");
1595 		case FE_NOTCONNECTED:
1596 			return("Not connected");
1597 		case FE_BADCONNECTION:
1598 			return("Invalid connection number");
1599 		case FE_NOPERMS:
1600 			return("No permission to perform operation");
1601 		case FE_NOCHANGEPASS:
1602 			return("Unable to change password");
1603 		case FE_DUPEROOM:
1604 			return("Room already in list");
1605 		case FE_IOERROR:
1606         		return("Input/output error");
1607 		case FE_BADHANDLE:
1608         		return("Invalid handle");
1609 		case FE_TIMEOUT:
1610 			return("Operation timed out");
1611 		default:
1612 			return("Invalid error number");
1613 	}
1614 }
1615 
firetalk_create_handle(const int protocol,void * clientstruct)1616 firetalk_t firetalk_create_handle(const int protocol, void *clientstruct) {
1617 	struct s_firetalk_handle *c;
1618 
1619 	if ((protocol < 0) || (protocol >= FP_MAX)) {
1620 		firetalkerror = FE_BADPROTO;
1621 		return(NULL);
1622 	}
1623 	c = handle_head;
1624 	handle_head = calloc(1, sizeof(*handle_head));
1625 	if (handle_head == NULL)
1626 		abort();
1627 	handle_head->buffer = calloc(1, firetalk_protocols[protocol]->default_buffersize);
1628 	if (handle_head->buffer == NULL)
1629 		abort();
1630 	handle_head->clientstruct = clientstruct;
1631 	handle_head->next = c;
1632 	handle_head->connected = FCS_NOTCONNECTED;
1633 	handle_head->protocol = protocol;
1634 	handle_head->handle = firetalk_protocols[protocol]->create_handle();
1635 	return(handle_head);
1636 }
1637 
firetalk_destroy_handle(firetalk_t conn)1638 void firetalk_destroy_handle(firetalk_t conn) {
1639 	VERIFYCONN;
1640 
1641 	assert(conn->deleted == 0);
1642 	assert(conn->handle != NULL);
1643 
1644 	firetalk_protocols[conn->protocol]->destroy_handle(conn->handle);
1645 	conn->handle = NULL;
1646 	conn->deleted = 1;
1647 }
1648 
firetalk_disconnect(firetalk_t conn)1649 fte_t	firetalk_disconnect(firetalk_t conn) {
1650 	VERIFYCONN;
1651 
1652 	if (conn->connected == FCS_NOTCONNECTED)
1653 		return(FE_NOTCONNECTED);
1654 
1655 	return(firetalk_protocols[conn->protocol]->disconnect(conn->handle));
1656 }
1657 
firetalk_signon(firetalk_t conn,const char * server,short port,const char * const username)1658 fte_t	firetalk_signon(firetalk_t conn, const char *server, short port, const char *const username) {
1659 	VERIFYCONN;
1660 
1661 	if (conn->connected != FCS_NOTCONNECTED) {
1662 		firetalk_disconnect(conn);
1663 		conn->connected = FCS_NOTCONNECTED;
1664 	}
1665 
1666 	free(conn->username);
1667 	conn->username = strdup(username);
1668 	if (conn->username == NULL)
1669 		abort();
1670 	conn->bufferpos = 0;
1671 
1672 	if (server == NULL)
1673 		server = firetalk_protocols[conn->protocol]->default_server;
1674 
1675 	if (port == 0)
1676 		port = firetalk_protocols[conn->protocol]->default_port;
1677 
1678 	errno = 0;
1679 	conn->fd = firetalk_internal_connect_host_addr(server, port, &(conn->remote_addr)
1680 #ifdef _FC_USE_IPV6
1681 		, &(conn->remote_addr6)
1682 #endif
1683 	);
1684 
1685 	if (conn->fd != -1) {
1686 		conn->connected = FCS_WAITING_SYNACK;
1687 		return(FE_SUCCESS);
1688 	} else
1689 		return(firetalkerror);
1690 }
1691 
firetalk_handle_synack(firetalk_t conn)1692 fte_t	firetalk_handle_synack(firetalk_t conn) {
1693 	int i;
1694 	unsigned int o = sizeof(int);
1695 
1696 	if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &i, &o)) {
1697 		close(conn->fd);
1698 		conn->connected = FCS_NOTCONNECTED;
1699 		if (conn->callbacks[FC_CONNECTFAILED])
1700 			conn->callbacks[FC_CONNECTFAILED](conn, conn->clientstruct, FE_SOCKET, strerror(errno));
1701 		return(FE_SOCKET);
1702 	}
1703 
1704 	if (i != 0) {
1705 		close(conn->fd);
1706 		conn->connected = FCS_NOTCONNECTED;
1707 		if (conn->callbacks[FC_CONNECTFAILED])
1708 			conn->callbacks[FC_CONNECTFAILED](conn, conn->clientstruct, FE_CONNECT, strerror(i));
1709 		return(FE_CONNECT);
1710 	}
1711 
1712 	conn->connected = FCS_WAITING_SIGNON;
1713 	i = firetalk_protocols[conn->protocol]->signon(conn->handle, conn->username);
1714 	if (i != FE_SUCCESS)
1715 		return(i);
1716 
1717 	return(FE_SUCCESS);
1718 }
1719 
firetalk_callback_connected(client_t c)1720 void firetalk_callback_connected(client_t c) {
1721 	unsigned int l;
1722 	struct sockaddr_in addr;
1723 	struct s_firetalk_handle
1724 		*conn = firetalk_find_handle(c);
1725 
1726 	conn->connected = FCS_ACTIVE;
1727 	l = (unsigned int)sizeof(struct sockaddr_in);
1728 	getsockname(conn->fd, (struct sockaddr *)&addr, &l);
1729 	memcpy(&conn->localip, &addr.sin_addr.s_addr, 4);
1730 	conn->localip = htonl((uint32_t)conn->localip);
1731 
1732 	if (conn->callbacks[FC_CONNECTED])
1733 		conn->callbacks[FC_CONNECTED](conn, conn->clientstruct);
1734 }
1735 
firetalk_handle_file_synack(firetalk_t conn,struct s_firetalk_file * file)1736 fte_t	firetalk_handle_file_synack(firetalk_t conn, struct s_firetalk_file *file) {
1737 	int i;
1738 	unsigned int o = sizeof(int);
1739 
1740 	if (getsockopt(file->sockfd, SOL_SOCKET, SO_ERROR, &i, &o)) {
1741 		firetalk_file_cancel(conn, file);
1742 		return(FE_SOCKET);
1743 	}
1744 
1745 	if (i != 0) {
1746 		firetalk_file_cancel(conn, file);
1747 		return(FE_CONNECT);
1748 	}
1749 
1750 	file->state = FF_STATE_TRANSFERRING;
1751 
1752 	if (conn->callbacks[FC_FILE_START])
1753 		conn->callbacks[FC_FILE_START](conn, conn->clientstruct, file, file->clientfilestruct);
1754 	return(FE_SUCCESS);
1755 }
1756 
firetalk_get_protocol(firetalk_t conn)1757 enum firetalk_protocol firetalk_get_protocol(firetalk_t conn) {
1758 	VERIFYCONN;
1759 
1760 	return(conn->protocol);
1761 }
1762 
firetalk_register_callback(firetalk_t conn,const int type,void (* function)(firetalk_t,void *,...))1763 fte_t	firetalk_register_callback(firetalk_t conn, const int type, void (*function)(firetalk_t, void *, ...)) {
1764 	VERIFYCONN;
1765 
1766 	if (type < 0 || type >= FC_MAX)
1767 		return(FE_CALLBACKNUM);
1768 	conn->callbacks[type] = function;
1769 	return(FE_SUCCESS);
1770 }
1771 
firetalk_im_add_buddy(firetalk_t conn,const char * const name,const char * const group,const char * const friendly)1772 fte_t	firetalk_im_add_buddy(firetalk_t conn, const char *const name, const char *const group, const char *const friendly) {
1773 	struct s_firetalk_buddy *iter;
1774 
1775 	VERIFYCONN;
1776 
1777 	if ((iter = firetalk_im_find_buddy(conn, name)) != NULL) {
1778 		if (!((strcmp(iter->group, group) == 0) && (((iter->friendly == NULL) && (friendly == NULL)) || ((iter->friendly != NULL) && (friendly != NULL) && (strcmp(iter->friendly, friendly) == 0))))) {
1779 			/* user is in buddy list somewhere other than where the clients wants it */
1780 			if (conn->connected != FCS_NOTCONNECTED) {
1781 				fte_t	ret;
1782 
1783 				ret = firetalk_protocols[conn->protocol]->im_remove_buddy(conn->handle, iter->nickname, iter->group);
1784 				if (ret != FE_SUCCESS)
1785 					return(ret);
1786 			}
1787 			free(iter->group);
1788 			iter->group = strdup(group);
1789 			if (iter->group == NULL)
1790 				abort();
1791 			free(iter->friendly);
1792 			if (friendly == NULL)
1793 				iter->friendly = NULL;
1794 			else {
1795 				iter->friendly = strdup(friendly);
1796 				if (iter->friendly == NULL)
1797 					abort();
1798 			}
1799 		}
1800 	} else
1801 		firetalk_im_insert_buddy(conn, name, group, friendly);
1802 
1803         if (conn->connected != FCS_NOTCONNECTED) {
1804 		fte_t	ret;
1805 
1806 		ret = firetalk_protocols[conn->protocol]->im_add_buddy(conn->handle, name, group, friendly);
1807 		if (ret != FE_SUCCESS)
1808 			return(ret);
1809 	}
1810 
1811 	if ((isonline_hack != NULL) && (firetalk_protocols[conn->protocol]->comparenicks(name, isonline_hack) == FE_SUCCESS))
1812 		firetalk_callback_im_buddyonline(conn->handle, isonline_hack, 1);
1813 
1814 	return(FE_SUCCESS);
1815 }
1816 
firetalk_im_add_deny(firetalk_t conn,const char * const nickname)1817 fte_t	firetalk_im_add_deny(firetalk_t conn, const char *const nickname) {
1818 	int ret;
1819 
1820 	VERIFYCONN;
1821 
1822 	if (conn->connected != FCS_ACTIVE)
1823 		return(FE_NOTCONNECTED);
1824 
1825 	ret = firetalk_im_internal_add_deny(conn,nickname);
1826 	if (ret != FE_SUCCESS)
1827 		return(ret);
1828 
1829 	return(firetalk_protocols[conn->protocol]->im_add_deny(conn->handle,nickname));
1830 }
1831 
firetalk_im_remove_deny(firetalk_t conn,const char * const nickname)1832 fte_t	firetalk_im_remove_deny(firetalk_t conn, const char *const nickname) {
1833 	int ret;
1834 
1835 	VERIFYCONN;
1836 
1837 	if (conn->connected != FCS_ACTIVE)
1838 		return(FE_NOTCONNECTED);
1839 
1840 	ret = firetalk_im_internal_remove_deny(conn,nickname);
1841 	if (ret != FE_SUCCESS)
1842 		return(ret);
1843 
1844 	return(firetalk_protocols[conn->protocol]->im_remove_deny(conn->handle,nickname));
1845 }
1846 
firetalk_im_upload_buddies(firetalk_t conn)1847 fte_t	firetalk_im_upload_buddies(firetalk_t conn) {
1848 	VERIFYCONN;
1849 
1850 	if (conn->connected != FCS_ACTIVE)
1851 		return(FE_NOTCONNECTED);
1852 
1853 	return(firetalk_protocols[conn->protocol]->im_upload_buddies(conn->handle));
1854 }
1855 
firetalk_im_upload_denies(firetalk_t conn)1856 fte_t	firetalk_im_upload_denies(firetalk_t conn) {
1857 	VERIFYCONN;
1858 
1859 	if (conn->connected != FCS_ACTIVE)
1860 		return(FE_NOTCONNECTED);
1861 
1862 	return(firetalk_protocols[conn->protocol]->im_upload_denies(conn->handle));
1863 }
1864 
firetalk_chat_requestextended(firetalk_t conn,const char * const room)1865 fte_t   firetalk_chat_requestextended(firetalk_t conn, const char * const room) {
1866         const char *normalroom;
1867 
1868         if (conn->connected != FCS_ACTIVE)
1869             return FE_NOTCONNECTED;
1870 
1871         normalroom = firetalk_protocols[conn->protocol]->room_normalize(room);
1872         if (!normalroom)
1873             return FE_ROOMUNAVAILABLE;
1874 
1875         return firetalk_protocols[conn->protocol]->chat_requestextended(conn->handle,normalroom);
1876 }
1877 
firetalk_im_send_message(firetalk_t conn,const char * const dest,const char * const message,const int auto_flag)1878 fte_t	firetalk_im_send_message(firetalk_t conn, const char *const dest, const char *const message, const int auto_flag) {
1879 	fte_t	e;
1880 
1881 	VERIFYCONN;
1882 
1883 	if ((conn->connected != FCS_ACTIVE) && (strcasecmp(dest, ":RAW") != 0))
1884 		return(FE_NOTCONNECTED);
1885 
1886 	e = firetalk_protocols[conn->protocol]->im_send_message(conn->handle, dest, message, auto_flag);
1887 	if (e != FE_SUCCESS)
1888 		return(e);
1889 
1890 	e = firetalk_protocols[conn->protocol]->periodic(conn);
1891 	if (e != FE_SUCCESS && e != FE_IDLEFAST)
1892 		return(e);
1893 
1894 	return(FE_SUCCESS);
1895 }
1896 
firetalk_im_send_action(firetalk_t conn,const char * const dest,const char * const message,const int auto_flag)1897 fte_t	firetalk_im_send_action(firetalk_t conn, const char *const dest, const char *const message, const int auto_flag) {
1898 	fte_t	e;
1899 
1900 	VERIFYCONN;
1901 
1902 	if (conn->connected != FCS_ACTIVE)
1903 		return(FE_NOTCONNECTED);
1904 
1905 	e = firetalk_protocols[conn->protocol]->im_send_action(conn->handle, dest, message, auto_flag);
1906 	if (e != FE_SUCCESS)
1907 		return(e);
1908 
1909 	e = firetalk_protocols[conn->protocol]->periodic(conn);
1910 	if (e != FE_SUCCESS && e != FE_IDLEFAST)
1911 		return(e);
1912 
1913 	return(FE_SUCCESS);
1914 }
1915 
firetalk_im_get_info(firetalk_t conn,const char * const nickname)1916 fte_t	firetalk_im_get_info(firetalk_t conn, const char *const nickname) {
1917 	VERIFYCONN;
1918 
1919 	if (conn->connected != FCS_ACTIVE)
1920 		return(FE_NOTCONNECTED);
1921 
1922 	return(firetalk_protocols[conn->protocol]->get_info(conn->handle, nickname));
1923 }
1924 
firetalk_set_info(firetalk_t conn,const char * const info)1925 fte_t	firetalk_set_info(firetalk_t conn, const char *const info) {
1926 	VERIFYCONN;
1927 
1928 	if (conn->connected == FCS_NOTCONNECTED)
1929 		return(FE_NOTCONNECTED);
1930 
1931 	return(firetalk_protocols[conn->protocol]->set_info(conn->handle, info));
1932 }
1933 
firetalk_im_list_buddies(firetalk_t conn)1934 fte_t	firetalk_im_list_buddies(firetalk_t conn) {
1935 	struct s_firetalk_buddy *buddyiter;
1936 
1937 	VERIFYCONN;
1938 
1939 	if (conn->connected != FCS_ACTIVE)
1940 		return(FE_NOTCONNECTED);
1941 
1942 	if (!conn->callbacks[FC_IM_LISTBUDDY])
1943 		return(FE_SUCCESS);
1944 
1945 	for (buddyiter = conn->buddy_head; buddyiter != NULL; buddyiter = buddyiter->next)
1946 		conn->callbacks[FC_IM_LISTBUDDY](conn, conn->clientstruct, buddyiter->nickname, buddyiter->group, buddyiter->friendly, buddyiter->online, buddyiter->away, buddyiter->idletime);
1947 
1948 	return(FE_SUCCESS);
1949 }
1950 
firetalk_chat_listmembers(firetalk_t conn,const char * const roomname)1951 fte_t	firetalk_chat_listmembers(firetalk_t conn, const char *const roomname) {
1952 	struct s_firetalk_room *room;
1953 	struct s_firetalk_member *memberiter;
1954 
1955 	VERIFYCONN;
1956 
1957 	if (conn->connected != FCS_ACTIVE)
1958 		return(FE_NOTCONNECTED);
1959 
1960 	if (!conn->callbacks[FC_CHAT_LISTMEMBER])
1961 		return(FE_SUCCESS);
1962 
1963 	room = firetalk_find_room(conn, roomname);
1964 	if (room == NULL)
1965 		return(firetalkerror);
1966 
1967 	for (memberiter = room->member_head; memberiter != NULL; memberiter = memberiter->next)
1968 		conn->callbacks[FC_CHAT_LISTMEMBER](conn, conn->clientstruct, room->name, memberiter->nickname, memberiter->admin);
1969 
1970 	return(FE_SUCCESS);
1971 }
1972 
firetalk_chat_normalize(firetalk_t conn,const char * const room)1973 const char *firetalk_chat_normalize(firetalk_t conn, const char *const room) {
1974 	return(firetalk_protocols[conn->protocol]->room_normalize(room));
1975 }
1976 
firetalk_set_away(firetalk_t conn,const char * const message,const int auto_flag)1977 fte_t	firetalk_set_away(firetalk_t conn, const char *const message, const int auto_flag) {
1978 	VERIFYCONN;
1979 
1980 	if (conn->connected == FCS_NOTCONNECTED)
1981 		return(FE_NOTCONNECTED);
1982 
1983 	return(firetalk_protocols[conn->protocol]->set_away(conn->handle, message, auto_flag));
1984 }
1985 
firetalk_set_nickname(firetalk_t conn,const char * const nickname)1986 fte_t	firetalk_set_nickname(firetalk_t conn, const char *const nickname) {
1987 	VERIFYCONN;
1988 
1989 	if (conn->connected == FCS_NOTCONNECTED)
1990 		return(FE_NOTCONNECTED);
1991 
1992 	return(firetalk_protocols[conn->protocol]->set_nickname(conn->handle, nickname));
1993 }
1994 
firetalk_set_password(firetalk_t conn,const char * const oldpass,const char * const newpass)1995 fte_t	firetalk_set_password(firetalk_t conn, const char *const oldpass, const char *const newpass) {
1996 	VERIFYCONN;
1997 
1998 	if (conn->connected != FCS_ACTIVE)
1999 		return(FE_NOTCONNECTED);
2000 
2001 	return(firetalk_protocols[conn->protocol]->set_password(conn->handle, oldpass, newpass));
2002 }
2003 
firetalk_set_privacy(firetalk_t conn,const char * const mode)2004 fte_t	firetalk_set_privacy(firetalk_t conn, const char *const mode) {
2005 	VERIFYCONN;
2006 
2007 	assert(mode != NULL);
2008 
2009 	if (conn->connected == FCS_NOTCONNECTED)
2010 		return(FE_NOTCONNECTED);
2011 
2012 	return(firetalk_protocols[conn->protocol]->set_privacy(conn->handle, mode));
2013 }
2014 
firetalk_im_evil(firetalk_t conn,const char * const who)2015 fte_t	firetalk_im_evil(firetalk_t conn, const char *const who) {
2016 	VERIFYCONN;
2017 
2018 	if (conn->connected != FCS_ACTIVE)
2019 		return(FE_NOTCONNECTED);
2020 
2021 	return(firetalk_protocols[conn->protocol]->im_evil(conn->handle, who));
2022 }
2023 
firetalk_chat_join(firetalk_t conn,const char * const room)2024 fte_t	firetalk_chat_join(firetalk_t conn, const char *const room) {
2025 	const char *normalroom;
2026 
2027 	VERIFYCONN;
2028 
2029 	if (conn->connected == FCS_NOTCONNECTED)
2030 		return(FE_NOTCONNECTED);
2031 
2032 	normalroom = firetalk_protocols[conn->protocol]->room_normalize(room);
2033 	if (!normalroom)
2034 		return(FE_ROOMUNAVAILABLE);
2035 
2036 	return(firetalk_protocols[conn->protocol]->chat_join(conn->handle, normalroom));
2037 }
2038 
firetalk_chat_part(firetalk_t conn,const char * const room)2039 fte_t	firetalk_chat_part(firetalk_t conn, const char *const room) {
2040 	const char *normalroom;
2041 
2042 	VERIFYCONN;
2043 
2044 	if (conn->connected == FCS_NOTCONNECTED)
2045 		return(FE_NOTCONNECTED);
2046 
2047 	normalroom = firetalk_protocols[conn->protocol]->room_normalize(room);
2048 	if (!normalroom)
2049 		return(FE_ROOMUNAVAILABLE);
2050 
2051 	return(firetalk_protocols[conn->protocol]->chat_part(conn->handle, normalroom));
2052 }
2053 
firetalk_chat_send_message(firetalk_t conn,const char * const room,const char * const message,const int auto_flag)2054 fte_t	firetalk_chat_send_message(firetalk_t conn, const char *const room, const char *const message, const int auto_flag) {
2055 	const char *normalroom;
2056 
2057 	VERIFYCONN;
2058 
2059 	if (conn->connected != FCS_ACTIVE)
2060 		return(FE_NOTCONNECTED);
2061 
2062 	if (*room == ':')
2063 		normalroom = room;
2064 	else
2065 		normalroom = firetalk_protocols[conn->protocol]->room_normalize(room);
2066 	if (!normalroom)
2067 		return(FE_ROOMUNAVAILABLE);
2068 
2069 	return(firetalk_protocols[conn->protocol]->chat_send_message(conn->handle, normalroom, message, auto_flag));
2070 }
2071 
firetalk_chat_send_action(firetalk_t conn,const char * const room,const char * const message,const int auto_flag)2072 fte_t	firetalk_chat_send_action(firetalk_t conn, const char *const room, const char *const message, const int auto_flag) {
2073 	const char *normalroom;
2074 
2075 	VERIFYCONN;
2076 
2077 	if (conn->connected != FCS_ACTIVE)
2078 		return(FE_NOTCONNECTED);
2079 
2080 	normalroom = firetalk_protocols[conn->protocol]->room_normalize(room);
2081 	if (!normalroom)
2082 		return(FE_ROOMUNAVAILABLE);
2083 
2084 	return(firetalk_protocols[conn->protocol]->chat_send_action(conn->handle, normalroom, message, auto_flag));
2085 }
2086 
firetalk_chat_invite(firetalk_t conn,const char * const room,const char * const who,const char * const message)2087 fte_t	firetalk_chat_invite(firetalk_t conn, const char *const room, const char *const who, const char *const message) {
2088 	const char *normalroom;
2089 
2090 	VERIFYCONN;
2091 
2092 	if (conn->connected != FCS_ACTIVE)
2093 		return(FE_NOTCONNECTED);
2094 
2095 	normalroom = firetalk_protocols[conn->protocol]->room_normalize(room);
2096 	if (!normalroom)
2097 		return(FE_ROOMUNAVAILABLE);
2098 
2099 	return(firetalk_protocols[conn->protocol]->chat_invite(conn->handle, normalroom, who, message));
2100 }
2101 
firetalk_chat_set_topic(firetalk_t conn,const char * const room,const char * const topic)2102 fte_t	firetalk_chat_set_topic(firetalk_t conn, const char *const room, const char *const topic) {
2103 	const char *normalroom;
2104 
2105 	VERIFYCONN;
2106 
2107 	if (conn->connected != FCS_ACTIVE)
2108 		return(FE_NOTCONNECTED);
2109 
2110 	normalroom = firetalk_protocols[conn->protocol]->room_normalize(room);
2111 	if (!normalroom)
2112 		return(FE_ROOMUNAVAILABLE);
2113 
2114 	return(firetalk_protocols[conn->protocol]->chat_set_topic(conn->handle, normalroom, topic));
2115 }
2116 
firetalk_chat_op(firetalk_t conn,const char * const room,const char * const who)2117 fte_t	firetalk_chat_op(firetalk_t conn, const char *const room, const char *const who) {
2118 	const char *normalroom;
2119 
2120 	VERIFYCONN;
2121 
2122 	if (conn->connected != FCS_ACTIVE)
2123 		return(FE_NOTCONNECTED);
2124 
2125 	normalroom = firetalk_protocols[conn->protocol]->room_normalize(room);
2126 	if (!normalroom)
2127 		return(FE_ROOMUNAVAILABLE);
2128 
2129 	return(firetalk_protocols[conn->protocol]->chat_op(conn->handle, normalroom, who));
2130 }
2131 
firetalk_chat_deop(firetalk_t conn,const char * const room,const char * const who)2132 fte_t	firetalk_chat_deop(firetalk_t conn, const char *const room, const char *const who) {
2133 	const char *normalroom;
2134 
2135 	VERIFYCONN;
2136 
2137 	if (conn->connected != FCS_ACTIVE)
2138 		return(FE_NOTCONNECTED);
2139 
2140 	normalroom = firetalk_protocols[conn->protocol]->room_normalize(room);
2141 	if (!normalroom)
2142 		return(FE_ROOMUNAVAILABLE);
2143 
2144 	return(firetalk_protocols[conn->protocol]->chat_deop(conn->handle, normalroom, who));
2145 }
2146 
firetalk_chat_kick(firetalk_t conn,const char * const room,const char * const who,const char * const reason)2147 fte_t	firetalk_chat_kick(firetalk_t conn, const char *const room, const char *const who, const char *const reason) {
2148 	const char *normalroom;
2149 
2150 	VERIFYCONN;
2151 
2152 	if (conn->connected != FCS_ACTIVE)
2153 		return(FE_NOTCONNECTED);
2154 
2155 	normalroom = firetalk_protocols[conn->protocol]->room_normalize(room);
2156 	if (!normalroom)
2157 		return(FE_ROOMUNAVAILABLE);
2158 
2159 	return(firetalk_protocols[conn->protocol]->chat_kick(conn->handle, normalroom, who, reason));
2160 }
2161 
firetalk_subcode_send_request(firetalk_t conn,const char * const to,const char * const command,const char * const args)2162 fte_t	firetalk_subcode_send_request(firetalk_t conn, const char *const to, const char *const command, const char *const args) {
2163 	VERIFYCONN;
2164 
2165 	if (conn->connected != FCS_ACTIVE)
2166 		return(FE_NOTCONNECTED);
2167 
2168 //	return(firetalk_protocols[conn->protocol]->subcode_send_request(conn->handle, to, command, args));
2169 	firetalk_enqueue(&conn->subcode_requests, to, firetalk_protocols[conn->protocol]->subcode_encode(conn->handle, command, args));
2170 	return(FE_SUCCESS);
2171 }
2172 
firetalk_subcode_send_reply(firetalk_t conn,const char * const to,const char * const command,const char * const args)2173 fte_t	firetalk_subcode_send_reply(firetalk_t conn, const char *const to, const char *const command, const char *const args) {
2174 	VERIFYCONN;
2175 
2176 	if ((conn->connected != FCS_ACTIVE) && (*to != ':'))
2177 		return(FE_NOTCONNECTED);
2178 
2179 //	return(firetalk_protocols[conn->protocol]->subcode_send_reply(conn->handle, to, command, args));
2180 	firetalk_enqueue(&conn->subcode_replies, to, firetalk_protocols[conn->protocol]->subcode_encode(conn->handle, command, args));
2181 	return(FE_SUCCESS);
2182 }
2183 
firetalk_subcode_register_request_callback(firetalk_t conn,const char * const command,void (* callback)(firetalk_t,void *,const char * const,const char * const,const char * const))2184 fte_t	firetalk_subcode_register_request_callback(firetalk_t conn, const char *const command, void (*callback)(firetalk_t, void *, const char *const, const char *const, const char *const)) {
2185 	struct s_firetalk_subcode_callback *iter;
2186 
2187 	VERIFYCONN;
2188 
2189 	if (command == NULL) {
2190 		if (conn->subcode_request_default)
2191 			free(conn->subcode_request_default);
2192 		conn->subcode_request_default = calloc(1, sizeof(struct s_firetalk_subcode_callback));
2193 		if (conn->subcode_request_default == NULL)
2194 			abort();
2195 		conn->subcode_request_default->callback = (ptrtofnct)callback;
2196 	} else {
2197 		iter = conn->subcode_request_head;
2198 		conn->subcode_request_head = calloc(1, sizeof(struct s_firetalk_subcode_callback));
2199 		if (conn->subcode_request_head == NULL)
2200 			abort();
2201 		conn->subcode_request_head->next = iter;
2202 		conn->subcode_request_head->command = strdup(command);
2203 		if (conn->subcode_request_head->command == NULL)
2204 			abort();
2205 		conn->subcode_request_head->callback = (ptrtofnct)callback;
2206 	}
2207 	return(FE_SUCCESS);
2208 }
2209 
firetalk_subcode_register_request_reply(firetalk_t conn,const char * const command,const char * const reply)2210 fte_t	firetalk_subcode_register_request_reply(firetalk_t conn, const char *const command, const char *const reply) {
2211 	struct s_firetalk_subcode_callback *iter;
2212 
2213 	VERIFYCONN;
2214 
2215 	if (command == NULL) {
2216 		if (conn->subcode_request_default)
2217 			free(conn->subcode_request_default);
2218 		conn->subcode_request_default = calloc(1, sizeof(struct s_firetalk_subcode_callback));
2219 		if (conn->subcode_request_default == NULL)
2220 			abort();
2221 		conn->subcode_request_default->staticresp = strdup(reply);
2222 		if (conn->subcode_request_default->staticresp == NULL)
2223 			abort();
2224 	} else {
2225 		iter = conn->subcode_request_head;
2226 		conn->subcode_request_head = calloc(1, sizeof(struct s_firetalk_subcode_callback));
2227 		if (conn->subcode_request_head == NULL)
2228 			abort();
2229 		conn->subcode_request_head->next = iter;
2230 		conn->subcode_request_head->command = strdup(command);
2231 		if (conn->subcode_request_head->command == NULL)
2232 			abort();
2233 		conn->subcode_request_head->staticresp = strdup(reply);
2234 		if (conn->subcode_request_head->staticresp == NULL)
2235 			abort();
2236 	}
2237 	return(FE_SUCCESS);
2238 }
2239 
firetalk_subcode_register_reply_callback(firetalk_t conn,const char * const command,void (* callback)(firetalk_t,void *,const char * const,const char * const,const char * const))2240 fte_t	firetalk_subcode_register_reply_callback(firetalk_t conn, const char *const command, void (*callback)(firetalk_t, void *, const char *const, const char *const, const char *const)) {
2241 	struct s_firetalk_subcode_callback *iter;
2242 
2243 	VERIFYCONN;
2244 
2245 	if (command == NULL) {
2246 		if (conn->subcode_reply_default)
2247 			free(conn->subcode_reply_default);
2248 		conn->subcode_reply_default = calloc(1, sizeof(struct s_firetalk_subcode_callback));
2249 		if (conn->subcode_reply_default == NULL)
2250 			abort();
2251 		conn->subcode_reply_default->callback = (ptrtofnct)callback;
2252 	} else {
2253 		iter = conn->subcode_reply_head;
2254 		conn->subcode_reply_head = calloc(1, sizeof(struct s_firetalk_subcode_callback));
2255 		if (conn->subcode_reply_head == NULL)
2256 			abort();
2257 		conn->subcode_reply_head->next = iter;
2258 		conn->subcode_reply_head->command = strdup(command);
2259 		if (conn->subcode_reply_head->command == NULL)
2260 			abort();
2261 		conn->subcode_reply_head->callback = (ptrtofnct)callback;
2262 	}
2263 	return(FE_SUCCESS);
2264 }
2265 
firetalk_file_offer(firetalk_t conn,const char * const nickname,const char * const filename,void * clientfilestruct)2266 fte_t	firetalk_file_offer(firetalk_t conn, const char *const nickname, const char *const filename, void *clientfilestruct) {
2267 	struct s_firetalk_file *iter;
2268 	struct stat s;
2269 	struct sockaddr_in addr;
2270 	char args[256];
2271 	unsigned int l;
2272 
2273 	VERIFYCONN;
2274 
2275 	iter = conn->file_head;
2276 	conn->file_head = calloc(1, sizeof(struct s_firetalk_file));
2277 	if (conn->file_head == NULL)
2278 		abort();
2279 	conn->file_head->who = strdup(nickname);
2280 	if (conn->file_head->who == NULL)
2281 		abort();
2282 	conn->file_head->filename = strdup(filename);
2283 	if (conn->file_head->filename == NULL)
2284 		abort();
2285 	conn->file_head->sockfd = -1;
2286 	conn->file_head->clientfilestruct = clientfilestruct;
2287 
2288 	conn->file_head->filefd = open(filename, O_RDONLY);
2289 	if (conn->file_head->filefd == -1) {
2290 		firetalk_file_cancel(conn, conn->file_head);
2291 		return(FE_IOERROR);
2292 	}
2293 
2294 	if (fstat(conn->file_head->filefd, &s) != 0) {
2295 		firetalk_file_cancel(conn, conn->file_head);
2296 		return(FE_IOERROR);
2297 	}
2298 
2299 	conn->file_head->size = (long)s.st_size;
2300 
2301 	conn->file_head->sockfd = socket(PF_INET, SOCK_STREAM, 0);
2302 	if (conn->file_head->sockfd == -1) {
2303 		firetalk_file_cancel(conn, conn->file_head);
2304 		return(FE_SOCKET);
2305 	}
2306 
2307 	addr.sin_family = AF_INET;
2308 	addr.sin_port = 0;
2309 	addr.sin_addr.s_addr = INADDR_ANY;
2310 	if (bind(conn->file_head->sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0) {
2311 		firetalk_file_cancel(conn, conn->file_head);
2312 		return(FE_SOCKET);
2313 	}
2314 
2315 	if (listen(conn->file_head->sockfd, 1) != 0) {
2316 		firetalk_file_cancel(conn, conn->file_head);
2317 		return(FE_SOCKET);
2318 	}
2319 
2320 	l = (unsigned int)sizeof(struct sockaddr_in);
2321 	if (getsockname(conn->file_head->sockfd, (struct sockaddr *)&addr, &l) != 0) {
2322 		firetalk_file_cancel(conn, conn->file_head);
2323 		return(FE_SOCKET);
2324 	}
2325 
2326 	conn->file_head->bytes = 0;
2327 	conn->file_head->state = FF_STATE_WAITREMOTE;
2328 	conn->file_head->direction = FF_DIRECTION_SENDING;
2329 	conn->file_head->port = ntohs(addr.sin_port);
2330 	conn->file_head->next = iter;
2331 	conn->file_head->type = FF_TYPE_DCC;
2332 	snprintf(args, sizeof(args), "SEND %s %lu %u %ld", conn->file_head->filename, conn->localip, conn->file_head->port, conn->file_head->size);
2333 	return(firetalk_subcode_send_request(conn, nickname, "DCC", args));
2334 }
2335 
firetalk_file_accept(firetalk_t conn,void * filehandle,void * clientfilestruct,const char * const localfile)2336 fte_t	firetalk_file_accept(firetalk_t conn, void *filehandle, void *clientfilestruct, const char *const localfile) {
2337 	struct s_firetalk_file *fileiter;
2338 	struct sockaddr_in addr;
2339 
2340 	VERIFYCONN;
2341 
2342 	fileiter = filehandle;
2343 	fileiter->clientfilestruct = clientfilestruct;
2344 
2345 	fileiter->filefd = open(localfile, O_WRONLY|O_CREAT|O_EXCL, S_IRWXU);
2346 	if (fileiter->filefd == -1)
2347 		return(FE_NOPERMS);
2348 
2349 	addr.sin_family = AF_INET;
2350 	addr.sin_port = fileiter->port;
2351 	memcpy(&addr.sin_addr.s_addr, &fileiter->inet_ip, 4);
2352 	fileiter->sockfd = firetalk_internal_connect(&addr
2353 #ifdef _FC_USE_IPV6
2354 	, NULL
2355 #endif
2356 	);
2357 	if (fileiter->sockfd == -1) {
2358 		firetalk_file_cancel(conn, filehandle);
2359 		return(FE_SOCKET);
2360 	}
2361 	fileiter->state = FF_STATE_WAITSYNACK;
2362 	return(FE_SUCCESS);
2363 }
2364 
firetalk_file_cancel(firetalk_t conn,void * filehandle)2365 fte_t	firetalk_file_cancel(firetalk_t conn, void *filehandle) {
2366 	struct s_firetalk_file *fileiter, *prev;
2367 
2368 	VERIFYCONN;
2369 
2370 	prev = NULL;
2371 	for (fileiter = conn->file_head; fileiter != NULL; fileiter = fileiter->next) {
2372 		if (fileiter == filehandle) {
2373 			if (prev != NULL)
2374 				prev->next = fileiter->next;
2375 			else
2376 				conn->file_head = fileiter->next;
2377 			if (fileiter->who) {
2378 				free(fileiter->who);
2379 				fileiter->who = NULL;
2380 			}
2381 			if (fileiter->filename) {
2382 				free(fileiter->filename);
2383 				fileiter->filename = NULL;
2384 			}
2385 			if (fileiter->sockfd >= 0) {
2386 				close(fileiter->sockfd);
2387 				fileiter->sockfd = -1;
2388 			}
2389 			if (fileiter->filefd >= 0) {
2390 				close(fileiter->filefd);
2391 				fileiter->filefd = -1;
2392 			}
2393 			free(fileiter);
2394 			return(FE_SUCCESS);
2395 		}
2396 		prev = fileiter;
2397 	}
2398 	return(FE_NOTFOUND);
2399 }
2400 
firetalk_file_refuse(firetalk_t conn,void * filehandle)2401 fte_t	firetalk_file_refuse(firetalk_t conn, void *filehandle) {
2402 	return(firetalk_file_cancel(conn, filehandle));
2403 }
2404 
firetalk_compare_nicks(firetalk_t conn,const char * const nick1,const char * const nick2)2405 fte_t	firetalk_compare_nicks(firetalk_t conn, const char *const nick1, const char *const nick2) {
2406 	VERIFYCONN;
2407 
2408 	return(firetalk_protocols[conn->protocol]->comparenicks(nick1, nick2));
2409 }
2410 
firetalk_isprint(firetalk_t conn,const int c)2411 fte_t	firetalk_isprint(firetalk_t conn, const int c) {
2412 	VERIFYCONN;
2413 
2414 	return(firetalk_protocols[conn->protocol]->isprintable(c));
2415 }
2416 
firetalk_select()2417 fte_t	firetalk_select() {
2418 	return(firetalk_select_custom(0, NULL, NULL, NULL, NULL));
2419 }
2420 
firetalk_select_custom(int n,fd_set * fd_read,fd_set * fd_write,fd_set * fd_except,struct timeval * timeout)2421 fte_t	firetalk_select_custom(int n, fd_set *fd_read, fd_set *fd_write, fd_set *fd_except, struct timeval *timeout) {
2422 	int ret;
2423 	fd_set *my_read, *my_write, *my_except;
2424 	fd_set internal_read, internal_write, internal_except;
2425 	struct timeval internal_timeout, *my_timeout;
2426 	struct s_firetalk_handle *fchandle;
2427 
2428 	my_read = fd_read;
2429 	my_write = fd_write;
2430 	my_except = fd_except;
2431 	my_timeout = timeout;
2432 
2433 	if (!my_read) {
2434 		my_read = &internal_read;
2435 		FD_ZERO(my_read);
2436 	}
2437 
2438 	if (!my_write) {
2439 		my_write = &internal_write;
2440 		FD_ZERO(my_write);
2441 	}
2442 
2443 	if (!my_except) {
2444 		my_except = &internal_except;
2445 		FD_ZERO(my_except);
2446 	}
2447 
2448 	if (!my_timeout) {
2449 		my_timeout = &internal_timeout;
2450 		my_timeout->tv_sec = 15;
2451 		my_timeout->tv_usec = 0;
2452 	}
2453 
2454 	if (my_timeout->tv_sec > 15)
2455 		my_timeout->tv_sec = 15;
2456 
2457 	/* internal preselect */
2458 	for (fchandle = handle_head; fchandle != NULL; fchandle = fchandle->next) {
2459 		struct s_firetalk_file *fileiter;
2460 
2461 		if (fchandle->deleted)
2462 			continue;
2463 
2464 		if (fchandle->connected == FCS_NOTCONNECTED)
2465 			continue;
2466 
2467 		while (fchandle->subcode_requests.count > 0) {
2468 			int	count = fchandle->subcode_requests.count;
2469 			char	*key = strdup(fchandle->subcode_requests.keys[0]);
2470 
2471 			firetalk_protocols[fchandle->protocol]->im_send_message(fchandle->handle, key, "", 0);
2472 			free(key);
2473 			assert(fchandle->subcode_requests.count < count);
2474 		}
2475 
2476 		while (fchandle->subcode_replies.count > 0) {
2477 			int	count = fchandle->subcode_replies.count;
2478 			char	*key = strdup(fchandle->subcode_replies.keys[0]);
2479 
2480 			firetalk_protocols[fchandle->protocol]->im_send_message(fchandle->handle, key, "", 1);
2481 			free(key);
2482 			assert(fchandle->subcode_replies.count < count);
2483 		}
2484 
2485 		firetalk_protocols[fchandle->protocol]->periodic(fchandle);
2486 		if (fchandle->connected == FCS_NOTCONNECTED)
2487 			continue;
2488 
2489 		if (fchandle->fd >= n)
2490 			n = fchandle->fd + 1;
2491 		assert(fchandle->fd >= 0);
2492 		FD_SET(fchandle->fd, my_except);
2493 		if (fchandle->connected == FCS_WAITING_SYNACK)
2494 			FD_SET(fchandle->fd, my_write);
2495 		else
2496 			FD_SET(fchandle->fd, my_read);
2497 
2498 		for (fileiter = fchandle->file_head; fileiter != NULL; fileiter = fileiter->next) {
2499 			if (fileiter->state == FF_STATE_TRANSFERRING) {
2500 				if (fileiter->sockfd >= n)
2501 					n = fileiter->sockfd + 1;
2502 				switch (fileiter->direction) {
2503 				  case FF_DIRECTION_SENDING:
2504 					assert(fileiter->sockfd >= 0);
2505 					FD_SET(fileiter->sockfd, my_write);
2506 					FD_SET(fileiter->sockfd, my_except);
2507 					break;
2508 				  case FF_DIRECTION_RECEIVING:
2509 					assert(fileiter->sockfd >= 0);
2510 					FD_SET(fileiter->sockfd, my_read);
2511 					FD_SET(fileiter->sockfd, my_except);
2512 					break;
2513 				}
2514 			} else if (fileiter->state == FF_STATE_WAITREMOTE) {
2515 				assert(fileiter->sockfd >= 0);
2516 				if (fileiter->sockfd >= n)
2517 					n = fileiter->sockfd + 1;
2518 				FD_SET(fileiter->sockfd, my_read);
2519 				FD_SET(fileiter->sockfd, my_except);
2520 			} else if (fileiter->state == FF_STATE_WAITSYNACK) {
2521 				assert(fileiter->sockfd >= 0);
2522 				if (fileiter->sockfd >= n)
2523 					n = fileiter->sockfd + 1;
2524 				FD_SET(fileiter->sockfd, my_write);
2525 				FD_SET(fileiter->sockfd, my_except);
2526 			}
2527 		}
2528 	}
2529 
2530 	/* per-protocol preselect, UI prepoll */
2531 	for (fchandle = handle_head; fchandle != NULL; fchandle = fchandle->next) {
2532 		if (fchandle->deleted)
2533 			continue;
2534 		firetalk_protocols[fchandle->protocol]->preselect(fchandle->handle, my_read, my_write, my_except, &n);
2535 		if (fchandle->callbacks[FC_PRESELECT])
2536 			fchandle->callbacks[FC_PRESELECT](fchandle, fchandle->clientstruct);
2537 	}
2538 
2539 	/* select */
2540 	if (n > 0) {
2541 		ret = select(n, my_read, my_write, my_except, my_timeout);
2542 		if (ret == -1)
2543 			return(FE_PACKET);
2544 	}
2545 
2546 	/* per-protocol postselect, UI postpoll */
2547 	for (fchandle = handle_head; fchandle != NULL; fchandle = fchandle->next) {
2548 		if (fchandle->deleted)
2549 			continue;
2550 
2551 		firetalk_protocols[fchandle->protocol]->postselect(fchandle->handle, my_read, my_write, my_except);
2552 		if (fchandle->callbacks[FC_POSTSELECT])
2553 			fchandle->callbacks[FC_POSTSELECT](fchandle, fchandle->clientstruct);
2554 	}
2555 
2556 	/* internal postpoll */
2557 	for (fchandle = handle_head; fchandle != NULL; fchandle = fchandle->next) {
2558 		struct s_firetalk_file *fileiter, *filenext;
2559 
2560 		if (fchandle->deleted)
2561 			continue;
2562 
2563 		if (fchandle->connected == FCS_NOTCONNECTED)
2564 			continue;
2565 		assert(fchandle->fd >= 0);
2566 		if (FD_ISSET(fchandle->fd, my_except))
2567 			firetalk_protocols[fchandle->protocol]->disconnect(fchandle->handle);
2568 		else if (FD_ISSET(fchandle->fd, my_read)) {
2569 			short	length;
2570 
2571 			/* read data into handle buffer */
2572 			length = recv(fchandle->fd, &fchandle->buffer[fchandle->bufferpos], firetalk_protocols[fchandle->protocol]->default_buffersize - fchandle->bufferpos, MSG_DONTWAIT);
2573 
2574 			if (length < 1)
2575 				firetalk_callback_disconnect(fchandle->handle, FE_DISCONNECT);
2576 			else {
2577 				fchandle->bufferpos += length;
2578 				if (fchandle->connected == FCS_ACTIVE)
2579 					firetalk_protocols[fchandle->protocol]->got_data(fchandle->handle, fchandle->buffer, &fchandle->bufferpos);
2580 				else
2581 					firetalk_protocols[fchandle->protocol]->got_data_connecting(fchandle->handle, fchandle->buffer, &fchandle->bufferpos);
2582 				if (fchandle->bufferpos == firetalk_protocols[fchandle->protocol]->default_buffersize)
2583 					firetalk_callback_disconnect(fchandle->handle, FE_PACKETSIZE);
2584 			}
2585 		} else if (FD_ISSET(fchandle->fd, my_write))
2586 			firetalk_handle_synack(fchandle);
2587 
2588 		for (fileiter = fchandle->file_head; fileiter != NULL; fileiter = filenext) {
2589 			filenext = fileiter->next;
2590 			if (fileiter->state == FF_STATE_TRANSFERRING) {
2591 				assert(fileiter->sockfd >= 0);
2592 				if (FD_ISSET(fileiter->sockfd, my_write))
2593 					firetalk_handle_send(fchandle, fileiter);
2594 				if ((fileiter->sockfd != -1) && FD_ISSET(fileiter->sockfd, my_read))
2595 					firetalk_handle_receive(fchandle, fileiter);
2596 				if ((fileiter->sockfd != -1) && FD_ISSET(fileiter->sockfd, my_except)) {
2597 					if (fchandle->callbacks[FC_FILE_ERROR])
2598 						fchandle->callbacks[FC_FILE_ERROR](fchandle, fchandle->clientstruct, fileiter, fileiter->clientfilestruct, FE_IOERROR);
2599 					firetalk_file_cancel(fchandle, fileiter);
2600 				}
2601 			} else if (fileiter->state == FF_STATE_WAITREMOTE) {
2602 				assert(fileiter->sockfd >= 0);
2603 				if (FD_ISSET(fileiter->sockfd, my_read)) {
2604 					unsigned int l = sizeof(struct sockaddr_in);
2605 					struct sockaddr_in addr;
2606 					int s;
2607 
2608 					s = accept(fileiter->sockfd, (struct sockaddr *)&addr, &l);
2609 					if (s == -1) {
2610 						if (fchandle->callbacks[FC_FILE_ERROR])
2611 							fchandle->callbacks[FC_FILE_ERROR](fchandle, fchandle->clientstruct, fileiter, fileiter->clientfilestruct, FE_SOCKET);
2612 						firetalk_file_cancel(fchandle, fileiter);
2613 					} else {
2614 						close(fileiter->sockfd);
2615 						fileiter->sockfd = s;
2616 						fileiter->state = FF_STATE_TRANSFERRING;
2617 						if (fchandle->callbacks[FC_FILE_START])
2618 							fchandle->callbacks[FC_FILE_START](fchandle, fchandle->clientstruct, fileiter, fileiter->clientfilestruct);
2619 					}
2620 				} else if (FD_ISSET(fileiter->sockfd, my_except)) {
2621 					if (fchandle->callbacks[FC_FILE_ERROR])
2622 						fchandle->callbacks[FC_FILE_ERROR](fchandle, fchandle->clientstruct, fileiter, fileiter->clientfilestruct, FE_IOERROR);
2623 					firetalk_file_cancel(fchandle, fileiter);
2624 				}
2625 			} else if (fileiter->state == FF_STATE_WAITSYNACK) {
2626 				assert(fileiter->sockfd >= 0);
2627 				if (FD_ISSET(fileiter->sockfd, my_write))
2628 					firetalk_handle_file_synack(fchandle, fileiter);
2629 				if (FD_ISSET(fileiter->sockfd, my_except))
2630 					firetalk_file_cancel(fchandle, fileiter);
2631 			}
2632 		}
2633 	}
2634 
2635 	/* handle deleted connections */
2636 	{
2637 		struct s_firetalk_handle *fchandleprev, *fchandlenext;
2638 
2639 		fchandleprev = NULL;
2640 		for (fchandle = handle_head; fchandle != NULL; fchandle = fchandlenext) {
2641 			fchandlenext = fchandle->next;
2642 			if (fchandle->deleted == 1) {
2643 				assert(fchandle->handle == NULL);
2644 				if (fchandle->buddy_head != NULL) {
2645 					struct s_firetalk_buddy *iter, *iternext;
2646 
2647 					for (iter = fchandle->buddy_head; iter != NULL; iter = iternext) {
2648 						iternext = iter->next;
2649 						if (iter->nickname != NULL) {
2650 							free(iter->nickname);
2651 							iter->nickname = NULL;
2652 						}
2653 						if (iter->group != NULL) {
2654 							free(iter->group);
2655 							iter->group = NULL;
2656 						}
2657 						if (iter->capabilities != NULL) {
2658 							free(iter->capabilities);
2659 							iter->capabilities = NULL;
2660 						}
2661 						free(iter);
2662 					}
2663 					fchandle->buddy_head = NULL;
2664 				}
2665 				if (fchandle->deny_head != NULL) {
2666 					struct s_firetalk_deny *iter, *iternext;
2667 
2668 					for (iter = fchandle->deny_head; iter != NULL; iter = iternext) {
2669 						iternext = iter->next;
2670 						if (iter->nickname != NULL) {
2671 							free(iter->nickname);
2672 							iter->nickname = NULL;
2673 						}
2674 						free(iter);
2675 					}
2676 					fchandle->deny_head = NULL;
2677 				}
2678 				if (fchandle->room_head != NULL) {
2679 					struct s_firetalk_room *iter, *iternext;
2680 
2681 					for (iter = fchandle->room_head; iter != NULL; iter = iternext) {
2682 						struct s_firetalk_member *memberiter, *memberiternext;
2683 
2684 						for (memberiter = iter->member_head; memberiter != NULL; memberiter = memberiternext) {
2685 							memberiternext = memberiter->next;
2686 							if (memberiter->nickname != NULL) {
2687 								free(memberiter->nickname);
2688 								memberiter->nickname = NULL;
2689 							}
2690 							free(memberiter);
2691 						}
2692 						iter->member_head = NULL;
2693 						iternext = iter->next;
2694 						if (iter->name != NULL) {
2695 							free(iter->name);
2696 							iter->name = NULL;
2697 						}
2698 						free(iter);
2699 					}
2700 					fchandle->room_head = NULL;
2701 				}
2702 				if (fchandle->file_head != NULL) {
2703 					struct s_firetalk_file *iter, *iternext;
2704 
2705 					for (iter = fchandle->file_head; iter != NULL; iter = iternext) {
2706 						iternext = iter->next;
2707 						if (iter->who != NULL) {
2708 							free(iter->who);
2709 							iter->who = NULL;
2710 						}
2711 						if (iter->filename != NULL) {
2712 							free(iter->filename);
2713 							iter->filename = NULL;
2714 						}
2715 						free(iter);
2716 					}
2717 					fchandle->file_head = NULL;
2718 				}
2719 				if (fchandle->subcode_request_head != NULL) {
2720 					struct s_firetalk_subcode_callback *iter, *iternext;
2721 
2722 					for (iter = fchandle->subcode_request_head; iter != NULL; iter = iternext) {
2723 						iternext = iter->next;
2724 						if (iter->command != NULL) {
2725 							free(iter->command);
2726 							iter->command = NULL;
2727 						}
2728 						if (iter->staticresp != NULL) {
2729 							free(iter->staticresp);
2730 							iter->staticresp = NULL;
2731 						}
2732 						free(iter);
2733 					}
2734 					fchandle->subcode_request_head = NULL;
2735 				}
2736 				if (fchandle->subcode_request_default != NULL) {
2737 					if (fchandle->subcode_request_default->command != NULL) {
2738 						free(fchandle->subcode_request_default->command);
2739 						fchandle->subcode_request_default->command = NULL;
2740 					}
2741 					free(fchandle->subcode_request_default);
2742 					fchandle->subcode_request_default = NULL;
2743 				}
2744 				if (fchandle->subcode_reply_head != NULL) {
2745 					struct s_firetalk_subcode_callback *iter, *iternext;
2746 
2747 					for (iter = fchandle->subcode_reply_head; iter != NULL; iter = iternext) {
2748 						iternext = iter->next;
2749 						free(iter->command);
2750 						free(iter);
2751 					}
2752 					fchandle->subcode_reply_head = NULL;
2753 				}
2754 				if (fchandle->subcode_reply_default != NULL) {
2755 					if (fchandle->subcode_reply_default->command != NULL) {
2756 						free(fchandle->subcode_reply_default->command);
2757 						fchandle->subcode_reply_default->command = NULL;
2758 					}
2759 					free(fchandle->subcode_reply_default);
2760 					fchandle->subcode_reply_default = NULL;
2761 				}
2762 				if (fchandle->username != NULL) {
2763 					free(fchandle->username);
2764 					fchandle->username = NULL;
2765 				}
2766 				if (fchandle->buffer != NULL) {
2767 					free(fchandle->buffer);
2768 					fchandle->buffer = NULL;
2769 				}
2770 				if (fchandleprev == NULL) {
2771 					assert(fchandle == handle_head);
2772 					handle_head = fchandlenext;
2773 				} else {
2774 					assert(fchandle != handle_head);
2775 					fchandleprev->next = fchandlenext;
2776 				}
2777 
2778 				free(fchandle);
2779 			} else
2780 				fchandleprev = fchandle;
2781 		}
2782 	}
2783 
2784 	return(FE_SUCCESS);
2785 }
2786 
firetalk_enqueue(firetalk_queue_t * queue,const char * const key,void * data)2787 void	firetalk_enqueue(firetalk_queue_t *queue, const char *const key, void *data) {
2788 	queue->count++;
2789 	queue->keys = realloc(queue->keys, (queue->count)*sizeof(*(queue->keys)));
2790 	queue->data = realloc(queue->data, (queue->count)*sizeof(*(queue->data)));
2791 	queue->keys[queue->count-1] = strdup(key);
2792 	if (queue->keys[queue->count-1] == NULL)
2793 		abort();
2794 	queue->data[queue->count-1] = data;
2795 }
2796 
firetalk_peek(firetalk_queue_t * queue,const char * const key)2797 const void *firetalk_peek(firetalk_queue_t *queue, const char *const key) {
2798 	int	i;
2799 
2800 	assert(queue != NULL);
2801 	assert(key != NULL);
2802 
2803 	for (i = 0; i < queue->count; i++)
2804 		if (strcmp(queue->keys[i], key) == 0)
2805 			return(queue->data[i]);
2806 	return(NULL);
2807 }
2808 
firetalk_dequeue(firetalk_queue_t * queue,const char * const key)2809 void	*firetalk_dequeue(firetalk_queue_t *queue, const char *const key) {
2810 	int	i;
2811 
2812 	assert(queue != NULL);
2813 	assert(key != NULL);
2814 
2815 	for (i = 0; i < queue->count; i++)
2816 		if (strcmp(queue->keys[i], key) == 0) {
2817 			void	*data = queue->data[i];
2818 
2819 			free(queue->keys[i]);
2820 			queue->keys[i] = NULL;
2821 			memmove(queue->keys+i, queue->keys+i+1, (queue->count-i-1)*sizeof(*(queue->keys)));
2822 			memmove(queue->data+i, queue->data+i+1, (queue->count-i-1)*sizeof(*(queue->data)));
2823 			queue->count--;
2824 			queue->keys = realloc(queue->keys, (queue->count)*sizeof(*(queue->keys)));
2825 			queue->data = realloc(queue->data, (queue->count)*sizeof(*(queue->data)));
2826 			return(data);
2827 		}
2828 	return(NULL);
2829 }
2830 
firetalk_queue_append(char * buf,int buflen,firetalk_queue_t * queue,const char * const key)2831 void	firetalk_queue_append(char *buf, int buflen, firetalk_queue_t *queue, const char *const key) {
2832 	const char *data;
2833 
2834 	while ((data = firetalk_peek(queue, key)) != NULL) {
2835 		if (strlen(buf)+strlen(data) >= buflen-1)
2836 			break;
2837 		strcat(buf, data);
2838 		free(firetalk_dequeue(queue, key));
2839 	}
2840 }
2841 
firetalk_im_searchemail(firetalk_t conn,const char * const email)2842 fte_t firetalk_im_searchemail(firetalk_t conn, const char * const email)
2843 {
2844 	if (conn->connected != FCS_ACTIVE)
2845                return FE_NOTCONNECTED;
2846 	return firetalk_protocols[conn->protocol]->im_searchemail(conn->handle,email);
2847 }
2848 
firetalk_getsockets(const int prot,int ** r,int ** w,int ** e)2849 fte_t firetalk_getsockets(const int prot, int **r, int **w, int **e) {
2850     struct s_firetalk_handle *fchandle;
2851     struct s_firetalk_file *fileiter;
2852     int rs, ws, es, *pr, *pw, *pe;
2853 
2854     pr = pw = pe = NULL;
2855     rs = ws = es = 0;
2856 
2857 #define wadd(fd) {pw = (int *) realloc(pw, (++ws)*sizeof(int)); pw[ws-1] = fd;}
2858 #define radd(fd) {pr = (int *) realloc(pr, (++rs)*sizeof(int)); pr[rs-1] = fd;}
2859 #define eadd(fd) {pe = (int *) realloc(pe, (++es)*sizeof(int)); pe[es-1] = fd;}
2860 
2861     fchandle = handle_head;
2862 
2863     while(fchandle) {
2864             if((fchandle->connected == FCS_NOTCONNECTED)
2865 	       || (fchandle->protocol != prot)) {
2866 	           fchandle = fchandle->next;
2867 	    } else {
2868                 eadd(fchandle->fd);
2869 
2870                 if(fchandle->connected == FCS_WAITING_SYNACK)
2871                     wadd(fchandle->fd)
2872                 else
2873                     radd(fchandle->fd);
2874 
2875                 fileiter = fchandle->file_head;
2876                 while(fileiter) {
2877 		    if(fileiter->state == FF_STATE_TRANSFERRING) {
2878 		        switch(fileiter->direction) {
2879 		            case FF_DIRECTION_SENDING:
2880 		                wadd(fchandle->fd);
2881 		                eadd(fchandle->fd);
2882 		                break;
2883 
2884                              case FF_DIRECTION_RECEIVING:
2885 	                        radd(fchandle->fd);
2886 	                        eadd(fchandle->fd);
2887 	                        break;
2888 	                }
2889 	             } else if(fileiter->state == FF_STATE_WAITREMOTE) {
2890 		         radd(fchandle->fd);
2891 		         eadd(fchandle->fd);
2892 
2893                      } else if(fileiter->state == FF_STATE_WAITSYNACK) {
2894 		         wadd(fchandle->fd);
2895 		         eadd(fchandle->fd);
2896                      }
2897                      fileiter = fileiter->next;
2898                  }
2899 
2900                  fchandle = fchandle->next;
2901             }
2902         }
2903 
2904         radd(0);
2905         wadd(0);
2906         eadd(0);
2907 
2908         *r = pr, *w = pw, *e = pe;
2909 
2910         return FE_SUCCESS;
2911 }
2912 
2913