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, 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 (!strcmp(conn->nickname, cmd->argv[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   SilcChannelUser chu;
932   SilcClientEntry client_entry;
933   SilcBuffer buffer, clidp, chidp;
934   unsigned char *name, *cp, modebuf[4];
935   unsigned int mode = 0, add, len;
936   char *nickname = NULL, *server = NULL;
937   unsigned int num = 0;
938   int i;
939
940   if (!cmd->conn) {
941     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
942     COMMAND_ERROR;
943     goto out;
944   }
945
946   if (cmd->argc < 4) {
947     cmd->client->ops->say(cmd->client, conn, 
948                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
949     COMMAND_ERROR;
950     goto out;
951   }
952
953   if (cmd->argv[1][0] == '*') {
954     if (!conn->current_channel) {
955       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
956       COMMAND_ERROR;
957       goto out;
958     }
959
960     channel = conn->current_channel;
961   } else {
962     name = cmd->argv[1];
963
964     channel = silc_idlist_get_channel(cmd->client, conn, name);
965     if (!channel) {
966       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
967       COMMAND_ERROR;
968       goto out;
969     }
970   }
971
972   /* Parse the typed nickname. */
973   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
974     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
975     COMMAND_ERROR;
976     goto out;
977   }
978
979   /* Find client entry */
980   client_entry = silc_idlist_get_client(cmd->client, conn, 
981                                         nickname, server, num);
982   if (!client_entry) {
983     /* Client entry not found, it was requested thus mark this to be
984        pending command. */
985     silc_client_command_pending(SILC_COMMAND_CUMODE, 
986                                 silc_client_command_cumode, context);
987     return;
988   }
989   
990   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
991     if (chu->client == client_entry) {
992       chu->mode = mode;
993       break;
994     }
995   }
996
997   /* Are we adding or removing mode */
998   if (cmd->argv[2][0] == '-')
999     add = FALSE;
1000   else
1001     add = TRUE;
1002
1003   /* Parse mode */
1004   cp = cmd->argv[2] + 1;
1005   len = strlen(cp);
1006   for (i = 0; i < len; i++) {
1007     switch(cp[i]) {
1008     case 'a':
1009       if (add) {
1010         mode |= SILC_CHANNEL_UMODE_CHANFO;
1011         mode |= SILC_CHANNEL_UMODE_CHANOP;
1012       } else {
1013         mode = SILC_CHANNEL_UMODE_NONE;
1014       }
1015       break;
1016     case 'f':
1017       if (add)
1018         mode |= SILC_CHANNEL_UMODE_CHANFO;
1019       else
1020         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1021       break;
1022     case 'o':
1023       if (add)
1024         mode |= SILC_CHANNEL_UMODE_CHANOP;
1025       else
1026         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1027       break;
1028     default:
1029       COMMAND_ERROR;
1030       goto out;
1031       break;
1032     }
1033   }
1034
1035   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1036   SILC_PUT32_MSB(mode, modebuf);
1037   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1038
1039   /* Send the command packet. We support sending only one mode at once
1040      that requires an argument. */
1041   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1042                                           1, chidp->data, chidp->len, 
1043                                           2, modebuf, 4,
1044                                           3, clidp->data, clidp->len);
1045
1046   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1047                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1048   silc_buffer_free(buffer);
1049   silc_buffer_free(chidp);
1050   silc_buffer_free(clidp);
1051   
1052   /* Notify application */
1053   COMMAND;
1054
1055  out:
1056   silc_client_command_free(cmd);
1057 }
1058
1059 /* KICK command. Kicks a client out of channel. */
1060
1061 SILC_CLIENT_CMD_FUNC(kick)
1062 {
1063   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1064   SilcClientConnection conn = cmd->conn;
1065
1066 }
1067
1068 SILC_CLIENT_CMD_FUNC(restart)
1069 {
1070 }
1071  
1072 SILC_CLIENT_CMD_FUNC(close)
1073 {
1074 }
1075  
1076 SILC_CLIENT_CMD_FUNC(die)
1077 {
1078 }
1079  
1080 SILC_CLIENT_CMD_FUNC(silcoper)
1081 {
1082 }
1083
1084 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1085
1086 SILC_CLIENT_CMD_FUNC(leave)
1087 {
1088   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1089   SilcClientConnection conn = cmd->conn;
1090   SilcIDCacheEntry id_cache = NULL;
1091   SilcChannelEntry channel;
1092   SilcBuffer buffer, idp;
1093   char *name;
1094
1095   if (!cmd->conn) {
1096     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1097     COMMAND_ERROR;
1098     goto out;
1099   }
1100
1101   if (cmd->argc != 2) {
1102     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1103     COMMAND_ERROR;
1104     goto out;
1105   }
1106
1107   if (cmd->argv[1][0] == '*') {
1108     if (!conn->current_channel) {
1109       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1110       COMMAND_ERROR;
1111       goto out;
1112     }
1113     name = conn->current_channel->channel_name;
1114   } else {
1115     name = cmd->argv[1];
1116   }
1117
1118   if (!conn->current_channel) {
1119     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1120     COMMAND_ERROR;
1121     goto out;
1122   }
1123
1124   /* Get the Channel ID of the channel */
1125   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1126     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1127     COMMAND_ERROR;
1128     goto out;
1129   }
1130
1131   channel = (SilcChannelEntry)id_cache->context;
1132
1133   /* Send LEAVE command to the server */
1134   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1135   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1136                                           1, idp->data, idp->len);
1137   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1138                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1139   silc_buffer_free(buffer);
1140   silc_buffer_free(idp);
1141
1142   /* We won't talk anymore on this channel */
1143   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1144
1145   conn->current_channel = NULL;
1146
1147   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1148   silc_free(channel->channel_name);
1149   silc_free(channel->id);
1150   silc_free(channel->key);
1151   silc_cipher_free(channel->channel_key);
1152   silc_free(channel);
1153
1154   /* Notify application */
1155   COMMAND;
1156
1157  out:
1158   silc_client_command_free(cmd);
1159 }
1160
1161 /* Command NAMES. Requests the names of the clients joined on requested
1162    channel. */
1163
1164 SILC_CLIENT_CMD_FUNC(names)
1165 {
1166   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1167   SilcClientConnection conn = cmd->conn;
1168   SilcIDCacheEntry id_cache = NULL;
1169   SilcBuffer buffer, idp;
1170   char *name;
1171
1172   if (!cmd->conn) {
1173     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1174     COMMAND_ERROR;
1175     goto out;
1176   }
1177
1178   if (cmd->argc != 2) {
1179     cmd->client->ops->say(cmd->client, conn, "Usage: /NAMES <channel>");
1180     COMMAND_ERROR;
1181     goto out;
1182   }
1183
1184   if (cmd->argv[1][0] == '*') {
1185     if (!conn->current_channel) {
1186       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1187       COMMAND_ERROR;
1188       goto out;
1189     }
1190     name = conn->current_channel->channel_name;
1191   } else {
1192     name = cmd->argv[1];
1193   }
1194
1195   if (!conn->current_channel) {
1196     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1197     COMMAND_ERROR;
1198     goto out;
1199   }
1200
1201   /* Get the Channel ID of the channel */
1202   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1203     /* XXX should resolve the channel ID; LIST command */
1204     cmd->client->ops->say(cmd->client, conn, 
1205                           "You are not on that channel", name);
1206     COMMAND_ERROR;
1207     goto out;
1208   }
1209
1210   /* Send NAMES command to the server */
1211   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1212   buffer = silc_command_payload_encode_va(SILC_COMMAND_NAMES, 0, 1, 
1213                                           1, idp->data, idp->len);
1214   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1215                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1216   silc_buffer_free(buffer);
1217   silc_buffer_free(idp);
1218
1219   /* Register dummy pending command that will tell the reply command
1220      that user called this command. Server may send reply to this command
1221      even if user did not send this command thus we want to handle things
1222      differently when user sent the command. This is dummy and won't be
1223      executed. */
1224   /* XXX this is kludge and should be removed after pending command reply 
1225      support is added. Currently only commands may be pending not command
1226      replies. */
1227   silc_client_command_pending(SILC_COMMAND_NAMES, 
1228                               silc_client_command_names, NULL);
1229
1230   /* Notify application */
1231   COMMAND;
1232
1233  out:
1234   silc_client_command_free(cmd);
1235 }