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