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