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