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