Fixed nickname format parsing in JOIN notify and WHOIS reply.
[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[0] ? "" : 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, tmp[32];
774       int count = 0;
775
776       silc_client_nickname_parse(client, conn, client_entry->nickname, &nick);
777       clients = silc_client_get_clients_local(client, conn, nick, TRUE);
778       if (!clients || silc_dlist_count(clients) < 2) {
779         silc_free(nick);
780         silc_client_list_free(client, conn, clients);
781         break;
782       }
783       silc_dlist_start(clients);
784       while ((client_entry2 = silc_dlist_get(clients)))
785         if (silc_client_on_channel(channel, client_entry2))
786           count++;
787       if (count > 1) {
788         silc_snprintf(tmp, sizeof(tmp), "%d", silc_dlist_count(clients));
789         printformat_module("fe-common/silc", server, channel->channel_name,
790                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_MANY_NICKS,
791                            tmp, nick);
792         printformat_module("fe-common/silc", server, channel->channel_name,
793                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_USER_APPEARS,
794                            buf, client_entry->nickname);
795       }
796       silc_client_list_free(client, conn, clients);
797       silc_free(nick);
798     }
799     break;
800
801   case SILC_NOTIFY_TYPE_LEAVE:
802     /*
803      * Left a channel.
804      */
805
806     SILC_LOG_DEBUG(("Notify: LEAVE"));
807
808     client_entry = va_arg(va, SilcClientEntry);
809     channel = va_arg(va, SilcChannelEntry);
810
811     memset(buf, 0, sizeof(buf));
812     if (client_entry->username)
813       snprintf(buf, sizeof(buf) - 1, "%s@%s",
814                client_entry->username, client_entry->hostname);
815     signal_emit("message part", 5, server, channel->channel_name,
816                 client_entry->nickname,  client_entry->username[0] ?
817                 buf : "", client_entry->nickname);
818
819     chanrec = silc_channel_find_entry(server, channel);
820     if (chanrec != NULL) {
821       nickrec = silc_nicklist_find(chanrec, client_entry);
822       if (nickrec != NULL)
823         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
824     }
825     break;
826
827   case SILC_NOTIFY_TYPE_SIGNOFF:
828     /*
829      * Left the network.
830      */
831
832     SILC_LOG_DEBUG(("Notify: SIGNOFF"));
833
834     client_entry = va_arg(va, SilcClientEntry);
835     tmp = va_arg(va, char *);
836
837     silc_server_free_ftp(server, client_entry);
838
839     /* Print only if we have the nickname.  If this cliente has just quit
840        when we were only resolving it, it is possible we don't have the
841        nickname. */
842     if (client_entry->nickname[0]) {
843       memset(buf, 0, sizeof(buf));
844       if (client_entry->username)
845         snprintf(buf, sizeof(buf) - 1, "%s@%s",
846                  client_entry->username, client_entry->hostname);
847       signal_emit("message quit", 4, server, client_entry->nickname,
848                   client_entry->username[0] ? buf : "",
849                   tmp ? tmp : "");
850     }
851
852     list1 = nicklist_get_same_unique(SERVER(server), client_entry);
853     for (list_tmp = list1; list_tmp != NULL; list_tmp =
854            list_tmp->next->next) {
855       CHANNEL_REC *channel = list_tmp->data;
856       NICK_REC *nickrec = list_tmp->next->data;
857
858       nicklist_remove(channel, nickrec);
859     }
860     break;
861
862   case SILC_NOTIFY_TYPE_TOPIC_SET:
863     /*
864      * Changed topic.
865      */
866
867     SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
868
869     idtype = va_arg(va, int);
870     entry = va_arg(va, void *);
871     tmp = va_arg(va, char *);
872     channel = va_arg(va, SilcChannelEntry);
873
874     chanrec = silc_channel_find_entry(server, channel);
875     if (chanrec != NULL) {
876       char tmp2[256], *cp, *dm = NULL;
877
878       g_free_not_null(chanrec->topic);
879       if (tmp && !silc_term_utf8() && silc_utf8_valid(tmp, strlen(tmp))) {
880         memset(tmp2, 0, sizeof(tmp2));
881         cp = tmp2;
882         if (strlen(tmp) > sizeof(tmp2) - 1) {
883           dm = silc_calloc(strlen(tmp) + 1, sizeof(*dm));
884           cp = dm;
885         }
886
887         silc_utf8_decode(tmp, strlen(tmp), SILC_STRING_LANGUAGE,
888                          cp, strlen(tmp));
889         tmp = cp;
890       }
891
892       chanrec->topic = *tmp == '\0' ? NULL : g_strdup(tmp);
893       signal_emit("channel topic changed", 1, chanrec);
894
895       silc_free(dm);
896     }
897
898     if (idtype == SILC_ID_CLIENT) {
899       client_entry = (SilcClientEntry)entry;
900       memset(buf, 0, sizeof(buf));
901       snprintf(buf, sizeof(buf) - 1, "%s@%s",
902                client_entry->username, client_entry->hostname);
903       signal_emit("message topic", 5, server, channel->channel_name,
904                   tmp, client_entry->nickname, buf);
905     } else if (idtype == SILC_ID_SERVER) {
906       server_entry = (SilcServerEntry)entry;
907       signal_emit("message topic", 5, server, channel->channel_name,
908                   tmp, server_entry->server_name,
909                   server_entry->server_name);
910     } else if (idtype == SILC_ID_CHANNEL) {
911       channel = (SilcChannelEntry)entry;
912       signal_emit("message topic", 5, server, channel->channel_name,
913                   tmp, channel->channel_name, channel->channel_name);
914     }
915     break;
916
917   case SILC_NOTIFY_TYPE_NICK_CHANGE:
918     /*
919      * Changed nickname.
920      */
921
922     SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
923
924     client_entry = va_arg(va, SilcClientEntry);
925     name = va_arg(va, char *);                 /* old nickname */
926
927     if (!strcmp(client_entry->nickname, name))
928       break;
929
930     memset(buf, 0, sizeof(buf));
931     snprintf(buf, sizeof(buf) - 1, "%s@%s",
932              client_entry->username, client_entry->hostname);
933     nicklist_rename_unique(SERVER(server),
934                            client_entry, name,
935                            client_entry, client_entry->nickname);
936     signal_emit("message nick", 4, server, client_entry->nickname, name, buf);
937     break;
938
939   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
940     /*
941      * Changed channel mode.
942      */
943
944     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
945
946     idtype = va_arg(va, int);
947     entry = va_arg(va, void *);
948     mode = va_arg(va, SilcUInt32);
949     cipher = va_arg(va, char *);               /* cipher */
950     hmac = va_arg(va, char *);                 /* hmac */
951     (void)va_arg(va, char *);                  /* passphrase */
952     (void)va_arg(va, SilcPublicKey);           /* founder key */
953     chpks = va_arg(va, SilcDList);             /* channel public keys */
954     channel = va_arg(va, SilcChannelEntry);
955
956     tmp = silc_client_chmode(mode, cipher ? cipher : "",
957                              hmac ? hmac : "");
958
959     chanrec = silc_channel_find_entry(server, channel);
960     if (chanrec != NULL) {
961       g_free_not_null(chanrec->mode);
962       chanrec->mode = g_strdup(tmp == NULL ? "" : tmp);
963       signal_emit("channel mode changed", 1, chanrec);
964     }
965
966     if (idtype == SILC_ID_CLIENT) {
967       client_entry = (SilcClientEntry)entry;
968       printformat_module("fe-common/silc", server, channel->channel_name,
969                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
970                          channel->channel_name, tmp ? tmp : "removed all",
971                          client_entry->nickname);
972     } else if (idtype == SILC_ID_SERVER) {
973       server_entry = (SilcServerEntry)entry;
974       printformat_module("fe-common/silc", server, channel->channel_name,
975                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
976                          channel->channel_name, tmp ? tmp : "removed all",
977                          server_entry->server_name);
978     } else if (idtype == SILC_ID_CHANNEL) {
979       channel2 = (SilcChannelEntry)entry;
980       printformat_module("fe-common/silc", server, channel->channel_name,
981                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
982                          channel->channel_name, tmp ? tmp : "removed all",
983                          channel2->channel_name);
984     }
985
986     /* Print the channel public key list */
987     if (chpks)
988       silc_parse_channel_public_keys(server, channel, chpks);
989
990     silc_free(tmp);
991     break;
992
993   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
994     /*
995      * Changed user's mode on channel.
996      */
997
998     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
999
1000     idtype = va_arg(va, int);
1001     entry = va_arg(va, void *);
1002     mode = va_arg(va, SilcUInt32);
1003     client_entry2 = va_arg(va, SilcClientEntry);
1004     channel = va_arg(va, SilcChannelEntry);
1005
1006     tmp = silc_client_chumode(mode);
1007     chanrec = silc_channel_find_entry(server, channel);
1008     if (chanrec != NULL) {
1009       SILC_NICK_REC *nick;
1010
1011       if (client_entry2 == server->conn->local_entry)
1012         chanrec->chanop = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
1013
1014       nick = silc_nicklist_find(chanrec, client_entry2);
1015       if (nick != NULL) {
1016         nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
1017         nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
1018         signal_emit("nick mode changed", 2, chanrec, nick);
1019       }
1020     }
1021
1022     if (idtype == SILC_ID_CLIENT) {
1023       client_entry = (SilcClientEntry)entry;
1024       printformat_module("fe-common/silc", server, channel->channel_name,
1025                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
1026                          channel->channel_name, client_entry2->nickname,
1027                          tmp ? tmp : "removed all",
1028                          client_entry->nickname);
1029     } else if (idtype == SILC_ID_SERVER) {
1030       server_entry = (SilcServerEntry)entry;
1031       printformat_module("fe-common/silc", server, channel->channel_name,
1032                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
1033                          channel->channel_name, client_entry2->nickname,
1034                          tmp ? tmp : "removed all",
1035                          server_entry->server_name);
1036     } else if (idtype == SILC_ID_CHANNEL) {
1037       channel2 = (SilcChannelEntry)entry;
1038       printformat_module("fe-common/silc", server, channel->channel_name,
1039                          MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
1040                          channel->channel_name, client_entry2->nickname,
1041                          tmp ? tmp : "removed all",
1042                          channel2->channel_name);
1043     }
1044
1045     if (mode & SILC_CHANNEL_UMODE_CHANFO)
1046       printformat_module("fe-common/silc",
1047                          server, channel->channel_name, MSGLEVEL_CRAP,
1048                          SILCTXT_CHANNEL_FOUNDER,
1049                          channel->channel_name, client_entry2->nickname);
1050
1051     if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2)
1052       printformat_module("fe-common/silc",
1053                          server, channel->channel_name, MSGLEVEL_CRAP,
1054                          SILCTXT_CHANNEL_QUIETED, channel->channel_name);
1055
1056     silc_free(tmp);
1057     break;
1058
1059   case SILC_NOTIFY_TYPE_MOTD:
1060     /*
1061      * Received MOTD.
1062      */
1063
1064     SILC_LOG_DEBUG(("Notify: MOTD"));
1065
1066     tmp = va_arg(va, char *);
1067
1068     if (!settings_get_bool("skip_motd"))
1069       printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", tmp);
1070     break;
1071
1072   case SILC_NOTIFY_TYPE_KICKED:
1073     /*
1074      * Someone was kicked from channel.
1075      */
1076
1077     SILC_LOG_DEBUG(("Notify: KICKED"));
1078
1079     client_entry = va_arg(va, SilcClientEntry);
1080     tmp = va_arg(va, char *);
1081     client_entry2 = va_arg(va, SilcClientEntry);
1082     channel = va_arg(va, SilcChannelEntry);
1083
1084     chanrec = silc_channel_find_entry(server, channel);
1085
1086     if (client_entry == conn->local_entry) {
1087       printformat_module("fe-common/silc", server, channel->channel_name,
1088                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU,
1089                          channel->channel_name,
1090                          client_entry ? client_entry2->nickname : "",
1091                          tmp ? tmp : "");
1092       if (chanrec) {
1093         chanrec->kicked = TRUE;
1094         channel_destroy((CHANNEL_REC *)chanrec);
1095       }
1096     } else {
1097       printformat_module("fe-common/silc", server, channel->channel_name,
1098                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED,
1099                          client_entry->nickname, channel->channel_name,
1100                          client_entry2 ? client_entry2->nickname : "",
1101                          tmp ? tmp : "");
1102
1103       if (chanrec) {
1104         SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
1105         if (nickrec != NULL)
1106           nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
1107       }
1108     }
1109     break;
1110
1111   case SILC_NOTIFY_TYPE_KILLED:
1112     /*
1113      * Someone was killed from the network.
1114      */
1115
1116     SILC_LOG_DEBUG(("Notify: KILLED"));
1117
1118     client_entry = va_arg(va, SilcClientEntry);
1119     tmp = va_arg(va, char *);
1120     idtype = va_arg(va, int);
1121     entry = va_arg(va, SilcClientEntry);
1122
1123     if (client_entry == conn->local_entry) {
1124       if (idtype == SILC_ID_CLIENT) {
1125         client_entry2 = (SilcClientEntry)entry;
1126         printformat_module("fe-common/silc", server, NULL,
1127                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1128                            client_entry2 ? client_entry2->nickname : "",
1129                            tmp ? tmp : "");
1130       } else if (idtype == SILC_ID_SERVER) {
1131         server_entry = (SilcServerEntry)entry;
1132         printformat_module("fe-common/silc", server, NULL,
1133                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1134                            server_entry->server_name, tmp ? tmp : "");
1135       } else if (idtype == SILC_ID_CHANNEL) {
1136         channel = (SilcChannelEntry)entry;
1137         printformat_module("fe-common/silc", server, NULL,
1138                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU,
1139                            channel->channel_name, tmp ? tmp : "");
1140       }
1141     } else {
1142       list1 = nicklist_get_same_unique(SERVER(server), client_entry);
1143       for (list_tmp = list1; list_tmp != NULL; list_tmp =
1144              list_tmp->next->next) {
1145         CHANNEL_REC *channel = list_tmp->data;
1146         NICK_REC *nickrec = list_tmp->next->data;
1147         nicklist_remove(channel, nickrec);
1148       }
1149
1150       if (idtype == SILC_ID_CLIENT) {
1151         client_entry2 = (SilcClientEntry)entry;
1152         printformat_module("fe-common/silc", server, NULL,
1153                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1154                            client_entry->nickname,
1155                            client_entry2 ? client_entry2->nickname : "",
1156                            tmp ? tmp : "");
1157       } else if (idtype == SILC_ID_SERVER) {
1158         server_entry = (SilcServerEntry)entry;
1159         printformat_module("fe-common/silc", server, NULL,
1160                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1161                            client_entry->nickname,
1162                            server_entry->server_name, tmp ? tmp : "");
1163       } else if (idtype == SILC_ID_CHANNEL) {
1164         channel = (SilcChannelEntry)entry;
1165         printformat_module("fe-common/silc", server, NULL,
1166                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
1167                            client_entry->nickname,
1168                            channel->channel_name, tmp ? tmp : "");
1169       }
1170     }
1171     break;
1172
1173   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
1174     break;
1175
1176   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
1177     {
1178       /*
1179        * Server has quit the network.
1180        */
1181       SilcDList clients;
1182
1183       SILC_LOG_DEBUG(("Notify: SERVER_SIGNOFF"));
1184
1185       (void)va_arg(va, void *);
1186       clients = va_arg(va, SilcDList);
1187
1188       silc_dlist_start(clients);
1189       while ((client_entry = silc_dlist_get(clients))) {
1190         memset(buf, 0, sizeof(buf));
1191
1192         /* Print only if we have the nickname.  If this client has just quit
1193            when we were only resolving it, it is possible we don't have the
1194            nickname. */
1195         if (client_entry->nickname[0]) {
1196           if (client_entry->username[0])
1197             snprintf(buf, sizeof(buf) - 1, "%s@%s",
1198                      client_entry->username, client_entry->hostname);
1199           signal_emit("message quit", 4, server, client_entry->nickname,
1200                       client_entry->username[0] ? buf : "",
1201                       "server signoff");
1202         }
1203
1204         silc_server_free_ftp(server, client_entry);
1205
1206         list1 = nicklist_get_same_unique(SERVER(server), client_entry);
1207         for (list_tmp = list1; list_tmp != NULL; list_tmp =
1208                list_tmp->next->next) {
1209           CHANNEL_REC *channel = list_tmp->data;
1210           NICK_REC *nickrec = list_tmp->next->data;
1211           nicklist_remove(channel, nickrec);
1212         }
1213       }
1214     }
1215     break;
1216
1217   case SILC_NOTIFY_TYPE_ERROR:
1218     {
1219       SilcStatus error = va_arg(va, int);
1220
1221       silc_say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1222                 "%s", silc_get_status_message(error));
1223     }
1224     break;
1225
1226   case SILC_NOTIFY_TYPE_WATCH:
1227     {
1228       SilcNotifyType notify;
1229
1230       client_entry = va_arg(va, SilcClientEntry);
1231       name = va_arg(va, char *);          /* Maybe NULL */
1232       mode = va_arg(va, SilcUInt32);
1233       notify = va_arg(va, int);
1234
1235       if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
1236         if (name)
1237           printformat_module("fe-common/silc", server, NULL,
1238                              MSGLEVEL_CRAP, SILCTXT_WATCH_NICK_CHANGE,
1239                              client_entry->nickname, name);
1240         else
1241           printformat_module("fe-common/silc", server, NULL,
1242                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1243                              client_entry->nickname);
1244       } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
1245         /* See if client was away and is now present */
1246         if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
1247                       SILC_UMODE_BUSY | SILC_UMODE_PAGE |
1248                       SILC_UMODE_DETACHED)) &&
1249             (client_entry->mode & SILC_UMODE_GONE ||
1250              client_entry->mode & SILC_UMODE_INDISPOSED ||
1251              client_entry->mode & SILC_UMODE_BUSY ||
1252              client_entry->mode & SILC_UMODE_PAGE ||
1253              client_entry->mode & SILC_UMODE_DETACHED)) {
1254           printformat_module("fe-common/silc", server, NULL,
1255                              MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1256                              client_entry->nickname);
1257         }
1258
1259         if (mode) {
1260           memset(buf, 0, sizeof(buf));
1261           silc_get_umode_string(mode, buf, sizeof(buf) - 1);
1262           printformat_module("fe-common/silc", server, NULL,
1263                              MSGLEVEL_CRAP, SILCTXT_WATCH_UMODE_CHANGE,
1264                              client_entry->nickname, buf);
1265         }
1266       } else if (notify == SILC_NOTIFY_TYPE_KILLED) {
1267         printformat_module("fe-common/silc", server, NULL,
1268                            MSGLEVEL_CRAP, SILCTXT_WATCH_KILLED,
1269                            client_entry->nickname);
1270       } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1271                  notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF) {
1272         printformat_module("fe-common/silc", server, NULL,
1273                            MSGLEVEL_CRAP, SILCTXT_WATCH_SIGNOFF,
1274                            client_entry->nickname);
1275       } else if (notify == SILC_NOTIFY_TYPE_NONE) {
1276         /* Client logged in to the network */
1277         printformat_module("fe-common/silc", server, NULL,
1278                            MSGLEVEL_CRAP, SILCTXT_WATCH_PRESENT,
1279                            client_entry->nickname);
1280       }
1281     }
1282     break;
1283
1284   default:
1285     /* Unknown notify */
1286     printformat_module("fe-common/silc", server, NULL,
1287                        MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
1288     break;
1289   }
1290
1291   va_end(va);
1292 }
1293
1294 /* Command handler. This function is called always in the command function.
1295    If error occurs it will be called as well. `conn' is the associated
1296    client connection. `cmd_context' is the command context that was
1297    originally sent to the command. `success' is FALSE if error occured
1298    during command. `command' is the command being processed. It must be
1299    noted that this is not reply from server. This is merely called just
1300    after application has called the command. Just to tell application
1301    that the command really was processed. */
1302
1303 static SilcBool cmode_list_chpks = FALSE;
1304
1305 void silc_command(SilcClient client, SilcClientConnection conn,
1306                   SilcBool success, SilcCommand command, SilcStatus status,
1307                   SilcUInt32 argc, unsigned char **argv)
1308 {
1309   SILC_SERVER_REC *server = conn->context;
1310
1311   SILC_LOG_DEBUG(("Start"));
1312
1313   if (!success) {
1314     silc_say_error("%s", silc_get_status_message(status));
1315     return;
1316   }
1317
1318   switch (command) {
1319
1320   case SILC_COMMAND_INVITE:
1321     if (argc > 2)
1322       printformat_module("fe-common/silc", server, NULL,
1323                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
1324                          argv[2],
1325                          (argv[1][0] == '*' ?
1326                           (char *)conn->current_channel->channel_name :
1327                           (char *)argv[1]));
1328     break;
1329
1330   case SILC_COMMAND_DETACH:
1331     server->no_reconnect = TRUE;
1332     break;
1333
1334   case SILC_COMMAND_CMODE:
1335     if (argc == 3 && !strcmp(argv[2], "+C"))
1336       cmode_list_chpks = TRUE;
1337     else
1338       cmode_list_chpks = FALSE;
1339     break;
1340
1341   default:
1342     break;
1343   }
1344 }
1345
1346 typedef struct {
1347   SilcClient client;
1348   SilcClientConnection conn;
1349   void *entry;
1350   SilcIdType id_type;
1351 } *GetkeyContext;
1352
1353 void silc_getkey_cb(bool success, void *context)
1354 {
1355   GetkeyContext getkey = (GetkeyContext)context;
1356   char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
1357   char *name = (getkey->id_type == SILC_ID_CLIENT ?
1358                 ((SilcClientEntry)getkey->entry)->nickname :
1359                 ((SilcServerEntry)getkey->entry)->server_name);
1360
1361   if (success) {
1362     printformat_module("fe-common/silc", NULL, NULL,
1363                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
1364   } else {
1365     printformat_module("fe-common/silc", NULL, NULL,
1366                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
1367                        entity, name);
1368   }
1369
1370   silc_free(getkey);
1371 }
1372
1373 /* Parse an invite or ban list */
1374 void silc_parse_inviteban_list(SilcClient client,
1375                                SilcClientConnection conn,
1376                                SILC_SERVER_REC *server,
1377                                SilcChannelEntry channel,
1378                                const char *list_type,
1379                                SilcArgumentPayload list)
1380 {
1381   unsigned char *tmp;
1382   SilcUInt32 type, len;
1383   SILC_CHANNEL_REC *chanrec = silc_channel_find_entry(server, channel);
1384   int counter=0, resolving = FALSE;
1385
1386   if (!silc_argument_get_arg_num(list)) {
1387     printformat_module("fe-common/silc", server,
1388                        (chanrec ? chanrec->visible_name : NULL),
1389                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_NO_INVITEBAN_LIST,
1390                        channel->channel_name, list_type);
1391     return;
1392   }
1393
1394   printformat_module("fe-common/silc", server,
1395                      (chanrec ? chanrec->visible_name : NULL),
1396                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_LIST,
1397                      channel->channel_name, list_type);
1398
1399   /* Parse the list */
1400   tmp = silc_argument_get_first_arg(list, &type, &len);
1401   while (tmp) {
1402     switch (type) {
1403       case 1:
1404         {
1405           /* An invite string */
1406           char **list;
1407           int i=0;
1408
1409           if (tmp[len-1] == ',')
1410             tmp[len-1] = '\0';
1411
1412           list = g_strsplit(tmp, ",", -1);
1413           while (list[i])
1414             printformat_module("fe-common/silc", server,
1415                                (chanrec ? chanrec->visible_name : NULL),
1416                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1417                                ++counter, channel->channel_name, list_type,
1418                                list[i++]);
1419           g_strfreev(list);
1420         }
1421         break;
1422
1423       case 2:
1424         {
1425           /* A public key */
1426           char *fingerprint, *babbleprint;
1427
1428           /* tmp is Public Key Payload, take public key from it. */
1429           fingerprint = silc_hash_fingerprint(NULL, tmp + 4, len - 4);
1430           babbleprint = silc_hash_babbleprint(NULL, tmp + 4, len - 4);
1431
1432           printformat_module("fe-common/silc", server,
1433                              (chanrec ? chanrec->visible_name : NULL),
1434                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_PUBKEY,
1435                              ++counter, channel->channel_name, list_type,
1436                              fingerprint, babbleprint);
1437         }
1438         break;
1439
1440       case 3:
1441         {
1442           /* A Client ID */
1443           SilcClientEntry client_entry;
1444           SilcID id;
1445
1446           if (!silc_id_payload_parse_id(tmp, len, &id)) {
1447             silc_say_error("Invalid data in %s list encountered", list_type);
1448             break;
1449           }
1450
1451           client_entry = silc_client_get_client_by_id(client, conn,
1452                                                       &id.u.client_id);
1453           if (client_entry) {
1454             printformat_module("fe-common/silc", server,
1455                                (chanrec ? chanrec->visible_name : NULL),
1456                                MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_STRING,
1457                                ++counter, channel->channel_name, list_type,
1458                                client_entry->nickname);
1459             silc_client_unref_client(client, conn, client_entry);
1460           } else {
1461             resolving = TRUE;
1462             silc_client_get_client_by_id_resolve(client, conn, &id.u.client_id,
1463                                                  NULL, NULL, NULL);
1464           }
1465         }
1466         break;
1467
1468       default:
1469         /* "trash" */
1470         silc_say_error("Unkown type in %s list: %u (len %u)",
1471                        list_type, type, len);
1472         break;
1473     }
1474     tmp = silc_argument_get_next_arg(list, &type, &len);
1475   }
1476
1477   if (resolving)
1478     printformat_module("fe-common/silc", server,
1479                        (chanrec ? chanrec->visible_name : NULL),
1480                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITEBAN_REGET,
1481                        list_type, channel->channel_name);
1482 }
1483
1484 /* Command reply handler. This function is called always in the command reply
1485    function. If error occurs it will be called as well. Normal scenario
1486    is that it will be called after the received command data has been parsed
1487    and processed. The function is used to pass the received command data to
1488    the application.
1489
1490    `conn' is the associated client connection. `cmd_payload' is the command
1491    payload data received from server and it can be ignored. It is provided
1492    if the application would like to re-parse the received command data,
1493    however, it must be noted that the data is parsed already by the library
1494    thus the payload can be ignored. `success' is FALSE if error occured.
1495    In this case arguments are not sent to the application. `command' is the
1496    command reply being processed. The function has variable argument list
1497    and each command defines the number and type of arguments it passes to the
1498    application (on error they are not sent). */
1499
1500 void silc_command_reply(SilcClient client, SilcClientConnection conn,
1501                         SilcCommand command, SilcStatus status,
1502                         SilcStatus error, va_list vp)
1503 {
1504   SILC_SERVER_REC *server = conn->context;
1505   SILC_CHANNEL_REC *chanrec;
1506
1507   SILC_LOG_DEBUG(("Start"));
1508
1509   switch(command) {
1510   case SILC_COMMAND_WHOIS:
1511     {
1512       char buf[1024], *nickname, *username, *realname, *nick;
1513       unsigned char *fingerprint;
1514       SilcUInt32 idle, mode, *user_modes;
1515       SilcDList channels;
1516       SilcClientEntry client_entry;
1517       SilcDList attrs;
1518
1519       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1520         /* Print the unknown nick for user */
1521         char *tmp = va_arg(vp, char *);
1522         if (tmp)
1523           silc_say_error("%s: %s", tmp, silc_get_status_message(status));
1524         break;
1525       } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1526         /* Try to find the entry for the unknown client ID, since we
1527            might have, and print the nickname of it for user. */
1528         SilcClientID *id = va_arg(vp, SilcClientID *);
1529         if (id) {
1530           client_entry = silc_client_get_client_by_id(client, conn, id);
1531           if (client_entry && client_entry->nickname[0])
1532             silc_say_error("%s: %s", client_entry->nickname,
1533                            silc_get_status_message(status));
1534           silc_client_unref_client(client, conn, client_entry);
1535         }
1536         break;
1537       } else if (SILC_STATUS_IS_ERROR(status)) {
1538         silc_say_error("WHOIS: %s", silc_get_status_message(status));
1539         return;
1540       }
1541
1542       client_entry = va_arg(vp, SilcClientEntry);
1543       nickname = va_arg(vp, char *);
1544       username = va_arg(vp, char *);
1545       realname = va_arg(vp, char *);
1546       channels = va_arg(vp, SilcDList);
1547       mode = va_arg(vp, SilcUInt32);
1548       idle = va_arg(vp, SilcUInt32);
1549       fingerprint = va_arg(vp, unsigned char *);
1550       user_modes = va_arg(vp, SilcUInt32 *);
1551       attrs = va_arg(vp, SilcDList);
1552
1553       silc_client_nickname_parse(client, conn, client_entry->nickname, &nick);
1554       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1555                          SILCTXT_WHOIS_USERINFO, nickname,
1556                          client_entry->username, client_entry->hostname,
1557                          nick, client_entry->nickname);
1558       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1559                          SILCTXT_WHOIS_REALNAME, realname);
1560       silc_free(nick);
1561
1562       if (channels && user_modes) {
1563         SilcChannelPayload entry;
1564         int i = 0;
1565
1566         memset(buf, 0, sizeof(buf));
1567         silc_dlist_start(channels);
1568         while ((entry = silc_dlist_get(channels))) {
1569           SilcUInt32 name_len;
1570           char *m = silc_client_chumode_char(user_modes[i++]);
1571           char *name = silc_channel_get_name(entry, &name_len);
1572
1573           if (m)
1574             silc_strncat(buf, sizeof(buf) - 1, m, strlen(m));
1575           silc_strncat(buf, sizeof(buf) - 1, name, name_len);
1576           silc_strncat(buf, sizeof(buf) - 1, " ", 1);
1577           silc_free(m);
1578         }
1579
1580         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1581                            SILCTXT_WHOIS_CHANNELS, buf);
1582       }
1583
1584       if (mode) {
1585         memset(buf, 0, sizeof(buf));
1586         silc_get_umode_string(mode, buf, sizeof(buf - 1));
1587         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1588                            SILCTXT_WHOIS_MODES, buf);
1589       }
1590
1591       if (idle && nickname) {
1592         memset(buf, 0, sizeof(buf));
1593         snprintf(buf, sizeof(buf) - 1, "%lu %s",
1594                  idle > 60 ? (idle / 60) : idle,
1595                  idle > 60 ? "minutes" : "seconds");
1596
1597         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1598                            SILCTXT_WHOIS_IDLE, buf);
1599       }
1600
1601       if (fingerprint) {
1602         fingerprint = silc_fingerprint(fingerprint, 20);
1603         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1604                            SILCTXT_WHOIS_FINGERPRINT, fingerprint);
1605         silc_free(fingerprint);
1606       }
1607
1608       if (attrs)
1609         silc_query_attributes_print(server, silc_client, conn, attrs,
1610                                     client_entry);
1611     }
1612     break;
1613
1614   case SILC_COMMAND_WHOWAS:
1615     {
1616       char *nickname, *username, *realname;
1617
1618       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
1619         char *tmp = va_arg(vp, char *);
1620         if (tmp)
1621           silc_say_error("%s: %s", tmp,
1622                          silc_get_status_message(status));
1623         break;
1624       } else if (SILC_STATUS_IS_ERROR(status)) {
1625         silc_say_error("WHOWAS: %s", silc_get_status_message(status));
1626         return;
1627       }
1628
1629       (void)va_arg(vp, SilcClientEntry);
1630       nickname = va_arg(vp, char *);
1631       username = va_arg(vp, char *);
1632       realname = va_arg(vp, char *);
1633
1634       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1635                          SILCTXT_WHOWAS_USERINFO, nickname, username,
1636                          realname ? realname : "");
1637     }
1638     break;
1639
1640   case SILC_COMMAND_INVITE:
1641     {
1642       SilcChannelEntry channel;
1643       SilcArgumentPayload invite_list;
1644
1645       if (SILC_STATUS_IS_ERROR(status))
1646         return;
1647
1648       channel = va_arg(vp, SilcChannelEntry);
1649       invite_list = va_arg(vp, SilcArgumentPayload);
1650
1651       if (invite_list)
1652         silc_parse_inviteban_list(client, conn, server, channel,
1653                                   "invite", invite_list);
1654     }
1655     break;
1656
1657   case SILC_COMMAND_JOIN:
1658     {
1659       char *channel, *mode, *topic, *cipher, *hmac;
1660       SilcUInt32 modei;
1661       SilcHashTableList *user_list;
1662       SilcChannelEntry channel_entry;
1663       SilcChannelUser chu;
1664       SilcClientEntry founder = NULL;
1665       NICK_REC *ownnick;
1666
1667       if (SILC_STATUS_IS_ERROR(status)) {
1668         silc_say_error("JOIN: %s", silc_get_status_message(status));
1669         return;
1670       }
1671
1672       channel = va_arg(vp, char *);
1673       channel_entry = va_arg(vp, SilcChannelEntry);
1674       modei = va_arg(vp, SilcUInt32);
1675       user_list = va_arg(vp, SilcHashTableList *);
1676       topic = va_arg(vp, char *);
1677       cipher = va_arg(vp, char *);
1678       hmac = va_arg(vp, char *);
1679
1680       chanrec = silc_channel_find(server, channel);
1681       if (!chanrec)
1682         chanrec = silc_channel_create(server, channel, channel, TRUE);
1683
1684       if (topic) {
1685         char tmp[256], *cp, *dm = NULL;
1686         g_free_not_null(chanrec->topic);
1687
1688         if (!silc_term_utf8() && silc_utf8_valid(topic, strlen(topic))) {
1689           memset(tmp, 0, sizeof(tmp));
1690           cp = tmp;
1691           if (strlen(topic) > sizeof(tmp) - 1) {
1692             dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1693             cp = dm;
1694           }
1695
1696           silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1697                            cp, strlen(topic));
1698           topic = cp;
1699         }
1700
1701         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
1702         signal_emit("channel topic changed", 1, chanrec);
1703
1704         silc_free(dm);
1705       }
1706
1707       mode = silc_client_chmode(modei, cipher ? cipher : "", hmac ? hmac : "");
1708       g_free_not_null(chanrec->mode);
1709       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
1710       signal_emit("channel mode changed", 1, chanrec);
1711
1712       /* Get user list */
1713       while (silc_hash_table_get(user_list, NULL, (void *)&chu)) {
1714         if (!chu->client->nickname[0])
1715           continue;
1716         if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1717           founder = chu->client;
1718         silc_nicklist_insert(chanrec, chu, FALSE);
1719       }
1720
1721       ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
1722       if (!ownnick)
1723         break;
1724       nicklist_set_own(CHANNEL(chanrec), ownnick);
1725       signal_emit("channel joined", 1, chanrec);
1726       chanrec->entry = channel_entry;
1727
1728       if (chanrec->topic)
1729         printformat_module("fe-common/silc", server,
1730                            channel_entry->channel_name,
1731                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
1732                            channel_entry->channel_name, chanrec->topic);
1733
1734       if (founder) {
1735         if (founder == conn->local_entry) {
1736           printformat_module("fe-common/silc",
1737                              server, channel_entry->channel_name,
1738                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_FOUNDER_YOU,
1739                              channel_entry->channel_name);
1740           signal_emit("nick mode changed", 2, chanrec, ownnick);
1741         } else
1742           printformat_module("fe-common/silc",
1743                              server, channel_entry->channel_name,
1744                              MSGLEVEL_CRAP, SILCTXT_CHANNEL_FOUNDER,
1745                              channel_entry->channel_name, founder->nickname);
1746       }
1747
1748       break;
1749     }
1750
1751   case SILC_COMMAND_NICK:
1752     {
1753       char *old;
1754       SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
1755       GSList *nicks;
1756
1757       if (SILC_STATUS_IS_ERROR(status)) {
1758         silc_say_error("NICK: %s", silc_get_status_message(status));
1759         return;
1760       }
1761
1762       nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
1763       if ((nicks != NULL) &&
1764           (strcmp(SERVER(server)->nick, client_entry->nickname))) {
1765         char buf[512];
1766         SilcClientEntry collider, old;
1767
1768         old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
1769         collider = silc_client_get_client_by_id(client, conn, &old->id);
1770         if (collider != client_entry) {
1771           memset(buf, 0, sizeof(buf));
1772           snprintf(buf, sizeof(buf) - 1, "%s@%s",
1773                    collider->username, collider->hostname);
1774           nicklist_rename_unique(SERVER(server),
1775                                  old, old->nickname,
1776                                  collider, collider->nickname);
1777           silc_print_nick_change(server, collider->nickname,
1778                                  client_entry->nickname, buf);
1779         }
1780         silc_client_unref_client(client, conn, collider);
1781       }
1782
1783       if (nicks != NULL)
1784         g_slist_free(nicks);
1785
1786       old = g_strdup(server->nick);
1787       server_change_nick(SERVER(server), client_entry->nickname);
1788       nicklist_rename_unique(SERVER(server),
1789                              server->conn->local_entry, server->nick,
1790                              client_entry, client_entry->nickname);
1791       signal_emit("message own_nick", 4, server, server->nick, old, "");
1792       g_free(old);
1793
1794       /* when connecting to a server, the last thing we receive
1795          is a SILC_COMMAND_LIST reply. Since we enable queueing
1796          during the connection, we can now safely disable it again */
1797       silc_queue_disable(conn);
1798       break;
1799     }
1800
1801   case SILC_COMMAND_LIST:
1802     {
1803       char *topic, *name;
1804       int usercount;
1805       char users[20];
1806       char tmp[256], *cp, *dm = NULL;
1807
1808       if (SILC_STATUS_IS_ERROR(status))
1809         return;
1810
1811       (void)va_arg(vp, SilcChannelEntry);
1812       name = va_arg(vp, char *);
1813       topic = va_arg(vp, char *);
1814       usercount = va_arg(vp, int);
1815
1816       if (topic && !silc_term_utf8() &&
1817           silc_utf8_valid(topic, strlen(topic))) {
1818         memset(tmp, 0, sizeof(tmp));
1819         cp = tmp;
1820         if (strlen(topic) > sizeof(tmp) - 1) {
1821           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
1822           cp = dm;
1823         }
1824
1825         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
1826                          cp, strlen(topic));
1827         topic = cp;
1828       }
1829
1830       if (status == SILC_STATUS_LIST_START ||
1831           status == SILC_STATUS_OK)
1832         printformat_module("fe-common/silc", server, NULL,
1833                            MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
1834
1835       if (!usercount)
1836         snprintf(users, sizeof(users) - 1, "N/A");
1837       else
1838         snprintf(users, sizeof(users) - 1, "%d", usercount);
1839       printformat_module("fe-common/silc", server, NULL,
1840                          MSGLEVEL_CRAP, SILCTXT_LIST,
1841                          name, users, topic ? topic : "");
1842       silc_free(dm);
1843     }
1844     break;
1845
1846   case SILC_COMMAND_UMODE:
1847     {
1848       SilcUInt32 mode;
1849       char *reason;
1850
1851       if (SILC_STATUS_IS_ERROR(status))
1852         return;
1853
1854       mode = va_arg(vp, SilcUInt32);
1855
1856       if (mode & SILC_UMODE_SERVER_OPERATOR &&
1857           !(server->umode & SILC_UMODE_SERVER_OPERATOR))
1858         printformat_module("fe-common/silc", server, NULL,
1859                            MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1860
1861       if (mode & SILC_UMODE_ROUTER_OPERATOR &&
1862           !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
1863         printformat_module("fe-common/silc", server, NULL,
1864                            MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1865
1866       if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
1867         if (mode & SILC_UMODE_GONE) {
1868           if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
1869             reason = g_strdup(server->away_reason);
1870           else
1871             reason = g_strdup("away");
1872         } else
1873           reason = g_strdup("");
1874
1875         silc_set_away(reason, server);
1876
1877         g_free(reason);
1878       }
1879
1880       server->umode = mode;
1881       signal_emit("user mode changed", 2, server, NULL);
1882     }
1883     break;
1884
1885   case SILC_COMMAND_OPER:
1886     if (SILC_STATUS_IS_ERROR(status)) {
1887       silc_say_error("OPER: %s", silc_get_status_message(status));
1888       return;
1889     }
1890
1891     server->umode |= SILC_UMODE_SERVER_OPERATOR;
1892     signal_emit("user mode changed", 2, server, NULL);
1893
1894     printformat_module("fe-common/silc", server, NULL,
1895                        MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
1896     break;
1897
1898   case SILC_COMMAND_SILCOPER:
1899     if (SILC_STATUS_IS_ERROR(status)) {
1900       silc_say_error("SILCOPER: %s", silc_get_status_message(status));
1901       return;
1902     }
1903
1904     server->umode |= SILC_UMODE_ROUTER_OPERATOR;
1905     signal_emit("user mode changed", 2, server, NULL);
1906
1907     printformat_module("fe-common/silc", server, NULL,
1908                        MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
1909     break;
1910
1911   case SILC_COMMAND_USERS:
1912     {
1913       SilcHashTableList htl;
1914       SilcChannelEntry channel;
1915       SilcChannelUser chu;
1916
1917       if (SILC_STATUS_IS_ERROR(status)) {
1918         silc_say_error("USERS: %s", silc_get_status_message(status));
1919         return;
1920       }
1921
1922       channel = va_arg(vp, SilcChannelEntry);
1923
1924       printformat_module("fe-common/silc", server, channel->channel_name,
1925                          MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
1926                          channel->channel_name);
1927
1928       silc_hash_table_list(channel->user_list, &htl);
1929       while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1930         SilcClientEntry e = chu->client;
1931         char stat[5], *mode;
1932
1933         if (!e->nickname[0])
1934           continue;
1935
1936         memset(stat, 0, sizeof(stat));
1937         mode = silc_client_chumode_char(chu->mode);
1938         if (e->mode & SILC_UMODE_GONE)
1939           strcat(stat, "G");
1940         else if (e->mode & SILC_UMODE_INDISPOSED)
1941           strcat(stat, "I");
1942         else if (e->mode & SILC_UMODE_BUSY)
1943           strcat(stat, "B");
1944         else if (e->mode & SILC_UMODE_PAGE)
1945           strcat(stat, "P");
1946         else if (e->mode & SILC_UMODE_HYPER)
1947           strcat(stat, "H");
1948         else if (e->mode & SILC_UMODE_ROBOT)
1949           strcat(stat, "R");
1950         else if (e->mode & SILC_UMODE_ANONYMOUS)
1951           strcat(stat, "?");
1952         else
1953           strcat(stat, "A");
1954         if (mode)
1955           strcat(stat, mode);
1956
1957         printformat_module("fe-common/silc", server, channel->channel_name,
1958                            MSGLEVEL_CRAP, SILCTXT_USERS,
1959                            e->nickname, stat,
1960                            e->username[0] ? e->username : "",
1961                            e->hostname[0] ? e->hostname : "",
1962                            e->realname ? e->realname : "");
1963         if (mode)
1964           silc_free(mode);
1965       }
1966       silc_hash_table_list_reset(&htl);
1967     }
1968     break;
1969
1970   case SILC_COMMAND_BAN:
1971     {
1972       SilcChannelEntry channel;
1973       SilcArgumentPayload invite_list;
1974
1975       if (SILC_STATUS_IS_ERROR(status))
1976         return;
1977
1978       channel = va_arg(vp, SilcChannelEntry);
1979       invite_list = va_arg(vp, SilcArgumentPayload);
1980
1981       if (invite_list)
1982         silc_parse_inviteban_list(client, conn, server, channel,
1983                                   "ban", invite_list);
1984     }
1985     break;
1986
1987   case SILC_COMMAND_GETKEY:
1988     {
1989       SilcIdType id_type;
1990       void *entry;
1991       SilcPublicKey public_key;
1992       GetkeyContext getkey;
1993       char *name;
1994
1995       if (SILC_STATUS_IS_ERROR(status)) {
1996         silc_say_error("GETKEY: %s", silc_get_status_message(status));
1997         return;
1998       }
1999
2000       id_type = va_arg(vp, SilcUInt32);
2001       entry = va_arg(vp, void *);
2002       public_key = va_arg(vp, SilcPublicKey);
2003
2004       if (public_key) {
2005         getkey = silc_calloc(1, sizeof(*getkey));
2006         getkey->entry = entry;
2007         getkey->id_type = id_type;
2008         getkey->client = client;
2009         getkey->conn = conn;
2010
2011         name = (id_type == SILC_ID_CLIENT ?
2012                 ((SilcClientEntry)entry)->nickname :
2013                 ((SilcServerEntry)entry)->server_name);
2014
2015         silc_verify_public_key_internal(client, conn, name,
2016                                         (id_type == SILC_ID_CLIENT ?
2017                                          SILC_CONN_CLIENT :
2018                                          SILC_CONN_SERVER),
2019                                         public_key, silc_getkey_cb, getkey);
2020       } else {
2021         printformat_module("fe-common/silc", server, NULL,
2022                            MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
2023       }
2024     }
2025     break;
2026
2027   case SILC_COMMAND_INFO:
2028     {
2029       SilcServerEntry server_entry;
2030       char *server_name;
2031       char *server_info;
2032
2033       if (SILC_STATUS_IS_ERROR(status))
2034         return;
2035
2036       server_entry = va_arg(vp, SilcServerEntry);
2037       server_name = va_arg(vp, char *);
2038       server_info = va_arg(vp, char *);
2039
2040       if (server_name && server_info )
2041         {
2042           printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
2043           printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
2044         }
2045     }
2046     break;
2047
2048   case SILC_COMMAND_TOPIC:
2049     {
2050       SilcChannelEntry channel;
2051       char *topic;
2052       char tmp[256], *cp, *dm = NULL;
2053
2054       if (SILC_STATUS_IS_ERROR(status))
2055         return;
2056
2057       channel = va_arg(vp, SilcChannelEntry);
2058       topic = va_arg(vp, char *);
2059
2060       if (topic && !silc_term_utf8() &&
2061           silc_utf8_valid(topic, strlen(topic))) {
2062         memset(tmp, 0, sizeof(tmp));
2063         cp = tmp;
2064         if (strlen(topic) > sizeof(tmp) - 1) {
2065           dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
2066           cp = dm;
2067         }
2068
2069         silc_utf8_decode(topic, strlen(topic), SILC_STRING_LOCALE,
2070                          cp, strlen(topic));
2071         topic = cp;
2072       }
2073
2074       if (topic) {
2075         chanrec = silc_channel_find_entry(server, channel);
2076         if (chanrec) {
2077           g_free_not_null(chanrec->topic);
2078           chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
2079           signal_emit("channel topic changed", 1, chanrec);
2080         }
2081         printformat_module("fe-common/silc", server, channel->channel_name,
2082                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
2083                            channel->channel_name, topic);
2084       } else {
2085         printformat_module("fe-common/silc", server, channel->channel_name,
2086                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
2087                            channel->channel_name);
2088       }
2089       silc_free(dm);
2090     }
2091     break;
2092
2093   case SILC_COMMAND_WATCH:
2094     break;
2095
2096   case SILC_COMMAND_STATS:
2097     {
2098       SilcClientStats *cstats;
2099       char tmp[40];
2100       const char *tmptime;
2101       int days, hours, mins, secs;
2102
2103       if (SILC_STATUS_IS_ERROR(status))
2104         return;
2105
2106       cstats = va_arg(vp, SilcClientStats *);
2107       if (!cstats) {
2108         printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
2109         return;
2110       }
2111
2112       tmptime = silc_time_string(cstats->starttime);
2113       printformat_module("fe-common/silc", server, NULL,
2114                          MSGLEVEL_CRAP, SILCTXT_STATS,
2115                          "Local server start time", tmptime);
2116
2117       days = cstats->uptime / (24 * 60 * 60);
2118       cstats->uptime -= days * (24 * 60 * 60);
2119       hours = cstats->uptime / (60 * 60);
2120       cstats->uptime -= hours * (60 * 60);
2121       mins = cstats->uptime / 60;
2122       cstats->uptime -= mins * 60;
2123       secs = cstats->uptime;
2124       snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
2125                days, hours, mins, secs);
2126       printformat_module("fe-common/silc", server, NULL,
2127                          MSGLEVEL_CRAP, SILCTXT_STATS,
2128                          "Local server uptime", tmp);
2129
2130       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_clients);
2131       printformat_module("fe-common/silc", server, NULL,
2132                          MSGLEVEL_CRAP, SILCTXT_STATS,
2133                          "Local server clients", tmp);
2134
2135       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_channels);
2136       printformat_module("fe-common/silc", server, NULL,
2137                          MSGLEVEL_CRAP, SILCTXT_STATS,
2138                          "Local server channels", tmp);
2139
2140       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_server_ops);
2141       printformat_module("fe-common/silc", server, NULL,
2142                          MSGLEVEL_CRAP, SILCTXT_STATS,
2143                          "Local server operators", tmp);
2144
2145       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->my_router_ops);
2146       printformat_module("fe-common/silc", server, NULL,
2147                          MSGLEVEL_CRAP, SILCTXT_STATS,
2148                          "Local router operators", tmp);
2149
2150       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_clients);
2151       printformat_module("fe-common/silc", server, NULL,
2152                          MSGLEVEL_CRAP, SILCTXT_STATS,
2153                          "Local cell clients", tmp);
2154
2155       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_channels);
2156       printformat_module("fe-common/silc", server, NULL,
2157                          MSGLEVEL_CRAP, SILCTXT_STATS,
2158                          "Local cell channels", tmp);
2159
2160       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->cell_servers);
2161       printformat_module("fe-common/silc", server, NULL,
2162                          MSGLEVEL_CRAP, SILCTXT_STATS,
2163                          "Local cell servers", tmp);
2164
2165       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->clients);
2166       printformat_module("fe-common/silc", server, NULL,
2167                          MSGLEVEL_CRAP, SILCTXT_STATS,
2168                          "Total clients", tmp);
2169
2170       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->channels);
2171       printformat_module("fe-common/silc", server, NULL,
2172                          MSGLEVEL_CRAP, SILCTXT_STATS,
2173                          "Total channels", tmp);
2174
2175       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->servers);
2176       printformat_module("fe-common/silc", server, NULL,
2177                          MSGLEVEL_CRAP, SILCTXT_STATS,
2178                          "Total servers", tmp);
2179
2180       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->routers);
2181       printformat_module("fe-common/silc", server, NULL,
2182                          MSGLEVEL_CRAP, SILCTXT_STATS,
2183                          "Total routers", tmp);
2184
2185       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->server_ops);
2186       printformat_module("fe-common/silc", server, NULL,
2187                          MSGLEVEL_CRAP, SILCTXT_STATS,
2188                            "Total server operators", tmp);
2189
2190       snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cstats->router_ops);
2191       printformat_module("fe-common/silc", server, NULL,
2192                          MSGLEVEL_CRAP, SILCTXT_STATS,
2193                          "Total router operators", tmp);
2194     }
2195     break;
2196
2197   case SILC_COMMAND_CMODE:
2198     {
2199       SilcChannelEntry channel_entry;
2200       SilcDList chpks;
2201
2202       channel_entry = va_arg(vp, SilcChannelEntry);
2203       (void)va_arg(vp, SilcUInt32);
2204       (void)va_arg(vp, SilcPublicKey);
2205       chpks = va_arg(vp, SilcDList);
2206
2207       if (SILC_STATUS_IS_ERROR(status) || !cmode_list_chpks ||
2208           !channel_entry || !channel_entry->channel_name)
2209         return;
2210
2211       /* Print the channel public key list */
2212       if (chpks)
2213         silc_parse_channel_public_keys(server, channel_entry, chpks);
2214       else
2215         printformat_module("fe-common/silc", server, NULL,
2216                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_PK_NO_LIST,
2217                          channel_entry->channel_name);
2218
2219     }
2220     break;
2221
2222   case SILC_COMMAND_LEAVE:
2223     {
2224       if (SILC_STATUS_IS_ERROR(status))
2225         return;
2226
2227       /* We might be cycling, so disable queueing again */
2228       silc_queue_disable(conn);
2229     }
2230     break;
2231
2232   case SILC_COMMAND_DETACH:
2233     {
2234       /* Save the detachment data to file. */
2235       char *file;
2236       SilcBuffer detach;
2237
2238       if (SILC_STATUS_IS_ERROR(status))
2239         return;
2240
2241       detach = va_arg(vp, SilcBuffer);
2242       file = silc_get_session_filename(server);
2243       silc_file_writefile(file, silc_buffer_data(detach),
2244                           silc_buffer_len(detach));
2245       silc_free(file);
2246     }
2247     break;
2248
2249   case SILC_COMMAND_KILL:
2250     {
2251       SilcClientEntry client_entry;
2252
2253       if (SILC_STATUS_IS_ERROR(status)) {
2254         silc_say_error("KILL: %s", silc_get_status_message(status));
2255         return;
2256       }
2257
2258       client_entry = va_arg(vp, SilcClientEntry);
2259       if (!client_entry || !client_entry->nickname[0])
2260         break;
2261
2262       /* Print this only if the killed client isn't joined on channels.
2263          If it is, we receive KILLED notify and we'll print this there. */
2264       if (!silc_hash_table_count(client_entry->channels))
2265         printformat_module("fe-common/silc", server, NULL,
2266                            MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED,
2267                            client_entry->nickname,
2268                            conn->local_entry->nickname, "");
2269     }
2270   }
2271 }
2272
2273 typedef struct {
2274   SilcClient client;
2275   SilcClientConnection conn;
2276   char *filename;
2277   char *entity;
2278   char *entity_name;
2279   SilcPublicKey public_key;
2280   SilcVerifyPublicKey completion;
2281   void *context;
2282 } *PublicKeyVerify;
2283
2284 static void verify_public_key_completion(const char *line, void *context)
2285 {
2286   PublicKeyVerify verify = (PublicKeyVerify)context;
2287
2288   if (line[0] == 'Y' || line[0] == 'y') {
2289     /* Call the completion */
2290     if (verify->completion)
2291       verify->completion(TRUE, verify->context);
2292
2293     /* Save the key for future checking */
2294     silc_pkcs_save_public_key(verify->filename, verify->public_key,
2295                               SILC_PKCS_FILE_BASE64);
2296   } else {
2297     /* Call the completion */
2298     if (verify->completion)
2299       verify->completion(FALSE, verify->context);
2300
2301     printformat_module("fe-common/silc", NULL, NULL,
2302                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD,
2303                        verify->entity_name ? verify->entity_name :
2304                        verify->entity);
2305   }
2306
2307   silc_free(verify->filename);
2308   silc_free(verify->entity);
2309   silc_free(verify->entity_name);
2310   silc_free(verify);
2311 }
2312
2313 /* Internal routine to verify public key. If the `completion' is provided
2314    it will be called to indicate whether public was verified or not. For
2315    server/router public key this will check for filename that includes the
2316    remote host's IP address and remote host's hostname. */
2317
2318 static void
2319 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
2320                                 const char *name,
2321                                 SilcConnectionType conn_type,
2322                                 SilcPublicKey public_key,
2323                                 SilcVerifyPublicKey completion, void *context)
2324 {
2325   PublicKeyVerify verify;
2326   char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
2327   char *fingerprint, *babbleprint, *format;
2328   SilcPublicKey local_pubkey;
2329   SilcUInt16 port;
2330   const char *hostname, *ip;
2331   unsigned char *pk;
2332   SilcUInt32 pk_len;
2333   struct passwd *pw;
2334   struct stat st;
2335   char *entity = ((conn_type == SILC_CONN_SERVER ||
2336                    conn_type == SILC_CONN_ROUTER) ?
2337                   "server" : "client");
2338   int i;
2339
2340   if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
2341     printformat_module("fe-common/silc", NULL, NULL,
2342                        MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED,
2343                        entity, silc_pkcs_get_type(public_key));
2344     if (completion)
2345       completion(FALSE, context);
2346     return;
2347   }
2348
2349   /* Encode public key */
2350   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
2351   if (!pk) {
2352     if (completion)
2353       completion(FALSE, context);
2354     return;
2355   }
2356
2357   pw = getpwuid(getuid());
2358   if (!pw) {
2359     if (completion)
2360       completion(FALSE, context);
2361     silc_free(pk);
2362     return;
2363   }
2364
2365   memset(filename, 0, sizeof(filename));
2366   memset(filename2, 0, sizeof(filename2));
2367   memset(file, 0, sizeof(file));
2368
2369   /* Get remote host information */
2370   silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
2371                               NULL, &hostname, &ip, &port);
2372
2373   if (conn_type == SILC_CONN_SERVER ||
2374       conn_type == SILC_CONN_ROUTER) {
2375     if (!name) {
2376       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, ip, port);
2377       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2378                get_irssi_dir(), entity, file);
2379
2380       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2381                hostname, port);
2382       snprintf(filename2, sizeof(filename2) - 1, "%s/%skeys/%s",
2383                get_irssi_dir(), entity, file);
2384
2385       ipf = filename;
2386       hostf = filename2;
2387     } else {
2388       snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
2389                name, port);
2390       snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2391                get_irssi_dir(), entity, file);
2392
2393       ipf = filename;
2394     }
2395   } else {
2396     /* Replace all whitespaces with `_'. */
2397     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2398     for (i = 0; i < strlen(fingerprint); i++)
2399       if (fingerprint[i] == ' ')
2400         fingerprint[i] = '_';
2401
2402     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
2403     snprintf(filename, sizeof(filename) - 1, "%s/%skeys/%s",
2404              get_irssi_dir(), entity, file);
2405     silc_free(fingerprint);
2406
2407     ipf = filename;
2408   }
2409
2410   /* Take fingerprint of the public key */
2411   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
2412   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
2413
2414   verify = silc_calloc(1, sizeof(*verify));
2415   verify->client = client;
2416   verify->conn = conn;
2417   verify->filename = strdup(ipf);
2418   verify->entity = strdup(entity);
2419   verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
2420                          (name ? strdup(name) : strdup(hostname))
2421                          : NULL);
2422   verify->public_key = public_key;
2423   verify->completion = completion;
2424   verify->context = context;
2425
2426   /* Check whether this key already exists */
2427   if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
2428     /* Key does not exist, ask user to verify the key and save it */
2429
2430     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2431                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2432                        verify->entity_name : entity);
2433     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2434                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2435     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2436                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2437     format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2438                              SILCTXT_PUBKEY_ACCEPT);
2439     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2440                             format, 0, verify);
2441     g_free(format);
2442     silc_free(fingerprint);
2443     silc_free(babbleprint);
2444     silc_free(pk);
2445     return;
2446   } else {
2447     /* The key already exists, verify it. */
2448     unsigned char *encpk;
2449     SilcUInt32 encpk_len;
2450
2451     /* Load the key file, try for both IP filename and hostname filename */
2452     if (!silc_pkcs_load_public_key(ipf, &local_pubkey) &&
2453         (!hostf || (!silc_pkcs_load_public_key(hostf, &local_pubkey)))) {
2454       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2455                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2456                          verify->entity_name : entity);
2457       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2458                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2459       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2460                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2461       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2462                          SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
2463       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2464                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2465       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2466                               format, 0, verify);
2467       g_free(format);
2468       silc_free(fingerprint);
2469       silc_free(babbleprint);
2470       silc_free(pk);
2471       return;
2472     }
2473
2474     /* Encode the key data */
2475     encpk = silc_pkcs_public_key_encode(local_pubkey, &encpk_len);
2476     if (!encpk) {
2477       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2478                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2479                          verify->entity_name : entity);
2480       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2481                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2482       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2483                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2484       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2485                          SILCTXT_PUBKEY_MALFORMED, entity);
2486       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2487                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2488       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2489                               format, 0, verify);
2490       g_free(format);
2491       silc_free(fingerprint);
2492       silc_free(babbleprint);
2493       silc_free(pk);
2494       return;
2495     }
2496     silc_pkcs_public_key_free(local_pubkey);
2497
2498     /* Compare the keys */
2499     if (memcmp(encpk, pk, encpk_len)) {
2500       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2501                          SILCTXT_PUBKEY_RECEIVED,verify->entity_name ?
2502                          verify->entity_name : entity);
2503       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2504                          SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
2505       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2506                          SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
2507       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2508                          SILCTXT_PUBKEY_NO_MATCH, entity);
2509       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2510                          SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
2511       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2512                          SILCTXT_PUBKEY_MITM_ATTACK, entity);
2513
2514       /* Ask user to verify the key and save it */
2515       format = format_get_text("fe-common/silc", NULL, NULL, NULL,
2516                                SILCTXT_PUBKEY_ACCEPT_ANYWAY);
2517       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
2518                               format, 0, verify);
2519       g_free(format);
2520       silc_free(fingerprint);
2521       silc_free(babbleprint);
2522       silc_free(encpk);
2523       silc_free(pk);
2524       return;
2525     }
2526
2527     /* Local copy matched */
2528     if (completion)
2529       completion(TRUE, context);
2530     silc_free(encpk);
2531     silc_free(fingerprint);
2532     silc_free(babbleprint);
2533     silc_free(verify->filename);
2534     silc_free(verify->entity);
2535     silc_free(verify->entity_name);
2536     silc_free(verify);
2537     silc_free(pk);
2538   }
2539 }
2540
2541 /* Verifies received public key. The `conn_type' indicates which entity
2542    (server, client etc.) has sent the public key. If user decides to trust
2543    the key may be saved as trusted public key for later use. The
2544    `completion' must be called after the public key has been verified. */
2545
2546 void
2547 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
2548                        SilcConnectionType conn_type,
2549                        SilcPublicKey public_key,
2550                        SilcVerifyPublicKey completion, void *context)
2551 {
2552   silc_verify_public_key_internal(client, conn, NULL, conn_type, public_key,
2553                                   completion, context);
2554 }
2555
2556 /* Asks passphrase from user on the input line. */
2557
2558 typedef struct {
2559   SilcAskPassphrase completion;
2560   void *context;
2561 } *AskPassphrase;
2562
2563 void ask_passphrase_completion(const char *passphrase, void *context)
2564 {
2565   AskPassphrase p = (AskPassphrase)context;
2566   if (passphrase && passphrase[0] == '\0')
2567     passphrase = NULL;
2568   p->completion((unsigned char *)passphrase,
2569                 passphrase ? strlen(passphrase) : 0, p->context);
2570   silc_free(p);
2571 }
2572
2573 void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
2574                          SilcAskPassphrase completion, void *context)
2575 {
2576   AskPassphrase p = silc_calloc(1, sizeof(*p));
2577   p->completion = completion;
2578   p->context = context;
2579
2580   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
2581                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
2582 }
2583
2584 typedef struct {
2585   SilcGetAuthMeth completion;
2586   void *context;
2587 } *GetAuthMethod;
2588
2589 static void silc_get_auth_ask_passphrase(unsigned char *passphrase,
2590                                          SilcUInt32 passphrase_len,
2591                                          void *context)
2592 {
2593   GetAuthMethod a = context;
2594   a->completion(passphrase ? SILC_AUTH_PASSWORD : SILC_AUTH_NONE,
2595                 passphrase, passphrase_len, a->context);
2596   silc_free(a);
2597 }
2598
2599 /* Find authentication data by hostname and port. The hostname may be IP
2600    address as well.*/
2601
2602 void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
2603                           char *hostname, SilcUInt16 port,
2604                           SilcAuthMethod auth_meth,
2605                           SilcGetAuthMeth completion, void *context)
2606 {
2607   SERVER_SETUP_REC *setup;
2608
2609   SILC_LOG_DEBUG(("Start"));
2610
2611   if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
2612     /* Returning NULL will cause library to use our private key configured
2613        for this connection */
2614     completion(SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
2615     return;
2616   }
2617
2618   /* Check whether we find the password for this server in our
2619      configuration.  If it's set, always send it server. */
2620   setup = server_setup_find_port(hostname, port);
2621   if (setup && setup->password) {
2622     completion(SILC_AUTH_PASSWORD, setup->password, strlen(setup->password),
2623                context);
2624     return;
2625   }
2626
2627   /* Didn't find password.  If server wants it, ask it from user. */
2628   if (auth_meth == SILC_AUTH_PASSWORD) {
2629     GetAuthMethod a;
2630     a = silc_calloc(1, sizeof(*a));
2631     if (a) {
2632       a->completion = completion;
2633       a->context = context;
2634       silc_ask_passphrase(client, conn, silc_get_auth_ask_passphrase, a);
2635       return;
2636     }
2637   }
2638
2639   /* No authentication */
2640   completion(SILC_AUTH_NONE, NULL, 0, context);
2641 }
2642
2643 /* Asks whether the user would like to perform the key agreement protocol.
2644    This is called after we have received an key agreement packet or an
2645    reply to our key agreement packet. This returns TRUE if the user wants
2646    the library to perform the key agreement protocol and FALSE if it is not
2647    desired (application may start it later by calling the function
2648    silc_client_perform_key_agreement). */
2649
2650 void silc_key_agreement(SilcClient client, SilcClientConnection conn,
2651                         SilcClientEntry client_entry, const char *hostname,
2652                         SilcUInt16 protocol, SilcUInt16 port)
2653 {
2654   char portstr[12], protostr[5];
2655
2656   SILC_LOG_DEBUG(("Start"));
2657
2658   /* We will just display the info on the screen and return FALSE and user
2659      will have to start the key agreement with a command. */
2660
2661   if (hostname) {
2662     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2663     snprintf(protostr, sizeof(protostr) - 1, "%s", protocol == 1 ? "UDP" :
2664              "TCP");
2665   }
2666
2667   if (!hostname)
2668     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2669                        SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
2670   else
2671     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2672                        SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
2673                        client_entry->nickname, hostname, portstr, protostr);
2674 }
2675
2676 /* Notifies application that file transfer protocol session is being
2677    requested by the remote client indicated by the `client_entry' from
2678    the `hostname' and `port'. The `session_id' is the file transfer
2679    session and it can be used to either accept or reject the file
2680    transfer request, by calling the silc_client_file_receive or
2681    silc_client_file_close, respectively. */
2682
2683 void silc_ftp(SilcClient client, SilcClientConnection conn,
2684               SilcClientEntry client_entry, SilcUInt32 session_id,
2685               const char *hostname, SilcUInt16 port)
2686 {
2687   SILC_SERVER_REC *server;
2688   char portstr[12];
2689   FtpSession ftp = NULL;
2690
2691   SILC_LOG_DEBUG(("Start"));
2692
2693   server = conn->context;
2694
2695   silc_dlist_start(server->ftp_sessions);
2696   while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
2697     if (ftp->client_entry == client_entry &&
2698         ftp->session_id == session_id) {
2699       server->current_session = ftp;
2700       break;
2701     }
2702   }
2703   if (ftp == SILC_LIST_END) {
2704     ftp = silc_calloc(1, sizeof(*ftp));
2705     ftp->client_entry = client_entry;
2706     ftp->session_id = session_id;
2707     ftp->send = FALSE;
2708     ftp->conn = conn;
2709     silc_dlist_add(server->ftp_sessions, ftp);
2710     server->current_session = ftp;
2711   }
2712
2713   if (hostname)
2714     snprintf(portstr, sizeof(portstr) - 1, "%d", port);
2715
2716   if (!hostname)
2717     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2718                        SILCTXT_FILE_REQUEST, client_entry->nickname);
2719   else
2720     printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
2721                        SILCTXT_FILE_REQUEST_HOST,
2722                        client_entry->nickname, hostname, portstr);
2723 }
2724
2725 /* SILC client operations */
2726 SilcClientOperations ops = {
2727   silc_say,
2728   silc_channel_message,
2729   silc_private_message,
2730   silc_notify,
2731   silc_command,
2732   silc_command_reply,
2733   silc_get_auth_method,
2734   silc_verify_public_key,
2735   silc_ask_passphrase,
2736   silc_key_agreement,
2737   silc_ftp,
2738 };