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