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