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