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