f5d0e9f8f5d18bafebf9007d022109a464d25346
[silc.git] / apps / irssi / src / core / chat-commands.c
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
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 (strcmp(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 (proto == NULL)
77                 proto = chat_protocol_find_id(conn->chat_type);
78
79         if (proto->not_initialized) {
80                 /* trying to use protocol that isn't yet initialized */
81                 signal_emit("chat protocol unknown", 1, proto->name);
82                 server_connect_unref(conn);
83                 cmd_params_free(free_arg);
84                 return NULL;
85         }
86
87         if (strchr(addr, '/') != NULL)
88                 conn->unix_socket = TRUE;
89
90         if (g_hash_table_lookup(optlist, "6") != NULL)
91                 conn->family = AF_INET6;
92         else if (g_hash_table_lookup(optlist, "4") != NULL)
93                 conn->family = AF_INET;
94
95         if (g_hash_table_lookup(optlist, "ssl") != NULL)
96                 conn->use_ssl = TRUE;
97         if ((tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
98                 conn->ssl_cert = g_strdup(tmp);
99         if ((tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
100                 conn->ssl_pkey = g_strdup(tmp);
101         if (g_hash_table_lookup(optlist, "ssl_verify") != NULL)
102                 conn->ssl_verify = TRUE;
103         if ((tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
104                 conn->ssl_cafile = g_strdup(tmp);
105         if ((tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
106                 conn->ssl_capath = g_strdup(tmp);
107         if ((conn->ssl_capath != NULL && conn->ssl_capath[0] != '\0')
108         ||  (conn->ssl_cafile != NULL && conn->ssl_cafile[0] != '\0'))
109                 conn->ssl_verify = TRUE;
110         if ((conn->ssl_cert != NULL && conn->ssl_cert[0] != '\0') || conn->ssl_verify)
111                 conn->use_ssl = TRUE;
112
113         if (g_hash_table_lookup(optlist, "!") != NULL)
114                 conn->no_autojoin_channels = TRUE;
115
116     if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
117         conn->no_autosendcmd = TRUE;
118
119         if (g_hash_table_lookup(optlist, "noproxy") != NULL)
120                 g_free_and_null(conn->proxy);
121
122
123         *rawlog_file = g_strdup(g_hash_table_lookup(optlist, "rawlog"));
124
125         host = g_hash_table_lookup(optlist, "host");
126         if (host != NULL && *host != '\0') {
127                 IPADDR ip4, ip6;
128
129                 if (net_gethostbyname(host, &ip4, &ip6) == 0)
130                         server_connect_own_ip_save(conn, &ip4, &ip6);
131         }
132
133         cmd_params_free(free_arg);
134         return conn;
135 }
136
137 /* SYNTAX: CONNECT [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>]
138                    [-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
139                    [-!] [-noautosendcmd]
140                    [-noproxy] [-network <network>] [-host <hostname>]
141                    [-rawlog <file>]
142                    <address>|<chatnet> [<port> [<password> [<nick>]]] */
143 /* NOTE: -network replaces the old -ircnet flag. */
144 static void cmd_connect(const char *data)
145 {
146         SERVER_CONNECT_REC *conn;
147         SERVER_REC *server;
148         char *rawlog_file;
149
150         conn = get_server_connect(data, NULL, &rawlog_file);
151         if (conn != NULL) {
152                 server = server_connect(conn);
153                 server_connect_unref(conn);
154
155                 if (server != NULL && rawlog_file != NULL)
156                         rawlog_open(server->rawlog, rawlog_file);
157
158                 g_free(rawlog_file);
159         }
160 }
161
162 static RECONNECT_REC *find_reconnect_server(int chat_type,
163                                             const char *addr, int port)
164 {
165         RECONNECT_REC *match, *last_proto_match;
166         GSList *tmp;
167         int count;
168
169         g_return_val_if_fail(addr != NULL, NULL);
170
171         /* check if there's a reconnection to the same host and maybe even
172            the same port */
173         match = last_proto_match = NULL; count = 0;
174         for (tmp = reconnects; tmp != NULL; tmp = tmp->next) {
175                 RECONNECT_REC *rec = tmp->data;
176
177                 if (rec->conn->chat_type == chat_type) {
178                         count++; last_proto_match = rec;
179                         if (g_strcasecmp(rec->conn->address, addr) == 0) {
180                                 if (rec->conn->port == port)
181                                         return rec;
182                                 match = rec;
183                         }
184                 }
185         }
186
187         if (count == 1) {
188                 /* only one reconnection with wanted protocol,
189                    we probably want to use it */
190                 return last_proto_match;
191         }
192
193         return match;
194 }
195
196 static void update_reconnection(SERVER_CONNECT_REC *conn, SERVER_REC *server)
197 {
198         SERVER_CONNECT_REC *oldconn;
199         RECONNECT_REC *recon;
200
201         if (server != NULL) {
202                 oldconn = server->connrec;
203                 server_connect_ref(oldconn);
204                 reconnect_save_status(conn, server);
205         } else {
206                 /* maybe we can reconnect some server from
207                    reconnection queue */
208                 recon = find_reconnect_server(conn->chat_type,
209                                               conn->address, conn->port);
210                 if (recon == NULL) return;
211
212                 oldconn = recon->conn;
213                 server_connect_ref(oldconn);
214                 server_reconnect_destroy(recon);
215
216                 conn->away_reason = g_strdup(oldconn->away_reason);
217                 conn->channels = g_strdup(oldconn->channels);
218         }
219
220         conn->reconnection = TRUE;
221
222         if (conn->chatnet == NULL && oldconn->chatnet != NULL)
223                 conn->chatnet = g_strdup(oldconn->chatnet);
224
225         server_connect_unref(oldconn);
226         if (server != NULL) {
227                 signal_emit("command disconnect", 2,
228                             "* Changing server", server);
229         }
230 }
231
232 static void cmd_server(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
233 {
234         command_runsub("server", data, server, item);
235 }
236
237 static void sig_default_command_server(const char *data, SERVER_REC *server,
238                                        WI_ITEM_REC *item)
239 {
240         signal_emit("command server connect", 3, data, server, item);
241 }
242
243 /* SYNTAX: SERVER [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>]
244                   [-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
245                   [-!] [-noautosendcmd]
246                   [-noproxy] [-network <network>] [-host <hostname>]
247                   [-rawlog <file>]
248                   [+]<address>|<chatnet> [<port> [<password> [<nick>]]] */
249 /* NOTE: -network replaces the old -ircnet flag. */
250 static void cmd_server_connect(const char *data, SERVER_REC *server)
251 {
252         SERVER_CONNECT_REC *conn;
253         char *rawlog_file;
254         int plus_addr;
255
256         g_return_if_fail(data != NULL);
257
258         /* create connection record */
259         conn = get_server_connect(data, &plus_addr, &rawlog_file);
260         if (conn != NULL) {
261                 if (!plus_addr)
262                         update_reconnection(conn, server);
263                 server = server_connect(conn);
264                 server_connect_unref(conn);
265
266                 if (server != NULL && rawlog_file != NULL)
267                         rawlog_open(server->rawlog, rawlog_file);
268
269                 g_free(rawlog_file);
270         }
271 }
272
273 /* SYNTAX: DISCONNECT *|<tag> [<message>] */
274 static void cmd_disconnect(const char *data, SERVER_REC *server)
275 {
276         char *tag, *msg;
277         void *free_arg;
278
279         g_return_if_fail(data != NULL);
280
281         if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &tag, &msg))
282                 return;
283
284         if (*tag != '\0' && strcmp(tag, "*") != 0) {
285                 server = server_find_tag(tag);
286                 if (server == NULL)
287                         server = server_find_lookup_tag(tag);
288         }
289         if (server == NULL) cmd_param_error(CMDERR_NOT_CONNECTED);
290
291         if (*msg == '\0') msg = (char *) settings_get_str("quit_message");
292         signal_emit("server quit", 2, server, msg);
293
294         cmd_params_free(free_arg);
295         server_disconnect(server);
296 }
297
298 /* SYNTAX: QUIT [<message>] */
299 static void cmd_quit(const char *data)
300 {
301         GSList *tmp, *next;
302         const char *quitmsg;
303         char *str;
304
305         g_return_if_fail(data != NULL);
306
307         quitmsg = *data != '\0' ? data :
308                 settings_get_str("quit_message");
309
310         /* disconnect from every server */
311         for (tmp = servers; tmp != NULL; tmp = next) {
312                 next = tmp->next;
313
314                 str = g_strdup_printf("* %s", quitmsg);
315                 cmd_disconnect(str, tmp->data);
316                 g_free(str);
317         }
318
319         signal_emit("gui exit", 0);
320 }
321
322 /* SYNTAX: MSG [-<server tag>] [-channel | -nick] <targets> <message> */
323 static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
324 {
325         GHashTable *optlist;
326         char *target, *origtarget, *msg;
327         void *free_arg;
328         int free_ret, target_type = SEND_TARGET_NICK;
329
330         g_return_if_fail(data != NULL);
331
332         if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
333                             PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
334                             "msg", &optlist, &target, &msg))
335                 return;
336         if (*target == '\0' || *msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
337
338         server = cmd_options_get_server("msg", optlist, server);
339         if (server == NULL || !server->connected)
340                 cmd_param_error(CMDERR_NOT_CONNECTED);
341
342         origtarget = target;
343         free_ret = FALSE;
344         if (strcmp(target, ",") == 0 || strcmp(target, ".") == 0) {
345                 target = parse_special(&target, server, item,
346                                        NULL, &free_ret, NULL, 0);
347                 if (target != NULL && *target == '\0') {
348                         if (free_ret)
349                                 g_free(target);
350                         target = NULL;
351                         free_ret = FALSE;
352                 }
353         }
354
355         if (target != NULL) {
356                 if (strcmp(target, "*") == 0) {
357                         /* send to active channel/query */
358                         if (item == NULL)
359                                 cmd_param_error(CMDERR_NOT_JOINED);
360
361                         target_type = IS_CHANNEL(item) ?
362                                 SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
363                         target = (char *) window_item_get_target(item);
364                 } else if (g_hash_table_lookup(optlist, "channel") != NULL)
365                         target_type = SEND_TARGET_CHANNEL;
366                 else if (g_hash_table_lookup(optlist, "nick") != NULL)
367                         target_type = SEND_TARGET_NICK;
368                 else {
369                         /* Need to rely on server_ischannel(). If the protocol
370                            doesn't really know if it's channel or nick based on
371                            the name, it should just assume it's nick, because
372                            when typing text to channels it's always sent with
373                            /MSG -channel. */
374                         target_type = server_ischannel(server, target) ?
375                                 SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
376                 }
377         }
378         if (target != NULL) {
379                 signal_emit("server sendmsg", 4, server, target, msg,
380                             GINT_TO_POINTER(target_type));
381         }
382         signal_emit(target != NULL && target_type == SEND_TARGET_CHANNEL ?
383                     "message own_public" : "message own_private", 4,
384                     server, msg, target, origtarget);
385
386         if (free_ret && target != NULL) g_free(target);
387         cmd_params_free(free_arg);
388 }
389
390 static void sig_server_sendmsg(SERVER_REC *server, const char *target,
391                                const char *msg, void *target_type_p)
392 {
393         server->send_message(server, target, msg,
394                              GPOINTER_TO_INT(target_type_p));
395 }
396
397 static void cmd_foreach(const char *data, SERVER_REC *server,
398                         WI_ITEM_REC *item)
399 {
400         command_runsub("foreach", data, server, item);
401 }
402
403 /* SYNTAX: FOREACH SERVER <command> */
404 static void cmd_foreach_server(const char *data, SERVER_REC *server)
405 {
406         GSList *list;
407
408         list = g_slist_copy(servers);
409         while (list != NULL) {
410                 signal_emit("send command", 3, data, list->data, NULL);
411                 list = g_slist_remove(list, list->data);
412         }
413 }
414
415 /* SYNTAX: FOREACH CHANNEL <command> */
416 static void cmd_foreach_channel(const char *data)
417 {
418         GSList *list;
419
420         list = g_slist_copy(channels);
421         while (list != NULL) {
422                 CHANNEL_REC *rec = list->data;
423
424                 signal_emit("send command", 3, data, rec->server, rec);
425                 list = g_slist_remove(list, list->data);
426         }
427 }
428
429 /* SYNTAX: FOREACH QUERY <command> */
430 static void cmd_foreach_query(const char *data)
431 {
432         GSList *list;
433
434         list = g_slist_copy(queries);
435         while (list != NULL) {
436                 QUERY_REC *rec = list->data;
437
438                 signal_emit("send command", 3, data, rec->server, rec);
439                 list = g_slist_remove(list, list->data);
440         }
441 }
442
443 void chat_commands_init(void)
444 {
445         settings_add_str("misc", "quit_message", "leaving");
446
447         command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
448         command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect);
449         command_bind("connect", NULL, (SIGNAL_FUNC) cmd_connect);
450         command_bind("disconnect", NULL, (SIGNAL_FUNC) cmd_disconnect);
451         command_bind("quit", NULL, (SIGNAL_FUNC) cmd_quit);
452         command_bind("msg", NULL, (SIGNAL_FUNC) cmd_msg);
453         command_bind("foreach", NULL, (SIGNAL_FUNC) cmd_foreach);
454         command_bind("foreach server", NULL, (SIGNAL_FUNC) cmd_foreach_server);
455         command_bind("foreach channel", NULL, (SIGNAL_FUNC) cmd_foreach_channel);
456         command_bind("foreach query", NULL, (SIGNAL_FUNC) cmd_foreach_query);
457
458         signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server);
459         signal_add("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
460
461         command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey ssl_verify +ssl_cafile +ssl_capath +host noproxy -rawlog noautosendcmd");
462         command_set_options("msg", "channel nick");
463 }
464
465 void chat_commands_deinit(void)
466 {
467         command_unbind("server", (SIGNAL_FUNC) cmd_server);
468         command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect);
469         command_unbind("connect", (SIGNAL_FUNC) cmd_connect);
470         command_unbind("disconnect", (SIGNAL_FUNC) cmd_disconnect);
471         command_unbind("quit", (SIGNAL_FUNC) cmd_quit);
472         command_unbind("msg", (SIGNAL_FUNC) cmd_msg);
473         command_unbind("foreach", (SIGNAL_FUNC) cmd_foreach);
474         command_unbind("foreach server", (SIGNAL_FUNC) cmd_foreach_server);
475         command_unbind("foreach channel", (SIGNAL_FUNC) cmd_foreach_channel);
476         command_unbind("foreach query", (SIGNAL_FUNC) cmd_foreach_query);
477
478         signal_remove("default command server", (SIGNAL_FUNC) sig_default_command_server);
479         signal_remove("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
480 }