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_SERVER_LEN);
866   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
867                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
868   silc_buffer_free(buffer);
869
870   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
871                       SILC_ID_SERVER);
872   if (!id) {
873     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
874     COMMAND_ERROR;
875     goto out;
876   }
877
878   /* Start counting time */
879   for (i = 0; i < conn->ping_count; i++) {
880     if (conn->ping[i].dest_id == NULL) {
881       conn->ping[i].start_time = time(NULL);
882       conn->ping[i].dest_id = id;
883       conn->ping[i].dest_name = name;
884       conn->ping_count++;
885       break;
886     }
887   }
888   if (i >= conn->ping_count) {
889     i = conn->ping_count;
890     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
891     conn->ping[i].start_time = time(NULL);
892     conn->ping[i].dest_id = id;
893     conn->ping[i].dest_name = name;
894     conn->ping_count++;
895   }
896   
897   /* Notify application */
898   COMMAND;
899
900  out:
901   silc_client_command_free(cmd);
902 }
903
904 SILC_CLIENT_CMD_FUNC(notice)
905 {
906 }
907
908 /* Command JOIN. Joins to a channel. */
909
910 SILC_CLIENT_CMD_FUNC(join)
911 {
912   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
913   SilcClientConnection conn = cmd->conn;
914   SilcIDCacheEntry id_cache = NULL;
915   SilcBuffer buffer, idp;
916
917   if (!cmd->conn) {
918     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
919     COMMAND_ERROR;
920     goto out;
921   }
922
923   if (cmd->argc < 2) {
924     /* Show channels currently joined to */
925
926     goto out;
927   }
928
929   /* See if we have joined to the requested channel already */
930   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
931                                     &id_cache)) {
932     cmd->client->ops->say(cmd->client, conn, 
933                           "You are talking to channel %s", cmd->argv[1]);
934     conn->current_channel = (SilcChannelEntry)id_cache->context;
935 #if 0
936     cmd->client->screen->bottom_line->channel = cmd->argv[1];
937     silc_screen_print_bottom_line(cmd->client->screen, 0);
938 #endif
939     goto out;
940   }
941
942   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
943
944   /* Send JOIN command to the server */
945   if (cmd->argc == 2)
946     buffer = 
947       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
948                                      1, cmd->argv[1], cmd->argv_lens[1],
949                                      2, idp->data, idp->len);
950   else if (cmd->argc == 3)
951     /* XXX Buggy */
952     buffer = 
953       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
954                                      1, cmd->argv[1], cmd->argv_lens[1],
955                                      2, idp->data, idp->len,
956                                      3, cmd->argv[2], cmd->argv_lens[2]);
957   else
958     buffer = 
959       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
960                                      1, cmd->argv[1], cmd->argv_lens[1],
961                                      2, idp->data, idp->len,
962                                      3, cmd->argv[2], cmd->argv_lens[2],
963                                      4, cmd->argv[3], cmd->argv_lens[3]);
964
965   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
966                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
967   silc_buffer_free(buffer);
968   silc_buffer_free(idp);
969
970   /* Notify application */
971   COMMAND;
972
973  out:
974   silc_client_command_free(cmd);
975 }
976
977 /* MOTD command. Requests motd from server. */
978
979 SILC_CLIENT_CMD_FUNC(motd)
980 {
981   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
982   SilcClientConnection conn = cmd->conn;
983   SilcBuffer buffer;
984
985   if (!cmd->conn) {
986     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
987     COMMAND_ERROR;
988     goto out;
989   }
990
991   if (cmd->argc < 1 || cmd->argc > 2) {
992     cmd->client->ops->say(cmd->client, conn,
993                           "Usage: /MOTD [<server>]");
994     COMMAND_ERROR;
995     goto out;
996   }
997
998   /* Send TOPIC command to the server */
999   if (cmd->argc == 1)
1000     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1001                                             1, conn->remote_host, 
1002                                             strlen(conn->remote_host));
1003   else
1004     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1005                                             1, cmd->argv[1], 
1006                                             cmd->argv_lens[1]);
1007   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1008                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1009   silc_buffer_free(buffer);
1010
1011   /* Notify application */
1012   COMMAND;
1013
1014  out:
1015   silc_client_command_free(cmd);
1016 }
1017
1018 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1019    modes as client cannot set itself server/router operator privileges. */
1020
1021 SILC_CLIENT_CMD_FUNC(umode)
1022 {
1023   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1024   SilcClientConnection conn = cmd->conn;
1025   SilcBuffer buffer, idp;
1026   unsigned char *cp, modebuf[4];
1027   uint32 mode, add, len;
1028   int i;
1029
1030   if (!cmd->conn) {
1031     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1032     COMMAND_ERROR;
1033     goto out;
1034   }
1035
1036   if (cmd->argc < 2) {
1037     cmd->client->ops->say(cmd->client, conn, 
1038                   "Usage: /UMODE +|-<modes>");
1039     COMMAND_ERROR;
1040     goto out;
1041   }
1042
1043   mode = conn->local_entry->mode;
1044
1045   /* Are we adding or removing mode */
1046   if (cmd->argv[1][0] == '-')
1047     add = FALSE;
1048   else
1049     add = TRUE;
1050
1051   /* Parse mode */
1052   cp = cmd->argv[1] + 1;
1053   len = strlen(cp);
1054   for (i = 0; i < len; i++) {
1055     switch(cp[i]) {
1056     case 'a':
1057       if (add) {
1058         mode = 0;
1059         mode |= SILC_UMODE_SERVER_OPERATOR;
1060         mode |= SILC_UMODE_ROUTER_OPERATOR;
1061       } else {
1062         mode = SILC_UMODE_NONE;
1063       }
1064       break;
1065     case 's':
1066       if (add)
1067         mode |= SILC_UMODE_SERVER_OPERATOR;
1068       else
1069         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1070       break;
1071     case 'r':
1072       if (add)
1073         mode |= SILC_UMODE_ROUTER_OPERATOR;
1074       else
1075         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1076       break;
1077     case 'g':
1078       if (add)
1079         mode |= SILC_UMODE_GONE;
1080       else
1081         mode &= ~SILC_UMODE_GONE;
1082       break;
1083     default:
1084       COMMAND_ERROR;
1085       goto out;
1086       break;
1087     }
1088   }
1089
1090   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1091   SILC_PUT32_MSB(mode, modebuf);
1092
1093   /* Send the command packet. We support sending only one mode at once
1094      that requires an argument. */
1095   buffer = 
1096     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1097                                    1, idp->data, idp->len, 
1098                                    2, modebuf, sizeof(modebuf));
1099   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1100                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1101   silc_buffer_free(buffer);
1102   silc_buffer_free(idp);
1103
1104   /* Notify application */
1105   COMMAND;
1106
1107  out:
1108   silc_client_command_free(cmd);
1109 }
1110
1111 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1112    can be set several at once. Those modes that require argument must be set
1113    separately (unless set with modes that does not require arguments). */
1114
1115 SILC_CLIENT_CMD_FUNC(cmode)
1116 {
1117   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1118   SilcClientConnection conn = cmd->conn;
1119   SilcChannelEntry channel;
1120   SilcBuffer buffer, chidp, auth = NULL;
1121   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1122   uint32 mode, add, type, len, arg_len = 0;
1123   int i;
1124
1125   if (!cmd->conn) {
1126     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1127     COMMAND_ERROR;
1128     goto out;
1129   }
1130
1131   if (cmd->argc < 3) {
1132     cmd->client->ops->say(cmd->client, conn, 
1133                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1134     COMMAND_ERROR;
1135     goto out;
1136   }
1137
1138   if (cmd->argv[1][0] == '*') {
1139     if (!conn->current_channel) {
1140       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1141       COMMAND_ERROR;
1142       goto out;
1143     }
1144
1145     channel = conn->current_channel;
1146   } else {
1147     name = cmd->argv[1];
1148
1149     channel = silc_client_get_channel(cmd->client, conn, name);
1150     if (!channel) {
1151       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1152       COMMAND_ERROR;
1153       goto out;
1154     }
1155   }
1156
1157   mode = channel->mode;
1158
1159   /* Are we adding or removing mode */
1160   if (cmd->argv[2][0] == '-')
1161     add = FALSE;
1162   else
1163     add = TRUE;
1164
1165   /* Argument type to be sent to server */
1166   type = 0;
1167
1168   /* Parse mode */
1169   cp = cmd->argv[2] + 1;
1170   len = strlen(cp);
1171   for (i = 0; i < len; i++) {
1172     switch(cp[i]) {
1173     case 'p':
1174       if (add)
1175         mode |= SILC_CHANNEL_MODE_PRIVATE;
1176       else
1177         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1178       break;
1179     case 's':
1180       if (add)
1181         mode |= SILC_CHANNEL_MODE_SECRET;
1182       else
1183         mode &= ~SILC_CHANNEL_MODE_SECRET;
1184       break;
1185     case 'k':
1186       if (add)
1187         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1188       else
1189         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1190       break;
1191     case 'i':
1192       if (add)
1193         mode |= SILC_CHANNEL_MODE_INVITE;
1194       else
1195         mode &= ~SILC_CHANNEL_MODE_INVITE;
1196       break;
1197     case 't':
1198       if (add)
1199         mode |= SILC_CHANNEL_MODE_TOPIC;
1200       else
1201         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1202       break;
1203     case 'l':
1204       if (add) {
1205         int ll;
1206         mode |= SILC_CHANNEL_MODE_ULIMIT;
1207         type = 3;
1208         ll = atoi(cmd->argv[3]);
1209         SILC_PUT32_MSB(ll, tmp);
1210         arg = tmp;
1211         arg_len = 4;
1212       } else {
1213         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1214       }
1215       break;
1216     case 'a':
1217       if (add) {
1218         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1219         type = 4;
1220         arg = cmd->argv[3];
1221         arg_len = cmd->argv_lens[3];
1222       } else {
1223         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1224       }
1225       break;
1226     case 'c':
1227       if (add) {
1228         mode |= SILC_CHANNEL_MODE_CIPHER;
1229         type = 5;
1230         arg = cmd->argv[3];
1231         arg_len = cmd->argv_lens[3];
1232       } else {
1233         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1234       }
1235       break;
1236     case 'h':
1237       if (add) {
1238         mode |= SILC_CHANNEL_MODE_HMAC;
1239         type = 6;
1240         arg = cmd->argv[3];
1241         arg_len = cmd->argv_lens[3];
1242       } else {
1243         mode &= ~SILC_CHANNEL_MODE_HMAC;
1244       }
1245       break;
1246     case 'f':
1247       if (add) {
1248         mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1249         type = 7;
1250
1251         if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1252           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1253                                                     cmd->client->private_key,
1254                                                     conn->hash,
1255                                                     conn->local_id,
1256                                                     SILC_ID_CLIENT);
1257         } else {
1258           auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1259                                           cmd->argv[3], cmd->argv_lens[3]);
1260         }
1261
1262         arg = auth->data;
1263         arg_len = auth->len;
1264       } else {
1265         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1266       }
1267       break;
1268     default:
1269       COMMAND_ERROR;
1270       goto out;
1271       break;
1272     }
1273   }
1274
1275   if (type && cmd->argc < 3) {
1276     COMMAND_ERROR;
1277     goto out;
1278   }
1279
1280   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1281   SILC_PUT32_MSB(mode, modebuf);
1282
1283   /* Send the command packet. We support sending only one mode at once
1284      that requires an argument. */
1285   if (type && arg) {
1286     buffer = 
1287       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1288                                      1, chidp->data, chidp->len, 
1289                                      2, modebuf, sizeof(modebuf),
1290                                      type, arg, arg_len);
1291   } else {
1292     buffer = 
1293       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1294                                      1, chidp->data, chidp->len, 
1295                                      2, modebuf, sizeof(modebuf));
1296   }
1297
1298   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1299                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1300   silc_buffer_free(buffer);
1301   silc_buffer_free(chidp);
1302   if (auth)
1303     silc_buffer_free(auth);
1304
1305   /* Notify application */
1306   COMMAND;
1307
1308  out:
1309   silc_client_command_free(cmd);
1310 }
1311
1312 /* CUMODE command. Changes client's mode on a channel. */
1313
1314 SILC_CLIENT_CMD_FUNC(cumode)
1315 {
1316   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1317   SilcClientConnection conn = cmd->conn;
1318   SilcChannelEntry channel;
1319   SilcChannelUser chu;
1320   SilcClientEntry client_entry;
1321   SilcBuffer buffer, clidp, chidp, auth = NULL;
1322   unsigned char *name, *cp, modebuf[4];
1323   uint32 mode = 0, add, len;
1324   char *nickname = NULL, *server = NULL;
1325   uint32 num = 0;
1326   int i;
1327
1328   if (!cmd->conn) {
1329     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1330     COMMAND_ERROR;
1331     goto out;
1332   }
1333
1334   if (cmd->argc < 4) {
1335     cmd->client->ops->say(cmd->client, conn, 
1336                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1337     COMMAND_ERROR;
1338     goto out;
1339   }
1340
1341   if (cmd->argv[1][0] == '*') {
1342     if (!conn->current_channel) {
1343       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1344       COMMAND_ERROR;
1345       goto out;
1346     }
1347
1348     channel = conn->current_channel;
1349   } else {
1350     name = cmd->argv[1];
1351
1352     channel = silc_client_get_channel(cmd->client, conn, name);
1353     if (!channel) {
1354       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1355       COMMAND_ERROR;
1356       goto out;
1357     }
1358   }
1359
1360   /* Parse the typed nickname. */
1361   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1362     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1363     COMMAND_ERROR;
1364     goto out;
1365   }
1366
1367   /* Find client entry */
1368   client_entry = silc_idlist_get_client(cmd->client, conn, 
1369                                         nickname, server, num, TRUE);
1370   if (!client_entry) {
1371     if (cmd->pending) {
1372       COMMAND_ERROR;
1373       goto out;
1374     }
1375
1376     /* Client entry not found, it was requested thus mark this to be
1377        pending command. */
1378     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1379                                 conn->cmd_ident,  
1380                                 silc_client_command_destructor,
1381                                 silc_client_command_cumode, 
1382                                 silc_client_command_dup(cmd));
1383     cmd->pending = 1;
1384     return;
1385   }
1386   
1387   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1388     if (chu->client == client_entry) {
1389       chu->mode = mode;
1390       break;
1391     }
1392   }
1393
1394   /* Are we adding or removing mode */
1395   if (cmd->argv[2][0] == '-')
1396     add = FALSE;
1397   else
1398     add = TRUE;
1399
1400   /* Parse mode */
1401   cp = cmd->argv[2] + 1;
1402   len = strlen(cp);
1403   for (i = 0; i < len; i++) {
1404     switch(cp[i]) {
1405     case 'a':
1406       if (add) {
1407         mode |= SILC_CHANNEL_UMODE_CHANFO;
1408         mode |= SILC_CHANNEL_UMODE_CHANOP;
1409       } else {
1410         mode = SILC_CHANNEL_UMODE_NONE;
1411       }
1412       break;
1413     case 'f':
1414       if (add) {
1415         if (cmd->argc == 5) {
1416           if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1417           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1418                                                     cmd->client->private_key,
1419                                                     conn->hash,
1420                                                     conn->local_id,
1421                                                     SILC_ID_CLIENT);
1422           } else {
1423             auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1424                                             cmd->argv[4], cmd->argv_lens[4]);
1425           }
1426         }
1427         mode |= SILC_CHANNEL_UMODE_CHANFO;
1428       } else {
1429         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1430       }
1431       break;
1432     case 'o':
1433       if (add)
1434         mode |= SILC_CHANNEL_UMODE_CHANOP;
1435       else
1436         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1437       break;
1438     default:
1439       COMMAND_ERROR;
1440       goto out;
1441       break;
1442     }
1443   }
1444
1445   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1446   SILC_PUT32_MSB(mode, modebuf);
1447   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1448
1449   /* Send the command packet. We support sending only one mode at once
1450      that requires an argument. */
1451   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 4, 
1452                                           1, chidp->data, chidp->len, 
1453                                           2, modebuf, 4,
1454                                           3, clidp->data, clidp->len,
1455                                           4, auth ? auth->data : NULL, 
1456                                           auth ? auth->len : 0);
1457   
1458   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1459                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1460   silc_buffer_free(buffer);
1461   silc_buffer_free(chidp);
1462   silc_buffer_free(clidp);
1463   if (auth)
1464     silc_buffer_free(auth);
1465   
1466   /* Notify application */
1467   COMMAND;
1468
1469  out:
1470   if (nickname)
1471     silc_free(nickname);
1472   if (server)
1473     silc_free(server);
1474   silc_client_command_free(cmd);
1475 }
1476
1477 /* KICK command. Kicks a client out of channel. */
1478
1479 SILC_CLIENT_CMD_FUNC(kick)
1480 {
1481   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1482   SilcClientConnection conn = cmd->conn;
1483   SilcIDCacheEntry id_cache = NULL;
1484   SilcChannelEntry channel;
1485   SilcBuffer buffer, idp, idp2;
1486   SilcClientEntry target;
1487   char *name;
1488   uint32 num = 0;
1489   char *nickname = NULL, *server = NULL;
1490
1491   if (!cmd->conn) {
1492     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1493     COMMAND_ERROR;
1494     goto out;
1495   }
1496
1497   if (cmd->argc < 3) {
1498     cmd->client->ops->say(cmd->client, conn, 
1499                           "Usage: /KICK <channel> <nickname> [<comment>]");
1500     COMMAND_ERROR;
1501     goto out;
1502   }
1503
1504   if (cmd->argv[1][0] == '*') {
1505     if (!conn->current_channel) {
1506       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1507       COMMAND_ERROR;
1508       goto out;
1509     }
1510     name = conn->current_channel->channel_name;
1511   } else {
1512     name = cmd->argv[1];
1513   }
1514
1515   if (!conn->current_channel) {
1516     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1517     COMMAND_ERROR;
1518     goto out;
1519   }
1520
1521   /* Get the Channel ID of the channel */
1522   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1523     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1524     COMMAND_ERROR;
1525     goto out;
1526   }
1527
1528   channel = (SilcChannelEntry)id_cache->context;
1529
1530   /* Parse the typed nickname. */
1531   if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1532     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1533     COMMAND_ERROR;
1534     goto out;
1535   }
1536
1537   /* Get the target client */
1538   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1539                                   server, num, FALSE);
1540   if (!target) {
1541     cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1542                           cmd->argv[2]);
1543     COMMAND_ERROR;
1544     goto out;
1545   }
1546
1547   /* Send KICK command to the server */
1548   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1549   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1550   if (cmd->argc == 3)
1551     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1552                                             1, idp->data, idp->len,
1553                                             2, idp2->data, idp2->len);
1554   else
1555     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1556                                             1, idp->data, idp->len,
1557                                             2, idp2->data, idp2->len,
1558                                             3, cmd->argv[3], 
1559                                             strlen(cmd->argv[3]));
1560   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1561                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1562   silc_buffer_free(buffer);
1563   silc_buffer_free(idp);
1564   silc_buffer_free(idp2);
1565
1566   /* Notify application */
1567   COMMAND;
1568
1569  out:
1570   if (nickname)
1571     silc_free(nickname);
1572   if (server)
1573     silc_free(server);
1574   silc_client_command_free(cmd);
1575 }
1576
1577 /* OPER command. Used to obtain server operator privileges. */
1578
1579 SILC_CLIENT_CMD_FUNC(oper)
1580 {
1581   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1582   SilcClientConnection conn = cmd->conn;
1583   SilcBuffer buffer;
1584   unsigned char *auth_data;
1585   SilcBuffer auth;
1586
1587   if (!cmd->conn) {
1588     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1589     COMMAND_ERROR;
1590     goto out;
1591   }
1592
1593   if (cmd->argc < 2) {
1594     cmd->client->ops->say(cmd->client, conn, 
1595                           "Usage: /OPER <username> [<public key>]");
1596     COMMAND_ERROR;
1597     goto out;
1598   }
1599
1600   if (cmd->argc == 3) {
1601     /* XXX Get public key */
1602     auth_data = NULL;
1603     COMMAND_ERROR;
1604     goto out;
1605   } else {
1606     /* Get passphrase */
1607
1608     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1609     if (!auth_data) {
1610       COMMAND_ERROR;
1611       goto out;
1612     }
1613
1614     /* Encode the authentication payload */
1615     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1616                                     auth_data, strlen(auth_data));
1617   }
1618
1619   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1620                                           1, cmd->argv[1], 
1621                                           strlen(cmd->argv[1]),
1622                                           2, auth->data, auth->len);
1623   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1624                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1625
1626   silc_buffer_free(buffer);
1627   silc_buffer_free(auth);
1628   memset(auth_data, 0, strlen(auth_data));
1629   silc_free(auth_data);
1630
1631   /* Notify application */
1632   COMMAND;
1633
1634  out:
1635   silc_client_command_free(cmd);
1636 }
1637
1638 /* SILCOPER command. Used to obtain router operator privileges. */
1639
1640 SILC_CLIENT_CMD_FUNC(silcoper)
1641 {
1642   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1643   SilcClientConnection conn = cmd->conn;
1644   SilcBuffer buffer;
1645   unsigned char *auth_data;
1646   SilcBuffer auth;
1647
1648   if (!cmd->conn) {
1649     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1650     COMMAND_ERROR;
1651     goto out;
1652   }
1653
1654   if (cmd->argc < 2) {
1655     cmd->client->ops->say(cmd->client, conn, 
1656                           "Usage: /SILCOPER <username> [<public key>]");
1657     COMMAND_ERROR;
1658     goto out;
1659   }
1660
1661   if (cmd->argc == 3) {
1662     /* XXX Get public key */
1663     auth_data = NULL;
1664     COMMAND_ERROR;
1665     goto out;
1666   } else {
1667     /* Get passphrase */
1668
1669     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1670     if (!auth_data) {
1671       COMMAND_ERROR;
1672       goto out;
1673     }
1674
1675     /* Encode the authentication payload */
1676     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1677                                     auth_data, strlen(auth_data));
1678   }
1679
1680   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1681                                           1, cmd->argv[1], 
1682                                           strlen(cmd->argv[1]),
1683                                           2, auth->data, auth->len);
1684   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1685                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1686
1687   silc_buffer_free(buffer);
1688   silc_buffer_free(auth);
1689   memset(auth_data, 0, strlen(auth_data));
1690   silc_free(auth_data);
1691
1692   /* Notify application */
1693   COMMAND;
1694
1695  out:
1696   silc_client_command_free(cmd);
1697 }
1698
1699 /* CONNECT command. Connects the server to another server. */
1700
1701 SILC_CLIENT_CMD_FUNC(connect)
1702 {
1703   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1704   SilcClientConnection conn = cmd->conn;
1705   SilcBuffer buffer;
1706   unsigned char port[4];
1707   uint32 tmp;
1708
1709   if (!cmd->conn) {
1710     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1711     COMMAND_ERROR;
1712     goto out;
1713   }
1714
1715   if (cmd->argc < 2) {
1716     cmd->client->ops->say(cmd->client, conn, 
1717                           "Usage: /CONNECT <server> [<port>]");
1718     COMMAND_ERROR;
1719     goto out;
1720   }
1721
1722   if (cmd->argc == 3) {
1723     tmp = atoi(cmd->argv[2]);
1724     SILC_PUT32_MSB(tmp, port);
1725   }
1726
1727   if (cmd->argc == 3)
1728     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1729                                             1, cmd->argv[1], 
1730                                             strlen(cmd->argv[1]),
1731                                             2, port, 4);
1732   else
1733     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1734                                             1, cmd->argv[1], 
1735                                             strlen(cmd->argv[1]));
1736   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1737                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1738   silc_buffer_free(buffer);
1739
1740   /* Notify application */
1741   COMMAND;
1742
1743  out:
1744   silc_client_command_free(cmd);
1745 }
1746
1747 /* Command BAN. This is used to manage the ban list of the channel. */
1748
1749 SILC_CLIENT_CMD_FUNC(ban)
1750 {
1751   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1752   SilcClientConnection conn = cmd->conn;
1753   SilcChannelEntry channel;
1754   SilcBuffer buffer, chidp;
1755   int type = 0;
1756   char *name, *ban = NULL;
1757
1758   if (!cmd->conn) {
1759     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1760     COMMAND_ERROR;
1761     goto out;
1762   }
1763
1764   if (cmd->argc < 2) {
1765     cmd->client->ops->say(cmd->client, conn, 
1766                    "Usage: /BAN <channel> "
1767                    "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1768     COMMAND_ERROR;
1769     goto out;
1770   }
1771
1772   if (cmd->argv[1][0] == '*') {
1773     if (!conn->current_channel) {
1774       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1775       COMMAND_ERROR;
1776       goto out;
1777     }
1778
1779     channel = conn->current_channel;
1780   } else {
1781     name = cmd->argv[1];
1782
1783     channel = silc_client_get_channel(cmd->client, conn, name);
1784     if (!channel) {
1785       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1786       COMMAND_ERROR;
1787       goto out;
1788     }
1789   }
1790
1791   if (cmd->argc == 3) {
1792     if (cmd->argv[2][0] == '+')
1793       type = 2;
1794     else
1795       type = 3;
1796
1797     ban = cmd->argv[2];
1798     ban++;
1799   }
1800
1801   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1802
1803   /* Send the command */
1804   if (ban)
1805     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, 
1806                                             1, chidp->data, chidp->len,
1807                                             type, ban, strlen(ban));
1808   else
1809     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, 
1810                                             1, chidp->data, chidp->len);
1811
1812   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1813                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1814   silc_buffer_free(buffer);
1815   silc_buffer_free(chidp);
1816
1817   /* Notify application */
1818   COMMAND;
1819
1820  out:
1821   silc_client_command_free(cmd);
1822 }
1823
1824 /* CLOSE command. Close server connection to the remote server */
1825  
1826 SILC_CLIENT_CMD_FUNC(close)
1827 {
1828   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1829   SilcClientConnection conn = cmd->conn;
1830   SilcBuffer buffer;
1831   unsigned char port[4];
1832   uint32 tmp;
1833
1834   if (!cmd->conn) {
1835     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1836     COMMAND_ERROR;
1837     goto out;
1838   }
1839
1840   if (cmd->argc < 2) {
1841     cmd->client->ops->say(cmd->client, conn, 
1842                           "Usage: /CLOSE <server> [<port>]");
1843     COMMAND_ERROR;
1844     goto out;
1845   }
1846
1847   if (cmd->argc == 3) {
1848     tmp = atoi(cmd->argv[2]);
1849     SILC_PUT32_MSB(tmp, port);
1850   }
1851
1852   if (cmd->argc == 3)
1853     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1854                                             1, cmd->argv[1], 
1855                                             strlen(cmd->argv[1]),
1856                                             2, port, 4);
1857   else
1858     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1859                                             1, cmd->argv[1], 
1860                                             strlen(cmd->argv[1]));
1861   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1862                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1863   silc_buffer_free(buffer);
1864
1865   /* Notify application */
1866   COMMAND;
1867
1868  out:
1869   silc_client_command_free(cmd);
1870 }
1871  
1872 /* SHUTDOWN command. Shutdowns the server. */
1873
1874 SILC_CLIENT_CMD_FUNC(shutdown)
1875 {
1876   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1877
1878   if (!cmd->conn) {
1879     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1880     COMMAND_ERROR;
1881     goto out;
1882   }
1883
1884   /* Send the command */
1885   silc_client_send_command(cmd->client, cmd->conn, 
1886                            SILC_COMMAND_SHUTDOWN, 0, 0);
1887
1888   /* Notify application */
1889   COMMAND;
1890
1891  out:
1892   silc_client_command_free(cmd);
1893 }
1894
1895 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1896
1897 SILC_CLIENT_CMD_FUNC(leave)
1898 {
1899   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1900   SilcClientConnection conn = cmd->conn;
1901   SilcIDCacheEntry id_cache = NULL;
1902   SilcChannelEntry channel;
1903   SilcBuffer buffer, idp;
1904   char *name;
1905
1906   if (!cmd->conn) {
1907     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1908     COMMAND_ERROR;
1909     goto out;
1910   }
1911
1912   if (cmd->argc != 2) {
1913     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1914     COMMAND_ERROR;
1915     goto out;
1916   }
1917
1918   if (cmd->argv[1][0] == '*') {
1919     if (!conn->current_channel) {
1920       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1921       COMMAND_ERROR;
1922       goto out;
1923     }
1924     name = conn->current_channel->channel_name;
1925   } else {
1926     name = cmd->argv[1];
1927   }
1928
1929   if (!conn->current_channel) {
1930     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1931     COMMAND_ERROR;
1932     goto out;
1933   }
1934
1935   /* Get the Channel ID of the channel */
1936   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1937     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1938     COMMAND_ERROR;
1939     goto out;
1940   }
1941
1942   channel = (SilcChannelEntry)id_cache->context;
1943
1944   /* Send LEAVE command to the server */
1945   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1946   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1947                                           1, idp->data, idp->len);
1948   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1949                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1950   silc_buffer_free(buffer);
1951   silc_buffer_free(idp);
1952
1953   /* We won't talk anymore on this channel */
1954   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1955
1956   conn->current_channel = NULL;
1957
1958   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1959   silc_free(channel->channel_name);
1960   silc_free(channel->id);
1961   silc_free(channel->key);
1962   silc_cipher_free(channel->channel_key);
1963   silc_free(channel);
1964
1965   /* Notify application */
1966   COMMAND;
1967
1968  out:
1969   silc_client_command_free(cmd);
1970 }
1971
1972 /* Command USERS. Requests the USERS of the clients joined on requested
1973    channel. */
1974
1975 SILC_CLIENT_CMD_FUNC(users)
1976 {
1977   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1978   SilcClientConnection conn = cmd->conn;
1979   SilcIDCacheEntry id_cache = NULL;
1980   SilcChannelEntry channel;
1981   SilcBuffer buffer, idp;
1982   char *name;
1983
1984   if (!cmd->conn) {
1985     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1986     COMMAND_ERROR;
1987     goto out;
1988   }
1989
1990   if (cmd->argc != 2) {
1991     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1992     COMMAND_ERROR;
1993     goto out;
1994   }
1995
1996   if (cmd->argv[1][0] == '*') {
1997     if (!conn->current_channel) {
1998       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1999       COMMAND_ERROR;
2000       goto out;
2001     }
2002     name = conn->current_channel->channel_name;
2003   } else {
2004     name = cmd->argv[1];
2005   }
2006
2007   if (!conn->current_channel) {
2008     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
2009     COMMAND_ERROR;
2010     goto out;
2011   }
2012
2013   /* Get the Channel ID of the channel */
2014   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
2015     /* XXX should resolve the channel ID; LIST command */
2016     cmd->client->ops->say(cmd->client, conn, 
2017                           "You are not on that channel", name);
2018     COMMAND_ERROR;
2019     goto out;
2020   }
2021
2022   channel = (SilcChannelEntry)id_cache->context;
2023
2024   if (!cmd->pending) {
2025     /* Send USERS command to the server */
2026     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
2027     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
2028                                             ++conn->cmd_ident, 1, 
2029                                             1, idp->data, idp->len);
2030     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
2031                             NULL, 0, NULL, NULL, buffer->data, 
2032                             buffer->len, TRUE);
2033     silc_buffer_free(buffer);
2034     silc_buffer_free(idp);
2035
2036     /* Register pending callback which will recall this command callback with
2037        same context and reprocesses the command. When reprocessing we actually
2038        display the information on the screen. */
2039     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, 
2040                                 silc_client_command_destructor,
2041                                 silc_client_command_users, 
2042                                 silc_client_command_dup(cmd));
2043     cmd->pending = TRUE;
2044     return;
2045   }
2046
2047   /* Notify application */
2048   COMMAND;
2049
2050  out:
2051   silc_client_command_free(cmd);
2052 }
2053
2054 /* Command GETKEY. Used to fetch remote client's public key. */
2055
2056 SILC_CLIENT_CMD_FUNC(getkey)
2057 {
2058   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2059   SilcClientConnection conn = cmd->conn;
2060   SilcClient client = cmd->client;
2061   SilcClientEntry client_entry = NULL;
2062   uint32 num = 0;
2063   char *nickname = NULL, *server = NULL;
2064   SilcBuffer idp, buffer;
2065
2066   if (!cmd->conn) {
2067     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2068     COMMAND_ERROR;
2069     goto out;
2070   }
2071
2072   if (cmd->argc < 2) {
2073     client->ops->say(client, conn, "Usage: /GETKEY <nickname>");
2074     COMMAND_ERROR;
2075     goto out;
2076   }
2077
2078   /* Parse the typed nickname. */
2079   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
2080     client->ops->say(client, conn, "Bad nickname");
2081     COMMAND_ERROR;
2082     goto out;
2083   }
2084
2085   /* Find client entry */
2086   client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
2087                                         TRUE);
2088   if (!client_entry) {
2089     /* Client entry not found, it was requested thus mark this to be
2090        pending command. */
2091     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2092                                 conn->cmd_ident,  
2093                                 silc_client_command_destructor,
2094                                 silc_client_command_getkey, 
2095                                 silc_client_command_dup(cmd));
2096     cmd->pending = 1;
2097     return;
2098   }
2099
2100   idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2101   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
2102                                           1, idp->data, idp->len);
2103   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2104                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2105   silc_buffer_free(buffer);
2106   silc_buffer_free(idp);
2107
2108   /* Notify application */
2109   COMMAND;
2110
2111  out:
2112   silc_client_command_free(cmd);
2113 }