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