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