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