Added WHOWAS command to server and client.
[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, 2),
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(restart, RESTART, "RESTART",
52                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
53   SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
54                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
55   SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
56                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
57   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
58                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
59   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
60   SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
61
62   { NULL, 0, NULL, 0, 0 },
63 };
64
65 #define SILC_NOT_CONNECTED(x, c) \
66   x->ops->say((x), (c), \
67            "You are not connected to a server, use /SERVER to connect");
68
69 /* Command operation that is called at the end of all commands. 
70    Usage: COMMAND; */
71 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
72   cmd, TRUE, cmd->command->cmd)
73
74 /* Error to application. Usage: COMMAND_ERROR; */
75 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
76   cmd, FALSE, cmd->command->cmd)
77
78 /* Generic function to send any command. The arguments must be sent already
79    encoded into correct form and in correct order. */
80
81 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
82                               SilcCommand command, unsigned short ident,
83                               unsigned int 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                                  unsigned short 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                                      unsigned short 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                                       unsigned short 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);
199   }
200 }
201
202 /* Duplicate Command Context by adding reference counter. The context won't
203    be free'd untill it hits zero. */
204
205 SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
206 {
207   ctx->users++;
208   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
209                   ctx->users));
210   return ctx;
211 }
212
213 /* Pending command destructor. */
214
215 static void silc_client_command_destructor(void *context)
216 {
217   silc_client_command_free((SilcClientCommandContext)context);
218 }
219
220 /* silc_client_get_client completion callback */
221 void silc_client_command_completion(SilcClient client,
222                                     SilcClientConnection conn,
223                                     SilcClientEntry clients,
224                                     unsigned int clients_count,
225                                     void *context)
226 {
227
228 }
229
230 /* Command WHOIS. This command is used to query information about 
231    specific user. */
232
233 SILC_CLIENT_CMD_FUNC(whois)
234 {
235   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
236   SilcClientConnection conn = cmd->conn;
237   SilcBuffer buffer;
238
239   if (!cmd->conn) {
240     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
241     COMMAND_ERROR;
242     goto out;
243   }
244
245   if (cmd->argc < 2 || cmd->argc > 3) {
246     cmd->client->ops->say(cmd->client, conn, 
247              "Usage: /WHOIS <nickname>[@<server>] [<count>]");
248     COMMAND_ERROR;
249     goto out;
250   }
251
252   buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
253                                        cmd->argc - 1, ++cmd->argv,
254                                        ++cmd->argv_lens, ++cmd->argv_types,
255                                        0);
256   silc_client_packet_send(cmd->client, cmd->conn->sock,
257                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
258                           buffer->data, buffer->len, TRUE);
259   silc_buffer_free(buffer);
260   cmd->argv--;
261   cmd->argv_lens--;
262   cmd->argv_types--;
263
264   /* Notify application */
265   COMMAND;
266
267  out:
268   silc_client_command_free(cmd);
269 }
270
271 /* Command WHOWAS. This command is used to query history information about
272    specific user that used to exist in the network. */
273
274 SILC_CLIENT_CMD_FUNC(whowas)
275 {
276   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
277   SilcClientConnection conn = cmd->conn;
278   SilcBuffer buffer;
279
280   if (!cmd->conn) {
281     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
282     COMMAND_ERROR;
283     goto out;
284   }
285
286   if (cmd->argc < 2 || cmd->argc > 3) {
287     cmd->client->ops->say(cmd->client, conn, 
288              "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
289     COMMAND_ERROR;
290     goto out;
291   }
292
293   buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
294                                        cmd->argc - 1, ++cmd->argv,
295                                        ++cmd->argv_lens, ++cmd->argv_types,
296                                        0);
297   silc_client_packet_send(cmd->client, cmd->conn->sock,
298                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
299                           buffer->data, buffer->len, TRUE);
300   silc_buffer_free(buffer);
301   cmd->argv--;
302   cmd->argv_lens--;
303   cmd->argv_types--;
304
305   /* Notify application */
306   COMMAND;
307
308  out:
309   silc_client_command_free(cmd);
310 }
311
312 /* Command IDENTIFY. This command is used to query information about 
313    specific user, especially ID's. */
314
315 SILC_CLIENT_CMD_FUNC(identify)
316 {
317   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
318   SilcClientConnection conn = cmd->conn;
319   SilcBuffer buffer;
320
321   if (!cmd->conn) {
322     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
323     COMMAND_ERROR;
324     goto out;
325   }
326
327   if (cmd->argc < 2 || cmd->argc > 3) {
328     cmd->client->ops->say(cmd->client, conn,
329              "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
330     COMMAND_ERROR;
331     goto out;
332   }
333
334   buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
335                                        cmd->argc - 1, ++cmd->argv,
336                                        ++cmd->argv_lens, ++cmd->argv_types,
337                                        0);
338   silc_client_packet_send(cmd->client, cmd->conn->sock,
339                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
340                           buffer->data, buffer->len, TRUE);
341   silc_buffer_free(buffer);
342   cmd->argv--;
343   cmd->argv_lens--;
344   cmd->argv_types--;
345
346   /* Notify application */
347   COMMAND;
348
349  out:
350   silc_client_command_free(cmd);
351 }
352
353 /* Command NICK. Shows current nickname/sets new nickname on current
354    window. */
355
356 SILC_CLIENT_CMD_FUNC(nick)
357 {
358   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
359   SilcClientConnection conn = cmd->conn;
360   SilcBuffer buffer;
361
362   if (!cmd->conn) {
363     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
364     COMMAND_ERROR;
365     goto out;
366   }
367
368   if (!strcmp(conn->nickname, cmd->argv[1]))
369     goto out;
370
371   /* Show current nickname */
372   if (cmd->argc < 2) {
373     if (cmd->conn) {
374       cmd->client->ops->say(cmd->client, conn, 
375                             "Your nickname is %s on server %s", 
376                             conn->nickname, conn->remote_host);
377     } else {
378       cmd->client->ops->say(cmd->client, conn, 
379                             "Your nickname is %s", conn->nickname);
380     }
381
382     /* XXX Notify application */
383     COMMAND;
384     goto out;
385   }
386
387   /* Set new nickname */
388   buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
389                                        cmd->argc - 1, ++cmd->argv,
390                                        ++cmd->argv_lens, ++cmd->argv_types,
391                                        ++cmd->conn->cmd_ident);
392   silc_client_packet_send(cmd->client, cmd->conn->sock,
393                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
394                           buffer->data, buffer->len, TRUE);
395   silc_buffer_free(buffer);
396   cmd->argv--;
397   cmd->argv_lens--;
398   cmd->argv_types--;
399   if (conn->nickname)
400     silc_free(conn->nickname);
401   conn->nickname = strdup(cmd->argv[1]);
402
403   /* Notify application */
404   COMMAND;
405
406  out:
407   silc_client_command_free(cmd);
408 }
409
410 SILC_CLIENT_CMD_FUNC(list)
411 {
412 }
413
414 /* Command TOPIC. Sets/shows topic on a channel. */
415
416 SILC_CLIENT_CMD_FUNC(topic)
417 {
418   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
419   SilcClientConnection conn = cmd->conn;
420   SilcIDCacheEntry id_cache = NULL;
421   SilcChannelEntry channel;
422   SilcBuffer buffer, idp;
423   char *name;
424
425   if (!cmd->conn) {
426     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
427     COMMAND_ERROR;
428     goto out;
429   }
430
431   if (cmd->argc < 2 || cmd->argc > 3) {
432     cmd->client->ops->say(cmd->client, conn,
433                           "Usage: /TOPIC <channel> [<topic>]");
434     COMMAND_ERROR;
435     goto out;
436   }
437
438   if (cmd->argv[1][0] == '*') {
439     if (!conn->current_channel) {
440       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
441       COMMAND_ERROR;
442       goto out;
443     }
444     name = conn->current_channel->channel_name;
445   } else {
446     name = cmd->argv[1];
447   }
448
449   if (!conn->current_channel) {
450     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
451     COMMAND_ERROR;
452     goto out;
453   }
454
455   /* Get the Channel ID of the channel */
456   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
457     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
458     COMMAND_ERROR;
459     goto out;
460   }
461
462   channel = (SilcChannelEntry)id_cache->context;
463
464   /* Send TOPIC command to the server */
465   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
466   if (cmd->argc > 2)
467     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2, 
468                                             1, idp->data, idp->len,
469                                             2, cmd->argv[2], 
470                                             strlen(cmd->argv[2]));
471   else
472     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1, 
473                                             1, idp->data, idp->len,
474                                             0);
475   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
476                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
477   silc_buffer_free(buffer);
478   silc_buffer_free(idp);
479
480   /* Notify application */
481   COMMAND;
482
483  out:
484   silc_client_command_free(cmd);
485 }
486
487 /* Command INVITE. Invites specific client to join a channel. */
488
489 SILC_CLIENT_CMD_FUNC(invite)
490 {
491   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
492   SilcClient client = cmd->client;
493   SilcClientConnection conn = cmd->conn;
494   SilcClientEntry client_entry;
495   SilcChannelEntry channel_entry;
496   SilcBuffer buffer, clidp, chidp;
497   unsigned int num = 0;
498   char *nickname = NULL, *server = NULL;
499
500   if (!cmd->conn) {
501     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
502     COMMAND_ERROR;
503     goto out;
504   }
505
506   if (cmd->argc != 3) {
507     cmd->client->ops->say(cmd->client, conn,
508                           "Usage: /INVITE <nickname>[@<server>] <channel>");
509     COMMAND_ERROR;
510     goto out;
511   }
512
513   /* Parse the typed nickname. */
514   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
515     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
516     COMMAND_ERROR;
517     goto out;
518   }
519
520   /* Find client entry */
521   client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
522                                         TRUE);
523   if (!client_entry) {
524     if (nickname)
525       silc_free(nickname);
526     if (server)
527       silc_free(server);
528
529     /* Client entry not found, it was requested thus mark this to be
530        pending command. */
531     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 0,
532                                 silc_client_command_destructor,
533                                 silc_client_command_invite, 
534                                 silc_client_command_dup(cmd));
535     cmd->pending = 1;
536     return;
537   }
538
539   /* Find channel entry */
540   channel_entry = silc_client_get_channel(client, conn, cmd->argv[2]);
541   if (!channel_entry) {
542     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
543     COMMAND_ERROR;
544     goto out;
545   }
546
547   /* Send command */
548   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
549   chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
550   buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
551                                           1, clidp->data, clidp->len,
552                                           2, chidp->data, chidp->len);
553   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
554                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
555   silc_buffer_free(buffer);
556   silc_buffer_free(clidp);
557   silc_buffer_free(chidp);
558
559   cmd->client->ops->say(cmd->client, conn, 
560                         "Inviting %s to channel %s", cmd->argv[1], 
561                         cmd->argv[2]);
562
563   /* Notify application */
564   COMMAND;
565
566  out:
567   silc_client_command_free(cmd);
568 }
569
570 typedef struct {
571   SilcClient client;
572   SilcClientConnection conn;
573 } *QuitInternal;
574
575 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
576 {
577   QuitInternal q = (QuitInternal)context;
578
579   /* Close connection */
580   q->client->ops->disconnect(q->client, q->conn);
581   silc_client_close_connection(q->client, q->conn->sock->user_data);
582
583   silc_free(q);
584 }
585
586 /* Command QUIT. Closes connection with current server. */
587  
588 SILC_CLIENT_CMD_FUNC(quit)
589 {
590   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
591   SilcBuffer buffer;
592   QuitInternal q;
593
594   if (!cmd->conn) {
595     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
596     COMMAND_ERROR;
597     goto out;
598   }
599
600   if (cmd->argc > 1)
601     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, 
602                                          &cmd->argv[1], &cmd->argv_lens[1],
603                                          &cmd->argv_types[1], 0);
604   else
605     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
606                                          NULL, NULL, NULL, 0);
607   silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
608                           NULL, 0, NULL, NULL, 
609                           buffer->data, buffer->len, TRUE);
610   silc_buffer_free(buffer);
611
612   q = silc_calloc(1, sizeof(*q));
613   q->client = cmd->client;
614   q->conn = cmd->conn;
615
616   /* We quit the connection with little timeout */
617   silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
618                      silc_client_command_quit_cb, (void *)q,
619                      1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
620
621   /* Notify application */
622   COMMAND;
623
624  out:
625   silc_client_command_free(cmd);
626 }
627
628 SILC_CLIENT_CMD_FUNC(kill)
629 {
630 }
631
632 /* Command INFO. Request information about specific server. If specific
633    server is not provided the current server is used. */
634
635 SILC_CLIENT_CMD_FUNC(info)
636 {
637   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
638   SilcClientConnection conn = cmd->conn;
639   SilcBuffer buffer;
640   char *name;
641
642   if (!cmd->conn) {
643     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
644     COMMAND_ERROR;
645     goto out;
646   }
647
648   if (cmd->argc < 2)
649     name = strdup(conn->remote_host);
650   else
651     name = strdup(cmd->argv[1]);
652
653   /* Send the command */
654   buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
655                                           1, name, strlen(name));
656   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
657                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
658   silc_buffer_free(buffer);
659
660   /* Notify application */
661   COMMAND;
662
663  out:
664   silc_client_command_free(cmd);
665 }
666
667 /* Command PING. Sends ping to server. This is used to test the 
668    communication channel. */
669
670 SILC_CLIENT_CMD_FUNC(ping)
671 {
672   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
673   SilcClientConnection conn = cmd->conn;
674   SilcBuffer buffer;
675   void *id;
676   int i;
677   char *name = NULL;
678
679   if (!cmd->conn) {
680     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
681     COMMAND_ERROR;
682     goto out;
683   }
684
685   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
686     name = strdup(conn->remote_host);
687
688   /* Send the command */
689   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
690                                           1, conn->remote_id_data, 
691                                           SILC_ID_SERVER_LEN);
692   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
693                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
694   silc_buffer_free(buffer);
695
696   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
697                       SILC_ID_SERVER);
698   if (!id) {
699     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
700     COMMAND_ERROR;
701     goto out;
702   }
703
704   /* Start counting time */
705   for (i = 0; i < conn->ping_count; i++) {
706     if (conn->ping[i].dest_id == NULL) {
707       conn->ping[i].start_time = time(NULL);
708       conn->ping[i].dest_id = id;
709       conn->ping[i].dest_name = name;
710       conn->ping_count++;
711       break;
712     }
713   }
714   if (i >= conn->ping_count) {
715     i = conn->ping_count;
716     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
717     conn->ping[i].start_time = time(NULL);
718     conn->ping[i].dest_id = id;
719     conn->ping[i].dest_name = name;
720     conn->ping_count++;
721   }
722   
723   /* Notify application */
724   COMMAND;
725
726  out:
727   silc_client_command_free(cmd);
728 }
729
730 SILC_CLIENT_CMD_FUNC(notice)
731 {
732 }
733
734 /* Command JOIN. Joins to a channel. */
735
736 SILC_CLIENT_CMD_FUNC(join)
737 {
738   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
739   SilcClientConnection conn = cmd->conn;
740   SilcIDCacheEntry id_cache = NULL;
741   SilcBuffer buffer, idp;
742
743   if (!cmd->conn) {
744     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
745     COMMAND_ERROR;
746     goto out;
747   }
748
749   if (cmd->argc < 2) {
750     /* Show channels currently joined to */
751
752     goto out;
753   }
754
755   /* See if we have joined to the requested channel already */
756   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
757                                     &id_cache)) {
758     cmd->client->ops->say(cmd->client, conn, 
759                           "You are talking to channel %s", cmd->argv[1]);
760     conn->current_channel = (SilcChannelEntry)id_cache->context;
761 #if 0
762     cmd->client->screen->bottom_line->channel = cmd->argv[1];
763     silc_screen_print_bottom_line(cmd->client->screen, 0);
764 #endif
765     goto out;
766   }
767
768   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
769
770   /* Send JOIN command to the server */
771   if (cmd->argc == 2)
772     buffer = 
773       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
774                                      1, cmd->argv[1], cmd->argv_lens[1],
775                                      2, idp->data, idp->len);
776   else if (cmd->argc == 3)
777     /* XXX Buggy */
778     buffer = 
779       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
780                                      1, cmd->argv[1], cmd->argv_lens[1],
781                                      2, idp->data, idp->len,
782                                      3, cmd->argv[2], cmd->argv_lens[2]);
783   else
784     buffer = 
785       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
786                                      1, cmd->argv[1], cmd->argv_lens[1],
787                                      2, idp->data, idp->len,
788                                      3, cmd->argv[2], cmd->argv_lens[2],
789                                      4, cmd->argv[3], cmd->argv_lens[3]);
790
791   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
792                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
793   silc_buffer_free(buffer);
794   silc_buffer_free(idp);
795
796   /* Notify application */
797   COMMAND;
798
799  out:
800   silc_client_command_free(cmd);
801 }
802
803 /* MOTD command. Requests motd from server. */
804
805 SILC_CLIENT_CMD_FUNC(motd)
806 {
807   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
808   SilcClientConnection conn = cmd->conn;
809   SilcBuffer buffer;
810
811   if (!cmd->conn) {
812     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
813     COMMAND_ERROR;
814     goto out;
815   }
816
817   if (cmd->argc < 1 || cmd->argc > 1) {
818     cmd->client->ops->say(cmd->client, conn,
819                           "Usage: /MOTD");
820     COMMAND_ERROR;
821     goto out;
822   }
823
824   /* Send TOPIC command to the server */
825   buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
826                                           2, conn->remote_host, 
827                                           strlen(conn->remote_host));
828   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
829                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
830   silc_buffer_free(buffer);
831
832   /* Notify application */
833   COMMAND;
834
835  out:
836   silc_client_command_free(cmd);
837 }
838
839 /* UMODE. Set user mode in SILC. */
840
841 SILC_CLIENT_CMD_FUNC(umode)
842 {
843
844 }
845
846 /* CMODE command. Sets channel mode. Modes that does not require any arguments
847    can be set several at once. Those modes that require argument must be set
848    separately (unless set with modes that does not require arguments). */
849
850 SILC_CLIENT_CMD_FUNC(cmode)
851 {
852   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
853   SilcClientConnection conn = cmd->conn;
854   SilcChannelEntry channel;
855   SilcBuffer buffer, chidp;
856   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
857   unsigned int mode, add, type, len, arg_len = 0;
858   int i;
859
860   if (!cmd->conn) {
861     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
862     COMMAND_ERROR;
863     goto out;
864   }
865
866   if (cmd->argc < 3) {
867     cmd->client->ops->say(cmd->client, conn, 
868                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
869     COMMAND_ERROR;
870     goto out;
871   }
872
873   if (cmd->argv[1][0] == '*') {
874     if (!conn->current_channel) {
875       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
876       COMMAND_ERROR;
877       goto out;
878     }
879
880     channel = conn->current_channel;
881   } else {
882     name = cmd->argv[1];
883
884     channel = silc_client_get_channel(cmd->client, conn, name);
885     if (!channel) {
886       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
887       COMMAND_ERROR;
888       goto out;
889     }
890   }
891
892   mode = channel->mode;
893
894   /* Are we adding or removing mode */
895   if (cmd->argv[2][0] == '-')
896     add = FALSE;
897   else
898     add = TRUE;
899
900   /* Argument type to be sent to server */
901   type = 0;
902
903   /* Parse mode */
904   cp = cmd->argv[2] + 1;
905   len = strlen(cp);
906   for (i = 0; i < len; i++) {
907     switch(cp[i]) {
908     case 'p':
909       if (add)
910         mode |= SILC_CHANNEL_MODE_PRIVATE;
911       else
912         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
913       break;
914     case 's':
915       if (add)
916         mode |= SILC_CHANNEL_MODE_SECRET;
917       else
918         mode &= ~SILC_CHANNEL_MODE_SECRET;
919       break;
920     case 'k':
921       if (add)
922         mode |= SILC_CHANNEL_MODE_PRIVKEY;
923       else
924         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
925       break;
926     case 'i':
927       if (add)
928         mode |= SILC_CHANNEL_MODE_INVITE;
929       else
930         mode &= ~SILC_CHANNEL_MODE_INVITE;
931       break;
932     case 't':
933       if (add)
934         mode |= SILC_CHANNEL_MODE_TOPIC;
935       else
936         mode &= ~SILC_CHANNEL_MODE_TOPIC;
937       break;
938     case 'l':
939       if (add) {
940         int ll;
941         mode |= SILC_CHANNEL_MODE_ULIMIT;
942         type = 3;
943         ll = atoi(cmd->argv[3]);
944         SILC_PUT32_MSB(ll, tmp);
945         arg = tmp;
946         arg_len = 4;
947       } else {
948         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
949       }
950       break;
951     case 'a':
952       if (add) {
953         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
954         type = 4;
955         arg = cmd->argv[3];
956         arg_len = cmd->argv_lens[3];
957       } else {
958         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
959       }
960       break;
961     case 'b':
962       if (add) {
963         mode |= SILC_CHANNEL_MODE_BAN;
964         type = 5;
965         arg = cmd->argv[3];
966         arg_len = cmd->argv_lens[3];
967       } else {
968         mode &= ~SILC_CHANNEL_MODE_BAN;
969       }
970       break;
971     case 'I':
972       if (add) {
973         mode |= SILC_CHANNEL_MODE_INVITE_LIST;
974         type = 6;
975         arg = cmd->argv[3];
976         arg_len = cmd->argv_lens[3];
977       } else {
978         mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
979       }
980       break;
981     case 'c':
982       if (add) {
983         mode |= SILC_CHANNEL_MODE_CIPHER;
984         type = 8;
985         arg = cmd->argv[3];
986         arg_len = cmd->argv_lens[3];
987       } else {
988         mode &= ~SILC_CHANNEL_MODE_CIPHER;
989       }
990       break;
991     default:
992       COMMAND_ERROR;
993       goto out;
994       break;
995     }
996   }
997
998   if (type && cmd->argc < 3) {
999     COMMAND_ERROR;
1000     goto out;
1001   }
1002
1003   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1004   SILC_PUT32_MSB(mode, modebuf);
1005
1006   /* Send the command packet. We support sending only one mode at once
1007      that requires an argument. */
1008   if (type && arg) {
1009     buffer = 
1010       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1011                                      1, chidp->data, chidp->len, 
1012                                      2, modebuf, sizeof(modebuf),
1013                                      type, arg, arg_len);
1014   } else {
1015     buffer = 
1016       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1017                                      1, chidp->data, chidp->len, 
1018                                      2, modebuf, sizeof(modebuf));
1019   }
1020
1021   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1022                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1023   silc_buffer_free(buffer);
1024   silc_buffer_free(chidp);
1025
1026   /* Notify application */
1027   COMMAND;
1028
1029  out:
1030   silc_client_command_free(cmd);
1031 }
1032
1033 /* CUMODE command. Changes client's mode on a channel. */
1034
1035 SILC_CLIENT_CMD_FUNC(cumode)
1036 {
1037   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1038   SilcClientConnection conn = cmd->conn;
1039   SilcChannelEntry channel;
1040   SilcChannelUser chu;
1041   SilcClientEntry client_entry;
1042   SilcBuffer buffer, clidp, chidp;
1043   unsigned char *name, *cp, modebuf[4];
1044   unsigned int mode = 0, add, len;
1045   char *nickname = NULL, *server = NULL;
1046   unsigned int num = 0;
1047   int i;
1048
1049   if (!cmd->conn) {
1050     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1051     COMMAND_ERROR;
1052     goto out;
1053   }
1054
1055   if (cmd->argc < 4) {
1056     cmd->client->ops->say(cmd->client, conn, 
1057                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1058     COMMAND_ERROR;
1059     goto out;
1060   }
1061
1062   if (cmd->argv[1][0] == '*') {
1063     if (!conn->current_channel) {
1064       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1065       COMMAND_ERROR;
1066       goto out;
1067     }
1068
1069     channel = conn->current_channel;
1070   } else {
1071     name = cmd->argv[1];
1072
1073     channel = silc_client_get_channel(cmd->client, conn, name);
1074     if (!channel) {
1075       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1076       COMMAND_ERROR;
1077       goto out;
1078     }
1079   }
1080
1081   /* Parse the typed nickname. */
1082   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1083     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1084     COMMAND_ERROR;
1085     goto out;
1086   }
1087
1088   /* Find client entry */
1089   client_entry = silc_idlist_get_client(cmd->client, conn, 
1090                                         nickname, server, num, TRUE);
1091   if (!client_entry) {
1092     /* Client entry not found, it was requested thus mark this to be
1093        pending command. */
1094     silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,  
1095                                 silc_client_command_destructor,
1096                                 silc_client_command_cumode, 
1097                                 silc_client_command_dup(cmd));
1098     cmd->pending = 1;
1099     return;
1100   }
1101   
1102   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1103     if (chu->client == client_entry) {
1104       chu->mode = mode;
1105       break;
1106     }
1107   }
1108
1109   /* Are we adding or removing mode */
1110   if (cmd->argv[2][0] == '-')
1111     add = FALSE;
1112   else
1113     add = TRUE;
1114
1115   /* Parse mode */
1116   cp = cmd->argv[2] + 1;
1117   len = strlen(cp);
1118   for (i = 0; i < len; i++) {
1119     switch(cp[i]) {
1120     case 'a':
1121       if (add) {
1122         mode |= SILC_CHANNEL_UMODE_CHANFO;
1123         mode |= SILC_CHANNEL_UMODE_CHANOP;
1124       } else {
1125         mode = SILC_CHANNEL_UMODE_NONE;
1126       }
1127       break;
1128     case 'f':
1129       if (add)
1130         mode |= SILC_CHANNEL_UMODE_CHANFO;
1131       else
1132         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1133       break;
1134     case 'o':
1135       if (add)
1136         mode |= SILC_CHANNEL_UMODE_CHANOP;
1137       else
1138         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1139       break;
1140     default:
1141       COMMAND_ERROR;
1142       goto out;
1143       break;
1144     }
1145   }
1146
1147   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1148   SILC_PUT32_MSB(mode, modebuf);
1149   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1150
1151   /* Send the command packet. We support sending only one mode at once
1152      that requires an argument. */
1153   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1154                                           1, chidp->data, chidp->len, 
1155                                           2, modebuf, 4,
1156                                           3, clidp->data, clidp->len);
1157
1158   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1159                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1160   silc_buffer_free(buffer);
1161   silc_buffer_free(chidp);
1162   silc_buffer_free(clidp);
1163   
1164   /* Notify application */
1165   COMMAND;
1166
1167  out:
1168   silc_client_command_free(cmd);
1169 }
1170
1171 /* KICK command. Kicks a client out of channel. */
1172
1173 SILC_CLIENT_CMD_FUNC(kick)
1174 {
1175   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1176   SilcClientConnection conn = cmd->conn;
1177   SilcIDCacheEntry id_cache = NULL;
1178   SilcChannelEntry channel;
1179   SilcBuffer buffer, idp, idp2;
1180   SilcClientEntry target;
1181   char *name;
1182   unsigned int num = 0;
1183   char *nickname = NULL, *server = NULL;
1184
1185   if (!cmd->conn) {
1186     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1187     COMMAND_ERROR;
1188     goto out;
1189   }
1190
1191   if (cmd->argc < 3) {
1192     cmd->client->ops->say(cmd->client, conn, 
1193                           "Usage: /KICK <channel> <client> [<comment>]");
1194     COMMAND_ERROR;
1195     goto out;
1196   }
1197
1198   if (cmd->argv[1][0] == '*') {
1199     if (!conn->current_channel) {
1200       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1201       COMMAND_ERROR;
1202       goto out;
1203     }
1204     name = conn->current_channel->channel_name;
1205   } else {
1206     name = cmd->argv[1];
1207   }
1208
1209   if (!conn->current_channel) {
1210     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1211     COMMAND_ERROR;
1212     goto out;
1213   }
1214
1215   /* Get the Channel ID of the channel */
1216   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1217     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1218     COMMAND_ERROR;
1219     goto out;
1220   }
1221
1222   channel = (SilcChannelEntry)id_cache->context;
1223
1224   /* Parse the typed nickname. */
1225   if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1226     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1227     COMMAND_ERROR;
1228     goto out;
1229   }
1230
1231   /* Get the target client */
1232   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1233                                   server, num, FALSE);
1234   if (!target) {
1235     cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1236                           cmd->argv[2]);
1237     COMMAND_ERROR;
1238     goto out;
1239   }
1240
1241   /* Send KICK command to the server */
1242   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1243   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1244   if (cmd->argc == 3)
1245     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1246                                             1, idp->data, idp->len,
1247                                             2, idp2->data, idp2->len);
1248   else
1249     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1250                                             1, idp->data, idp->len,
1251                                             2, idp2->data, idp2->len,
1252                                             3, cmd->argv[3], 
1253                                             strlen(cmd->argv[3]));
1254   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1255                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1256   silc_buffer_free(buffer);
1257   silc_buffer_free(idp);
1258   silc_buffer_free(idp2);
1259
1260   /* Notify application */
1261   COMMAND;
1262
1263  out:
1264   silc_client_command_free(cmd);
1265 }
1266
1267 SILC_CLIENT_CMD_FUNC(silcoper)
1268 {
1269 }
1270
1271 SILC_CLIENT_CMD_FUNC(oper)
1272 {
1273 }
1274
1275 /* CONNECT command. Connects the server to another server. */
1276
1277 SILC_CLIENT_CMD_FUNC(connect)
1278 {
1279   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1280   SilcClientConnection conn = cmd->conn;
1281   SilcBuffer buffer;
1282   unsigned char port[4];
1283   unsigned int tmp;
1284
1285   if (!cmd->conn) {
1286     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1287     COMMAND_ERROR;
1288     goto out;
1289   }
1290
1291   if (cmd->argc < 2) {
1292     cmd->client->ops->say(cmd->client, conn, 
1293                           "Usage: /CONNECT <server> [<port>]");
1294     COMMAND_ERROR;
1295     goto out;
1296   }
1297
1298   if (cmd->argc == 3) {
1299     tmp = atoi(cmd->argv[2]);
1300     SILC_PUT32_MSB(tmp, port);
1301   }
1302
1303   if (cmd->argc == 3)
1304     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1305                                             1, cmd->argv[1], 
1306                                             strlen(cmd->argv[1]),
1307                                             2, port, 4);
1308   else
1309     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1310                                             1, cmd->argv[1], 
1311                                             strlen(cmd->argv[1]));
1312   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1313                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1314   silc_buffer_free(buffer);
1315
1316   /* Notify application */
1317   COMMAND;
1318
1319  out:
1320   silc_client_command_free(cmd);
1321 }
1322
1323 SILC_CLIENT_CMD_FUNC(restart)
1324 {
1325 }
1326
1327 /* CLOSE command. Close server connection to the remote server */
1328  
1329 SILC_CLIENT_CMD_FUNC(close)
1330 {
1331   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1332   SilcClientConnection conn = cmd->conn;
1333   SilcBuffer buffer;
1334   unsigned char port[4];
1335   unsigned int tmp;
1336
1337   if (!cmd->conn) {
1338     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1339     COMMAND_ERROR;
1340     goto out;
1341   }
1342
1343   if (cmd->argc < 2) {
1344     cmd->client->ops->say(cmd->client, conn, 
1345                           "Usage: /CLOSE <server> [<port>]");
1346     COMMAND_ERROR;
1347     goto out;
1348   }
1349
1350   if (cmd->argc == 3) {
1351     tmp = atoi(cmd->argv[2]);
1352     SILC_PUT32_MSB(tmp, port);
1353   }
1354
1355   if (cmd->argc == 3)
1356     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1357                                             1, cmd->argv[1], 
1358                                             strlen(cmd->argv[1]),
1359                                             2, port, 4);
1360   else
1361     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1362                                             1, cmd->argv[1], 
1363                                             strlen(cmd->argv[1]));
1364   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1365                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1366   silc_buffer_free(buffer);
1367
1368   /* Notify application */
1369   COMMAND;
1370
1371  out:
1372   silc_client_command_free(cmd);
1373 }
1374  
1375 /* SHUTDOWN command. Shutdowns the server. */
1376
1377 SILC_CLIENT_CMD_FUNC(shutdown)
1378 {
1379   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1380
1381   if (!cmd->conn) {
1382     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1383     COMMAND_ERROR;
1384     goto out;
1385   }
1386
1387   /* Send the command */
1388   silc_client_send_command(cmd->client, cmd->conn, 
1389                            SILC_COMMAND_SHUTDOWN, 0, 0);
1390
1391   /* Notify application */
1392   COMMAND;
1393
1394  out:
1395   silc_client_command_free(cmd);
1396 }
1397  
1398 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1399
1400 SILC_CLIENT_CMD_FUNC(leave)
1401 {
1402   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1403   SilcClientConnection conn = cmd->conn;
1404   SilcIDCacheEntry id_cache = NULL;
1405   SilcChannelEntry channel;
1406   SilcBuffer buffer, idp;
1407   char *name;
1408
1409   if (!cmd->conn) {
1410     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1411     COMMAND_ERROR;
1412     goto out;
1413   }
1414
1415   if (cmd->argc != 2) {
1416     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1417     COMMAND_ERROR;
1418     goto out;
1419   }
1420
1421   if (cmd->argv[1][0] == '*') {
1422     if (!conn->current_channel) {
1423       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1424       COMMAND_ERROR;
1425       goto out;
1426     }
1427     name = conn->current_channel->channel_name;
1428   } else {
1429     name = cmd->argv[1];
1430   }
1431
1432   if (!conn->current_channel) {
1433     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1434     COMMAND_ERROR;
1435     goto out;
1436   }
1437
1438   /* Get the Channel ID of the channel */
1439   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1440     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1441     COMMAND_ERROR;
1442     goto out;
1443   }
1444
1445   channel = (SilcChannelEntry)id_cache->context;
1446
1447   /* Send LEAVE command to the server */
1448   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1449   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1450                                           1, idp->data, idp->len);
1451   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1452                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1453   silc_buffer_free(buffer);
1454   silc_buffer_free(idp);
1455
1456   /* We won't talk anymore on this channel */
1457   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1458
1459   conn->current_channel = NULL;
1460
1461   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1462   silc_free(channel->channel_name);
1463   silc_free(channel->id);
1464   silc_free(channel->key);
1465   silc_cipher_free(channel->channel_key);
1466   silc_free(channel);
1467
1468   /* Notify application */
1469   COMMAND;
1470
1471  out:
1472   silc_client_command_free(cmd);
1473 }
1474
1475 /* Command USERS. Requests the USERS of the clients joined on requested
1476    channel. */
1477
1478 SILC_CLIENT_CMD_FUNC(users)
1479 {
1480   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1481   SilcClientConnection conn = cmd->conn;
1482   SilcIDCacheEntry id_cache = NULL;
1483   SilcChannelEntry channel;
1484   SilcBuffer buffer, idp;
1485   char *name, *line = NULL;
1486   unsigned int line_len = 0;
1487
1488   if (!cmd->conn) {
1489     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1490     COMMAND_ERROR;
1491     goto out;
1492   }
1493
1494   if (cmd->argc != 2) {
1495     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1496     COMMAND_ERROR;
1497     goto out;
1498   }
1499
1500   if (cmd->argv[1][0] == '*') {
1501     if (!conn->current_channel) {
1502       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1503       COMMAND_ERROR;
1504       goto out;
1505     }
1506     name = conn->current_channel->channel_name;
1507   } else {
1508     name = cmd->argv[1];
1509   }
1510
1511   if (!conn->current_channel) {
1512     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1513     COMMAND_ERROR;
1514     goto out;
1515   }
1516
1517   /* Get the Channel ID of the channel */
1518   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1519     /* XXX should resolve the channel ID; LIST command */
1520     cmd->client->ops->say(cmd->client, conn, 
1521                           "You are not on that channel", name);
1522     COMMAND_ERROR;
1523     goto out;
1524   }
1525
1526   channel = (SilcChannelEntry)id_cache->context;
1527
1528   if (!cmd->pending) {
1529     /* Send USERS command to the server */
1530     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1531     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1, 
1532                                             1, idp->data, idp->len);
1533     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1534                             NULL, 0, NULL, NULL, buffer->data, 
1535                             buffer->len, TRUE);
1536     silc_buffer_free(buffer);
1537     silc_buffer_free(idp);
1538
1539     /* Register pending callback which will recall this command callback with
1540        same context and reprocesses the command. When reprocessing we actually
1541        display the information on the screen. */
1542     silc_client_command_pending(conn, SILC_COMMAND_USERS, 0, 
1543                                 silc_client_command_destructor,
1544                                 silc_client_command_users, 
1545                                 silc_client_command_dup(cmd));
1546     cmd->pending = TRUE;
1547     return;
1548   }
1549
1550   if (cmd->pending) {
1551     /* Pending command. Now we've resolved the information from server and
1552        we are ready to display the information on screen. */
1553     int i;
1554     SilcChannelUser chu;
1555
1556     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1557                           channel->channel_name);
1558
1559     line = silc_calloc(4096, sizeof(*line));
1560     line_len = 4096;
1561     silc_list_start(channel->clients);
1562     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1563       SilcClientEntry e = chu->client;
1564       char *m, tmp[80], len1;
1565
1566       memset(line, 0, sizeof(line_len));
1567
1568       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1569         silc_free(line);
1570         line_len += strlen(e->nickname) + strlen(e->server) + 100;
1571         line = silc_calloc(line_len, sizeof(*line));
1572       }
1573
1574       memset(tmp, 0, sizeof(tmp));
1575       m = silc_client_chumode_char(chu->mode);
1576
1577       strncat(line, " ", 1);
1578       strncat(line, e->nickname, strlen(e->nickname));
1579       strncat(line, e->server ? "@" : "", 1);
1580
1581       len1 = 0;
1582       if (e->server)
1583         len1 = strlen(e->server);
1584       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1585
1586       len1 = strlen(line);
1587       if (len1 >= 30) {
1588         memset(&line[29], 0, len1 - 29);
1589       } else {
1590         for (i = 0; i < 30 - len1 - 1; i++)
1591           strcat(line, " ");
1592       }
1593
1594       strncat(line, "  H", 3);
1595       strcat(tmp, m ? m : "");
1596       strncat(line, tmp, strlen(tmp));
1597
1598       if (strlen(tmp) < 5)
1599         for (i = 0; i < 5 - strlen(tmp); i++)
1600           strcat(line, " ");
1601
1602       strcat(line, e->username ? e->username : "");
1603
1604       cmd->client->ops->say(cmd->client, conn, "%s", line);
1605
1606       if (m)
1607         silc_free(m);
1608     }
1609   }
1610
1611   if (line)
1612     silc_free(line);
1613
1614   /* Notify application */
1615   COMMAND;
1616
1617  out:
1618   silc_client_command_free(cmd);
1619 }