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