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