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