Merged from silc_1_0_branch.
[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   silc_free(passphrase);
1179
1180   /* Notify application */
1181   COMMAND(SILC_STATUS_OK);
1182
1183  out:
1184   silc_client_command_free(cmd);
1185 }
1186
1187 /* MOTD command. Requests motd from server. */
1188
1189 SILC_CLIENT_CMD_FUNC(motd)
1190 {
1191   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1192   SilcClientConnection conn = cmd->conn;
1193   SilcBuffer buffer;
1194
1195   if (!cmd->conn) {
1196     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1197     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1198     goto out;
1199   }
1200
1201   if (cmd->argc < 1 || cmd->argc > 2) {
1202     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1203         "Usage: /MOTD [<server>]");
1204     COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1205                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
1206     goto out;
1207   }
1208
1209   /* Send TOPIC command to the server */
1210   if (cmd->argc == 1)
1211     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1212                                             1, conn->remote_host, 
1213                                             strlen(conn->remote_host));
1214   else
1215     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1216                                             1, cmd->argv[1], 
1217                                             cmd->argv_lens[1]);
1218   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1219                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1220   silc_buffer_free(buffer);
1221
1222   /* Notify application */
1223   COMMAND(SILC_STATUS_OK);
1224
1225  out:
1226   silc_client_command_free(cmd);
1227 }
1228
1229 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1230    modes as client cannot set itself server/router operator privileges. */
1231
1232 SILC_CLIENT_CMD_FUNC(umode)
1233 {
1234   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1235   SilcClientConnection conn = cmd->conn;
1236   SilcBuffer buffer, idp;
1237   unsigned char *cp, modebuf[4];
1238   SilcUInt32 mode, add, len;
1239   int i;
1240
1241   if (!cmd->conn) {
1242     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1243     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1244     goto out;
1245   }
1246
1247   if (cmd->argc < 2) {
1248     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1249         "Usage: /UMODE +|-<modes>");
1250     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1251     goto out;
1252   }
1253
1254   mode = conn->local_entry->mode;
1255
1256   /* Are we adding or removing mode */
1257   if (cmd->argv[1][0] == '-')
1258     add = FALSE;
1259   else
1260     add = TRUE;
1261
1262   /* Parse mode */
1263   cp = cmd->argv[1] + 1;
1264   len = strlen(cp);
1265   for (i = 0; i < len; i++) {
1266     switch(cp[i]) {
1267     case 'a':
1268       if (add) {
1269         mode = 0;
1270         mode |= SILC_UMODE_SERVER_OPERATOR;
1271         mode |= SILC_UMODE_ROUTER_OPERATOR;
1272         mode |= SILC_UMODE_GONE;
1273         mode |= SILC_UMODE_INDISPOSED;
1274         mode |= SILC_UMODE_BUSY;
1275         mode |= SILC_UMODE_PAGE;
1276         mode |= SILC_UMODE_HYPER;
1277         mode |= SILC_UMODE_ROBOT;
1278         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1279         mode |= SILC_UMODE_REJECT_WATCHING;
1280       } else {
1281         mode = SILC_UMODE_NONE;
1282       }
1283       break;
1284     case 's':
1285       if (add)
1286         mode |= SILC_UMODE_SERVER_OPERATOR;
1287       else
1288         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1289       break;
1290     case 'r':
1291       if (add)
1292         mode |= SILC_UMODE_ROUTER_OPERATOR;
1293       else
1294         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1295       break;
1296     case 'g':
1297       if (add)
1298         mode |= SILC_UMODE_GONE;
1299       else
1300         mode &= ~SILC_UMODE_GONE;
1301       break;
1302     case 'i':
1303       if (add)
1304         mode |= SILC_UMODE_INDISPOSED;
1305       else
1306         mode &= ~SILC_UMODE_INDISPOSED;
1307       break;
1308     case 'b':
1309       if (add)
1310         mode |= SILC_UMODE_BUSY;
1311       else
1312         mode &= ~SILC_UMODE_BUSY;
1313       break;
1314     case 'p':
1315       if (add)
1316         mode |= SILC_UMODE_PAGE;
1317       else
1318         mode &= ~SILC_UMODE_PAGE;
1319       break;
1320     case 'h':
1321       if (add)
1322         mode |= SILC_UMODE_HYPER;
1323       else
1324         mode &= ~SILC_UMODE_HYPER;
1325       break;
1326     case 't':
1327       if (add)
1328         mode |= SILC_UMODE_ROBOT;
1329       else
1330         mode &= ~SILC_UMODE_ROBOT;
1331       break;
1332     case 'P':
1333       if (add)
1334         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1335       else
1336         mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1337       break;
1338     case 'w':
1339       if (add)
1340         mode |= SILC_UMODE_REJECT_WATCHING;
1341       else
1342         mode &= ~SILC_UMODE_REJECT_WATCHING;
1343       break;
1344     case 'I':
1345       if (add)
1346         mode |= SILC_UMODE_BLOCK_INVITE;
1347       else
1348         mode &= ~SILC_UMODE_BLOCK_INVITE;
1349       break;
1350     default:
1351       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1352       goto out;
1353       break;
1354     }
1355   }
1356
1357   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1358   SILC_PUT32_MSB(mode, modebuf);
1359
1360   /* Send the command packet. We support sending only one mode at once
1361      that requires an argument. */
1362   buffer = 
1363     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1364                                    1, idp->data, idp->len, 
1365                                    2, modebuf, sizeof(modebuf));
1366   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1367                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1368   silc_buffer_free(buffer);
1369   silc_buffer_free(idp);
1370
1371   /* Notify application */
1372   COMMAND(SILC_STATUS_OK);
1373
1374  out:
1375   silc_client_command_free(cmd);
1376 }
1377
1378 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1379    can be set several at once. Those modes that require argument must be set
1380    separately (unless set with modes that does not require arguments). */
1381
1382 SILC_CLIENT_CMD_FUNC(cmode)
1383 {
1384   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1385   SilcClientConnection conn = cmd->conn;
1386   SilcChannelEntry channel;
1387   SilcBuffer buffer, chidp, auth = NULL, pk = NULL;
1388   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1389   SilcUInt32 mode, add, type, len, arg_len = 0;
1390   int i;
1391
1392   if (!cmd->conn) {
1393     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1394     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1395     goto out;
1396   }
1397
1398   if (cmd->argc < 3) {
1399     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1400         "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1401     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1402     goto out;
1403   }
1404
1405   if (cmd->argv[1][0] == '*') {
1406     if (!conn->current_channel) {
1407       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1408       goto out;
1409     }
1410
1411     channel = conn->current_channel;
1412   } else {
1413     name = cmd->argv[1];
1414
1415     channel = silc_client_get_channel(cmd->client, conn, name);
1416     if (!channel) {
1417       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1418       goto out;
1419     }
1420   }
1421
1422   mode = channel->mode;
1423
1424   /* Are we adding or removing mode */
1425   if (cmd->argv[2][0] == '-')
1426     add = FALSE;
1427   else
1428     add = TRUE;
1429
1430   /* Argument type to be sent to server */
1431   type = 0;
1432
1433   /* Parse mode */
1434   cp = cmd->argv[2] + 1;
1435   len = strlen(cp);
1436   for (i = 0; i < len; i++) {
1437     switch(cp[i]) {
1438     case 'p':
1439       if (add)
1440         mode |= SILC_CHANNEL_MODE_PRIVATE;
1441       else
1442         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1443       break;
1444     case 's':
1445       if (add)
1446         mode |= SILC_CHANNEL_MODE_SECRET;
1447       else
1448         mode &= ~SILC_CHANNEL_MODE_SECRET;
1449       break;
1450     case 'k':
1451       if (add)
1452         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1453       else
1454         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1455       break;
1456     case 'i':
1457       if (add)
1458         mode |= SILC_CHANNEL_MODE_INVITE;
1459       else
1460         mode &= ~SILC_CHANNEL_MODE_INVITE;
1461       break;
1462     case 't':
1463       if (add)
1464         mode |= SILC_CHANNEL_MODE_TOPIC;
1465       else
1466         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1467       break;
1468     case 'm':
1469       if (add)
1470         mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1471       else
1472         mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1473       break;
1474     case 'M':
1475       if (add)
1476         mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1477       else
1478         mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1479       break;
1480     case 'l':
1481       if (add) {
1482         int ll;
1483         mode |= SILC_CHANNEL_MODE_ULIMIT;
1484         type = 3;
1485         if (cmd->argc < 4) {
1486           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1487               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1488           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1489           goto out;
1490         }
1491         ll = atoi(cmd->argv[3]);
1492         SILC_PUT32_MSB(ll, tmp);
1493         arg = tmp;
1494         arg_len = 4;
1495       } else {
1496         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1497       }
1498       break;
1499     case 'a':
1500       if (add) {
1501         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1502         type = 4;
1503         if (cmd->argc < 4) {
1504           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1505               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1506           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1507           goto out;
1508         }
1509         arg = cmd->argv[3];
1510         arg_len = cmd->argv_lens[3];
1511       } else {
1512         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1513       }
1514       break;
1515     case 'c':
1516       if (add) {
1517         mode |= SILC_CHANNEL_MODE_CIPHER;
1518         type = 5;
1519         if (cmd->argc < 4) {
1520           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1521               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1522           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1523           goto out;
1524         }
1525         arg = cmd->argv[3];
1526         arg_len = cmd->argv_lens[3];
1527       } else {
1528         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1529       }
1530       break;
1531     case 'h':
1532       if (add) {
1533         mode |= SILC_CHANNEL_MODE_HMAC;
1534         type = 6;
1535         if (cmd->argc < 4) {
1536           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1537               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1538           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1539           goto out;
1540         }
1541         arg = cmd->argv[3];
1542         arg_len = cmd->argv_lens[3];
1543       } else {
1544         mode &= ~SILC_CHANNEL_MODE_HMAC;
1545       }
1546       break;
1547     case 'f':
1548       if (add) {
1549         SilcPublicKey pubkey = cmd->client->public_key;
1550         SilcPrivateKey privkey = cmd->client->private_key;
1551
1552         mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1553         type = 7;
1554
1555         if (cmd->argc >= 5) {
1556           char *pass = "";
1557           if (cmd->argc >= 6)
1558             pass = cmd->argv[5];
1559           if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1560                                   NULL, &pubkey, &privkey)) {
1561             SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1562                 "Could not load key pair, check your arguments");
1563             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1564             goto out;
1565           }
1566         }
1567
1568         pk = silc_pkcs_public_key_payload_encode(pubkey);
1569         auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1570                                                   cmd->client->rng, 
1571                                                   cmd->client->sha1hash,
1572                                                   conn->local_id,
1573                                                   SILC_ID_CLIENT);
1574         arg = auth->data;
1575         arg_len = auth->len;
1576       } else {
1577         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1578       }
1579       break;
1580     default:
1581       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1582       goto out;
1583       break;
1584     }
1585   }
1586
1587   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1588   SILC_PUT32_MSB(mode, modebuf);
1589
1590   /* Send the command packet. We support sending only one mode at once
1591      that requires an argument. */
1592   if (type && arg) {
1593     buffer = 
1594       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 4,
1595                                      1, chidp->data, chidp->len, 
1596                                      2, modebuf, sizeof(modebuf),
1597                                      type, arg, arg_len,
1598                                      8, pk ? pk->data : NULL,
1599                                      pk ? pk->len : 0);
1600   } else {
1601     buffer = 
1602       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
1603                                      1, chidp->data, chidp->len, 
1604                                      2, modebuf, sizeof(modebuf));
1605   }
1606
1607   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1608                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1609   silc_buffer_free(buffer);
1610   silc_buffer_free(chidp);
1611   silc_buffer_free(auth);
1612   silc_buffer_free(pk);
1613
1614   /* Notify application */
1615   COMMAND(SILC_STATUS_OK);
1616
1617  out:
1618   silc_client_command_free(cmd);
1619 }
1620
1621 /* CUMODE command. Changes client's mode on a channel. */
1622
1623 SILC_CLIENT_CMD_FUNC(cumode)
1624 {
1625   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1626   SilcClient client = cmd->client;
1627   SilcClientConnection conn = cmd->conn;
1628   SilcChannelEntry channel;
1629   SilcChannelUser chu;
1630   SilcClientEntry client_entry;
1631   SilcBuffer buffer, clidp, chidp, auth = NULL;
1632   unsigned char *name, *cp, modebuf[4];
1633   SilcUInt32 mode = 0, add, len;
1634   char *nickname = NULL;
1635   int i;
1636
1637   if (!cmd->conn) {
1638     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1639     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1640     goto out;
1641   }
1642
1643   if (cmd->argc < 4) {
1644     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1645         "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1646     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1647     goto out;
1648   }
1649
1650   if (cmd->argv[1][0] == '*') {
1651     if (!conn->current_channel) {
1652       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1653       goto out;
1654     }
1655
1656     channel = conn->current_channel;
1657   } else {
1658     name = cmd->argv[1];
1659
1660     channel = silc_client_get_channel(cmd->client, conn, name);
1661     if (!channel) {
1662       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1663       goto out;
1664     }
1665   }
1666
1667   /* Parse the typed nickname. */
1668   if (client->internal->params->nickname_parse)
1669     client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1670   else
1671     nickname = strdup(cmd->argv[3]);
1672
1673   /* Find client entry */
1674   client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
1675                                         cmd->argv[3], TRUE);
1676   if (!client_entry) {
1677     if (cmd->pending) {
1678       COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
1679       goto out;
1680     }
1681
1682     /* Client entry not found, it was requested thus mark this to be
1683        pending command. */
1684     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1685                                 conn->cmd_ident,  
1686                                 silc_client_command_cumode, 
1687                                 silc_client_command_dup(cmd));
1688     cmd->pending = 1;
1689     goto out;
1690   }
1691   
1692   /* Get the current mode */
1693   chu = silc_client_on_channel(channel, client_entry);
1694   if (chu)
1695     mode = chu->mode;
1696
1697   /* Are we adding or removing mode */
1698   if (cmd->argv[2][0] == '-')
1699     add = FALSE;
1700   else
1701     add = TRUE;
1702
1703   /* Parse mode */
1704   cp = cmd->argv[2] + 1;
1705   len = strlen(cp);
1706   for (i = 0; i < len; i++) {
1707     switch(cp[i]) {
1708     case 'a':
1709       if (add) {
1710         mode |= SILC_CHANNEL_UMODE_CHANFO;
1711         mode |= SILC_CHANNEL_UMODE_CHANOP;
1712         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1713         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1714         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1715       } else {
1716         mode = SILC_CHANNEL_UMODE_NONE;
1717       }
1718       break;
1719     case 'f':
1720       if (add) {
1721         SilcPublicKey pubkey = cmd->client->public_key;
1722         SilcPrivateKey privkey = cmd->client->private_key;
1723
1724         if (cmd->argc >= 6) {
1725           char *pass = "";
1726           if (cmd->argc >= 7)
1727             pass = cmd->argv[6];
1728           if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
1729                                   NULL, &pubkey, &privkey)) {
1730             SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1731                 "Could not load key pair, check your arguments");
1732             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1733             goto out;
1734           }
1735         }
1736
1737         auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1738                                                   cmd->client->rng,
1739                                                   cmd->client->sha1hash,
1740                                                   conn->local_id,
1741                                                   SILC_ID_CLIENT);
1742         mode |= SILC_CHANNEL_UMODE_CHANFO;
1743       } else {
1744         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1745       }
1746       break;
1747     case 'o':
1748       if (add)
1749         mode |= SILC_CHANNEL_UMODE_CHANOP;
1750       else
1751         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1752       break;
1753     case 'b':
1754       if (add)
1755         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1756       else
1757         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1758       break;
1759     case 'u':
1760       if (add)
1761         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1762       else
1763         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1764       break;
1765     case 'r':
1766       if (add)
1767         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1768       else
1769         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1770       break;
1771     case 'q':
1772       if (add)
1773         mode |= SILC_CHANNEL_UMODE_QUIET;
1774       else
1775         mode &= ~SILC_CHANNEL_UMODE_QUIET;
1776       break;
1777     default:
1778       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1779       goto out;
1780       break;
1781     }
1782   }
1783
1784   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1785   SILC_PUT32_MSB(mode, modebuf);
1786   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1787
1788   /* Send the command packet. We support sending only one mode at once
1789      that requires an argument. */
1790   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 
1791                                           auth ? 4 : 3, 
1792                                           1, chidp->data, chidp->len, 
1793                                           2, modebuf, 4,
1794                                           3, clidp->data, clidp->len,
1795                                           4, auth ? auth->data : NULL, 
1796                                           auth ? auth->len : 0);
1797   
1798   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1799                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1800   silc_buffer_free(buffer);
1801   silc_buffer_free(chidp);
1802   silc_buffer_free(clidp);
1803   if (auth)
1804     silc_buffer_free(auth);
1805   
1806   /* Notify application */
1807   COMMAND(SILC_STATUS_OK);
1808
1809  out:
1810   silc_free(nickname);
1811   silc_client_command_free(cmd);
1812 }
1813
1814 /* KICK command. Kicks a client out of channel. */
1815
1816 SILC_CLIENT_CMD_FUNC(kick)
1817 {
1818   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1819   SilcClient client = cmd->client;
1820   SilcClientConnection conn = cmd->conn;
1821   SilcIDCacheEntry id_cache = NULL;
1822   SilcChannelEntry channel;
1823   SilcBuffer buffer, idp, idp2;
1824   SilcClientEntry target;
1825   char *name;
1826   char *nickname = NULL;
1827
1828   if (!cmd->conn) {
1829     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1830     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1831     goto out;
1832   }
1833
1834   if (cmd->argc < 3) {
1835     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1836         "Usage: /KICK <channel> <nickname> [<comment>]");
1837     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1838     goto out;
1839   }
1840
1841   if (cmd->argv[1][0] == '*') {
1842     if (!conn->current_channel) {
1843       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1844       goto out;
1845     }
1846     name = conn->current_channel->channel_name;
1847   } else {
1848     name = cmd->argv[1];
1849   }
1850
1851   if (!conn->current_channel) {
1852     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1853     goto out;
1854   }
1855
1856   /* Get the Channel ID of the channel */
1857   if (!silc_idcache_find_by_name_one(conn->internal->channel_cache,
1858                                      name, &id_cache)) {
1859     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1860     goto out;
1861   }
1862
1863   channel = (SilcChannelEntry)id_cache->context;
1864
1865   /* Parse the typed nickname. */
1866   if (client->internal->params->nickname_parse)
1867     client->internal->params->nickname_parse(cmd->argv[2], &nickname);
1868   else
1869     nickname = strdup(cmd->argv[2]);
1870
1871   /* Get the target client */
1872   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1873                                   cmd->argv[2], FALSE);
1874   if (!target) {
1875     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1876         "No such client: %s", cmd->argv[2]);
1877     COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
1878     goto out;
1879   }
1880
1881   /* Send KICK command to the server */
1882   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1883   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1884   if (cmd->argc == 3)
1885     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1886                                             1, idp->data, idp->len,
1887                                             2, idp2->data, idp2->len);
1888   else
1889     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1890                                             1, idp->data, idp->len,
1891                                             2, idp2->data, idp2->len,
1892                                             3, cmd->argv[3], 
1893                                             strlen(cmd->argv[3]));
1894   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1895                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1896   silc_buffer_free(buffer);
1897   silc_buffer_free(idp);
1898   silc_buffer_free(idp2);
1899
1900   /* Notify application */
1901   COMMAND(SILC_STATUS_OK);
1902
1903  out:
1904   silc_free(nickname);
1905   silc_client_command_free(cmd);
1906 }
1907
1908 static void silc_client_command_oper_send(unsigned char *data,
1909                                           SilcUInt32 data_len, void *context)
1910 {
1911   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1912   SilcClientConnection conn = cmd->conn;
1913   SilcBuffer buffer, auth;
1914
1915   if (cmd->argc >= 3) {
1916     /* Encode the public key authentication payload */
1917     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1918                                               cmd->client->private_key,
1919                                               cmd->client->rng,
1920                                               conn->internal->hash,
1921                                               conn->local_id,
1922                                               SILC_ID_CLIENT);
1923   } else {
1924     /* Encode the password authentication payload */
1925     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1926                                     data, data_len);
1927   }
1928
1929   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1930                                           1, cmd->argv[1], 
1931                                           strlen(cmd->argv[1]),
1932                                           2, auth ? auth->data : NULL,
1933                                           auth ? auth->len : 0);
1934   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1935                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1936
1937   silc_buffer_free(buffer);
1938   silc_buffer_free(auth);
1939
1940   /* Notify application */
1941   COMMAND(SILC_STATUS_OK);
1942 }
1943
1944 /* OPER command. Used to obtain server operator privileges. */
1945
1946 SILC_CLIENT_CMD_FUNC(oper)
1947 {
1948   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1949   SilcClientConnection conn = cmd->conn;
1950
1951   if (!cmd->conn) {
1952     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1953     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1954     goto out;
1955   }
1956
1957   if (cmd->argc < 2) {
1958     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1959         "Usage: /OPER <username> [-pubkey]");
1960     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1961     goto out;
1962   }
1963
1964   if (cmd->argc < 3) {
1965     /* Get passphrase */
1966     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1967                                      silc_client_command_oper_send,
1968                                      context);
1969     return;
1970   }
1971
1972   silc_client_command_oper_send(NULL, 0, context);
1973
1974  out:
1975   silc_client_command_free(cmd);
1976 }
1977
1978 static void silc_client_command_silcoper_send(unsigned char *data,
1979                                               SilcUInt32 data_len, 
1980                                               void *context)
1981 {
1982   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1983   SilcClientConnection conn = cmd->conn;
1984   SilcBuffer buffer, auth;
1985
1986   if (cmd->argc >= 3) {
1987     /* Encode the public key authentication payload */
1988     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1989                                               cmd->client->private_key,
1990                                               cmd->client->rng,
1991                                               conn->internal->hash,
1992                                               conn->local_id,
1993                                               SILC_ID_CLIENT);
1994   } else {
1995     /* Encode the password authentication payload */
1996     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1997                                     data, data_len);
1998   }
1999
2000   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
2001                                           1, cmd->argv[1], 
2002                                           strlen(cmd->argv[1]),
2003                                           2, auth ? auth->data : NULL,
2004                                           auth ? auth->len : 0);
2005   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2006                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2007
2008   silc_buffer_free(buffer);
2009   silc_buffer_free(auth);
2010
2011   /* Notify application */
2012   COMMAND(SILC_STATUS_OK);
2013 }
2014
2015 /* SILCOPER command. Used to obtain router operator privileges. */
2016
2017 SILC_CLIENT_CMD_FUNC(silcoper)
2018 {
2019   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2020   SilcClientConnection conn = cmd->conn;
2021
2022   if (!cmd->conn) {
2023     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2024     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2025     goto out;
2026   }
2027
2028   if (cmd->argc < 2) {
2029     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2030         "Usage: /SILCOPER <username> [-pubkey]");
2031     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2032     goto out;
2033   }
2034
2035   if (cmd->argc < 3) {
2036     /* Get passphrase */
2037     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
2038                                      silc_client_command_silcoper_send,
2039                                      context);
2040     return;
2041   }
2042
2043   silc_client_command_silcoper_send(NULL, 0, context);
2044
2045  out:
2046   silc_client_command_free(cmd);
2047 }
2048
2049 /* Command BAN. This is used to manage the ban list of the channel. */
2050
2051 SILC_CLIENT_CMD_FUNC(ban)
2052 {
2053   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2054   SilcClientConnection conn = cmd->conn;
2055   SilcChannelEntry channel;
2056   SilcBuffer buffer, chidp, args = NULL;
2057   char *name, *ban = NULL;
2058   unsigned char action[1];
2059   SilcPublicKey pubkey = NULL;
2060   
2061   if (!cmd->conn) {
2062     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2063     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2064     goto out;
2065   }
2066
2067   if (cmd->argc < 2) {
2068     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2069         "Usage: /BAN <channel> "
2070         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2071     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2072     goto out;
2073   }
2074
2075   if (cmd->argv[1][0] == '*') {
2076     if (!conn->current_channel) {
2077       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2078       goto out;
2079     }
2080
2081     channel = conn->current_channel;
2082   } else {
2083     name = cmd->argv[1];
2084
2085     channel = silc_client_get_channel(cmd->client, conn, name);
2086     if (!channel) {
2087       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2088       goto out;
2089     }
2090   }
2091
2092   if (cmd->argc == 3) {
2093     if (cmd->argv[2][0] == '+')
2094       action[0] = 0x00;
2095     else
2096       action[0] = 0x01;
2097
2098     /* Check if it is public key file to be added to invite list */
2099     if (!silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey,
2100                                    SILC_PKCS_FILE_PEM))
2101       silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey,
2102                                 SILC_PKCS_FILE_BIN);
2103     ban = cmd->argv[2];
2104     if (!pubkey)
2105       ban++;
2106   }
2107
2108   if (ban) {
2109     args = silc_buffer_alloc_size(2);
2110     silc_buffer_format(args,
2111                        SILC_STR_UI_SHORT(1),
2112                        SILC_STR_END);
2113     if (pubkey) {
2114       chidp = silc_pkcs_public_key_payload_encode(pubkey);
2115       args = silc_argument_payload_encode_one(args, chidp->data,
2116                                               chidp->len, 2);
2117       silc_buffer_free(chidp);
2118       silc_pkcs_public_key_free(pubkey);
2119     } else {
2120       args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2121     }
2122   }
2123
2124   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2125
2126   /* Send the command */
2127   buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 
2128                                           ++conn->cmd_ident, 3,
2129                                           1, chidp->data, chidp->len,
2130                                           2, args ? action : NULL,
2131                                           args ? 1 : 0,
2132                                           3, args ? args->data : NULL,
2133                                           args ? args->len : 0);
2134   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2135                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2136   silc_buffer_free(buffer);
2137   silc_buffer_free(chidp);
2138   silc_buffer_free(args);
2139
2140   /* Notify application */
2141   COMMAND(SILC_STATUS_OK);
2142
2143  out:
2144   silc_client_command_free(cmd);
2145 }
2146
2147 /* Command DETACH. This is used to detach from the server */
2148
2149 SILC_CLIENT_CMD_FUNC(detach)
2150 {
2151   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2152   SilcClientConnection conn = cmd->conn;
2153   SilcBuffer buffer;
2154
2155   if (!cmd->conn) {
2156     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2157     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2158     goto out;
2159   }
2160
2161   buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH,
2162                                           ++conn->cmd_ident, 0);
2163   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2164                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2165   silc_buffer_free(buffer);
2166
2167   /* Notify application */
2168   COMMAND(SILC_STATUS_OK);
2169
2170  out:
2171   silc_client_command_free(cmd);
2172 }
2173
2174 /* Command WATCH. */
2175
2176 SILC_CLIENT_CMD_FUNC(watch)
2177 {
2178   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2179   SilcClientConnection conn = cmd->conn;
2180   SilcBuffer buffer, idp = NULL;
2181   int type = 0;
2182
2183   if (!cmd->conn) {
2184     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2185     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2186     goto out;
2187   }
2188
2189   if (cmd->argc < 3) {
2190     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2191     goto out;
2192   }
2193
2194   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
2195
2196   if (!strcasecmp(cmd->argv[1], "-add")) {
2197     type = 2;
2198   } else if (!strcasecmp(cmd->argv[1], "-del")) {
2199     type = 3;
2200   } else {
2201     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2202     goto out;
2203   }
2204
2205   buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH, 
2206                                           ++conn->cmd_ident, 2,
2207                                           1, idp->data, idp->len,
2208                                           type, cmd->argv[2],
2209                                           cmd->argv_lens[2]);
2210   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2211                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2212   silc_buffer_free(buffer);
2213
2214   /* Notify application */
2215   COMMAND(SILC_STATUS_OK);
2216
2217  out:
2218   if (idp)
2219     silc_buffer_free(idp);
2220   silc_client_command_free(cmd);
2221 }
2222
2223 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2224
2225 SILC_CLIENT_CMD_FUNC(leave)
2226 {
2227   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2228   SilcClientConnection conn = cmd->conn;
2229   SilcChannelEntry channel;
2230   SilcChannelUser chu;
2231   SilcBuffer buffer, idp;
2232   char *name;
2233
2234   if (!cmd->conn) {
2235     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2236     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2237     goto out;
2238   }
2239
2240   if (cmd->argc != 2) {
2241     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2242         "Usage: /LEAVE <channel>");
2243     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2244     goto out;
2245   }
2246
2247   if (cmd->argv[1][0] == '*') {
2248     if (!conn->current_channel) {
2249       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2250       goto out;
2251     }
2252     name = conn->current_channel->channel_name;
2253   } else {
2254     name = cmd->argv[1];
2255   }
2256
2257   /* Get the channel entry */
2258   channel = silc_client_get_channel(cmd->client, conn, name);
2259   if (!channel) {
2260     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2261     goto out;
2262   }
2263
2264   /* Remove us from channel */
2265   chu = silc_client_on_channel(channel, conn->local_entry);
2266   if (chu) {
2267     silc_hash_table_del(chu->client->channels, chu->channel);
2268     silc_hash_table_del(chu->channel->user_list, chu->client);
2269     silc_free(chu);
2270   }
2271
2272   /* Send LEAVE command to the server */
2273   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2274   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
2275                                           1, idp->data, idp->len);
2276   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2277                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2278   silc_buffer_free(buffer);
2279   silc_buffer_free(idp);
2280
2281   /* Notify application */
2282   COMMAND(SILC_STATUS_OK);
2283
2284   if (conn->current_channel == channel)
2285     conn->current_channel = NULL;
2286
2287   silc_client_del_channel(cmd->client, cmd->conn, channel);
2288
2289  out:
2290   silc_client_command_free(cmd);
2291 }
2292
2293 /* Command USERS. Requests the USERS of the clients joined on requested
2294    channel. */
2295
2296 SILC_CLIENT_CMD_FUNC(users)
2297 {
2298   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2299   SilcClientConnection conn = cmd->conn;
2300   SilcBuffer buffer;
2301   char *name;
2302
2303   if (!cmd->conn) {
2304     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2305     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2306     goto out;
2307   }
2308
2309   if (cmd->argc != 2) {
2310     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2311         "Usage: /USERS <channel>");
2312     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2313     goto out;
2314   }
2315
2316   if (cmd->argv[1][0] == '*') {
2317     if (!conn->current_channel) {
2318       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2319       goto out;
2320     }
2321     name = conn->current_channel->channel_name;
2322   } else {
2323     name = cmd->argv[1];
2324   }
2325
2326   /* Send USERS command to the server */
2327   buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
2328                                           ++conn->cmd_ident, 1, 
2329                                           2, name, strlen(name));
2330   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
2331                           NULL, 0, NULL, NULL, buffer->data, 
2332                           buffer->len, TRUE);
2333   silc_buffer_free(buffer);
2334
2335   /* Notify application */
2336   COMMAND(SILC_STATUS_OK);
2337
2338  out:
2339   silc_client_command_free(cmd);
2340 }
2341
2342 /* Command GETKEY. Used to fetch remote client's public key. */
2343
2344 SILC_CLIENT_CMD_FUNC(getkey)
2345 {
2346   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2347   SilcClientConnection conn = cmd->conn;
2348   SilcClient client = cmd->client;
2349   SilcClientEntry client_entry = NULL;
2350   SilcServerEntry server_entry = NULL;
2351   char *nickname = NULL;
2352   SilcBuffer idp, buffer;
2353
2354   SILC_LOG_DEBUG(("Start"));
2355
2356   if (!cmd->conn) {
2357     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2358     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2359     goto out;
2360   }
2361
2362   if (cmd->argc < 2) {
2363     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
2364                      "Usage: /GETKEY <nickname or server name>");
2365     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2366     goto out;
2367   }
2368
2369   /* Parse the typed nickname. */
2370   if (client->internal->params->nickname_parse)
2371     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2372   else
2373     nickname = strdup(cmd->argv[1]);
2374
2375   /* Find client entry */
2376   client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2377                                         FALSE);
2378   if (!client_entry) {
2379     /* Check whether user requested server actually */
2380     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2381
2382     if (!server_entry) {
2383       /* No. what ever user wants we don't have it, so resolve it. We
2384          will first try to resolve the client, and if that fails then
2385          we'll try to resolve the server. */
2386
2387       if (!cmd->pending) {
2388         /* This will send the IDENTIFY command for nickname */
2389         silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2390         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2391                                     conn->cmd_ident,  
2392                                     silc_client_command_getkey, 
2393                                     silc_client_command_dup(cmd));
2394         cmd->pending = 1;
2395         goto out;
2396       } else {
2397         SilcClientCommandReplyContext reply = 
2398           (SilcClientCommandReplyContext)context2;
2399         SilcStatus error;
2400
2401         /* If nickname was not found, then resolve the server. */
2402         silc_command_get_status(reply->payload, NULL, &error);
2403         if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
2404           /* This sends the IDENTIFY command to resolve the server. */
2405           silc_client_command_register(client, SILC_COMMAND_IDENTIFY, 
2406                                        NULL, NULL,
2407                                        silc_client_command_reply_identify_i, 0,
2408                                        ++conn->cmd_ident);
2409           silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2410                                    conn->cmd_ident, 1, 
2411                                    2, cmd->argv[1], cmd->argv_lens[1]);
2412           silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2413                                       conn->cmd_ident, 
2414                                       silc_client_command_getkey, 
2415                                       silc_client_command_dup(cmd));
2416           goto out;
2417         }
2418
2419         /* If server was not found, then we've resolved both nickname and
2420            server and did not find anybody. */
2421         if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2422           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2423              silc_get_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2424           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2425            silc_get_status_message(error));
2426           COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2427           goto out;
2428         }
2429
2430         COMMAND_ERROR(error);
2431         goto out;
2432       }
2433     }
2434
2435     idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2436   } else {
2437     idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2438   }
2439
2440   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
2441                                           1, idp->data, idp->len);
2442   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2443                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2444   silc_buffer_free(buffer);
2445   silc_buffer_free(idp);
2446
2447   /* Notify application */
2448   COMMAND(SILC_STATUS_OK);
2449
2450  out:
2451   silc_free(nickname);
2452   silc_client_command_free(cmd);
2453 }
2454
2455 /* Register a new command indicated by the `command' to the SILC client.
2456    The `name' is optional command name.  If provided the command may be
2457    searched using the silc_client_command_find by that name.  The
2458    `command_function' is the function to be called when the command is
2459    executed, and the `command_reply_function' is the function to be
2460    called after the server has sent reply back to the command. 
2461
2462    The `ident' is optional identifier for the command.  If non-zero
2463    the `command_reply_function' for the command type `command' will be
2464    called only if the command reply sent by server includes the 
2465    command identifier `ident'. Application usually does not need it
2466    and set it to zero value. */
2467
2468 bool silc_client_command_register(SilcClient client,
2469                                   SilcCommand command,
2470                                   const char *name,
2471                                   SilcCommandCb command_function,
2472                                   SilcCommandCb command_reply_function,
2473                                   SilcUInt8 max_args,
2474                                   SilcUInt16 ident)
2475 {
2476   SilcClientCommand cmd;
2477
2478   cmd = silc_calloc(1, sizeof(*cmd));
2479   cmd->cmd = command;
2480   cmd->command = command_function;
2481   cmd->reply = command_reply_function;
2482   cmd->name = name ? strdup(name) : NULL;
2483   cmd->max_args = max_args;
2484   cmd->ident = ident;
2485
2486   silc_list_add(client->internal->commands, cmd);
2487
2488   return TRUE;
2489 }
2490
2491 /* Unregister a command indicated by the `command' with command function
2492    `command_function' and command reply function `command_reply_function'.
2493    Returns TRUE if the command was found and unregistered. */
2494
2495 bool silc_client_command_unregister(SilcClient client,
2496                                     SilcCommand command,
2497                                     SilcCommandCb command_function,
2498                                     SilcCommandCb command_reply_function,
2499                                     SilcUInt16 ident)
2500 {
2501   SilcClientCommand cmd;
2502
2503   silc_list_start(client->internal->commands);
2504   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2505     if (cmd->cmd == command && cmd->command == command_function &&
2506         cmd->reply == command_reply_function && cmd->ident == ident) {
2507       silc_list_del(client->internal->commands, cmd);
2508       silc_free(cmd->name);
2509       silc_free(cmd);
2510       return TRUE;
2511     }
2512   }
2513
2514   return FALSE;
2515 }
2516
2517 /* Private range commands, specific to this implementation (and compatible
2518    with SILC Server). */
2519
2520 /* CONNECT command. Connects the server to another server. */
2521
2522 SILC_CLIENT_CMD_FUNC(connect)
2523 {
2524   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2525   SilcClientConnection conn = cmd->conn;
2526   SilcBuffer buffer;
2527   unsigned char port[4];
2528   SilcUInt32 tmp;
2529
2530   if (!cmd->conn) {
2531     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2532     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2533     goto out;
2534   }
2535
2536   if (cmd->argc < 2) {
2537     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2538         "Usage: /CONNECT <server> [<port>]");
2539     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2540     goto out;
2541   }
2542
2543   if (cmd->argc == 3) {
2544     tmp = atoi(cmd->argv[2]);
2545     SILC_PUT32_MSB(tmp, port);
2546   }
2547
2548   if (cmd->argc == 3)
2549     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2, 
2550                                             1, cmd->argv[1], 
2551                                             strlen(cmd->argv[1]),
2552                                             2, port, 4);
2553   else
2554     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2555                                             1, cmd->argv[1], 
2556                                             strlen(cmd->argv[1]));
2557   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2558                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2559   silc_buffer_free(buffer);
2560
2561   /* Notify application */
2562   COMMAND(SILC_STATUS_OK);
2563
2564  out:
2565   silc_client_command_free(cmd);
2566 }
2567
2568
2569 /* CLOSE command. Close server connection to the remote server */
2570  
2571 SILC_CLIENT_CMD_FUNC(close)
2572 {
2573   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2574   SilcClientConnection conn = cmd->conn;
2575   SilcBuffer buffer;
2576   unsigned char port[4];
2577   SilcUInt32 tmp;
2578
2579   if (!cmd->conn) {
2580     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2581     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2582     goto out;
2583   }
2584
2585   if (cmd->argc < 2) {
2586     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2587         "Usage: /CLOSE <server> [<port>]");
2588     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2589     goto out;
2590   }
2591
2592   if (cmd->argc == 3) {
2593     tmp = atoi(cmd->argv[2]);
2594     SILC_PUT32_MSB(tmp, port);
2595   }
2596
2597   if (cmd->argc == 3)
2598     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2, 
2599                                             1, cmd->argv[1], 
2600                                             strlen(cmd->argv[1]),
2601                                             2, port, 4);
2602   else
2603     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2604                                             1, cmd->argv[1], 
2605                                             strlen(cmd->argv[1]));
2606   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2607                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2608   silc_buffer_free(buffer);
2609
2610   /* Notify application */
2611   COMMAND(SILC_STATUS_OK);
2612
2613  out:
2614   silc_client_command_free(cmd);
2615 }
2616  
2617 /* SHUTDOWN command. Shutdowns the server. */
2618
2619 SILC_CLIENT_CMD_FUNC(shutdown)
2620 {
2621   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2622
2623   if (!cmd->conn) {
2624     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2625     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2626     goto out;
2627   }
2628
2629   /* Send the command */
2630   silc_client_command_send(cmd->client, cmd->conn, 
2631                            SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2632
2633   /* Notify application */
2634   COMMAND(SILC_STATUS_OK);
2635
2636  out:
2637   silc_client_command_free(cmd);
2638 }
2639
2640 /* Register all default commands provided by the client library for the
2641    application. */
2642
2643 void silc_client_commands_register(SilcClient client)
2644 {
2645   silc_list_init(client->internal->commands, struct SilcClientCommandStruct, 
2646                  next);
2647
2648   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2649   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2650   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2651   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2652   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2653   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2654   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2655   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2656   SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2657   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2658   SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2659   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2660   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2661   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2662   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2663   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2664   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2665   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2666   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2667   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2668   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2669   SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2670   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2671   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2672   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2673   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2674
2675   SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2676   SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2677   SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2678 }
2679
2680 /* Unregister all commands. */
2681
2682 void silc_client_commands_unregister(SilcClient client)
2683 {
2684   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2685   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2686   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2687   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2688   SILC_CLIENT_CMDU(list, LIST, "LIST");
2689   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2690   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2691   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2692   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2693   SILC_CLIENT_CMDU(info, INFO, "INFO");
2694   SILC_CLIENT_CMDU(stats, STATS, "STATS");
2695   SILC_CLIENT_CMDU(ping, PING, "PING");
2696   SILC_CLIENT_CMDU(oper, OPER, "OPER");
2697   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2698   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2699   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2700   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2701   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2702   SILC_CLIENT_CMDU(kick, KICK, "KICK");
2703   SILC_CLIENT_CMDU(ban, BAN, "BAN");
2704   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2705   SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2706   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2707   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2708   SILC_CLIENT_CMDU(users, USERS, "USERS");
2709   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2710
2711   SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2712   SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2713   SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");
2714 }
2715
2716 /**** Client side incoming command handling **********************************/
2717
2718 void silc_client_command_process_whois(SilcClient client,
2719                                        SilcSocketConnection sock,
2720                                        SilcCommandPayload payload,
2721                                        SilcArgumentPayload args);
2722
2723 /* Client is able to receive some command packets even though they are
2724    special case.  Server may send WHOIS command to the client to retrieve
2725    Requested Attributes information for WHOIS query the server is
2726    processing. This function currently handles only the WHOIS command,
2727    but if in the future for commands may arrive then this can be made
2728    to support other commands too. */
2729
2730 void silc_client_command_process(SilcClient client,
2731                                  SilcSocketConnection sock,
2732                                  SilcPacketContext *packet)
2733 {
2734   SilcCommandPayload payload;
2735   SilcCommand command;
2736   SilcArgumentPayload args;
2737
2738   /* Get command payload from packet */
2739   payload = silc_command_payload_parse(packet->buffer->data,
2740                                        packet->buffer->len);
2741   if (!payload) {
2742     /* Silently ignore bad reply packet */
2743     SILC_LOG_DEBUG(("Bad command packet"));
2744     return;
2745   }
2746
2747   /* Get arguments */
2748   args = silc_command_get_args(payload);
2749   
2750   /* Get the command */
2751   command = silc_command_get(payload);
2752   switch (command) {
2753
2754   case SILC_COMMAND_WHOIS:
2755     /* Ignore everything if requested by application */
2756     if (client->internal->params->ignore_requested_attributes)
2757       break;
2758
2759     silc_client_command_process_whois(client, sock, payload, args);
2760     break;
2761
2762   default:
2763     break;
2764   }
2765
2766   silc_command_payload_free(payload);
2767 }
2768
2769 void silc_client_command_process_whois(SilcClient client,
2770                                        SilcSocketConnection sock,
2771                                        SilcCommandPayload payload,
2772                                        SilcArgumentPayload args)
2773 {
2774   SilcDList attrs;
2775   unsigned char *tmp;
2776   SilcUInt32 tmp_len;
2777   SilcBuffer buffer, packet;
2778
2779   SILC_LOG_DEBUG(("Received WHOIS command"));
2780
2781   /* Try to take the Requested Attributes */
2782   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2783   if (!tmp)
2784     return;
2785
2786   attrs = silc_attribute_payload_parse(tmp, tmp_len);
2787   if (!attrs)
2788     return;
2789
2790   /* Process requested attributes */
2791   buffer = silc_client_attributes_process(client, sock, attrs);
2792   if (!buffer) {
2793     silc_attribute_payload_list_free(attrs);
2794     return;
2795   }
2796
2797   /* Send the attributes back */
2798   packet =
2799     silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2800                                          SILC_STATUS_OK, 0,
2801                                          silc_command_get_ident(payload),
2802                                          1, 11, buffer->data, buffer->len);
2803   silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
2804                           NULL, 0, NULL, NULL, packet->data, 
2805                           packet->len, TRUE);
2806   silc_buffer_free(packet);
2807   silc_buffer_free(buffer);
2808 }