1 /*
2 irc-server.c : irssi
3
4 Copyright (C) 1999-2000 Timo Sirainen
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "module.h"
22
23 #include "net-sendbuffer.h"
24 #include "signals.h"
25 #include "rawlog.h"
26 #include "misc.h"
27
28 #include "channels.h"
29 #include "queries.h"
30
31 #include "irc-nicklist.h"
32 #include "irc-queries.h"
33 #include "irc-servers-setup.h"
34 #include "irc-servers.h"
35 #include "irc-cap.h"
36 #include "sasl.h"
37
38 #include "channels-setup.h"
39 #include "channel-rejoin.h"
40 #include "servers-idle.h"
41 #include "servers-reconnect.h"
42 #include "servers-redirect.h"
43 #include "modes.h"
44
45 #include "settings.h"
46 #include "recode.h"
47
48 #define DEFAULT_MAX_KICKS 1
49 #define DEFAULT_MAX_MODES 3
50 #define DEFAULT_MAX_WHOIS 4
51 #define DEFAULT_MAX_MSGS 1
52
53 #define DEFAULT_USER_MODE "+i"
54 #define DEFAULT_CMD_QUEUE_SPEED "2200msec"
55 #define DEFAULT_CMDS_MAX_AT_ONCE 5
56 #define DEFAULT_MAX_QUERY_CHANS 1 /* more and more IRC networks are using stupid ircds.. */
57
58 /*
59 * 63 is the maximum hostname length defined by the protocol. 10 is a common
60 * username limit on many networks. 1 is for the `@'.
61 */
62 #define MAX_USERHOST_LEN (63 + 10 + 1)
63
64 void irc_servers_reconnect_init(void);
65 void irc_servers_reconnect_deinit(void);
66
67 static int cmd_tag;
68
isnickflag_func(SERVER_REC * server,char flag)69 static int isnickflag_func(SERVER_REC *server, char flag)
70 {
71 IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
72
73 return isnickflag(irc_server, flag);
74 }
75
ischannel_func(SERVER_REC * server,const char * data)76 static int ischannel_func(SERVER_REC *server, const char *data)
77 {
78 IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
79 char *chantypes, *statusmsg;
80
81 g_return_val_if_fail(data != NULL, FALSE);
82
83 /* empty string is no channel */
84 if (*data == '\0')
85 return FALSE;
86
87 chantypes = g_hash_table_lookup(irc_server->isupport, "chantypes");
88 if (chantypes == NULL)
89 chantypes = "#&!+"; /* normal, local, secure, modeless */
90
91 statusmsg = g_hash_table_lookup(irc_server->isupport, "statusmsg");
92 if (statusmsg == NULL)
93 statusmsg = "@";
94
95 data += strspn(data, statusmsg);
96
97 /* strchr(3) considers the trailing NUL as part of the string, make sure
98 * we didn't advance too much. */
99 return *data != '\0' && strchr(chantypes, *data) != NULL;
100 }
101
split_line(const SERVER_REC * server,const char * line,const char * target,int len)102 static char **split_line(const SERVER_REC *server, const char *line,
103 const char *target, int len)
104 {
105 const char *start = settings_get_str("split_line_start");
106 const char *end = settings_get_str("split_line_end");
107 gboolean onspace = settings_get_bool("split_line_on_space");
108 char *recoded_start = recode_out(server, start, target);
109 char *recoded_end = recode_out(server, end, target);
110 char **lines;
111 int i;
112
113 /*
114 * Having the same length limit on all lines will make the first line
115 * shorter than necessary if `split_line_start' is set, but it makes
116 * the code much simpler. It's worth it.
117 */
118 len -= strlen(recoded_start) + strlen(recoded_end);
119 g_warn_if_fail(len > 0);
120 if (len <= 0) {
121 /* There is no room for anything. */
122 g_free(recoded_start);
123 g_free(recoded_end);
124 lines = g_new(char *, 1);
125 lines[0] = NULL;
126 return lines;
127 }
128
129 lines = recode_split(server, line, target, len, onspace);
130 for (i = 0; lines[i] != NULL; i++) {
131 if (i != 0 && *start != '\0') {
132 /* Not the first line. */
133 char *tmp = lines[i];
134 lines[i] = g_strconcat(start, tmp, NULL);
135 g_free(tmp);
136 }
137 if (lines[i + 1] != NULL && *end != '\0') {
138 /* Not the last line. */
139 char *tmp = lines[i];
140
141 if (lines[i + 2] == NULL) {
142 /* Next to last line. Check if we have room
143 * to append the last line to the current line,
144 * to avoid an unnecessary line break.
145 */
146 char *recoded_l = recode_out(server,
147 lines[i+1],
148 target);
149 if (strlen(recoded_l) <= strlen(recoded_end)) {
150 lines[i] = g_strconcat(tmp, lines[i+1],
151 NULL);
152 g_free_and_null(lines[i+1]);
153 lines = g_renew(char *, lines, i + 2);
154
155 g_free(recoded_l);
156 g_free(tmp);
157 break;
158 }
159 g_free(recoded_l);
160 }
161
162 lines[i] = g_strconcat(tmp, end, NULL);
163 g_free(tmp);
164 }
165 }
166
167 g_free(recoded_start);
168 g_free(recoded_end);
169 return lines;
170 }
171
send_message(SERVER_REC * server,const char * target,const char * msg,int target_type)172 static void send_message(SERVER_REC *server, const char *target,
173 const char *msg, int target_type)
174 {
175 IRC_SERVER_REC *ircserver;
176 CHANNEL_REC *channel;
177 char *str;
178 char *recoded;
179
180 ircserver = IRC_SERVER(server);
181 g_return_if_fail(ircserver != NULL);
182 g_return_if_fail(target != NULL);
183 g_return_if_fail(msg != NULL);
184
185 if (*target == '!') {
186 /* !chan -> !12345chan */
187 channel = channel_find(server, target);
188 if (channel != NULL &&
189 g_ascii_strcasecmp(channel->name, target) != 0)
190 target = channel->name;
191 }
192
193 recoded = recode_out(SERVER(server), msg, target);
194 str = g_strdup_printf("PRIVMSG %s :%s", target, recoded);
195 irc_send_cmd_split(ircserver, str, 2, ircserver->max_msgs_in_cmd);
196 g_free(str);
197 g_free(recoded);
198 }
199
split_message(SERVER_REC * server,const char * target,const char * msg)200 static char **split_message(SERVER_REC *server, const char *target,
201 const char *msg)
202 {
203 IRC_SERVER_REC *ircserver = IRC_SERVER(server);
204
205 g_return_val_if_fail(ircserver != NULL, NULL);
206 g_return_val_if_fail(target != NULL, NULL);
207 g_return_val_if_fail(msg != NULL, NULL);
208
209 /* length calculation shamelessly stolen from splitlong_safe.pl */
210 return split_line(SERVER(server), msg, target,
211 510 - strlen(":! PRIVMSG :") -
212 strlen(ircserver->nick) - MAX_USERHOST_LEN -
213 strlen(target));
214 }
215
server_init(IRC_SERVER_REC * server)216 static void server_init(IRC_SERVER_REC *server)
217 {
218 IRC_SERVER_CONNECT_REC *conn;
219 char *address, *ptr, *username, *cmd;
220
221 g_return_if_fail(server != NULL);
222
223 conn = server->connrec;
224
225 if (conn->proxy != NULL && conn->proxy_password != NULL &&
226 *conn->proxy_password != '\0') {
227 cmd = g_strdup_printf("PASS %s", conn->proxy_password);
228 irc_send_cmd_now(server, cmd);
229 g_free(cmd);
230 }
231
232 if (conn->proxy != NULL && conn->proxy_string != NULL) {
233 cmd = g_strdup_printf(conn->proxy_string, conn->address, conn->port);
234 irc_send_cmd_now(server, cmd);
235 g_free(cmd);
236 }
237
238 if (conn->sasl_mechanism != SASL_MECHANISM_NONE)
239 irc_cap_toggle(server, "sasl", TRUE);
240
241 irc_cap_toggle(server, "multi-prefix", TRUE);
242
243 irc_send_cmd_now(server, "CAP LS");
244
245 if (conn->password != NULL && *conn->password != '\0') {
246 /* send password */
247 cmd = g_strdup_printf("PASS %s", conn->password);
248 irc_send_cmd_now(server, cmd);
249 g_free(cmd);
250 }
251
252 /* send nick */
253 cmd = g_strdup_printf("NICK %s", conn->nick);
254 irc_send_cmd_now(server, cmd);
255 g_free(cmd);
256
257 /* send user/realname */
258 address = server->connrec->address;
259 ptr = strrchr(address, ':');
260 if (ptr != NULL) {
261 /* IPv6 address .. doesn't work here, use the string after
262 the last : char */
263 address = ptr+1;
264 if (*address == '\0')
265 address = "x";
266 }
267
268 username = g_strdup(conn->username);
269 ptr = strchr(username, ' ');
270 if (ptr != NULL) *ptr = '\0';
271
272 cmd = g_strdup_printf("USER %s %s %s :%s", username, username, address, conn->realname);
273 irc_send_cmd_now(server, cmd);
274 g_free(cmd);
275 g_free(username);
276
277 if (conn->proxy != NULL && conn->proxy_string_after != NULL) {
278 cmd = g_strdup_printf(conn->proxy_string_after, conn->address, conn->port);
279 irc_send_cmd_now(server, cmd);
280 g_free(cmd);
281 }
282
283 server->isupport = g_hash_table_new((GHashFunc) g_istr_hash,
284 (GCompareFunc) g_istr_equal);
285
286 /* set the standards */
287 g_hash_table_insert(server->isupport, g_strdup("CHANMODES"), g_strdup("beI,k,l,imnpst"));
288 g_hash_table_insert(server->isupport, g_strdup("PREFIX"), g_strdup("(ohv)@%+"));
289
290 server->cmdcount = 0;
291
292 /* prevent the queue from sending too early, we have a max cut off of 120 secs */
293 /* this will reset to 1 sec after we get the 001 event */
294 g_get_current_time(&server->wait_cmd);
295 g_time_val_add(&server->wait_cmd, 120 * G_USEC_PER_SEC);
296 }
297
irc_server_init_connect(SERVER_CONNECT_REC * conn)298 SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
299 {
300 IRC_SERVER_CONNECT_REC *ircconn;
301 IRC_SERVER_REC *server;
302
303 g_return_val_if_fail(IS_IRC_SERVER_CONNECT(conn), NULL);
304 if (conn->address == NULL || *conn->address == '\0') return NULL;
305 if (conn->nick == NULL || *conn->nick == '\0') return NULL;
306
307 server = g_new0(IRC_SERVER_REC, 1);
308 server->chat_type = IRC_PROTOCOL;
309
310 ircconn = (IRC_SERVER_CONNECT_REC *) conn;
311 server->connrec = ircconn;
312 server_connect_ref(conn);
313
314 if (server->connrec->port <= 0) {
315 server->connrec->port =
316 server->connrec->use_tls ? 6697 : 6667;
317 }
318
319 server->cmd_queue_speed = ircconn->cmd_queue_speed > 0 ?
320 ircconn->cmd_queue_speed : settings_get_time("cmd_queue_speed");
321 server->max_cmds_at_once = ircconn->max_cmds_at_once > 0 ?
322 ircconn->max_cmds_at_once : settings_get_int("cmds_max_at_once");
323 server->max_query_chans = ircconn->max_query_chans > 0 ?
324 ircconn->max_query_chans : DEFAULT_MAX_QUERY_CHANS;
325
326 server->max_kicks_in_cmd = ircconn->max_kicks > 0 ?
327 ircconn->max_kicks : DEFAULT_MAX_KICKS;
328 server->max_modes_in_cmd = ircconn->max_modes > 0 ?
329 ircconn->max_modes : DEFAULT_MAX_MODES;
330 server->max_whois_in_cmd = ircconn->max_whois > 0 ?
331 ircconn->max_whois : DEFAULT_MAX_WHOIS;
332 server->max_msgs_in_cmd = ircconn->max_msgs > 0 ?
333 ircconn->max_msgs : DEFAULT_MAX_MSGS;
334 server->connrec->use_tls = conn->use_tls;
335
336 modes_server_init(server);
337
338 server_connect_init((SERVER_REC *) server);
339 return (SERVER_REC *) server;
340 }
341
irc_server_connect(SERVER_REC * server)342 void irc_server_connect(SERVER_REC *server)
343 {
344 g_return_if_fail(server != NULL);
345
346 if (!server_start_connect(server)) {
347 server_connect_unref(server->connrec);
348 g_free(server);
349 }
350 }
351
352 /* Returns TRUE if `command' is sent to `target' */
command_has_target(const char * cmd,const char * target)353 static int command_has_target(const char *cmd, const char *target)
354 {
355 const char *p;
356 int len;
357
358 /* just assume the command is in form "<command> <target> <data>" */
359 p = strchr(cmd, ' ');
360 if (p == NULL) return FALSE;
361 p++;
362
363 len = strlen(target);
364 return strncmp(p, target, len) == 0 && p[len] == ' ';
365 }
366
367 /* Purge server output, either all or for specified target */
irc_server_purge_output(IRC_SERVER_REC * server,const char * target)368 void irc_server_purge_output(IRC_SERVER_REC *server, const char *target)
369 {
370 GSList *tmp, *next, *link;
371 REDIRECT_REC *redirect;
372 char *cmd;
373
374 if (target != NULL && *target == '\0')
375 target = NULL;
376
377 for (tmp = server->cmdqueue; tmp != NULL; tmp = next) {
378 next = tmp->next->next;
379 cmd = tmp->data;
380 redirect = tmp->next->data;
381
382 if ((target == NULL || command_has_target(cmd, target)) &&
383 g_ascii_strncasecmp(cmd, "PONG ", 5) != 0) {
384 /* remove the redirection */
385 link = tmp->next;
386 server->cmdqueue =
387 g_slist_remove_link(server->cmdqueue, link);
388 g_slist_free_1(link);
389
390 if (redirect != NULL)
391 server_redirect_destroy(redirect);
392
393 /* remove the command */
394 server->cmdqueue =
395 g_slist_remove(server->cmdqueue, cmd);
396 g_free(cmd);
397 server->cmdcount--;
398 }
399 }
400 }
401
sig_connected(IRC_SERVER_REC * server)402 static void sig_connected(IRC_SERVER_REC *server)
403 {
404 if (!IS_IRC_SERVER(server))
405 return;
406
407 server->isnickflag = isnickflag_func;
408 server->ischannel = ischannel_func;
409 server->split_message = split_message;
410 server->send_message = send_message;
411 server->query_find_func =
412 (QUERY_REC *(*)(SERVER_REC *, const char *)) irc_query_find;
413 server->nick_comp_func = irc_nickcmp_rfc1459;
414
415 server->splits = g_hash_table_new((GHashFunc) g_istr_hash,
416 (GCompareFunc) g_istr_equal);
417
418 if (!server->session_reconnect)
419 server_init(server);
420 }
421
isupport_destroy_hash(void * key,void * value)422 static void isupport_destroy_hash(void *key, void *value)
423 {
424 g_free(key);
425 g_free(value);
426 }
427
sig_destroyed(IRC_SERVER_REC * server)428 static void sig_destroyed(IRC_SERVER_REC *server)
429 {
430 GSList *tmp;
431
432 if (!IS_IRC_SERVER(server))
433 return;
434
435 for (tmp = server->cmdqueue; tmp != NULL; tmp = tmp->next->next) {
436 g_free(tmp->data);
437 if (tmp->next->data != NULL)
438 server_redirect_destroy(tmp->next->data);
439 }
440 g_slist_free(server->cmdqueue);
441 server->cmdqueue = NULL;
442
443 gslist_free_full(server->cap_active, (GDestroyNotify) g_free);
444 server->cap_active = NULL;
445
446 if (server->cap_supported) {
447 g_hash_table_destroy(server->cap_supported);
448 server->cap_supported = NULL;
449 }
450
451 gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
452 server->cap_queue = NULL;
453
454 /* was g_free_and_null, but can't use on a GString */
455 if (server->sasl_buffer != NULL) {
456 g_string_free(server->sasl_buffer, TRUE);
457 server->sasl_buffer = NULL;
458 }
459
460 /* these are dynamically allocated only if isupport was sent */
461 g_hash_table_foreach(server->isupport,
462 (GHFunc) isupport_destroy_hash, server);
463 g_hash_table_destroy(server->isupport);
464 server->isupport = NULL;
465
466 g_free_and_null(server->wanted_usermode);
467 g_free_and_null(server->real_address);
468 g_free_and_null(server->usermode);
469 g_free_and_null(server->userhost);
470 g_free_and_null(server->last_invite);
471 }
472
sig_server_quit(IRC_SERVER_REC * server,const char * msg)473 static void sig_server_quit(IRC_SERVER_REC *server, const char *msg)
474 {
475 char *str;
476 char *recoded;
477
478 if (!IS_IRC_SERVER(server) || !server->connected)
479 return;
480
481 recoded = recode_out(SERVER(server), msg, NULL);
482 str = g_strdup_printf("QUIT :%s", recoded);
483 irc_send_cmd_now(server, str);
484 g_free(str);
485 g_free(recoded);
486 }
487
irc_server_send_action(IRC_SERVER_REC * server,const char * target,const char * data)488 void irc_server_send_action(IRC_SERVER_REC *server, const char *target, const char *data)
489 {
490 char *recoded;
491
492 recoded = recode_out(SERVER(server), data, target);
493 irc_send_cmdv(server, "PRIVMSG %s :\001ACTION %s\001", target, recoded);
494 g_free(recoded);
495 }
496
irc_server_split_action(IRC_SERVER_REC * server,const char * target,const char * data)497 char **irc_server_split_action(IRC_SERVER_REC *server, const char *target,
498 const char *data)
499 {
500 g_return_val_if_fail(server != NULL, NULL);
501 g_return_val_if_fail(target != NULL, NULL);
502 g_return_val_if_fail(data != NULL, NULL);
503
504 return split_line(SERVER(server), data, target,
505 510 - strlen(":! PRIVMSG :\001ACTION \001") -
506 strlen(server->nick) - MAX_USERHOST_LEN -
507 strlen(target));
508 }
509
irc_server_send_away(IRC_SERVER_REC * server,const char * reason)510 void irc_server_send_away(IRC_SERVER_REC *server, const char *reason)
511 {
512 char *recoded = NULL;
513
514 if (!IS_IRC_SERVER(server))
515 return;
516
517 if (*reason != '\0' || server->usermode_away) {
518 g_free_and_null(server->away_reason);
519 if (*reason != '\0') {
520 server->away_reason = g_strdup(reason);
521 reason = recoded = recode_out(SERVER(server), reason, NULL);
522 irc_send_cmdv(server, "AWAY :%s", reason);
523 } else {
524 irc_send_cmdv(server, "AWAY");
525 }
526
527 }
528 g_free(recoded);
529 }
530
irc_server_send_data(IRC_SERVER_REC * server,const char * data,int len)531 void irc_server_send_data(IRC_SERVER_REC *server, const char *data, int len)
532 {
533 if (net_sendbuffer_send(server->handle, data, len) == -1) {
534 /* something bad happened */
535 server->connection_lost = TRUE;
536 return;
537 }
538
539 g_get_current_time(&server->last_cmd);
540
541 /* A bit kludgy way to do the flood protection. In ircnet, there
542 actually is 1sec / 100 bytes penalty, but we rather want to deal
543 with the max. 1000 bytes input buffer problem. If we send more
544 than that with the burst, we'll get excess flooded. */
545 if (len < 100 || server->cmd_queue_speed <= 10)
546 server->wait_cmd.tv_sec = 0;
547 else {
548 memcpy(&server->wait_cmd, &server->last_cmd, sizeof(GTimeVal));
549 g_time_val_add(&server->wait_cmd, (2 + len/100) * G_USEC_PER_SEC);
550 }
551 }
552
server_cmd_timeout(IRC_SERVER_REC * server,GTimeVal * now)553 static int server_cmd_timeout(IRC_SERVER_REC *server, GTimeVal *now)
554 {
555 REDIRECT_REC *redirect;
556 GSList *link;
557 long usecs;
558 char *cmd;
559 int len;
560
561 if (!IS_IRC_SERVER(server))
562 return 0;
563
564 if (server->cmdcount == 0 && server->cmdqueue == NULL)
565 return 0;
566
567 if (g_timeval_cmp(now, &server->wait_cmd) == -1)
568 return 1;
569
570 usecs = get_timeval_diff(now, &server->last_cmd);
571 if (usecs < server->cmd_queue_speed)
572 return 1;
573
574 server->cmdcount--;
575 if (server->cmdqueue == NULL) return 1;
576
577 /* get command */
578 cmd = server->cmdqueue->data;
579 redirect = server->cmdqueue->next->data;
580
581 /* send command */
582 len = strlen(cmd);
583 irc_server_send_data(server, cmd, len);
584
585 /* add to rawlog without [CR+]LF */
586 if (len > 2 && cmd[len-2] == '\r')
587 cmd[len-2] = '\0';
588 else if (cmd[len-1] == '\n')
589 cmd[len-1] = '\0';
590 rawlog_output(server->rawlog, cmd);
591 server_redirect_command(server, cmd, redirect);
592
593 /* remove from queue */
594 server->cmdqueue = g_slist_remove(server->cmdqueue, cmd);
595 g_free(cmd);
596
597 link = server->cmdqueue;
598 server->cmdqueue = g_slist_remove_link(server->cmdqueue, link);
599 g_slist_free_1(link);
600 return 1;
601 }
602
603 /* check every now and then if there's data to be sent in command buffer */
servers_cmd_timeout(void)604 static int servers_cmd_timeout(void)
605 {
606 GTimeVal now;
607 GSList *tmp;
608 int keep = 0;
609
610 g_get_current_time(&now);
611 for (tmp = servers; tmp != NULL; tmp = tmp->next) {
612 keep |= server_cmd_timeout(tmp->data, &now);
613 }
614 if (keep)
615 return 1;
616 else {
617 cmd_tag = -1;
618 return 0;
619 }
620 }
621
622 /* Start the timeout for sending data later and decreasing cmdcount again */
irc_servers_start_cmd_timeout(void)623 void irc_servers_start_cmd_timeout(void)
624 {
625 if (cmd_tag == -1)
626 cmd_tag = g_timeout_add(500, (GSourceFunc) servers_cmd_timeout, NULL);
627 }
628
629 /* Return a string of all channels (and keys, if any have them) in server,
630 like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g" */
irc_server_get_channels(IRC_SERVER_REC * server)631 char *irc_server_get_channels(IRC_SERVER_REC *server)
632 {
633 GSList *tmp;
634 GString *chans, *keys;
635 char *ret;
636 int use_keys;
637 int rejoin_channels_mode;
638
639 g_return_val_if_fail(server != NULL, FALSE);
640
641 rejoin_channels_mode = settings_get_choice("rejoin_channels_on_reconnect");
642
643 /* do we want to rejoin channels in the first place? */
644 if(rejoin_channels_mode == 0)
645 return g_strdup("");
646
647 chans = g_string_new(NULL);
648 keys = g_string_new(NULL);
649 use_keys = FALSE;
650
651 /* get currently joined channels */
652 for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
653 CHANNEL_REC *channel = tmp->data;
654 CHANNEL_SETUP_REC *setup = channel_setup_find(channel->name, channel->server->connrec->chatnet);
655 if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
656 g_string_append_printf(chans, "%s,", channel->name);
657 g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" : channel->key);
658 if (channel->key != NULL)
659 use_keys = TRUE;
660 }
661 }
662
663 /* get also the channels that are in rejoin list */
664 for (tmp = server->rejoin_channels; tmp != NULL; tmp = tmp->next) {
665 REJOIN_REC *rec = tmp->data;
666 CHANNEL_SETUP_REC *setup = channel_setup_find(rec->channel, server->tag);
667
668 if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
669 g_string_append_printf(chans, "%s,", rec->channel);
670 g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
671 rec->key);
672
673 if (rec->key != NULL) use_keys = TRUE;
674 }
675 }
676
677 if (chans->len > 0) {
678 g_string_truncate(chans, chans->len-1);
679 g_string_truncate(keys, keys->len-1);
680 if (use_keys) g_string_append_printf(chans, " %s", keys->str);
681 }
682
683 ret = chans->str;
684 g_string_free(chans, FALSE);
685 g_string_free(keys, TRUE);
686
687 return ret;
688 }
689
event_connected(IRC_SERVER_REC * server,const char * data,const char * from)690 static void event_connected(IRC_SERVER_REC *server, const char *data, const char *from)
691 {
692 char *params, *nick;
693
694 g_return_if_fail(server != NULL);
695
696 params = event_get_params(data, 1, &nick);
697
698 if (g_strcmp0(server->nick, nick) != 0) {
699 /* nick changed unexpectedly .. connected via proxy, etc. */
700 g_free(server->nick);
701 server->nick = g_strdup(nick);
702 }
703
704 /* set the server address */
705 g_free(server->real_address);
706 server->real_address = from == NULL ?
707 g_strdup(server->connrec->address) : /* shouldn't happen.. */
708 g_strdup(from);
709
710 /* last welcome message found - commands can be sent to server now. */
711 server->connected = 1;
712 server->real_connect_time = time(NULL);
713
714 /* let the queue send now that we are identified */
715 g_get_current_time(&server->wait_cmd);
716
717 if (server->connrec->usermode != NULL) {
718 /* Send the user mode, before the autosendcmd.
719 * Do not pass this through cmd_mode because it
720 * is not known whether the resulting MODE message
721 * (if any) is the initial umode or a reply to this.
722 */
723 irc_send_cmdv(server, "MODE %s %s", server->nick,
724 server->connrec->usermode);
725 g_free_not_null(server->wanted_usermode);
726 server->wanted_usermode = g_strdup(server->connrec->usermode);
727 }
728
729 signal_emit("event connected", 1, server);
730 g_free(params);
731 }
732
event_server_info(IRC_SERVER_REC * server,const char * data)733 static void event_server_info(IRC_SERVER_REC *server, const char *data)
734 {
735 char *params, *ircd_version, *usermodes, *chanmodes;
736
737 g_return_if_fail(server != NULL);
738
739 params = event_get_params(data, 5, NULL, NULL, &ircd_version, &usermodes, &chanmodes);
740
741 /* check if server understands I and e channel modes */
742 if (strchr(chanmodes, 'I') && strchr(chanmodes, 'e'))
743 server->emode_known = TRUE;
744
745 /* save server version */
746 g_free_not_null(server->version);
747 server->version = g_strdup(ircd_version);
748
749 g_free(params);
750 }
751
parse_chanmodes(IRC_SERVER_REC * server,const char * sptr)752 static void parse_chanmodes(IRC_SERVER_REC *server, const char *sptr)
753 {
754 mode_func_t *modefuncs[] = {
755 modes_type_a,
756 modes_type_b,
757 modes_type_c,
758 modes_type_d
759 };
760 char **item, **chanmodes;
761 int i;
762
763 chanmodes = g_strsplit(sptr, ",", 5); /* ignore extras */
764
765 for (i = 0, item = chanmodes; *item != NULL && i < 4; item++, i++) {
766 unsigned char *p = (unsigned char*) *item;
767 while (*p != '\0') {
768 server->modes[(int)*p].func = modefuncs[i];
769 p++;
770 }
771 }
772
773 g_strfreev(chanmodes);
774 }
775
parse_prefix(IRC_SERVER_REC * server,const char * sptr)776 static void parse_prefix(IRC_SERVER_REC *server, const char *sptr)
777 {
778 const char *eptr;
779
780 if (*sptr++ != '(')
781 return; /* Unknown prefix format */
782
783 eptr = strchr(sptr, ')');
784 if (eptr == NULL)
785 return;
786
787 eptr++;
788 while (*sptr != '\0' && *eptr != '\0' && *sptr != ')' && *eptr != ' ') {
789 server->modes[(int)(unsigned char) *sptr].func =
790 modes_type_prefix;
791 server->modes[(int)(unsigned char) *sptr].prefix = *eptr;
792 server->prefix[(int)(unsigned char) *eptr] = *sptr;
793 sptr++; eptr++;
794 }
795 }
796
797
event_isupport(IRC_SERVER_REC * server,const char * data)798 static void event_isupport(IRC_SERVER_REC *server, const char *data)
799 {
800 char **item, *sptr, *eptr;
801 char **isupport;
802 gpointer key, value;
803
804 g_return_if_fail(server != NULL);
805
806 server->isupport_sent = TRUE;
807
808 sptr = strchr(data, ' ');
809 if (sptr == NULL)
810 return;
811 sptr++;
812
813 isupport = g_strsplit(sptr, " ", -1);
814
815 for(item = isupport; *item != NULL; item++) {
816 int removed = FALSE;
817
818 if (**item == '\0')
819 continue;
820
821 if (**item == ':')
822 break;
823
824 sptr = strchr(*item, '=');
825 if (sptr != NULL) {
826 *sptr = '\0';
827 sptr++;
828 }
829
830 eptr = *item;
831 if(*eptr == '-') {
832 removed = TRUE;
833 eptr++;
834 }
835
836 key = value = NULL;
837 if (!g_hash_table_lookup_extended(server->isupport, eptr,
838 &key, &value) && removed)
839 continue;
840
841 g_hash_table_remove(server->isupport, eptr);
842 if (!removed) {
843 g_hash_table_insert(server->isupport, g_strdup(eptr),
844 g_strdup(sptr != NULL ? sptr : ""));
845 }
846
847 g_free(key);
848 g_free(value);
849 }
850 g_strfreev(isupport);
851 irc_server_init_isupport(server);
852
853 }
854
event_motd(IRC_SERVER_REC * server,const char * data,const char * from)855 static void event_motd(IRC_SERVER_REC *server, const char *data, const char *from)
856 {
857 if (server->connected)
858 return;
859
860 /* Stupid broken piece of shit ircd didn't send us 001,
861 you'd think they could at least get that right??
862 But no, then I'll have to go and add these idiotic kludges
863 to make them work. Maybe I should instead get the users of these
864 servers to complain about it to their admins.
865
866 Oh, and looks like it also doesn't answer anything to PINGs,
867 disable lag checking. */
868 server->disable_lag = TRUE;
869 event_connected(server, data, from);
870 }
871
event_end_of_motd(IRC_SERVER_REC * server,const char * data)872 static void event_end_of_motd(IRC_SERVER_REC *server, const char *data)
873 {
874 server->motd_got = TRUE;
875 }
876
event_channels_formed(IRC_SERVER_REC * server,const char * data)877 static void event_channels_formed(IRC_SERVER_REC *server, const char *data)
878 {
879 char *params, *channels;
880
881 g_return_if_fail(server != NULL);
882
883 params = event_get_params(data, 2, NULL, &channels);
884 server->channels_formed = atoi(channels);
885 g_free(params);
886 }
887
event_hosthidden(IRC_SERVER_REC * server,const char * data)888 static void event_hosthidden(IRC_SERVER_REC *server, const char *data)
889 {
890 char *params, *newhost, *p, *newuserhost;
891
892 g_return_if_fail(server != NULL);
893
894 params = event_get_params(data, 2, NULL, &newhost);
895 /* do a sanity check */
896 if (!strchr(newhost, '*') && !strchr(newhost, '?') &&
897 !strchr(newhost, '!') && !strchr(newhost, '#') &&
898 !strchr(newhost, '&') && !strchr(newhost, ' ') &&
899 *newhost != '\0' && *newhost != '@' &&
900 *newhost != ':' && *newhost != '-' &&
901 newhost[strlen(newhost) - 1] != '-') {
902 if (strchr(newhost, '@')) {
903 newuserhost = g_strdup(newhost);
904 g_free(server->userhost);
905 server->userhost = newuserhost;
906 } else if (server->userhost != NULL) {
907 /* no user@, only process if we know the user@
908 * already
909 */
910 p = strchr(server->userhost, '@');
911 if (p == NULL)
912 p = server->userhost;
913 newuserhost = g_strdup_printf("%.*s@%s", (int)(p - server->userhost), server->userhost, newhost);
914 g_free(server->userhost);
915 server->userhost = newuserhost;
916 }
917 }
918 g_free(params);
919 }
920
event_server_banned(IRC_SERVER_REC * server,const char * data)921 static void event_server_banned(IRC_SERVER_REC *server, const char *data)
922 {
923 g_return_if_fail(server != NULL);
924
925 server->banned = TRUE;
926 }
927
event_error(IRC_SERVER_REC * server,const char * data)928 static void event_error(IRC_SERVER_REC *server, const char *data)
929 {
930 g_return_if_fail(server != NULL);
931
932 if (!server->connected && (stristr(data, "Unauthorized") != NULL ||
933 stristr(data, "K-lined") != NULL ||
934 stristr(data, "Banned") != NULL ||
935 stristr(data, "Bad user info") != NULL))
936 server->banned = TRUE;
937 }
938
event_ping(IRC_SERVER_REC * server,const char * data)939 static void event_ping(IRC_SERVER_REC *server, const char *data)
940 {
941 char *params, *origin, *target, *str;
942
943 params = event_get_params(data, 2, &origin, &target);
944 str = *target == '\0' ? g_strconcat("PONG :", origin, NULL) :
945 g_strdup_printf("PONG %s :%s", target, origin);
946 irc_send_cmd_now(server, str);
947 g_free(str);
948 g_free(params);
949 }
950
event_empty(void)951 static void event_empty(void)
952 {
953 }
954
irc_server_init_isupport(IRC_SERVER_REC * server)955 void irc_server_init_isupport(IRC_SERVER_REC *server)
956 {
957 char *sptr;
958 gpointer key, value;
959 /* chanmodes/prefix will fully override defaults */
960 memset(server->modes, 0, sizeof(server->modes));
961 memset(server->prefix, 0, sizeof(server->prefix));
962
963 if ((sptr = g_hash_table_lookup(server->isupport, "CHANMODES")))
964 parse_chanmodes(server, sptr);
965
966 /* This is after chanmode because some servers define modes in both */
967 if (g_hash_table_lookup_extended(server->isupport, "PREFIX",
968 &key, &value)) {
969 sptr = value;
970 if (*sptr != '(') {
971 /* server incompatible with isupport draft */
972 g_hash_table_remove(server->isupport, key);
973 g_free(key);
974 g_free(value);
975 sptr = NULL;
976 }
977 } else {
978 sptr = NULL;
979 }
980
981 if (sptr == NULL) {
982 sptr = g_strdup("(ohv)@%+");
983 g_hash_table_insert(server->isupport, g_strdup("PREFIX"), sptr);
984 }
985 parse_prefix(server, sptr);
986
987 if ((sptr = g_hash_table_lookup(server->isupport, "MODES"))) {
988 server->max_modes_in_cmd = atoi(sptr);
989 if (server->max_modes_in_cmd < 1)
990 server->max_modes_in_cmd = DEFAULT_MAX_MODES;
991 }
992
993 if ((sptr = g_hash_table_lookup(server->isupport, "CASEMAPPING"))) {
994 if (strstr(sptr, "rfc1459") != NULL)
995 server->nick_comp_func = irc_nickcmp_rfc1459;
996 else
997 server->nick_comp_func = irc_nickcmp_ascii;
998 }
999
1000 if ((sptr = g_hash_table_lookup(server->isupport, "TARGMAX"))) {
1001 char *p = sptr;
1002 server->max_kicks_in_cmd = 1;
1003 server->max_msgs_in_cmd = 1;
1004 /* Not doing WHOIS here until it is clear what it means. */
1005 while (*p != '\0') {
1006 if (!g_ascii_strncasecmp(p, "KICK:", 5)) {
1007 server->max_kicks_in_cmd = atoi(p + 5);
1008 if (server->max_kicks_in_cmd <= 0)
1009 server->max_kicks_in_cmd = 30;
1010 } else if (!g_ascii_strncasecmp(p, "PRIVMSG:", 8)) {
1011 server->max_msgs_in_cmd = atoi(p + 8);
1012 if (server->max_msgs_in_cmd <= 0)
1013 server->max_msgs_in_cmd = 30;
1014 }
1015 p = strchr(p, ',');
1016 if (p == NULL)
1017 break;
1018 p++;
1019 }
1020 } else if ((sptr = g_hash_table_lookup(server->isupport, "MAXTARGETS"))) {
1021 server->max_msgs_in_cmd = atoi(sptr);
1022 if (server->max_msgs_in_cmd <= 0)
1023 server->max_msgs_in_cmd = 1;
1024 }
1025 }
1026
irc_servers_init(void)1027 void irc_servers_init(void)
1028 {
1029 settings_add_choice("servers", "rejoin_channels_on_reconnect", 1, "off;on;auto");
1030 settings_add_str("misc", "usermode", DEFAULT_USER_MODE);
1031 settings_add_str("misc", "split_line_start", "");
1032 settings_add_str("misc", "split_line_end", "");
1033 settings_add_bool("misc", "split_line_on_space", TRUE);
1034 settings_add_time("flood", "cmd_queue_speed", DEFAULT_CMD_QUEUE_SPEED);
1035 settings_add_int("flood", "cmds_max_at_once", DEFAULT_CMDS_MAX_AT_ONCE);
1036
1037 cmd_tag = -1;
1038
1039 signal_add_first("server connected", (SIGNAL_FUNC) sig_connected);
1040 signal_add_last("server destroyed", (SIGNAL_FUNC) sig_destroyed);
1041 signal_add_last("server quit", (SIGNAL_FUNC) sig_server_quit);
1042 signal_add("event 001", (SIGNAL_FUNC) event_connected);
1043 signal_add("event 004", (SIGNAL_FUNC) event_server_info);
1044 signal_add("event 005", (SIGNAL_FUNC) event_isupport);
1045 signal_add("event 375", (SIGNAL_FUNC) event_motd);
1046 signal_add_last("event 376", (SIGNAL_FUNC) event_end_of_motd);
1047 signal_add_last("event 422", (SIGNAL_FUNC) event_end_of_motd); /* no motd */
1048 signal_add("event 254", (SIGNAL_FUNC) event_channels_formed);
1049 signal_add("event 396", (SIGNAL_FUNC) event_hosthidden);
1050 signal_add("event 465", (SIGNAL_FUNC) event_server_banned);
1051 signal_add("event error", (SIGNAL_FUNC) event_error);
1052 signal_add("event ping", (SIGNAL_FUNC) event_ping);
1053 signal_add("event empty", (SIGNAL_FUNC) event_empty);
1054
1055 irc_servers_setup_init();
1056 irc_servers_reconnect_init();
1057 servers_redirect_init();
1058 servers_idle_init();
1059 }
1060
irc_servers_deinit(void)1061 void irc_servers_deinit(void)
1062 {
1063 if (cmd_tag != -1)
1064 g_source_remove(cmd_tag);
1065
1066 signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1067 signal_remove("server destroyed", (SIGNAL_FUNC) sig_destroyed);
1068 signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1069 signal_remove("event 001", (SIGNAL_FUNC) event_connected);
1070 signal_remove("event 004", (SIGNAL_FUNC) event_server_info);
1071 signal_remove("event 005", (SIGNAL_FUNC) event_isupport);
1072 signal_remove("event 375", (SIGNAL_FUNC) event_motd);
1073 signal_remove("event 376", (SIGNAL_FUNC) event_end_of_motd);
1074 signal_remove("event 422", (SIGNAL_FUNC) event_end_of_motd); /* no motd */
1075 signal_remove("event 254", (SIGNAL_FUNC) event_channels_formed);
1076 signal_remove("event 396", (SIGNAL_FUNC) event_hosthidden);
1077 signal_remove("event 465", (SIGNAL_FUNC) event_server_banned);
1078 signal_remove("event error", (SIGNAL_FUNC) event_error);
1079 signal_remove("event ping", (SIGNAL_FUNC) event_ping);
1080 signal_remove("event empty", (SIGNAL_FUNC) event_empty);
1081
1082 irc_servers_setup_deinit();
1083 irc_servers_reconnect_deinit();
1084 servers_redirect_deinit();
1085 servers_idle_deinit();
1086 }
1087