updates.
[silc.git] / lib / silcclient / command.c
1 /*
2
3   command.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "clientlibincludes.h"
23 #include "client_internal.h"
24
25 /* Client command list. */
26 SilcClientCommand silc_command_list[] =
27 {
28   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
29   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
30   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
31                   SILC_CF_LAG | SILC_CF_REG, 3),
32   SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
33   SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
34   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
35   SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
36   SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
37   SILC_CLIENT_CMD(kill, KILL, "KILL", 
38                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
39   SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
40   SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
41                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
42   SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
43   SILC_CLIENT_CMD(oper, OPER, "OPER",
44                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
45   SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
46   SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
47   SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
48   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
49   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
50   SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
51   SILC_CLIENT_CMD(ban, BAN, "BAN", SILC_CF_LAG | SILC_CF_REG, 3),
52   SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
53                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
54   SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
55                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
56   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER",
57                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 3),
58   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
59   SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
60   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", SILC_CF_LAG | SILC_CF_REG, 2),
61
62   { NULL, 0, NULL, 0, 0 },
63 };
64
65 #define SILC_NOT_CONNECTED(x, c) \
66   x->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \
67            "You are not connected to a server, use /SERVER to connect");
68
69 /* Command operation that is called at the end of all commands. 
70    Usage: COMMAND; */
71 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
72   cmd, TRUE, cmd->command->cmd)
73
74 /* Error to application. Usage: COMMAND_ERROR; */
75 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
76   cmd, FALSE, cmd->command->cmd)
77
78 /* Generic function to send any command. The arguments must be sent already
79    encoded into correct form and in correct order. */
80
81 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
82                               SilcCommand command, uint16 ident,
83                               uint32 argc, ...)
84 {
85   SilcBuffer packet;
86   va_list ap;
87
88   va_start(ap, argc);
89
90   packet = silc_command_payload_encode_vap(command, ident, argc, ap);
91   silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
92                           NULL, 0, NULL, NULL, packet->data, 
93                           packet->len, TRUE);
94   silc_buffer_free(packet);
95 }
96
97 /* Finds and returns a pointer to the command list. Return NULL if the
98    command is not found. */
99
100 SilcClientCommand *silc_client_command_find(const char *name)
101 {
102   SilcClientCommand *cmd;
103
104   for (cmd = silc_command_list; cmd->name; cmd++) {
105     if (!strcmp(cmd->name, name))
106       return cmd;
107   }
108
109   return NULL;
110 }
111
112 /* Add new pending command to be executed when reply to a command has been
113    received.  The `reply_cmd' is the command that will call the `callback'
114    with `context' when reply has been received.  If `ident is non-zero
115    the `callback' will be executed when received reply with command 
116    identifier `ident'. */
117
118 void silc_client_command_pending(SilcClientConnection conn,
119                                  SilcCommand reply_cmd,
120                                  uint16 ident,
121                                  SilcClientPendingDestructor destructor,
122                                  SilcCommandCb callback,
123                                  void *context)
124 {
125   SilcClientCommandPending *reply;
126
127   reply = silc_calloc(1, sizeof(*reply));
128   reply->reply_cmd = reply_cmd;
129   reply->ident = ident;
130   reply->context = context;
131   reply->callback = callback;
132   reply->destructor = destructor;
133   silc_dlist_add(conn->pending_commands, reply);
134 }
135
136 /* Deletes pending command by reply command type. */
137
138 void silc_client_command_pending_del(SilcClientConnection conn,
139                                      SilcCommand reply_cmd,
140                                      uint16 ident)
141 {
142   SilcClientCommandPending *r;
143
144   silc_dlist_start(conn->pending_commands);
145   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
146     if (r->reply_cmd == reply_cmd && r->ident == ident) {
147       silc_dlist_del(conn->pending_commands, r);
148       break;
149     }
150   }
151 }
152
153 /* Checks for pending commands and marks callbacks to be called from
154    the command reply function. Returns TRUE if there were pending command. */
155
156 int silc_client_command_pending_check(SilcClientConnection conn,
157                                       SilcClientCommandReplyContext ctx,
158                                       SilcCommand command, 
159                                       uint16 ident)
160 {
161   SilcClientCommandPending *r;
162
163   silc_dlist_start(conn->pending_commands);
164   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
165     if (r->reply_cmd == command && r->ident == ident) {
166       ctx->context = r->context;
167       ctx->callback = r->callback;
168       ctx->destructor = r->destructor;
169       ctx->ident = ident;
170       return TRUE;
171     }
172   }
173
174   return FALSE;
175 }
176
177 /* Allocate Command Context */
178
179 SilcClientCommandContext silc_client_command_alloc()
180 {
181   SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
182   ctx->users++;
183   return ctx;
184 }
185
186 /* Free command context and its internals */
187
188 void silc_client_command_free(SilcClientCommandContext ctx)
189 {
190   ctx->users--;
191   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
192                   ctx->users));
193   if (ctx->users < 1) {
194     int i;
195
196     for (i = 0; i < ctx->argc; i++)
197       silc_free(ctx->argv[i]);
198     silc_free(ctx->argv_lens);
199     silc_free(ctx->argv_types);
200     silc_free(ctx);
201   }
202 }
203
204 /* Duplicate Command Context by adding reference counter. The context won't
205    be free'd untill it hits zero. */
206
207 SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
208 {
209   ctx->users++;
210   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
211                   ctx->users));
212   return ctx;
213 }
214
215 /* Pending command destructor. */
216
217 static void silc_client_command_destructor(void *context)
218 {
219   silc_client_command_free((SilcClientCommandContext)context);
220 }
221
222 /* Command WHOIS. This command is used to query information about 
223    specific user. */
224
225 SILC_CLIENT_CMD_FUNC(whois)
226 {
227   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
228   SilcClientConnection conn = cmd->conn;
229   SilcBuffer buffer;
230
231   if (!cmd->conn) {
232     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
233     COMMAND_ERROR;
234     goto out;
235   }
236
237   /* Given without arguments fetches client's own information */
238   if (cmd->argc < 2) {
239     buffer = silc_id_payload_encode(cmd->conn->local_id, SILC_ID_CLIENT);
240     silc_client_send_command(cmd->client, cmd->conn, SILC_COMMAND_WHOIS, 
241                              ++conn->cmd_ident,
242                              1, 3, buffer->data, buffer->len);
243     silc_buffer_free(buffer);
244     goto out;
245   }
246
247   buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
248                                        cmd->argc - 1, ++cmd->argv,
249                                        ++cmd->argv_lens, ++cmd->argv_types,
250                                        0);
251   silc_client_packet_send(cmd->client, cmd->conn->sock,
252                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
253                           buffer->data, buffer->len, TRUE);
254   silc_buffer_free(buffer);
255   cmd->argv--;
256   cmd->argv_lens--;
257   cmd->argv_types--;
258
259   /* Notify application */
260   COMMAND;
261
262  out:
263   silc_client_command_free(cmd);
264 }
265
266 /* Command WHOWAS. This command is used to query history information about
267    specific user that used to exist in the network. */
268
269 SILC_CLIENT_CMD_FUNC(whowas)
270 {
271   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
272   SilcClientConnection conn = cmd->conn;
273   SilcBuffer buffer;
274
275   if (!cmd->conn) {
276     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
277     COMMAND_ERROR;
278     goto out;
279   }
280
281   if (cmd->argc < 2 || cmd->argc > 3) {
282     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
283              "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
284     COMMAND_ERROR;
285     goto out;
286   }
287
288   buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
289                                        cmd->argc - 1, ++cmd->argv,
290                                        ++cmd->argv_lens, ++cmd->argv_types,
291                                        0);
292   silc_client_packet_send(cmd->client, cmd->conn->sock,
293                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
294                           buffer->data, buffer->len, TRUE);
295   silc_buffer_free(buffer);
296   cmd->argv--;
297   cmd->argv_lens--;
298   cmd->argv_types--;
299
300   /* Notify application */
301   COMMAND;
302
303  out:
304   silc_client_command_free(cmd);
305 }
306
307 /* Command IDENTIFY. This command is used to query information about 
308    specific user, especially ID's. */
309
310 SILC_CLIENT_CMD_FUNC(identify)
311 {
312   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
313   SilcClientConnection conn = cmd->conn;
314   SilcBuffer buffer;
315
316   if (!cmd->conn) {
317     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
318     COMMAND_ERROR;
319     goto out;
320   }
321
322   if (cmd->argc < 2 || cmd->argc > 3) {
323     COMMAND_ERROR;
324     goto out;
325   }
326
327   if (cmd->argc == 2)
328     buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
329                                             ++conn->cmd_ident, 1,
330                                             1, cmd->argv[1],
331                                             cmd->argv_lens[1]);
332   else
333     buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
334                                             ++conn->cmd_ident, 2,
335                                             1, cmd->argv[1],
336                                             cmd->argv_lens[1],
337                                             4, cmd->argv[2],
338                                             cmd->argv_lens[2]);
339
340   silc_client_packet_send(cmd->client, cmd->conn->sock,
341                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
342                           buffer->data, buffer->len, TRUE);
343   silc_buffer_free(buffer);
344
345   /* Notify application */
346   COMMAND;
347
348  out:
349   silc_client_command_free(cmd);
350 }
351
352 /* Command NICK. Shows current nickname/sets new nickname on current
353    window. */
354
355 SILC_CLIENT_CMD_FUNC(nick)
356 {
357   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
358   SilcClientConnection conn = cmd->conn;
359   SilcBuffer buffer;
360
361   if (!cmd->conn) {
362     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
363     COMMAND_ERROR;
364     goto out;
365   }
366
367   if (cmd->argc < 2) {
368     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
369                           "Usage: /NICK <nickname>");
370     COMMAND_ERROR;
371     goto out;
372   }
373
374   if (!strcmp(conn->nickname, cmd->argv[1]))
375     goto out;
376
377   /* Show current nickname */
378   if (cmd->argc < 2) {
379     if (cmd->conn) {
380       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
381                             "Your nickname is %s on server %s", 
382                             conn->nickname, conn->remote_host);
383     } else {
384       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
385                             "Your nickname is %s", conn->nickname);
386     }
387
388     COMMAND;
389     goto out;
390   }
391
392   /* Set new nickname */
393   buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
394                                        cmd->argc - 1, ++cmd->argv,
395                                        ++cmd->argv_lens, ++cmd->argv_types,
396                                        ++cmd->conn->cmd_ident);
397   silc_client_packet_send(cmd->client, cmd->conn->sock,
398                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
399                           buffer->data, buffer->len, TRUE);
400   silc_buffer_free(buffer);
401   cmd->argv--;
402   cmd->argv_lens--;
403   cmd->argv_types--;
404   if (conn->nickname)
405     silc_free(conn->nickname);
406   conn->nickname = strdup(cmd->argv[1]);
407
408   /* Notify application */
409   COMMAND;
410
411  out:
412   silc_client_command_free(cmd);
413 }
414
415 /* Command LIST. Lists channels on the current server. */
416
417 SILC_CLIENT_CMD_FUNC(list)
418 {
419   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
420   SilcClientConnection conn = cmd->conn;
421   SilcIDCacheEntry id_cache = NULL;
422   SilcChannelEntry channel;
423   SilcBuffer buffer, idp = NULL;
424   char *name;
425
426   if (!cmd->conn) {
427     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
428     COMMAND_ERROR;
429     goto out;
430   }
431
432   if (cmd->argc == 2) {
433     name = cmd->argv[1];
434
435     /* Get the Channel ID of the channel */
436     if (silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
437       channel = (SilcChannelEntry)id_cache->context;
438       idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
439     }
440   }
441
442   if (!idp)
443     buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, 
444                                             ++conn->cmd_ident, 0);
445   else
446     buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, 
447                                             ++conn->cmd_ident, 1,
448                                             1, idp->data, idp->len);
449
450   silc_client_packet_send(cmd->client, cmd->conn->sock,
451                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
452                           buffer->data, buffer->len, TRUE);
453   silc_buffer_free(buffer);
454   if (idp)
455     silc_buffer_free(idp);
456
457   /* Notify application */
458   COMMAND;
459
460  out:
461   silc_client_command_free(cmd);
462 }
463
464 /* Command TOPIC. Sets/shows topic on a channel. */
465
466 SILC_CLIENT_CMD_FUNC(topic)
467 {
468   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
469   SilcClientConnection conn = cmd->conn;
470   SilcIDCacheEntry id_cache = NULL;
471   SilcChannelEntry channel;
472   SilcBuffer buffer, idp;
473   char *name;
474
475   if (!cmd->conn) {
476     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
477     COMMAND_ERROR;
478     goto out;
479   }
480
481   if (cmd->argc < 2 || cmd->argc > 3) {
482     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
483                           "Usage: /TOPIC <channel> [<topic>]");
484     COMMAND_ERROR;
485     goto out;
486   }
487
488   if (cmd->argv[1][0] == '*') {
489     if (!conn->current_channel) {
490       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
491                             "You are not on any channel");
492       COMMAND_ERROR;
493       goto out;
494     }
495     name = conn->current_channel->channel_name;
496   } else {
497     name = cmd->argv[1];
498   }
499
500   if (!conn->current_channel) {
501     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
502                           "You are not on that channel");
503     COMMAND_ERROR;
504     goto out;
505   }
506
507   /* Get the Channel ID of the channel */
508   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
509     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
510                           "You are not on that channel");
511     COMMAND_ERROR;
512     goto out;
513   }
514
515   channel = (SilcChannelEntry)id_cache->context;
516
517   /* Send TOPIC command to the server */
518   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
519   if (cmd->argc > 2)
520     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 
521                                             ++conn->cmd_ident, 2, 
522                                             1, idp->data, idp->len,
523                                             2, cmd->argv[2], 
524                                             strlen(cmd->argv[2]));
525   else
526     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 
527                                             ++conn->cmd_ident, 1,
528                                             1, idp->data, idp->len);
529   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
530                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
531   silc_buffer_free(buffer);
532   silc_buffer_free(idp);
533
534   /* Notify application */
535   COMMAND;
536
537  out:
538   silc_client_command_free(cmd);
539 }
540
541 /* Command INVITE. Invites specific client to join a channel. This is
542    also used to mange the invite list of the channel. */
543
544 SILC_CLIENT_CMD_FUNC(invite)
545 {
546   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
547   SilcClient client = cmd->client;
548   SilcClientConnection conn = cmd->conn;
549   SilcClientEntry client_entry = NULL;
550   SilcChannelEntry channel;
551   SilcBuffer buffer, clidp, chidp;
552   uint32 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     /* Pulic key auth XXX TODO */
1610     auth = NULL;
1611   } else {
1612     /* Encode the authentication payload */
1613     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1614                                     data, data_len);
1615   }
1616
1617   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1618                                           1, cmd->argv[1], 
1619                                           strlen(cmd->argv[1]),
1620                                           2, auth->data, auth->len);
1621   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1622                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1623
1624   silc_buffer_free(buffer);
1625   silc_buffer_free(auth);
1626
1627   /* Notify application */
1628   COMMAND;
1629 }
1630
1631 /* OPER command. Used to obtain server operator privileges. */
1632
1633 SILC_CLIENT_CMD_FUNC(oper)
1634 {
1635   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1636   SilcClientConnection conn = cmd->conn;
1637   unsigned char *auth_data;
1638   uint32 auth_data_len = 0;
1639
1640   if (!cmd->conn) {
1641     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1642     COMMAND_ERROR;
1643     goto out;
1644   }
1645
1646   if (cmd->argc < 2) {
1647     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1648                           "Usage: /OPER <username> [<public key>]");
1649     COMMAND_ERROR;
1650     goto out;
1651   }
1652
1653   if (cmd->argc == 3) {
1654     /* XXX Get public key */
1655     auth_data = NULL;
1656     COMMAND_ERROR;
1657     goto out;
1658   } else {
1659     /* Get passphrase */
1660     cmd->client->ops->ask_passphrase(cmd->client, conn,
1661                                      silc_client_command_oper_send,
1662                                      context);
1663     return;
1664   }
1665
1666   silc_client_command_oper_send(auth_data, auth_data_len, context);
1667
1668   memset(auth_data, 0, auth_data_len);
1669   silc_free(auth_data);
1670
1671   /* Notify application */
1672   COMMAND;
1673
1674  out:
1675   silc_client_command_free(cmd);
1676 }
1677
1678 static void silc_client_command_silcoper_send(unsigned char *data,
1679                                               uint32 data_len, void *context)
1680 {
1681   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1682   SilcClientConnection conn = cmd->conn;
1683   SilcBuffer buffer, auth;
1684
1685   if (cmd->argc == 3) {
1686     /* Pulic key auth XXX TODO */
1687     auth = NULL;
1688   } else {
1689     /* Encode the authentication payload */
1690     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1691                                     data, data_len);
1692   }
1693
1694   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1695                                           1, cmd->argv[1], 
1696                                           strlen(cmd->argv[1]),
1697                                           2, auth->data, auth->len);
1698   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1699                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1700
1701   silc_buffer_free(buffer);
1702   silc_buffer_free(auth);
1703
1704   /* Notify application */
1705   COMMAND;
1706 }
1707
1708 /* SILCOPER command. Used to obtain router operator privileges. */
1709
1710 SILC_CLIENT_CMD_FUNC(silcoper)
1711 {
1712   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1713   SilcClientConnection conn = cmd->conn;
1714   unsigned char *auth_data;
1715   uint32 auth_data_len = 0;
1716
1717   if (!cmd->conn) {
1718     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1719     COMMAND_ERROR;
1720     goto out;
1721   }
1722
1723   if (cmd->argc < 2) {
1724     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1725                           "Usage: /SILCOPER <username> [<public key>]");
1726     COMMAND_ERROR;
1727     goto out;
1728   }
1729
1730   if (cmd->argc == 3) {
1731     /* XXX Get public key */
1732     auth_data = NULL;
1733     COMMAND_ERROR;
1734     goto out;
1735   } else {
1736     /* Get passphrase */
1737     cmd->client->ops->ask_passphrase(cmd->client, conn,
1738                                      silc_client_command_silcoper_send,
1739                                      context);
1740     return;
1741   }
1742
1743   silc_client_command_silcoper_send(auth_data, auth_data_len, context);
1744
1745   memset(auth_data, 0, auth_data_len);
1746   silc_free(auth_data);
1747
1748   /* Notify application */
1749   COMMAND;
1750
1751  out:
1752   silc_client_command_free(cmd);
1753 }
1754
1755 /* CONNECT command. Connects the server to another server. */
1756
1757 SILC_CLIENT_CMD_FUNC(connect)
1758 {
1759   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1760   SilcClientConnection conn = cmd->conn;
1761   SilcBuffer buffer;
1762   unsigned char port[4];
1763   uint32 tmp;
1764
1765   if (!cmd->conn) {
1766     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1767     COMMAND_ERROR;
1768     goto out;
1769   }
1770
1771   if (cmd->argc < 2) {
1772     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1773                           "Usage: /CONNECT <server> [<port>]");
1774     COMMAND_ERROR;
1775     goto out;
1776   }
1777
1778   if (cmd->argc == 3) {
1779     tmp = atoi(cmd->argv[2]);
1780     SILC_PUT32_MSB(tmp, port);
1781   }
1782
1783   if (cmd->argc == 3)
1784     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1785                                             1, cmd->argv[1], 
1786                                             strlen(cmd->argv[1]),
1787                                             2, port, 4);
1788   else
1789     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1790                                             1, cmd->argv[1], 
1791                                             strlen(cmd->argv[1]));
1792   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1793                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1794   silc_buffer_free(buffer);
1795
1796   /* Notify application */
1797   COMMAND;
1798
1799  out:
1800   silc_client_command_free(cmd);
1801 }
1802
1803 /* Command BAN. This is used to manage the ban list of the channel. */
1804
1805 SILC_CLIENT_CMD_FUNC(ban)
1806 {
1807   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1808   SilcClientConnection conn = cmd->conn;
1809   SilcChannelEntry channel;
1810   SilcBuffer buffer, chidp;
1811   int type = 0;
1812   char *name, *ban = NULL;
1813
1814   if (!cmd->conn) {
1815     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1816     COMMAND_ERROR;
1817     goto out;
1818   }
1819
1820   if (cmd->argc < 2) {
1821     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1822                    "Usage: /BAN <channel> "
1823                    "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1824     COMMAND_ERROR;
1825     goto out;
1826   }
1827
1828   if (cmd->argv[1][0] == '*') {
1829     if (!conn->current_channel) {
1830       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1831                             "You are not on any channel");
1832       COMMAND_ERROR;
1833       goto out;
1834     }
1835
1836     channel = conn->current_channel;
1837   } else {
1838     name = cmd->argv[1];
1839
1840     channel = silc_client_get_channel(cmd->client, conn, name);
1841     if (!channel) {
1842       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1843                             "You are on that channel");
1844       COMMAND_ERROR;
1845       goto out;
1846     }
1847   }
1848
1849   if (cmd->argc == 3) {
1850     if (cmd->argv[2][0] == '+')
1851       type = 2;
1852     else
1853       type = 3;
1854
1855     ban = cmd->argv[2];
1856     ban++;
1857   }
1858
1859   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1860
1861   /* Send the command */
1862   if (ban)
1863     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, 
1864                                             1, chidp->data, chidp->len,
1865                                             type, ban, strlen(ban));
1866   else
1867     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, 
1868                                             1, chidp->data, chidp->len);
1869
1870   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1871                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1872   silc_buffer_free(buffer);
1873   silc_buffer_free(chidp);
1874
1875   /* Notify application */
1876   COMMAND;
1877
1878  out:
1879   silc_client_command_free(cmd);
1880 }
1881
1882 /* CLOSE command. Close server connection to the remote server */
1883  
1884 SILC_CLIENT_CMD_FUNC(close)
1885 {
1886   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1887   SilcClientConnection conn = cmd->conn;
1888   SilcBuffer buffer;
1889   unsigned char port[4];
1890   uint32 tmp;
1891
1892   if (!cmd->conn) {
1893     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1894     COMMAND_ERROR;
1895     goto out;
1896   }
1897
1898   if (cmd->argc < 2) {
1899     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1900                           "Usage: /CLOSE <server> [<port>]");
1901     COMMAND_ERROR;
1902     goto out;
1903   }
1904
1905   if (cmd->argc == 3) {
1906     tmp = atoi(cmd->argv[2]);
1907     SILC_PUT32_MSB(tmp, port);
1908   }
1909
1910   if (cmd->argc == 3)
1911     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1912                                             1, cmd->argv[1], 
1913                                             strlen(cmd->argv[1]),
1914                                             2, port, 4);
1915   else
1916     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1917                                             1, cmd->argv[1], 
1918                                             strlen(cmd->argv[1]));
1919   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1920                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1921   silc_buffer_free(buffer);
1922
1923   /* Notify application */
1924   COMMAND;
1925
1926  out:
1927   silc_client_command_free(cmd);
1928 }
1929  
1930 /* SHUTDOWN command. Shutdowns the server. */
1931
1932 SILC_CLIENT_CMD_FUNC(shutdown)
1933 {
1934   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1935
1936   if (!cmd->conn) {
1937     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1938     COMMAND_ERROR;
1939     goto out;
1940   }
1941
1942   /* Send the command */
1943   silc_client_send_command(cmd->client, cmd->conn, 
1944                            SILC_COMMAND_SHUTDOWN, 0, 0);
1945
1946   /* Notify application */
1947   COMMAND;
1948
1949  out:
1950   silc_client_command_free(cmd);
1951 }
1952
1953 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1954
1955 SILC_CLIENT_CMD_FUNC(leave)
1956 {
1957   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1958   SilcClientConnection conn = cmd->conn;
1959   SilcIDCacheEntry id_cache = NULL;
1960   SilcChannelEntry channel;
1961   SilcBuffer buffer, idp;
1962   char *name;
1963
1964   if (!cmd->conn) {
1965     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1966     COMMAND_ERROR;
1967     goto out;
1968   }
1969
1970   if (cmd->argc != 2) {
1971     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1972                           "Usage: /LEAVE <channel>");
1973     COMMAND_ERROR;
1974     goto out;
1975   }
1976
1977   if (cmd->argv[1][0] == '*') {
1978     if (!conn->current_channel) {
1979       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1980                             "You are not on any channel");
1981       COMMAND_ERROR;
1982       goto out;
1983     }
1984     name = conn->current_channel->channel_name;
1985   } else {
1986     name = cmd->argv[1];
1987   }
1988
1989   /* Get the Channel ID of the channel */
1990   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1991     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1992                           "You are not on that channel");
1993     COMMAND_ERROR;
1994     goto out;
1995   }
1996
1997   channel = (SilcChannelEntry)id_cache->context;
1998
1999   /* Send LEAVE command to the server */
2000   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
2001   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
2002                                           1, idp->data, idp->len);
2003   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2004                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2005   silc_buffer_free(buffer);
2006   silc_buffer_free(idp);
2007
2008   /* Notify application */
2009   COMMAND;
2010
2011   if (conn->current_channel == channel)
2012     conn->current_channel = NULL;
2013
2014   silc_client_del_channel(cmd->client, cmd->conn, channel);
2015
2016  out:
2017   silc_client_command_free(cmd);
2018 }
2019
2020 /* Command USERS. Requests the USERS of the clients joined on requested
2021    channel. */
2022
2023 SILC_CLIENT_CMD_FUNC(users)
2024 {
2025   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2026   SilcClientConnection conn = cmd->conn;
2027   SilcBuffer buffer;
2028   char *name;
2029
2030   if (!cmd->conn) {
2031     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2032     COMMAND_ERROR;
2033     goto out;
2034   }
2035
2036   if (cmd->argc != 2) {
2037     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2038                           "Usage: /USERS <channel>");
2039     COMMAND_ERROR;
2040     goto out;
2041   }
2042
2043   if (cmd->argv[1][0] == '*') {
2044     if (!conn->current_channel) {
2045       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2046                             "You are not on any channel");
2047       COMMAND_ERROR;
2048       goto out;
2049     }
2050     name = conn->current_channel->channel_name;
2051   } else {
2052     name = cmd->argv[1];
2053   }
2054
2055   /* Send USERS command to the server */
2056   buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
2057                                           ++conn->cmd_ident, 1, 
2058                                           2, name, strlen(name));
2059   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
2060                           NULL, 0, NULL, NULL, buffer->data, 
2061                           buffer->len, TRUE);
2062   silc_buffer_free(buffer);
2063
2064   /* Notify application */
2065   COMMAND;
2066
2067  out:
2068   silc_client_command_free(cmd);
2069 }
2070
2071 /* Command GETKEY. Used to fetch remote client's public key. */
2072
2073 SILC_CLIENT_CMD_FUNC(getkey)
2074 {
2075   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2076   SilcClientConnection conn = cmd->conn;
2077   SilcClient client = cmd->client;
2078   SilcClientEntry client_entry = NULL;
2079   SilcServerEntry server_entry = NULL;
2080   uint32 num = 0;
2081   char *nickname = NULL, *server = NULL;
2082   SilcBuffer idp, buffer;
2083
2084   if (!cmd->conn) {
2085     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2086     COMMAND_ERROR;
2087     goto out;
2088   }
2089
2090   if (cmd->argc < 2) {
2091     client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
2092                      "Usage: /GETKEY <nickname or server name>");
2093     COMMAND_ERROR;
2094     goto out;
2095   }
2096
2097   /* Parse the typed nickname. */
2098   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
2099     client->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, "Bad nickname");
2100     COMMAND_ERROR;
2101     goto out;
2102   }
2103
2104   /* Find client entry */
2105   client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
2106                                         FALSE);
2107   if (!client_entry) {
2108     /* Check whether user requested server actually */
2109     server_entry = silc_client_get_server(client, conn, nickname);
2110
2111     if (!server_entry) {
2112       /* No. what ever user wants we don't have it, so resolve it. We
2113          will try to resolve both client and server, one of them is
2114          bound to be wrong. */
2115
2116       /* This will send the IDENTIFY command */
2117       silc_idlist_get_client(client, conn, nickname, server, num, TRUE);
2118       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2119                                   conn->cmd_ident,  
2120                                   silc_client_command_destructor,
2121                                   silc_client_command_getkey, 
2122                                   silc_client_command_dup(cmd));
2123
2124       /* This sends the INFO command to resolve the server. */
2125       silc_client_send_command(client, conn, SILC_COMMAND_INFO,
2126                                ++conn->cmd_ident, 1, 
2127                                1, nickname, strlen(nickname));
2128       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2129                                   conn->cmd_ident,  
2130                                   silc_client_command_destructor,
2131                                   silc_client_command_getkey, 
2132                                   silc_client_command_dup(cmd));
2133
2134       cmd->pending = 1;
2135       return;
2136     }
2137
2138     idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2139   } else {
2140     idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2141   }
2142
2143   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
2144                                           1, idp->data, idp->len);
2145   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2146                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2147   silc_buffer_free(buffer);
2148   silc_buffer_free(idp);
2149
2150   /* Notify application */
2151   COMMAND;
2152
2153  out:
2154   silc_client_command_free(cmd);
2155 }