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