76be0744d7adff60ffb07546cc97a1a56d37010b
[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 object 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       unsigned char *fingerprint;
405       uint32 idle, mode;
406       SilcBuffer channels;
407       SilcClientEntry client_entry;
408       
409       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
410         /* Print the unknown nick for user */
411         unsigned char *tmp =
412           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
413                                      3, NULL);
414         if (tmp)
415           silc_say_error("%s: %s", tmp, 
416                          silc_client_command_status_message(status));
417         break;
418       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
419         /* Try to find the entry for the unknown client ID, since we
420            might have, and print the nickname of it for user. */
421         uint32 tmp_len;
422         unsigned char *tmp =
423           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
424                                      2, &tmp_len);
425         if (tmp) {
426           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
427           if (client_id) {
428             client_entry = silc_client_get_client_by_id(client, conn,
429                                                         client_id);
430             if (client_entry && client_entry->nickname)
431               silc_say_error("%s: %s", client_entry->nickname,
432                              silc_client_command_status_message(status));
433             silc_free(client_id);
434           }
435         }
436       }
437       
438       if (!success)
439         return;
440       
441       client_entry = va_arg(vp, SilcClientEntry);
442       nickname = va_arg(vp, char *);
443       username = va_arg(vp, char *);
444       realname = va_arg(vp, char *);
445       channels = va_arg(vp, SilcBuffer);
446       mode = va_arg(vp, uint32);
447       idle = va_arg(vp, uint32);
448       fingerprint = va_arg(vp, unsigned char *);
449       
450       silc_parse_userfqdn(nickname, &nick, NULL);
451       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
452                          SILCTXT_WHOIS_USERINFO, nickname, 
453                          client_entry->username, client_entry->hostname,
454                          nick, client_entry->nickname);
455       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
456                          SILCTXT_WHOIS_REALNAME, realname);
457       silc_free(nick);
458
459       if (channels) {
460         SilcDList list = silc_channel_payload_parse_list(channels->data,
461                                                          channels->len);
462         if (list) {
463           SilcChannelPayload entry;
464           memset(buf, 0, sizeof(buf));
465           silc_dlist_start(list);
466           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
467             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
468             uint32 name_len;
469             char *name = silc_channel_get_name(entry, &name_len);
470             
471             if (m)
472               strncat(buf, m, strlen(m));
473             strncat(buf, name, name_len);
474             strncat(buf, " ", 1);
475             silc_free(m);
476           }
477
478           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
479                              SILCTXT_WHOIS_CHANNELS, buf);
480           silc_channel_payload_list_free(list);
481         }
482       }
483       
484       if (mode) {
485         memset(buf, 0, sizeof(buf));
486
487         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
488             (mode & SILC_UMODE_ROUTER_OPERATOR)) {
489           strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
490                  "Server Operator " :
491                  (mode & SILC_UMODE_ROUTER_OPERATOR) ?
492                  "SILC Operator " : "[Unknown mode] ");
493         }
494         if (mode & SILC_UMODE_GONE)
495           strcat(buf, "away");
496
497         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
498                            SILCTXT_WHOIS_MODES, buf);
499       }
500       
501       if (idle && nickname) {
502         memset(buf, 0, sizeof(buf));
503         snprintf(buf, sizeof(buf) - 1, "%lu %s",
504                  idle > 60 ? (idle / 60) : idle,
505                  idle > 60 ? "minutes" : "seconds");
506
507         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
508                            SILCTXT_WHOIS_IDLE, buf);
509       }
510
511       if (fingerprint) {
512         fingerprint = silc_fingerprint(fingerprint, 20);
513         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
514                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
515         silc_free(fingerprint);
516       }
517     }
518     break;
519     
520   case SILC_COMMAND_IDENTIFY:
521     {
522       SilcClientEntry client_entry;
523       
524       /* Identify command is used only internally by the client library
525          but it still might send some interesting stuff for user interface
526          so let's print errors. */
527       
528       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
529         /* Print the unknown nick for user */
530         unsigned char *tmp =
531           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
532                                      3, NULL);
533         if (tmp)
534           silc_say_error("%s: %s", tmp, 
535                          silc_client_command_status_message(status));
536         break;
537       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
538         /* Try to find the entry for the unknown client ID, since we
539            might have, and print the nickname of it for user. */
540         uint32 tmp_len;
541         unsigned char *tmp =
542           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
543                                      2, &tmp_len);
544         if (tmp) {
545           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
546           if (client_id) {
547             client_entry = silc_client_get_client_by_id(client, conn,
548                                                         client_id);
549             if (client_entry && client_entry->nickname)
550               silc_say_error("%s: %s", client_entry->nickname,
551                              silc_client_command_status_message(status));
552             silc_free(client_id);
553           }
554         }
555       }
556     }
557     break;
558
559   case SILC_COMMAND_WHOWAS:
560     {
561       char *nickname, *username, *realname;
562       
563       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
564           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
565         char *tmp;
566         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
567                                          3, NULL);
568         if (tmp)
569           silc_say_error("%s: %s", tmp, 
570                          silc_client_command_status_message(status));
571         break;
572       }
573       
574       if (!success)
575         return;
576       
577       (void)va_arg(vp, SilcClientEntry);
578       nickname = va_arg(vp, char *);
579       username = va_arg(vp, char *);
580       realname = va_arg(vp, char *);
581       
582       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
583                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
584                          realname ? realname : "");
585     }
586     break;
587     
588   case SILC_COMMAND_INVITE:
589     {
590       SilcChannelEntry channel;
591       char *invite_list;
592       SilcArgumentPayload args;
593       int argc = 0;
594       
595       if (!success)
596         return;
597       
598       channel = va_arg(vp, SilcChannelEntry);
599       invite_list = va_arg(vp, char *);
600
601       args = silc_command_get_args(cmd_payload);
602       if (args)
603         argc = silc_argument_get_arg_num(args);
604
605       if (invite_list)
606         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
607                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
608                            invite_list);
609       else if (argc == 3)
610         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
611                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
612                            channel->channel_name);
613     }
614     break;
615
616   case SILC_COMMAND_JOIN: 
617     {
618       char *channel, *mode, *topic;
619       uint32 modei;
620       SilcChannelEntry channel_entry;
621       SilcBuffer client_id_list;
622       uint32 list_count;
623
624       if (!success)
625         return;
626
627       channel = va_arg(vp, char *);
628       channel_entry = va_arg(vp, SilcChannelEntry);
629       modei = va_arg(vp, uint32);
630       (void)va_arg(vp, uint32);
631       (void)va_arg(vp, unsigned char *);
632       (void)va_arg(vp, unsigned char *);
633       (void)va_arg(vp, unsigned char *);
634       topic = va_arg(vp, char *);
635       (void)va_arg(vp, unsigned char *);
636       list_count = va_arg(vp, uint32);
637       client_id_list = va_arg(vp, SilcBuffer);
638
639       chanrec = silc_channel_find(server, channel);
640       if (!chanrec)
641         chanrec = silc_channel_create(server, channel, TRUE);
642
643       if (topic) {
644         g_free_not_null(chanrec->topic);
645         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
646         signal_emit("channel topic changed", 1, chanrec);
647       }
648
649       mode = silc_client_chmode(modei, 
650                                 channel_entry->channel_key ? 
651                                 channel_entry->channel_key->cipher->name : "",
652                                 channel_entry->hmac ? 
653                                 silc_hmac_get_name(channel_entry->hmac) : "");
654       g_free_not_null(chanrec->mode);
655       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
656       signal_emit("channel mode changed", 1, chanrec);
657
658       /* Resolve the client information */
659       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
660                                       silc_client_join_get_users, 
661                                       channel_entry);
662       break;
663     }
664
665   case SILC_COMMAND_NICK: 
666     {
667       SilcClientEntry client = va_arg(vp, SilcClientEntry);
668       char *old;
669       
670       if (!success)
671         return;
672
673       old = g_strdup(server->nick);
674       server_change_nick(SERVER(server), client->nickname);
675       nicklist_rename_unique(SERVER(server),
676                              server->conn->local_entry, server->nick,
677                              client, client->nickname);
678       
679       signal_emit("message own_nick", 4, server, server->nick, old, "");
680       g_free(old);
681       break;
682     }
683     
684   case SILC_COMMAND_LIST:
685     {
686       char *topic, *name;
687       int usercount;
688       char users[20];
689       
690       if (!success)
691         return;
692       
693       (void)va_arg(vp, SilcChannelEntry);
694       name = va_arg(vp, char *);
695       topic = va_arg(vp, char *);
696       usercount = va_arg(vp, int);
697       
698       if (status == SILC_STATUS_LIST_START ||
699           status == SILC_STATUS_OK)
700         printformat_module("fe-common/silc", server, NULL,
701                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
702
703       if (!usercount)
704         snprintf(users, sizeof(users) - 1, "N/A");
705       else
706         snprintf(users, sizeof(users) - 1, "%d", usercount);
707       printformat_module("fe-common/silc", server, NULL,
708                          MSGLEVEL_CRAP, SILCTXT_LIST,
709                          name, users, topic ? topic : "");
710     }
711     break;
712     
713   case SILC_COMMAND_UMODE:
714     {
715       uint32 mode;
716       
717       if (!success)
718         return;
719       
720       mode = va_arg(vp, uint32);
721       
722       if (mode & SILC_UMODE_SERVER_OPERATOR &&
723           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
724         printformat_module("fe-common/silc", server, NULL,
725                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
726
727       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
728           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
729         printformat_module("fe-common/silc", server, NULL,
730                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
731
732       server->umode = mode;
733     }
734     break;
735     
736   case SILC_COMMAND_OPER:
737     if (!success)
738       return;
739
740     printformat_module("fe-common/silc", server, NULL,
741                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
742     break;
743     
744   case SILC_COMMAND_SILCOPER:
745     if (!success)
746       return;
747
748     printformat_module("fe-common/silc", server, NULL,
749                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
750     break;
751     
752   case SILC_COMMAND_USERS: 
753     {
754       SilcChannelEntry channel;
755       SilcChannelUser chu;
756       
757       if (!success)
758         return;
759       
760       channel = va_arg(vp, SilcChannelEntry);
761       
762       printformat_module("fe-common/silc", server, channel->channel_name,
763                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
764                          channel->channel_name);
765
766       silc_list_start(channel->clients);
767       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
768         SilcClientEntry e = chu->client;
769         char stat[5], *mode;
770
771         if (!e->nickname)
772           continue;
773         
774         memset(stat, 0, sizeof(stat));
775         mode = silc_client_chumode_char(chu->mode);
776         if (e->mode & SILC_UMODE_GONE)
777           strcat(stat, "G");
778         else
779           strcat(stat, "H");
780         if (mode)
781           strcat(stat, mode);
782
783         printformat_module("fe-common/silc", server, channel->channel_name,
784                            MSGLEVEL_CRAP, SILCTXT_USERS,
785                            e->nickname, stat, 
786                            e->username ? e->username : "",
787                            e->hostname ? e->hostname : "",
788                            e->realname ? e->realname : "");
789         if (mode)
790           silc_free(mode);
791       }
792     }
793     break;
794
795   case SILC_COMMAND_BAN:
796     {
797       SilcChannelEntry channel;
798       char *ban_list;
799       
800       if (!success)
801         return;
802       
803       channel = va_arg(vp, SilcChannelEntry);
804       ban_list = va_arg(vp, char *);
805       
806       if (ban_list)
807         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
808                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
809                            ban_list);
810       else
811         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
812                            SILCTXT_CHANNEL_NO_BAN_LIST, 
813                            channel->channel_name);
814     }
815     break;
816     
817   case SILC_COMMAND_GETKEY:
818     {
819       SilcIdType id_type;
820       void *entry;
821       SilcPublicKey public_key;
822       unsigned char *pk;
823       uint32 pk_len;
824       GetkeyContext getkey;
825       
826       if (!success)
827         return;
828       
829       id_type = va_arg(vp, uint32);
830       entry = va_arg(vp, void *);
831       public_key = va_arg(vp, SilcPublicKey);
832
833       if (public_key) {
834         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
835
836         getkey = silc_calloc(1, sizeof(*getkey));
837         getkey->entry = entry;
838         getkey->id_type = id_type;
839         getkey->client = client;
840         getkey->conn = conn;
841         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
842         
843         silc_verify_public_key_internal(client, conn, 
844                                         (id_type == SILC_ID_CLIENT ?
845                                          SILC_SOCKET_TYPE_CLIENT :
846                                          SILC_SOCKET_TYPE_SERVER),
847                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
848                                         silc_getkey_cb, getkey);
849         silc_free(pk);
850       } else {
851         printformat_module("fe-common/silc", server, NULL,
852                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
853       }
854     }
855     break;
856
857   case SILC_COMMAND_INFO:
858     {
859       SilcServerEntry server_entry;
860       char *server_name;
861       char *server_info;
862
863       if (!success)
864         return;
865       
866       server_entry = va_arg(vp, SilcServerEntry);
867       server_name = va_arg(vp, char *);
868       server_info = va_arg(vp, char *);
869
870       if (server_name && server_info )
871         {
872           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
873           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
874         }
875     }
876     break;
877     
878   case SILC_COMMAND_TOPIC:
879     {
880       SilcChannelEntry channel;
881       char *topic;
882       
883       if (!success)
884         return;
885       
886       channel = va_arg(vp, SilcChannelEntry);
887       topic = va_arg(vp, char *);
888       
889       if (topic) {
890         chanrec = silc_channel_find_entry(server, channel);
891         if (chanrec) {
892           g_free_not_null(chanrec->topic);
893           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
894           signal_emit("channel topic changed", 1, chanrec);
895         }
896         printformat_module("fe-common/silc", server, channel->channel_name,
897                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
898                            channel->channel_name, topic);
899       } else {
900         printformat_module("fe-common/silc", server, channel->channel_name,
901                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
902                            channel->channel_name);
903       }
904     }
905     break;
906
907   }
908
909   va_end(vp);
910 }
911
912 /* Internal routine to verify public key. If the `completion' is provided
913    it will be called to indicate whether public was verified or not. */
914
915 typedef struct {
916   SilcClient client;
917   SilcClientConnection conn;
918   char *filename;
919   char *entity;
920   unsigned char *pk;
921   uint32 pk_len;
922   SilcSKEPKType pk_type;
923   SilcVerifyPublicKey completion;
924   void *context;
925 } *PublicKeyVerify;
926
927 static void verify_public_key_completion(const char *line, void *context)
928 {
929   PublicKeyVerify verify = (PublicKeyVerify)context;
930
931   if (line[0] == 'Y' || line[0] == 'y') {
932     /* Call the completion */
933     if (verify->completion)
934       verify->completion(TRUE, verify->context);
935
936     /* Save the key for future checking */
937     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
938                                    verify->pk_len, SILC_PKCS_FILE_PEM);
939   } else {
940     /* Call the completion */
941     if (verify->completion)
942       verify->completion(FALSE, verify->context);
943
944     printformat_module("fe-common/silc", NULL, NULL,
945                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, verify->entity);
946   }
947
948   silc_free(verify->filename);
949   silc_free(verify->entity);
950   silc_free(verify->pk);
951   silc_free(verify);
952 }
953
954 static void 
955 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
956                                 SilcSocketType conn_type, unsigned char *pk, 
957                                 uint32 pk_len, SilcSKEPKType pk_type,
958                                 SilcVerifyPublicKey completion, void *context)
959 {
960   int i;
961   char file[256], filename[256], *fingerprint, *babbleprint, *format;
962   struct passwd *pw;
963   struct stat st;
964   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
965                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
966                   "server" : "client");
967   PublicKeyVerify verify;
968
969   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
970     printformat_module("fe-common/silc", NULL, NULL,
971                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
972                        entity, pk_type);
973     if (completion)
974       completion(FALSE, context);
975     return;
976   }
977
978   pw = getpwuid(getuid());
979   if (!pw) {
980     if (completion)
981       completion(FALSE, context);
982     return;
983   }
984
985   memset(filename, 0, sizeof(filename));
986   memset(file, 0, sizeof(file));
987
988   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
989       conn_type == SILC_SOCKET_TYPE_ROUTER) {
990     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
991              conn->sock->ip, conn->sock->port);
992     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
993              pw->pw_dir, entity, file);
994   } else {
995     /* Replace all whitespaces with `_'. */
996     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
997     for (i = 0; i < strlen(fingerprint); i++)
998       if (fingerprint[i] == ' ')
999         fingerprint[i] = '_';
1000     
1001     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1002     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
1003              pw->pw_dir, entity, file);
1004     silc_free(fingerprint);
1005   }
1006
1007   /* Take fingerprint of the public key */
1008   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1009   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1010
1011   verify = silc_calloc(1, sizeof(*verify));
1012   verify->client = client;
1013   verify->conn = conn;
1014   verify->filename = strdup(filename);
1015   verify->entity = strdup(entity);
1016   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1017   memcpy(verify->pk, pk, pk_len);
1018   verify->pk_len = pk_len;
1019   verify->pk_type = pk_type;
1020   verify->completion = completion;
1021   verify->context = context;
1022
1023   /* Check whether this key already exists */
1024   if (stat(filename, &st) < 0) {
1025     /* Key does not exist, ask user to verify the key and save it */
1026
1027     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1028                        SILCTXT_PUBKEY_RECEIVED, entity);
1029     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1030                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1031     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1032                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1033     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1034                              SILCTXT_PUBKEY_ACCEPT);
1035     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1036                             format, 0, verify);
1037     g_free(format);
1038     silc_free(fingerprint);
1039     return;
1040   } else {
1041     /* The key already exists, verify it. */
1042     SilcPublicKey public_key;
1043     unsigned char *encpk;
1044     uint32 encpk_len;
1045
1046     /* Load the key file */
1047     if (!silc_pkcs_load_public_key(filename, &public_key, 
1048                                    SILC_PKCS_FILE_PEM))
1049       if (!silc_pkcs_load_public_key(filename, &public_key, 
1050                                      SILC_PKCS_FILE_BIN)) {
1051         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1052                            SILCTXT_PUBKEY_RECEIVED, entity);
1053         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1054                            SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1055         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1056                            SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1057         printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1058                            SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1059         format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1060                                  SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1061         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1062                                 format, 0, verify);
1063         g_free(format);
1064         silc_free(fingerprint);
1065         return;
1066       }
1067   
1068     /* Encode the key data */
1069     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1070     if (!encpk) {
1071       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1072                          SILCTXT_PUBKEY_RECEIVED, entity);
1073       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1074                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1075       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1076                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1077       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1078                          SILCTXT_PUBKEY_MALFORMED, entity);
1079       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1080                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1081       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1082                               format, 0, verify);
1083       g_free(format);
1084       silc_free(fingerprint);
1085       return;
1086     }
1087
1088     /* Compare the keys */
1089     if (memcmp(encpk, pk, encpk_len)) {
1090       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1091                          SILCTXT_PUBKEY_RECEIVED, entity);
1092       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1093                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1094       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1095                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1096       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1097                          SILCTXT_PUBKEY_NO_MATCH, entity);
1098       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1099                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1100       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1101                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1102
1103       /* Ask user to verify the key and save it */
1104       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1105                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1106       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1107                               format, 0, verify);
1108       g_free(format);
1109       silc_free(fingerprint);
1110       return;
1111     }
1112
1113     /* Local copy matched */
1114     if (completion)
1115       completion(TRUE, context);
1116     silc_free(fingerprint);
1117   }
1118 }
1119
1120 /* Verifies received public key. The `conn_type' indicates which entity
1121    (server, client etc.) has sent the public key. If user decides to trust
1122    the key may be saved as trusted public key for later use. The 
1123    `completion' must be called after the public key has been verified. */
1124
1125 void 
1126 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1127                        SilcSocketType conn_type, unsigned char *pk, 
1128                        uint32 pk_len, SilcSKEPKType pk_type,
1129                        SilcVerifyPublicKey completion, void *context)
1130 {
1131   silc_verify_public_key_internal(client, conn, conn_type, pk,
1132                                   pk_len, pk_type,
1133                                   completion, context);
1134 }
1135
1136 /* Asks passphrase from user on the input line. */
1137
1138 typedef struct {
1139   SilcAskPassphrase completion;
1140   void *context;
1141 } *AskPassphrase;
1142
1143 void ask_passphrase_completion(const char *passphrase, void *context)
1144 {
1145   AskPassphrase p = (AskPassphrase)context;
1146   p->completion((unsigned char *)passphrase, 
1147                 passphrase ? strlen(passphrase) : 0, p->context);
1148   silc_free(p);
1149 }
1150
1151 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1152                          SilcAskPassphrase completion, void *context)
1153 {
1154   AskPassphrase p = silc_calloc(1, sizeof(*p));
1155   p->completion = completion;
1156   p->context = context;
1157
1158   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1159                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1160 }
1161
1162 typedef struct {
1163   SilcGetAuthMeth completion;
1164   void *context;
1165 } *InternalGetAuthMethod;
1166
1167 /* Callback called when we've received the authentication method information
1168    from the server after we've requested it. This will get the authentication
1169    data from the user if needed. */
1170
1171 static void silc_get_auth_method_callback(SilcClient client,
1172                                           SilcClientConnection conn,
1173                                           SilcAuthMethod auth_meth,
1174                                           void *context)
1175 {
1176   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1177
1178   SILC_LOG_DEBUG(("Start"));
1179
1180   switch (auth_meth) {
1181   case SILC_AUTH_NONE:
1182     /* No authentication required. */
1183     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1184     break;
1185   case SILC_AUTH_PASSWORD:
1186     /* Do not ask the passphrase from user, the library will ask it if
1187        we do not provide it here. */
1188     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1189     break;
1190   case SILC_AUTH_PUBLIC_KEY:
1191     /* Do not get the authentication data now, the library will generate
1192        it using our default key, if we do not provide it here. */
1193     /* XXX In the future when we support multiple local keys and multiple
1194        local certificates we will need to ask from user which one to use. */
1195     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1196     break;
1197   }
1198
1199   silc_free(internal);
1200 }
1201
1202 /* Find authentication method and authentication data by hostname and
1203    port. The hostname may be IP address as well. The found authentication
1204    method and authentication data is returned to `auth_meth', `auth_data'
1205    and `auth_data_len'. The function returns TRUE if authentication method
1206    is found and FALSE if not. `conn' may be NULL. */
1207
1208 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1209                           char *hostname, uint16 port,
1210                           SilcGetAuthMeth completion, void *context)
1211 {
1212   InternalGetAuthMethod internal;
1213
1214   SILC_LOG_DEBUG(("Start"));
1215
1216   /* XXX must resolve from configuration whether this connection has
1217      any specific authentication data */
1218
1219   /* If we do not have this connection configured by the user in a
1220      configuration file then resolve the authentication method from the
1221      server for this session. */
1222   internal = silc_calloc(1, sizeof(*internal));
1223   internal->completion = completion;
1224   internal->context = context;
1225
1226   silc_client_request_authentication_method(client, conn, 
1227                                             silc_get_auth_method_callback,
1228                                             internal);
1229 }
1230
1231 /* Notifies application that failure packet was received.  This is called
1232    if there is some protocol active in the client.  The `protocol' is the
1233    protocol context.  The `failure' is opaque pointer to the failure
1234    indication.  Note, that the `failure' is protocol dependant and application
1235    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1236    failure type (see protocol specs for all protocol failure types). */
1237
1238 void silc_failure(SilcClient client, SilcClientConnection conn, 
1239                   SilcProtocol protocol, void *failure)
1240 {
1241   SILC_LOG_DEBUG(("Start"));
1242
1243   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1244     SilcSKEStatus status = (SilcSKEStatus)failure;
1245     
1246     if (status == SILC_SKE_STATUS_BAD_VERSION)
1247       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1248                          SILCTXT_KE_BAD_VERSION);
1249     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1250       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1251                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1252     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1253       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1254                          SILCTXT_KE_UNKNOWN_GROUP);
1255     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1256       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1257                          SILCTXT_KE_UNKNOWN_CIPHER);
1258     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1259       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1260                          SILCTXT_KE_UNKNOWN_PKCS);
1261     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1262       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1263                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1264     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1265       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1266                          SILCTXT_KE_UNKNOWN_HMAC);
1267     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1268       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1269                          SILCTXT_KE_INCORRECT_SIGNATURE);
1270     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1271       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1272                          SILCTXT_KE_INVALID_COOKIE);
1273   }
1274
1275   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1276     uint32 err = (uint32)failure;
1277
1278     if (err == SILC_AUTH_FAILED)
1279       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1280                          SILCTXT_AUTH_FAILED);
1281   }
1282 }
1283
1284 /* Asks whether the user would like to perform the key agreement protocol.
1285    This is called after we have received an key agreement packet or an
1286    reply to our key agreement packet. This returns TRUE if the user wants
1287    the library to perform the key agreement protocol and FALSE if it is not
1288    desired (application may start it later by calling the function
1289    silc_client_perform_key_agreement). */
1290
1291 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1292                        SilcClientEntry client_entry, const char *hostname,
1293                        uint16 port, SilcKeyAgreementCallback *completion,
1294                        void **context)
1295 {
1296   char portstr[12];
1297
1298   SILC_LOG_DEBUG(("Start"));
1299
1300   /* We will just display the info on the screen and return FALSE and user
1301      will have to start the key agreement with a command. */
1302
1303   if (hostname) 
1304     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1305
1306   if (!hostname)
1307     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1308                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1309   else
1310     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1311                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1312                        client_entry->nickname, hostname, portstr);
1313
1314   *completion = NULL;
1315   *context = NULL;
1316
1317   return FALSE;
1318 }
1319
1320 void silc_ftp(SilcClient client, SilcClientConnection conn,
1321               SilcClientEntry client_entry, uint32 session_id,
1322               const char *hostname, uint16 port)
1323 {
1324   SILC_SERVER_REC *server;
1325   char portstr[12];
1326   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1327
1328   SILC_LOG_DEBUG(("Start"));
1329
1330   server = conn->context;
1331
1332   ftp->client_entry = client_entry;
1333   ftp->session_id = session_id;
1334   ftp->send = FALSE;
1335   ftp->conn = conn;
1336   silc_dlist_add(server->ftp_sessions, ftp);
1337   server->current_session = ftp;
1338
1339   if (hostname) 
1340     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1341
1342   if (!hostname)
1343     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1344                        SILCTXT_FILE_REQUEST, client_entry->nickname);
1345   else
1346     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1347                        SILCTXT_FILE_REQUEST_HOST, 
1348                        client_entry->nickname, hostname, portstr);
1349 }
1350
1351 /* SILC client operations */
1352 SilcClientOperations ops = {
1353   silc_say,
1354   silc_channel_message,
1355   silc_private_message,
1356   silc_notify,
1357   silc_command,
1358   silc_command_reply,
1359   silc_connect,
1360   silc_disconnect,
1361   silc_get_auth_method,
1362   silc_verify_public_key,
1363   silc_ask_passphrase,
1364   silc_failure,
1365   silc_key_agreement,
1366   silc_ftp,
1367 };