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
1237   if (!cmd->conn) {
1238     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1239     COMMAND_ERROR;
1240     goto out;
1241   }
1242
1243   if (cmd->argc < 2) {
1244     cmd->client->ops->say(cmd->client, conn, 
1245                           "Usage: /CONNECT <server> [<port>]");
1246     COMMAND_ERROR;
1247     goto out;
1248   }
1249
1250   if (cmd->argc == 3)
1251     SILC_PUT32_MSB(atoi(cmd->argv[2]), port);
1252
1253   if (cmd->argc == 3)
1254     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1255                                             1, cmd->argv[1], 
1256                                             strlen(cmd->argv[1]),
1257                                             2, port, 4);
1258   else
1259     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1260                                             1, cmd->argv[1], 
1261                                             strlen(cmd->argv[1]));
1262   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1263                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1264   silc_buffer_free(buffer);
1265
1266   /* Notify application */
1267   COMMAND;
1268
1269  out:
1270   silc_client_command_free(cmd);
1271 }
1272
1273 SILC_CLIENT_CMD_FUNC(restart)
1274 {
1275 }
1276
1277 /* CLOSE command. Close server connection to the remote server */
1278  
1279 SILC_CLIENT_CMD_FUNC(close)
1280 {
1281   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1282   SilcClientConnection conn = cmd->conn;
1283   SilcBuffer buffer;
1284   unsigned char port[4];
1285
1286   if (!cmd->conn) {
1287     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1288     COMMAND_ERROR;
1289     goto out;
1290   }
1291
1292   if (cmd->argc < 2) {
1293     cmd->client->ops->say(cmd->client, conn, 
1294                           "Usage: /CLOSE <server> [<port>]");
1295     COMMAND_ERROR;
1296     goto out;
1297   }
1298
1299   if (cmd->argc == 3)
1300     SILC_PUT32_MSB(atoi(cmd->argv[2]), port);
1301
1302   if (cmd->argc == 3)
1303     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1304                                             1, cmd->argv[1], 
1305                                             strlen(cmd->argv[1]),
1306                                             2, port, 4);
1307   else
1308     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1309                                             1, cmd->argv[1], 
1310                                             strlen(cmd->argv[1]));
1311   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1312                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1313   silc_buffer_free(buffer);
1314
1315   /* Notify application */
1316   COMMAND;
1317
1318  out:
1319   silc_client_command_free(cmd);
1320 }
1321  
1322 /* SHUTDOWN command. Shutdowns the server. */
1323
1324 SILC_CLIENT_CMD_FUNC(shutdown)
1325 {
1326   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1327
1328   if (!cmd->conn) {
1329     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1330     COMMAND_ERROR;
1331     goto out;
1332   }
1333
1334   /* Send the command */
1335   silc_client_send_command(cmd->client, cmd->conn, 
1336                            SILC_COMMAND_SHUTDOWN, 0, 0);
1337
1338   /* Notify application */
1339   COMMAND;
1340
1341  out:
1342   silc_client_command_free(cmd);
1343 }
1344  
1345 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1346
1347 SILC_CLIENT_CMD_FUNC(leave)
1348 {
1349   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1350   SilcClientConnection conn = cmd->conn;
1351   SilcIDCacheEntry id_cache = NULL;
1352   SilcChannelEntry channel;
1353   SilcBuffer buffer, idp;
1354   char *name;
1355
1356   if (!cmd->conn) {
1357     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1358     COMMAND_ERROR;
1359     goto out;
1360   }
1361
1362   if (cmd->argc != 2) {
1363     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1364     COMMAND_ERROR;
1365     goto out;
1366   }
1367
1368   if (cmd->argv[1][0] == '*') {
1369     if (!conn->current_channel) {
1370       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1371       COMMAND_ERROR;
1372       goto out;
1373     }
1374     name = conn->current_channel->channel_name;
1375   } else {
1376     name = cmd->argv[1];
1377   }
1378
1379   if (!conn->current_channel) {
1380     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1381     COMMAND_ERROR;
1382     goto out;
1383   }
1384
1385   /* Get the Channel ID of the channel */
1386   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1387     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1388     COMMAND_ERROR;
1389     goto out;
1390   }
1391
1392   channel = (SilcChannelEntry)id_cache->context;
1393
1394   /* Send LEAVE command to the server */
1395   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1396   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1397                                           1, idp->data, idp->len);
1398   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1399                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1400   silc_buffer_free(buffer);
1401   silc_buffer_free(idp);
1402
1403   /* We won't talk anymore on this channel */
1404   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1405
1406   conn->current_channel = NULL;
1407
1408   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1409   silc_free(channel->channel_name);
1410   silc_free(channel->id);
1411   silc_free(channel->key);
1412   silc_cipher_free(channel->channel_key);
1413   silc_free(channel);
1414
1415   /* Notify application */
1416   COMMAND;
1417
1418  out:
1419   silc_client_command_free(cmd);
1420 }
1421
1422 /* Command USERS. Requests the USERS of the clients joined on requested
1423    channel. */
1424
1425 SILC_CLIENT_CMD_FUNC(users)
1426 {
1427   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1428   SilcClientConnection conn = cmd->conn;
1429   SilcIDCacheEntry id_cache = NULL;
1430   SilcChannelEntry channel;
1431   SilcBuffer buffer, idp;
1432   char *name, *line = NULL;
1433   unsigned int line_len = 0;
1434
1435   if (!cmd->conn) {
1436     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1437     COMMAND_ERROR;
1438     goto out;
1439   }
1440
1441   if (cmd->argc != 2) {
1442     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1443     COMMAND_ERROR;
1444     goto out;
1445   }
1446
1447   if (cmd->argv[1][0] == '*') {
1448     if (!conn->current_channel) {
1449       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1450       COMMAND_ERROR;
1451       goto out;
1452     }
1453     name = conn->current_channel->channel_name;
1454   } else {
1455     name = cmd->argv[1];
1456   }
1457
1458   if (!conn->current_channel) {
1459     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1460     COMMAND_ERROR;
1461     goto out;
1462   }
1463
1464   /* Get the Channel ID of the channel */
1465   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1466     /* XXX should resolve the channel ID; LIST command */
1467     cmd->client->ops->say(cmd->client, conn, 
1468                           "You are not on that channel", name);
1469     COMMAND_ERROR;
1470     goto out;
1471   }
1472
1473   channel = (SilcChannelEntry)id_cache->context;
1474
1475   if (!cmd->pending) {
1476     /* Send USERS command to the server */
1477     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1478     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1, 
1479                                             1, idp->data, idp->len);
1480     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1481                             NULL, 0, NULL, NULL, buffer->data, 
1482                             buffer->len, TRUE);
1483     silc_buffer_free(buffer);
1484     silc_buffer_free(idp);
1485
1486     /* Register pending callback which will recall this command callback with
1487        same context and reprocesses the command. When reprocessing we actually
1488        display the information on the screen. */
1489     silc_client_command_pending(conn, SILC_COMMAND_USERS, 0, 
1490                                 silc_client_command_destructor,
1491                                 silc_client_command_users, 
1492                                 silc_client_command_dup(cmd));
1493     cmd->pending = TRUE;
1494     return;
1495   }
1496
1497   if (cmd->pending) {
1498     /* Pending command. Now we've resolved the information from server and
1499        we are ready to display the information on screen. */
1500     int i;
1501     SilcChannelUser chu;
1502
1503     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1504                           channel->channel_name);
1505
1506     line = silc_calloc(4096, sizeof(*line));
1507     line_len = 4096;
1508     silc_list_start(channel->clients);
1509     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1510       SilcClientEntry e = chu->client;
1511       char *m, tmp[80], len1;
1512
1513       memset(line, 0, sizeof(line_len));
1514
1515       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1516         silc_free(line);
1517         line_len += strlen(e->nickname) + strlen(e->server) + 100;
1518         line = silc_calloc(line_len, sizeof(*line));
1519       }
1520
1521       memset(tmp, 0, sizeof(tmp));
1522       m = silc_client_chumode_char(chu->mode);
1523
1524       strncat(line, " ", 1);
1525       strncat(line, e->nickname, strlen(e->nickname));
1526       strncat(line, e->server ? "@" : "", 1);
1527
1528       len1 = 0;
1529       if (e->server)
1530         len1 = strlen(e->server);
1531       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1532
1533       len1 = strlen(line);
1534       if (len1 >= 30) {
1535         memset(&line[29], 0, len1 - 29);
1536       } else {
1537         for (i = 0; i < 30 - len1 - 1; i++)
1538           strcat(line, " ");
1539       }
1540
1541       strncat(line, "  H", 3);
1542       strcat(tmp, m ? m : "");
1543       strncat(line, tmp, strlen(tmp));
1544
1545       if (strlen(tmp) < 5)
1546         for (i = 0; i < 5 - strlen(tmp); i++)
1547           strcat(line, " ");
1548
1549       strcat(line, e->username ? e->username : "");
1550
1551       cmd->client->ops->say(cmd->client, conn, "%s", line);
1552
1553       if (m)
1554         silc_free(m);
1555     }
1556   }
1557
1558   if (line)
1559     silc_free(line);
1560
1561   /* Notify application */
1562   COMMAND;
1563
1564  out:
1565   silc_client_command_free(cmd);
1566 }