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