updates.
[silc.git] / apps / irssi / src / silc / core / client_ops.c
1 /*
2
3   client_ops.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 - 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     r->retry++;
1194     return;
1195   }
1196
1197   chanrec = silc_channel_find(server, channel->channel_name);
1198   if (chanrec == NULL)
1199     return;
1200
1201   silc_hash_table_list(channel->user_list, &htl);
1202   while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1203     if (!chu->client->nickname)
1204       continue;
1205     if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1206       founder = chu->client;
1207     silc_nicklist_insert(chanrec, chu, FALSE);
1208   }
1209   silc_hash_table_list_reset(&htl);
1210
1211   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1212   nicklist_set_own(CHANNEL(chanrec), ownnick);
1213   signal_emit("channel joined", 1, chanrec);
1214   chanrec->entry = channel;
1215
1216   if (chanrec->topic)
1217     printformat_module("fe-common/silc", server, channel->channel_name,
1218                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1219                        channel->channel_name, chanrec->topic);
1220
1221   if (founder) {
1222     if (founder == conn->local_entry) {
1223       printformat_module("fe-common/silc",
1224                          server, channel->channel_name, MSGLEVEL_CRAP,
1225                          SILCTXT_CHANNEL_FOUNDER_YOU,
1226                          channel->channel_name);
1227       signal_emit("nick mode changed", 2, chanrec, ownnick);
1228     } else
1229       printformat_module("fe-common/silc",
1230                          server, channel->channel_name, MSGLEVEL_CRAP,
1231                          SILCTXT_CHANNEL_FOUNDER,
1232                          channel->channel_name, founder->nickname);
1233   }
1234 }
1235
1236 typedef struct {
1237   SilcClient client;
1238   SilcClientConnection conn;
1239   void *entry;
1240   SilcIdType id_type;
1241   char *fingerprint;
1242 } *GetkeyContext;
1243
1244 void silc_getkey_cb(bool success, void *context)
1245 {
1246   GetkeyContext getkey = (GetkeyContext)context;
1247   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1248   char *name = (getkey->id_type == SILC_ID_CLIENT ?
1249                 ((SilcClientEntry)getkey->entry)->nickname :
1250                 ((SilcServerEntry)getkey->entry)->server_name);
1251
1252   if (success) {
1253     printformat_module("fe-common/silc", NULL, NULL,
1254                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1255   } else {
1256     printformat_module("fe-common/silc", NULL, NULL,
1257                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1258                        entity, name);
1259   }
1260
1261   silc_free(getkey->fingerprint);
1262   silc_free(getkey);
1263 }
1264
1265 /* Parse an invite or ban list */
1266 void  silc_parse_inviteban_list(SilcClient client,
1267                                 SilcClientConnection conn,
1268                                 SILC_SERVER_REC *server,
1269                                 SilcChannelEntry channel,
1270                                 const char *list_type,
1271                                 SilcArgumentPayload list)
1272 {
1273   unsigned char *tmp;
1274   SilcUInt32 type, len;
1275   SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1276   int counter=0, resolving = FALSE;
1277
1278   if (!silc_argument_get_arg_num(list)) {
1279     printformat_module("fe-common/silc", server,
1280                        (chanrec ? chanrec->visible_name : NULL),
1281                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1282                        channel->channel_name, list_type);
1283     return;
1284   }
1285
1286   printformat_module("fe-common/silc", server,
1287                      (chanrec ? chanrec->visible_name : NULL),
1288                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1289                      channel->channel_name, list_type);
1290
1291   /* parse the list */
1292   tmp = silc_argument_get_first_arg(list, &type, &len);
1293   while (tmp) {
1294     switch (type) {
1295       case 1:
1296         {
1297           /* an invite string */
1298           char **list;
1299           int i=0;
1300
1301           if (tmp[len-1] == ',')
1302             tmp[len-1] = '\0';
1303
1304           list = g_strsplit(tmp, ",", -1);
1305           while (list[i])
1306             printformat_module("fe-common/silc", server,
1307                                (chanrec ? chanrec->visible_name : NULL),
1308                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1309                                ++counter, channel->channel_name, list_type,
1310                                list[i++]);
1311           g_strfreev(list);
1312         }
1313         break;
1314
1315       case 2:
1316         {
1317           /* a public key */
1318           char *fingerprint, *babbleprint;
1319
1320           /* tmp is Public Key Payload, take public key from it. */
1321           fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1322           babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1323
1324           printformat_module("fe-common/silc", server,
1325                              (chanrec ? chanrec->visible_name : NULL),
1326                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1327                              ++counter, channel->channel_name, list_type,
1328                              fingerprint, babbleprint);
1329         }
1330         break;
1331
1332       case 3:
1333         {
1334           /* a client ID */
1335           SilcClientID *client_id;
1336           SilcClientEntry client_entry;
1337
1338           client_id = silc_id_payload_parse_id(tmp, len, NULL);
1339
1340           if (client_id == NULL) {
1341             silc_say_error("Invalid data in %s list encountered", list_type);
1342             break;
1343           }
1344
1345           client_entry = silc_client_get_client_by_id(client, conn, client_id);
1346
1347           if (client_entry) {
1348             printformat_module("fe-common/silc", server,
1349                                (chanrec ? chanrec->visible_name : NULL),
1350                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1351                                ++counter, channel->channel_name, list_type,
1352                                client_entry->nickname);
1353           } else {
1354             resolving = TRUE;
1355             silc_client_get_client_by_id_resolve(client, conn, client_id,
1356                                                  NULL, NULL, NULL);
1357           }
1358
1359           silc_free(client_id);
1360         }
1361         break;
1362
1363       default:
1364         /* "trash" */
1365         silc_say_error("Unkown type in %s list: %u (len %u)",
1366                        list_type, type, len);
1367     }
1368     tmp = silc_argument_get_next_arg(list, &type, &len);
1369   }
1370
1371   if (resolving)
1372     printformat_module("fe-common/silc", server,
1373                        (chanrec ? chanrec->visible_name : NULL),
1374                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1375                        list_type, channel->channel_name);
1376 }
1377
1378 /* Command reply handler. This function is called always in the command reply
1379    function. If error occurs it will be called as well. Normal scenario
1380    is that it will be called after the received command data has been parsed
1381    and processed. The function is used to pass the received command data to
1382    the application.
1383
1384    `conn' is the associated client connection. `cmd_payload' is the command
1385    payload data received from server and it can be ignored. It is provided
1386    if the application would like to re-parse the received command data,
1387    however, it must be noted that the data is parsed already by the library
1388    thus the payload can be ignored. `success' is FALSE if error occured.
1389    In this case arguments are not sent to the application. `command' is the
1390    command reply being processed. The function has variable argument list
1391    and each command defines the number and type of arguments it passes to the
1392    application (on error they are not sent). */
1393
1394 void
1395 silc_command_reply(SilcClient client, SilcClientConnection conn,
1396                    SilcCommandPayload cmd_payload, bool success,
1397                    SilcCommand command, SilcStatus status, ...)
1398
1399 {
1400   SILC_SERVER_REC *server = conn->context;
1401   SILC_CHANNEL_REC *chanrec;
1402   va_list vp;
1403
1404   va_start(vp, status);
1405
1406   SILC_LOG_DEBUG(("Start"));
1407
1408   switch(command) {
1409   case SILC_COMMAND_WHOIS:
1410     {
1411       char buf[1024], *nickname, *username, *realname, *nick;
1412       unsigned char *fingerprint;
1413       SilcUInt32 idle, mode;
1414       SilcBuffer channels, user_modes;
1415       SilcClientEntry client_entry;
1416       SilcDList attrs;
1417
1418       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1419         /* Print the unknown nick for user */
1420         unsigned char *tmp =
1421           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1422                                      3, NULL);
1423         if (tmp)
1424           silc_say_error("%s: %s", tmp,
1425                          silc_get_status_message(status));
1426         break;
1427       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1428         /* Try to find the entry for the unknown client ID, since we
1429            might have, and print the nickname of it for user. */
1430         SilcUInt32 tmp_len;
1431         unsigned char *tmp =
1432           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1433                                      2, &tmp_len);
1434         if (tmp) {
1435           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1436                                                              NULL);
1437           if (client_id) {
1438             client_entry = silc_client_get_client_by_id(client, conn,
1439                                                         client_id);
1440             if (client_entry && client_entry->nickname)
1441               silc_say_error("%s: %s", client_entry->nickname,
1442                              silc_get_status_message(status));
1443             silc_free(client_id);
1444           }
1445         }
1446         break;
1447       } else if (!success) {
1448         silc_say_error("WHOIS: %s", silc_get_status_message(status));
1449         return;
1450       }
1451
1452       client_entry = va_arg(vp, SilcClientEntry);
1453       nickname = va_arg(vp, char *);
1454       username = va_arg(vp, char *);
1455       realname = va_arg(vp, char *);
1456       channels = va_arg(vp, SilcBuffer);
1457       mode = va_arg(vp, SilcUInt32);
1458       idle = va_arg(vp, SilcUInt32);
1459       fingerprint = va_arg(vp, unsigned char *);
1460       user_modes = va_arg(vp, SilcBuffer);
1461       attrs = va_arg(vp, SilcDList);
1462
1463       silc_parse_userfqdn(nickname, &nick, NULL);
1464       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1465                          SILCTXT_WHOIS_USERINFO, nickname,
1466                          client_entry->username, client_entry->hostname,
1467                          nick, client_entry->nickname);
1468       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1469                          SILCTXT_WHOIS_REALNAME, realname);
1470       silc_free(nick);
1471
1472       if (channels && user_modes) {
1473         SilcUInt32 *umodes;
1474         SilcDList list = silc_channel_payload_parse_list(channels->data,
1475                                                          channels->len);
1476         if (list && silc_get_mode_list(user_modes, silc_dlist_count(list),
1477                                        &umodes)) {
1478           SilcChannelPayload entry;
1479           int i = 0;
1480
1481           memset(buf, 0, sizeof(buf));
1482           silc_dlist_start(list);
1483           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
1484             SilcUInt32 name_len;
1485             char *m = silc_client_chumode_char(umodes[i++]);
1486             char *name = silc_channel_get_name(entry, &name_len);
1487
1488             if (m)
1489               silc_strncat(buf, sizeof(buf) - 1, m, strlen(m));
1490             silc_strncat(buf, sizeof(buf) - 1, name, name_len);
1491             silc_strncat(buf, sizeof(buf) - 1, " ", 1);
1492             silc_free(m);
1493           }
1494
1495           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1496                              SILCTXT_WHOIS_CHANNELS, buf);
1497           silc_channel_payload_list_free(list);
1498           silc_free(umodes);
1499         }
1500       }
1501
1502       if (mode) {
1503         memset(buf, 0, sizeof(buf));
1504         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1505         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1506                            SILCTXT_WHOIS_MODES, buf);
1507       }
1508
1509       if (idle && nickname) {
1510         memset(buf, 0, sizeof(buf));
1511         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1512                  idle > 60 ? (idle / 60) : idle,
1513                  idle > 60 ? "minutes" : "seconds");
1514
1515         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1516                            SILCTXT_WHOIS_IDLE, buf);
1517       }
1518
1519       if (fingerprint) {
1520         fingerprint = silc_fingerprint(fingerprint, 20);
1521         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1522                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1523         silc_free(fingerprint);
1524       }
1525
1526       if (attrs)
1527         silc_query_attributes_print(server, silc_client, conn, attrs,
1528                                     client_entry);
1529     }
1530     break;
1531
1532   case SILC_COMMAND_IDENTIFY:
1533     {
1534       SilcClientEntry client_entry;
1535
1536       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1537         /* Print the unknown nick for user */
1538         unsigned char *tmp =
1539           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1540                                      3, NULL);
1541         if (tmp)
1542           silc_say_error("%s: %s", tmp,
1543                          silc_get_status_message(status));
1544         break;
1545       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1546         /* Try to find the entry for the unknown client ID, since we
1547            might have, and print the nickname of it for user. */
1548         SilcUInt32 tmp_len;
1549         unsigned char *tmp =
1550           silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1551                                      2, &tmp_len);
1552         if (tmp) {
1553           SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len,
1554                                                              NULL);
1555           if (client_id) {
1556             client_entry = silc_client_get_client_by_id(client, conn,
1557                                                         client_id);
1558             if (client_entry && client_entry->nickname)
1559               silc_say_error("%s: %s", client_entry->nickname,
1560                              silc_get_status_message(status));
1561             silc_free(client_id);
1562           }
1563         }
1564         break;
1565       }
1566
1567       break;
1568     }
1569
1570   case SILC_COMMAND_WHOWAS:
1571     {
1572       char *nickname, *username, *realname;
1573
1574       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
1575           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1576         char *tmp;
1577         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
1578                                          3, NULL);
1579         if (tmp)
1580           silc_say_error("%s: %s", tmp,
1581                          silc_get_status_message(status));
1582         break;
1583       } else if (!success) {
1584         silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1585         return;
1586       }
1587
1588       (void)va_arg(vp, SilcClientEntry);
1589       nickname = va_arg(vp, char *);
1590       username = va_arg(vp, char *);
1591       realname = va_arg(vp, char *);
1592
1593       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1594                          SILCTXT_WHOWAS_USERINFO, nickname, username,
1595                          realname ? realname : "");
1596     }
1597     break;
1598
1599   case SILC_COMMAND_INVITE:
1600     {
1601       SilcChannelEntry channel;
1602       SilcBuffer payload;
1603       SilcArgumentPayload invite_list;
1604       SilcUInt32 argc;
1605
1606       if (!success)
1607         return;
1608
1609       channel = va_arg(vp, SilcChannelEntry);
1610       payload = va_arg(vp, SilcBuffer);
1611
1612       if (payload) {
1613         SILC_GET16_MSB(argc, payload->data);
1614         invite_list = silc_argument_payload_parse(payload->data + 2,
1615                                                   payload->len - 2, argc);
1616         if (invite_list) {
1617           silc_parse_inviteban_list(client, conn, server, channel,
1618                                     "invite", invite_list);
1619           silc_argument_payload_free(invite_list);
1620         }
1621       }
1622     }
1623     break;
1624
1625   case SILC_COMMAND_JOIN:
1626     {
1627       char *channel, *mode, *topic;
1628       SilcUInt32 modei;
1629       SilcChannelEntry channel_entry;
1630       SilcBuffer client_id_list;
1631       SilcUInt32 list_count;
1632
1633       if (!success)
1634         return;
1635
1636       channel = va_arg(vp, char *);
1637       channel_entry = va_arg(vp, SilcChannelEntry);
1638       modei = va_arg(vp, SilcUInt32);
1639       (void)va_arg(vp, SilcUInt32);
1640       (void)va_arg(vp, unsigned char *);
1641       (void)va_arg(vp, unsigned char *);
1642       (void)va_arg(vp, unsigned char *);
1643       topic = va_arg(vp, char *);
1644       (void)va_arg(vp, unsigned char *);
1645       list_count = va_arg(vp, SilcUInt32);
1646       client_id_list = va_arg(vp, SilcBuffer);
1647
1648       chanrec = silc_channel_find(server, channel);
1649       if (!chanrec)
1650         chanrec = silc_channel_create(server, channel, channel, TRUE);
1651
1652       if (topic) {
1653         char tmp[256], *cp, *dm = NULL;
1654         g_free_not_null(chanrec->topic);
1655
1656         if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1657           memset(tmp, 0, sizeof(tmp));
1658           cp = tmp;
1659           if (strlen(topic) > sizeof(tmp) - 1) {
1660             dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1661             cp = dm;
1662           }
1663
1664           silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1665                            cp, strlen(topic));
1666           topic = cp;
1667         }
1668
1669         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1670         signal_emit("channel topic changed", 1, chanrec);
1671
1672         silc_free(dm);
1673       }
1674
1675       mode = silc_client_chmode(modei,
1676                                 channel_entry->channel_key ?
1677                                 silc_cipher_get_name(channel_entry->
1678                                                      channel_key) : "",
1679                                 channel_entry->hmac ?
1680                                 silc_hmac_get_name(channel_entry->hmac) : "");
1681       g_free_not_null(chanrec->mode);
1682       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1683       signal_emit("channel mode changed", 1, chanrec);
1684
1685       /* Resolve the client information */
1686       SilcJoinResolve r = silc_calloc(1, sizeof(*r));
1687       r->channel = channel_entry;
1688       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
1689                                       silc_client_join_get_users, r);
1690
1691       break;
1692     }
1693
1694   case SILC_COMMAND_NICK:
1695     {
1696       char *old;
1697       SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1698       GSList *nicks;
1699
1700       if (!success)
1701         return;
1702
1703       nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1704       if ((nicks != NULL) &&
1705         (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1706         char buf[512];
1707         SilcClientEntry collider, old;
1708
1709         old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1710         collider = silc_client_get_client_by_id(client, conn,
1711                                                 old->id);
1712
1713         memset(buf, 0, sizeof(buf));
1714         snprintf(buf, sizeof(buf) - 1, "%s@%s",
1715                  collider->username, collider->hostname);
1716         nicklist_rename_unique(SERVER(server),
1717                                old, old->nickname,
1718                                collider, collider->nickname);
1719         silc_print_nick_change(server, collider->nickname,
1720                                client_entry->nickname, buf);
1721         g_slist_free(nicks);
1722       }
1723
1724       old = g_strdup(server->nick);
1725       server_change_nick(SERVER(server), client_entry->nickname);
1726       nicklist_rename_unique(SERVER(server),
1727                              server->conn->local_entry, server->nick,
1728                              client_entry, client_entry->nickname);
1729       signal_emit("message own_nick", 4, server, server->nick, old, "");
1730       g_free(old);
1731       break;
1732     }
1733
1734   case SILC_COMMAND_LIST:
1735     {
1736       char *topic, *name;
1737       int usercount;
1738       char users[20];
1739       char tmp[256], *cp, *dm = NULL;
1740
1741       if (!success)
1742         return;
1743
1744       (void)va_arg(vp, SilcChannelEntry);
1745       name = va_arg(vp, char *);
1746       topic = va_arg(vp, char *);
1747       usercount = va_arg(vp, int);
1748
1749       if (topic && !silc_term_utf8() &&
1750           silc_utf8_valid(topic, strlen(topic))) {
1751         memset(tmp, 0, sizeof(tmp));
1752         cp = tmp;
1753         if (strlen(topic) > sizeof(tmp) - 1) {
1754           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1755           cp = dm;
1756         }
1757
1758         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
1759                          cp, strlen(topic));
1760         topic = cp;
1761       }
1762
1763       if (status == SILC_STATUS_LIST_START ||
1764           status == SILC_STATUS_OK)
1765         printformat_module("fe-common/silc", server, NULL,
1766                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1767
1768       if (!usercount)
1769         snprintf(users, sizeof(users) - 1, "N/A");
1770       else
1771         snprintf(users, sizeof(users) - 1, "%d", usercount);
1772       printformat_module("fe-common/silc", server, NULL,
1773                          MSGLEVEL_CRAP, SILCTXT_LIST,
1774                          name, users, topic ? topic : "");
1775       silc_free(dm);
1776     }
1777     break;
1778
1779   case SILC_COMMAND_UMODE:
1780     {
1781       SilcUInt32 mode;
1782       char *reason;
1783
1784       if (!success)
1785         return;
1786
1787       mode = va_arg(vp, SilcUInt32);
1788
1789       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1790           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1791         printformat_module("fe-common/silc", server, NULL,
1792                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1793
1794       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1795           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1796         printformat_module("fe-common/silc", server, NULL,
1797                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1798
1799       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1800         if (mode & SILC_UMODE_GONE) {
1801           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1802             reason = g_strdup(server->away_reason);
1803           else
1804             reason = g_strdup("away");
1805         } else
1806           reason = g_strdup("");
1807
1808         silc_set_away(reason, server);
1809
1810         g_free(reason);
1811       }
1812
1813       server->umode = mode;
1814       signal_emit("user mode changed", 2, server, NULL);
1815     }
1816     break;
1817
1818   case SILC_COMMAND_OPER:
1819     if (!success)
1820       return;
1821
1822     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1823     signal_emit("user mode changed", 2, server, NULL);
1824
1825     printformat_module("fe-common/silc", server, NULL,
1826                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1827     break;
1828
1829   case SILC_COMMAND_SILCOPER:
1830     if (!success)
1831       return;
1832
1833     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1834     signal_emit("user mode changed", 2, server, NULL);
1835
1836     printformat_module("fe-common/silc", server, NULL,
1837                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1838     break;
1839
1840   case SILC_COMMAND_USERS:
1841     {
1842       SilcHashTableList htl;
1843       SilcChannelEntry channel;
1844       SilcChannelUser chu;
1845
1846       if (!success)
1847         return;
1848
1849       channel = va_arg(vp, SilcChannelEntry);
1850
1851       printformat_module("fe-common/silc", server, channel->channel_name,
1852                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1853                          channel->channel_name);
1854
1855       silc_hash_table_list(channel->user_list, &htl);
1856       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1857         SilcClientEntry e = chu->client;
1858         char stat[5], *mode;
1859
1860         if (!e->nickname)
1861           continue;
1862
1863         memset(stat, 0, sizeof(stat));
1864         mode = silc_client_chumode_char(chu->mode);
1865         if (e->mode & SILC_UMODE_GONE)
1866           strcat(stat, "G");
1867         else if (e->mode & SILC_UMODE_INDISPOSED)
1868           strcat(stat, "I");
1869         else if (e->mode & SILC_UMODE_BUSY)
1870           strcat(stat, "B");
1871         else if (e->mode & SILC_UMODE_PAGE)
1872           strcat(stat, "P");
1873         else if (e->mode & SILC_UMODE_HYPER)
1874           strcat(stat, "H");
1875         else if (e->mode & SILC_UMODE_ROBOT)
1876           strcat(stat, "R");
1877         else if (e->mode & SILC_UMODE_ANONYMOUS)
1878           strcat(stat, "?");
1879         else
1880           strcat(stat, "A");
1881         if (mode)
1882           strcat(stat, mode);
1883
1884         printformat_module("fe-common/silc", server, channel->channel_name,
1885                            MSGLEVEL_CRAP, SILCTXT_USERS,
1886                            e->nickname, stat,
1887                            e->username ? e->username : "",
1888                            e->hostname ? e->hostname : "",
1889                            e->realname ? e->realname : "");
1890         if (mode)
1891           silc_free(mode);
1892       }
1893       silc_hash_table_list_reset(&htl);
1894     }
1895     break;
1896
1897   case SILC_COMMAND_BAN:
1898     {
1899       SilcChannelEntry channel;
1900       SilcBuffer payload;
1901       SilcArgumentPayload ban_list;
1902       SilcUInt32 argc;
1903
1904       if (!success)
1905         return;
1906
1907       channel = va_arg(vp, SilcChannelEntry);
1908       payload = va_arg(vp, SilcBuffer);
1909
1910       if (payload) {
1911         SILC_GET16_MSB(argc, payload->data);
1912         ban_list = silc_argument_payload_parse(payload->data + 2,
1913                                                payload->len - 2, argc);
1914         if (ban_list) {
1915           silc_parse_inviteban_list(client, conn, server, channel,
1916                                     "ban", ban_list);
1917           silc_argument_payload_free(ban_list);
1918         }
1919       }
1920     }
1921     break;
1922
1923   case SILC_COMMAND_GETKEY:
1924     {
1925       SilcIdType id_type;
1926       void *entry;
1927       SilcPublicKey public_key;
1928       unsigned char *pk;
1929       SilcUInt32 pk_len;
1930       GetkeyContext getkey;
1931       char *name;
1932
1933       if (!success)
1934         return;
1935
1936       id_type = va_arg(vp, SilcUInt32);
1937       entry = va_arg(vp, void *);
1938       public_key = va_arg(vp, SilcPublicKey);
1939
1940       if (public_key) {
1941         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1942
1943         getkey = silc_calloc(1, sizeof(*getkey));
1944         getkey->entry = entry;
1945         getkey->id_type = id_type;
1946         getkey->client = client;
1947         getkey->conn = conn;
1948         getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1949
1950         name = (id_type == SILC_ID_CLIENT ?
1951                 ((SilcClientEntry)entry)->nickname :
1952                 ((SilcServerEntry)entry)->server_name);
1953
1954         silc_verify_public_key_internal(client, conn, name,
1955                                         (id_type == SILC_ID_CLIENT ?
1956                                          SILC_SOCKET_TYPE_CLIENT :
1957                                          SILC_SOCKET_TYPE_SERVER),
1958                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1959                                         silc_getkey_cb, getkey);
1960         silc_free(pk);
1961       } else {
1962         printformat_module("fe-common/silc", server, NULL,
1963                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
1964       }
1965     }
1966     break;
1967
1968   case SILC_COMMAND_INFO:
1969     {
1970       SilcServerEntry server_entry;
1971       char *server_name;
1972       char *server_info;
1973
1974       if (!success)
1975         return;
1976
1977       server_entry = va_arg(vp, SilcServerEntry);
1978       server_name = va_arg(vp, char *);
1979       server_info = va_arg(vp, char *);
1980
1981       if (server_name && server_info )
1982         {
1983           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
1984           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
1985         }
1986     }
1987     break;
1988
1989   case SILC_COMMAND_TOPIC:
1990     {
1991       SilcChannelEntry channel;
1992       char *topic;
1993       char tmp[256], *cp, *dm = NULL;
1994
1995       if (!success)
1996         return;
1997
1998       channel = va_arg(vp, SilcChannelEntry);
1999       topic = va_arg(vp, char *);
2000
2001       if (topic && !silc_term_utf8() &&
2002           silc_utf8_valid(topic, strlen(topic))) {
2003         memset(tmp, 0, sizeof(tmp));
2004         cp = tmp;
2005         if (strlen(topic) > sizeof(tmp) - 1) {
2006           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2007           cp = dm;
2008         }
2009
2010         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
2011                          cp, strlen(topic));
2012         topic = cp;
2013       }
2014
2015       if (topic) {
2016         chanrec = silc_channel_find_entry(server, channel);
2017         if (chanrec) {
2018           g_free_not_null(chanrec->topic);
2019           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2020           signal_emit("channel topic changed", 1, chanrec);
2021         }
2022         printformat_module("fe-common/silc", server, channel->channel_name,
2023                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2024                            channel->channel_name, topic);
2025       } else {
2026         printformat_module("fe-common/silc", server, channel->channel_name,
2027                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2028                            channel->channel_name);
2029       }
2030       silc_free(dm);
2031     }
2032     break;
2033
2034   case SILC_COMMAND_WATCH:
2035     break;
2036
2037   case SILC_COMMAND_STATS:
2038     {
2039       SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
2040                  my_router_ops, cell_clients, cell_channels, cell_servers,
2041                  clients, channels, servers, routers, server_ops, router_ops;
2042       SilcUInt32 buf_len;
2043       SilcBufferStruct buf;
2044       unsigned char *tmp_buf;
2045       char tmp[40];
2046       const char *tmptime;
2047       int days, hours, mins, secs;
2048
2049       if (!success)
2050         return;
2051
2052       tmp_buf = va_arg(vp, unsigned char *);
2053       buf_len = va_arg(vp, SilcUInt32);
2054
2055       if (!tmp_buf || !buf_len) {
2056         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2057         return;
2058       }
2059
2060       /* Get statistics structure */
2061       silc_buffer_set(&buf, tmp_buf, buf_len);
2062       silc_buffer_unformat(&buf,
2063                            SILC_STR_UI_INT(&starttime),
2064                            SILC_STR_UI_INT(&uptime),
2065                            SILC_STR_UI_INT(&my_clients),
2066                            SILC_STR_UI_INT(&my_channels),
2067                            SILC_STR_UI_INT(&my_server_ops),
2068                            SILC_STR_UI_INT(&my_router_ops),
2069                            SILC_STR_UI_INT(&cell_clients),
2070                            SILC_STR_UI_INT(&cell_channels),
2071                            SILC_STR_UI_INT(&cell_servers),
2072                            SILC_STR_UI_INT(&clients),
2073                            SILC_STR_UI_INT(&channels),
2074                            SILC_STR_UI_INT(&servers),
2075                            SILC_STR_UI_INT(&routers),
2076                            SILC_STR_UI_INT(&server_ops),
2077                            SILC_STR_UI_INT(&router_ops),
2078                            SILC_STR_END);
2079
2080       tmptime = silc_get_time(starttime);
2081       printformat_module("fe-common/silc", server, NULL,
2082                          MSGLEVEL_CRAP, SILCTXT_STATS,
2083                          "Local server start time", tmptime);
2084
2085       days = uptime / (24 * 60 * 60);
2086       uptime -= days * (24 * 60 * 60);
2087       hours = uptime / (60 * 60);
2088       uptime -= hours * (60 * 60);
2089       mins = uptime / 60;
2090       uptime -= mins * 60;
2091       secs = uptime;
2092       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2093                days, hours, mins, secs);
2094       printformat_module("fe-common/silc", server, NULL,
2095                          MSGLEVEL_CRAP, SILCTXT_STATS,
2096                          "Local server uptime", tmp);
2097
2098       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
2099       printformat_module("fe-common/silc", server, NULL,
2100                          MSGLEVEL_CRAP, SILCTXT_STATS,
2101                          "Local server clients", tmp);
2102
2103       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
2104       printformat_module("fe-common/silc", server, NULL,
2105                          MSGLEVEL_CRAP, SILCTXT_STATS,
2106                          "Local server channels", tmp);
2107
2108       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
2109       printformat_module("fe-common/silc", server, NULL,
2110                          MSGLEVEL_CRAP, SILCTXT_STATS,
2111                          "Local server operators", tmp);
2112
2113       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
2114       printformat_module("fe-common/silc", server, NULL,
2115                          MSGLEVEL_CRAP, SILCTXT_STATS,
2116                          "Local router operators", tmp);
2117
2118       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
2119       printformat_module("fe-common/silc", server, NULL,
2120                          MSGLEVEL_CRAP, SILCTXT_STATS,
2121                          "Local cell clients", tmp);
2122
2123       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
2124       printformat_module("fe-common/silc", server, NULL,
2125                          MSGLEVEL_CRAP, SILCTXT_STATS,
2126                          "Local cell channels", tmp);
2127
2128       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
2129       printformat_module("fe-common/silc", server, NULL,
2130                          MSGLEVEL_CRAP, SILCTXT_STATS,
2131                          "Local cell servers", tmp);
2132
2133       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
2134       printformat_module("fe-common/silc", server, NULL,
2135                          MSGLEVEL_CRAP, SILCTXT_STATS,
2136                          "Total clients", tmp);
2137
2138       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
2139       printformat_module("fe-common/silc", server, NULL,
2140                          MSGLEVEL_CRAP, SILCTXT_STATS,
2141                          "Total channels", tmp);
2142
2143       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
2144       printformat_module("fe-common/silc", server, NULL,
2145                          MSGLEVEL_CRAP, SILCTXT_STATS,
2146                          "Total servers", tmp);
2147
2148       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
2149       printformat_module("fe-common/silc", server, NULL,
2150                          MSGLEVEL_CRAP, SILCTXT_STATS,
2151                          "Total routers", tmp);
2152
2153       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
2154       printformat_module("fe-common/silc", server, NULL,
2155                          MSGLEVEL_CRAP, SILCTXT_STATS,
2156                            "Total server operators", tmp);
2157
2158       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
2159       printformat_module("fe-common/silc", server, NULL,
2160                          MSGLEVEL_CRAP, SILCTXT_STATS,
2161                          "Total router operators", tmp);
2162     }
2163     break;
2164
2165   }
2166
2167   va_end(vp);
2168 }
2169
2170 typedef struct {
2171   SilcClient client;
2172   SilcClientConnection conn;
2173   char *filename;
2174   char *entity;
2175   char *entity_name;
2176   unsigned char *pk;
2177   SilcUInt32 pk_len;
2178   SilcSKEPKType pk_type;
2179   SilcVerifyPublicKey completion;
2180   void *context;
2181 } *PublicKeyVerify;
2182
2183 static void verify_public_key_completion(const char *line, void *context)
2184 {
2185   PublicKeyVerify verify = (PublicKeyVerify)context;
2186
2187   if (line[0] == 'Y' || line[0] == 'y') {
2188     /* Call the completion */
2189     if (verify->completion)
2190       verify->completion(TRUE, verify->context);
2191
2192     /* Save the key for future checking */
2193     silc_pkcs_save_public_key_data(verify->filename, verify->pk,
2194                                    verify->pk_len, SILC_PKCS_FILE_PEM);
2195   } else {
2196     /* Call the completion */
2197     if (verify->completion)
2198       verify->completion(FALSE, verify->context);
2199
2200     printformat_module("fe-common/silc", NULL, NULL,
2201                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2202                        verify->entity_name ? verify->entity_name :
2203                        verify->entity);
2204   }
2205
2206   silc_free(verify->filename);
2207   silc_free(verify->entity);
2208   silc_free(verify->entity_name);
2209   silc_free(verify->pk);
2210   silc_free(verify);
2211 }
2212
2213 /* Internal routine to verify public key. If the `completion' is provided
2214    it will be called to indicate whether public was verified or not. For
2215    server/router public key this will check for filename that includes the
2216    remote host's IP address and remote host's hostname. */
2217
2218 static void
2219 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2220                                 const char *name, SilcSocketType conn_type,
2221                                 unsigned char *pk, SilcUInt32 pk_len,
2222                                 SilcSKEPKType pk_type,
2223                                 SilcVerifyPublicKey completion, void *context)
2224 {
2225   int i;
2226   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2227   char *fingerprint, *babbleprint, *format;
2228   struct passwd *pw;
2229   struct stat st;
2230   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
2231                    conn_type == SILC_SOCKET_TYPE_ROUTER) ?
2232                   "server" : "client");
2233   PublicKeyVerify verify;
2234
2235   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
2236     printformat_module("fe-common/silc", NULL, NULL,
2237                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2238                        entity, pk_type);
2239     if (completion)
2240       completion(FALSE, context);
2241     return;
2242   }
2243
2244   pw = getpwuid(getuid());
2245   if (!pw) {
2246     if (completion)
2247       completion(FALSE, context);
2248     return;
2249   }
2250
2251   memset(filename, 0, sizeof(filename));
2252   memset(filename2, 0, sizeof(filename2));
2253   memset(file, 0, sizeof(file));
2254
2255   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
2256       conn_type == SILC_SOCKET_TYPE_ROUTER) {
2257     if (!name) {
2258       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2259                conn->sock->ip, conn->sock->port);
2260       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2261                get_irssi_dir(), entity, file);
2262
2263       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2264                conn->sock->hostname, conn->sock->port);
2265       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2266                get_irssi_dir(), entity, file);
2267
2268       ipf = filename;
2269       hostf = filename2;
2270     } else {
2271       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2272                name, conn->sock->port);
2273       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2274                get_irssi_dir(), entity, file);
2275
2276       ipf = filename;
2277     }
2278   } else {
2279     /* Replace all whitespaces with `_'. */
2280     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2281     for (i = 0; i < strlen(fingerprint); i++)
2282       if (fingerprint[i] == ' ')
2283         fingerprint[i] = '_';
2284
2285     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2286     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2287              get_irssi_dir(), entity, file);
2288     silc_free(fingerprint);
2289
2290     ipf = filename;
2291   }
2292
2293   /* Take fingerprint of the public key */
2294   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2295   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2296
2297   verify = silc_calloc(1, sizeof(*verify));
2298   verify->client = client;
2299   verify->conn = conn;
2300   verify->filename = strdup(ipf);
2301   verify->entity = strdup(entity);
2302   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
2303                          (name ? strdup(name) : strdup(conn->sock->hostname))
2304                          : NULL);
2305   verify->pk = silc_memdup(pk, pk_len);
2306   verify->pk_len = pk_len;
2307   verify->pk_type = pk_type;
2308   verify->completion = completion;
2309   verify->context = context;
2310
2311   /* Check whether this key already exists */
2312   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2313     /* Key does not exist, ask user to verify the key and save it */
2314
2315     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2316                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2317                        verify->entity_name : entity);
2318     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2319                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2320     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2321                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2322     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2323                              SILCTXT_PUBKEY_ACCEPT);
2324     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2325                             format, 0, verify);
2326     g_free(format);
2327     silc_free(fingerprint);
2328     return;
2329   } else {
2330     /* The key already exists, verify it. */
2331     SilcPublicKey public_key;
2332     unsigned char *encpk;
2333     SilcUInt32 encpk_len;
2334
2335     /* Load the key file, try for both IP filename and hostname filename */
2336     if (!silc_pkcs_load_public_key(ipf, &public_key,
2337                                    SILC_PKCS_FILE_PEM) &&
2338         !silc_pkcs_load_public_key(ipf, &public_key,
2339                                    SILC_PKCS_FILE_BIN) &&
2340         (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
2341                                                SILC_PKCS_FILE_PEM) &&
2342                     !silc_pkcs_load_public_key(hostf, &public_key,
2343                                                SILC_PKCS_FILE_BIN)))) {
2344       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2345                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2346                          verify->entity_name : entity);
2347       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2348                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2349       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2350                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2351       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2352                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2353       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2354                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2355       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2356                               format, 0, verify);
2357       g_free(format);
2358       silc_free(fingerprint);
2359       return;
2360     }
2361
2362     /* Encode the key data */
2363     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
2364     if (!encpk) {
2365       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2366                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2367                          verify->entity_name : entity);
2368       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2369                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2370       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2371                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2372       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2373                          SILCTXT_PUBKEY_MALFORMED, entity);
2374       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2375                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2376       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2377                               format, 0, verify);
2378       g_free(format);
2379       silc_free(fingerprint);
2380       return;
2381     }
2382
2383     /* Compare the keys */
2384     if (memcmp(encpk, pk, encpk_len)) {
2385       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2386                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2387                          verify->entity_name : entity);
2388       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2389                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2390       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2391                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2392       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2393                          SILCTXT_PUBKEY_NO_MATCH, entity);
2394       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2395                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2396       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2397                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2398
2399       /* Ask user to verify the key and save it */
2400       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2401                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2402       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2403                               format, 0, verify);
2404       g_free(format);
2405       silc_free(fingerprint);
2406       return;
2407     }
2408
2409     /* Local copy matched */
2410     if (completion)
2411       completion(TRUE, context);
2412     silc_free(fingerprint);
2413     silc_free(verify->filename);
2414     silc_free(verify->entity);
2415     silc_free(verify->entity_name);
2416     silc_free(verify->pk);
2417     silc_free(verify);
2418   }
2419 }
2420
2421 /* Verifies received public key. The `conn_type' indicates which entity
2422    (server, client etc.) has sent the public key. If user decides to trust
2423    the key may be saved as trusted public key for later use. The
2424    `completion' must be called after the public key has been verified. */
2425
2426 void
2427 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2428                        SilcSocketType conn_type, unsigned char *pk,
2429                        SilcUInt32 pk_len, SilcSKEPKType pk_type,
2430                        SilcVerifyPublicKey completion, void *context)
2431 {
2432   silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
2433                                   pk_len, pk_type,
2434                                   completion, context);
2435 }
2436
2437 /* Asks passphrase from user on the input line. */
2438
2439 typedef struct {
2440   SilcAskPassphrase completion;
2441   void *context;
2442 } *AskPassphrase;
2443
2444 void ask_passphrase_completion(const char *passphrase, void *context)
2445 {
2446   AskPassphrase p = (AskPassphrase)context;
2447   if (passphrase && passphrase[0] == '\0')
2448     passphrase = NULL;
2449   p->completion((unsigned char *)passphrase,
2450                 passphrase ? strlen(passphrase) : 0, p->context);
2451   silc_free(p);
2452 }
2453
2454 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2455                          SilcAskPassphrase completion, void *context)
2456 {
2457   AskPassphrase p = silc_calloc(1, sizeof(*p));
2458   p->completion = completion;
2459   p->context = context;
2460
2461   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2462                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2463 }
2464
2465 typedef struct {
2466   SilcGetAuthMeth completion;
2467   void *context;
2468 } *InternalGetAuthMethod;
2469
2470 /* Callback called when we've received the authentication method information
2471    from the server after we've requested it. This will get the authentication
2472    data from the user if needed. */
2473
2474 static void silc_get_auth_method_callback(SilcClient client,
2475                                           SilcClientConnection conn,
2476                                           SilcAuthMethod auth_meth,
2477                                           void *context)
2478 {
2479   InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
2480
2481   SILC_LOG_DEBUG(("Start"));
2482
2483   switch (auth_meth) {
2484   case SILC_AUTH_NONE:
2485     /* No authentication required. */
2486     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2487     break;
2488   case SILC_AUTH_PASSWORD:
2489     {
2490       /* Check whether we find the password for this server in our
2491          configuration.  If not, then don't provide so library will ask
2492          it from the user. */
2493       SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
2494                                                        conn->remote_port);
2495       if (!setup || !setup->password) {
2496         (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2497         break;
2498       }
2499
2500       (*internal->completion)(TRUE, auth_meth, setup->password,
2501                               strlen(setup->password), internal->context);
2502     }
2503     break;
2504   case SILC_AUTH_PUBLIC_KEY:
2505     /* Do not get the authentication data now, the library will generate
2506        it using our default key, if we do not provide it here. */
2507     /* XXX In the future when we support multiple local keys and multiple
2508        local certificates we will need to ask from user which one to use. */
2509     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
2510     break;
2511   }
2512
2513   silc_free(internal);
2514 }
2515
2516 /* Find authentication method and authentication data by hostname and
2517    port. The hostname may be IP address as well. The found authentication
2518    method and authentication data is returned to `auth_meth', `auth_data'
2519    and `auth_data_len'. The function returns TRUE if authentication method
2520    is found and FALSE if not. `conn' may be NULL. */
2521
2522 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2523                           char *hostname, SilcUInt16 port,
2524                           SilcGetAuthMeth completion, void *context)
2525 {
2526   InternalGetAuthMethod internal;
2527
2528   SILC_LOG_DEBUG(("Start"));
2529
2530   /* If we do not have this connection configured by the user in a
2531      configuration file then resolve the authentication method from the
2532      server for this session. */
2533   internal = silc_calloc(1, sizeof(*internal));
2534   internal->completion = completion;
2535   internal->context = context;
2536
2537   silc_client_request_authentication_method(client, conn,
2538                                             silc_get_auth_method_callback,
2539                                             internal);
2540 }
2541
2542 /* Notifies application that failure packet was received.  This is called
2543    if there is some protocol active in the client.  The `protocol' is the
2544    protocol context.  The `failure' is opaque pointer to the failure
2545    indication.  Note, that the `failure' is protocol dependant and application
2546    must explicitly cast it to correct type.  Usually `failure' is 32 bit
2547    failure type (see protocol specs for all protocol failure types). */
2548
2549 void silc_failure(SilcClient client, SilcClientConnection conn,
2550                   SilcProtocol protocol, void *failure)
2551 {
2552   SILC_LOG_DEBUG(("Start"));
2553
2554   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
2555     SilcSKEStatus status = (SilcSKEStatus)failure;
2556
2557     if (status == SILC_SKE_STATUS_BAD_VERSION)
2558       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2559                          SILCTXT_KE_BAD_VERSION);
2560     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
2561       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2562                          SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
2563     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
2564       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2565                          SILCTXT_KE_UNKNOWN_GROUP);
2566     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
2567       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2568                          SILCTXT_KE_UNKNOWN_CIPHER);
2569     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
2570       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2571                          SILCTXT_KE_UNKNOWN_PKCS);
2572     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
2573       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2574                          SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
2575     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
2576       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2577                          SILCTXT_KE_UNKNOWN_HMAC);
2578     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
2579       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2580                          SILCTXT_KE_INCORRECT_SIGNATURE);
2581     if (status == SILC_SKE_STATUS_INVALID_COOKIE)
2582       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2583                          SILCTXT_KE_INVALID_COOKIE);
2584   }
2585
2586   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
2587     SilcUInt32 err = (SilcUInt32)failure;
2588
2589     if (err == SILC_AUTH_FAILED)
2590       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2591                          SILCTXT_AUTH_FAILED);
2592   }
2593 }
2594
2595 /* Asks whether the user would like to perform the key agreement protocol.
2596    This is called after we have received an key agreement packet or an
2597    reply to our key agreement packet. This returns TRUE if the user wants
2598    the library to perform the key agreement protocol and FALSE if it is not
2599    desired (application may start it later by calling the function
2600    silc_client_perform_key_agreement). */
2601
2602 bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
2603                         SilcClientEntry client_entry, const char *hostname,
2604                         SilcUInt16 port, SilcKeyAgreementCallback *completion,
2605                         void **context)
2606 {
2607   char portstr[12];
2608
2609   SILC_LOG_DEBUG(("Start"));
2610
2611   /* We will just display the info on the screen and return FALSE and user
2612      will have to start the key agreement with a command. */
2613
2614   if (hostname)
2615     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2616
2617   if (!hostname)
2618     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2619                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2620   else
2621     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2622                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2623                        client_entry->nickname, hostname, portstr);
2624
2625   *completion = NULL;
2626   *context = NULL;
2627
2628   return FALSE;
2629 }
2630
2631 /* Notifies application that file transfer protocol session is being
2632    requested by the remote client indicated by the `client_entry' from
2633    the `hostname' and `port'. The `session_id' is the file transfer
2634    session and it can be used to either accept or reject the file
2635    transfer request, by calling the silc_client_file_receive or
2636    silc_client_file_close, respectively. */
2637
2638 void silc_ftp(SilcClient client, SilcClientConnection conn,
2639               SilcClientEntry client_entry, SilcUInt32 session_id,
2640               const char *hostname, SilcUInt16 port)
2641 {
2642   SILC_SERVER_REC *server;
2643   char portstr[12];
2644   FtpSession ftp = NULL;
2645
2646   SILC_LOG_DEBUG(("Start"));
2647
2648   server = conn->context;
2649
2650   silc_dlist_start(server->ftp_sessions);
2651   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2652     if (ftp->client_entry == client_entry &&
2653         ftp->session_id == session_id) {
2654       server->current_session = ftp;
2655       break;
2656     }
2657   }
2658   if (ftp == SILC_LIST_END) {
2659     ftp = silc_calloc(1, sizeof(*ftp));
2660     ftp->client_entry = client_entry;
2661     ftp->session_id = session_id;
2662     ftp->send = FALSE;
2663     ftp->conn = conn;
2664     silc_dlist_add(server->ftp_sessions, ftp);
2665     server->current_session = ftp;
2666   }
2667
2668   if (hostname)
2669     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2670
2671   if (!hostname)
2672     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2673                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2674   else
2675     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2676                        SILCTXT_FILE_REQUEST_HOST,
2677                        client_entry->nickname, hostname, portstr);
2678 }
2679
2680 /* Delivers SILC session detachment data indicated by `detach_data' to the
2681    application.  If application has issued SILC_COMMAND_DETACH command
2682    the client session in the SILC network is not quit.  The client remains
2683    in the network but is detached.  The detachment data may be used later
2684    to resume the session in the SILC Network.  The appliation is
2685    responsible of saving the `detach_data', to for example in a file.
2686
2687    The detachment data can be given as argument to the functions
2688    silc_client_connect_to_server, or silc_client_add_connection when
2689    creating connection to remote server, inside SilcClientConnectionParams
2690    structure.  If it is provided the client library will attempt to resume
2691    the session in the network.  After the connection is created
2692    successfully, the application is responsible of setting the user
2693    interface for user into the same state it was before detaching (showing
2694    same channels, channel modes, etc).  It can do this by fetching the
2695    information (like joined channels) from the client library. */
2696
2697 void
2698 silc_detach(SilcClient client, SilcClientConnection conn,
2699             const unsigned char *detach_data, SilcUInt32 detach_data_len)
2700 {
2701   char file[256];
2702
2703   /* Save the detachment data to file. */
2704
2705   memset(file, 0, sizeof(file));
2706   snprintf(file, sizeof(file) - 1, "%s/session", get_irssi_dir());
2707   silc_file_writefile(file, detach_data, detach_data_len);
2708 }
2709
2710
2711 /* SILC client operations */
2712 SilcClientOperations ops = {
2713   silc_say,
2714   silc_channel_message,
2715   silc_private_message,
2716   silc_notify,
2717   silc_command,
2718   silc_command_reply,
2719   silc_connect,
2720   silc_disconnect,
2721   silc_get_auth_method,
2722   silc_verify_public_key,
2723   silc_ask_passphrase,
2724   silc_failure,
2725   silc_key_agreement,
2726   silc_ftp,
2727   silc_detach,
2728 };