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