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