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