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   uint32 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   unsigned char modebuf[4];
222   SilcBuffer idp, buffer;
223
224   if (!cmd->conn) {
225     silc_say(client, conn,
226              "You are not connected to a server, use /SERVER to connect");
227     goto out;
228   }
229
230   if (cmd->argc == 1) {
231     conn->local_entry->mode &= ~SILC_UMODE_GONE;
232
233     if (conn->away) {
234       silc_free(conn->away->away);
235       silc_free(conn->away);
236       conn->away = NULL;
237       app->screen->bottom_line->away = FALSE;
238
239       silc_say(client, conn, "Away message removed");
240       silc_screen_print_bottom_line(app->screen, 0);
241     }
242   } else {
243     conn->local_entry->mode |= SILC_UMODE_GONE;
244   
245     if (conn->away)
246       silc_free(conn->away->away);
247     else
248       conn->away = silc_calloc(1, sizeof(*conn->away));
249     
250     app->screen->bottom_line->away = TRUE;
251     conn->away->away = strdup(cmd->argv[1]);
252
253     silc_say(client, conn, "Away message set: %s", conn->away->away);
254     silc_screen_print_bottom_line(app->screen, 0);
255   }
256
257   /* Send the UMODE command to se myself as gone */
258   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
259   SILC_PUT32_MSB(conn->local_entry->mode, modebuf);
260   buffer = silc_command_payload_encode_va(SILC_COMMAND_UMODE, 
261                                           ++conn->cmd_ident, 2, 
262                                           1, idp->data, idp->len, 
263                                           2, modebuf, sizeof(modebuf));
264   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
265                           NULL, 0, NULL, NULL, buffer->data, 
266                           buffer->len, TRUE);
267   silc_buffer_free(buffer);
268   silc_buffer_free(idp);
269
270  out:
271   silc_client_command_free(cmd);
272 }
273
274 typedef struct {
275   int type;                     /* 1 = msg, 2 = channel */
276 } *KeyInternal;
277
278 static SilcSKEKeyMaterial *curr_key = NULL;
279
280 /* Key agreement callback that is called after the key agreement protocol
281    has been performed. This is called also if error occured during the
282    key agreement protocol. The `key' is the allocated key material and
283    the caller is responsible of freeing it. The `key' is NULL if error
284    has occured. The application can freely use the `key' to whatever
285    purpose it needs. See lib/silcske/silcske.h for the definition of
286    the SilcSKEKeyMaterial structure. */
287
288 static void keyagr_completion(SilcClient client,
289                               SilcClientConnection conn,
290                               SilcClientEntry client_entry,
291                               SilcKeyAgreementStatus status,
292                               SilcSKEKeyMaterial *key,
293                               void *context)
294 {
295   KeyInternal i = (KeyInternal)context;
296
297   curr_key = NULL;
298
299   switch(status) {
300   case SILC_KEY_AGREEMENT_OK:
301     silc_say(client, conn, "Key agreement compeleted successfully with %s",
302              client_entry->nickname);;
303
304     if (i->type == 1) {
305       /* Set the private key for this client */
306       silc_client_del_private_message_key(client, conn, client_entry);
307       silc_client_add_private_message_key_ske(client, conn, client_entry,
308                                               NULL, key);
309       silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
310       silc_ske_free_key_material(key);
311     }
312     
313     break;
314     
315   case SILC_KEY_AGREEMENT_ERROR:
316     silc_say(client, conn, "Error occured during key agreement with %s",
317              client_entry->nickname);
318     break;
319     
320   case SILC_KEY_AGREEMENT_FAILURE:
321     silc_say(client, conn, "The key agreement failed with %s",
322              client_entry->nickname);
323     break;
324     
325   case SILC_KEY_AGREEMENT_TIMEOUT:
326     silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
327              client_entry->nickname);
328     break;
329     
330   default:
331     break;
332   } 
333
334   if (i)
335     silc_free(i);
336 }
337
338 /* Local command KEY. This command is used to set and unset private
339    keys for channels, set and unset private keys for private messages
340    with remote clients and to send key agreement requests and
341    negotiate the key agreement protocol with remote client.  The
342    key agreement is supported only to negotiate private message keys,
343    it currently cannot be used to negotiate private keys for channels,
344    as it is not convenient for that purpose. */
345
346 SILC_CLIENT_LCMD_FUNC(key)
347 {
348   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
349   SilcClientConnection conn = cmd->conn;
350   SilcClient client = cmd->client;
351   SilcClientEntry client_entry = NULL;
352   SilcChannelEntry channel_entry = NULL;
353   uint32 num = 0;
354   char *nickname = NULL, *server = NULL;
355   int command = 0, port = 0, type = 0;
356   char *hostname = NULL;
357   KeyInternal internal = NULL;
358
359   if (!cmd->conn) {
360     silc_say(client, conn,
361              "You are not connected to a server, use /SERVER to connect");
362     goto out;
363   }
364
365   if (cmd->argc < 4) {
366     silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
367              "set|unset|agreement|negotiate [<arguments>]");
368     goto out;
369   }
370
371   /* Get type */
372   if (!strcasecmp(cmd->argv[1], "msg"))
373     type = 1;
374   if (!strcasecmp(cmd->argv[1], "channel"))
375     type = 2;
376
377   if (type == 0) {
378     silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
379              "set|unset|agreement|negotiate [<arguments>]");
380     goto out;
381   }
382
383   if (type == 1) {
384     if (cmd->argv[2][0] == '*') {
385       nickname = "*";
386     } else {
387       /* Parse the typed nickname. */
388       if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
389         silc_say(client, conn, "Bad nickname");
390         goto out;
391       }
392       
393       /* Find client entry */
394       client_entry = silc_idlist_get_client(client, conn, nickname, 
395                                             server, num, TRUE);
396       if (!client_entry) {
397         /* Client entry not found, it was requested thus mark this to be
398            pending command. */
399         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
400                                     conn->cmd_ident, 
401                                     NULL, silc_client_local_command_key, 
402                                     context);
403         return;
404       }
405     }
406   }
407
408   if (type == 2) {
409     /* Get channel entry */
410     char *name;
411
412     if (cmd->argv[2][0] == '*') {
413       if (!conn->current_channel) {
414         cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
415         goto out;
416       }
417       name = conn->current_channel->channel_name;
418     } else {
419       name = cmd->argv[2];
420     }
421
422     channel_entry = silc_client_get_channel(client, conn, name);
423     if (!channel_entry) {
424       silc_say(client, conn, "You are not on that channel");
425       goto out;
426     }
427   }
428
429   /* Set command */
430   if (!strcasecmp(cmd->argv[3], "set")) {
431     command = 1;
432
433     if (cmd->argc == 4) {
434       if (curr_key && type == 1 && client_entry) {
435         silc_client_del_private_message_key(client, conn, client_entry);
436         silc_client_add_private_message_key_ske(client, conn, client_entry,
437                                                 NULL, curr_key);
438         goto out;
439       }
440     }
441
442     if (cmd->argc >= 5) {
443       if (type == 1 && client_entry) {
444         /* Set private message key */
445         
446         silc_client_del_private_message_key(client, conn, client_entry);
447
448         if (cmd->argc >= 6)
449           silc_client_add_private_message_key(client, conn, client_entry,
450                                               cmd->argv[5], cmd->argv[4],
451                                               cmd->argv_lens[4],
452                                               (cmd->argv[4][0] == '*' ?
453                                                TRUE : FALSE));
454         else
455           silc_client_add_private_message_key(client, conn, client_entry,
456                                               NULL, cmd->argv[4],
457                                               cmd->argv_lens[4],
458                                               (cmd->argv[4][0] == '*' ?
459                                                TRUE : FALSE));
460
461         /* Send the key to the remote client so that it starts using it
462            too. */
463         silc_client_send_private_message_key(client, conn, client_entry, TRUE);
464       } else if (type == 2) {
465         /* Set private channel key */
466         char *cipher = NULL, *hmac = NULL;
467
468         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
469           silc_say(client, conn, 
470                    "Private key mode is not set on this channel");
471           goto out;
472         }
473
474         if (cmd->argc >= 6)
475           cipher = cmd->argv[5];
476         if (cmd->argc >= 7)
477           hmac = cmd->argv[6];
478
479         if (!silc_client_add_channel_private_key(client, conn, channel_entry,
480                                                  cipher, hmac,
481                                                  cmd->argv[4],
482                                                  cmd->argv_lens[4])) {
483           silc_say(client, conn, "Could not add channel private key");
484           goto out;
485         }
486       }
487     }
488
489     goto out;
490   }
491   
492   /* Unset command */
493   if (!strcasecmp(cmd->argv[3], "unset")) {
494     command = 2;
495
496     if (type == 1 && client_entry) {
497       /* Unset private message key */
498       silc_client_del_private_message_key(client, conn, client_entry);
499     } else if (type == 2) {
500       /* Unset channel key(s) */
501       SilcChannelPrivateKey *keys;
502       uint32 keys_count;
503       int number;
504
505       if (cmd->argc == 4)
506         silc_client_del_channel_private_keys(client, conn, channel_entry);
507
508       if (cmd->argc > 4) {
509         number = atoi(cmd->argv[4]);
510         keys = silc_client_list_channel_private_keys(client, conn, 
511                                                      channel_entry,
512                                                      &keys_count);
513         if (!keys)
514           goto out;
515
516         if (!number || number > keys_count) {
517           silc_client_free_channel_private_keys(keys, keys_count);
518           goto out;
519         }
520
521         silc_client_del_channel_private_key(client, conn, channel_entry,
522                                             keys[number - 1]);
523         silc_client_free_channel_private_keys(keys, keys_count);
524       }
525
526       goto out;
527     }
528   }
529
530   /* List command */
531   if (!strcasecmp(cmd->argv[3], "list")) {
532     command = 3;
533
534     if (type == 1) {
535       SilcPrivateMessageKeys keys;
536       uint32 keys_count;
537       int k, i, len;
538       char buf[1024];
539
540       keys = silc_client_list_private_message_keys(client, conn, 
541                                                    &keys_count);
542       if (!keys)
543         goto out;
544
545       /* list the private message key(s) */
546       if (nickname[0] == '*') {
547         silc_say(client, conn, "Private message keys");
548         silc_say(client, conn, 
549                  "  Client                         Cipher         Key");
550         for (k = 0; k < keys_count; k++) {
551           memset(buf, 0, sizeof(buf));
552           strncat(buf, "  ", 2);
553           len = strlen(keys[k].client_entry->nickname);
554           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
555           if (len < 30)
556             for (i = 0; i < 30 - len; i++)
557               strcat(buf, " ");
558           strcat(buf, " ");
559           
560           len = strlen(keys[k].cipher);
561           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
562           if (len < 14)
563             for (i = 0; i < 14 - len; i++)
564               strcat(buf, " ");
565           strcat(buf, " ");
566
567           if (keys[k].key)
568             strcat(buf, "<hidden>");
569           else
570             strcat(buf, "*generated*");
571
572           silc_say(client, conn, "%s", buf);
573         }
574       } else {
575         silc_say(client, conn, "Private message key", 
576                  client_entry->nickname);
577         silc_say(client, conn, 
578                  "  Client                         Cipher         Key");
579         for (k = 0; k < keys_count; k++) {
580           if (keys[k].client_entry != client_entry)
581             continue;
582
583           memset(buf, 0, sizeof(buf));
584           strncat(buf, "  ", 2);
585           len = strlen(keys[k].client_entry->nickname);
586           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
587           if (len < 30)
588             for (i = 0; i < 30 - len; i++)
589               strcat(buf, " ");
590           strcat(buf, " ");
591           
592           len = strlen(keys[k].cipher);
593           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
594           if (len < 14)
595             for (i = 0; i < 14 - len; i++)
596               strcat(buf, " ");
597           strcat(buf, " ");
598
599           if (keys[k].key)
600             strcat(buf, "<hidden>");
601           else
602             strcat(buf, "*generated*");
603
604           silc_say(client, conn, "%s", buf);
605         }
606       }
607
608       silc_client_free_private_message_keys(keys, keys_count);
609     } else if (type == 2) {
610       SilcChannelPrivateKey *keys;
611       uint32 keys_count;
612       int k, i, len;
613       char buf[1024];
614
615       keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
616                                                    &keys_count);
617       if (!keys)
618         goto out;
619
620       silc_say(client, conn, "Channel %s private keys", 
621                channel_entry->channel_name);
622       silc_say(client, conn, 
623                "  Cipher           Hmac             Key");
624       for (k = 0; k < keys_count; k++) {
625         memset(buf, 0, sizeof(buf));
626         strncat(buf, "  ", 2);
627
628         len = strlen(keys[k]->cipher->cipher->name);
629         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
630         if (len < 16)
631           for (i = 0; i < 16 - len; i++)
632             strcat(buf, " ");
633         strcat(buf, " ");
634         
635         len = strlen(keys[k]->hmac->hmac->name);
636         strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
637         if (len < 16)
638           for (i = 0; i < 16 - len; i++)
639             strcat(buf, " ");
640         strcat(buf, " ");
641         
642         strcat(buf, "<hidden>");
643
644         silc_say(client, conn, "%s", buf);
645       }
646       
647       silc_client_free_channel_private_keys(keys, keys_count);
648     }
649
650     goto out;
651   }
652
653   /* Send command is used to send key agreement */
654   if (!strcasecmp(cmd->argv[3], "agreement")) {
655     command = 4;
656
657     if (cmd->argc >= 5)
658       hostname = cmd->argv[4];
659     if (cmd->argc >= 6)
660       port = atoi(cmd->argv[5]);
661
662     internal = silc_calloc(1, sizeof(*internal));
663     internal->type = type;
664   }
665
666   /* Start command is used to start key agreement (after receiving the
667      key_agreement client operation). */
668   if (!strcasecmp(cmd->argv[3], "negotiate")) {
669     command = 5;
670
671     if (cmd->argc >= 5)
672       hostname = cmd->argv[4];
673     if (cmd->argc >= 6)
674       port = atoi(cmd->argv[5]);
675
676     internal = silc_calloc(1, sizeof(*internal));
677     internal->type = type;
678   }
679
680   if (command == 0) {
681     silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
682              "set|unset|agreement|negotiate [<arguments>]");
683     goto out;
684   }
685
686   if (command == 4 && client_entry) {
687     silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
688     silc_client_send_key_agreement(client, conn, client_entry, hostname, 
689                                    port, 120, keyagr_completion, internal);
690     goto out;
691   }
692
693   if (command == 5 && client_entry) {
694     silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
695     silc_client_perform_key_agreement(client, conn, client_entry, hostname, 
696                                       port, keyagr_completion, internal);
697     goto out;
698   }
699
700  out:
701   if (nickname)
702     silc_free(nickname);
703   if (server)
704     silc_free(server);
705   silc_client_command_free(cmd);
706 }
707
708 /* Sends an action to the channel.  Equals CTCP's ACTION (IRC's /ME) 
709    command. */
710
711 SILC_CLIENT_LCMD_FUNC(me)
712 {
713   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
714   SilcClientConnection conn = cmd->conn;
715   SilcClient client = cmd->client;
716   SilcChannelEntry channel_entry;
717   char *name;
718
719   if (!cmd->conn) {
720     silc_say(client, conn,
721              "You are not connected to a server, use /SERVER to connect");
722     goto out;
723   }
724
725   if (cmd->argc < 3) {
726     silc_say(client, conn, "Usage: /ME <channel> <action message>");
727     goto out;
728   }
729
730   if (cmd->argv[1][0] == '*') {
731     if (!conn->current_channel) {
732       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
733       goto out;
734     }
735     name = conn->current_channel->channel_name;
736   } else {
737     name = cmd->argv[1];
738   }
739
740   channel_entry = silc_client_get_channel(client, conn, name);
741   if (!channel_entry) {
742     silc_say(client, conn, "You are not on that channel");
743     goto out;
744   }
745
746   /* Send the action message */
747   silc_client_send_channel_message(client, conn, channel_entry, NULL,
748                                    SILC_MESSAGE_FLAG_ACTION, 
749                                    cmd->argv[2], cmd->argv_lens[2], TRUE);
750
751   silc_print(client, "* %s %s", conn->nickname, cmd->argv[2]);
752
753  out:
754   silc_client_command_free(cmd);
755 }
756
757 /* Sends an notice to the channel.  */
758
759 SILC_CLIENT_LCMD_FUNC(notice)
760 {
761   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
762   SilcClientConnection conn = cmd->conn;
763   SilcClient client = cmd->client;
764   SilcChannelEntry channel_entry;
765   char *name;
766
767   if (!cmd->conn) {
768     silc_say(client, conn,
769              "You are not connected to a server, use /SERVER to connect");
770     goto out;
771   }
772
773   if (cmd->argc < 3) {
774     silc_say(client, conn, "Usage: /NOTICE <channel> <message>");
775     goto out;
776   }
777
778   if (cmd->argv[1][0] == '*') {
779     if (!conn->current_channel) {
780       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
781       goto out;
782     }
783     name = conn->current_channel->channel_name;
784   } else {
785     name = cmd->argv[1];
786   }
787
788   channel_entry = silc_client_get_channel(client, conn, name);
789   if (!channel_entry) {
790     silc_say(client, conn, "You are not on that channel");
791     goto out;
792   }
793
794   /* Send the action message */
795   silc_client_send_channel_message(client, conn, channel_entry, NULL,
796                                    SILC_MESSAGE_FLAG_NOTICE, 
797                                    cmd->argv[2], cmd->argv_lens[2], TRUE);
798
799   silc_print(client, "- %s %s", conn->nickname, cmd->argv[2]);
800
801  out:
802   silc_client_command_free(cmd);
803 }