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