updates.
[silc.git] / lib / silcclient / command.c
1 /*
2
3   command.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "clientlibincludes.h"
23 #include "client_internal.h"
24
25 /* Client command list. */
26 SilcClientCommand silc_command_list[] =
27 {
28   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
29   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
30   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
31                   SILC_CF_LAG | SILC_CF_REG, 3),
32   SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
33   SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
34   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
35   SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
36   SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
37   SILC_CLIENT_CMD(kill, KILL, "KILL", 
38                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
39   SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
40   SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
41                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
42   SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
43   SILC_CLIENT_CMD(oper, OPER, "OPER",
44                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
45   SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
46   SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
47   SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
48   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
49   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
50   SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
51   SILC_CLIENT_CMD(ban, BAN, "BAN", SILC_CF_LAG | SILC_CF_REG, 3),
52   SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
53                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
54   SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
55                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
56   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER",
57                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 3),
58   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
59   SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
60   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", SILC_CF_LAG | SILC_CF_REG, 2),
61
62   { NULL, 0, NULL, 0, 0 },
63 };
64
65 #define SILC_NOT_CONNECTED(x, c) \
66   x->ops->say((x), (c), \
67            "You are not connected to a server, use /SERVER to connect");
68
69 /* Command operation that is called at the end of all commands. 
70    Usage: COMMAND; */
71 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
72   cmd, TRUE, cmd->command->cmd)
73
74 /* Error to application. Usage: COMMAND_ERROR; */
75 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
76   cmd, FALSE, cmd->command->cmd)
77
78 /* Generic function to send any command. The arguments must be sent already
79    encoded into correct form and in correct order. */
80
81 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
82                               SilcCommand command, uint16 ident,
83                               uint32 argc, ...)
84 {
85   SilcBuffer packet;
86   va_list ap;
87
88   va_start(ap, argc);
89
90   packet = silc_command_payload_encode_vap(command, ident, argc, ap);
91   silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
92                           NULL, 0, NULL, NULL, packet->data, 
93                           packet->len, TRUE);
94   silc_buffer_free(packet);
95 }
96
97 /* Finds and returns a pointer to the command list. Return NULL if the
98    command is not found. */
99
100 SilcClientCommand *silc_client_command_find(const char *name)
101 {
102   SilcClientCommand *cmd;
103
104   for (cmd = silc_command_list; cmd->name; cmd++) {
105     if (!strcmp(cmd->name, name))
106       return cmd;
107   }
108
109   return NULL;
110 }
111
112 /* Add new pending command to be executed when reply to a command has been
113    received.  The `reply_cmd' is the command that will call the `callback'
114    with `context' when reply has been received.  If `ident is non-zero
115    the `callback' will be executed when received reply with command 
116    identifier `ident'. */
117
118 void silc_client_command_pending(SilcClientConnection conn,
119                                  SilcCommand reply_cmd,
120                                  uint16 ident,
121                                  SilcClientPendingDestructor destructor,
122                                  SilcCommandCb callback,
123                                  void *context)
124 {
125   SilcClientCommandPending *reply;
126
127   reply = silc_calloc(1, sizeof(*reply));
128   reply->reply_cmd = reply_cmd;
129   reply->ident = ident;
130   reply->context = context;
131   reply->callback = callback;
132   reply->destructor = destructor;
133   silc_dlist_add(conn->pending_commands, reply);
134 }
135
136 /* Deletes pending command by reply command type. */
137
138 void silc_client_command_pending_del(SilcClientConnection conn,
139                                      SilcCommand reply_cmd,
140                                      uint16 ident)
141 {
142   SilcClientCommandPending *r;
143
144   silc_dlist_start(conn->pending_commands);
145   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
146     if (r->reply_cmd == reply_cmd && r->ident == ident) {
147       silc_dlist_del(conn->pending_commands, r);
148       break;
149     }
150   }
151 }
152
153 /* Checks for pending commands and marks callbacks to be called from
154    the command reply function. Returns TRUE if there were pending command. */
155
156 int silc_client_command_pending_check(SilcClientConnection conn,
157                                       SilcClientCommandReplyContext ctx,
158                                       SilcCommand command, 
159                                       uint16 ident)
160 {
161   SilcClientCommandPending *r;
162
163   silc_dlist_start(conn->pending_commands);
164   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
165     if (r->reply_cmd == command && r->ident == ident) {
166       ctx->context = r->context;
167       ctx->callback = r->callback;
168       ctx->destructor = r->destructor;
169       ctx->ident = ident;
170       return TRUE;
171     }
172   }
173
174   return FALSE;
175 }
176
177 /* Allocate Command Context */
178
179 SilcClientCommandContext silc_client_command_alloc()
180 {
181   SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
182   ctx->users++;
183   return ctx;
184 }
185
186 /* Free command context and its internals */
187
188 void silc_client_command_free(SilcClientCommandContext ctx)
189 {
190   ctx->users--;
191   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
192                   ctx->users));
193   if (ctx->users < 1) {
194     int i;
195
196     for (i = 0; i < ctx->argc; i++)
197       silc_free(ctx->argv[i]);
198     silc_free(ctx->argv_lens);
199     silc_free(ctx->argv_types);
200     silc_free(ctx);
201   }
202 }
203
204 /* Duplicate Command Context by adding reference counter. The context won't
205    be free'd untill it hits zero. */
206
207 SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
208 {
209   ctx->users++;
210   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
211                   ctx->users));
212   return ctx;
213 }
214
215 /* Pending command destructor. */
216
217 static void silc_client_command_destructor(void *context)
218 {
219   silc_client_command_free((SilcClientCommandContext)context);
220 }
221
222 /* silc_client_get_client completion callback */
223 void silc_client_command_completion(SilcClient client,
224                                     SilcClientConnection conn,
225                                     SilcClientEntry clients,
226                                     uint32 clients_count,
227                                     void *context)
228 {
229
230 }
231
232 /* Command WHOIS. This command is used to query information about 
233    specific user. */
234
235 SILC_CLIENT_CMD_FUNC(whois)
236 {
237   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
238   SilcClientConnection conn = cmd->conn;
239   SilcBuffer buffer;
240
241   if (!cmd->conn) {
242     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
243     COMMAND_ERROR;
244     goto out;
245   }
246
247   if (cmd->argc < 2 || cmd->argc > 3) {
248     cmd->client->ops->say(cmd->client, conn, 
249              "Usage: /WHOIS <nickname>[@<server>] [<count>]");
250     COMMAND_ERROR;
251     goto out;
252   }
253
254   buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
255                                        cmd->argc - 1, ++cmd->argv,
256                                        ++cmd->argv_lens, ++cmd->argv_types,
257                                        0);
258   silc_client_packet_send(cmd->client, cmd->conn->sock,
259                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
260                           buffer->data, buffer->len, TRUE);
261   silc_buffer_free(buffer);
262   cmd->argv--;
263   cmd->argv_lens--;
264   cmd->argv_types--;
265
266   /* Notify application */
267   COMMAND;
268
269  out:
270   silc_client_command_free(cmd);
271 }
272
273 /* Command WHOWAS. This command is used to query history information about
274    specific user that used to exist in the network. */
275
276 SILC_CLIENT_CMD_FUNC(whowas)
277 {
278   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
279   SilcClientConnection conn = cmd->conn;
280   SilcBuffer buffer;
281
282   if (!cmd->conn) {
283     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
284     COMMAND_ERROR;
285     goto out;
286   }
287
288   if (cmd->argc < 2 || cmd->argc > 3) {
289     cmd->client->ops->say(cmd->client, conn, 
290              "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
291     COMMAND_ERROR;
292     goto out;
293   }
294
295   buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
296                                        cmd->argc - 1, ++cmd->argv,
297                                        ++cmd->argv_lens, ++cmd->argv_types,
298                                        0);
299   silc_client_packet_send(cmd->client, cmd->conn->sock,
300                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
301                           buffer->data, buffer->len, TRUE);
302   silc_buffer_free(buffer);
303   cmd->argv--;
304   cmd->argv_lens--;
305   cmd->argv_types--;
306
307   /* Notify application */
308   COMMAND;
309
310  out:
311   silc_client_command_free(cmd);
312 }
313
314 /* Command IDENTIFY. This command is used to query information about 
315    specific user, especially ID's. */
316
317 SILC_CLIENT_CMD_FUNC(identify)
318 {
319   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
320   SilcClientConnection conn = cmd->conn;
321   SilcBuffer buffer;
322
323   if (!cmd->conn) {
324     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
325     COMMAND_ERROR;
326     goto out;
327   }
328
329   if (cmd->argc < 2 || cmd->argc > 3) {
330     cmd->client->ops->say(cmd->client, conn,
331              "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
332     COMMAND_ERROR;
333     goto out;
334   }
335
336   buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
337                                        cmd->argc - 1, ++cmd->argv,
338                                        ++cmd->argv_lens, ++cmd->argv_types,
339                                        ++conn->cmd_ident);
340   silc_client_packet_send(cmd->client, cmd->conn->sock,
341                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
342                           buffer->data, buffer->len, TRUE);
343   silc_buffer_free(buffer);
344   cmd->argv--;
345   cmd->argv_lens--;
346   cmd->argv_types--;
347
348   /* Notify application */
349   COMMAND;
350
351  out:
352   silc_client_command_free(cmd);
353 }
354
355 /* Command NICK. Shows current nickname/sets new nickname on current
356    window. */
357
358 SILC_CLIENT_CMD_FUNC(nick)
359 {
360   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
361   SilcClientConnection conn = cmd->conn;
362   SilcBuffer buffer;
363
364   if (!cmd->conn) {
365     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
366     COMMAND_ERROR;
367     goto out;
368   }
369
370   if (!strcmp(conn->nickname, cmd->argv[1]))
371     goto out;
372
373   /* Show current nickname */
374   if (cmd->argc < 2) {
375     if (cmd->conn) {
376       cmd->client->ops->say(cmd->client, conn, 
377                             "Your nickname is %s on server %s", 
378                             conn->nickname, conn->remote_host);
379     } else {
380       cmd->client->ops->say(cmd->client, conn, 
381                             "Your nickname is %s", conn->nickname);
382     }
383
384     /* XXX Notify application */
385     COMMAND;
386     goto out;
387   }
388
389   /* Set new nickname */
390   buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
391                                        cmd->argc - 1, ++cmd->argv,
392                                        ++cmd->argv_lens, ++cmd->argv_types,
393                                        ++cmd->conn->cmd_ident);
394   silc_client_packet_send(cmd->client, cmd->conn->sock,
395                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
396                           buffer->data, buffer->len, TRUE);
397   silc_buffer_free(buffer);
398   cmd->argv--;
399   cmd->argv_lens--;
400   cmd->argv_types--;
401   if (conn->nickname)
402     silc_free(conn->nickname);
403   conn->nickname = strdup(cmd->argv[1]);
404
405   /* Notify application */
406   COMMAND;
407
408  out:
409   silc_client_command_free(cmd);
410 }
411
412 /* Command LIST. Lists channels on the current server. */
413
414 SILC_CLIENT_CMD_FUNC(list)
415 {
416   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
417   SilcClientConnection conn = cmd->conn;
418   SilcIDCacheEntry id_cache = NULL;
419   SilcChannelEntry channel;
420   SilcBuffer buffer, idp = NULL;
421   char *name;
422
423   if (!cmd->conn) {
424     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
425     COMMAND_ERROR;
426     goto out;
427   }
428
429   if (cmd->argc == 2) {
430     name = cmd->argv[1];
431
432     /* Get the Channel ID of the channel */
433     if (silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
434       channel = (SilcChannelEntry)id_cache->context;
435       idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
436     }
437   }
438
439   if (!idp)
440     buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, 
441                                             ++conn->cmd_ident, 0);
442   else
443     buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, 
444                                             ++conn->cmd_ident, 1,
445                                             1, idp->data, idp->len);
446
447   silc_client_packet_send(cmd->client, cmd->conn->sock,
448                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
449                           buffer->data, buffer->len, TRUE);
450   silc_buffer_free(buffer);
451   if (idp)
452     silc_buffer_free(idp);
453
454   /* Notify application */
455   COMMAND;
456
457  out:
458   silc_client_command_free(cmd);
459 }
460
461 /* Command TOPIC. Sets/shows topic on a channel. */
462
463 SILC_CLIENT_CMD_FUNC(topic)
464 {
465   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
466   SilcClientConnection conn = cmd->conn;
467   SilcIDCacheEntry id_cache = NULL;
468   SilcChannelEntry channel;
469   SilcBuffer buffer, idp;
470   char *name;
471
472   if (!cmd->conn) {
473     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
474     COMMAND_ERROR;
475     goto out;
476   }
477
478   if (cmd->argc < 2 || cmd->argc > 3) {
479     cmd->client->ops->say(cmd->client, conn,
480                           "Usage: /TOPIC <channel> [<topic>]");
481     COMMAND_ERROR;
482     goto out;
483   }
484
485   if (cmd->argv[1][0] == '*') {
486     if (!conn->current_channel) {
487       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
488       COMMAND_ERROR;
489       goto out;
490     }
491     name = conn->current_channel->channel_name;
492   } else {
493     name = cmd->argv[1];
494   }
495
496   if (!conn->current_channel) {
497     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
498     COMMAND_ERROR;
499     goto out;
500   }
501
502   /* Get the Channel ID of the channel */
503   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
504     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
505     COMMAND_ERROR;
506     goto out;
507   }
508
509   channel = (SilcChannelEntry)id_cache->context;
510
511   /* Send TOPIC command to the server */
512   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
513   if (cmd->argc > 2)
514     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 
515                                             ++conn->cmd_ident, 2, 
516                                             1, idp->data, idp->len,
517                                             2, cmd->argv[2], 
518                                             strlen(cmd->argv[2]));
519   else
520     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 
521                                             ++conn->cmd_ident, 1,
522                                             1, idp->data, idp->len);
523   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
524                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
525   silc_buffer_free(buffer);
526   silc_buffer_free(idp);
527
528   /* Notify application */
529   COMMAND;
530
531  out:
532   silc_client_command_free(cmd);
533 }
534
535 /* Command INVITE. Invites specific client to join a channel. This is
536    also used to mange the invite list of the channel. */
537
538 SILC_CLIENT_CMD_FUNC(invite)
539 {
540   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
541   SilcClient client = cmd->client;
542   SilcClientConnection conn = cmd->conn;
543   SilcClientEntry client_entry = NULL;
544   SilcChannelEntry channel;
545   SilcBuffer buffer, clidp, chidp;
546   uint32 num = 0, type = 0;
547   char *nickname = NULL, *server = NULL, *name;
548   char *invite = NULL;
549
550   if (!cmd->conn) {
551     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
552     COMMAND_ERROR;
553     goto out;
554   }
555
556   if (cmd->argc < 2) {
557     cmd->client->ops->say(cmd->client, conn,
558                    "Usage: /INVITE <channel> [<nickname>[@server>]"
559                    "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
560     COMMAND_ERROR;
561     goto out;
562   }
563
564   if (cmd->argv[1][0] == '*') {
565     if (!conn->current_channel) {
566       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
567       COMMAND_ERROR;
568       goto out;
569     }
570
571     channel = conn->current_channel;
572   } else {
573     name = cmd->argv[1];
574
575     channel = silc_client_get_channel(cmd->client, conn, name);
576     if (!channel) {
577       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
578       COMMAND_ERROR;
579       goto out;
580     }
581   }
582
583   /* Parse the typed nickname. */
584   if (cmd->argc == 3) {
585     if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
586       if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
587         cmd->client->ops->say(cmd->client, conn, "Bad nickname");
588         COMMAND_ERROR;
589         goto out;
590       }
591       
592       /* Find client entry */
593       client_entry = silc_idlist_get_client(client, conn, nickname, 
594                                             server, num, TRUE);
595       if (!client_entry) {
596         if (nickname)
597           silc_free(nickname);
598         if (server)
599           silc_free(server);
600         
601         if (cmd->pending) {
602           COMMAND_ERROR;
603           goto out;
604         }
605       
606         /* Client entry not found, it was requested thus mark this to be
607            pending command. */
608         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
609                                     conn->cmd_ident,
610                                     silc_client_command_destructor,
611                                     silc_client_command_invite, 
612                                     silc_client_command_dup(cmd));
613         cmd->pending = 1;
614         return;
615       }
616       
617       cmd->client->ops->say(cmd->client, conn, 
618                             "Inviting %s to channel %s", cmd->argv[2], 
619                             channel->channel_name);
620     } else {
621       invite = cmd->argv[2];
622       invite++;
623       if (cmd->argv[2][0] == '+')
624         type = 3;
625       else
626         type = 4;
627     }
628   }
629
630   /* Send the command */
631   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
632   if (client_entry) {
633     clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
634     buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 
635                                             ++conn->cmd_ident, 3,
636                                             1, chidp->data, chidp->len,
637                                             2, clidp->data, clidp->len,
638                                             type, invite, invite ?
639                                             strlen(invite) : 0);
640     silc_buffer_free(clidp);
641   } else {
642     buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 
643                                             ++conn->cmd_ident, 2,
644                                             1, chidp->data, chidp->len,
645                                             type, invite, invite ?
646                                             strlen(invite) : 0);
647   }
648
649   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
650                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
651   silc_buffer_free(buffer);
652   silc_buffer_free(chidp);
653
654   /* Notify application */
655   COMMAND;
656
657  out:
658   if (nickname)
659     silc_free(nickname);
660   if (server)
661     silc_free(server);
662   silc_client_command_free(cmd);
663 }
664
665 typedef struct {
666   SilcClient client;
667   SilcClientConnection conn;
668 } *QuitInternal;
669
670 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
671 {
672   QuitInternal q = (QuitInternal)context;
673
674   /* Close connection */
675   q->client->ops->disconnect(q->client, q->conn);
676   silc_client_close_connection(q->client, NULL, q->conn->sock->user_data);
677
678   silc_free(q);
679 }
680
681 /* Command QUIT. Closes connection with current server. */
682  
683 SILC_CLIENT_CMD_FUNC(quit)
684 {
685   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
686   SilcBuffer buffer;
687   QuitInternal q;
688
689   if (!cmd->conn) {
690     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
691     COMMAND_ERROR;
692     goto out;
693   }
694
695   if (cmd->argc > 1)
696     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, 
697                                          &cmd->argv[1], &cmd->argv_lens[1],
698                                          &cmd->argv_types[1], 0);
699   else
700     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
701                                          NULL, NULL, NULL, 0);
702   silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
703                           NULL, 0, NULL, NULL, 
704                           buffer->data, buffer->len, TRUE);
705   silc_buffer_free(buffer);
706
707   q = silc_calloc(1, sizeof(*q));
708   q->client = cmd->client;
709   q->conn = cmd->conn;
710
711   /* We quit the connection with little timeout */
712   silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
713                      silc_client_command_quit_cb, (void *)q,
714                      1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
715
716   /* Notify application */
717   COMMAND;
718
719  out:
720   silc_client_command_free(cmd);
721 }
722
723 /* Command KILL. Router operator can use this command to remove an client
724    fromthe SILC Network. */
725
726 SILC_CLIENT_CMD_FUNC(kill)
727 {
728   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
729   SilcClientConnection conn = cmd->conn;
730   SilcBuffer buffer, idp;
731   SilcClientEntry target;
732   uint32 num = 0;
733   char *nickname = NULL, *server = NULL;
734
735   if (!cmd->conn) {
736     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
737     COMMAND_ERROR;
738     goto out;
739   }
740
741   if (cmd->argc < 2) {
742     cmd->client->ops->say(cmd->client, conn, 
743                           "Usage: /KILL <nickname> [<comment>]");
744     COMMAND_ERROR;
745     goto out;
746   }
747
748   /* Parse the typed nickname. */
749   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
750     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
751     COMMAND_ERROR;
752     goto out;
753   }
754
755   /* Get the target client */
756   target = silc_idlist_get_client(cmd->client, conn, nickname, 
757                                   server, num, TRUE);
758   if (!target) {
759     silc_free(nickname);
760     if (server)
761       silc_free(server);
762
763     if (cmd->pending) {
764       COMMAND_ERROR;
765       goto out;
766     }
767
768     /* Client entry not found, it was requested thus mark this to be
769        pending command. */
770     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
771                                 conn->cmd_ident,  
772                                 silc_client_command_destructor,
773                                 silc_client_command_kill, 
774                                 silc_client_command_dup(cmd));
775     cmd->pending = 1;
776     return;
777   }
778
779   /* Send the KILL command to the server */
780   idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
781   if (cmd->argc == 2)
782     buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 1, 
783                                             1, idp->data, idp->len);
784   else
785     buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 2, 
786                                             1, idp->data, idp->len,
787                                             2, cmd->argv[2], 
788                                             strlen(cmd->argv[2]));
789   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
790                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
791   silc_buffer_free(buffer);
792   silc_buffer_free(idp);
793
794   /* Notify application */
795   COMMAND;
796
797  out:
798   if (nickname)
799     silc_free(nickname);
800   if (server)
801     silc_free(server);
802   silc_client_command_free(cmd);
803 }
804
805 /* Command INFO. Request information about specific server. If specific
806    server is not provided the current server is used. */
807
808 SILC_CLIENT_CMD_FUNC(info)
809 {
810   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
811   SilcClientConnection conn = cmd->conn;
812   SilcBuffer buffer;
813   char *name = NULL;
814
815   if (!cmd->conn) {
816     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
817     COMMAND_ERROR;
818     goto out;
819   }
820
821   if (cmd->argc == 2)
822     name = strdup(cmd->argv[1]);
823
824   /* Send the command */
825   if (name)
826     buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
827                                             1, name, strlen(name));
828   else
829     buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0,
830                                          NULL, NULL, NULL, 0);
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   if (name)
835     silc_free(name);
836
837   /* Notify application */
838   COMMAND;
839
840  out:
841   silc_client_command_free(cmd);
842 }
843
844 /* Command PING. Sends ping to server. This is used to test the 
845    communication channel. */
846
847 SILC_CLIENT_CMD_FUNC(ping)
848 {
849   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
850   SilcClientConnection conn = cmd->conn;
851   SilcBuffer buffer;
852   void *id;
853   int i;
854   char *name = NULL;
855
856   if (!cmd->conn) {
857     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
858     COMMAND_ERROR;
859     goto out;
860   }
861
862   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
863     name = strdup(conn->remote_host);
864
865   /* Send the command */
866   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
867                                           1, conn->remote_id_data, 
868                                           silc_id_get_len(conn->remote_id,
869                                                           SILC_ID_SERVER));
870   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
871                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
872   silc_buffer_free(buffer);
873
874   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
875                       SILC_ID_SERVER);
876   if (!id) {
877     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
878     COMMAND_ERROR;
879     goto out;
880   }
881
882   /* Start counting time */
883   for (i = 0; i < conn->ping_count; i++) {
884     if (conn->ping[i].dest_id == NULL) {
885       conn->ping[i].start_time = time(NULL);
886       conn->ping[i].dest_id = id;
887       conn->ping[i].dest_name = name;
888       conn->ping_count++;
889       break;
890     }
891   }
892   if (i >= conn->ping_count) {
893     i = conn->ping_count;
894     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
895     conn->ping[i].start_time = time(NULL);
896     conn->ping[i].dest_id = id;
897     conn->ping[i].dest_name = name;
898     conn->ping_count++;
899   }
900   
901   /* Notify application */
902   COMMAND;
903
904  out:
905   silc_client_command_free(cmd);
906 }
907
908 SILC_CLIENT_CMD_FUNC(notice)
909 {
910 }
911
912 /* Command JOIN. Joins to a channel. */
913
914 SILC_CLIENT_CMD_FUNC(join)
915 {
916   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
917   SilcClientConnection conn = cmd->conn;
918   SilcIDCacheEntry id_cache = NULL;
919   SilcBuffer buffer, idp;
920
921   if (!cmd->conn) {
922     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
923     COMMAND_ERROR;
924     goto out;
925   }
926
927   if (cmd->argc < 2) {
928     /* Show channels currently joined to */
929
930     goto out;
931   }
932
933   /* See if we have joined to the requested channel already */
934   if (silc_idcache_find_by_name_one(conn->channel_cache, cmd->argv[1],
935                                     &id_cache)) {
936 #if 0
937     cmd->client->ops->say(cmd->client, conn, 
938                           "You are talking to channel %s", cmd->argv[1]);
939     conn->current_channel = (SilcChannelEntry)id_cache->context;
940     cmd->client->screen->bottom_line->channel = cmd->argv[1];
941     silc_screen_print_bottom_line(cmd->client->screen, 0);
942 #endif
943     goto out;
944   }
945
946   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
947
948   /* Send JOIN command to the server */
949   if (cmd->argc == 2)
950     buffer = 
951       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
952                                      1, cmd->argv[1], cmd->argv_lens[1],
953                                      2, idp->data, idp->len);
954   else if (cmd->argc == 3)
955     /* XXX Buggy */
956     buffer = 
957       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
958                                      1, cmd->argv[1], cmd->argv_lens[1],
959                                      2, idp->data, idp->len,
960                                      3, cmd->argv[2], cmd->argv_lens[2]);
961   else
962     buffer = 
963       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
964                                      1, cmd->argv[1], cmd->argv_lens[1],
965                                      2, idp->data, idp->len,
966                                      3, cmd->argv[2], cmd->argv_lens[2],
967                                      4, cmd->argv[3], cmd->argv_lens[3]);
968
969   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
970                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
971   silc_buffer_free(buffer);
972   silc_buffer_free(idp);
973
974   /* Notify application */
975   COMMAND;
976
977  out:
978   silc_client_command_free(cmd);
979 }
980
981 /* MOTD command. Requests motd from server. */
982
983 SILC_CLIENT_CMD_FUNC(motd)
984 {
985   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
986   SilcClientConnection conn = cmd->conn;
987   SilcBuffer buffer;
988
989   if (!cmd->conn) {
990     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
991     COMMAND_ERROR;
992     goto out;
993   }
994
995   if (cmd->argc < 1 || cmd->argc > 2) {
996     cmd->client->ops->say(cmd->client, conn,
997                           "Usage: /MOTD [<server>]");
998     COMMAND_ERROR;
999     goto out;
1000   }
1001
1002   /* Send TOPIC command to the server */
1003   if (cmd->argc == 1)
1004     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1005                                             1, conn->remote_host, 
1006                                             strlen(conn->remote_host));
1007   else
1008     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1009                                             1, cmd->argv[1], 
1010                                             cmd->argv_lens[1]);
1011   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1012                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1013   silc_buffer_free(buffer);
1014
1015   /* Notify application */
1016   COMMAND;
1017
1018  out:
1019   silc_client_command_free(cmd);
1020 }
1021
1022 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1023    modes as client cannot set itself server/router operator privileges. */
1024
1025 SILC_CLIENT_CMD_FUNC(umode)
1026 {
1027   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1028   SilcClientConnection conn = cmd->conn;
1029   SilcBuffer buffer, idp;
1030   unsigned char *cp, modebuf[4];
1031   uint32 mode, add, len;
1032   int i;
1033
1034   if (!cmd->conn) {
1035     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1036     COMMAND_ERROR;
1037     goto out;
1038   }
1039
1040   if (cmd->argc < 2) {
1041     cmd->client->ops->say(cmd->client, conn, 
1042                   "Usage: /UMODE +|-<modes>");
1043     COMMAND_ERROR;
1044     goto out;
1045   }
1046
1047   mode = conn->local_entry->mode;
1048
1049   /* Are we adding or removing mode */
1050   if (cmd->argv[1][0] == '-')
1051     add = FALSE;
1052   else
1053     add = TRUE;
1054
1055   /* Parse mode */
1056   cp = cmd->argv[1] + 1;
1057   len = strlen(cp);
1058   for (i = 0; i < len; i++) {
1059     switch(cp[i]) {
1060     case 'a':
1061       if (add) {
1062         mode = 0;
1063         mode |= SILC_UMODE_SERVER_OPERATOR;
1064         mode |= SILC_UMODE_ROUTER_OPERATOR;
1065       } else {
1066         mode = SILC_UMODE_NONE;
1067       }
1068       break;
1069     case 's':
1070       if (add)
1071         mode |= SILC_UMODE_SERVER_OPERATOR;
1072       else
1073         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1074       break;
1075     case 'r':
1076       if (add)
1077         mode |= SILC_UMODE_ROUTER_OPERATOR;
1078       else
1079         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1080       break;
1081     case 'g':
1082       if (add)
1083         mode |= SILC_UMODE_GONE;
1084       else
1085         mode &= ~SILC_UMODE_GONE;
1086       break;
1087     default:
1088       COMMAND_ERROR;
1089       goto out;
1090       break;
1091     }
1092   }
1093
1094   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1095   SILC_PUT32_MSB(mode, modebuf);
1096
1097   /* Send the command packet. We support sending only one mode at once
1098      that requires an argument. */
1099   buffer = 
1100     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1101                                    1, idp->data, idp->len, 
1102                                    2, modebuf, sizeof(modebuf));
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   silc_buffer_free(idp);
1107
1108   /* Notify application */
1109   COMMAND;
1110
1111  out:
1112   silc_client_command_free(cmd);
1113 }
1114
1115 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1116    can be set several at once. Those modes that require argument must be set
1117    separately (unless set with modes that does not require arguments). */
1118
1119 SILC_CLIENT_CMD_FUNC(cmode)
1120 {
1121   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1122   SilcClientConnection conn = cmd->conn;
1123   SilcChannelEntry channel;
1124   SilcBuffer buffer, chidp, auth = NULL;
1125   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1126   uint32 mode, add, type, len, arg_len = 0;
1127   int i;
1128
1129   if (!cmd->conn) {
1130     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1131     COMMAND_ERROR;
1132     goto out;
1133   }
1134
1135   if (cmd->argc < 3) {
1136     cmd->client->ops->say(cmd->client, conn, 
1137                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1138     COMMAND_ERROR;
1139     goto out;
1140   }
1141
1142   if (cmd->argv[1][0] == '*') {
1143     if (!conn->current_channel) {
1144       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1145       COMMAND_ERROR;
1146       goto out;
1147     }
1148
1149     channel = conn->current_channel;
1150   } else {
1151     name = cmd->argv[1];
1152
1153     channel = silc_client_get_channel(cmd->client, conn, name);
1154     if (!channel) {
1155       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1156       COMMAND_ERROR;
1157       goto out;
1158     }
1159   }
1160
1161   mode = channel->mode;
1162
1163   /* Are we adding or removing mode */
1164   if (cmd->argv[2][0] == '-')
1165     add = FALSE;
1166   else
1167     add = TRUE;
1168
1169   /* Argument type to be sent to server */
1170   type = 0;
1171
1172   /* Parse mode */
1173   cp = cmd->argv[2] + 1;
1174   len = strlen(cp);
1175   for (i = 0; i < len; i++) {
1176     switch(cp[i]) {
1177     case 'p':
1178       if (add)
1179         mode |= SILC_CHANNEL_MODE_PRIVATE;
1180       else
1181         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1182       break;
1183     case 's':
1184       if (add)
1185         mode |= SILC_CHANNEL_MODE_SECRET;
1186       else
1187         mode &= ~SILC_CHANNEL_MODE_SECRET;
1188       break;
1189     case 'k':
1190       if (add)
1191         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1192       else
1193         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1194       break;
1195     case 'i':
1196       if (add)
1197         mode |= SILC_CHANNEL_MODE_INVITE;
1198       else
1199         mode &= ~SILC_CHANNEL_MODE_INVITE;
1200       break;
1201     case 't':
1202       if (add)
1203         mode |= SILC_CHANNEL_MODE_TOPIC;
1204       else
1205         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1206       break;
1207     case 'l':
1208       if (add) {
1209         int ll;
1210         mode |= SILC_CHANNEL_MODE_ULIMIT;
1211         type = 3;
1212         ll = atoi(cmd->argv[3]);
1213         SILC_PUT32_MSB(ll, tmp);
1214         arg = tmp;
1215         arg_len = 4;
1216       } else {
1217         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1218       }
1219       break;
1220     case 'a':
1221       if (add) {
1222         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1223         type = 4;
1224         arg = cmd->argv[3];
1225         arg_len = cmd->argv_lens[3];
1226       } else {
1227         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1228       }
1229       break;
1230     case 'c':
1231       if (add) {
1232         mode |= SILC_CHANNEL_MODE_CIPHER;
1233         type = 5;
1234         arg = cmd->argv[3];
1235         arg_len = cmd->argv_lens[3];
1236       } else {
1237         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1238       }
1239       break;
1240     case 'h':
1241       if (add) {
1242         mode |= SILC_CHANNEL_MODE_HMAC;
1243         type = 6;
1244         arg = cmd->argv[3];
1245         arg_len = cmd->argv_lens[3];
1246       } else {
1247         mode &= ~SILC_CHANNEL_MODE_HMAC;
1248       }
1249       break;
1250     case 'f':
1251       if (add) {
1252         mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1253         type = 7;
1254
1255         if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1256           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1257                                                     cmd->client->private_key,
1258                                                     conn->hash,
1259                                                     conn->local_id,
1260                                                     SILC_ID_CLIENT);
1261         } else {
1262           auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1263                                           cmd->argv[3], cmd->argv_lens[3]);
1264         }
1265
1266         arg = auth->data;
1267         arg_len = auth->len;
1268       } else {
1269         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1270       }
1271       break;
1272     default:
1273       COMMAND_ERROR;
1274       goto out;
1275       break;
1276     }
1277   }
1278
1279   if (type && cmd->argc < 3) {
1280     COMMAND_ERROR;
1281     goto out;
1282   }
1283
1284   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1285   SILC_PUT32_MSB(mode, modebuf);
1286
1287   /* Send the command packet. We support sending only one mode at once
1288      that requires an argument. */
1289   if (type && arg) {
1290     buffer = 
1291       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1292                                      1, chidp->data, chidp->len, 
1293                                      2, modebuf, sizeof(modebuf),
1294                                      type, arg, arg_len);
1295   } else {
1296     buffer = 
1297       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1298                                      1, chidp->data, chidp->len, 
1299                                      2, modebuf, sizeof(modebuf));
1300   }
1301
1302   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1303                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1304   silc_buffer_free(buffer);
1305   silc_buffer_free(chidp);
1306   if (auth)
1307     silc_buffer_free(auth);
1308
1309   /* Notify application */
1310   COMMAND;
1311
1312  out:
1313   silc_client_command_free(cmd);
1314 }
1315
1316 /* CUMODE command. Changes client's mode on a channel. */
1317
1318 SILC_CLIENT_CMD_FUNC(cumode)
1319 {
1320   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1321   SilcClientConnection conn = cmd->conn;
1322   SilcChannelEntry channel;
1323   SilcChannelUser chu;
1324   SilcClientEntry client_entry;
1325   SilcBuffer buffer, clidp, chidp, auth = NULL;
1326   unsigned char *name, *cp, modebuf[4];
1327   uint32 mode = 0, add, len;
1328   char *nickname = NULL, *server = NULL;
1329   uint32 num = 0;
1330   int i;
1331
1332   if (!cmd->conn) {
1333     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1334     COMMAND_ERROR;
1335     goto out;
1336   }
1337
1338   if (cmd->argc < 4) {
1339     cmd->client->ops->say(cmd->client, conn, 
1340                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1341     COMMAND_ERROR;
1342     goto out;
1343   }
1344
1345   if (cmd->argv[1][0] == '*') {
1346     if (!conn->current_channel) {
1347       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1348       COMMAND_ERROR;
1349       goto out;
1350     }
1351
1352     channel = conn->current_channel;
1353   } else {
1354     name = cmd->argv[1];
1355
1356     channel = silc_client_get_channel(cmd->client, conn, name);
1357     if (!channel) {
1358       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1359       COMMAND_ERROR;
1360       goto out;
1361     }
1362   }
1363
1364   /* Parse the typed nickname. */
1365   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1366     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1367     COMMAND_ERROR;
1368     goto out;
1369   }
1370
1371   /* Find client entry */
1372   client_entry = silc_idlist_get_client(cmd->client, conn, 
1373                                         nickname, server, num, TRUE);
1374   if (!client_entry) {
1375     if (cmd->pending) {
1376       COMMAND_ERROR;
1377       goto out;
1378     }
1379
1380     /* Client entry not found, it was requested thus mark this to be
1381        pending command. */
1382     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1383                                 conn->cmd_ident,  
1384                                 silc_client_command_destructor,
1385                                 silc_client_command_cumode, 
1386                                 silc_client_command_dup(cmd));
1387     cmd->pending = 1;
1388     return;
1389   }
1390   
1391   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1392     if (chu->client == client_entry) {
1393       chu->mode = mode;
1394       break;
1395     }
1396   }
1397
1398   /* Are we adding or removing mode */
1399   if (cmd->argv[2][0] == '-')
1400     add = FALSE;
1401   else
1402     add = TRUE;
1403
1404   /* Parse mode */
1405   cp = cmd->argv[2] + 1;
1406   len = strlen(cp);
1407   for (i = 0; i < len; i++) {
1408     switch(cp[i]) {
1409     case 'a':
1410       if (add) {
1411         mode |= SILC_CHANNEL_UMODE_CHANFO;
1412         mode |= SILC_CHANNEL_UMODE_CHANOP;
1413       } else {
1414         mode = SILC_CHANNEL_UMODE_NONE;
1415       }
1416       break;
1417     case 'f':
1418       if (add) {
1419         if (cmd->argc == 5) {
1420           if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1421           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1422                                                     cmd->client->private_key,
1423                                                     conn->hash,
1424                                                     conn->local_id,
1425                                                     SILC_ID_CLIENT);
1426           } else {
1427             auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1428                                             cmd->argv[4], cmd->argv_lens[4]);
1429           }
1430         }
1431         mode |= SILC_CHANNEL_UMODE_CHANFO;
1432       } else {
1433         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1434       }
1435       break;
1436     case 'o':
1437       if (add)
1438         mode |= SILC_CHANNEL_UMODE_CHANOP;
1439       else
1440         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1441       break;
1442     default:
1443       COMMAND_ERROR;
1444       goto out;
1445       break;
1446     }
1447   }
1448
1449   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1450   SILC_PUT32_MSB(mode, modebuf);
1451   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1452
1453   /* Send the command packet. We support sending only one mode at once
1454      that requires an argument. */
1455   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 4, 
1456                                           1, chidp->data, chidp->len, 
1457                                           2, modebuf, 4,
1458                                           3, clidp->data, clidp->len,
1459                                           4, auth ? auth->data : NULL, 
1460                                           auth ? auth->len : 0);
1461   
1462   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1463                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1464   silc_buffer_free(buffer);
1465   silc_buffer_free(chidp);
1466   silc_buffer_free(clidp);
1467   if (auth)
1468     silc_buffer_free(auth);
1469   
1470   /* Notify application */
1471   COMMAND;
1472
1473  out:
1474   if (nickname)
1475     silc_free(nickname);
1476   if (server)
1477     silc_free(server);
1478   silc_client_command_free(cmd);
1479 }
1480
1481 /* KICK command. Kicks a client out of channel. */
1482
1483 SILC_CLIENT_CMD_FUNC(kick)
1484 {
1485   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1486   SilcClientConnection conn = cmd->conn;
1487   SilcIDCacheEntry id_cache = NULL;
1488   SilcChannelEntry channel;
1489   SilcBuffer buffer, idp, idp2;
1490   SilcClientEntry target;
1491   char *name;
1492   uint32 num = 0;
1493   char *nickname = NULL, *server = NULL;
1494
1495   if (!cmd->conn) {
1496     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1497     COMMAND_ERROR;
1498     goto out;
1499   }
1500
1501   if (cmd->argc < 3) {
1502     cmd->client->ops->say(cmd->client, conn, 
1503                           "Usage: /KICK <channel> <nickname> [<comment>]");
1504     COMMAND_ERROR;
1505     goto out;
1506   }
1507
1508   if (cmd->argv[1][0] == '*') {
1509     if (!conn->current_channel) {
1510       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1511       COMMAND_ERROR;
1512       goto out;
1513     }
1514     name = conn->current_channel->channel_name;
1515   } else {
1516     name = cmd->argv[1];
1517   }
1518
1519   if (!conn->current_channel) {
1520     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1521     COMMAND_ERROR;
1522     goto out;
1523   }
1524
1525   /* Get the Channel ID of the channel */
1526   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1527     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1528     COMMAND_ERROR;
1529     goto out;
1530   }
1531
1532   channel = (SilcChannelEntry)id_cache->context;
1533
1534   /* Parse the typed nickname. */
1535   if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1536     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1537     COMMAND_ERROR;
1538     goto out;
1539   }
1540
1541   /* Get the target client */
1542   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1543                                   server, num, FALSE);
1544   if (!target) {
1545     cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1546                           cmd->argv[2]);
1547     COMMAND_ERROR;
1548     goto out;
1549   }
1550
1551   /* Send KICK command to the server */
1552   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1553   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1554   if (cmd->argc == 3)
1555     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1556                                             1, idp->data, idp->len,
1557                                             2, idp2->data, idp2->len);
1558   else
1559     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1560                                             1, idp->data, idp->len,
1561                                             2, idp2->data, idp2->len,
1562                                             3, cmd->argv[3], 
1563                                             strlen(cmd->argv[3]));
1564   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1565                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1566   silc_buffer_free(buffer);
1567   silc_buffer_free(idp);
1568   silc_buffer_free(idp2);
1569
1570   /* Notify application */
1571   COMMAND;
1572
1573  out:
1574   if (nickname)
1575     silc_free(nickname);
1576   if (server)
1577     silc_free(server);
1578   silc_client_command_free(cmd);
1579 }
1580
1581 /* OPER command. Used to obtain server operator privileges. */
1582
1583 SILC_CLIENT_CMD_FUNC(oper)
1584 {
1585   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1586   SilcClientConnection conn = cmd->conn;
1587   SilcBuffer buffer;
1588   unsigned char *auth_data;
1589   SilcBuffer auth;
1590
1591   if (!cmd->conn) {
1592     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1593     COMMAND_ERROR;
1594     goto out;
1595   }
1596
1597   if (cmd->argc < 2) {
1598     cmd->client->ops->say(cmd->client, conn, 
1599                           "Usage: /OPER <username> [<public key>]");
1600     COMMAND_ERROR;
1601     goto out;
1602   }
1603
1604   if (cmd->argc == 3) {
1605     /* XXX Get public key */
1606     auth_data = NULL;
1607     COMMAND_ERROR;
1608     goto out;
1609   } else {
1610     /* Get passphrase */
1611
1612     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1613     if (!auth_data) {
1614       COMMAND_ERROR;
1615       goto out;
1616     }
1617
1618     /* Encode the authentication payload */
1619     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1620                                     auth_data, strlen(auth_data));
1621   }
1622
1623   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1624                                           1, cmd->argv[1], 
1625                                           strlen(cmd->argv[1]),
1626                                           2, auth->data, auth->len);
1627   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1628                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1629
1630   silc_buffer_free(buffer);
1631   silc_buffer_free(auth);
1632   memset(auth_data, 0, strlen(auth_data));
1633   silc_free(auth_data);
1634
1635   /* Notify application */
1636   COMMAND;
1637
1638  out:
1639   silc_client_command_free(cmd);
1640 }
1641
1642 /* SILCOPER command. Used to obtain router operator privileges. */
1643
1644 SILC_CLIENT_CMD_FUNC(silcoper)
1645 {
1646   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1647   SilcClientConnection conn = cmd->conn;
1648   SilcBuffer buffer;
1649   unsigned char *auth_data;
1650   SilcBuffer auth;
1651
1652   if (!cmd->conn) {
1653     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1654     COMMAND_ERROR;
1655     goto out;
1656   }
1657
1658   if (cmd->argc < 2) {
1659     cmd->client->ops->say(cmd->client, conn, 
1660                           "Usage: /SILCOPER <username> [<public key>]");
1661     COMMAND_ERROR;
1662     goto out;
1663   }
1664
1665   if (cmd->argc == 3) {
1666     /* XXX Get public key */
1667     auth_data = NULL;
1668     COMMAND_ERROR;
1669     goto out;
1670   } else {
1671     /* Get passphrase */
1672
1673     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1674     if (!auth_data) {
1675       COMMAND_ERROR;
1676       goto out;
1677     }
1678
1679     /* Encode the authentication payload */
1680     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1681                                     auth_data, strlen(auth_data));
1682   }
1683
1684   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1685                                           1, cmd->argv[1], 
1686                                           strlen(cmd->argv[1]),
1687                                           2, auth->data, auth->len);
1688   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1689                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1690
1691   silc_buffer_free(buffer);
1692   silc_buffer_free(auth);
1693   memset(auth_data, 0, strlen(auth_data));
1694   silc_free(auth_data);
1695
1696   /* Notify application */
1697   COMMAND;
1698
1699  out:
1700   silc_client_command_free(cmd);
1701 }
1702
1703 /* CONNECT command. Connects the server to another server. */
1704
1705 SILC_CLIENT_CMD_FUNC(connect)
1706 {
1707   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1708   SilcClientConnection conn = cmd->conn;
1709   SilcBuffer buffer;
1710   unsigned char port[4];
1711   uint32 tmp;
1712
1713   if (!cmd->conn) {
1714     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1715     COMMAND_ERROR;
1716     goto out;
1717   }
1718
1719   if (cmd->argc < 2) {
1720     cmd->client->ops->say(cmd->client, conn, 
1721                           "Usage: /CONNECT <server> [<port>]");
1722     COMMAND_ERROR;
1723     goto out;
1724   }
1725
1726   if (cmd->argc == 3) {
1727     tmp = atoi(cmd->argv[2]);
1728     SILC_PUT32_MSB(tmp, port);
1729   }
1730
1731   if (cmd->argc == 3)
1732     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1733                                             1, cmd->argv[1], 
1734                                             strlen(cmd->argv[1]),
1735                                             2, port, 4);
1736   else
1737     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1738                                             1, cmd->argv[1], 
1739                                             strlen(cmd->argv[1]));
1740   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1741                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1742   silc_buffer_free(buffer);
1743
1744   /* Notify application */
1745   COMMAND;
1746
1747  out:
1748   silc_client_command_free(cmd);
1749 }
1750
1751 /* Command BAN. This is used to manage the ban list of the channel. */
1752
1753 SILC_CLIENT_CMD_FUNC(ban)
1754 {
1755   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1756   SilcClientConnection conn = cmd->conn;
1757   SilcChannelEntry channel;
1758   SilcBuffer buffer, chidp;
1759   int type = 0;
1760   char *name, *ban = NULL;
1761
1762   if (!cmd->conn) {
1763     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1764     COMMAND_ERROR;
1765     goto out;
1766   }
1767
1768   if (cmd->argc < 2) {
1769     cmd->client->ops->say(cmd->client, conn, 
1770                    "Usage: /BAN <channel> "
1771                    "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1772     COMMAND_ERROR;
1773     goto out;
1774   }
1775
1776   if (cmd->argv[1][0] == '*') {
1777     if (!conn->current_channel) {
1778       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1779       COMMAND_ERROR;
1780       goto out;
1781     }
1782
1783     channel = conn->current_channel;
1784   } else {
1785     name = cmd->argv[1];
1786
1787     channel = silc_client_get_channel(cmd->client, conn, name);
1788     if (!channel) {
1789       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1790       COMMAND_ERROR;
1791       goto out;
1792     }
1793   }
1794
1795   if (cmd->argc == 3) {
1796     if (cmd->argv[2][0] == '+')
1797       type = 2;
1798     else
1799       type = 3;
1800
1801     ban = cmd->argv[2];
1802     ban++;
1803   }
1804
1805   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1806
1807   /* Send the command */
1808   if (ban)
1809     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, 
1810                                             1, chidp->data, chidp->len,
1811                                             type, ban, strlen(ban));
1812   else
1813     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, 
1814                                             1, chidp->data, chidp->len);
1815
1816   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1817                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1818   silc_buffer_free(buffer);
1819   silc_buffer_free(chidp);
1820
1821   /* Notify application */
1822   COMMAND;
1823
1824  out:
1825   silc_client_command_free(cmd);
1826 }
1827
1828 /* CLOSE command. Close server connection to the remote server */
1829  
1830 SILC_CLIENT_CMD_FUNC(close)
1831 {
1832   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1833   SilcClientConnection conn = cmd->conn;
1834   SilcBuffer buffer;
1835   unsigned char port[4];
1836   uint32 tmp;
1837
1838   if (!cmd->conn) {
1839     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1840     COMMAND_ERROR;
1841     goto out;
1842   }
1843
1844   if (cmd->argc < 2) {
1845     cmd->client->ops->say(cmd->client, conn, 
1846                           "Usage: /CLOSE <server> [<port>]");
1847     COMMAND_ERROR;
1848     goto out;
1849   }
1850
1851   if (cmd->argc == 3) {
1852     tmp = atoi(cmd->argv[2]);
1853     SILC_PUT32_MSB(tmp, port);
1854   }
1855
1856   if (cmd->argc == 3)
1857     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1858                                             1, cmd->argv[1], 
1859                                             strlen(cmd->argv[1]),
1860                                             2, port, 4);
1861   else
1862     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1863                                             1, cmd->argv[1], 
1864                                             strlen(cmd->argv[1]));
1865   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1866                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1867   silc_buffer_free(buffer);
1868
1869   /* Notify application */
1870   COMMAND;
1871
1872  out:
1873   silc_client_command_free(cmd);
1874 }
1875  
1876 /* SHUTDOWN command. Shutdowns the server. */
1877
1878 SILC_CLIENT_CMD_FUNC(shutdown)
1879 {
1880   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1881
1882   if (!cmd->conn) {
1883     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1884     COMMAND_ERROR;
1885     goto out;
1886   }
1887
1888   /* Send the command */
1889   silc_client_send_command(cmd->client, cmd->conn, 
1890                            SILC_COMMAND_SHUTDOWN, 0, 0);
1891
1892   /* Notify application */
1893   COMMAND;
1894
1895  out:
1896   silc_client_command_free(cmd);
1897 }
1898
1899 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1900
1901 SILC_CLIENT_CMD_FUNC(leave)
1902 {
1903   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1904   SilcClientConnection conn = cmd->conn;
1905   SilcIDCacheEntry id_cache = NULL;
1906   SilcChannelEntry channel;
1907   SilcBuffer buffer, idp;
1908   char *name;
1909
1910   if (!cmd->conn) {
1911     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1912     COMMAND_ERROR;
1913     goto out;
1914   }
1915
1916   if (cmd->argc != 2) {
1917     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1918     COMMAND_ERROR;
1919     goto out;
1920   }
1921
1922   if (cmd->argv[1][0] == '*') {
1923     if (!conn->current_channel) {
1924       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1925       COMMAND_ERROR;
1926       goto out;
1927     }
1928     name = conn->current_channel->channel_name;
1929   } else {
1930     name = cmd->argv[1];
1931   }
1932
1933   if (!conn->current_channel) {
1934     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1935     COMMAND_ERROR;
1936     goto out;
1937   }
1938
1939   /* Get the Channel ID of the channel */
1940   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1941     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1942     COMMAND_ERROR;
1943     goto out;
1944   }
1945
1946   channel = (SilcChannelEntry)id_cache->context;
1947
1948   /* Send LEAVE command to the server */
1949   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1950   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1951                                           1, idp->data, idp->len);
1952   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1953                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1954   silc_buffer_free(buffer);
1955   silc_buffer_free(idp);
1956
1957   /* Notify application */
1958   COMMAND;
1959
1960   conn->current_channel = NULL;
1961
1962   silc_idcache_del_by_id(conn->channel_cache, channel->id);
1963   silc_free(channel->channel_name);
1964   silc_free(channel->id);
1965   silc_free(channel->key);
1966   silc_cipher_free(channel->channel_key);
1967   silc_free(channel);
1968
1969  out:
1970   silc_client_command_free(cmd);
1971 }
1972
1973 /* Command USERS. Requests the USERS of the clients joined on requested
1974    channel. */
1975
1976 SILC_CLIENT_CMD_FUNC(users)
1977 {
1978   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1979   SilcClientConnection conn = cmd->conn;
1980   SilcIDCacheEntry id_cache = NULL;
1981   SilcChannelEntry channel;
1982   SilcBuffer buffer, idp;
1983   char *name;
1984
1985   if (!cmd->conn) {
1986     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1987     COMMAND_ERROR;
1988     goto out;
1989   }
1990
1991   if (cmd->argc != 2) {
1992     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1993     COMMAND_ERROR;
1994     goto out;
1995   }
1996
1997   if (cmd->argv[1][0] == '*') {
1998     if (!conn->current_channel) {
1999       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
2000       COMMAND_ERROR;
2001       goto out;
2002     }
2003     name = conn->current_channel->channel_name;
2004   } else {
2005     name = cmd->argv[1];
2006   }
2007
2008   if (!conn->current_channel) {
2009     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
2010     COMMAND_ERROR;
2011     goto out;
2012   }
2013
2014   /* Get the Channel ID of the channel */
2015   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
2016     /* XXX should resolve the channel ID; LIST command */
2017     cmd->client->ops->say(cmd->client, conn, 
2018                           "You are not on that channel", name);
2019     COMMAND_ERROR;
2020     goto out;
2021   }
2022
2023   channel = (SilcChannelEntry)id_cache->context;
2024
2025   if (!cmd->pending) {
2026     /* Send USERS command to the server */
2027     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
2028     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
2029                                             ++conn->cmd_ident, 1, 
2030                                             1, idp->data, idp->len);
2031     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
2032                             NULL, 0, NULL, NULL, buffer->data, 
2033                             buffer->len, TRUE);
2034     silc_buffer_free(buffer);
2035     silc_buffer_free(idp);
2036
2037     /* Register pending callback which will recall this command callback with
2038        same context and reprocesses the command. When reprocessing we actually
2039        display the information on the screen. */
2040     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, 
2041                                 silc_client_command_destructor,
2042                                 silc_client_command_users, 
2043                                 silc_client_command_dup(cmd));
2044     cmd->pending = TRUE;
2045     return;
2046   }
2047
2048   /* Notify application */
2049   COMMAND;
2050
2051  out:
2052   silc_client_command_free(cmd);
2053 }
2054
2055 /* Command GETKEY. Used to fetch remote client's public key. */
2056
2057 SILC_CLIENT_CMD_FUNC(getkey)
2058 {
2059   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2060   SilcClientConnection conn = cmd->conn;
2061   SilcClient client = cmd->client;
2062   SilcClientEntry client_entry = NULL;
2063   uint32 num = 0;
2064   char *nickname = NULL, *server = NULL;
2065   SilcBuffer idp, buffer;
2066
2067   if (!cmd->conn) {
2068     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2069     COMMAND_ERROR;
2070     goto out;
2071   }
2072
2073   if (cmd->argc < 2) {
2074     client->ops->say(client, conn, "Usage: /GETKEY <nickname>");
2075     COMMAND_ERROR;
2076     goto out;
2077   }
2078
2079   /* Parse the typed nickname. */
2080   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
2081     client->ops->say(client, conn, "Bad nickname");
2082     COMMAND_ERROR;
2083     goto out;
2084   }
2085
2086   /* Find client entry */
2087   client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
2088                                         TRUE);
2089   if (!client_entry) {
2090     /* Client entry not found, it was requested thus mark this to be
2091        pending command. */
2092     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2093                                 conn->cmd_ident,  
2094                                 silc_client_command_destructor,
2095                                 silc_client_command_getkey, 
2096                                 silc_client_command_dup(cmd));
2097     cmd->pending = 1;
2098     return;
2099   }
2100
2101   idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2102   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
2103                                           1, idp->data, idp->len);
2104   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2105                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2106   silc_buffer_free(buffer);
2107   silc_buffer_free(idp);
2108
2109   /* Notify application */
2110   COMMAND;
2111
2112  out:
2113   silc_client_command_free(cmd);
2114 }