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