Integer type name change.
[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_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, 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, NULL, 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_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, 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, *cipher = NULL, *hmac = NULL;
954   int i;
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                                                   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       passphrase = cmd->argv[i];
1001     }
1002   }
1003
1004   /* Send JOIN command to the server */
1005   buffer =
1006     silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6,
1007                                    1, name, strlen(name),
1008                                    2, idp->data, idp->len,
1009                                    3, passphrase, 
1010                                    passphrase ? strlen(passphrase) : 0,
1011                                    4, cipher, cipher ? strlen(cipher) : 0,
1012                                    5, hmac, hmac ? strlen(hmac) : 0,
1013                                    6, auth ? auth->data : NULL,
1014                                    auth ? auth->len : 0);
1015   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1016                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1017   silc_buffer_free(buffer);
1018   silc_buffer_free(idp);
1019   if (auth)
1020     silc_buffer_free(auth);
1021
1022   /* Notify application */
1023   COMMAND;
1024
1025  out:
1026   silc_client_command_free(cmd);
1027 }
1028
1029 /* MOTD command. Requests motd from server. */
1030
1031 SILC_CLIENT_CMD_FUNC(motd)
1032 {
1033   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1034   SilcClientConnection conn = cmd->conn;
1035   SilcBuffer buffer;
1036
1037   if (!cmd->conn) {
1038     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1039     COMMAND_ERROR;
1040     goto out;
1041   }
1042
1043   if (cmd->argc < 1 || cmd->argc > 2) {
1044     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1045         "Usage: /MOTD [<server>]");
1046     COMMAND_ERROR;
1047     goto out;
1048   }
1049
1050   /* Send TOPIC command to the server */
1051   if (cmd->argc == 1)
1052     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1053                                             1, conn->remote_host, 
1054                                             strlen(conn->remote_host));
1055   else
1056     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1057                                             1, cmd->argv[1], 
1058                                             cmd->argv_lens[1]);
1059   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1060                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1061   silc_buffer_free(buffer);
1062
1063   /* Notify application */
1064   COMMAND;
1065
1066  out:
1067   silc_client_command_free(cmd);
1068 }
1069
1070 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1071    modes as client cannot set itself server/router operator privileges. */
1072
1073 SILC_CLIENT_CMD_FUNC(umode)
1074 {
1075   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1076   SilcClientConnection conn = cmd->conn;
1077   SilcBuffer buffer, idp;
1078   unsigned char *cp, modebuf[4];
1079   SilcUInt32 mode, add, len;
1080   int i;
1081
1082   if (!cmd->conn) {
1083     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1084     COMMAND_ERROR;
1085     goto out;
1086   }
1087
1088   if (cmd->argc < 2) {
1089     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1090         "Usage: /UMODE +|-<modes>");
1091     COMMAND_ERROR;
1092     goto out;
1093   }
1094
1095   mode = conn->local_entry->mode;
1096
1097   /* Are we adding or removing mode */
1098   if (cmd->argv[1][0] == '-')
1099     add = FALSE;
1100   else
1101     add = TRUE;
1102
1103   /* Parse mode */
1104   cp = cmd->argv[1] + 1;
1105   len = strlen(cp);
1106   for (i = 0; i < len; i++) {
1107     switch(cp[i]) {
1108     case 'a':
1109       if (add) {
1110         mode = 0;
1111         mode |= SILC_UMODE_SERVER_OPERATOR;
1112         mode |= SILC_UMODE_ROUTER_OPERATOR;
1113       } else {
1114         mode = SILC_UMODE_NONE;
1115       }
1116       break;
1117     case 's':
1118       if (add)
1119         mode |= SILC_UMODE_SERVER_OPERATOR;
1120       else
1121         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1122       break;
1123     case 'r':
1124       if (add)
1125         mode |= SILC_UMODE_ROUTER_OPERATOR;
1126       else
1127         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1128       break;
1129     case 'g':
1130       if (add)
1131         mode |= SILC_UMODE_GONE;
1132       else
1133         mode &= ~SILC_UMODE_GONE;
1134       break;
1135     default:
1136       COMMAND_ERROR;
1137       goto out;
1138       break;
1139     }
1140   }
1141
1142   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1143   SILC_PUT32_MSB(mode, modebuf);
1144
1145   /* Send the command packet. We support sending only one mode at once
1146      that requires an argument. */
1147   buffer = 
1148     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1149                                    1, idp->data, idp->len, 
1150                                    2, modebuf, sizeof(modebuf));
1151   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1152                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1153   silc_buffer_free(buffer);
1154   silc_buffer_free(idp);
1155
1156   /* Notify application */
1157   COMMAND;
1158
1159  out:
1160   silc_client_command_free(cmd);
1161 }
1162
1163 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1164    can be set several at once. Those modes that require argument must be set
1165    separately (unless set with modes that does not require arguments). */
1166
1167 SILC_CLIENT_CMD_FUNC(cmode)
1168 {
1169   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1170   SilcClientConnection conn = cmd->conn;
1171   SilcChannelEntry channel;
1172   SilcBuffer buffer, chidp, auth = NULL;
1173   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1174   SilcUInt32 mode, add, type, len, arg_len = 0;
1175   int i;
1176
1177   if (!cmd->conn) {
1178     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1179     COMMAND_ERROR;
1180     goto out;
1181   }
1182
1183   if (cmd->argc < 3) {
1184     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1185         "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1186     COMMAND_ERROR;
1187     goto out;
1188   }
1189
1190   if (cmd->argv[1][0] == '*') {
1191     if (!conn->current_channel) {
1192       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1193           "You are not on any channel");
1194       COMMAND_ERROR;
1195       goto out;
1196     }
1197
1198     channel = conn->current_channel;
1199   } else {
1200     name = cmd->argv[1];
1201
1202     channel = silc_client_get_channel(cmd->client, conn, name);
1203     if (!channel) {
1204       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1205           "You are on that channel");
1206       COMMAND_ERROR;
1207       goto out;
1208     }
1209   }
1210
1211   mode = channel->mode;
1212
1213   /* Are we adding or removing mode */
1214   if (cmd->argv[2][0] == '-')
1215     add = FALSE;
1216   else
1217     add = TRUE;
1218
1219   /* Argument type to be sent to server */
1220   type = 0;
1221
1222   /* Parse mode */
1223   cp = cmd->argv[2] + 1;
1224   len = strlen(cp);
1225   for (i = 0; i < len; i++) {
1226     switch(cp[i]) {
1227     case 'p':
1228       if (add)
1229         mode |= SILC_CHANNEL_MODE_PRIVATE;
1230       else
1231         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1232       break;
1233     case 's':
1234       if (add)
1235         mode |= SILC_CHANNEL_MODE_SECRET;
1236       else
1237         mode &= ~SILC_CHANNEL_MODE_SECRET;
1238       break;
1239     case 'k':
1240       if (add)
1241         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1242       else
1243         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1244       break;
1245     case 'i':
1246       if (add)
1247         mode |= SILC_CHANNEL_MODE_INVITE;
1248       else
1249         mode &= ~SILC_CHANNEL_MODE_INVITE;
1250       break;
1251     case 't':
1252       if (add)
1253         mode |= SILC_CHANNEL_MODE_TOPIC;
1254       else
1255         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1256       break;
1257     case 'l':
1258       if (add) {
1259         int ll;
1260         mode |= SILC_CHANNEL_MODE_ULIMIT;
1261         type = 3;
1262         if (cmd->argc < 4) {
1263           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1264               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1265           COMMAND_ERROR;
1266           goto out;
1267         }
1268         ll = atoi(cmd->argv[3]);
1269         SILC_PUT32_MSB(ll, tmp);
1270         arg = tmp;
1271         arg_len = 4;
1272       } else {
1273         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1274       }
1275       break;
1276     case 'a':
1277       if (add) {
1278         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1279         type = 4;
1280         if (cmd->argc < 4) {
1281           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1282               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1283           COMMAND_ERROR;
1284           goto out;
1285         }
1286         arg = cmd->argv[3];
1287         arg_len = cmd->argv_lens[3];
1288       } else {
1289         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1290       }
1291       break;
1292     case 'c':
1293       if (add) {
1294         mode |= SILC_CHANNEL_MODE_CIPHER;
1295         type = 5;
1296         if (cmd->argc < 4) {
1297           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1298               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1299           COMMAND_ERROR;
1300           goto out;
1301         }
1302         arg = cmd->argv[3];
1303         arg_len = cmd->argv_lens[3];
1304       } else {
1305         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1306       }
1307       break;
1308     case 'h':
1309       if (add) {
1310         mode |= SILC_CHANNEL_MODE_HMAC;
1311         type = 6;
1312         if (cmd->argc < 4) {
1313           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1314               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1315           COMMAND_ERROR;
1316           goto out;
1317         }
1318         arg = cmd->argv[3];
1319         arg_len = cmd->argv_lens[3];
1320       } else {
1321         mode &= ~SILC_CHANNEL_MODE_HMAC;
1322       }
1323       break;
1324     case 'f':
1325       if (add) {
1326         mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1327         type = 7;
1328
1329         if (cmd->argc < 4) {
1330           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1331               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1332           COMMAND_ERROR;
1333           goto out;
1334         }
1335
1336         if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1337           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1338                                                     cmd->client->private_key,
1339                                                     conn->hash,
1340                                                     conn->local_id,
1341                                                     SILC_ID_CLIENT);
1342         } else {
1343           auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1344                                           cmd->argv[3], cmd->argv_lens[3]);
1345         }
1346
1347         arg = auth->data;
1348         arg_len = auth->len;
1349       } else {
1350         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1351       }
1352       break;
1353     default:
1354       COMMAND_ERROR;
1355       goto out;
1356       break;
1357     }
1358   }
1359
1360   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1361   SILC_PUT32_MSB(mode, modebuf);
1362
1363   /* Send the command packet. We support sending only one mode at once
1364      that requires an argument. */
1365   if (type && arg) {
1366     buffer = 
1367       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1368                                      1, chidp->data, chidp->len, 
1369                                      2, modebuf, sizeof(modebuf),
1370                                      type, arg, arg_len);
1371   } else {
1372     buffer = 
1373       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1374                                      1, chidp->data, chidp->len, 
1375                                      2, modebuf, sizeof(modebuf));
1376   }
1377
1378   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1379                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1380   silc_buffer_free(buffer);
1381   silc_buffer_free(chidp);
1382   if (auth)
1383     silc_buffer_free(auth);
1384
1385   /* Notify application */
1386   COMMAND;
1387
1388  out:
1389   silc_client_command_free(cmd);
1390 }
1391
1392 /* CUMODE command. Changes client's mode on a channel. */
1393
1394 SILC_CLIENT_CMD_FUNC(cumode)
1395 {
1396   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1397   SilcClient client = cmd->client;
1398   SilcClientConnection conn = cmd->conn;
1399   SilcChannelEntry channel;
1400   SilcChannelUser chu;
1401   SilcClientEntry client_entry;
1402   SilcBuffer buffer, clidp, chidp, auth = NULL;
1403   unsigned char *name, *cp, modebuf[4];
1404   SilcUInt32 mode = 0, add, len;
1405   char *nickname = NULL;
1406   int i;
1407
1408   if (!cmd->conn) {
1409     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1410     COMMAND_ERROR;
1411     goto out;
1412   }
1413
1414   if (cmd->argc < 4) {
1415     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1416         "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1417     COMMAND_ERROR;
1418     goto out;
1419   }
1420
1421   if (cmd->argv[1][0] == '*') {
1422     if (!conn->current_channel) {
1423       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1424           "You are not on any channel");
1425       COMMAND_ERROR;
1426       goto out;
1427     }
1428
1429     channel = conn->current_channel;
1430   } else {
1431     name = cmd->argv[1];
1432
1433     channel = silc_client_get_channel(cmd->client, conn, name);
1434     if (!channel) {
1435       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1436           "You are on that channel");
1437       COMMAND_ERROR;
1438       goto out;
1439     }
1440   }
1441
1442   /* Parse the typed nickname. */
1443   if (client->internal->params->nickname_parse)
1444     client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1445   else
1446     nickname = strdup(cmd->argv[3]);
1447
1448   /* Find client entry */
1449   client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
1450                                         cmd->argv[3], TRUE);
1451   if (!client_entry) {
1452     if (cmd->pending) {
1453       COMMAND_ERROR;
1454       goto out;
1455     }
1456
1457     /* Client entry not found, it was requested thus mark this to be
1458        pending command. */
1459     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1460                                 conn->cmd_ident,  
1461                                 silc_client_command_cumode, 
1462                                 silc_client_command_dup(cmd));
1463     cmd->pending = 1;
1464     goto out;
1465   }
1466   
1467   /* Get the current mode */
1468   chu = silc_client_on_channel(channel, client_entry);
1469   if (chu)
1470     mode = chu->mode;
1471
1472   /* Are we adding or removing mode */
1473   if (cmd->argv[2][0] == '-')
1474     add = FALSE;
1475   else
1476     add = TRUE;
1477
1478   /* Parse mode */
1479   cp = cmd->argv[2] + 1;
1480   len = strlen(cp);
1481   for (i = 0; i < len; i++) {
1482     switch(cp[i]) {
1483     case 'a':
1484       if (add) {
1485         mode |= SILC_CHANNEL_UMODE_CHANFO;
1486         mode |= SILC_CHANNEL_UMODE_CHANOP;
1487       } else {
1488         mode = SILC_CHANNEL_UMODE_NONE;
1489       }
1490       break;
1491     case 'f':
1492       if (add) {
1493         if (cmd->argc == 5) {
1494           if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1495             auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1496                                                       cmd->client->private_key,
1497                                                       conn->hash,
1498                                                       conn->local_id,
1499                                                       SILC_ID_CLIENT);
1500           } else {
1501             auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1502                                             cmd->argv[4], cmd->argv_lens[4]);
1503           }
1504         }
1505         mode |= SILC_CHANNEL_UMODE_CHANFO;
1506       } else {
1507         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1508       }
1509       break;
1510     case 'o':
1511       if (add)
1512         mode |= SILC_CHANNEL_UMODE_CHANOP;
1513       else
1514         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1515       break;
1516     default:
1517       COMMAND_ERROR;
1518       goto out;
1519       break;
1520     }
1521   }
1522
1523   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1524   SILC_PUT32_MSB(mode, modebuf);
1525   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1526
1527   /* Send the command packet. We support sending only one mode at once
1528      that requires an argument. */
1529   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 
1530                                           auth ? 4 : 3, 
1531                                           1, chidp->data, chidp->len, 
1532                                           2, modebuf, 4,
1533                                           3, clidp->data, clidp->len,
1534                                           4, auth ? auth->data : NULL, 
1535                                           auth ? auth->len : 0);
1536   
1537   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1538                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1539   silc_buffer_free(buffer);
1540   silc_buffer_free(chidp);
1541   silc_buffer_free(clidp);
1542   if (auth)
1543     silc_buffer_free(auth);
1544   
1545   /* Notify application */
1546   COMMAND;
1547
1548  out:
1549   silc_free(nickname);
1550   silc_client_command_free(cmd);
1551 }
1552
1553 /* KICK command. Kicks a client out of channel. */
1554
1555 SILC_CLIENT_CMD_FUNC(kick)
1556 {
1557   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1558   SilcClient client = cmd->client;
1559   SilcClientConnection conn = cmd->conn;
1560   SilcIDCacheEntry id_cache = NULL;
1561   SilcChannelEntry channel;
1562   SilcBuffer buffer, idp, idp2;
1563   SilcClientEntry target;
1564   char *name;
1565   char *nickname = NULL;
1566
1567   if (!cmd->conn) {
1568     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1569     COMMAND_ERROR;
1570     goto out;
1571   }
1572
1573   if (cmd->argc < 3) {
1574     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1575         "Usage: /KICK <channel> <nickname> [<comment>]");
1576     COMMAND_ERROR;
1577     goto out;
1578   }
1579
1580   if (cmd->argv[1][0] == '*') {
1581     if (!conn->current_channel) {
1582       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1583           "You are not on any channel");
1584       COMMAND_ERROR;
1585       goto out;
1586     }
1587     name = conn->current_channel->channel_name;
1588   } else {
1589     name = cmd->argv[1];
1590   }
1591
1592   if (!conn->current_channel) {
1593     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1594         "You are not on that channel");
1595     COMMAND_ERROR;
1596     goto out;
1597   }
1598
1599   /* Get the Channel ID of the channel */
1600   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1601     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1602         "You are not on that channel");
1603     COMMAND_ERROR;
1604     goto out;
1605   }
1606
1607   channel = (SilcChannelEntry)id_cache->context;
1608
1609   /* Parse the typed nickname. */
1610   if (client->internal->params->nickname_parse)
1611     client->internal->params->nickname_parse(cmd->argv[2], &nickname);
1612   else
1613     nickname = strdup(cmd->argv[2]);
1614
1615   /* Get the target client */
1616   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1617                                   cmd->argv[2], FALSE);
1618   if (!target) {
1619     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1620         "No such client: %s", cmd->argv[2]);
1621     COMMAND_ERROR;
1622     goto out;
1623   }
1624
1625   /* Send KICK command to the server */
1626   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1627   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1628   if (cmd->argc == 3)
1629     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1630                                             1, idp->data, idp->len,
1631                                             2, idp2->data, idp2->len);
1632   else
1633     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1634                                             1, idp->data, idp->len,
1635                                             2, idp2->data, idp2->len,
1636                                             3, cmd->argv[3], 
1637                                             strlen(cmd->argv[3]));
1638   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1639                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1640   silc_buffer_free(buffer);
1641   silc_buffer_free(idp);
1642   silc_buffer_free(idp2);
1643
1644   /* Notify application */
1645   COMMAND;
1646
1647  out:
1648   silc_free(nickname);
1649   silc_client_command_free(cmd);
1650 }
1651
1652 static void silc_client_command_oper_send(unsigned char *data,
1653                                           SilcUInt32 data_len, void *context)
1654 {
1655   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1656   SilcClientConnection conn = cmd->conn;
1657   SilcBuffer buffer, auth;
1658
1659   if (cmd->argc >= 3) {
1660     /* Encode the public key authentication payload */
1661     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1662                                               cmd->client->private_key,
1663                                               conn->hash,
1664                                               conn->local_id,
1665                                               SILC_ID_CLIENT);
1666   } else {
1667     /* Encode the password authentication payload */
1668     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1669                                     data, data_len);
1670   }
1671
1672   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1673                                           1, cmd->argv[1], 
1674                                           strlen(cmd->argv[1]),
1675                                           2, auth->data, auth->len);
1676   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1677                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1678
1679   silc_buffer_free(buffer);
1680   silc_buffer_free(auth);
1681
1682   /* Notify application */
1683   COMMAND;
1684 }
1685
1686 /* OPER command. Used to obtain server operator privileges. */
1687
1688 SILC_CLIENT_CMD_FUNC(oper)
1689 {
1690   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1691   SilcClientConnection conn = cmd->conn;
1692
1693   if (!cmd->conn) {
1694     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1695     COMMAND_ERROR;
1696     goto out;
1697   }
1698
1699   if (cmd->argc < 2) {
1700     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1701         "Usage: /OPER <username> [-pubkey]");
1702     COMMAND_ERROR;
1703     goto out;
1704   }
1705
1706   if (cmd->argc < 3) {
1707     /* Get passphrase */
1708     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1709                                      silc_client_command_oper_send,
1710                                      context);
1711     return;
1712   }
1713
1714   silc_client_command_oper_send(NULL, 0, context);
1715
1716  out:
1717   silc_client_command_free(cmd);
1718 }
1719
1720 static void silc_client_command_silcoper_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                                               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_SILCOPER, 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 /* SILCOPER command. Used to obtain router operator privileges. */
1755
1756 SILC_CLIENT_CMD_FUNC(silcoper)
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: /SILCOPER <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_silcoper_send,
1778                                      context);
1779     return;
1780   }
1781
1782   silc_client_command_silcoper_send(NULL, 0, context);
1783
1784  out:
1785   silc_client_command_free(cmd);
1786 }
1787
1788 /* CONNECT command. Connects the server to another server. */
1789
1790 SILC_CLIENT_CMD_FUNC(connect)
1791 {
1792   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1793   SilcClientConnection conn = cmd->conn;
1794   SilcBuffer buffer;
1795   unsigned char port[4];
1796   SilcUInt32 tmp;
1797
1798   if (!cmd->conn) {
1799     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1800     COMMAND_ERROR;
1801     goto out;
1802   }
1803
1804   if (cmd->argc < 2) {
1805     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1806         "Usage: /CONNECT <server> [<port>]");
1807     COMMAND_ERROR;
1808     goto out;
1809   }
1810
1811   if (cmd->argc == 3) {
1812     tmp = atoi(cmd->argv[2]);
1813     SILC_PUT32_MSB(tmp, port);
1814   }
1815
1816   if (cmd->argc == 3)
1817     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
1818                                             1, cmd->argv[1], 
1819                                             strlen(cmd->argv[1]),
1820                                             2, port, 4);
1821   else
1822     buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1823                                             1, cmd->argv[1], 
1824                                             strlen(cmd->argv[1]));
1825   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1826                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1827   silc_buffer_free(buffer);
1828
1829   /* Notify application */
1830   COMMAND;
1831
1832  out:
1833   silc_client_command_free(cmd);
1834 }
1835
1836 /* Command BAN. This is used to manage the ban list of the channel. */
1837
1838 SILC_CLIENT_CMD_FUNC(ban)
1839 {
1840   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1841   SilcClientConnection conn = cmd->conn;
1842   SilcChannelEntry channel;
1843   SilcBuffer buffer, chidp;
1844   int type = 0;
1845   char *name, *ban = NULL;
1846
1847   if (!cmd->conn) {
1848     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1849     COMMAND_ERROR;
1850     goto out;
1851   }
1852
1853   if (cmd->argc < 2) {
1854     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1855         "Usage: /BAN <channel> "
1856         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1857     COMMAND_ERROR;
1858     goto out;
1859   }
1860
1861   if (cmd->argv[1][0] == '*') {
1862     if (!conn->current_channel) {
1863       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1864           "You are not on any channel");
1865       COMMAND_ERROR;
1866       goto out;
1867     }
1868
1869     channel = conn->current_channel;
1870   } else {
1871     name = cmd->argv[1];
1872
1873     channel = silc_client_get_channel(cmd->client, conn, name);
1874     if (!channel) {
1875       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1876           "You are on that channel");
1877       COMMAND_ERROR;
1878       goto out;
1879     }
1880   }
1881
1882   if (cmd->argc == 3) {
1883     if (cmd->argv[2][0] == '+')
1884       type = 2;
1885     else
1886       type = 3;
1887
1888     ban = cmd->argv[2];
1889     ban++;
1890   }
1891
1892   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1893
1894   /* Send the command */
1895   if (ban)
1896     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, 
1897                                             1, chidp->data, chidp->len,
1898                                             type, ban, strlen(ban));
1899   else
1900     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, 
1901                                             1, chidp->data, chidp->len);
1902
1903   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1904                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1905   silc_buffer_free(buffer);
1906   silc_buffer_free(chidp);
1907
1908   /* Notify application */
1909   COMMAND;
1910
1911  out:
1912   silc_client_command_free(cmd);
1913 }
1914
1915 /* CLOSE command. Close server connection to the remote server */
1916  
1917 SILC_CLIENT_CMD_FUNC(close)
1918 {
1919   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1920   SilcClientConnection conn = cmd->conn;
1921   SilcBuffer buffer;
1922   unsigned char port[4];
1923   SilcUInt32 tmp;
1924
1925   if (!cmd->conn) {
1926     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1927     COMMAND_ERROR;
1928     goto out;
1929   }
1930
1931   if (cmd->argc < 2) {
1932     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1933         "Usage: /CLOSE <server> [<port>]");
1934     COMMAND_ERROR;
1935     goto out;
1936   }
1937
1938   if (cmd->argc == 3) {
1939     tmp = atoi(cmd->argv[2]);
1940     SILC_PUT32_MSB(tmp, port);
1941   }
1942
1943   if (cmd->argc == 3)
1944     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
1945                                             1, cmd->argv[1], 
1946                                             strlen(cmd->argv[1]),
1947                                             2, port, 4);
1948   else
1949     buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1950                                             1, cmd->argv[1], 
1951                                             strlen(cmd->argv[1]));
1952   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1953                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1954   silc_buffer_free(buffer);
1955
1956   /* Notify application */
1957   COMMAND;
1958
1959  out:
1960   silc_client_command_free(cmd);
1961 }
1962  
1963 /* SHUTDOWN command. Shutdowns the server. */
1964
1965 SILC_CLIENT_CMD_FUNC(shutdown)
1966 {
1967   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1968
1969   if (!cmd->conn) {
1970     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1971     COMMAND_ERROR;
1972     goto out;
1973   }
1974
1975   /* Send the command */
1976   silc_client_command_send(cmd->client, cmd->conn, 
1977                            SILC_COMMAND_SHUTDOWN, 0, 0);
1978
1979   /* Notify application */
1980   COMMAND;
1981
1982  out:
1983   silc_client_command_free(cmd);
1984 }
1985
1986 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1987
1988 SILC_CLIENT_CMD_FUNC(leave)
1989 {
1990   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1991   SilcClientConnection conn = cmd->conn;
1992   SilcChannelEntry channel;
1993   SilcChannelUser chu;
1994   SilcBuffer buffer, idp;
1995   char *name;
1996
1997   if (!cmd->conn) {
1998     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1999     COMMAND_ERROR;
2000     goto out;
2001   }
2002
2003   if (cmd->argc != 2) {
2004     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2005         "Usage: /LEAVE <channel>");
2006     COMMAND_ERROR;
2007     goto out;
2008   }
2009
2010   if (cmd->argv[1][0] == '*') {
2011     if (!conn->current_channel) {
2012       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2013           "You are not on any channel");
2014       COMMAND_ERROR;
2015       goto out;
2016     }
2017     name = conn->current_channel->channel_name;
2018   } else {
2019     name = cmd->argv[1];
2020   }
2021
2022   /* Get the channel entry */
2023   channel = silc_client_get_channel(cmd->client, conn, name);
2024   if (!channel) {
2025     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2026         "You are not on that channel");
2027     COMMAND_ERROR;
2028     goto out;
2029   }
2030
2031   /* Remove us from channel */
2032   chu = silc_client_on_channel(channel, conn->local_entry);
2033   if (chu) {
2034     silc_hash_table_del(chu->client->channels, chu->channel);
2035     silc_hash_table_del(chu->channel->user_list, chu->client);
2036     silc_free(chu);
2037   }
2038
2039   /* Send LEAVE command to the server */
2040   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2041   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
2042                                           1, idp->data, idp->len);
2043   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2044                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2045   silc_buffer_free(buffer);
2046   silc_buffer_free(idp);
2047
2048   /* Notify application */
2049   COMMAND;
2050
2051   if (conn->current_channel == channel)
2052     conn->current_channel = NULL;
2053
2054   silc_client_del_channel(cmd->client, cmd->conn, channel);
2055
2056  out:
2057   silc_client_command_free(cmd);
2058 }
2059
2060 /* Command USERS. Requests the USERS of the clients joined on requested
2061    channel. */
2062
2063 SILC_CLIENT_CMD_FUNC(users)
2064 {
2065   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2066   SilcClientConnection conn = cmd->conn;
2067   SilcBuffer buffer;
2068   char *name;
2069
2070   if (!cmd->conn) {
2071     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2072     COMMAND_ERROR;
2073     goto out;
2074   }
2075
2076   if (cmd->argc != 2) {
2077     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2078         "Usage: /USERS <channel>");
2079     COMMAND_ERROR;
2080     goto out;
2081   }
2082
2083   if (cmd->argv[1][0] == '*') {
2084     if (!conn->current_channel) {
2085       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2086           "You are not on any channel");
2087       COMMAND_ERROR;
2088       goto out;
2089     }
2090     name = conn->current_channel->channel_name;
2091   } else {
2092     name = cmd->argv[1];
2093   }
2094
2095   /* Send USERS command to the server */
2096   buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
2097                                           ++conn->cmd_ident, 1, 
2098                                           2, name, strlen(name));
2099   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
2100                           NULL, 0, NULL, NULL, buffer->data, 
2101                           buffer->len, TRUE);
2102   silc_buffer_free(buffer);
2103
2104   /* Notify application */
2105   COMMAND;
2106
2107  out:
2108   silc_client_command_free(cmd);
2109 }
2110
2111 /* Command GETKEY. Used to fetch remote client's public key. */
2112
2113 SILC_CLIENT_CMD_FUNC(getkey)
2114 {
2115   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2116   SilcClientConnection conn = cmd->conn;
2117   SilcClient client = cmd->client;
2118   SilcClientEntry client_entry = NULL;
2119   SilcServerEntry server_entry = NULL;
2120   char *nickname = NULL;
2121   SilcBuffer idp, buffer;
2122
2123   SILC_LOG_DEBUG(("Start"));
2124
2125   if (!cmd->conn) {
2126     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2127     COMMAND_ERROR;
2128     goto out;
2129   }
2130
2131   if (cmd->argc < 2) {
2132     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
2133                      "Usage: /GETKEY <nickname or server name>");
2134     COMMAND_ERROR;
2135     goto out;
2136   }
2137
2138   /* Parse the typed nickname. */
2139   if (client->internal->params->nickname_parse)
2140     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2141   else
2142     nickname = strdup(cmd->argv[1]);
2143
2144   /* Find client entry */
2145   client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2146                                         FALSE);
2147   if (!client_entry) {
2148     /* Check whether user requested server actually */
2149     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2150
2151     if (!server_entry) {
2152       /* No. what ever user wants we don't have it, so resolve it. We
2153          will first try to resolve the client, and if that fails then
2154          we'll try to resolve the server. */
2155
2156       if (!cmd->pending) {
2157         /* This will send the IDENTIFY command for nickname */
2158         silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2159         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2160                                     conn->cmd_ident,  
2161                                     silc_client_command_getkey, 
2162                                     silc_client_command_dup(cmd));
2163         cmd->pending = 1;
2164         goto out;
2165       } else {
2166         SilcClientCommandReplyContext reply = 
2167           (SilcClientCommandReplyContext)context2;
2168         SilcCommandStatus status;
2169         unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
2170         SILC_GET16_MSB(status, tmp);
2171         
2172         /* If nickname was not found, then resolve the server. */
2173         if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
2174           /* This sends the IDENTIFY command to resolve the server. */
2175           silc_client_command_register(client, SILC_COMMAND_IDENTIFY, 
2176                                        NULL, NULL,
2177                                        silc_client_command_reply_identify_i, 0,
2178                                        ++conn->cmd_ident);
2179           silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2180                                    conn->cmd_ident, 1, 
2181                                    2, cmd->argv[1], cmd->argv_lens[1]);
2182           silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2183                                       conn->cmd_ident, 
2184                                       silc_client_command_getkey, 
2185                                       silc_client_command_dup(cmd));
2186           goto out;
2187         }
2188
2189         /* If server was not found, then we've resolved both nickname and
2190            server and did not find anybody. */
2191         if (status == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2192           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2193              silc_client_command_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2194           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2195            silc_client_command_status_message(status));
2196           COMMAND_ERROR;
2197           goto out;
2198         }
2199
2200         COMMAND_ERROR;
2201         goto out;
2202       }
2203     }
2204
2205     idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2206   } else {
2207     idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2208   }
2209
2210   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
2211                                           1, idp->data, idp->len);
2212   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2213                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2214   silc_buffer_free(buffer);
2215   silc_buffer_free(idp);
2216
2217   /* Notify application */
2218   COMMAND;
2219
2220  out:
2221   silc_free(nickname);
2222   silc_client_command_free(cmd);
2223 }
2224
2225 /* Register a new command indicated by the `command' to the SILC client.
2226    The `name' is optional command name.  If provided the command may be
2227    searched using the silc_client_command_find by that name.  The
2228    `command_function' is the function to be called when the command is
2229    executed, and the `command_reply_function' is the function to be
2230    called after the server has sent reply back to the command. 
2231
2232    The `ident' is optional identifier for the command.  If non-zero
2233    the `command_reply_function' for the command type `command' will be
2234    called only if the command reply sent by server includes the 
2235    command identifier `ident'. Application usually does not need it
2236    and set it to zero value. */
2237
2238 bool silc_client_command_register(SilcClient client,
2239                                   SilcCommand command,
2240                                   const char *name,
2241                                   SilcCommandCb command_function,
2242                                   SilcCommandCb command_reply_function,
2243                                   SilcUInt8 max_args,
2244                                   SilcUInt16 ident)
2245 {
2246   SilcClientCommand cmd;
2247
2248   cmd = silc_calloc(1, sizeof(*cmd));
2249   cmd->cmd = command;
2250   cmd->command = command_function;
2251   cmd->reply = command_reply_function;
2252   cmd->name = name ? strdup(name) : NULL;
2253   cmd->max_args = max_args;
2254   cmd->ident = ident;
2255
2256   silc_list_add(client->internal->commands, cmd);
2257
2258   return TRUE;
2259 }
2260
2261 /* Unregister a command indicated by the `command' with command function
2262    `command_function' and command reply function `command_reply_function'.
2263    Returns TRUE if the command was found and unregistered. */
2264
2265 bool silc_client_command_unregister(SilcClient client,
2266                                     SilcCommand command,
2267                                     SilcCommandCb command_function,
2268                                     SilcCommandCb command_reply_function,
2269                                     SilcUInt16 ident)
2270 {
2271   SilcClientCommand cmd;
2272
2273   silc_list_start(client->internal->commands);
2274   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2275     if (cmd->cmd == command && cmd->command == command_function &&
2276         cmd->reply == command_reply_function && cmd->ident == ident) {
2277       silc_list_del(client->internal->commands, cmd);
2278       silc_free(cmd->name);
2279       silc_free(cmd);
2280       return TRUE;
2281     }
2282   }
2283
2284   return FALSE;
2285 }
2286
2287 /* Register all default commands provided by the client library for the
2288    application. */
2289
2290 void silc_client_commands_register(SilcClient client)
2291 {
2292   silc_list_init(client->internal->commands, struct SilcClientCommandStruct, 
2293                  next);
2294
2295   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2296   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2297   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2298   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2299   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2300   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2301   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2302   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2303   SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
2304   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2305   SILC_CLIENT_CMD(connect, CONNECT, "CONNECT", 3);
2306   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2307   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2308   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2309   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2310   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2311   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
2312   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
2313   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2314   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2315   SILC_CLIENT_CMD(close, CLOSE, "CLOSE", 3);
2316   SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN", 1);
2317   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2318   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2319   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2320   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2321 }
2322
2323 /* Unregister all commands. */
2324
2325 void silc_client_commands_unregister(SilcClient client)
2326 {
2327   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2328   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2329   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2330   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2331   SILC_CLIENT_CMDU(list, LIST, "LIST");
2332   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2333   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2334   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2335   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2336   SILC_CLIENT_CMDU(info, INFO, "INFO");
2337   SILC_CLIENT_CMDU(connect, CONNECT, "CONNECT");
2338   SILC_CLIENT_CMDU(ping, PING, "PING");
2339   SILC_CLIENT_CMDU(oper, OPER, "OPER");
2340   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2341   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2342   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2343   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2344   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2345   SILC_CLIENT_CMDU(kick, KICK, "KICK");
2346   SILC_CLIENT_CMDU(ban, BAN, "BAN");
2347   SILC_CLIENT_CMDU(close, CLOSE, "CLOSE");
2348   SILC_CLIENT_CMDU(shutdown, SHUTDOWN, "SHUTDOWN");
2349   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2350   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2351   SILC_CLIENT_CMDU(users, USERS, "USERS");
2352   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2353 }