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 - 2001 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 #include "client_internal.h"
24
25 /* Client command list. */
26 SilcClientCommand silc_command_list[] =
27 {
28   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
29   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
30   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
31                   SILC_CF_LAG | SILC_CF_REG, 3),
32   SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
33   SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
34   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
35   SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
36   SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
37   SILC_CLIENT_CMD(kill, KILL, "KILL", 
38                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
39   SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
40   SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
41                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
42   SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
43   SILC_CLIENT_CMD(oper, OPER, "OPER",
44                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
45   SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
46   SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
47   SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
48   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
49   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
50   SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
51   SILC_CLIENT_CMD(restart, RESTART, "RESTART",
52                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
53   SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
54                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
55   SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
56                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
57   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER",
58                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 3),
59   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
60   SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
61
62   { NULL, 0, NULL, 0, 0 },
63 };
64
65 #define SILC_NOT_CONNECTED(x, c) \
66   x->ops->say((x), (c), \
67            "You are not connected to a server, use /SERVER to connect");
68
69 /* Command operation that is called at the end of all commands. 
70    Usage: COMMAND; */
71 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
72   cmd, TRUE, cmd->command->cmd)
73
74 /* Error to application. Usage: COMMAND_ERROR; */
75 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
76   cmd, FALSE, cmd->command->cmd)
77
78 /* Generic function to send any command. The arguments must be sent already
79    encoded into correct form and in correct order. */
80
81 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
82                               SilcCommand command, unsigned short ident,
83                               unsigned int argc, ...)
84 {
85   SilcBuffer packet;
86   va_list ap;
87
88   va_start(ap, argc);
89
90   packet = silc_command_payload_encode_vap(command, ident, argc, ap);
91   silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
92                           NULL, 0, NULL, NULL, packet->data, 
93                           packet->len, TRUE);
94   silc_buffer_free(packet);
95 }
96
97 /* Finds and returns a pointer to the command list. Return NULL if the
98    command is not found. */
99
100 SilcClientCommand *silc_client_command_find(const char *name)
101 {
102   SilcClientCommand *cmd;
103
104   for (cmd = silc_command_list; cmd->name; cmd++) {
105     if (!strcmp(cmd->name, name))
106       return cmd;
107   }
108
109   return NULL;
110 }
111
112 /* Add new pending command to be executed when reply to a command has been
113    received.  The `reply_cmd' is the command that will call the `callback'
114    with `context' when reply has been received.  If `ident is non-zero
115    the `callback' will be executed when received reply with command 
116    identifier `ident'. */
117
118 void silc_client_command_pending(SilcClientConnection conn,
119                                  SilcCommand reply_cmd,
120                                  unsigned short ident,
121                                  SilcClientPendingDestructor destructor,
122                                  SilcCommandCb callback,
123                                  void *context)
124 {
125   SilcClientCommandPending *reply;
126
127   reply = silc_calloc(1, sizeof(*reply));
128   reply->reply_cmd = reply_cmd;
129   reply->ident = ident;
130   reply->context = context;
131   reply->callback = callback;
132   reply->destructor = destructor;
133   silc_dlist_add(conn->pending_commands, reply);
134 }
135
136 /* Deletes pending command by reply command type. */
137
138 void silc_client_command_pending_del(SilcClientConnection conn,
139                                      SilcCommand reply_cmd,
140                                      unsigned short ident)
141 {
142   SilcClientCommandPending *r;
143
144   silc_dlist_start(conn->pending_commands);
145   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
146     if (r->reply_cmd == reply_cmd && r->ident == ident) {
147       silc_dlist_del(conn->pending_commands, r);
148       break;
149     }
150   }
151 }
152
153 /* Checks for pending commands and marks callbacks to be called from
154    the command reply function. Returns TRUE if there were pending command. */
155
156 int silc_client_command_pending_check(SilcClientConnection conn,
157                                       SilcClientCommandReplyContext ctx,
158                                       SilcCommand command, 
159                                       unsigned short ident)
160 {
161   SilcClientCommandPending *r;
162
163   silc_dlist_start(conn->pending_commands);
164   while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
165     if (r->reply_cmd == command && r->ident == ident) {
166       ctx->context = r->context;
167       ctx->callback = r->callback;
168       ctx->destructor = r->destructor;
169       ctx->ident = ident;
170       return TRUE;
171     }
172   }
173
174   return FALSE;
175 }
176
177 /* Allocate Command Context */
178
179 SilcClientCommandContext silc_client_command_alloc()
180 {
181   SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
182   ctx->users++;
183   return ctx;
184 }
185
186 /* Free command context and its internals */
187
188 void silc_client_command_free(SilcClientCommandContext ctx)
189 {
190   ctx->users--;
191   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
192                   ctx->users));
193   if (ctx->users < 1) {
194     int i;
195
196     for (i = 0; i < ctx->argc; i++)
197       silc_free(ctx->argv[i]);
198     silc_free(ctx);
199   }
200 }
201
202 /* Duplicate Command Context by adding reference counter. The context won't
203    be free'd untill it hits zero. */
204
205 SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
206 {
207   ctx->users++;
208   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
209                   ctx->users));
210   return ctx;
211 }
212
213 /* Pending command destructor. */
214
215 static void silc_client_command_destructor(void *context)
216 {
217   silc_client_command_free((SilcClientCommandContext)context);
218 }
219
220 /* silc_client_get_client completion callback */
221 void silc_client_command_completion(SilcClient client,
222                                     SilcClientConnection conn,
223                                     SilcClientEntry clients,
224                                     unsigned int clients_count,
225                                     void *context)
226 {
227
228 }
229
230 /* Command WHOIS. This command is used to query information about 
231    specific user. */
232
233 SILC_CLIENT_CMD_FUNC(whois)
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: /WHOIS <nickname>[@<server>] [<count>]");
248     COMMAND_ERROR;
249     goto out;
250   }
251
252   buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
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 WHOWAS. This command is used to query history information about
272    specific user that used to exist in the network. */
273
274 SILC_CLIENT_CMD_FUNC(whowas)
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 (cmd->argc < 2 || cmd->argc > 3) {
287     cmd->client->ops->say(cmd->client, conn, 
288              "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
289     COMMAND_ERROR;
290     goto out;
291   }
292
293   buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
294                                        cmd->argc - 1, ++cmd->argv,
295                                        ++cmd->argv_lens, ++cmd->argv_types,
296                                        0);
297   silc_client_packet_send(cmd->client, cmd->conn->sock,
298                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
299                           buffer->data, buffer->len, TRUE);
300   silc_buffer_free(buffer);
301   cmd->argv--;
302   cmd->argv_lens--;
303   cmd->argv_types--;
304
305   /* Notify application */
306   COMMAND;
307
308  out:
309   silc_client_command_free(cmd);
310 }
311
312 /* Command IDENTIFY. This command is used to query information about 
313    specific user, especially ID's. */
314
315 SILC_CLIENT_CMD_FUNC(identify)
316 {
317   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
318   SilcClientConnection conn = cmd->conn;
319   SilcBuffer buffer;
320
321   if (!cmd->conn) {
322     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
323     COMMAND_ERROR;
324     goto out;
325   }
326
327   if (cmd->argc < 2 || cmd->argc > 3) {
328     cmd->client->ops->say(cmd->client, conn,
329              "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
330     COMMAND_ERROR;
331     goto out;
332   }
333
334   buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
335                                        cmd->argc - 1, ++cmd->argv,
336                                        ++cmd->argv_lens, ++cmd->argv_types,
337                                        ++conn->cmd_ident);
338   silc_client_packet_send(cmd->client, cmd->conn->sock,
339                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
340                           buffer->data, buffer->len, TRUE);
341   silc_buffer_free(buffer);
342   cmd->argv--;
343   cmd->argv_lens--;
344   cmd->argv_types--;
345
346   /* Notify application */
347   COMMAND;
348
349  out:
350   silc_client_command_free(cmd);
351 }
352
353 /* Command NICK. Shows current nickname/sets new nickname on current
354    window. */
355
356 SILC_CLIENT_CMD_FUNC(nick)
357 {
358   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
359   SilcClientConnection conn = cmd->conn;
360   SilcBuffer buffer;
361
362   if (!cmd->conn) {
363     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
364     COMMAND_ERROR;
365     goto out;
366   }
367
368   if (!strcmp(conn->nickname, cmd->argv[1]))
369     goto out;
370
371   /* Show current nickname */
372   if (cmd->argc < 2) {
373     if (cmd->conn) {
374       cmd->client->ops->say(cmd->client, conn, 
375                             "Your nickname is %s on server %s", 
376                             conn->nickname, conn->remote_host);
377     } else {
378       cmd->client->ops->say(cmd->client, conn, 
379                             "Your nickname is %s", conn->nickname);
380     }
381
382     /* XXX Notify application */
383     COMMAND;
384     goto out;
385   }
386
387   /* Set new nickname */
388   buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
389                                        cmd->argc - 1, ++cmd->argv,
390                                        ++cmd->argv_lens, ++cmd->argv_types,
391                                        ++cmd->conn->cmd_ident);
392   silc_client_packet_send(cmd->client, cmd->conn->sock,
393                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
394                           buffer->data, buffer->len, TRUE);
395   silc_buffer_free(buffer);
396   cmd->argv--;
397   cmd->argv_lens--;
398   cmd->argv_types--;
399   if (conn->nickname)
400     silc_free(conn->nickname);
401   conn->nickname = strdup(cmd->argv[1]);
402
403   /* Notify application */
404   COMMAND;
405
406  out:
407   silc_client_command_free(cmd);
408 }
409
410 /* Command LIST. Lists channels on the current server. */
411
412 SILC_CLIENT_CMD_FUNC(list)
413 {
414   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
415   SilcClientConnection conn = cmd->conn;
416   SilcIDCacheEntry id_cache = NULL;
417   SilcChannelEntry channel;
418   SilcBuffer buffer, idp = NULL;
419   char *name;
420
421   if (!cmd->conn) {
422     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
423     COMMAND_ERROR;
424     goto out;
425   }
426
427   if (cmd->argc == 2) {
428     name = cmd->argv[1];
429
430     /* Get the Channel ID of the channel */
431     if (silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
432       channel = (SilcChannelEntry)id_cache->context;
433       idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
434     }
435   }
436
437   if (!idp)
438     buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, 
439                                             ++conn->cmd_ident, 0);
440   else
441     buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, 
442                                             ++conn->cmd_ident, 1,
443                                             1, idp->data, idp->len);
444
445   silc_client_packet_send(cmd->client, cmd->conn->sock,
446                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
447                           buffer->data, buffer->len, TRUE);
448   silc_buffer_free(buffer);
449   if (idp)
450     silc_buffer_free(idp);
451
452   /* Notify application */
453   COMMAND;
454
455  out:
456   silc_client_command_free(cmd);
457 }
458
459 /* Command TOPIC. Sets/shows topic on a channel. */
460
461 SILC_CLIENT_CMD_FUNC(topic)
462 {
463   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
464   SilcClientConnection conn = cmd->conn;
465   SilcIDCacheEntry id_cache = NULL;
466   SilcChannelEntry channel;
467   SilcBuffer buffer, idp;
468   char *name;
469
470   if (!cmd->conn) {
471     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
472     COMMAND_ERROR;
473     goto out;
474   }
475
476   if (cmd->argc < 2 || cmd->argc > 3) {
477     cmd->client->ops->say(cmd->client, conn,
478                           "Usage: /TOPIC <channel> [<topic>]");
479     COMMAND_ERROR;
480     goto out;
481   }
482
483   if (cmd->argv[1][0] == '*') {
484     if (!conn->current_channel) {
485       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
486       COMMAND_ERROR;
487       goto out;
488     }
489     name = conn->current_channel->channel_name;
490   } else {
491     name = cmd->argv[1];
492   }
493
494   if (!conn->current_channel) {
495     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
496     COMMAND_ERROR;
497     goto out;
498   }
499
500   /* Get the Channel ID of the channel */
501   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
502     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
503     COMMAND_ERROR;
504     goto out;
505   }
506
507   channel = (SilcChannelEntry)id_cache->context;
508
509   /* Send TOPIC command to the server */
510   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
511   if (cmd->argc > 2)
512     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2, 
513                                             1, idp->data, idp->len,
514                                             2, cmd->argv[2], 
515                                             strlen(cmd->argv[2]));
516   else
517     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1, 
518                                             1, idp->data, idp->len,
519                                             0);
520   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
521                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
522   silc_buffer_free(buffer);
523   silc_buffer_free(idp);
524
525   /* Notify application */
526   COMMAND;
527
528  out:
529   silc_client_command_free(cmd);
530 }
531
532 /* Command INVITE. Invites specific client to join a channel. */
533
534 SILC_CLIENT_CMD_FUNC(invite)
535 {
536   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
537   SilcClient client = cmd->client;
538   SilcClientConnection conn = cmd->conn;
539   SilcClientEntry client_entry;
540   SilcChannelEntry channel_entry;
541   SilcBuffer buffer, clidp, chidp;
542   unsigned int num = 0;
543   char *nickname = NULL, *server = NULL;
544
545   if (!cmd->conn) {
546     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
547     COMMAND_ERROR;
548     goto out;
549   }
550
551   if (cmd->argc != 3) {
552     cmd->client->ops->say(cmd->client, conn,
553                           "Usage: /INVITE <nickname>[@<server>] <channel>");
554     COMMAND_ERROR;
555     goto out;
556   }
557
558   /* Parse the typed nickname. */
559   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
560     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
561     COMMAND_ERROR;
562     goto out;
563   }
564
565   /* Find client entry */
566   client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
567                                         TRUE);
568   if (!client_entry) {
569     if (nickname)
570       silc_free(nickname);
571     if (server)
572       silc_free(server);
573
574     /* Client entry not found, it was requested thus mark this to be
575        pending command. */
576     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
577                                 silc_client_command_destructor,
578                                 silc_client_command_invite, 
579                                 silc_client_command_dup(cmd));
580     cmd->pending = 1;
581     return;
582   }
583
584   /* Find channel entry */
585   channel_entry = silc_client_get_channel(client, conn, cmd->argv[2]);
586   if (!channel_entry) {
587     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
588     COMMAND_ERROR;
589     goto out;
590   }
591
592   /* Send command */
593   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
594   chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
595   buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
596                                           1, clidp->data, clidp->len,
597                                           2, chidp->data, chidp->len);
598   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
599                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
600   silc_buffer_free(buffer);
601   silc_buffer_free(clidp);
602   silc_buffer_free(chidp);
603
604   cmd->client->ops->say(cmd->client, conn, 
605                         "Inviting %s to channel %s", cmd->argv[1], 
606                         cmd->argv[2]);
607
608   /* Notify application */
609   COMMAND;
610
611  out:
612   if (nickname)
613     silc_free(nickname);
614   if (server)
615     silc_free(server);
616   silc_client_command_free(cmd);
617 }
618
619 typedef struct {
620   SilcClient client;
621   SilcClientConnection conn;
622 } *QuitInternal;
623
624 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
625 {
626   QuitInternal q = (QuitInternal)context;
627
628   /* Close connection */
629   q->client->ops->disconnect(q->client, q->conn);
630   silc_client_close_connection(q->client, q->conn->sock->user_data);
631
632   silc_free(q);
633 }
634
635 /* Command QUIT. Closes connection with current server. */
636  
637 SILC_CLIENT_CMD_FUNC(quit)
638 {
639   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
640   SilcBuffer buffer;
641   QuitInternal q;
642
643   if (!cmd->conn) {
644     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
645     COMMAND_ERROR;
646     goto out;
647   }
648
649   if (cmd->argc > 1)
650     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, 
651                                          &cmd->argv[1], &cmd->argv_lens[1],
652                                          &cmd->argv_types[1], 0);
653   else
654     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
655                                          NULL, NULL, NULL, 0);
656   silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
657                           NULL, 0, NULL, NULL, 
658                           buffer->data, buffer->len, TRUE);
659   silc_buffer_free(buffer);
660
661   q = silc_calloc(1, sizeof(*q));
662   q->client = cmd->client;
663   q->conn = cmd->conn;
664
665   /* We quit the connection with little timeout */
666   silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
667                      silc_client_command_quit_cb, (void *)q,
668                      1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
669
670   /* Notify application */
671   COMMAND;
672
673  out:
674   silc_client_command_free(cmd);
675 }
676
677 /* Command KILL. Router operator can use this command to remove an client
678    fromthe SILC Network. */
679
680 SILC_CLIENT_CMD_FUNC(kill)
681 {
682   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
683   SilcClientConnection conn = cmd->conn;
684   SilcBuffer buffer, idp;
685   SilcClientEntry target;
686   unsigned int num = 0;
687   char *nickname = NULL, *server = NULL;
688
689   if (!cmd->conn) {
690     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
691     COMMAND_ERROR;
692     goto out;
693   }
694
695   if (cmd->argc < 2) {
696     cmd->client->ops->say(cmd->client, conn, 
697                           "Usage: /KILL <nickname> [<comment>]");
698     COMMAND_ERROR;
699     goto out;
700   }
701
702   /* Parse the typed nickname. */
703   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
704     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
705     COMMAND_ERROR;
706     goto out;
707   }
708
709   /* Get the target client */
710   target = silc_idlist_get_client(cmd->client, conn, nickname, 
711                                   server, num, TRUE);
712   if (!target) {
713     silc_free(nickname);
714     if (server)
715       silc_free(server);
716
717     /* Client entry not found, it was requested thus mark this to be
718        pending command. */
719     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
720                                 conn->cmd_ident,  
721                                 silc_client_command_destructor,
722                                 silc_client_command_kill, 
723                                 silc_client_command_dup(cmd));
724     cmd->pending = 1;
725     return;
726   }
727
728   /* Send the KILL command to the server */
729   idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
730   if (cmd->argc == 2)
731     buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 1, 
732                                             1, idp->data, idp->len);
733   else
734     buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 2, 
735                                             1, idp->data, idp->len,
736                                             2, cmd->argv[2], 
737                                             strlen(cmd->argv[2]));
738   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
739                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
740   silc_buffer_free(buffer);
741   silc_buffer_free(idp);
742
743   /* Notify application */
744   COMMAND;
745
746   /* Remove the client entry to be killed */
747   silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
748                          target->id);
749   if (target->nickname)
750     silc_free(target->nickname);
751   if (target->server)
752     silc_free(target->server);
753   if (target->id)
754     silc_free(target->id);
755   if (target->send_key)
756     silc_cipher_free(target->send_key);
757   if (target->receive_key)
758     silc_cipher_free(target->receive_key);
759   silc_free(target);
760
761  out:
762   if (nickname)
763     silc_free(nickname);
764   if (server)
765     silc_free(server);
766   silc_client_command_free(cmd);
767 }
768
769 /* Command INFO. Request information about specific server. If specific
770    server is not provided the current server is used. */
771
772 SILC_CLIENT_CMD_FUNC(info)
773 {
774   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
775   SilcClientConnection conn = cmd->conn;
776   SilcBuffer buffer;
777   char *name;
778
779   if (!cmd->conn) {
780     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
781     COMMAND_ERROR;
782     goto out;
783   }
784
785   if (cmd->argc < 2)
786     name = strdup(conn->remote_host);
787   else
788     name = strdup(cmd->argv[1]);
789
790   /* Send the command */
791   buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
792                                           1, name, strlen(name));
793   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
794                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
795   silc_buffer_free(buffer);
796
797   /* Notify application */
798   COMMAND;
799
800  out:
801   silc_client_command_free(cmd);
802 }
803
804 /* Command PING. Sends ping to server. This is used to test the 
805    communication channel. */
806
807 SILC_CLIENT_CMD_FUNC(ping)
808 {
809   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
810   SilcClientConnection conn = cmd->conn;
811   SilcBuffer buffer;
812   void *id;
813   int i;
814   char *name = NULL;
815
816   if (!cmd->conn) {
817     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
818     COMMAND_ERROR;
819     goto out;
820   }
821
822   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
823     name = strdup(conn->remote_host);
824
825   /* Send the command */
826   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
827                                           1, conn->remote_id_data, 
828                                           SILC_ID_SERVER_LEN);
829   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
830                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
831   silc_buffer_free(buffer);
832
833   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
834                       SILC_ID_SERVER);
835   if (!id) {
836     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
837     COMMAND_ERROR;
838     goto out;
839   }
840
841   /* Start counting time */
842   for (i = 0; i < conn->ping_count; i++) {
843     if (conn->ping[i].dest_id == NULL) {
844       conn->ping[i].start_time = time(NULL);
845       conn->ping[i].dest_id = id;
846       conn->ping[i].dest_name = name;
847       conn->ping_count++;
848       break;
849     }
850   }
851   if (i >= conn->ping_count) {
852     i = conn->ping_count;
853     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
854     conn->ping[i].start_time = time(NULL);
855     conn->ping[i].dest_id = id;
856     conn->ping[i].dest_name = name;
857     conn->ping_count++;
858   }
859   
860   /* Notify application */
861   COMMAND;
862
863  out:
864   silc_client_command_free(cmd);
865 }
866
867 SILC_CLIENT_CMD_FUNC(notice)
868 {
869 }
870
871 /* Command JOIN. Joins to a channel. */
872
873 SILC_CLIENT_CMD_FUNC(join)
874 {
875   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
876   SilcClientConnection conn = cmd->conn;
877   SilcIDCacheEntry id_cache = NULL;
878   SilcBuffer buffer, idp;
879
880   if (!cmd->conn) {
881     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
882     COMMAND_ERROR;
883     goto out;
884   }
885
886   if (cmd->argc < 2) {
887     /* Show channels currently joined to */
888
889     goto out;
890   }
891
892   /* See if we have joined to the requested channel already */
893   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
894                                     &id_cache)) {
895     cmd->client->ops->say(cmd->client, conn, 
896                           "You are talking to channel %s", cmd->argv[1]);
897     conn->current_channel = (SilcChannelEntry)id_cache->context;
898 #if 0
899     cmd->client->screen->bottom_line->channel = cmd->argv[1];
900     silc_screen_print_bottom_line(cmd->client->screen, 0);
901 #endif
902     goto out;
903   }
904
905   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
906
907   /* Send JOIN command to the server */
908   if (cmd->argc == 2)
909     buffer = 
910       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
911                                      1, cmd->argv[1], cmd->argv_lens[1],
912                                      2, idp->data, idp->len);
913   else if (cmd->argc == 3)
914     /* XXX Buggy */
915     buffer = 
916       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
917                                      1, cmd->argv[1], cmd->argv_lens[1],
918                                      2, idp->data, idp->len,
919                                      3, cmd->argv[2], cmd->argv_lens[2]);
920   else
921     buffer = 
922       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
923                                      1, cmd->argv[1], cmd->argv_lens[1],
924                                      2, idp->data, idp->len,
925                                      3, cmd->argv[2], cmd->argv_lens[2],
926                                      4, cmd->argv[3], cmd->argv_lens[3]);
927
928   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
929                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
930   silc_buffer_free(buffer);
931   silc_buffer_free(idp);
932
933   /* Notify application */
934   COMMAND;
935
936  out:
937   silc_client_command_free(cmd);
938 }
939
940 /* MOTD command. Requests motd from server. */
941
942 SILC_CLIENT_CMD_FUNC(motd)
943 {
944   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
945   SilcClientConnection conn = cmd->conn;
946   SilcBuffer buffer;
947
948   if (!cmd->conn) {
949     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
950     COMMAND_ERROR;
951     goto out;
952   }
953
954   if (cmd->argc < 1 || cmd->argc > 2) {
955     cmd->client->ops->say(cmd->client, conn,
956                           "Usage: /MOTD [<server>]");
957     COMMAND_ERROR;
958     goto out;
959   }
960
961   /* Send TOPIC command to the server */
962   if (cmd->argc == 1)
963     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
964                                             1, conn->remote_host, 
965                                             strlen(conn->remote_host));
966   else
967     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
968                                             1, cmd->argv[1], 
969                                             cmd->argv_lens[1]);
970   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
971                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
972   silc_buffer_free(buffer);
973
974   /* Notify application */
975   COMMAND;
976
977  out:
978   silc_client_command_free(cmd);
979 }
980
981 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
982    modes as client cannot set itself server/router operator privileges. */
983
984 SILC_CLIENT_CMD_FUNC(umode)
985 {
986   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
987   SilcClientConnection conn = cmd->conn;
988   SilcBuffer buffer, idp;
989   unsigned char *cp, modebuf[4];
990   unsigned int mode, add, len;
991   int i;
992
993   if (!cmd->conn) {
994     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
995     COMMAND_ERROR;
996     goto out;
997   }
998
999   if (cmd->argc < 2) {
1000     cmd->client->ops->say(cmd->client, conn, 
1001                   "Usage: /UMODE +|-<modes>");
1002     COMMAND_ERROR;
1003     goto out;
1004   }
1005
1006   mode = conn->local_entry->mode;
1007
1008   /* Are we adding or removing mode */
1009   if (cmd->argv[1][0] == '-')
1010     add = FALSE;
1011   else
1012     add = TRUE;
1013
1014   /* Parse mode */
1015   cp = cmd->argv[1] + 1;
1016   len = strlen(cp);
1017   for (i = 0; i < len; i++) {
1018     switch(cp[i]) {
1019     case 'a':
1020       if (add) {
1021         mode = 0;
1022         mode |= SILC_UMODE_SERVER_OPERATOR;
1023         mode |= SILC_UMODE_ROUTER_OPERATOR;
1024       } else {
1025         mode = SILC_UMODE_NONE;
1026       }
1027       break;
1028     case 's':
1029       if (add)
1030         mode |= SILC_UMODE_SERVER_OPERATOR;
1031       else
1032         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1033       break;
1034     case 'r':
1035       if (add)
1036         mode |= SILC_UMODE_ROUTER_OPERATOR;
1037       else
1038         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1039       break;
1040     default:
1041       COMMAND_ERROR;
1042       goto out;
1043       break;
1044     }
1045   }
1046
1047   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1048   SILC_PUT32_MSB(mode, modebuf);
1049
1050   /* Send the command packet. We support sending only one mode at once
1051      that requires an argument. */
1052   buffer = 
1053     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1054                                    1, idp->data, idp->len, 
1055                                    2, modebuf, sizeof(modebuf));
1056   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1057                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1058   silc_buffer_free(buffer);
1059   silc_buffer_free(idp);
1060
1061   /* Notify application */
1062   COMMAND;
1063
1064  out:
1065   silc_client_command_free(cmd);
1066 }
1067
1068 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1069    can be set several at once. Those modes that require argument must be set
1070    separately (unless set with modes that does not require arguments). */
1071
1072 SILC_CLIENT_CMD_FUNC(cmode)
1073 {
1074   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1075   SilcClientConnection conn = cmd->conn;
1076   SilcChannelEntry channel;
1077   SilcBuffer buffer, chidp;
1078   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1079   unsigned int mode, add, type, len, arg_len = 0;
1080   int i;
1081
1082   if (!cmd->conn) {
1083     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1084     COMMAND_ERROR;
1085     goto out;
1086   }
1087
1088   if (cmd->argc < 3) {
1089     cmd->client->ops->say(cmd->client, conn, 
1090                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1091     COMMAND_ERROR;
1092     goto out;
1093   }
1094
1095   if (cmd->argv[1][0] == '*') {
1096     if (!conn->current_channel) {
1097       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1098       COMMAND_ERROR;
1099       goto out;
1100     }
1101
1102     channel = conn->current_channel;
1103   } else {
1104     name = cmd->argv[1];
1105
1106     channel = silc_client_get_channel(cmd->client, conn, name);
1107     if (!channel) {
1108       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1109       COMMAND_ERROR;
1110       goto out;
1111     }
1112   }
1113
1114   mode = channel->mode;
1115
1116   /* Are we adding or removing mode */
1117   if (cmd->argv[2][0] == '-')
1118     add = FALSE;
1119   else
1120     add = TRUE;
1121
1122   /* Argument type to be sent to server */
1123   type = 0;
1124
1125   /* Parse mode */
1126   cp = cmd->argv[2] + 1;
1127   len = strlen(cp);
1128   for (i = 0; i < len; i++) {
1129     switch(cp[i]) {
1130     case 'p':
1131       if (add)
1132         mode |= SILC_CHANNEL_MODE_PRIVATE;
1133       else
1134         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1135       break;
1136     case 's':
1137       if (add)
1138         mode |= SILC_CHANNEL_MODE_SECRET;
1139       else
1140         mode &= ~SILC_CHANNEL_MODE_SECRET;
1141       break;
1142     case 'k':
1143       if (add)
1144         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1145       else
1146         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1147       break;
1148     case 'i':
1149       if (add)
1150         mode |= SILC_CHANNEL_MODE_INVITE;
1151       else
1152         mode &= ~SILC_CHANNEL_MODE_INVITE;
1153       break;
1154     case 't':
1155       if (add)
1156         mode |= SILC_CHANNEL_MODE_TOPIC;
1157       else
1158         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1159       break;
1160     case 'l':
1161       if (add) {
1162         int ll;
1163         mode |= SILC_CHANNEL_MODE_ULIMIT;
1164         type = 3;
1165         ll = atoi(cmd->argv[3]);
1166         SILC_PUT32_MSB(ll, tmp);
1167         arg = tmp;
1168         arg_len = 4;
1169       } else {
1170         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1171       }
1172       break;
1173     case 'a':
1174       if (add) {
1175         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1176         type = 4;
1177         arg = cmd->argv[3];
1178         arg_len = cmd->argv_lens[3];
1179       } else {
1180         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1181       }
1182       break;
1183     case 'b':
1184       if (add) {
1185         mode |= SILC_CHANNEL_MODE_BAN;
1186         type = 5;
1187         arg = cmd->argv[3];
1188         arg_len = cmd->argv_lens[3];
1189       } else {
1190         mode &= ~SILC_CHANNEL_MODE_BAN;
1191       }
1192       break;
1193     case 'I':
1194       if (add) {
1195         mode |= SILC_CHANNEL_MODE_INVITE_LIST;
1196         type = 6;
1197         arg = cmd->argv[3];
1198         arg_len = cmd->argv_lens[3];
1199       } else {
1200         mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
1201       }
1202       break;
1203     case 'c':
1204       if (add) {
1205         mode |= SILC_CHANNEL_MODE_CIPHER;
1206         type = 8;
1207         arg = cmd->argv[3];
1208         arg_len = cmd->argv_lens[3];
1209       } else {
1210         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1211       }
1212       break;
1213     default:
1214       COMMAND_ERROR;
1215       goto out;
1216       break;
1217     }
1218   }
1219
1220   if (type && cmd->argc < 3) {
1221     COMMAND_ERROR;
1222     goto out;
1223   }
1224
1225   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1226   SILC_PUT32_MSB(mode, modebuf);
1227
1228   /* Send the command packet. We support sending only one mode at once
1229      that requires an argument. */
1230   if (type && arg) {
1231     buffer = 
1232       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1233                                      1, chidp->data, chidp->len, 
1234                                      2, modebuf, sizeof(modebuf),
1235                                      type, arg, arg_len);
1236   } else {
1237     buffer = 
1238       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1239                                      1, chidp->data, chidp->len, 
1240                                      2, modebuf, sizeof(modebuf));
1241   }
1242
1243   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1244                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1245   silc_buffer_free(buffer);
1246   silc_buffer_free(chidp);
1247
1248   /* Notify application */
1249   COMMAND;
1250
1251  out:
1252   silc_client_command_free(cmd);
1253 }
1254
1255 /* CUMODE command. Changes client's mode on a channel. */
1256
1257 SILC_CLIENT_CMD_FUNC(cumode)
1258 {
1259   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1260   SilcClientConnection conn = cmd->conn;
1261   SilcChannelEntry channel;
1262   SilcChannelUser chu;
1263   SilcClientEntry client_entry;
1264   SilcBuffer buffer, clidp, chidp;
1265   unsigned char *name, *cp, modebuf[4];
1266   unsigned int mode = 0, add, len;
1267   char *nickname = NULL, *server = NULL;
1268   unsigned int num = 0;
1269   int i;
1270
1271   if (!cmd->conn) {
1272     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1273     COMMAND_ERROR;
1274     goto out;
1275   }
1276
1277   if (cmd->argc < 4) {
1278     cmd->client->ops->say(cmd->client, conn, 
1279                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1280     COMMAND_ERROR;
1281     goto out;
1282   }
1283
1284   if (cmd->argv[1][0] == '*') {
1285     if (!conn->current_channel) {
1286       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1287       COMMAND_ERROR;
1288       goto out;
1289     }
1290
1291     channel = conn->current_channel;
1292   } else {
1293     name = cmd->argv[1];
1294
1295     channel = silc_client_get_channel(cmd->client, conn, name);
1296     if (!channel) {
1297       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1298       COMMAND_ERROR;
1299       goto out;
1300     }
1301   }
1302
1303   /* Parse the typed nickname. */
1304   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1305     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1306     COMMAND_ERROR;
1307     goto out;
1308   }
1309
1310   /* Find client entry */
1311   client_entry = silc_idlist_get_client(cmd->client, conn, 
1312                                         nickname, server, num, TRUE);
1313   if (!client_entry) {
1314     /* Client entry not found, it was requested thus mark this to be
1315        pending command. */
1316     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1317                                 conn->cmd_ident,  
1318                                 silc_client_command_destructor,
1319                                 silc_client_command_cumode, 
1320                                 silc_client_command_dup(cmd));
1321     cmd->pending = 1;
1322     return;
1323   }
1324   
1325   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1326     if (chu->client == client_entry) {
1327       chu->mode = mode;
1328       break;
1329     }
1330   }
1331
1332   /* Are we adding or removing mode */
1333   if (cmd->argv[2][0] == '-')
1334     add = FALSE;
1335   else
1336     add = TRUE;
1337
1338   /* Parse mode */
1339   cp = cmd->argv[2] + 1;
1340   len = strlen(cp);
1341   for (i = 0; i < len; i++) {
1342     switch(cp[i]) {
1343     case 'a':
1344       if (add) {
1345         mode |= SILC_CHANNEL_UMODE_CHANFO;
1346         mode |= SILC_CHANNEL_UMODE_CHANOP;
1347       } else {
1348         mode = SILC_CHANNEL_UMODE_NONE;
1349       }
1350       break;
1351     case 'f':
1352       if (add)
1353         mode |= SILC_CHANNEL_UMODE_CHANFO;
1354       else
1355         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1356       break;
1357     case 'o':
1358       if (add)
1359         mode |= SILC_CHANNEL_UMODE_CHANOP;
1360       else
1361         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1362       break;
1363     default:
1364       COMMAND_ERROR;
1365       goto out;
1366       break;
1367     }
1368   }
1369
1370   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1371   SILC_PUT32_MSB(mode, modebuf);
1372   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1373
1374   /* Send the command packet. We support sending only one mode at once
1375      that requires an argument. */
1376   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1377                                           1, chidp->data, chidp->len, 
1378                                           2, modebuf, 4,
1379                                           3, clidp->data, clidp->len);
1380
1381   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1382                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1383   silc_buffer_free(buffer);
1384   silc_buffer_free(chidp);
1385   silc_buffer_free(clidp);
1386   
1387   /* Notify application */
1388   COMMAND;
1389
1390  out:
1391   if (nickname)
1392     silc_free(nickname);
1393   if (server)
1394     silc_free(server);
1395   silc_client_command_free(cmd);
1396 }
1397
1398 /* KICK command. Kicks a client out of channel. */
1399
1400 SILC_CLIENT_CMD_FUNC(kick)
1401 {
1402   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1403   SilcClientConnection conn = cmd->conn;
1404   SilcIDCacheEntry id_cache = NULL;
1405   SilcChannelEntry channel;
1406   SilcBuffer buffer, idp, idp2;
1407   SilcClientEntry target;
1408   char *name;
1409   unsigned int num = 0;
1410   char *nickname = NULL, *server = NULL;
1411
1412   if (!cmd->conn) {
1413     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1414     COMMAND_ERROR;
1415     goto out;
1416   }
1417
1418   if (cmd->argc < 3) {
1419     cmd->client->ops->say(cmd->client, conn, 
1420                           "Usage: /KICK <channel> <nickname> [<comment>]");
1421     COMMAND_ERROR;
1422     goto out;
1423   }
1424
1425   if (cmd->argv[1][0] == '*') {
1426     if (!conn->current_channel) {
1427       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1428       COMMAND_ERROR;
1429       goto out;
1430     }
1431     name = conn->current_channel->channel_name;
1432   } else {
1433     name = cmd->argv[1];
1434   }
1435
1436   if (!conn->current_channel) {
1437     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1438     COMMAND_ERROR;
1439     goto out;
1440   }
1441
1442   /* Get the Channel ID of the channel */
1443   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1444     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1445     COMMAND_ERROR;
1446     goto out;
1447   }
1448
1449   channel = (SilcChannelEntry)id_cache->context;
1450
1451   /* Parse the typed nickname. */
1452   if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1453     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1454     COMMAND_ERROR;
1455     goto out;
1456   }
1457
1458   /* Get the target client */
1459   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1460                                   server, num, FALSE);
1461   if (!target) {
1462     cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1463                           cmd->argv[2]);
1464     COMMAND_ERROR;
1465     goto out;
1466   }
1467
1468   /* Send KICK command to the server */
1469   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1470   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1471   if (cmd->argc == 3)
1472     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1473                                             1, idp->data, idp->len,
1474                                             2, idp2->data, idp2->len);
1475   else
1476     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1477                                             1, idp->data, idp->len,
1478                                             2, idp2->data, idp2->len,
1479                                             3, cmd->argv[3], 
1480                                             strlen(cmd->argv[3]));
1481   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1482                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1483   silc_buffer_free(buffer);
1484   silc_buffer_free(idp);
1485   silc_buffer_free(idp2);
1486
1487   /* Notify application */
1488   COMMAND;
1489
1490  out:
1491   if (nickname)
1492     silc_free(nickname);
1493   if (server)
1494     silc_free(server);
1495   silc_client_command_free(cmd);
1496 }
1497
1498 /* OPER command. Used to obtain server operator privileges. */
1499
1500 SILC_CLIENT_CMD_FUNC(oper)
1501 {
1502   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1503   SilcClientConnection conn = cmd->conn;
1504   SilcBuffer buffer;
1505   unsigned char *auth_data;
1506   SilcBuffer auth;
1507
1508   if (!cmd->conn) {
1509     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1510     COMMAND_ERROR;
1511     goto out;
1512   }
1513
1514   if (cmd->argc < 2) {
1515     cmd->client->ops->say(cmd->client, conn, 
1516                           "Usage: /OPER <username> [<public key>]");
1517     COMMAND_ERROR;
1518     goto out;
1519   }
1520
1521   if (cmd->argc == 3) {
1522     /* XXX Get public key */
1523     auth_data = NULL;
1524     COMMAND_ERROR;
1525     goto out;
1526   } else {
1527     /* Get passphrase */
1528
1529     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1530     if (!auth_data) {
1531       COMMAND_ERROR;
1532       goto out;
1533     }
1534
1535     /* Encode the authentication payload */
1536     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1537                                     auth_data, strlen(auth_data));
1538   }
1539
1540   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1541                                           1, cmd->argv[1], 
1542                                           strlen(cmd->argv[1]),
1543                                           2, auth->data, auth->len);
1544   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1545                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1546
1547   silc_buffer_free(buffer);
1548   silc_buffer_free(auth);
1549   memset(auth_data, 0, strlen(auth_data));
1550   silc_free(auth_data);
1551
1552   /* Notify application */
1553   COMMAND;
1554
1555  out:
1556   silc_client_command_free(cmd);
1557 }
1558
1559 /* SILCOPER command. Used to obtain router operator privileges. */
1560
1561 SILC_CLIENT_CMD_FUNC(silcoper)
1562 {
1563   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1564   SilcClientConnection conn = cmd->conn;
1565   SilcBuffer buffer;
1566   unsigned char *auth_data;
1567   SilcBuffer auth;
1568
1569   if (!cmd->conn) {
1570     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1571     COMMAND_ERROR;
1572     goto out;
1573   }
1574
1575   if (cmd->argc < 2) {
1576     cmd->client->ops->say(cmd->client, conn, 
1577                           "Usage: /SILCOPER <username> [<public key>]");
1578     COMMAND_ERROR;
1579     goto out;
1580   }
1581
1582   if (cmd->argc == 3) {
1583     /* XXX Get public key */
1584     auth_data = NULL;
1585     COMMAND_ERROR;
1586     goto out;
1587   } else {
1588     /* Get passphrase */
1589
1590     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1591     if (!auth_data) {
1592       COMMAND_ERROR;
1593       goto out;
1594     }
1595
1596     /* Encode the authentication payload */
1597     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1598                                     auth_data, strlen(auth_data));
1599   }
1600
1601   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1602                                           1, cmd->argv[1], 
1603                                           strlen(cmd->argv[1]),
1604                                           2, auth->data, auth->len);
1605   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1606                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1607
1608   silc_buffer_free(buffer);
1609   silc_buffer_free(auth);
1610   memset(auth_data, 0, strlen(auth_data));
1611   silc_free(auth_data);
1612
1613   /* Notify application */
1614   COMMAND;
1615
1616  out:
1617   silc_client_command_free(cmd);
1618 }
1619
1620 /* CONNECT command. Connects the server to another server. */
1621
1622 SILC_CLIENT_CMD_FUNC(connect)
1623 {
1624   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1625   SilcClientConnection conn = cmd->conn;
1626   SilcBuffer buffer;
1627   unsigned char port[4];
1628   unsigned int tmp;
1629
1630   if (!cmd->conn) {
1631     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1632     COMMAND_ERROR;
1633     goto out;
1634   }
1635
1636   if (cmd->argc < 2) {
1637     cmd->client->ops->say(cmd->client, conn, 
1638                           "Usage: /CONNECT <server> [<port>]");
1639     COMMAND_ERROR;
1640     goto out;
1641   }
1642
1643   if (cmd->argc == 3) {
1644     tmp = atoi(cmd->argv[2]);
1645     SILC_PUT32_MSB(tmp, port);
1646   }
1647
1648   if (cmd->argc == 3)
1649     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1650                                             1, cmd->argv[1], 
1651                                             strlen(cmd->argv[1]),
1652                                             2, port, 4);
1653   else
1654     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1655                                             1, cmd->argv[1], 
1656                                             strlen(cmd->argv[1]));
1657   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1658                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1659   silc_buffer_free(buffer);
1660
1661   /* Notify application */
1662   COMMAND;
1663
1664  out:
1665   silc_client_command_free(cmd);
1666 }
1667
1668 SILC_CLIENT_CMD_FUNC(restart)
1669 {
1670 }
1671
1672 /* CLOSE command. Close server connection to the remote server */
1673  
1674 SILC_CLIENT_CMD_FUNC(close)
1675 {
1676   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1677   SilcClientConnection conn = cmd->conn;
1678   SilcBuffer buffer;
1679   unsigned char port[4];
1680   unsigned int tmp;
1681
1682   if (!cmd->conn) {
1683     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1684     COMMAND_ERROR;
1685     goto out;
1686   }
1687
1688   if (cmd->argc < 2) {
1689     cmd->client->ops->say(cmd->client, conn, 
1690                           "Usage: /CLOSE <server> [<port>]");
1691     COMMAND_ERROR;
1692     goto out;
1693   }
1694
1695   if (cmd->argc == 3) {
1696     tmp = atoi(cmd->argv[2]);
1697     SILC_PUT32_MSB(tmp, port);
1698   }
1699
1700   if (cmd->argc == 3)
1701     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1702                                             1, cmd->argv[1], 
1703                                             strlen(cmd->argv[1]),
1704                                             2, port, 4);
1705   else
1706     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1707                                             1, cmd->argv[1], 
1708                                             strlen(cmd->argv[1]));
1709   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1710                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1711   silc_buffer_free(buffer);
1712
1713   /* Notify application */
1714   COMMAND;
1715
1716  out:
1717   silc_client_command_free(cmd);
1718 }
1719  
1720 /* SHUTDOWN command. Shutdowns the server. */
1721
1722 SILC_CLIENT_CMD_FUNC(shutdown)
1723 {
1724   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1725
1726   if (!cmd->conn) {
1727     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1728     COMMAND_ERROR;
1729     goto out;
1730   }
1731
1732   /* Send the command */
1733   silc_client_send_command(cmd->client, cmd->conn, 
1734                            SILC_COMMAND_SHUTDOWN, 0, 0);
1735
1736   /* Notify application */
1737   COMMAND;
1738
1739  out:
1740   silc_client_command_free(cmd);
1741 }
1742  
1743 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1744
1745 SILC_CLIENT_CMD_FUNC(leave)
1746 {
1747   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1748   SilcClientConnection conn = cmd->conn;
1749   SilcIDCacheEntry id_cache = NULL;
1750   SilcChannelEntry channel;
1751   SilcBuffer buffer, idp;
1752   char *name;
1753
1754   if (!cmd->conn) {
1755     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1756     COMMAND_ERROR;
1757     goto out;
1758   }
1759
1760   if (cmd->argc != 2) {
1761     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1762     COMMAND_ERROR;
1763     goto out;
1764   }
1765
1766   if (cmd->argv[1][0] == '*') {
1767     if (!conn->current_channel) {
1768       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1769       COMMAND_ERROR;
1770       goto out;
1771     }
1772     name = conn->current_channel->channel_name;
1773   } else {
1774     name = cmd->argv[1];
1775   }
1776
1777   if (!conn->current_channel) {
1778     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1779     COMMAND_ERROR;
1780     goto out;
1781   }
1782
1783   /* Get the Channel ID of the channel */
1784   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1785     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1786     COMMAND_ERROR;
1787     goto out;
1788   }
1789
1790   channel = (SilcChannelEntry)id_cache->context;
1791
1792   /* Send LEAVE command to the server */
1793   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1794   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1795                                           1, idp->data, idp->len);
1796   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1797                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1798   silc_buffer_free(buffer);
1799   silc_buffer_free(idp);
1800
1801   /* We won't talk anymore on this channel */
1802   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1803
1804   conn->current_channel = NULL;
1805
1806   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1807   silc_free(channel->channel_name);
1808   silc_free(channel->id);
1809   silc_free(channel->key);
1810   silc_cipher_free(channel->channel_key);
1811   silc_free(channel);
1812
1813   /* Notify application */
1814   COMMAND;
1815
1816  out:
1817   silc_client_command_free(cmd);
1818 }
1819
1820 /* Command USERS. Requests the USERS of the clients joined on requested
1821    channel. */
1822
1823 SILC_CLIENT_CMD_FUNC(users)
1824 {
1825   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1826   SilcClientConnection conn = cmd->conn;
1827   SilcIDCacheEntry id_cache = NULL;
1828   SilcChannelEntry channel;
1829   SilcBuffer buffer, idp;
1830   char *name, *line = NULL;
1831   unsigned int line_len = 0;
1832
1833   if (!cmd->conn) {
1834     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1835     COMMAND_ERROR;
1836     goto out;
1837   }
1838
1839   if (cmd->argc != 2) {
1840     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1841     COMMAND_ERROR;
1842     goto out;
1843   }
1844
1845   if (cmd->argv[1][0] == '*') {
1846     if (!conn->current_channel) {
1847       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1848       COMMAND_ERROR;
1849       goto out;
1850     }
1851     name = conn->current_channel->channel_name;
1852   } else {
1853     name = cmd->argv[1];
1854   }
1855
1856   if (!conn->current_channel) {
1857     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1858     COMMAND_ERROR;
1859     goto out;
1860   }
1861
1862   /* Get the Channel ID of the channel */
1863   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1864     /* XXX should resolve the channel ID; LIST command */
1865     cmd->client->ops->say(cmd->client, conn, 
1866                           "You are not on that channel", name);
1867     COMMAND_ERROR;
1868     goto out;
1869   }
1870
1871   channel = (SilcChannelEntry)id_cache->context;
1872
1873   if (!cmd->pending) {
1874     /* Send USERS command to the server */
1875     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1876     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
1877                                             ++conn->cmd_ident, 1, 
1878                                             1, idp->data, idp->len);
1879     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1880                             NULL, 0, NULL, NULL, buffer->data, 
1881                             buffer->len, TRUE);
1882     silc_buffer_free(buffer);
1883     silc_buffer_free(idp);
1884
1885     /* Register pending callback which will recall this command callback with
1886        same context and reprocesses the command. When reprocessing we actually
1887        display the information on the screen. */
1888     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, 
1889                                 silc_client_command_destructor,
1890                                 silc_client_command_users, 
1891                                 silc_client_command_dup(cmd));
1892     cmd->pending = TRUE;
1893     return;
1894   }
1895
1896   if (cmd->pending) {
1897     /* Pending command. Now we've resolved the information from server and
1898        we are ready to display the information on screen. */
1899     int i;
1900     SilcChannelUser chu;
1901
1902     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1903                           channel->channel_name);
1904
1905     line = silc_calloc(4096, sizeof(*line));
1906     line_len = 4096;
1907     silc_list_start(channel->clients);
1908     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1909       SilcClientEntry e = chu->client;
1910       char *m, tmp[80], len1;
1911
1912       memset(line, 0, sizeof(line_len));
1913
1914       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1915         silc_free(line);
1916         line_len += strlen(e->nickname) + strlen(e->server) + 100;
1917         line = silc_calloc(line_len, sizeof(*line));
1918       }
1919
1920       memset(tmp, 0, sizeof(tmp));
1921       m = silc_client_chumode_char(chu->mode);
1922
1923       strncat(line, " ", 1);
1924       strncat(line, e->nickname, strlen(e->nickname));
1925       strncat(line, e->server ? "@" : "", 1);
1926
1927       len1 = 0;
1928       if (e->server)
1929         len1 = strlen(e->server);
1930       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1931
1932       len1 = strlen(line);
1933       if (len1 >= 30) {
1934         memset(&line[29], 0, len1 - 29);
1935       } else {
1936         for (i = 0; i < 30 - len1 - 1; i++)
1937           strcat(line, " ");
1938       }
1939
1940       strncat(line, "  H", 3);
1941       strcat(tmp, m ? m : "");
1942       strncat(line, tmp, strlen(tmp));
1943
1944       if (strlen(tmp) < 5)
1945         for (i = 0; i < 5 - strlen(tmp); i++)
1946           strcat(line, " ");
1947
1948       strcat(line, e->username ? e->username : "");
1949
1950       cmd->client->ops->say(cmd->client, conn, "%s", line);
1951
1952       if (m)
1953         silc_free(m);
1954     }
1955   }
1956
1957   if (line)
1958     silc_free(line);
1959
1960   /* Notify application */
1961   COMMAND;
1962
1963  out:
1964   silc_client_command_free(cmd);
1965 }