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 {
71   SILC_SERVER_REC *server;
72   va_list va;
73   char *str;
74
75   server = conn == NULL ? NULL : conn->context;
76   
77   va_start(va, msg);
78   str = g_strdup_vprintf(msg, va);
79   printtext(server, "#silc", MSGLEVEL_CRAP, "%s", str);
80   g_free(str);
81   va_end(va);
82 }
83
84 /* Message for a channel. The `sender' is the nickname of the sender 
85    received in the packet. The `channel_name' is the name of the channel. */
86
87 static void 
88 silc_channel_message(SilcClient client, SilcClientConnection conn,
89                      SilcClientEntry sender, SilcChannelEntry channel,
90                      SilcMessageFlags flags, char *msg)
91 {
92   SILC_SERVER_REC *server;
93   SILC_NICK_REC *nick;
94   SILC_CHANNEL_REC *chanrec;
95   
96   server = conn == NULL ? NULL : conn->context;
97   chanrec = silc_channel_find_entry(server, channel);
98   
99   nick = silc_nicklist_find(chanrec, sender);
100   signal_emit("message public", 6, server, msg,
101               nick == NULL ? "(unknown)" : nick->nick,
102               nick == NULL ? NULL : nick->host,
103               chanrec->name, nick);
104 }
105
106 /* Private message to the client. The `sender' is the nickname of the
107    sender received in the packet. */
108
109 static void 
110 silc_private_message(SilcClient client, SilcClientConnection conn,
111                      SilcClientEntry sender, SilcMessageFlags flags,
112                           char *msg)
113 {
114   SILC_SERVER_REC *server;
115   
116   server = conn == NULL ? NULL : conn->context;
117   signal_emit("message private", 4, server, msg,
118               sender->nickname ? sender->nickname : "(unknown)",
119               sender->username ? sender->username : NULL);
120 }
121
122 /* Notify message to the client. The notify arguments are sent in the
123    same order as servers sends them. The arguments are same as received
124    from the server except for ID's.  If ID is received application receives
125    the corresponding entry to the ID. For example, if Client ID is received
126    application receives SilcClientEntry.  Also, if the notify type is
127    for channel the channel entry is sent to application (even if server
128    does not send it). */
129
130 typedef struct {
131   int type;
132   const char *name;
133 } NOTIFY_REC;
134
135 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
136 static NOTIFY_REC notifies[] = {
137   { SILC_NOTIFY_TYPE_NONE,              NULL },
138   { SILC_NOTIFY_TYPE_INVITE,            "invite" },
139   { SILC_NOTIFY_TYPE_JOIN,              "join" },
140   { SILC_NOTIFY_TYPE_LEAVE,             "leave" },
141   { SILC_NOTIFY_TYPE_SIGNOFF,           "signoff" },
142   { SILC_NOTIFY_TYPE_TOPIC_SET,         "topic" },
143   { SILC_NOTIFY_TYPE_NICK_CHANGE,               "nick" },
144   { SILC_NOTIFY_TYPE_CMODE_CHANGE,      "cmode" },
145   { SILC_NOTIFY_TYPE_CUMODE_CHANGE,     "cumode" },
146   { SILC_NOTIFY_TYPE_MOTD,              "motd" }
147 };
148
149 static void silc_notify(SilcClient client, SilcClientConnection conn,
150                         SilcNotifyType type, ...)
151 {
152   SILC_SERVER_REC *server;
153   va_list va;
154   
155   server = conn == NULL ? NULL : conn->context;
156   va_start(va, type);
157   
158   if (type == SILC_NOTIFY_TYPE_NONE) {
159     /* some generic notice from server */
160     printtext(server, NULL, MSGLEVEL_CRAP, "%s",
161               (char *) va_arg(va, char *));
162   } else if (type < MAX_NOTIFY) {
163     /* send signal about the notify event */
164     char signal[50];
165     
166     g_snprintf(signal, sizeof(signal), "silc event %s",
167                notifies[type].name);
168     signal_emit(signal, 2, server, va);
169   } else {
170     /* unknown notify */
171     printtext(server, NULL, MSGLEVEL_CRAP,
172               "Unknown notify %d", type);
173   }
174   va_end(va);
175 }
176
177 /* Called to indicate that connection was either successfully established
178    or connecting failed.  This is also the first time application receives
179    the SilcClientConnection objecet which it should save somewhere. */
180
181 static void 
182 silc_connect(SilcClient client, SilcClientConnection conn, int success)
183 {
184   SILC_SERVER_REC *server = conn->context;
185   
186   if (success) {
187     server->connected = TRUE;
188     signal_emit("event connected", 1, server);
189   } else {
190     server->connection_lost = TRUE;
191     server->conn->context = NULL;
192     server_disconnect(SERVER(server));
193   }
194 }
195
196 /* Called to indicate that connection was disconnected to the server. */
197
198 static void 
199 silc_disconnect(SilcClient client, SilcClientConnection conn)
200 {
201         SILC_SERVER_REC *server = conn->context;
202
203         server->conn->context = NULL;
204         server->conn = NULL;
205         server->connection_lost = TRUE;
206         server_disconnect(SERVER(server));
207 }
208
209 /* Command handler. This function is called always in the command function.
210    If error occurs it will be called as well. `conn' is the associated
211    client connection. `cmd_context' is the command context that was
212    originally sent to the command. `success' is FALSE if error occured
213    during command. `command' is the command being processed. It must be
214    noted that this is not reply from server. This is merely called just
215    after application has called the command. Just to tell application
216    that the command really was processed. */
217
218 static void 
219 silc_command(SilcClient client, SilcClientConnection conn, 
220              SilcClientCommandContext cmd_context, int success,
221              SilcCommand command)
222 {
223 }
224
225 /* Command reply handler. This function is called always in the command reply
226    function. If error occurs it will be called as well. Normal scenario
227    is that it will be called after the received command data has been parsed
228    and processed. The function is used to pass the received command data to
229    the application. 
230
231    `conn' is the associated client connection. `cmd_payload' is the command
232    payload data received from server and it can be ignored. It is provided
233    if the application would like to re-parse the received command data,
234    however, it must be noted that the data is parsed already by the library
235    thus the payload can be ignored. `success' is FALSE if error occured.
236    In this case arguments are not sent to the application. `command' is the
237    command reply being processed. The function has variable argument list
238    and each command defines the number and type of arguments it passes to the
239    application (on error they are not sent). */
240
241 static void 
242 silc_command_reply(SilcClient client, SilcClientConnection conn,
243                    SilcCommandPayload cmd_payload, int success,
244                    SilcCommand command, SilcCommandStatus status, ...)
245
246 {
247   SILC_SERVER_REC *server = conn->context;
248   SILC_CHANNEL_REC *chanrec;
249   va_list va;
250
251   va_start(va, status);
252
253   /*g_snprintf(signal, sizeof(signal), "silc command reply %s",
254     silc_commands[type]);
255     signal_emit(signal, 2, server, va);*/
256
257   switch(command) {
258   case SILC_COMMAND_JOIN: 
259     {
260       char *channel, *mode;
261       uint32 modei;
262       SilcChannelEntry channel_entry;
263       
264       channel = va_arg(va, char *);
265       channel_entry = va_arg(va, SilcChannelEntry);
266       modei = va_arg(va, uint32);
267       mode = silc_client_chmode(modei, channel_entry);
268       
269       chanrec = silc_channel_find(server, channel);
270       if (chanrec != NULL && !success)
271         channel_destroy(CHANNEL(chanrec));
272       else if (chanrec == NULL && success)
273         chanrec = silc_channel_create(server, channel, TRUE);
274       
275       g_free_not_null(chanrec->mode);
276       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
277       signal_emit("channel mode changed", 1, chanrec);
278       break;
279     }
280   case SILC_COMMAND_NICK: 
281     {
282       SilcClientEntry client = va_arg(va, SilcClientEntry);
283       char *old;
284       
285       old = g_strdup(server->nick);
286       server_change_nick(SERVER(server), client->nickname);
287       nicklist_rename_unique(SERVER(server),
288                              server->conn->local_entry, server->nick,
289                              client, client->nickname);
290       
291       signal_emit("message own_nick", 4,
292                   server, server->nick, old, "");
293       g_free(old);
294       break;
295     }
296   case SILC_COMMAND_USERS: 
297     {
298       SilcChannelEntry channel;
299       SilcChannelUser user;
300       NICK_REC *ownnick;
301       
302       channel = va_arg(va, SilcChannelEntry);
303       chanrec = silc_channel_find_entry(server, channel);
304       if (chanrec == NULL)
305         break;
306       
307       silc_list_start(channel->clients);
308       while ((user = silc_list_get(channel->clients)) != NULL)
309         silc_nicklist_insert(chanrec, user, 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),
315                            CHANNEL_NICKLIST_FLAG_ALL);
316       break;
317     }
318   }
319   
320   va_end(va);
321 }
322
323 /* Verifies received public key. If user decides to trust the key it is
324    saved as public server key for later use. If user does not trust the
325    key this returns FALSE. */
326
327 static int silc_verify_public_key(SilcClient client,
328                                   SilcClientConnection conn, 
329                                   SilcSocketType conn_type,
330                                   unsigned char *pk, uint32 pk_len,
331                                   SilcSKEPKType pk_type)
332 {
333   return TRUE;
334 }
335
336 /* Asks passphrase from user on the input line. */
337
338 static unsigned char *silc_ask_passphrase(SilcClient client,
339                                           SilcClientConnection conn)
340 {
341   return NULL;
342 }
343
344 /* Find authentication method and authentication data by hostname and
345    port. The hostname may be IP address as well. The found authentication
346    method and authentication data is returned to `auth_meth', `auth_data'
347    and `auth_data_len'. The function returns TRUE if authentication method
348    is found and FALSE if not. `conn' may be NULL. */
349
350 static int 
351 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
352                      char *hostname, uint16 port,
353                      SilcProtocolAuthMeth *auth_meth,
354                      unsigned char **auth_data,
355                      uint32 *auth_data_len)
356 {
357   return FALSE;
358 }
359
360 /* Notifies application that failure packet was received.  This is called
361    if there is some protocol active in the client.  The `protocol' is the
362    protocol context.  The `failure' is opaque pointer to the failure
363    indication.  Note, that the `failure' is protocol dependant and application
364    must explicitly cast it to correct type.  Usually `failure' is 32 bit
365    failure type (see protocol specs for all protocol failure types). */
366
367 static void 
368 silc_failure(SilcClient client, SilcClientConnection conn, 
369              SilcProtocol protocol, void *failure)
370 {
371   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
372     SilcSKEStatus status = (SilcSKEStatus)failure;
373     
374     if (status == SILC_SKE_STATUS_BAD_VERSION)
375       silc_say(client, conn, 
376                "You are running incompatible client version (it may be "
377                "too old or too new)");
378     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
379       silc_say(client, conn, "Server does not support your public key type");
380     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
381       silc_say(client, conn, 
382                "Server does not support one of your proposed KE group");
383     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
384       silc_say(client, conn, 
385                "Server does not support one of your proposed cipher");
386     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
387       silc_say(client, conn, 
388                "Server does not support one of your proposed PKCS");
389     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
390       silc_say(client, conn, 
391                "Server does not support one of your proposed hash function");
392     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
393       silc_say(client, conn, 
394                "Server does not support one of your proposed HMAC");
395     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
396       silc_say(client, conn, "Incorrect signature");
397   }
398
399   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
400     uint32 err = (uint32)failure;
401
402     if (err == SILC_AUTH_FAILED)
403       silc_say(client, conn, "Authentication failed");
404   }
405 }
406
407 /* Asks whether the user would like to perform the key agreement protocol.
408    This is called after we have received an key agreement packet or an
409    reply to our key agreement packet. This returns TRUE if the user wants
410    the library to perform the key agreement protocol and FALSE if it is not
411    desired (application may start it later by calling the function
412    silc_client_perform_key_agreement). */
413
414 static int 
415 silc_key_agreement(SilcClient client, SilcClientConnection conn,
416                    SilcClientEntry client_entry, char *hostname,
417                    int port,
418                    SilcKeyAgreementCallback *completion,
419                    void **context)
420 {
421   char host[256];
422
423   /* We will just display the info on the screen and return FALSE and user
424      will have to start the key agreement with a command. */
425
426   if (hostname) {
427     memset(host, 0, sizeof(host));
428     snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port); 
429   }
430
431   silc_say(client, conn, "%s wants to perform key agreement %s",
432            client_entry->nickname, hostname ? host : "");
433
434   *completion = NULL;
435   *context = NULL;
436
437   return FALSE;
438 }
439
440 /* SILC client operations */
441 SilcClientOperations ops = {
442   silc_say,
443   silc_channel_message,
444   silc_private_message,
445   silc_notify,
446   silc_command,
447   silc_command_reply,
448   silc_connect,
449   silc_disconnect,
450   silc_get_auth_method,
451   silc_verify_public_key,
452   silc_ask_passphrase,
453   silc_failure,
454   silc_key_agreement,
455 };
456
457 static int my_silc_scheduler(void)
458 {
459   silc_schedule_one(0);
460   return 1;
461 }
462
463 static CHATNET_REC *create_chatnet(void)
464 {
465   return g_malloc0(sizeof(CHATNET_REC));
466 }
467
468 static SERVER_SETUP_REC *create_server_setup(void)
469 {
470   return g_malloc0(sizeof(SERVER_SETUP_REC));
471 }
472
473 static CHANNEL_SETUP_REC *create_channel_setup(void)
474 {
475   return g_malloc0(sizeof(CHANNEL_SETUP_REC));
476 }
477
478 static SERVER_CONNECT_REC *create_server_connect(void)
479 {
480   return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
481 }
482
483 /* Checks user information and saves them to the config file it they
484    do not exist there already. */
485
486 static void silc_init_userinfo(void)
487 {
488   const char *set, *nick, *user_name;
489   char *str;   
490         
491   /* check if nick/username/realname wasn't read from setup.. */
492   set = settings_get_str("real_name");
493   if (set == NULL || *set == '\0') {
494     str = g_getenv("SILCNAME");
495     if (!str)
496       str = g_getenv("IRCNAME");
497     settings_set_str("real_name",
498                      str != NULL ? str : g_get_real_name());
499   }
500  
501   /* username */
502   user_name = settings_get_str("user_name");
503   if (user_name == NULL || *user_name == '\0') {
504     str = g_getenv("SILCUSER");
505     if (!str)
506       str = g_getenv("IRCUSER");
507     settings_set_str("user_name",
508                      str != NULL ? str : g_get_user_name());
509     
510     user_name = settings_get_str("user_name");
511   }
512          
513   /* nick */
514   nick = settings_get_str("nick");
515   if (nick == NULL || *nick == '\0') {
516     str = g_getenv("SILCNICK");
517     if (!str)
518       str = g_getenv("IRCNICK");
519     settings_set_str("nick", str != NULL ? str : user_name);
520     
521     nick = settings_get_str("nick");
522   }
523                 
524   /* alternate nick */
525   set = settings_get_str("alternate_nick");
526   if (set == NULL || *set == '\0') {
527     if (strlen(nick) < 9)
528       str = g_strconcat(nick, "_", NULL);
529     else { 
530       str = g_strdup(nick);
531       str[strlen(str)-1] = '_';
532     }
533     settings_set_str("alternate_nick", str);
534     g_free(str);
535   }
536
537   /* host name */
538   set = settings_get_str("hostname");
539   if (set == NULL || *set == '\0') {
540     str = g_getenv("SILCHOST");
541     if (!str)
542       str = g_getenv("IRCHOST");
543     if (str != NULL)
544       settings_set_str("hostname", str);
545   }
546 }
547
548 /* Init SILC. Called from src/fe-text/silc.c */
549
550 void silc_core_init(void)
551 {
552   static struct poptOption options[] = {
553     { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0, 
554       "Create new public key pair", NULL },
555     { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0, 
556       "Set the PKCS of the public key pair", "PKCS" },
557     { "bits", 0, POPT_ARG_INT, &opt_bits, 0, 
558       "Set the length of the public key pair", "VALUE" },
559     { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0, 
560       "Show the contents of the public key", "FILE" },
561     { NULL, '\0', 0, NULL }
562   };
563
564   args_register(options);
565 }
566
567 /* Finalize init. Called from src/fe-text/silc.c */
568
569 void silc_core_init_finish(void)
570 {
571   CHAT_PROTOCOL_REC *rec;
572
573   if (opt_create_keypair == TRUE) {
574     /* Create new key pair and exit */
575     silc_cipher_register_default();
576     silc_pkcs_register_default();
577     silc_hash_register_default();
578     silc_hmac_register_default();
579     silc_client_create_key_pair(opt_pkcs, opt_bits, 
580                                 NULL, NULL, NULL, NULL, NULL);
581     exit(0);
582   }
583
584   if (opt_keyfile) {
585     /* Dump the key */
586     silc_cipher_register_default();
587     silc_pkcs_register_default();
588     silc_hash_register_default();
589     silc_hmac_register_default();
590     silc_client_show_key(opt_keyfile);
591     exit(0);
592   }
593
594   /* Allocate SILC client */
595   silc_client = silc_client_alloc(&ops, NULL);
596
597   /* Load local config file */
598   silc_config = silc_client_config_alloc(SILC_CLIENT_HOME_CONFIG_FILE);
599
600   /* Get user information */
601   silc_client->username = g_strdup(settings_get_str("user_name"));
602   silc_client->hostname = silc_net_localhost();
603   silc_client->realname = g_strdup(settings_get_str("real_name"));
604
605   /* Register all configured ciphers, PKCS and hash functions. */
606   if (silc_config) {
607     silc_config->client = silc_client;
608     if (!silc_client_config_register_ciphers(silc_config))
609       silc_cipher_register_default();
610     if (!silc_client_config_register_pkcs(silc_config))
611       silc_pkcs_register_default();
612     if (!silc_client_config_register_hashfuncs(silc_config))
613       silc_hash_register_default();
614     if (!silc_client_config_register_hmacs(silc_config))
615       silc_hmac_register_default();
616   } else {
617     /* Register default ciphers, pkcs, hash funtions and hmacs. */
618     silc_cipher_register_default();
619     silc_pkcs_register_default();
620     silc_hash_register_default();
621     silc_hmac_register_default();
622   }
623
624   /* Check ~/.silc directory and public and private keys */
625   if (silc_client_check_silc_dir() == FALSE) {
626     idletag = -1;
627     return;
628   }
629
630   /* Load public and private key */
631   if (silc_client_load_keys(silc_client) == FALSE) {
632     idletag = -1;
633     return;
634   }
635
636   /* Initialize the SILC client */
637   if (!silc_client_init(silc_client)) {
638     idletag = -1;
639     return;
640   }
641
642   /* Register SILC to the irssi */
643   rec = g_new0(CHAT_PROTOCOL_REC, 1);
644   rec->name = "SILC";
645   rec->fullname = "Secure Internet Live Conferencing";
646   rec->chatnet = "silcnet";
647   rec->create_chatnet = create_chatnet;
648   rec->create_server_setup = create_server_setup;
649   rec->create_channel_setup = create_channel_setup;
650   rec->create_server_connect = create_server_connect;
651   rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
652     silc_server_connect; 
653   rec->channel_create = (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
654     silc_channel_create;
655   rec->query_create = (QUERY_REC *(*) (const char *, const char *, int))
656     silc_query_create;
657   
658   chat_protocol_register(rec);
659   g_free(rec);
660
661   silc_init_userinfo();
662   silc_server_init();
663   silc_channels_init();
664   silc_queries_init();
665
666   idletag = g_timeout_add(100, (GSourceFunc) my_silc_scheduler, NULL);
667 }
668
669 /* Deinit SILC. Called from src/fe-text/silc.c */
670
671 void silc_core_deinit(void)
672 {
673   if (idletag != -1) {
674     signal_emit("chat protocol deinit", 1,
675                 chat_protocol_find("SILC"));
676     
677     silc_server_deinit();
678     silc_channels_deinit();
679     silc_queries_deinit();
680     
681     chat_protocol_unregister("SILC");
682     
683     g_source_remove(idletag);
684   }
685   
686   g_free(silc_client->username);
687   g_free(silc_client->realname);
688   silc_client_free(silc_client);
689 }