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