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 /* Init SILC. Called from src/fe-text/silc.c */
484
485 void silc_core_init(void)
486 {
487   static struct poptOption options[] = {
488     { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0, 
489       "Create new public key pair", NULL },
490     { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0, 
491       "Set the PKCS of the public key pair", "PKCS" },
492     { "bits", 0, POPT_ARG_INT, &opt_bits, 0, 
493       "Set the length of the public key pair", "VALUE" },
494     { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0, 
495       "Show the contents of the public key", "FILE" },
496     { NULL, '\0', 0, NULL }
497   };
498
499   args_register(options);
500 }
501
502 /* Finalize init. Called from src/fe-text/silc.c */
503
504 void silc_core_init_finish(void)
505 {
506   CHAT_PROTOCOL_REC *rec;
507
508   if (opt_create_keypair == TRUE) {
509     /* Create new key pair and exit */
510     silc_cipher_register_default();
511     silc_pkcs_register_default();
512     silc_hash_register_default();
513     silc_hmac_register_default();
514     silc_client_create_key_pair(opt_pkcs, opt_bits, 
515                                 NULL, NULL, NULL, NULL, NULL);
516     silc_free(opt_pkcs);
517     exit(0);
518   }
519
520   if (opt_keyfile) {
521     /* Dump the key */
522     silc_cipher_register_default();
523     silc_pkcs_register_default();
524     silc_hash_register_default();
525     silc_hmac_register_default();
526     silc_client_show_key(opt_keyfile);
527     silc_free(opt_keyfile);
528     exit(0);
529   }
530
531   /* Allocate SILC client */
532   silc_client = silc_client_alloc(&ops, NULL);
533
534   /* Load local config file */
535   silc_config = silc_client_config_alloc(SILC_CLIENT_HOME_CONFIG_FILE);
536
537   /* Get user information */
538   silc_client->username = g_strdup(settings_get_str("user_name"));
539   silc_client->hostname = silc_net_localhost();
540   silc_client->realname = g_strdup(settings_get_str("real_name"));
541
542   /* Register all configured ciphers, PKCS and hash functions. */
543   if (silc_config) {
544     silc_config->client = silc_client;
545     if (!silc_client_config_register_ciphers(silc_config))
546       silc_cipher_register_default();
547     if (!silc_client_config_register_pkcs(silc_config))
548       silc_pkcs_register_default();
549     if (!silc_client_config_register_hashfuncs(silc_config))
550       silc_hash_register_default();
551     if (!silc_client_config_register_hmacs(silc_config))
552       silc_hmac_register_default();
553   } else {
554     /* Register default ciphers, pkcs, hash funtions and hmacs. */
555     silc_cipher_register_default();
556     silc_pkcs_register_default();
557     silc_hash_register_default();
558     silc_hmac_register_default();
559   }
560
561   /* Check ~/.silc directory and public and private keys */
562   if (silc_client_check_silc_dir() == FALSE) {
563     idletag = -1;
564     return;
565   }
566
567   /* Load public and private key */
568   if (silc_client_load_keys(silc_client) == FALSE) {
569     idletag = -1;
570     return;
571   }
572
573   /* Initialize the SILC client */
574   if (!silc_client_init(silc_client)) {
575     idletag = -1;
576     return;
577   }
578
579   /* Register SILC to the irssi */
580   rec = g_new0(CHAT_PROTOCOL_REC, 1);
581   rec->name = "SILC";
582   rec->fullname = "Secure Internet Live Conferencing";
583   rec->chatnet = "silcnet";
584   rec->create_chatnet = create_chatnet;
585   rec->create_server_setup = create_server_setup;
586   rec->create_channel_setup = create_channel_setup;
587   rec->create_server_connect = create_server_connect;
588   rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
589     silc_server_connect; 
590   rec->channel_create = (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
591     silc_channel_create;
592   rec->query_create = (QUERY_REC *(*) (const char *, const char *, int))
593     silc_query_create;
594   
595   chat_protocol_register(rec);
596   g_free(rec);
597
598   silc_server_init();
599   silc_channels_init();
600   silc_queries_init();
601
602   idletag = g_timeout_add(100, (GSourceFunc) my_silc_scheduler, NULL);
603 }
604
605 /* Deinit SILC. Called from src/fe-text/silc.c */
606
607 void silc_core_deinit(void)
608 {
609   if (idletag != -1) {
610     signal_emit("chat protocol deinit", 1,
611                 chat_protocol_find("SILC"));
612     
613     silc_server_deinit();
614     silc_channels_deinit();
615     silc_queries_deinit();
616     
617     chat_protocol_unregister("SILC");
618     
619     g_source_remove(idletag);
620   }
621   
622   g_free(silc_client->username);
623   g_free(silc_client->realname);
624   silc_client_free(silc_client);
625 }