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