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