A LOT updates. Cannot separate. :)
[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, 1),
36   SILC_CLIENT_CMD(kill, KILL, "KILL", 
37                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
38   SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
39   SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
40                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
41   SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
42   SILC_CLIENT_CMD(oper, OPER, "OPER",
43                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
44   SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 4),
45   SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
46   SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
47   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
48   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
49   SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
50   SILC_CLIENT_CMD(restart, RESTART, "RESTART",
51                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
52   SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
53                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
54   SILC_CLIENT_CMD(die, DIE, "DIE",
55                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
56   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
57                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
58   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
59   SILC_CLIENT_CMD(names, NAMES, "NAMES", 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 /* List of pending commands. */
78 SilcClientCommandPending *silc_command_pending = NULL;
79
80 /* Generic function to send any command. The arguments must be sent already
81    encoded into correct form in correct order. */
82
83 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
84                               SilcCommand command, unsigned int argc, ...)
85 {
86   SilcBuffer packet;
87   va_list ap;
88
89   va_start(ap, argc);
90
91   packet = silc_command_payload_encode_vap(command, 0, argc, ap);
92   silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
93                           NULL, 0, NULL, NULL, packet->data, 
94                           packet->len, TRUE);
95   silc_buffer_free(packet);
96 }
97
98 /* Finds and returns a pointer to the command list. Return NULL if the
99    command is not found. */
100
101 SilcClientCommand *silc_client_command_find(const char *name)
102 {
103   SilcClientCommand *cmd;
104
105   for (cmd = silc_command_list; cmd->name; cmd++) {
106     if (!strcmp(cmd->name, name))
107       return cmd;
108   }
109
110   return NULL;
111 }
112
113 /* Add new pending command to the list of pending commands. Currently
114    pending commands are executed from command replies, thus we can
115    execute any command after receiving some specific command reply.
116
117    The argument `reply_cmd' is the command reply from where the callback
118    function is to be called, thus, it IS NOT the command to be executed.
119
120    XXX: If needed in the future this support may be extended for
121    commands as well, when any command could be executed after executing
122    some specific command. */
123
124 void silc_client_command_pending(SilcCommand reply_cmd,
125                                  SilcClientCommandCallback callback,
126                                  void *context)
127 {
128   SilcClientCommandPending *reply, *r;
129
130   reply = silc_calloc(1, sizeof(*reply));
131   reply->reply_cmd = reply_cmd;
132   reply->context = context;
133   reply->callback = callback;
134
135   if (silc_command_pending == NULL) {
136     silc_command_pending = reply;
137     return;
138   }
139
140   for (r = silc_command_pending; r; r = r->next) {
141     if (r->next == NULL) {
142       r->next = reply;
143       break;
144     }
145   }
146 }
147
148 /* Deletes pending command by reply command type. */
149
150 void silc_client_command_pending_del(SilcCommand reply_cmd)
151 {
152   SilcClientCommandPending *r, *tmp;
153   
154   if (silc_command_pending) {
155     if (silc_command_pending->reply_cmd == reply_cmd) {
156       silc_free(silc_command_pending);
157       silc_command_pending = NULL;
158       return;
159     }
160
161     for (r = silc_command_pending; r; r = r->next) {
162       if (r->next && r->next->reply_cmd == reply_cmd) {
163         tmp = r->next;
164         r->next = r->next->next;
165         silc_free(tmp);
166         break;
167       }
168     }
169   }
170 }
171
172 /* Free command context and its internals */
173
174 void silc_client_command_free(SilcClientCommandContext cmd)
175 {
176   int i;
177
178   if (cmd) {
179     for (i = 0; i < cmd->argc; i++)
180       silc_free(cmd->argv[i]);
181     silc_free(cmd);
182   }
183 }
184
185 /* Command WHOIS. This command is used to query information about 
186    specific user. */
187
188 SILC_CLIENT_CMD_FUNC(whois)
189 {
190   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
191   SilcClientConnection conn = cmd->conn;
192   SilcBuffer buffer;
193
194   if (!cmd->conn) {
195     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
196     COMMAND_ERROR;
197     goto out;
198   }
199
200   if (cmd->argc < 2 || cmd->argc > 3) {
201     cmd->client->ops->say(cmd->client, conn, 
202              "Usage: /WHOIS <nickname>[@<server>] [<count>]");
203     COMMAND_ERROR;
204     goto out;
205   }
206
207   buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
208                                        cmd->argc - 1, ++cmd->argv,
209                                        ++cmd->argv_lens, ++cmd->argv_types,
210                                        0);
211   silc_client_packet_send(cmd->client, cmd->conn->sock,
212                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
213                           buffer->data, buffer->len, TRUE);
214   silc_buffer_free(buffer);
215   cmd->argv--;
216   cmd->argv_lens--;
217   cmd->argv_types--;
218
219   /* Notify application */
220   COMMAND;
221
222  out:
223   silc_client_command_free(cmd);
224 }
225
226 SILC_CLIENT_CMD_FUNC(whowas)
227 {
228 }
229
230 /* Command IDENTIFY. This command is used to query information about 
231    specific user, especially ID's. */
232
233 SILC_CLIENT_CMD_FUNC(identify)
234 {
235   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
236   SilcClientConnection conn = cmd->conn;
237   SilcBuffer buffer;
238
239   if (!cmd->conn) {
240     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
241     COMMAND_ERROR;
242     goto out;
243   }
244
245   if (cmd->argc < 2 || cmd->argc > 3) {
246     cmd->client->ops->say(cmd->client, conn,
247              "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
248     COMMAND_ERROR;
249     goto out;
250   }
251
252   buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
253                                        cmd->argc - 1, ++cmd->argv,
254                                        ++cmd->argv_lens, ++cmd->argv_types,
255                                        0);
256   silc_client_packet_send(cmd->client, cmd->conn->sock,
257                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
258                           buffer->data, buffer->len, TRUE);
259   silc_buffer_free(buffer);
260   cmd->argv--;
261   cmd->argv_lens--;
262   cmd->argv_types--;
263
264   /* Notify application */
265   COMMAND;
266
267  out:
268   silc_client_command_free(cmd);
269 }
270
271 /* Command NICK. Shows current nickname/sets new nickname on current
272    window. */
273
274 SILC_CLIENT_CMD_FUNC(nick)
275 {
276   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
277   SilcClientConnection conn = cmd->conn;
278   SilcBuffer buffer;
279
280   if (!cmd->conn) {
281     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
282     COMMAND_ERROR;
283     goto out;
284   }
285
286   if (!strncmp(conn->nickname, cmd->argv[1], cmd->argv_lens[1]))
287     goto out;
288
289   /* Show current nickname */
290   if (cmd->argc < 2) {
291     if (cmd->conn) {
292       cmd->client->ops->say(cmd->client, conn, 
293                             "Your nickname is %s on server %s", 
294                             conn->nickname, conn->remote_host);
295     } else {
296       cmd->client->ops->say(cmd->client, conn, 
297                             "Your nickname is %s", conn->nickname);
298     }
299
300     /* XXX Notify application */
301     COMMAND;
302     goto out;
303   }
304
305   /* Set new nickname */
306   buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
307                                        cmd->argc - 1, ++cmd->argv,
308                                        ++cmd->argv_lens, ++cmd->argv_types,
309                                        0);
310   silc_client_packet_send(cmd->client, cmd->conn->sock,
311                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
312                           buffer->data, buffer->len, TRUE);
313   silc_buffer_free(buffer);
314   cmd->argv--;
315   cmd->argv_lens--;
316   cmd->argv_types--;
317   if (conn->nickname)
318     silc_free(conn->nickname);
319   conn->nickname = strdup(cmd->argv[1]);
320
321   /* Notify application */
322   COMMAND;
323
324  out:
325   silc_client_command_free(cmd);
326 }
327
328 SILC_CLIENT_CMD_FUNC(list)
329 {
330 }
331
332 /* Command TOPIC. Sets/shows topic on a channel. */
333
334 SILC_CLIENT_CMD_FUNC(topic)
335 {
336   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
337   SilcClientConnection conn = cmd->conn;
338   SilcIDCacheEntry id_cache = NULL;
339   SilcChannelEntry channel;
340   SilcBuffer buffer, idp;
341   char *name;
342
343   if (!cmd->conn) {
344     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
345     COMMAND_ERROR;
346     goto out;
347   }
348
349   if (cmd->argc < 2 || cmd->argc > 3) {
350     cmd->client->ops->say(cmd->client, conn,
351                           "Usage: /TOPIC <channel> [<topic>]");
352     COMMAND_ERROR;
353     goto out;
354   }
355
356   if (cmd->argv[1][0] == '*') {
357     if (!conn->current_channel) {
358       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
359       COMMAND_ERROR;
360       goto out;
361     }
362     name = conn->current_channel->channel_name;
363   } else {
364     name = cmd->argv[1];
365   }
366
367   if (!conn->current_channel) {
368     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
369     COMMAND_ERROR;
370     goto out;
371   }
372
373   /* Get the Channel ID of the channel */
374   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
375     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
376     COMMAND_ERROR;
377     goto out;
378   }
379
380   channel = (SilcChannelEntry)id_cache->context;
381
382   /* Send TOPIC command to the server */
383   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
384   if (cmd->argc > 2)
385     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2, 
386                                             1, idp->data, idp->len,
387                                             2, cmd->argv[2], 
388                                             strlen(cmd->argv[2]));
389   else
390     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1, 
391                                             1, idp->data, idp->len,
392                                             0);
393   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
394                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
395   silc_buffer_free(buffer);
396   silc_buffer_free(idp);
397
398   /* Notify application */
399   COMMAND;
400
401  out:
402   silc_client_command_free(cmd);
403 }
404
405 /* Command INVITE. Invites specific client to join a channel. */
406
407 SILC_CLIENT_CMD_FUNC(invite)
408 {
409   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
410   SilcClient client = cmd->client;
411   SilcClientConnection conn = cmd->conn;
412   SilcClientEntry client_entry;
413   SilcChannelEntry channel_entry;
414   SilcBuffer buffer, clidp, chidp;
415   unsigned int num = 0;
416   char *nickname = NULL, *server = NULL;
417
418   if (!cmd->conn) {
419     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
420     COMMAND_ERROR;
421     goto out;
422   }
423
424   if (cmd->argc != 3) {
425     cmd->client->ops->say(cmd->client, conn,
426                           "Usage: /INVITE <nickname>[@<server>] <channel>");
427     COMMAND_ERROR;
428     goto out;
429   }
430
431   /* Parse the typed nickname. */
432   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
433     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
434     COMMAND_ERROR;
435     goto out;
436   }
437
438   /* Find client entry */
439   client_entry = silc_idlist_get_client(client, conn, nickname, server, num);
440   if (!client_entry) {
441     if (nickname)
442       silc_free(nickname);
443     if (server)
444       silc_free(server);
445
446     /* Client entry not found, it was requested thus mark this to be
447        pending command. */
448     silc_client_command_pending(SILC_COMMAND_IDENTIFY, 
449                                 silc_client_command_invite, context);
450     return;
451   }
452
453   /* Find channel entry */
454   channel_entry = silc_idlist_get_channel(client, conn, cmd->argv[2]);
455   if (!channel_entry) {
456     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
457     COMMAND_ERROR;
458     goto out;
459   }
460
461   /* Send command */
462   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
463   chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
464   buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
465                                           1, clidp->data, clidp->len,
466                                           2, chidp->data, chidp->len);
467   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
468                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
469   silc_buffer_free(buffer);
470   silc_buffer_free(clidp);
471   silc_buffer_free(chidp);
472
473   cmd->client->ops->say(cmd->client, conn, 
474                         "Inviting %s to channel %s", cmd->argv[1], 
475                         cmd->argv[2]);
476
477   /* Notify application */
478   COMMAND;
479
480  out:
481   silc_client_command_free(cmd);
482 }
483
484 /* Command QUIT. Closes connection with current server. */
485  
486 SILC_CLIENT_CMD_FUNC(quit)
487 {
488   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
489   SilcBuffer buffer;
490
491   if (!cmd->conn) {
492     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
493     COMMAND_ERROR;
494     goto out;
495   }
496
497   buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, 
498                                        ++cmd->argv, ++cmd->argv_lens,
499                                        ++cmd->argv_types, 0);
500   silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
501                           NULL, 0, NULL, NULL, 
502                           buffer->data, buffer->len, TRUE);
503   silc_buffer_free(buffer);
504   cmd->argv--;
505   cmd->argv_lens--;
506   cmd->argv_types--;
507
508   /* Close connection */
509   silc_client_close_connection(cmd->client, cmd->conn->sock);
510   cmd->client->ops->disconnect(cmd->client, cmd->conn);
511
512   /* Notify application */
513   COMMAND;
514
515  out:
516   silc_client_command_free(cmd);
517 }
518
519 SILC_CLIENT_CMD_FUNC(kill)
520 {
521 }
522
523 /* Command INFO. Request information about specific server. If specific
524    server is not provided the current server is used. */
525
526 SILC_CLIENT_CMD_FUNC(info)
527 {
528   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
529   SilcClientConnection conn = cmd->conn;
530   SilcBuffer buffer;
531   char *name;
532
533   if (!cmd->conn) {
534     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
535     COMMAND_ERROR;
536     goto out;
537   }
538
539   if (cmd->argc < 2)
540     name = strdup(conn->remote_host);
541   else
542     name = strdup(cmd->argv[1]);
543
544   /* Send the command */
545   buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
546                                           1, name, strlen(name));
547   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
548                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
549   silc_buffer_free(buffer);
550
551   /* Notify application */
552   COMMAND;
553
554  out:
555   silc_client_command_free(cmd);
556 }
557
558 SILC_CLIENT_CMD_FUNC(connect)
559 {
560 }
561
562 /* Command PING. Sends ping to server. This is used to test the 
563    communication channel. */
564
565 SILC_CLIENT_CMD_FUNC(ping)
566 {
567   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
568   SilcClientConnection conn = cmd->conn;
569   SilcBuffer buffer;
570   void *id;
571   int i;
572   char *name = NULL;
573
574   if (!cmd->conn) {
575     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
576     COMMAND_ERROR;
577     goto out;
578   }
579
580   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
581     name = strdup(conn->remote_host);
582
583   /* Send the command */
584   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
585                                           1, conn->remote_id_data, 
586                                           SILC_ID_SERVER_LEN);
587   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
588                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
589   silc_buffer_free(buffer);
590
591   id = silc_id_str2id(conn->remote_id_data, SILC_ID_SERVER);
592
593   /* Start counting time */
594   for (i = 0; i < conn->ping_count; i++) {
595     if (conn->ping[i].dest_id == NULL) {
596       conn->ping[i].start_time = time(NULL);
597       conn->ping[i].dest_id = id;
598       conn->ping[i].dest_name = name;
599       conn->ping_count++;
600       break;
601     }
602   }
603   if (i >= conn->ping_count) {
604     i = conn->ping_count;
605     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
606     conn->ping[i].start_time = time(NULL);
607     conn->ping[i].dest_id = id;
608     conn->ping[i].dest_name = name;
609     conn->ping_count++;
610   }
611   
612   /* Notify application */
613   COMMAND;
614
615  out:
616   silc_client_command_free(cmd);
617 }
618
619 SILC_CLIENT_CMD_FUNC(oper)
620 {
621 }
622
623 SILC_CLIENT_CMD_FUNC(trace)
624 {
625 }
626
627 SILC_CLIENT_CMD_FUNC(notice)
628 {
629 }
630
631 /* Command JOIN. Joins to a channel. */
632
633 SILC_CLIENT_CMD_FUNC(join)
634 {
635   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
636   SilcClientConnection conn = cmd->conn;
637   SilcIDCacheEntry id_cache = NULL;
638   SilcBuffer buffer;
639
640   if (!cmd->conn) {
641     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
642     COMMAND_ERROR;
643     goto out;
644   }
645
646   if (cmd->argc < 2) {
647     /* Show channels currently joined to */
648
649     goto out;
650   }
651
652   /* See if we have joined to the requested channel already */
653   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
654                                     &id_cache)) {
655     cmd->client->ops->say(cmd->client, conn, 
656                           "You are talking to channel %s", cmd->argv[1]);
657     conn->current_channel = (SilcChannelEntry)id_cache->context;
658 #if 0
659     cmd->client->screen->bottom_line->channel = cmd->argv[1];
660     silc_screen_print_bottom_line(cmd->client->screen, 0);
661 #endif
662     goto out;
663   }
664
665   /* Send JOIN command to the server */
666   if (cmd->argc == 2)
667     buffer = 
668       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 1,
669                                      1, cmd->argv[1], cmd->argv_lens[1]);
670   else if (cmd->argc == 3)
671     /* XXX Buggy */
672     buffer = 
673       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
674                                      1, cmd->argv[1], cmd->argv_lens[1],
675                                      2, cmd->argv[2], cmd->argv_lens[2]);
676   else
677     buffer = 
678       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
679                                      1, cmd->argv[1], cmd->argv_lens[1],
680                                      2, cmd->argv[2], cmd->argv_lens[2],
681                                      3, cmd->argv[3], cmd->argv_lens[3]);
682
683   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
684                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
685   silc_buffer_free(buffer);
686
687   /* Notify application */
688   COMMAND;
689
690  out:
691   silc_client_command_free(cmd);
692 }
693
694 /* MOTD command. Requests motd from server. */
695
696 SILC_CLIENT_CMD_FUNC(motd)
697 {
698   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
699   SilcClientConnection conn = cmd->conn;
700   SilcBuffer buffer;
701
702   if (!cmd->conn) {
703     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
704     COMMAND_ERROR;
705     goto out;
706   }
707
708   if (cmd->argc < 1 || cmd->argc > 1) {
709     cmd->client->ops->say(cmd->client, conn,
710                           "Usage: /MOTD");
711     COMMAND_ERROR;
712     goto out;
713   }
714
715   /* Send TOPIC command to the server */
716   buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
717                                           2, conn->remote_host, 
718                                           strlen(conn->remote_host));
719   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
720                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
721   silc_buffer_free(buffer);
722
723   /* Notify application */
724   COMMAND;
725
726  out:
727   silc_client_command_free(cmd);
728 }
729
730 /* UMODE. Set user mode in SILC. */
731
732 SILC_CLIENT_CMD_FUNC(umode)
733 {
734
735 }
736
737 /* CMODE command. Sets channel mode. Modes that does not require any arguments
738    can be set several at once. Those modes that require argument must be set
739    separately (unless set with modes that does not require arguments). */
740
741 SILC_CLIENT_CMD_FUNC(cmode)
742 {
743   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
744   SilcClientConnection conn = cmd->conn;
745   SilcChannelEntry channel;
746   SilcBuffer buffer, chidp;
747   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
748   unsigned int mode, add, type, len, arg_len = 0;
749   int i;
750
751   if (!cmd->conn) {
752     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
753     COMMAND_ERROR;
754     goto out;
755   }
756
757   if (cmd->argc < 3) {
758     cmd->client->ops->say(cmd->client, conn, 
759                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
760     COMMAND_ERROR;
761     goto out;
762   }
763
764   if (cmd->argv[1][0] == '*') {
765     if (!conn->current_channel) {
766       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
767       COMMAND_ERROR;
768       goto out;
769     }
770
771     channel = conn->current_channel;
772   } else {
773     name = cmd->argv[1];
774
775     channel = silc_idlist_get_channel(cmd->client, conn, name);
776     if (!channel) {
777       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
778       COMMAND_ERROR;
779       goto out;
780     }
781   }
782
783   mode = channel->mode;
784
785   /* Are we adding or removing mode */
786   if (cmd->argv[2][0] == '-')
787     add = FALSE;
788   else
789     add = TRUE;
790
791   /* Argument type to be sent to server */
792   type = 0;
793
794   /* Parse mode */
795   cp = cmd->argv[2] + 1;
796   len = strlen(cp);
797   for (i = 0; i < len; i++) {
798     switch(cp[i]) {
799     case 'p':
800       if (add)
801         mode |= SILC_CHANNEL_MODE_PRIVATE;
802       else
803         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
804       break;
805     case 's':
806       if (add)
807         mode |= SILC_CHANNEL_MODE_SECRET;
808       else
809         mode &= ~SILC_CHANNEL_MODE_SECRET;
810       break;
811     case 'k':
812       if (add)
813         mode |= SILC_CHANNEL_MODE_PRIVKEY;
814       else
815         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
816       break;
817     case 'i':
818       if (add)
819         mode |= SILC_CHANNEL_MODE_INVITE;
820       else
821         mode &= ~SILC_CHANNEL_MODE_INVITE;
822       break;
823     case 't':
824       if (add)
825         mode |= SILC_CHANNEL_MODE_TOPIC;
826       else
827         mode &= ~SILC_CHANNEL_MODE_TOPIC;
828       break;
829     case 'l':
830       if (add) {
831         int ll;
832         mode |= SILC_CHANNEL_MODE_ULIMIT;
833         type = 3;
834         ll = atoi(cmd->argv[3]);
835         SILC_PUT32_MSB(ll, tmp);
836         arg = tmp;
837         arg_len = 4;
838       } else {
839         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
840       }
841       break;
842     case 'a':
843       if (add) {
844         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
845         type = 4;
846         arg = cmd->argv[3];
847         arg_len = cmd->argv_lens[3];
848       } else {
849         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
850       }
851       break;
852     case 'b':
853       if (add) {
854         mode |= SILC_CHANNEL_MODE_BAN;
855         type = 5;
856         arg = cmd->argv[3];
857         arg_len = cmd->argv_lens[3];
858       } else {
859         mode &= ~SILC_CHANNEL_MODE_BAN;
860       }
861       break;
862     case 'I':
863       if (add) {
864         mode |= SILC_CHANNEL_MODE_INVITE_LIST;
865         type = 6;
866         arg = cmd->argv[3];
867         arg_len = cmd->argv_lens[3];
868       } else {
869         mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
870       }
871       break;
872     case 'c':
873       if (add) {
874         mode |= SILC_CHANNEL_MODE_CIPHER;
875         type = 8;
876         arg = cmd->argv[3];
877         arg_len = cmd->argv_lens[3];
878       } else {
879         mode &= ~SILC_CHANNEL_MODE_CIPHER;
880       }
881       break;
882     default:
883       COMMAND_ERROR;
884       goto out;
885       break;
886     }
887   }
888
889   if (type && cmd->argc < 3) {
890     COMMAND_ERROR;
891     goto out;
892   }
893
894   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
895   SILC_PUT32_MSB(mode, modebuf);
896
897   /* Send the command packet. We support sending only one mode at once
898      that requires an argument. */
899   if (type && arg) {
900     buffer = 
901       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
902                                      1, chidp->data, chidp->len, 
903                                      2, modebuf, sizeof(modebuf),
904                                      type, arg, arg_len);
905   } else {
906     buffer = 
907       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
908                                      1, chidp->data, chidp->len, 
909                                      2, modebuf, sizeof(modebuf));
910   }
911
912   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
913                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
914   silc_buffer_free(buffer);
915   silc_buffer_free(chidp);
916
917   /* Notify application */
918   COMMAND;
919
920  out:
921   silc_client_command_free(cmd);
922 }
923
924 /* CUMODE command. Changes client's mode on a channel. */
925
926 SILC_CLIENT_CMD_FUNC(cumode)
927 {
928   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
929   SilcClientConnection conn = cmd->conn;
930   SilcChannelEntry channel;
931   SilcClientEntry client_entry;
932   SilcBuffer buffer, clidp, chidp;
933   unsigned char *name, *cp, modebuf[4];
934   unsigned int mode = 0, add, len;
935   char *nickname = NULL, *server = NULL;
936   unsigned int num = 0;
937   int i;
938
939   if (!cmd->conn) {
940     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
941     COMMAND_ERROR;
942     goto out;
943   }
944
945   if (cmd->argc < 4) {
946     cmd->client->ops->say(cmd->client, conn, 
947                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
948     COMMAND_ERROR;
949     goto out;
950   }
951
952   if (cmd->argv[1][0] == '*') {
953     if (!conn->current_channel) {
954       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
955       COMMAND_ERROR;
956       goto out;
957     }
958
959     channel = conn->current_channel;
960   } else {
961     name = cmd->argv[1];
962
963     channel = silc_idlist_get_channel(cmd->client, conn, name);
964     if (!channel) {
965       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
966       COMMAND_ERROR;
967       goto out;
968     }
969   }
970
971   /* Parse the typed nickname. */
972   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
973     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
974     COMMAND_ERROR;
975     goto out;
976   }
977
978   /* Find client entry */
979   client_entry = silc_idlist_get_client(cmd->client, conn, 
980                                         nickname, server, num);
981   if (!client_entry) {
982     /* Client entry not found, it was requested thus mark this to be
983        pending command. */
984     silc_client_command_pending(SILC_COMMAND_CUMODE, 
985                                 silc_client_command_cumode, context);
986     return;
987   }
988   
989   for (i = 0; i < channel->clients_count; i++)
990     if (channel->clients[i].client == client_entry) {
991       mode = channel->clients[i].mode;
992       break;
993     }
994
995   /* Are we adding or removing mode */
996   if (cmd->argv[2][0] == '-')
997     add = FALSE;
998   else
999     add = TRUE;
1000
1001   /* Parse mode */
1002   cp = cmd->argv[2] + 1;
1003   len = strlen(cp);
1004   for (i = 0; i < len; i++) {
1005     switch(cp[i]) {
1006     case 'a':
1007       if (add) {
1008         mode |= SILC_CHANNEL_UMODE_CHANFO;
1009         mode |= SILC_CHANNEL_UMODE_CHANOP;
1010       } else {
1011         mode = SILC_CHANNEL_UMODE_NONE;
1012       }
1013       break;
1014     case 'f':
1015       if (add)
1016         mode |= SILC_CHANNEL_UMODE_CHANFO;
1017       else
1018         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1019       break;
1020     case 'o':
1021       if (add)
1022         mode |= SILC_CHANNEL_UMODE_CHANOP;
1023       else
1024         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1025       break;
1026     default:
1027       COMMAND_ERROR;
1028       goto out;
1029       break;
1030     }
1031   }
1032
1033   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1034   SILC_PUT32_MSB(mode, modebuf);
1035   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1036
1037   /* Send the command packet. We support sending only one mode at once
1038      that requires an argument. */
1039   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1040                                           1, chidp->data, chidp->len, 
1041                                           2, modebuf, 4,
1042                                           3, clidp->data, clidp->len);
1043
1044   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1045                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1046   silc_buffer_free(buffer);
1047   silc_buffer_free(chidp);
1048   silc_buffer_free(clidp);
1049   
1050   /* Notify application */
1051   COMMAND;
1052
1053  out:
1054   silc_client_command_free(cmd);
1055 }
1056
1057 /* KICK command. Kicks a client out of channel. */
1058
1059 SILC_CLIENT_CMD_FUNC(kick)
1060 {
1061   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1062   SilcClientConnection conn = cmd->conn;
1063
1064 }
1065
1066 SILC_CLIENT_CMD_FUNC(restart)
1067 {
1068 }
1069  
1070 SILC_CLIENT_CMD_FUNC(close)
1071 {
1072 }
1073  
1074 SILC_CLIENT_CMD_FUNC(die)
1075 {
1076 }
1077  
1078 SILC_CLIENT_CMD_FUNC(silcoper)
1079 {
1080 }
1081
1082 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1083
1084 SILC_CLIENT_CMD_FUNC(leave)
1085 {
1086   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1087   SilcClientConnection conn = cmd->conn;
1088   SilcIDCacheEntry id_cache = NULL;
1089   SilcChannelEntry channel;
1090   SilcBuffer buffer, idp;
1091   char *name;
1092
1093   if (!cmd->conn) {
1094     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1095     COMMAND_ERROR;
1096     goto out;
1097   }
1098
1099   if (cmd->argc != 2) {
1100     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1101     COMMAND_ERROR;
1102     goto out;
1103   }
1104
1105   if (cmd->argv[1][0] == '*') {
1106     if (!conn->current_channel) {
1107       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1108       COMMAND_ERROR;
1109       goto out;
1110     }
1111     name = conn->current_channel->channel_name;
1112   } else {
1113     name = cmd->argv[1];
1114   }
1115
1116   if (!conn->current_channel) {
1117     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1118     COMMAND_ERROR;
1119     goto out;
1120   }
1121
1122   /* Get the Channel ID of the channel */
1123   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1124     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1125     COMMAND_ERROR;
1126     goto out;
1127   }
1128
1129   channel = (SilcChannelEntry)id_cache->context;
1130
1131   /* Send LEAVE command to the server */
1132   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1133   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1134                                           1, idp->data, idp->len);
1135   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1136                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1137   silc_buffer_free(buffer);
1138   silc_buffer_free(idp);
1139
1140   /* We won't talk anymore on this channel */
1141   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1142
1143   conn->current_channel = NULL;
1144
1145   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1146   silc_free(channel->channel_name);
1147   silc_free(channel->id);
1148   silc_free(channel->key);
1149   silc_cipher_free(channel->channel_key);
1150   silc_free(channel);
1151
1152   /* Notify application */
1153   COMMAND;
1154
1155  out:
1156   silc_client_command_free(cmd);
1157 }
1158
1159 /* Command NAMES. Requests the names of the clients joined on requested
1160    channel. */
1161
1162 SILC_CLIENT_CMD_FUNC(names)
1163 {
1164   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1165   SilcClientConnection conn = cmd->conn;
1166   SilcIDCacheEntry id_cache = NULL;
1167   SilcBuffer buffer, idp;
1168   char *name;
1169
1170   if (!cmd->conn) {
1171     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1172     COMMAND_ERROR;
1173     goto out;
1174   }
1175
1176   if (cmd->argc != 2) {
1177     cmd->client->ops->say(cmd->client, conn, "Usage: /NAMES <channel>");
1178     COMMAND_ERROR;
1179     goto out;
1180   }
1181
1182   if (cmd->argv[1][0] == '*')
1183     name = conn->current_channel->channel_name;
1184   else
1185     name = cmd->argv[1];
1186
1187   /* Get the Channel ID of the channel */
1188   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1189     /* XXX should resolve the channel ID; LIST command */
1190     cmd->client->ops->say(cmd->client, conn, 
1191                           "You are not on that channel", name);
1192     COMMAND_ERROR;
1193     goto out;
1194   }
1195
1196   /* Send NAMES command to the server */
1197   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1198   buffer = silc_command_payload_encode_va(SILC_COMMAND_NAMES, 0, 1, 
1199                                           1, idp->data, idp->len);
1200   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1201                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1202   silc_buffer_free(buffer);
1203   silc_buffer_free(idp);
1204
1205   /* Register dummy pending command that will tell the reply command
1206      that user called this command. Server may send reply to this command
1207      even if user did not send this command thus we want to handle things
1208      differently when user sent the command. This is dummy and won't be
1209      executed. */
1210   /* XXX this is kludge and should be removed after pending command reply 
1211      support is added. Currently only commands may be pending not command
1212      replies. */
1213   silc_client_command_pending(SILC_COMMAND_NAMES, 
1214                               silc_client_command_names, NULL);
1215
1216   /* Notify application */
1217   COMMAND;
1218
1219  out:
1220   silc_client_command_free(cmd);
1221 }