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