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