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