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