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