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