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