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