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