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     default:
1242       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1243       goto out;
1244       break;
1245     }
1246   }
1247
1248   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1249   SILC_PUT32_MSB(mode, modebuf);
1250
1251   /* Send the command packet. We support sending only one mode at once
1252      that requires an argument. */
1253   buffer = 
1254     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1255                                    1, idp->data, idp->len, 
1256                                    2, modebuf, sizeof(modebuf));
1257   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1258                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1259   silc_buffer_free(buffer);
1260   silc_buffer_free(idp);
1261
1262   /* Notify application */
1263   COMMAND(SILC_STATUS_OK);
1264
1265  out:
1266   silc_client_command_free(cmd);
1267 }
1268
1269 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1270    can be set several at once. Those modes that require argument must be set
1271    separately (unless set with modes that does not require arguments). */
1272
1273 SILC_CLIENT_CMD_FUNC(cmode)
1274 {
1275   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1276   SilcClientConnection conn = cmd->conn;
1277   SilcChannelEntry channel;
1278   SilcBuffer buffer, chidp, auth = NULL;
1279   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1280   SilcUInt32 mode, add, type, len, arg_len = 0;
1281   int i;
1282
1283   if (!cmd->conn) {
1284     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1285     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1286     goto out;
1287   }
1288
1289   if (cmd->argc < 3) {
1290     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1291         "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1292     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1293     goto out;
1294   }
1295
1296   if (cmd->argv[1][0] == '*') {
1297     if (!conn->current_channel) {
1298       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1299           "You are not on any channel");
1300       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1301       goto out;
1302     }
1303
1304     channel = conn->current_channel;
1305   } else {
1306     name = cmd->argv[1];
1307
1308     channel = silc_client_get_channel(cmd->client, conn, name);
1309     if (!channel) {
1310       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1311           "You are on that channel");
1312       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1313       goto out;
1314     }
1315   }
1316
1317   mode = channel->mode;
1318
1319   /* Are we adding or removing mode */
1320   if (cmd->argv[2][0] == '-')
1321     add = FALSE;
1322   else
1323     add = TRUE;
1324
1325   /* Argument type to be sent to server */
1326   type = 0;
1327
1328   /* Parse mode */
1329   cp = cmd->argv[2] + 1;
1330   len = strlen(cp);
1331   for (i = 0; i < len; i++) {
1332     switch(cp[i]) {
1333     case 'p':
1334       if (add)
1335         mode |= SILC_CHANNEL_MODE_PRIVATE;
1336       else
1337         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1338       break;
1339     case 's':
1340       if (add)
1341         mode |= SILC_CHANNEL_MODE_SECRET;
1342       else
1343         mode &= ~SILC_CHANNEL_MODE_SECRET;
1344       break;
1345     case 'k':
1346       if (add)
1347         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1348       else
1349         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1350       break;
1351     case 'i':
1352       if (add)
1353         mode |= SILC_CHANNEL_MODE_INVITE;
1354       else
1355         mode &= ~SILC_CHANNEL_MODE_INVITE;
1356       break;
1357     case 't':
1358       if (add)
1359         mode |= SILC_CHANNEL_MODE_TOPIC;
1360       else
1361         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1362       break;
1363     case 'm':
1364       if (add)
1365         mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1366       else
1367         mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1368       break;
1369     case 'M':
1370       if (add)
1371         mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1372       else
1373         mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1374       break;
1375     case 'l':
1376       if (add) {
1377         int ll;
1378         mode |= SILC_CHANNEL_MODE_ULIMIT;
1379         type = 3;
1380         if (cmd->argc < 4) {
1381           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1382               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1383           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1384           goto out;
1385         }
1386         ll = atoi(cmd->argv[3]);
1387         SILC_PUT32_MSB(ll, tmp);
1388         arg = tmp;
1389         arg_len = 4;
1390       } else {
1391         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1392       }
1393       break;
1394     case 'a':
1395       if (add) {
1396         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1397         type = 4;
1398         if (cmd->argc < 4) {
1399           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1400               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1401           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1402           goto out;
1403         }
1404         arg = cmd->argv[3];
1405         arg_len = cmd->argv_lens[3];
1406       } else {
1407         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1408       }
1409       break;
1410     case 'c':
1411       if (add) {
1412         mode |= SILC_CHANNEL_MODE_CIPHER;
1413         type = 5;
1414         if (cmd->argc < 4) {
1415           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1416               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1417           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1418           goto out;
1419         }
1420         arg = cmd->argv[3];
1421         arg_len = cmd->argv_lens[3];
1422       } else {
1423         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1424       }
1425       break;
1426     case 'h':
1427       if (add) {
1428         mode |= SILC_CHANNEL_MODE_HMAC;
1429         type = 6;
1430         if (cmd->argc < 4) {
1431           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1432               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1433           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1434           goto out;
1435         }
1436         arg = cmd->argv[3];
1437         arg_len = cmd->argv_lens[3];
1438       } else {
1439         mode &= ~SILC_CHANNEL_MODE_HMAC;
1440       }
1441       break;
1442     case 'f':
1443       if (add) {
1444         mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1445         type = 7;
1446
1447         if (cmd->argc < 4) {
1448           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1449               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1450           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1451           goto out;
1452         }
1453
1454         if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1455           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1456                                                     cmd->client->private_key,
1457                                                     cmd->client->rng, 
1458                                                     conn->hash,
1459                                                     conn->local_id,
1460                                                     SILC_ID_CLIENT);
1461         } else {
1462           auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1463                                           cmd->argv[3], cmd->argv_lens[3]);
1464         }
1465
1466         arg = auth->data;
1467         arg_len = auth->len;
1468       } else {
1469         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1470       }
1471       break;
1472     default:
1473       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1474       goto out;
1475       break;
1476     }
1477   }
1478
1479   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1480   SILC_PUT32_MSB(mode, modebuf);
1481
1482   /* Send the command packet. We support sending only one mode at once
1483      that requires an argument. */
1484   if (type && arg) {
1485     buffer = 
1486       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1487                                      1, chidp->data, chidp->len, 
1488                                      2, modebuf, sizeof(modebuf),
1489                                      type, arg, arg_len);
1490   } else {
1491     buffer = 
1492       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1493                                      1, chidp->data, chidp->len, 
1494                                      2, modebuf, sizeof(modebuf));
1495   }
1496
1497   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1498                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1499   silc_buffer_free(buffer);
1500   silc_buffer_free(chidp);
1501   if (auth)
1502     silc_buffer_free(auth);
1503
1504   /* Notify application */
1505   COMMAND(SILC_STATUS_OK);
1506
1507  out:
1508   silc_client_command_free(cmd);
1509 }
1510
1511 /* CUMODE command. Changes client's mode on a channel. */
1512
1513 SILC_CLIENT_CMD_FUNC(cumode)
1514 {
1515   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1516   SilcClient client = cmd->client;
1517   SilcClientConnection conn = cmd->conn;
1518   SilcChannelEntry channel;
1519   SilcChannelUser chu;
1520   SilcClientEntry client_entry;
1521   SilcBuffer buffer, clidp, chidp, auth = NULL;
1522   unsigned char *name, *cp, modebuf[4];
1523   SilcUInt32 mode = 0, add, len;
1524   char *nickname = NULL;
1525   int i;
1526
1527   if (!cmd->conn) {
1528     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1529     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1530     goto out;
1531   }
1532
1533   if (cmd->argc < 4) {
1534     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1535         "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1536     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1537     goto out;
1538   }
1539
1540   if (cmd->argv[1][0] == '*') {
1541     if (!conn->current_channel) {
1542       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1543           "You are not on any channel");
1544       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1545       goto out;
1546     }
1547
1548     channel = conn->current_channel;
1549   } else {
1550     name = cmd->argv[1];
1551
1552     channel = silc_client_get_channel(cmd->client, conn, name);
1553     if (!channel) {
1554       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1555           "You are on that channel");
1556       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1557       goto out;
1558     }
1559   }
1560
1561   /* Parse the typed nickname. */
1562   if (client->internal->params->nickname_parse)
1563     client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1564   else
1565     nickname = strdup(cmd->argv[3]);
1566
1567   /* Find client entry */
1568   client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
1569                                         cmd->argv[3], TRUE);
1570   if (!client_entry) {
1571     if (cmd->pending) {
1572       COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
1573       goto out;
1574     }
1575
1576     /* Client entry not found, it was requested thus mark this to be
1577        pending command. */
1578     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1579                                 conn->cmd_ident,  
1580                                 silc_client_command_cumode, 
1581                                 silc_client_command_dup(cmd));
1582     cmd->pending = 1;
1583     goto out;
1584   }
1585   
1586   /* Get the current mode */
1587   chu = silc_client_on_channel(channel, client_entry);
1588   if (chu)
1589     mode = chu->mode;
1590
1591   /* Are we adding or removing mode */
1592   if (cmd->argv[2][0] == '-')
1593     add = FALSE;
1594   else
1595     add = TRUE;
1596
1597   /* Parse mode */
1598   cp = cmd->argv[2] + 1;
1599   len = strlen(cp);
1600   for (i = 0; i < len; i++) {
1601     switch(cp[i]) {
1602     case 'a':
1603       if (add) {
1604         mode |= SILC_CHANNEL_UMODE_CHANFO;
1605         mode |= SILC_CHANNEL_UMODE_CHANOP;
1606         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1607         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1608         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1609       } else {
1610         mode = SILC_CHANNEL_UMODE_NONE;
1611       }
1612       break;
1613     case 'f':
1614       if (add) {
1615         if (cmd->argc == 5) {
1616           if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1617             auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1618                                                       cmd->client->private_key,
1619                                                       cmd->client->rng,
1620                                                       conn->hash,
1621                                                       conn->local_id,
1622                                                       SILC_ID_CLIENT);
1623           } else {
1624             auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1625                                             cmd->argv[4], cmd->argv_lens[4]);
1626           }
1627         }
1628         mode |= SILC_CHANNEL_UMODE_CHANFO;
1629       } else {
1630         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1631       }
1632       break;
1633     case 'o':
1634       if (add)
1635         mode |= SILC_CHANNEL_UMODE_CHANOP;
1636       else
1637         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1638       break;
1639     case 'b':
1640       if (add)
1641         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1642       else
1643         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1644       break;
1645     case 'u':
1646       if (add)
1647         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1648       else
1649         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1650       break;
1651     case 'r':
1652       if (add)
1653         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1654       else
1655         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1656       break;
1657     default:
1658       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1659       goto out;
1660       break;
1661     }
1662   }
1663
1664   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1665   SILC_PUT32_MSB(mode, modebuf);
1666   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1667
1668   /* Send the command packet. We support sending only one mode at once
1669      that requires an argument. */
1670   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 
1671                                           auth ? 4 : 3, 
1672                                           1, chidp->data, chidp->len, 
1673                                           2, modebuf, 4,
1674                                           3, clidp->data, clidp->len,
1675                                           4, auth ? auth->data : NULL, 
1676                                           auth ? auth->len : 0);
1677   
1678   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1679                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1680   silc_buffer_free(buffer);
1681   silc_buffer_free(chidp);
1682   silc_buffer_free(clidp);
1683   if (auth)
1684     silc_buffer_free(auth);
1685   
1686   /* Notify application */
1687   COMMAND(SILC_STATUS_OK);
1688
1689  out:
1690   silc_free(nickname);
1691   silc_client_command_free(cmd);
1692 }
1693
1694 /* KICK command. Kicks a client out of channel. */
1695
1696 SILC_CLIENT_CMD_FUNC(kick)
1697 {
1698   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1699   SilcClient client = cmd->client;
1700   SilcClientConnection conn = cmd->conn;
1701   SilcIDCacheEntry id_cache = NULL;
1702   SilcChannelEntry channel;
1703   SilcBuffer buffer, idp, idp2;
1704   SilcClientEntry target;
1705   char *name;
1706   char *nickname = NULL;
1707
1708   if (!cmd->conn) {
1709     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1710     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1711     goto out;
1712   }
1713
1714   if (cmd->argc < 3) {
1715     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1716         "Usage: /KICK <channel> <nickname> [<comment>]");
1717     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1718     goto out;
1719   }
1720
1721   if (cmd->argv[1][0] == '*') {
1722     if (!conn->current_channel) {
1723       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1724           "You are not on any channel");
1725       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1726       goto out;
1727     }
1728     name = conn->current_channel->channel_name;
1729   } else {
1730     name = cmd->argv[1];
1731   }
1732
1733   if (!conn->current_channel) {
1734     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1735         "You are not on that channel");
1736     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1737     goto out;
1738   }
1739
1740   /* Get the Channel ID of the channel */
1741   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1742     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1743         "You are not on that channel");
1744     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1745     goto out;
1746   }
1747
1748   channel = (SilcChannelEntry)id_cache->context;
1749
1750   /* Parse the typed nickname. */
1751   if (client->internal->params->nickname_parse)
1752     client->internal->params->nickname_parse(cmd->argv[2], &nickname);
1753   else
1754     nickname = strdup(cmd->argv[2]);
1755
1756   /* Get the target client */
1757   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1758                                   cmd->argv[2], FALSE);
1759   if (!target) {
1760     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1761         "No such client: %s", cmd->argv[2]);
1762     COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
1763     goto out;
1764   }
1765
1766   /* Send KICK command to the server */
1767   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1768   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1769   if (cmd->argc == 3)
1770     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1771                                             1, idp->data, idp->len,
1772                                             2, idp2->data, idp2->len);
1773   else
1774     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1775                                             1, idp->data, idp->len,
1776                                             2, idp2->data, idp2->len,
1777                                             3, cmd->argv[3], 
1778                                             strlen(cmd->argv[3]));
1779   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1780                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1781   silc_buffer_free(buffer);
1782   silc_buffer_free(idp);
1783   silc_buffer_free(idp2);
1784
1785   /* Notify application */
1786   COMMAND(SILC_STATUS_OK);
1787
1788  out:
1789   silc_free(nickname);
1790   silc_client_command_free(cmd);
1791 }
1792
1793 static void silc_client_command_oper_send(unsigned char *data,
1794                                           SilcUInt32 data_len, void *context)
1795 {
1796   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1797   SilcClientConnection conn = cmd->conn;
1798   SilcBuffer buffer, auth;
1799
1800   if (cmd->argc >= 3) {
1801     /* Encode the public key authentication payload */
1802     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1803                                               cmd->client->private_key,
1804                                               cmd->client->rng, conn->hash,
1805                                               conn->local_id,
1806                                               SILC_ID_CLIENT);
1807   } else {
1808     /* Encode the password authentication payload */
1809     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1810                                     data, data_len);
1811   }
1812
1813   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1814                                           1, cmd->argv[1], 
1815                                           strlen(cmd->argv[1]),
1816                                           2, auth->data, auth->len);
1817   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1818                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1819
1820   silc_buffer_free(buffer);
1821   silc_buffer_free(auth);
1822
1823   /* Notify application */
1824   COMMAND(SILC_STATUS_OK);
1825 }
1826
1827 /* OPER command. Used to obtain server operator privileges. */
1828
1829 SILC_CLIENT_CMD_FUNC(oper)
1830 {
1831   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1832   SilcClientConnection conn = cmd->conn;
1833
1834   if (!cmd->conn) {
1835     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1836     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1837     goto out;
1838   }
1839
1840   if (cmd->argc < 2) {
1841     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1842         "Usage: /OPER <username> [-pubkey]");
1843     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1844     goto out;
1845   }
1846
1847   if (cmd->argc < 3) {
1848     /* Get passphrase */
1849     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1850                                      silc_client_command_oper_send,
1851                                      context);
1852     return;
1853   }
1854
1855   silc_client_command_oper_send(NULL, 0, context);
1856
1857  out:
1858   silc_client_command_free(cmd);
1859 }
1860
1861 static void silc_client_command_silcoper_send(unsigned char *data,
1862                                               SilcUInt32 data_len, 
1863                                               void *context)
1864 {
1865   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1866   SilcClientConnection conn = cmd->conn;
1867   SilcBuffer buffer, auth;
1868
1869   if (cmd->argc >= 3) {
1870     /* Encode the public key authentication payload */
1871     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1872                                               cmd->client->private_key,
1873                                               cmd->client->rng, conn->hash,
1874                                               conn->local_id,
1875                                               SILC_ID_CLIENT);
1876   } else {
1877     /* Encode the password authentication payload */
1878     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1879                                     data, data_len);
1880   }
1881
1882   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1883                                           1, cmd->argv[1], 
1884                                           strlen(cmd->argv[1]),
1885                                           2, auth->data, auth->len);
1886   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1887                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1888
1889   silc_buffer_free(buffer);
1890   silc_buffer_free(auth);
1891
1892   /* Notify application */
1893   COMMAND(SILC_STATUS_OK);
1894 }
1895
1896 /* SILCOPER command. Used to obtain router operator privileges. */
1897
1898 SILC_CLIENT_CMD_FUNC(silcoper)
1899 {
1900   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1901   SilcClientConnection conn = cmd->conn;
1902
1903   if (!cmd->conn) {
1904     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1905     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1906     goto out;
1907   }
1908
1909   if (cmd->argc < 2) {
1910     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1911         "Usage: /SILCOPER <username> [-pubkey]");
1912     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1913     goto out;
1914   }
1915
1916   if (cmd->argc < 3) {
1917     /* Get passphrase */
1918     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1919                                      silc_client_command_silcoper_send,
1920                                      context);
1921     return;
1922   }
1923
1924   silc_client_command_silcoper_send(NULL, 0, context);
1925
1926  out:
1927   silc_client_command_free(cmd);
1928 }
1929
1930 /* Command BAN. This is used to manage the ban list of the channel. */
1931
1932 SILC_CLIENT_CMD_FUNC(ban)
1933 {
1934   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1935   SilcClientConnection conn = cmd->conn;
1936   SilcChannelEntry channel;
1937   SilcBuffer buffer, chidp;
1938   int type = 0;
1939   char *name, *ban = NULL;
1940
1941   if (!cmd->conn) {
1942     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1943     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
1944     goto out;
1945   }
1946
1947   if (cmd->argc < 2) {
1948     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1949         "Usage: /BAN <channel> "
1950         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1951     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1952     goto out;
1953   }
1954
1955   if (cmd->argv[1][0] == '*') {
1956     if (!conn->current_channel) {
1957       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1958           "You are not on any channel");
1959       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1960       goto out;
1961     }
1962
1963     channel = conn->current_channel;
1964   } else {
1965     name = cmd->argv[1];
1966
1967     channel = silc_client_get_channel(cmd->client, conn, name);
1968     if (!channel) {
1969       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1970           "You are noton that channel");
1971       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1972       goto out;
1973     }
1974   }
1975
1976   if (cmd->argc == 3) {
1977     if (cmd->argv[2][0] == '+')
1978       type = 2;
1979     else
1980       type = 3;
1981
1982     ban = cmd->argv[2];
1983     ban++;
1984   }
1985
1986   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1987
1988   /* Send the command */
1989   buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 
1990                                           ++conn->cmd_ident, 2,
1991                                           1, chidp->data, chidp->len,
1992                                           type, ban, ban ? strlen(ban) : 0);
1993   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1994                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1995   silc_buffer_free(buffer);
1996   silc_buffer_free(chidp);
1997
1998   /* Notify application */
1999   COMMAND(SILC_STATUS_OK);
2000
2001  out:
2002   silc_client_command_free(cmd);
2003 }
2004
2005 /* Command DETACH. This is used to detach from the server */
2006
2007 SILC_CLIENT_CMD_FUNC(detach)
2008 {
2009   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2010   SilcClientConnection conn = cmd->conn;
2011   SilcBuffer buffer;
2012
2013   if (!cmd->conn) {
2014     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2015     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2016     goto out;
2017   }
2018
2019   buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH,
2020                                           ++conn->cmd_ident, 0);
2021   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2022                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2023   silc_buffer_free(buffer);
2024
2025   /* Notify application */
2026   COMMAND(SILC_STATUS_OK);
2027
2028  out:
2029   silc_client_command_free(cmd);
2030 }
2031
2032 /* Command WATCH. */
2033
2034 SILC_CLIENT_CMD_FUNC(watch)
2035 {
2036   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2037   SilcClientConnection conn = cmd->conn;
2038   SilcBuffer buffer, idp = NULL;
2039   int type = 0;
2040
2041   if (!cmd->conn) {
2042     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2043     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2044     goto out;
2045   }
2046
2047   if (cmd->argc < 3) {
2048     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2049     goto out;
2050   }
2051
2052   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
2053
2054   if (!strcasecmp(cmd->argv[1], "-add")) {
2055     type = 2;
2056   } else if (!strcasecmp(cmd->argv[1], "-del")) {
2057     type = 3;
2058   } else {
2059     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2060     goto out;
2061   }
2062
2063   buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH, 
2064                                           ++conn->cmd_ident, 2,
2065                                           1, idp->data, idp->len,
2066                                           type, cmd->argv[2],
2067                                           cmd->argv_lens[2]);
2068   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2069                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2070   silc_buffer_free(buffer);
2071
2072   /* Notify application */
2073   COMMAND(SILC_STATUS_OK);
2074
2075  out:
2076   if (idp)
2077     silc_buffer_free(idp);
2078   silc_client_command_free(cmd);
2079 }
2080
2081 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2082
2083 SILC_CLIENT_CMD_FUNC(leave)
2084 {
2085   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2086   SilcClientConnection conn = cmd->conn;
2087   SilcChannelEntry channel;
2088   SilcChannelUser chu;
2089   SilcBuffer buffer, idp;
2090   char *name;
2091
2092   if (!cmd->conn) {
2093     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2094     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2095     goto out;
2096   }
2097
2098   if (cmd->argc != 2) {
2099     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2100         "Usage: /LEAVE <channel>");
2101     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2102     goto out;
2103   }
2104
2105   if (cmd->argv[1][0] == '*') {
2106     if (!conn->current_channel) {
2107       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2108           "You are not on any channel");
2109       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2110       goto out;
2111     }
2112     name = conn->current_channel->channel_name;
2113   } else {
2114     name = cmd->argv[1];
2115   }
2116
2117   /* Get the channel entry */
2118   channel = silc_client_get_channel(cmd->client, conn, name);
2119   if (!channel) {
2120     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2121         "You are not on that channel");
2122     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2123     goto out;
2124   }
2125
2126   /* Remove us from channel */
2127   chu = silc_client_on_channel(channel, conn->local_entry);
2128   if (chu) {
2129     silc_hash_table_del(chu->client->channels, chu->channel);
2130     silc_hash_table_del(chu->channel->user_list, chu->client);
2131     silc_free(chu);
2132   }
2133
2134   /* Send LEAVE command to the server */
2135   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2136   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
2137                                           1, idp->data, idp->len);
2138   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2139                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2140   silc_buffer_free(buffer);
2141   silc_buffer_free(idp);
2142
2143   /* Notify application */
2144   COMMAND(SILC_STATUS_OK);
2145
2146   if (conn->current_channel == channel)
2147     conn->current_channel = NULL;
2148
2149   silc_client_del_channel(cmd->client, cmd->conn, channel);
2150
2151  out:
2152   silc_client_command_free(cmd);
2153 }
2154
2155 /* Command USERS. Requests the USERS of the clients joined on requested
2156    channel. */
2157
2158 SILC_CLIENT_CMD_FUNC(users)
2159 {
2160   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2161   SilcClientConnection conn = cmd->conn;
2162   SilcBuffer buffer;
2163   char *name;
2164
2165   if (!cmd->conn) {
2166     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2167     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2168     goto out;
2169   }
2170
2171   if (cmd->argc != 2) {
2172     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2173         "Usage: /USERS <channel>");
2174     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2175     goto out;
2176   }
2177
2178   if (cmd->argv[1][0] == '*') {
2179     if (!conn->current_channel) {
2180       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2181           "You are not on any channel");
2182       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2183       goto out;
2184     }
2185     name = conn->current_channel->channel_name;
2186   } else {
2187     name = cmd->argv[1];
2188   }
2189
2190   /* Send USERS command to the server */
2191   buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
2192                                           ++conn->cmd_ident, 1, 
2193                                           2, name, strlen(name));
2194   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
2195                           NULL, 0, NULL, NULL, buffer->data, 
2196                           buffer->len, TRUE);
2197   silc_buffer_free(buffer);
2198
2199   /* Notify application */
2200   COMMAND(SILC_STATUS_OK);
2201
2202  out:
2203   silc_client_command_free(cmd);
2204 }
2205
2206 /* Command GETKEY. Used to fetch remote client's public key. */
2207
2208 SILC_CLIENT_CMD_FUNC(getkey)
2209 {
2210   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2211   SilcClientConnection conn = cmd->conn;
2212   SilcClient client = cmd->client;
2213   SilcClientEntry client_entry = NULL;
2214   SilcServerEntry server_entry = NULL;
2215   char *nickname = NULL;
2216   SilcBuffer idp, buffer;
2217
2218   SILC_LOG_DEBUG(("Start"));
2219
2220   if (!cmd->conn) {
2221     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2222     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2223     goto out;
2224   }
2225
2226   if (cmd->argc < 2) {
2227     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
2228                      "Usage: /GETKEY <nickname or server name>");
2229     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2230     goto out;
2231   }
2232
2233   /* Parse the typed nickname. */
2234   if (client->internal->params->nickname_parse)
2235     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2236   else
2237     nickname = strdup(cmd->argv[1]);
2238
2239   /* Find client entry */
2240   client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2241                                         FALSE);
2242   if (!client_entry) {
2243     /* Check whether user requested server actually */
2244     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2245
2246     if (!server_entry) {
2247       /* No. what ever user wants we don't have it, so resolve it. We
2248          will first try to resolve the client, and if that fails then
2249          we'll try to resolve the server. */
2250
2251       if (!cmd->pending) {
2252         /* This will send the IDENTIFY command for nickname */
2253         silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2254         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2255                                     conn->cmd_ident,  
2256                                     silc_client_command_getkey, 
2257                                     silc_client_command_dup(cmd));
2258         cmd->pending = 1;
2259         goto out;
2260       } else {
2261         SilcClientCommandReplyContext reply = 
2262           (SilcClientCommandReplyContext)context2;
2263         SilcStatus error;
2264
2265         /* If nickname was not found, then resolve the server. */
2266         silc_command_get_status(reply->payload, NULL, &error);
2267         if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
2268           /* This sends the IDENTIFY command to resolve the server. */
2269           silc_client_command_register(client, SILC_COMMAND_IDENTIFY, 
2270                                        NULL, NULL,
2271                                        silc_client_command_reply_identify_i, 0,
2272                                        ++conn->cmd_ident);
2273           silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2274                                    conn->cmd_ident, 1, 
2275                                    2, cmd->argv[1], cmd->argv_lens[1]);
2276           silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2277                                       conn->cmd_ident, 
2278                                       silc_client_command_getkey, 
2279                                       silc_client_command_dup(cmd));
2280           goto out;
2281         }
2282
2283         /* If server was not found, then we've resolved both nickname and
2284            server and did not find anybody. */
2285         if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2286           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2287              silc_get_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2288           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2289            silc_get_status_message(error));
2290           COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2291           goto out;
2292         }
2293
2294         COMMAND_ERROR(error);
2295         goto out;
2296       }
2297     }
2298
2299     idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2300   } else {
2301     idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2302   }
2303
2304   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
2305                                           1, idp->data, idp->len);
2306   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2307                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2308   silc_buffer_free(buffer);
2309   silc_buffer_free(idp);
2310
2311   /* Notify application */
2312   COMMAND(SILC_STATUS_OK);
2313
2314  out:
2315   silc_free(nickname);
2316   silc_client_command_free(cmd);
2317 }
2318
2319 /* Register a new command indicated by the `command' to the SILC client.
2320    The `name' is optional command name.  If provided the command may be
2321    searched using the silc_client_command_find by that name.  The
2322    `command_function' is the function to be called when the command is
2323    executed, and the `command_reply_function' is the function to be
2324    called after the server has sent reply back to the command. 
2325
2326    The `ident' is optional identifier for the command.  If non-zero
2327    the `command_reply_function' for the command type `command' will be
2328    called only if the command reply sent by server includes the 
2329    command identifier `ident'. Application usually does not need it
2330    and set it to zero value. */
2331
2332 bool silc_client_command_register(SilcClient client,
2333                                   SilcCommand command,
2334                                   const char *name,
2335                                   SilcCommandCb command_function,
2336                                   SilcCommandCb command_reply_function,
2337                                   SilcUInt8 max_args,
2338                                   SilcUInt16 ident)
2339 {
2340   SilcClientCommand cmd;
2341
2342   cmd = silc_calloc(1, sizeof(*cmd));
2343   cmd->cmd = command;
2344   cmd->command = command_function;
2345   cmd->reply = command_reply_function;
2346   cmd->name = name ? strdup(name) : NULL;
2347   cmd->max_args = max_args;
2348   cmd->ident = ident;
2349
2350   silc_list_add(client->internal->commands, cmd);
2351
2352   return TRUE;
2353 }
2354
2355 /* Unregister a command indicated by the `command' with command function
2356    `command_function' and command reply function `command_reply_function'.
2357    Returns TRUE if the command was found and unregistered. */
2358
2359 bool silc_client_command_unregister(SilcClient client,
2360                                     SilcCommand command,
2361                                     SilcCommandCb command_function,
2362                                     SilcCommandCb command_reply_function,
2363                                     SilcUInt16 ident)
2364 {
2365   SilcClientCommand cmd;
2366
2367   silc_list_start(client->internal->commands);
2368   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2369     if (cmd->cmd == command && cmd->command == command_function &&
2370         cmd->reply == command_reply_function && cmd->ident == ident) {
2371       silc_list_del(client->internal->commands, cmd);
2372       silc_free(cmd->name);
2373       silc_free(cmd);
2374       return TRUE;
2375     }
2376   }
2377
2378   return FALSE;
2379 }
2380
2381 /* Private range commands, specific to this implementation (and compatible
2382    with SILC Server). */
2383
2384 /* CONNECT command. Connects the server to another server. */
2385
2386 SILC_CLIENT_CMD_FUNC(connect)
2387 {
2388   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2389   SilcClientConnection conn = cmd->conn;
2390   SilcBuffer buffer;
2391   unsigned char port[4];
2392   SilcUInt32 tmp;
2393
2394   if (!cmd->conn) {
2395     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2396     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2397     goto out;
2398   }
2399
2400   if (cmd->argc < 2) {
2401     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2402         "Usage: /CONNECT <server> [<port>]");
2403     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2404     goto out;
2405   }
2406
2407   if (cmd->argc == 3) {
2408     tmp = atoi(cmd->argv[2]);
2409     SILC_PUT32_MSB(tmp, port);
2410   }
2411
2412   if (cmd->argc == 3)
2413     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2, 
2414                                             1, cmd->argv[1], 
2415                                             strlen(cmd->argv[1]),
2416                                             2, port, 4);
2417   else
2418     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2419                                             1, cmd->argv[1], 
2420                                             strlen(cmd->argv[1]));
2421   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2422                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2423   silc_buffer_free(buffer);
2424
2425   /* Notify application */
2426   COMMAND(SILC_STATUS_OK);
2427
2428  out:
2429   silc_client_command_free(cmd);
2430 }
2431
2432
2433 /* CLOSE command. Close server connection to the remote server */
2434  
2435 SILC_CLIENT_CMD_FUNC(close)
2436 {
2437   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2438   SilcClientConnection conn = cmd->conn;
2439   SilcBuffer buffer;
2440   unsigned char port[4];
2441   SilcUInt32 tmp;
2442
2443   if (!cmd->conn) {
2444     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2445     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2446     goto out;
2447   }
2448
2449   if (cmd->argc < 2) {
2450     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2451         "Usage: /CLOSE <server> [<port>]");
2452     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2453     goto out;
2454   }
2455
2456   if (cmd->argc == 3) {
2457     tmp = atoi(cmd->argv[2]);
2458     SILC_PUT32_MSB(tmp, port);
2459   }
2460
2461   if (cmd->argc == 3)
2462     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2, 
2463                                             1, cmd->argv[1], 
2464                                             strlen(cmd->argv[1]),
2465                                             2, port, 4);
2466   else
2467     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2468                                             1, cmd->argv[1], 
2469                                             strlen(cmd->argv[1]));
2470   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2471                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2472   silc_buffer_free(buffer);
2473
2474   /* Notify application */
2475   COMMAND(SILC_STATUS_OK);
2476
2477  out:
2478   silc_client_command_free(cmd);
2479 }
2480  
2481 /* SHUTDOWN command. Shutdowns the server. */
2482
2483 SILC_CLIENT_CMD_FUNC(shutdown)
2484 {
2485   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2486
2487   if (!cmd->conn) {
2488     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2489     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2490     goto out;
2491   }
2492
2493   /* Send the command */
2494   silc_client_command_send(cmd->client, cmd->conn, 
2495                            SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2496
2497   /* Notify application */
2498   COMMAND(SILC_STATUS_OK);
2499
2500  out:
2501   silc_client_command_free(cmd);
2502 }
2503
2504 /* Register all default commands provided by the client library for the
2505    application. */
2506
2507 void silc_client_commands_register(SilcClient client)
2508 {
2509   silc_list_init(client->internal->commands, struct SilcClientCommandStruct, 
2510                  next);
2511
2512   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2513   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2514   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2515   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2516   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2517   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2518   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2519   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2520   SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
2521   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2522   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2523   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2524   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2525   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2526   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2527   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
2528   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
2529   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2530   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2531   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2532   SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2533   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2534   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2535   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2536   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2537
2538   SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2539   SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2540   SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2541 }
2542
2543 /* Unregister all commands. */
2544
2545 void silc_client_commands_unregister(SilcClient client)
2546 {
2547   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2548   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2549   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2550   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2551   SILC_CLIENT_CMDU(list, LIST, "LIST");
2552   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2553   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2554   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2555   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2556   SILC_CLIENT_CMDU(info, INFO, "INFO");
2557   SILC_CLIENT_CMDU(ping, PING, "PING");
2558   SILC_CLIENT_CMDU(oper, OPER, "OPER");
2559   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2560   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2561   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2562   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2563   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2564   SILC_CLIENT_CMDU(kick, KICK, "KICK");
2565   SILC_CLIENT_CMDU(ban, BAN, "BAN");
2566   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2567   SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2568   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2569   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2570   SILC_CLIENT_CMDU(users, USERS, "USERS");
2571   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2572
2573   SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2574   SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2575   SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");
2576 }