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