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