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