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