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