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