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