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