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