updates updates..
[silc.git] / apps / silc / local_command.c
1 /*
2
3   local_command.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "clientincludes.h"
23 #include "client_internal.h"
24
25 /* Local commands. */
26 SilcClientCommand silc_local_command_list[] =
27 {
28   SILC_CLIENT_LCMD(help, HELP, "HELP", 0, 2),
29   SILC_CLIENT_LCMD(clear, CLEAR, "CLEAR", 0, 1),
30   SILC_CLIENT_LCMD(version, VERSION, "VERSION", 0, 1),
31   SILC_CLIENT_LCMD(server, SERVER, "SERVER", 0, 2),
32   SILC_CLIENT_LCMD(msg, MSG, "MSG", 0, 3),
33   SILC_CLIENT_LCMD(away, AWAY, "AWAY", 0, 2),
34   SILC_CLIENT_LCMD(key, KEY, "KEY", 0, 7),
35   SILC_CLIENT_LCMD(me, ME, "ME", 0, 3),
36   SILC_CLIENT_LCMD(notice, NOTICE, "NOTICE", 0, 3),
37
38   { NULL, 0, NULL, 0, 0 },
39 };
40
41 /* Finds and returns a pointer to the command list. Return NULL if the
42    command is not found. */
43
44 SilcClientCommand *silc_client_local_command_find(const char *name)
45 {
46   SilcClientCommand *cmd;
47
48   for (cmd = silc_local_command_list; cmd->name; cmd++) {
49     if (!strcmp(cmd->name, name))
50       return cmd;
51   }
52
53   return NULL;
54 }
55
56 /* HELP command. This is local command and shows help on SILC */
57
58 SILC_CLIENT_LCMD_FUNC(help)
59 {
60
61 }
62
63 /* CLEAR command. This is local command and clears current output window */
64
65 SILC_CLIENT_LCMD_FUNC(clear)
66 {
67   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
68
69   silc_client_command_free(cmd);
70 }
71
72 /* VERSION command. This is local command and shows version of the client */
73
74 SILC_CLIENT_LCMD_FUNC(version)
75 {
76   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
77   SilcClient client = cmd->client;
78   extern char *silc_version;
79   extern char *silc_name;
80   extern char *silc_fullname;
81
82   silc_say(client, cmd->conn,
83            "%s (%s) version %s", silc_name, silc_fullname,
84            silc_version);
85
86   silc_client_command_free(cmd);
87 }
88
89 /* Command MSG. Sends private message to user or list of users. Note that
90    private messages are not really commands, they are message packets,
91    however, on user interface it is convenient to show them as commands
92    as that is the common way of sending private messages (like in IRC). */
93 /* XXX supports only one destination */
94
95 SILC_CLIENT_LCMD_FUNC(msg)
96 {
97   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
98   SilcClientConnection conn = cmd->conn;
99   SilcClient client = cmd->client;
100   SilcClientEntry client_entry = NULL;
101   unsigned int num = 0;
102   char *nickname = NULL, *server = NULL;
103
104   if (!cmd->conn) {
105     silc_say(client, conn,
106              "You are not connected to a server, use /SERVER to connect");
107     goto out;
108   }
109
110   if (cmd->argc < 3) {
111     silc_say(client, conn, "Usage: /MSG <nickname> <message>");
112     goto out;
113   }
114
115   /* Parse the typed nickname. */
116   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
117     silc_say(client, conn, "Bad nickname");
118     goto out;
119   }
120
121   /* Find client entry */
122   client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
123                                         TRUE);
124   if (!client_entry) {
125     /* Client entry not found, it was requested thus mark this to be
126        pending command. */
127     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident, 
128                                 NULL, silc_client_local_command_msg, context);
129     return;
130   }
131
132   /* Display the message for our eyes. */
133   silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
134
135   /* Send the private message */
136   silc_client_send_private_message(client, conn, client_entry, 0,
137                                    cmd->argv[2], cmd->argv_lens[2],
138                                    TRUE);
139
140  out:
141   silc_client_command_free(cmd);
142 }
143
144
145 /* Command SERVER. Connects to remote SILC server. This is local command. */
146
147 SILC_CLIENT_LCMD_FUNC(server)
148 {
149   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
150   SilcClient client = cmd->client;
151   SilcClientConnection conn = cmd->conn;
152   int i = 0, len, port;
153   char *hostname;
154
155   if (cmd->argc < 2) {
156     /* Show current servers */
157
158     if (!cmd->conn) {
159       silc_say(client, conn, "You are not connected to any server");
160       silc_say(client, conn, "Usage: /SERVER [<server>[:<port>]]");
161       goto out;
162     }
163
164     silc_say(client, conn, "Current server: %s on %d %s", 
165              conn->remote_host, conn->remote_port,
166              conn->remote_info ? conn->remote_info : "");
167     
168     silc_say(client, conn, "Server list:");
169     for (i = 0; i < client->conns_count; i++) {
170       silc_say(client, conn, " [%d] %s on %d %s", i + 1,
171                client->conns[i]->remote_host, 
172                client->conns[i]->remote_port,
173                client->conns[i]->remote_info ? 
174                client->conns[i]->remote_info : "");
175     }
176
177     goto out;
178   }
179
180   /* See if port is included and then extract it */
181   if (strchr(cmd->argv[1], ':')) {
182     len = strcspn(cmd->argv[1], ":");
183     hostname = silc_calloc(len + 1, sizeof(char));
184     memcpy(hostname, cmd->argv[1], len);
185     port = atoi(cmd->argv[1] + 1 + len);
186   } else {
187     hostname = cmd->argv[1];
188     port = 706;
189   }
190
191 #if 0
192   if (conn && conn->remote_host) {
193     if (!strcmp(hostname, conn->remote_host) && port == conn->remote_port) {
194       silc_say(client, conn, "You are already connected to that server");
195       goto out;
196     }
197
198     /* Close connection */
199     cmd->client->ops->disconnect(cmd->client, cmd->conn);
200     silc_client_close_connection(cmd->client, cmd->conn->sock);
201   }
202 #endif
203
204   /* Connect asynchronously to not to block user interface */
205   silc_client_connect_to_server(cmd->client, port, hostname, NULL);
206
207  out:
208   silc_client_command_free(cmd);
209 }
210
211 /* Local command AWAY. Client replies with away message to whomever sends
212    private message to the client if the away message is set. If this is
213    given without arguments the away message is removed. */
214
215 SILC_CLIENT_LCMD_FUNC(away)
216 {
217   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
218   SilcClientConnection conn = cmd->conn;
219   SilcClient client = cmd->client;
220   SilcClientInternal app = (SilcClientInternal)client->application;
221
222   if (!cmd->conn) {
223     silc_say(client, conn,
224              "You are not connected to a server, use /SERVER to connect");
225     goto out;
226   }
227
228   if (cmd->argc == 1) {
229     if (conn->away) {
230       silc_free(conn->away->away);
231       silc_free(conn->away);
232       conn->away = NULL;
233       app->screen->bottom_line->away = FALSE;
234
235       silc_say(client, conn, "Away message removed");
236       silc_screen_print_bottom_line(app->screen, 0);
237     }
238   } else {
239
240     if (conn->away)
241       silc_free(conn->away->away);
242     else
243       conn->away = silc_calloc(1, sizeof(*conn->away));
244     
245     app->screen->bottom_line->away = TRUE;
246     conn->away->away = strdup(cmd->argv[1]);
247
248     silc_say(client, conn, "Away message set: %s", conn->away->away);
249     silc_screen_print_bottom_line(app->screen, 0);
250   }
251
252  out:
253   silc_client_command_free(cmd);
254 }
255
256 typedef struct {
257   int type;                     /* 1 = msg, 2 = channel */
258 } *KeyInternal;
259
260 static SilcSKEKeyMaterial *curr_key = NULL;
261
262 /* Key agreement callback that is called after the key agreement protocol
263    has been performed. This is called also if error occured during the
264    key agreement protocol. The `key' is the allocated key material and
265    the caller is responsible of freeing it. The `key' is NULL if error
266    has occured. The application can freely use the `key' to whatever
267    purpose it needs. See lib/silcske/silcske.h for the definition of
268    the SilcSKEKeyMaterial structure. */
269
270 static void keyagr_completion(SilcClient client,
271                               SilcClientConnection conn,
272                               SilcClientEntry client_entry,
273                               SilcKeyAgreementStatus status,
274                               SilcSKEKeyMaterial *key,
275                               void *context)
276 {
277   KeyInternal i = (KeyInternal)context;
278
279   curr_key = NULL;
280
281   switch(status) {
282   case SILC_KEY_AGREEMENT_OK:
283     silc_say(client, conn, "Key agreement compeleted successfully with %s",
284              client_entry->nickname);;
285
286     if (i->type == 1) {
287       if (!silc_client_ask_yes_no(client, 
288          "Would you like to use the key with private messages (y/n)? ")) {
289         silc_say(client, conn, "You can set the key material into use later by giving /KEY msg set command");
290         curr_key = key;
291         break;
292       }
293       
294       /* Set the private key for this client */
295       silc_client_del_private_message_key(client, conn, client_entry);
296       silc_client_add_private_message_key_ske(client, conn, client_entry,
297                                               NULL, key);
298       silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
299       silc_ske_free_key_material(key);
300     }
301     
302     break;
303     
304   case SILC_KEY_AGREEMENT_ERROR:
305     silc_say(client, conn, "Error occured during key agreement with %s",
306              client_entry->nickname);
307     break;
308     
309   case SILC_KEY_AGREEMENT_FAILURE:
310     silc_say(client, conn, "The key agreement failed with %s",
311              client_entry->nickname);
312     break;
313     
314   case SILC_KEY_AGREEMENT_TIMEOUT:
315     silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
316              client_entry->nickname);
317     break;
318     
319   default:
320     break;
321   } 
322
323   if (i)
324     silc_free(i);
325 }
326
327 /* Local command KEY. This command is used to set and unset private
328    keys for channels, set and unset private keys for private messages
329    with remote clients and to send key agreement requests and
330    negotiate the key agreement protocol with remote client.  The
331    key agreement is supported only to negotiate private message keys,
332    it currently cannot be used to negotiate private keys for channels,
333    as it is not convenient for that purpose. */
334
335 SILC_CLIENT_LCMD_FUNC(key)
336 {
337   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
338   SilcClientConnection conn = cmd->conn;
339   SilcClient client = cmd->client;
340   SilcClientEntry client_entry = NULL;
341   SilcChannelEntry channel_entry = NULL;
342   unsigned int num = 0;
343   char *nickname = NULL, *server = NULL;
344   int command = 0, port = 0, type = 0;
345   char *hostname = NULL;
346   KeyInternal internal = NULL;
347
348   if (!cmd->conn) {
349     silc_say(client, conn,
350              "You are not connected to a server, use /SERVER to connect");
351     goto out;
352   }
353
354   if (cmd->argc < 4) {
355     silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
356              "set|unset|agreement|negotiate [<arguments>]");
357     goto out;
358   }
359
360   /* Get type */
361   if (!strcasecmp(cmd->argv[1], "msg"))
362     type = 1;
363   if (!strcasecmp(cmd->argv[1], "channel"))
364     type = 2;
365
366   if (type == 0) {
367     silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
368              "set|unset|agreement|negotiate [<arguments>]");
369     goto out;
370   }
371
372   if (type == 1) {
373     if (cmd->argv[2][0] == '*') {
374       nickname = "*";
375     } else {
376       /* Parse the typed nickname. */
377       if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
378         silc_say(client, conn, "Bad nickname");
379         goto out;
380       }
381       
382       /* Find client entry */
383       client_entry = silc_idlist_get_client(client, conn, nickname, 
384                                             server, num, TRUE);
385       if (!client_entry) {
386         /* Client entry not found, it was requested thus mark this to be
387            pending command. */
388         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
389                                     conn->cmd_ident, 
390                                     NULL, silc_client_local_command_key, 
391                                     context);
392         return;
393       }
394     }
395   }
396
397   if (type == 2) {
398     /* Get channel entry */
399     char *name;
400
401     if (cmd->argv[2][0] == '*') {
402       if (!conn->current_channel) {
403         cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
404         goto out;
405       }
406       name = conn->current_channel->channel_name;
407     } else {
408       name = cmd->argv[2];
409     }
410
411     channel_entry = silc_client_get_channel(client, conn, name);
412     if (!channel_entry) {
413       silc_say(client, conn, "You are not on that channel");
414       goto out;
415     }
416   }
417
418   /* Set command */
419   if (!strcasecmp(cmd->argv[3], "set")) {
420     command = 1;
421
422     if (cmd->argc == 4) {
423       if (curr_key && type == 1 && client_entry) {
424         silc_client_del_private_message_key(client, conn, client_entry);
425         silc_client_add_private_message_key_ske(client, conn, client_entry,
426                                                 NULL, curr_key);
427         goto out;
428       }
429     }
430
431     if (cmd->argc >= 5) {
432       if (type == 1 && client_entry) {
433         /* Set private message key */
434         
435         silc_client_del_private_message_key(client, conn, client_entry);
436
437         if (cmd->argc >= 6)
438           silc_client_add_private_message_key(client, conn, client_entry,
439                                               cmd->argv[5], cmd->argv[4],
440                                               cmd->argv_lens[4],
441                                               (cmd->argv[4][0] == '*' ?
442                                                TRUE : FALSE));
443         else
444           silc_client_add_private_message_key(client, conn, client_entry,
445                                               NULL, cmd->argv[4],
446                                               cmd->argv_lens[4],
447                                               (cmd->argv[4][0] == '*' ?
448                                                TRUE : FALSE));
449
450         /* Send the key to the remote client so that it starts using it
451            too. */
452         silc_client_send_private_message_key(client, conn, client_entry, TRUE);
453       } else if (type == 2) {
454         /* Set private channel key */
455         char *cipher = NULL, *hmac = NULL;
456
457         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
458           silc_say(client, conn, 
459                    "Private key mode is not set on this channel");
460           goto out;
461         }
462
463         if (cmd->argc >= 6)
464           cipher = cmd->argv[5];
465         if (cmd->argc >= 7)
466           hmac = cmd->argv[6];
467
468         if (!silc_client_add_channel_private_key(client, conn, channel_entry,
469                                                  cipher, hmac,
470                                                  cmd->argv[4],
471                                                  cmd->argv_lens[4])) {
472           silc_say(client, conn, "Could not add channel private key");
473           goto out;
474         }
475       }
476     }
477
478     goto out;
479   }
480   
481   /* Unset command */
482   if (!strcasecmp(cmd->argv[3], "unset")) {
483     command = 2;
484
485     if (type == 1 && client_entry) {
486       /* Unset private message key */
487       silc_client_del_private_message_key(client, conn, client_entry);
488     } else if (type == 2) {
489       /* Unset channel key(s) */
490       SilcChannelPrivateKey *keys;
491       unsigned int keys_count;
492       int number;
493
494       if (cmd->argc == 4)
495         silc_client_del_channel_private_keys(client, conn, channel_entry);
496
497       if (cmd->argc > 4) {
498         number = atoi(cmd->argv[4]);
499         keys = silc_client_list_channel_private_keys(client, conn, 
500                                                      channel_entry,
501                                                      &keys_count);
502         if (!keys)
503           goto out;
504
505         if (!number || number > keys_count) {
506           silc_client_free_channel_private_keys(keys, keys_count);
507           goto out;
508         }
509
510         silc_client_del_channel_private_key(client, conn, channel_entry,
511                                             keys[number - 1]);
512         silc_client_free_channel_private_keys(keys, keys_count);
513       }
514
515       goto out;
516     }
517   }
518
519   /* List command */
520   if (!strcasecmp(cmd->argv[3], "list")) {
521     command = 3;
522
523     if (type == 1) {
524       SilcPrivateMessageKeys keys;
525       unsigned int keys_count;
526       int k, i, len;
527       char buf[1024];
528
529       keys = silc_client_list_private_message_keys(client, conn, 
530                                                    &keys_count);
531       if (!keys)
532         goto out;
533
534       /* list the private message key(s) */
535       if (nickname[0] == '*') {
536         silc_say(client, conn, "Private message keys");
537         silc_say(client, conn, 
538                  "  Client                         Cipher         Key");
539         for (k = 0; k < keys_count; k++) {
540           memset(buf, 0, sizeof(buf));
541           strncat(buf, "  ", 2);
542           len = strlen(keys[k].client_entry->nickname);
543           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
544           if (len < 30)
545             for (i = 0; i < 30 - len; i++)
546               strcat(buf, " ");
547           strcat(buf, " ");
548           
549           len = strlen(keys[k].cipher);
550           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
551           if (len < 14)
552             for (i = 0; i < 14 - len; i++)
553               strcat(buf, " ");
554           strcat(buf, " ");
555
556           if (keys[k].key)
557             strcat(buf, "<hidden>");
558           else
559             strcat(buf, "*generated*");
560
561           silc_say(client, conn, "%s", buf);
562         }
563       } else {
564         silc_say(client, conn, "Private message key", 
565                  client_entry->nickname);
566         silc_say(client, conn, 
567                  "  Client                         Cipher         Key");
568         for (k = 0; k < keys_count; k++) {
569           if (keys[k].client_entry != client_entry)
570             continue;
571
572           memset(buf, 0, sizeof(buf));
573           strncat(buf, "  ", 2);
574           len = strlen(keys[k].client_entry->nickname);
575           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
576           if (len < 30)
577             for (i = 0; i < 30 - len; i++)
578               strcat(buf, " ");
579           strcat(buf, " ");
580           
581           len = strlen(keys[k].cipher);
582           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
583           if (len < 14)
584             for (i = 0; i < 14 - len; i++)
585               strcat(buf, " ");
586           strcat(buf, " ");
587
588           if (keys[k].key)
589             strcat(buf, "<hidden>");
590           else
591             strcat(buf, "*generated*");
592
593           silc_say(client, conn, "%s", buf);
594         }
595       }
596
597       silc_client_free_private_message_keys(keys, keys_count);
598     } else if (type == 2) {
599       SilcChannelPrivateKey *keys;
600       unsigned int keys_count;
601       int k, i, len;
602       char buf[1024];
603
604       keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
605                                                    &keys_count);
606       if (!keys)
607         goto out;
608
609       silc_say(client, conn, "Channel %s private keys", 
610                channel_entry->channel_name);
611       silc_say(client, conn, 
612                "  Cipher           Hmac             Key");
613       for (k = 0; k < keys_count; k++) {
614         memset(buf, 0, sizeof(buf));
615         strncat(buf, "  ", 2);
616
617         len = strlen(keys[k]->cipher->cipher->name);
618         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
619         if (len < 16)
620           for (i = 0; i < 16 - len; i++)
621             strcat(buf, " ");
622         strcat(buf, " ");
623         
624         len = strlen(keys[k]->hmac->hmac->name);
625         strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
626         if (len < 16)
627           for (i = 0; i < 16 - len; i++)
628             strcat(buf, " ");
629         strcat(buf, " ");
630         
631         strcat(buf, "<hidden>");
632
633         silc_say(client, conn, "%s", buf);
634       }
635       
636       silc_client_free_channel_private_keys(keys, keys_count);
637     }
638
639     goto out;
640   }
641
642   /* Send command is used to send key agreement */
643   if (!strcasecmp(cmd->argv[3], "agreement")) {
644     command = 4;
645
646     if (cmd->argc >= 5)
647       hostname = cmd->argv[4];
648     if (cmd->argc >= 6)
649       port = atoi(cmd->argv[5]);
650
651     internal = silc_calloc(1, sizeof(*internal));
652     internal->type = type;
653   }
654
655   /* Start command is used to start key agreement (after receiving the
656      key_agreement client operation). */
657   if (!strcasecmp(cmd->argv[3], "negotiate")) {
658     command = 5;
659
660     if (cmd->argc >= 5)
661       hostname = cmd->argv[4];
662     if (cmd->argc >= 6)
663       port = atoi(cmd->argv[5]);
664
665     internal = silc_calloc(1, sizeof(*internal));
666     internal->type = type;
667   }
668
669   if (command == 0) {
670     silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
671              "set|unset|agreement|negotiate [<arguments>]");
672     goto out;
673   }
674
675   if (command == 4 && client_entry) {
676     silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
677     silc_client_send_key_agreement(client, conn, client_entry, hostname, 
678                                    port, 120, keyagr_completion, internal);
679     goto out;
680   }
681
682   if (command == 5 && client_entry) {
683     silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
684     silc_client_perform_key_agreement(client, conn, client_entry, hostname, 
685                                       port, keyagr_completion, internal);
686     goto out;
687   }
688
689  out:
690   if (nickname)
691     silc_free(nickname);
692   if (server)
693     silc_free(server);
694   silc_client_command_free(cmd);
695 }
696
697 /* Sends an action to the channel.  Equals CTCP's ACTION (IRC's /ME) 
698    command. */
699
700 SILC_CLIENT_LCMD_FUNC(me)
701 {
702   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
703   SilcClientConnection conn = cmd->conn;
704   SilcClient client = cmd->client;
705   SilcChannelEntry channel_entry;
706   char *name;
707
708   if (!cmd->conn) {
709     silc_say(client, conn,
710              "You are not connected to a server, use /SERVER to connect");
711     goto out;
712   }
713
714   if (cmd->argc < 3) {
715     silc_say(client, conn, "Usage: /ME <channel> <action message>");
716     goto out;
717   }
718
719   if (cmd->argv[1][0] == '*') {
720     if (!conn->current_channel) {
721       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
722       goto out;
723     }
724     name = conn->current_channel->channel_name;
725   } else {
726     name = cmd->argv[1];
727   }
728
729   channel_entry = silc_client_get_channel(client, conn, name);
730   if (!channel_entry) {
731     silc_say(client, conn, "You are not on that channel");
732     goto out;
733   }
734
735   /* Send the action message */
736   silc_client_send_channel_message(client, conn, channel_entry, NULL,
737                                    SILC_MESSAGE_FLAG_ACTION, 
738                                    cmd->argv[2], cmd->argv_lens[2], TRUE);
739
740   silc_print(client, "* %s %s", conn->nickname, cmd->argv[2]);
741
742  out:
743   silc_client_command_free(cmd);
744 }
745
746 /* Sends an notice to the channel.  */
747
748 SILC_CLIENT_LCMD_FUNC(notice)
749 {
750   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
751   SilcClientConnection conn = cmd->conn;
752   SilcClient client = cmd->client;
753   SilcChannelEntry channel_entry;
754   char *name;
755
756   if (!cmd->conn) {
757     silc_say(client, conn,
758              "You are not connected to a server, use /SERVER to connect");
759     goto out;
760   }
761
762   if (cmd->argc < 3) {
763     silc_say(client, conn, "Usage: /NOTICE <channel> <message>");
764     goto out;
765   }
766
767   if (cmd->argv[1][0] == '*') {
768     if (!conn->current_channel) {
769       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
770       goto out;
771     }
772     name = conn->current_channel->channel_name;
773   } else {
774     name = cmd->argv[1];
775   }
776
777   channel_entry = silc_client_get_channel(client, conn, name);
778   if (!channel_entry) {
779     silc_say(client, conn, "You are not on that channel");
780     goto out;
781   }
782
783   /* Send the action message */
784   silc_client_send_channel_message(client, conn, channel_entry, NULL,
785                                    SILC_MESSAGE_FLAG_NOTICE, 
786                                    cmd->argv[2], cmd->argv_lens[2], TRUE);
787
788   silc_print(client, "- %s %s", conn->nickname, cmd->argv[2]);
789
790  out:
791   silc_client_command_free(cmd);
792 }