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