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, "Bad nickname");
756     COMMAND_ERROR;
757     goto out;
758   }
759
760   /* Get the target client */
761   target = silc_idlist_get_client(cmd->client, conn, nickname, 
762                                   server, num, TRUE);
763   if (!target) {
764     silc_free(nickname);
765     if (server)
766       silc_free(server);
767
768     if (cmd->pending) {
769       COMMAND_ERROR;
770       goto out;
771     }
772
773     /* Client entry not found, it was requested thus mark this to be
774        pending command. */
775     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
776                                 conn->cmd_ident,  
777                                 silc_client_command_destructor,
778                                 silc_client_command_kill, 
779                                 silc_client_command_dup(cmd));
780     cmd->pending = 1;
781     return;
782   }
783
784   /* Send the KILL command to the server */
785   idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
786   if (cmd->argc == 2)
787     buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 1, 
788                                             1, idp->data, idp->len);
789   else
790     buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 2, 
791                                             1, idp->data, idp->len,
792                                             2, cmd->argv[2], 
793                                             strlen(cmd->argv[2]));
794   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
795                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
796   silc_buffer_free(buffer);
797   silc_buffer_free(idp);
798
799   /* Notify application */
800   COMMAND;
801
802  out:
803   if (nickname)
804     silc_free(nickname);
805   if (server)
806     silc_free(server);
807   silc_client_command_free(cmd);
808 }
809
810 /* Command INFO. Request information about specific server. If specific
811    server is not provided the current server is used. */
812
813 SILC_CLIENT_CMD_FUNC(info)
814 {
815   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
816   SilcClientConnection conn = cmd->conn;
817   SilcBuffer buffer;
818   char *name = NULL;
819
820   if (!cmd->conn) {
821     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
822     COMMAND_ERROR;
823     goto out;
824   }
825
826   if (cmd->argc == 2)
827     name = strdup(cmd->argv[1]);
828
829   /* Send the command */
830   if (name)
831     buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
832                                             1, name, strlen(name));
833   else
834     buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0,
835                                          NULL, NULL, NULL, 0);
836   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
837                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
838   silc_buffer_free(buffer);
839   if (name)
840     silc_free(name);
841
842   /* Notify application */
843   COMMAND;
844
845  out:
846   silc_client_command_free(cmd);
847 }
848
849 /* Command PING. Sends ping to server. This is used to test the 
850    communication channel. */
851
852 SILC_CLIENT_CMD_FUNC(ping)
853 {
854   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
855   SilcClientConnection conn = cmd->conn;
856   SilcBuffer buffer;
857   void *id;
858   int i;
859
860   if (!cmd->conn) {
861     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
862     COMMAND_ERROR;
863     goto out;
864   }
865
866   /* Send the command */
867   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
868                                           1, conn->remote_id_data, 
869                                           silc_id_get_len(conn->remote_id,
870                                                           SILC_ID_SERVER));
871   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
872                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
873   silc_buffer_free(buffer);
874
875   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
876                       SILC_ID_SERVER);
877   if (!id) {
878     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
879     COMMAND_ERROR;
880     goto out;
881   }
882
883   /* Start counting time */
884   for (i = 0; i < conn->ping_count; i++) {
885     if (conn->ping[i].dest_id == NULL) {
886       conn->ping[i].start_time = time(NULL);
887       conn->ping[i].dest_id = id;
888       conn->ping[i].dest_name = strdup(conn->remote_host);
889       conn->ping_count++;
890       break;
891     }
892   }
893   if (i >= conn->ping_count) {
894     i = conn->ping_count;
895     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
896     conn->ping[i].start_time = time(NULL);
897     conn->ping[i].dest_id = id;
898     conn->ping[i].dest_name = strdup(conn->remote_host);
899     conn->ping_count++;
900   }
901   
902   /* Notify application */
903   COMMAND;
904
905  out:
906   silc_client_command_free(cmd);
907 }
908
909 SILC_CLIENT_CMD_FUNC(notice)
910 {
911 }
912
913 /* Command JOIN. Joins to a channel. */
914
915 SILC_CLIENT_CMD_FUNC(join)
916 {
917   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
918   SilcClientConnection conn = cmd->conn;
919   SilcIDCacheEntry id_cache = NULL;
920   SilcBuffer buffer, idp;
921
922   if (!cmd->conn) {
923     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
924     COMMAND_ERROR;
925     goto out;
926   }
927
928   if (cmd->argc < 2) {
929     /* Show channels currently joined to */
930
931     goto out;
932   }
933
934   /* See if we have joined to the requested channel already */
935   if (silc_idcache_find_by_name_one(conn->channel_cache, cmd->argv[1],
936                                     &id_cache)) {
937 #if 0
938     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
939                           "You are talking to channel %s", cmd->argv[1]);
940     conn->current_channel = (SilcChannelEntry)id_cache->context;
941     cmd->client->screen->bottom_line->channel = cmd->argv[1];
942     silc_screen_print_bottom_line(cmd->client->screen, 0);
943 #endif
944     goto out;
945   }
946
947   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
948
949   /* Send JOIN command to the server */
950   if (cmd->argc == 2)
951     buffer = 
952       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
953                                      1, cmd->argv[1], cmd->argv_lens[1],
954                                      2, idp->data, idp->len);
955   else if (cmd->argc == 3)
956     /* XXX Buggy */
957     buffer = 
958       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
959                                      1, cmd->argv[1], cmd->argv_lens[1],
960                                      2, idp->data, idp->len,
961                                      3, cmd->argv[2], cmd->argv_lens[2]);
962   else
963     buffer = 
964       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
965                                      1, cmd->argv[1], cmd->argv_lens[1],
966                                      2, idp->data, idp->len,
967                                      3, cmd->argv[2], cmd->argv_lens[2],
968                                      4, cmd->argv[3], cmd->argv_lens[3]);
969
970   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
971                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
972   silc_buffer_free(buffer);
973   silc_buffer_free(idp);
974
975   /* Notify application */
976   COMMAND;
977
978  out:
979   silc_client_command_free(cmd);
980 }
981
982 /* MOTD command. Requests motd from server. */
983
984 SILC_CLIENT_CMD_FUNC(motd)
985 {
986   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
987   SilcClientConnection conn = cmd->conn;
988   SilcBuffer buffer;
989
990   if (!cmd->conn) {
991     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
992     COMMAND_ERROR;
993     goto out;
994   }
995
996   if (cmd->argc < 1 || cmd->argc > 2) {
997     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
998                           "Usage: /MOTD [<server>]");
999     COMMAND_ERROR;
1000     goto out;
1001   }
1002
1003   /* Send TOPIC command to the server */
1004   if (cmd->argc == 1)
1005     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1006                                             1, conn->remote_host, 
1007                                             strlen(conn->remote_host));
1008   else
1009     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1010                                             1, cmd->argv[1], 
1011                                             cmd->argv_lens[1]);
1012   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1013                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1014   silc_buffer_free(buffer);
1015
1016   /* Notify application */
1017   COMMAND;
1018
1019  out:
1020   silc_client_command_free(cmd);
1021 }
1022
1023 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1024    modes as client cannot set itself server/router operator privileges. */
1025
1026 SILC_CLIENT_CMD_FUNC(umode)
1027 {
1028   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1029   SilcClientConnection conn = cmd->conn;
1030   SilcBuffer buffer, idp;
1031   unsigned char *cp, modebuf[4];
1032   uint32 mode, add, len;
1033   int i;
1034
1035   if (!cmd->conn) {
1036     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1037     COMMAND_ERROR;
1038     goto out;
1039   }
1040
1041   if (cmd->argc < 2) {
1042     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1043                   "Usage: /UMODE +|-<modes>");
1044     COMMAND_ERROR;
1045     goto out;
1046   }
1047
1048   mode = conn->local_entry->mode;
1049
1050   /* Are we adding or removing mode */
1051   if (cmd->argv[1][0] == '-')
1052     add = FALSE;
1053   else
1054     add = TRUE;
1055
1056   /* Parse mode */
1057   cp = cmd->argv[1] + 1;
1058   len = strlen(cp);
1059   for (i = 0; i < len; i++) {
1060     switch(cp[i]) {
1061     case 'a':
1062       if (add) {
1063         mode = 0;
1064         mode |= SILC_UMODE_SERVER_OPERATOR;
1065         mode |= SILC_UMODE_ROUTER_OPERATOR;
1066       } else {
1067         mode = SILC_UMODE_NONE;
1068       }
1069       break;
1070     case 's':
1071       if (add)
1072         mode |= SILC_UMODE_SERVER_OPERATOR;
1073       else
1074         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1075       break;
1076     case 'r':
1077       if (add)
1078         mode |= SILC_UMODE_ROUTER_OPERATOR;
1079       else
1080         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1081       break;
1082     case 'g':
1083       if (add)
1084         mode |= SILC_UMODE_GONE;
1085       else
1086         mode &= ~SILC_UMODE_GONE;
1087       break;
1088     default:
1089       COMMAND_ERROR;
1090       goto out;
1091       break;
1092     }
1093   }
1094
1095   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1096   SILC_PUT32_MSB(mode, modebuf);
1097
1098   /* Send the command packet. We support sending only one mode at once
1099      that requires an argument. */
1100   buffer = 
1101     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1102                                    1, idp->data, idp->len, 
1103                                    2, modebuf, sizeof(modebuf));
1104   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1105                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1106   silc_buffer_free(buffer);
1107   silc_buffer_free(idp);
1108
1109   /* Notify application */
1110   COMMAND;
1111
1112  out:
1113   silc_client_command_free(cmd);
1114 }
1115
1116 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1117    can be set several at once. Those modes that require argument must be set
1118    separately (unless set with modes that does not require arguments). */
1119
1120 SILC_CLIENT_CMD_FUNC(cmode)
1121 {
1122   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1123   SilcClientConnection conn = cmd->conn;
1124   SilcChannelEntry channel;
1125   SilcBuffer buffer, chidp, auth = NULL;
1126   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1127   uint32 mode, add, type, len, arg_len = 0;
1128   int i;
1129
1130   if (!cmd->conn) {
1131     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1132     COMMAND_ERROR;
1133     goto out;
1134   }
1135
1136   if (cmd->argc < 3) {
1137     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1138                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1139     COMMAND_ERROR;
1140     goto out;
1141   }
1142
1143   if (cmd->argv[1][0] == '*') {
1144     if (!conn->current_channel) {
1145       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on any channel");
1146       COMMAND_ERROR;
1147       goto out;
1148     }
1149
1150     channel = conn->current_channel;
1151   } else {
1152     name = cmd->argv[1];
1153
1154     channel = silc_client_get_channel(cmd->client, conn, name);
1155     if (!channel) {
1156       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are on that channel");
1157       COMMAND_ERROR;
1158       goto out;
1159     }
1160   }
1161
1162   mode = channel->mode;
1163
1164   /* Are we adding or removing mode */
1165   if (cmd->argv[2][0] == '-')
1166     add = FALSE;
1167   else
1168     add = TRUE;
1169
1170   /* Argument type to be sent to server */
1171   type = 0;
1172
1173   /* Parse mode */
1174   cp = cmd->argv[2] + 1;
1175   len = strlen(cp);
1176   for (i = 0; i < len; i++) {
1177     switch(cp[i]) {
1178     case 'p':
1179       if (add)
1180         mode |= SILC_CHANNEL_MODE_PRIVATE;
1181       else
1182         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1183       break;
1184     case 's':
1185       if (add)
1186         mode |= SILC_CHANNEL_MODE_SECRET;
1187       else
1188         mode &= ~SILC_CHANNEL_MODE_SECRET;
1189       break;
1190     case 'k':
1191       if (add)
1192         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1193       else
1194         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1195       break;
1196     case 'i':
1197       if (add)
1198         mode |= SILC_CHANNEL_MODE_INVITE;
1199       else
1200         mode &= ~SILC_CHANNEL_MODE_INVITE;
1201       break;
1202     case 't':
1203       if (add)
1204         mode |= SILC_CHANNEL_MODE_TOPIC;
1205       else
1206         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1207       break;
1208     case 'l':
1209       if (add) {
1210         int ll;
1211         mode |= SILC_CHANNEL_MODE_ULIMIT;
1212         type = 3;
1213         if (cmd->argc < 4) {
1214           cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1215                "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1216           COMMAND_ERROR;
1217           goto out;
1218         }
1219         ll = atoi(cmd->argv[3]);
1220         SILC_PUT32_MSB(ll, tmp);
1221         arg = tmp;
1222         arg_len = 4;
1223       } else {
1224         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1225       }
1226       break;
1227     case 'a':
1228       if (add) {
1229         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1230         type = 4;
1231         if (cmd->argc < 4) {
1232           cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1233                "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1234           COMMAND_ERROR;
1235           goto out;
1236         }
1237         arg = cmd->argv[3];
1238         arg_len = cmd->argv_lens[3];
1239       } else {
1240         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1241       }
1242       break;
1243     case 'c':
1244       if (add) {
1245         mode |= SILC_CHANNEL_MODE_CIPHER;
1246         type = 5;
1247         if (cmd->argc < 4) {
1248           cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1249                "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1250           COMMAND_ERROR;
1251           goto out;
1252         }
1253         arg = cmd->argv[3];
1254         arg_len = cmd->argv_lens[3];
1255       } else {
1256         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1257       }
1258       break;
1259     case 'h':
1260       if (add) {
1261         mode |= SILC_CHANNEL_MODE_HMAC;
1262         type = 6;
1263         if (cmd->argc < 4) {
1264           cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1265                "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1266           COMMAND_ERROR;
1267           goto out;
1268         }
1269         arg = cmd->argv[3];
1270         arg_len = cmd->argv_lens[3];
1271       } else {
1272         mode &= ~SILC_CHANNEL_MODE_HMAC;
1273       }
1274       break;
1275     case 'f':
1276       if (add) {
1277         mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1278         type = 7;
1279
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
1287         if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1288           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1289                                                     cmd->client->private_key,
1290                                                     conn->hash,
1291                                                     conn->local_id,
1292                                                     SILC_ID_CLIENT);
1293         } else {
1294           auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1295                                           cmd->argv[3], cmd->argv_lens[3]);
1296         }
1297
1298         arg = auth->data;
1299         arg_len = auth->len;
1300       } else {
1301         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1302       }
1303       break;
1304     default:
1305       COMMAND_ERROR;
1306       goto out;
1307       break;
1308     }
1309   }
1310
1311   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1312   SILC_PUT32_MSB(mode, modebuf);
1313
1314   /* Send the command packet. We support sending only one mode at once
1315      that requires an argument. */
1316   if (type && arg) {
1317     buffer = 
1318       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1319                                      1, chidp->data, chidp->len, 
1320                                      2, modebuf, sizeof(modebuf),
1321                                      type, arg, arg_len);
1322   } else {
1323     buffer = 
1324       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1325                                      1, chidp->data, chidp->len, 
1326                                      2, modebuf, sizeof(modebuf));
1327   }
1328
1329   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1330                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1331   silc_buffer_free(buffer);
1332   silc_buffer_free(chidp);
1333   if (auth)
1334     silc_buffer_free(auth);
1335
1336   /* Notify application */
1337   COMMAND;
1338
1339  out:
1340   silc_client_command_free(cmd);
1341 }
1342
1343 /* CUMODE command. Changes client's mode on a channel. */
1344
1345 SILC_CLIENT_CMD_FUNC(cumode)
1346 {
1347   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1348   SilcClientConnection conn = cmd->conn;
1349   SilcChannelEntry channel;
1350   SilcChannelUser chu;
1351   SilcClientEntry client_entry;
1352   SilcBuffer buffer, clidp, chidp, auth = NULL;
1353   unsigned char *name, *cp, modebuf[4];
1354   uint32 mode = 0, add, len;
1355   char *nickname = NULL, *server = NULL;
1356   uint32 num = 0;
1357   int i;
1358
1359   if (!cmd->conn) {
1360     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1361     COMMAND_ERROR;
1362     goto out;
1363   }
1364
1365   if (cmd->argc < 4) {
1366     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1367                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1368     COMMAND_ERROR;
1369     goto out;
1370   }
1371
1372   if (cmd->argv[1][0] == '*') {
1373     if (!conn->current_channel) {
1374       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on any channel");
1375       COMMAND_ERROR;
1376       goto out;
1377     }
1378
1379     channel = conn->current_channel;
1380   } else {
1381     name = cmd->argv[1];
1382
1383     channel = silc_client_get_channel(cmd->client, conn, name);
1384     if (!channel) {
1385       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are on that channel");
1386       COMMAND_ERROR;
1387       goto out;
1388     }
1389   }
1390
1391   /* Parse the typed nickname. */
1392   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1393     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Bad nickname");
1394     COMMAND_ERROR;
1395     goto out;
1396   }
1397
1398   /* Find client entry */
1399   client_entry = silc_idlist_get_client(cmd->client, conn, 
1400                                         nickname, server, num, TRUE);
1401   if (!client_entry) {
1402     if (cmd->pending) {
1403       COMMAND_ERROR;
1404       goto out;
1405     }
1406
1407     /* Client entry not found, it was requested thus mark this to be
1408        pending command. */
1409     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1410                                 conn->cmd_ident,  
1411                                 silc_client_command_destructor,
1412                                 silc_client_command_cumode, 
1413                                 silc_client_command_dup(cmd));
1414     cmd->pending = 1;
1415     return;
1416   }
1417   
1418   /* Get the current mode */
1419   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1420     if (chu->client == client_entry) {
1421       mode = chu->mode;
1422       break;
1423     }
1424   }
1425
1426   /* Are we adding or removing mode */
1427   if (cmd->argv[2][0] == '-')
1428     add = FALSE;
1429   else
1430     add = TRUE;
1431
1432   /* Parse mode */
1433   cp = cmd->argv[2] + 1;
1434   len = strlen(cp);
1435   for (i = 0; i < len; i++) {
1436     switch(cp[i]) {
1437     case 'a':
1438       if (add) {
1439         mode |= SILC_CHANNEL_UMODE_CHANFO;
1440         mode |= SILC_CHANNEL_UMODE_CHANOP;
1441       } else {
1442         mode = SILC_CHANNEL_UMODE_NONE;
1443       }
1444       break;
1445     case 'f':
1446       if (add) {
1447         if (cmd->argc == 5) {
1448           if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1449           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1450                                                     cmd->client->private_key,
1451                                                     conn->hash,
1452                                                     conn->local_id,
1453                                                     SILC_ID_CLIENT);
1454           } else {
1455             auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1456                                             cmd->argv[4], cmd->argv_lens[4]);
1457           }
1458         }
1459         mode |= SILC_CHANNEL_UMODE_CHANFO;
1460       } else {
1461         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1462       }
1463       break;
1464     case 'o':
1465       if (add)
1466         mode |= SILC_CHANNEL_UMODE_CHANOP;
1467       else
1468         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1469       break;
1470     default:
1471       COMMAND_ERROR;
1472       goto out;
1473       break;
1474     }
1475   }
1476
1477   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1478   SILC_PUT32_MSB(mode, modebuf);
1479   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1480
1481   /* Send the command packet. We support sending only one mode at once
1482      that requires an argument. */
1483   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 4, 
1484                                           1, chidp->data, chidp->len, 
1485                                           2, modebuf, 4,
1486                                           3, clidp->data, clidp->len,
1487                                           4, auth ? auth->data : NULL, 
1488                                           auth ? auth->len : 0);
1489   
1490   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1491                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1492   silc_buffer_free(buffer);
1493   silc_buffer_free(chidp);
1494   silc_buffer_free(clidp);
1495   if (auth)
1496     silc_buffer_free(auth);
1497   
1498   /* Notify application */
1499   COMMAND;
1500
1501  out:
1502   if (nickname)
1503     silc_free(nickname);
1504   if (server)
1505     silc_free(server);
1506   silc_client_command_free(cmd);
1507 }
1508
1509 /* KICK command. Kicks a client out of channel. */
1510
1511 SILC_CLIENT_CMD_FUNC(kick)
1512 {
1513   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1514   SilcClientConnection conn = cmd->conn;
1515   SilcIDCacheEntry id_cache = NULL;
1516   SilcChannelEntry channel;
1517   SilcBuffer buffer, idp, idp2;
1518   SilcClientEntry target;
1519   char *name;
1520   uint32 num = 0;
1521   char *nickname = NULL, *server = NULL;
1522
1523   if (!cmd->conn) {
1524     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1525     COMMAND_ERROR;
1526     goto out;
1527   }
1528
1529   if (cmd->argc < 3) {
1530     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1531                           "Usage: /KICK <channel> <nickname> [<comment>]");
1532     COMMAND_ERROR;
1533     goto out;
1534   }
1535
1536   if (cmd->argv[1][0] == '*') {
1537     if (!conn->current_channel) {
1538       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on any channel");
1539       COMMAND_ERROR;
1540       goto out;
1541     }
1542     name = conn->current_channel->channel_name;
1543   } else {
1544     name = cmd->argv[1];
1545   }
1546
1547   if (!conn->current_channel) {
1548     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on that channel");
1549     COMMAND_ERROR;
1550     goto out;
1551   }
1552
1553   /* Get the Channel ID of the channel */
1554   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1555     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on that channel");
1556     COMMAND_ERROR;
1557     goto out;
1558   }
1559
1560   channel = (SilcChannelEntry)id_cache->context;
1561
1562   /* Parse the typed nickname. */
1563   if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1564     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Bad nickname");
1565     COMMAND_ERROR;
1566     goto out;
1567   }
1568
1569   /* Get the target client */
1570   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1571                                   server, num, FALSE);
1572   if (!target) {
1573     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "No such client: %s",
1574                           cmd->argv[2]);
1575     COMMAND_ERROR;
1576     goto out;
1577   }
1578
1579   /* Send KICK command to the server */
1580   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1581   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1582   if (cmd->argc == 3)
1583     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1584                                             1, idp->data, idp->len,
1585                                             2, idp2->data, idp2->len);
1586   else
1587     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1588                                             1, idp->data, idp->len,
1589                                             2, idp2->data, idp2->len,
1590                                             3, cmd->argv[3], 
1591                                             strlen(cmd->argv[3]));
1592   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1593                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1594   silc_buffer_free(buffer);
1595   silc_buffer_free(idp);
1596   silc_buffer_free(idp2);
1597
1598   /* Notify application */
1599   COMMAND;
1600
1601  out:
1602   if (nickname)
1603     silc_free(nickname);
1604   if (server)
1605     silc_free(server);
1606   silc_client_command_free(cmd);
1607 }
1608
1609 static void silc_client_command_oper_send(unsigned char *data,
1610                                           uint32 data_len, void *context)
1611 {
1612   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1613   SilcClientConnection conn = cmd->conn;
1614   SilcBuffer buffer, auth;
1615
1616   if (cmd->argc == 3) {
1617     /* Pulic key auth XXX TODO */
1618     auth = NULL;
1619   } else {
1620     /* Encode the authentication payload */
1621     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1622                                     data, data_len);
1623   }
1624
1625   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1626                                           1, cmd->argv[1], 
1627                                           strlen(cmd->argv[1]),
1628                                           2, auth->data, auth->len);
1629   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1630                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1631
1632   silc_buffer_free(buffer);
1633   silc_buffer_free(auth);
1634
1635   /* Notify application */
1636   COMMAND;
1637 }
1638
1639 /* OPER command. Used to obtain server operator privileges. */
1640
1641 SILC_CLIENT_CMD_FUNC(oper)
1642 {
1643   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1644   SilcClientConnection conn = cmd->conn;
1645   unsigned char *auth_data;
1646   uint32 auth_data_len = 0;
1647
1648   if (!cmd->conn) {
1649     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1650     COMMAND_ERROR;
1651     goto out;
1652   }
1653
1654   if (cmd->argc < 2) {
1655     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1656                           "Usage: /OPER <username> [<public key>]");
1657     COMMAND_ERROR;
1658     goto out;
1659   }
1660
1661   if (cmd->argc == 3) {
1662     /* XXX Get public key */
1663     auth_data = NULL;
1664     COMMAND_ERROR;
1665     goto out;
1666   } else {
1667     /* Get passphrase */
1668     cmd->client->ops->ask_passphrase(cmd->client, conn,
1669                                      silc_client_command_oper_send,
1670                                      context);
1671     return;
1672   }
1673
1674   silc_client_command_oper_send(auth_data, auth_data_len, context);
1675
1676   memset(auth_data, 0, auth_data_len);
1677   silc_free(auth_data);
1678
1679   /* Notify application */
1680   COMMAND;
1681
1682  out:
1683   silc_client_command_free(cmd);
1684 }
1685
1686 static void silc_client_command_silcoper_send(unsigned char *data,
1687                                               uint32 data_len, void *context)
1688 {
1689   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1690   SilcClientConnection conn = cmd->conn;
1691   SilcBuffer buffer, auth;
1692
1693   if (cmd->argc == 3) {
1694     /* Pulic key auth XXX TODO */
1695     auth = NULL;
1696   } else {
1697     /* Encode the authentication payload */
1698     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1699                                     data, data_len);
1700   }
1701
1702   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1703                                           1, cmd->argv[1], 
1704                                           strlen(cmd->argv[1]),
1705                                           2, auth->data, auth->len);
1706   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1707                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1708
1709   silc_buffer_free(buffer);
1710   silc_buffer_free(auth);
1711
1712   /* Notify application */
1713   COMMAND;
1714 }
1715
1716 /* SILCOPER command. Used to obtain router operator privileges. */
1717
1718 SILC_CLIENT_CMD_FUNC(silcoper)
1719 {
1720   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1721   SilcClientConnection conn = cmd->conn;
1722   unsigned char *auth_data;
1723   uint32 auth_data_len = 0;
1724
1725   if (!cmd->conn) {
1726     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1727     COMMAND_ERROR;
1728     goto out;
1729   }
1730
1731   if (cmd->argc < 2) {
1732     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1733                           "Usage: /SILCOPER <username> [<public key>]");
1734     COMMAND_ERROR;
1735     goto out;
1736   }
1737
1738   if (cmd->argc == 3) {
1739     /* XXX Get public key */
1740     auth_data = NULL;
1741     COMMAND_ERROR;
1742     goto out;
1743   } else {
1744     /* Get passphrase */
1745     cmd->client->ops->ask_passphrase(cmd->client, conn,
1746                                      silc_client_command_silcoper_send,
1747                                      context);
1748     return;
1749   }
1750
1751   silc_client_command_silcoper_send(auth_data, auth_data_len, context);
1752
1753   memset(auth_data, 0, auth_data_len);
1754   silc_free(auth_data);
1755
1756   /* Notify application */
1757   COMMAND;
1758
1759  out:
1760   silc_client_command_free(cmd);
1761 }
1762
1763 /* CONNECT command. Connects the server to another server. */
1764
1765 SILC_CLIENT_CMD_FUNC(connect)
1766 {
1767   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1768   SilcClientConnection conn = cmd->conn;
1769   SilcBuffer buffer;
1770   unsigned char port[4];
1771   uint32 tmp;
1772
1773   if (!cmd->conn) {
1774     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1775     COMMAND_ERROR;
1776     goto out;
1777   }
1778
1779   if (cmd->argc < 2) {
1780     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1781                           "Usage: /CONNECT <server> [<port>]");
1782     COMMAND_ERROR;
1783     goto out;
1784   }
1785
1786   if (cmd->argc == 3) {
1787     tmp = atoi(cmd->argv[2]);
1788     SILC_PUT32_MSB(tmp, port);
1789   }
1790
1791   if (cmd->argc == 3)
1792     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1793                                             1, cmd->argv[1], 
1794                                             strlen(cmd->argv[1]),
1795                                             2, port, 4);
1796   else
1797     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1798                                             1, cmd->argv[1], 
1799                                             strlen(cmd->argv[1]));
1800   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1801                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1802   silc_buffer_free(buffer);
1803
1804   /* Notify application */
1805   COMMAND;
1806
1807  out:
1808   silc_client_command_free(cmd);
1809 }
1810
1811 /* Command BAN. This is used to manage the ban list of the channel. */
1812
1813 SILC_CLIENT_CMD_FUNC(ban)
1814 {
1815   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1816   SilcClientConnection conn = cmd->conn;
1817   SilcChannelEntry channel;
1818   SilcBuffer buffer, chidp;
1819   int type = 0;
1820   char *name, *ban = NULL;
1821
1822   if (!cmd->conn) {
1823     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1824     COMMAND_ERROR;
1825     goto out;
1826   }
1827
1828   if (cmd->argc < 2) {
1829     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1830                    "Usage: /BAN <channel> "
1831                    "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1832     COMMAND_ERROR;
1833     goto out;
1834   }
1835
1836   if (cmd->argv[1][0] == '*') {
1837     if (!conn->current_channel) {
1838       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on any channel");
1839       COMMAND_ERROR;
1840       goto out;
1841     }
1842
1843     channel = conn->current_channel;
1844   } else {
1845     name = cmd->argv[1];
1846
1847     channel = silc_client_get_channel(cmd->client, conn, name);
1848     if (!channel) {
1849       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are on that channel");
1850       COMMAND_ERROR;
1851       goto out;
1852     }
1853   }
1854
1855   if (cmd->argc == 3) {
1856     if (cmd->argv[2][0] == '+')
1857       type = 2;
1858     else
1859       type = 3;
1860
1861     ban = cmd->argv[2];
1862     ban++;
1863   }
1864
1865   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1866
1867   /* Send the command */
1868   if (ban)
1869     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, 
1870                                             1, chidp->data, chidp->len,
1871                                             type, ban, strlen(ban));
1872   else
1873     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, 
1874                                             1, chidp->data, chidp->len);
1875
1876   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1877                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1878   silc_buffer_free(buffer);
1879   silc_buffer_free(chidp);
1880
1881   /* Notify application */
1882   COMMAND;
1883
1884  out:
1885   silc_client_command_free(cmd);
1886 }
1887
1888 /* CLOSE command. Close server connection to the remote server */
1889  
1890 SILC_CLIENT_CMD_FUNC(close)
1891 {
1892   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1893   SilcClientConnection conn = cmd->conn;
1894   SilcBuffer buffer;
1895   unsigned char port[4];
1896   uint32 tmp;
1897
1898   if (!cmd->conn) {
1899     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1900     COMMAND_ERROR;
1901     goto out;
1902   }
1903
1904   if (cmd->argc < 2) {
1905     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1906                           "Usage: /CLOSE <server> [<port>]");
1907     COMMAND_ERROR;
1908     goto out;
1909   }
1910
1911   if (cmd->argc == 3) {
1912     tmp = atoi(cmd->argv[2]);
1913     SILC_PUT32_MSB(tmp, port);
1914   }
1915
1916   if (cmd->argc == 3)
1917     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1918                                             1, cmd->argv[1], 
1919                                             strlen(cmd->argv[1]),
1920                                             2, port, 4);
1921   else
1922     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1923                                             1, cmd->argv[1], 
1924                                             strlen(cmd->argv[1]));
1925   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1926                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1927   silc_buffer_free(buffer);
1928
1929   /* Notify application */
1930   COMMAND;
1931
1932  out:
1933   silc_client_command_free(cmd);
1934 }
1935  
1936 /* SHUTDOWN command. Shutdowns the server. */
1937
1938 SILC_CLIENT_CMD_FUNC(shutdown)
1939 {
1940   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1941
1942   if (!cmd->conn) {
1943     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1944     COMMAND_ERROR;
1945     goto out;
1946   }
1947
1948   /* Send the command */
1949   silc_client_send_command(cmd->client, cmd->conn, 
1950                            SILC_COMMAND_SHUTDOWN, 0, 0);
1951
1952   /* Notify application */
1953   COMMAND;
1954
1955  out:
1956   silc_client_command_free(cmd);
1957 }
1958
1959 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1960
1961 SILC_CLIENT_CMD_FUNC(leave)
1962 {
1963   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1964   SilcClientConnection conn = cmd->conn;
1965   SilcIDCacheEntry id_cache = NULL;
1966   SilcChannelEntry channel;
1967   SilcBuffer buffer, idp;
1968   char *name;
1969
1970   if (!cmd->conn) {
1971     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1972     COMMAND_ERROR;
1973     goto out;
1974   }
1975
1976   if (cmd->argc != 2) {
1977     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /LEAVE <channel>");
1978     COMMAND_ERROR;
1979     goto out;
1980   }
1981
1982   if (cmd->argv[1][0] == '*') {
1983     if (!conn->current_channel) {
1984       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on any channel");
1985       COMMAND_ERROR;
1986       goto out;
1987     }
1988     name = conn->current_channel->channel_name;
1989   } else {
1990     name = cmd->argv[1];
1991   }
1992
1993   /* Get the Channel ID of the channel */
1994   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1995     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on that channel");
1996     COMMAND_ERROR;
1997     goto out;
1998   }
1999
2000   channel = (SilcChannelEntry)id_cache->context;
2001
2002   /* Send LEAVE command to the server */
2003   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
2004   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
2005                                           1, idp->data, idp->len);
2006   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2007                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2008   silc_buffer_free(buffer);
2009   silc_buffer_free(idp);
2010
2011   /* Notify application */
2012   COMMAND;
2013
2014   if (conn->current_channel == channel)
2015     conn->current_channel = NULL;
2016
2017   silc_idcache_del_by_id(conn->channel_cache, channel->id);
2018   silc_free(channel->channel_name);
2019   silc_free(channel->id);
2020   silc_free(channel->key);
2021   silc_cipher_free(channel->channel_key);
2022   silc_free(channel);
2023
2024  out:
2025   silc_client_command_free(cmd);
2026 }
2027
2028 /* Command USERS. Requests the USERS of the clients joined on requested
2029    channel. */
2030
2031 SILC_CLIENT_CMD_FUNC(users)
2032 {
2033   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2034   SilcClientConnection conn = cmd->conn;
2035   SilcIDCacheEntry id_cache = NULL;
2036   SilcChannelEntry channel;
2037   SilcBuffer buffer, idp;
2038   char *name;
2039
2040   if (!cmd->conn) {
2041     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2042     COMMAND_ERROR;
2043     goto out;
2044   }
2045
2046   if (cmd->argc != 2) {
2047     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /USERS <channel>");
2048     COMMAND_ERROR;
2049     goto out;
2050   }
2051
2052   if (cmd->argv[1][0] == '*') {
2053     if (!conn->current_channel) {
2054       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on any channel");
2055       COMMAND_ERROR;
2056       goto out;
2057     }
2058     name = conn->current_channel->channel_name;
2059   } else {
2060     name = cmd->argv[1];
2061   }
2062
2063   if (!conn->current_channel) {
2064     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "You are not on that channel");
2065     COMMAND_ERROR;
2066     goto out;
2067   }
2068
2069   /* Get the Channel ID of the channel */
2070   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
2071     /* XXX should resolve the channel ID; LIST command */
2072     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2073                           "You are not on that channel", name);
2074     COMMAND_ERROR;
2075     goto out;
2076   }
2077
2078   channel = (SilcChannelEntry)id_cache->context;
2079
2080   if (!cmd->pending) {
2081     /* Send USERS command to the server */
2082     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
2083     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
2084                                             ++conn->cmd_ident, 1, 
2085                                             1, idp->data, idp->len);
2086     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
2087                             NULL, 0, NULL, NULL, buffer->data, 
2088                             buffer->len, TRUE);
2089     silc_buffer_free(buffer);
2090     silc_buffer_free(idp);
2091
2092     /* Register pending callback which will recall this command callback with
2093        same context and reprocesses the command. When reprocessing we actually
2094        display the information on the screen. */
2095     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, 
2096                                 silc_client_command_destructor,
2097                                 silc_client_command_users, 
2098                                 silc_client_command_dup(cmd));
2099     cmd->pending = TRUE;
2100     return;
2101   }
2102
2103   /* Notify application */
2104   COMMAND;
2105
2106  out:
2107   silc_client_command_free(cmd);
2108 }
2109
2110 /* Command GETKEY. Used to fetch remote client's public key. */
2111
2112 SILC_CLIENT_CMD_FUNC(getkey)
2113 {
2114   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2115   SilcClientConnection conn = cmd->conn;
2116   SilcClient client = cmd->client;
2117   SilcClientEntry client_entry = NULL;
2118   uint32 num = 0;
2119   char *nickname = NULL, *server = NULL;
2120   SilcBuffer idp, buffer;
2121
2122   if (!cmd->conn) {
2123     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2124     COMMAND_ERROR;
2125     goto out;
2126   }
2127
2128   if (cmd->argc < 2) {
2129     client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /GETKEY <nickname>");
2130     COMMAND_ERROR;
2131     goto out;
2132   }
2133
2134   /* Parse the typed nickname. */
2135   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
2136     client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, "Bad nickname");
2137     COMMAND_ERROR;
2138     goto out;
2139   }
2140
2141   /* Find client entry */
2142   client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
2143                                         TRUE);
2144   if (!client_entry) {
2145     /* Client entry not found, it was requested thus mark this to be
2146        pending command. */
2147     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2148                                 conn->cmd_ident,  
2149                                 silc_client_command_destructor,
2150                                 silc_client_command_getkey, 
2151                                 silc_client_command_dup(cmd));
2152     cmd->pending = 1;
2153     return;
2154   }
2155
2156   idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2157   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
2158                                           1, idp->data, idp->len);
2159   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2160                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2161   silc_buffer_free(buffer);
2162   silc_buffer_free(idp);
2163
2164   /* Notify application */
2165   COMMAND;
2166
2167  out:
2168   silc_client_command_free(cmd);
2169 }