Added STATS command. Patch by Ville Räsänen.
[silc.git] / lib / silcclient / command.c
1 /*
2
3   command.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2002 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silcincludes.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 #define SILC_NOT_CONNECTED(x, c) \
26   x->internal->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \
27            "You are not connected to a server, use /SERVER to connect");
28
29 /* Command operation that is called at the end of all commands. 
30    Usage: COMMAND(status); */
31 #define COMMAND(status) cmd->client->internal->ops->command(cmd->client, \
32   cmd->conn, cmd, TRUE, cmd->command->cmd, (status))
33
34 /* Error to application. Usage: COMMAND_ERROR(status); */
35 #define COMMAND_ERROR(status)                           \
36   cmd->client->internal->ops->command(cmd->client,      \
37   cmd->conn, cmd, FALSE, cmd->command->cmd, (status))
38
39 #define SAY cmd->client->internal->ops->say
40
41 /* Generic function to send any command. The arguments must be sent already
42    encoded into correct form and in correct order. */
43
44 void silc_client_command_send(SilcClient client, SilcClientConnection conn,
45                               SilcCommand command, SilcUInt16 ident,
46                               SilcUInt32 argc, ...)
47 {
48   SilcBuffer packet;
49   va_list ap;
50
51   va_start(ap, argc);
52
53   packet = silc_command_payload_encode_vap(command, ident, argc, ap);
54   silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
55                           NULL, 0, NULL, NULL, packet->data, 
56                           packet->len, TRUE);
57   silc_buffer_free(packet);
58 }
59
60 /* Finds and returns a pointer to the command list. Return NULL if the
61    command is not found. */
62
63 SilcClientCommand silc_client_command_find(SilcClient client,
64                                            const char *name)
65 {
66   SilcClientCommand cmd;
67
68   silc_list_start(client->internal->commands);
69   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
70     if (cmd->name && !strcmp(cmd->name, name))
71       return cmd;
72   }
73
74   return NULL;
75 }
76
77 /* Calls the command (executes it).  Application can call this after
78    it has allocated the SilcClientCommandContext with the function
79    silc_client_command_alloc and found the command from the client
80    library by calling silc_client_command_find.  This will execute
81    the command. */
82
83 void silc_client_command_call(SilcClientCommand command, 
84                               SilcClientCommandContext cmd)
85 {
86   (*command->command)((void *)cmd, NULL);
87 }
88
89 /* Add new pending command to be executed when reply to a command has been
90    received. The `reply_cmd' is the command that will call the `callback'
91    with `context' when reply has been received.  It can be SILC_COMMAND_NONE
92    to match any command with the `ident'.  If `ident' is non-zero
93    the `callback' will be executed when received reply with command
94    identifier `ident'. If there already exists pending command for the
95    specified command, ident, callback and context this function has no
96    effect. */
97
98 void silc_client_command_pending(SilcClientConnection conn,
99                                  SilcCommand reply_cmd,
100                                  SilcUInt16 ident,
101                                  SilcCommandCb callback,
102                                  void *context)
103 {
104   SilcClientCommandPending *reply;
105
106   reply = silc_calloc(1, sizeof(*reply));
107   reply->reply_cmd = reply_cmd;
108   reply->ident = ident;
109   reply->context = context;
110   reply->callback = callback;
111   silc_dlist_add(conn->pending_commands, reply);
112 }
113
114 /* Deletes pending command by reply command type. */
115
116 void silc_client_command_pending_del(SilcClientConnection conn,
117                                      SilcCommand reply_cmd,
118                                      SilcUInt16 ident)
119 {
120   SilcClientCommandPending *r;
121
122   if (!conn->pending_commands)
123     return;
124
125   silc_dlist_start(conn->pending_commands);
126   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
127     if ((r->reply_cmd == reply_cmd || (r->reply_cmd == SILC_COMMAND_NONE &&
128                                        r->reply_check))
129         && r->ident == ident) {
130       silc_dlist_del(conn->pending_commands, r);
131       silc_free(r);
132     }
133   }
134 }
135
136 /* Checks for pending commands and marks callbacks to be called from
137    the command reply function. */
138
139 SilcClientCommandPendingCallbacks
140 silc_client_command_pending_check(SilcClientConnection conn,
141                                   SilcClientCommandReplyContext ctx,
142                                   SilcCommand command, 
143                                   SilcUInt16 ident,
144                                   SilcUInt32 *callbacks_count)
145 {
146   SilcClientCommandPending *r;
147   SilcClientCommandPendingCallbacks callbacks = NULL;
148   int i = 0;
149
150   silc_dlist_start(conn->pending_commands);
151   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
152     if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
153         && r->ident == ident) {
154       callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
155       callbacks[i].context = r->context;
156       callbacks[i].callback = r->callback;
157       r->reply_check = TRUE;
158       ctx->ident = ident;
159       i++;
160     }
161   }
162
163   *callbacks_count = i;
164   return callbacks;
165 }
166
167 /* Allocate Command Context */
168
169 SilcClientCommandContext silc_client_command_alloc(void)
170 {
171   SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
172   ctx->users++;
173   return ctx;
174 }
175
176 /* Free command context and its internals */
177
178 void silc_client_command_free(SilcClientCommandContext ctx)
179 {
180   ctx->users--;
181   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
182                   ctx->users));
183   if (ctx->users < 1) {
184     int i;
185
186     for (i = 0; i < ctx->argc; i++)
187       silc_free(ctx->argv[i]);
188     silc_free(ctx->argv_lens);
189     silc_free(ctx->argv_types);
190     silc_free(ctx);
191   }
192 }
193
194 /* Duplicate Command Context by adding reference counter. The context won't
195    be free'd untill it hits zero. */
196
197 SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
198 {
199   ctx->users++;
200   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
201                   ctx->users));
202   return ctx;
203 }
204
205 /* Command WHOIS. This command is used to query information about 
206    specific user. */
207
208 SILC_CLIENT_CMD_FUNC(whois)
209 {
210   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
211   SilcClientConnection conn = cmd->conn;
212   SilcBuffer buffer;
213   unsigned char count[4];
214
215   if (!cmd->conn) {
216     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
217     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
218     goto out;
219   }
220
221   /* Given without arguments fetches client's own information */
222   if (cmd->argc < 2) {
223     buffer = silc_id_payload_encode(cmd->conn->local_id, SILC_ID_CLIENT);
224     silc_client_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS, 
225                              ++conn->cmd_ident,
226                              1, 4, buffer->data, buffer->len);
227     silc_buffer_free(buffer);
228     goto out;
229   }
230
231   if (cmd->argc == 2) {
232     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
233                                             ++conn->cmd_ident, 1,
234                                             1, cmd->argv[1], 
235                                             cmd->argv_lens[1]);
236   } else {
237     int c = atoi(cmd->argv[2]);
238     memset(count, 0, sizeof(count));
239     SILC_PUT32_MSB(c, count);
240     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
241                                             ++conn->cmd_ident, 2,
242                                             1, cmd->argv[1], cmd->argv_lens[1],
243                                             2, count, sizeof(count));
244   }
245   silc_client_packet_send(cmd->client, cmd->conn->sock,
246                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
247                           buffer->data, buffer->len, TRUE);
248   silc_buffer_free(buffer);
249
250   /* Notify application */
251   COMMAND(SILC_STATUS_OK);
252
253  out:
254   silc_client_command_free(cmd);
255 }
256
257 /* Command WHOWAS. This command is used to query history information about
258    specific user that used to exist in the network. */
259
260 SILC_CLIENT_CMD_FUNC(whowas)
261 {
262   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
263   SilcClientConnection conn = cmd->conn;
264   SilcBuffer buffer;
265   unsigned char count[4];
266
267   if (!cmd->conn) {
268     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
269     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
270     goto out;
271   }
272
273   if (cmd->argc < 2 || cmd->argc > 3) {
274     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
275         "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
276     COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
277                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
278     goto out;
279   }
280
281   if (cmd->argc == 2) {
282     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOWAS,
283                                             ++conn->cmd_ident, 1,
284                                             1, cmd->argv[1], 
285                                             cmd->argv_lens[1]);
286   } else {
287     int c = atoi(cmd->argv[2]);
288     memset(count, 0, sizeof(count));
289     SILC_PUT32_MSB(c, count);
290     buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOWAS,
291                                             ++conn->cmd_ident, 2,
292                                             1, cmd->argv[1], cmd->argv_lens[1],
293                                             2, count, sizeof(count));
294   }
295   silc_client_packet_send(cmd->client, cmd->conn->sock,
296                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
297                           buffer->data, buffer->len, TRUE);
298   silc_buffer_free(buffer);
299
300   /* Notify application */
301   COMMAND(SILC_STATUS_OK);
302
303  out:
304   silc_client_command_free(cmd);
305 }
306
307 /* Command IDENTIFY. This command is used to query information about 
308    specific user, especially ID's. 
309
310    NOTE: This command is used only internally by the client library
311    and application MUST NOT call this command directly. */
312
313 SILC_CLIENT_CMD_FUNC(identify)
314 {
315   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
316   SilcClientConnection conn = cmd->conn;
317   SilcBuffer buffer;
318   unsigned char count[4];
319
320   if (!cmd->conn) {
321     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
322     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
323     goto out;
324   }
325
326   if (cmd->argc < 2 || cmd->argc > 3)
327     goto out;
328
329   if (cmd->argc == 2) {
330     buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
331                                             ++conn->cmd_ident, 1,
332                                             1, cmd->argv[1],
333                                             cmd->argv_lens[1]);
334   } else {
335     int c = atoi(cmd->argv[2]);
336     memset(count, 0, sizeof(count));
337     SILC_PUT32_MSB(c, count);
338     buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
339                                             ++conn->cmd_ident, 2,
340                                             1, cmd->argv[1],
341                                             cmd->argv_lens[1],
342                                             4, count, sizeof(count));
343   }
344
345   silc_client_packet_send(cmd->client, cmd->conn->sock,
346                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
347                           buffer->data, buffer->len, TRUE);
348   silc_buffer_free(buffer);
349
350  out:
351   silc_client_command_free(cmd);
352 }
353
354 /* Command NICK. Shows current nickname/sets new nickname on current
355    window. */
356
357 SILC_CLIENT_CMD_FUNC(nick)
358 {
359   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
360   SilcClientConnection conn = cmd->conn;
361   SilcBuffer buffer;
362
363   if (!cmd->conn) {
364     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
365     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
366     goto out;
367   }
368
369   if (cmd->argc < 2) {
370     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
371         "Usage: /NICK <nickname>");
372     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
373     goto out;
374   }
375
376   if (!strcmp(conn->nickname, cmd->argv[1]))
377     goto out;
378
379   /* Show current nickname */
380   if (cmd->argc < 2) {
381     if (cmd->conn) {
382       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
383           "Your nickname is %s on server %s", 
384           conn->nickname, conn->remote_host);
385     } else {
386       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
387           "Your nickname is %s", conn->nickname);
388     }
389
390     COMMAND(SILC_STATUS_OK);
391     goto out;
392   }
393
394   if (cmd->argv_lens[1] > 128)
395     cmd->argv_lens[1] = 128;
396
397   /* Send the NICK command */
398   buffer = silc_command_payload_encode(SILC_COMMAND_NICK, 1,
399                                        &cmd->argv[1],
400                                        &cmd->argv_lens[1], 
401                                        &cmd->argv_types[1],
402                                        ++cmd->conn->cmd_ident);
403   silc_client_packet_send(cmd->client, cmd->conn->sock,
404                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
405                           buffer->data, buffer->len, TRUE);
406   silc_buffer_free(buffer);
407
408  out:
409   silc_client_command_free(cmd);
410 }
411
412 /* Command LIST. Lists channels on the current server. */
413
414 SILC_CLIENT_CMD_FUNC(list)
415 {
416   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
417   SilcClientConnection conn = cmd->conn;
418   SilcIDCacheEntry id_cache = NULL;
419   SilcChannelEntry channel;
420   SilcBuffer buffer, idp = NULL;
421   char *name;
422
423   if (!cmd->conn) {
424     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
425     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
426     goto out;
427   }
428
429   if (cmd->argc == 2) {
430     name = cmd->argv[1];
431
432     /* Get the Channel ID of the channel */
433     if (silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
434       channel = (SilcChannelEntry)id_cache->context;
435       idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
436     }
437   }
438
439   if (!idp)
440     buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, 
441                                             ++conn->cmd_ident, 0);
442   else
443     buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, 
444                                             ++conn->cmd_ident, 1,
445                                             1, idp->data, idp->len);
446
447   silc_client_packet_send(cmd->client, cmd->conn->sock,
448                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
449                           buffer->data, buffer->len, TRUE);
450   silc_buffer_free(buffer);
451   if (idp)
452     silc_buffer_free(idp);
453
454   /* Notify application */
455   COMMAND(SILC_STATUS_OK);
456
457  out:
458   silc_client_command_free(cmd);
459 }
460
461 /* Command TOPIC. Sets/shows topic on a channel. */
462
463 SILC_CLIENT_CMD_FUNC(topic)
464 {
465   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
466   SilcClientConnection conn = cmd->conn;
467   SilcIDCacheEntry id_cache = NULL;
468   SilcChannelEntry channel;
469   SilcBuffer buffer, idp;
470   char *name;
471
472   if (!cmd->conn) {
473     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
474     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
475     goto out;
476   }
477
478   if (cmd->argc < 2 || cmd->argc > 3) {
479     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
480         "Usage: /TOPIC <channel> [<topic>]");
481     COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
482                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
483     goto out;
484   }
485
486   if (cmd->argv[1][0] == '*') {
487     if (!conn->current_channel) {
488       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
489       goto out;
490     }
491     name = conn->current_channel->channel_name;
492   } else {
493     name = cmd->argv[1];
494   }
495
496   if (!conn->current_channel) {
497     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
498     goto out;
499   }
500
501   /* Get the Channel ID of the channel */
502   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
503     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
504     goto out;
505   }
506
507   channel = (SilcChannelEntry)id_cache->context;
508
509   /* Send TOPIC command to the server */
510   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
511   if (cmd->argc > 2)
512     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 
513                                             ++conn->cmd_ident, 2, 
514                                             1, idp->data, idp->len,
515                                             2, cmd->argv[2], 
516                                             strlen(cmd->argv[2]));
517   else
518     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 
519                                             ++conn->cmd_ident, 1,
520                                             1, idp->data, idp->len);
521   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
522                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
523   silc_buffer_free(buffer);
524   silc_buffer_free(idp);
525
526   /* Notify application */
527   COMMAND(SILC_STATUS_OK);
528
529  out:
530   silc_client_command_free(cmd);
531 }
532
533 /* Command INVITE. Invites specific client to join a channel. This is
534    also used to mange the invite list of the channel. */
535
536 SILC_CLIENT_CMD_FUNC(invite)
537 {
538   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
539   SilcClient client = cmd->client;
540   SilcClientConnection conn = cmd->conn;
541   SilcClientEntry client_entry = NULL;
542   SilcChannelEntry channel;
543   SilcBuffer buffer, clidp, chidp;
544   SilcUInt32 type = 0;
545   char *nickname = NULL, *name;
546   char *invite = NULL;
547
548   if (!cmd->conn) {
549     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
550     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
551     goto out;
552   }
553
554   if (cmd->argc < 2) {
555     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
556         "Usage: /INVITE <channel> [<nickname>[@server>]"
557         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
558     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
559     goto out;
560   }
561
562   if (cmd->argv[1][0] == '*') {
563     if (!conn->current_channel) {
564       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
565       goto out;
566     }
567
568     channel = conn->current_channel;
569   } else {
570     name = cmd->argv[1];
571
572     channel = silc_client_get_channel(cmd->client, conn, name);
573     if (!channel) {
574       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
575       goto out;
576     }
577   }
578
579   /* Parse the typed nickname. */
580   if (cmd->argc == 3) {
581     if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
582       if (client->internal->params->nickname_parse)
583         client->internal->params->nickname_parse(cmd->argv[2], &nickname);
584       else
585         nickname = strdup(cmd->argv[2]);
586
587       /* Find client entry */
588       client_entry = silc_idlist_get_client(client, conn, nickname, 
589                                             cmd->argv[2], TRUE);
590       if (!client_entry) {
591         if (cmd->pending) {
592           COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
593           goto out;
594         }
595       
596         /* Client entry not found, it was requested thus mark this to be
597            pending command. */
598         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
599                                     conn->cmd_ident,
600                                     silc_client_command_invite, 
601                                     silc_client_command_dup(cmd));
602         cmd->pending = 1;
603         goto out;
604       }
605     } else {
606       invite = cmd->argv[2];
607       invite++;
608       if (cmd->argv[2][0] == '+')
609         type = 3;
610       else
611         type = 4;
612     }
613   }
614
615   /* Send the command */
616   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
617   if (client_entry) {
618     clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
619     buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 
620                                             ++conn->cmd_ident, 3,
621                                             1, chidp->data, chidp->len,
622                                             2, clidp->data, clidp->len,
623                                             type, invite, invite ?
624                                             strlen(invite) : 0);
625     silc_buffer_free(clidp);
626   } else {
627     buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 
628                                             ++conn->cmd_ident, 2,
629                                             1, chidp->data, chidp->len,
630                                             type, invite, invite ?
631                                             strlen(invite) : 0);
632   }
633
634   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
635                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
636   silc_buffer_free(buffer);
637   silc_buffer_free(chidp);
638
639   /* Notify application */
640   COMMAND(SILC_STATUS_OK);
641
642  out:
643   silc_free(nickname);
644   silc_client_command_free(cmd);
645 }
646
647 typedef struct {
648   SilcClient client;
649   SilcClientConnection conn;
650 } *QuitInternal;
651
652 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
653 {
654   QuitInternal q = (QuitInternal)context;
655
656   /* Close connection */
657   q->client->internal->ops->disconnect(q->client, q->conn, 0, NULL);
658   silc_client_close_connection(q->client, q->conn->sock->user_data);
659
660   silc_free(q);
661 }
662
663 /* Command QUIT. Closes connection with current server. */
664  
665 SILC_CLIENT_CMD_FUNC(quit)
666 {
667   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
668   SilcBuffer buffer;
669   QuitInternal q;
670
671   if (!cmd->conn) {
672     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
673     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
674     goto out;
675   }
676
677   if (cmd->argc > 1)
678     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, 
679                                          &cmd->argv[1], &cmd->argv_lens[1],
680                                          &cmd->argv_types[1], 0);
681   else
682     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
683                                          NULL, NULL, NULL, 0);
684   silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
685                           NULL, 0, NULL, NULL, 
686                           buffer->data, buffer->len, TRUE);
687   silc_buffer_free(buffer);
688
689   q = silc_calloc(1, sizeof(*q));
690   q->client = cmd->client;
691   q->conn = cmd->conn;
692
693   /* Sleep for a while */
694   sleep(2);
695
696   /* We quit the connection with little timeout */
697   silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
698                          silc_client_command_quit_cb, (void *)q,
699                          1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
700
701   /* Notify application */
702   COMMAND(SILC_STATUS_OK);
703
704  out:
705   silc_client_command_free(cmd);
706 }
707
708 /* Timeout callback to remove the killed client from cache */
709
710 SILC_TASK_CALLBACK(silc_client_command_kill_remove_later)
711 {
712   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
713   SilcClient client = cmd->client;
714   SilcClientConnection conn = cmd->conn;
715   SilcClientEntry target;
716   char *nickname = NULL;
717   
718   /* Parse the typed nickname. */
719   if (client->internal->params->nickname_parse)
720     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
721   else
722     nickname = strdup(cmd->argv[1]);
723
724   /* Get the target client */
725   target = silc_idlist_get_client(cmd->client, conn, nickname, 
726                                   cmd->argv[1], FALSE);
727   if (target)
728     /* Remove the client from all channels and free it */
729     silc_client_del_client(client, conn, target);
730
731   silc_free(nickname);
732   silc_client_command_free(cmd);
733 }
734
735 /* Kill command's pending command callback to actually remove the killed
736    client from our local cache. */
737
738 SILC_CLIENT_CMD_FUNC(kill_remove)
739 {
740   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
741   SilcClientCommandReplyContext reply = 
742     (SilcClientCommandReplyContext)context2;
743   SilcStatus status;
744
745   silc_command_get_status(reply->payload, &status, NULL);
746   if (status == SILC_STATUS_OK) {
747     /* Remove with timeout */
748     silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
749                            silc_client_command_kill_remove_later, context,
750                            1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
751     return;
752   }
753
754   silc_client_command_free(cmd);
755 }
756
757 /* Command KILL. Router operator can use this command to remove an client
758    fromthe SILC Network. */
759
760 SILC_CLIENT_CMD_FUNC(kill)
761 {
762   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
763   SilcClient client = cmd->client;
764   SilcClientConnection conn = cmd->conn;
765   SilcBuffer buffer, idp;
766   SilcClientEntry target;
767   char *nickname = NULL;
768
769   if (!cmd->conn) {
770     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
771     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
772     goto out;
773   }
774
775   if (cmd->argc < 2) {
776     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
777         "Usage: /KILL <nickname> [<comment>]");
778     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
779     goto out;
780   }
781
782   /* Parse the typed nickname. */
783   if (client->internal->params->nickname_parse)
784     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
785   else
786     nickname = strdup(cmd->argv[1]);
787
788   /* Get the target client */
789   target = silc_idlist_get_client(cmd->client, conn, nickname, 
790                                   cmd->argv[1], TRUE);
791   if (!target) {
792     if (cmd->pending) {
793       COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
794       goto out;
795     }
796
797     /* Client entry not found, it was requested thus mark this to be
798        pending command. */
799     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
800                                 conn->cmd_ident,  
801                                 silc_client_command_kill, 
802                                 silc_client_command_dup(cmd));
803     cmd->pending = 1;
804     goto out;
805   }
806
807   /* Send the KILL command to the server */
808   idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
809   if (cmd->argc == 2)
810     buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 
811                                             ++conn->cmd_ident, 1, 
812                                             1, idp->data, idp->len);
813   else
814     buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 
815                                             ++conn->cmd_ident, 2, 
816                                             1, idp->data, idp->len,
817                                             2, cmd->argv[2], 
818                                             strlen(cmd->argv[2]));
819   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
820                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
821   silc_buffer_free(buffer);
822   silc_buffer_free(idp);
823
824   /* Notify application */
825   COMMAND(SILC_STATUS_OK);
826
827   /* Register a pending callback that will actually remove the killed
828      client from our cache. */
829   silc_client_command_pending(conn, SILC_COMMAND_KILL, conn->cmd_ident,
830                               silc_client_command_kill_remove,
831                               silc_client_command_dup(cmd));
832
833  out:
834   silc_free(nickname);
835   silc_client_command_free(cmd);
836 }
837
838 /* Command INFO. Request information about specific server. If specific
839    server is not provided the current server is used. */
840
841 SILC_CLIENT_CMD_FUNC(info)
842 {
843   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
844   SilcClientConnection conn = cmd->conn;
845   SilcBuffer buffer;
846   char *name = NULL;
847
848   if (!cmd->conn) {
849     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
850     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
851     goto out;
852   }
853
854   if (cmd->argc == 2)
855     name = strdup(cmd->argv[1]);
856
857   /* Send the command */
858   if (name)
859     buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
860                                             1, name, strlen(name));
861   else
862     buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0,
863                                          NULL, NULL, NULL, 0);
864   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
865                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
866   silc_buffer_free(buffer);
867   if (name)
868     silc_free(name);
869
870   /* Notify application */
871   COMMAND(SILC_STATUS_OK);
872
873  out:
874   silc_client_command_free(cmd);
875 }
876
877 /* Command STATS. Shows server and network statistics. */
878
879 SILC_CLIENT_CMD_FUNC(stats)
880 {
881   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
882   SilcClientConnection conn = cmd->conn;
883   SilcBuffer buffer, idp = NULL;
884
885   if (!cmd->conn) {
886     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
887     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
888     goto out;
889   }
890
891   idp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); 
892   
893   /* Send the command */
894   buffer = silc_command_payload_encode_va(SILC_COMMAND_STATS,
895                                           ++conn->cmd_ident, 1,
896                                           SILC_ID_SERVER, idp->data, idp->len);
897   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
898                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
899   silc_buffer_free(buffer);
900   silc_buffer_free(idp);
901
902   /* Notify application */
903   COMMAND(SILC_STATUS_OK);
904
905  out:
906   silc_client_command_free(cmd);
907 }
908
909 /* Command PING. Sends ping to server. This is used to test the 
910    communication channel. */
911
912 SILC_CLIENT_CMD_FUNC(ping)
913 {
914   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
915   SilcClientConnection conn = cmd->conn;
916   SilcBuffer buffer;
917   void *id;
918   int i;
919
920   if (!cmd->conn) {
921     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
922     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
923     goto out;
924   }
925
926   /* Send the command */
927   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
928                                           1, conn->remote_id_data, 
929                                           silc_id_get_len(conn->remote_id,
930                                                           SILC_ID_SERVER));
931   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
932                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
933   silc_buffer_free(buffer);
934
935   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
936                       SILC_ID_SERVER);
937   if (!id) {
938     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
939     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
940     goto out;
941   }
942
943   /* Start counting time */
944   for (i = 0; i < conn->ping_count; i++) {
945     if (conn->ping[i].dest_id == NULL) {
946       conn->ping[i].start_time = time(NULL);
947       conn->ping[i].dest_id = id;
948       conn->ping[i].dest_name = strdup(conn->remote_host);
949       break;
950     }
951   }
952   if (i >= conn->ping_count) {
953     i = conn->ping_count;
954     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
955     conn->ping[i].start_time = time(NULL);
956     conn->ping[i].dest_id = id;
957     conn->ping[i].dest_name = strdup(conn->remote_host);
958     conn->ping_count++;
959   }
960   
961   /* Notify application */
962   COMMAND(SILC_STATUS_OK);
963
964  out:
965   silc_client_command_free(cmd);
966 }
967
968 /* Command JOIN. Joins to a channel. */
969
970 SILC_CLIENT_CMD_FUNC(join)
971 {
972   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
973   SilcClientConnection conn = cmd->conn;
974   SilcChannelEntry channel;
975   SilcBuffer buffer, idp, auth = NULL;
976   char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
977   int i, passphrase_len = 0;
978
979   if (!cmd->conn) {
980     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
981     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
982     goto out;
983   }
984
985   if (cmd->argc < 2) {
986     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
987     goto out;
988   }
989   
990   /* See if we have joined to the requested channel already */
991   channel = silc_client_get_channel(cmd->client, conn, cmd->argv[1]);
992   if (channel && silc_client_on_channel(channel, conn->local_entry))
993     goto out;
994
995   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
996
997   if (cmd->argv_lens[1] > 256)
998     cmd->argv_lens[1] = 256;
999
1000   name = cmd->argv[1];
1001
1002   for (i = 2; i < cmd->argc; i++) {
1003     if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1004       cipher = cmd->argv[i + 1];
1005       i++;
1006     } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1007       hmac = cmd->argv[i + 1];
1008       i++;
1009     } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1010       auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1011                                                 cmd->client->private_key,
1012                                                 cmd->client->rng, 
1013                                                 cmd->client->internal->
1014                                                 sha1hash,
1015                                                 conn->local_id,
1016                                                 SILC_ID_CLIENT);
1017       i++;
1018     } else {
1019       /* Passphrases must be UTF-8 encoded, so encode if it is not */
1020       if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1021         passphrase_len = silc_utf8_encoded_len(cmd->argv[i], 
1022                                                cmd->argv_lens[i], 0);
1023         pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1024         passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1025                                           0, pu8, passphrase_len);
1026         passphrase = pu8;
1027       } else {
1028         passphrase = strdup(cmd->argv[i]);
1029         passphrase_len = cmd->argv_lens[i];
1030       }
1031     }
1032   }
1033
1034   /* Send JOIN command to the server */
1035   buffer =
1036     silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6,
1037                                    1, name, strlen(name),
1038                                    2, idp->data, idp->len,
1039                                    3, passphrase, passphrase_len,
1040                                    4, cipher, cipher ? strlen(cipher) : 0,
1041                                    5, hmac, hmac ? strlen(hmac) : 0,
1042                                    6, auth ? auth->data : NULL,
1043                                    auth ? auth->len : 0);
1044   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1045                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1046   silc_buffer_free(buffer);
1047   silc_buffer_free(idp);
1048   if (auth)
1049     silc_buffer_free(auth);
1050   silc_free(passphrase);
1051
1052   /* Notify application */
1053   COMMAND(SILC_STATUS_OK);
1054
1055  out:
1056   silc_client_command_free(cmd);
1057 }
1058
1059 /* MOTD command. Requests motd from server. */
1060
1061 SILC_CLIENT_CMD_FUNC(motd)
1062 {
1063   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1064   SilcClientConnection conn = cmd->conn;
1065   SilcBuffer buffer;
1066
1067   if (!cmd->conn) {
1068     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1069     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1070     goto out;
1071   }
1072
1073   if (cmd->argc < 1 || cmd->argc > 2) {
1074     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1075         "Usage: /MOTD [<server>]");
1076     COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1077                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
1078     goto out;
1079   }
1080
1081   /* Send TOPIC command to the server */
1082   if (cmd->argc == 1)
1083     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1084                                             1, conn->remote_host, 
1085                                             strlen(conn->remote_host));
1086   else
1087     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1088                                             1, cmd->argv[1], 
1089                                             cmd->argv_lens[1]);
1090   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1091                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1092   silc_buffer_free(buffer);
1093
1094   /* Notify application */
1095   COMMAND(SILC_STATUS_OK);
1096
1097  out:
1098   silc_client_command_free(cmd);
1099 }
1100
1101 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1102    modes as client cannot set itself server/router operator privileges. */
1103
1104 SILC_CLIENT_CMD_FUNC(umode)
1105 {
1106   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1107   SilcClientConnection conn = cmd->conn;
1108   SilcBuffer buffer, idp;
1109   unsigned char *cp, modebuf[4];
1110   SilcUInt32 mode, add, len;
1111   int i;
1112
1113   if (!cmd->conn) {
1114     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1115     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1116     goto out;
1117   }
1118
1119   if (cmd->argc < 2) {
1120     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1121         "Usage: /UMODE +|-<modes>");
1122     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1123     goto out;
1124   }
1125
1126   mode = conn->local_entry->mode;
1127
1128   /* Are we adding or removing mode */
1129   if (cmd->argv[1][0] == '-')
1130     add = FALSE;
1131   else
1132     add = TRUE;
1133
1134   /* Parse mode */
1135   cp = cmd->argv[1] + 1;
1136   len = strlen(cp);
1137   for (i = 0; i < len; i++) {
1138     switch(cp[i]) {
1139     case 'a':
1140       if (add) {
1141         mode = 0;
1142         mode |= SILC_UMODE_SERVER_OPERATOR;
1143         mode |= SILC_UMODE_ROUTER_OPERATOR;
1144         mode |= SILC_UMODE_GONE;
1145         mode |= SILC_UMODE_INDISPOSED;
1146         mode |= SILC_UMODE_BUSY;
1147         mode |= SILC_UMODE_PAGE;
1148         mode |= SILC_UMODE_HYPER;
1149         mode |= SILC_UMODE_ROBOT;
1150         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1151         mode |= SILC_UMODE_REJECT_WATCHING;
1152       } else {
1153         mode = SILC_UMODE_NONE;
1154       }
1155       break;
1156     case 's':
1157       if (add)
1158         mode |= SILC_UMODE_SERVER_OPERATOR;
1159       else
1160         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1161       break;
1162     case 'r':
1163       if (add)
1164         mode |= SILC_UMODE_ROUTER_OPERATOR;
1165       else
1166         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1167       break;
1168     case 'g':
1169       if (add)
1170         mode |= SILC_UMODE_GONE;
1171       else
1172         mode &= ~SILC_UMODE_GONE;
1173       break;
1174     case 'i':
1175       if (add)
1176         mode |= SILC_UMODE_INDISPOSED;
1177       else
1178         mode &= ~SILC_UMODE_INDISPOSED;
1179       break;
1180     case 'b':
1181       if (add)
1182         mode |= SILC_UMODE_BUSY;
1183       else
1184         mode &= ~SILC_UMODE_BUSY;
1185       break;
1186     case 'p':
1187       if (add)
1188         mode |= SILC_UMODE_PAGE;
1189       else
1190         mode &= ~SILC_UMODE_PAGE;
1191       break;
1192     case 'h':
1193       if (add)
1194         mode |= SILC_UMODE_HYPER;
1195       else
1196         mode &= ~SILC_UMODE_HYPER;
1197       break;
1198     case 't':
1199       if (add)
1200         mode |= SILC_UMODE_ROBOT;
1201       else
1202         mode &= ~SILC_UMODE_ROBOT;
1203       break;
1204     case 'P':
1205       if (add)
1206         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1207       else
1208         mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1209       break;
1210     case 'w':
1211       if (add)
1212         mode |= SILC_UMODE_REJECT_WATCHING;
1213       else
1214         mode &= ~SILC_UMODE_REJECT_WATCHING;
1215       break;
1216     case 'I':
1217       if (add)
1218         mode |= SILC_UMODE_BLOCK_INVITE;
1219       else
1220         mode &= ~SILC_UMODE_BLOCK_INVITE;
1221       break;
1222     default:
1223       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1224       goto out;
1225       break;
1226     }
1227   }
1228
1229   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1230   SILC_PUT32_MSB(mode, modebuf);
1231
1232   /* Send the command packet. We support sending only one mode at once
1233      that requires an argument. */
1234   buffer = 
1235     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1236                                    1, idp->data, idp->len, 
1237                                    2, modebuf, sizeof(modebuf));
1238   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1239                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1240   silc_buffer_free(buffer);
1241   silc_buffer_free(idp);
1242
1243   /* Notify application */
1244   COMMAND(SILC_STATUS_OK);
1245
1246  out:
1247   silc_client_command_free(cmd);
1248 }
1249
1250 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1251    can be set several at once. Those modes that require argument must be set
1252    separately (unless set with modes that does not require arguments). */
1253
1254 SILC_CLIENT_CMD_FUNC(cmode)
1255 {
1256   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1257   SilcClientConnection conn = cmd->conn;
1258   SilcChannelEntry channel;
1259   SilcBuffer buffer, chidp, auth = NULL;
1260   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1261   SilcUInt32 mode, add, type, len, arg_len = 0;
1262   int i;
1263
1264   if (!cmd->conn) {
1265     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1266     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1267     goto out;
1268   }
1269
1270   if (cmd->argc < 3) {
1271     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1272         "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1273     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1274     goto out;
1275   }
1276
1277   if (cmd->argv[1][0] == '*') {
1278     if (!conn->current_channel) {
1279       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1280       goto out;
1281     }
1282
1283     channel = conn->current_channel;
1284   } else {
1285     name = cmd->argv[1];
1286
1287     channel = silc_client_get_channel(cmd->client, conn, name);
1288     if (!channel) {
1289       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1290       goto out;
1291     }
1292   }
1293
1294   mode = channel->mode;
1295
1296   /* Are we adding or removing mode */
1297   if (cmd->argv[2][0] == '-')
1298     add = FALSE;
1299   else
1300     add = TRUE;
1301
1302   /* Argument type to be sent to server */
1303   type = 0;
1304
1305   /* Parse mode */
1306   cp = cmd->argv[2] + 1;
1307   len = strlen(cp);
1308   for (i = 0; i < len; i++) {
1309     switch(cp[i]) {
1310     case 'p':
1311       if (add)
1312         mode |= SILC_CHANNEL_MODE_PRIVATE;
1313       else
1314         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1315       break;
1316     case 's':
1317       if (add)
1318         mode |= SILC_CHANNEL_MODE_SECRET;
1319       else
1320         mode &= ~SILC_CHANNEL_MODE_SECRET;
1321       break;
1322     case 'k':
1323       if (add)
1324         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1325       else
1326         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1327       break;
1328     case 'i':
1329       if (add)
1330         mode |= SILC_CHANNEL_MODE_INVITE;
1331       else
1332         mode &= ~SILC_CHANNEL_MODE_INVITE;
1333       break;
1334     case 't':
1335       if (add)
1336         mode |= SILC_CHANNEL_MODE_TOPIC;
1337       else
1338         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1339       break;
1340     case 'm':
1341       if (add)
1342         mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1343       else
1344         mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1345       break;
1346     case 'M':
1347       if (add)
1348         mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1349       else
1350         mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1351       break;
1352     case 'l':
1353       if (add) {
1354         int ll;
1355         mode |= SILC_CHANNEL_MODE_ULIMIT;
1356         type = 3;
1357         if (cmd->argc < 4) {
1358           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1359               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1360           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1361           goto out;
1362         }
1363         ll = atoi(cmd->argv[3]);
1364         SILC_PUT32_MSB(ll, tmp);
1365         arg = tmp;
1366         arg_len = 4;
1367       } else {
1368         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1369       }
1370       break;
1371     case 'a':
1372       if (add) {
1373         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1374         type = 4;
1375         if (cmd->argc < 4) {
1376           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1377               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1378           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1379           goto out;
1380         }
1381         arg = cmd->argv[3];
1382         arg_len = cmd->argv_lens[3];
1383       } else {
1384         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1385       }
1386       break;
1387     case 'c':
1388       if (add) {
1389         mode |= SILC_CHANNEL_MODE_CIPHER;
1390         type = 5;
1391         if (cmd->argc < 4) {
1392           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1393               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1394           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1395           goto out;
1396         }
1397         arg = cmd->argv[3];
1398         arg_len = cmd->argv_lens[3];
1399       } else {
1400         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1401       }
1402       break;
1403     case 'h':
1404       if (add) {
1405         mode |= SILC_CHANNEL_MODE_HMAC;
1406         type = 6;
1407         if (cmd->argc < 4) {
1408           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1409               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1410           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1411           goto out;
1412         }
1413         arg = cmd->argv[3];
1414         arg_len = cmd->argv_lens[3];
1415       } else {
1416         mode &= ~SILC_CHANNEL_MODE_HMAC;
1417       }
1418       break;
1419     case 'f':
1420       if (add) {
1421         mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1422         type = 7;
1423         auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1424                                                   cmd->client->private_key,
1425                                                   cmd->client->rng, 
1426                                                   cmd->client->internal->
1427                                                   sha1hash,
1428                                                   conn->local_id,
1429                                                   SILC_ID_CLIENT);
1430         arg = auth->data;
1431         arg_len = auth->len;
1432       } else {
1433         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1434       }
1435       break;
1436     default:
1437       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1438       goto out;
1439       break;
1440     }
1441   }
1442
1443   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1444   SILC_PUT32_MSB(mode, modebuf);
1445
1446   /* Send the command packet. We support sending only one mode at once
1447      that requires an argument. */
1448   if (type && arg) {
1449     buffer = 
1450       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1451                                      1, chidp->data, chidp->len, 
1452                                      2, modebuf, sizeof(modebuf),
1453                                      type, arg, arg_len);
1454   } else {
1455     buffer = 
1456       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1457                                      1, chidp->data, chidp->len, 
1458                                      2, modebuf, sizeof(modebuf));
1459   }
1460
1461   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1462                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1463   silc_buffer_free(buffer);
1464   silc_buffer_free(chidp);
1465   if (auth)
1466     silc_buffer_free(auth);
1467
1468   /* Notify application */
1469   COMMAND(SILC_STATUS_OK);
1470
1471  out:
1472   silc_client_command_free(cmd);
1473 }
1474
1475 /* CUMODE command. Changes client's mode on a channel. */
1476
1477 SILC_CLIENT_CMD_FUNC(cumode)
1478 {
1479   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1480   SilcClient client = cmd->client;
1481   SilcClientConnection conn = cmd->conn;
1482   SilcChannelEntry channel;
1483   SilcChannelUser chu;
1484   SilcClientEntry client_entry;
1485   SilcBuffer buffer, clidp, chidp, auth = NULL;
1486   unsigned char *name, *cp, modebuf[4];
1487   SilcUInt32 mode = 0, add, len;
1488   char *nickname = NULL;
1489   int i;
1490
1491   if (!cmd->conn) {
1492     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1493     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1494     goto out;
1495   }
1496
1497   if (cmd->argc < 4) {
1498     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1499         "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1500     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1501     goto out;
1502   }
1503
1504   if (cmd->argv[1][0] == '*') {
1505     if (!conn->current_channel) {
1506       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1507       goto out;
1508     }
1509
1510     channel = conn->current_channel;
1511   } else {
1512     name = cmd->argv[1];
1513
1514     channel = silc_client_get_channel(cmd->client, conn, name);
1515     if (!channel) {
1516       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1517       goto out;
1518     }
1519   }
1520
1521   /* Parse the typed nickname. */
1522   if (client->internal->params->nickname_parse)
1523     client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1524   else
1525     nickname = strdup(cmd->argv[3]);
1526
1527   /* Find client entry */
1528   client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
1529                                         cmd->argv[3], TRUE);
1530   if (!client_entry) {
1531     if (cmd->pending) {
1532       COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
1533       goto out;
1534     }
1535
1536     /* Client entry not found, it was requested thus mark this to be
1537        pending command. */
1538     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1539                                 conn->cmd_ident,  
1540                                 silc_client_command_cumode, 
1541                                 silc_client_command_dup(cmd));
1542     cmd->pending = 1;
1543     goto out;
1544   }
1545   
1546   /* Get the current mode */
1547   chu = silc_client_on_channel(channel, client_entry);
1548   if (chu)
1549     mode = chu->mode;
1550
1551   /* Are we adding or removing mode */
1552   if (cmd->argv[2][0] == '-')
1553     add = FALSE;
1554   else
1555     add = TRUE;
1556
1557   /* Parse mode */
1558   cp = cmd->argv[2] + 1;
1559   len = strlen(cp);
1560   for (i = 0; i < len; i++) {
1561     switch(cp[i]) {
1562     case 'a':
1563       if (add) {
1564         mode |= SILC_CHANNEL_UMODE_CHANFO;
1565         mode |= SILC_CHANNEL_UMODE_CHANOP;
1566         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1567         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1568         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1569       } else {
1570         mode = SILC_CHANNEL_UMODE_NONE;
1571       }
1572       break;
1573     case 'f':
1574       if (add) {
1575         auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1576                                                   cmd->client->private_key,
1577                                                   cmd->client->rng,
1578                                                   cmd->client->internal->
1579                                                   sha1hash,
1580                                                   conn->local_id,
1581                                                   SILC_ID_CLIENT);
1582         mode |= SILC_CHANNEL_UMODE_CHANFO;
1583       } else {
1584         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1585       }
1586       break;
1587     case 'o':
1588       if (add)
1589         mode |= SILC_CHANNEL_UMODE_CHANOP;
1590       else
1591         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1592       break;
1593     case 'b':
1594       if (add)
1595         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1596       else
1597         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1598       break;
1599     case 'u':
1600       if (add)
1601         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1602       else
1603         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1604       break;
1605     case 'r':
1606       if (add)
1607         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1608       else
1609         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1610       break;
1611     case 'q':
1612       if (add)
1613         mode |= SILC_CHANNEL_UMODE_QUIET;
1614       else
1615         mode &= ~SILC_CHANNEL_UMODE_QUIET;
1616       break;
1617     default:
1618       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1619       goto out;
1620       break;
1621     }
1622   }
1623
1624   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1625   SILC_PUT32_MSB(mode, modebuf);
1626   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1627
1628   /* Send the command packet. We support sending only one mode at once
1629      that requires an argument. */
1630   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 
1631                                           auth ? 4 : 3, 
1632                                           1, chidp->data, chidp->len, 
1633                                           2, modebuf, 4,
1634                                           3, clidp->data, clidp->len,
1635                                           4, auth ? auth->data : NULL, 
1636                                           auth ? auth->len : 0);
1637   
1638   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1639                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1640   silc_buffer_free(buffer);
1641   silc_buffer_free(chidp);
1642   silc_buffer_free(clidp);
1643   if (auth)
1644     silc_buffer_free(auth);
1645   
1646   /* Notify application */
1647   COMMAND(SILC_STATUS_OK);
1648
1649  out:
1650   silc_free(nickname);
1651   silc_client_command_free(cmd);
1652 }
1653
1654 /* KICK command. Kicks a client out of channel. */
1655
1656 SILC_CLIENT_CMD_FUNC(kick)
1657 {
1658   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1659   SilcClient client = cmd->client;
1660   SilcClientConnection conn = cmd->conn;
1661   SilcIDCacheEntry id_cache = NULL;
1662   SilcChannelEntry channel;
1663   SilcBuffer buffer, idp, idp2;
1664   SilcClientEntry target;
1665   char *name;
1666   char *nickname = NULL;
1667
1668   if (!cmd->conn) {
1669     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1670     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1671     goto out;
1672   }
1673
1674   if (cmd->argc < 3) {
1675     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1676         "Usage: /KICK <channel> <nickname> [<comment>]");
1677     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1678     goto out;
1679   }
1680
1681   if (cmd->argv[1][0] == '*') {
1682     if (!conn->current_channel) {
1683       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1684       goto out;
1685     }
1686     name = conn->current_channel->channel_name;
1687   } else {
1688     name = cmd->argv[1];
1689   }
1690
1691   if (!conn->current_channel) {
1692     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1693     goto out;
1694   }
1695
1696   /* Get the Channel ID of the channel */
1697   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1698     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1699     goto out;
1700   }
1701
1702   channel = (SilcChannelEntry)id_cache->context;
1703
1704   /* Parse the typed nickname. */
1705   if (client->internal->params->nickname_parse)
1706     client->internal->params->nickname_parse(cmd->argv[2], &nickname);
1707   else
1708     nickname = strdup(cmd->argv[2]);
1709
1710   /* Get the target client */
1711   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1712                                   cmd->argv[2], FALSE);
1713   if (!target) {
1714     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1715         "No such client: %s", cmd->argv[2]);
1716     COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
1717     goto out;
1718   }
1719
1720   /* Send KICK command to the server */
1721   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1722   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1723   if (cmd->argc == 3)
1724     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1725                                             1, idp->data, idp->len,
1726                                             2, idp2->data, idp2->len);
1727   else
1728     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1729                                             1, idp->data, idp->len,
1730                                             2, idp2->data, idp2->len,
1731                                             3, cmd->argv[3], 
1732                                             strlen(cmd->argv[3]));
1733   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1734                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1735   silc_buffer_free(buffer);
1736   silc_buffer_free(idp);
1737   silc_buffer_free(idp2);
1738
1739   /* Notify application */
1740   COMMAND(SILC_STATUS_OK);
1741
1742  out:
1743   silc_free(nickname);
1744   silc_client_command_free(cmd);
1745 }
1746
1747 static void silc_client_command_oper_send(unsigned char *data,
1748                                           SilcUInt32 data_len, void *context)
1749 {
1750   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1751   SilcClientConnection conn = cmd->conn;
1752   SilcBuffer buffer, auth;
1753
1754   if (cmd->argc >= 3) {
1755     /* Encode the public key authentication payload */
1756     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1757                                               cmd->client->private_key,
1758                                               cmd->client->rng, conn->hash,
1759                                               conn->local_id,
1760                                               SILC_ID_CLIENT);
1761   } else {
1762     /* Encode the password authentication payload */
1763     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1764                                     data, data_len);
1765   }
1766
1767   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1768                                           1, cmd->argv[1], 
1769                                           strlen(cmd->argv[1]),
1770                                           2, auth ? auth->data : NULL,
1771                                           auth ? auth->len : 0);
1772   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1773                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1774
1775   silc_buffer_free(buffer);
1776   silc_buffer_free(auth);
1777
1778   /* Notify application */
1779   COMMAND(SILC_STATUS_OK);
1780 }
1781
1782 /* OPER command. Used to obtain server operator privileges. */
1783
1784 SILC_CLIENT_CMD_FUNC(oper)
1785 {
1786   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1787   SilcClientConnection conn = cmd->conn;
1788
1789   if (!cmd->conn) {
1790     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1791     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1792     goto out;
1793   }
1794
1795   if (cmd->argc < 2) {
1796     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1797         "Usage: /OPER <username> [-pubkey]");
1798     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1799     goto out;
1800   }
1801
1802   if (cmd->argc < 3) {
1803     /* Get passphrase */
1804     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1805                                      silc_client_command_oper_send,
1806                                      context);
1807     return;
1808   }
1809
1810   silc_client_command_oper_send(NULL, 0, context);
1811
1812  out:
1813   silc_client_command_free(cmd);
1814 }
1815
1816 static void silc_client_command_silcoper_send(unsigned char *data,
1817                                               SilcUInt32 data_len, 
1818                                               void *context)
1819 {
1820   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1821   SilcClientConnection conn = cmd->conn;
1822   SilcBuffer buffer, auth;
1823
1824   if (cmd->argc >= 3) {
1825     /* Encode the public key authentication payload */
1826     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1827                                               cmd->client->private_key,
1828                                               cmd->client->rng, conn->hash,
1829                                               conn->local_id,
1830                                               SILC_ID_CLIENT);
1831   } else {
1832     /* Encode the password authentication payload */
1833     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1834                                     data, data_len);
1835   }
1836
1837   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1838                                           1, cmd->argv[1], 
1839                                           strlen(cmd->argv[1]),
1840                                           2, auth ? auth->data : NULL,
1841                                           auth ? auth->len : 0);
1842   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1843                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1844
1845   silc_buffer_free(buffer);
1846   silc_buffer_free(auth);
1847
1848   /* Notify application */
1849   COMMAND(SILC_STATUS_OK);
1850 }
1851
1852 /* SILCOPER command. Used to obtain router operator privileges. */
1853
1854 SILC_CLIENT_CMD_FUNC(silcoper)
1855 {
1856   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1857   SilcClientConnection conn = cmd->conn;
1858
1859   if (!cmd->conn) {
1860     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1861     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1862     goto out;
1863   }
1864
1865   if (cmd->argc < 2) {
1866     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1867         "Usage: /SILCOPER <username> [-pubkey]");
1868     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1869     goto out;
1870   }
1871
1872   if (cmd->argc < 3) {
1873     /* Get passphrase */
1874     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1875                                      silc_client_command_silcoper_send,
1876                                      context);
1877     return;
1878   }
1879
1880   silc_client_command_silcoper_send(NULL, 0, context);
1881
1882  out:
1883   silc_client_command_free(cmd);
1884 }
1885
1886 /* Command BAN. This is used to manage the ban list of the channel. */
1887
1888 SILC_CLIENT_CMD_FUNC(ban)
1889 {
1890   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1891   SilcClientConnection conn = cmd->conn;
1892   SilcChannelEntry channel;
1893   SilcBuffer buffer, chidp;
1894   int type = 0;
1895   char *name, *ban = NULL;
1896
1897   if (!cmd->conn) {
1898     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1899     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1900     goto out;
1901   }
1902
1903   if (cmd->argc < 2) {
1904     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1905         "Usage: /BAN <channel> "
1906         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1907     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1908     goto out;
1909   }
1910
1911   if (cmd->argv[1][0] == '*') {
1912     if (!conn->current_channel) {
1913       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1914       goto out;
1915     }
1916
1917     channel = conn->current_channel;
1918   } else {
1919     name = cmd->argv[1];
1920
1921     channel = silc_client_get_channel(cmd->client, conn, name);
1922     if (!channel) {
1923       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1924       goto out;
1925     }
1926   }
1927
1928   if (cmd->argc == 3) {
1929     if (cmd->argv[2][0] == '+')
1930       type = 2;
1931     else
1932       type = 3;
1933
1934     ban = cmd->argv[2];
1935     ban++;
1936   }
1937
1938   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1939
1940   /* Send the command */
1941   buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 
1942                                           ++conn->cmd_ident, 2,
1943                                           1, chidp->data, chidp->len,
1944                                           type, ban, ban ? strlen(ban) : 0);
1945   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1946                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1947   silc_buffer_free(buffer);
1948   silc_buffer_free(chidp);
1949
1950   /* Notify application */
1951   COMMAND(SILC_STATUS_OK);
1952
1953  out:
1954   silc_client_command_free(cmd);
1955 }
1956
1957 /* Command DETACH. This is used to detach from the server */
1958
1959 SILC_CLIENT_CMD_FUNC(detach)
1960 {
1961   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1962   SilcClientConnection conn = cmd->conn;
1963   SilcBuffer buffer;
1964
1965   if (!cmd->conn) {
1966     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1967     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1968     goto out;
1969   }
1970
1971   buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH,
1972                                           ++conn->cmd_ident, 0);
1973   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1974                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1975   silc_buffer_free(buffer);
1976
1977   /* Notify application */
1978   COMMAND(SILC_STATUS_OK);
1979
1980  out:
1981   silc_client_command_free(cmd);
1982 }
1983
1984 /* Command WATCH. */
1985
1986 SILC_CLIENT_CMD_FUNC(watch)
1987 {
1988   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1989   SilcClientConnection conn = cmd->conn;
1990   SilcBuffer buffer, idp = NULL;
1991   int type = 0;
1992
1993   if (!cmd->conn) {
1994     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1995     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1996     goto out;
1997   }
1998
1999   if (cmd->argc < 3) {
2000     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2001     goto out;
2002   }
2003
2004   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
2005
2006   if (!strcasecmp(cmd->argv[1], "-add")) {
2007     type = 2;
2008   } else if (!strcasecmp(cmd->argv[1], "-del")) {
2009     type = 3;
2010   } else {
2011     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2012     goto out;
2013   }
2014
2015   buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH, 
2016                                           ++conn->cmd_ident, 2,
2017                                           1, idp->data, idp->len,
2018                                           type, cmd->argv[2],
2019                                           cmd->argv_lens[2]);
2020   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2021                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2022   silc_buffer_free(buffer);
2023
2024   /* Notify application */
2025   COMMAND(SILC_STATUS_OK);
2026
2027  out:
2028   if (idp)
2029     silc_buffer_free(idp);
2030   silc_client_command_free(cmd);
2031 }
2032
2033 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2034
2035 SILC_CLIENT_CMD_FUNC(leave)
2036 {
2037   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2038   SilcClientConnection conn = cmd->conn;
2039   SilcChannelEntry channel;
2040   SilcChannelUser chu;
2041   SilcBuffer buffer, idp;
2042   char *name;
2043
2044   if (!cmd->conn) {
2045     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2046     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2047     goto out;
2048   }
2049
2050   if (cmd->argc != 2) {
2051     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2052         "Usage: /LEAVE <channel>");
2053     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2054     goto out;
2055   }
2056
2057   if (cmd->argv[1][0] == '*') {
2058     if (!conn->current_channel) {
2059       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2060       goto out;
2061     }
2062     name = conn->current_channel->channel_name;
2063   } else {
2064     name = cmd->argv[1];
2065   }
2066
2067   /* Get the channel entry */
2068   channel = silc_client_get_channel(cmd->client, conn, name);
2069   if (!channel) {
2070     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2071     goto out;
2072   }
2073
2074   /* Remove us from channel */
2075   chu = silc_client_on_channel(channel, conn->local_entry);
2076   if (chu) {
2077     silc_hash_table_del(chu->client->channels, chu->channel);
2078     silc_hash_table_del(chu->channel->user_list, chu->client);
2079     silc_free(chu);
2080   }
2081
2082   /* Send LEAVE command to the server */
2083   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2084   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
2085                                           1, idp->data, idp->len);
2086   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2087                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2088   silc_buffer_free(buffer);
2089   silc_buffer_free(idp);
2090
2091   /* Notify application */
2092   COMMAND(SILC_STATUS_OK);
2093
2094   if (conn->current_channel == channel)
2095     conn->current_channel = NULL;
2096
2097   silc_client_del_channel(cmd->client, cmd->conn, channel);
2098
2099  out:
2100   silc_client_command_free(cmd);
2101 }
2102
2103 /* Command USERS. Requests the USERS of the clients joined on requested
2104    channel. */
2105
2106 SILC_CLIENT_CMD_FUNC(users)
2107 {
2108   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2109   SilcClientConnection conn = cmd->conn;
2110   SilcBuffer buffer;
2111   char *name;
2112
2113   if (!cmd->conn) {
2114     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2115     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2116     goto out;
2117   }
2118
2119   if (cmd->argc != 2) {
2120     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2121         "Usage: /USERS <channel>");
2122     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2123     goto out;
2124   }
2125
2126   if (cmd->argv[1][0] == '*') {
2127     if (!conn->current_channel) {
2128       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2129       goto out;
2130     }
2131     name = conn->current_channel->channel_name;
2132   } else {
2133     name = cmd->argv[1];
2134   }
2135
2136   /* Send USERS command to the server */
2137   buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
2138                                           ++conn->cmd_ident, 1, 
2139                                           2, name, strlen(name));
2140   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
2141                           NULL, 0, NULL, NULL, buffer->data, 
2142                           buffer->len, TRUE);
2143   silc_buffer_free(buffer);
2144
2145   /* Notify application */
2146   COMMAND(SILC_STATUS_OK);
2147
2148  out:
2149   silc_client_command_free(cmd);
2150 }
2151
2152 /* Command GETKEY. Used to fetch remote client's public key. */
2153
2154 SILC_CLIENT_CMD_FUNC(getkey)
2155 {
2156   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2157   SilcClientConnection conn = cmd->conn;
2158   SilcClient client = cmd->client;
2159   SilcClientEntry client_entry = NULL;
2160   SilcServerEntry server_entry = NULL;
2161   char *nickname = NULL;
2162   SilcBuffer idp, buffer;
2163
2164   SILC_LOG_DEBUG(("Start"));
2165
2166   if (!cmd->conn) {
2167     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2168     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2169     goto out;
2170   }
2171
2172   if (cmd->argc < 2) {
2173     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
2174                      "Usage: /GETKEY <nickname or server name>");
2175     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2176     goto out;
2177   }
2178
2179   /* Parse the typed nickname. */
2180   if (client->internal->params->nickname_parse)
2181     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2182   else
2183     nickname = strdup(cmd->argv[1]);
2184
2185   /* Find client entry */
2186   client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2187                                         FALSE);
2188   if (!client_entry) {
2189     /* Check whether user requested server actually */
2190     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2191
2192     if (!server_entry) {
2193       /* No. what ever user wants we don't have it, so resolve it. We
2194          will first try to resolve the client, and if that fails then
2195          we'll try to resolve the server. */
2196
2197       if (!cmd->pending) {
2198         /* This will send the IDENTIFY command for nickname */
2199         silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2200         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2201                                     conn->cmd_ident,  
2202                                     silc_client_command_getkey, 
2203                                     silc_client_command_dup(cmd));
2204         cmd->pending = 1;
2205         goto out;
2206       } else {
2207         SilcClientCommandReplyContext reply = 
2208           (SilcClientCommandReplyContext)context2;
2209         SilcStatus error;
2210
2211         /* If nickname was not found, then resolve the server. */
2212         silc_command_get_status(reply->payload, NULL, &error);
2213         if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
2214           /* This sends the IDENTIFY command to resolve the server. */
2215           silc_client_command_register(client, SILC_COMMAND_IDENTIFY, 
2216                                        NULL, NULL,
2217                                        silc_client_command_reply_identify_i, 0,
2218                                        ++conn->cmd_ident);
2219           silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2220                                    conn->cmd_ident, 1, 
2221                                    2, cmd->argv[1], cmd->argv_lens[1]);
2222           silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2223                                       conn->cmd_ident, 
2224                                       silc_client_command_getkey, 
2225                                       silc_client_command_dup(cmd));
2226           goto out;
2227         }
2228
2229         /* If server was not found, then we've resolved both nickname and
2230            server and did not find anybody. */
2231         if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2232           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2233              silc_get_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2234           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2235            silc_get_status_message(error));
2236           COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2237           goto out;
2238         }
2239
2240         COMMAND_ERROR(error);
2241         goto out;
2242       }
2243     }
2244
2245     idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2246   } else {
2247     idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2248   }
2249
2250   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
2251                                           1, idp->data, idp->len);
2252   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2253                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2254   silc_buffer_free(buffer);
2255   silc_buffer_free(idp);
2256
2257   /* Notify application */
2258   COMMAND(SILC_STATUS_OK);
2259
2260  out:
2261   silc_free(nickname);
2262   silc_client_command_free(cmd);
2263 }
2264
2265 /* Register a new command indicated by the `command' to the SILC client.
2266    The `name' is optional command name.  If provided the command may be
2267    searched using the silc_client_command_find by that name.  The
2268    `command_function' is the function to be called when the command is
2269    executed, and the `command_reply_function' is the function to be
2270    called after the server has sent reply back to the command. 
2271
2272    The `ident' is optional identifier for the command.  If non-zero
2273    the `command_reply_function' for the command type `command' will be
2274    called only if the command reply sent by server includes the 
2275    command identifier `ident'. Application usually does not need it
2276    and set it to zero value. */
2277
2278 bool silc_client_command_register(SilcClient client,
2279                                   SilcCommand command,
2280                                   const char *name,
2281                                   SilcCommandCb command_function,
2282                                   SilcCommandCb command_reply_function,
2283                                   SilcUInt8 max_args,
2284                                   SilcUInt16 ident)
2285 {
2286   SilcClientCommand cmd;
2287
2288   cmd = silc_calloc(1, sizeof(*cmd));
2289   cmd->cmd = command;
2290   cmd->command = command_function;
2291   cmd->reply = command_reply_function;
2292   cmd->name = name ? strdup(name) : NULL;
2293   cmd->max_args = max_args;
2294   cmd->ident = ident;
2295
2296   silc_list_add(client->internal->commands, cmd);
2297
2298   return TRUE;
2299 }
2300
2301 /* Unregister a command indicated by the `command' with command function
2302    `command_function' and command reply function `command_reply_function'.
2303    Returns TRUE if the command was found and unregistered. */
2304
2305 bool silc_client_command_unregister(SilcClient client,
2306                                     SilcCommand command,
2307                                     SilcCommandCb command_function,
2308                                     SilcCommandCb command_reply_function,
2309                                     SilcUInt16 ident)
2310 {
2311   SilcClientCommand cmd;
2312
2313   silc_list_start(client->internal->commands);
2314   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2315     if (cmd->cmd == command && cmd->command == command_function &&
2316         cmd->reply == command_reply_function && cmd->ident == ident) {
2317       silc_list_del(client->internal->commands, cmd);
2318       silc_free(cmd->name);
2319       silc_free(cmd);
2320       return TRUE;
2321     }
2322   }
2323
2324   return FALSE;
2325 }
2326
2327 /* Private range commands, specific to this implementation (and compatible
2328    with SILC Server). */
2329
2330 /* CONNECT command. Connects the server to another server. */
2331
2332 SILC_CLIENT_CMD_FUNC(connect)
2333 {
2334   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2335   SilcClientConnection conn = cmd->conn;
2336   SilcBuffer buffer;
2337   unsigned char port[4];
2338   SilcUInt32 tmp;
2339
2340   if (!cmd->conn) {
2341     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2342     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2343     goto out;
2344   }
2345
2346   if (cmd->argc < 2) {
2347     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2348         "Usage: /CONNECT <server> [<port>]");
2349     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2350     goto out;
2351   }
2352
2353   if (cmd->argc == 3) {
2354     tmp = atoi(cmd->argv[2]);
2355     SILC_PUT32_MSB(tmp, port);
2356   }
2357
2358   if (cmd->argc == 3)
2359     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2, 
2360                                             1, cmd->argv[1], 
2361                                             strlen(cmd->argv[1]),
2362                                             2, port, 4);
2363   else
2364     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2365                                             1, cmd->argv[1], 
2366                                             strlen(cmd->argv[1]));
2367   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2368                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2369   silc_buffer_free(buffer);
2370
2371   /* Notify application */
2372   COMMAND(SILC_STATUS_OK);
2373
2374  out:
2375   silc_client_command_free(cmd);
2376 }
2377
2378
2379 /* CLOSE command. Close server connection to the remote server */
2380  
2381 SILC_CLIENT_CMD_FUNC(close)
2382 {
2383   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2384   SilcClientConnection conn = cmd->conn;
2385   SilcBuffer buffer;
2386   unsigned char port[4];
2387   SilcUInt32 tmp;
2388
2389   if (!cmd->conn) {
2390     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2391     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2392     goto out;
2393   }
2394
2395   if (cmd->argc < 2) {
2396     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2397         "Usage: /CLOSE <server> [<port>]");
2398     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2399     goto out;
2400   }
2401
2402   if (cmd->argc == 3) {
2403     tmp = atoi(cmd->argv[2]);
2404     SILC_PUT32_MSB(tmp, port);
2405   }
2406
2407   if (cmd->argc == 3)
2408     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2, 
2409                                             1, cmd->argv[1], 
2410                                             strlen(cmd->argv[1]),
2411                                             2, port, 4);
2412   else
2413     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2414                                             1, cmd->argv[1], 
2415                                             strlen(cmd->argv[1]));
2416   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2417                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2418   silc_buffer_free(buffer);
2419
2420   /* Notify application */
2421   COMMAND(SILC_STATUS_OK);
2422
2423  out:
2424   silc_client_command_free(cmd);
2425 }
2426  
2427 /* SHUTDOWN command. Shutdowns the server. */
2428
2429 SILC_CLIENT_CMD_FUNC(shutdown)
2430 {
2431   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2432
2433   if (!cmd->conn) {
2434     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2435     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2436     goto out;
2437   }
2438
2439   /* Send the command */
2440   silc_client_command_send(cmd->client, cmd->conn, 
2441                            SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2442
2443   /* Notify application */
2444   COMMAND(SILC_STATUS_OK);
2445
2446  out:
2447   silc_client_command_free(cmd);
2448 }
2449
2450 /* Register all default commands provided by the client library for the
2451    application. */
2452
2453 void silc_client_commands_register(SilcClient client)
2454 {
2455   silc_list_init(client->internal->commands, struct SilcClientCommandStruct, 
2456                  next);
2457
2458   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2459   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2460   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2461   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2462   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2463   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2464   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2465   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2466   SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
2467   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2468   SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2469   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2470   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2471   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2472   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2473   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2474   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
2475   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
2476   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2477   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2478   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2479   SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2480   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2481   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2482   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2483   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2484
2485   SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2486   SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2487   SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2488 }
2489
2490 /* Unregister all commands. */
2491
2492 void silc_client_commands_unregister(SilcClient client)
2493 {
2494   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2495   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2496   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2497   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2498   SILC_CLIENT_CMDU(list, LIST, "LIST");
2499   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2500   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2501   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2502   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2503   SILC_CLIENT_CMDU(info, INFO, "INFO");
2504   SILC_CLIENT_CMDU(stats, STATS, "STATS");
2505   SILC_CLIENT_CMDU(ping, PING, "PING");
2506   SILC_CLIENT_CMDU(oper, OPER, "OPER");
2507   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2508   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2509   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2510   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2511   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2512   SILC_CLIENT_CMDU(kick, KICK, "KICK");
2513   SILC_CLIENT_CMDU(ban, BAN, "BAN");
2514   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2515   SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2516   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2517   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2518   SILC_CLIENT_CMDU(users, USERS, "USERS");
2519   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2520
2521   SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2522   SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2523   SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");
2524 }