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