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