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 ? auth->data : NULL,
1764                                           auth ? auth->len : 0);
1765   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1766                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1767
1768   silc_buffer_free(buffer);
1769   silc_buffer_free(auth);
1770
1771   /* Notify application */
1772   COMMAND(SILC_STATUS_OK);
1773 }
1774
1775 /* OPER command. Used to obtain server operator privileges. */
1776
1777 SILC_CLIENT_CMD_FUNC(oper)
1778 {
1779   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1780   SilcClientConnection conn = cmd->conn;
1781
1782   if (!cmd->conn) {
1783     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1784     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1785     goto out;
1786   }
1787
1788   if (cmd->argc < 2) {
1789     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1790         "Usage: /OPER <username> [-pubkey]");
1791     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1792     goto out;
1793   }
1794
1795   if (cmd->argc < 3) {
1796     /* Get passphrase */
1797     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1798                                      silc_client_command_oper_send,
1799                                      context);
1800     return;
1801   }
1802
1803   silc_client_command_oper_send(NULL, 0, context);
1804
1805  out:
1806   silc_client_command_free(cmd);
1807 }
1808
1809 static void silc_client_command_silcoper_send(unsigned char *data,
1810                                               SilcUInt32 data_len, 
1811                                               void *context)
1812 {
1813   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1814   SilcClientConnection conn = cmd->conn;
1815   SilcBuffer buffer, auth;
1816
1817   if (cmd->argc >= 3) {
1818     /* Encode the public key authentication payload */
1819     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1820                                               cmd->client->private_key,
1821                                               cmd->client->rng, conn->hash,
1822                                               conn->local_id,
1823                                               SILC_ID_CLIENT);
1824   } else {
1825     /* Encode the password authentication payload */
1826     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1827                                     data, data_len);
1828   }
1829
1830   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1831                                           1, cmd->argv[1], 
1832                                           strlen(cmd->argv[1]),
1833                                           2, auth ? auth->data : NULL,
1834                                           auth ? auth->len : 0);
1835   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1836                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1837
1838   silc_buffer_free(buffer);
1839   silc_buffer_free(auth);
1840
1841   /* Notify application */
1842   COMMAND(SILC_STATUS_OK);
1843 }
1844
1845 /* SILCOPER command. Used to obtain router operator privileges. */
1846
1847 SILC_CLIENT_CMD_FUNC(silcoper)
1848 {
1849   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1850   SilcClientConnection conn = cmd->conn;
1851
1852   if (!cmd->conn) {
1853     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1854     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1855     goto out;
1856   }
1857
1858   if (cmd->argc < 2) {
1859     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1860         "Usage: /SILCOPER <username> [-pubkey]");
1861     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1862     goto out;
1863   }
1864
1865   if (cmd->argc < 3) {
1866     /* Get passphrase */
1867     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1868                                      silc_client_command_silcoper_send,
1869                                      context);
1870     return;
1871   }
1872
1873   silc_client_command_silcoper_send(NULL, 0, context);
1874
1875  out:
1876   silc_client_command_free(cmd);
1877 }
1878
1879 /* Command BAN. This is used to manage the ban list of the channel. */
1880
1881 SILC_CLIENT_CMD_FUNC(ban)
1882 {
1883   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1884   SilcClientConnection conn = cmd->conn;
1885   SilcChannelEntry channel;
1886   SilcBuffer buffer, chidp;
1887   int type = 0;
1888   char *name, *ban = NULL;
1889
1890   if (!cmd->conn) {
1891     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1892     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1893     goto out;
1894   }
1895
1896   if (cmd->argc < 2) {
1897     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1898         "Usage: /BAN <channel> "
1899         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1900     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1901     goto out;
1902   }
1903
1904   if (cmd->argv[1][0] == '*') {
1905     if (!conn->current_channel) {
1906       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1907           "You are not on any channel");
1908       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1909       goto out;
1910     }
1911
1912     channel = conn->current_channel;
1913   } else {
1914     name = cmd->argv[1];
1915
1916     channel = silc_client_get_channel(cmd->client, conn, name);
1917     if (!channel) {
1918       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1919           "You are noton that channel");
1920       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1921       goto out;
1922     }
1923   }
1924
1925   if (cmd->argc == 3) {
1926     if (cmd->argv[2][0] == '+')
1927       type = 2;
1928     else
1929       type = 3;
1930
1931     ban = cmd->argv[2];
1932     ban++;
1933   }
1934
1935   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1936
1937   /* Send the command */
1938   buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 
1939                                           ++conn->cmd_ident, 2,
1940                                           1, chidp->data, chidp->len,
1941                                           type, ban, ban ? strlen(ban) : 0);
1942   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1943                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1944   silc_buffer_free(buffer);
1945   silc_buffer_free(chidp);
1946
1947   /* Notify application */
1948   COMMAND(SILC_STATUS_OK);
1949
1950  out:
1951   silc_client_command_free(cmd);
1952 }
1953
1954 /* Command DETACH. This is used to detach from the server */
1955
1956 SILC_CLIENT_CMD_FUNC(detach)
1957 {
1958   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1959   SilcClientConnection conn = cmd->conn;
1960   SilcBuffer buffer;
1961
1962   if (!cmd->conn) {
1963     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1964     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1965     goto out;
1966   }
1967
1968   buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH,
1969                                           ++conn->cmd_ident, 0);
1970   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1971                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1972   silc_buffer_free(buffer);
1973
1974   /* Notify application */
1975   COMMAND(SILC_STATUS_OK);
1976
1977  out:
1978   silc_client_command_free(cmd);
1979 }
1980
1981 /* Command WATCH. */
1982
1983 SILC_CLIENT_CMD_FUNC(watch)
1984 {
1985   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1986   SilcClientConnection conn = cmd->conn;
1987   SilcBuffer buffer, idp = NULL;
1988   int type = 0;
1989
1990   if (!cmd->conn) {
1991     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1992     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1993     goto out;
1994   }
1995
1996   if (cmd->argc < 3) {
1997     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1998     goto out;
1999   }
2000
2001   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
2002
2003   if (!strcasecmp(cmd->argv[1], "-add")) {
2004     type = 2;
2005   } else if (!strcasecmp(cmd->argv[1], "-del")) {
2006     type = 3;
2007   } else {
2008     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2009     goto out;
2010   }
2011
2012   buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH, 
2013                                           ++conn->cmd_ident, 2,
2014                                           1, idp->data, idp->len,
2015                                           type, cmd->argv[2],
2016                                           cmd->argv_lens[2]);
2017   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2018                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2019   silc_buffer_free(buffer);
2020
2021   /* Notify application */
2022   COMMAND(SILC_STATUS_OK);
2023
2024  out:
2025   if (idp)
2026     silc_buffer_free(idp);
2027   silc_client_command_free(cmd);
2028 }
2029
2030 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2031
2032 SILC_CLIENT_CMD_FUNC(leave)
2033 {
2034   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2035   SilcClientConnection conn = cmd->conn;
2036   SilcChannelEntry channel;
2037   SilcChannelUser chu;
2038   SilcBuffer buffer, idp;
2039   char *name;
2040
2041   if (!cmd->conn) {
2042     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2043     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2044     goto out;
2045   }
2046
2047   if (cmd->argc != 2) {
2048     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2049         "Usage: /LEAVE <channel>");
2050     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2051     goto out;
2052   }
2053
2054   if (cmd->argv[1][0] == '*') {
2055     if (!conn->current_channel) {
2056       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2057           "You are not on any channel");
2058       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2059       goto out;
2060     }
2061     name = conn->current_channel->channel_name;
2062   } else {
2063     name = cmd->argv[1];
2064   }
2065
2066   /* Get the channel entry */
2067   channel = silc_client_get_channel(cmd->client, conn, name);
2068   if (!channel) {
2069     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2070         "You are not on that channel");
2071     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2072     goto out;
2073   }
2074
2075   /* Remove us from channel */
2076   chu = silc_client_on_channel(channel, conn->local_entry);
2077   if (chu) {
2078     silc_hash_table_del(chu->client->channels, chu->channel);
2079     silc_hash_table_del(chu->channel->user_list, chu->client);
2080     silc_free(chu);
2081   }
2082
2083   /* Send LEAVE command to the server */
2084   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2085   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
2086                                           1, idp->data, idp->len);
2087   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2088                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2089   silc_buffer_free(buffer);
2090   silc_buffer_free(idp);
2091
2092   /* Notify application */
2093   COMMAND(SILC_STATUS_OK);
2094
2095   if (conn->current_channel == channel)
2096     conn->current_channel = NULL;
2097
2098   silc_client_del_channel(cmd->client, cmd->conn, channel);
2099
2100  out:
2101   silc_client_command_free(cmd);
2102 }
2103
2104 /* Command USERS. Requests the USERS of the clients joined on requested
2105    channel. */
2106
2107 SILC_CLIENT_CMD_FUNC(users)
2108 {
2109   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2110   SilcClientConnection conn = cmd->conn;
2111   SilcBuffer buffer;
2112   char *name;
2113
2114   if (!cmd->conn) {
2115     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2116     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2117     goto out;
2118   }
2119
2120   if (cmd->argc != 2) {
2121     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2122         "Usage: /USERS <channel>");
2123     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2124     goto out;
2125   }
2126
2127   if (cmd->argv[1][0] == '*') {
2128     if (!conn->current_channel) {
2129       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2130           "You are not on any channel");
2131       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2132       goto out;
2133     }
2134     name = conn->current_channel->channel_name;
2135   } else {
2136     name = cmd->argv[1];
2137   }
2138
2139   /* Send USERS command to the server */
2140   buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
2141                                           ++conn->cmd_ident, 1, 
2142                                           2, name, strlen(name));
2143   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
2144                           NULL, 0, NULL, NULL, buffer->data, 
2145                           buffer->len, TRUE);
2146   silc_buffer_free(buffer);
2147
2148   /* Notify application */
2149   COMMAND(SILC_STATUS_OK);
2150
2151  out:
2152   silc_client_command_free(cmd);
2153 }
2154
2155 /* Command GETKEY. Used to fetch remote client's public key. */
2156
2157 SILC_CLIENT_CMD_FUNC(getkey)
2158 {
2159   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2160   SilcClientConnection conn = cmd->conn;
2161   SilcClient client = cmd->client;
2162   SilcClientEntry client_entry = NULL;
2163   SilcServerEntry server_entry = NULL;
2164   char *nickname = NULL;
2165   SilcBuffer idp, buffer;
2166
2167   SILC_LOG_DEBUG(("Start"));
2168
2169   if (!cmd->conn) {
2170     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2171     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2172     goto out;
2173   }
2174
2175   if (cmd->argc < 2) {
2176     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
2177                      "Usage: /GETKEY <nickname or server name>");
2178     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2179     goto out;
2180   }
2181
2182   /* Parse the typed nickname. */
2183   if (client->internal->params->nickname_parse)
2184     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2185   else
2186     nickname = strdup(cmd->argv[1]);
2187
2188   /* Find client entry */
2189   client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2190                                         FALSE);
2191   if (!client_entry) {
2192     /* Check whether user requested server actually */
2193     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2194
2195     if (!server_entry) {
2196       /* No. what ever user wants we don't have it, so resolve it. We
2197          will first try to resolve the client, and if that fails then
2198          we'll try to resolve the server. */
2199
2200       if (!cmd->pending) {
2201         /* This will send the IDENTIFY command for nickname */
2202         silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2203         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2204                                     conn->cmd_ident,  
2205                                     silc_client_command_getkey, 
2206                                     silc_client_command_dup(cmd));
2207         cmd->pending = 1;
2208         goto out;
2209       } else {
2210         SilcClientCommandReplyContext reply = 
2211           (SilcClientCommandReplyContext)context2;
2212         SilcStatus error;
2213
2214         /* If nickname was not found, then resolve the server. */
2215         silc_command_get_status(reply->payload, NULL, &error);
2216         if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
2217           /* This sends the IDENTIFY command to resolve the server. */
2218           silc_client_command_register(client, SILC_COMMAND_IDENTIFY, 
2219                                        NULL, NULL,
2220                                        silc_client_command_reply_identify_i, 0,
2221                                        ++conn->cmd_ident);
2222           silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2223                                    conn->cmd_ident, 1, 
2224                                    2, cmd->argv[1], cmd->argv_lens[1]);
2225           silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2226                                       conn->cmd_ident, 
2227                                       silc_client_command_getkey, 
2228                                       silc_client_command_dup(cmd));
2229           goto out;
2230         }
2231
2232         /* If server was not found, then we've resolved both nickname and
2233            server and did not find anybody. */
2234         if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2235           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2236              silc_get_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2237           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2238            silc_get_status_message(error));
2239           COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2240           goto out;
2241         }
2242
2243         COMMAND_ERROR(error);
2244         goto out;
2245       }
2246     }
2247
2248     idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2249   } else {
2250     idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2251   }
2252
2253   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
2254                                           1, idp->data, idp->len);
2255   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2256                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2257   silc_buffer_free(buffer);
2258   silc_buffer_free(idp);
2259
2260   /* Notify application */
2261   COMMAND(SILC_STATUS_OK);
2262
2263  out:
2264   silc_free(nickname);
2265   silc_client_command_free(cmd);
2266 }
2267
2268 /* Register a new command indicated by the `command' to the SILC client.
2269    The `name' is optional command name.  If provided the command may be
2270    searched using the silc_client_command_find by that name.  The
2271    `command_function' is the function to be called when the command is
2272    executed, and the `command_reply_function' is the function to be
2273    called after the server has sent reply back to the command. 
2274
2275    The `ident' is optional identifier for the command.  If non-zero
2276    the `command_reply_function' for the command type `command' will be
2277    called only if the command reply sent by server includes the 
2278    command identifier `ident'. Application usually does not need it
2279    and set it to zero value. */
2280
2281 bool silc_client_command_register(SilcClient client,
2282                                   SilcCommand command,
2283                                   const char *name,
2284                                   SilcCommandCb command_function,
2285                                   SilcCommandCb command_reply_function,
2286                                   SilcUInt8 max_args,
2287                                   SilcUInt16 ident)
2288 {
2289   SilcClientCommand cmd;
2290
2291   cmd = silc_calloc(1, sizeof(*cmd));
2292   cmd->cmd = command;
2293   cmd->command = command_function;
2294   cmd->reply = command_reply_function;
2295   cmd->name = name ? strdup(name) : NULL;
2296   cmd->max_args = max_args;
2297   cmd->ident = ident;
2298
2299   silc_list_add(client->internal->commands, cmd);
2300
2301   return TRUE;
2302 }
2303
2304 /* Unregister a command indicated by the `command' with command function
2305    `command_function' and command reply function `command_reply_function'.
2306    Returns TRUE if the command was found and unregistered. */
2307
2308 bool silc_client_command_unregister(SilcClient client,
2309                                     SilcCommand command,
2310                                     SilcCommandCb command_function,
2311                                     SilcCommandCb command_reply_function,
2312                                     SilcUInt16 ident)
2313 {
2314   SilcClientCommand cmd;
2315
2316   silc_list_start(client->internal->commands);
2317   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2318     if (cmd->cmd == command && cmd->command == command_function &&
2319         cmd->reply == command_reply_function && cmd->ident == ident) {
2320       silc_list_del(client->internal->commands, cmd);
2321       silc_free(cmd->name);
2322       silc_free(cmd);
2323       return TRUE;
2324     }
2325   }
2326
2327   return FALSE;
2328 }
2329
2330 /* Private range commands, specific to this implementation (and compatible
2331    with SILC Server). */
2332
2333 /* CONNECT command. Connects the server to another server. */
2334
2335 SILC_CLIENT_CMD_FUNC(connect)
2336 {
2337   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2338   SilcClientConnection conn = cmd->conn;
2339   SilcBuffer buffer;
2340   unsigned char port[4];
2341   SilcUInt32 tmp;
2342
2343   if (!cmd->conn) {
2344     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2345     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2346     goto out;
2347   }
2348
2349   if (cmd->argc < 2) {
2350     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2351         "Usage: /CONNECT <server> [<port>]");
2352     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2353     goto out;
2354   }
2355
2356   if (cmd->argc == 3) {
2357     tmp = atoi(cmd->argv[2]);
2358     SILC_PUT32_MSB(tmp, port);
2359   }
2360
2361   if (cmd->argc == 3)
2362     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2, 
2363                                             1, cmd->argv[1], 
2364                                             strlen(cmd->argv[1]),
2365                                             2, port, 4);
2366   else
2367     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2368                                             1, cmd->argv[1], 
2369                                             strlen(cmd->argv[1]));
2370   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2371                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2372   silc_buffer_free(buffer);
2373
2374   /* Notify application */
2375   COMMAND(SILC_STATUS_OK);
2376
2377  out:
2378   silc_client_command_free(cmd);
2379 }
2380
2381
2382 /* CLOSE command. Close server connection to the remote server */
2383  
2384 SILC_CLIENT_CMD_FUNC(close)
2385 {
2386   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2387   SilcClientConnection conn = cmd->conn;
2388   SilcBuffer buffer;
2389   unsigned char port[4];
2390   SilcUInt32 tmp;
2391
2392   if (!cmd->conn) {
2393     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2394     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2395     goto out;
2396   }
2397
2398   if (cmd->argc < 2) {
2399     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2400         "Usage: /CLOSE <server> [<port>]");
2401     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2402     goto out;
2403   }
2404
2405   if (cmd->argc == 3) {
2406     tmp = atoi(cmd->argv[2]);
2407     SILC_PUT32_MSB(tmp, port);
2408   }
2409
2410   if (cmd->argc == 3)
2411     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2, 
2412                                             1, cmd->argv[1], 
2413                                             strlen(cmd->argv[1]),
2414                                             2, port, 4);
2415   else
2416     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2417                                             1, cmd->argv[1], 
2418                                             strlen(cmd->argv[1]));
2419   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2420                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2421   silc_buffer_free(buffer);
2422
2423   /* Notify application */
2424   COMMAND(SILC_STATUS_OK);
2425
2426  out:
2427   silc_client_command_free(cmd);
2428 }
2429  
2430 /* SHUTDOWN command. Shutdowns the server. */
2431
2432 SILC_CLIENT_CMD_FUNC(shutdown)
2433 {
2434   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2435
2436   if (!cmd->conn) {
2437     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2438     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2439     goto out;
2440   }
2441
2442   /* Send the command */
2443   silc_client_command_send(cmd->client, cmd->conn, 
2444                            SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2445
2446   /* Notify application */
2447   COMMAND(SILC_STATUS_OK);
2448
2449  out:
2450   silc_client_command_free(cmd);
2451 }
2452
2453 /* Register all default commands provided by the client library for the
2454    application. */
2455
2456 void silc_client_commands_register(SilcClient client)
2457 {
2458   silc_list_init(client->internal->commands, struct SilcClientCommandStruct, 
2459                  next);
2460
2461   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2462   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2463   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2464   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2465   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2466   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2467   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2468   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2469   SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
2470   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2471   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2472   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2473   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2474   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2475   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2476   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
2477   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
2478   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2479   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2480   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2481   SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2482   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2483   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2484   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2485   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2486
2487   SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2488   SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2489   SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2490 }
2491
2492 /* Unregister all commands. */
2493
2494 void silc_client_commands_unregister(SilcClient client)
2495 {
2496   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2497   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2498   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2499   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2500   SILC_CLIENT_CMDU(list, LIST, "LIST");
2501   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2502   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2503   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2504   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2505   SILC_CLIENT_CMDU(info, INFO, "INFO");
2506   SILC_CLIENT_CMDU(ping, PING, "PING");
2507   SILC_CLIENT_CMDU(oper, OPER, "OPER");
2508   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2509   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2510   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2511   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2512   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2513   SILC_CLIENT_CMDU(kick, KICK, "KICK");
2514   SILC_CLIENT_CMDU(ban, BAN, "BAN");
2515   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2516   SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2517   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2518   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2519   SILC_CLIENT_CMDU(users, USERS, "USERS");
2520   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2521
2522   SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2523   SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2524   SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");
2525 }