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 user;
678       NICK_REC *ownnick;
679       
680       if (!success)
681         return;
682
683       channel = va_arg(vp, SilcChannelEntry);
684       chanrec = silc_channel_find_entry(server, channel);
685       if (chanrec == NULL)
686         break;
687       
688       silc_list_start(channel->clients);
689       while ((user = silc_list_get(channel->clients)) != NULL)
690         silc_nicklist_insert(chanrec, user, FALSE);
691       
692       ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
693       nicklist_set_own(CHANNEL(chanrec), ownnick);
694       signal_emit("channel joined", 1, chanrec);
695       fe_channels_nicklist(CHANNEL(chanrec),
696                            CHANNEL_NICKLIST_FLAG_ALL);
697       break;
698     }
699
700     case SILC_COMMAND_BAN:
701       {
702         SilcChannelEntry channel;
703         char *ban_list;
704
705         if (!success)
706           return;
707
708         /* XXX should use irssi routines */
709
710         
711         channel = va_arg(vp, SilcChannelEntry);
712         ban_list = va_arg(vp, char *);
713
714         if (ban_list)
715           silc_say(client, conn, "%s ban list: %s", channel->channel_name,
716                    ban_list);
717         else
718           silc_say(client, conn, "%s ban list not set", channel->channel_name);
719       }
720       break;
721
722     case SILC_COMMAND_GETKEY:
723       {
724         SilcIdType id_type;
725         void *entry;
726         SilcPublicKey public_key;
727         unsigned char *pk;
728         uint32 pk_len;
729
730         id_type = va_arg(vp, uint32);
731         entry = va_arg(vp, void *);
732         public_key = va_arg(vp, SilcPublicKey);
733
734         pk = silc_pkcs_public_key_encode(public_key, &pk_len);
735
736         if (id_type == SILC_ID_CLIENT) {
737           silc_verify_public_key(client, conn, SILC_SOCKET_TYPE_CLIENT,
738                                  pk, pk_len, SILC_SKE_PK_TYPE_SILC);
739         }
740
741         silc_free(pk);
742       }
743
744     case SILC_COMMAND_TOPIC:
745       {
746         SilcChannelEntry channel;
747         char *topic;
748
749         if (!success)
750           return;
751         
752         channel = va_arg(vp, SilcChannelEntry);
753         topic = va_arg(vp, char *);
754
755         /* XXX should use irssi routines */
756
757         if (topic)
758           silc_say(client, conn, 
759                    "Topic on channel %s: %s", channel->channel_name,
760                    topic);
761       }
762       break;
763   }
764   
765   va_end(vp);
766 }
767
768 /* Verifies received public key. If user decides to trust the key it is
769    saved as public server key for later use. If user does not trust the
770    key this returns FALSE. */
771
772 static int silc_verify_public_key(SilcClient client,
773                                   SilcClientConnection conn, 
774                                   SilcSocketType conn_type,
775                                   unsigned char *pk, uint32 pk_len,
776                                   SilcSKEPKType pk_type)
777 {
778   return TRUE;
779 }
780
781 /* Asks passphrase from user on the input line. */
782
783 static unsigned char *silc_ask_passphrase(SilcClient client,
784                                           SilcClientConnection conn)
785 {
786   return NULL;
787 }
788
789 /* Find authentication method and authentication data by hostname and
790    port. The hostname may be IP address as well. The found authentication
791    method and authentication data is returned to `auth_meth', `auth_data'
792    and `auth_data_len'. The function returns TRUE if authentication method
793    is found and FALSE if not. `conn' may be NULL. */
794
795 static int 
796 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
797                      char *hostname, uint16 port,
798                      SilcProtocolAuthMeth *auth_meth,
799                      unsigned char **auth_data,
800                      uint32 *auth_data_len)
801 {
802
803   /* XXX must resolve from configuration whether this connection has
804      any specific authentication data */
805
806   *auth_meth = SILC_AUTH_NONE;
807   *auth_data = NULL;
808   *auth_data_len = 0;
809
810   return TRUE;
811 }
812
813 /* Notifies application that failure packet was received.  This is called
814    if there is some protocol active in the client.  The `protocol' is the
815    protocol context.  The `failure' is opaque pointer to the failure
816    indication.  Note, that the `failure' is protocol dependant and application
817    must explicitly cast it to correct type.  Usually `failure' is 32 bit
818    failure type (see protocol specs for all protocol failure types). */
819
820 static void 
821 silc_failure(SilcClient client, SilcClientConnection conn, 
822              SilcProtocol protocol, void *failure)
823 {
824   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
825     SilcSKEStatus status = (SilcSKEStatus)failure;
826     
827     if (status == SILC_SKE_STATUS_BAD_VERSION)
828       silc_say_error("You are running incompatible client version (it may be "
829                      "too old or too new)");
830     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
831       silc_say_error("Server does not support your public key type");
832     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
833       silc_say_error("Server does not support one of your proposed KE group");
834     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
835       silc_say_error("Server does not support one of your proposed cipher");
836     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
837       silc_say_error("Server does not support one of your proposed PKCS");
838     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
839       silc_say_error("Server does not support one of your proposed "
840                     "hash function");
841     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
842       silc_say_error("Server does not support one of your proposed HMAC");
843     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
844       silc_say_error("Incorrect signature");
845   }
846
847   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
848     uint32 err = (uint32)failure;
849
850     if (err == SILC_AUTH_FAILED)
851       silc_say(client, conn, "Authentication failed");
852   }
853 }
854
855 /* Asks whether the user would like to perform the key agreement protocol.
856    This is called after we have received an key agreement packet or an
857    reply to our key agreement packet. This returns TRUE if the user wants
858    the library to perform the key agreement protocol and FALSE if it is not
859    desired (application may start it later by calling the function
860    silc_client_perform_key_agreement). */
861
862 static int 
863 silc_key_agreement(SilcClient client, SilcClientConnection conn,
864                    SilcClientEntry client_entry, char *hostname,
865                    int port,
866                    SilcKeyAgreementCallback *completion,
867                    void **context)
868 {
869   char host[256];
870
871   /* We will just display the info on the screen and return FALSE and user
872      will have to start the key agreement with a command. */
873
874   if (hostname) {
875     memset(host, 0, sizeof(host));
876     snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port); 
877   }
878
879   silc_say(client, conn, "%s wants to perform key agreement %s",
880            client_entry->nickname, hostname ? host : "");
881
882   *completion = NULL;
883   *context = NULL;
884
885   return FALSE;
886 }
887
888 /* SILC client operations */
889 SilcClientOperations ops = {
890   silc_say,
891   silc_channel_message,
892   silc_private_message,
893   silc_notify,
894   silc_command,
895   silc_command_reply,
896   silc_connect,
897   silc_disconnect,
898   silc_get_auth_method,
899   silc_verify_public_key,
900   silc_ask_passphrase,
901   silc_failure,
902   silc_key_agreement,
903 };
904
905 static int my_silc_scheduler(void)
906 {
907   silc_schedule_one(0);
908   return 1;
909 }
910
911 static CHATNET_REC *create_chatnet(void)
912 {
913   return g_malloc0(sizeof(CHATNET_REC));
914 }
915
916 static SERVER_SETUP_REC *create_server_setup(void)
917 {
918   return g_malloc0(sizeof(SERVER_SETUP_REC));
919 }
920
921 static CHANNEL_SETUP_REC *create_channel_setup(void)
922 {
923   return g_malloc0(sizeof(CHANNEL_SETUP_REC));
924 }
925
926 static SERVER_CONNECT_REC *create_server_connect(void)
927 {
928   return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
929 }
930
931 /* Checks user information and saves them to the config file it they
932    do not exist there already. */
933
934 static void silc_init_userinfo(void)
935 {
936   const char *set, *nick, *user_name;
937   char *str;   
938         
939   /* check if nick/username/realname wasn't read from setup.. */
940   set = settings_get_str("real_name");
941   if (set == NULL || *set == '\0') {
942     str = g_getenv("SILCNAME");
943     if (!str)
944       str = g_getenv("IRCNAME");
945     settings_set_str("real_name",
946                      str != NULL ? str : g_get_real_name());
947   }
948  
949   /* username */
950   user_name = settings_get_str("user_name");
951   if (user_name == NULL || *user_name == '\0') {
952     str = g_getenv("SILCUSER");
953     if (!str)
954       str = g_getenv("IRCUSER");
955     settings_set_str("user_name",
956                      str != NULL ? str : g_get_user_name());
957     
958     user_name = settings_get_str("user_name");
959   }
960          
961   /* nick */
962   nick = settings_get_str("nick");
963   if (nick == NULL || *nick == '\0') {
964     str = g_getenv("SILCNICK");
965     if (!str)
966       str = g_getenv("IRCNICK");
967     settings_set_str("nick", str != NULL ? str : user_name);
968     
969     nick = settings_get_str("nick");
970   }
971                 
972   /* alternate nick */
973   set = settings_get_str("alternate_nick");
974   if (set == NULL || *set == '\0') {
975     if (strlen(nick) < 9)
976       str = g_strconcat(nick, "_", NULL);
977     else { 
978       str = g_strdup(nick);
979       str[strlen(str)-1] = '_';
980     }
981     settings_set_str("alternate_nick", str);
982     g_free(str);
983   }
984
985   /* host name */
986   set = settings_get_str("hostname");
987   if (set == NULL || *set == '\0') {
988     str = g_getenv("SILCHOST");
989     if (!str)
990       str = g_getenv("IRCHOST");
991     if (str != NULL)
992       settings_set_str("hostname", str);
993   }
994 }
995
996 /* Init SILC. Called from src/fe-text/silc.c */
997
998 void silc_core_init(void)
999 {
1000   static struct poptOption options[] = {
1001     { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0, 
1002       "Create new public key pair", NULL },
1003     { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0, 
1004       "Set the PKCS of the public key pair", "PKCS" },
1005     { "bits", 0, POPT_ARG_INT, &opt_bits, 0, 
1006       "Set the length of the public key pair", "VALUE" },
1007     { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0, 
1008       "Show the contents of the public key", "FILE" },
1009     { NULL, '\0', 0, NULL }
1010   };
1011
1012   args_register(options);
1013 }
1014
1015 /* Finalize init. Called from src/fe-text/silc.c */
1016
1017 void silc_core_init_finish(void)
1018 {
1019   CHAT_PROTOCOL_REC *rec;
1020
1021   if (opt_create_keypair == TRUE) {
1022     /* Create new key pair and exit */
1023     silc_cipher_register_default();
1024     silc_pkcs_register_default();
1025     silc_hash_register_default();
1026     silc_hmac_register_default();
1027     silc_client_create_key_pair(opt_pkcs, opt_bits, 
1028                                 NULL, NULL, NULL, NULL, NULL);
1029     exit(0);
1030   }
1031
1032   if (opt_keyfile) {
1033     /* Dump the key */
1034     silc_cipher_register_default();
1035     silc_pkcs_register_default();
1036     silc_hash_register_default();
1037     silc_hmac_register_default();
1038     silc_client_show_key(opt_keyfile);
1039     exit(0);
1040   }
1041
1042   /* Allocate SILC client */
1043   silc_client = silc_client_alloc(&ops, NULL);
1044
1045   /* Load local config file */
1046   silc_config = silc_client_config_alloc(SILC_CLIENT_HOME_CONFIG_FILE);
1047
1048   /* Get user information */
1049   silc_client->username = g_strdup(settings_get_str("user_name"));
1050   silc_client->hostname = silc_net_localhost();
1051   silc_client->realname = g_strdup(settings_get_str("real_name"));
1052
1053   /* Register all configured ciphers, PKCS and hash functions. */
1054   if (silc_config) {
1055     silc_config->client = silc_client;
1056     if (!silc_client_config_register_ciphers(silc_config))
1057       silc_cipher_register_default();
1058     if (!silc_client_config_register_pkcs(silc_config))
1059       silc_pkcs_register_default();
1060     if (!silc_client_config_register_hashfuncs(silc_config))
1061       silc_hash_register_default();
1062     if (!silc_client_config_register_hmacs(silc_config))
1063       silc_hmac_register_default();
1064   } else {
1065     /* Register default ciphers, pkcs, hash funtions and hmacs. */
1066     silc_cipher_register_default();
1067     silc_pkcs_register_default();
1068     silc_hash_register_default();
1069     silc_hmac_register_default();
1070   }
1071
1072   /* Check ~/.silc directory and public and private keys */
1073   if (silc_client_check_silc_dir() == FALSE) {
1074     idletag = -1;
1075     return;
1076   }
1077
1078   /* Load public and private key */
1079   if (silc_client_load_keys(silc_client) == FALSE) {
1080     idletag = -1;
1081     return;
1082   }
1083
1084   /* Initialize the SILC client */
1085   if (!silc_client_init(silc_client)) {
1086     idletag = -1;
1087     return;
1088   }
1089
1090   /* Register SILC to the irssi */
1091   rec = g_new0(CHAT_PROTOCOL_REC, 1);
1092   rec->name = "SILC";
1093   rec->fullname = "Secure Internet Live Conferencing";
1094   rec->chatnet = "silcnet";
1095   rec->create_chatnet = create_chatnet;
1096   rec->create_server_setup = create_server_setup;
1097   rec->create_channel_setup = create_channel_setup;
1098   rec->create_server_connect = create_server_connect;
1099   rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
1100     silc_server_connect; 
1101   rec->channel_create = (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
1102     silc_channel_create;
1103   rec->query_create = (QUERY_REC *(*) (const char *, const char *, int))
1104     silc_query_create;
1105   
1106   chat_protocol_register(rec);
1107   g_free(rec);
1108
1109   silc_init_userinfo();
1110   silc_server_init();
1111   silc_channels_init();
1112   silc_queries_init();
1113
1114   idletag = g_timeout_add(100, (GSourceFunc) my_silc_scheduler, NULL);
1115 }
1116
1117 /* Deinit SILC. Called from src/fe-text/silc.c */
1118
1119 void silc_core_deinit(void)
1120 {
1121   if (idletag != -1) {
1122     signal_emit("chat protocol deinit", 1,
1123                 chat_protocol_find("SILC"));
1124     
1125     silc_server_deinit();
1126     silc_channels_deinit();
1127     silc_queries_deinit();
1128     
1129     chat_protocol_unregister("SILC");
1130     
1131     g_source_remove(idletag);
1132   }
1133   
1134   g_free(silc_client->username);
1135   g_free(silc_client->realname);
1136   silc_client_free(silc_client);
1137 }