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