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