Improved UTF-8 encoding and decoding, improved toolkit doc,
[silc.git] / apps / irssi / src / silc / core / silc-servers.c
1 /*
2   silc-server.c : irssi
3
4   Copyright (C) 2000 - 2001 Timo Sirainen
5                             Pekka Riikonen <priikone@poseidon.pspt.fi>
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 "silc-servers.h"
38 #include "silc-channels.h"
39 #include "silc-queries.h"
40 #include "silc-nicklist.h"
41 #include "window-item-def.h"
42
43 #include "fe-common/core/printtext.h"
44 #include "fe-common/core/fe-channels.h"
45 #include "fe-common/core/keyboard.h"
46 #include "fe-common/silc/module-formats.h"
47
48 #include "silc-commands.h"
49
50 void silc_servers_reconnect_init(void);
51 void silc_servers_reconnect_deinit(void);
52
53 static void silc_send_channel(SILC_SERVER_REC *server,
54                               char *channel, char *msg)
55 {
56   SILC_CHANNEL_REC *rec;
57   
58   rec = silc_channel_find(server, channel);
59   if (rec == NULL || rec->entry == NULL) {
60     cmd_return_error(CMDERR_NOT_JOINED);
61     return;
62   }
63
64   silc_client_send_channel_message(silc_client, server->conn, rec->entry, 
65                                    NULL, SILC_MESSAGE_FLAG_UTF8,
66                                    msg, strlen(msg), TRUE);
67 }
68
69 typedef struct {
70   char *nick;
71   char *msg;
72   SILC_SERVER_REC *server;
73 } PRIVMSG_REC;
74
75 /* Callback function that sends the private message if the client was
76    resolved from the server. */
77
78 static void silc_send_msg_clients(SilcClient client,
79                                   SilcClientConnection conn,
80                                   SilcClientEntry *clients,
81                                   SilcUInt32 clients_count,
82                                   void *context)
83 {
84   PRIVMSG_REC *rec = context;
85   SILC_SERVER_REC *server = rec->server;
86   SilcClientEntry target;
87   char *nickname = NULL;
88
89   if (!clients_count) {
90     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
91               "%s: There is no such client", rec->nick);
92   } else {
93     if (clients_count > 1) {
94       silc_parse_userfqdn(rec->nick, &nickname, NULL);
95
96       /* Find the correct one. The rec->nick might be a formatted nick
97          so this will find the correct one. */
98       clients = silc_client_get_clients_local(silc_client, server->conn, 
99                                               nickname, rec->nick, 
100                                               &clients_count);
101       if (!clients) {
102         printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
103                   "%s: There is no such client", rec->nick);
104         silc_free(nickname);
105         goto out;
106       }
107       silc_free(nickname);
108     }
109
110     target = clients[0];
111
112     /* Still check for exact math for nickname, this compares the
113        real (formatted) nickname and the nick (maybe formatted) that
114        use gave. This is to assure that `nick' does not match 
115        `nick@host'. */
116     if (strcasecmp(rec->nick, clients[0]->nickname)) {
117       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
118                 "%s: There is no such client", rec->nick);
119       goto out;
120     }
121
122     /* Send the private message */
123     silc_client_send_private_message(client, conn, target, 
124                                      SILC_MESSAGE_FLAG_UTF8,
125                                      rec->msg, strlen(rec->msg),
126                                      TRUE);
127   }
128   
129  out:
130   g_free(rec->nick);
131   g_free(rec->msg);
132   g_free(rec);
133 }
134
135 static void silc_send_msg(SILC_SERVER_REC *server, char *nick, char *msg)
136 {
137   PRIVMSG_REC *rec;
138   SilcClientEntry *clients;
139   SilcUInt32 clients_count;
140   char *nickname = NULL;
141
142   if (!silc_parse_userfqdn(nick, &nickname, NULL)) {
143     printformat_module("fe-common/silc", server, NULL,
144                        MSGLEVEL_CRAP, SILCTXT_BAD_NICK, nick);
145     return;
146   }
147
148   /* Find client entry */
149   clients = silc_client_get_clients_local(silc_client, server->conn, 
150                                           nickname, nick, &clients_count);
151   if (!clients) {
152     rec = g_new0(PRIVMSG_REC, 1);
153     rec->nick = g_strdup(nick);
154     rec->msg = g_strdup(msg);
155     rec->server = server;
156
157     /* Could not find client with that nick, resolve it from server. */
158     silc_client_get_clients(silc_client, server->conn,
159                             nickname, NULL, silc_send_msg_clients, rec);
160     silc_free(nickname);
161     return;
162   }
163
164   /* Send the private message directly */
165   silc_free(nickname);
166   silc_client_send_private_message(silc_client, server->conn, 
167                                    clients[0], SILC_MESSAGE_FLAG_UTF8,
168                                    msg, strlen(msg), TRUE);
169 }
170
171 static int isnickflag_func(char flag)
172 {
173   return flag == '@' || flag == '+';
174 }
175
176 static int ischannel_func(SERVER_REC *server, const char *data)
177 {
178   return FALSE;
179 }
180
181 const char *get_nick_flags(void)
182 {
183   return "@\0\0";
184 }
185
186 static void send_message(SILC_SERVER_REC *server, char *target,
187                          char *msg, int target_type)
188 {
189   char *message = NULL;
190   int len;
191
192   g_return_if_fail(server != NULL);
193   g_return_if_fail(target != NULL);
194   g_return_if_fail(msg != NULL);
195
196   if (!silc_term_utf8()) {
197     len = silc_utf8_encoded_len(msg, strlen(msg), SILC_STRING_LANGUAGE);
198     message = silc_calloc(len + 1, sizeof(*message));
199     g_return_if_fail(message != NULL);
200     silc_utf8_encode(msg, strlen(msg), SILC_STRING_LANGUAGE, message, len);
201   }
202
203   if (target_type == SEND_TARGET_CHANNEL)
204     silc_send_channel(server, target, message ? message : msg);
205   else
206     silc_send_msg(server, target, message ? message : msg);
207
208   silc_free(message);
209 }
210
211 static void sig_connected(SILC_SERVER_REC *server)
212 {
213   SilcClientConnection conn;
214   SilcClientConnectionParams params;
215   char file[256];
216   int fd;
217
218   if (!IS_SILC_SERVER(server))
219     return;
220
221   /* Try to read detached session data and use it if found. */
222   memset(&params, 0, sizeof(params));
223   memset(file, 0, sizeof(file));
224   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
225   params.detach_data = silc_file_readfile(file, &params.detach_data_len);
226
227   /* Add connection to the client library */
228   conn = silc_client_add_connection(silc_client, &params,
229                                     server->connrec->address,
230                                     server->connrec->port,
231                                     server);
232   server->conn = conn;
233
234   if (params.detach_data)
235     keyboard_entry_redirect(NULL,
236                             "-- Resuming old session, may take a while ...",
237                             ENTRY_REDIRECT_FLAG_HIDDEN, server);
238
239   silc_free(params.detach_data);
240   unlink(file);
241
242   fd = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
243
244   /* Start key exchange with the server */
245   silc_client_start_key_exchange(silc_client, conn, fd);
246
247   server->ftp_sessions = silc_dlist_init();
248   server->isnickflag = isnickflag_func;
249   server->ischannel = ischannel_func;
250   server->get_nick_flags = get_nick_flags;
251   server->send_message = (void *) send_message;
252 }
253
254 static void sig_disconnected(SILC_SERVER_REC *server)
255 {
256   if (!IS_SILC_SERVER(server))
257     return;
258
259   silc_dlist_uninit(server->ftp_sessions);
260
261   if (server->conn && server->conn->sock != NULL) {
262     silc_client_close_connection(silc_client, server->conn);
263
264     /* SILC closes the handle */
265     g_io_channel_unref(net_sendbuffer_handle(server->handle));
266     net_sendbuffer_destroy(server->handle, FALSE);
267     server->handle = NULL;
268   }
269 }
270
271 SILC_SERVER_REC *silc_server_connect(SILC_SERVER_CONNECT_REC *conn){
272   SILC_SERVER_REC *server;
273
274   g_return_val_if_fail(IS_SILC_SERVER_CONNECT(conn), NULL);
275   if (conn->address == NULL || *conn->address == '\0') 
276     return NULL;
277   if (conn->nick == NULL || *conn->nick == '\0') {
278     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
279               "Cannot connect: nickname is not set");
280     return NULL;
281   }
282
283   server = g_new0(SILC_SERVER_REC, 1);
284   server->chat_type = SILC_PROTOCOL;
285   server->connrec = conn;
286   if (server->connrec->port <= 0) 
287     server->connrec->port = 706;
288
289   server_connect_ref(SERVER_CONNECT(conn));
290
291   if (!server_start_connect((SERVER_REC *) server)) {
292     server_connect_unref(SERVER_CONNECT(conn));
293     g_free(server);
294     return NULL;
295   }
296
297   return server;
298 }
299
300 /* Return a string of all channels in server in server->channels_join() 
301    format */
302
303 char *silc_server_get_channels(SILC_SERVER_REC *server)
304 {
305   GSList *tmp;
306   GString *chans;
307   char *ret;
308
309   g_return_val_if_fail(server != NULL, FALSE);
310
311   chans = g_string_new(NULL);
312   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
313     CHANNEL_REC *channel = tmp->data;
314     
315     g_string_sprintfa(chans, "%s,", channel->name);
316   }
317
318   if (chans->len > 0)
319     g_string_truncate(chans, chans->len-1);
320
321   ret = chans->str;
322   g_string_free(chans, FALSE);
323   
324   return ret;
325 }
326
327 /* Syntaxes of all SILC commands for HELP files (the help file generation
328    will snoop these from here). */
329
330 /* SYNTAX: BAN <channel> [+|-[<nickname>[@<server>[!<username>[@hostname>]]]]] */
331 /* SYNTAX: CMODE <channel> +|-<modes> [{ <arguments>}] */
332 /* SYNTAX: CUMODE <channel> +|-<modes> <nickname>[@<hostname>] */
333 /* SYNTAX: GETKEY <nickname or server name> */
334 /* SYNTAX: INVITE <channel> [<nickname>[@hostname>] */
335 /* SYNTAX: INVITE <channel> [+|-[<nickname>[@<server>[!<username>[@hostname>]]]]] */
336 /* SYNTAX: KEY MSG <nickname> set|unset|list|agreement|negotiate [<arguments>] */
337 /* SYNTAX: KEY CHANNEL <channel> set|unset|list|change [<arguments>] */
338 /* SYNTAX: KICK <channel> <nickname>[@<hostname>] [<comment>] */
339 /* SYNTAX: KILL <nickname>[@<hostname>] [<comment>] */
340 /* SYNTAX: OPER <username> [-pubkey] */
341 /* SYNTAX: SILCOPER <username> [-pubkey] */
342 /* SYNTAX: TOPIC <channel> [<topic>] */
343 /* SYNTAX: UMODE +|-<modes> */
344 /* SYNTAX: WHOIS <nickname>[@<hostname>] [<count>] */
345 /* SYNTAX: WHOWAS <nickname>[@<hostname>] [<count>] */
346 /* SYNTAX: CLOSE <server> [<port>] */
347 /* SYNTAX: SHUTDOWN */
348 /* SYNTAX: MOTD [<server>] */
349 /* SYNTAX: LIST [<channel>] */
350 /* SYNTAX: ME <message> */
351 /* SYNTAX: ACTION <channel> <message> */
352 /* SYNTAX: AWAY [<message>] */
353 /* SYNTAX: INFO [<server>] */
354 /* SYNTAX: NICK <nickname> */
355 /* SYNTAX: NOTICE <message> */
356 /* SYNTAX: PART [<channel>] */
357 /* SYNTAX: PING */
358 /* SYNTAX: SCONNECT <server> [<port>] */
359 /* SYNTAX: USERS <channel> */
360 /* SYNTAX: FILE SEND <filepath> <nickname> [<local IP> [<local port>]] */
361 /* SYNTAX: FILE RECEIVE [<nickname>] */
362 /* SYNTAX: FILE CLOSE [<nickname>] */
363 /* SYNTAX: FILE */
364 /* SYNTAX: JOIN <channel> [<passphrase>] [-cipher <cipher>] [-hmac <hmac>] [-founder] */
365 /* SYNTAX: DETACH */
366 /* SYNTAX: WATCH [<-add | -del> <nickname>] */
367
368 void silc_command_exec(SILC_SERVER_REC *server,
369                        const char *command, const char *args)
370 {
371   SilcUInt32 argc = 0;
372   unsigned char **argv;
373   SilcUInt32 *argv_lens, *argv_types;
374   char *data, *tmpcmd;
375   SilcClientCommand cmd;
376   SilcClientCommandContext ctx;
377
378   g_return_if_fail(server != NULL);
379
380   tmpcmd = g_strdup(command); 
381   g_strup(tmpcmd);
382   cmd = silc_client_command_find(silc_client, tmpcmd);
383   g_free(tmpcmd);
384   if (cmd == NULL)
385     return;
386   
387   /* Now parse all arguments */
388   data = g_strconcat(command, " ", args, NULL);
389   silc_parse_command_line(data, &argv, &argv_lens,
390                           &argv_types, &argc, cmd->max_args);
391   g_free(data);
392
393   /* Allocate command context. This and its internals must be free'd
394      by the command routine receiving it. */
395   ctx = silc_client_command_alloc();
396   ctx->client = silc_client;
397   ctx->conn = server->conn;
398   ctx->command = cmd;
399   ctx->argc = argc;
400   ctx->argv = argv;
401   ctx->argv_lens = argv_lens;
402   ctx->argv_types = argv_types;
403   
404   /* Execute command */
405   silc_client_command_call(cmd, ctx);
406 }
407
408 /* Generic command function to call any SILC command directly. */
409
410 static void command_self(const char *data, SILC_SERVER_REC *server,
411                          WI_ITEM_REC *item)
412 {
413   CMD_SILC_SERVER(server);
414
415   if (!IS_SILC_SERVER(server) || !server->connected) {
416     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Not connected to server");
417     return;
418   }
419
420   if (IS_SILC_CHANNEL(item)) {
421     SILC_CHANNEL_REC *chanrec;
422     chanrec = silc_channel_find(server, item->name);
423     if (chanrec)
424       server->conn->current_channel = chanrec->entry;
425   }
426
427   silc_command_exec(server, current_command, data);
428   signal_stop();
429 }
430
431 /* SCONNECT command.  Calls actually SILC's CONNECT command since Irssi
432    has CONNECT command for other purposes. */
433
434 static void command_sconnect(const char *data, SILC_SERVER_REC *server)
435 {
436   CMD_SILC_SERVER(server);
437   if (!IS_SILC_SERVER(server) || !server->connected) {
438     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Not connected to server");
439     return;
440   }
441
442   silc_command_exec(server, "CONNECT", data);
443   signal_stop();
444 }
445
446 /* FILE command */
447
448 SILC_TASK_CALLBACK(silc_client_file_close_later)
449 {
450   FtpSession ftp = (FtpSession)context;
451
452   SILC_LOG_DEBUG(("Start"));
453
454   silc_client_file_close(silc_client, ftp->conn, ftp->session_id);
455   silc_free(ftp->filepath);
456   silc_free(ftp);
457 }
458
459 static void silc_client_file_monitor(SilcClient client,
460                                      SilcClientConnection conn,
461                                      SilcClientMonitorStatus status,
462                                      SilcClientFileError error,
463                                      SilcUInt64 offset,
464                                      SilcUInt64 filesize,
465                                      SilcClientEntry client_entry,
466                                      SilcUInt32 session_id,
467                                      const char *filepath,
468                                      void *context)
469 {
470   SILC_SERVER_REC *server = (SILC_SERVER_REC *)context;
471   FtpSession ftp;
472   char fsize[32];
473
474   snprintf(fsize, sizeof(fsize) - 1, "%llu", ((filesize + 1023) / 1024));
475
476   silc_dlist_start(server->ftp_sessions);
477   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
478     if (ftp->session_id == session_id) {
479       if (!ftp->filepath && filepath)
480         ftp->filepath = strdup(filepath);
481       break;
482     }
483   }
484
485   if (ftp == SILC_LIST_END)
486     return;
487
488   if (status == SILC_CLIENT_FILE_MONITOR_ERROR) {
489     if (error == SILC_CLIENT_FILE_NO_SUCH_FILE)
490       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
491                          SILCTXT_FILE_ERROR_NO_SUCH_FILE, 
492                          client_entry->nickname, 
493                          filepath ? filepath : "[N/A]");
494     else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED)
495       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
496                          SILCTXT_FILE_ERROR_PERMISSION_DENIED, 
497                          client_entry->nickname);
498     else
499       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
500                          SILCTXT_FILE_ERROR, client_entry->nickname);
501     silc_schedule_task_add(silc_client->schedule, 0,
502                            silc_client_file_close_later, ftp,
503                            1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
504     if (ftp == server->current_session)
505       server->current_session = NULL;
506     silc_dlist_del(server->ftp_sessions, ftp);
507   }
508
509   if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT) {
510     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
511                        SILCTXT_FILE_KEY_EXCHANGE, client_entry->nickname);
512   }
513
514   /* Save some transmission data */
515   if (offset && filesize) {
516     unsigned long delta = time(NULL) - ftp->starttime;
517
518     ftp->percent = ((double)offset / (double)filesize) * (double)100.0;
519     if (delta)
520       ftp->kps = (double)((offset / (double)delta) + 1023) / (double)1024;
521     else
522       ftp->kps = (double)(offset + 1023) / (double)1024;
523     ftp->offset = offset;
524     ftp->filesize = filesize;
525   }
526
527   if (status == SILC_CLIENT_FILE_MONITOR_SEND) {
528     if (offset == 0) {
529       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
530                          SILCTXT_FILE_TRANSMIT, filepath, fsize,
531                          client_entry->nickname);
532       ftp->starttime = time(NULL);
533     }
534     if (offset == filesize) {
535       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
536                          SILCTXT_FILE_TRANSMITTED, filepath, fsize,
537                          client_entry->nickname, ftp->kps);
538       silc_schedule_task_add(silc_client->schedule, 0,
539                              silc_client_file_close_later, ftp,
540                              1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
541       if (ftp == server->current_session)
542         server->current_session = NULL;
543       silc_dlist_del(server->ftp_sessions, ftp);
544     }
545   }
546
547   if (status == SILC_CLIENT_FILE_MONITOR_RECEIVE) {
548     if (offset == 0) {
549       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
550                          SILCTXT_FILE_RECEIVE, filepath, fsize,
551                          client_entry->nickname);
552       ftp->starttime = time(NULL);
553     }
554
555     if (offset == filesize) {
556       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
557                          SILCTXT_FILE_RECEIVED, filepath, fsize,
558                          client_entry->nickname, ftp->kps);
559       silc_schedule_task_add(silc_client->schedule, 0,
560                              silc_client_file_close_later, ftp,
561                              1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
562       if (ftp == server->current_session)
563         server->current_session = NULL;
564       silc_dlist_del(server->ftp_sessions, ftp);
565     }
566   }
567 }
568
569 typedef struct {
570   SILC_SERVER_REC *server;
571   char *data;
572   char *nick;
573   WI_ITEM_REC *item;
574 } *FileGetClients;
575
576 static void silc_client_command_file_get_clients(SilcClient client,
577                                                  SilcClientConnection conn,
578                                                  SilcClientEntry *clients,
579                                                  SilcUInt32 clients_count,
580                                                  void *context)
581 {
582   FileGetClients internal = (FileGetClients)context;
583
584   if (!clients) {
585     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
586               internal->nick);
587     silc_free(internal->data);
588     silc_free(internal->nick);
589     silc_free(internal);
590     return;
591   }
592
593   signal_emit("command file", 3, internal->data, internal->server,
594               internal->item);
595
596   silc_free(internal->data);
597   silc_free(internal->nick);
598   silc_free(internal);
599 }
600
601 static void command_file(const char *data, SILC_SERVER_REC *server,
602                          WI_ITEM_REC *item)
603 {
604   SilcClientConnection conn;
605   SilcClientEntry *entrys, client_entry;
606   SilcClientFileError ret;
607   SilcUInt32 entry_count;
608   char *nickname = NULL, *tmp;
609   unsigned char **argv;
610   SilcUInt32 argc;
611   SilcUInt32 *argv_lens, *argv_types;
612   int type = 0;
613   FtpSession ftp;
614   char *local_ip = NULL;
615   SilcUInt32 local_port = 0;
616   SilcUInt32 session_id;
617
618   CMD_SILC_SERVER(server);
619   if (!server || !IS_SILC_SERVER(server) || !server->connected)
620     cmd_return_error(CMDERR_NOT_CONNECTED);
621
622   conn = server->conn;
623
624   /* Now parse all arguments */
625   tmp = g_strconcat("FILE", " ", data, NULL);
626   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 6);
627   g_free(tmp);
628
629   if (argc == 1)
630     type = 4;
631
632   if (argc >= 2) {
633     if (!strcasecmp(argv[1], "send"))
634       type = 1;
635     if (!strcasecmp(argv[1], "receive"))
636       type = 2;
637     if (!strcasecmp(argv[1], "close"))
638       type = 3;
639   }
640   
641   if (type == 0)
642     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
643
644   switch (type) {
645   case 1:
646     if (argc < 4)
647       cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
648
649     /* Parse the typed nickname. */
650     if (!silc_parse_userfqdn(argv[3], &nickname, NULL)) {
651       printformat_module("fe-common/silc", server, NULL,
652                          MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[3]);
653       goto out;
654     }
655     
656     /* Find client entry */
657     entrys = silc_client_get_clients_local(silc_client, conn, nickname,
658                                            argv[3], &entry_count);
659     if (!entrys) {
660       FileGetClients inter = silc_calloc(1, sizeof(*inter));
661       inter->server = server;
662       inter->data = strdup(data);
663       inter->nick = strdup(nickname);
664       inter->item = item;
665       silc_client_get_clients(silc_client, conn, nickname, argv[3],
666                               silc_client_command_file_get_clients, inter);
667       goto out;
668     }
669     client_entry = entrys[0];
670     silc_free(entrys);
671
672     if (argc >= 5)
673       local_ip = argv[4];
674     if (argc >= 6)
675       local_port = atoi(argv[5]);
676
677     ret = 
678       silc_client_file_send(silc_client, conn, silc_client_file_monitor, 
679                             server, local_ip, local_port, 
680                             client_entry, argv[2], &session_id);
681     if (ret == SILC_CLIENT_FILE_OK) {
682       ftp = silc_calloc(1, sizeof(*ftp));
683       ftp->session_id = session_id;
684
685       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
686                          SILCTXT_FILE_SEND, client_entry->nickname,
687                          argv[2]);
688
689       ftp->client_entry = client_entry;
690       ftp->filepath = strdup(argv[2]);
691       ftp->conn = conn;
692       ftp->send = TRUE;
693       silc_dlist_add(server->ftp_sessions, ftp);
694       server->current_session = ftp;
695     } else {
696       if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
697         printformat_module("fe-common/silc", server, NULL,
698                            MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
699                            client_entry->nickname);
700       if (ret == SILC_CLIENT_FILE_NO_SUCH_FILE)
701         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
702                            SILCTXT_FILE_ERROR_NO_SUCH_FILE, 
703                            client_entry->nickname, argv[2]);
704     }
705
706     break;
707
708   case 2:
709     /* Parse the typed nickname. */
710     if (argc >= 3) {
711       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
712         printformat_module("fe-common/silc", server, NULL,
713                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
714         goto out;
715       }
716     
717       /* Find client entry */
718       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
719                                              argv[2], &entry_count);
720       if (!entrys) {
721         FileGetClients inter = silc_calloc(1, sizeof(*inter));
722         inter->server = server;
723         inter->data = strdup(data);
724         inter->nick = strdup(nickname);
725         inter->item = item;
726         silc_client_get_clients(silc_client, conn, nickname, argv[2],
727                                 silc_client_command_file_get_clients, inter);
728         goto out;
729       }
730       client_entry = entrys[0];
731       silc_free(entrys);
732     } else {
733       if (!server->current_session) {
734         printformat_module("fe-common/silc", server, NULL,
735                            MSGLEVEL_CRAP, SILCTXT_FILE_NA);
736         goto out;
737       }
738
739       ret = silc_client_file_receive(silc_client, conn, 
740                                      silc_client_file_monitor, server, NULL,
741                                      server->current_session->session_id);
742       if (ret != SILC_CLIENT_FILE_OK) {
743         if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
744           printformat_module("fe-common/silc", server, NULL,
745                              MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
746                              server->current_session->client_entry->nickname);
747         else
748           printformat_module("fe-common/silc", server, NULL,
749                              MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
750                              server->current_session->client_entry->nickname);
751       }
752
753       goto out;
754     }
755
756     silc_dlist_start(server->ftp_sessions);
757     while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
758       if (ftp->client_entry == client_entry && !ftp->filepath) {
759         ret = silc_client_file_receive(silc_client, conn, 
760                                        silc_client_file_monitor, server,
761                                        NULL, ftp->session_id);
762         if (ret != SILC_CLIENT_FILE_OK) {
763           if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
764             printformat_module("fe-common/silc", server, NULL,
765                                MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
766                                client_entry->nickname);
767           else
768             printformat_module("fe-common/silc", server, NULL,
769                                MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
770                                client_entry->nickname);
771         }
772         break;
773       }
774     }
775
776     if (ftp == SILC_LIST_END) {
777       printformat_module("fe-common/silc", server, NULL,
778                          MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
779                          client_entry->nickname);
780       goto out;
781     }
782     break;
783
784   case 3:
785     /* Parse the typed nickname. */
786     if (argc >= 3) {
787       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
788         printformat_module("fe-common/silc", server, NULL,
789                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
790         goto out;
791       }
792     
793       /* Find client entry */
794       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
795                                              argv[2], &entry_count);
796       if (!entrys) {
797         FileGetClients inter = silc_calloc(1, sizeof(*inter));
798         inter->server = server;
799         inter->data = strdup(data);
800         inter->nick = strdup(nickname);
801         inter->item = item;
802         silc_client_get_clients(silc_client, conn, nickname, argv[2],
803                                 silc_client_command_file_get_clients, inter);
804         goto out;
805       }
806       client_entry = entrys[0];
807       silc_free(entrys);
808     } else {
809       if (!server->current_session) {
810         printformat_module("fe-common/silc", server, NULL,
811                            MSGLEVEL_CRAP, SILCTXT_FILE_NA);
812         goto out;
813       }
814  
815       silc_client_file_close(silc_client, conn, 
816                              server->current_session->session_id);
817       printformat_module("fe-common/silc", server, NULL,
818                          MSGLEVEL_CRAP, SILCTXT_FILE_CLOSED,
819                          server->current_session->client_entry->nickname,
820                          server->current_session->filepath ?
821                          server->current_session->filepath : "[N/A]");
822       silc_dlist_del(server->ftp_sessions, server->current_session);
823       silc_free(server->current_session->filepath);
824       silc_free(server->current_session);
825       server->current_session = NULL;
826       goto out;
827     }
828
829     silc_dlist_start(server->ftp_sessions);
830     while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
831       if (ftp->client_entry == client_entry) {
832         silc_client_file_close(silc_client, conn, ftp->session_id);
833         printformat_module("fe-common/silc", server, NULL,
834                            MSGLEVEL_CRAP, SILCTXT_FILE_CLOSED,
835                            client_entry->nickname,
836                            ftp->filepath ? ftp->filepath : "[N/A]");
837         if (ftp == server->current_session)
838           server->current_session = NULL;
839         silc_dlist_del(server->ftp_sessions, ftp);
840         silc_free(ftp->filepath);
841         silc_free(ftp);
842         break;
843       }
844     }
845
846     if (ftp == SILC_LIST_END) {
847       printformat_module("fe-common/silc", server, NULL,
848                          MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
849                          client_entry->nickname);
850       goto out;
851     }
852     break;
853
854   case 4:
855
856     if (!silc_dlist_count(server->ftp_sessions)) {
857       printformat_module("fe-common/silc", server, NULL,
858                          MSGLEVEL_CRAP, SILCTXT_FILE_NA);
859       goto out;
860     }
861
862     printformat_module("fe-common/silc", server, NULL,
863                        MSGLEVEL_CRAP, SILCTXT_FILE_SHOW_HEADER);
864
865     silc_dlist_start(server->ftp_sessions);
866     while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
867       printformat_module("fe-common/silc", server, NULL,
868                          MSGLEVEL_CRAP, SILCTXT_FILE_SHOW_LINE,
869                          ftp->client_entry->nickname, 
870                          ftp->send ? "send" : "receive",
871                          (SilcUInt32)(ftp->offset + 1023) / 1024,
872                          (SilcUInt32)(ftp->filesize + 1023) / 1024,
873                          ftp->percent, ftp->kps,
874                          ftp->filepath ? ftp->filepath : "[N/A]");
875     }
876
877     break;
878
879   default:
880     break;
881   }
882
883  out:
884   silc_free(nickname);
885 }
886
887 void silc_server_init(void)
888 {
889   silc_servers_reconnect_init();
890
891   signal_add_first("server connected", (SIGNAL_FUNC) sig_connected);
892   signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
893   command_bind_silc("whois", MODULE_NAME, (SIGNAL_FUNC) command_self);
894   command_bind_silc("whowas", MODULE_NAME, (SIGNAL_FUNC) command_self);
895   command_bind_silc("nick", MODULE_NAME, (SIGNAL_FUNC) command_self);
896   command_bind_silc("topic", MODULE_NAME, (SIGNAL_FUNC) command_self);
897   command_bind_silc("cmode", MODULE_NAME, (SIGNAL_FUNC) command_self);
898   command_bind_silc("cumode", MODULE_NAME, (SIGNAL_FUNC) command_self);
899   command_bind_silc("users", MODULE_NAME, (SIGNAL_FUNC) command_self);
900   command_bind_silc("list", MODULE_NAME, (SIGNAL_FUNC) command_self);
901   command_bind_silc("ban", MODULE_NAME, (SIGNAL_FUNC) command_self);
902   command_bind_silc("oper", MODULE_NAME, (SIGNAL_FUNC) command_self);
903   command_bind_silc("silcoper", MODULE_NAME, (SIGNAL_FUNC) command_self);
904   command_bind_silc("umode", MODULE_NAME, (SIGNAL_FUNC) command_self);
905   command_bind_silc("invite", MODULE_NAME, (SIGNAL_FUNC) command_self);
906   command_bind_silc("kill", MODULE_NAME, (SIGNAL_FUNC) command_self);
907   command_bind_silc("kick", MODULE_NAME, (SIGNAL_FUNC) command_self);
908   command_bind_silc("info", MODULE_NAME, (SIGNAL_FUNC) command_self);
909   command_bind_silc("ping", MODULE_NAME, (SIGNAL_FUNC) command_self);
910   command_bind_silc("motd", MODULE_NAME, (SIGNAL_FUNC) command_self);
911   command_bind_silc("close", MODULE_NAME, (SIGNAL_FUNC) command_self);
912   command_bind_silc("shutdown", MODULE_NAME, (SIGNAL_FUNC) command_self);
913   command_bind_silc("getkey", MODULE_NAME, (SIGNAL_FUNC) command_self);
914   command_bind_silc("sconnect", MODULE_NAME, (SIGNAL_FUNC) command_sconnect);
915   command_bind_silc("file", MODULE_NAME, (SIGNAL_FUNC) command_file);
916   command_bind_silc("detach", MODULE_NAME, (SIGNAL_FUNC) command_self);
917   command_bind_silc("watch", MODULE_NAME, (SIGNAL_FUNC) command_self);
918
919   command_set_options("connect", "+silcnet");
920 }
921
922 void silc_server_deinit(void)
923 {
924   silc_servers_reconnect_deinit();
925
926   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
927   signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
928   command_unbind("whois", (SIGNAL_FUNC) command_self);
929   command_unbind("whowas", (SIGNAL_FUNC) command_self);
930   command_unbind("nick", (SIGNAL_FUNC) command_self);
931   command_unbind("topic", (SIGNAL_FUNC) command_self);
932   command_unbind("cmode", (SIGNAL_FUNC) command_self);
933   command_unbind("cumode", (SIGNAL_FUNC) command_self);
934   command_unbind("users", (SIGNAL_FUNC) command_self);
935   command_unbind("list", (SIGNAL_FUNC) command_self);
936   command_unbind("oper", (SIGNAL_FUNC) command_self);
937   command_unbind("silcoper", (SIGNAL_FUNC) command_self);
938   command_unbind("umode", (SIGNAL_FUNC) command_self);
939   command_unbind("invite", (SIGNAL_FUNC) command_self);
940   command_unbind("kill", (SIGNAL_FUNC) command_self);
941   command_unbind("kick", (SIGNAL_FUNC) command_self);
942   command_unbind("info", (SIGNAL_FUNC) command_self);
943   command_unbind("ping", (SIGNAL_FUNC) command_self);
944   command_unbind("motd", (SIGNAL_FUNC) command_self);
945   command_unbind("ban", (SIGNAL_FUNC) command_self);
946   command_unbind("close", (SIGNAL_FUNC) command_self);
947   command_unbind("shutdown", (SIGNAL_FUNC) command_self);
948   command_unbind("getkey", (SIGNAL_FUNC) command_self);
949   command_unbind("sconnect", (SIGNAL_FUNC) command_sconnect);
950   command_unbind("file", (SIGNAL_FUNC) command_file);
951   command_unbind("detach", (SIGNAL_FUNC) command_self);
952   command_unbind("watch", (SIGNAL_FUNC) command_self);
953 }
954
955 void silc_server_free_ftp(SILC_SERVER_REC *server,
956                           SilcClientEntry client_entry)
957 {
958   FtpSession ftp;
959
960   silc_dlist_start(server->ftp_sessions);
961   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
962     if (ftp->client_entry == client_entry) {
963       silc_dlist_del(server->ftp_sessions, ftp);
964       silc_free(ftp->filepath);
965       silc_free(ftp);
966     }
967   }
968 }
969
970 bool silc_term_utf8(void)
971 {
972   const char *str;
973   str = settings_get_str("term_type");
974   if (str)
975     if (g_strcasecmp(str, "utf-8") == 0)
976       return TRUE;
977   return FALSE;
978 }