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