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 #include "core.h"
42
43 static void 
44 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
45                                 const char *name, SilcSocketType conn_type, 
46                                 unsigned char *pk, SilcUInt32 pk_len, 
47                                 SilcSKEPKType pk_type,
48                                 SilcVerifyPublicKey completion, void *context);
49
50 static void silc_get_umode_string(SilcUInt32 mode, char *buf, 
51                                   SilcUInt32 buf_size)
52 {
53   if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
54       (mode & SILC_UMODE_ROUTER_OPERATOR)) {
55     strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
56            "[server operator]" :
57            (mode & SILC_UMODE_ROUTER_OPERATOR) ?
58            "[SILC operator]" : "[unknown mode]");
59   }
60   if (mode & SILC_UMODE_GONE)
61     strcat(buf, " [away]");
62   if (mode & SILC_UMODE_INDISPOSED)
63     strcat(buf, " [indisposed]");
64   if (mode & SILC_UMODE_BUSY)
65     strcat(buf, " [busy]");
66   if (mode & SILC_UMODE_PAGE)
67     strcat(buf, " [page to reach]");
68   if (mode & SILC_UMODE_HYPER)
69     strcat(buf, " [hyper active]");
70   if (mode & SILC_UMODE_ROBOT)
71     strcat(buf, " [robot]");
72   if (mode & SILC_UMODE_ANONYMOUS)
73     strcat(buf, " [anonymous]");
74   if (mode & SILC_UMODE_BLOCK_PRIVMSG)
75     strcat(buf, " [blocks private messages]");
76   if (mode & SILC_UMODE_DETACHED)
77     strcat(buf, " [detached]");
78 }
79
80 void silc_say(SilcClient client, SilcClientConnection conn,
81               SilcClientMessageType type, char *msg, ...)
82 {
83   SILC_SERVER_REC *server;
84   va_list va;
85   char *str;
86
87   server = conn == NULL ? NULL : conn->context;
88   
89   va_start(va, msg);
90   str = g_strdup_vprintf(msg, va);
91   printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
92   g_free(str);
93   va_end(va);
94 }
95
96 void silc_say_error(char *msg, ...)
97 {
98   va_list va;
99   char *str;
100
101   va_start(va, msg);
102   str = g_strdup_vprintf(msg, va);
103   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
104
105   g_free(str);
106   va_end(va);
107 }
108
109 /* Message for a channel. The `sender' is the nickname of the sender 
110    received in the packet. The `channel_name' is the name of the channel. */
111
112 void silc_channel_message(SilcClient client, SilcClientConnection conn,
113                           SilcClientEntry sender, SilcChannelEntry channel,
114                           SilcMessageFlags flags, const unsigned char *message,
115                           SilcUInt32 message_len)
116 {
117   SILC_SERVER_REC *server;
118   SILC_NICK_REC *nick;
119   SILC_CHANNEL_REC *chanrec;
120   
121   SILC_LOG_DEBUG(("Start"));
122
123   if (!message)
124     return;
125
126   server = conn == NULL ? NULL : conn->context;
127   chanrec = silc_channel_find_entry(server, channel);
128   if (!chanrec)
129     return;
130   
131   nick = silc_nicklist_find(chanrec, sender);
132   if (!nick) {
133     /* We didn't find client but it clearly exists, add it. */
134     SilcChannelUser chu = silc_client_on_channel(channel, sender);
135     if (chu)
136       nick = silc_nicklist_insert(chanrec, chu, FALSE);
137   }
138
139   if (flags & SILC_MESSAGE_FLAG_DATA) {
140     /* MIME object received, try to display it as well as we can */
141     char type[128];
142     unsigned char *data;
143
144     memset(type, 0, sizeof(type));
145     if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
146                          NULL, 0, &data, NULL))
147       return;
148
149     /* Then figure out what we can display */
150     if (strstr(type, "text/") && !strstr(type, "text/t140") &&
151         !strstr(type, "text/vnd")) {
152       /* It is something textual, display it */
153       message = (const unsigned char *)data;
154     } else {
155       printformat_module("fe-common/silc", server, channel->channel_name,
156                          MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA,
157                          nick == NULL ? "[<unknown>]" : nick->nick, type);
158       message = NULL;
159     }
160   }
161
162   if (!message)
163     return;
164
165   if (flags & SILC_MESSAGE_FLAG_ACTION)
166     printformat_module("fe-common/silc", server, channel->channel_name,
167                        MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, 
168                        nick == NULL ? "[<unknown>]" : nick->nick, message);
169   else if (flags & SILC_MESSAGE_FLAG_NOTICE)
170     printformat_module("fe-common/silc", server, channel->channel_name,
171                        MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, 
172                        nick == NULL ? "[<unknown>]" : nick->nick, message);
173   else
174     signal_emit("message public", 6, server, message,
175                 nick == NULL ? "[<unknown>]" : nick->nick,
176                 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
177                 chanrec->name, nick);
178 }
179
180 /* Private message to the client. The `sender' is the nickname of the
181    sender received in the packet. */
182
183 void silc_private_message(SilcClient client, SilcClientConnection conn,
184                           SilcClientEntry sender, SilcMessageFlags flags,
185                           const unsigned char *message,
186                           SilcUInt32 message_len)
187 {
188   SILC_SERVER_REC *server;
189   char userhost[256];
190   
191   SILC_LOG_DEBUG(("Start"));
192
193   server = conn == NULL ? NULL : conn->context;
194   memset(userhost, 0, sizeof(userhost));
195   if (sender->username)
196     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
197              sender->username, sender->hostname);
198
199   if (flags & SILC_MESSAGE_FLAG_DATA) {
200     /* MIME object received, try to display it as well as we can */
201     char type[128];
202     unsigned char *data;
203
204     memset(type, 0, sizeof(type));
205     if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
206                          NULL, 0, &data, NULL))
207       return;
208
209     /* Then figure out what we can display */
210     if (strstr(type, "text/") && !strstr(type, "text/t140") &&
211         !strstr(type, "text/vnd")) {
212       /* It is something textual, display it */
213       message = (const unsigned char *)data;
214     } else {
215       printformat_module("fe-common/silc", server, NULL,
216                          MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA,
217                          sender->nickname ? sender->nickname : "[<unknown>]",
218                          type);
219       message = NULL;
220     }
221   }
222
223   if (!message)
224     return;
225
226   signal_emit("message private", 4, server, message,
227               sender->nickname ? sender->nickname : "[<unknown>]",
228               sender->username ? userhost : NULL);
229 }
230
231 /* Notify message to the client. The notify arguments are sent in the
232    same order as servers sends them. The arguments are same as received
233    from the server except for ID's.  If ID is received application receives
234    the corresponding entry to the ID. For example, if Client ID is received
235    application receives SilcClientEntry.  Also, if the notify type is
236    for channel the channel entry is sent to application (even if server
237    does not send it). */
238
239 void silc_notify(SilcClient client, SilcClientConnection conn,
240                  SilcNotifyType type, ...)
241 {
242   va_list va;
243   SILC_SERVER_REC *server;
244   SILC_CHANNEL_REC *chanrec;
245   SILC_NICK_REC *nickrec;
246   SilcClientEntry client_entry, client_entry2;
247   SilcChannelEntry channel, channel2;
248   SilcServerEntry server_entry;
249   SilcIdType idtype;
250   void *entry;
251   SilcUInt32 mode;
252   char buf[512];
253   char *name, *tmp;
254   GSList *list1, *list_tmp;
255
256   SILC_LOG_DEBUG(("Start"));
257
258   va_start(va, type);
259
260   server = conn == NULL ? NULL : conn->context;
261   
262   switch(type) {
263   case SILC_NOTIFY_TYPE_NONE:
264     /* Some generic notice from server */
265     printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
266     break;
267
268   case SILC_NOTIFY_TYPE_INVITE:
269     /*
270      * Invited or modified invite list.
271      */
272
273     SILC_LOG_DEBUG(("Notify: INVITE"));
274
275     channel = va_arg(va, SilcChannelEntry);
276     name = va_arg(va, char *);
277     client_entry = va_arg(va, SilcClientEntry);
278
279     memset(buf, 0, sizeof(buf));
280     snprintf(buf, sizeof(buf) - 1, "%s@%s",
281              client_entry->username, client_entry->hostname);
282     signal_emit("message invite", 4, server, channel ? channel->channel_name :
283                 name, client_entry->nickname, buf);
284     break;
285
286   case SILC_NOTIFY_TYPE_JOIN:
287     /*
288      * Joined channel.
289      */
290  
291     SILC_LOG_DEBUG(("Notify: JOIN"));
292
293     client_entry = va_arg(va, SilcClientEntry);
294     channel = va_arg(va, SilcChannelEntry);
295
296     if (client_entry == server->conn->local_entry) {
297       /* You joined to channel */
298       chanrec = silc_channel_find(server, channel->channel_name);
299       if (chanrec != NULL && !chanrec->joined)
300         chanrec->entry = channel;
301     } else {
302       chanrec = silc_channel_find_entry(server, channel);
303       if (chanrec != NULL) {
304         SilcChannelUser chu = silc_client_on_channel(channel, client_entry);
305         if (chu)
306           nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
307       }
308     }
309     
310     memset(buf, 0, sizeof(buf));
311     if (client_entry->username)
312     snprintf(buf, sizeof(buf) - 1, "%s@%s",
313              client_entry->username, client_entry->hostname);
314     signal_emit("message join", 4, server, channel->channel_name,
315                 client_entry->nickname,
316                 client_entry->username == NULL ? "" : buf);
317     break;
318
319   case SILC_NOTIFY_TYPE_LEAVE:
320     /*
321      * Left a channel.
322      */
323
324     SILC_LOG_DEBUG(("Notify: LEAVE"));
325
326     client_entry = va_arg(va, SilcClientEntry);
327     channel = va_arg(va, SilcChannelEntry);
328     
329     memset(buf, 0, sizeof(buf));
330     if (client_entry->username)
331       snprintf(buf, sizeof(buf) - 1, "%s@%s",
332                client_entry->username, client_entry->hostname);
333     signal_emit("message part", 5, server, channel->channel_name,
334                 client_entry->nickname,  client_entry->username ? 
335                 buf : "", client_entry->nickname);
336     
337     chanrec = silc_channel_find_entry(server, channel);
338     if (chanrec != NULL) {
339       nickrec = silc_nicklist_find(chanrec, client_entry);
340       if (nickrec != NULL)
341         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
342     }
343     break;
344
345   case SILC_NOTIFY_TYPE_SIGNOFF:
346     /*
347      * Left the network.
348      */
349
350     SILC_LOG_DEBUG(("Notify: SIGNOFF"));
351
352     client_entry = va_arg(va, SilcClientEntry);
353     tmp = va_arg(va, char *);
354     
355     silc_server_free_ftp(server, client_entry);
356     
357     memset(buf, 0, sizeof(buf));
358     if (client_entry->username)
359       snprintf(buf, sizeof(buf) - 1, "%s@%s",
360                client_entry->username, client_entry->hostname);
361     signal_emit("message quit", 4, server, client_entry->nickname,
362                 client_entry->username ? buf : "", 
363                 tmp ? tmp : "");
364     
365     list1 = nicklist_get_same_unique(SERVER(server), client_entry);
366     for (list_tmp = list1; list_tmp != NULL; list_tmp = 
367            list_tmp->next->next) {
368       CHANNEL_REC *channel = list_tmp->data;
369       NICK_REC *nickrec = list_tmp->next->data;
370       
371       nicklist_remove(channel, nickrec);
372     }
373     break;
374
375   case SILC_NOTIFY_TYPE_TOPIC_SET:
376     /*
377      * Changed topic.
378      */
379
380     SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
381
382     idtype = va_arg(va, int);
383     entry = va_arg(va, void *);
384     tmp = va_arg(va, char *);
385     channel = va_arg(va, SilcChannelEntry);
386     
387     chanrec = silc_channel_find_entry(server, channel);
388     if (chanrec != NULL) {
389       g_free_not_null(chanrec->topic);
390       chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
391       signal_emit("channel topic changed", 1, chanrec);
392     }
393     
394     if (idtype == SILC_ID_CLIENT) {
395       client_entry = (SilcClientEntry)entry;
396       memset(buf, 0, sizeof(buf));
397       snprintf(buf, sizeof(buf) - 1, "%s@%s",
398                client_entry->username, client_entry->hostname);
399       signal_emit("message topic", 5, server, channel->channel_name,
400                   tmp, client_entry->nickname, buf);
401     } else if (idtype == SILC_ID_SERVER) {
402       server_entry = (SilcServerEntry)entry;
403       signal_emit("message topic", 5, server, channel->channel_name,
404                   tmp, server_entry->server_name, 
405                   server_entry->server_name);
406     } else if (idtype == SILC_ID_CHANNEL) {
407       channel = (SilcChannelEntry)entry;
408       signal_emit("message topic", 5, server, channel->channel_name,
409                   tmp, channel->channel_name, channel->channel_name);
410     }
411     break;
412
413   case SILC_NOTIFY_TYPE_NICK_CHANGE:
414     /*
415      * Changed nickname.
416      */
417
418     SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
419
420     client_entry = va_arg(va, SilcClientEntry);
421     client_entry2 = va_arg(va, SilcClientEntry);
422
423     if (!strcmp(client_entry->nickname, client_entry2->nickname))
424       break;
425     
426     memset(buf, 0, sizeof(buf));
427     snprintf(buf, sizeof(buf) - 1, "%s@%s",
428              client_entry2->username, client_entry2->hostname);
429     nicklist_rename_unique(SERVER(server),
430                            client_entry, client_entry->nickname,
431                            client_entry2, client_entry2->nickname);
432     signal_emit("message nick", 4, server, client_entry2->nickname, 
433                 client_entry->nickname, buf);
434     break;
435
436   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
437     /*
438      * Changed channel mode.
439      */
440
441     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
442
443     idtype = va_arg(va, int);
444     entry = va_arg(va, void *);
445     mode = va_arg(va, SilcUInt32);
446     (void)va_arg(va, char *);
447     (void)va_arg(va, char *);
448     channel = va_arg(va, SilcChannelEntry);
449
450     tmp = silc_client_chmode(mode,
451                              channel->channel_key ? 
452                              channel->channel_key->cipher->name : "",
453                              channel->hmac ? 
454                              silc_hmac_get_name(channel->hmac) : "");
455     
456     chanrec = silc_channel_find_entry(server, channel);
457     if (chanrec != NULL) {
458       g_free_not_null(chanrec->mode);
459       chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
460       signal_emit("channel mode changed", 1, chanrec);
461     }
462     
463     if (idtype == SILC_ID_CLIENT) {
464       client_entry = (SilcClientEntry)entry;
465       printformat_module("fe-common/silc", server, channel->channel_name,
466                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
467                          channel->channel_name, tmp ? tmp : "removed all",
468                          client_entry->nickname);
469     } else if (idtype == SILC_ID_SERVER) {
470       server_entry = (SilcServerEntry)entry;
471       printformat_module("fe-common/silc", server, channel->channel_name,
472                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
473                          channel->channel_name, tmp ? tmp : "removed all",
474                          server_entry->server_name);
475     } else if (idtype == SILC_ID_CHANNEL) {
476       channel2 = (SilcChannelEntry)entry;
477       printformat_module("fe-common/silc", server, channel->channel_name,
478                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
479                          channel->channel_name, tmp ? tmp : "removed all",
480                          channel2->channel_name);
481     }
482
483     silc_free(tmp);
484     break;
485
486   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
487     /*
488      * Changed user's mode on channel.
489      */
490
491     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
492
493     idtype = va_arg(va, int);
494     entry = va_arg(va, void *);
495     mode = va_arg(va, SilcUInt32);
496     client_entry2 = va_arg(va, SilcClientEntry);
497     channel = va_arg(va, SilcChannelEntry);
498
499     tmp = silc_client_chumode(mode);
500     chanrec = silc_channel_find_entry(server, channel);
501     if (chanrec != NULL) {
502       SILC_NICK_REC *nick;
503
504       if (client_entry2 == server->conn->local_entry)
505         chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
506       
507       nick = silc_nicklist_find(chanrec, client_entry2);
508       if (nick != NULL) {
509         nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
510         nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
511         signal_emit("nick mode changed", 2, chanrec, nick);
512       }
513     }
514
515     if (idtype == SILC_ID_CLIENT) {
516       client_entry = (SilcClientEntry)entry;
517       printformat_module("fe-common/silc", server, channel->channel_name,
518                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
519                          channel->channel_name, client_entry2->nickname, 
520                          tmp ? tmp : "removed all",
521                          client_entry->nickname);
522     } else if (idtype == SILC_ID_SERVER) {
523       server_entry = (SilcServerEntry)entry;
524       printformat_module("fe-common/silc", server, channel->channel_name,
525                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
526                          channel->channel_name, client_entry2->nickname, 
527                          tmp ? tmp : "removed all",
528                          server_entry->server_name);
529     } else if (idtype == SILC_ID_CHANNEL) {
530       channel2 = (SilcChannelEntry)entry;
531       printformat_module("fe-common/silc", server, channel->channel_name,
532                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
533                          channel->channel_name, client_entry2->nickname, 
534                          tmp ? tmp : "removed all",
535                          channel2->channel_name);
536     }
537
538     if (mode & SILC_CHANNEL_UMODE_CHANFO)
539       printformat_module("fe-common/silc", 
540                          server, channel->channel_name, MSGLEVEL_CRAP,
541                          SILCTXT_CHANNEL_FOUNDER,
542                          channel->channel_name, client_entry2->nickname);
543
544     silc_free(tmp);
545     break;
546
547   case SILC_NOTIFY_TYPE_MOTD:
548     /*
549      * Received MOTD.
550      */
551
552     SILC_LOG_DEBUG(("Notify: MOTD"));
553
554     tmp = va_arg(va, char *);
555
556     if (!settings_get_bool("skip_motd"))
557       printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
558     break;
559
560   case SILC_NOTIFY_TYPE_KICKED:
561     /*
562      * Someone was kicked from channel.
563      */
564
565     SILC_LOG_DEBUG(("Notify: KICKED"));
566
567     client_entry = va_arg(va, SilcClientEntry);
568     tmp = va_arg(va, char *);
569     client_entry2 = va_arg(va, SilcClientEntry);
570     channel = va_arg(va, SilcChannelEntry);
571
572     chanrec = silc_channel_find_entry(server, channel);
573   
574     if (client_entry == conn->local_entry) {
575       printformat_module("fe-common/silc", server, channel->channel_name,
576                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
577                          channel->channel_name, 
578                          client_entry ? client_entry2->nickname : "",
579                          tmp ? tmp : "");
580       if (chanrec) {
581         chanrec->kicked = TRUE;
582         channel_destroy((CHANNEL_REC *)chanrec);
583       }
584     } else {
585       printformat_module("fe-common/silc", server, channel->channel_name,
586                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
587                          client_entry->nickname, channel->channel_name, 
588                          client_entry2 ? client_entry2->nickname : "", 
589                          tmp ? tmp : "");
590
591       if (chanrec) {
592         SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
593         if (nickrec != NULL)
594           nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
595       }
596     }
597     break;
598
599   case SILC_NOTIFY_TYPE_KILLED:
600     /*
601      * Someone was killed from the network.
602      */
603
604     SILC_LOG_DEBUG(("Notify: KILLED"));
605
606     client_entry = va_arg(va, SilcClientEntry);
607     tmp = va_arg(va, char *);
608     idtype = va_arg(va, int);
609     entry = va_arg(va, SilcClientEntry);
610   
611     if (client_entry == conn->local_entry) {
612       if (idtype == SILC_ID_CLIENT) {
613         client_entry2 = (SilcClientEntry)entry;
614         printformat_module("fe-common/silc", server, NULL,
615                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
616                            client_entry2 ? client_entry2->nickname : "",
617                            tmp ? tmp : "");
618       } else if (idtype == SILC_ID_SERVER) {
619         server_entry = (SilcServerEntry)entry;
620         printformat_module("fe-common/silc", server, NULL,
621                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
622                            server_entry->server_name, tmp ? tmp : "");
623       } else if (idtype == SILC_ID_CHANNEL) {
624         channel = (SilcChannelEntry)entry;
625         printformat_module("fe-common/silc", server, NULL,
626                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
627                            channel->channel_name, tmp ? tmp : "");
628       }
629     } else {
630       list1 = nicklist_get_same_unique(SERVER(server), client_entry);
631       for (list_tmp = list1; list_tmp != NULL; list_tmp = 
632              list_tmp->next->next) {
633         CHANNEL_REC *channel = list_tmp->data;
634         NICK_REC *nickrec = list_tmp->next->data;
635         nicklist_remove(channel, nickrec);
636       }
637
638       if (idtype == SILC_ID_CLIENT) {
639         client_entry2 = (SilcClientEntry)entry;
640         printformat_module("fe-common/silc", server, NULL,
641                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
642                            client_entry->nickname,
643                            client_entry2 ? client_entry2->nickname : "",
644                            tmp ? tmp : "");
645       } else if (idtype == SILC_ID_SERVER) {
646         server_entry = (SilcServerEntry)entry;
647         printformat_module("fe-common/silc", server, NULL,
648                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
649                            client_entry->nickname,
650                            server_entry->server_name, tmp ? tmp : "");
651       } else if (idtype == SILC_ID_CHANNEL) {
652         channel = (SilcChannelEntry)entry;
653         printformat_module("fe-common/silc", server, NULL,
654                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
655                            client_entry->nickname,
656                            channel->channel_name, tmp ? tmp : "");
657       }
658     }
659     break;
660
661   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
662     break;
663
664   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
665     {
666       /*
667        * Server has quit the network.
668        */
669       int i;
670       SilcClientEntry *clients;
671       SilcUInt32 clients_count;
672
673       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
674       
675       (void)va_arg(va, void *);
676       clients = va_arg(va, SilcClientEntry *);
677       clients_count = va_arg(va, SilcUInt32);
678   
679       for (i = 0; i < clients_count; i++) {
680         memset(buf, 0, sizeof(buf));
681         if (clients[i]->username)
682           snprintf(buf, sizeof(buf) - 1, "%s@%s",
683                    clients[i]->username, clients[i]->hostname);
684         signal_emit("message quit", 4, server, clients[i]->nickname,
685                     clients[i]->username ? buf : "", 
686                     "server signoff");
687
688         silc_server_free_ftp(server, clients[i]);
689         
690         list1 = nicklist_get_same_unique(SERVER(server), clients[i]);
691         for (list_tmp = list1; list_tmp != NULL; list_tmp = 
692                list_tmp->next->next) {
693           CHANNEL_REC *channel = list_tmp->data;
694           NICK_REC *nickrec = list_tmp->next->data;
695           nicklist_remove(channel, nickrec);
696         }
697       }
698     }
699     break;
700
701   case SILC_NOTIFY_TYPE_ERROR:
702     {
703       SilcStatus error = va_arg(va, int);
704
705       silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
706                 "%s", silc_get_status_message(error));
707     }
708     break;
709
710   case SILC_NOTIFY_TYPE_WATCH:
711     {
712       SilcNotifyType notify;
713
714       client_entry = va_arg(va, SilcClientEntry);
715       name = va_arg(va, char *);          /* Maybe NULL */
716       mode = va_arg(va, SilcUInt32);
717       notify = va_arg(va, int);
718
719       if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
720         if (name)
721           printformat_module("fe-common/silc", server, NULL,
722                              MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
723                              client_entry->nickname, name);
724         else
725           printformat_module("fe-common/silc", server, NULL,
726                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
727                              client_entry->nickname);
728       } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
729         /* See if client was away and is now present */
730         if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
731                       SILC_UMODE_BUSY | SILC_UMODE_PAGE |
732                       SILC_UMODE_DETACHED)) &&
733             (client_entry->mode & SILC_UMODE_GONE ||
734              client_entry->mode & SILC_UMODE_INDISPOSED ||
735              client_entry->mode & SILC_UMODE_BUSY ||
736              client_entry->mode & SILC_UMODE_PAGE ||
737              client_entry->mode & SILC_UMODE_DETACHED)) {
738           printformat_module("fe-common/silc", server, NULL,
739                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
740                              client_entry->nickname);
741         }
742
743         if (mode) {
744           memset(buf, 0, sizeof(buf));
745           silc_get_umode_string(mode, buf, sizeof(buf) - 1);
746           printformat_module("fe-common/silc", server, NULL,
747                              MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
748                              client_entry->nickname, buf);
749         }
750       } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
751         printformat_module("fe-common/silc", server, NULL,
752                            MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
753                            client_entry->nickname);
754       } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
755                  notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
756         printformat_module("fe-common/silc", server, NULL,
757                            MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
758                            client_entry->nickname);
759       } else if (notify == SILC_NOTIFY_TYPE_NONE) {
760         /* Client logged in to the network */
761         printformat_module("fe-common/silc", server, NULL,
762                            MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
763                            client_entry->nickname);
764       }
765     }
766     break;
767
768   default:
769     /* Unknown notify */
770     printformat_module("fe-common/silc", server, NULL,
771                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
772     break;
773   }
774
775   va_end(va);
776 }
777
778 /* Called to indicate that connection was either successfully established
779    or connecting failed.  This is also the first time application receives
780    the SilcClientConnection object which it should save somewhere. */
781
782 void silc_connect(SilcClient client, SilcClientConnection conn,
783                   SilcClientConnectionStatus status)
784 {
785   SILC_SERVER_REC *server = conn->context;
786
787   if (!server) {
788     silc_client_close_connection(client, conn);
789     return;
790   }
791
792   switch (status) {
793   case SILC_CLIENT_CONN_SUCCESS:
794     /* We have successfully connected to server */
795     server->connected = TRUE;
796     signal_emit("event connected", 1, server);
797     break;
798
799   case SILC_CLIENT_CONN_SUCCESS_RESUME:
800     /* We have successfully resumed old detached session */
801     server->connected = TRUE;
802     signal_emit("event connected", 1, server);
803
804     /* If we resumed old session check whether we need to update 
805        our nickname */
806     if (strcmp(server->nick, conn->local_entry->nickname)) {
807       char *old;
808       old = g_strdup(server->nick);
809       server_change_nick(SERVER(server), conn->local_entry->nickname);
810       nicklist_rename_unique(SERVER(server), 
811                              conn->local_entry, server->nick,
812                              conn->local_entry, conn->local_entry->nickname);
813       signal_emit("message own_nick", 4, server, server->nick, old, "");
814       g_free(old);
815     }
816     break;
817
818   default:
819     server->connection_lost = TRUE;
820     if (server->conn)
821       server->conn->context = NULL;
822     server_disconnect(SERVER(server));
823     break;
824   }
825 }
826
827 /* Called to indicate that connection was disconnected to the server. */
828
829 void silc_disconnect(SilcClient client, SilcClientConnection conn)
830 {
831   SILC_SERVER_REC *server = conn->context;
832
833   SILC_LOG_DEBUG(("Start"));
834
835   if (!server || server->connection_lost)
836     return;
837
838   if (server->conn && server->conn->local_entry) {
839     nicklist_rename_unique(SERVER(server),
840                            server->conn->local_entry, server->nick,
841                            server->conn->local_entry, 
842                            silc_client->username);
843     silc_change_nick(server, silc_client->username);
844   }
845
846   server->conn->context = NULL;
847   server->conn = NULL;
848   server->connection_lost = TRUE;
849   server_disconnect(SERVER(server));
850 }
851
852 /* Command handler. This function is called always in the command function.
853    If error occurs it will be called as well. `conn' is the associated
854    client connection. `cmd_context' is the command context that was
855    originally sent to the command. `success' is FALSE if error occured
856    during command. `command' is the command being processed. It must be
857    noted that this is not reply from server. This is merely called just
858    after application has called the command. Just to tell application
859    that the command really was processed. */
860
861 void silc_command(SilcClient client, SilcClientConnection conn, 
862                   SilcClientCommandContext cmd_context, bool success,
863                   SilcCommand command, SilcStatus status)
864 {
865   SILC_SERVER_REC *server = conn->context;
866
867   SILC_LOG_DEBUG(("Start"));
868
869   if (!success)
870     return;
871
872   switch(command) {
873   case SILC_COMMAND_INVITE:
874     printformat_module("fe-common/silc", server, NULL,
875                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
876                        cmd_context->argv[2], 
877                        (cmd_context->argv[1][0] == '*' ?
878                         (char *)conn->current_channel->channel_name :
879                         (char *)cmd_context->argv[1]));
880     break;
881   default:
882     break;
883   }
884 }
885
886 /* Client info resolving callback when JOIN command reply is received.
887    This will cache all users on the channel. */
888
889 static void silc_client_join_get_users(SilcClient client,
890                                        SilcClientConnection conn,
891                                        SilcClientEntry *clients,
892                                        SilcUInt32 clients_count,
893                                        void *context)
894 {
895   SilcChannelEntry channel = (SilcChannelEntry)context;
896   SilcHashTableList htl;
897   SilcChannelUser chu;
898   SILC_SERVER_REC *server = conn->context;
899   SILC_CHANNEL_REC *chanrec;
900   SilcClientEntry founder = NULL;
901   NICK_REC *ownnick;
902
903   SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
904                   silc_hash_table_count(channel->user_list)));
905
906   if (!clients)
907     return;
908
909   chanrec = silc_channel_find(server, channel->channel_name);
910   if (chanrec == NULL)
911     return;
912
913   silc_hash_table_list(channel->user_list, &htl);
914   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
915     if (!chu->client->nickname)
916       continue;
917     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
918       founder = chu->client;
919     silc_nicklist_insert(chanrec, chu, FALSE);
920   }
921   silc_hash_table_list_reset(&htl);
922
923   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
924   nicklist_set_own(CHANNEL(chanrec), ownnick);
925   signal_emit("channel joined", 1, chanrec);
926   chanrec->entry = channel;
927
928   if (chanrec->topic)
929     printformat_module("fe-common/silc", server, channel->channel_name,
930                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
931                        channel->channel_name, chanrec->topic);
932
933   fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
934
935   if (founder) {
936     if (founder == conn->local_entry)
937       printformat_module("fe-common/silc", 
938                          server, channel->channel_name, MSGLEVEL_CRAP,
939                          SILCTXT_CHANNEL_FOUNDER_YOU,
940                          channel->channel_name);
941     else
942       printformat_module("fe-common/silc", 
943                          server, channel->channel_name, MSGLEVEL_CRAP,
944                          SILCTXT_CHANNEL_FOUNDER,
945                          channel->channel_name, founder->nickname);
946   }
947 }
948
949 typedef struct {
950   SilcClient client;
951   SilcClientConnection conn;
952   void *entry;
953   SilcIdType id_type;
954   char *fingerprint;
955 } *GetkeyContext;
956
957 void silc_getkey_cb(bool success, void *context)
958 {
959   GetkeyContext getkey = (GetkeyContext)context;
960   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
961   char *name = (getkey->id_type == SILC_ID_CLIENT ? 
962                 ((SilcClientEntry)getkey->entry)->nickname :
963                 ((SilcServerEntry)getkey->entry)->server_name);
964
965   if (success) {
966     printformat_module("fe-common/silc", NULL, NULL,
967                        MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
968   } else {
969     printformat_module("fe-common/silc", NULL, NULL,
970                        MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
971   }
972
973   silc_free(getkey->fingerprint);
974   silc_free(getkey);
975 }
976
977 /* Command reply handler. This function is called always in the command reply
978    function. If error occurs it will be called as well. Normal scenario
979    is that it will be called after the received command data has been parsed
980    and processed. The function is used to pass the received command data to
981    the application. 
982
983    `conn' is the associated client connection. `cmd_payload' is the command
984    payload data received from server and it can be ignored. It is provided
985    if the application would like to re-parse the received command data,
986    however, it must be noted that the data is parsed already by the library
987    thus the payload can be ignored. `success' is FALSE if error occured.
988    In this case arguments are not sent to the application. `command' is the
989    command reply being processed. The function has variable argument list
990    and each command defines the number and type of arguments it passes to the
991    application (on error they are not sent). */
992
993 void 
994 silc_command_reply(SilcClient client, SilcClientConnection conn,
995                    SilcCommandPayload cmd_payload, bool success,
996                    SilcCommand command, SilcStatus status, ...)
997
998 {
999   SILC_SERVER_REC *server = conn->context;
1000   SILC_CHANNEL_REC *chanrec;
1001   va_list vp;
1002
1003   va_start(vp, status);
1004
1005   SILC_LOG_DEBUG(("Start"));
1006
1007   switch(command) {
1008   case SILC_COMMAND_WHOIS:
1009     {
1010       char buf[1024], *nickname, *username, *realname, *nick;
1011       unsigned char *fingerprint;
1012       SilcUInt32 idle, mode;
1013       SilcBuffer channels, user_modes;
1014       SilcClientEntry client_entry;
1015       
1016       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1017         /* Print the unknown nick for user */
1018         unsigned char *tmp =
1019           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1020                                      3, NULL);
1021         if (tmp)
1022           silc_say_error("%s: %s", tmp, 
1023                          silc_get_status_message(status));
1024         break;
1025       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1026         /* Try to find the entry for the unknown client ID, since we
1027            might have, and print the nickname of it for user. */
1028         SilcUInt32 tmp_len;
1029         unsigned char *tmp =
1030           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1031                                      2, &tmp_len);
1032         if (tmp) {
1033           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, 
1034                                                              NULL);
1035           if (client_id) {
1036             client_entry = silc_client_get_client_by_id(client, conn,
1037                                                         client_id);
1038             if (client_entry && client_entry->nickname)
1039               silc_say_error("%s: %s", client_entry->nickname,
1040                              silc_get_status_message(status));
1041             silc_free(client_id);
1042           }
1043         }
1044         break;
1045       }
1046       
1047       if (!success)
1048         return;
1049       
1050       client_entry = va_arg(vp, SilcClientEntry);
1051       nickname = va_arg(vp, char *);
1052       username = va_arg(vp, char *);
1053       realname = va_arg(vp, char *);
1054       channels = va_arg(vp, SilcBuffer);
1055       mode = va_arg(vp, SilcUInt32);
1056       idle = va_arg(vp, SilcUInt32);
1057       fingerprint = va_arg(vp, unsigned char *);
1058       user_modes = va_arg(vp, SilcBuffer);
1059       
1060       silc_parse_userfqdn(nickname, &nick, NULL);
1061       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1062                          SILCTXT_WHOIS_USERINFO, nickname, 
1063                          client_entry->username, client_entry->hostname,
1064                          nick, client_entry->nickname);
1065       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1066                          SILCTXT_WHOIS_REALNAME, realname);
1067       silc_free(nick);
1068
1069       if (channels && user_modes) {
1070         SilcUInt32 *umodes;
1071         SilcDList list = silc_channel_payload_parse_list(channels->data,
1072                                                          channels->len);
1073         if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1074                                        &umodes)) {
1075           SilcChannelPayload entry;
1076           int i = 0;
1077
1078           memset(buf, 0, sizeof(buf));
1079           silc_dlist_start(list);
1080           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1081             SilcUInt32 name_len;
1082             char *m = silc_client_chumode_char(umodes[i++]);
1083             char *name = silc_channel_get_name(entry, &name_len);
1084             
1085             if (m)
1086               strncat(buf, m, strlen(m));
1087             strncat(buf, name, name_len);
1088             strncat(buf, " ", 1);
1089             silc_free(m);
1090           }
1091
1092           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1093                              SILCTXT_WHOIS_CHANNELS, buf);
1094           silc_channel_payload_list_free(list);
1095           silc_free(umodes);
1096         }
1097       }
1098       
1099       if (mode) {
1100         memset(buf, 0, sizeof(buf));
1101         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1102         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1103                            SILCTXT_WHOIS_MODES, buf);
1104       }
1105       
1106       if (idle && nickname) {
1107         memset(buf, 0, sizeof(buf));
1108         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1109                  idle > 60 ? (idle / 60) : idle,
1110                  idle > 60 ? "minutes" : "seconds");
1111
1112         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1113                            SILCTXT_WHOIS_IDLE, buf);
1114       }
1115
1116       if (fingerprint) {
1117         fingerprint = silc_fingerprint(fingerprint, 20);
1118         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1119                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1120         silc_free(fingerprint);
1121       }
1122     }
1123     break;
1124     
1125   case SILC_COMMAND_IDENTIFY:
1126     {
1127       SilcClientEntry client_entry;
1128       
1129       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1130         /* Print the unknown nick for user */
1131         unsigned char *tmp =
1132           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1133                                      3, NULL);
1134         if (tmp)
1135           silc_say_error("%s: %s", tmp, 
1136                          silc_get_status_message(status));
1137         break;
1138       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1139         /* Try to find the entry for the unknown client ID, since we
1140            might have, and print the nickname of it for user. */
1141         SilcUInt32 tmp_len;
1142         unsigned char *tmp =
1143           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1144                                      2, &tmp_len);
1145         if (tmp) {
1146           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1147                                                              NULL);
1148           if (client_id) {
1149             client_entry = silc_client_get_client_by_id(client, conn,
1150                                                         client_id);
1151             if (client_entry && client_entry->nickname)
1152               silc_say_error("%s: %s", client_entry->nickname,
1153                              silc_get_status_message(status));
1154             silc_free(client_id);
1155           }
1156         }
1157         break;
1158       }
1159
1160       break;
1161     }
1162
1163   case SILC_COMMAND_WHOWAS:
1164     {
1165       char *nickname, *username, *realname;
1166       
1167       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1168           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1169         char *tmp;
1170         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1171                                          3, NULL);
1172         if (tmp)
1173           silc_say_error("%s: %s", tmp, 
1174                          silc_get_status_message(status));
1175         break;
1176       }
1177       
1178       if (!success)
1179         return;
1180       
1181       (void)va_arg(vp, SilcClientEntry);
1182       nickname = va_arg(vp, char *);
1183       username = va_arg(vp, char *);
1184       realname = va_arg(vp, char *);
1185       
1186       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1187                          SILCTXT_WHOWAS_USERINFO, nickname, username, 
1188                          realname ? realname : "");
1189     }
1190     break;
1191     
1192   case SILC_COMMAND_INVITE:
1193     {
1194       SilcChannelEntry channel;
1195       char *invite_list;
1196       SilcArgumentPayload args;
1197       int argc = 0;
1198       
1199       if (!success)
1200         return;
1201       
1202       channel = va_arg(vp, SilcChannelEntry);
1203       invite_list = va_arg(vp, char *);
1204
1205       args = silc_command_get_args(cmd_payload);
1206       if (args)
1207         argc = silc_argument_get_arg_num(args);
1208
1209       if (invite_list)
1210         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1211                            SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
1212                            invite_list);
1213       else if (argc == 3)
1214         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1215                            SILCTXT_CHANNEL_NO_INVITE_LIST, 
1216                            channel->channel_name);
1217     }
1218     break;
1219
1220   case SILC_COMMAND_JOIN: 
1221     {
1222       char *channel, *mode, *topic;
1223       SilcUInt32 modei;
1224       SilcChannelEntry channel_entry;
1225       SilcBuffer client_id_list;
1226       SilcUInt32 list_count;
1227
1228       if (!success)
1229         return;
1230
1231       channel = va_arg(vp, char *);
1232       channel_entry = va_arg(vp, SilcChannelEntry);
1233       modei = va_arg(vp, SilcUInt32);
1234       (void)va_arg(vp, SilcUInt32);
1235       (void)va_arg(vp, unsigned char *);
1236       (void)va_arg(vp, unsigned char *);
1237       (void)va_arg(vp, unsigned char *);
1238       topic = va_arg(vp, char *);
1239       (void)va_arg(vp, unsigned char *);
1240       list_count = va_arg(vp, SilcUInt32);
1241       client_id_list = va_arg(vp, SilcBuffer);
1242
1243       chanrec = silc_channel_find(server, channel);
1244       if (!chanrec)
1245         chanrec = silc_channel_create(server, channel, TRUE);
1246
1247       if (topic) {
1248         g_free_not_null(chanrec->topic);
1249         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1250         signal_emit("channel topic changed", 1, chanrec);
1251       }
1252
1253       mode = silc_client_chmode(modei, 
1254                                 channel_entry->channel_key ? 
1255                                 channel_entry->channel_key->cipher->name : "",
1256                                 channel_entry->hmac ? 
1257                                 silc_hmac_get_name(channel_entry->hmac) : "");
1258       g_free_not_null(chanrec->mode);
1259       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1260       signal_emit("channel mode changed", 1, chanrec);
1261
1262       /* Resolve the client information */
1263       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1264                                       silc_client_join_get_users, 
1265                                       channel_entry);
1266
1267       break;
1268     }
1269
1270   case SILC_COMMAND_NICK: 
1271     {
1272       char *old;
1273       SilcClientEntry client = va_arg(vp, SilcClientEntry);
1274       
1275       if (!success)
1276         return;
1277
1278       old = g_strdup(server->nick);
1279       server_change_nick(SERVER(server), client->nickname);
1280       nicklist_rename_unique(SERVER(server),
1281                              server->conn->local_entry, server->nick,
1282                              client, client->nickname);
1283       signal_emit("message own_nick", 4, server, server->nick, old, "");
1284       g_free(old);
1285       break;
1286     }
1287     
1288   case SILC_COMMAND_LIST:
1289     {
1290       char *topic, *name;
1291       int usercount;
1292       char users[20];
1293       
1294       if (!success)
1295         return;
1296       
1297       (void)va_arg(vp, SilcChannelEntry);
1298       name = va_arg(vp, char *);
1299       topic = va_arg(vp, char *);
1300       usercount = va_arg(vp, int);
1301       
1302       if (status == SILC_STATUS_LIST_START ||
1303           status == SILC_STATUS_OK)
1304         printformat_module("fe-common/silc", server, NULL,
1305                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1306
1307       if (!usercount)
1308         snprintf(users, sizeof(users) - 1, "N/A");
1309       else
1310         snprintf(users, sizeof(users) - 1, "%d", usercount);
1311       printformat_module("fe-common/silc", server, NULL,
1312                          MSGLEVEL_CRAP, SILCTXT_LIST,
1313                          name, users, topic ? topic : "");
1314     }
1315     break;
1316     
1317   case SILC_COMMAND_UMODE:
1318     {
1319       SilcUInt32 mode;
1320       
1321       if (!success)
1322         return;
1323       
1324       mode = va_arg(vp, SilcUInt32);
1325       
1326       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1327           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1328         printformat_module("fe-common/silc", server, NULL,
1329                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1330
1331       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1332           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1333         printformat_module("fe-common/silc", server, NULL,
1334                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1335
1336       server->umode = mode;
1337       signal_emit("user mode changed", 2, server, NULL);
1338     }
1339     break;
1340     
1341   case SILC_COMMAND_OPER:
1342     if (!success)
1343       return;
1344
1345     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1346     signal_emit("user mode changed", 2, server, NULL);
1347
1348     printformat_module("fe-common/silc", server, NULL,
1349                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1350     break;
1351     
1352   case SILC_COMMAND_SILCOPER:
1353     if (!success)
1354       return;
1355
1356     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1357     signal_emit("user mode changed", 2, server, NULL);
1358
1359     printformat_module("fe-common/silc", server, NULL,
1360                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1361     break;
1362     
1363   case SILC_COMMAND_USERS: 
1364     {
1365       SilcHashTableList htl;
1366       SilcChannelEntry channel;
1367       SilcChannelUser chu;
1368       
1369       if (!success)
1370         return;
1371       
1372       channel = va_arg(vp, SilcChannelEntry);
1373       
1374       printformat_module("fe-common/silc", server, channel->channel_name,
1375                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1376                          channel->channel_name);
1377
1378       silc_hash_table_list(channel->user_list, &htl);
1379       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1380         SilcClientEntry e = chu->client;
1381         char stat[5], *mode;
1382
1383         if (!e->nickname)
1384           continue;
1385         
1386         memset(stat, 0, sizeof(stat));
1387         mode = silc_client_chumode_char(chu->mode);
1388         if (e->mode & SILC_UMODE_GONE)
1389           strcat(stat, "G");
1390         else if (e->mode & SILC_UMODE_INDISPOSED)
1391           strcat(stat, "I");
1392         else if (e->mode & SILC_UMODE_BUSY)
1393           strcat(stat, "B");
1394         else if (e->mode & SILC_UMODE_PAGE)
1395           strcat(stat, "P");
1396         else if (e->mode & SILC_UMODE_HYPER)
1397           strcat(stat, "H");
1398         else if (e->mode & SILC_UMODE_ROBOT)
1399           strcat(stat, "R");
1400         else if (e->mode & SILC_UMODE_ANONYMOUS)
1401           strcat(stat, "?");
1402         else
1403           strcat(stat, "A");
1404         if (mode)
1405           strcat(stat, mode);
1406
1407         printformat_module("fe-common/silc", server, channel->channel_name,
1408                            MSGLEVEL_CRAP, SILCTXT_USERS,
1409                            e->nickname, stat, 
1410                            e->username ? e->username : "",
1411                            e->hostname ? e->hostname : "",
1412                            e->realname ? e->realname : "");
1413         if (mode)
1414           silc_free(mode);
1415       }
1416       silc_hash_table_list_reset(&htl);
1417     }
1418     break;
1419
1420   case SILC_COMMAND_BAN:
1421     {
1422       SilcChannelEntry channel;
1423       char *ban_list;
1424       
1425       if (!success)
1426         return;
1427       
1428       channel = va_arg(vp, SilcChannelEntry);
1429       ban_list = va_arg(vp, char *);
1430       
1431       if (ban_list)
1432         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1433                            SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
1434                            ban_list);
1435       else
1436         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1437                            SILCTXT_CHANNEL_NO_BAN_LIST, 
1438                            channel->channel_name);
1439     }
1440     break;
1441     
1442   case SILC_COMMAND_GETKEY:
1443     {
1444       SilcIdType id_type;
1445       void *entry;
1446       SilcPublicKey public_key;
1447       unsigned char *pk;
1448       SilcUInt32 pk_len;
1449       GetkeyContext getkey;
1450       char *name;
1451       
1452       if (!success)
1453         return;
1454       
1455       id_type = va_arg(vp, SilcUInt32);
1456       entry = va_arg(vp, void *);
1457       public_key = va_arg(vp, SilcPublicKey);
1458
1459       if (public_key) {
1460         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1461
1462         getkey = silc_calloc(1, sizeof(*getkey));
1463         getkey->entry = entry;
1464         getkey->id_type = id_type;
1465         getkey->client = client;
1466         getkey->conn = conn;
1467         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1468
1469         name = (id_type == SILC_ID_CLIENT ? 
1470                 ((SilcClientEntry)entry)->nickname :
1471                 ((SilcServerEntry)entry)->server_name);
1472
1473         silc_verify_public_key_internal(client, conn, name,
1474                                         (id_type == SILC_ID_CLIENT ?
1475                                          SILC_SOCKET_TYPE_CLIENT :
1476                                          SILC_SOCKET_TYPE_SERVER),
1477                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1478                                         silc_getkey_cb, getkey);
1479         silc_free(pk);
1480       } else {
1481         printformat_module("fe-common/silc", server, NULL,
1482                            MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
1483       }
1484     }
1485     break;
1486
1487   case SILC_COMMAND_INFO:
1488     {
1489       SilcServerEntry server_entry;
1490       char *server_name;
1491       char *server_info;
1492
1493       if (!success)
1494         return;
1495       
1496       server_entry = va_arg(vp, SilcServerEntry);
1497       server_name = va_arg(vp, char *);
1498       server_info = va_arg(vp, char *);
1499
1500       if (server_name && server_info )
1501         {
1502           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1503           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1504         }
1505     }
1506     break;
1507     
1508   case SILC_COMMAND_TOPIC:
1509     {
1510       SilcChannelEntry channel;
1511       char *topic;
1512       
1513       if (!success)
1514         return;
1515       
1516       channel = va_arg(vp, SilcChannelEntry);
1517       topic = va_arg(vp, char *);
1518       
1519       if (topic) {
1520         chanrec = silc_channel_find_entry(server, channel);
1521         if (chanrec) {
1522           g_free_not_null(chanrec->topic);
1523           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1524           signal_emit("channel topic changed", 1, chanrec);
1525         }
1526         printformat_module("fe-common/silc", server, channel->channel_name,
1527                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1528                            channel->channel_name, topic);
1529       } else {
1530         printformat_module("fe-common/silc", server, channel->channel_name,
1531                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
1532                            channel->channel_name);
1533       }
1534     }
1535     break;
1536
1537   case SILC_COMMAND_WATCH:
1538     break;
1539   }
1540
1541   va_end(vp);
1542 }
1543
1544 typedef struct {
1545   SilcClient client;
1546   SilcClientConnection conn;
1547   char *filename;
1548   char *entity;
1549   char *entity_name;
1550   unsigned char *pk;
1551   SilcUInt32 pk_len;
1552   SilcSKEPKType pk_type;
1553   SilcVerifyPublicKey completion;
1554   void *context;
1555 } *PublicKeyVerify;
1556
1557 static void verify_public_key_completion(const char *line, void *context)
1558 {
1559   PublicKeyVerify verify = (PublicKeyVerify)context;
1560
1561   if (line[0] == 'Y' || line[0] == 'y') {
1562     /* Call the completion */
1563     if (verify->completion)
1564       verify->completion(TRUE, verify->context);
1565
1566     /* Save the key for future checking */
1567     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
1568                                    verify->pk_len, SILC_PKCS_FILE_PEM);
1569   } else {
1570     /* Call the completion */
1571     if (verify->completion)
1572       verify->completion(FALSE, verify->context);
1573
1574     printformat_module("fe-common/silc", NULL, NULL,
1575                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
1576                        verify->entity_name ? verify->entity_name :
1577                        verify->entity);
1578   }
1579
1580   silc_free(verify->filename);
1581   silc_free(verify->entity);
1582   silc_free(verify->entity_name);
1583   silc_free(verify->pk);
1584   silc_free(verify);
1585 }
1586
1587 /* Internal routine to verify public key. If the `completion' is provided
1588    it will be called to indicate whether public was verified or not. For
1589    server/router public key this will check for filename that includes the
1590    remote host's IP address and remote host's hostname. */
1591
1592 static void 
1593 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
1594                                 const char *name, SilcSocketType conn_type, 
1595                                 unsigned char *pk, SilcUInt32 pk_len, 
1596                                 SilcSKEPKType pk_type,
1597                                 SilcVerifyPublicKey completion, void *context)
1598 {
1599   int i;
1600   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
1601   char *fingerprint, *babbleprint, *format;
1602   struct passwd *pw;
1603   struct stat st;
1604   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
1605                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
1606                   "server" : "client");
1607   PublicKeyVerify verify;
1608
1609   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
1610     printformat_module("fe-common/silc", NULL, NULL,
1611                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
1612                        entity, pk_type);
1613     if (completion)
1614       completion(FALSE, context);
1615     return;
1616   }
1617
1618   pw = getpwuid(getuid());
1619   if (!pw) {
1620     if (completion)
1621       completion(FALSE, context);
1622     return;
1623   }
1624
1625   memset(filename, 0, sizeof(filename));
1626   memset(filename2, 0, sizeof(filename2));
1627   memset(file, 0, sizeof(file));
1628
1629   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
1630       conn_type == SILC_SOCKET_TYPE_ROUTER) {
1631     if (!name) {
1632       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1633                conn->sock->ip, conn->sock->port);
1634       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1635                get_irssi_dir(), entity, file);
1636       
1637       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1638                conn->sock->hostname, conn->sock->port);
1639       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s", 
1640                get_irssi_dir(), entity, file);
1641       
1642       ipf = filename;
1643       hostf = filename2;
1644     } else {
1645       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
1646                name, conn->sock->port);
1647       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1648                get_irssi_dir(), entity, file);
1649       
1650       ipf = filename;
1651     }
1652   } else {
1653     /* Replace all whitespaces with `_'. */
1654     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1655     for (i = 0; i < strlen(fingerprint); i++)
1656       if (fingerprint[i] == ' ')
1657         fingerprint[i] = '_';
1658     
1659     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
1660     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s", 
1661              get_irssi_dir(), entity, file);
1662     silc_free(fingerprint);
1663
1664     ipf = filename;
1665   }
1666
1667   /* Take fingerprint of the public key */
1668   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1669   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1670
1671   verify = silc_calloc(1, sizeof(*verify));
1672   verify->client = client;
1673   verify->conn = conn;
1674   verify->filename = strdup(ipf);
1675   verify->entity = strdup(entity);
1676   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
1677                          (name ? strdup(name) : strdup(conn->sock->hostname))
1678                          : NULL);
1679   verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
1680   memcpy(verify->pk, pk, pk_len);
1681   verify->pk_len = pk_len;
1682   verify->pk_type = pk_type;
1683   verify->completion = completion;
1684   verify->context = context;
1685
1686   /* Check whether this key already exists */
1687   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
1688     /* Key does not exist, ask user to verify the key and save it */
1689
1690     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1691                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1692                        verify->entity_name : entity);
1693     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1694                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1695     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1696                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1697     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1698                              SILCTXT_PUBKEY_ACCEPT);
1699     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1700                             format, 0, verify);
1701     g_free(format);
1702     silc_free(fingerprint);
1703     return;
1704   } else {
1705     /* The key already exists, verify it. */
1706     SilcPublicKey public_key;
1707     unsigned char *encpk;
1708     SilcUInt32 encpk_len;
1709
1710     /* Load the key file, try for both IP filename and hostname filename */
1711     if (!silc_pkcs_load_public_key(ipf, &public_key, 
1712                                    SILC_PKCS_FILE_PEM) &&
1713         !silc_pkcs_load_public_key(ipf, &public_key, 
1714                                    SILC_PKCS_FILE_BIN) &&
1715         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
1716                                                SILC_PKCS_FILE_PEM) &&
1717                     !silc_pkcs_load_public_key(hostf, &public_key, 
1718                                                SILC_PKCS_FILE_BIN)))) {
1719       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1720                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1721                          verify->entity_name : entity);
1722       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1723                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1724       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1725                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1726       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1727                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
1728       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1729                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1730       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1731                               format, 0, verify);
1732       g_free(format);
1733       silc_free(fingerprint);
1734       return;
1735     }
1736
1737     /* Encode the key data */
1738     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
1739     if (!encpk) {
1740       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1741                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1742                          verify->entity_name : entity);
1743       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1744                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1745       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1746                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1747       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1748                          SILCTXT_PUBKEY_MALFORMED, entity);
1749       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1750                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1751       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1752                               format, 0, verify);
1753       g_free(format);
1754       silc_free(fingerprint);
1755       return;
1756     }
1757
1758     /* Compare the keys */
1759     if (memcmp(encpk, pk, encpk_len)) {
1760       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1761                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
1762                          verify->entity_name : entity);
1763       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1764                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
1765       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1766                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
1767       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1768                          SILCTXT_PUBKEY_NO_MATCH, entity);
1769       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1770                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
1771       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1772                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
1773
1774       /* Ask user to verify the key and save it */
1775       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
1776                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
1777       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1778                               format, 0, verify);
1779       g_free(format);
1780       silc_free(fingerprint);
1781       return;
1782     }
1783
1784     /* Local copy matched */
1785     if (completion)
1786       completion(TRUE, context);
1787     silc_free(fingerprint);
1788   }
1789 }
1790
1791 /* Verifies received public key. The `conn_type' indicates which entity
1792    (server, client etc.) has sent the public key. If user decides to trust
1793    the key may be saved as trusted public key for later use. The 
1794    `completion' must be called after the public key has been verified. */
1795
1796 void 
1797 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1798                        SilcSocketType conn_type, unsigned char *pk, 
1799                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
1800                        SilcVerifyPublicKey completion, void *context)
1801 {
1802   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
1803                                   pk_len, pk_type,
1804                                   completion, context);
1805 }
1806
1807 /* Asks passphrase from user on the input line. */
1808
1809 typedef struct {
1810   SilcAskPassphrase completion;
1811   void *context;
1812 } *AskPassphrase;
1813
1814 void ask_passphrase_completion(const char *passphrase, void *context)
1815 {
1816   AskPassphrase p = (AskPassphrase)context;
1817   if (passphrase && passphrase[0] == '\0')
1818     passphrase = NULL;
1819   p->completion((unsigned char *)passphrase, 
1820                 passphrase ? strlen(passphrase) : 0, p->context);
1821   silc_free(p);
1822 }
1823
1824 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1825                          SilcAskPassphrase completion, void *context)
1826 {
1827   AskPassphrase p = silc_calloc(1, sizeof(*p));
1828   p->completion = completion;
1829   p->context = context;
1830
1831   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1832                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1833 }
1834
1835 typedef struct {
1836   SilcGetAuthMeth completion;
1837   void *context;
1838 } *InternalGetAuthMethod;
1839
1840 /* Callback called when we've received the authentication method information
1841    from the server after we've requested it. This will get the authentication
1842    data from the user if needed. */
1843
1844 static void silc_get_auth_method_callback(SilcClient client,
1845                                           SilcClientConnection conn,
1846                                           SilcAuthMethod auth_meth,
1847                                           void *context)
1848 {
1849   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
1850
1851   SILC_LOG_DEBUG(("Start"));
1852
1853   switch (auth_meth) {
1854   case SILC_AUTH_NONE:
1855     /* No authentication required. */
1856     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1857     break;
1858   case SILC_AUTH_PASSWORD:
1859     /* Do not ask the passphrase from user, the library will ask it if
1860        we do not provide it here. */
1861     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1862     break;
1863   case SILC_AUTH_PUBLIC_KEY:
1864     /* Do not get the authentication data now, the library will generate
1865        it using our default key, if we do not provide it here. */
1866     /* XXX In the future when we support multiple local keys and multiple
1867        local certificates we will need to ask from user which one to use. */
1868     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1869     break;
1870   }
1871
1872   silc_free(internal);
1873 }
1874
1875 /* Find authentication method and authentication data by hostname and
1876    port. The hostname may be IP address as well. The found authentication
1877    method and authentication data is returned to `auth_meth', `auth_data'
1878    and `auth_data_len'. The function returns TRUE if authentication method
1879    is found and FALSE if not. `conn' may be NULL. */
1880
1881 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1882                           char *hostname, SilcUInt16 port,
1883                           SilcGetAuthMeth completion, void *context)
1884 {
1885   InternalGetAuthMethod internal;
1886
1887   SILC_LOG_DEBUG(("Start"));
1888
1889   /* XXX must resolve from configuration whether this connection has
1890      any specific authentication data */
1891
1892   /* If we do not have this connection configured by the user in a
1893      configuration file then resolve the authentication method from the
1894      server for this session. */
1895   internal = silc_calloc(1, sizeof(*internal));
1896   internal->completion = completion;
1897   internal->context = context;
1898
1899   silc_client_request_authentication_method(client, conn, 
1900                                             silc_get_auth_method_callback,
1901                                             internal);
1902 }
1903
1904 /* Notifies application that failure packet was received.  This is called
1905    if there is some protocol active in the client.  The `protocol' is the
1906    protocol context.  The `failure' is opaque pointer to the failure
1907    indication.  Note, that the `failure' is protocol dependant and application
1908    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1909    failure type (see protocol specs for all protocol failure types). */
1910
1911 void silc_failure(SilcClient client, SilcClientConnection conn, 
1912                   SilcProtocol protocol, void *failure)
1913 {
1914   SILC_LOG_DEBUG(("Start"));
1915
1916   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1917     SilcSKEStatus status = (SilcSKEStatus)failure;
1918     
1919     if (status == SILC_SKE_STATUS_BAD_VERSION)
1920       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1921                          SILCTXT_KE_BAD_VERSION);
1922     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1923       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1924                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
1925     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1926       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1927                          SILCTXT_KE_UNKNOWN_GROUP);
1928     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1929       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1930                          SILCTXT_KE_UNKNOWN_CIPHER);
1931     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1932       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1933                          SILCTXT_KE_UNKNOWN_PKCS);
1934     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1935       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1936                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
1937     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1938       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1939                          SILCTXT_KE_UNKNOWN_HMAC);
1940     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1941       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1942                          SILCTXT_KE_INCORRECT_SIGNATURE);
1943     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1944       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1945                          SILCTXT_KE_INVALID_COOKIE);
1946   }
1947
1948   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1949     SilcUInt32 err = (SilcUInt32)failure;
1950
1951     if (err == SILC_AUTH_FAILED)
1952       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
1953                          SILCTXT_AUTH_FAILED);
1954   }
1955 }
1956
1957 /* Asks whether the user would like to perform the key agreement protocol.
1958    This is called after we have received an key agreement packet or an
1959    reply to our key agreement packet. This returns TRUE if the user wants
1960    the library to perform the key agreement protocol and FALSE if it is not
1961    desired (application may start it later by calling the function
1962    silc_client_perform_key_agreement). */
1963
1964 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
1965                        SilcClientEntry client_entry, const char *hostname,
1966                        SilcUInt16 port, SilcKeyAgreementCallback *completion,
1967                        void **context)
1968 {
1969   char portstr[12];
1970
1971   SILC_LOG_DEBUG(("Start"));
1972
1973   /* We will just display the info on the screen and return FALSE and user
1974      will have to start the key agreement with a command. */
1975
1976   if (hostname) 
1977     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
1978
1979   if (!hostname)
1980     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1981                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
1982   else
1983     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
1984                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
1985                        client_entry->nickname, hostname, portstr);
1986
1987   *completion = NULL;
1988   *context = NULL;
1989
1990   return FALSE;
1991 }
1992
1993 /* Notifies application that file transfer protocol session is being
1994    requested by the remote client indicated by the `client_entry' from
1995    the `hostname' and `port'. The `session_id' is the file transfer
1996    session and it can be used to either accept or reject the file
1997    transfer request, by calling the silc_client_file_receive or
1998    silc_client_file_close, respectively. */
1999
2000 void silc_ftp(SilcClient client, SilcClientConnection conn,
2001               SilcClientEntry client_entry, SilcUInt32 session_id,
2002               const char *hostname, SilcUInt16 port)
2003 {
2004   SILC_SERVER_REC *server;
2005   char portstr[12];
2006   FtpSession ftp = silc_calloc(1, sizeof(*ftp));
2007
2008   SILC_LOG_DEBUG(("Start"));
2009
2010   server = conn->context;
2011
2012   ftp->client_entry = client_entry;
2013   ftp->session_id = session_id;
2014   ftp->send = FALSE;
2015   ftp->conn = conn;
2016   silc_dlist_add(server->ftp_sessions, ftp);
2017   server->current_session = ftp;
2018
2019   if (hostname) 
2020     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2021
2022   if (!hostname)
2023     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2024                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2025   else
2026     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2027                        SILCTXT_FILE_REQUEST_HOST, 
2028                        client_entry->nickname, hostname, portstr);
2029 }
2030
2031 /* Delivers SILC session detachment data indicated by `detach_data' to the
2032    application.  If application has issued SILC_COMMAND_DETACH command
2033    the client session in the SILC network is not quit.  The client remains
2034    in the network but is detached.  The detachment data may be used later
2035    to resume the session in the SILC Network.  The appliation is
2036    responsible of saving the `detach_data', to for example in a file.
2037
2038    The detachment data can be given as argument to the functions
2039    silc_client_connect_to_server, or silc_client_add_connection when
2040    creating connection to remote server, inside SilcClientConnectionParams
2041    structure.  If it is provided the client library will attempt to resume
2042    the session in the network.  After the connection is created
2043    successfully, the application is responsible of setting the user
2044    interface for user into the same state it was before detaching (showing
2045    same channels, channel modes, etc).  It can do this by fetching the
2046    information (like joined channels) from the client library. */
2047
2048 void
2049 silc_detach(SilcClient client, SilcClientConnection conn,
2050             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2051 {
2052   char file[256];
2053
2054   /* Save the detachment data to file. */
2055
2056   memset(file, 0, sizeof(file));
2057   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2058   silc_file_writefile(file, detach_data, detach_data_len);
2059 }
2060
2061
2062 /* SILC client operations */
2063 SilcClientOperations ops = {
2064   silc_say,
2065   silc_channel_message,
2066   silc_private_message,
2067   silc_notify,
2068   silc_command,
2069   silc_command_reply,
2070   silc_connect,
2071   silc_disconnect,
2072   silc_get_auth_method,
2073   silc_verify_public_key,
2074   silc_ask_passphrase,
2075   silc_failure,
2076   silc_key_agreement,
2077   silc_ftp,
2078   silc_detach,
2079 };