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