updates.
[silc.git] / apps / irssi / src / silc / core / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20
21 #include "module.h"
22 #include "chat-protocols.h"
23 #include "args.h"
24
25 #include "chatnets.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
32
33 #include "signals.h"
34 #include "levels.h"
35 #include "settings.h"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
38 #include "fe-common/core/keyboard.h"
39 #include "fe-common/silc/module-formats.h"
40
41 static void 
42 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
43                                 SilcSocketType conn_type, unsigned char *pk, 
44                                 uint32 pk_len, SilcSKEPKType pk_type,
45                                 SilcVerifyPublicKey completion, void *context);
46
47 void silc_say(SilcClient client, SilcClientConnection conn,
48               SilcClientMessageType type, char *msg, ...)
49 {
50   SILC_SERVER_REC *server;
51   va_list va;
52   char *str;
53
54   server = conn == NULL ? NULL : conn->context;
55   
56   va_start(va, msg);
57   str = g_strdup_vprintf(msg, va);
58   printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
59   g_free(str);
60   va_end(va);
61 }
62
63 void silc_say_error(char *msg, ...)
64 {
65   va_list va;
66   char *str;
67
68   va_start(va, msg);
69   str = g_strdup_vprintf(msg, va);
70   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
71
72   g_free(str);
73   va_end(va);
74 }
75
76 /* Message for a channel. The `sender' is the nickname of the sender 
77    received in the packet. The `channel_name' is the name of the channel. */
78
79 void silc_channel_message(SilcClient client, SilcClientConnection conn,
80                           SilcClientEntry sender, SilcChannelEntry channel,
81                           SilcMessageFlags flags, char *msg)
82 {
83   SILC_SERVER_REC *server;
84   SILC_NICK_REC *nick;
85   SILC_CHANNEL_REC *chanrec;
86   
87   SILC_LOG_DEBUG(("Start"));
88
89   server = conn == NULL ? NULL : conn->context;
90   chanrec = silc_channel_find_entry(server, channel);
91   if (!chanrec)
92     return;
93   
94   nick = silc_nicklist_find(chanrec, sender);
95   if (!nick) {
96     /* We didn't find client but it clearly exists, add it. */
97     SilcChannelUser chu;
98
99     silc_list_start(channel->clients);
100     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
101       if (chu->client == sender) {
102         nick = silc_nicklist_insert(chanrec, chu, FALSE);
103         break;
104       }
105     }
106   }
107
108   if (flags & SILC_MESSAGE_FLAG_ACTION)
109     printformat_module("fe-common/silc", server, channel->channel_name,
110                        MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, 
111                        nick == NULL ? "[<unknown>]" : nick->nick, msg);
112   else if (flags & SILC_MESSAGE_FLAG_NOTICE)
113     printformat_module("fe-common/silc", server, channel->channel_name,
114                        MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, 
115                        nick == NULL ? "[<unknown>]" : nick->nick, msg);
116   else
117     signal_emit("message public", 6, server, msg,
118                 nick == NULL ? "[<unknown>]" : nick->nick,
119                 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
120                 chanrec->name, nick);
121 }
122
123 /* Private message to the client. The `sender' is the nickname of the
124    sender received in the packet. */
125
126 void silc_private_message(SilcClient client, SilcClientConnection conn,
127                           SilcClientEntry sender, SilcMessageFlags flags,
128                           char *msg)
129 {
130   SILC_SERVER_REC *server;
131   char userhost[256];
132   
133   SILC_LOG_DEBUG(("Start"));
134
135   server = conn == NULL ? NULL : conn->context;
136   memset(userhost, 0, sizeof(userhost));
137   if (sender->username)
138     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
139              sender->username, sender->hostname);
140   signal_emit("message private", 4, server, msg,
141               sender->nickname ? sender->nickname : "[<unknown>]",
142               sender->username ? userhost : NULL);
143 }
144
145 /* Notify message to the client. The notify arguments are sent in the
146    same order as servers sends them. The arguments are same as received
147    from the server except for ID's.  If ID is received application receives
148    the corresponding entry to the ID. For example, if Client ID is received
149    application receives SilcClientEntry.  Also, if the notify type is
150    for channel the channel entry is sent to application (even if server
151    does not send it). */
152
153 typedef struct {
154   int type;
155   const char *name;
156 } NOTIFY_REC;
157
158 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
159 static NOTIFY_REC notifies[] = {
160   { SILC_NOTIFY_TYPE_NONE,              NULL },
161   { SILC_NOTIFY_TYPE_INVITE,            "invite" },
162   { SILC_NOTIFY_TYPE_JOIN,              "join" },
163   { SILC_NOTIFY_TYPE_LEAVE,             "leave" },
164   { SILC_NOTIFY_TYPE_SIGNOFF,           "signoff" },
165   { SILC_NOTIFY_TYPE_TOPIC_SET,         "topic" },
166   { SILC_NOTIFY_TYPE_NICK_CHANGE,       "nick" },
167   { SILC_NOTIFY_TYPE_CMODE_CHANGE,      "cmode" },
168   { SILC_NOTIFY_TYPE_CUMODE_CHANGE,     "cumode" },
169   { SILC_NOTIFY_TYPE_MOTD,              "motd" },
170   { SILC_NOTIFY_TYPE_CHANNEL_CHANGE,    "channel_change" },
171   { SILC_NOTIFY_TYPE_SERVER_SIGNOFF,    "server_signoff" },
172   { SILC_NOTIFY_TYPE_KICKED,            "kick" },
173   { SILC_NOTIFY_TYPE_KILLED,            "kill" },
174   { SILC_NOTIFY_TYPE_UMODE_CHANGE,      "umode" },
175   { SILC_NOTIFY_TYPE_BAN,               "ban" },
176 };
177
178 void silc_notify(SilcClient client, SilcClientConnection conn,
179                  SilcNotifyType type, ...)
180 {
181   SILC_SERVER_REC *server;
182   va_list va;
183   
184   SILC_LOG_DEBUG(("Start"));
185
186   server = conn == NULL ? NULL : conn->context;
187   va_start(va, type);
188   
189   if (type == SILC_NOTIFY_TYPE_NONE) {
190     /* Some generic notice from server */
191     printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
192   } else if (type < MAX_NOTIFY) {
193     /* Send signal about the notify event */
194     char signal[50];
195     g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
196     signal_emit(signal, 2, server, va);
197   } else {
198     /* Unknown notify */
199     printformat_module("fe-common/silc", server, NULL,
200                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
201   }
202
203   va_end(va);
204 }
205
206 /* Called to indicate that connection was either successfully established
207    or connecting failed.  This is also the first time application receives
208    the SilcClientConnection objecet which it should save somewhere. */
209
210 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
211 {
212   SILC_SERVER_REC *server = conn->context;
213
214   if (!server && !success) {
215     silc_client_close_connection(client, NULL, conn);
216     return;
217   }
218
219   if (success) {
220     server->connected = TRUE;
221     signal_emit("event connected", 1, server);
222   } else {
223     server->connection_lost = TRUE;
224     server->conn->context = NULL;
225     server_disconnect(SERVER(server));
226   }
227 }
228
229 /* Called to indicate that connection was disconnected to the server. */
230
231 void silc_disconnect(SilcClient client, SilcClientConnection conn)
232 {
233   SILC_SERVER_REC *server = conn->context;
234
235   SILC_LOG_DEBUG(("Start"));
236
237   if (server->conn) {
238     nicklist_rename_unique(SERVER(server),
239                            server->conn->local_entry, server->nick,
240                            server->conn->local_entry, 
241                            silc_client->username);
242     silc_change_nick(server, silc_client->username);
243   }
244
245   server->conn->context = NULL;
246   server->conn = NULL;
247   server->connection_lost = TRUE;
248   server_disconnect(SERVER(server));
249 }
250
251 /* Command handler. This function is called always in the command function.
252    If error occurs it will be called as well. `conn' is the associated
253    client connection. `cmd_context' is the command context that was
254    originally sent to the command. `success' is FALSE if error occured
255    during command. `command' is the command being processed. It must be
256    noted that this is not reply from server. This is merely called just
257    after application has called the command. Just to tell application
258    that the command really was processed. */
259
260 void silc_command(SilcClient client, SilcClientConnection conn, 
261                   SilcClientCommandContext cmd_context, int success,
262                   SilcCommand command)
263 {
264   SILC_SERVER_REC *server = conn->context;
265
266   SILC_LOG_DEBUG(("Start"));
267
268   if (!success)
269     return;
270
271   switch(command) {
272   case SILC_COMMAND_INVITE:
273     printformat_module("fe-common/silc", server, NULL,
274                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
275                        cmd_context->argv[2], 
276                        (cmd_context->argv[1][0] == '*' ?
277                         (char *)conn->current_channel->channel_name :
278                         (char *)cmd_context->argv[1]));
279     break;
280   default:
281     break;
282   }
283 }
284
285 /* Client info resolving callback when JOIN command reply is received.
286    This will cache all users on the channel. */
287
288 static void silc_client_join_get_users(SilcClient client,
289                                        SilcClientConnection conn,
290                                        SilcClientEntry *clients,
291                                        uint32 clients_count,
292                                        void *context)
293 {
294   SilcChannelEntry channel = (SilcChannelEntry)context;
295   SilcChannelUser chu;
296   SILC_SERVER_REC *server = conn->context;
297   SILC_CHANNEL_REC *chanrec;
298   SilcClientEntry founder = NULL;
299   NICK_REC *ownnick;
300
301   if (!clients)
302     return;
303
304   chanrec = silc_channel_find(server, channel->channel_name);
305   if (chanrec == NULL)
306     return;
307
308   silc_list_start(channel->clients);
309   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
310     if (!chu->client->nickname)
311       continue;
312     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
313       founder = chu->client;
314     silc_nicklist_insert(chanrec, chu, FALSE);
315   }
316
317   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
318   nicklist_set_own(CHANNEL(chanrec), ownnick);
319   signal_emit("channel joined", 1, chanrec);
320
321   if (chanrec->topic)
322     printformat_module("fe-common/silc", server, channel->channel_name,
323                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
324                        channel->channel_name, chanrec->topic);
325
326   fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
327
328   if (founder) {
329     if (founder == conn->local_entry)
330       printformat_module("fe-common/silc", 
331                          server, channel->channel_name, MSGLEVEL_CRAP,
332                          SILCTXT_CHANNEL_FOUNDER_YOU,
333                          channel->channel_name);
334     else
335       printformat_module("fe-common/silc", 
336                          server, channel->channel_name, MSGLEVEL_CRAP,
337                          SILCTXT_CHANNEL_FOUNDER,
338                          channel->channel_name, founder->nickname);
339   }
340 }
341
342 typedef struct {
343   SilcClient client;
344   SilcClientConnection conn;
345   void *entry;
346   SilcIdType id_type;
347   char *fingerprint;
348 } *GetkeyContext;
349
350 void silc_getkey_cb(bool success, void *context)
351 {
352   GetkeyContext getkey = (GetkeyContext)context;
353   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
354   char *name = (getkey->id_type == SILC_ID_CLIENT ? 
355                 ((SilcClientEntry)getkey->entry)->nickname :
356                 ((SilcServerEntry)getkey->entry)->server_name);
357
358   if (success) {
359     printformat_module("fe-common/silc", NULL, NULL,
360                        MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
361   } else {
362     printformat_module("fe-common/silc", NULL, NULL,
363                        MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
364   }
365
366   silc_free(getkey->fingerprint);
367   silc_free(getkey);
368 }
369
370 /* Command reply handler. This function is called always in the command reply
371    function. If error occurs it will be called as well. Normal scenario
372    is that it will be called after the received command data has been parsed
373    and processed. The function is used to pass the received command data to
374    the application. 
375
376    `conn' is the associated client connection. `cmd_payload' is the command
377    payload data received from server and it can be ignored. It is provided
378    if the application would like to re-parse the received command data,
379    however, it must be noted that the data is parsed already by the library
380    thus the payload can be ignored. `success' is FALSE if error occured.
381    In this case arguments are not sent to the application. `command' is the
382    command reply being processed. The function has variable argument list
383    and each command defines the number and type of arguments it passes to the
384    application (on error they are not sent). */
385
386 void 
387 silc_command_reply(SilcClient client, SilcClientConnection conn,
388                    SilcCommandPayload cmd_payload, int success,
389                    SilcCommand command, SilcCommandStatus status, ...)
390
391 {
392   SILC_SERVER_REC *server = conn->context;
393   SILC_CHANNEL_REC *chanrec;
394   va_list vp;
395
396   va_start(vp, status);
397
398   SILC_LOG_DEBUG(("Start"));
399
400   switch(command) {
401   case SILC_COMMAND_WHOIS:
402     {
403       char buf[1024], *nickname, *username, *realname, *nick;
404       uint32 idle, mode;
405       SilcBuffer channels;
406       SilcClientEntry client_entry;
407       
408       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
409           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
410         char *tmp;
411         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
412                                          3, NULL);
413         if (tmp)
414           silc_say_error("%s: %s", tmp, 
415                          silc_client_command_status_message(status));
416         break;
417       }
418       
419       if (!success)
420         return;
421       
422       client_entry = va_arg(vp, SilcClientEntry);
423       nickname = va_arg(vp, char *);
424       username = va_arg(vp, char *);
425       realname = va_arg(vp, char *);
426       channels = va_arg(vp, SilcBuffer);
427       mode = va_arg(vp, uint32);
428       idle = va_arg(vp, uint32);
429       
430       silc_parse_userfqdn(nickname, &nick, NULL);
431       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
432                          SILCTXT_WHOIS_USERINFO, nickname, 
433                          client_entry->username, client_entry->hostname,
434                          nick, client_entry->nickname);
435       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
436                          SILCTXT_WHOIS_REALNAME, realname);
437       silc_free(nick);
438
439       if (channels) {
440         SilcDList list = silc_channel_payload_parse_list(channels);
441         if (list) {
442           SilcChannelPayload entry;
443           memset(buf, 0, sizeof(buf));
444           silc_dlist_start(list);
445           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
446             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
447             uint32 name_len;
448             char *name = silc_channel_get_name(entry, &name_len);
449             
450             if (m)
451               strncat(buf, m, strlen(m));
452             strncat(buf, name, name_len);
453             strncat(buf, " ", 1);
454             silc_free(m);
455           }
456
457           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
458                              SILCTXT_WHOIS_CHANNELS, buf);
459           silc_channel_payload_list_free(list);
460         }
461       }
462       
463       if (mode) {
464         memset(buf, 0, sizeof(buf));
465
466         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
467             (mode & SILC_UMODE_ROUTER_OPERATOR)) {
468           strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
469                  "Server Operator " :
470                  (mode & SILC_UMODE_ROUTER_OPERATOR) ?
471                  "SILC Operator " : "[Unknown mode] ");
472         }
473         if (mode & SILC_UMODE_GONE)
474           strcat(buf, "away");
475
476         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
477                            SILCTXT_WHOIS_MODES, buf);
478       }
479       
480       if (idle && nickname) {
481         memset(buf, 0, sizeof(buf));
482         snprintf(buf, sizeof(buf) - 1, "%lu %s",
483                  idle > 60 ? (idle / 60) : idle,
484                  idle > 60 ? "minutes" : "seconds");
485
486         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
487                            SILCTXT_WHOIS_IDLE, buf);
488       }
489     }
490     break;
491     
492   case SILC_COMMAND_WHOWAS:
493     {
494       char *nickname, *username, *realname;
495       
496       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
497           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
498         char *tmp;
499         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
500                                          3, NULL);
501         if (tmp)
502           silc_say_error("%s: %s", tmp, 
503                          silc_client_command_status_message(status));
504         break;
505       }
506       
507       if (!success)
508         return;
509       
510       (void)va_arg(vp, SilcClientEntry);
511       nickname = va_arg(vp, char *);
512       username = va_arg(vp, char *);
513       realname = va_arg(vp, char *);
514       
515       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
516                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
517                          realname ? realname : "");
518     }
519     break;
520     
521   case SILC_COMMAND_INVITE:
522     {
523       SilcChannelEntry channel;
524       char *invite_list;
525       SilcArgumentPayload args;
526       int argc = 0;
527       
528       if (!success)
529         return;
530       
531       channel = va_arg(vp, SilcChannelEntry);
532       invite_list = va_arg(vp, char *);
533
534       args = silc_command_get_args(cmd_payload);
535       if (args)
536         argc = silc_argument_get_arg_num(args);
537
538       if (invite_list)
539         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
540                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
541                            invite_list);
542       else if (argc == 3)
543         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
544                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
545                            channel->channel_name);
546     }
547     break;
548
549   case SILC_COMMAND_JOIN: 
550     {
551       char *channel, *mode, *topic;
552       uint32 modei;
553       SilcChannelEntry channel_entry;
554       SilcBuffer client_id_list;
555       uint32 list_count;
556
557       if (!success)
558         return;
559
560       channel = va_arg(vp, char *);
561       channel_entry = va_arg(vp, SilcChannelEntry);
562       modei = va_arg(vp, uint32);
563       (void)va_arg(vp, uint32);
564       (void)va_arg(vp, unsigned char *);
565       (void)va_arg(vp, unsigned char *);
566       (void)va_arg(vp, unsigned char *);
567       topic = va_arg(vp, char *);
568       (void)va_arg(vp, unsigned char *);
569       list_count = va_arg(vp, uint32);
570       client_id_list = va_arg(vp, SilcBuffer);
571
572       chanrec = silc_channel_find(server, channel);
573       if (!chanrec)
574         chanrec = silc_channel_create(server, channel, TRUE);
575
576       if (topic) {
577         g_free_not_null(chanrec->topic);
578         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
579         signal_emit("channel topic changed", 1, chanrec);
580       }
581
582       mode = silc_client_chmode(modei, 
583                                 channel_entry->channel_key ? 
584                                 channel_entry->channel_key->cipher->name : "",
585                                 channel_entry->hmac ? 
586                                 silc_hmac_get_name(channel_entry->hmac) : "");
587       g_free_not_null(chanrec->mode);
588       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
589       signal_emit("channel mode changed", 1, chanrec);
590
591       /* Resolve the client information */
592       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
593                                       silc_client_join_get_users, 
594                                       channel_entry);
595       break;
596     }
597
598   case SILC_COMMAND_NICK: 
599     {
600       SilcClientEntry client = va_arg(vp, SilcClientEntry);
601       char *old;
602       
603       if (!success)
604         return;
605
606       old = g_strdup(server->nick);
607       server_change_nick(SERVER(server), client->nickname);
608       nicklist_rename_unique(SERVER(server),
609                              server->conn->local_entry, server->nick,
610                              client, client->nickname);
611       
612       signal_emit("message own_nick", 4, server, server->nick, old, "");
613       g_free(old);
614       break;
615     }
616     
617   case SILC_COMMAND_LIST:
618     {
619       char *topic, *name;
620       int usercount;
621       char users[20];
622       
623       if (!success)
624         return;
625       
626       (void)va_arg(vp, SilcChannelEntry);
627       name = va_arg(vp, char *);
628       topic = va_arg(vp, char *);
629       usercount = va_arg(vp, int);
630       
631       if (status == SILC_STATUS_LIST_START ||
632           status == SILC_STATUS_OK)
633         printformat_module("fe-common/silc", server, NULL,
634                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
635
636       snprintf(users, sizeof(users) - 1, "%d", usercount);
637       printformat_module("fe-common/silc", server, NULL,
638                          MSGLEVEL_CRAP, SILCTXT_LIST,
639                          name, users, topic ? topic : "");
640     }
641     break;
642     
643   case SILC_COMMAND_UMODE:
644     {
645       uint32 mode;
646       
647       if (!success)
648         return;
649       
650       mode = va_arg(vp, uint32);
651       
652       if (mode & SILC_UMODE_SERVER_OPERATOR)
653         printformat_module("fe-common/silc", server, NULL,
654                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
655
656       if (mode & SILC_UMODE_ROUTER_OPERATOR)
657         printformat_module("fe-common/silc", server, NULL,
658                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
659     }
660     break;
661     
662   case SILC_COMMAND_OPER:
663     if (!success)
664       return;
665
666     printformat_module("fe-common/silc", server, NULL,
667                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
668     break;
669     
670   case SILC_COMMAND_SILCOPER:
671     if (!success)
672       return;
673
674     printformat_module("fe-common/silc", server, NULL,
675                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
676     break;
677     
678   case SILC_COMMAND_USERS: 
679     {
680       SilcChannelEntry channel;
681       SilcChannelUser chu;
682       
683       if (!success)
684         return;
685       
686       channel = va_arg(vp, SilcChannelEntry);
687       
688       printformat_module("fe-common/silc", server, channel->channel_name,
689                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
690                          channel->channel_name);
691
692       silc_list_start(channel->clients);
693       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
694         SilcClientEntry e = chu->client;
695         char stat[5], *mode;
696
697         if (!e->nickname)
698           continue;
699         
700         memset(stat, 0, sizeof(stat));
701         mode = silc_client_chumode_char(chu->mode);
702         if (e->mode & SILC_UMODE_GONE)
703           strcat(stat, "G");
704         else
705           strcat(stat, "H");
706         if (mode)
707           strcat(stat, mode);
708
709         printformat_module("fe-common/silc", server, channel->channel_name,
710                            MSGLEVEL_CRAP, SILCTXT_USERS,
711                            e->nickname, stat, 
712                            e->username ? e->username : "",
713                            e->hostname ? e->hostname : "",
714                            e->realname ? e->realname : "");
715         if (mode)
716           silc_free(mode);
717       }
718     }
719     break;
720
721   case SILC_COMMAND_BAN:
722     {
723       SilcChannelEntry channel;
724       char *ban_list;
725       
726       if (!success)
727         return;
728       
729       channel = va_arg(vp, SilcChannelEntry);
730       ban_list = va_arg(vp, char *);
731       
732       if (ban_list)
733         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
734                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
735                            ban_list);
736       else
737         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
738                            SILCTXT_CHANNEL_NO_BAN_LIST, 
739                            channel->channel_name);
740     }
741     break;
742     
743   case SILC_COMMAND_GETKEY:
744     {
745       SilcIdType id_type;
746       void *entry;
747       SilcPublicKey public_key;
748       unsigned char *pk;
749       uint32 pk_len;
750       GetkeyContext getkey;
751       
752       if (!success)
753         return;
754       
755       id_type = va_arg(vp, uint32);
756       entry = va_arg(vp, void *);
757       public_key = va_arg(vp, SilcPublicKey);
758
759       if (public_key) {
760         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
761
762         getkey = silc_calloc(1, sizeof(*getkey));
763         getkey->entry = entry;
764         getkey->id_type = id_type;
765         getkey->client = client;
766         getkey->conn = conn;
767         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
768         
769         silc_verify_public_key_internal(client, conn, 
770                                         (id_type == SILC_ID_CLIENT ?
771                                          SILC_SOCKET_TYPE_CLIENT :
772                                          SILC_SOCKET_TYPE_SERVER),
773                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
774                                         silc_getkey_cb, getkey);
775         silc_free(pk);
776       } else {
777         printformat_module("fe-common/silc", server, NULL,
778                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
779       }
780     }
781     break;
782     
783   case SILC_COMMAND_TOPIC:
784     {
785       SilcChannelEntry channel;
786       char *topic;
787       
788       if (!success)
789         return;
790       
791       channel = va_arg(vp, SilcChannelEntry);
792       topic = va_arg(vp, char *);
793       
794       if (topic) {
795         chanrec = silc_channel_find_entry(server, channel);
796         if (chanrec) {
797           g_free_not_null(chanrec->topic);
798           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
799           signal_emit("channel topic changed", 1, chanrec);
800         }
801         printformat_module("fe-common/silc", server, channel->channel_name,
802                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
803                            channel->channel_name, topic);
804       } else {
805         printformat_module("fe-common/silc", server, channel->channel_name,
806                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
807                            channel->channel_name);
808       }
809     }
810     break;
811
812   }
813
814   va_end(vp);
815 }
816
817 /* Internal routine to verify public key. If the `completion' is provided
818    it will be called to indicate whether public was verified or not. */
819
820 typedef struct {
821   SilcClient client;
822   SilcClientConnection conn;
823   char *filename;
824   char *entity;
825   unsigned char *pk;
826   uint32 pk_len;
827   SilcSKEPKType pk_type;
828   SilcVerifyPublicKey completion;
829   void *context;
830 } *PublicKeyVerify;
831
832 static void verify_public_key_completion(const char *line, void *context)
833 {
834   PublicKeyVerify verify = (PublicKeyVerify)context;
835
836   if (line[0] == 'Y' || line[0] == 'y') {
837     /* Call the completion */
838     if (verify->completion)
839       verify->completion(TRUE, verify->context);
840
841     /* Save the key for future checking */
842     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
843                                    verify->pk_len, SILC_PKCS_FILE_PEM);
844   } else {
845     /* Call the completion */
846     if (verify->completion)
847       verify->completion(FALSE, verify->context);
848
849     printformat_module("fe-common/silc", NULL, NULL,
850                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
851   }
852
853   silc_free(verify->filename);
854   silc_free(verify->entity);
855   silc_free(verify->pk);
856   silc_free(verify);
857 }
858
859 static void 
860 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
861                                 SilcSocketType conn_type, unsigned char *pk, 
862                                 uint32 pk_len, SilcSKEPKType pk_type,
863                                 SilcVerifyPublicKey completion, void *context)
864 {
865   int i;
866   char file[256], filename[256], *fingerprint, *babbleprint, *format;
867   struct passwd *pw;
868   struct stat st;
869   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
870                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
871                   "server" : "client");
872   PublicKeyVerify verify;
873
874   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
875     printformat_module("fe-common/silc", NULL, NULL,
876                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
877                        entity, pk_type);
878     if (completion)
879       completion(FALSE, context);
880     return;
881   }
882
883   pw = getpwuid(getuid());
884   if (!pw) {
885     if (completion)
886       completion(FALSE, context);
887     return;
888   }
889
890   memset(filename, 0, sizeof(filename));
891   memset(file, 0, sizeof(file));
892
893   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
894       conn_type == SILC_SOCKET_TYPE_ROUTER) {
895     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
896              conn->sock->hostname, conn->sock->port);
897     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
898              pw->pw_dir, entity, file);
899   } else {
900     /* Replace all whitespaces with `_'. */
901     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
902     for (i = 0; i < strlen(fingerprint); i++)
903       if (fingerprint[i] == ' ')
904         fingerprint[i] = '_';
905     
906     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
907     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
908              pw->pw_dir, entity, file);
909     silc_free(fingerprint);
910   }
911
912   /* Take fingerprint of the public key */
913   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
914   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
915
916   verify = silc_calloc(1, sizeof(*verify));
917   verify->client = client;
918   verify->conn = conn;
919   verify->filename = strdup(filename);
920   verify->entity = strdup(entity);
921   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
922   memcpy(verify->pk, pk, pk_len);
923   verify->pk_len = pk_len;
924   verify->pk_type = pk_type;
925   verify->completion = completion;
926   verify->context = context;
927
928   /* Check whether this key already exists */
929   if (stat(filename, &st) < 0) {
930     /* Key does not exist, ask user to verify the key and save it */
931
932     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
933                        SILCTXT_PUBKEY_RECEIVED, entity);
934     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
935                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
936     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
937                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
938     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
939                              SILCTXT_PUBKEY_ACCEPT);
940     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
941                             format, 0, verify);
942     g_free(format);
943     silc_free(fingerprint);
944     return;
945   } else {
946     /* The key already exists, verify it. */
947     SilcPublicKey public_key;
948     unsigned char *encpk;
949     uint32 encpk_len;
950
951     /* Load the key file */
952     if (!silc_pkcs_load_public_key(filename, &public_key, 
953                                    SILC_PKCS_FILE_PEM))
954       if (!silc_pkcs_load_public_key(filename, &public_key, 
955                                      SILC_PKCS_FILE_BIN)) {
956         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
957                            SILCTXT_PUBKEY_RECEIVED, entity);
958         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
959                            SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
960         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
961                            SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
962         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
963                            SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
964         format = format_get_text("fe-common/silc", NULL, NULL, NULL,
965                                  SILCTXT_PUBKEY_ACCEPT_ANYWAY);
966         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
967                                 format, 0, verify);
968         g_free(format);
969         silc_free(fingerprint);
970         return;
971       }
972   
973     /* Encode the key data */
974     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
975     if (!encpk) {
976       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
977                          SILCTXT_PUBKEY_RECEIVED, entity);
978       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
979                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
980       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
981                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
982       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
983                          SILCTXT_PUBKEY_MALFORMED, entity);
984       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
985                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
986       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
987                               format, 0, verify);
988       g_free(format);
989       silc_free(fingerprint);
990       return;
991     }
992
993     /* Compare the keys */
994     if (memcmp(encpk, pk, encpk_len)) {
995       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
996                          SILCTXT_PUBKEY_RECEIVED, entity);
997       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
998                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
999       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1000                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1001       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1002                          SILCTXT_PUBKEY_NO_MATCH, entity);
1003       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1004                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1005       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1006                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1007
1008       /* Ask user to verify the key and save it */
1009       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1010                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1011       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1012                               format, 0, verify);
1013       g_free(format);
1014       silc_free(fingerprint);
1015       return;
1016     }
1017
1018     /* Local copy matched */
1019     if (completion)
1020       completion(TRUE, context);
1021     silc_free(fingerprint);
1022   }
1023 }
1024
1025 /* Verifies received public key. The `conn_type' indicates which entity
1026    (server, client etc.) has sent the public key. If user decides to trust
1027    the key may be saved as trusted public key for later use. The 
1028    `completion' must be called after the public key has been verified. */
1029
1030 void 
1031 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1032                        SilcSocketType conn_type, unsigned char *pk, 
1033                        uint32 pk_len, SilcSKEPKType pk_type,
1034                        SilcVerifyPublicKey completion, void *context)
1035 {
1036   silc_verify_public_key_internal(client, conn, conn_type, pk,
1037                                   pk_len, pk_type,
1038                                   completion, context);
1039 }
1040
1041 /* Asks passphrase from user on the input line. */
1042
1043 typedef struct {
1044   SilcAskPassphrase completion;
1045   void *context;
1046 } *AskPassphrase;
1047
1048 void ask_passphrase_completion(const char *passphrase, void *context)
1049 {
1050   AskPassphrase p = (AskPassphrase)context;
1051   p->completion((unsigned char *)passphrase, 
1052                 passphrase ? strlen(passphrase) : 0, p->context);
1053   silc_free(p);
1054 }
1055
1056 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1057                          SilcAskPassphrase completion, void *context)
1058 {
1059   AskPassphrase p = silc_calloc(1, sizeof(*p));
1060   p->completion = completion;
1061   p->context = context;
1062
1063   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1064                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1065 }
1066
1067 typedef struct {
1068   SilcGetAuthMeth completion;
1069   void *context;
1070 } *InternalGetAuthMethod;
1071
1072 /* Callback called when we've received the authentication method information
1073    from the server after we've requested it. This will get the authentication
1074    data from the user if needed. */
1075
1076 static void silc_get_auth_method_callback(SilcClient client,
1077                                           SilcClientConnection conn,
1078                                           SilcAuthMethod auth_meth,
1079                                           void *context)
1080 {
1081   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1082
1083   SILC_LOG_DEBUG(("Start"));
1084
1085   switch (auth_meth) {
1086   case SILC_AUTH_NONE:
1087     /* No authentication required. */
1088     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1089     break;
1090   case SILC_AUTH_PASSWORD:
1091     /* Do not ask the passphrase from user, the library will ask it if
1092        we do not provide it here. */
1093     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1094     break;
1095   case SILC_AUTH_PUBLIC_KEY:
1096     /* Do not get the authentication data now, the library will generate
1097        it using our default key, if we do not provide it here. */
1098     /* XXX In the future when we support multiple local keys and multiple
1099        local certificates we will need to ask from user which one to use. */
1100     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1101     break;
1102   }
1103
1104   silc_free(internal);
1105 }
1106
1107 /* Find authentication method and authentication data by hostname and
1108    port. The hostname may be IP address as well. The found authentication
1109    method and authentication data is returned to `auth_meth', `auth_data'
1110    and `auth_data_len'. The function returns TRUE if authentication method
1111    is found and FALSE if not. `conn' may be NULL. */
1112
1113 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1114                           char *hostname, uint16 port,
1115                           SilcGetAuthMeth completion, void *context)
1116 {
1117   InternalGetAuthMethod internal;
1118
1119   SILC_LOG_DEBUG(("Start"));
1120
1121   /* XXX must resolve from configuration whether this connection has
1122      any specific authentication data */
1123
1124   /* If we do not have this connection configured by the user in a
1125      configuration file then resolve the authentication method from the
1126      server for this session. */
1127   internal = silc_calloc(1, sizeof(*internal));
1128   internal->completion = completion;
1129   internal->context = context;
1130
1131   silc_client_request_authentication_method(client, conn, 
1132                                             silc_get_auth_method_callback,
1133                                             internal);
1134 }
1135
1136 /* Notifies application that failure packet was received.  This is called
1137    if there is some protocol active in the client.  The `protocol' is the
1138    protocol context.  The `failure' is opaque pointer to the failure
1139    indication.  Note, that the `failure' is protocol dependant and application
1140    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1141    failure type (see protocol specs for all protocol failure types). */
1142
1143 void silc_failure(SilcClient client, SilcClientConnection conn, 
1144                   SilcProtocol protocol, void *failure)
1145 {
1146   SILC_LOG_DEBUG(("Start"));
1147
1148   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1149     SilcSKEStatus status = (SilcSKEStatus)failure;
1150     
1151     if (status == SILC_SKE_STATUS_BAD_VERSION)
1152       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1153                          SILCTXT_KE_BAD_VERSION);
1154     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1155       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1156                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1157     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1158       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1159                          SILCTXT_KE_UNKNOWN_GROUP);
1160     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1161       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1162                          SILCTXT_KE_UNKNOWN_CIPHER);
1163     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1164       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1165                          SILCTXT_KE_UNKNOWN_PKCS);
1166     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1167       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1168                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1169     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1170       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1171                          SILCTXT_KE_UNKNOWN_HMAC);
1172     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1173       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1174                          SILCTXT_KE_INCORRECT_SIGNATURE);
1175     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1176       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1177                          SILCTXT_KE_INVALID_COOKIE);
1178   }
1179
1180   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1181     uint32 err = (uint32)failure;
1182
1183     if (err == SILC_AUTH_FAILED)
1184       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1185                          SILCTXT_AUTH_FAILED);
1186   }
1187 }
1188
1189 /* Asks whether the user would like to perform the key agreement protocol.
1190    This is called after we have received an key agreement packet or an
1191    reply to our key agreement packet. This returns TRUE if the user wants
1192    the library to perform the key agreement protocol and FALSE if it is not
1193    desired (application may start it later by calling the function
1194    silc_client_perform_key_agreement). */
1195
1196 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1197                        SilcClientEntry client_entry, const char *hostname,
1198                        uint16 port, SilcKeyAgreementCallback *completion,
1199                        void **context)
1200 {
1201   char portstr[12];
1202
1203   SILC_LOG_DEBUG(("Start"));
1204
1205   /* We will just display the info on the screen and return FALSE and user
1206      will have to start the key agreement with a command. */
1207
1208   if (hostname) 
1209     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1210
1211   if (!hostname)
1212     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1213                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1214   else
1215     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1216                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1217                        client_entry->nickname, hostname, portstr);
1218
1219   *completion = NULL;
1220   *context = NULL;
1221
1222   return FALSE;
1223 }
1224
1225 void silc_ftp(SilcClient client, SilcClientConnection conn,
1226               SilcClientEntry client_entry, uint32 session_id,
1227               const char *hostname, uint16 port)
1228 {
1229   SILC_SERVER_REC *server;
1230   char portstr[12];
1231   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1232
1233   SILC_LOG_DEBUG(("Start"));
1234
1235   server = conn->context;
1236
1237   ftp->client_entry = client_entry;
1238   ftp->session_id = session_id;
1239   ftp->send = FALSE;
1240   ftp->conn = conn;
1241   silc_dlist_add(server->ftp_sessions, ftp);
1242   server->current_session = ftp;
1243
1244   if (hostname) 
1245     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1246
1247   if (!hostname)
1248     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1249                        SILCTXT_FILE_REQUEST, client_entry->nickname);
1250   else
1251     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1252                        SILCTXT_FILE_REQUEST_HOST, 
1253                        client_entry->nickname, hostname, portstr);
1254 }
1255
1256 /* SILC client operations */
1257 SilcClientOperations ops = {
1258   silc_say,
1259   silc_channel_message,
1260   silc_private_message,
1261   silc_notify,
1262   silc_command,
1263   silc_command_reply,
1264   silc_connect,
1265   silc_disconnect,
1266   silc_get_auth_method,
1267   silc_verify_public_key,
1268   silc_ask_passphrase,
1269   silc_failure,
1270   silc_key_agreement,
1271   silc_ftp,
1272 };