updates.
[silc.git] / apps / irssi / src / silc / core / silc-core.c
1 /*
2
3   silc-core.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 #include "version_internal.h"
33 #include "version.h"
34
35 #include "signals.h"
36 #include "levels.h"
37 #include "settings.h"
38 #include "fe-common/core/printtext.h"
39 #include "fe-common/core/fe-channels.h"
40 #include "fe-common/core/keyboard.h"
41
42 /* Command line option variables */
43 static bool opt_create_keypair = FALSE;
44 static bool opt_debug = FALSE;
45 static char *opt_pkcs = NULL;
46 static char *opt_keyfile = NULL;
47 static int opt_bits = 0;
48
49 static int idletag;
50
51 SilcClient silc_client = NULL;
52 SilcClientConfig silc_config = NULL;
53 extern SilcClientOperations ops;
54 extern int silc_debug;
55 #ifdef SILC_SIM
56 /* SIM (SILC Module) table */
57 SilcSimContext **sims = NULL;
58 uint32 sims_count = 0;
59 #endif
60
61 static void silc_say(SilcClient client, SilcClientConnection conn,
62                      char *msg, ...);
63 static void 
64 silc_channel_message(SilcClient client, SilcClientConnection conn,
65                      SilcClientEntry sender, SilcChannelEntry channel,
66                      SilcMessageFlags flags, char *msg);
67 static void 
68 silc_private_message(SilcClient client, SilcClientConnection conn,
69                      SilcClientEntry sender, SilcMessageFlags flags,
70                      char *msg);
71 static void silc_notify(SilcClient client, SilcClientConnection conn,
72                         SilcNotifyType type, ...);
73 static void 
74 silc_connect(SilcClient client, SilcClientConnection conn, int success);
75 static void 
76 silc_disconnect(SilcClient client, SilcClientConnection conn);
77 static void 
78 silc_command(SilcClient client, SilcClientConnection conn, 
79              SilcClientCommandContext cmd_context, int success,
80              SilcCommand command);
81 static void 
82 silc_command_reply(SilcClient client, SilcClientConnection conn,
83                    SilcCommandPayload cmd_payload, int success,
84                    SilcCommand command, SilcCommandStatus status, ...);
85 static void 
86 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
87                                 SilcSocketType conn_type, unsigned char *pk, 
88                                 uint32 pk_len, SilcSKEPKType pk_type,
89                                 SilcVerifyPublicKey completion, void *context);
90 static void 
91 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
92                        SilcSocketType conn_type, unsigned char *pk, 
93                        uint32 pk_len, SilcSKEPKType pk_type,
94                        SilcVerifyPublicKey completion, void *context);
95 static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
96                                 SilcAskPassphrase completion, void *context);
97 static int 
98 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
99                      char *hostname, uint16 port,
100                      SilcProtocolAuthMeth *auth_meth,
101                      unsigned char **auth_data,
102                      uint32 *auth_data_len);
103 static void 
104 silc_failure(SilcClient client, SilcClientConnection conn, 
105              SilcProtocol protocol, void *failure);
106 static int 
107 silc_key_agreement(SilcClient client, SilcClientConnection conn,
108                    SilcClientEntry client_entry, char *hostname,
109                    int port,
110                    SilcKeyAgreementCallback *completion,
111                    void **context);
112
113 static void silc_say(SilcClient client, SilcClientConnection conn,
114                      char *msg, ...)
115 {
116   SILC_SERVER_REC *server;
117   va_list va;
118   char *str;
119
120   server = conn == NULL ? NULL : conn->context;
121   
122   va_start(va, msg);
123   str = g_strdup_vprintf(msg, va);
124   printtext(server, "#silc", MSGLEVEL_CRAP, "%s", str);
125   g_free(str);
126   va_end(va);
127 }
128
129 static void silc_say_error(char *msg, ...)
130 {
131   va_list va;
132   char *str;
133
134   va_start(va, msg);
135   str = g_strdup_vprintf(msg, va);
136   printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
137
138   g_free(str);
139   va_end(va);
140 }
141
142 /* Message for a channel. The `sender' is the nickname of the sender 
143    received in the packet. The `channel_name' is the name of the channel. */
144
145 static void 
146 silc_channel_message(SilcClient client, SilcClientConnection conn,
147                      SilcClientEntry sender, SilcChannelEntry channel,
148                      SilcMessageFlags flags, char *msg)
149 {
150   SILC_SERVER_REC *server;
151   SILC_NICK_REC *nick;
152   SILC_CHANNEL_REC *chanrec;
153   
154   server = conn == NULL ? NULL : conn->context;
155   chanrec = silc_channel_find_entry(server, channel);
156   
157   nick = silc_nicklist_find(chanrec, sender);
158
159   if (flags & SILC_MESSAGE_FLAG_ACTION)
160     ;
161   else if (flags & SILC_MESSAGE_FLAG_NOTICE)
162     ;
163   else
164     signal_emit("message public", 6, server, msg,
165                 nick == NULL ? "[<unknown>]" : nick->nick,
166                 nick == NULL ? NULL : nick->host,
167                 chanrec->name, nick);
168 }
169
170 /* Private message to the client. The `sender' is the nickname of the
171    sender received in the packet. */
172
173 static void 
174 silc_private_message(SilcClient client, SilcClientConnection conn,
175                      SilcClientEntry sender, SilcMessageFlags flags,
176                           char *msg)
177 {
178   SILC_SERVER_REC *server;
179   
180   server = conn == NULL ? NULL : conn->context;
181   signal_emit("message private", 4, server, msg,
182               sender->nickname ? sender->nickname : "[<unknown>]",
183               sender->username ? sender->username : NULL);
184 }
185
186 /* Notify message to the client. The notify arguments are sent in the
187    same order as servers sends them. The arguments are same as received
188    from the server except for ID's.  If ID is received application receives
189    the corresponding entry to the ID. For example, if Client ID is received
190    application receives SilcClientEntry.  Also, if the notify type is
191    for channel the channel entry is sent to application (even if server
192    does not send it). */
193
194 typedef struct {
195   int type;
196   const char *name;
197 } NOTIFY_REC;
198
199 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
200 static NOTIFY_REC notifies[] = {
201   { SILC_NOTIFY_TYPE_NONE,              NULL },
202   { SILC_NOTIFY_TYPE_INVITE,            "invite" },
203   { SILC_NOTIFY_TYPE_JOIN,              "join" },
204   { SILC_NOTIFY_TYPE_LEAVE,             "leave" },
205   { SILC_NOTIFY_TYPE_SIGNOFF,           "signoff" },
206   { SILC_NOTIFY_TYPE_TOPIC_SET,         "topic" },
207   { SILC_NOTIFY_TYPE_NICK_CHANGE,       "nick" },
208   { SILC_NOTIFY_TYPE_CMODE_CHANGE,      "cmode" },
209   { SILC_NOTIFY_TYPE_CUMODE_CHANGE,     "cumode" },
210   { SILC_NOTIFY_TYPE_MOTD,              "motd" },
211   { SILC_NOTIFY_TYPE_CHANNEL_CHANGE,    "channel_change" },
212   { SILC_NOTIFY_TYPE_SERVER_SIGNOFF,    "server_signoff" },
213   { SILC_NOTIFY_TYPE_KICKED,            "kick" },
214   { SILC_NOTIFY_TYPE_KILLED,            "kill" },
215   { SILC_NOTIFY_TYPE_UMODE_CHANGE,      "umode" },
216   { SILC_NOTIFY_TYPE_BAN,               "ban" },
217 };
218
219 static void silc_notify(SilcClient client, SilcClientConnection conn,
220                         SilcNotifyType type, ...)
221 {
222   SILC_SERVER_REC *server;
223   va_list va;
224   
225   server = conn == NULL ? NULL : conn->context;
226   va_start(va, type);
227   
228   if (type == SILC_NOTIFY_TYPE_NONE) {
229     /* Some generic notice from server */
230     printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
231   } else if (type < MAX_NOTIFY) {
232     /* Send signal about the notify event */
233     char signal[50];
234     g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
235     signal_emit(signal, 2, server, va);
236   } else {
237     /* Unknown notify */
238     printtext(server, NULL, MSGLEVEL_CRAP, "Unknown notify type %d", type);
239   }
240
241   va_end(va);
242 }
243
244 /* Called to indicate that connection was either successfully established
245    or connecting failed.  This is also the first time application receives
246    the SilcClientConnection objecet which it should save somewhere. */
247
248 static void 
249 silc_connect(SilcClient client, SilcClientConnection conn, int success)
250 {
251   SILC_SERVER_REC *server = conn->context;
252
253   if (success) {
254     server->connected = TRUE;
255     signal_emit("event connected", 1, server);
256   } else {
257     server->connection_lost = TRUE;
258     server->conn->context = NULL;
259     server_disconnect(SERVER(server));
260   }
261 }
262
263 /* Called to indicate that connection was disconnected to the server. */
264
265 static void 
266 silc_disconnect(SilcClient client, SilcClientConnection conn)
267 {
268   SILC_SERVER_REC *server = conn->context;
269
270   server->conn->context = NULL;
271   server->conn = NULL;
272   server->connection_lost = TRUE;
273   server_disconnect(SERVER(server));
274 }
275
276 /* Command handler. This function is called always in the command function.
277    If error occurs it will be called as well. `conn' is the associated
278    client connection. `cmd_context' is the command context that was
279    originally sent to the command. `success' is FALSE if error occured
280    during command. `command' is the command being processed. It must be
281    noted that this is not reply from server. This is merely called just
282    after application has called the command. Just to tell application
283    that the command really was processed. */
284
285 static void 
286 silc_command(SilcClient client, SilcClientConnection conn, 
287              SilcClientCommandContext cmd_context, int success,
288              SilcCommand command)
289 {
290 }
291
292 /* Client info resolving callback when JOIN command reply is received.
293    This will cache all users on the channel. */
294
295 void silc_client_join_get_users(SilcClient client,
296                                 SilcClientConnection conn,
297                                 SilcClientEntry *clients,
298                                 uint32 clients_count,
299                                 void *context)
300 {
301   SilcChannelEntry channel = (SilcChannelEntry)context;
302   SilcChannelUser chu;
303   SILC_SERVER_REC *server = conn->context;
304   SILC_CHANNEL_REC *chanrec;
305   NICK_REC *ownnick;
306
307   if (!clients)
308     return;
309
310   chanrec = silc_channel_find_entry(server, channel);
311   if (chanrec == NULL)
312     return;
313
314   silc_list_start(channel->clients);
315   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END)
316     silc_nicklist_insert(chanrec, chu, FALSE);
317
318   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
319   nicklist_set_own(CHANNEL(chanrec), ownnick);
320   signal_emit("channel joined", 1, chanrec);
321   fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
322 }
323
324 /* Command reply handler. This function is called always in the command reply
325    function. If error occurs it will be called as well. Normal scenario
326    is that it will be called after the received command data has been parsed
327    and processed. The function is used to pass the received command data to
328    the application. 
329
330    `conn' is the associated client connection. `cmd_payload' is the command
331    payload data received from server and it can be ignored. It is provided
332    if the application would like to re-parse the received command data,
333    however, it must be noted that the data is parsed already by the library
334    thus the payload can be ignored. `success' is FALSE if error occured.
335    In this case arguments are not sent to the application. `command' is the
336    command reply being processed. The function has variable argument list
337    and each command defines the number and type of arguments it passes to the
338    application (on error they are not sent). */
339
340 static void 
341 silc_command_reply(SilcClient client, SilcClientConnection conn,
342                    SilcCommandPayload cmd_payload, int success,
343                    SilcCommand command, SilcCommandStatus status, ...)
344
345 {
346   SILC_SERVER_REC *server = conn->context;
347   SILC_CHANNEL_REC *chanrec;
348   va_list vp;
349
350   va_start(vp, status);
351
352   switch(command) {
353   case SILC_COMMAND_WHOIS:
354     {
355       char buf[1024], *nickname, *username, *realname;
356       int len;
357       uint32 idle, mode;
358       SilcBuffer channels;
359       
360       /* XXX should use irssi routines */
361       
362       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
363           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
364         char *tmp;
365         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
366                                          3, NULL);
367         if (tmp)
368           client->ops->say(client, conn, "%s: %s", tmp,
369                            silc_client_command_status_message(status));
370         else
371           client->ops->say(client, conn, "%s",
372                            silc_client_command_status_message(status));
373         break;
374       }
375       
376       if (!success)
377         return;
378       
379       (void)va_arg(vp, SilcClientEntry);
380       nickname = va_arg(vp, char *);
381       username = va_arg(vp, char *);
382       realname = va_arg(vp, char *);
383       channels = va_arg(vp, SilcBuffer);
384       mode = va_arg(vp, uint32);
385       idle = va_arg(vp, uint32);
386       
387       memset(buf, 0, sizeof(buf));
388       
389       if (nickname) {
390         len = strlen(nickname);
391         strncat(buf, nickname, len);
392         strncat(buf, " is ", 4);
393       }
394         
395       if (username) {
396         strncat(buf, username, strlen(username));
397       }
398         
399       if (realname) {
400         strncat(buf, " (", 2);
401         strncat(buf, realname, strlen(realname));
402         strncat(buf, ")", 1);
403       }
404       
405       client->ops->say(client, conn, "%s", buf);
406       
407       if (channels) {
408         SilcDList list = silc_channel_payload_parse_list(channels);
409         if (list) {
410           SilcChannelPayload entry;
411           
412           memset(buf, 0, sizeof(buf));
413           strcat(buf, "on channels: ");
414           
415           silc_dlist_start(list);
416           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
417             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
418             uint32 name_len;
419             char *name = silc_channel_get_name(entry, &name_len);
420             
421             if (m)
422               strncat(buf, m, strlen(m));
423             strncat(buf, name, name_len);
424             strncat(buf, " ", 1);
425             silc_free(m);
426           }
427
428           client->ops->say(client, conn, "%s", buf);
429           silc_channel_payload_list_free(list);
430         }
431       }
432       
433       if (mode) {
434         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
435             (mode & SILC_UMODE_ROUTER_OPERATOR))
436           client->ops->say(client, conn, "%s is %s", nickname,
437                            (mode & SILC_UMODE_SERVER_OPERATOR) ?
438                            "Server Operator" :
439                            (mode & SILC_UMODE_ROUTER_OPERATOR) ?
440                            "SILC Operator" : "[Unknown mode]");
441         
442         if (mode & SILC_UMODE_GONE)
443           client->ops->say(client, conn, "%s is gone", nickname);
444       }
445       
446       if (idle && nickname)
447         client->ops->say(client, conn, "%s has been idle %d %s",
448                          nickname,
449                          idle > 60 ? (idle / 60) : idle,
450                          idle > 60 ? "minutes" : "seconds");
451     }
452     break;
453     
454   case SILC_COMMAND_WHOWAS:
455     {
456       char buf[1024], *nickname, *username, *realname;
457       int len;
458       
459       /* XXX should use irssi routines */
460       
461       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
462           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
463         char *tmp;
464         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
465                                          3, NULL);
466         if (tmp)
467           client->ops->say(client, conn, "%s: %s", tmp,
468                            silc_client_command_status_message(status));
469         else
470           client->ops->say(client, conn, "%s",
471                            silc_client_command_status_message(status));
472         break;
473       }
474       
475       if (!success)
476         return;
477       
478       (void)va_arg(vp, SilcClientEntry);
479       nickname = va_arg(vp, char *);
480       username = va_arg(vp, char *);
481       realname = va_arg(vp, char *);
482       
483       memset(buf, 0, sizeof(buf));
484       
485       if (nickname) {
486         len = strlen(nickname);
487         strncat(buf, nickname, len);
488         strncat(buf, " was ", 5);
489       }
490       
491       if (username) {
492         strncat(buf, username, strlen(nickname));
493       }
494         
495       if (realname) {
496         strncat(buf, " (", 2);
497         strncat(buf, realname, strlen(realname));
498         strncat(buf, ")", 1);
499       }
500       
501       client->ops->say(client, conn, "%s", buf);
502     }
503     break;
504     
505   case SILC_COMMAND_INVITE:
506     {
507       SilcChannelEntry channel;
508       char *invite_list;
509       
510       if (!success)
511         return;
512       
513       /* XXX should use irssi routines */
514       
515       channel = va_arg(vp, SilcChannelEntry);
516       invite_list = va_arg(vp, char *);
517       
518       if (invite_list)
519         silc_say(client, conn, "%s invite list: %s", channel->channel_name,
520                  invite_list);
521       else
522         silc_say(client, conn, "%s invite list not set", 
523                  channel->channel_name);
524     }
525     break;
526
527   case SILC_COMMAND_JOIN: 
528     {
529       char *channel, *mode, *topic;
530       uint32 modei;
531       SilcChannelEntry channel_entry;
532       SilcBuffer client_id_list;
533       uint32 list_count;
534
535       channel = va_arg(vp, char *);
536       channel_entry = va_arg(vp, SilcChannelEntry);
537       modei = va_arg(vp, uint32);
538       (void)va_arg(vp, uint32);
539       (void)va_arg(vp, unsigned char *);
540       (void)va_arg(vp, unsigned char *);
541       (void)va_arg(vp, unsigned char *);
542       topic = va_arg(vp, char *);
543       (void)va_arg(vp, unsigned char *);
544       list_count = va_arg(vp, uint32);
545       client_id_list = va_arg(vp, SilcBuffer);
546
547       if (!success)
548         return;
549
550       chanrec = silc_channel_find(server, channel);
551       if (chanrec != NULL && !success)
552         channel_destroy(CHANNEL(chanrec));
553       else if (chanrec == NULL && success)
554         chanrec = silc_channel_create(server, channel, TRUE);
555       
556       if (topic) {
557         g_free_not_null(chanrec->topic);
558         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
559         signal_emit("channel topic changed", 1, chanrec);
560         silc_say(client, conn, "Topic for %s: %s", channel, topic);
561       }
562
563       mode = silc_client_chmode(modei, channel_entry);
564       g_free_not_null(chanrec->mode);
565       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
566       signal_emit("channel mode changed", 1, chanrec);
567
568       /* Resolve the client information */
569       silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
570                                       silc_client_join_get_users, 
571                                       channel_entry);
572       break;
573     }
574
575   case SILC_COMMAND_NICK: 
576     {
577       SilcClientEntry client = va_arg(vp, SilcClientEntry);
578       char *old;
579       
580       if (!success)
581         return;
582
583       old = g_strdup(server->nick);
584       server_change_nick(SERVER(server), client->nickname);
585       nicklist_rename_unique(SERVER(server),
586                              server->conn->local_entry, server->nick,
587                              client, client->nickname);
588       
589       signal_emit("message own_nick", 4, server, server->nick, old, "");
590       g_free(old);
591       break;
592     }
593     
594   case SILC_COMMAND_LIST:
595     {
596       char *topic, *name;
597       int usercount;
598       unsigned char buf[256], tmp[16];
599       int i, len;
600       
601       if (!success)
602         return;
603       
604       /* XXX should use irssi routines */
605       
606       (void)va_arg(vp, SilcChannelEntry);
607       name = va_arg(vp, char *);
608       topic = va_arg(vp, char *);
609       usercount = va_arg(vp, int);
610       
611       if (status == SILC_STATUS_LIST_START ||
612           status == SILC_STATUS_OK)
613         silc_say(client, conn, 
614                  "  Channel                                  Users     Topic");
615       
616       memset(buf, 0, sizeof(buf));
617       strncat(buf, "  ", 2);
618       len = strlen(name);
619       strncat(buf, name, len > 40 ? 40 : len);
620       if (len < 40)
621         for (i = 0; i < 40 - len; i++)
622           strcat(buf, " ");
623       strcat(buf, " ");
624       
625       memset(tmp, 0, sizeof(tmp));
626       if (usercount) {
627         snprintf(tmp, sizeof(tmp), "%d", usercount);
628         strcat(buf, tmp);
629       }
630       len = strlen(tmp);
631       if (len < 10)
632         for (i = 0; i < 10 - len; i++)
633           strcat(buf, " ");
634       strcat(buf, " ");
635       
636       if (topic) {
637         len = strlen(topic);
638         strncat(buf, topic, len);
639       }
640       
641       silc_say(client, conn, "%s", buf);
642     }
643     break;
644     
645   case SILC_COMMAND_UMODE:
646     {
647       uint32 mode;
648       
649       if (!success)
650         return;
651       
652       mode = va_arg(vp, uint32);
653       
654       /* XXX todo */
655     }
656     break;
657     
658   case SILC_COMMAND_OPER:
659 #if 0
660     if (status == SILC_STATUS_OK) {
661       conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
662       if (app->screen->bottom_line->umode)
663         silc_free(app->screen->bottom_line->umode);
664       app->screen->bottom_line->umode = strdup("Server Operator");;
665       silc_screen_print_bottom_line(app->screen, 0);
666     }
667 #endif
668     break;
669     
670   case SILC_COMMAND_SILCOPER:
671 #if 0
672     if (status == SILC_STATUS_OK) {
673       conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
674       if (app->screen->bottom_line->umode)
675         silc_free(app->screen->bottom_line->umode);
676       app->screen->bottom_line->umode = strdup("SILC Operator");;
677       silc_screen_print_bottom_line(app->screen, 0);
678     }
679 #endif
680     break;
681     
682   case SILC_COMMAND_USERS: 
683     {
684       SilcChannelEntry channel;
685       SilcChannelUser chu;
686       int line_len;
687       char *line;
688       
689       if (!success)
690         return;
691       
692       channel = va_arg(vp, SilcChannelEntry);
693       
694       /* There are two ways to do this, either parse the list (that
695          the command_reply sends (just take it with va_arg()) or just
696          traverse the channel's client list.  I'll do the latter.  See
697          JOIN command reply for example for the list. */
698       
699       silc_say(client, conn, "Users on %s", channel->channel_name);
700         
701       line = silc_calloc(1024, sizeof(*line));
702       line_len = 1024;
703       silc_list_start(channel->clients);
704       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
705         SilcClientEntry e = chu->client;
706         int i, len1;
707         char *m, tmp[80];
708         
709         memset(line, 0, line_len);
710         
711         if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
712           silc_free(line);
713           line_len += strlen(e->nickname) + strlen(e->server) + 100;
714           line = silc_calloc(line_len, sizeof(*line));
715         }
716         
717         memset(tmp, 0, sizeof(tmp));
718         m = silc_client_chumode_char(chu->mode);
719         
720         strncat(line, " ", 1);
721         strncat(line, e->nickname, strlen(e->nickname));
722         strncat(line, e->server ? "@" : "", 1);
723         
724         len1 = 0;
725         if (e->server)
726           len1 = strlen(e->server);
727         strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
728         
729         len1 = strlen(line);
730         if (len1 >= 30) {
731           memset(&line[29], 0, len1 - 29);
732         } else {
733           for (i = 0; i < 30 - len1 - 1; i++)
734             strcat(line, " ");
735         }
736         
737         if (e->mode & SILC_UMODE_GONE)
738           strcat(line, "  G");
739         else
740           strcat(line, "  H");
741         strcat(tmp, m ? m : "");
742         strncat(line, tmp, strlen(tmp));
743         
744         if (strlen(tmp) < 5)
745           for (i = 0; i < 5 - strlen(tmp); i++)
746             strcat(line, " ");
747         
748         strcat(line, e->username ? e->username : "");
749         
750         silc_say(client, conn, "%s", line);
751         
752         if (m)
753           silc_free(m);
754       }
755       
756       silc_free(line);
757     }
758     break;
759
760   case SILC_COMMAND_BAN:
761     {
762       SilcChannelEntry channel;
763       char *ban_list;
764       
765       if (!success)
766         return;
767       
768       /* XXX should use irssi routines */
769           
770       channel = va_arg(vp, SilcChannelEntry);
771       ban_list = va_arg(vp, char *);
772       
773       if (ban_list)
774         silc_say(client, conn, "%s ban list: %s", channel->channel_name,
775                  ban_list);
776       else
777         silc_say(client, conn, "%s ban list not set", channel->channel_name);
778     }
779     break;
780     
781   case SILC_COMMAND_GETKEY:
782     {
783       SilcIdType id_type;
784       void *entry;
785       SilcPublicKey public_key;
786       unsigned char *pk;
787       uint32 pk_len;
788       
789       id_type = va_arg(vp, uint32);
790       entry = va_arg(vp, void *);
791       public_key = va_arg(vp, SilcPublicKey);
792       
793       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
794       
795       if (id_type == SILC_ID_CLIENT) {
796         silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
797                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC,
798                                         NULL, NULL);
799       }
800       
801       silc_free(pk);
802     }
803     
804   case SILC_COMMAND_TOPIC:
805     {
806       SilcChannelEntry channel;
807       char *topic;
808       
809       if (!success)
810         return;
811       
812       channel = va_arg(vp, SilcChannelEntry);
813       topic = va_arg(vp, char *);
814       
815       /* XXX should use irssi routines */
816       
817       if (topic)
818         silc_say(client, conn, 
819                  "Topic on channel %s: %s", channel->channel_name,
820                  topic);
821     }
822     break;
823   }
824
825   va_end(vp);
826 }
827
828 /* Internal routine to verify public key. If the `completion' is provided
829    it will be called to indicate whether public was verified or not. */
830
831 typedef struct {
832   SilcClient client;
833   SilcClientConnection conn;
834   char *filename;
835   char *entity;
836   unsigned char *pk;
837   uint32 pk_len;
838   SilcSKEPKType pk_type;
839   SilcVerifyPublicKey completion;
840   void *context;
841 } *PublicKeyVerify;
842
843 static void verify_public_key_completion(const char *line, void *context)
844 {
845   PublicKeyVerify verify = (PublicKeyVerify)context;
846
847   if (line[0] == 'Y' || line[0] == 'y') {
848     /* Call the completion */
849     if (verify->completion)
850       verify->completion(TRUE, verify->context);
851
852     /* Save the key for future checking */
853     silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
854                                    verify->pk_len, SILC_PKCS_FILE_PEM);
855   } else {
856     /* Call the completion */
857     if (verify->completion)
858       verify->completion(FALSE, verify->context);
859
860     silc_say(verify->client, 
861              verify->conn, "Will not accept the %s key", verify->entity);
862   }
863
864   silc_free(verify->filename);
865   silc_free(verify->entity);
866   silc_free(verify);
867 }
868
869 static void 
870 silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
871                                 SilcSocketType conn_type, unsigned char *pk, 
872                                 uint32 pk_len, SilcSKEPKType pk_type,
873                                 SilcVerifyPublicKey completion, void *context)
874 {
875   int i;
876   char file[256], filename[256], *fingerprint;
877   struct passwd *pw;
878   struct stat st;
879   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
880                    conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
881                   "server" : "client");
882   PublicKeyVerify verify;
883
884   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
885     silc_say(client, conn, "We don't support %s public key type %d", 
886              entity, pk_type);
887     if (completion)
888       completion(FALSE, context);
889     return;
890   }
891
892   pw = getpwuid(getuid());
893   if (!pw) {
894     if (completion)
895       completion(FALSE, context);
896     return;
897   }
898
899   memset(filename, 0, sizeof(filename));
900   memset(file, 0, sizeof(file));
901
902   if (conn_type == SILC_SOCKET_TYPE_SERVER ||
903       conn_type == SILC_SOCKET_TYPE_ROUTER) {
904     snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
905              conn->sock->hostname, conn->sock->port);
906     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
907              pw->pw_dir, entity, file);
908   } else {
909     /* Replace all whitespaces with `_'. */
910     fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
911     for (i = 0; i < strlen(fingerprint); i++)
912       if (fingerprint[i] == ' ')
913         fingerprint[i] = '_';
914     
915     snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
916     snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
917              pw->pw_dir, entity, file);
918     silc_free(fingerprint);
919   }
920
921   /* Take fingerprint of the public key */
922   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
923
924   verify = silc_calloc(1, sizeof(*verify));
925   verify->client = client;
926   verify->conn = conn;
927   verify->filename = strdup(filename);
928   verify->entity = strdup(entity);
929   verify->pk = pk;
930   verify->pk_len = pk_len;
931   verify->pk_type = pk_type;
932   verify->completion = completion;
933   verify->context = context;
934
935   /* Check whether this key already exists */
936   if (stat(filename, &st) < 0) {
937     /* Key does not exist, ask user to verify the key and save it */
938
939     silc_say(client, conn, "Received %s public key", entity);
940     silc_say(client, conn, "Fingerprint for the %s key is", entity);
941     silc_say(client, conn, "%s", fingerprint);
942
943     keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
944                             "Would you like to accept the key (y/n)? ", 0,
945                             verify);
946     silc_free(fingerprint);
947     return;
948   } else {
949     /* The key already exists, verify it. */
950     SilcPublicKey public_key;
951     unsigned char *encpk;
952     uint32 encpk_len;
953
954     /* Load the key file */
955     if (!silc_pkcs_load_public_key(filename, &public_key, 
956                                    SILC_PKCS_FILE_PEM))
957       if (!silc_pkcs_load_public_key(filename, &public_key, 
958                                      SILC_PKCS_FILE_BIN)) {
959         silc_say(client, conn, "Received %s public key", entity);
960         silc_say(client, conn, "Fingerprint for the %s key is", entity);
961         silc_say(client, conn, "%s", fingerprint);
962         silc_say(client, conn, "Could not load your local copy of the %s key",
963                  entity);
964         keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
965                                 "Would you like to accept the key "
966                                 "anyway (y/n)? ", 0,
967                                 verify);
968         silc_free(fingerprint);
969         return;
970       }
971   
972     /* Encode the key data */
973     encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
974     if (!encpk) {
975       silc_say(client, conn, "Received %s public key", entity);
976       silc_say(client, conn, "Fingerprint for the %s key is", entity);
977       silc_say(client, conn, "%s", fingerprint);
978       silc_say(client, conn, "Your local copy of the %s key is malformed",
979                entity);
980       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
981                               "Would you like to accept the key "
982                               "anyway (y/n)? ", 0,
983                               verify);
984       silc_free(fingerprint);
985       return;
986     }
987
988     /* Compare the keys */
989     if (memcmp(encpk, pk, encpk_len)) {
990       silc_say(client, conn, "Received %s public key", entity);
991       silc_say(client, conn, "Fingerprint for the %s key is", entity);
992       silc_say(client, conn, "%s", fingerprint);
993       silc_say(client, conn, "%s key does not match with your local copy",
994                entity);
995       silc_say(client, conn, 
996                "It is possible that the key has expired or changed");
997       silc_say(client, conn, "It is also possible that some one is performing "
998                        "man-in-the-middle attack");
999
1000       /* Ask user to verify the key and save it */
1001       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
1002                               "Would you like to accept the key "
1003                               "anyway (y/n)? ", 0,
1004                               verify);
1005       silc_free(fingerprint);
1006       return;
1007     }
1008
1009     /* Local copy matched */
1010     if (completion)
1011       completion(TRUE, context);
1012     silc_free(fingerprint);
1013   }
1014 }
1015
1016 /* Verifies received public key. The `conn_type' indicates which entity
1017    (server, client etc.) has sent the public key. If user decides to trust
1018    the key may be saved as trusted public key for later use. The 
1019    `completion' must be called after the public key has been verified. */
1020
1021 static void 
1022 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1023                        SilcSocketType conn_type, unsigned char *pk, 
1024                        uint32 pk_len, SilcSKEPKType pk_type,
1025                        SilcVerifyPublicKey completion, void *context)
1026 {
1027   silc_verify_public_key_internal(client, conn, conn_type, pk,
1028                                   pk_len, pk_type,
1029                                   completion, context);
1030 }
1031
1032 /* Asks passphrase from user on the input line. */
1033
1034 typedef struct {
1035   SilcAskPassphrase completion;
1036   void *context;
1037 } *AskPassphrase;
1038
1039 static void ask_passphrase_completion(const char *passphrase, void *context)
1040 {
1041   AskPassphrase p = (AskPassphrase)context;
1042   p->completion((unsigned char *)passphrase, 
1043                 passphrase ? strlen(passphrase) : 0, p->context);
1044   silc_free(p);
1045 }
1046
1047 static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1048                                 SilcAskPassphrase completion, void *context)
1049 {
1050   AskPassphrase p = silc_calloc(1, sizeof(*p));
1051   p->completion = completion;
1052   p->context = context;
1053
1054   keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
1055                           "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
1056 }
1057
1058 /* Find authentication method and authentication data by hostname and
1059    port. The hostname may be IP address as well. The found authentication
1060    method and authentication data is returned to `auth_meth', `auth_data'
1061    and `auth_data_len'. The function returns TRUE if authentication method
1062    is found and FALSE if not. `conn' may be NULL. */
1063
1064 static int 
1065 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1066                      char *hostname, uint16 port,
1067                      SilcProtocolAuthMeth *auth_meth,
1068                      unsigned char **auth_data,
1069                      uint32 *auth_data_len)
1070 {
1071
1072   /* XXX must resolve from configuration whether this connection has
1073      any specific authentication data */
1074
1075   *auth_meth = SILC_AUTH_NONE;
1076   *auth_data = NULL;
1077   *auth_data_len = 0;
1078
1079   return TRUE;
1080 }
1081
1082 /* Notifies application that failure packet was received.  This is called
1083    if there is some protocol active in the client.  The `protocol' is the
1084    protocol context.  The `failure' is opaque pointer to the failure
1085    indication.  Note, that the `failure' is protocol dependant and application
1086    must explicitly cast it to correct type.  Usually `failure' is 32 bit
1087    failure type (see protocol specs for all protocol failure types). */
1088
1089 static void 
1090 silc_failure(SilcClient client, SilcClientConnection conn, 
1091              SilcProtocol protocol, void *failure)
1092 {
1093   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1094     SilcSKEStatus status = (SilcSKEStatus)failure;
1095     
1096     if (status == SILC_SKE_STATUS_BAD_VERSION)
1097       silc_say_error("You are running incompatible client version (it may be "
1098                      "too old or too new)");
1099     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1100       silc_say_error("Server does not support your public key type");
1101     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1102       silc_say_error("Server does not support one of your proposed KE group");
1103     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1104       silc_say_error("Server does not support one of your proposed cipher");
1105     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1106       silc_say_error("Server does not support one of your proposed PKCS");
1107     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1108       silc_say_error("Server does not support one of your proposed "
1109                      "hash function");
1110     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1111       silc_say_error("Server does not support one of your proposed HMAC");
1112     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1113       silc_say_error("Incorrect signature");
1114   }
1115
1116   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1117     uint32 err = (uint32)failure;
1118
1119     if (err == SILC_AUTH_FAILED)
1120       silc_say(client, conn, "Authentication failed");
1121   }
1122 }
1123
1124 /* Asks whether the user would like to perform the key agreement protocol.
1125    This is called after we have received an key agreement packet or an
1126    reply to our key agreement packet. This returns TRUE if the user wants
1127    the library to perform the key agreement protocol and FALSE if it is not
1128    desired (application may start it later by calling the function
1129    silc_client_perform_key_agreement). */
1130
1131 static int 
1132 silc_key_agreement(SilcClient client, SilcClientConnection conn,
1133                    SilcClientEntry client_entry, char *hostname,
1134                    int port,
1135                    SilcKeyAgreementCallback *completion,
1136                    void **context)
1137 {
1138   char host[256];
1139
1140   /* We will just display the info on the screen and return FALSE and user
1141      will have to start the key agreement with a command. */
1142
1143   if (hostname) {
1144     memset(host, 0, sizeof(host));
1145     snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port); 
1146   }
1147
1148   silc_say(client, conn, "%s wants to perform key agreement %s",
1149            client_entry->nickname, hostname ? host : "");
1150
1151   *completion = NULL;
1152   *context = NULL;
1153
1154   return FALSE;
1155 }
1156
1157 /* SILC client operations */
1158 SilcClientOperations ops = {
1159   silc_say,
1160   silc_channel_message,
1161   silc_private_message,
1162   silc_notify,
1163   silc_command,
1164   silc_command_reply,
1165   silc_connect,
1166   silc_disconnect,
1167   silc_get_auth_method,
1168   silc_verify_public_key,
1169   silc_ask_passphrase,
1170   silc_failure,
1171   silc_key_agreement,
1172 };
1173
1174 static int my_silc_scheduler(void)
1175 {
1176   silc_schedule_one(0);
1177   return 1;
1178 }
1179
1180 static CHATNET_REC *create_chatnet(void)
1181 {
1182   return g_malloc0(sizeof(CHATNET_REC));
1183 }
1184
1185 static SERVER_SETUP_REC *create_server_setup(void)
1186 {
1187   return g_malloc0(sizeof(SERVER_SETUP_REC));
1188 }
1189
1190 static CHANNEL_SETUP_REC *create_channel_setup(void)
1191 {
1192   return g_malloc0(sizeof(CHANNEL_SETUP_REC));
1193 }
1194
1195 static SERVER_CONNECT_REC *create_server_connect(void)
1196 {
1197   return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
1198 }
1199
1200 /* Checks user information and saves them to the config file it they
1201    do not exist there already. */
1202
1203 static void silc_init_userinfo(void)
1204 {
1205   const char *set, *nick, *user_name;
1206   char *str;   
1207         
1208   /* check if nick/username/realname wasn't read from setup.. */
1209   set = settings_get_str("real_name");
1210   if (set == NULL || *set == '\0') {
1211     str = g_getenv("SILCNAME");
1212     if (!str)
1213       str = g_getenv("IRCNAME");
1214     settings_set_str("real_name",
1215                      str != NULL ? str : g_get_real_name());
1216   }
1217  
1218   /* username */
1219   user_name = settings_get_str("user_name");
1220   if (user_name == NULL || *user_name == '\0') {
1221     str = g_getenv("SILCUSER");
1222     if (!str)
1223       str = g_getenv("IRCUSER");
1224     settings_set_str("user_name",
1225                      str != NULL ? str : g_get_user_name());
1226     
1227     user_name = settings_get_str("user_name");
1228   }
1229          
1230   /* nick */
1231   nick = settings_get_str("nick");
1232   if (nick == NULL || *nick == '\0') {
1233     str = g_getenv("SILCNICK");
1234     if (!str)
1235       str = g_getenv("IRCNICK");
1236     settings_set_str("nick", str != NULL ? str : user_name);
1237     
1238     nick = settings_get_str("nick");
1239   }
1240                 
1241   /* alternate nick */
1242   set = settings_get_str("alternate_nick");
1243   if (set == NULL || *set == '\0') {
1244     if (strlen(nick) < 9)
1245       str = g_strconcat(nick, "_", NULL);
1246     else { 
1247       str = g_strdup(nick);
1248       str[strlen(str)-1] = '_';
1249     }
1250     settings_set_str("alternate_nick", str);
1251     g_free(str);
1252   }
1253
1254   /* host name */
1255   set = settings_get_str("hostname");
1256   if (set == NULL || *set == '\0') {
1257     str = g_getenv("SILCHOST");
1258     if (!str)
1259       str = g_getenv("IRCHOST");
1260     if (str != NULL)
1261       settings_set_str("hostname", str);
1262   }
1263 }
1264
1265 /* Log callbacks */
1266
1267 static void silc_log_info(char *message)
1268 {
1269   fprintf(stderr, "%s\n", message);
1270 }
1271
1272 static void silc_log_warning(char *message)
1273 {
1274   fprintf(stderr, "%s\n", message);
1275 }
1276
1277 static void silc_log_error(char *message)
1278 {
1279   fprintf(stderr, "%s\n", message);
1280 }
1281
1282 /* Init SILC. Called from src/fe-text/silc.c */
1283
1284 void silc_core_init(void)
1285 {
1286   static struct poptOption options[] = {
1287     { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0, 
1288       "Create new public key pair", NULL },
1289     { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0, 
1290       "Set the PKCS of the public key pair", "PKCS" },
1291     { "bits", 0, POPT_ARG_INT, &opt_bits, 0, 
1292       "Set the length of the public key pair", "VALUE" },
1293     { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0, 
1294       "Show the contents of the public key", "FILE" },
1295     { "debug", 'd', POPT_ARG_NONE, &opt_debug, 0,
1296       "Enable debugging", NULL },
1297     { NULL, '\0', 0, NULL }
1298   };
1299
1300   args_register(options);
1301 }
1302
1303 /* Finalize init. Called from src/fe-text/silc.c */
1304
1305 void silc_core_init_finish(void)
1306 {
1307   CHAT_PROTOCOL_REC *rec;
1308
1309   if (opt_create_keypair == TRUE) {
1310     /* Create new key pair and exit */
1311     silc_cipher_register_default();
1312     silc_pkcs_register_default();
1313     silc_hash_register_default();
1314     silc_hmac_register_default();
1315     silc_client_create_key_pair(opt_pkcs, opt_bits, 
1316                                 NULL, NULL, NULL, NULL, NULL);
1317     exit(0);
1318   }
1319
1320   if (opt_keyfile) {
1321     /* Dump the key */
1322     silc_cipher_register_default();
1323     silc_pkcs_register_default();
1324     silc_hash_register_default();
1325     silc_hmac_register_default();
1326     silc_client_show_key(opt_keyfile);
1327     exit(0);
1328   }
1329
1330   silc_debug = opt_debug;
1331   silc_log_set_callbacks(silc_log_info, silc_log_warning,
1332                          silc_log_error, NULL);
1333
1334   silc_init_userinfo();
1335
1336   /* Allocate SILC client */
1337   silc_client = silc_client_alloc(&ops, NULL);
1338
1339   /* Load local config file */
1340   silc_config = silc_client_config_alloc(SILC_CLIENT_HOME_CONFIG_FILE);
1341
1342   /* Get user information */
1343   silc_client->username = g_strdup(settings_get_str("user_name"));
1344   silc_client->hostname = silc_net_localhost();
1345   silc_client->realname = g_strdup(settings_get_str("real_name"));
1346
1347   /* Register all configured ciphers, PKCS and hash functions. */
1348   if (silc_config) {
1349     silc_config->client = silc_client;
1350     if (!silc_client_config_register_ciphers(silc_config))
1351       silc_cipher_register_default();
1352     if (!silc_client_config_register_pkcs(silc_config))
1353       silc_pkcs_register_default();
1354     if (!silc_client_config_register_hashfuncs(silc_config))
1355       silc_hash_register_default();
1356     if (!silc_client_config_register_hmacs(silc_config))
1357       silc_hmac_register_default();
1358   } else {
1359     /* Register default ciphers, pkcs, hash funtions and hmacs. */
1360     silc_cipher_register_default();
1361     silc_pkcs_register_default();
1362     silc_hash_register_default();
1363     silc_hmac_register_default();
1364   }
1365
1366   /* Check ~/.silc directory and public and private keys */
1367   if (silc_client_check_silc_dir() == FALSE) {
1368     idletag = -1;
1369     return;
1370   }
1371
1372   /* Load public and private key */
1373   if (silc_client_load_keys(silc_client) == FALSE) {
1374     idletag = -1;
1375     return;
1376   }
1377
1378   /* Initialize the SILC client */
1379   if (!silc_client_init(silc_client)) {
1380     idletag = -1;
1381     return;
1382   }
1383
1384   /* Register SILC to the irssi */
1385   rec = g_new0(CHAT_PROTOCOL_REC, 1);
1386   rec->name = "SILC";
1387   rec->fullname = "Secure Internet Live Conferencing";
1388   rec->chatnet = "silcnet";
1389   rec->create_chatnet = create_chatnet;
1390   rec->create_server_setup = create_server_setup;
1391   rec->create_channel_setup = create_channel_setup;
1392   rec->create_server_connect = create_server_connect;
1393   rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
1394     silc_server_connect; 
1395   rec->channel_create = (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
1396     silc_channel_create;
1397   rec->query_create = (QUERY_REC *(*) (const char *, const char *, int))
1398     silc_query_create;
1399   
1400   chat_protocol_register(rec);
1401   g_free(rec);
1402
1403   silc_server_init();
1404   silc_channels_init();
1405   silc_queries_init();
1406
1407   idletag = g_timeout_add(50, (GSourceFunc) my_silc_scheduler, NULL);
1408 }
1409
1410 /* Deinit SILC. Called from src/fe-text/silc.c */
1411
1412 void silc_core_deinit(void)
1413 {
1414   if (idletag != -1) {
1415     signal_emit("chat protocol deinit", 1,
1416                 chat_protocol_find("SILC"));
1417     
1418     silc_server_deinit();
1419     silc_channels_deinit();
1420     silc_queries_deinit();
1421     
1422     chat_protocol_unregister("SILC");
1423     
1424     g_source_remove(idletag);
1425   }
1426   
1427   g_free(silc_client->username);
1428   g_free(silc_client->realname);
1429   silc_client_free(silc_client);
1430 }