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