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