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, 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   /* Remove the client entry to be killed */
796   silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
797                          target->id);
798   if (target->nickname)
799     silc_free(target->nickname);
800   if (target->server)
801     silc_free(target->server);
802   if (target->id)
803     silc_free(target->id);
804   if (target->send_key)
805     silc_cipher_free(target->send_key);
806   if (target->receive_key)
807     silc_cipher_free(target->receive_key);
808   silc_free(target);
809
810  out:
811   if (nickname)
812     silc_free(nickname);
813   if (server)
814     silc_free(server);
815   silc_client_command_free(cmd);
816 }
817
818 /* Command INFO. Request information about specific server. If specific
819    server is not provided the current server is used. */
820
821 SILC_CLIENT_CMD_FUNC(info)
822 {
823   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
824   SilcClientConnection conn = cmd->conn;
825   SilcBuffer buffer;
826   char *name;
827
828   if (!cmd->conn) {
829     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
830     COMMAND_ERROR;
831     goto out;
832   }
833
834   if (cmd->argc < 2)
835     name = strdup(conn->remote_host);
836   else
837     name = strdup(cmd->argv[1]);
838
839   /* Send the command */
840   buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
841                                           1, name, strlen(name));
842   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
843                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
844   silc_buffer_free(buffer);
845
846   /* Notify application */
847   COMMAND;
848
849  out:
850   silc_client_command_free(cmd);
851 }
852
853 /* Command PING. Sends ping to server. This is used to test the 
854    communication channel. */
855
856 SILC_CLIENT_CMD_FUNC(ping)
857 {
858   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
859   SilcClientConnection conn = cmd->conn;
860   SilcBuffer buffer;
861   void *id;
862   int i;
863   char *name = NULL;
864
865   if (!cmd->conn) {
866     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
867     COMMAND_ERROR;
868     goto out;
869   }
870
871   if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
872     name = strdup(conn->remote_host);
873
874   /* Send the command */
875   buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
876                                           1, conn->remote_id_data, 
877                                           SILC_ID_SERVER_LEN);
878   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
879                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
880   silc_buffer_free(buffer);
881
882   id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
883                       SILC_ID_SERVER);
884   if (!id) {
885     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
886     COMMAND_ERROR;
887     goto out;
888   }
889
890   /* Start counting time */
891   for (i = 0; i < conn->ping_count; i++) {
892     if (conn->ping[i].dest_id == NULL) {
893       conn->ping[i].start_time = time(NULL);
894       conn->ping[i].dest_id = id;
895       conn->ping[i].dest_name = name;
896       conn->ping_count++;
897       break;
898     }
899   }
900   if (i >= conn->ping_count) {
901     i = conn->ping_count;
902     conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
903     conn->ping[i].start_time = time(NULL);
904     conn->ping[i].dest_id = id;
905     conn->ping[i].dest_name = name;
906     conn->ping_count++;
907   }
908   
909   /* Notify application */
910   COMMAND;
911
912  out:
913   silc_client_command_free(cmd);
914 }
915
916 SILC_CLIENT_CMD_FUNC(notice)
917 {
918 }
919
920 /* Command JOIN. Joins to a channel. */
921
922 SILC_CLIENT_CMD_FUNC(join)
923 {
924   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
925   SilcClientConnection conn = cmd->conn;
926   SilcIDCacheEntry id_cache = NULL;
927   SilcBuffer buffer, idp;
928
929   if (!cmd->conn) {
930     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
931     COMMAND_ERROR;
932     goto out;
933   }
934
935   if (cmd->argc < 2) {
936     /* Show channels currently joined to */
937
938     goto out;
939   }
940
941   /* See if we have joined to the requested channel already */
942   if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
943                                     &id_cache)) {
944     cmd->client->ops->say(cmd->client, conn, 
945                           "You are talking to channel %s", cmd->argv[1]);
946     conn->current_channel = (SilcChannelEntry)id_cache->context;
947 #if 0
948     cmd->client->screen->bottom_line->channel = cmd->argv[1];
949     silc_screen_print_bottom_line(cmd->client->screen, 0);
950 #endif
951     goto out;
952   }
953
954   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
955
956   /* Send JOIN command to the server */
957   if (cmd->argc == 2)
958     buffer = 
959       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
960                                      1, cmd->argv[1], cmd->argv_lens[1],
961                                      2, idp->data, idp->len);
962   else if (cmd->argc == 3)
963     /* XXX Buggy */
964     buffer = 
965       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
966                                      1, cmd->argv[1], cmd->argv_lens[1],
967                                      2, idp->data, idp->len,
968                                      3, cmd->argv[2], cmd->argv_lens[2]);
969   else
970     buffer = 
971       silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
972                                      1, cmd->argv[1], cmd->argv_lens[1],
973                                      2, idp->data, idp->len,
974                                      3, cmd->argv[2], cmd->argv_lens[2],
975                                      4, cmd->argv[3], cmd->argv_lens[3]);
976
977   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
978                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
979   silc_buffer_free(buffer);
980   silc_buffer_free(idp);
981
982   /* Notify application */
983   COMMAND;
984
985  out:
986   silc_client_command_free(cmd);
987 }
988
989 /* MOTD command. Requests motd from server. */
990
991 SILC_CLIENT_CMD_FUNC(motd)
992 {
993   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
994   SilcClientConnection conn = cmd->conn;
995   SilcBuffer buffer;
996
997   if (!cmd->conn) {
998     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
999     COMMAND_ERROR;
1000     goto out;
1001   }
1002
1003   if (cmd->argc < 1 || cmd->argc > 2) {
1004     cmd->client->ops->say(cmd->client, conn,
1005                           "Usage: /MOTD [<server>]");
1006     COMMAND_ERROR;
1007     goto out;
1008   }
1009
1010   /* Send TOPIC command to the server */
1011   if (cmd->argc == 1)
1012     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1013                                             1, conn->remote_host, 
1014                                             strlen(conn->remote_host));
1015   else
1016     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1017                                             1, cmd->argv[1], 
1018                                             cmd->argv_lens[1]);
1019   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1020                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1021   silc_buffer_free(buffer);
1022
1023   /* Notify application */
1024   COMMAND;
1025
1026  out:
1027   silc_client_command_free(cmd);
1028 }
1029
1030 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1031    modes as client cannot set itself server/router operator privileges. */
1032
1033 SILC_CLIENT_CMD_FUNC(umode)
1034 {
1035   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1036   SilcClientConnection conn = cmd->conn;
1037   SilcBuffer buffer, idp;
1038   unsigned char *cp, modebuf[4];
1039   unsigned int mode, add, len;
1040   int i;
1041
1042   if (!cmd->conn) {
1043     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1044     COMMAND_ERROR;
1045     goto out;
1046   }
1047
1048   if (cmd->argc < 2) {
1049     cmd->client->ops->say(cmd->client, conn, 
1050                   "Usage: /UMODE +|-<modes>");
1051     COMMAND_ERROR;
1052     goto out;
1053   }
1054
1055   mode = conn->local_entry->mode;
1056
1057   /* Are we adding or removing mode */
1058   if (cmd->argv[1][0] == '-')
1059     add = FALSE;
1060   else
1061     add = TRUE;
1062
1063   /* Parse mode */
1064   cp = cmd->argv[1] + 1;
1065   len = strlen(cp);
1066   for (i = 0; i < len; i++) {
1067     switch(cp[i]) {
1068     case 'a':
1069       if (add) {
1070         mode = 0;
1071         mode |= SILC_UMODE_SERVER_OPERATOR;
1072         mode |= SILC_UMODE_ROUTER_OPERATOR;
1073       } else {
1074         mode = SILC_UMODE_NONE;
1075       }
1076       break;
1077     case 's':
1078       if (add)
1079         mode |= SILC_UMODE_SERVER_OPERATOR;
1080       else
1081         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1082       break;
1083     case 'r':
1084       if (add)
1085         mode |= SILC_UMODE_ROUTER_OPERATOR;
1086       else
1087         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1088       break;
1089     default:
1090       COMMAND_ERROR;
1091       goto out;
1092       break;
1093     }
1094   }
1095
1096   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1097   SILC_PUT32_MSB(mode, modebuf);
1098
1099   /* Send the command packet. We support sending only one mode at once
1100      that requires an argument. */
1101   buffer = 
1102     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1103                                    1, idp->data, idp->len, 
1104                                    2, modebuf, sizeof(modebuf));
1105   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1106                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1107   silc_buffer_free(buffer);
1108   silc_buffer_free(idp);
1109
1110   /* Notify application */
1111   COMMAND;
1112
1113  out:
1114   silc_client_command_free(cmd);
1115 }
1116
1117 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1118    can be set several at once. Those modes that require argument must be set
1119    separately (unless set with modes that does not require arguments). */
1120
1121 SILC_CLIENT_CMD_FUNC(cmode)
1122 {
1123   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1124   SilcClientConnection conn = cmd->conn;
1125   SilcChannelEntry channel;
1126   SilcBuffer buffer, chidp;
1127   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1128   unsigned int mode, add, type, len, arg_len = 0;
1129   int i;
1130
1131   if (!cmd->conn) {
1132     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1133     COMMAND_ERROR;
1134     goto out;
1135   }
1136
1137   if (cmd->argc < 3) {
1138     cmd->client->ops->say(cmd->client, conn, 
1139                   "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1140     COMMAND_ERROR;
1141     goto out;
1142   }
1143
1144   if (cmd->argv[1][0] == '*') {
1145     if (!conn->current_channel) {
1146       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1147       COMMAND_ERROR;
1148       goto out;
1149     }
1150
1151     channel = conn->current_channel;
1152   } else {
1153     name = cmd->argv[1];
1154
1155     channel = silc_client_get_channel(cmd->client, conn, name);
1156     if (!channel) {
1157       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1158       COMMAND_ERROR;
1159       goto out;
1160     }
1161   }
1162
1163   mode = channel->mode;
1164
1165   /* Are we adding or removing mode */
1166   if (cmd->argv[2][0] == '-')
1167     add = FALSE;
1168   else
1169     add = TRUE;
1170
1171   /* Argument type to be sent to server */
1172   type = 0;
1173
1174   /* Parse mode */
1175   cp = cmd->argv[2] + 1;
1176   len = strlen(cp);
1177   for (i = 0; i < len; i++) {
1178     switch(cp[i]) {
1179     case 'p':
1180       if (add)
1181         mode |= SILC_CHANNEL_MODE_PRIVATE;
1182       else
1183         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1184       break;
1185     case 's':
1186       if (add)
1187         mode |= SILC_CHANNEL_MODE_SECRET;
1188       else
1189         mode &= ~SILC_CHANNEL_MODE_SECRET;
1190       break;
1191     case 'k':
1192       if (add)
1193         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1194       else
1195         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1196       break;
1197     case 'i':
1198       if (add)
1199         mode |= SILC_CHANNEL_MODE_INVITE;
1200       else
1201         mode &= ~SILC_CHANNEL_MODE_INVITE;
1202       break;
1203     case 't':
1204       if (add)
1205         mode |= SILC_CHANNEL_MODE_TOPIC;
1206       else
1207         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1208       break;
1209     case 'l':
1210       if (add) {
1211         int ll;
1212         mode |= SILC_CHANNEL_MODE_ULIMIT;
1213         type = 3;
1214         ll = atoi(cmd->argv[3]);
1215         SILC_PUT32_MSB(ll, tmp);
1216         arg = tmp;
1217         arg_len = 4;
1218       } else {
1219         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1220       }
1221       break;
1222     case 'a':
1223       if (add) {
1224         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1225         type = 4;
1226         arg = cmd->argv[3];
1227         arg_len = cmd->argv_lens[3];
1228       } else {
1229         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1230       }
1231       break;
1232     case 'c':
1233       if (add) {
1234         mode |= SILC_CHANNEL_MODE_CIPHER;
1235         type = 8;
1236         arg = cmd->argv[3];
1237         arg_len = cmd->argv_lens[3];
1238       } else {
1239         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1240       }
1241       break;
1242     default:
1243       COMMAND_ERROR;
1244       goto out;
1245       break;
1246     }
1247   }
1248
1249   if (type && cmd->argc < 3) {
1250     COMMAND_ERROR;
1251     goto out;
1252   }
1253
1254   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1255   SILC_PUT32_MSB(mode, modebuf);
1256
1257   /* Send the command packet. We support sending only one mode at once
1258      that requires an argument. */
1259   if (type && arg) {
1260     buffer = 
1261       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1262                                      1, chidp->data, chidp->len, 
1263                                      2, modebuf, sizeof(modebuf),
1264                                      type, arg, arg_len);
1265   } else {
1266     buffer = 
1267       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1268                                      1, chidp->data, chidp->len, 
1269                                      2, modebuf, sizeof(modebuf));
1270   }
1271
1272   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1273                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1274   silc_buffer_free(buffer);
1275   silc_buffer_free(chidp);
1276
1277   /* Notify application */
1278   COMMAND;
1279
1280  out:
1281   silc_client_command_free(cmd);
1282 }
1283
1284 /* CUMODE command. Changes client's mode on a channel. */
1285
1286 SILC_CLIENT_CMD_FUNC(cumode)
1287 {
1288   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1289   SilcClientConnection conn = cmd->conn;
1290   SilcChannelEntry channel;
1291   SilcChannelUser chu;
1292   SilcClientEntry client_entry;
1293   SilcBuffer buffer, clidp, chidp;
1294   unsigned char *name, *cp, modebuf[4];
1295   unsigned int mode = 0, add, len;
1296   char *nickname = NULL, *server = NULL;
1297   unsigned int num = 0;
1298   int i;
1299
1300   if (!cmd->conn) {
1301     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1302     COMMAND_ERROR;
1303     goto out;
1304   }
1305
1306   if (cmd->argc < 4) {
1307     cmd->client->ops->say(cmd->client, conn, 
1308                   "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1309     COMMAND_ERROR;
1310     goto out;
1311   }
1312
1313   if (cmd->argv[1][0] == '*') {
1314     if (!conn->current_channel) {
1315       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1316       COMMAND_ERROR;
1317       goto out;
1318     }
1319
1320     channel = conn->current_channel;
1321   } else {
1322     name = cmd->argv[1];
1323
1324     channel = silc_client_get_channel(cmd->client, conn, name);
1325     if (!channel) {
1326       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1327       COMMAND_ERROR;
1328       goto out;
1329     }
1330   }
1331
1332   /* Parse the typed nickname. */
1333   if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1334     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1335     COMMAND_ERROR;
1336     goto out;
1337   }
1338
1339   /* Find client entry */
1340   client_entry = silc_idlist_get_client(cmd->client, conn, 
1341                                         nickname, server, num, TRUE);
1342   if (!client_entry) {
1343     if (cmd->pending) {
1344       COMMAND_ERROR;
1345       goto out;
1346     }
1347
1348     /* Client entry not found, it was requested thus mark this to be
1349        pending command. */
1350     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1351                                 conn->cmd_ident,  
1352                                 silc_client_command_destructor,
1353                                 silc_client_command_cumode, 
1354                                 silc_client_command_dup(cmd));
1355     cmd->pending = 1;
1356     return;
1357   }
1358   
1359   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1360     if (chu->client == client_entry) {
1361       chu->mode = mode;
1362       break;
1363     }
1364   }
1365
1366   /* Are we adding or removing mode */
1367   if (cmd->argv[2][0] == '-')
1368     add = FALSE;
1369   else
1370     add = TRUE;
1371
1372   /* Parse mode */
1373   cp = cmd->argv[2] + 1;
1374   len = strlen(cp);
1375   for (i = 0; i < len; i++) {
1376     switch(cp[i]) {
1377     case 'a':
1378       if (add) {
1379         mode |= SILC_CHANNEL_UMODE_CHANFO;
1380         mode |= SILC_CHANNEL_UMODE_CHANOP;
1381       } else {
1382         mode = SILC_CHANNEL_UMODE_NONE;
1383       }
1384       break;
1385     case 'f':
1386       if (add)
1387         mode |= SILC_CHANNEL_UMODE_CHANFO;
1388       else
1389         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1390       break;
1391     case 'o':
1392       if (add)
1393         mode |= SILC_CHANNEL_UMODE_CHANOP;
1394       else
1395         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1396       break;
1397     default:
1398       COMMAND_ERROR;
1399       goto out;
1400       break;
1401     }
1402   }
1403
1404   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1405   SILC_PUT32_MSB(mode, modebuf);
1406   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1407
1408   /* Send the command packet. We support sending only one mode at once
1409      that requires an argument. */
1410   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3, 
1411                                           1, chidp->data, chidp->len, 
1412                                           2, modebuf, 4,
1413                                           3, clidp->data, clidp->len);
1414
1415   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1416                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1417   silc_buffer_free(buffer);
1418   silc_buffer_free(chidp);
1419   silc_buffer_free(clidp);
1420   
1421   /* Notify application */
1422   COMMAND;
1423
1424  out:
1425   if (nickname)
1426     silc_free(nickname);
1427   if (server)
1428     silc_free(server);
1429   silc_client_command_free(cmd);
1430 }
1431
1432 /* KICK command. Kicks a client out of channel. */
1433
1434 SILC_CLIENT_CMD_FUNC(kick)
1435 {
1436   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1437   SilcClientConnection conn = cmd->conn;
1438   SilcIDCacheEntry id_cache = NULL;
1439   SilcChannelEntry channel;
1440   SilcBuffer buffer, idp, idp2;
1441   SilcClientEntry target;
1442   char *name;
1443   unsigned int num = 0;
1444   char *nickname = NULL, *server = NULL;
1445
1446   if (!cmd->conn) {
1447     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1448     COMMAND_ERROR;
1449     goto out;
1450   }
1451
1452   if (cmd->argc < 3) {
1453     cmd->client->ops->say(cmd->client, conn, 
1454                           "Usage: /KICK <channel> <nickname> [<comment>]");
1455     COMMAND_ERROR;
1456     goto out;
1457   }
1458
1459   if (cmd->argv[1][0] == '*') {
1460     if (!conn->current_channel) {
1461       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1462       COMMAND_ERROR;
1463       goto out;
1464     }
1465     name = conn->current_channel->channel_name;
1466   } else {
1467     name = cmd->argv[1];
1468   }
1469
1470   if (!conn->current_channel) {
1471     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1472     COMMAND_ERROR;
1473     goto out;
1474   }
1475
1476   /* Get the Channel ID of the channel */
1477   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1478     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1479     COMMAND_ERROR;
1480     goto out;
1481   }
1482
1483   channel = (SilcChannelEntry)id_cache->context;
1484
1485   /* Parse the typed nickname. */
1486   if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1487     cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1488     COMMAND_ERROR;
1489     goto out;
1490   }
1491
1492   /* Get the target client */
1493   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1494                                   server, num, FALSE);
1495   if (!target) {
1496     cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1497                           cmd->argv[2]);
1498     COMMAND_ERROR;
1499     goto out;
1500   }
1501
1502   /* Send KICK command to the server */
1503   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1504   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1505   if (cmd->argc == 3)
1506     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1507                                             1, idp->data, idp->len,
1508                                             2, idp2->data, idp2->len);
1509   else
1510     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1511                                             1, idp->data, idp->len,
1512                                             2, idp2->data, idp2->len,
1513                                             3, cmd->argv[3], 
1514                                             strlen(cmd->argv[3]));
1515   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1516                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1517   silc_buffer_free(buffer);
1518   silc_buffer_free(idp);
1519   silc_buffer_free(idp2);
1520
1521   /* Notify application */
1522   COMMAND;
1523
1524  out:
1525   if (nickname)
1526     silc_free(nickname);
1527   if (server)
1528     silc_free(server);
1529   silc_client_command_free(cmd);
1530 }
1531
1532 /* OPER command. Used to obtain server operator privileges. */
1533
1534 SILC_CLIENT_CMD_FUNC(oper)
1535 {
1536   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1537   SilcClientConnection conn = cmd->conn;
1538   SilcBuffer buffer;
1539   unsigned char *auth_data;
1540   SilcBuffer auth;
1541
1542   if (!cmd->conn) {
1543     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1544     COMMAND_ERROR;
1545     goto out;
1546   }
1547
1548   if (cmd->argc < 2) {
1549     cmd->client->ops->say(cmd->client, conn, 
1550                           "Usage: /OPER <username> [<public key>]");
1551     COMMAND_ERROR;
1552     goto out;
1553   }
1554
1555   if (cmd->argc == 3) {
1556     /* XXX Get public key */
1557     auth_data = NULL;
1558     COMMAND_ERROR;
1559     goto out;
1560   } else {
1561     /* Get passphrase */
1562
1563     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1564     if (!auth_data) {
1565       COMMAND_ERROR;
1566       goto out;
1567     }
1568
1569     /* Encode the authentication payload */
1570     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1571                                     auth_data, strlen(auth_data));
1572   }
1573
1574   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1575                                           1, cmd->argv[1], 
1576                                           strlen(cmd->argv[1]),
1577                                           2, auth->data, auth->len);
1578   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1579                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1580
1581   silc_buffer_free(buffer);
1582   silc_buffer_free(auth);
1583   memset(auth_data, 0, strlen(auth_data));
1584   silc_free(auth_data);
1585
1586   /* Notify application */
1587   COMMAND;
1588
1589  out:
1590   silc_client_command_free(cmd);
1591 }
1592
1593 /* SILCOPER command. Used to obtain router operator privileges. */
1594
1595 SILC_CLIENT_CMD_FUNC(silcoper)
1596 {
1597   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1598   SilcClientConnection conn = cmd->conn;
1599   SilcBuffer buffer;
1600   unsigned char *auth_data;
1601   SilcBuffer auth;
1602
1603   if (!cmd->conn) {
1604     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1605     COMMAND_ERROR;
1606     goto out;
1607   }
1608
1609   if (cmd->argc < 2) {
1610     cmd->client->ops->say(cmd->client, conn, 
1611                           "Usage: /SILCOPER <username> [<public key>]");
1612     COMMAND_ERROR;
1613     goto out;
1614   }
1615
1616   if (cmd->argc == 3) {
1617     /* XXX Get public key */
1618     auth_data = NULL;
1619     COMMAND_ERROR;
1620     goto out;
1621   } else {
1622     /* Get passphrase */
1623
1624     auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1625     if (!auth_data) {
1626       COMMAND_ERROR;
1627       goto out;
1628     }
1629
1630     /* Encode the authentication payload */
1631     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1632                                     auth_data, strlen(auth_data));
1633   }
1634
1635   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1636                                           1, cmd->argv[1], 
1637                                           strlen(cmd->argv[1]),
1638                                           2, auth->data, auth->len);
1639   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1640                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1641
1642   silc_buffer_free(buffer);
1643   silc_buffer_free(auth);
1644   memset(auth_data, 0, strlen(auth_data));
1645   silc_free(auth_data);
1646
1647   /* Notify application */
1648   COMMAND;
1649
1650  out:
1651   silc_client_command_free(cmd);
1652 }
1653
1654 /* CONNECT command. Connects the server to another server. */
1655
1656 SILC_CLIENT_CMD_FUNC(connect)
1657 {
1658   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1659   SilcClientConnection conn = cmd->conn;
1660   SilcBuffer buffer;
1661   unsigned char port[4];
1662   unsigned int tmp;
1663
1664   if (!cmd->conn) {
1665     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1666     COMMAND_ERROR;
1667     goto out;
1668   }
1669
1670   if (cmd->argc < 2) {
1671     cmd->client->ops->say(cmd->client, conn, 
1672                           "Usage: /CONNECT <server> [<port>]");
1673     COMMAND_ERROR;
1674     goto out;
1675   }
1676
1677   if (cmd->argc == 3) {
1678     tmp = atoi(cmd->argv[2]);
1679     SILC_PUT32_MSB(tmp, port);
1680   }
1681
1682   if (cmd->argc == 3)
1683     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1684                                             1, cmd->argv[1], 
1685                                             strlen(cmd->argv[1]),
1686                                             2, port, 4);
1687   else
1688     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1689                                             1, cmd->argv[1], 
1690                                             strlen(cmd->argv[1]));
1691   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1692                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1693   silc_buffer_free(buffer);
1694
1695   /* Notify application */
1696   COMMAND;
1697
1698  out:
1699   silc_client_command_free(cmd);
1700 }
1701
1702 SILC_CLIENT_CMD_FUNC(restart)
1703 {
1704 }
1705
1706 /* CLOSE command. Close server connection to the remote server */
1707  
1708 SILC_CLIENT_CMD_FUNC(close)
1709 {
1710   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1711   SilcClientConnection conn = cmd->conn;
1712   SilcBuffer buffer;
1713   unsigned char port[4];
1714   unsigned int tmp;
1715
1716   if (!cmd->conn) {
1717     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1718     COMMAND_ERROR;
1719     goto out;
1720   }
1721
1722   if (cmd->argc < 2) {
1723     cmd->client->ops->say(cmd->client, conn, 
1724                           "Usage: /CLOSE <server> [<port>]");
1725     COMMAND_ERROR;
1726     goto out;
1727   }
1728
1729   if (cmd->argc == 3) {
1730     tmp = atoi(cmd->argv[2]);
1731     SILC_PUT32_MSB(tmp, port);
1732   }
1733
1734   if (cmd->argc == 3)
1735     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1736                                             1, cmd->argv[1], 
1737                                             strlen(cmd->argv[1]),
1738                                             2, port, 4);
1739   else
1740     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1741                                             1, cmd->argv[1], 
1742                                             strlen(cmd->argv[1]));
1743   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1744                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1745   silc_buffer_free(buffer);
1746
1747   /* Notify application */
1748   COMMAND;
1749
1750  out:
1751   silc_client_command_free(cmd);
1752 }
1753  
1754 /* SHUTDOWN command. Shutdowns the server. */
1755
1756 SILC_CLIENT_CMD_FUNC(shutdown)
1757 {
1758   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1759
1760   if (!cmd->conn) {
1761     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1762     COMMAND_ERROR;
1763     goto out;
1764   }
1765
1766   /* Send the command */
1767   silc_client_send_command(cmd->client, cmd->conn, 
1768                            SILC_COMMAND_SHUTDOWN, 0, 0);
1769
1770   /* Notify application */
1771   COMMAND;
1772
1773  out:
1774   silc_client_command_free(cmd);
1775 }
1776  
1777 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1778
1779 SILC_CLIENT_CMD_FUNC(leave)
1780 {
1781   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1782   SilcClientConnection conn = cmd->conn;
1783   SilcIDCacheEntry id_cache = NULL;
1784   SilcChannelEntry channel;
1785   SilcBuffer buffer, idp;
1786   char *name;
1787
1788   if (!cmd->conn) {
1789     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1790     COMMAND_ERROR;
1791     goto out;
1792   }
1793
1794   if (cmd->argc != 2) {
1795     cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1796     COMMAND_ERROR;
1797     goto out;
1798   }
1799
1800   if (cmd->argv[1][0] == '*') {
1801     if (!conn->current_channel) {
1802       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1803       COMMAND_ERROR;
1804       goto out;
1805     }
1806     name = conn->current_channel->channel_name;
1807   } else {
1808     name = cmd->argv[1];
1809   }
1810
1811   if (!conn->current_channel) {
1812     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1813     COMMAND_ERROR;
1814     goto out;
1815   }
1816
1817   /* Get the Channel ID of the channel */
1818   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1819     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1820     COMMAND_ERROR;
1821     goto out;
1822   }
1823
1824   channel = (SilcChannelEntry)id_cache->context;
1825
1826   /* Send LEAVE command to the server */
1827   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1828   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1829                                           1, idp->data, idp->len);
1830   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1831                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1832   silc_buffer_free(buffer);
1833   silc_buffer_free(idp);
1834
1835   /* We won't talk anymore on this channel */
1836   cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1837
1838   conn->current_channel = NULL;
1839
1840   silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1841   silc_free(channel->channel_name);
1842   silc_free(channel->id);
1843   silc_free(channel->key);
1844   silc_cipher_free(channel->channel_key);
1845   silc_free(channel);
1846
1847   /* Notify application */
1848   COMMAND;
1849
1850  out:
1851   silc_client_command_free(cmd);
1852 }
1853
1854 /* Command USERS. Requests the USERS of the clients joined on requested
1855    channel. */
1856
1857 SILC_CLIENT_CMD_FUNC(users)
1858 {
1859   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1860   SilcClientConnection conn = cmd->conn;
1861   SilcIDCacheEntry id_cache = NULL;
1862   SilcChannelEntry channel;
1863   SilcBuffer buffer, idp;
1864   char *name, *line = NULL;
1865   unsigned int line_len = 0;
1866
1867   if (!cmd->conn) {
1868     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1869     COMMAND_ERROR;
1870     goto out;
1871   }
1872
1873   if (cmd->argc != 2) {
1874     cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1875     COMMAND_ERROR;
1876     goto out;
1877   }
1878
1879   if (cmd->argv[1][0] == '*') {
1880     if (!conn->current_channel) {
1881       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1882       COMMAND_ERROR;
1883       goto out;
1884     }
1885     name = conn->current_channel->channel_name;
1886   } else {
1887     name = cmd->argv[1];
1888   }
1889
1890   if (!conn->current_channel) {
1891     cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1892     COMMAND_ERROR;
1893     goto out;
1894   }
1895
1896   /* Get the Channel ID of the channel */
1897   if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1898     /* XXX should resolve the channel ID; LIST command */
1899     cmd->client->ops->say(cmd->client, conn, 
1900                           "You are not on that channel", name);
1901     COMMAND_ERROR;
1902     goto out;
1903   }
1904
1905   channel = (SilcChannelEntry)id_cache->context;
1906
1907   if (!cmd->pending) {
1908     /* Send USERS command to the server */
1909     idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1910     buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
1911                                             ++conn->cmd_ident, 1, 
1912                                             1, idp->data, idp->len);
1913     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1914                             NULL, 0, NULL, NULL, buffer->data, 
1915                             buffer->len, TRUE);
1916     silc_buffer_free(buffer);
1917     silc_buffer_free(idp);
1918
1919     /* Register pending callback which will recall this command callback with
1920        same context and reprocesses the command. When reprocessing we actually
1921        display the information on the screen. */
1922     silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident, 
1923                                 silc_client_command_destructor,
1924                                 silc_client_command_users, 
1925                                 silc_client_command_dup(cmd));
1926     cmd->pending = TRUE;
1927     return;
1928   }
1929
1930   if (cmd->pending) {
1931     /* Pending command. Now we've resolved the information from server and
1932        we are ready to display the information on screen. */
1933     int i;
1934     SilcChannelUser chu;
1935
1936     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1937                           channel->channel_name);
1938
1939     line = silc_calloc(4096, sizeof(*line));
1940     line_len = 4096;
1941     silc_list_start(channel->clients);
1942     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1943       SilcClientEntry e = chu->client;
1944       char *m, tmp[80], len1;
1945
1946       memset(line, 0, sizeof(line_len));
1947
1948       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1949         silc_free(line);
1950         line_len += strlen(e->nickname) + strlen(e->server) + 100;
1951         line = silc_calloc(line_len, sizeof(*line));
1952       }
1953
1954       memset(tmp, 0, sizeof(tmp));
1955       m = silc_client_chumode_char(chu->mode);
1956
1957       strncat(line, " ", 1);
1958       strncat(line, e->nickname, strlen(e->nickname));
1959       strncat(line, e->server ? "@" : "", 1);
1960
1961       len1 = 0;
1962       if (e->server)
1963         len1 = strlen(e->server);
1964       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1965
1966       len1 = strlen(line);
1967       if (len1 >= 30) {
1968         memset(&line[29], 0, len1 - 29);
1969       } else {
1970         for (i = 0; i < 30 - len1 - 1; i++)
1971           strcat(line, " ");
1972       }
1973
1974       strncat(line, "  H", 3);
1975       strcat(tmp, m ? m : "");
1976       strncat(line, tmp, strlen(tmp));
1977
1978       if (strlen(tmp) < 5)
1979         for (i = 0; i < 5 - strlen(tmp); i++)
1980           strcat(line, " ");
1981
1982       strcat(line, e->username ? e->username : "");
1983
1984       cmd->client->ops->say(cmd->client, conn, "%s", line);
1985
1986       if (m)
1987         silc_free(m);
1988     }
1989   }
1990
1991   if (line)
1992     silc_free(line);
1993
1994   /* Notify application */
1995   COMMAND;
1996
1997  out:
1998   silc_client_command_free(cmd);
1999 }
2000
2001 /* Command BAN. This is used to manage the ban list of the channel. */
2002
2003 SILC_CLIENT_CMD_FUNC(ban)
2004 {
2005   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2006   SilcClientConnection conn = cmd->conn;
2007   SilcChannelEntry channel;
2008   SilcBuffer buffer, chidp;
2009   int type = 0;
2010   char *name, *ban = NULL;
2011
2012   if (!cmd->conn) {
2013     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2014     COMMAND_ERROR;
2015     goto out;
2016   }
2017
2018   if (cmd->argc < 2) {
2019     cmd->client->ops->say(cmd->client, conn, 
2020                    "Usage: /BAN <channel> "
2021                    "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2022     COMMAND_ERROR;
2023     goto out;
2024   }
2025
2026   if (cmd->argv[1][0] == '*') {
2027     if (!conn->current_channel) {
2028       cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
2029       COMMAND_ERROR;
2030       goto out;
2031     }
2032
2033     channel = conn->current_channel;
2034   } else {
2035     name = cmd->argv[1];
2036
2037     channel = silc_client_get_channel(cmd->client, conn, name);
2038     if (!channel) {
2039       cmd->client->ops->say(cmd->client, conn, "You are on that channel");
2040       COMMAND_ERROR;
2041       goto out;
2042     }
2043   }
2044
2045   if (cmd->argc == 3) {
2046     if (cmd->argv[2][0] == '+')
2047       type = 2;
2048     else
2049       type = 3;
2050
2051     ban = cmd->argv[2];
2052     ban++;
2053   }
2054
2055   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2056
2057   /* Send the command */
2058   if (ban)
2059     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, 
2060                                             1, chidp->data, chidp->len,
2061                                             type, ban, strlen(ban));
2062   else
2063     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, 
2064                                             1, chidp->data, chidp->len);
2065
2066   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2067                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2068   silc_buffer_free(buffer);
2069   silc_buffer_free(chidp);
2070
2071   /* Notify application */
2072   COMMAND;
2073
2074  out:
2075   silc_client_command_free(cmd);
2076 }