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