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