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