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