implemented KICK command
[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, 2),
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(die, DIE, "DIE",
55                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
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 SILC_CLIENT_CMD_FUNC(connect)
621 {
622 }
623
624 /* Command PING. Sends ping to server. This is used to test the 
625    communication channel. */
626
627 SILC_CLIENT_CMD_FUNC(ping)
628 {
629   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
630   SilcClientConnection conn = cmd->conn;
631   SilcBuffer buffer;
632   void *id;
633   int i;
634   char *name = NULL;
635
636   if (!cmd->conn) {
637     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
638     COMMAND_ERROR;
639     goto out;
640   }
641
642   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
643     name = strdup(conn->remote_host);
644
645   /* Send the command */
646   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
647                                           1, conn->remote_id_data, 
648                                           SILC_ID_SERVER_LEN);
649   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
650                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
651   silc_buffer_free(buffer);
652
653   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
654                       SILC_ID_SERVER);
655   if (!id) {
656     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
657     COMMAND_ERROR;
658     goto out;
659   }
660
661   /* Start counting time */
662   for (i = 0; i < conn->ping_count; i++) {
663     if (conn->ping[i].dest_id == NULL) {
664       conn->ping[i].start_time = time(NULL);
665       conn->ping[i].dest_id = id;
666       conn->ping[i].dest_name = name;
667       conn->ping_count++;
668       break;
669     }
670   }
671   if (i >= conn->ping_count) {
672     i = conn->ping_count;
673     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
674     conn->ping[i].start_time = time(NULL);
675     conn->ping[i].dest_id = id;
676     conn->ping[i].dest_name = name;
677     conn->ping_count++;
678   }
679   
680   /* Notify application */
681   COMMAND;
682
683  out:
684   silc_client_command_free(cmd);
685 }
686
687 SILC_CLIENT_CMD_FUNC(oper)
688 {
689 }
690
691 SILC_CLIENT_CMD_FUNC(trace)
692 {
693 }
694
695 SILC_CLIENT_CMD_FUNC(notice)
696 {
697 }
698
699 /* Command JOIN. Joins to a channel. */
700
701 SILC_CLIENT_CMD_FUNC(join)
702 {
703   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
704   SilcClientConnection conn = cmd->conn;
705   SilcIDCacheEntry id_cache = NULL;
706   SilcBuffer buffer, idp;
707
708   if (!cmd->conn) {
709     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
710     COMMAND_ERROR;
711     goto out;
712   }
713
714   if (cmd->argc < 2) {
715     /* Show channels currently joined to */
716
717     goto out;
718   }
719
720   /* See if we have joined to the requested channel already */
721   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
722                                     &id_cache)) {
723     cmd->client->ops->say(cmd->client, conn, 
724                           "You are talking to channel %s", cmd->argv[1]);
725     conn->current_channel = (SilcChannelEntry)id_cache->context;
726 #if 0
727     cmd->client->screen->bottom_line->channel = cmd->argv[1];
728     silc_screen_print_bottom_line(cmd->client->screen, 0);
729 #endif
730     goto out;
731   }
732
733   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
734
735   /* Send JOIN command to the server */
736   if (cmd->argc == 2)
737     buffer = 
738       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
739                                      1, cmd->argv[1], cmd->argv_lens[1],
740                                      2, idp->data, idp->len);
741   else if (cmd->argc == 3)
742     /* XXX Buggy */
743     buffer = 
744       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
745                                      1, cmd->argv[1], cmd->argv_lens[1],
746                                      2, idp->data, idp->len,
747                                      3, cmd->argv[2], cmd->argv_lens[2]);
748   else
749     buffer = 
750       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
751                                      1, cmd->argv[1], cmd->argv_lens[1],
752                                      2, idp->data, idp->len,
753                                      3, cmd->argv[2], cmd->argv_lens[2],
754                                      4, cmd->argv[3], cmd->argv_lens[3]);
755
756   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
757                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
758   silc_buffer_free(buffer);
759   silc_buffer_free(idp);
760
761   /* Notify application */
762   COMMAND;
763
764  out:
765   silc_client_command_free(cmd);
766 }
767
768 /* MOTD command. Requests motd from server. */
769
770 SILC_CLIENT_CMD_FUNC(motd)
771 {
772   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
773   SilcClientConnection conn = cmd->conn;
774   SilcBuffer buffer;
775
776   if (!cmd->conn) {
777     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
778     COMMAND_ERROR;
779     goto out;
780   }
781
782   if (cmd->argc < 1 || cmd->argc > 1) {
783     cmd->client->ops->say(cmd->client, conn,
784                           "Usage: /MOTD");
785     COMMAND_ERROR;
786     goto out;
787   }
788
789   /* Send TOPIC command to the server */
790   buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
791                                           2, conn->remote_host, 
792                                           strlen(conn->remote_host));
793   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
794                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
795   silc_buffer_free(buffer);
796
797   /* Notify application */
798   COMMAND;
799
800  out:
801   silc_client_command_free(cmd);
802 }
803
804 /* UMODE. Set user mode in SILC. */
805
806 SILC_CLIENT_CMD_FUNC(umode)
807 {
808
809 }
810
811 /* CMODE command. Sets channel mode. Modes that does not require any arguments
812    can be set several at once. Those modes that require argument must be set
813    separately (unless set with modes that does not require arguments). */
814
815 SILC_CLIENT_CMD_FUNC(cmode)
816 {
817   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
818   SilcClientConnection conn = cmd->conn;
819   SilcChannelEntry channel;
820   SilcBuffer buffer, chidp;
821   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
822   unsigned int mode, add, type, len, arg_len = 0;
823   int i;
824
825   if (!cmd->conn) {
826     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
827     COMMAND_ERROR;
828     goto out;
829   }
830
831   if (cmd->argc < 3) {
832     cmd->client->ops->say(cmd->client, conn, 
833                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
834     COMMAND_ERROR;
835     goto out;
836   }
837
838   if (cmd->argv[1][0] == '*') {
839     if (!conn->current_channel) {
840       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
841       COMMAND_ERROR;
842       goto out;
843     }
844
845     channel = conn->current_channel;
846   } else {
847     name = cmd->argv[1];
848
849     channel = silc_idlist_get_channel(cmd->client, conn, name);
850     if (!channel) {
851       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
852       COMMAND_ERROR;
853       goto out;
854     }
855   }
856
857   mode = channel->mode;
858
859   /* Are we adding or removing mode */
860   if (cmd->argv[2][0] == '-')
861     add = FALSE;
862   else
863     add = TRUE;
864
865   /* Argument type to be sent to server */
866   type = 0;
867
868   /* Parse mode */
869   cp = cmd->argv[2] + 1;
870   len = strlen(cp);
871   for (i = 0; i < len; i++) {
872     switch(cp[i]) {
873     case 'p':
874       if (add)
875         mode |= SILC_CHANNEL_MODE_PRIVATE;
876       else
877         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
878       break;
879     case 's':
880       if (add)
881         mode |= SILC_CHANNEL_MODE_SECRET;
882       else
883         mode &= ~SILC_CHANNEL_MODE_SECRET;
884       break;
885     case 'k':
886       if (add)
887         mode |= SILC_CHANNEL_MODE_PRIVKEY;
888       else
889         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
890       break;
891     case 'i':
892       if (add)
893         mode |= SILC_CHANNEL_MODE_INVITE;
894       else
895         mode &= ~SILC_CHANNEL_MODE_INVITE;
896       break;
897     case 't':
898       if (add)
899         mode |= SILC_CHANNEL_MODE_TOPIC;
900       else
901         mode &= ~SILC_CHANNEL_MODE_TOPIC;
902       break;
903     case 'l':
904       if (add) {
905         int ll;
906         mode |= SILC_CHANNEL_MODE_ULIMIT;
907         type = 3;
908         ll = atoi(cmd->argv[3]);
909         SILC_PUT32_MSB(ll, tmp);
910         arg = tmp;
911         arg_len = 4;
912       } else {
913         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
914       }
915       break;
916     case 'a':
917       if (add) {
918         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
919         type = 4;
920         arg = cmd->argv[3];
921         arg_len = cmd->argv_lens[3];
922       } else {
923         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
924       }
925       break;
926     case 'b':
927       if (add) {
928         mode |= SILC_CHANNEL_MODE_BAN;
929         type = 5;
930         arg = cmd->argv[3];
931         arg_len = cmd->argv_lens[3];
932       } else {
933         mode &= ~SILC_CHANNEL_MODE_BAN;
934       }
935       break;
936     case 'I':
937       if (add) {
938         mode |= SILC_CHANNEL_MODE_INVITE_LIST;
939         type = 6;
940         arg = cmd->argv[3];
941         arg_len = cmd->argv_lens[3];
942       } else {
943         mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
944       }
945       break;
946     case 'c':
947       if (add) {
948         mode |= SILC_CHANNEL_MODE_CIPHER;
949         type = 8;
950         arg = cmd->argv[3];
951         arg_len = cmd->argv_lens[3];
952       } else {
953         mode &= ~SILC_CHANNEL_MODE_CIPHER;
954       }
955       break;
956     default:
957       COMMAND_ERROR;
958       goto out;
959       break;
960     }
961   }
962
963   if (type && cmd->argc < 3) {
964     COMMAND_ERROR;
965     goto out;
966   }
967
968   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
969   SILC_PUT32_MSB(mode, modebuf);
970
971   /* Send the command packet. We support sending only one mode at once
972      that requires an argument. */
973   if (type && arg) {
974     buffer = 
975       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
976                                      1, chidp->data, chidp->len, 
977                                      2, modebuf, sizeof(modebuf),
978                                      type, arg, arg_len);
979   } else {
980     buffer = 
981       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
982                                      1, chidp->data, chidp->len, 
983                                      2, modebuf, sizeof(modebuf));
984   }
985
986   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
987                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
988   silc_buffer_free(buffer);
989   silc_buffer_free(chidp);
990
991   /* Notify application */
992   COMMAND;
993
994  out:
995   silc_client_command_free(cmd);
996 }
997
998 /* CUMODE command. Changes client's mode on a channel. */
999
1000 SILC_CLIENT_CMD_FUNC(cumode)
1001 {
1002   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1003   SilcClientConnection conn = cmd->conn;
1004   SilcChannelEntry channel;
1005   SilcChannelUser chu;
1006   SilcClientEntry client_entry;
1007   SilcBuffer buffer, clidp, chidp;
1008   unsigned char *name, *cp, modebuf[4];
1009   unsigned int mode = 0, add, len;
1010   char *nickname = NULL, *server = NULL;
1011   unsigned int num = 0;
1012   int i;
1013
1014   if (!cmd->conn) {
1015     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1016     COMMAND_ERROR;
1017     goto out;
1018   }
1019
1020   if (cmd->argc < 4) {
1021     cmd->client->ops->say(cmd->client, conn, 
1022                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1023     COMMAND_ERROR;
1024     goto out;
1025   }
1026
1027   if (cmd->argv[1][0] == '*') {
1028     if (!conn->current_channel) {
1029       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1030       COMMAND_ERROR;
1031       goto out;
1032     }
1033
1034     channel = conn->current_channel;
1035   } else {
1036     name = cmd->argv[1];
1037
1038     channel = silc_idlist_get_channel(cmd->client, conn, name);
1039     if (!channel) {
1040       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1041       COMMAND_ERROR;
1042       goto out;
1043     }
1044   }
1045
1046   /* Parse the typed nickname. */
1047   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1048     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1049     COMMAND_ERROR;
1050     goto out;
1051   }
1052
1053   /* Find client entry */
1054   client_entry = silc_idlist_get_client(cmd->client, conn, 
1055                                         nickname, server, num, TRUE);
1056   if (!client_entry) {
1057     /* Client entry not found, it was requested thus mark this to be
1058        pending command. */
1059     silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,  
1060                                 silc_client_command_destructor,
1061                                 silc_client_command_cumode, 
1062                                 silc_client_command_dup(cmd));
1063     cmd->pending = 1;
1064     return;
1065   }
1066   
1067   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1068     if (chu->client == client_entry) {
1069       chu->mode = mode;
1070       break;
1071     }
1072   }
1073
1074   /* Are we adding or removing mode */
1075   if (cmd->argv[2][0] == '-')
1076     add = FALSE;
1077   else
1078     add = TRUE;
1079
1080   /* Parse mode */
1081   cp = cmd->argv[2] + 1;
1082   len = strlen(cp);
1083   for (i = 0; i < len; i++) {
1084     switch(cp[i]) {
1085     case 'a':
1086       if (add) {
1087         mode |= SILC_CHANNEL_UMODE_CHANFO;
1088         mode |= SILC_CHANNEL_UMODE_CHANOP;
1089       } else {
1090         mode = SILC_CHANNEL_UMODE_NONE;
1091       }
1092       break;
1093     case 'f':
1094       if (add)
1095         mode |= SILC_CHANNEL_UMODE_CHANFO;
1096       else
1097         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1098       break;
1099     case 'o':
1100       if (add)
1101         mode |= SILC_CHANNEL_UMODE_CHANOP;
1102       else
1103         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1104       break;
1105     default:
1106       COMMAND_ERROR;
1107       goto out;
1108       break;
1109     }
1110   }
1111
1112   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1113   SILC_PUT32_MSB(mode, modebuf);
1114   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1115
1116   /* Send the command packet. We support sending only one mode at once
1117      that requires an argument. */
1118   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1119                                           1, chidp->data, chidp->len, 
1120                                           2, modebuf, 4,
1121                                           3, clidp->data, clidp->len);
1122
1123   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1124                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1125   silc_buffer_free(buffer);
1126   silc_buffer_free(chidp);
1127   silc_buffer_free(clidp);
1128   
1129   /* Notify application */
1130   COMMAND;
1131
1132  out:
1133   silc_client_command_free(cmd);
1134 }
1135
1136 /* KICK command. Kicks a client out of channel. */
1137
1138 SILC_CLIENT_CMD_FUNC(kick)
1139 {
1140   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1141   SilcClientConnection conn = cmd->conn;
1142   SilcIDCacheEntry id_cache = NULL;
1143   SilcChannelEntry channel;
1144   SilcBuffer buffer, idp, idp2;
1145   SilcClientEntry target;
1146   char *name;
1147   unsigned int num = 0;
1148   char *nickname = NULL, *server = NULL;
1149
1150   if (!cmd->conn) {
1151     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1152     COMMAND_ERROR;
1153     goto out;
1154   }
1155
1156   if (cmd->argc < 3) {
1157     cmd->client->ops->say(cmd->client, conn, 
1158                           "Usage: /KICK <channel> <client> [<comment>]");
1159     COMMAND_ERROR;
1160     goto out;
1161   }
1162
1163   if (cmd->argv[1][0] == '*') {
1164     if (!conn->current_channel) {
1165       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1166       COMMAND_ERROR;
1167       goto out;
1168     }
1169     name = conn->current_channel->channel_name;
1170   } else {
1171     name = cmd->argv[1];
1172   }
1173
1174   if (!conn->current_channel) {
1175     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1176     COMMAND_ERROR;
1177     goto out;
1178   }
1179
1180   /* Get the Channel ID of the channel */
1181   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1182     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1183     COMMAND_ERROR;
1184     goto out;
1185   }
1186
1187   channel = (SilcChannelEntry)id_cache->context;
1188
1189   /* Parse the typed nickname. */
1190   if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1191     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1192     COMMAND_ERROR;
1193     goto out;
1194   }
1195
1196   /* Get the target client */
1197   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1198                                   server, num, FALSE);
1199   if (!target) {
1200     cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1201                           cmd->argv[2]);
1202     COMMAND_ERROR;
1203     goto out;
1204   }
1205
1206   /* Send KICK command to the server */
1207   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1208   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1209   if (cmd->argc == 3)
1210     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1211                                             1, idp->data, idp->len,
1212                                             2, idp2->data, idp2->len);
1213   else
1214     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1215                                             1, idp->data, idp->len,
1216                                             2, idp2->data, idp2->len,
1217                                             3, cmd->argv[3], 
1218                                             strlen(cmd->argv[3]));
1219   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1220                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1221   silc_buffer_free(buffer);
1222   silc_buffer_free(idp);
1223   silc_buffer_free(idp2);
1224
1225   /* Notify application */
1226   COMMAND;
1227
1228  out:
1229   silc_client_command_free(cmd);
1230 }
1231
1232 SILC_CLIENT_CMD_FUNC(restart)
1233 {
1234 }
1235  
1236 SILC_CLIENT_CMD_FUNC(close)
1237 {
1238 }
1239  
1240 SILC_CLIENT_CMD_FUNC(die)
1241 {
1242 }
1243  
1244 SILC_CLIENT_CMD_FUNC(silcoper)
1245 {
1246 }
1247
1248 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1249
1250 SILC_CLIENT_CMD_FUNC(leave)
1251 {
1252   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1253   SilcClientConnection conn = cmd->conn;
1254   SilcIDCacheEntry id_cache = NULL;
1255   SilcChannelEntry channel;
1256   SilcBuffer buffer, idp;
1257   char *name;
1258
1259   if (!cmd->conn) {
1260     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1261     COMMAND_ERROR;
1262     goto out;
1263   }
1264
1265   if (cmd->argc != 2) {
1266     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1267     COMMAND_ERROR;
1268     goto out;
1269   }
1270
1271   if (cmd->argv[1][0] == '*') {
1272     if (!conn->current_channel) {
1273       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1274       COMMAND_ERROR;
1275       goto out;
1276     }
1277     name = conn->current_channel->channel_name;
1278   } else {
1279     name = cmd->argv[1];
1280   }
1281
1282   if (!conn->current_channel) {
1283     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1284     COMMAND_ERROR;
1285     goto out;
1286   }
1287
1288   /* Get the Channel ID of the channel */
1289   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1290     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1291     COMMAND_ERROR;
1292     goto out;
1293   }
1294
1295   channel = (SilcChannelEntry)id_cache->context;
1296
1297   /* Send LEAVE command to the server */
1298   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1299   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1300                                           1, idp->data, idp->len);
1301   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1302                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1303   silc_buffer_free(buffer);
1304   silc_buffer_free(idp);
1305
1306   /* We won't talk anymore on this channel */
1307   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1308
1309   conn->current_channel = NULL;
1310
1311   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1312   silc_free(channel->channel_name);
1313   silc_free(channel->id);
1314   silc_free(channel->key);
1315   silc_cipher_free(channel->channel_key);
1316   silc_free(channel);
1317
1318   /* Notify application */
1319   COMMAND;
1320
1321  out:
1322   silc_client_command_free(cmd);
1323 }
1324
1325 /* Command USERS. Requests the USERS of the clients joined on requested
1326    channel. */
1327
1328 SILC_CLIENT_CMD_FUNC(users)
1329 {
1330   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1331   SilcClientConnection conn = cmd->conn;
1332   SilcIDCacheEntry id_cache = NULL;
1333   SilcChannelEntry channel;
1334   SilcBuffer buffer, idp;
1335   char *name, *line = NULL;
1336   unsigned int line_len = 0;
1337
1338   if (!cmd->conn) {
1339     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1340     COMMAND_ERROR;
1341     goto out;
1342   }
1343
1344   if (cmd->argc != 2) {
1345     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1346     COMMAND_ERROR;
1347     goto out;
1348   }
1349
1350   if (cmd->argv[1][0] == '*') {
1351     if (!conn->current_channel) {
1352       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1353       COMMAND_ERROR;
1354       goto out;
1355     }
1356     name = conn->current_channel->channel_name;
1357   } else {
1358     name = cmd->argv[1];
1359   }
1360
1361   if (!conn->current_channel) {
1362     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1363     COMMAND_ERROR;
1364     goto out;
1365   }
1366
1367   /* Get the Channel ID of the channel */
1368   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1369     /* XXX should resolve the channel ID; LIST command */
1370     cmd->client->ops->say(cmd->client, conn, 
1371                           "You are not on that channel", name);
1372     COMMAND_ERROR;
1373     goto out;
1374   }
1375
1376   channel = (SilcChannelEntry)id_cache->context;
1377
1378   if (!cmd->pending) {
1379     /* Send USERS command to the server */
1380     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1381     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1, 
1382                                             1, idp->data, idp->len);
1383     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1384                             NULL, 0, NULL, NULL, buffer->data, 
1385                             buffer->len, TRUE);
1386     silc_buffer_free(buffer);
1387     silc_buffer_free(idp);
1388
1389     /* Register pending callback which will recall this command callback with
1390        same context and reprocesses the command. When reprocessing we actually
1391        display the information on the screen. */
1392     silc_client_command_pending(conn, SILC_COMMAND_USERS, 0, 
1393                                 silc_client_command_destructor,
1394                                 silc_client_command_users, 
1395                                 silc_client_command_dup(cmd));
1396     cmd->pending = TRUE;
1397     return;
1398   }
1399
1400   if (cmd->pending) {
1401     /* Pending command. Now we've resolved the information from server and
1402        we are ready to display the information on screen. */
1403     int i;
1404     SilcChannelUser chu;
1405
1406     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1407                           channel->channel_name);
1408
1409     line = silc_calloc(4096, sizeof(*line));
1410     line_len = 4096;
1411     silc_list_start(channel->clients);
1412     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1413       SilcClientEntry e = chu->client;
1414       char *m, tmp[80], len1;
1415
1416       memset(line, 0, sizeof(line_len));
1417
1418       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1419         silc_free(line);
1420         line_len += strlen(e->nickname) + strlen(e->server) + 100;
1421         line = silc_calloc(line_len, sizeof(*line));
1422       }
1423
1424       memset(tmp, 0, sizeof(tmp));
1425       m = silc_client_chumode_char(chu->mode);
1426
1427       strncat(line, " ", 1);
1428       strncat(line, e->nickname, strlen(e->nickname));
1429       strncat(line, e->server ? "@" : "", 1);
1430
1431       len1 = 0;
1432       if (e->server)
1433         len1 = strlen(e->server);
1434       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1435
1436       len1 = strlen(line);
1437       if (len1 >= 30) {
1438         memset(&line[29], 0, len1 - 29);
1439       } else {
1440         for (i = 0; i < 30 - len1 - 1; i++)
1441           strcat(line, " ");
1442       }
1443
1444       strncat(line, "  H", 3);
1445       strcat(tmp, m ? m : "");
1446       strncat(line, tmp, strlen(tmp));
1447
1448       if (strlen(tmp) < 5)
1449         for (i = 0; i < 5 - strlen(tmp); i++)
1450           strcat(line, " ");
1451
1452       strcat(line, e->username ? e->username : "");
1453
1454       cmd->client->ops->say(cmd->client, conn, "%s", line);
1455
1456       if (m)
1457         silc_free(m);
1458     }
1459   }
1460
1461   if (line)
1462     silc_free(line);
1463
1464   /* Notify application */
1465   COMMAND;
1466
1467  out:
1468   silc_client_command_free(cmd);
1469 }