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