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