updates.
[silc.git] / apps / irssi / src / silc / core / silc-channels.c
1 /*
2   silc-channels.c : irssi
3
4   Copyright (C) 2000 - 2001 Timo Sirainen
5                             Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11   
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16   
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22 #include "module.h"
23
24 #include "net-nonblock.h"
25 #include "net-sendbuffer.h"
26 #include "signals.h"
27 #include "servers.h"
28 #include "commands.h"
29 #include "levels.h"
30 #include "modules.h"
31 #include "rawlog.h"
32 #include "misc.h"
33 #include "settings.h"
34
35 #include "channels-setup.h"
36
37 #include "silc-servers.h"
38 #include "silc-channels.h"
39 #include "silc-queries.h"
40 #include "silc-nicklist.h"
41 #include "window-item-def.h"
42
43 #include "fe-common/core/printtext.h"
44 #include "fe-common/silc/module-formats.h"
45
46 SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
47                                       const char *name, int automatic)
48 {
49   SILC_CHANNEL_REC *rec;
50
51   g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
52   g_return_val_if_fail(name != NULL, NULL);
53
54   rec = g_new0(SILC_CHANNEL_REC, 1);
55   rec->chat_type = SILC_PROTOCOL;
56   rec->name = g_strdup(name);
57   rec->server = server;
58
59   channel_init((CHANNEL_REC *) rec, automatic);
60   return rec;
61 }
62
63 static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
64 {
65   if (!IS_SILC_CHANNEL(channel))
66     return;
67
68   if (channel->server != NULL && !channel->left && !channel->kicked) {
69     /* destroying channel record without actually
70        having left the channel yet */
71     silc_command_exec(channel->server, "PART", channel->name);
72   }
73 }
74
75 static void silc_channels_join(SILC_SERVER_REC *server,
76                                const char *channels, int automatic)
77 {
78   char **list, **tmp, *channel;
79   SILC_CHANNEL_REC *chanrec;
80
81   list = g_strsplit(channels, ",", -1);
82   for (tmp = list; *tmp != NULL; tmp++) {
83     channel = **tmp == '#' ? g_strdup(*tmp) :
84       g_strconcat("#", *tmp, NULL);
85
86     chanrec = silc_channel_find(server, channel);
87     if (chanrec) {
88       g_free(channel);
89       continue;
90     }
91
92     silc_command_exec(server, "JOIN", channel);
93     g_free(channel);
94   }
95
96   g_strfreev(list);
97 }
98
99 static void sig_connected(SILC_SERVER_REC *server)
100 {
101   if (IS_SILC_SERVER(server))
102     server->channels_join = (void *) silc_channels_join;
103 }
104
105 /* "server quit" signal from the core to indicate that QUIT command
106    was called. */
107
108 static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
109 {
110   if (IS_SILC_SERVER(server) && server->conn && server->conn->sock)
111     silc_command_exec(server, "QUIT", msg);
112 }
113
114 /*
115  * "event join". Joined to a channel.
116  */
117
118 SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
119                                           SilcChannelEntry entry)
120 {
121   GSList *tmp;
122
123   g_return_val_if_fail(IS_SILC_SERVER(server), NULL);
124
125   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
126     SILC_CHANNEL_REC *rec = tmp->data;
127
128     if (rec->entry == entry)
129       return rec;
130   }
131
132   return NULL;
133 }
134
135 static void event_join(SILC_SERVER_REC *server, va_list va)
136 {
137   SILC_CHANNEL_REC *chanrec;
138   SILC_NICK_REC *nickrec;
139   SilcClientEntry client;
140   SilcChannelEntry channel;
141
142   client = va_arg(va, SilcClientEntry);
143   channel = va_arg(va, SilcChannelEntry);
144
145   if (client == server->conn->local_entry) {
146     /* You joined to channel */
147     chanrec = silc_channel_find(server, channel->channel_name);
148     if (chanrec != NULL && !chanrec->joined)
149       chanrec->entry = channel;
150   } else {
151     chanrec = silc_channel_find_entry(server, channel);
152     if (chanrec != NULL) {
153       SilcChannelUser user;
154
155       silc_list_start(chanrec->entry->clients);
156       while ((user = silc_list_get(chanrec->entry->clients)) != NULL)
157         if (user->client == client) {
158           nickrec = silc_nicklist_insert(chanrec, user, TRUE);
159           break;
160         }
161     }
162   }
163
164   signal_emit("message join", 4, server, channel->channel_name,
165               client->nickname,
166               client->username == NULL ? "" : client->username);
167 }
168
169 /*
170  * "event leave". Left a channel.
171  */
172
173 static void event_leave(SILC_SERVER_REC *server, va_list va)
174 {
175   SILC_CHANNEL_REC *chanrec;
176   SILC_NICK_REC *nickrec;
177   SilcClientEntry client;
178   SilcChannelEntry channel;
179
180   client = va_arg(va, SilcClientEntry);
181   channel = va_arg(va, SilcChannelEntry);
182
183   signal_emit("message part", 5, server, channel->channel_name,
184               client->nickname,  client->username ?  client->username : "", 
185               client->nickname);
186
187   chanrec = silc_channel_find_entry(server, channel);
188   if (chanrec != NULL) {
189     nickrec = silc_nicklist_find(chanrec, client);
190     if (nickrec != NULL)
191       nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
192   }
193 }
194
195 /*
196  * "event signoff". Left the network.
197  */
198
199 static void event_signoff(SILC_SERVER_REC *server, va_list va)
200 {
201   SilcClientEntry client;
202   GSList *nicks, *tmp;
203   char *message;
204
205   client = va_arg(va, SilcClientEntry);
206   message = va_arg(va, char *);
207
208   signal_emit("message quit", 4, server, client->nickname,
209               client->username ? client->username : "", 
210               message ? message : "");
211
212   nicks = nicklist_get_same_unique(SERVER(server), client);
213   for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
214     CHANNEL_REC *channel = tmp->data;
215     NICK_REC *nickrec = tmp->next->data;
216     
217     nicklist_remove(channel, nickrec);
218   }
219 }
220
221 /*
222  * "event topic". Changed topic.
223  */
224
225 static void event_topic(SILC_SERVER_REC *server, va_list va)
226 {
227   SILC_CHANNEL_REC *chanrec;
228   SilcClientEntry client;
229   SilcChannelEntry channel;
230   char *topic;
231
232   client = va_arg(va, SilcClientEntry);
233   topic = va_arg(va, char *);
234   channel = va_arg(va, SilcChannelEntry);
235
236   chanrec = silc_channel_find_entry(server, channel);
237   if (chanrec != NULL) {
238     g_free_not_null(chanrec->topic);
239     chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
240     signal_emit("channel topic changed", 1, chanrec);
241   }
242
243   signal_emit("message topic", 5, server, channel->channel_name,
244               topic, client->nickname, client->username);
245 }
246
247 /*
248  * "event invite". Invited or modified invite list.
249  */
250
251 static void event_invite(SILC_SERVER_REC *server, va_list va)
252 {
253   SilcClientEntry client;
254   SilcChannelEntry channel;
255   char *channel_name;
256   
257   channel = va_arg(va, SilcChannelEntry);
258   channel_name = va_arg(va, char *);
259   client = va_arg(va, SilcClientEntry);
260
261   signal_emit("message invite", 4, server, channel ? channel->channel_name :
262               channel_name, client->nickname, client->username);
263 }
264
265 /*
266  * "event nick". Changed nickname.
267  */
268
269 static void event_nick(SILC_SERVER_REC *server, va_list va)
270 {
271   SilcClientEntry oldclient, newclient;
272
273   oldclient = va_arg(va, SilcClientEntry);
274   newclient = va_arg(va, SilcClientEntry);
275
276   nicklist_rename_unique(SERVER(server),
277                          oldclient, oldclient->nickname,
278                          newclient, newclient->nickname);
279
280   signal_emit("message nick", 4, server, newclient->nickname, 
281               oldclient->nickname, newclient->username);
282 }
283
284 /*
285  * "event cmode". Changed channel mode.
286  */
287
288 static void event_cmode(SILC_SERVER_REC *server, va_list va)
289 {
290   SILC_CHANNEL_REC *chanrec;
291   void *entry;
292   SilcClientEntry client;
293   SilcServerEntry server_entry;
294   SilcChannelEntry channel;
295   char *mode;
296   uint32 modei;
297   SilcIdType idtype;
298
299   idtype = va_arg(va, int);
300   entry = va_arg(va, void *);
301   modei = va_arg(va, uint32);
302   (void)va_arg(va, char *);
303   (void)va_arg(va, char *);
304   channel = va_arg(va, SilcChannelEntry);
305
306   mode = silc_client_chmode(modei, 
307                             channel->channel_key->cipher->name,
308                             channel->hmac->hmac->name);
309   
310   chanrec = silc_channel_find_entry(server, channel);
311   if (chanrec != NULL) {
312     g_free_not_null(chanrec->mode);
313     chanrec->mode = g_strdup(mode == NULL ? "" : mode);
314     signal_emit("channel mode changed", 1, chanrec);
315   }
316   
317   if (idtype == SILC_ID_CLIENT) {
318     client = (SilcClientEntry)entry;
319     printformat_module("fe-common/silc", server, channel->channel_name,
320                        MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
321                        channel->channel_name, mode ? mode : "removed all",
322                        client->nickname);
323   } else {
324     server_entry = (SilcServerEntry)entry;
325     printformat_module("fe-common/silc", server, channel->channel_name,
326                        MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
327                        channel->channel_name, mode ? mode : "removed all",
328                        server_entry->server_name);
329   }
330   
331   g_free(mode);
332 }
333
334 /*
335  * "event cumode". Changed user's mode on channel.
336  */
337
338 static void event_cumode(SILC_SERVER_REC *server, va_list va)
339 {
340   SILC_CHANNEL_REC *chanrec;
341   SilcClientEntry client, destclient;
342   SilcChannelEntry channel;
343   int mode;
344   char *modestr;
345   
346   client = va_arg(va, SilcClientEntry);
347   mode = va_arg(va, uint32);
348   destclient = va_arg(va, SilcClientEntry);
349   channel = va_arg(va, SilcChannelEntry);
350   
351   modestr = silc_client_chumode(mode);
352   chanrec = silc_channel_find_entry(server, channel);
353   if (chanrec != NULL) {
354     SILC_NICK_REC *nick;
355     
356     if (destclient == server->conn->local_entry) {
357       chanrec->chanop =
358         (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
359     }
360
361     nick = silc_nicklist_find(chanrec, destclient);
362     if (nick != NULL) {
363       nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
364       nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
365       signal_emit("nick mode changed", 2, chanrec, nick);
366     }
367   }
368   
369   printformat_module("fe-common/silc", server, channel->channel_name,
370                      MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
371                      channel->channel_name, destclient->nickname, 
372                      modestr ? modestr : "removed all",
373                      client->nickname);
374
375   if (mode & SILC_CHANNEL_UMODE_CHANFO)
376     printformat_module("fe-common/silc", 
377                        server, channel->channel_name, MSGLEVEL_CRAP,
378                        SILCTXT_CHANNEL_FOUNDER,
379                        channel->channel_name, destclient->nickname);
380
381   g_free(modestr);
382 }
383
384 /*
385  * "event motd". Received MOTD.
386  */
387
388 static void event_motd(SILC_SERVER_REC *server, va_list va)
389 {
390   char *text = va_arg(va, char *);
391
392   if (!settings_get_bool("skip_motd"))
393     printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", text);
394 }
395
396 /*
397  * "event channel_change". Channel ID has changed.
398  */
399
400 static void event_channel_change(SILC_SERVER_REC *server, va_list va)
401 {
402   /* Nothing interesting to do */
403 }
404
405 /*
406  * "event server_signoff". Server has quit the network.
407  */
408
409 static void event_server_signoff(SILC_SERVER_REC *server, va_list va)
410 {
411   SilcClientEntry *clients;
412   uint32 clients_count;
413   int i;
414   
415   (void)va_arg(va, void *);
416   clients = va_arg(va, SilcClientEntry *);
417   clients_count = va_arg(va, uint32);
418   
419   for (i = 0; i < clients_count; i++)
420     signal_emit("message quit", 4, server, clients[i]->nickname,
421                 clients[i]->username ? clients[i]->username : "", 
422                 "server signoff");
423 }
424
425 /*
426  * "event kick". Someone was kicked from channel.
427  */
428
429 static void event_kick(SILC_SERVER_REC *server, va_list va)
430 {
431   SilcClientConnection conn = server->conn;
432   SilcClientEntry client_entry;
433   SilcChannelEntry channel_entry;
434   char *tmp;
435   SILC_CHANNEL_REC *chanrec;
436
437   client_entry = va_arg(va, SilcClientEntry);
438   tmp = va_arg(va, char *);
439   channel_entry = va_arg(va, SilcChannelEntry);
440
441   chanrec = silc_channel_find_entry(server, channel_entry);
442   
443   if (client_entry == conn->local_entry) {
444     printformat_module("fe-common/silc", server, channel_entry->channel_name,
445                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
446                        channel_entry->channel_name, tmp ? tmp : "");
447     if (chanrec) {
448       chanrec->kicked = TRUE;
449       channel_destroy((CHANNEL_REC *)chanrec);
450     }
451   } else {
452     printformat_module("fe-common/silc", server, channel_entry->channel_name,
453                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
454                        client_entry->nickname,
455                        channel_entry->channel_name, tmp ? tmp : "");
456
457     if (chanrec) {
458       SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
459       if (nickrec != NULL)
460         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
461     }
462   }
463 }
464
465 /*
466  * "event kill". Someone was killed from the network.
467  */
468
469 static void event_kill(SILC_SERVER_REC *server, va_list va)
470 {
471   SilcClientConnection conn = server->conn;
472   SilcClientEntry client_entry;
473   char *tmp;
474
475   client_entry = va_arg(va, SilcClientEntry);
476   tmp = va_arg(va, char *);
477   
478   if (client_entry == conn->local_entry) {
479     printformat_module("fe-common/silc", server, NULL,
480                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
481                        tmp ? tmp : "");
482   } else {
483     GSList *nicks, *tmpn;
484     nicks = nicklist_get_same_unique(SERVER(server), client_entry);
485     for (tmpn = nicks; tmpn != NULL; tmpn = tmpn->next->next) {
486       CHANNEL_REC *channel = tmpn->data;
487       NICK_REC *nickrec = tmpn->next->data;
488       nicklist_remove(channel, nickrec);
489     }
490
491     printformat_module("fe-common/silc", server, NULL,
492                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
493                        client_entry->nickname,
494                        tmp ? tmp : "");
495   }
496 }
497
498 /* PART (LEAVE) command. */
499
500 static void command_part(const char *data, SILC_SERVER_REC *server,
501                          WI_ITEM_REC *item)
502 {
503   SILC_CHANNEL_REC *chanrec;
504   
505   if (!IS_SILC_SERVER(server) || !server->connected)
506     cmd_return_error(CMDERR_NOT_CONNECTED);
507
508   if (!strcmp(data, "*") || *data == '\0') {
509     if (!IS_SILC_CHANNEL(item))
510       cmd_return_error(CMDERR_NOT_JOINED);
511     data = item->name;
512   }
513
514   chanrec = silc_channel_find(server, data);
515   if (chanrec == NULL) 
516     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
517
518   signal_emit("message part", 5, server, chanrec->name,
519               server->nick, server->conn->local_entry->username, "");
520   
521   silc_command_exec(server, "LEAVE", chanrec->name);
522   signal_stop();
523   
524   channel_destroy(CHANNEL(chanrec));
525 }
526
527 /* ME local command. */
528
529 static void command_me(const char *data, SILC_SERVER_REC *server,
530                        WI_ITEM_REC *item)
531 {
532   SILC_CHANNEL_REC *chanrec;
533   char *tmpcmd = "ME", *tmp;
534   uint32 argc = 0;
535   unsigned char **argv;
536   uint32 *argv_lens, *argv_types;
537   int i;
538  
539   if (!IS_SILC_SERVER(server) || !server->connected)
540     cmd_return_error(CMDERR_NOT_CONNECTED);
541
542   if (!IS_SILC_CHANNEL(item))
543     cmd_return_error(CMDERR_NOT_JOINED);
544
545   /* Now parse all arguments */
546   tmp = g_strconcat(tmpcmd, " ", data, NULL);
547   silc_parse_command_line(tmp, &argv, &argv_lens,
548                           &argv_types, &argc, 2);
549   g_free(tmp);
550
551   if (argc < 2)
552     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
553
554   chanrec = silc_channel_find(server, item->name);
555   if (chanrec == NULL) 
556     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
557
558   /* Send the action message */
559   silc_client_send_channel_message(silc_client, server->conn, 
560                                    chanrec->entry, NULL,
561                                    SILC_MESSAGE_FLAG_ACTION, 
562                                    argv[1], argv_lens[1], TRUE);
563
564   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
565                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
566                      server->conn->local_entry->nickname, argv[1]);
567
568   for (i = 0; i < argc; i++)
569     silc_free(argv[i]);
570   silc_free(argv_lens);
571   silc_free(argv_types);
572 }
573
574 /* ACTION local command. Same as ME but takes the channel as mandatory
575    argument. */
576
577 static void command_action(const char *data, SILC_SERVER_REC *server,
578                            WI_ITEM_REC *item)
579 {
580   SILC_CHANNEL_REC *chanrec;
581   char *tmpcmd = "ME", *tmp;
582   uint32 argc = 0;
583   unsigned char **argv;
584   uint32 *argv_lens, *argv_types;
585   int i;
586  
587   if (!IS_SILC_SERVER(server) || !server->connected)
588     cmd_return_error(CMDERR_NOT_CONNECTED);
589
590   if (!IS_SILC_CHANNEL(item))
591     cmd_return_error(CMDERR_NOT_JOINED);
592
593   /* Now parse all arguments */
594   tmp = g_strconcat(tmpcmd, " ", data, NULL);
595   silc_parse_command_line(tmp, &argv, &argv_lens,
596                           &argv_types, &argc, 3);
597   g_free(tmp);
598
599   if (argc < 3)
600     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
601
602   chanrec = silc_channel_find(server, argv[1]);
603   if (chanrec == NULL) 
604     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
605
606   /* Send the action message */
607   silc_client_send_channel_message(silc_client, server->conn, 
608                                    chanrec->entry, NULL,
609                                    SILC_MESSAGE_FLAG_ACTION, 
610                                    argv[2], argv_lens[2], TRUE);
611
612   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
613                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
614                      server->conn->local_entry->nickname, argv[2]);
615
616   for (i = 0; i < argc; i++)
617     silc_free(argv[i]);
618   silc_free(argv_lens);
619   silc_free(argv_types);
620 }
621
622 /* NOTICE local command. */
623
624 static void command_notice(const char *data, SILC_SERVER_REC *server,
625                            WI_ITEM_REC *item)
626 {
627   SILC_CHANNEL_REC *chanrec;
628   char *tmpcmd = "ME", *tmp;
629   uint32 argc = 0;
630   unsigned char **argv;
631   uint32 *argv_lens, *argv_types;
632   int i;
633  
634   if (!IS_SILC_SERVER(server) || !server->connected)
635     cmd_return_error(CMDERR_NOT_CONNECTED);
636
637   if (!IS_SILC_CHANNEL(item))
638     cmd_return_error(CMDERR_NOT_JOINED);
639
640   /* Now parse all arguments */
641   tmp = g_strconcat(tmpcmd, " ", data, NULL);
642   silc_parse_command_line(tmp, &argv, &argv_lens,
643                           &argv_types, &argc, 2);
644   g_free(tmp);
645
646   if (argc < 2)
647     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
648
649   chanrec = silc_channel_find(server, item->name);
650   if (chanrec == NULL) 
651     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
652
653   /* Send the action message */
654   silc_client_send_channel_message(silc_client, server->conn, 
655                                    chanrec->entry, NULL,
656                                    SILC_MESSAGE_FLAG_NOTICE, 
657                                    argv[1], argv_lens[1], TRUE);
658
659   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
660                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, 
661                      server->conn->local_entry->nickname, argv[1]);
662
663   for (i = 0; i < argc; i++)
664     silc_free(argv[i]);
665   silc_free(argv_lens);
666   silc_free(argv_types);
667 }
668
669 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
670    flag. */
671
672 static void command_away(const char *data, SILC_SERVER_REC *server,
673                          WI_ITEM_REC *item)
674 {
675   bool set;
676
677   if (!IS_SILC_SERVER(server) || !server->connected)
678     cmd_return_error(CMDERR_NOT_CONNECTED);
679
680   if (*data == '\0') {
681     /* Remove any possible away message */
682     silc_client_set_away_message(silc_client, server->conn, NULL);
683     set = FALSE;
684
685     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
686                        SILCTXT_UNSET_AWAY);
687   } else {
688     /* Set the away message */
689     silc_client_set_away_message(silc_client, server->conn, (char *)data);
690     set = TRUE;
691
692     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
693                        SILCTXT_SET_AWAY, data);
694   }
695
696   signal_emit("away mode changed", 1, server);
697
698   silc_command_exec(server, "UMODE", set ? "+g" : "-g");
699 }
700
701 typedef struct {
702   int type;                     /* 1 = msg, 2 = channel */
703   bool responder;
704   SILC_SERVER_REC *server;
705 } *KeyInternal;
706
707 /* Key agreement callback that is called after the key agreement protocol
708    has been performed. This is called also if error occured during the
709    key agreement protocol. The `key' is the allocated key material and
710    the caller is responsible of freeing it. The `key' is NULL if error
711    has occured. The application can freely use the `key' to whatever
712    purpose it needs. See lib/silcske/silcske.h for the definition of
713    the SilcSKEKeyMaterial structure. */
714
715 static void keyagr_completion(SilcClient client,
716                               SilcClientConnection conn,
717                               SilcClientEntry client_entry,
718                               SilcKeyAgreementStatus status,
719                               SilcSKEKeyMaterial *key,
720                               void *context)
721 {
722   KeyInternal i = (KeyInternal)context;
723
724   switch(status) {
725   case SILC_KEY_AGREEMENT_OK:
726     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
727                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
728
729     if (i->type == 1) {
730       /* Set the private key for this client */
731       silc_client_del_private_message_key(client, conn, client_entry);
732       silc_client_add_private_message_key_ske(client, conn, client_entry,
733                                               NULL, key, i->responder);
734       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
735                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
736                          client_entry->nickname);
737       silc_ske_free_key_material(key);
738     }
739     
740     break;
741     
742   case SILC_KEY_AGREEMENT_ERROR:
743     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
744                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
745     break;
746     
747   case SILC_KEY_AGREEMENT_FAILURE:
748     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
749                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
750     break;
751     
752   case SILC_KEY_AGREEMENT_TIMEOUT:
753     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_NOTICES,
754                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
755     break;
756     
757   default:
758     break;
759   } 
760
761   if (i)
762     silc_free(i);
763 }
764
765 /* Local command KEY. This command is used to set and unset private
766    keys for channels, set and unset private keys for private messages
767    with remote clients and to send key agreement requests and
768    negotiate the key agreement protocol with remote client.  The
769    key agreement is supported only to negotiate private message keys,
770    it currently cannot be used to negotiate private keys for channels,
771    as it is not convenient for that purpose. */
772
773 typedef struct {
774   SILC_SERVER_REC *server;
775   char *data;
776   WI_ITEM_REC *item;
777 } *KeyGetClients;
778
779 /* Callback to be called after client information is resolved from the
780    server. */
781
782 SILC_CLIENT_CMD_FUNC(key_get_clients)
783 {
784   KeyGetClients internal = (KeyGetClients)context;
785   signal_emit("command key", 3, internal->data, internal->server,
786               internal->item);
787   silc_free(internal->data);
788   silc_free(internal);
789 }
790
791 static void command_key(const char *data, SILC_SERVER_REC *server,
792                         WI_ITEM_REC *item)
793 {
794   SilcClientConnection conn = server->conn;
795   SilcClientEntry client_entry = NULL;
796   SilcChannelEntry channel_entry = NULL;
797   char *nickname = NULL, *tmp;
798   int command = 0, port = 0, type = 0;
799   char *hostname = NULL;
800   KeyInternal internal = NULL;
801   uint32 argc = 0;
802   unsigned char **argv;
803   uint32 *argv_lens, *argv_types;
804  
805   if (!IS_SILC_SERVER(server) || !server->connected)
806     cmd_return_error(CMDERR_NOT_CONNECTED);
807
808   /* Now parse all arguments */
809   tmp = g_strconcat("KEY", " ", data, NULL);
810   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
811   g_free(tmp);
812
813   if (argc < 4) {
814     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
815              "Usage: /KEY msg|channel <nickname|channel> "
816              "set|unset|agreement|negotiate [<arguments>]");
817     return;
818   }
819
820   /* Get type */
821   if (!strcasecmp(argv[1], "msg"))
822     type = 1;
823   if (!strcasecmp(argv[1], "channel"))
824     type = 2;
825
826   if (type == 0) {
827     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
828              "Usage: /KEY msg|channel <nickname|channel> "
829              "set|unset|agreement|negotiate [<arguments>]");
830     return;
831   }
832
833   if (type == 1) {
834     if (argv[2][0] == '*') {
835       nickname = "*";
836     } else {
837       /* Parse the typed nickname. */
838       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
839         printformat_module("fe-common/silc", server, NULL,
840                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
841         return;
842       }
843       
844       /* Find client entry */
845       client_entry = silc_idlist_get_client(silc_client, conn, nickname, 
846                                             argv[2], TRUE);
847       if (!client_entry) {
848         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
849         inter->server = server;
850         inter->data = strdup(data);
851         inter->item = item;
852
853         /* Client entry not found, it was requested thus mark this to be
854            pending command. */
855         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
856                                     conn->cmd_ident, 
857                                     NULL, silc_client_command_key_get_clients, 
858                                     inter);
859         goto out;
860       }
861     }
862   }
863
864   if (type == 2) {
865     /* Get channel entry */
866     char *name;
867
868     if (argv[2][0] == '*') {
869       if (!conn->current_channel) {
870         silc_free(nickname);
871         cmd_return_error(CMDERR_NOT_JOINED);
872       }
873       name = conn->current_channel->channel_name;
874     } else {
875       name = argv[2];
876     }
877
878     channel_entry = silc_client_get_channel(silc_client, conn, name);
879     if (!channel_entry) {
880       silc_free(nickname);
881       cmd_return_error(CMDERR_NOT_JOINED);
882     }
883   }
884
885   /* Set command */
886   if (!strcasecmp(argv[3], "set")) {
887     command = 1;
888
889     if (argc >= 5) {
890       if (type == 1 && client_entry) {
891         /* Set private message key */
892         
893         silc_client_del_private_message_key(silc_client, conn, client_entry);
894
895         if (argc >= 6)
896           silc_client_add_private_message_key(silc_client, conn, client_entry,
897                                               argv[5], argv[4],
898                                               argv_lens[4],
899                                               (argv[4][0] == '*' ?
900                                                TRUE : FALSE), FALSE);
901         else
902           silc_client_add_private_message_key(silc_client, conn, client_entry,
903                                               NULL, argv[4],
904                                               argv_lens[4],
905                                               (argv[4][0] == '*' ?
906                                                TRUE : FALSE), FALSE);
907
908         /* Send the key to the remote client so that it starts using it
909            too. */
910         silc_client_send_private_message_key(silc_client, conn, 
911                                              client_entry, TRUE);
912       } else if (type == 2) {
913         /* Set private channel key */
914         char *cipher = NULL, *hmac = NULL;
915
916         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
917           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
918                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
919                              channel_entry->channel_name);
920           goto out;
921         }
922
923         if (argc >= 6)
924           cipher = argv[5];
925         if (argc >= 7)
926           hmac = argv[6];
927
928         if (!silc_client_add_channel_private_key(silc_client, conn, 
929                                                  channel_entry,
930                                                  cipher, hmac,
931                                                  argv[4],
932                                                  argv_lens[4])) {
933           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
934                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
935                              channel_entry->channel_name);
936           goto out;
937         }
938
939         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
940                            SILCTXT_CH_PRIVATE_KEY_ADD, 
941                            channel_entry->channel_name);
942       }
943     }
944
945     goto out;
946   }
947   
948   /* Unset command */
949   if (!strcasecmp(argv[3], "unset")) {
950     command = 2;
951
952     if (type == 1 && client_entry) {
953       /* Unset private message key */
954       silc_client_del_private_message_key(silc_client, conn, client_entry);
955     } else if (type == 2) {
956       /* Unset channel key(s) */
957       SilcChannelPrivateKey *keys;
958       uint32 keys_count;
959       int number;
960
961       if (argc == 4)
962         silc_client_del_channel_private_keys(silc_client, conn, 
963                                              channel_entry);
964
965       if (argc > 4) {
966         number = atoi(argv[4]);
967         keys = silc_client_list_channel_private_keys(silc_client, conn, 
968                                                      channel_entry,
969                                                      &keys_count);
970         if (!keys)
971           goto out;
972
973         if (!number || number > keys_count) {
974           silc_client_free_channel_private_keys(keys, keys_count);
975           goto out;
976         }
977
978         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
979                                             keys[number - 1]);
980         silc_client_free_channel_private_keys(keys, keys_count);
981       }
982
983       goto out;
984     }
985   }
986
987   /* List command */
988   if (!strcasecmp(argv[3], "list")) {
989     command = 3;
990
991     if (type == 1) {
992       SilcPrivateMessageKeys keys;
993       uint32 keys_count;
994       int k, i, len;
995       char buf[1024];
996
997       keys = silc_client_list_private_message_keys(silc_client, conn, 
998                                                    &keys_count);
999       if (!keys)
1000         goto out;
1001
1002       /* list the private message key(s) */
1003       if (nickname[0] == '*') {
1004         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1005                            SILCTXT_PRIVATE_KEY_LIST);
1006         for (k = 0; k < keys_count; k++) {
1007           memset(buf, 0, sizeof(buf));
1008           strncat(buf, "  ", 2);
1009           len = strlen(keys[k].client_entry->nickname);
1010           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1011           if (len < 30)
1012             for (i = 0; i < 30 - len; i++)
1013               strcat(buf, " ");
1014           strcat(buf, " ");
1015           
1016           len = strlen(keys[k].cipher);
1017           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1018           if (len < 14)
1019             for (i = 0; i < 14 - len; i++)
1020               strcat(buf, " ");
1021           strcat(buf, " ");
1022
1023           if (keys[k].key)
1024             strcat(buf, "<hidden>");
1025           else
1026             strcat(buf, "*generated*");
1027
1028           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1029         }
1030       } else {
1031         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1032                            SILCTXT_PRIVATE_KEY_LIST_NICK,
1033                            client_entry->nickname);
1034         for (k = 0; k < keys_count; k++) {
1035           if (keys[k].client_entry != client_entry)
1036             continue;
1037
1038           memset(buf, 0, sizeof(buf));
1039           strncat(buf, "  ", 2);
1040           len = strlen(keys[k].client_entry->nickname);
1041           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1042           if (len < 30)
1043             for (i = 0; i < 30 - len; i++)
1044               strcat(buf, " ");
1045           strcat(buf, " ");
1046           
1047           len = strlen(keys[k].cipher);
1048           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1049           if (len < 14)
1050             for (i = 0; i < 14 - len; i++)
1051               strcat(buf, " ");
1052           strcat(buf, " ");
1053
1054           if (keys[k].key)
1055             strcat(buf, "<hidden>");
1056           else
1057             strcat(buf, "*generated*");
1058
1059           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1060         }
1061       }
1062
1063       silc_client_free_private_message_keys(keys, keys_count);
1064     } else if (type == 2) {
1065       SilcChannelPrivateKey *keys;
1066       uint32 keys_count;
1067       int k, i, len;
1068       char buf[1024];
1069
1070       keys = silc_client_list_channel_private_keys(silc_client, conn, 
1071                                                    channel_entry,
1072                                                    &keys_count);
1073       if (!keys)
1074         goto out;
1075       
1076       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1077                          SILCTXT_CH_PRIVATE_KEY_LIST,
1078                          channel_entry->channel_name);
1079       for (k = 0; k < keys_count; k++) {
1080         memset(buf, 0, sizeof(buf));
1081         strncat(buf, "  ", 2);
1082
1083         len = strlen(keys[k]->cipher->cipher->name);
1084         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
1085         if (len < 16)
1086           for (i = 0; i < 16 - len; i++)
1087             strcat(buf, " ");
1088         strcat(buf, " ");
1089         
1090         len = strlen(keys[k]->hmac->hmac->name);
1091         strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
1092         if (len < 16)
1093           for (i = 0; i < 16 - len; i++)
1094             strcat(buf, " ");
1095         strcat(buf, " ");
1096         
1097         strcat(buf, "<hidden>");
1098
1099         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1100       }
1101       
1102       silc_client_free_channel_private_keys(keys, keys_count);
1103     }
1104
1105     goto out;
1106   }
1107
1108   /* Send command is used to send key agreement */
1109   if (!strcasecmp(argv[3], "agreement")) {
1110     command = 4;
1111
1112     if (argc >= 5)
1113       hostname = argv[4];
1114     if (argc >= 6)
1115       port = atoi(argv[5]);
1116
1117     internal = silc_calloc(1, sizeof(*internal));
1118     internal->type = type;
1119     internal->server = server;
1120   }
1121
1122   /* Start command is used to start key agreement (after receiving the
1123      key_agreement client operation). */
1124   if (!strcasecmp(argv[3], "negotiate")) {
1125     command = 5;
1126
1127     if (argc >= 5)
1128       hostname = argv[4];
1129     if (argc >= 6)
1130       port = atoi(argv[5]);
1131
1132     internal = silc_calloc(1, sizeof(*internal));
1133     internal->type = type;
1134     internal->server = server;
1135   }
1136
1137   if (command == 0) {
1138     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
1139              "Usage: /KEY msg|channel <nickname|channel> "
1140              "set|unset|agreement|negotiate [<arguments>]");
1141     goto out;
1142   }
1143
1144   if (command == 4 && client_entry) {
1145     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1146                        SILCTXT_KEY_AGREEMENT, argv[2]);
1147     internal->responder = TRUE;
1148     silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
1149                                    port, 120, keyagr_completion, internal);
1150     if (!hostname)
1151       silc_free(internal);
1152     goto out;
1153   }
1154
1155   if (command == 5 && client_entry && hostname) {
1156     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1157                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1158     internal->responder = FALSE;
1159     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1160                                       hostname, port, keyagr_completion, 
1161                                       internal);
1162     goto out;
1163   }
1164
1165  out:
1166   silc_free(nickname);
1167 }
1168
1169 /* Lists locally saved client and server public keys. */
1170
1171 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1172                              WI_ITEM_REC *item)
1173 {
1174
1175 }
1176
1177 void silc_channels_init(void)
1178 {
1179   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1180   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1181   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1182
1183   signal_add("silc event join", (SIGNAL_FUNC) event_join);
1184   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
1185   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
1186   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
1187   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
1188   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
1189   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
1190   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
1191   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
1192   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
1193   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
1194   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
1195   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
1196   
1197   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1198   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1199   command_bind("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1200   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1201   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1202   command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1203   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1204
1205   silc_nicklist_init();
1206 }
1207
1208 void silc_channels_deinit(void)
1209 {
1210   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1211   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1212   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1213
1214   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
1215   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
1216   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
1217   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
1218   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
1219   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
1220   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
1221   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
1222   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
1223   signal_remove("silc event channel_change", 
1224                 (SIGNAL_FUNC) event_channel_change);
1225   signal_remove("silc event server_signoff", 
1226                 (SIGNAL_FUNC) event_server_signoff);
1227   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
1228   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
1229   
1230   command_unbind("part", (SIGNAL_FUNC) command_part);
1231   command_unbind("me", (SIGNAL_FUNC) command_me);
1232   command_unbind("action", (SIGNAL_FUNC) command_action);
1233   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1234   command_unbind("away", (SIGNAL_FUNC) command_away);
1235   command_unbind("key", (SIGNAL_FUNC) command_key);
1236   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1237
1238   silc_nicklist_deinit();
1239 }