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