update.
[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/unset user mode in SILC. This is used mainly to unset the
932    modes as client cannot set itself server/router operator privileges. */
933
934 SILC_CLIENT_CMD_FUNC(umode)
935 {
936   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
937   SilcClientConnection conn = cmd->conn;
938   SilcBuffer buffer, idp;
939   unsigned char *cp, modebuf[4];
940   unsigned int mode, add, len;
941   int i;
942
943   if (!cmd->conn) {
944     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
945     COMMAND_ERROR;
946     goto out;
947   }
948
949   if (cmd->argc < 2) {
950     cmd->client->ops->say(cmd->client, conn, 
951                   "Usage: /UMODE +|-<modes>");
952     COMMAND_ERROR;
953     goto out;
954   }
955
956   mode = conn->local_entry->mode;
957
958   /* Are we adding or removing mode */
959   if (cmd->argv[1][0] == '-')
960     add = FALSE;
961   else
962     add = TRUE;
963
964   /* Parse mode */
965   cp = cmd->argv[1] + 1;
966   len = strlen(cp);
967   for (i = 0; i < len; i++) {
968     switch(cp[i]) {
969     case 'a':
970       if (add) {
971         mode = 0;
972         mode |= SILC_UMODE_SERVER_OPERATOR;
973         mode |= SILC_UMODE_ROUTER_OPERATOR;
974       } else {
975         mode = SILC_UMODE_NONE;
976       }
977       break;
978     case 's':
979       if (add)
980         mode |= SILC_UMODE_SERVER_OPERATOR;
981       else
982         mode &= ~SILC_UMODE_SERVER_OPERATOR;
983       break;
984     case 'r':
985       if (add)
986         mode |= SILC_UMODE_ROUTER_OPERATOR;
987       else
988         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
989       break;
990     default:
991       COMMAND_ERROR;
992       goto out;
993       break;
994     }
995   }
996
997   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
998   SILC_PUT32_MSB(mode, modebuf);
999
1000   /* Send the command packet. We support sending only one mode at once
1001      that requires an argument. */
1002   buffer = 
1003     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1004                                    1, idp->data, idp->len, 
1005                                    2, modebuf, sizeof(modebuf));
1006   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1007                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1008   silc_buffer_free(buffer);
1009   silc_buffer_free(idp);
1010
1011   /* Notify application */
1012   COMMAND;
1013
1014  out:
1015   silc_client_command_free(cmd);
1016 }
1017
1018 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1019    can be set several at once. Those modes that require argument must be set
1020    separately (unless set with modes that does not require arguments). */
1021
1022 SILC_CLIENT_CMD_FUNC(cmode)
1023 {
1024   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1025   SilcClientConnection conn = cmd->conn;
1026   SilcChannelEntry channel;
1027   SilcBuffer buffer, chidp;
1028   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1029   unsigned int mode, add, type, len, arg_len = 0;
1030   int i;
1031
1032   if (!cmd->conn) {
1033     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1034     COMMAND_ERROR;
1035     goto out;
1036   }
1037
1038   if (cmd->argc < 3) {
1039     cmd->client->ops->say(cmd->client, conn, 
1040                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1041     COMMAND_ERROR;
1042     goto out;
1043   }
1044
1045   if (cmd->argv[1][0] == '*') {
1046     if (!conn->current_channel) {
1047       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1048       COMMAND_ERROR;
1049       goto out;
1050     }
1051
1052     channel = conn->current_channel;
1053   } else {
1054     name = cmd->argv[1];
1055
1056     channel = silc_client_get_channel(cmd->client, conn, name);
1057     if (!channel) {
1058       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1059       COMMAND_ERROR;
1060       goto out;
1061     }
1062   }
1063
1064   mode = channel->mode;
1065
1066   /* Are we adding or removing mode */
1067   if (cmd->argv[2][0] == '-')
1068     add = FALSE;
1069   else
1070     add = TRUE;
1071
1072   /* Argument type to be sent to server */
1073   type = 0;
1074
1075   /* Parse mode */
1076   cp = cmd->argv[2] + 1;
1077   len = strlen(cp);
1078   for (i = 0; i < len; i++) {
1079     switch(cp[i]) {
1080     case 'p':
1081       if (add)
1082         mode |= SILC_CHANNEL_MODE_PRIVATE;
1083       else
1084         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1085       break;
1086     case 's':
1087       if (add)
1088         mode |= SILC_CHANNEL_MODE_SECRET;
1089       else
1090         mode &= ~SILC_CHANNEL_MODE_SECRET;
1091       break;
1092     case 'k':
1093       if (add)
1094         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1095       else
1096         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1097       break;
1098     case 'i':
1099       if (add)
1100         mode |= SILC_CHANNEL_MODE_INVITE;
1101       else
1102         mode &= ~SILC_CHANNEL_MODE_INVITE;
1103       break;
1104     case 't':
1105       if (add)
1106         mode |= SILC_CHANNEL_MODE_TOPIC;
1107       else
1108         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1109       break;
1110     case 'l':
1111       if (add) {
1112         int ll;
1113         mode |= SILC_CHANNEL_MODE_ULIMIT;
1114         type = 3;
1115         ll = atoi(cmd->argv[3]);
1116         SILC_PUT32_MSB(ll, tmp);
1117         arg = tmp;
1118         arg_len = 4;
1119       } else {
1120         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1121       }
1122       break;
1123     case 'a':
1124       if (add) {
1125         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1126         type = 4;
1127         arg = cmd->argv[3];
1128         arg_len = cmd->argv_lens[3];
1129       } else {
1130         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1131       }
1132       break;
1133     case 'b':
1134       if (add) {
1135         mode |= SILC_CHANNEL_MODE_BAN;
1136         type = 5;
1137         arg = cmd->argv[3];
1138         arg_len = cmd->argv_lens[3];
1139       } else {
1140         mode &= ~SILC_CHANNEL_MODE_BAN;
1141       }
1142       break;
1143     case 'I':
1144       if (add) {
1145         mode |= SILC_CHANNEL_MODE_INVITE_LIST;
1146         type = 6;
1147         arg = cmd->argv[3];
1148         arg_len = cmd->argv_lens[3];
1149       } else {
1150         mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
1151       }
1152       break;
1153     case 'c':
1154       if (add) {
1155         mode |= SILC_CHANNEL_MODE_CIPHER;
1156         type = 8;
1157         arg = cmd->argv[3];
1158         arg_len = cmd->argv_lens[3];
1159       } else {
1160         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1161       }
1162       break;
1163     default:
1164       COMMAND_ERROR;
1165       goto out;
1166       break;
1167     }
1168   }
1169
1170   if (type && cmd->argc < 3) {
1171     COMMAND_ERROR;
1172     goto out;
1173   }
1174
1175   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1176   SILC_PUT32_MSB(mode, modebuf);
1177
1178   /* Send the command packet. We support sending only one mode at once
1179      that requires an argument. */
1180   if (type && arg) {
1181     buffer = 
1182       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1183                                      1, chidp->data, chidp->len, 
1184                                      2, modebuf, sizeof(modebuf),
1185                                      type, arg, arg_len);
1186   } else {
1187     buffer = 
1188       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1189                                      1, chidp->data, chidp->len, 
1190                                      2, modebuf, sizeof(modebuf));
1191   }
1192
1193   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1194                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1195   silc_buffer_free(buffer);
1196   silc_buffer_free(chidp);
1197
1198   /* Notify application */
1199   COMMAND;
1200
1201  out:
1202   silc_client_command_free(cmd);
1203 }
1204
1205 /* CUMODE command. Changes client's mode on a channel. */
1206
1207 SILC_CLIENT_CMD_FUNC(cumode)
1208 {
1209   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1210   SilcClientConnection conn = cmd->conn;
1211   SilcChannelEntry channel;
1212   SilcChannelUser chu;
1213   SilcClientEntry client_entry;
1214   SilcBuffer buffer, clidp, chidp;
1215   unsigned char *name, *cp, modebuf[4];
1216   unsigned int mode = 0, add, len;
1217   char *nickname = NULL, *server = NULL;
1218   unsigned int num = 0;
1219   int i;
1220
1221   if (!cmd->conn) {
1222     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1223     COMMAND_ERROR;
1224     goto out;
1225   }
1226
1227   if (cmd->argc < 4) {
1228     cmd->client->ops->say(cmd->client, conn, 
1229                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1230     COMMAND_ERROR;
1231     goto out;
1232   }
1233
1234   if (cmd->argv[1][0] == '*') {
1235     if (!conn->current_channel) {
1236       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1237       COMMAND_ERROR;
1238       goto out;
1239     }
1240
1241     channel = conn->current_channel;
1242   } else {
1243     name = cmd->argv[1];
1244
1245     channel = silc_client_get_channel(cmd->client, conn, name);
1246     if (!channel) {
1247       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1248       COMMAND_ERROR;
1249       goto out;
1250     }
1251   }
1252
1253   /* Parse the typed nickname. */
1254   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1255     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1256     COMMAND_ERROR;
1257     goto out;
1258   }
1259
1260   /* Find client entry */
1261   client_entry = silc_idlist_get_client(cmd->client, conn, 
1262                                         nickname, server, num, TRUE);
1263   if (!client_entry) {
1264     /* Client entry not found, it was requested thus mark this to be
1265        pending command. */
1266     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1267                                 conn->cmd_ident,  
1268                                 silc_client_command_destructor,
1269                                 silc_client_command_cumode, 
1270                                 silc_client_command_dup(cmd));
1271     cmd->pending = 1;
1272     return;
1273   }
1274   
1275   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1276     if (chu->client == client_entry) {
1277       chu->mode = mode;
1278       break;
1279     }
1280   }
1281
1282   /* Are we adding or removing mode */
1283   if (cmd->argv[2][0] == '-')
1284     add = FALSE;
1285   else
1286     add = TRUE;
1287
1288   /* Parse mode */
1289   cp = cmd->argv[2] + 1;
1290   len = strlen(cp);
1291   for (i = 0; i < len; i++) {
1292     switch(cp[i]) {
1293     case 'a':
1294       if (add) {
1295         mode |= SILC_CHANNEL_UMODE_CHANFO;
1296         mode |= SILC_CHANNEL_UMODE_CHANOP;
1297       } else {
1298         mode = SILC_CHANNEL_UMODE_NONE;
1299       }
1300       break;
1301     case 'f':
1302       if (add)
1303         mode |= SILC_CHANNEL_UMODE_CHANFO;
1304       else
1305         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1306       break;
1307     case 'o':
1308       if (add)
1309         mode |= SILC_CHANNEL_UMODE_CHANOP;
1310       else
1311         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1312       break;
1313     default:
1314       COMMAND_ERROR;
1315       goto out;
1316       break;
1317     }
1318   }
1319
1320   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1321   SILC_PUT32_MSB(mode, modebuf);
1322   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1323
1324   /* Send the command packet. We support sending only one mode at once
1325      that requires an argument. */
1326   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1327                                           1, chidp->data, chidp->len, 
1328                                           2, modebuf, 4,
1329                                           3, clidp->data, clidp->len);
1330
1331   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1332                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1333   silc_buffer_free(buffer);
1334   silc_buffer_free(chidp);
1335   silc_buffer_free(clidp);
1336   
1337   /* Notify application */
1338   COMMAND;
1339
1340  out:
1341   if (nickname)
1342     silc_free(nickname);
1343   if (server)
1344     silc_free(server);
1345   silc_client_command_free(cmd);
1346 }
1347
1348 /* KICK command. Kicks a client out of channel. */
1349
1350 SILC_CLIENT_CMD_FUNC(kick)
1351 {
1352   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1353   SilcClientConnection conn = cmd->conn;
1354   SilcIDCacheEntry id_cache = NULL;
1355   SilcChannelEntry channel;
1356   SilcBuffer buffer, idp, idp2;
1357   SilcClientEntry target;
1358   char *name;
1359   unsigned int num = 0;
1360   char *nickname = NULL, *server = NULL;
1361
1362   if (!cmd->conn) {
1363     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1364     COMMAND_ERROR;
1365     goto out;
1366   }
1367
1368   if (cmd->argc < 3) {
1369     cmd->client->ops->say(cmd->client, conn, 
1370                           "Usage: /KICK <channel> <nickname> [<comment>]");
1371     COMMAND_ERROR;
1372     goto out;
1373   }
1374
1375   if (cmd->argv[1][0] == '*') {
1376     if (!conn->current_channel) {
1377       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1378       COMMAND_ERROR;
1379       goto out;
1380     }
1381     name = conn->current_channel->channel_name;
1382   } else {
1383     name = cmd->argv[1];
1384   }
1385
1386   if (!conn->current_channel) {
1387     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1388     COMMAND_ERROR;
1389     goto out;
1390   }
1391
1392   /* Get the Channel ID of the channel */
1393   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1394     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1395     COMMAND_ERROR;
1396     goto out;
1397   }
1398
1399   channel = (SilcChannelEntry)id_cache->context;
1400
1401   /* Parse the typed nickname. */
1402   if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1403     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1404     COMMAND_ERROR;
1405     goto out;
1406   }
1407
1408   /* Get the target client */
1409   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1410                                   server, num, FALSE);
1411   if (!target) {
1412     cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1413                           cmd->argv[2]);
1414     COMMAND_ERROR;
1415     goto out;
1416   }
1417
1418   /* Send KICK command to the server */
1419   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1420   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1421   if (cmd->argc == 3)
1422     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1423                                             1, idp->data, idp->len,
1424                                             2, idp2->data, idp2->len);
1425   else
1426     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1427                                             1, idp->data, idp->len,
1428                                             2, idp2->data, idp2->len,
1429                                             3, cmd->argv[3], 
1430                                             strlen(cmd->argv[3]));
1431   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1432                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1433   silc_buffer_free(buffer);
1434   silc_buffer_free(idp);
1435   silc_buffer_free(idp2);
1436
1437   /* Notify application */
1438   COMMAND;
1439
1440  out:
1441   if (nickname)
1442     silc_free(nickname);
1443   if (server)
1444     silc_free(server);
1445   silc_client_command_free(cmd);
1446 }
1447
1448 /* OPER command. Used to obtain server operator privileges. */
1449
1450 SILC_CLIENT_CMD_FUNC(oper)
1451 {
1452   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1453   SilcClientConnection conn = cmd->conn;
1454   SilcBuffer buffer;
1455   unsigned char *auth_data;
1456   SilcBuffer auth;
1457
1458   if (!cmd->conn) {
1459     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1460     COMMAND_ERROR;
1461     goto out;
1462   }
1463
1464   if (cmd->argc < 2) {
1465     cmd->client->ops->say(cmd->client, conn, 
1466                           "Usage: /OPER <username> [<public key>]");
1467     COMMAND_ERROR;
1468     goto out;
1469   }
1470
1471   if (cmd->argc == 3) {
1472     /* XXX Get public key */
1473     auth_data = NULL;
1474     COMMAND_ERROR;
1475     goto out;
1476   } else {
1477     /* Get passphrase */
1478
1479     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1480     if (!auth_data) {
1481       COMMAND_ERROR;
1482       goto out;
1483     }
1484
1485     /* Encode the authentication payload */
1486     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1487                                     auth_data, strlen(auth_data));
1488   }
1489
1490   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1491                                           1, cmd->argv[1], 
1492                                           strlen(cmd->argv[1]),
1493                                           2, auth->data, auth->len);
1494   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1495                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1496
1497   silc_buffer_free(buffer);
1498   silc_buffer_free(auth);
1499   memset(auth_data, 0, strlen(auth_data));
1500   silc_free(auth_data);
1501
1502   /* Notify application */
1503   COMMAND;
1504
1505  out:
1506   silc_client_command_free(cmd);
1507 }
1508
1509 /* SILCOPER command. Used to obtain router operator privileges. */
1510
1511 SILC_CLIENT_CMD_FUNC(silcoper)
1512 {
1513   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1514   SilcClientConnection conn = cmd->conn;
1515   SilcBuffer buffer;
1516   unsigned char *auth_data;
1517   SilcBuffer auth;
1518
1519   if (!cmd->conn) {
1520     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1521     COMMAND_ERROR;
1522     goto out;
1523   }
1524
1525   if (cmd->argc < 2) {
1526     cmd->client->ops->say(cmd->client, conn, 
1527                           "Usage: /SILCOPER <username> [<public key>]");
1528     COMMAND_ERROR;
1529     goto out;
1530   }
1531
1532   if (cmd->argc == 3) {
1533     /* XXX Get public key */
1534     auth_data = NULL;
1535     COMMAND_ERROR;
1536     goto out;
1537   } else {
1538     /* Get passphrase */
1539
1540     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1541     if (!auth_data) {
1542       COMMAND_ERROR;
1543       goto out;
1544     }
1545
1546     /* Encode the authentication payload */
1547     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1548                                     auth_data, strlen(auth_data));
1549   }
1550
1551   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1552                                           1, cmd->argv[1], 
1553                                           strlen(cmd->argv[1]),
1554                                           2, auth->data, auth->len);
1555   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1556                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1557
1558   silc_buffer_free(buffer);
1559   silc_buffer_free(auth);
1560   memset(auth_data, 0, strlen(auth_data));
1561   silc_free(auth_data);
1562
1563   /* Notify application */
1564   COMMAND;
1565
1566  out:
1567   silc_client_command_free(cmd);
1568 }
1569
1570 /* CONNECT command. Connects the server to another server. */
1571
1572 SILC_CLIENT_CMD_FUNC(connect)
1573 {
1574   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1575   SilcClientConnection conn = cmd->conn;
1576   SilcBuffer buffer;
1577   unsigned char port[4];
1578   unsigned int tmp;
1579
1580   if (!cmd->conn) {
1581     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1582     COMMAND_ERROR;
1583     goto out;
1584   }
1585
1586   if (cmd->argc < 2) {
1587     cmd->client->ops->say(cmd->client, conn, 
1588                           "Usage: /CONNECT <server> [<port>]");
1589     COMMAND_ERROR;
1590     goto out;
1591   }
1592
1593   if (cmd->argc == 3) {
1594     tmp = atoi(cmd->argv[2]);
1595     SILC_PUT32_MSB(tmp, port);
1596   }
1597
1598   if (cmd->argc == 3)
1599     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1600                                             1, cmd->argv[1], 
1601                                             strlen(cmd->argv[1]),
1602                                             2, port, 4);
1603   else
1604     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1605                                             1, cmd->argv[1], 
1606                                             strlen(cmd->argv[1]));
1607   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1608                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1609   silc_buffer_free(buffer);
1610
1611   /* Notify application */
1612   COMMAND;
1613
1614  out:
1615   silc_client_command_free(cmd);
1616 }
1617
1618 SILC_CLIENT_CMD_FUNC(restart)
1619 {
1620 }
1621
1622 /* CLOSE command. Close server connection to the remote server */
1623  
1624 SILC_CLIENT_CMD_FUNC(close)
1625 {
1626   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1627   SilcClientConnection conn = cmd->conn;
1628   SilcBuffer buffer;
1629   unsigned char port[4];
1630   unsigned int tmp;
1631
1632   if (!cmd->conn) {
1633     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1634     COMMAND_ERROR;
1635     goto out;
1636   }
1637
1638   if (cmd->argc < 2) {
1639     cmd->client->ops->say(cmd->client, conn, 
1640                           "Usage: /CLOSE <server> [<port>]");
1641     COMMAND_ERROR;
1642     goto out;
1643   }
1644
1645   if (cmd->argc == 3) {
1646     tmp = atoi(cmd->argv[2]);
1647     SILC_PUT32_MSB(tmp, port);
1648   }
1649
1650   if (cmd->argc == 3)
1651     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1652                                             1, cmd->argv[1], 
1653                                             strlen(cmd->argv[1]),
1654                                             2, port, 4);
1655   else
1656     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1657                                             1, cmd->argv[1], 
1658                                             strlen(cmd->argv[1]));
1659   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1660                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1661   silc_buffer_free(buffer);
1662
1663   /* Notify application */
1664   COMMAND;
1665
1666  out:
1667   silc_client_command_free(cmd);
1668 }
1669  
1670 /* SHUTDOWN command. Shutdowns the server. */
1671
1672 SILC_CLIENT_CMD_FUNC(shutdown)
1673 {
1674   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1675
1676   if (!cmd->conn) {
1677     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1678     COMMAND_ERROR;
1679     goto out;
1680   }
1681
1682   /* Send the command */
1683   silc_client_send_command(cmd->client, cmd->conn, 
1684                            SILC_COMMAND_SHUTDOWN, 0, 0);
1685
1686   /* Notify application */
1687   COMMAND;
1688
1689  out:
1690   silc_client_command_free(cmd);
1691 }
1692  
1693 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1694
1695 SILC_CLIENT_CMD_FUNC(leave)
1696 {
1697   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1698   SilcClientConnection conn = cmd->conn;
1699   SilcIDCacheEntry id_cache = NULL;
1700   SilcChannelEntry channel;
1701   SilcBuffer buffer, idp;
1702   char *name;
1703
1704   if (!cmd->conn) {
1705     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1706     COMMAND_ERROR;
1707     goto out;
1708   }
1709
1710   if (cmd->argc != 2) {
1711     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1712     COMMAND_ERROR;
1713     goto out;
1714   }
1715
1716   if (cmd->argv[1][0] == '*') {
1717     if (!conn->current_channel) {
1718       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1719       COMMAND_ERROR;
1720       goto out;
1721     }
1722     name = conn->current_channel->channel_name;
1723   } else {
1724     name = cmd->argv[1];
1725   }
1726
1727   if (!conn->current_channel) {
1728     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1729     COMMAND_ERROR;
1730     goto out;
1731   }
1732
1733   /* Get the Channel ID of the channel */
1734   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1735     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1736     COMMAND_ERROR;
1737     goto out;
1738   }
1739
1740   channel = (SilcChannelEntry)id_cache->context;
1741
1742   /* Send LEAVE command to the server */
1743   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1744   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1745                                           1, idp->data, idp->len);
1746   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1747                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1748   silc_buffer_free(buffer);
1749   silc_buffer_free(idp);
1750
1751   /* We won't talk anymore on this channel */
1752   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1753
1754   conn->current_channel = NULL;
1755
1756   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1757   silc_free(channel->channel_name);
1758   silc_free(channel->id);
1759   silc_free(channel->key);
1760   silc_cipher_free(channel->channel_key);
1761   silc_free(channel);
1762
1763   /* Notify application */
1764   COMMAND;
1765
1766  out:
1767   silc_client_command_free(cmd);
1768 }
1769
1770 /* Command USERS. Requests the USERS of the clients joined on requested
1771    channel. */
1772
1773 SILC_CLIENT_CMD_FUNC(users)
1774 {
1775   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1776   SilcClientConnection conn = cmd->conn;
1777   SilcIDCacheEntry id_cache = NULL;
1778   SilcChannelEntry channel;
1779   SilcBuffer buffer, idp;
1780   char *name, *line = NULL;
1781   unsigned int line_len = 0;
1782
1783   if (!cmd->conn) {
1784     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1785     COMMAND_ERROR;
1786     goto out;
1787   }
1788
1789   if (cmd->argc != 2) {
1790     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1791     COMMAND_ERROR;
1792     goto out;
1793   }
1794
1795   if (cmd->argv[1][0] == '*') {
1796     if (!conn->current_channel) {
1797       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1798       COMMAND_ERROR;
1799       goto out;
1800     }
1801     name = conn->current_channel->channel_name;
1802   } else {
1803     name = cmd->argv[1];
1804   }
1805
1806   if (!conn->current_channel) {
1807     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1808     COMMAND_ERROR;
1809     goto out;
1810   }
1811
1812   /* Get the Channel ID of the channel */
1813   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1814     /* XXX should resolve the channel ID; LIST command */
1815     cmd->client->ops->say(cmd->client, conn, 
1816                           "You are not on that channel", name);
1817     COMMAND_ERROR;
1818     goto out;
1819   }
1820
1821   channel = (SilcChannelEntry)id_cache->context;
1822
1823   if (!cmd->pending) {
1824     /* Send USERS command to the server */
1825     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1826     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
1827                                             ++conn->cmd_ident, 1, 
1828                                             1, idp->data, idp->len);
1829     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1830                             NULL, 0, NULL, NULL, buffer->data, 
1831                             buffer->len, TRUE);
1832     silc_buffer_free(buffer);
1833     silc_buffer_free(idp);
1834
1835     /* Register pending callback which will recall this command callback with
1836        same context and reprocesses the command. When reprocessing we actually
1837        display the information on the screen. */
1838     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, 
1839                                 silc_client_command_destructor,
1840                                 silc_client_command_users, 
1841                                 silc_client_command_dup(cmd));
1842     cmd->pending = TRUE;
1843     return;
1844   }
1845
1846   if (cmd->pending) {
1847     /* Pending command. Now we've resolved the information from server and
1848        we are ready to display the information on screen. */
1849     int i;
1850     SilcChannelUser chu;
1851
1852     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1853                           channel->channel_name);
1854
1855     line = silc_calloc(4096, sizeof(*line));
1856     line_len = 4096;
1857     silc_list_start(channel->clients);
1858     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1859       SilcClientEntry e = chu->client;
1860       char *m, tmp[80], len1;
1861
1862       memset(line, 0, sizeof(line_len));
1863
1864       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1865         silc_free(line);
1866         line_len += strlen(e->nickname) + strlen(e->server) + 100;
1867         line = silc_calloc(line_len, sizeof(*line));
1868       }
1869
1870       memset(tmp, 0, sizeof(tmp));
1871       m = silc_client_chumode_char(chu->mode);
1872
1873       strncat(line, " ", 1);
1874       strncat(line, e->nickname, strlen(e->nickname));
1875       strncat(line, e->server ? "@" : "", 1);
1876
1877       len1 = 0;
1878       if (e->server)
1879         len1 = strlen(e->server);
1880       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1881
1882       len1 = strlen(line);
1883       if (len1 >= 30) {
1884         memset(&line[29], 0, len1 - 29);
1885       } else {
1886         for (i = 0; i < 30 - len1 - 1; i++)
1887           strcat(line, " ");
1888       }
1889
1890       strncat(line, "  H", 3);
1891       strcat(tmp, m ? m : "");
1892       strncat(line, tmp, strlen(tmp));
1893
1894       if (strlen(tmp) < 5)
1895         for (i = 0; i < 5 - strlen(tmp); i++)
1896           strcat(line, " ");
1897
1898       strcat(line, e->username ? e->username : "");
1899
1900       cmd->client->ops->say(cmd->client, conn, "%s", line);
1901
1902       if (m)
1903         silc_free(m);
1904     }
1905   }
1906
1907   if (line)
1908     silc_free(line);
1909
1910   /* Notify application */
1911   COMMAND;
1912
1913  out:
1914   silc_client_command_free(cmd);
1915 }