1 /*
2 chat-commands.c : irssi
3
4 Copyright (C) 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 #include "network.h"
23 #include "signals.h"
24 #include "commands.h"
25 #include "special-vars.h"
26 #include "settings.h"
27
28 #include "chat-protocols.h"
29 #include "servers.h"
30 #include "servers-setup.h"
31 #include "servers-reconnect.h"
32 #include "channels.h"
33 #include "queries.h"
34 #include "window-item-def.h"
35 #include "rawlog.h"
36
get_server_connect(const char * data,int * plus_addr,char ** rawlog_file)37 static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
38 char **rawlog_file)
39 {
40 CHAT_PROTOCOL_REC *proto;
41 SERVER_CONNECT_REC *conn;
42 GHashTable *optlist;
43 char *addr, *portstr, *password, *nick, *chatnet, *host, *tmp;
44 void *free_arg;
45
46 g_return_val_if_fail(data != NULL, NULL);
47
48 if (!cmd_get_params(data, &free_arg, 4 | PARAM_FLAG_OPTIONS,
49 "connect", &optlist, &addr, &portstr,
50 &password, &nick))
51 return NULL;
52 if (plus_addr != NULL) *plus_addr = *addr == '+';
53 if (*addr == '+') addr++;
54 if (*addr == '\0') {
55 signal_emit("error command", 1,
56 GINT_TO_POINTER(CMDERR_NOT_ENOUGH_PARAMS));
57 cmd_params_free(free_arg);
58 return NULL;
59 }
60
61 if (g_strcmp0(password, "-") == 0)
62 *password = '\0';
63
64 /* check if -<chatnet> option is used to specify chat protocol */
65 proto = chat_protocol_find_net(optlist);
66
67 /* connect to server */
68 chatnet = proto == NULL ? NULL :
69 g_hash_table_lookup(optlist, proto->chatnet);
70
71 if (chatnet == NULL)
72 chatnet = g_hash_table_lookup(optlist, "network");
73
74 conn = server_create_conn(proto != NULL ? proto->id : -1, addr,
75 atoi(portstr), chatnet, password, nick);
76 if (conn == NULL) {
77 signal_emit("error command", 1,
78 GINT_TO_POINTER(CMDERR_NO_SERVER_DEFINED));
79 cmd_params_free(free_arg);
80 return NULL;
81 }
82
83 if (proto == NULL)
84 proto = chat_protocol_find_id(conn->chat_type);
85
86 if (proto->not_initialized) {
87 /* trying to use protocol that isn't yet initialized */
88 signal_emit("chat protocol unknown", 1, proto->name);
89 server_connect_unref(conn);
90 cmd_params_free(free_arg);
91 return NULL;
92 }
93
94 if (strchr(addr, '/') != NULL)
95 conn->unix_socket = TRUE;
96
97 if (g_hash_table_lookup(optlist, "6") != NULL)
98 conn->family = AF_INET6;
99 else if (g_hash_table_lookup(optlist, "4") != NULL)
100 conn->family = AF_INET;
101
102 if (g_hash_table_lookup(optlist, "tls") != NULL || g_hash_table_lookup(optlist, "ssl") != NULL)
103 conn->use_tls = TRUE;
104 if ((tmp = g_hash_table_lookup(optlist, "tls_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
105 conn->tls_cert = g_strdup(tmp);
106 if ((tmp = g_hash_table_lookup(optlist, "tls_pkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
107 conn->tls_pkey = g_strdup(tmp);
108 if ((tmp = g_hash_table_lookup(optlist, "tls_pass")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL)
109 conn->tls_pass = g_strdup(tmp);
110 if (g_hash_table_lookup(optlist, "tls_verify") != NULL || g_hash_table_lookup(optlist, "ssl_verify") != NULL)
111 conn->tls_verify = TRUE;
112 if ((tmp = g_hash_table_lookup(optlist, "tls_cafile")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
113 conn->tls_cafile = g_strdup(tmp);
114 if ((tmp = g_hash_table_lookup(optlist, "tls_capath")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
115 conn->tls_capath = g_strdup(tmp);
116 if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL)
117 conn->tls_ciphers = g_strdup(tmp);
118 if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_cert")) != NULL)
119 conn->tls_pinned_cert = g_strdup(tmp);
120 if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_pubkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_pubkey")) != NULL)
121 conn->tls_pinned_pubkey = g_strdup(tmp);
122 if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0')
123 || (conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0'))
124 conn->tls_verify = TRUE;
125 if ((conn->tls_cert != NULL && conn->tls_cert[0] != '\0') || conn->tls_verify)
126 conn->use_tls = TRUE;
127
128 if (g_hash_table_lookup(optlist, "!") != NULL)
129 conn->no_autojoin_channels = TRUE;
130
131 if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
132 conn->no_autosendcmd = TRUE;
133
134 if (g_hash_table_lookup(optlist, "noproxy") != NULL)
135 g_free_and_null(conn->proxy);
136
137
138 *rawlog_file = g_strdup(g_hash_table_lookup(optlist, "rawlog"));
139
140 host = g_hash_table_lookup(optlist, "host");
141 if (host != NULL && *host != '\0') {
142 IPADDR ip4, ip6;
143
144 if (net_gethostbyname(host, &ip4, &ip6) == 0)
145 server_connect_own_ip_save(conn, &ip4, &ip6);
146 }
147
148 cmd_params_free(free_arg);
149 return conn;
150 }
151
152 /* SYNTAX: CONNECT [-4 | -6] [-tls] [-tls_cert <cert>] [-tls_pkey <pkey>] [-tls_pass <password>]
153 [-tls_verify] [-tls_cafile <cafile>] [-tls_capath <capath>]
154 [-tls_ciphers <list>] [-tls_pinned_cert <fingerprint>] [-tls_pinned_pubkey <fingerprint>]
155 [-!] [-noautosendcmd]
156 [-noproxy] [-network <network>] [-host <hostname>]
157 [-rawlog <file>]
158 <address>|<chatnet> [<port> [<password> [<nick>]]] */
159 /* NOTE: -network replaces the old -ircnet flag. */
cmd_connect(const char * data)160 static void cmd_connect(const char *data)
161 {
162 SERVER_CONNECT_REC *conn;
163 SERVER_REC *server;
164 char *rawlog_file;
165
166 conn = get_server_connect(data, NULL, &rawlog_file);
167 if (conn != NULL) {
168 server = server_connect(conn);
169 server_connect_unref(conn);
170
171 if (server != NULL && rawlog_file != NULL)
172 rawlog_open(server->rawlog, rawlog_file);
173
174 g_free(rawlog_file);
175 }
176 }
177
find_reconnect_server(int chat_type,const char * addr,int port)178 static RECONNECT_REC *find_reconnect_server(int chat_type,
179 const char *addr, int port)
180 {
181 RECONNECT_REC *match, *last_proto_match;
182 GSList *tmp;
183 int count;
184
185 g_return_val_if_fail(addr != NULL, NULL);
186
187 /* check if there's a reconnection to the same host and maybe even
188 the same port */
189 match = last_proto_match = NULL; count = 0;
190 for (tmp = reconnects; tmp != NULL; tmp = tmp->next) {
191 RECONNECT_REC *rec = tmp->data;
192
193 if (rec->conn->chat_type == chat_type) {
194 count++; last_proto_match = rec;
195 if (g_ascii_strcasecmp(rec->conn->address, addr) == 0) {
196 if (rec->conn->port == port)
197 return rec;
198 match = rec;
199 }
200 }
201 }
202
203 if (count == 1) {
204 /* only one reconnection with wanted protocol,
205 we probably want to use it */
206 return last_proto_match;
207 }
208
209 return match;
210 }
211
update_reconnection(SERVER_CONNECT_REC * conn,SERVER_REC * server)212 static void update_reconnection(SERVER_CONNECT_REC *conn, SERVER_REC *server)
213 {
214 SERVER_CONNECT_REC *oldconn;
215 RECONNECT_REC *recon;
216
217 if (server != NULL) {
218 oldconn = server->connrec;
219 server_connect_ref(oldconn);
220 reconnect_save_status(conn, server);
221 } else {
222 /* maybe we can reconnect some server from
223 reconnection queue */
224 recon = find_reconnect_server(conn->chat_type,
225 conn->address, conn->port);
226 if (recon == NULL) return;
227
228 oldconn = recon->conn;
229 server_connect_ref(oldconn);
230 server_reconnect_destroy(recon);
231
232 conn->away_reason = g_strdup(oldconn->away_reason);
233 conn->channels = g_strdup(oldconn->channels);
234 }
235
236 conn->reconnection = TRUE;
237
238 if (conn->chatnet == NULL && oldconn->chatnet != NULL)
239 conn->chatnet = g_strdup(oldconn->chatnet);
240
241 server_connect_unref(oldconn);
242 if (server != NULL) {
243 signal_emit("command disconnect", 2,
244 "* Changing server", server);
245 }
246 }
247
cmd_server(const char * data,SERVER_REC * server,WI_ITEM_REC * item)248 static void cmd_server(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
249 {
250 command_runsub("server", data, server, item);
251 }
252
253 /* SYNTAX: SERVER CONNECT [-4 | -6] [-tls] [-tls_cert <cert>] [-tls_pkey <pkey>]
254 [-tls_pass <password>] [-tls_verify] [-tls_cafile <cafile>]
255 [-tls_capath <capath>]
256 [-tls_ciphers <list>] [-tls_pinned_cert <fingerprint>] [-tls_pinned_pubkey <fingerprint>]
257 [-!] [-noautosendcmd]
258 [-noproxy] [-network <network>] [-host <hostname>]
259 [-rawlog <file>]
260 [+]<address>|<chatnet> [<port> [<password> [<nick>]]] */
261 /* NOTE: -network replaces the old -ircnet flag. */
cmd_server_connect(const char * data,SERVER_REC * server)262 static void cmd_server_connect(const char *data, SERVER_REC *server)
263 {
264 SERVER_CONNECT_REC *conn;
265 char *rawlog_file;
266 int plus_addr;
267
268 g_return_if_fail(data != NULL);
269
270 /* create connection record */
271 conn = get_server_connect(data, &plus_addr, &rawlog_file);
272 if (conn != NULL) {
273 if (!plus_addr)
274 update_reconnection(conn, server);
275 server = server_connect(conn);
276 server_connect_unref(conn);
277
278 if (server != NULL && rawlog_file != NULL)
279 rawlog_open(server->rawlog, rawlog_file);
280
281 g_free(rawlog_file);
282 }
283 }
284
285 /* SYNTAX: DISCONNECT *|<tag> [<message>] */
cmd_disconnect(const char * data,SERVER_REC * server)286 static void cmd_disconnect(const char *data, SERVER_REC *server)
287 {
288 char *tag, *msg;
289 void *free_arg;
290
291 g_return_if_fail(data != NULL);
292
293 if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &tag, &msg))
294 return;
295
296 if (*tag != '\0' && g_strcmp0(tag, "*") != 0) {
297 server = server_find_tag(tag);
298 if (server == NULL)
299 server = server_find_lookup_tag(tag);
300 }
301 if (server == NULL) cmd_param_error(CMDERR_NOT_CONNECTED);
302
303 if (*msg == '\0') msg = (char *) settings_get_str("quit_message");
304 signal_emit("server quit", 2, server, msg);
305
306 cmd_params_free(free_arg);
307 server_disconnect(server);
308 }
309
310 /* SYNTAX: QUIT [<message>] */
cmd_quit(const char * data)311 static void cmd_quit(const char *data)
312 {
313 GSList *tmp, *next;
314 const char *quitmsg;
315 char *str;
316
317 g_return_if_fail(data != NULL);
318
319 quitmsg = *data != '\0' ? data :
320 settings_get_str("quit_message");
321
322 /* disconnect from every server */
323 for (tmp = servers; tmp != NULL; tmp = next) {
324 next = tmp->next;
325
326 str = g_strdup_printf("* %s", quitmsg);
327 cmd_disconnect(str, tmp->data);
328 g_free(str);
329 }
330
331 signal_emit("gui exit", 0);
332 }
333
334 /* SYNTAX: MSG [-<server tag>] [-channel | -nick] *|<targets> <message> */
cmd_msg(const char * data,SERVER_REC * server,WI_ITEM_REC * item)335 static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
336 {
337 GHashTable *optlist;
338 char *target, *origtarget, *msg;
339 void *free_arg;
340 int free_ret, target_type = SEND_TARGET_NICK;
341
342 g_return_if_fail(data != NULL);
343
344 if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
345 PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
346 "msg", &optlist, &target, &msg))
347 return;
348 if (*target == '\0' || *msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
349
350 server = cmd_options_get_server("msg", optlist, server);
351 if (server == NULL || !server->connected)
352 cmd_param_error(CMDERR_NOT_CONNECTED);
353
354 origtarget = target;
355 free_ret = FALSE;
356 if (g_strcmp0(target, ",") == 0 || g_strcmp0(target, ".") == 0) {
357 target = parse_special(&target, server, item,
358 NULL, &free_ret, NULL, 0);
359 if (target != NULL && *target == '\0') {
360 if (free_ret)
361 g_free(target);
362 target = NULL;
363 free_ret = FALSE;
364 }
365 }
366
367 if (target != NULL) {
368 if (g_strcmp0(target, "*") == 0) {
369 /* send to active channel/query */
370 if (item == NULL)
371 cmd_param_error(CMDERR_NOT_JOINED);
372
373 target_type = IS_CHANNEL(item) ?
374 SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
375 target = (char *) window_item_get_target(item);
376 } else if (g_hash_table_lookup(optlist, "channel") != NULL)
377 target_type = SEND_TARGET_CHANNEL;
378 else if (g_hash_table_lookup(optlist, "nick") != NULL)
379 target_type = SEND_TARGET_NICK;
380 else {
381 /* Need to rely on server_ischannel(). If the protocol
382 doesn't really know if it's channel or nick based on
383 the name, it should just assume it's nick, because
384 when typing text to channels it's always sent with
385 /MSG -channel. */
386 target_type = server_ischannel(server, target) ?
387 SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
388 }
389 }
390 if (target != NULL) {
391 char **splitmsgs;
392 char **tmp = NULL;
393 char *singlemsg[] = { msg, NULL };
394 char *m;
395 int n = 0;
396
397 /*
398 * If split_message is NULL, the server doesn't need to split
399 * long messages.
400 */
401 if (server->split_message != NULL)
402 splitmsgs = tmp = server->split_message(server, target,
403 msg);
404 else
405 splitmsgs = singlemsg;
406
407 while ((m = splitmsgs[n++])) {
408 signal_emit("server sendmsg", 4, server, target, m,
409 GINT_TO_POINTER(target_type));
410 signal_emit(target_type == SEND_TARGET_CHANNEL ?
411 "message own_public" :
412 "message own_private", 4, server, m,
413 target, origtarget);
414 }
415 g_strfreev(tmp);
416 } else {
417 signal_emit("message own_private", 4, server, msg, target,
418 origtarget);
419 }
420
421 if (free_ret && target != NULL) g_free(target);
422 cmd_params_free(free_arg);
423 }
424
sig_server_sendmsg(SERVER_REC * server,const char * target,const char * msg,void * target_type_p)425 static void sig_server_sendmsg(SERVER_REC *server, const char *target,
426 const char *msg, void *target_type_p)
427 {
428 server->send_message(server, target, msg,
429 GPOINTER_TO_INT(target_type_p));
430 }
431
cmd_foreach(const char * data,SERVER_REC * server,WI_ITEM_REC * item)432 static void cmd_foreach(const char *data, SERVER_REC *server,
433 WI_ITEM_REC *item)
434 {
435 command_runsub("foreach", data, server, item);
436 }
437
438 /* SYNTAX: FOREACH SERVER <command> */
cmd_foreach_server(const char * data,SERVER_REC * server)439 static void cmd_foreach_server(const char *data, SERVER_REC *server)
440 {
441 GSList *list;
442 const char *cmdchars;
443 char *str;
444
445 cmdchars = settings_get_str("cmdchars");
446 str = strchr(cmdchars, *data) != NULL ? g_strdup(data) :
447 g_strdup_printf("%c%s", *cmdchars, data);
448
449 list = g_slist_copy(servers);
450 while (list != NULL) {
451 signal_emit("send command", 3, str, list->data, NULL);
452 list = g_slist_remove(list, list->data);
453 }
454
455 g_free(str);
456 }
457
458 /* SYNTAX: FOREACH CHANNEL <command> */
cmd_foreach_channel(const char * data)459 static void cmd_foreach_channel(const char *data)
460 {
461 GSList *list;
462 const char *cmdchars;
463 char *str;
464
465 cmdchars = settings_get_str("cmdchars");
466 str = strchr(cmdchars, *data) != NULL ? g_strdup(data) :
467 g_strdup_printf("%c%s", *cmdchars, data);
468
469 list = g_slist_copy(channels);
470 while (list != NULL) {
471 CHANNEL_REC *rec = list->data;
472
473 signal_emit("send command", 3, str, rec->server, rec);
474 list = g_slist_remove(list, list->data);
475 }
476
477 g_free(str);
478 }
479
480 /* SYNTAX: FOREACH QUERY <command> */
cmd_foreach_query(const char * data)481 static void cmd_foreach_query(const char *data)
482 {
483 GSList *list;
484 const char *cmdchars;
485 char *str;
486
487 cmdchars = settings_get_str("cmdchars");
488 str = strchr(cmdchars, *data) != NULL ? g_strdup(data) :
489 g_strdup_printf("%c%s", *cmdchars, data);
490
491
492 list = g_slist_copy(queries);
493 while (list != NULL) {
494 QUERY_REC *rec = list->data;
495
496 signal_emit("send command", 3, str, rec->server, rec);
497 list = g_slist_remove(list, list->data);
498 }
499
500 g_free(str);
501 }
502
chat_commands_init(void)503 void chat_commands_init(void)
504 {
505 settings_add_str("misc", "quit_message", "leaving");
506
507 command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
508 command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect);
509 command_bind("connect", NULL, (SIGNAL_FUNC) cmd_connect);
510 command_bind("disconnect", NULL, (SIGNAL_FUNC) cmd_disconnect);
511 command_bind("quit", NULL, (SIGNAL_FUNC) cmd_quit);
512 command_bind("msg", NULL, (SIGNAL_FUNC) cmd_msg);
513 command_bind("foreach", NULL, (SIGNAL_FUNC) cmd_foreach);
514 command_bind("foreach server", NULL, (SIGNAL_FUNC) cmd_foreach_server);
515 command_bind("foreach channel", NULL, (SIGNAL_FUNC) cmd_foreach_channel);
516 command_bind("foreach query", NULL, (SIGNAL_FUNC) cmd_foreach_query);
517
518 signal_add("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
519
520 command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_pinned_cert +ssl_pinned_pubkey tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey +host noproxy -rawlog noautosendcmd");
521 command_set_options("msg", "channel nick");
522 }
523
chat_commands_deinit(void)524 void chat_commands_deinit(void)
525 {
526 command_unbind("server", (SIGNAL_FUNC) cmd_server);
527 command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect);
528 command_unbind("connect", (SIGNAL_FUNC) cmd_connect);
529 command_unbind("disconnect", (SIGNAL_FUNC) cmd_disconnect);
530 command_unbind("quit", (SIGNAL_FUNC) cmd_quit);
531 command_unbind("msg", (SIGNAL_FUNC) cmd_msg);
532 command_unbind("foreach", (SIGNAL_FUNC) cmd_foreach);
533 command_unbind("foreach server", (SIGNAL_FUNC) cmd_foreach_server);
534 command_unbind("foreach channel", (SIGNAL_FUNC) cmd_foreach_channel);
535 command_unbind("foreach query", (SIGNAL_FUNC) cmd_foreach_query);
536
537 signal_remove("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
538 }
539