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