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
24 #include "chatnets.h"
25 #include "servers-setup.h"
26 #include "channels-setup.h"
27 #include "silc-servers.h"
28 #include "silc-channels.h"
29 #include "silc-queries.h"
30 #include "silc-nicklist.h"
31 #include "version_internal.h"
32
33 #include "signals.h"
34 #include "levels.h"
35 #include "settings.h"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
38
39 #define SILC_CLIENT_PUBLIC_KEY_NAME "public_key.pub"
40 #define SILC_CLIENT_PRIVATE_KEY_NAME "private_key.prv"
41
42 #define SILC_CLIENT_DEF_PKCS "rsa"
43 #define SILC_CLIENT_DEF_PKCS_LEN 1024
44
45 SilcClient silc_client;
46 const char *silc_version_string = SILC_PROTOCOL_VERSION_STRING;
47
48 static int idletag;
49
50 extern SilcClientOperations ops;
51
52 static void silc_say(SilcClient client, SilcClientConnection conn,
53                      char *msg, ...)
54 {
55   SILC_SERVER_REC *server;
56   va_list va;
57   char *str;
58
59   server = conn == NULL ? NULL : conn->context;
60   
61   va_start(va, msg);
62   str = g_strdup_vprintf(msg, va);
63   printtext(server, "#silc", MSGLEVEL_CRAP, "%s", str);
64   g_free(str);
65   va_end(va);
66 }
67
68 /* Message for a channel. The `sender' is the nickname of the sender 
69    received in the packet. The `channel_name' is the name of the channel. */
70
71 static void 
72 silc_channel_message(SilcClient client, SilcClientConnection conn,
73                      SilcClientEntry sender, SilcChannelEntry channel,
74                      SilcMessageFlags flags, char *msg)
75 {
76   SILC_SERVER_REC *server;
77   SILC_NICK_REC *nick;
78   SILC_CHANNEL_REC *chanrec;
79   
80   server = conn == NULL ? NULL : conn->context;
81   chanrec = silc_channel_find_entry(server, channel);
82   
83   nick = silc_nicklist_find(chanrec, sender);
84   signal_emit("message public", 6, server, msg,
85               nick == NULL ? "(unknown)" : nick->nick,
86               nick == NULL ? NULL : nick->host,
87               chanrec->name, nick);
88 }
89
90 /* Private message to the client. The `sender' is the nickname of the
91    sender received in the packet. */
92
93 static void 
94 silc_private_message(SilcClient client, SilcClientConnection conn,
95                      SilcClientEntry sender, SilcMessageFlags flags,
96                           char *msg)
97 {
98   SILC_SERVER_REC *server;
99   
100   server = conn == NULL ? NULL : conn->context;
101   signal_emit("message private", 4, server, msg,
102               sender->nickname ? sender->nickname : "(unknown)",
103               sender->username ? sender->username : NULL);
104 }
105
106 /* Notify message to the client. The notify arguments are sent in the
107    same order as servers sends them. The arguments are same as received
108    from the server except for ID's.  If ID is received application receives
109    the corresponding entry to the ID. For example, if Client ID is received
110    application receives SilcClientEntry.  Also, if the notify type is
111    for channel the channel entry is sent to application (even if server
112    does not send it). */
113
114 typedef struct {
115   int type;
116   const char *name;
117 } NOTIFY_REC;
118
119 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
120 static NOTIFY_REC notifies[] = {
121   { SILC_NOTIFY_TYPE_NONE,              NULL },
122   { SILC_NOTIFY_TYPE_INVITE,            "invite" },
123   { SILC_NOTIFY_TYPE_JOIN,              "join" },
124   { SILC_NOTIFY_TYPE_LEAVE,             "leave" },
125   { SILC_NOTIFY_TYPE_SIGNOFF,           "signoff" },
126   { SILC_NOTIFY_TYPE_TOPIC_SET,         "topic" },
127   { SILC_NOTIFY_TYPE_NICK_CHANGE,               "nick" },
128   { SILC_NOTIFY_TYPE_CMODE_CHANGE,      "cmode" },
129   { SILC_NOTIFY_TYPE_CUMODE_CHANGE,     "cumode" },
130   { SILC_NOTIFY_TYPE_MOTD,              "motd" }
131 };
132
133 static void silc_notify(SilcClient client, SilcClientConnection conn,
134                         SilcNotifyType type, ...)
135 {
136   SILC_SERVER_REC *server;
137   va_list va;
138   
139   server = conn == NULL ? NULL : conn->context;
140   va_start(va, type);
141   
142   if (type == SILC_NOTIFY_TYPE_NONE) {
143     /* some generic notice from server */
144     printtext(server, NULL, MSGLEVEL_CRAP, "%s",
145               (char *) va_arg(va, char *));
146   } else if (type < MAX_NOTIFY) {
147     /* send signal about the notify event */
148     char signal[50];
149     
150     g_snprintf(signal, sizeof(signal), "silc event %s",
151                notifies[type].name);
152     signal_emit(signal, 2, server, va);
153   } else {
154     /* unknown notify */
155     printtext(server, NULL, MSGLEVEL_CRAP,
156               "Unknown notify %d", type);
157   }
158   va_end(va);
159 }
160
161 /* Called to indicate that connection was either successfully established
162    or connecting failed.  This is also the first time application receives
163    the SilcClientConnection objecet which it should save somewhere. */
164
165 static void 
166 silc_connect(SilcClient client, SilcClientConnection conn, int success)
167 {
168   SILC_SERVER_REC *server = conn->context;
169   
170   if (success) {
171     server->connected = TRUE;
172     signal_emit("event connected", 1, server);
173   } else {
174     server->connection_lost = TRUE;
175     server->conn->context = NULL;
176     server_disconnect(SERVER(server));
177   }
178 }
179
180 /* Called to indicate that connection was disconnected to the server. */
181
182 static void 
183 silc_disconnect(SilcClient client, SilcClientConnection conn)
184 {
185         SILC_SERVER_REC *server = conn->context;
186
187         server->conn->context = NULL;
188         server->conn = NULL;
189         server->connection_lost = TRUE;
190         server_disconnect(SERVER(server));
191 }
192
193 /* Command handler. This function is called always in the command function.
194    If error occurs it will be called as well. `conn' is the associated
195    client connection. `cmd_context' is the command context that was
196    originally sent to the command. `success' is FALSE if error occured
197    during command. `command' is the command being processed. It must be
198    noted that this is not reply from server. This is merely called just
199    after application has called the command. Just to tell application
200    that the command really was processed. */
201
202 static void 
203 silc_command(SilcClient client, SilcClientConnection conn, 
204              SilcClientCommandContext cmd_context, int success,
205              SilcCommand command)
206 {
207 }
208
209 /* Command reply handler. This function is called always in the command reply
210    function. If error occurs it will be called as well. Normal scenario
211    is that it will be called after the received command data has been parsed
212    and processed. The function is used to pass the received command data to
213    the application. 
214
215    `conn' is the associated client connection. `cmd_payload' is the command
216    payload data received from server and it can be ignored. It is provided
217    if the application would like to re-parse the received command data,
218    however, it must be noted that the data is parsed already by the library
219    thus the payload can be ignored. `success' is FALSE if error occured.
220    In this case arguments are not sent to the application. `command' is the
221    command reply being processed. The function has variable argument list
222    and each command defines the number and type of arguments it passes to the
223    application (on error they are not sent). */
224
225 static void 
226 silc_command_reply(SilcClient client, SilcClientConnection conn,
227                    SilcCommandPayload cmd_payload, int success,
228                    SilcCommand command, SilcCommandStatus status, ...)
229
230 {
231   SILC_SERVER_REC *server = conn->context;
232   SILC_CHANNEL_REC *chanrec;
233   va_list va;
234
235   va_start(va, status);
236
237   /*g_snprintf(signal, sizeof(signal), "silc command reply %s",
238     silc_commands[type]);
239     signal_emit(signal, 2, server, va);*/
240
241   switch(command) {
242   case SILC_COMMAND_JOIN: 
243     {
244       char *channel, *mode;
245       uint32 modei;
246       SilcChannelEntry channel_entry;
247       
248       channel = va_arg(va, char *);
249       channel_entry = va_arg(va, SilcChannelEntry);
250       modei = va_arg(va, uint32);
251       mode = silc_client_chmode(modei, channel_entry);
252       
253       chanrec = silc_channel_find(server, channel);
254       if (chanrec != NULL && !success)
255         channel_destroy(CHANNEL(chanrec));
256       else if (chanrec == NULL && success)
257         chanrec = silc_channel_create(server, channel, TRUE);
258       
259       g_free_not_null(chanrec->mode);
260       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
261       signal_emit("channel mode changed", 1, chanrec);
262       break;
263     }
264   case SILC_COMMAND_NICK: 
265     {
266       SilcClientEntry client = va_arg(va, SilcClientEntry);
267       char *old;
268       
269       old = g_strdup(server->nick);
270       server_change_nick(SERVER(server), client->nickname);
271       nicklist_rename_unique(SERVER(server),
272                              server->conn->local_entry, server->nick,
273                              client, client->nickname);
274       
275       signal_emit("message own_nick", 4,
276                   server, server->nick, old, "");
277       g_free(old);
278       break;
279     }
280   case SILC_COMMAND_USERS: 
281     {
282       SilcChannelEntry channel;
283       SilcChannelUser user;
284       NICK_REC *ownnick;
285       
286       channel = va_arg(va, SilcChannelEntry);
287       chanrec = silc_channel_find_entry(server, channel);
288       if (chanrec == NULL)
289         break;
290       
291       silc_list_start(channel->clients);
292       while ((user = silc_list_get(channel->clients)) != NULL)
293         silc_nicklist_insert(chanrec, user, FALSE);
294       
295       ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
296       nicklist_set_own(CHANNEL(chanrec), ownnick);
297       signal_emit("channel joined", 1, chanrec);
298       fe_channels_nicklist(CHANNEL(chanrec),
299                            CHANNEL_NICKLIST_FLAG_ALL);
300       break;
301     }
302   }
303   
304   va_end(va);
305 }
306
307 /* Verifies received public key. If user decides to trust the key it is
308    saved as public server key for later use. If user does not trust the
309    key this returns FALSE. */
310
311 static int silc_verify_public_key(SilcClient client,
312                                   SilcClientConnection conn, 
313                                   SilcSocketType conn_type,
314                                   unsigned char *pk, uint32 pk_len,
315                                   SilcSKEPKType pk_type)
316 {
317   return TRUE;
318 }
319
320 /* Asks passphrase from user on the input line. */
321
322 static unsigned char *silc_ask_passphrase(SilcClient client,
323                                           SilcClientConnection conn)
324 {
325         return NULL;
326 }
327
328 /* Find authentication method and authentication data by hostname and
329    port. The hostname may be IP address as well. The found authentication
330    method and authentication data is returned to `auth_meth', `auth_data'
331    and `auth_data_len'. The function returns TRUE if authentication method
332    is found and FALSE if not. `conn' may be NULL. */
333
334 static int 
335 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
336                      char *hostname, uint16 port,
337                      SilcProtocolAuthMeth *auth_meth,
338                      unsigned char **auth_data,
339                      uint32 *auth_data_len)
340 {
341   return FALSE;
342 }
343
344 /* Notifies application that failure packet was received.  This is called
345    if there is some protocol active in the client.  The `protocol' is the
346    protocol context.  The `failure' is opaque pointer to the failure
347    indication.  Note, that the `failure' is protocol dependant and application
348    must explicitly cast it to correct type.  Usually `failure' is 32 bit
349    failure type (see protocol specs for all protocol failure types). */
350
351 static void 
352 silc_failure(SilcClient client, SilcClientConnection conn, 
353              SilcProtocol protocol, void *failure)
354 {
355   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
356     SilcSKEStatus status = (SilcSKEStatus)failure;
357     
358     if (status == SILC_SKE_STATUS_BAD_VERSION)
359       silc_say(client, conn, 
360                "You are running incompatible client version (it may be "
361                "too old or too new)");
362     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
363       silc_say(client, conn, "Server does not support your public key type");
364     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
365       silc_say(client, conn, 
366                "Server does not support one of your proposed KE group");
367     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
368       silc_say(client, conn, 
369                "Server does not support one of your proposed cipher");
370     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
371       silc_say(client, conn, 
372                "Server does not support one of your proposed PKCS");
373     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
374       silc_say(client, conn, 
375                "Server does not support one of your proposed hash function");
376     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
377       silc_say(client, conn, 
378                "Server does not support one of your proposed HMAC");
379     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
380       silc_say(client, conn, "Incorrect signature");
381   }
382
383   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
384     uint32 err = (uint32)failure;
385
386     if (err == SILC_AUTH_FAILED)
387       silc_say(client, conn, "Authentication failed");
388   }
389 }
390
391 /* Asks whether the user would like to perform the key agreement protocol.
392    This is called after we have received an key agreement packet or an
393    reply to our key agreement packet. This returns TRUE if the user wants
394    the library to perform the key agreement protocol and FALSE if it is not
395    desired (application may start it later by calling the function
396    silc_client_perform_key_agreement). */
397
398 static int 
399 silc_key_agreement(SilcClient client, SilcClientConnection conn,
400                    SilcClientEntry client_entry, char *hostname,
401                    int port,
402                    SilcKeyAgreementCallback *completion,
403                    void **context)
404 {
405   char host[256];
406
407   /* We will just display the info on the screen and return FALSE and user
408      will have to start the key agreement with a command. */
409
410   if (hostname) {
411     memset(host, 0, sizeof(host));
412     snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port); 
413   }
414
415   silc_say(client, conn, "%s wants to perform key agreement %s",
416            client_entry->nickname, hostname ? host : "");
417
418   *completion = NULL;
419   *context = NULL;
420
421   return FALSE;
422 }
423
424 /* SILC client operations */
425 SilcClientOperations ops = {
426   silc_say,
427   silc_channel_message,
428   silc_private_message,
429   silc_notify,
430   silc_command,
431   silc_command_reply,
432   silc_connect,
433   silc_disconnect,
434   silc_get_auth_method,
435   silc_verify_public_key,
436   silc_ask_passphrase,
437   silc_failure,
438   silc_key_agreement,
439 };
440
441 /* Loads public and private key from files. */
442
443 static void silc_client_create_key_pair(char *pkcs_name, int bits,
444                                         char *identifier,
445                                         SilcPublicKey *pub_key,
446                                         SilcPrivateKey *prv_key)
447 {
448         SilcPKCS pkcs;
449         SilcRng rng;
450         unsigned char *key;
451         uint32 key_len;
452
453         rng = silc_rng_alloc();
454         silc_rng_init(rng);
455         silc_rng_global_init(rng);
456
457         silc_pkcs_alloc(pkcs_name, &pkcs);
458         pkcs->pkcs->init(pkcs->context, bits, rng);
459
460         /* Create public key */
461         key = silc_pkcs_get_public_key(pkcs, &key_len);
462         *pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
463                                               key, key_len);
464
465         memset(key, 0, sizeof(key_len));
466         silc_free(key);
467
468         /* Create private key */
469         key = silc_pkcs_get_private_key(pkcs, &key_len);
470         *prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
471
472         memset(key, 0, sizeof(key_len));
473         silc_free(key);
474
475         silc_rng_free(rng);
476         silc_pkcs_free(pkcs);
477 }
478
479 static int read_keyfiles(SilcClient client, char *public_file,
480                          char *private_file)
481 {
482         struct stat statbuf;
483
484         if (stat(public_file, &statbuf) != 0 ||
485             stat(private_file, &statbuf) != 0)
486                 return FALSE;
487
488         if (!silc_pkcs_load_private_key(private_file, &client->private_key,
489                                         SILC_PKCS_FILE_BIN) &&
490             !silc_pkcs_load_private_key(private_file, &client->private_key,
491                                         SILC_PKCS_FILE_PEM))
492                 return FALSE;
493
494         if (!silc_pkcs_load_public_key(public_file, &client->public_key,
495                                        SILC_PKCS_FILE_PEM) &&
496             !silc_pkcs_load_public_key(public_file, &client->public_key,
497                                        SILC_PKCS_FILE_BIN))
498                 return FALSE;
499
500         return TRUE;
501 }
502
503 static char *silc_create_identifier(SilcClient client)
504 {
505         char hostname[256], *email, *ret;
506
507         if (gethostname(hostname, sizeof(hostname)) != 0)
508                 hostname[0] = '\0';
509
510         email = g_strdup_printf("%s@%s", client->username, hostname);
511         ret = silc_pkcs_encode_identifier(client->username, hostname,
512                                           client->realname, email,
513                                           NULL, NULL);
514         g_free(email);
515         return ret;
516 }
517
518 static int load_keys(SilcClient client)
519 {
520         char *public_file, *private_file;
521         char *identifier;
522
523         public_file = g_strdup_printf("%s/.irssi/%s", g_get_home_dir(),
524                                       SILC_CLIENT_PUBLIC_KEY_NAME);
525         private_file = g_strdup_printf("%s/.irssi/%s", g_get_home_dir(),
526                                        SILC_CLIENT_PRIVATE_KEY_NAME);
527
528         if (!read_keyfiles(client, public_file, private_file)) {
529                 /* couldn't read key files, recreate them */
530                 identifier = silc_create_identifier(client);
531                 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
532                                             SILC_CLIENT_DEF_PKCS_LEN,
533                                             identifier,
534                                             &client->public_key,
535                                             &client->private_key);
536                 silc_free(identifier);
537
538                 silc_pkcs_save_public_key(public_file, client->public_key,
539                                           SILC_PKCS_FILE_PEM);
540                 silc_pkcs_save_private_key(private_file, client->private_key,
541                                            NULL, SILC_PKCS_FILE_BIN);
542         }
543
544         g_free(public_file);
545         g_free(private_file);
546         return TRUE;
547 }
548
549 static int my_silc_scheduler(void)
550 {
551         silc_schedule_one(0);
552         return 1;
553 }
554
555 static CHATNET_REC *create_chatnet(void)
556 {
557         return g_malloc0(sizeof(CHATNET_REC));
558 }
559
560 static SERVER_SETUP_REC *create_server_setup(void)
561 {
562         return g_malloc0(sizeof(SERVER_SETUP_REC));
563 }
564
565 static CHANNEL_SETUP_REC *create_channel_setup(void)
566 {
567         return g_malloc0(sizeof(CHANNEL_SETUP_REC));
568 }
569
570 static SERVER_CONNECT_REC *create_server_connect(void)
571 {
572         return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
573 }
574
575 /* Command line option variables */
576 void silc_core_init(void)
577 {
578         CHAT_PROTOCOL_REC *rec;
579
580         silc_client = silc_client_alloc(&ops, NULL);
581         silc_client->username = g_strdup(settings_get_str("user_name"));
582         silc_client->hostname = silc_net_localhost();
583         silc_client->realname = g_strdup(settings_get_str("real_name"));
584
585         if (!load_keys(silc_client)) {
586                 idletag = -1;
587                 return;
588         }
589
590         silc_client_init(silc_client);
591
592         rec = g_new0(CHAT_PROTOCOL_REC, 1);
593         rec->name = "SILC";
594         rec->fullname = "Secure Internet Live Conferencing";
595         rec->chatnet = "silcnet";
596
597         rec->create_chatnet = create_chatnet;
598         rec->create_server_setup = create_server_setup;
599         rec->create_channel_setup = create_channel_setup;
600         rec->create_server_connect = create_server_connect;
601
602         rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
603                 silc_server_connect;
604         rec->channel_create =
605                 (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
606                 silc_channel_create;
607         rec->query_create =
608                 (QUERY_REC *(*) (const char *, const char *, int))
609                 silc_query_create;
610
611         chat_protocol_register(rec);
612         g_free(rec);
613
614         silc_server_init();
615         silc_channels_init();
616         silc_queries_init();
617
618         idletag = g_timeout_add(100, (GSourceFunc) my_silc_scheduler, NULL);
619 }
620
621 void silc_core_deinit(void)
622 {
623         if (idletag != -1) {
624                 signal_emit("chat protocol deinit", 1,
625                             chat_protocol_find("SILC"));
626
627                 silc_server_deinit();
628                 silc_channels_deinit();
629                 silc_queries_deinit();
630
631                 chat_protocol_unregister("SILC");
632
633                 g_source_remove(idletag);
634         }
635
636         g_free(silc_client->username);
637         g_free(silc_client->realname);
638         silc_client_free(silc_client);
639 }