Merged silc_1_0_branch to trunk.
[silc.git] / apps / irssi / src / silc / core / silc-servers.c
1 /*
2   silc-server.c : irssi
3
4   Copyright (C) 2000 - 2005 Timo Sirainen
5                             Pekka Riikonen <priikone@silcnet.org>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22 #include "module.h"
23
24 #include "net-nonblock.h"
25 #include "net-sendbuffer.h"
26 #include "signals.h"
27 #include "servers.h"
28 #include "commands.h"
29 #include "levels.h"
30 #include "modules.h"
31 #include "rawlog.h"
32 #include "misc.h"
33 #include "settings.h"
34
35 #include "servers-setup.h"
36
37 #include "client_ops.h"
38 #include "silc-servers.h"
39 #include "silc-channels.h"
40 #include "silc-queries.h"
41 #include "silc-nicklist.h"
42 #include "silc-cmdqueue.h"
43 #include "window-item-def.h"
44
45 #include "fe-common/core/printtext.h"
46 #include "fe-common/core/fe-channels.h"
47 #include "fe-common/core/keyboard.h"
48 #include "fe-common/silc/module-formats.h"
49
50 #include "silc-commands.h"
51
52 void silc_servers_reconnect_init(void);
53 void silc_servers_reconnect_deinit(void);
54
55 int silc_send_channel(SILC_SERVER_REC *server,
56                       char *channel, char *msg,
57                       SilcMessageFlags flags)
58 {
59   SILC_CHANNEL_REC *rec;
60
61   rec = silc_channel_find(server, channel);
62   if (rec == NULL || rec->entry == NULL) {
63     cmd_return_error_value(CMDERR_NOT_JOINED, FALSE);
64   }
65
66   silc_client_send_channel_message(silc_client, server->conn, rec->entry,
67                                    NULL, flags, msg, strlen(msg), TRUE);
68   return TRUE;
69 }
70
71 typedef struct {
72   char *nick;
73   char *msg;
74   int len;
75   SilcMessageFlags flags;
76   SILC_SERVER_REC *server;
77 } PRIVMSG_REC;
78
79 /* Callback function that sends the private message if the client was
80    resolved from the server. */
81
82 static void silc_send_msg_clients(SilcClient client,
83                                   SilcClientConnection conn,
84                                   SilcClientEntry *clients,
85                                   SilcUInt32 clients_count,
86                                   void *context)
87 {
88   PRIVMSG_REC *rec = context;
89   SILC_SERVER_REC *server = rec->server;
90   SilcClientEntry target;
91   char *nickname = NULL;
92
93   if (!clients_count) {
94     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
95               "%s: There is no such client", rec->nick);
96   } else {
97     if (clients_count > 1) {
98       silc_parse_userfqdn(rec->nick, &nickname, NULL);
99
100       /* Find the correct one. The rec->nick might be a formatted nick
101          so this will find the correct one. */
102       clients = silc_client_get_clients_local(silc_client, server->conn,
103                                               nickname, rec->nick,
104                                               &clients_count);
105       if (!clients) {
106         printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
107                   "%s: There is no such client", rec->nick);
108         silc_free(nickname);
109         goto out;
110       }
111       silc_free(nickname);
112     }
113
114     target = clients[0];
115
116     /* Still check for exact math for nickname, this compares the
117        real (formatted) nickname and the nick (maybe formatted) that
118        use gave. This is to assure that `nick' does not match
119        `nick@host'. */
120     if (!silc_utf8_strcasecmp(rec->nick, clients[0]->nickname)) {
121       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
122                 "%s: There is no such client", rec->nick);
123       goto out;
124     }
125
126     /* Send the private message */
127     silc_client_send_private_message(client, conn, target,
128                                      rec->flags,
129                                      rec->msg, rec->len,
130                                      TRUE);
131   }
132
133  out:
134   g_free(rec->nick);
135   g_free(rec->msg);
136   g_free(rec);
137 }
138
139 int silc_send_msg(SILC_SERVER_REC *server, char *nick, char *msg,
140                   int msg_len, SilcMessageFlags flags)
141 {
142   PRIVMSG_REC *rec;
143   SilcClientEntry *clients;
144   SilcUInt32 clients_count;
145   char *nickname = NULL;
146
147   if (!silc_parse_userfqdn(nick, &nickname, NULL)) {
148     printformat_module("fe-common/silc", server, NULL,
149                        MSGLEVEL_CRAP, SILCTXT_BAD_NICK, nick);
150     return FALSE;
151   }
152
153   /* Find client entry */
154   clients = silc_client_get_clients_local(silc_client, server->conn,
155                                           nickname, nick, &clients_count);
156   if (!clients) {
157     rec = g_new0(PRIVMSG_REC, 1);
158     rec->nick = g_strdup(nick);
159     rec->msg = g_strdup(msg);
160     rec->server = server;
161     rec->flags = flags;
162     rec->len = msg_len;
163
164     /* Could not find client with that nick, resolve it from server. */
165     silc_client_get_clients(silc_client, server->conn,
166                             nickname, NULL, silc_send_msg_clients, rec);
167     silc_free(nickname);
168     return TRUE;
169   }
170
171   /* Send the private message directly */
172   silc_free(nickname);
173   silc_client_send_private_message(silc_client, server->conn,
174                                    clients[0], flags,
175                                    msg, msg_len, TRUE);
176   return TRUE;
177 }
178
179 void silc_send_mime(SILC_SERVER_REC *server, int channel, const char *to,
180                     const char *data, int sign)
181 {
182   char *unescaped_data;
183   SilcUInt32 unescaped_data_len;
184   int target_type;
185
186   if (!(IS_SILC_SERVER(server)) || (data == NULL) || (to == NULL))
187     return;
188
189   if (channel) {
190     target_type = SEND_TARGET_CHANNEL;
191   } else {
192     target_type = server_ischannel(SERVER(server), to) ?
193       SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
194   }
195
196   unescaped_data = silc_unescape_data(data, &unescaped_data_len);
197
198   if (target_type == SEND_TARGET_CHANNEL) {
199     SILC_CHANNEL_REC *rec;
200
201     rec = silc_channel_find(server, to);
202     if (rec == NULL || rec->entry == NULL) {
203       cmd_return_error(CMDERR_NOT_JOINED);
204     }
205
206     silc_client_send_channel_message(silc_client, server->conn, rec->entry,
207                                      NULL, SILC_MESSAGE_FLAG_DATA |
208                                      (sign ? SILC_MESSAGE_FLAG_SIGNED : 0),
209                                      unescaped_data, unescaped_data_len, TRUE);
210   } else {
211     silc_send_msg(server, (char *)to, unescaped_data, unescaped_data_len,
212                   SILC_MESSAGE_FLAG_DATA |
213                   (sign ? SILC_MESSAGE_FLAG_SIGNED : 0));
214
215   }
216
217   signal_stop();
218
219   silc_free(unescaped_data);
220 }
221
222 static int isnickflag_func(char flag)
223 {
224   return flag == '@' || flag == '+';
225 }
226
227 static int ischannel_func(SERVER_REC *server, const char *data)
228 {
229   return FALSE;
230 }
231
232 const char *get_nick_flags(void)
233 {
234   return "@\0\0";
235 }
236
237 static void send_message(SILC_SERVER_REC *server, char *target,
238                          char *msg, int target_type)
239 {
240   char *message = NULL, *t = NULL;
241   int len;
242
243   g_return_if_fail(server != NULL);
244   g_return_if_fail(target != NULL);
245   g_return_if_fail(msg != NULL);
246
247   if (!silc_term_utf8()) {
248     len = silc_utf8_encoded_len(msg, strlen(msg), SILC_STRING_LOCALE);
249     message = silc_calloc(len + 1, sizeof(*message));
250     g_return_if_fail(message != NULL);
251     silc_utf8_encode(msg, strlen(msg), SILC_STRING_LOCALE, message, len);
252   }
253
254   if (target_type == SEND_TARGET_CHANNEL)
255     silc_send_channel(server, target, message ? message : msg,
256                       SILC_MESSAGE_FLAG_UTF8);
257   else {
258     if (!silc_term_utf8()) {
259       len = silc_utf8_encoded_len(target, strlen(target), SILC_STRING_LOCALE);
260       t = silc_calloc(len + 1, sizeof(*t));
261       g_return_if_fail(t != NULL);
262       silc_utf8_encode(target, strlen(target), SILC_STRING_LOCALE, t, len);
263     }
264
265     silc_send_msg(server, t ? t : target, message ? message : msg,
266                   message ? strlen(message) : strlen(msg),
267                   SILC_MESSAGE_FLAG_UTF8);
268   }
269
270   silc_free(message);
271   silc_free(t);
272 }
273
274 void silc_send_heartbeat(SilcSocketConnection sock,
275                          void *hb_context)
276 {
277   SILC_SERVER_REC *server = SILC_SERVER(hb_context);
278
279   if (server == NULL)
280     return;
281
282   silc_client_send_packet(silc_client, server->conn, SILC_PACKET_HEARTBEAT,
283                           NULL, 0);
284 }
285
286 static void sig_connected(SILC_SERVER_REC *server)
287 {
288   SilcClientConnection conn;
289   SilcClientConnectionParams params;
290   char *file;
291   int fd;
292
293   if (!IS_SILC_SERVER(server))
294     return;
295
296   /* Try to read detached session data and use it if found. */
297   memset(&params, 0, sizeof(params));
298   file = silc_get_session_filename(server);
299   params.detach_data = silc_file_readfile(file, &params.detach_data_len);
300   if (params.detach_data)
301     params.detach_data[params.detach_data_len] = 0;
302
303   /* Add connection to the client library */
304   conn = silc_client_add_connection(silc_client, &params,
305                                     server->connrec->address,
306                                     server->connrec->port,
307                                     server);
308   server->conn = conn;
309
310   if (params.detach_data)
311     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
312                         SILCTXT_REATTACH, server->tag);
313
314   silc_free(params.detach_data);
315
316   fd = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
317
318   /* Start key exchange with the server */
319   silc_client_start_key_exchange(silc_client, conn, fd);
320
321   /* Put default attributes */
322   silc_query_attributes_default(silc_client, conn);
323
324   /* initialize heartbeat sending */
325   if (settings_get_int("heartbeat") > 0)
326     silc_socket_set_heartbeat(conn->sock, settings_get_int("heartbeat"),
327                                 (void *)server,
328                                 (SilcSocketConnectionHBCb)silc_send_heartbeat,
329                                 silc_client->schedule);
330
331   server->ftp_sessions = silc_dlist_init();
332   server->isnickflag = isnickflag_func;
333   server->ischannel = ischannel_func;
334   server->get_nick_flags = get_nick_flags;
335   server->send_message = (void *) send_message;
336 }
337
338 static void sig_disconnected(SILC_SERVER_REC *server)
339 {
340   if (!IS_SILC_SERVER(server))
341     return;
342
343   silc_dlist_uninit(server->ftp_sessions);
344
345   if (server->conn && server->conn->sock != NULL) {
346     silc_client_close_connection(silc_client, server->conn);
347
348     /* SILC closes the handle */
349     g_io_channel_unref(net_sendbuffer_handle(server->handle));
350     net_sendbuffer_destroy(server->handle, FALSE);
351     server->handle = NULL;
352   }
353 }
354
355 SERVER_REC *silc_server_init_connect(SERVER_CONNECT_REC *conn)
356 {
357   SILC_SERVER_REC *server;
358
359   g_return_val_if_fail(IS_SILC_SERVER_CONNECT(conn), NULL);
360   if (conn->address == NULL || *conn->address == '\0')
361     return NULL;
362   if (conn->nick == NULL || *conn->nick == '\0') {
363     silc_say_error("Cannot connect: nickname is not set");
364     return NULL;
365   }
366
367   server = g_new0(SILC_SERVER_REC, 1);
368   server->chat_type = SILC_PROTOCOL;
369   server->connrec = (SILC_SERVER_CONNECT_REC *)conn;
370   server_connect_ref(conn);
371
372   if (server->connrec->port <= 0)
373     server->connrec->port = 706;
374
375   server_connect_init((SERVER_REC *)server);
376   return (SERVER_REC *)server;
377 }
378
379 void silc_server_connect(SERVER_REC *server)
380 {
381   if (!server_start_connect(server)) {
382     server_connect_unref(server->connrec);
383     g_free(server);
384     return;
385   }
386 }
387
388 /* Return a string of all channels in server in server->channels_join()
389    format */
390
391 char *silc_server_get_channels(SILC_SERVER_REC *server)
392 {
393   GSList *tmp;
394   GString *chans;
395   char *ret;
396
397   g_return_val_if_fail(server != NULL, FALSE);
398
399   chans = g_string_new(NULL);
400   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
401     CHANNEL_REC *channel = tmp->data;
402
403     g_string_sprintfa(chans, "%s,", channel->name);
404   }
405
406   if (chans->len > 0)
407     g_string_truncate(chans, chans->len-1);
408
409   ret = chans->str;
410   g_string_free(chans, FALSE);
411
412   return ret;
413 }
414
415 /* Syntaxes of all SILC commands for HELP files (the help file generation
416    will snoop these from here). */
417
418 /* SYNTAX: BAN <channel> [+|-[<nickname>[@<server>[!<username>[@hostname>]]]]] */
419 /* SYNTAX: CMODE <channel> +|-<modes> [{ <arguments>}] */
420 /* SYNTAX: CUMODE <channel> +|-<modes> <nickname>[@<hostname>] */
421 /* SYNTAX: GETKEY <nickname or server name> */
422 /* SYNTAX: INVITE <channel> [<nickname>[@hostname>] */
423 /* SYNTAX: INVITE <channel> [+|-[<nickname>[@<server>[!<username>[@hostname>]]]]] */
424 /* SYNTAX: KEY MSG <nickname> set|unset|list|agreement|negotiate [<arguments>] */
425 /* SYNTAX: KEY CHANNEL <channel> set|unset|list|change [<arguments>] */
426 /* SYNTAX: KICK <channel> <nickname>[@<hostname>] [<comment>] */
427 /* SYNTAX: KILL <nickname>[@<hostname>] [<comment>] [-pubkey] */
428 /* SYNTAX: OPER <username> [-pubkey] */
429 /* SYNTAX: SILCOPER <username> [-pubkey] */
430 /* SYNTAX: TOPIC <channel> [<topic>] */
431 /* SYNTAX: UMODE +|-<modes> */
432 /* SYNTAX: WHOIS [<nickname>[@<hostname>]] [-details] [-pubkey <pubkeyfile>] [<count>] */
433 /* SYNTAX: WHOWAS <nickname>[@<hostname>] [<count>] */
434 /* SYNTAX: CLOSE <server> [<port>] */
435 /* SYNTAX: SHUTDOWN */
436 /* SYNTAX: MOTD [<server>] */
437 /* SYNTAX: LIST [<channel>] */
438 /* SYNTAX: ME <message> */
439 /* SYNTAX: ACTION [-sign] [-channel] <target> <message> */
440 /* SYNTAX: AWAY [<message>] */
441 /* SYNTAX: INFO [<server>] */
442 /* SYNTAX: NICK <nickname> */
443 /* SYNTAX: NOTICE [-sign] [-channel] <target> <message> */
444 /* SYNTAX: PART [<channel>] */
445 /* SYNTAX: PING */
446 /* SYNTAX: SCONNECT <server> [<port>] */
447 /* SYNTAX: USERS <channel> */
448 /* SYNTAX: FILE SEND <filepath> <nickname> [<local IP> [<local port>]] [-no-listener]*/
449 /* SYNTAX: FILE ACCEPT [<nickname>] */
450 /* SYNTAX: FILE CLOSE [<nickname>] */
451 /* SYNTAX: FILE */
452 /* SYNTAX: JOIN <channel> [<passphrase>] [-cipher <cipher>] [-hmac <hmac>] [-founder] [-auth [<pubkeyfile> <privkeyfile> [<privkey passphrase>]]]*/
453 /* SYNTAX: DETACH */
454 /* SYNTAX: WATCH [<-add | -del> <nickname>] [-pubkey +|-<pubkeyfile>] */
455 /* SYNTAX: STATS */
456 /* SYNTAX: ATTR [<-del> <option> [{ <value>}]] */
457 /* SYNTAX: SMSG [<-channel>] <target> <message> */
458 /* SYNTAX: LISTKEYS [-servers] [-clients] [<public key file>] */
459
460 void silc_command_exec(SILC_SERVER_REC *server,
461                        const char *command, const char *args)
462 {
463   char *data;
464   g_return_if_fail(server != NULL);
465
466   /* Call the command */
467   data = g_strconcat(command, " ", args, NULL);
468   silc_queue_command_call(silc_client, server->conn, data);
469   g_free(data);
470 }
471
472 /* Generic command function to call any SILC command directly. */
473
474 static void command_self(const char *data, SILC_SERVER_REC *server,
475                          WI_ITEM_REC *item)
476 {
477   CMD_SILC_SERVER(server);
478
479   if (!IS_SILC_SERVER(server) || !server->connected) {
480     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Not connected to server");
481     return;
482   }
483
484   if (IS_SILC_CHANNEL(item)) {
485     SILC_CHANNEL_REC *chanrec;
486     chanrec = silc_channel_find(server, item->visible_name);
487     if (chanrec)
488       server->conn->current_channel = chanrec->entry;
489   }
490
491   silc_command_exec(server, current_command, data);
492   signal_stop();
493 }
494
495 /* SCONNECT command.  Calls actually SILC's CONNECT command since Irssi
496    has CONNECT command for other purposes. */
497
498 static void command_sconnect(const char *data, SILC_SERVER_REC *server)
499 {
500   CMD_SILC_SERVER(server);
501   if (!IS_SILC_SERVER(server) || !server->connected) {
502     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Not connected to server");
503     return;
504   }
505
506   silc_command_exec(server, "CONNECT", data);
507   signal_stop();
508 }
509
510 /* SMSG command, to send digitally signed messages */
511
512 static void command_smsg(const char *data, SILC_SERVER_REC *server,
513                          WI_ITEM_REC *item)
514 {
515   GHashTable *optlist;
516   char *target, *origtarget, *msg;
517   void *free_arg;
518   int free_ret, target_type;
519
520   g_return_if_fail(data != NULL);
521   if (server == NULL || !server->connected)
522     cmd_param_error(CMDERR_NOT_CONNECTED);
523
524   if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
525                       PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
526                       "msg", &optlist, &target, &msg))
527     return;
528   if (*target == '\0' || *msg == '\0')
529     cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
530
531   origtarget = target;
532   free_ret = FALSE;
533
534   if (strcmp(target, "*") == 0) {
535     if (item == NULL)
536       cmd_param_error(CMDERR_NOT_JOINED);
537
538     target_type = IS_CHANNEL(item) ?
539       SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
540     target = (char *) window_item_get_target(item);
541   } else if (g_hash_table_lookup(optlist, "channel") != NULL) {
542     target_type = SEND_TARGET_CHANNEL;
543   } else {
544     target_type = server_ischannel(SERVER(server), target) ?
545       SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
546   }
547
548   if (target != NULL) {
549     char *message = NULL, *t = NULL;
550     int len, result;
551
552     if (!silc_term_utf8()) {
553       len = silc_utf8_encoded_len(msg, strlen(msg), SILC_STRING_LOCALE);
554       message = silc_calloc(len + 1, sizeof(*message));
555       g_return_if_fail(message != NULL);
556       silc_utf8_encode(msg, strlen(msg), SILC_STRING_LOCALE, message, len);
557     }
558
559     if (target_type == SEND_TARGET_CHANNEL)
560       result = silc_send_channel(server, target, message ? message : msg,
561                                  SILC_MESSAGE_FLAG_UTF8 |
562                                  SILC_MESSAGE_FLAG_SIGNED);
563     else {
564       if (!silc_term_utf8()) {
565         len = silc_utf8_encoded_len(target, strlen(target),
566                                     SILC_STRING_LOCALE);
567         t = silc_calloc(len + 1, sizeof(*t));
568         g_return_if_fail(t != NULL);
569         silc_utf8_encode(target, strlen(target), SILC_STRING_LOCALE, t, len);
570       }
571       result = silc_send_msg(server, t ? t : target, message ? message : msg,
572                              message ? strlen(message) : strlen(msg),
573                              SILC_MESSAGE_FLAG_UTF8 |
574                              SILC_MESSAGE_FLAG_SIGNED);
575     }
576     silc_free(message);
577     silc_free(t);
578     if (!result)
579       goto out;
580   }
581
582   signal_emit(target != NULL && target_type == SEND_TARGET_CHANNEL ?
583               "message signed_own_public" : "message signed_own_private", 4,
584               server, msg, target, origtarget);
585 out:
586   if (free_ret && target != NULL) g_free(target);
587   cmd_params_free(free_arg);
588 }
589
590 /* FILE command */
591
592 SILC_TASK_CALLBACK(silc_client_file_close_later)
593 {
594   FtpSession ftp = (FtpSession)context;
595
596   SILC_LOG_DEBUG(("Start"));
597
598   silc_client_file_close(silc_client, ftp->conn, ftp->session_id);
599   silc_free(ftp->filepath);
600   silc_free(ftp);
601 }
602
603 static void silc_client_file_monitor(SilcClient client,
604                                      SilcClientConnection conn,
605                                      SilcClientMonitorStatus status,
606                                      SilcClientFileError error,
607                                      SilcUInt64 offset,
608                                      SilcUInt64 filesize,
609                                      SilcClientEntry client_entry,
610                                      SilcUInt32 session_id,
611                                      const char *filepath,
612                                      void *context)
613 {
614   SILC_SERVER_REC *server = (SILC_SERVER_REC *)context;
615   FtpSession ftp;
616   char fsize[32];
617
618   if (status == SILC_CLIENT_FILE_MONITOR_CLOSED)
619     return;
620
621   snprintf(fsize, sizeof(fsize) - 1, "%llu", ((filesize + 1023) / 1024));
622
623   silc_dlist_start(server->ftp_sessions);
624   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
625     if (ftp->session_id == session_id) {
626       if (!ftp->filepath && filepath)
627         ftp->filepath = strdup(filepath);
628       break;
629     }
630   }
631
632   if (ftp == SILC_LIST_END)
633     return;
634
635   if (status == SILC_CLIENT_FILE_MONITOR_ERROR) {
636     if (error == SILC_CLIENT_FILE_NO_SUCH_FILE)
637       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
638                          SILCTXT_FILE_ERROR_NO_SUCH_FILE,
639                          client_entry->nickname,
640                          filepath ? filepath : "[N/A]");
641     else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED)
642       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
643                          SILCTXT_FILE_ERROR_PERMISSION_DENIED,
644                          client_entry->nickname);
645     else
646       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
647                          SILCTXT_FILE_ERROR, client_entry->nickname);
648     silc_schedule_task_add(silc_client->schedule, 0,
649                            silc_client_file_close_later, ftp,
650                            1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
651     silc_dlist_del(server->ftp_sessions, ftp);
652     if (ftp == server->current_session) {
653       server->current_session = NULL;
654       silc_dlist_start(server->ftp_sessions);
655       server->current_session = silc_dlist_get(server->ftp_sessions);
656     }
657   }
658
659   if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT) {
660     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
661                        SILCTXT_FILE_KEY_EXCHANGE, client_entry->nickname);
662   }
663
664   /* Save some transmission data */
665   if (offset && filesize) {
666     unsigned long delta = time(NULL) - ftp->starttime;
667
668     ftp->percent = ((double)offset / (double)filesize) * (double)100.0;
669     if (delta)
670       ftp->kps = (double)((offset / (double)delta) + 1023) / (double)1024;
671     else
672       ftp->kps = (double)(offset + 1023) / (double)1024;
673     ftp->offset = offset;
674     ftp->filesize = filesize;
675   }
676
677   if (status == SILC_CLIENT_FILE_MONITOR_SEND) {
678     if (offset == 0) {
679       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
680                          SILCTXT_FILE_TRANSMIT, filepath, fsize,
681                          client_entry->nickname);
682       ftp->starttime = time(NULL);
683     }
684     if (offset == filesize) {
685       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
686                          SILCTXT_FILE_TRANSMITTED, filepath, fsize,
687                          client_entry->nickname, ftp->kps);
688       silc_schedule_task_add(silc_client->schedule, 0,
689                              silc_client_file_close_later, ftp,
690                              1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
691       silc_dlist_del(server->ftp_sessions, ftp);
692       if (ftp == server->current_session) {
693         server->current_session = NULL;
694         silc_dlist_start(server->ftp_sessions);
695         server->current_session = silc_dlist_get(server->ftp_sessions);
696       }
697
698     }
699   }
700
701   if (status == SILC_CLIENT_FILE_MONITOR_RECEIVE) {
702     if (offset == 0) {
703       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
704                          SILCTXT_FILE_RECEIVE, filepath, fsize,
705                          client_entry->nickname);
706       ftp->starttime = time(NULL);
707     }
708
709     if (offset == filesize) {
710       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
711                          SILCTXT_FILE_RECEIVED, filepath, fsize,
712                          client_entry->nickname, ftp->kps);
713       silc_schedule_task_add(silc_client->schedule, 0,
714                              silc_client_file_close_later, ftp,
715                              1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
716       silc_dlist_del(server->ftp_sessions, ftp);
717       if (ftp == server->current_session) {
718         server->current_session = NULL;
719         silc_dlist_start(server->ftp_sessions);
720         server->current_session = silc_dlist_get(server->ftp_sessions);
721       }
722
723     }
724   }
725 }
726
727 typedef struct {
728   SILC_SERVER_REC *server;
729   char *data;
730   char *nick;
731   WI_ITEM_REC *item;
732 } *FileGetClients;
733
734 static void silc_client_command_file_get_clients(SilcClient client,
735                                                  SilcClientConnection conn,
736                                                  SilcClientEntry *clients,
737                                                  SilcUInt32 clients_count,
738                                                  void *context)
739 {
740   FileGetClients internal = (FileGetClients)context;
741
742   if (!clients) {
743     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s",
744               internal->nick);
745     silc_free(internal->data);
746     silc_free(internal->nick);
747     silc_free(internal);
748     return;
749   }
750
751   signal_emit("command file", 3, internal->data, internal->server,
752               internal->item);
753
754   silc_free(internal->data);
755   silc_free(internal->nick);
756   silc_free(internal);
757 }
758
759 static void command_file(const char *data, SILC_SERVER_REC *server,
760                          WI_ITEM_REC *item)
761 {
762   SilcClientConnection conn;
763   SilcClientEntry *entrys, client_entry;
764   SilcClientFileError ret;
765   SilcUInt32 entry_count;
766   char *nickname = NULL, *tmp;
767   unsigned char **argv;
768   SilcUInt32 argc;
769   SilcUInt32 *argv_lens, *argv_types;
770   int type = 0;
771   FtpSession ftp;
772   char *local_ip = NULL;
773   SilcUInt32 local_port = 0;
774   SilcUInt32 session_id;
775   bool do_not_bind = FALSE;
776
777   CMD_SILC_SERVER(server);
778   if (!server || !IS_SILC_SERVER(server) || !server->connected)
779     cmd_return_error(CMDERR_NOT_CONNECTED);
780
781   conn = server->conn;
782
783   /* Now parse all arguments */
784   tmp = g_strconcat("FILE", " ", data, NULL);
785   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
786   g_free(tmp);
787
788   if (argc == 1)
789     type = 4;
790
791   if (argc >= 2) {
792     if (!strcasecmp(argv[1], "send"))
793       type = 1;
794     if (!strcasecmp(argv[1], "accept"))
795       type = 2;
796     if (!strcasecmp(argv[1], "close"))
797       type = 3;
798   }
799
800   if (type == 0)
801     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
802
803   switch (type) {
804   case 1:
805     if (argc < 4)
806       cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
807
808     /* Parse the typed nickname. */
809     if (!silc_parse_userfqdn(argv[3], &nickname, NULL)) {
810       printformat_module("fe-common/silc", server, NULL,
811                          MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[3]);
812       goto out;
813     }
814
815     /* Find client entry */
816     entrys = silc_client_get_clients_local(silc_client, conn, nickname,
817                                            argv[3], &entry_count);
818     if (!entrys) {
819       FileGetClients inter = silc_calloc(1, sizeof(*inter));
820       inter->server = server;
821       inter->data = strdup(data);
822       inter->nick = strdup(nickname);
823       inter->item = item;
824       silc_client_get_clients(silc_client, conn, nickname, argv[3],
825                               silc_client_command_file_get_clients, inter);
826       goto out;
827     }
828     client_entry = entrys[0];
829     silc_free(entrys);
830
831     if (argc >= 5) {
832       if (!strcasecmp(argv[4], "-no-listener"))
833         do_not_bind = TRUE;
834       else
835         local_ip = argv[4];
836     }
837     if (argc >= 6) {
838       if (!strcasecmp(argv[5], "-no-listener"))
839         do_not_bind = TRUE;
840       else
841         local_port = atoi(argv[5]);
842     }
843     if (argc >= 7) {
844       if (!strcasecmp(argv[6], "-no-listener"))
845         do_not_bind = TRUE;
846     }
847
848     ret =
849       silc_client_file_send(silc_client, conn, silc_client_file_monitor,
850                             server, local_ip, local_port, do_not_bind,
851                             client_entry, argv[2], &session_id);
852     if (ret == SILC_CLIENT_FILE_OK) {
853       ftp = silc_calloc(1, sizeof(*ftp));
854       ftp->session_id = session_id;
855
856       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
857                          SILCTXT_FILE_SEND, client_entry->nickname,
858                          argv[2]);
859
860       ftp->client_entry = client_entry;
861       ftp->filepath = strdup(argv[2]);
862       ftp->conn = conn;
863       ftp->send = TRUE;
864       silc_dlist_add(server->ftp_sessions, ftp);
865       server->current_session = ftp;
866     } else {
867       if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
868         printformat_module("fe-common/silc", server, NULL,
869                            MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
870                            client_entry->nickname);
871       if (ret == SILC_CLIENT_FILE_NO_SUCH_FILE)
872         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
873                            SILCTXT_FILE_ERROR_NO_SUCH_FILE,
874                            client_entry->nickname, argv[2]);
875     }
876
877     break;
878
879   case 2:
880     /* Parse the typed nickname. */
881     if (argc >= 3) {
882       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
883         printformat_module("fe-common/silc", server, NULL,
884                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
885         goto out;
886       }
887
888       /* Find client entry */
889       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
890                                              argv[2], &entry_count);
891       if (!entrys) {
892         FileGetClients inter = silc_calloc(1, sizeof(*inter));
893         inter->server = server;
894         inter->data = strdup(data);
895         inter->nick = strdup(nickname);
896         inter->item = item;
897         silc_client_get_clients(silc_client, conn, nickname, argv[2],
898                                 silc_client_command_file_get_clients, inter);
899         goto out;
900       }
901       client_entry = entrys[0];
902       silc_free(entrys);
903     } else {
904       if (!server->current_session) {
905         printformat_module("fe-common/silc", server, NULL,
906                            MSGLEVEL_CRAP, SILCTXT_FILE_NA);
907         goto out;
908       }
909
910       ret = silc_client_file_receive(silc_client, conn,
911                                      silc_client_file_monitor, server, NULL,
912                                      server->current_session->session_id,
913                                      NULL, NULL);
914       if (ret != SILC_CLIENT_FILE_OK) {
915         if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
916           printformat_module("fe-common/silc", server, NULL,
917                              MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
918                              server->current_session->client_entry->nickname);
919         else {
920           printformat_module("fe-common/silc", server, NULL,
921                              MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
922                              server->current_session->client_entry->nickname);
923
924           silc_client_file_close(silc_client, conn,
925                                  server->current_session->session_id);
926           silc_dlist_del(server->ftp_sessions, server->current_session);
927           silc_free(server->current_session->filepath);
928           silc_free(server->current_session);
929           server->current_session = NULL;
930
931           silc_dlist_start(server->ftp_sessions);
932           server->current_session = silc_dlist_get(server->ftp_sessions);
933         }
934       }
935
936       goto out;
937     }
938
939     silc_dlist_start(server->ftp_sessions);
940     while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
941       if (ftp->client_entry == client_entry && !ftp->filepath) {
942         ret = silc_client_file_receive(silc_client, conn,
943                                        silc_client_file_monitor, server,
944                                        NULL, ftp->session_id, NULL, NULL);
945         if (ret != SILC_CLIENT_FILE_OK) {
946           if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
947             printformat_module("fe-common/silc", server, NULL,
948                                MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
949                                client_entry->nickname);
950           else {
951             printformat_module("fe-common/silc", server, NULL,
952                                MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
953                                client_entry->nickname);
954             silc_client_file_close(silc_client, conn, ftp->session_id);
955             silc_dlist_del(server->ftp_sessions, ftp);
956             if (ftp == server->current_session) {
957               server->current_session = NULL;
958               silc_dlist_start(server->ftp_sessions);
959               server->current_session = silc_dlist_get(server->ftp_sessions);
960             }
961             silc_free(ftp->filepath);
962             silc_free(ftp);
963           }
964         }
965         break;
966       }
967     }
968
969     if (ftp == SILC_LIST_END) {
970       printformat_module("fe-common/silc", server, NULL,
971                          MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
972                          client_entry->nickname);
973       goto out;
974     }
975     break;
976
977   case 3:
978     /* Parse the typed nickname. */
979     if (argc >= 3) {
980       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
981         printformat_module("fe-common/silc", server, NULL,
982                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
983         goto out;
984       }
985
986       /* Find client entry */
987       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
988                                              argv[2], &entry_count);
989       if (!entrys) {
990         FileGetClients inter = silc_calloc(1, sizeof(*inter));
991         inter->server = server;
992         inter->data = strdup(data);
993         inter->nick = strdup(nickname);
994         inter->item = item;
995         silc_client_get_clients(silc_client, conn, nickname, argv[2],
996                                 silc_client_command_file_get_clients, inter);
997         goto out;
998       }
999       client_entry = entrys[0];
1000       silc_free(entrys);
1001     } else {
1002       if (!server->current_session) {
1003         printformat_module("fe-common/silc", server, NULL,
1004                            MSGLEVEL_CRAP, SILCTXT_FILE_NA);
1005         goto out;
1006       }
1007
1008       silc_client_file_close(silc_client, conn,
1009                              server->current_session->session_id);
1010       printformat_module("fe-common/silc", server, NULL,
1011                          MSGLEVEL_CRAP, SILCTXT_FILE_CLOSED,
1012                          server->current_session->client_entry->nickname,
1013                          server->current_session->filepath ?
1014                          server->current_session->filepath : "[N/A]");
1015       silc_dlist_del(server->ftp_sessions, server->current_session);
1016       silc_free(server->current_session->filepath);
1017       silc_free(server->current_session);
1018       server->current_session = NULL;
1019
1020       silc_dlist_start(server->ftp_sessions);
1021       server->current_session = silc_dlist_get(server->ftp_sessions);
1022       goto out;
1023     }
1024
1025     silc_dlist_start(server->ftp_sessions);
1026     while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
1027       if (ftp->client_entry == client_entry) {
1028         silc_client_file_close(silc_client, conn, ftp->session_id);
1029         printformat_module("fe-common/silc", server, NULL,
1030                            MSGLEVEL_CRAP, SILCTXT_FILE_CLOSED,
1031                            client_entry->nickname,
1032                            ftp->filepath ? ftp->filepath : "[N/A]");
1033         silc_dlist_del(server->ftp_sessions, ftp);
1034         if (ftp == server->current_session) {
1035           server->current_session = NULL;
1036           silc_dlist_start(server->ftp_sessions);
1037           server->current_session = silc_dlist_get(server->ftp_sessions);
1038         }
1039         silc_free(ftp->filepath);
1040         silc_free(ftp);
1041         break;
1042       }
1043     }
1044
1045     if (ftp == SILC_LIST_END) {
1046       printformat_module("fe-common/silc", server, NULL,
1047                          MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
1048                          client_entry->nickname);
1049       goto out;
1050     }
1051     break;
1052
1053   case 4:
1054
1055     if (!silc_dlist_count(server->ftp_sessions)) {
1056       printformat_module("fe-common/silc", server, NULL,
1057                          MSGLEVEL_CRAP, SILCTXT_FILE_NA);
1058       goto out;
1059     }
1060
1061     printformat_module("fe-common/silc", server, NULL,
1062                        MSGLEVEL_CRAP, SILCTXT_FILE_SHOW_HEADER);
1063
1064     silc_dlist_start(server->ftp_sessions);
1065     while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
1066       printformat_module("fe-common/silc", server, NULL,
1067                          MSGLEVEL_CRAP, SILCTXT_FILE_SHOW_LINE,
1068                          ftp->client_entry->nickname,
1069                          ftp->session_id,
1070                          ftp->send ? "send" : "receive",
1071                          (SilcUInt32)(ftp->offset + 1023) / 1024,
1072                          (SilcUInt32)(ftp->filesize + 1023) / 1024,
1073                          ftp->percent, ftp->kps,
1074                          ftp->filepath ? ftp->filepath : "[N/A]");
1075     }
1076
1077     break;
1078
1079   default:
1080     break;
1081   }
1082
1083  out:
1084   silc_free(nickname);
1085 }
1086
1087 void silc_server_init(void)
1088 {
1089   silc_servers_reconnect_init();
1090
1091   signal_add_first("server connected", (SIGNAL_FUNC) sig_connected);
1092   signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
1093   signal_add("mime-send", (SIGNAL_FUNC)silc_send_mime);
1094   command_bind_silc("whois", MODULE_NAME, (SIGNAL_FUNC) command_self);
1095   command_bind_silc("whowas", MODULE_NAME, (SIGNAL_FUNC) command_self);
1096   command_bind_silc("nick", MODULE_NAME, (SIGNAL_FUNC) command_self);
1097   command_bind_silc("topic", MODULE_NAME, (SIGNAL_FUNC) command_self);
1098   command_bind_silc("cmode", MODULE_NAME, (SIGNAL_FUNC) command_self);
1099   command_bind_silc("cumode", MODULE_NAME, (SIGNAL_FUNC) command_self);
1100   command_bind_silc("users", MODULE_NAME, (SIGNAL_FUNC) command_self);
1101   command_bind_silc("list", MODULE_NAME, (SIGNAL_FUNC) command_self);
1102   command_bind_silc("ban", MODULE_NAME, (SIGNAL_FUNC) command_self);
1103   command_bind_silc("oper", MODULE_NAME, (SIGNAL_FUNC) command_self);
1104   command_bind_silc("silcoper", MODULE_NAME, (SIGNAL_FUNC) command_self);
1105   command_bind_silc("umode", MODULE_NAME, (SIGNAL_FUNC) command_self);
1106   command_bind_silc("invite", MODULE_NAME, (SIGNAL_FUNC) command_self);
1107   command_bind_silc("kill", MODULE_NAME, (SIGNAL_FUNC) command_self);
1108   command_bind_silc("kick", MODULE_NAME, (SIGNAL_FUNC) command_self);
1109   command_bind_silc("info", MODULE_NAME, (SIGNAL_FUNC) command_self);
1110   command_bind_silc("ping", MODULE_NAME, (SIGNAL_FUNC) command_self);
1111   command_bind_silc("motd", MODULE_NAME, (SIGNAL_FUNC) command_self);
1112   command_bind_silc("close", MODULE_NAME, (SIGNAL_FUNC) command_self);
1113   command_bind_silc("shutdown", MODULE_NAME, (SIGNAL_FUNC) command_self);
1114   command_bind_silc("getkey", MODULE_NAME, (SIGNAL_FUNC) command_self);
1115   command_bind_silc("sconnect", MODULE_NAME, (SIGNAL_FUNC) command_sconnect);
1116   command_bind_silc("file", MODULE_NAME, (SIGNAL_FUNC) command_file);
1117   command_bind_silc("detach", MODULE_NAME, (SIGNAL_FUNC) command_self);
1118   command_bind_silc("watch", MODULE_NAME, (SIGNAL_FUNC) command_self);
1119   command_bind_silc("stats", MODULE_NAME, (SIGNAL_FUNC) command_self);
1120   command_bind_silc("attr", MODULE_NAME, (SIGNAL_FUNC) command_attr);
1121   command_bind_silc("smsg", MODULE_NAME, (SIGNAL_FUNC) command_smsg);
1122
1123   command_set_options("connect", "+silcnet");
1124 }
1125
1126 void silc_server_deinit(void)
1127 {
1128   silc_servers_reconnect_deinit();
1129
1130   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1131   signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
1132   signal_remove("mime-send", (SIGNAL_FUNC)silc_send_mime);
1133   command_unbind("whois", (SIGNAL_FUNC) command_self);
1134   command_unbind("whowas", (SIGNAL_FUNC) command_self);
1135   command_unbind("nick", (SIGNAL_FUNC) command_self);
1136   command_unbind("topic", (SIGNAL_FUNC) command_self);
1137   command_unbind("cmode", (SIGNAL_FUNC) command_self);
1138   command_unbind("cumode", (SIGNAL_FUNC) command_self);
1139   command_unbind("users", (SIGNAL_FUNC) command_self);
1140   command_unbind("list", (SIGNAL_FUNC) command_self);
1141   command_unbind("oper", (SIGNAL_FUNC) command_self);
1142   command_unbind("silcoper", (SIGNAL_FUNC) command_self);
1143   command_unbind("umode", (SIGNAL_FUNC) command_self);
1144   command_unbind("invite", (SIGNAL_FUNC) command_self);
1145   command_unbind("kill", (SIGNAL_FUNC) command_self);
1146   command_unbind("kick", (SIGNAL_FUNC) command_self);
1147   command_unbind("info", (SIGNAL_FUNC) command_self);
1148   command_unbind("ping", (SIGNAL_FUNC) command_self);
1149   command_unbind("motd", (SIGNAL_FUNC) command_self);
1150   command_unbind("ban", (SIGNAL_FUNC) command_self);
1151   command_unbind("close", (SIGNAL_FUNC) command_self);
1152   command_unbind("shutdown", (SIGNAL_FUNC) command_self);
1153   command_unbind("getkey", (SIGNAL_FUNC) command_self);
1154   command_unbind("sconnect", (SIGNAL_FUNC) command_sconnect);
1155   command_unbind("file", (SIGNAL_FUNC) command_file);
1156   command_unbind("detach", (SIGNAL_FUNC) command_self);
1157   command_unbind("watch", (SIGNAL_FUNC) command_self);
1158   command_unbind("stats", (SIGNAL_FUNC) command_self);
1159   command_unbind("attr", (SIGNAL_FUNC) command_attr);
1160   command_unbind("smsg", (SIGNAL_FUNC) command_smsg);
1161 }
1162
1163 void silc_server_free_ftp(SILC_SERVER_REC *server,
1164                           SilcClientEntry client_entry)
1165 {
1166   FtpSession ftp;
1167
1168   silc_dlist_start(server->ftp_sessions);
1169   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
1170     if (ftp->client_entry == client_entry) {
1171       silc_dlist_del(server->ftp_sessions, ftp);
1172       silc_free(ftp->filepath);
1173       silc_free(ftp);
1174     }
1175   }
1176 }
1177
1178 bool silc_term_utf8(void)
1179 {
1180   const char *str;
1181   str = settings_get_str("term_type");
1182   if (str)
1183     if (g_strcasecmp(str, "utf-8") == 0)
1184       return TRUE;
1185   return FALSE;
1186 }