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