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