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