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