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