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