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(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 int argc, ...)
82 {
83   SilcBuffer packet;
84   va_list ap;
85
86   va_start(ap, argc);
87
88   packet = silc_command_payload_encode_vap(command, 0, argc, ap);
89   silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
90                           NULL, 0, NULL, NULL, packet->data, 
91                           packet->len, TRUE);
92   silc_buffer_free(packet);
93 }
94
95 /* Finds and returns a pointer to the command list. Return NULL if the
96    command is not found. */
97
98 SilcClientCommand *silc_client_command_find(const char *name)
99 {
100   SilcClientCommand *cmd;
101
102   for (cmd = silc_command_list; cmd->name; cmd++) {
103     if (!strcmp(cmd->name, name))
104       return cmd;
105   }
106
107   return NULL;
108 }
109
110 /* Add new pending command to be executed when reply to a command has been
111    received.  The `reply_cmd' is the command that will call the `callback'
112    with `context' when reply has been received.  If `ident is non-zero
113    the `callback' will be executed when received reply with command 
114    identifier `ident'. */
115
116 void silc_client_command_pending(SilcClientConnection conn,
117                                  SilcCommand reply_cmd,
118                                  unsigned short ident,
119                                  SilcCommandCb callback,
120                                  void *context)
121 {
122   SilcClientCommandPending *reply;
123
124   reply = silc_calloc(1, sizeof(*reply));
125   reply->reply_cmd = reply_cmd;
126   reply->ident = ident;
127   reply->context = context;
128   reply->callback = callback;
129   silc_dlist_add(conn->pending_commands, reply);
130 }
131
132 /* Deletes pending command by reply command type. */
133
134 void silc_client_command_pending_del(SilcClientConnection conn,
135                                      SilcCommand reply_cmd,
136                                      unsigned short ident)
137 {
138   SilcClientCommandPending *r;
139
140   silc_dlist_start(conn->pending_commands);
141   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
142     if (r->reply_cmd == reply_cmd && r->ident == ident) {
143       silc_dlist_del(conn->pending_commands, r);
144       break;
145     }
146   }
147 }
148
149 /* Checks for pending commands and marks callbacks to be called from
150    the command reply function. Returns TRUE if there were pending command. */
151
152 int silc_client_command_pending_check(SilcClientConnection conn,
153                                       SilcClientCommandReplyContext ctx,
154                                       SilcCommand command, 
155                                       unsigned short ident)
156 {
157   SilcClientCommandPending *r;
158
159   silc_dlist_start(conn->pending_commands);
160   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
161     if (r->reply_cmd == command && r->ident == ident) {
162       ctx->context = r->context;
163       ctx->callback = r->callback;
164       ctx->ident = ident;
165       return TRUE;
166     }
167   }
168
169   return FALSE;
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(conn, SILC_COMMAND_IDENTIFY, 0,
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, idp;
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   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
666
667   /* Send JOIN command to the server */
668   if (cmd->argc == 2)
669     buffer = 
670       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
671                                      1, cmd->argv[1], cmd->argv_lens[1],
672                                      2, idp->data, idp->len);
673   else if (cmd->argc == 3)
674     /* XXX Buggy */
675     buffer = 
676       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
677                                      1, cmd->argv[1], cmd->argv_lens[1],
678                                      2, idp->data, idp->len,
679                                      3, cmd->argv[2], cmd->argv_lens[2]);
680   else
681     buffer = 
682       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
683                                      1, cmd->argv[1], cmd->argv_lens[1],
684                                      2, idp->data, idp->len,
685                                      3, cmd->argv[2], cmd->argv_lens[2],
686                                      4, cmd->argv[3], cmd->argv_lens[3]);
687
688   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
689                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
690   silc_buffer_free(buffer);
691   silc_buffer_free(idp);
692
693   /* Notify application */
694   COMMAND;
695
696  out:
697   silc_client_command_free(cmd);
698 }
699
700 /* MOTD command. Requests motd from server. */
701
702 SILC_CLIENT_CMD_FUNC(motd)
703 {
704   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
705   SilcClientConnection conn = cmd->conn;
706   SilcBuffer buffer;
707
708   if (!cmd->conn) {
709     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
710     COMMAND_ERROR;
711     goto out;
712   }
713
714   if (cmd->argc < 1 || cmd->argc > 1) {
715     cmd->client->ops->say(cmd->client, conn,
716                           "Usage: /MOTD");
717     COMMAND_ERROR;
718     goto out;
719   }
720
721   /* Send TOPIC command to the server */
722   buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
723                                           2, conn->remote_host, 
724                                           strlen(conn->remote_host));
725   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
726                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
727   silc_buffer_free(buffer);
728
729   /* Notify application */
730   COMMAND;
731
732  out:
733   silc_client_command_free(cmd);
734 }
735
736 /* UMODE. Set user mode in SILC. */
737
738 SILC_CLIENT_CMD_FUNC(umode)
739 {
740
741 }
742
743 /* CMODE command. Sets channel mode. Modes that does not require any arguments
744    can be set several at once. Those modes that require argument must be set
745    separately (unless set with modes that does not require arguments). */
746
747 SILC_CLIENT_CMD_FUNC(cmode)
748 {
749   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
750   SilcClientConnection conn = cmd->conn;
751   SilcChannelEntry channel;
752   SilcBuffer buffer, chidp;
753   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
754   unsigned int mode, add, type, len, arg_len = 0;
755   int i;
756
757   if (!cmd->conn) {
758     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
759     COMMAND_ERROR;
760     goto out;
761   }
762
763   if (cmd->argc < 3) {
764     cmd->client->ops->say(cmd->client, conn, 
765                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
766     COMMAND_ERROR;
767     goto out;
768   }
769
770   if (cmd->argv[1][0] == '*') {
771     if (!conn->current_channel) {
772       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
773       COMMAND_ERROR;
774       goto out;
775     }
776
777     channel = conn->current_channel;
778   } else {
779     name = cmd->argv[1];
780
781     channel = silc_idlist_get_channel(cmd->client, conn, name);
782     if (!channel) {
783       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
784       COMMAND_ERROR;
785       goto out;
786     }
787   }
788
789   mode = channel->mode;
790
791   /* Are we adding or removing mode */
792   if (cmd->argv[2][0] == '-')
793     add = FALSE;
794   else
795     add = TRUE;
796
797   /* Argument type to be sent to server */
798   type = 0;
799
800   /* Parse mode */
801   cp = cmd->argv[2] + 1;
802   len = strlen(cp);
803   for (i = 0; i < len; i++) {
804     switch(cp[i]) {
805     case 'p':
806       if (add)
807         mode |= SILC_CHANNEL_MODE_PRIVATE;
808       else
809         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
810       break;
811     case 's':
812       if (add)
813         mode |= SILC_CHANNEL_MODE_SECRET;
814       else
815         mode &= ~SILC_CHANNEL_MODE_SECRET;
816       break;
817     case 'k':
818       if (add)
819         mode |= SILC_CHANNEL_MODE_PRIVKEY;
820       else
821         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
822       break;
823     case 'i':
824       if (add)
825         mode |= SILC_CHANNEL_MODE_INVITE;
826       else
827         mode &= ~SILC_CHANNEL_MODE_INVITE;
828       break;
829     case 't':
830       if (add)
831         mode |= SILC_CHANNEL_MODE_TOPIC;
832       else
833         mode &= ~SILC_CHANNEL_MODE_TOPIC;
834       break;
835     case 'l':
836       if (add) {
837         int ll;
838         mode |= SILC_CHANNEL_MODE_ULIMIT;
839         type = 3;
840         ll = atoi(cmd->argv[3]);
841         SILC_PUT32_MSB(ll, tmp);
842         arg = tmp;
843         arg_len = 4;
844       } else {
845         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
846       }
847       break;
848     case 'a':
849       if (add) {
850         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
851         type = 4;
852         arg = cmd->argv[3];
853         arg_len = cmd->argv_lens[3];
854       } else {
855         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
856       }
857       break;
858     case 'b':
859       if (add) {
860         mode |= SILC_CHANNEL_MODE_BAN;
861         type = 5;
862         arg = cmd->argv[3];
863         arg_len = cmd->argv_lens[3];
864       } else {
865         mode &= ~SILC_CHANNEL_MODE_BAN;
866       }
867       break;
868     case 'I':
869       if (add) {
870         mode |= SILC_CHANNEL_MODE_INVITE_LIST;
871         type = 6;
872         arg = cmd->argv[3];
873         arg_len = cmd->argv_lens[3];
874       } else {
875         mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
876       }
877       break;
878     case 'c':
879       if (add) {
880         mode |= SILC_CHANNEL_MODE_CIPHER;
881         type = 8;
882         arg = cmd->argv[3];
883         arg_len = cmd->argv_lens[3];
884       } else {
885         mode &= ~SILC_CHANNEL_MODE_CIPHER;
886       }
887       break;
888     default:
889       COMMAND_ERROR;
890       goto out;
891       break;
892     }
893   }
894
895   if (type && cmd->argc < 3) {
896     COMMAND_ERROR;
897     goto out;
898   }
899
900   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
901   SILC_PUT32_MSB(mode, modebuf);
902
903   /* Send the command packet. We support sending only one mode at once
904      that requires an argument. */
905   if (type && arg) {
906     buffer = 
907       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
908                                      1, chidp->data, chidp->len, 
909                                      2, modebuf, sizeof(modebuf),
910                                      type, arg, arg_len);
911   } else {
912     buffer = 
913       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
914                                      1, chidp->data, chidp->len, 
915                                      2, modebuf, sizeof(modebuf));
916   }
917
918   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
919                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
920   silc_buffer_free(buffer);
921   silc_buffer_free(chidp);
922
923   /* Notify application */
924   COMMAND;
925
926  out:
927   silc_client_command_free(cmd);
928 }
929
930 /* CUMODE command. Changes client's mode on a channel. */
931
932 SILC_CLIENT_CMD_FUNC(cumode)
933 {
934   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
935   SilcClientConnection conn = cmd->conn;
936   SilcChannelEntry channel;
937   SilcChannelUser chu;
938   SilcClientEntry client_entry;
939   SilcBuffer buffer, clidp, chidp;
940   unsigned char *name, *cp, modebuf[4];
941   unsigned int mode = 0, add, len;
942   char *nickname = NULL, *server = NULL;
943   unsigned int num = 0;
944   int i;
945
946   if (!cmd->conn) {
947     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
948     COMMAND_ERROR;
949     goto out;
950   }
951
952   if (cmd->argc < 4) {
953     cmd->client->ops->say(cmd->client, conn, 
954                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
955     COMMAND_ERROR;
956     goto out;
957   }
958
959   if (cmd->argv[1][0] == '*') {
960     if (!conn->current_channel) {
961       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
962       COMMAND_ERROR;
963       goto out;
964     }
965
966     channel = conn->current_channel;
967   } else {
968     name = cmd->argv[1];
969
970     channel = silc_idlist_get_channel(cmd->client, conn, name);
971     if (!channel) {
972       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
973       COMMAND_ERROR;
974       goto out;
975     }
976   }
977
978   /* Parse the typed nickname. */
979   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
980     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
981     COMMAND_ERROR;
982     goto out;
983   }
984
985   /* Find client entry */
986   client_entry = silc_idlist_get_client(cmd->client, conn, 
987                                         nickname, server, num);
988   if (!client_entry) {
989     /* Client entry not found, it was requested thus mark this to be
990        pending command. */
991     silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,  
992                                 silc_client_command_cumode, context);
993     return;
994   }
995   
996   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
997     if (chu->client == client_entry) {
998       chu->mode = mode;
999       break;
1000     }
1001   }
1002
1003   /* Are we adding or removing mode */
1004   if (cmd->argv[2][0] == '-')
1005     add = FALSE;
1006   else
1007     add = TRUE;
1008
1009   /* Parse mode */
1010   cp = cmd->argv[2] + 1;
1011   len = strlen(cp);
1012   for (i = 0; i < len; i++) {
1013     switch(cp[i]) {
1014     case 'a':
1015       if (add) {
1016         mode |= SILC_CHANNEL_UMODE_CHANFO;
1017         mode |= SILC_CHANNEL_UMODE_CHANOP;
1018       } else {
1019         mode = SILC_CHANNEL_UMODE_NONE;
1020       }
1021       break;
1022     case 'f':
1023       if (add)
1024         mode |= SILC_CHANNEL_UMODE_CHANFO;
1025       else
1026         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1027       break;
1028     case 'o':
1029       if (add)
1030         mode |= SILC_CHANNEL_UMODE_CHANOP;
1031       else
1032         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1033       break;
1034     default:
1035       COMMAND_ERROR;
1036       goto out;
1037       break;
1038     }
1039   }
1040
1041   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1042   SILC_PUT32_MSB(mode, modebuf);
1043   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1044
1045   /* Send the command packet. We support sending only one mode at once
1046      that requires an argument. */
1047   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1048                                           1, chidp->data, chidp->len, 
1049                                           2, modebuf, 4,
1050                                           3, clidp->data, clidp->len);
1051
1052   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1053                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1054   silc_buffer_free(buffer);
1055   silc_buffer_free(chidp);
1056   silc_buffer_free(clidp);
1057   
1058   /* Notify application */
1059   COMMAND;
1060
1061  out:
1062   silc_client_command_free(cmd);
1063 }
1064
1065 /* KICK command. Kicks a client out of channel. */
1066
1067 SILC_CLIENT_CMD_FUNC(kick)
1068 {
1069   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1070   SilcClientConnection conn = cmd->conn;
1071
1072 }
1073
1074 SILC_CLIENT_CMD_FUNC(restart)
1075 {
1076 }
1077  
1078 SILC_CLIENT_CMD_FUNC(close)
1079 {
1080 }
1081  
1082 SILC_CLIENT_CMD_FUNC(die)
1083 {
1084 }
1085  
1086 SILC_CLIENT_CMD_FUNC(silcoper)
1087 {
1088 }
1089
1090 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1091
1092 SILC_CLIENT_CMD_FUNC(leave)
1093 {
1094   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1095   SilcClientConnection conn = cmd->conn;
1096   SilcIDCacheEntry id_cache = NULL;
1097   SilcChannelEntry channel;
1098   SilcBuffer buffer, idp;
1099   char *name;
1100
1101   if (!cmd->conn) {
1102     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1103     COMMAND_ERROR;
1104     goto out;
1105   }
1106
1107   if (cmd->argc != 2) {
1108     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1109     COMMAND_ERROR;
1110     goto out;
1111   }
1112
1113   if (cmd->argv[1][0] == '*') {
1114     if (!conn->current_channel) {
1115       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1116       COMMAND_ERROR;
1117       goto out;
1118     }
1119     name = conn->current_channel->channel_name;
1120   } else {
1121     name = cmd->argv[1];
1122   }
1123
1124   if (!conn->current_channel) {
1125     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1126     COMMAND_ERROR;
1127     goto out;
1128   }
1129
1130   /* Get the Channel ID of the channel */
1131   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1132     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1133     COMMAND_ERROR;
1134     goto out;
1135   }
1136
1137   channel = (SilcChannelEntry)id_cache->context;
1138
1139   /* Send LEAVE command to the server */
1140   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1141   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1142                                           1, idp->data, idp->len);
1143   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1144                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1145   silc_buffer_free(buffer);
1146   silc_buffer_free(idp);
1147
1148   /* We won't talk anymore on this channel */
1149   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1150
1151   conn->current_channel = NULL;
1152
1153   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1154   silc_free(channel->channel_name);
1155   silc_free(channel->id);
1156   silc_free(channel->key);
1157   silc_cipher_free(channel->channel_key);
1158   silc_free(channel);
1159
1160   /* Notify application */
1161   COMMAND;
1162
1163  out:
1164   silc_client_command_free(cmd);
1165 }
1166
1167 /* Command USERS. Requests the USERS of the clients joined on requested
1168    channel. */
1169
1170 SILC_CLIENT_CMD_FUNC(users)
1171 {
1172   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1173   SilcClientConnection conn = cmd->conn;
1174   SilcIDCacheEntry id_cache = NULL;
1175   SilcBuffer buffer, idp;
1176   char *name;
1177
1178   if (!cmd->conn) {
1179     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1180     COMMAND_ERROR;
1181     goto out;
1182   }
1183
1184   if (cmd->argc != 2) {
1185     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1186     COMMAND_ERROR;
1187     goto out;
1188   }
1189
1190   if (cmd->argv[1][0] == '*') {
1191     if (!conn->current_channel) {
1192       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1193       COMMAND_ERROR;
1194       goto out;
1195     }
1196     name = conn->current_channel->channel_name;
1197   } else {
1198     name = cmd->argv[1];
1199   }
1200
1201   if (!conn->current_channel) {
1202     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1203     COMMAND_ERROR;
1204     goto out;
1205   }
1206
1207   /* Get the Channel ID of the channel */
1208   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1209     /* XXX should resolve the channel ID; LIST command */
1210     cmd->client->ops->say(cmd->client, conn, 
1211                           "You are not on that channel", name);
1212     COMMAND_ERROR;
1213     goto out;
1214   }
1215
1216   /* Send USERS command to the server */
1217   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1218   buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1, 
1219                                           1, idp->data, idp->len);
1220   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1221                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1222   silc_buffer_free(buffer);
1223   silc_buffer_free(idp);
1224
1225   /* Register dummy pending command that will tell the reply command
1226      that user called this command. Server may send reply to this command
1227      even if user did not send this command thus we want to handle things
1228      differently when user sent the command. This is dummy and won't be
1229      executed. */
1230   /* XXX this is kludge and should be removed after pending command reply 
1231      support is added. Currently only commands may be pending not command
1232      replies. */
1233   silc_client_command_pending(conn, SILC_COMMAND_USERS, 0, 
1234                               silc_client_command_users, NULL);
1235
1236   /* Notify application */
1237   COMMAND;
1238
1239  out:
1240   silc_client_command_free(cmd);
1241 }