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