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