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