2ee76c58e99ad308f936805e31052e5f0708e74f
[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   uint32 num = 0;
798   char *nickname = NULL, *serv = NULL, *tmp;
799   int command = 0, port = 0, type = 0;
800   char *hostname = NULL;
801   KeyInternal internal = NULL;
802   uint32 argc = 0;
803   unsigned char **argv;
804   uint32 *argv_lens, *argv_types;
805  
806   if (!IS_SILC_SERVER(server) || !server->connected)
807     cmd_return_error(CMDERR_NOT_CONNECTED);
808
809   /* Now parse all arguments */
810   tmp = g_strconcat("KEY", " ", data, NULL);
811   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
812   g_free(tmp);
813
814   if (argc < 4) {
815     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
816              "Usage: /KEY msg|channel <nickname|channel> "
817              "set|unset|agreement|negotiate [<arguments>]");
818     return;
819   }
820
821   /* Get type */
822   if (!strcasecmp(argv[1], "msg"))
823     type = 1;
824   if (!strcasecmp(argv[1], "channel"))
825     type = 2;
826
827   if (type == 0) {
828     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
829              "Usage: /KEY msg|channel <nickname|channel> "
830              "set|unset|agreement|negotiate [<arguments>]");
831     return;
832   }
833
834   if (type == 1) {
835     if (argv[2][0] == '*') {
836       nickname = "*";
837     } else {
838       /* Parse the typed nickname. */
839       if (!silc_parse_nickname(argv[2], &nickname, &serv, &num)) {
840         printformat_module("fe-common/silc", server, NULL,
841                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
842         return;
843       }
844       
845       /* Find client entry */
846       client_entry = silc_idlist_get_client(silc_client, conn, nickname, 
847                                             serv, num, TRUE);
848       if (!client_entry) {
849         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
850         inter->server = server;
851         inter->data = strdup(data);
852         inter->item = item;
853
854         /* Client entry not found, it was requested thus mark this to be
855            pending command. */
856         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
857                                     conn->cmd_ident, 
858                                     NULL, silc_client_command_key_get_clients, 
859                                     inter);
860         goto out;
861       }
862     }
863   }
864
865   if (type == 2) {
866     /* Get channel entry */
867     char *name;
868
869     if (argv[2][0] == '*') {
870       if (!conn->current_channel) {
871         if (nickname)
872           silc_free(nickname);
873         if (serv)
874           silc_free(serv);
875         cmd_return_error(CMDERR_NOT_JOINED);
876       }
877       name = conn->current_channel->channel_name;
878     } else {
879       name = argv[2];
880     }
881
882     channel_entry = silc_client_get_channel(silc_client, conn, name);
883     if (!channel_entry) {
884       if (nickname)
885         silc_free(nickname);
886       if (serv)
887         silc_free(serv);
888       cmd_return_error(CMDERR_NOT_JOINED);
889     }
890   }
891
892   /* Set command */
893   if (!strcasecmp(argv[3], "set")) {
894     command = 1;
895
896     if (argc >= 5) {
897       if (type == 1 && client_entry) {
898         /* Set private message key */
899         
900         silc_client_del_private_message_key(silc_client, conn, client_entry);
901
902         if (argc >= 6)
903           silc_client_add_private_message_key(silc_client, conn, client_entry,
904                                               argv[5], argv[4],
905                                               argv_lens[4],
906                                               (argv[4][0] == '*' ?
907                                                TRUE : FALSE), FALSE);
908         else
909           silc_client_add_private_message_key(silc_client, conn, client_entry,
910                                               NULL, argv[4],
911                                               argv_lens[4],
912                                               (argv[4][0] == '*' ?
913                                                TRUE : FALSE), FALSE);
914
915         /* Send the key to the remote client so that it starts using it
916            too. */
917         silc_client_send_private_message_key(silc_client, conn, 
918                                              client_entry, TRUE);
919       } else if (type == 2) {
920         /* Set private channel key */
921         char *cipher = NULL, *hmac = NULL;
922
923         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
924           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
925                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
926                              channel_entry->channel_name);
927           goto out;
928         }
929
930         if (argc >= 6)
931           cipher = argv[5];
932         if (argc >= 7)
933           hmac = argv[6];
934
935         if (!silc_client_add_channel_private_key(silc_client, conn, 
936                                                  channel_entry,
937                                                  cipher, hmac,
938                                                  argv[4],
939                                                  argv_lens[4])) {
940           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
941                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
942                              channel_entry->channel_name);
943           goto out;
944         }
945
946         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
947                            SILCTXT_CH_PRIVATE_KEY_ADD, 
948                            channel_entry->channel_name);
949       }
950     }
951
952     goto out;
953   }
954   
955   /* Unset command */
956   if (!strcasecmp(argv[3], "unset")) {
957     command = 2;
958
959     if (type == 1 && client_entry) {
960       /* Unset private message key */
961       silc_client_del_private_message_key(silc_client, conn, client_entry);
962     } else if (type == 2) {
963       /* Unset channel key(s) */
964       SilcChannelPrivateKey *keys;
965       uint32 keys_count;
966       int number;
967
968       if (argc == 4)
969         silc_client_del_channel_private_keys(silc_client, conn, 
970                                              channel_entry);
971
972       if (argc > 4) {
973         number = atoi(argv[4]);
974         keys = silc_client_list_channel_private_keys(silc_client, conn, 
975                                                      channel_entry,
976                                                      &keys_count);
977         if (!keys)
978           goto out;
979
980         if (!number || number > keys_count) {
981           silc_client_free_channel_private_keys(keys, keys_count);
982           goto out;
983         }
984
985         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
986                                             keys[number - 1]);
987         silc_client_free_channel_private_keys(keys, keys_count);
988       }
989
990       goto out;
991     }
992   }
993
994   /* List command */
995   if (!strcasecmp(argv[3], "list")) {
996     command = 3;
997
998     if (type == 1) {
999       SilcPrivateMessageKeys keys;
1000       uint32 keys_count;
1001       int k, i, len;
1002       char buf[1024];
1003
1004       keys = silc_client_list_private_message_keys(silc_client, conn, 
1005                                                    &keys_count);
1006       if (!keys)
1007         goto out;
1008
1009       /* list the private message key(s) */
1010       if (nickname[0] == '*') {
1011         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1012                            SILCTXT_PRIVATE_KEY_LIST);
1013         for (k = 0; k < keys_count; k++) {
1014           memset(buf, 0, sizeof(buf));
1015           strncat(buf, "  ", 2);
1016           len = strlen(keys[k].client_entry->nickname);
1017           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1018           if (len < 30)
1019             for (i = 0; i < 30 - len; i++)
1020               strcat(buf, " ");
1021           strcat(buf, " ");
1022           
1023           len = strlen(keys[k].cipher);
1024           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1025           if (len < 14)
1026             for (i = 0; i < 14 - len; i++)
1027               strcat(buf, " ");
1028           strcat(buf, " ");
1029
1030           if (keys[k].key)
1031             strcat(buf, "<hidden>");
1032           else
1033             strcat(buf, "*generated*");
1034
1035           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1036         }
1037       } else {
1038         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1039                            SILCTXT_PRIVATE_KEY_LIST_NICK,
1040                            client_entry->nickname);
1041         for (k = 0; k < keys_count; k++) {
1042           if (keys[k].client_entry != client_entry)
1043             continue;
1044
1045           memset(buf, 0, sizeof(buf));
1046           strncat(buf, "  ", 2);
1047           len = strlen(keys[k].client_entry->nickname);
1048           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1049           if (len < 30)
1050             for (i = 0; i < 30 - len; i++)
1051               strcat(buf, " ");
1052           strcat(buf, " ");
1053           
1054           len = strlen(keys[k].cipher);
1055           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1056           if (len < 14)
1057             for (i = 0; i < 14 - len; i++)
1058               strcat(buf, " ");
1059           strcat(buf, " ");
1060
1061           if (keys[k].key)
1062             strcat(buf, "<hidden>");
1063           else
1064             strcat(buf, "*generated*");
1065
1066           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1067         }
1068       }
1069
1070       silc_client_free_private_message_keys(keys, keys_count);
1071     } else if (type == 2) {
1072       SilcChannelPrivateKey *keys;
1073       uint32 keys_count;
1074       int k, i, len;
1075       char buf[1024];
1076
1077       keys = silc_client_list_channel_private_keys(silc_client, conn, 
1078                                                    channel_entry,
1079                                                    &keys_count);
1080       if (!keys)
1081         goto out;
1082       
1083       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1084                          SILCTXT_CH_PRIVATE_KEY_LIST,
1085                          channel_entry->channel_name);
1086       for (k = 0; k < keys_count; k++) {
1087         memset(buf, 0, sizeof(buf));
1088         strncat(buf, "  ", 2);
1089
1090         len = strlen(keys[k]->cipher->cipher->name);
1091         strncat(buf, keys[k]->cipher->cipher->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         len = strlen(keys[k]->hmac->hmac->name);
1098         strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
1099         if (len < 16)
1100           for (i = 0; i < 16 - len; i++)
1101             strcat(buf, " ");
1102         strcat(buf, " ");
1103         
1104         strcat(buf, "<hidden>");
1105
1106         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1107       }
1108       
1109       silc_client_free_channel_private_keys(keys, keys_count);
1110     }
1111
1112     goto out;
1113   }
1114
1115   /* Send command is used to send key agreement */
1116   if (!strcasecmp(argv[3], "agreement")) {
1117     command = 4;
1118
1119     if (argc >= 5)
1120       hostname = argv[4];
1121     if (argc >= 6)
1122       port = atoi(argv[5]);
1123
1124     internal = silc_calloc(1, sizeof(*internal));
1125     internal->type = type;
1126     internal->server = server;
1127   }
1128
1129   /* Start command is used to start key agreement (after receiving the
1130      key_agreement client operation). */
1131   if (!strcasecmp(argv[3], "negotiate")) {
1132     command = 5;
1133
1134     if (argc >= 5)
1135       hostname = argv[4];
1136     if (argc >= 6)
1137       port = atoi(argv[5]);
1138
1139     internal = silc_calloc(1, sizeof(*internal));
1140     internal->type = type;
1141     internal->server = server;
1142   }
1143
1144   if (command == 0) {
1145     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
1146              "Usage: /KEY msg|channel <nickname|channel> "
1147              "set|unset|agreement|negotiate [<arguments>]");
1148     goto out;
1149   }
1150
1151   if (command == 4 && client_entry) {
1152     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1153                        SILCTXT_KEY_AGREEMENT, argv[2]);
1154     internal->responder = TRUE;
1155     silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
1156                                    port, 120, keyagr_completion, internal);
1157     if (!hostname)
1158       silc_free(internal);
1159     goto out;
1160   }
1161
1162   if (command == 5 && client_entry && hostname) {
1163     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_NOTICES,
1164                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1165     internal->responder = FALSE;
1166     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1167                                       hostname, port, keyagr_completion, 
1168                                       internal);
1169     goto out;
1170   }
1171
1172  out:
1173   if (nickname)
1174     silc_free(nickname);
1175   if (serv)
1176     silc_free(serv);
1177 }
1178
1179 /* Lists locally saved client and server public keys. */
1180
1181 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1182                              WI_ITEM_REC *item)
1183 {
1184
1185 }
1186
1187 void silc_channels_init(void)
1188 {
1189   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1190   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1191   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1192
1193   signal_add("silc event join", (SIGNAL_FUNC) event_join);
1194   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
1195   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
1196   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
1197   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
1198   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
1199   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
1200   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
1201   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
1202   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
1203   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
1204   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
1205   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
1206   
1207   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1208   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1209   command_bind("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1210   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1211   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1212   command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1213   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1214
1215   silc_nicklist_init();
1216 }
1217
1218 void silc_channels_deinit(void)
1219 {
1220   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1221   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1222   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1223
1224   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
1225   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
1226   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
1227   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
1228   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
1229   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
1230   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
1231   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
1232   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
1233   signal_remove("silc event channel_change", 
1234                 (SIGNAL_FUNC) event_channel_change);
1235   signal_remove("silc event server_signoff", 
1236                 (SIGNAL_FUNC) event_server_signoff);
1237   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
1238   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
1239   
1240   command_unbind("part", (SIGNAL_FUNC) command_part);
1241   command_unbind("me", (SIGNAL_FUNC) command_me);
1242   command_unbind("action", (SIGNAL_FUNC) command_action);
1243   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1244   command_unbind("away", (SIGNAL_FUNC) command_away);
1245   command_unbind("key", (SIGNAL_FUNC) command_key);
1246   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1247
1248   silc_nicklist_deinit();
1249 }