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