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