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