Added KILL command.
[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 SILC_CLIENT_CMD_FUNC(list)
411 {
412 }
413
414 /* Command TOPIC. Sets/shows topic on a channel. */
415
416 SILC_CLIENT_CMD_FUNC(topic)
417 {
418   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
419   SilcClientConnection conn = cmd->conn;
420   SilcIDCacheEntry id_cache = NULL;
421   SilcChannelEntry channel;
422   SilcBuffer buffer, idp;
423   char *name;
424
425   if (!cmd->conn) {
426     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
427     COMMAND_ERROR;
428     goto out;
429   }
430
431   if (cmd->argc < 2 || cmd->argc > 3) {
432     cmd->client->ops->say(cmd->client, conn,
433                           "Usage: /TOPIC <channel> [<topic>]");
434     COMMAND_ERROR;
435     goto out;
436   }
437
438   if (cmd->argv[1][0] == '*') {
439     if (!conn->current_channel) {
440       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
441       COMMAND_ERROR;
442       goto out;
443     }
444     name = conn->current_channel->channel_name;
445   } else {
446     name = cmd->argv[1];
447   }
448
449   if (!conn->current_channel) {
450     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
451     COMMAND_ERROR;
452     goto out;
453   }
454
455   /* Get the Channel ID of the channel */
456   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
457     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
458     COMMAND_ERROR;
459     goto out;
460   }
461
462   channel = (SilcChannelEntry)id_cache->context;
463
464   /* Send TOPIC command to the server */
465   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
466   if (cmd->argc > 2)
467     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2, 
468                                             1, idp->data, idp->len,
469                                             2, cmd->argv[2], 
470                                             strlen(cmd->argv[2]));
471   else
472     buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1, 
473                                             1, idp->data, idp->len,
474                                             0);
475   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
476                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
477   silc_buffer_free(buffer);
478   silc_buffer_free(idp);
479
480   /* Notify application */
481   COMMAND;
482
483  out:
484   silc_client_command_free(cmd);
485 }
486
487 /* Command INVITE. Invites specific client to join a channel. */
488
489 SILC_CLIENT_CMD_FUNC(invite)
490 {
491   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
492   SilcClient client = cmd->client;
493   SilcClientConnection conn = cmd->conn;
494   SilcClientEntry client_entry;
495   SilcChannelEntry channel_entry;
496   SilcBuffer buffer, clidp, chidp;
497   unsigned int num = 0;
498   char *nickname = NULL, *server = NULL;
499
500   if (!cmd->conn) {
501     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
502     COMMAND_ERROR;
503     goto out;
504   }
505
506   if (cmd->argc != 3) {
507     cmd->client->ops->say(cmd->client, conn,
508                           "Usage: /INVITE <nickname>[@<server>] <channel>");
509     COMMAND_ERROR;
510     goto out;
511   }
512
513   /* Parse the typed nickname. */
514   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
515     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
516     COMMAND_ERROR;
517     goto out;
518   }
519
520   /* Find client entry */
521   client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
522                                         TRUE);
523   if (!client_entry) {
524     if (nickname)
525       silc_free(nickname);
526     if (server)
527       silc_free(server);
528
529     /* Client entry not found, it was requested thus mark this to be
530        pending command. */
531     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
532                                 silc_client_command_destructor,
533                                 silc_client_command_invite, 
534                                 silc_client_command_dup(cmd));
535     cmd->pending = 1;
536     return;
537   }
538
539   /* Find channel entry */
540   channel_entry = silc_client_get_channel(client, conn, cmd->argv[2]);
541   if (!channel_entry) {
542     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
543     COMMAND_ERROR;
544     goto out;
545   }
546
547   /* Send command */
548   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
549   chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
550   buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
551                                           1, clidp->data, clidp->len,
552                                           2, chidp->data, chidp->len);
553   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
554                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
555   silc_buffer_free(buffer);
556   silc_buffer_free(clidp);
557   silc_buffer_free(chidp);
558
559   cmd->client->ops->say(cmd->client, conn, 
560                         "Inviting %s to channel %s", cmd->argv[1], 
561                         cmd->argv[2]);
562
563   /* Notify application */
564   COMMAND;
565
566  out:
567   if (nickname)
568     silc_free(nickname);
569   if (server)
570     silc_free(server);
571   silc_client_command_free(cmd);
572 }
573
574 typedef struct {
575   SilcClient client;
576   SilcClientConnection conn;
577 } *QuitInternal;
578
579 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
580 {
581   QuitInternal q = (QuitInternal)context;
582
583   /* Close connection */
584   q->client->ops->disconnect(q->client, q->conn);
585   silc_client_close_connection(q->client, q->conn->sock->user_data);
586
587   silc_free(q);
588 }
589
590 /* Command QUIT. Closes connection with current server. */
591  
592 SILC_CLIENT_CMD_FUNC(quit)
593 {
594   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
595   SilcBuffer buffer;
596   QuitInternal q;
597
598   if (!cmd->conn) {
599     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
600     COMMAND_ERROR;
601     goto out;
602   }
603
604   if (cmd->argc > 1)
605     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, 
606                                          &cmd->argv[1], &cmd->argv_lens[1],
607                                          &cmd->argv_types[1], 0);
608   else
609     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
610                                          NULL, NULL, NULL, 0);
611   silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
612                           NULL, 0, NULL, NULL, 
613                           buffer->data, buffer->len, TRUE);
614   silc_buffer_free(buffer);
615
616   q = silc_calloc(1, sizeof(*q));
617   q->client = cmd->client;
618   q->conn = cmd->conn;
619
620   /* We quit the connection with little timeout */
621   silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
622                      silc_client_command_quit_cb, (void *)q,
623                      1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
624
625   /* Notify application */
626   COMMAND;
627
628  out:
629   silc_client_command_free(cmd);
630 }
631
632 /* Command KILL. Router operator can use this command to remove an client
633    fromthe SILC Network. */
634
635 SILC_CLIENT_CMD_FUNC(kill)
636 {
637   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
638   SilcClientConnection conn = cmd->conn;
639   SilcBuffer buffer, idp;
640   SilcClientEntry target;
641   unsigned int num = 0;
642   char *nickname = NULL, *server = NULL;
643
644   if (!cmd->conn) {
645     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
646     COMMAND_ERROR;
647     goto out;
648   }
649
650   if (cmd->argc < 2) {
651     cmd->client->ops->say(cmd->client, conn, 
652                           "Usage: /KILL <nickname> [<comment>]");
653     COMMAND_ERROR;
654     goto out;
655   }
656
657   /* Parse the typed nickname. */
658   if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
659     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
660     COMMAND_ERROR;
661     goto out;
662   }
663
664   /* Get the target client */
665   target = silc_idlist_get_client(cmd->client, conn, nickname, 
666                                   server, num, TRUE);
667   if (!target) {
668     silc_free(nickname);
669     if (server)
670       silc_free(server);
671
672     /* Client entry not found, it was requested thus mark this to be
673        pending command. */
674     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
675                                 conn->cmd_ident,  
676                                 silc_client_command_destructor,
677                                 silc_client_command_kill, 
678                                 silc_client_command_dup(cmd));
679     cmd->pending = 1;
680     return;
681   }
682
683   /* Send the KILL command to the server */
684   idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
685   if (cmd->argc == 2)
686     buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 1, 
687                                             1, idp->data, idp->len);
688   else
689     buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 2, 
690                                             1, idp->data, idp->len,
691                                             2, cmd->argv[2], 
692                                             strlen(cmd->argv[2]));
693   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
694                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
695   silc_buffer_free(buffer);
696   silc_buffer_free(idp);
697
698   /* Notify application */
699   COMMAND;
700
701   /* Remove the client entry to be killed */
702   silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
703                          target->id);
704   if (target->nickname)
705     silc_free(target->nickname);
706   if (target->server)
707     silc_free(target->server);
708   if (target->id)
709     silc_free(target->id);
710   if (target->send_key)
711     silc_cipher_free(target->send_key);
712   if (target->receive_key)
713     silc_cipher_free(target->receive_key);
714   silc_free(target);
715
716  out:
717   if (nickname)
718     silc_free(nickname);
719   if (server)
720     silc_free(server);
721   silc_client_command_free(cmd);
722 }
723
724 /* Command INFO. Request information about specific server. If specific
725    server is not provided the current server is used. */
726
727 SILC_CLIENT_CMD_FUNC(info)
728 {
729   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
730   SilcClientConnection conn = cmd->conn;
731   SilcBuffer buffer;
732   char *name;
733
734   if (!cmd->conn) {
735     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
736     COMMAND_ERROR;
737     goto out;
738   }
739
740   if (cmd->argc < 2)
741     name = strdup(conn->remote_host);
742   else
743     name = strdup(cmd->argv[1]);
744
745   /* Send the command */
746   buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
747                                           1, name, strlen(name));
748   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
749                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
750   silc_buffer_free(buffer);
751
752   /* Notify application */
753   COMMAND;
754
755  out:
756   silc_client_command_free(cmd);
757 }
758
759 /* Command PING. Sends ping to server. This is used to test the 
760    communication channel. */
761
762 SILC_CLIENT_CMD_FUNC(ping)
763 {
764   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
765   SilcClientConnection conn = cmd->conn;
766   SilcBuffer buffer;
767   void *id;
768   int i;
769   char *name = NULL;
770
771   if (!cmd->conn) {
772     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
773     COMMAND_ERROR;
774     goto out;
775   }
776
777   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
778     name = strdup(conn->remote_host);
779
780   /* Send the command */
781   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
782                                           1, conn->remote_id_data, 
783                                           SILC_ID_SERVER_LEN);
784   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
785                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
786   silc_buffer_free(buffer);
787
788   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
789                       SILC_ID_SERVER);
790   if (!id) {
791     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
792     COMMAND_ERROR;
793     goto out;
794   }
795
796   /* Start counting time */
797   for (i = 0; i < conn->ping_count; i++) {
798     if (conn->ping[i].dest_id == NULL) {
799       conn->ping[i].start_time = time(NULL);
800       conn->ping[i].dest_id = id;
801       conn->ping[i].dest_name = name;
802       conn->ping_count++;
803       break;
804     }
805   }
806   if (i >= conn->ping_count) {
807     i = conn->ping_count;
808     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
809     conn->ping[i].start_time = time(NULL);
810     conn->ping[i].dest_id = id;
811     conn->ping[i].dest_name = name;
812     conn->ping_count++;
813   }
814   
815   /* Notify application */
816   COMMAND;
817
818  out:
819   silc_client_command_free(cmd);
820 }
821
822 SILC_CLIENT_CMD_FUNC(notice)
823 {
824 }
825
826 /* Command JOIN. Joins to a channel. */
827
828 SILC_CLIENT_CMD_FUNC(join)
829 {
830   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
831   SilcClientConnection conn = cmd->conn;
832   SilcIDCacheEntry id_cache = NULL;
833   SilcBuffer buffer, idp;
834
835   if (!cmd->conn) {
836     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
837     COMMAND_ERROR;
838     goto out;
839   }
840
841   if (cmd->argc < 2) {
842     /* Show channels currently joined to */
843
844     goto out;
845   }
846
847   /* See if we have joined to the requested channel already */
848   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
849                                     &id_cache)) {
850     cmd->client->ops->say(cmd->client, conn, 
851                           "You are talking to channel %s", cmd->argv[1]);
852     conn->current_channel = (SilcChannelEntry)id_cache->context;
853 #if 0
854     cmd->client->screen->bottom_line->channel = cmd->argv[1];
855     silc_screen_print_bottom_line(cmd->client->screen, 0);
856 #endif
857     goto out;
858   }
859
860   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
861
862   /* Send JOIN command to the server */
863   if (cmd->argc == 2)
864     buffer = 
865       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
866                                      1, cmd->argv[1], cmd->argv_lens[1],
867                                      2, idp->data, idp->len);
868   else if (cmd->argc == 3)
869     /* XXX Buggy */
870     buffer = 
871       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
872                                      1, cmd->argv[1], cmd->argv_lens[1],
873                                      2, idp->data, idp->len,
874                                      3, cmd->argv[2], cmd->argv_lens[2]);
875   else
876     buffer = 
877       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
878                                      1, cmd->argv[1], cmd->argv_lens[1],
879                                      2, idp->data, idp->len,
880                                      3, cmd->argv[2], cmd->argv_lens[2],
881                                      4, cmd->argv[3], cmd->argv_lens[3]);
882
883   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
884                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
885   silc_buffer_free(buffer);
886   silc_buffer_free(idp);
887
888   /* Notify application */
889   COMMAND;
890
891  out:
892   silc_client_command_free(cmd);
893 }
894
895 /* MOTD command. Requests motd from server. */
896
897 SILC_CLIENT_CMD_FUNC(motd)
898 {
899   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
900   SilcClientConnection conn = cmd->conn;
901   SilcBuffer buffer;
902
903   if (!cmd->conn) {
904     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
905     COMMAND_ERROR;
906     goto out;
907   }
908
909   if (cmd->argc < 1 || cmd->argc > 1) {
910     cmd->client->ops->say(cmd->client, conn,
911                           "Usage: /MOTD");
912     COMMAND_ERROR;
913     goto out;
914   }
915
916   /* Send TOPIC command to the server */
917   buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
918                                           2, conn->remote_host, 
919                                           strlen(conn->remote_host));
920   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
921                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
922   silc_buffer_free(buffer);
923
924   /* Notify application */
925   COMMAND;
926
927  out:
928   silc_client_command_free(cmd);
929 }
930
931 /* UMODE. Set user mode in SILC. */
932
933 SILC_CLIENT_CMD_FUNC(umode)
934 {
935
936 }
937
938 /* CMODE command. Sets channel mode. Modes that does not require any arguments
939    can be set several at once. Those modes that require argument must be set
940    separately (unless set with modes that does not require arguments). */
941
942 SILC_CLIENT_CMD_FUNC(cmode)
943 {
944   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
945   SilcClientConnection conn = cmd->conn;
946   SilcChannelEntry channel;
947   SilcBuffer buffer, chidp;
948   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
949   unsigned int mode, add, type, len, arg_len = 0;
950   int i;
951
952   if (!cmd->conn) {
953     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
954     COMMAND_ERROR;
955     goto out;
956   }
957
958   if (cmd->argc < 3) {
959     cmd->client->ops->say(cmd->client, conn, 
960                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
961     COMMAND_ERROR;
962     goto out;
963   }
964
965   if (cmd->argv[1][0] == '*') {
966     if (!conn->current_channel) {
967       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
968       COMMAND_ERROR;
969       goto out;
970     }
971
972     channel = conn->current_channel;
973   } else {
974     name = cmd->argv[1];
975
976     channel = silc_client_get_channel(cmd->client, conn, name);
977     if (!channel) {
978       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
979       COMMAND_ERROR;
980       goto out;
981     }
982   }
983
984   mode = channel->mode;
985
986   /* Are we adding or removing mode */
987   if (cmd->argv[2][0] == '-')
988     add = FALSE;
989   else
990     add = TRUE;
991
992   /* Argument type to be sent to server */
993   type = 0;
994
995   /* Parse mode */
996   cp = cmd->argv[2] + 1;
997   len = strlen(cp);
998   for (i = 0; i < len; i++) {
999     switch(cp[i]) {
1000     case 'p':
1001       if (add)
1002         mode |= SILC_CHANNEL_MODE_PRIVATE;
1003       else
1004         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1005       break;
1006     case 's':
1007       if (add)
1008         mode |= SILC_CHANNEL_MODE_SECRET;
1009       else
1010         mode &= ~SILC_CHANNEL_MODE_SECRET;
1011       break;
1012     case 'k':
1013       if (add)
1014         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1015       else
1016         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1017       break;
1018     case 'i':
1019       if (add)
1020         mode |= SILC_CHANNEL_MODE_INVITE;
1021       else
1022         mode &= ~SILC_CHANNEL_MODE_INVITE;
1023       break;
1024     case 't':
1025       if (add)
1026         mode |= SILC_CHANNEL_MODE_TOPIC;
1027       else
1028         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1029       break;
1030     case 'l':
1031       if (add) {
1032         int ll;
1033         mode |= SILC_CHANNEL_MODE_ULIMIT;
1034         type = 3;
1035         ll = atoi(cmd->argv[3]);
1036         SILC_PUT32_MSB(ll, tmp);
1037         arg = tmp;
1038         arg_len = 4;
1039       } else {
1040         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1041       }
1042       break;
1043     case 'a':
1044       if (add) {
1045         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1046         type = 4;
1047         arg = cmd->argv[3];
1048         arg_len = cmd->argv_lens[3];
1049       } else {
1050         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1051       }
1052       break;
1053     case 'b':
1054       if (add) {
1055         mode |= SILC_CHANNEL_MODE_BAN;
1056         type = 5;
1057         arg = cmd->argv[3];
1058         arg_len = cmd->argv_lens[3];
1059       } else {
1060         mode &= ~SILC_CHANNEL_MODE_BAN;
1061       }
1062       break;
1063     case 'I':
1064       if (add) {
1065         mode |= SILC_CHANNEL_MODE_INVITE_LIST;
1066         type = 6;
1067         arg = cmd->argv[3];
1068         arg_len = cmd->argv_lens[3];
1069       } else {
1070         mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
1071       }
1072       break;
1073     case 'c':
1074       if (add) {
1075         mode |= SILC_CHANNEL_MODE_CIPHER;
1076         type = 8;
1077         arg = cmd->argv[3];
1078         arg_len = cmd->argv_lens[3];
1079       } else {
1080         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1081       }
1082       break;
1083     default:
1084       COMMAND_ERROR;
1085       goto out;
1086       break;
1087     }
1088   }
1089
1090   if (type && cmd->argc < 3) {
1091     COMMAND_ERROR;
1092     goto out;
1093   }
1094
1095   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1096   SILC_PUT32_MSB(mode, modebuf);
1097
1098   /* Send the command packet. We support sending only one mode at once
1099      that requires an argument. */
1100   if (type && arg) {
1101     buffer = 
1102       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1103                                      1, chidp->data, chidp->len, 
1104                                      2, modebuf, sizeof(modebuf),
1105                                      type, arg, arg_len);
1106   } else {
1107     buffer = 
1108       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1109                                      1, chidp->data, chidp->len, 
1110                                      2, modebuf, sizeof(modebuf));
1111   }
1112
1113   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1114                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1115   silc_buffer_free(buffer);
1116   silc_buffer_free(chidp);
1117
1118   /* Notify application */
1119   COMMAND;
1120
1121  out:
1122   silc_client_command_free(cmd);
1123 }
1124
1125 /* CUMODE command. Changes client's mode on a channel. */
1126
1127 SILC_CLIENT_CMD_FUNC(cumode)
1128 {
1129   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1130   SilcClientConnection conn = cmd->conn;
1131   SilcChannelEntry channel;
1132   SilcChannelUser chu;
1133   SilcClientEntry client_entry;
1134   SilcBuffer buffer, clidp, chidp;
1135   unsigned char *name, *cp, modebuf[4];
1136   unsigned int mode = 0, add, len;
1137   char *nickname = NULL, *server = NULL;
1138   unsigned int num = 0;
1139   int i;
1140
1141   if (!cmd->conn) {
1142     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1143     COMMAND_ERROR;
1144     goto out;
1145   }
1146
1147   if (cmd->argc < 4) {
1148     cmd->client->ops->say(cmd->client, conn, 
1149                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1150     COMMAND_ERROR;
1151     goto out;
1152   }
1153
1154   if (cmd->argv[1][0] == '*') {
1155     if (!conn->current_channel) {
1156       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1157       COMMAND_ERROR;
1158       goto out;
1159     }
1160
1161     channel = conn->current_channel;
1162   } else {
1163     name = cmd->argv[1];
1164
1165     channel = silc_client_get_channel(cmd->client, conn, name);
1166     if (!channel) {
1167       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1168       COMMAND_ERROR;
1169       goto out;
1170     }
1171   }
1172
1173   /* Parse the typed nickname. */
1174   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1175     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1176     COMMAND_ERROR;
1177     goto out;
1178   }
1179
1180   /* Find client entry */
1181   client_entry = silc_idlist_get_client(cmd->client, conn, 
1182                                         nickname, server, num, TRUE);
1183   if (!client_entry) {
1184     /* Client entry not found, it was requested thus mark this to be
1185        pending command. */
1186     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1187                                 conn->cmd_ident,  
1188                                 silc_client_command_destructor,
1189                                 silc_client_command_cumode, 
1190                                 silc_client_command_dup(cmd));
1191     cmd->pending = 1;
1192     return;
1193   }
1194   
1195   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1196     if (chu->client == client_entry) {
1197       chu->mode = mode;
1198       break;
1199     }
1200   }
1201
1202   /* Are we adding or removing mode */
1203   if (cmd->argv[2][0] == '-')
1204     add = FALSE;
1205   else
1206     add = TRUE;
1207
1208   /* Parse mode */
1209   cp = cmd->argv[2] + 1;
1210   len = strlen(cp);
1211   for (i = 0; i < len; i++) {
1212     switch(cp[i]) {
1213     case 'a':
1214       if (add) {
1215         mode |= SILC_CHANNEL_UMODE_CHANFO;
1216         mode |= SILC_CHANNEL_UMODE_CHANOP;
1217       } else {
1218         mode = SILC_CHANNEL_UMODE_NONE;
1219       }
1220       break;
1221     case 'f':
1222       if (add)
1223         mode |= SILC_CHANNEL_UMODE_CHANFO;
1224       else
1225         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1226       break;
1227     case 'o':
1228       if (add)
1229         mode |= SILC_CHANNEL_UMODE_CHANOP;
1230       else
1231         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1232       break;
1233     default:
1234       COMMAND_ERROR;
1235       goto out;
1236       break;
1237     }
1238   }
1239
1240   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1241   SILC_PUT32_MSB(mode, modebuf);
1242   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1243
1244   /* Send the command packet. We support sending only one mode at once
1245      that requires an argument. */
1246   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1247                                           1, chidp->data, chidp->len, 
1248                                           2, modebuf, 4,
1249                                           3, clidp->data, clidp->len);
1250
1251   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1252                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1253   silc_buffer_free(buffer);
1254   silc_buffer_free(chidp);
1255   silc_buffer_free(clidp);
1256   
1257   /* Notify application */
1258   COMMAND;
1259
1260  out:
1261   if (nickname)
1262     silc_free(nickname);
1263   if (server)
1264     silc_free(server);
1265   silc_client_command_free(cmd);
1266 }
1267
1268 /* KICK command. Kicks a client out of channel. */
1269
1270 SILC_CLIENT_CMD_FUNC(kick)
1271 {
1272   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1273   SilcClientConnection conn = cmd->conn;
1274   SilcIDCacheEntry id_cache = NULL;
1275   SilcChannelEntry channel;
1276   SilcBuffer buffer, idp, idp2;
1277   SilcClientEntry target;
1278   char *name;
1279   unsigned int num = 0;
1280   char *nickname = NULL, *server = NULL;
1281
1282   if (!cmd->conn) {
1283     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1284     COMMAND_ERROR;
1285     goto out;
1286   }
1287
1288   if (cmd->argc < 3) {
1289     cmd->client->ops->say(cmd->client, conn, 
1290                           "Usage: /KICK <channel> <nickname> [<comment>]");
1291     COMMAND_ERROR;
1292     goto out;
1293   }
1294
1295   if (cmd->argv[1][0] == '*') {
1296     if (!conn->current_channel) {
1297       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1298       COMMAND_ERROR;
1299       goto out;
1300     }
1301     name = conn->current_channel->channel_name;
1302   } else {
1303     name = cmd->argv[1];
1304   }
1305
1306   if (!conn->current_channel) {
1307     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1308     COMMAND_ERROR;
1309     goto out;
1310   }
1311
1312   /* Get the Channel ID of the channel */
1313   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1314     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1315     COMMAND_ERROR;
1316     goto out;
1317   }
1318
1319   channel = (SilcChannelEntry)id_cache->context;
1320
1321   /* Parse the typed nickname. */
1322   if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1323     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1324     COMMAND_ERROR;
1325     goto out;
1326   }
1327
1328   /* Get the target client */
1329   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1330                                   server, num, FALSE);
1331   if (!target) {
1332     cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1333                           cmd->argv[2]);
1334     COMMAND_ERROR;
1335     goto out;
1336   }
1337
1338   /* Send KICK command to the server */
1339   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1340   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1341   if (cmd->argc == 3)
1342     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1343                                             1, idp->data, idp->len,
1344                                             2, idp2->data, idp2->len);
1345   else
1346     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1347                                             1, idp->data, idp->len,
1348                                             2, idp2->data, idp2->len,
1349                                             3, cmd->argv[3], 
1350                                             strlen(cmd->argv[3]));
1351   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1352                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1353   silc_buffer_free(buffer);
1354   silc_buffer_free(idp);
1355   silc_buffer_free(idp2);
1356
1357   /* Notify application */
1358   COMMAND;
1359
1360  out:
1361   if (nickname)
1362     silc_free(nickname);
1363   if (server)
1364     silc_free(server);
1365   silc_client_command_free(cmd);
1366 }
1367
1368 /* OPER command. Used to obtain server operator privileges. */
1369
1370 SILC_CLIENT_CMD_FUNC(oper)
1371 {
1372   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1373   SilcClientConnection conn = cmd->conn;
1374   SilcBuffer buffer;
1375   unsigned char *auth_data;
1376   SilcBuffer auth;
1377
1378   if (!cmd->conn) {
1379     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1380     COMMAND_ERROR;
1381     goto out;
1382   }
1383
1384   if (cmd->argc < 2) {
1385     cmd->client->ops->say(cmd->client, conn, 
1386                           "Usage: /OPER <username> [<public key>]");
1387     COMMAND_ERROR;
1388     goto out;
1389   }
1390
1391   if (cmd->argc == 3) {
1392     /* XXX Get public key */
1393     auth_data = NULL;
1394     COMMAND_ERROR;
1395     goto out;
1396   } else {
1397     /* Get passphrase */
1398
1399     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1400     if (!auth_data) {
1401       COMMAND_ERROR;
1402       goto out;
1403     }
1404
1405     /* Encode the authentication payload */
1406     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1407                                     auth_data, strlen(auth_data));
1408   }
1409
1410   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1411                                           1, cmd->argv[1], 
1412                                           strlen(cmd->argv[1]),
1413                                           2, auth->data, auth->len);
1414   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1415                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1416
1417   silc_buffer_free(buffer);
1418   silc_buffer_free(auth);
1419   memset(auth_data, 0, strlen(auth_data));
1420   silc_free(auth_data);
1421
1422   /* Notify application */
1423   COMMAND;
1424
1425  out:
1426   silc_client_command_free(cmd);
1427 }
1428
1429 /* SILCOPER command. Used to obtain router operator privileges. */
1430
1431 SILC_CLIENT_CMD_FUNC(silcoper)
1432 {
1433   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1434   SilcClientConnection conn = cmd->conn;
1435   SilcBuffer buffer;
1436   unsigned char *auth_data;
1437   SilcBuffer auth;
1438
1439   if (!cmd->conn) {
1440     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1441     COMMAND_ERROR;
1442     goto out;
1443   }
1444
1445   if (cmd->argc < 2) {
1446     cmd->client->ops->say(cmd->client, conn, 
1447                           "Usage: /SILCOPER <username> [<public key>]");
1448     COMMAND_ERROR;
1449     goto out;
1450   }
1451
1452   if (cmd->argc == 3) {
1453     /* XXX Get public key */
1454     auth_data = NULL;
1455     COMMAND_ERROR;
1456     goto out;
1457   } else {
1458     /* Get passphrase */
1459
1460     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1461     if (!auth_data) {
1462       COMMAND_ERROR;
1463       goto out;
1464     }
1465
1466     /* Encode the authentication payload */
1467     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1468                                     auth_data, strlen(auth_data));
1469   }
1470
1471   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1472                                           1, cmd->argv[1], 
1473                                           strlen(cmd->argv[1]),
1474                                           2, auth->data, auth->len);
1475   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1476                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1477
1478   silc_buffer_free(buffer);
1479   silc_buffer_free(auth);
1480   memset(auth_data, 0, strlen(auth_data));
1481   silc_free(auth_data);
1482
1483   /* Notify application */
1484   COMMAND;
1485
1486  out:
1487   silc_client_command_free(cmd);
1488 }
1489
1490 /* CONNECT command. Connects the server to another server. */
1491
1492 SILC_CLIENT_CMD_FUNC(connect)
1493 {
1494   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1495   SilcClientConnection conn = cmd->conn;
1496   SilcBuffer buffer;
1497   unsigned char port[4];
1498   unsigned int tmp;
1499
1500   if (!cmd->conn) {
1501     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1502     COMMAND_ERROR;
1503     goto out;
1504   }
1505
1506   if (cmd->argc < 2) {
1507     cmd->client->ops->say(cmd->client, conn, 
1508                           "Usage: /CONNECT <server> [<port>]");
1509     COMMAND_ERROR;
1510     goto out;
1511   }
1512
1513   if (cmd->argc == 3) {
1514     tmp = atoi(cmd->argv[2]);
1515     SILC_PUT32_MSB(tmp, port);
1516   }
1517
1518   if (cmd->argc == 3)
1519     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1520                                             1, cmd->argv[1], 
1521                                             strlen(cmd->argv[1]),
1522                                             2, port, 4);
1523   else
1524     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1525                                             1, cmd->argv[1], 
1526                                             strlen(cmd->argv[1]));
1527   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1528                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1529   silc_buffer_free(buffer);
1530
1531   /* Notify application */
1532   COMMAND;
1533
1534  out:
1535   silc_client_command_free(cmd);
1536 }
1537
1538 SILC_CLIENT_CMD_FUNC(restart)
1539 {
1540 }
1541
1542 /* CLOSE command. Close server connection to the remote server */
1543  
1544 SILC_CLIENT_CMD_FUNC(close)
1545 {
1546   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1547   SilcClientConnection conn = cmd->conn;
1548   SilcBuffer buffer;
1549   unsigned char port[4];
1550   unsigned int tmp;
1551
1552   if (!cmd->conn) {
1553     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1554     COMMAND_ERROR;
1555     goto out;
1556   }
1557
1558   if (cmd->argc < 2) {
1559     cmd->client->ops->say(cmd->client, conn, 
1560                           "Usage: /CLOSE <server> [<port>]");
1561     COMMAND_ERROR;
1562     goto out;
1563   }
1564
1565   if (cmd->argc == 3) {
1566     tmp = atoi(cmd->argv[2]);
1567     SILC_PUT32_MSB(tmp, port);
1568   }
1569
1570   if (cmd->argc == 3)
1571     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1572                                             1, cmd->argv[1], 
1573                                             strlen(cmd->argv[1]),
1574                                             2, port, 4);
1575   else
1576     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1577                                             1, cmd->argv[1], 
1578                                             strlen(cmd->argv[1]));
1579   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1580                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1581   silc_buffer_free(buffer);
1582
1583   /* Notify application */
1584   COMMAND;
1585
1586  out:
1587   silc_client_command_free(cmd);
1588 }
1589  
1590 /* SHUTDOWN command. Shutdowns the server. */
1591
1592 SILC_CLIENT_CMD_FUNC(shutdown)
1593 {
1594   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1595
1596   if (!cmd->conn) {
1597     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1598     COMMAND_ERROR;
1599     goto out;
1600   }
1601
1602   /* Send the command */
1603   silc_client_send_command(cmd->client, cmd->conn, 
1604                            SILC_COMMAND_SHUTDOWN, 0, 0);
1605
1606   /* Notify application */
1607   COMMAND;
1608
1609  out:
1610   silc_client_command_free(cmd);
1611 }
1612  
1613 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1614
1615 SILC_CLIENT_CMD_FUNC(leave)
1616 {
1617   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1618   SilcClientConnection conn = cmd->conn;
1619   SilcIDCacheEntry id_cache = NULL;
1620   SilcChannelEntry channel;
1621   SilcBuffer buffer, idp;
1622   char *name;
1623
1624   if (!cmd->conn) {
1625     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1626     COMMAND_ERROR;
1627     goto out;
1628   }
1629
1630   if (cmd->argc != 2) {
1631     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1632     COMMAND_ERROR;
1633     goto out;
1634   }
1635
1636   if (cmd->argv[1][0] == '*') {
1637     if (!conn->current_channel) {
1638       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1639       COMMAND_ERROR;
1640       goto out;
1641     }
1642     name = conn->current_channel->channel_name;
1643   } else {
1644     name = cmd->argv[1];
1645   }
1646
1647   if (!conn->current_channel) {
1648     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1649     COMMAND_ERROR;
1650     goto out;
1651   }
1652
1653   /* Get the Channel ID of the channel */
1654   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1655     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1656     COMMAND_ERROR;
1657     goto out;
1658   }
1659
1660   channel = (SilcChannelEntry)id_cache->context;
1661
1662   /* Send LEAVE command to the server */
1663   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1664   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1665                                           1, idp->data, idp->len);
1666   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1667                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1668   silc_buffer_free(buffer);
1669   silc_buffer_free(idp);
1670
1671   /* We won't talk anymore on this channel */
1672   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1673
1674   conn->current_channel = NULL;
1675
1676   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1677   silc_free(channel->channel_name);
1678   silc_free(channel->id);
1679   silc_free(channel->key);
1680   silc_cipher_free(channel->channel_key);
1681   silc_free(channel);
1682
1683   /* Notify application */
1684   COMMAND;
1685
1686  out:
1687   silc_client_command_free(cmd);
1688 }
1689
1690 /* Command USERS. Requests the USERS of the clients joined on requested
1691    channel. */
1692
1693 SILC_CLIENT_CMD_FUNC(users)
1694 {
1695   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1696   SilcClientConnection conn = cmd->conn;
1697   SilcIDCacheEntry id_cache = NULL;
1698   SilcChannelEntry channel;
1699   SilcBuffer buffer, idp;
1700   char *name, *line = NULL;
1701   unsigned int line_len = 0;
1702
1703   if (!cmd->conn) {
1704     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1705     COMMAND_ERROR;
1706     goto out;
1707   }
1708
1709   if (cmd->argc != 2) {
1710     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1711     COMMAND_ERROR;
1712     goto out;
1713   }
1714
1715   if (cmd->argv[1][0] == '*') {
1716     if (!conn->current_channel) {
1717       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1718       COMMAND_ERROR;
1719       goto out;
1720     }
1721     name = conn->current_channel->channel_name;
1722   } else {
1723     name = cmd->argv[1];
1724   }
1725
1726   if (!conn->current_channel) {
1727     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1728     COMMAND_ERROR;
1729     goto out;
1730   }
1731
1732   /* Get the Channel ID of the channel */
1733   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1734     /* XXX should resolve the channel ID; LIST command */
1735     cmd->client->ops->say(cmd->client, conn, 
1736                           "You are not on that channel", name);
1737     COMMAND_ERROR;
1738     goto out;
1739   }
1740
1741   channel = (SilcChannelEntry)id_cache->context;
1742
1743   if (!cmd->pending) {
1744     /* Send USERS command to the server */
1745     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1746     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
1747                                             ++conn->cmd_ident, 1, 
1748                                             1, idp->data, idp->len);
1749     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1750                             NULL, 0, NULL, NULL, buffer->data, 
1751                             buffer->len, TRUE);
1752     silc_buffer_free(buffer);
1753     silc_buffer_free(idp);
1754
1755     /* Register pending callback which will recall this command callback with
1756        same context and reprocesses the command. When reprocessing we actually
1757        display the information on the screen. */
1758     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, 
1759                                 silc_client_command_destructor,
1760                                 silc_client_command_users, 
1761                                 silc_client_command_dup(cmd));
1762     cmd->pending = TRUE;
1763     return;
1764   }
1765
1766   if (cmd->pending) {
1767     /* Pending command. Now we've resolved the information from server and
1768        we are ready to display the information on screen. */
1769     int i;
1770     SilcChannelUser chu;
1771
1772     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1773                           channel->channel_name);
1774
1775     line = silc_calloc(4096, sizeof(*line));
1776     line_len = 4096;
1777     silc_list_start(channel->clients);
1778     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1779       SilcClientEntry e = chu->client;
1780       char *m, tmp[80], len1;
1781
1782       memset(line, 0, sizeof(line_len));
1783
1784       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1785         silc_free(line);
1786         line_len += strlen(e->nickname) + strlen(e->server) + 100;
1787         line = silc_calloc(line_len, sizeof(*line));
1788       }
1789
1790       memset(tmp, 0, sizeof(tmp));
1791       m = silc_client_chumode_char(chu->mode);
1792
1793       strncat(line, " ", 1);
1794       strncat(line, e->nickname, strlen(e->nickname));
1795       strncat(line, e->server ? "@" : "", 1);
1796
1797       len1 = 0;
1798       if (e->server)
1799         len1 = strlen(e->server);
1800       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1801
1802       len1 = strlen(line);
1803       if (len1 >= 30) {
1804         memset(&line[29], 0, len1 - 29);
1805       } else {
1806         for (i = 0; i < 30 - len1 - 1; i++)
1807           strcat(line, " ");
1808       }
1809
1810       strncat(line, "  H", 3);
1811       strcat(tmp, m ? m : "");
1812       strncat(line, tmp, strlen(tmp));
1813
1814       if (strlen(tmp) < 5)
1815         for (i = 0; i < 5 - strlen(tmp); i++)
1816           strcat(line, " ");
1817
1818       strcat(line, e->username ? e->username : "");
1819
1820       cmd->client->ops->say(cmd->client, conn, "%s", line);
1821
1822       if (m)
1823         silc_free(m);
1824     }
1825   }
1826
1827   if (line)
1828     silc_free(line);
1829
1830   /* Notify application */
1831   COMMAND;
1832
1833  out:
1834   silc_client_command_free(cmd);
1835 }