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