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