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