updates.
[silc.git] / lib / silcclient / command.c
1 /*
2
3   command.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "clientlibincludes.h"
23 #include "client_internal.h"
24
25 /* Client command list. */
26 SilcClientCommand silc_command_list[] =
27 {
28   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
29   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
30   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
31                   SILC_CF_LAG | SILC_CF_REG, 3),
32   SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
33   SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
34   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
35   SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
36   SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
37   SILC_CLIENT_CMD(kill, KILL, "KILL", 
38                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
39   SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
40   SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
41                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
42   SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
43   SILC_CLIENT_CMD(oper, OPER, "OPER",
44                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
45   SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
46   SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
47   SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
48   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
49   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
50   SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
51   SILC_CLIENT_CMD(restart, RESTART, "RESTART",
52                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
53   SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
54                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
55   SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
56                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
57   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
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                                        0);
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, 0,
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   silc_client_command_free(cmd);
568 }
569
570 typedef struct {
571   SilcClient client;
572   SilcClientConnection conn;
573 } *QuitInternal;
574
575 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
576 {
577   QuitInternal q = (QuitInternal)context;
578
579   /* Close connection */
580   q->client->ops->disconnect(q->client, q->conn);
581   silc_client_close_connection(q->client, q->conn->sock->user_data);
582
583   silc_free(q);
584 }
585
586 /* Command QUIT. Closes connection with current server. */
587  
588 SILC_CLIENT_CMD_FUNC(quit)
589 {
590   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
591   SilcBuffer buffer;
592   QuitInternal q;
593
594   if (!cmd->conn) {
595     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
596     COMMAND_ERROR;
597     goto out;
598   }
599
600   if (cmd->argc > 1)
601     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, 
602                                          &cmd->argv[1], &cmd->argv_lens[1],
603                                          &cmd->argv_types[1], 0);
604   else
605     buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
606                                          NULL, NULL, NULL, 0);
607   silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
608                           NULL, 0, NULL, NULL, 
609                           buffer->data, buffer->len, TRUE);
610   silc_buffer_free(buffer);
611
612   q = silc_calloc(1, sizeof(*q));
613   q->client = cmd->client;
614   q->conn = cmd->conn;
615
616   /* We quit the connection with little timeout */
617   silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
618                      silc_client_command_quit_cb, (void *)q,
619                      1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
620
621   /* Notify application */
622   COMMAND;
623
624  out:
625   silc_client_command_free(cmd);
626 }
627
628 SILC_CLIENT_CMD_FUNC(kill)
629 {
630 }
631
632 /* Command INFO. Request information about specific server. If specific
633    server is not provided the current server is used. */
634
635 SILC_CLIENT_CMD_FUNC(info)
636 {
637   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
638   SilcClientConnection conn = cmd->conn;
639   SilcBuffer buffer;
640   char *name;
641
642   if (!cmd->conn) {
643     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
644     COMMAND_ERROR;
645     goto out;
646   }
647
648   if (cmd->argc < 2)
649     name = strdup(conn->remote_host);
650   else
651     name = strdup(cmd->argv[1]);
652
653   /* Send the command */
654   buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
655                                           1, name, strlen(name));
656   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
657                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
658   silc_buffer_free(buffer);
659
660   /* Notify application */
661   COMMAND;
662
663  out:
664   silc_client_command_free(cmd);
665 }
666
667 /* Command PING. Sends ping to server. This is used to test the 
668    communication channel. */
669
670 SILC_CLIENT_CMD_FUNC(ping)
671 {
672   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
673   SilcClientConnection conn = cmd->conn;
674   SilcBuffer buffer;
675   void *id;
676   int i;
677   char *name = NULL;
678
679   if (!cmd->conn) {
680     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
681     COMMAND_ERROR;
682     goto out;
683   }
684
685   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
686     name = strdup(conn->remote_host);
687
688   /* Send the command */
689   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
690                                           1, conn->remote_id_data, 
691                                           SILC_ID_SERVER_LEN);
692   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
693                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
694   silc_buffer_free(buffer);
695
696   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
697                       SILC_ID_SERVER);
698   if (!id) {
699     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
700     COMMAND_ERROR;
701     goto out;
702   }
703
704   /* Start counting time */
705   for (i = 0; i < conn->ping_count; i++) {
706     if (conn->ping[i].dest_id == NULL) {
707       conn->ping[i].start_time = time(NULL);
708       conn->ping[i].dest_id = id;
709       conn->ping[i].dest_name = name;
710       conn->ping_count++;
711       break;
712     }
713   }
714   if (i >= conn->ping_count) {
715     i = conn->ping_count;
716     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
717     conn->ping[i].start_time = time(NULL);
718     conn->ping[i].dest_id = id;
719     conn->ping[i].dest_name = name;
720     conn->ping_count++;
721   }
722   
723   /* Notify application */
724   COMMAND;
725
726  out:
727   silc_client_command_free(cmd);
728 }
729
730 SILC_CLIENT_CMD_FUNC(notice)
731 {
732 }
733
734 /* Command JOIN. Joins to a channel. */
735
736 SILC_CLIENT_CMD_FUNC(join)
737 {
738   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
739   SilcClientConnection conn = cmd->conn;
740   SilcIDCacheEntry id_cache = NULL;
741   SilcBuffer buffer, idp;
742
743   if (!cmd->conn) {
744     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
745     COMMAND_ERROR;
746     goto out;
747   }
748
749   if (cmd->argc < 2) {
750     /* Show channels currently joined to */
751
752     goto out;
753   }
754
755   /* See if we have joined to the requested channel already */
756   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
757                                     &id_cache)) {
758     cmd->client->ops->say(cmd->client, conn, 
759                           "You are talking to channel %s", cmd->argv[1]);
760     conn->current_channel = (SilcChannelEntry)id_cache->context;
761 #if 0
762     cmd->client->screen->bottom_line->channel = cmd->argv[1];
763     silc_screen_print_bottom_line(cmd->client->screen, 0);
764 #endif
765     goto out;
766   }
767
768   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
769
770   /* Send JOIN command to the server */
771   if (cmd->argc == 2)
772     buffer = 
773       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
774                                      1, cmd->argv[1], cmd->argv_lens[1],
775                                      2, idp->data, idp->len);
776   else if (cmd->argc == 3)
777     /* XXX Buggy */
778     buffer = 
779       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
780                                      1, cmd->argv[1], cmd->argv_lens[1],
781                                      2, idp->data, idp->len,
782                                      3, cmd->argv[2], cmd->argv_lens[2]);
783   else
784     buffer = 
785       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
786                                      1, cmd->argv[1], cmd->argv_lens[1],
787                                      2, idp->data, idp->len,
788                                      3, cmd->argv[2], cmd->argv_lens[2],
789                                      4, cmd->argv[3], cmd->argv_lens[3]);
790
791   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
792                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
793   silc_buffer_free(buffer);
794   silc_buffer_free(idp);
795
796   /* Notify application */
797   COMMAND;
798
799  out:
800   silc_client_command_free(cmd);
801 }
802
803 /* MOTD command. Requests motd from server. */
804
805 SILC_CLIENT_CMD_FUNC(motd)
806 {
807   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
808   SilcClientConnection conn = cmd->conn;
809   SilcBuffer buffer;
810
811   if (!cmd->conn) {
812     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
813     COMMAND_ERROR;
814     goto out;
815   }
816
817   if (cmd->argc < 1 || cmd->argc > 1) {
818     cmd->client->ops->say(cmd->client, conn,
819                           "Usage: /MOTD");
820     COMMAND_ERROR;
821     goto out;
822   }
823
824   /* Send TOPIC command to the server */
825   buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
826                                           2, conn->remote_host, 
827                                           strlen(conn->remote_host));
828   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
829                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
830   silc_buffer_free(buffer);
831
832   /* Notify application */
833   COMMAND;
834
835  out:
836   silc_client_command_free(cmd);
837 }
838
839 /* UMODE. Set user mode in SILC. */
840
841 SILC_CLIENT_CMD_FUNC(umode)
842 {
843
844 }
845
846 /* CMODE command. Sets channel mode. Modes that does not require any arguments
847    can be set several at once. Those modes that require argument must be set
848    separately (unless set with modes that does not require arguments). */
849
850 SILC_CLIENT_CMD_FUNC(cmode)
851 {
852   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
853   SilcClientConnection conn = cmd->conn;
854   SilcChannelEntry channel;
855   SilcBuffer buffer, chidp;
856   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
857   unsigned int mode, add, type, len, arg_len = 0;
858   int i;
859
860   if (!cmd->conn) {
861     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
862     COMMAND_ERROR;
863     goto out;
864   }
865
866   if (cmd->argc < 3) {
867     cmd->client->ops->say(cmd->client, conn, 
868                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
869     COMMAND_ERROR;
870     goto out;
871   }
872
873   if (cmd->argv[1][0] == '*') {
874     if (!conn->current_channel) {
875       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
876       COMMAND_ERROR;
877       goto out;
878     }
879
880     channel = conn->current_channel;
881   } else {
882     name = cmd->argv[1];
883
884     channel = silc_client_get_channel(cmd->client, conn, name);
885     if (!channel) {
886       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
887       COMMAND_ERROR;
888       goto out;
889     }
890   }
891
892   mode = channel->mode;
893
894   /* Are we adding or removing mode */
895   if (cmd->argv[2][0] == '-')
896     add = FALSE;
897   else
898     add = TRUE;
899
900   /* Argument type to be sent to server */
901   type = 0;
902
903   /* Parse mode */
904   cp = cmd->argv[2] + 1;
905   len = strlen(cp);
906   for (i = 0; i < len; i++) {
907     switch(cp[i]) {
908     case 'p':
909       if (add)
910         mode |= SILC_CHANNEL_MODE_PRIVATE;
911       else
912         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
913       break;
914     case 's':
915       if (add)
916         mode |= SILC_CHANNEL_MODE_SECRET;
917       else
918         mode &= ~SILC_CHANNEL_MODE_SECRET;
919       break;
920     case 'k':
921       if (add)
922         mode |= SILC_CHANNEL_MODE_PRIVKEY;
923       else
924         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
925       break;
926     case 'i':
927       if (add)
928         mode |= SILC_CHANNEL_MODE_INVITE;
929       else
930         mode &= ~SILC_CHANNEL_MODE_INVITE;
931       break;
932     case 't':
933       if (add)
934         mode |= SILC_CHANNEL_MODE_TOPIC;
935       else
936         mode &= ~SILC_CHANNEL_MODE_TOPIC;
937       break;
938     case 'l':
939       if (add) {
940         int ll;
941         mode |= SILC_CHANNEL_MODE_ULIMIT;
942         type = 3;
943         ll = atoi(cmd->argv[3]);
944         SILC_PUT32_MSB(ll, tmp);
945         arg = tmp;
946         arg_len = 4;
947       } else {
948         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
949       }
950       break;
951     case 'a':
952       if (add) {
953         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
954         type = 4;
955         arg = cmd->argv[3];
956         arg_len = cmd->argv_lens[3];
957       } else {
958         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
959       }
960       break;
961     case 'b':
962       if (add) {
963         mode |= SILC_CHANNEL_MODE_BAN;
964         type = 5;
965         arg = cmd->argv[3];
966         arg_len = cmd->argv_lens[3];
967       } else {
968         mode &= ~SILC_CHANNEL_MODE_BAN;
969       }
970       break;
971     case 'I':
972       if (add) {
973         mode |= SILC_CHANNEL_MODE_INVITE_LIST;
974         type = 6;
975         arg = cmd->argv[3];
976         arg_len = cmd->argv_lens[3];
977       } else {
978         mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
979       }
980       break;
981     case 'c':
982       if (add) {
983         mode |= SILC_CHANNEL_MODE_CIPHER;
984         type = 8;
985         arg = cmd->argv[3];
986         arg_len = cmd->argv_lens[3];
987       } else {
988         mode &= ~SILC_CHANNEL_MODE_CIPHER;
989       }
990       break;
991     default:
992       COMMAND_ERROR;
993       goto out;
994       break;
995     }
996   }
997
998   if (type && cmd->argc < 3) {
999     COMMAND_ERROR;
1000     goto out;
1001   }
1002
1003   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1004   SILC_PUT32_MSB(mode, modebuf);
1005
1006   /* Send the command packet. We support sending only one mode at once
1007      that requires an argument. */
1008   if (type && arg) {
1009     buffer = 
1010       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1011                                      1, chidp->data, chidp->len, 
1012                                      2, modebuf, sizeof(modebuf),
1013                                      type, arg, arg_len);
1014   } else {
1015     buffer = 
1016       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1017                                      1, chidp->data, chidp->len, 
1018                                      2, modebuf, sizeof(modebuf));
1019   }
1020
1021   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1022                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1023   silc_buffer_free(buffer);
1024   silc_buffer_free(chidp);
1025
1026   /* Notify application */
1027   COMMAND;
1028
1029  out:
1030   silc_client_command_free(cmd);
1031 }
1032
1033 /* CUMODE command. Changes client's mode on a channel. */
1034
1035 SILC_CLIENT_CMD_FUNC(cumode)
1036 {
1037   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1038   SilcClientConnection conn = cmd->conn;
1039   SilcChannelEntry channel;
1040   SilcChannelUser chu;
1041   SilcClientEntry client_entry;
1042   SilcBuffer buffer, clidp, chidp;
1043   unsigned char *name, *cp, modebuf[4];
1044   unsigned int mode = 0, add, len;
1045   char *nickname = NULL, *server = NULL;
1046   unsigned int num = 0;
1047   int i;
1048
1049   if (!cmd->conn) {
1050     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1051     COMMAND_ERROR;
1052     goto out;
1053   }
1054
1055   if (cmd->argc < 4) {
1056     cmd->client->ops->say(cmd->client, conn, 
1057                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1058     COMMAND_ERROR;
1059     goto out;
1060   }
1061
1062   if (cmd->argv[1][0] == '*') {
1063     if (!conn->current_channel) {
1064       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1065       COMMAND_ERROR;
1066       goto out;
1067     }
1068
1069     channel = conn->current_channel;
1070   } else {
1071     name = cmd->argv[1];
1072
1073     channel = silc_client_get_channel(cmd->client, conn, name);
1074     if (!channel) {
1075       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1076       COMMAND_ERROR;
1077       goto out;
1078     }
1079   }
1080
1081   /* Parse the typed nickname. */
1082   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1083     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1084     COMMAND_ERROR;
1085     goto out;
1086   }
1087
1088   /* Find client entry */
1089   client_entry = silc_idlist_get_client(cmd->client, conn, 
1090                                         nickname, server, num, TRUE);
1091   if (!client_entry) {
1092     /* Client entry not found, it was requested thus mark this to be
1093        pending command. */
1094     silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,  
1095                                 silc_client_command_destructor,
1096                                 silc_client_command_cumode, 
1097                                 silc_client_command_dup(cmd));
1098     cmd->pending = 1;
1099     return;
1100   }
1101   
1102   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1103     if (chu->client == client_entry) {
1104       chu->mode = mode;
1105       break;
1106     }
1107   }
1108
1109   /* Are we adding or removing mode */
1110   if (cmd->argv[2][0] == '-')
1111     add = FALSE;
1112   else
1113     add = TRUE;
1114
1115   /* Parse mode */
1116   cp = cmd->argv[2] + 1;
1117   len = strlen(cp);
1118   for (i = 0; i < len; i++) {
1119     switch(cp[i]) {
1120     case 'a':
1121       if (add) {
1122         mode |= SILC_CHANNEL_UMODE_CHANFO;
1123         mode |= SILC_CHANNEL_UMODE_CHANOP;
1124       } else {
1125         mode = SILC_CHANNEL_UMODE_NONE;
1126       }
1127       break;
1128     case 'f':
1129       if (add)
1130         mode |= SILC_CHANNEL_UMODE_CHANFO;
1131       else
1132         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1133       break;
1134     case 'o':
1135       if (add)
1136         mode |= SILC_CHANNEL_UMODE_CHANOP;
1137       else
1138         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1139       break;
1140     default:
1141       COMMAND_ERROR;
1142       goto out;
1143       break;
1144     }
1145   }
1146
1147   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1148   SILC_PUT32_MSB(mode, modebuf);
1149   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1150
1151   /* Send the command packet. We support sending only one mode at once
1152      that requires an argument. */
1153   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1154                                           1, chidp->data, chidp->len, 
1155                                           2, modebuf, 4,
1156                                           3, clidp->data, clidp->len);
1157
1158   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1159                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1160   silc_buffer_free(buffer);
1161   silc_buffer_free(chidp);
1162   silc_buffer_free(clidp);
1163   
1164   /* Notify application */
1165   COMMAND;
1166
1167  out:
1168   silc_client_command_free(cmd);
1169 }
1170
1171 /* KICK command. Kicks a client out of channel. */
1172
1173 SILC_CLIENT_CMD_FUNC(kick)
1174 {
1175   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1176   SilcClientConnection conn = cmd->conn;
1177   SilcIDCacheEntry id_cache = NULL;
1178   SilcChannelEntry channel;
1179   SilcBuffer buffer, idp, idp2;
1180   SilcClientEntry target;
1181   char *name;
1182   unsigned int num = 0;
1183   char *nickname = NULL, *server = NULL;
1184
1185   if (!cmd->conn) {
1186     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1187     COMMAND_ERROR;
1188     goto out;
1189   }
1190
1191   if (cmd->argc < 3) {
1192     cmd->client->ops->say(cmd->client, conn, 
1193                           "Usage: /KICK <channel> <client> [<comment>]");
1194     COMMAND_ERROR;
1195     goto out;
1196   }
1197
1198   if (cmd->argv[1][0] == '*') {
1199     if (!conn->current_channel) {
1200       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1201       COMMAND_ERROR;
1202       goto out;
1203     }
1204     name = conn->current_channel->channel_name;
1205   } else {
1206     name = cmd->argv[1];
1207   }
1208
1209   if (!conn->current_channel) {
1210     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1211     COMMAND_ERROR;
1212     goto out;
1213   }
1214
1215   /* Get the Channel ID of the channel */
1216   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1217     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1218     COMMAND_ERROR;
1219     goto out;
1220   }
1221
1222   channel = (SilcChannelEntry)id_cache->context;
1223
1224   /* Parse the typed nickname. */
1225   if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1226     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1227     COMMAND_ERROR;
1228     goto out;
1229   }
1230
1231   /* Get the target client */
1232   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1233                                   server, num, FALSE);
1234   if (!target) {
1235     cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1236                           cmd->argv[2]);
1237     COMMAND_ERROR;
1238     goto out;
1239   }
1240
1241   /* Send KICK command to the server */
1242   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1243   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1244   if (cmd->argc == 3)
1245     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1246                                             1, idp->data, idp->len,
1247                                             2, idp2->data, idp2->len);
1248   else
1249     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1250                                             1, idp->data, idp->len,
1251                                             2, idp2->data, idp2->len,
1252                                             3, cmd->argv[3], 
1253                                             strlen(cmd->argv[3]));
1254   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1255                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1256   silc_buffer_free(buffer);
1257   silc_buffer_free(idp);
1258   silc_buffer_free(idp2);
1259
1260   /* Notify application */
1261   COMMAND;
1262
1263  out:
1264   silc_client_command_free(cmd);
1265 }
1266
1267 /* OPER command. Used to obtain server operator privileges. */
1268
1269 SILC_CLIENT_CMD_FUNC(oper)
1270 {
1271   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1272   SilcClientConnection conn = cmd->conn;
1273   SilcBuffer buffer;
1274   unsigned char *auth_data;
1275   SilcBuffer auth;
1276
1277   if (!cmd->conn) {
1278     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1279     COMMAND_ERROR;
1280     goto out;
1281   }
1282
1283   if (cmd->argc < 2) {
1284     cmd->client->ops->say(cmd->client, conn, 
1285                           "Usage: /OPER <username> [<public key>]");
1286     COMMAND_ERROR;
1287     goto out;
1288   }
1289
1290   if (cmd->argc == 3) {
1291     /* XXX Get public key */
1292     auth_data = NULL;
1293     COMMAND_ERROR;
1294     goto out;
1295   } else {
1296     /* Get passphrase */
1297
1298     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1299     if (!auth_data) {
1300       COMMAND_ERROR;
1301       goto out;
1302     }
1303
1304     /* Encode the authentication payload */
1305     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1306                                     auth_data, strlen(auth_data));
1307   }
1308
1309   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1310                                           1, cmd->argv[1], 
1311                                           strlen(cmd->argv[1]),
1312                                           2, auth->data, auth->len);
1313   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1314                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1315
1316   silc_buffer_free(buffer);
1317   silc_buffer_free(auth);
1318   memset(auth_data, 0, strlen(auth_data));
1319   silc_free(auth_data);
1320
1321   /* Notify application */
1322   COMMAND;
1323
1324  out:
1325   silc_client_command_free(cmd);
1326 }
1327
1328 /* SILCOPER command. Used to obtain router operator privileges. */
1329
1330 SILC_CLIENT_CMD_FUNC(silcoper)
1331 {
1332   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1333   SilcClientConnection conn = cmd->conn;
1334   SilcBuffer buffer;
1335   unsigned char *auth_data;
1336   SilcBuffer auth;
1337
1338   if (!cmd->conn) {
1339     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1340     COMMAND_ERROR;
1341     goto out;
1342   }
1343
1344   if (cmd->argc < 2) {
1345     cmd->client->ops->say(cmd->client, conn, 
1346                           "Usage: /SILCOPER <username> [<public key>]");
1347     COMMAND_ERROR;
1348     goto out;
1349   }
1350
1351   if (cmd->argc == 3) {
1352     /* XXX Get public key */
1353     auth_data = NULL;
1354     COMMAND_ERROR;
1355     goto out;
1356   } else {
1357     /* Get passphrase */
1358
1359     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1360     if (!auth_data) {
1361       COMMAND_ERROR;
1362       goto out;
1363     }
1364
1365     /* Encode the authentication payload */
1366     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1367                                     auth_data, strlen(auth_data));
1368   }
1369
1370   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1371                                           1, cmd->argv[1], 
1372                                           strlen(cmd->argv[1]),
1373                                           2, auth->data, auth->len);
1374   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1375                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1376
1377   silc_buffer_free(buffer);
1378   silc_buffer_free(auth);
1379   memset(auth_data, 0, strlen(auth_data));
1380   silc_free(auth_data);
1381
1382   /* Notify application */
1383   COMMAND;
1384
1385  out:
1386   silc_client_command_free(cmd);
1387 }
1388
1389 /* CONNECT command. Connects the server to another server. */
1390
1391 SILC_CLIENT_CMD_FUNC(connect)
1392 {
1393   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1394   SilcClientConnection conn = cmd->conn;
1395   SilcBuffer buffer;
1396   unsigned char port[4];
1397   unsigned int tmp;
1398
1399   if (!cmd->conn) {
1400     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1401     COMMAND_ERROR;
1402     goto out;
1403   }
1404
1405   if (cmd->argc < 2) {
1406     cmd->client->ops->say(cmd->client, conn, 
1407                           "Usage: /CONNECT <server> [<port>]");
1408     COMMAND_ERROR;
1409     goto out;
1410   }
1411
1412   if (cmd->argc == 3) {
1413     tmp = atoi(cmd->argv[2]);
1414     SILC_PUT32_MSB(tmp, port);
1415   }
1416
1417   if (cmd->argc == 3)
1418     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1419                                             1, cmd->argv[1], 
1420                                             strlen(cmd->argv[1]),
1421                                             2, port, 4);
1422   else
1423     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1424                                             1, cmd->argv[1], 
1425                                             strlen(cmd->argv[1]));
1426   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1427                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1428   silc_buffer_free(buffer);
1429
1430   /* Notify application */
1431   COMMAND;
1432
1433  out:
1434   silc_client_command_free(cmd);
1435 }
1436
1437 SILC_CLIENT_CMD_FUNC(restart)
1438 {
1439 }
1440
1441 /* CLOSE command. Close server connection to the remote server */
1442  
1443 SILC_CLIENT_CMD_FUNC(close)
1444 {
1445   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1446   SilcClientConnection conn = cmd->conn;
1447   SilcBuffer buffer;
1448   unsigned char port[4];
1449   unsigned int tmp;
1450
1451   if (!cmd->conn) {
1452     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1453     COMMAND_ERROR;
1454     goto out;
1455   }
1456
1457   if (cmd->argc < 2) {
1458     cmd->client->ops->say(cmd->client, conn, 
1459                           "Usage: /CLOSE <server> [<port>]");
1460     COMMAND_ERROR;
1461     goto out;
1462   }
1463
1464   if (cmd->argc == 3) {
1465     tmp = atoi(cmd->argv[2]);
1466     SILC_PUT32_MSB(tmp, port);
1467   }
1468
1469   if (cmd->argc == 3)
1470     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1471                                             1, cmd->argv[1], 
1472                                             strlen(cmd->argv[1]),
1473                                             2, port, 4);
1474   else
1475     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1476                                             1, cmd->argv[1], 
1477                                             strlen(cmd->argv[1]));
1478   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1479                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1480   silc_buffer_free(buffer);
1481
1482   /* Notify application */
1483   COMMAND;
1484
1485  out:
1486   silc_client_command_free(cmd);
1487 }
1488  
1489 /* SHUTDOWN command. Shutdowns the server. */
1490
1491 SILC_CLIENT_CMD_FUNC(shutdown)
1492 {
1493   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1494
1495   if (!cmd->conn) {
1496     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1497     COMMAND_ERROR;
1498     goto out;
1499   }
1500
1501   /* Send the command */
1502   silc_client_send_command(cmd->client, cmd->conn, 
1503                            SILC_COMMAND_SHUTDOWN, 0, 0);
1504
1505   /* Notify application */
1506   COMMAND;
1507
1508  out:
1509   silc_client_command_free(cmd);
1510 }
1511  
1512 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1513
1514 SILC_CLIENT_CMD_FUNC(leave)
1515 {
1516   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1517   SilcClientConnection conn = cmd->conn;
1518   SilcIDCacheEntry id_cache = NULL;
1519   SilcChannelEntry channel;
1520   SilcBuffer buffer, idp;
1521   char *name;
1522
1523   if (!cmd->conn) {
1524     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1525     COMMAND_ERROR;
1526     goto out;
1527   }
1528
1529   if (cmd->argc != 2) {
1530     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1531     COMMAND_ERROR;
1532     goto out;
1533   }
1534
1535   if (cmd->argv[1][0] == '*') {
1536     if (!conn->current_channel) {
1537       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1538       COMMAND_ERROR;
1539       goto out;
1540     }
1541     name = conn->current_channel->channel_name;
1542   } else {
1543     name = cmd->argv[1];
1544   }
1545
1546   if (!conn->current_channel) {
1547     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1548     COMMAND_ERROR;
1549     goto out;
1550   }
1551
1552   /* Get the Channel ID of the channel */
1553   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1554     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1555     COMMAND_ERROR;
1556     goto out;
1557   }
1558
1559   channel = (SilcChannelEntry)id_cache->context;
1560
1561   /* Send LEAVE command to the server */
1562   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1563   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1564                                           1, idp->data, idp->len);
1565   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1566                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1567   silc_buffer_free(buffer);
1568   silc_buffer_free(idp);
1569
1570   /* We won't talk anymore on this channel */
1571   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1572
1573   conn->current_channel = NULL;
1574
1575   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1576   silc_free(channel->channel_name);
1577   silc_free(channel->id);
1578   silc_free(channel->key);
1579   silc_cipher_free(channel->channel_key);
1580   silc_free(channel);
1581
1582   /* Notify application */
1583   COMMAND;
1584
1585  out:
1586   silc_client_command_free(cmd);
1587 }
1588
1589 /* Command USERS. Requests the USERS of the clients joined on requested
1590    channel. */
1591
1592 SILC_CLIENT_CMD_FUNC(users)
1593 {
1594   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1595   SilcClientConnection conn = cmd->conn;
1596   SilcIDCacheEntry id_cache = NULL;
1597   SilcChannelEntry channel;
1598   SilcBuffer buffer, idp;
1599   char *name, *line = NULL;
1600   unsigned int line_len = 0;
1601
1602   if (!cmd->conn) {
1603     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1604     COMMAND_ERROR;
1605     goto out;
1606   }
1607
1608   if (cmd->argc != 2) {
1609     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1610     COMMAND_ERROR;
1611     goto out;
1612   }
1613
1614   if (cmd->argv[1][0] == '*') {
1615     if (!conn->current_channel) {
1616       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1617       COMMAND_ERROR;
1618       goto out;
1619     }
1620     name = conn->current_channel->channel_name;
1621   } else {
1622     name = cmd->argv[1];
1623   }
1624
1625   if (!conn->current_channel) {
1626     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1627     COMMAND_ERROR;
1628     goto out;
1629   }
1630
1631   /* Get the Channel ID of the channel */
1632   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1633     /* XXX should resolve the channel ID; LIST command */
1634     cmd->client->ops->say(cmd->client, conn, 
1635                           "You are not on that channel", name);
1636     COMMAND_ERROR;
1637     goto out;
1638   }
1639
1640   channel = (SilcChannelEntry)id_cache->context;
1641
1642   if (!cmd->pending) {
1643     /* Send USERS command to the server */
1644     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1645     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1, 
1646                                             1, idp->data, idp->len);
1647     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1648                             NULL, 0, NULL, NULL, buffer->data, 
1649                             buffer->len, TRUE);
1650     silc_buffer_free(buffer);
1651     silc_buffer_free(idp);
1652
1653     /* Register pending callback which will recall this command callback with
1654        same context and reprocesses the command. When reprocessing we actually
1655        display the information on the screen. */
1656     silc_client_command_pending(conn, SILC_COMMAND_USERS, 0, 
1657                                 silc_client_command_destructor,
1658                                 silc_client_command_users, 
1659                                 silc_client_command_dup(cmd));
1660     cmd->pending = TRUE;
1661     return;
1662   }
1663
1664   if (cmd->pending) {
1665     /* Pending command. Now we've resolved the information from server and
1666        we are ready to display the information on screen. */
1667     int i;
1668     SilcChannelUser chu;
1669
1670     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1671                           channel->channel_name);
1672
1673     line = silc_calloc(4096, sizeof(*line));
1674     line_len = 4096;
1675     silc_list_start(channel->clients);
1676     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1677       SilcClientEntry e = chu->client;
1678       char *m, tmp[80], len1;
1679
1680       memset(line, 0, sizeof(line_len));
1681
1682       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1683         silc_free(line);
1684         line_len += strlen(e->nickname) + strlen(e->server) + 100;
1685         line = silc_calloc(line_len, sizeof(*line));
1686       }
1687
1688       memset(tmp, 0, sizeof(tmp));
1689       m = silc_client_chumode_char(chu->mode);
1690
1691       strncat(line, " ", 1);
1692       strncat(line, e->nickname, strlen(e->nickname));
1693       strncat(line, e->server ? "@" : "", 1);
1694
1695       len1 = 0;
1696       if (e->server)
1697         len1 = strlen(e->server);
1698       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1699
1700       len1 = strlen(line);
1701       if (len1 >= 30) {
1702         memset(&line[29], 0, len1 - 29);
1703       } else {
1704         for (i = 0; i < 30 - len1 - 1; i++)
1705           strcat(line, " ");
1706       }
1707
1708       strncat(line, "  H", 3);
1709       strcat(tmp, m ? m : "");
1710       strncat(line, tmp, strlen(tmp));
1711
1712       if (strlen(tmp) < 5)
1713         for (i = 0; i < 5 - strlen(tmp); i++)
1714           strcat(line, " ");
1715
1716       strcat(line, e->username ? e->username : "");
1717
1718       cmd->client->ops->say(cmd->client, conn, "%s", line);
1719
1720       if (m)
1721         silc_free(m);
1722     }
1723   }
1724
1725   if (line)
1726     silc_free(line);
1727
1728   /* Notify application */
1729   COMMAND;
1730
1731  out:
1732   silc_client_command_free(cmd);
1733 }