updates.
[silc.git] / apps / irssi / src / silc / core / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20
21 #include "module.h"
22 #include "chat-protocols.h"
23 #include "args.h"
24
25 #include "chatnets.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
32
33 #include "signals.h"
34 #include "levels.h"
35 #include "settings.h"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
38 #include "fe-common/core/keyboard.h"
39 #include "fe-common/silc/module-formats.h"
40
41 static void 
42 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
43                                 const char *name, SilcSocketType conn_type, 
44                                 unsigned char *pk, uint32 pk_len, 
45                                 SilcSKEPKType pk_type,
46                                 SilcVerifyPublicKey completion, void *context);
47
48 void silc_say(SilcClient client, SilcClientConnection conn,
49               SilcClientMessageType type, char *msg, ...)
50 {
51   SILC_SERVER_REC *server;
52   va_list va;
53   char *str;
54
55   server = conn == NULL ? NULL : conn->context;
56   
57   va_start(va, msg);
58   str = g_strdup_vprintf(msg, va);
59   printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
60   g_free(str);
61   va_end(va);
62 }
63
64 void silc_say_error(char *msg, ...)
65 {
66   va_list va;
67   char *str;
68
69   va_start(va, msg);
70   str = g_strdup_vprintf(msg, va);
71   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
72
73   g_free(str);
74   va_end(va);
75 }
76
77 /* Message for a channel. The `sender' is the nickname of the sender 
78    received in the packet. The `channel_name' is the name of the channel. */
79
80 void silc_channel_message(SilcClient client, SilcClientConnection conn,
81                           SilcClientEntry sender, SilcChannelEntry channel,
82                           SilcMessageFlags flags, char *msg)
83 {
84   SILC_SERVER_REC *server;
85   SILC_NICK_REC *nick;
86   SILC_CHANNEL_REC *chanrec;
87   
88   SILC_LOG_DEBUG(("Start"));
89
90   server = conn == NULL ? NULL : conn->context;
91   chanrec = silc_channel_find_entry(server, channel);
92   if (!chanrec)
93     return;
94   
95   nick = silc_nicklist_find(chanrec, sender);
96   if (!nick) {
97     /* We didn't find client but it clearly exists, add it. */
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       break;
660     }
661
662   case SILC_COMMAND_NICK: 
663     {
664       SilcClientEntry client = va_arg(vp, SilcClientEntry);
665       char *old;
666       
667       if (!success)
668         return;
669
670       old = g_strdup(server->nick);
671       server_change_nick(SERVER(server), client->nickname);
672       nicklist_rename_unique(SERVER(server),
673                              server->conn->local_entry, server->nick,
674                              client, client->nickname);
675       
676       signal_emit("message own_nick", 4, server, server->nick, old, "");
677       g_free(old);
678       break;
679     }
680     
681   case SILC_COMMAND_LIST:
682     {
683       char *topic, *name;
684       int usercount;
685       char users[20];
686       
687       if (!success)
688         return;
689       
690       (void)va_arg(vp, SilcChannelEntry);
691       name = va_arg(vp, char *);
692       topic = va_arg(vp, char *);
693       usercount = va_arg(vp, int);
694       
695       if (status == SILC_STATUS_LIST_START ||
696           status == SILC_STATUS_OK)
697         printformat_module("fe-common/silc", server, NULL,
698                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
699
700       if (!usercount)
701         snprintf(users, sizeof(users) - 1, "N/A");
702       else
703         snprintf(users, sizeof(users) - 1, "%d", usercount);
704       printformat_module("fe-common/silc", server, NULL,
705                          MSGLEVEL_CRAP, SILCTXT_LIST,
706                          name, users, topic ? topic : "");
707     }
708     break;
709     
710   case SILC_COMMAND_UMODE:
711     {
712       uint32 mode;
713       
714       if (!success)
715         return;
716       
717       mode = va_arg(vp, uint32);
718       
719       if (mode & SILC_UMODE_SERVER_OPERATOR &&
720           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
721         printformat_module("fe-common/silc", server, NULL,
722                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
723
724       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
725           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
726         printformat_module("fe-common/silc", server, NULL,
727                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
728
729       server->umode = mode;
730     }
731     break;
732     
733   case SILC_COMMAND_OPER:
734     if (!success)
735       return;
736
737     printformat_module("fe-common/silc", server, NULL,
738                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
739     break;
740     
741   case SILC_COMMAND_SILCOPER:
742     if (!success)
743       return;
744
745     printformat_module("fe-common/silc", server, NULL,
746                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
747     break;
748     
749   case SILC_COMMAND_USERS: 
750     {
751       SilcHashTableList htl;
752       SilcChannelEntry channel;
753       SilcChannelUser chu;
754       
755       if (!success)
756         return;
757       
758       channel = va_arg(vp, SilcChannelEntry);
759       
760       printformat_module("fe-common/silc", server, channel->channel_name,
761                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
762                          channel->channel_name);
763
764       silc_hash_table_list(channel->user_list, &htl);
765       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
766         SilcClientEntry e = chu->client;
767         char stat[5], *mode;
768
769         if (!e->nickname)
770           continue;
771         
772         memset(stat, 0, sizeof(stat));
773         mode = silc_client_chumode_char(chu->mode);
774         if (e->mode & SILC_UMODE_GONE)
775           strcat(stat, "G");
776         else
777           strcat(stat, "H");
778         if (mode)
779           strcat(stat, mode);
780
781         printformat_module("fe-common/silc", server, channel->channel_name,
782                            MSGLEVEL_CRAP, SILCTXT_USERS,
783                            e->nickname, stat, 
784                            e->username ? e->username : "",
785                            e->hostname ? e->hostname : "",
786                            e->realname ? e->realname : "");
787         if (mode)
788           silc_free(mode);
789       }
790       silc_hash_table_list_reset(&htl);
791     }
792     break;
793
794   case SILC_COMMAND_BAN:
795     {
796       SilcChannelEntry channel;
797       char *ban_list;
798       
799       if (!success)
800         return;
801       
802       channel = va_arg(vp, SilcChannelEntry);
803       ban_list = va_arg(vp, char *);
804       
805       if (ban_list)
806         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
807                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
808                            ban_list);
809       else
810         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
811                            SILCTXT_CHANNEL_NO_BAN_LIST, 
812                            channel->channel_name);
813     }
814     break;
815     
816   case SILC_COMMAND_GETKEY:
817     {
818       SilcIdType id_type;
819       void *entry;
820       SilcPublicKey public_key;
821       unsigned char *pk;
822       uint32 pk_len;
823       GetkeyContext getkey;
824       char *name;
825       
826       if (!success)
827         return;
828       
829       id_type = va_arg(vp, uint32);
830       entry = va_arg(vp, void *);
831       public_key = va_arg(vp, SilcPublicKey);
832
833       if (public_key) {
834         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
835
836         getkey = silc_calloc(1, sizeof(*getkey));
837         getkey->entry = entry;
838         getkey->id_type = id_type;
839         getkey->client = client;
840         getkey->conn = conn;
841         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
842
843         name = (id_type == SILC_ID_CLIENT ? 
844                 ((SilcClientEntry)entry)->nickname :
845                 ((SilcServerEntry)entry)->server_name);
846
847         silc_verify_public_key_internal(client, conn, name,
848                                         (id_type == SILC_ID_CLIENT ?
849                                          SILC_SOCKET_TYPE_CLIENT :
850                                          SILC_SOCKET_TYPE_SERVER),
851                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
852                                         silc_getkey_cb, getkey);
853         silc_free(pk);
854       } else {
855         printformat_module("fe-common/silc", server, NULL,
856                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
857       }
858     }
859     break;
860
861   case SILC_COMMAND_INFO:
862     {
863       SilcServerEntry server_entry;
864       char *server_name;
865       char *server_info;
866
867       if (!success)
868         return;
869       
870       server_entry = va_arg(vp, SilcServerEntry);
871       server_name = va_arg(vp, char *);
872       server_info = va_arg(vp, char *);
873
874       if (server_name && server_info )
875         {
876           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
877           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
878         }
879     }
880     break;
881     
882   case SILC_COMMAND_TOPIC:
883     {
884       SilcChannelEntry channel;
885       char *topic;
886       
887       if (!success)
888         return;
889       
890       channel = va_arg(vp, SilcChannelEntry);
891       topic = va_arg(vp, char *);
892       
893       if (topic) {
894         chanrec = silc_channel_find_entry(server, channel);
895         if (chanrec) {
896           g_free_not_null(chanrec->topic);
897           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
898           signal_emit("channel topic changed", 1, chanrec);
899         }
900         printformat_module("fe-common/silc", server, channel->channel_name,
901                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
902                            channel->channel_name, topic);
903       } else {
904         printformat_module("fe-common/silc", server, channel->channel_name,
905                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
906                            channel->channel_name);
907       }
908     }
909     break;
910
911   }
912
913   va_end(vp);
914 }
915
916 typedef struct {
917   SilcClient client;
918   SilcClientConnection conn;
919   char *filename;
920   char *entity;
921   char *entity_name;
922   unsigned char *pk;
923   uint32 pk_len;
924   SilcSKEPKType pk_type;
925   SilcVerifyPublicKey completion;
926   void *context;
927 } *PublicKeyVerify;
928
929 static void verify_public_key_completion(const char *line, void *context)
930 {
931   PublicKeyVerify verify = (PublicKeyVerify)context;
932
933   if (line[0] == 'Y' || line[0] == 'y') {
934     /* Call the completion */
935     if (verify->completion)
936       verify->completion(TRUE, verify->context);
937
938     /* Save the key for future checking */
939     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
940                                    verify->pk_len, SILC_PKCS_FILE_PEM);
941   } else {
942     /* Call the completion */
943     if (verify->completion)
944       verify->completion(FALSE, verify->context);
945
946     printformat_module("fe-common/silc", NULL, NULL,
947                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
948                        verify->entity_name ? verify->entity_name :
949                        verify->entity);
950   }
951
952   silc_free(verify->filename);
953   silc_free(verify->entity);
954   silc_free(verify->entity_name);
955   silc_free(verify->pk);
956   silc_free(verify);
957 }
958
959 /* Internal routine to verify public key. If the `completion' is provided
960    it will be called to indicate whether public was verified or not. For
961    server/router public key this will check for filename that includes the
962    remote host's IP address and remote host's hostname. */
963
964 static void 
965 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
966                                 const char *name, SilcSocketType conn_type, 
967                                 unsigned char *pk, uint32 pk_len, 
968                                 SilcSKEPKType pk_type,
969                                 SilcVerifyPublicKey completion, void *context)
970 {
971   int i;
972   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
973   char *fingerprint, *babbleprint, *format;
974   struct passwd *pw;
975   struct stat st;
976   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
977                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
978                   "server" : "client");
979   PublicKeyVerify verify;
980
981   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
982     printformat_module("fe-common/silc", NULL, NULL,
983                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
984                        entity, pk_type);
985     if (completion)
986       completion(FALSE, context);
987     return;
988   }
989
990   pw = getpwuid(getuid());
991   if (!pw) {
992     if (completion)
993       completion(FALSE, context);
994     return;
995   }
996
997   memset(filename, 0, sizeof(filename));
998   memset(filename2, 0, sizeof(filename2));
999   memset(file, 0, sizeof(file));
1000
1001   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1002       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1003     if (!name) {
1004       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1005                conn->sock->ip, conn->sock->port);
1006       snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
1007                pw->pw_dir, entity, file);
1008       
1009       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1010                conn->sock->hostname, conn->sock->port);
1011       snprintf(filename2, sizeof(filename2) - 1, "%s/.silc/%skeys/%s", 
1012                pw->pw_dir, entity, file);
1013       
1014       ipf = filename;
1015       hostf = filename2;
1016     } else {
1017       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1018                name, conn->sock->port);
1019       snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
1020                pw->pw_dir, entity, file);
1021       
1022       ipf = filename;
1023     }
1024   } else {
1025     /* Replace all whitespaces with `_'. */
1026     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1027     for (i = 0; i < strlen(fingerprint); i++)
1028       if (fingerprint[i] == ' ')
1029         fingerprint[i] = '_';
1030     
1031     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1032     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
1033              pw->pw_dir, entity, file);
1034     silc_free(fingerprint);
1035
1036     ipf = filename;
1037   }
1038
1039   /* Take fingerprint of the public key */
1040   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1041   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1042
1043   verify = silc_calloc(1, sizeof(*verify));
1044   verify->client = client;
1045   verify->conn = conn;
1046   verify->filename = strdup(ipf);
1047   verify->entity = strdup(entity);
1048   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1049                          (name ? strdup(name) : strdup(conn->sock->hostname))
1050                          : NULL);
1051   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1052   memcpy(verify->pk, pk, pk_len);
1053   verify->pk_len = pk_len;
1054   verify->pk_type = pk_type;
1055   verify->completion = completion;
1056   verify->context = context;
1057
1058   /* Check whether this key already exists */
1059   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1060     /* Key does not exist, ask user to verify the key and save it */
1061
1062     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1063                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1064                        verify->entity_name : entity);
1065     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1066                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1067     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1068                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1069     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1070                              SILCTXT_PUBKEY_ACCEPT);
1071     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1072                             format, 0, verify);
1073     g_free(format);
1074     silc_free(fingerprint);
1075     return;
1076   } else {
1077     /* The key already exists, verify it. */
1078     SilcPublicKey public_key;
1079     unsigned char *encpk;
1080     uint32 encpk_len;
1081
1082     /* Load the key file, try for both IP filename and hostname filename */
1083     if (!silc_pkcs_load_public_key(ipf, &public_key, 
1084                                    SILC_PKCS_FILE_PEM) &&
1085         !silc_pkcs_load_public_key(ipf, &public_key, 
1086                                    SILC_PKCS_FILE_BIN) &&
1087         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
1088                                                SILC_PKCS_FILE_PEM) &&
1089                     !silc_pkcs_load_public_key(hostf, &public_key, 
1090                                                SILC_PKCS_FILE_BIN)))) {
1091       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1092                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1093                          verify->entity_name : entity);
1094       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1095                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1096       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1097                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1098       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1099                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1100       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1101                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1102       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1103                               format, 0, verify);
1104       g_free(format);
1105       silc_free(fingerprint);
1106       return;
1107     }
1108
1109     /* Encode the key data */
1110     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1111     if (!encpk) {
1112       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1113                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1114                          verify->entity_name : entity);
1115       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1116                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1117       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1118                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1119       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1120                          SILCTXT_PUBKEY_MALFORMED, entity);
1121       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1122                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1123       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1124                               format, 0, verify);
1125       g_free(format);
1126       silc_free(fingerprint);
1127       return;
1128     }
1129
1130     /* Compare the keys */
1131     if (memcmp(encpk, pk, encpk_len)) {
1132       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1133                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1134                          verify->entity_name : entity);
1135       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1136                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1137       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1138                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1139       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1140                          SILCTXT_PUBKEY_NO_MATCH, entity);
1141       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1142                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1143       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1144                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1145
1146       /* Ask user to verify the key and save it */
1147       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1148                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1149       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1150                               format, 0, verify);
1151       g_free(format);
1152       silc_free(fingerprint);
1153       return;
1154     }
1155
1156     /* Local copy matched */
1157     if (completion)
1158       completion(TRUE, context);
1159     silc_free(fingerprint);
1160   }
1161 }
1162
1163 /* Verifies received public key. The `conn_type' indicates which entity
1164    (server, client etc.) has sent the public key. If user decides to trust
1165    the key may be saved as trusted public key for later use. The 
1166    `completion' must be called after the public key has been verified. */
1167
1168 void 
1169 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1170                        SilcSocketType conn_type, unsigned char *pk, 
1171                        uint32 pk_len, SilcSKEPKType pk_type,
1172                        SilcVerifyPublicKey completion, void *context)
1173 {
1174   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1175                                   pk_len, pk_type,
1176                                   completion, context);
1177 }
1178
1179 /* Asks passphrase from user on the input line. */
1180
1181 typedef struct {
1182   SilcAskPassphrase completion;
1183   void *context;
1184 } *AskPassphrase;
1185
1186 void ask_passphrase_completion(const char *passphrase, void *context)
1187 {
1188   AskPassphrase p = (AskPassphrase)context;
1189   p->completion((unsigned char *)passphrase, 
1190                 passphrase ? strlen(passphrase) : 0, p->context);
1191   silc_free(p);
1192 }
1193
1194 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1195                          SilcAskPassphrase completion, void *context)
1196 {
1197   AskPassphrase p = silc_calloc(1, sizeof(*p));
1198   p->completion = completion;
1199   p->context = context;
1200
1201   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1202                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1203 }
1204
1205 typedef struct {
1206   SilcGetAuthMeth completion;
1207   void *context;
1208 } *InternalGetAuthMethod;
1209
1210 /* Callback called when we've received the authentication method information
1211    from the server after we've requested it. This will get the authentication
1212    data from the user if needed. */
1213
1214 static void silc_get_auth_method_callback(SilcClient client,
1215                                           SilcClientConnection conn,
1216                                           SilcAuthMethod auth_meth,
1217                                           void *context)
1218 {
1219   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1220
1221   SILC_LOG_DEBUG(("Start"));
1222
1223   switch (auth_meth) {
1224   case SILC_AUTH_NONE:
1225     /* No authentication required. */
1226     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1227     break;
1228   case SILC_AUTH_PASSWORD:
1229     /* Do not ask the passphrase from user, the library will ask it if
1230        we do not provide it here. */
1231     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1232     break;
1233   case SILC_AUTH_PUBLIC_KEY:
1234     /* Do not get the authentication data now, the library will generate
1235        it using our default key, if we do not provide it here. */
1236     /* XXX In the future when we support multiple local keys and multiple
1237        local certificates we will need to ask from user which one to use. */
1238     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1239     break;
1240   }
1241
1242   silc_free(internal);
1243 }
1244
1245 /* Find authentication method and authentication data by hostname and
1246    port. The hostname may be IP address as well. The found authentication
1247    method and authentication data is returned to `auth_meth', `auth_data'
1248    and `auth_data_len'. The function returns TRUE if authentication method
1249    is found and FALSE if not. `conn' may be NULL. */
1250
1251 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1252                           char *hostname, uint16 port,
1253                           SilcGetAuthMeth completion, void *context)
1254 {
1255   InternalGetAuthMethod internal;
1256
1257   SILC_LOG_DEBUG(("Start"));
1258
1259   /* XXX must resolve from configuration whether this connection has
1260      any specific authentication data */
1261
1262   /* If we do not have this connection configured by the user in a
1263      configuration file then resolve the authentication method from the
1264      server for this session. */
1265   internal = silc_calloc(1, sizeof(*internal));
1266   internal->completion = completion;
1267   internal->context = context;
1268
1269   silc_client_request_authentication_method(client, conn, 
1270                                             silc_get_auth_method_callback,
1271                                             internal);
1272 }
1273
1274 /* Notifies application that failure packet was received.  This is called
1275    if there is some protocol active in the client.  The `protocol' is the
1276    protocol context.  The `failure' is opaque pointer to the failure
1277    indication.  Note, that the `failure' is protocol dependant and application
1278    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1279    failure type (see protocol specs for all protocol failure types). */
1280
1281 void silc_failure(SilcClient client, SilcClientConnection conn, 
1282                   SilcProtocol protocol, void *failure)
1283 {
1284   SILC_LOG_DEBUG(("Start"));
1285
1286   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1287     SilcSKEStatus status = (SilcSKEStatus)failure;
1288     
1289     if (status == SILC_SKE_STATUS_BAD_VERSION)
1290       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1291                          SILCTXT_KE_BAD_VERSION);
1292     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1293       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1294                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1295     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1296       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1297                          SILCTXT_KE_UNKNOWN_GROUP);
1298     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1299       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1300                          SILCTXT_KE_UNKNOWN_CIPHER);
1301     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1302       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1303                          SILCTXT_KE_UNKNOWN_PKCS);
1304     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1305       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1306                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1307     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1308       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1309                          SILCTXT_KE_UNKNOWN_HMAC);
1310     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1311       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1312                          SILCTXT_KE_INCORRECT_SIGNATURE);
1313     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1314       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1315                          SILCTXT_KE_INVALID_COOKIE);
1316   }
1317
1318   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1319     uint32 err = (uint32)failure;
1320
1321     if (err == SILC_AUTH_FAILED)
1322       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1323                          SILCTXT_AUTH_FAILED);
1324   }
1325 }
1326
1327 /* Asks whether the user would like to perform the key agreement protocol.
1328    This is called after we have received an key agreement packet or an
1329    reply to our key agreement packet. This returns TRUE if the user wants
1330    the library to perform the key agreement protocol and FALSE if it is not
1331    desired (application may start it later by calling the function
1332    silc_client_perform_key_agreement). */
1333
1334 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1335                        SilcClientEntry client_entry, const char *hostname,
1336                        uint16 port, SilcKeyAgreementCallback *completion,
1337                        void **context)
1338 {
1339   char portstr[12];
1340
1341   SILC_LOG_DEBUG(("Start"));
1342
1343   /* We will just display the info on the screen and return FALSE and user
1344      will have to start the key agreement with a command. */
1345
1346   if (hostname) 
1347     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1348
1349   if (!hostname)
1350     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1351                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1352   else
1353     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1354                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1355                        client_entry->nickname, hostname, portstr);
1356
1357   *completion = NULL;
1358   *context = NULL;
1359
1360   return FALSE;
1361 }
1362
1363 void silc_ftp(SilcClient client, SilcClientConnection conn,
1364               SilcClientEntry client_entry, uint32 session_id,
1365               const char *hostname, uint16 port)
1366 {
1367   SILC_SERVER_REC *server;
1368   char portstr[12];
1369   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
1370
1371   SILC_LOG_DEBUG(("Start"));
1372
1373   server = conn->context;
1374
1375   ftp->client_entry = client_entry;
1376   ftp->session_id = session_id;
1377   ftp->send = FALSE;
1378   ftp->conn = conn;
1379   silc_dlist_add(server->ftp_sessions, ftp);
1380   server->current_session = ftp;
1381
1382   if (hostname) 
1383     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1384
1385   if (!hostname)
1386     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1387                        SILCTXT_FILE_REQUEST, client_entry->nickname);
1388   else
1389     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1390                        SILCTXT_FILE_REQUEST_HOST, 
1391                        client_entry->nickname, hostname, portstr);
1392 }
1393
1394 /* SILC client operations */
1395 SilcClientOperations ops = {
1396   silc_say,
1397   silc_channel_message,
1398   silc_private_message,
1399   silc_notify,
1400   silc_command,
1401   silc_command_reply,
1402   silc_connect,
1403   silc_disconnect,
1404   silc_get_auth_method,
1405   silc_verify_public_key,
1406   silc_ask_passphrase,
1407   silc_failure,
1408   silc_key_agreement,
1409   silc_ftp,
1410 };