imported.
[silc.git] / apps / irssi / src / silc / core / silc-core.c
1 #include "module.h"
2 #include "chat-protocols.h"
3
4 #include "chatnets.h"
5 #include "servers-setup.h"
6 #include "channels-setup.h"
7 #include "silc-servers.h"
8 #include "silc-channels.h"
9 #include "silc-queries.h"
10 #include "silc-nicklist.h"
11 #include "version_internal.h"
12
13 #include "signals.h"
14 #include "levels.h"
15 #include "settings.h"
16 #include "fe-common/core/printtext.h"
17 #include "fe-common/core/fe-channels.h"
18
19 #define SILC_CLIENT_PUBLIC_KEY_NAME "public_key.pub"
20 #define SILC_CLIENT_PRIVATE_KEY_NAME "private_key.prv"
21
22 #define SILC_CLIENT_DEF_PKCS "rsa"
23 #define SILC_CLIENT_DEF_PKCS_LEN 1024
24
25 SilcClient silc_client;
26 const char *silc_version_string = SILC_PROTOCOL_VERSION_STRING;
27
28 static int idletag;
29
30 extern SilcClientOperations ops;
31
32 static void silc_say(SilcClient client, SilcClientConnection conn,
33                      char *msg, ...)
34 {
35         SILC_SERVER_REC *server;
36         va_list va;
37         char *str;
38
39         server = conn == NULL ? NULL : conn->context;
40
41         va_start(va, msg);
42         str = g_strdup_vprintf(msg, va);
43         printtext(server, "#silc", MSGLEVEL_CRAP, "%s", str);
44         g_free(str);
45         va_end(va);
46 }
47
48 static void silc_channel_message(SilcClient c, SilcClientConnection conn,
49                                  SilcClientEntry client,
50                                  SilcChannelEntry channel, char *msg)
51 {
52         SILC_SERVER_REC *server;
53         SILC_NICK_REC *nick;
54         SILC_CHANNEL_REC *chanrec;
55
56         server = conn == NULL ? NULL : conn->context;
57         chanrec = silc_channel_find_entry(server, channel);
58
59         nick = client == NULL ? NULL : silc_nicklist_find(chanrec, client);
60         signal_emit("message public", 6, server, msg,
61                     nick == NULL ? "(unknown)" : nick->nick,
62                     nick == NULL ? NULL : nick->host,
63                     chanrec->name, nick);
64 }
65
66 static void silc_private_message(SilcClient c, SilcClientConnection conn,
67                                  SilcClientEntry client, char *msg)
68 {
69         SILC_SERVER_REC *server;
70
71         server = conn == NULL ? NULL : conn->context;
72         signal_emit("message private", 4, server, msg,
73                     client == NULL ? "(unknown)" : client->nickname,
74                     client == NULL ? NULL : client->username);
75 }
76
77 typedef struct {
78         int type;
79         const char *name;
80 } NOTIFY_REC;
81
82 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
83 static NOTIFY_REC notifies[] = {
84         { SILC_NOTIFY_TYPE_NONE,                NULL },
85         { SILC_NOTIFY_TYPE_INVITE,              "invite" },
86         { SILC_NOTIFY_TYPE_JOIN,                "join" },
87         { SILC_NOTIFY_TYPE_LEAVE,               "leave" },
88         { SILC_NOTIFY_TYPE_SIGNOFF,             "signoff" },
89         { SILC_NOTIFY_TYPE_TOPIC_SET,           "topic" },
90         { SILC_NOTIFY_TYPE_NICK_CHANGE,         "nick" },
91         { SILC_NOTIFY_TYPE_CMODE_CHANGE,        "cmode" },
92         { SILC_NOTIFY_TYPE_CUMODE_CHANGE,       "cumode" },
93         { SILC_NOTIFY_TYPE_MOTD,                "motd" }
94 };
95
96 static void silc_notify(SilcClient client, SilcClientConnection conn,
97                         SilcNotifyType type, ...)
98 {
99         SILC_SERVER_REC *server;
100         va_list va;
101
102         server = conn == NULL ? NULL : conn->context;
103         va_start(va, type);
104
105         if (type == SILC_NOTIFY_TYPE_NONE) {
106                 /* some generic notice from server */
107                 printtext(server, NULL, MSGLEVEL_CRAP, "%s",
108                           (char *) va_arg(va, char *));
109         } else if (type < MAX_NOTIFY) {
110                 /* send signal about the notify event */
111                 char signal[50];
112
113                 g_snprintf(signal, sizeof(signal), "silc event %s",
114                            notifies[type].name);
115                 signal_emit(signal, 2, server, va);
116         } else {
117                 /* unknown notify */
118                 printtext(server, NULL, MSGLEVEL_CRAP,
119                           "Unknown notify %d", type);
120         }
121         va_end(va);
122 }
123
124 static void silc_connect(SilcClient client, SilcClientConnection conn, int success)
125 {
126         SILC_SERVER_REC *server = conn->context;
127
128         if (success) {
129                 server->connected = TRUE;
130                 signal_emit("event connected", 1, server);
131         } else {
132                 server->connection_lost = TRUE;
133                 server->conn->context = NULL;
134                 server_disconnect(SERVER(server));
135         }
136 }
137
138 static void silc_disconnect(SilcClient client, SilcClientConnection conn)
139 {
140         SILC_SERVER_REC *server = conn->context;
141
142         server->conn->context = NULL;
143         server->conn = NULL;
144         server->connection_lost = TRUE;
145         server_disconnect(SERVER(server));
146 }
147
148 static void silc_command(SilcClient client, SilcClientConnection conn,
149                          SilcClientCommandContext cmd_context, int success,
150                          SilcCommand command)
151 {
152 }
153
154 static void silc_command_reply(SilcClient client, SilcClientConnection conn,
155                                SilcCommandPayload cmd_payload, int success,
156                                SilcCommand command,
157                                SilcCommandStatus status, ...)
158 {
159         SILC_SERVER_REC *server = conn->context;
160         SILC_CHANNEL_REC *chanrec;
161         va_list va;
162
163         va_start(va, status);
164
165         /*g_snprintf(signal, sizeof(signal), "silc command reply %s",
166                    silc_commands[type]);
167         signal_emit(signal, 2, server, va);*/
168
169         switch(command) {
170         case SILC_COMMAND_JOIN: {
171                 char *channel, *mode;
172
173                 channel = va_arg(va, char *);
174                 (void)va_arg(va, SilcChannelEntry);
175                 mode = silc_client_chmode(va_arg(va, unsigned int));
176
177                 chanrec = silc_channel_find(server, channel);
178                 if (chanrec != NULL && !success)
179                         channel_destroy(CHANNEL(chanrec));
180                 else if (chanrec == NULL && success)
181                         chanrec = silc_channel_create(server, channel, TRUE);
182
183                 g_free_not_null(chanrec->mode);
184                 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
185                 signal_emit("channel mode changed", 1, chanrec);
186                 break;
187         }
188         case SILC_COMMAND_NICK: {
189                 SilcClientEntry client = va_arg(va, SilcClientEntry);
190                 char *old;
191
192                 old = g_strdup(server->nick);
193                 server_change_nick(SERVER(server), client->nickname);
194                 nicklist_rename_unique(SERVER(server),
195                                        server->conn->local_entry, server->nick,
196                                        client, client->nickname);
197
198                 signal_emit("message own_nick", 4,
199                             server, server->nick, old, "");
200                 g_free(old);
201                 break;
202         }
203         case SILC_COMMAND_USERS: {
204                 SilcChannelEntry channel;
205                 SilcChannelUser user;
206                 NICK_REC *ownnick;
207
208                 channel = va_arg(va, SilcChannelEntry);
209                 chanrec = silc_channel_find_entry(server, channel);
210                 if (chanrec == NULL)
211                         break;
212
213                 silc_list_start(channel->clients);
214                 while ((user = silc_list_get(channel->clients)) != NULL)
215                         silc_nicklist_insert(chanrec, user, FALSE);
216
217                 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
218                 nicklist_set_own(CHANNEL(chanrec), ownnick);
219                 signal_emit("channel joined", 1, chanrec);
220                 fe_channels_nicklist(CHANNEL(chanrec),
221                                      CHANNEL_NICKLIST_FLAG_ALL);
222                 break;
223         }
224         }
225
226         va_end(va);
227 }
228
229 static int silc_verify_server_key(SilcClient client, SilcClientConnection conn,
230                                   unsigned char *pk, unsigned int pk_len,
231                                   SilcSKEPKType pk_type)
232 {
233         return TRUE;
234 }
235
236 static unsigned char *silc_ask_passphrase(SilcClient client,
237                                           SilcClientConnection conn)
238 {
239         return NULL;
240 }
241
242 static int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
243                                 char *hostname, unsigned short port,
244                                 SilcProtocolAuthMeth *auth_meth,
245                                 unsigned char **auth_data,
246                                 unsigned int *auth_data_len)
247 {
248         return FALSE;
249 }
250
251 static void silc_failure(SilcClient client, SilcClientConnection conn,
252                          SilcProtocol protocol, void *failure)
253 {
254 }
255
256 static int key_agreement(SilcClient client, SilcClientConnection conn,
257                          SilcClientEntry client_entry, char *hostname,
258                          int port)
259 {
260         return FALSE;
261 }
262
263 SilcClientOperations ops = {
264         silc_say,
265         silc_channel_message,
266         silc_private_message,
267         silc_notify,
268         silc_command,
269         silc_command_reply,
270         silc_connect,
271         silc_disconnect,
272         silc_get_auth_method,
273         silc_verify_server_key,
274         silc_ask_passphrase,
275         silc_failure,
276         key_agreement
277 };
278
279 /* Loads public and private key from files. */
280
281 static void silc_client_create_key_pair(char *pkcs_name, int bits,
282                                         char *identifier,
283                                         SilcPublicKey *pub_key,
284                                         SilcPrivateKey *prv_key)
285 {
286         SilcPKCS pkcs;
287         SilcRng rng;
288         unsigned char *key;
289         unsigned int key_len;
290
291         rng = silc_rng_alloc();
292         silc_rng_init(rng);
293         silc_rng_global_init(rng);
294
295         silc_pkcs_alloc(pkcs_name, &pkcs);
296         pkcs->pkcs->init(pkcs->context, bits, rng);
297
298         /* Create public key */
299         key = silc_pkcs_get_public_key(pkcs, &key_len);
300         *pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
301                                               key, key_len);
302
303         memset(key, 0, sizeof(key_len));
304         silc_free(key);
305
306         /* Create private key */
307         key = silc_pkcs_get_private_key(pkcs, &key_len);
308         *prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
309
310         memset(key, 0, sizeof(key_len));
311         silc_free(key);
312
313         silc_rng_free(rng);
314         silc_pkcs_free(pkcs);
315 }
316
317 static int read_keyfiles(SilcClient client, char *public_file,
318                          char *private_file)
319 {
320         struct stat statbuf;
321
322         if (stat(public_file, &statbuf) != 0 ||
323             stat(private_file, &statbuf) != 0)
324                 return FALSE;
325
326         if (!silc_pkcs_load_private_key(private_file, &client->private_key,
327                                         SILC_PKCS_FILE_BIN) &&
328             !silc_pkcs_load_private_key(private_file, &client->private_key,
329                                         SILC_PKCS_FILE_PEM))
330                 return FALSE;
331
332         if (!silc_pkcs_load_public_key(public_file, &client->public_key,
333                                        SILC_PKCS_FILE_PEM) &&
334             !silc_pkcs_load_public_key(public_file, &client->public_key,
335                                        SILC_PKCS_FILE_BIN))
336                 return FALSE;
337
338         return TRUE;
339 }
340
341 static char *silc_create_identifier(SilcClient client)
342 {
343         char hostname[256], *email, *ret;
344
345         if (gethostname(hostname, sizeof(hostname)) != 0)
346                 hostname[0] = '\0';
347
348         email = g_strdup_printf("%s@%s", client->username, hostname);
349         ret = silc_pkcs_encode_identifier(client->username, hostname,
350                                           client->realname, email,
351                                           NULL, NULL);
352         g_free(email);
353         return ret;
354 }
355
356 static int load_keys(SilcClient client)
357 {
358         char *public_file, *private_file;
359         char *identifier;
360
361         public_file = g_strdup_printf("%s/.irssi/%s", g_get_home_dir(),
362                                       SILC_CLIENT_PUBLIC_KEY_NAME);
363         private_file = g_strdup_printf("%s/.irssi/%s", g_get_home_dir(),
364                                        SILC_CLIENT_PRIVATE_KEY_NAME);
365
366         if (!read_keyfiles(client, public_file, private_file)) {
367                 /* couldn't read key files, recreate them */
368                 identifier = silc_create_identifier(client);
369                 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
370                                             SILC_CLIENT_DEF_PKCS_LEN,
371                                             identifier,
372                                             &client->public_key,
373                                             &client->private_key);
374                 silc_free(identifier);
375
376                 silc_pkcs_save_public_key(public_file, client->public_key,
377                                           SILC_PKCS_FILE_PEM);
378                 silc_pkcs_save_private_key(private_file, client->private_key,
379                                            NULL, SILC_PKCS_FILE_BIN);
380         }
381
382         g_free(public_file);
383         g_free(private_file);
384         return TRUE;
385 }
386
387 static int my_silc_scheduler(void)
388 {
389         silc_schedule_one(0);
390         return 1;
391 }
392
393 static CHATNET_REC *create_chatnet(void)
394 {
395         return g_malloc0(sizeof(CHATNET_REC));
396 }
397
398 static SERVER_SETUP_REC *create_server_setup(void)
399 {
400         return g_malloc0(sizeof(SERVER_SETUP_REC));
401 }
402
403 static CHANNEL_SETUP_REC *create_channel_setup(void)
404 {
405         return g_malloc0(sizeof(CHANNEL_SETUP_REC));
406 }
407
408 static SERVER_CONNECT_REC *create_server_connect(void)
409 {
410         return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
411 }
412
413 /* Command line option variables */
414 void silc_core_init(void)
415 {
416         CHAT_PROTOCOL_REC *rec;
417
418         silc_client = silc_client_alloc(&ops, NULL);
419         silc_client->username = g_strdup(settings_get_str("user_name"));
420         silc_client->hostname = silc_net_localhost();
421         silc_client->realname = g_strdup(settings_get_str("real_name"));
422
423         if (!load_keys(silc_client)) {
424                 idletag = -1;
425                 return;
426         }
427
428         silc_client_init(silc_client);
429
430         rec = g_new0(CHAT_PROTOCOL_REC, 1);
431         rec->name = "SILC";
432         rec->fullname = "Secure Internet Live Conferencing";
433         rec->chatnet = "silcnet";
434
435         rec->create_chatnet = create_chatnet;
436         rec->create_server_setup = create_server_setup;
437         rec->create_channel_setup = create_channel_setup;
438         rec->create_server_connect = create_server_connect;
439
440         rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
441                 silc_server_connect;
442         rec->channel_create =
443                 (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
444                 silc_channel_create;
445         rec->query_create =
446                 (QUERY_REC *(*) (const char *, const char *, int))
447                 silc_query_create;
448
449         chat_protocol_register(rec);
450         g_free(rec);
451
452         silc_server_init();
453         silc_channels_init();
454         silc_queries_init();
455
456         idletag = g_timeout_add(100, (GSourceFunc) my_silc_scheduler, NULL);
457 }
458
459 void silc_core_deinit(void)
460 {
461         if (idletag != -1) {
462                 signal_emit("chat protocol deinit", 1,
463                             chat_protocol_find("SILC"));
464
465                 silc_server_deinit();
466                 silc_channels_deinit();
467                 silc_queries_deinit();
468
469                 chat_protocol_unregister("SILC");
470
471                 g_source_remove(idletag);
472         }
473
474         g_free(silc_client->username);
475         g_free(silc_client->realname);
476         silc_client_free(silc_client);
477 }