Started implementing protocol version 1.1 and narrowing down
[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, NULL, 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, *cipher = NULL, *hmac = NULL;
952   int i;
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       passphrase = cmd->argv[i];
999     }
1000   }
1001
1002   /* Send JOIN command to the server */
1003   buffer =
1004     silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6,
1005                                    1, name, strlen(name),
1006                                    2, idp->data, idp->len,
1007                                    3, passphrase, 
1008                                    passphrase ? strlen(passphrase) : 0,
1009                                    4, cipher, cipher ? strlen(cipher) : 0,
1010                                    5, hmac, hmac ? strlen(hmac) : 0,
1011                                    6, auth ? auth->data : NULL,
1012                                    auth ? auth->len : 0);
1013   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1014                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1015   silc_buffer_free(buffer);
1016   silc_buffer_free(idp);
1017   if (auth)
1018     silc_buffer_free(auth);
1019
1020   /* Notify application */
1021   COMMAND;
1022
1023  out:
1024   silc_client_command_free(cmd);
1025 }
1026
1027 /* MOTD command. Requests motd from server. */
1028
1029 SILC_CLIENT_CMD_FUNC(motd)
1030 {
1031   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1032   SilcClientConnection conn = cmd->conn;
1033   SilcBuffer buffer;
1034
1035   if (!cmd->conn) {
1036     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1037     COMMAND_ERROR;
1038     goto out;
1039   }
1040
1041   if (cmd->argc < 1 || cmd->argc > 2) {
1042     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1043         "Usage: /MOTD [<server>]");
1044     COMMAND_ERROR;
1045     goto out;
1046   }
1047
1048   /* Send TOPIC command to the server */
1049   if (cmd->argc == 1)
1050     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1051                                             1, conn->remote_host, 
1052                                             strlen(conn->remote_host));
1053   else
1054     buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
1055                                             1, cmd->argv[1], 
1056                                             cmd->argv_lens[1]);
1057   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1058                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1059   silc_buffer_free(buffer);
1060
1061   /* Notify application */
1062   COMMAND;
1063
1064  out:
1065   silc_client_command_free(cmd);
1066 }
1067
1068 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1069    modes as client cannot set itself server/router operator privileges. */
1070
1071 SILC_CLIENT_CMD_FUNC(umode)
1072 {
1073   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1074   SilcClientConnection conn = cmd->conn;
1075   SilcBuffer buffer, idp;
1076   unsigned char *cp, modebuf[4];
1077   SilcUInt32 mode, add, len;
1078   int i;
1079
1080   if (!cmd->conn) {
1081     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1082     COMMAND_ERROR;
1083     goto out;
1084   }
1085
1086   if (cmd->argc < 2) {
1087     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1088         "Usage: /UMODE +|-<modes>");
1089     COMMAND_ERROR;
1090     goto out;
1091   }
1092
1093   mode = conn->local_entry->mode;
1094
1095   /* Are we adding or removing mode */
1096   if (cmd->argv[1][0] == '-')
1097     add = FALSE;
1098   else
1099     add = TRUE;
1100
1101   /* Parse mode */
1102   cp = cmd->argv[1] + 1;
1103   len = strlen(cp);
1104   for (i = 0; i < len; i++) {
1105     switch(cp[i]) {
1106     case 'a':
1107       if (add) {
1108         mode = 0;
1109         mode |= SILC_UMODE_SERVER_OPERATOR;
1110         mode |= SILC_UMODE_ROUTER_OPERATOR;
1111       } else {
1112         mode = SILC_UMODE_NONE;
1113       }
1114       break;
1115     case 's':
1116       if (add)
1117         mode |= SILC_UMODE_SERVER_OPERATOR;
1118       else
1119         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1120       break;
1121     case 'r':
1122       if (add)
1123         mode |= SILC_UMODE_ROUTER_OPERATOR;
1124       else
1125         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1126       break;
1127     case 'g':
1128       if (add)
1129         mode |= SILC_UMODE_GONE;
1130       else
1131         mode &= ~SILC_UMODE_GONE;
1132       break;
1133     default:
1134       COMMAND_ERROR;
1135       goto out;
1136       break;
1137     }
1138   }
1139
1140   idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1141   SILC_PUT32_MSB(mode, modebuf);
1142
1143   /* Send the command packet. We support sending only one mode at once
1144      that requires an argument. */
1145   buffer = 
1146     silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
1147                                    1, idp->data, idp->len, 
1148                                    2, modebuf, sizeof(modebuf));
1149   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1150                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1151   silc_buffer_free(buffer);
1152   silc_buffer_free(idp);
1153
1154   /* Notify application */
1155   COMMAND;
1156
1157  out:
1158   silc_client_command_free(cmd);
1159 }
1160
1161 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1162    can be set several at once. Those modes that require argument must be set
1163    separately (unless set with modes that does not require arguments). */
1164
1165 SILC_CLIENT_CMD_FUNC(cmode)
1166 {
1167   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1168   SilcClientConnection conn = cmd->conn;
1169   SilcChannelEntry channel;
1170   SilcBuffer buffer, chidp, auth = NULL;
1171   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1172   SilcUInt32 mode, add, type, len, arg_len = 0;
1173   int i;
1174
1175   if (!cmd->conn) {
1176     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1177     COMMAND_ERROR;
1178     goto out;
1179   }
1180
1181   if (cmd->argc < 3) {
1182     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1183         "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1184     COMMAND_ERROR;
1185     goto out;
1186   }
1187
1188   if (cmd->argv[1][0] == '*') {
1189     if (!conn->current_channel) {
1190       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1191           "You are not on any channel");
1192       COMMAND_ERROR;
1193       goto out;
1194     }
1195
1196     channel = conn->current_channel;
1197   } else {
1198     name = cmd->argv[1];
1199
1200     channel = silc_client_get_channel(cmd->client, conn, name);
1201     if (!channel) {
1202       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1203           "You are on that channel");
1204       COMMAND_ERROR;
1205       goto out;
1206     }
1207   }
1208
1209   mode = channel->mode;
1210
1211   /* Are we adding or removing mode */
1212   if (cmd->argv[2][0] == '-')
1213     add = FALSE;
1214   else
1215     add = TRUE;
1216
1217   /* Argument type to be sent to server */
1218   type = 0;
1219
1220   /* Parse mode */
1221   cp = cmd->argv[2] + 1;
1222   len = strlen(cp);
1223   for (i = 0; i < len; i++) {
1224     switch(cp[i]) {
1225     case 'p':
1226       if (add)
1227         mode |= SILC_CHANNEL_MODE_PRIVATE;
1228       else
1229         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1230       break;
1231     case 's':
1232       if (add)
1233         mode |= SILC_CHANNEL_MODE_SECRET;
1234       else
1235         mode &= ~SILC_CHANNEL_MODE_SECRET;
1236       break;
1237     case 'k':
1238       if (add)
1239         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1240       else
1241         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1242       break;
1243     case 'i':
1244       if (add)
1245         mode |= SILC_CHANNEL_MODE_INVITE;
1246       else
1247         mode &= ~SILC_CHANNEL_MODE_INVITE;
1248       break;
1249     case 't':
1250       if (add)
1251         mode |= SILC_CHANNEL_MODE_TOPIC;
1252       else
1253         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1254       break;
1255     case 'm':
1256       if (add)
1257         mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1258       else
1259         mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1260       break;
1261     case 'M':
1262       if (add)
1263         mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1264       else
1265         mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1266       break;
1267     case 'l':
1268       if (add) {
1269         int ll;
1270         mode |= SILC_CHANNEL_MODE_ULIMIT;
1271         type = 3;
1272         if (cmd->argc < 4) {
1273           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1274               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1275           COMMAND_ERROR;
1276           goto out;
1277         }
1278         ll = atoi(cmd->argv[3]);
1279         SILC_PUT32_MSB(ll, tmp);
1280         arg = tmp;
1281         arg_len = 4;
1282       } else {
1283         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1284       }
1285       break;
1286     case 'a':
1287       if (add) {
1288         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1289         type = 4;
1290         if (cmd->argc < 4) {
1291           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1292               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1293           COMMAND_ERROR;
1294           goto out;
1295         }
1296         arg = cmd->argv[3];
1297         arg_len = cmd->argv_lens[3];
1298       } else {
1299         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1300       }
1301       break;
1302     case 'c':
1303       if (add) {
1304         mode |= SILC_CHANNEL_MODE_CIPHER;
1305         type = 5;
1306         if (cmd->argc < 4) {
1307           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1308               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1309           COMMAND_ERROR;
1310           goto out;
1311         }
1312         arg = cmd->argv[3];
1313         arg_len = cmd->argv_lens[3];
1314       } else {
1315         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1316       }
1317       break;
1318     case 'h':
1319       if (add) {
1320         mode |= SILC_CHANNEL_MODE_HMAC;
1321         type = 6;
1322         if (cmd->argc < 4) {
1323           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1324               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1325           COMMAND_ERROR;
1326           goto out;
1327         }
1328         arg = cmd->argv[3];
1329         arg_len = cmd->argv_lens[3];
1330       } else {
1331         mode &= ~SILC_CHANNEL_MODE_HMAC;
1332       }
1333       break;
1334     case 'f':
1335       if (add) {
1336         mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1337         type = 7;
1338
1339         if (cmd->argc < 4) {
1340           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1341               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1342           COMMAND_ERROR;
1343           goto out;
1344         }
1345
1346         if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1347           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1348                                                     cmd->client->private_key,
1349                                                     cmd->client->rng, 
1350                                                     conn->hash,
1351                                                     conn->local_id,
1352                                                     SILC_ID_CLIENT);
1353         } else {
1354           auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1355                                           cmd->argv[3], cmd->argv_lens[3]);
1356         }
1357
1358         arg = auth->data;
1359         arg_len = auth->len;
1360       } else {
1361         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1362       }
1363       break;
1364     default:
1365       COMMAND_ERROR;
1366       goto out;
1367       break;
1368     }
1369   }
1370
1371   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1372   SILC_PUT32_MSB(mode, modebuf);
1373
1374   /* Send the command packet. We support sending only one mode at once
1375      that requires an argument. */
1376   if (type && arg) {
1377     buffer = 
1378       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
1379                                      1, chidp->data, chidp->len, 
1380                                      2, modebuf, sizeof(modebuf),
1381                                      type, arg, arg_len);
1382   } else {
1383     buffer = 
1384       silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
1385                                      1, chidp->data, chidp->len, 
1386                                      2, modebuf, sizeof(modebuf));
1387   }
1388
1389   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1390                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1391   silc_buffer_free(buffer);
1392   silc_buffer_free(chidp);
1393   if (auth)
1394     silc_buffer_free(auth);
1395
1396   /* Notify application */
1397   COMMAND;
1398
1399  out:
1400   silc_client_command_free(cmd);
1401 }
1402
1403 /* CUMODE command. Changes client's mode on a channel. */
1404
1405 SILC_CLIENT_CMD_FUNC(cumode)
1406 {
1407   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1408   SilcClient client = cmd->client;
1409   SilcClientConnection conn = cmd->conn;
1410   SilcChannelEntry channel;
1411   SilcChannelUser chu;
1412   SilcClientEntry client_entry;
1413   SilcBuffer buffer, clidp, chidp, auth = NULL;
1414   unsigned char *name, *cp, modebuf[4];
1415   SilcUInt32 mode = 0, add, len;
1416   char *nickname = NULL;
1417   int i;
1418
1419   if (!cmd->conn) {
1420     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1421     COMMAND_ERROR;
1422     goto out;
1423   }
1424
1425   if (cmd->argc < 4) {
1426     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1427         "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1428     COMMAND_ERROR;
1429     goto out;
1430   }
1431
1432   if (cmd->argv[1][0] == '*') {
1433     if (!conn->current_channel) {
1434       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1435           "You are not on any channel");
1436       COMMAND_ERROR;
1437       goto out;
1438     }
1439
1440     channel = conn->current_channel;
1441   } else {
1442     name = cmd->argv[1];
1443
1444     channel = silc_client_get_channel(cmd->client, conn, name);
1445     if (!channel) {
1446       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1447           "You are on that channel");
1448       COMMAND_ERROR;
1449       goto out;
1450     }
1451   }
1452
1453   /* Parse the typed nickname. */
1454   if (client->internal->params->nickname_parse)
1455     client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1456   else
1457     nickname = strdup(cmd->argv[3]);
1458
1459   /* Find client entry */
1460   client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
1461                                         cmd->argv[3], TRUE);
1462   if (!client_entry) {
1463     if (cmd->pending) {
1464       COMMAND_ERROR;
1465       goto out;
1466     }
1467
1468     /* Client entry not found, it was requested thus mark this to be
1469        pending command. */
1470     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
1471                                 conn->cmd_ident,  
1472                                 silc_client_command_cumode, 
1473                                 silc_client_command_dup(cmd));
1474     cmd->pending = 1;
1475     goto out;
1476   }
1477   
1478   /* Get the current mode */
1479   chu = silc_client_on_channel(channel, client_entry);
1480   if (chu)
1481     mode = chu->mode;
1482
1483   /* Are we adding or removing mode */
1484   if (cmd->argv[2][0] == '-')
1485     add = FALSE;
1486   else
1487     add = TRUE;
1488
1489   /* Parse mode */
1490   cp = cmd->argv[2] + 1;
1491   len = strlen(cp);
1492   for (i = 0; i < len; i++) {
1493     switch(cp[i]) {
1494     case 'a':
1495       if (add) {
1496         mode |= SILC_CHANNEL_UMODE_CHANFO;
1497         mode |= SILC_CHANNEL_UMODE_CHANOP;
1498       } else {
1499         mode = SILC_CHANNEL_UMODE_NONE;
1500       }
1501       break;
1502     case 'f':
1503       if (add) {
1504         if (cmd->argc == 5) {
1505           if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1506             auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1507                                                       cmd->client->private_key,
1508                                                       cmd->client->rng,
1509                                                       conn->hash,
1510                                                       conn->local_id,
1511                                                       SILC_ID_CLIENT);
1512           } else {
1513             auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1514                                             cmd->argv[4], cmd->argv_lens[4]);
1515           }
1516         }
1517         mode |= SILC_CHANNEL_UMODE_CHANFO;
1518       } else {
1519         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1520       }
1521       break;
1522     case 'o':
1523       if (add)
1524         mode |= SILC_CHANNEL_UMODE_CHANOP;
1525       else
1526         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1527       break;
1528     default:
1529       COMMAND_ERROR;
1530       goto out;
1531       break;
1532     }
1533   }
1534
1535   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1536   SILC_PUT32_MSB(mode, modebuf);
1537   clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1538
1539   /* Send the command packet. We support sending only one mode at once
1540      that requires an argument. */
1541   buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 
1542                                           auth ? 4 : 3, 
1543                                           1, chidp->data, chidp->len, 
1544                                           2, modebuf, 4,
1545                                           3, clidp->data, clidp->len,
1546                                           4, auth ? auth->data : NULL, 
1547                                           auth ? auth->len : 0);
1548   
1549   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1550                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1551   silc_buffer_free(buffer);
1552   silc_buffer_free(chidp);
1553   silc_buffer_free(clidp);
1554   if (auth)
1555     silc_buffer_free(auth);
1556   
1557   /* Notify application */
1558   COMMAND;
1559
1560  out:
1561   silc_free(nickname);
1562   silc_client_command_free(cmd);
1563 }
1564
1565 /* KICK command. Kicks a client out of channel. */
1566
1567 SILC_CLIENT_CMD_FUNC(kick)
1568 {
1569   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1570   SilcClient client = cmd->client;
1571   SilcClientConnection conn = cmd->conn;
1572   SilcIDCacheEntry id_cache = NULL;
1573   SilcChannelEntry channel;
1574   SilcBuffer buffer, idp, idp2;
1575   SilcClientEntry target;
1576   char *name;
1577   char *nickname = NULL;
1578
1579   if (!cmd->conn) {
1580     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1581     COMMAND_ERROR;
1582     goto out;
1583   }
1584
1585   if (cmd->argc < 3) {
1586     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1587         "Usage: /KICK <channel> <nickname> [<comment>]");
1588     COMMAND_ERROR;
1589     goto out;
1590   }
1591
1592   if (cmd->argv[1][0] == '*') {
1593     if (!conn->current_channel) {
1594       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1595           "You are not on any channel");
1596       COMMAND_ERROR;
1597       goto out;
1598     }
1599     name = conn->current_channel->channel_name;
1600   } else {
1601     name = cmd->argv[1];
1602   }
1603
1604   if (!conn->current_channel) {
1605     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1606         "You are not on that channel");
1607     COMMAND_ERROR;
1608     goto out;
1609   }
1610
1611   /* Get the Channel ID of the channel */
1612   if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1613     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1614         "You are not on that channel");
1615     COMMAND_ERROR;
1616     goto out;
1617   }
1618
1619   channel = (SilcChannelEntry)id_cache->context;
1620
1621   /* Parse the typed nickname. */
1622   if (client->internal->params->nickname_parse)
1623     client->internal->params->nickname_parse(cmd->argv[2], &nickname);
1624   else
1625     nickname = strdup(cmd->argv[2]);
1626
1627   /* Get the target client */
1628   target = silc_idlist_get_client(cmd->client, conn, nickname, 
1629                                   cmd->argv[2], FALSE);
1630   if (!target) {
1631     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1632         "No such client: %s", cmd->argv[2]);
1633     COMMAND_ERROR;
1634     goto out;
1635   }
1636
1637   /* Send KICK command to the server */
1638   idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1639   idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1640   if (cmd->argc == 3)
1641     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
1642                                             1, idp->data, idp->len,
1643                                             2, idp2->data, idp2->len);
1644   else
1645     buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
1646                                             1, idp->data, idp->len,
1647                                             2, idp2->data, idp2->len,
1648                                             3, cmd->argv[3], 
1649                                             strlen(cmd->argv[3]));
1650   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1651                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1652   silc_buffer_free(buffer);
1653   silc_buffer_free(idp);
1654   silc_buffer_free(idp2);
1655
1656   /* Notify application */
1657   COMMAND;
1658
1659  out:
1660   silc_free(nickname);
1661   silc_client_command_free(cmd);
1662 }
1663
1664 static void silc_client_command_oper_send(unsigned char *data,
1665                                           SilcUInt32 data_len, void *context)
1666 {
1667   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1668   SilcClientConnection conn = cmd->conn;
1669   SilcBuffer buffer, auth;
1670
1671   if (cmd->argc >= 3) {
1672     /* Encode the public key authentication payload */
1673     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1674                                               cmd->client->private_key,
1675                                               cmd->client->rng, conn->hash,
1676                                               conn->local_id,
1677                                               SILC_ID_CLIENT);
1678   } else {
1679     /* Encode the password authentication payload */
1680     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1681                                     data, data_len);
1682   }
1683
1684   buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
1685                                           1, cmd->argv[1], 
1686                                           strlen(cmd->argv[1]),
1687                                           2, auth->data, auth->len);
1688   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1689                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1690
1691   silc_buffer_free(buffer);
1692   silc_buffer_free(auth);
1693
1694   /* Notify application */
1695   COMMAND;
1696 }
1697
1698 /* OPER command. Used to obtain server operator privileges. */
1699
1700 SILC_CLIENT_CMD_FUNC(oper)
1701 {
1702   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1703   SilcClientConnection conn = cmd->conn;
1704
1705   if (!cmd->conn) {
1706     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1707     COMMAND_ERROR;
1708     goto out;
1709   }
1710
1711   if (cmd->argc < 2) {
1712     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1713         "Usage: /OPER <username> [-pubkey]");
1714     COMMAND_ERROR;
1715     goto out;
1716   }
1717
1718   if (cmd->argc < 3) {
1719     /* Get passphrase */
1720     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1721                                      silc_client_command_oper_send,
1722                                      context);
1723     return;
1724   }
1725
1726   silc_client_command_oper_send(NULL, 0, context);
1727
1728  out:
1729   silc_client_command_free(cmd);
1730 }
1731
1732 static void silc_client_command_silcoper_send(unsigned char *data,
1733                                               SilcUInt32 data_len, void *context)
1734 {
1735   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1736   SilcClientConnection conn = cmd->conn;
1737   SilcBuffer buffer, auth;
1738
1739   if (cmd->argc >= 3) {
1740     /* Encode the public key authentication payload */
1741     auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1742                                               cmd->client->private_key,
1743                                               cmd->client->rng, conn->hash,
1744                                               conn->local_id,
1745                                               SILC_ID_CLIENT);
1746   } else {
1747     /* Encode the password authentication payload */
1748     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1749                                     data, data_len);
1750   }
1751
1752   buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
1753                                           1, cmd->argv[1], 
1754                                           strlen(cmd->argv[1]),
1755                                           2, auth->data, auth->len);
1756   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1757                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1758
1759   silc_buffer_free(buffer);
1760   silc_buffer_free(auth);
1761
1762   /* Notify application */
1763   COMMAND;
1764 }
1765
1766 /* SILCOPER command. Used to obtain router operator privileges. */
1767
1768 SILC_CLIENT_CMD_FUNC(silcoper)
1769 {
1770   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1771   SilcClientConnection conn = cmd->conn;
1772
1773   if (!cmd->conn) {
1774     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1775     COMMAND_ERROR;
1776     goto out;
1777   }
1778
1779   if (cmd->argc < 2) {
1780     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1781         "Usage: /SILCOPER <username> [-pubkey]");
1782     COMMAND_ERROR;
1783     goto out;
1784   }
1785
1786   if (cmd->argc < 3) {
1787     /* Get passphrase */
1788     cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1789                                      silc_client_command_silcoper_send,
1790                                      context);
1791     return;
1792   }
1793
1794   silc_client_command_silcoper_send(NULL, 0, context);
1795
1796  out:
1797   silc_client_command_free(cmd);
1798 }
1799
1800 /* Command BAN. This is used to manage the ban list of the channel. */
1801
1802 SILC_CLIENT_CMD_FUNC(ban)
1803 {
1804   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1805   SilcClientConnection conn = cmd->conn;
1806   SilcChannelEntry channel;
1807   SilcBuffer buffer, chidp;
1808   int type = 0;
1809   char *name, *ban = NULL;
1810
1811   if (!cmd->conn) {
1812     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1813     COMMAND_ERROR;
1814     goto out;
1815   }
1816
1817   if (cmd->argc < 2) {
1818     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1819         "Usage: /BAN <channel> "
1820         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1821     COMMAND_ERROR;
1822     goto out;
1823   }
1824
1825   if (cmd->argv[1][0] == '*') {
1826     if (!conn->current_channel) {
1827       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1828           "You are not on any channel");
1829       COMMAND_ERROR;
1830       goto out;
1831     }
1832
1833     channel = conn->current_channel;
1834   } else {
1835     name = cmd->argv[1];
1836
1837     channel = silc_client_get_channel(cmd->client, conn, name);
1838     if (!channel) {
1839       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1840           "You are on that channel");
1841       COMMAND_ERROR;
1842       goto out;
1843     }
1844   }
1845
1846   if (cmd->argc == 3) {
1847     if (cmd->argv[2][0] == '+')
1848       type = 2;
1849     else
1850       type = 3;
1851
1852     ban = cmd->argv[2];
1853     ban++;
1854   }
1855
1856   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1857
1858   /* Send the command */
1859   if (ban)
1860     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, 
1861                                             1, chidp->data, chidp->len,
1862                                             type, ban, strlen(ban));
1863   else
1864     buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, 
1865                                             1, chidp->data, chidp->len);
1866
1867   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1868                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1869   silc_buffer_free(buffer);
1870   silc_buffer_free(chidp);
1871
1872   /* Notify application */
1873   COMMAND;
1874
1875  out:
1876   silc_client_command_free(cmd);
1877 }
1878
1879 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1880
1881 SILC_CLIENT_CMD_FUNC(leave)
1882 {
1883   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1884   SilcClientConnection conn = cmd->conn;
1885   SilcChannelEntry channel;
1886   SilcChannelUser chu;
1887   SilcBuffer buffer, idp;
1888   char *name;
1889
1890   if (!cmd->conn) {
1891     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1892     COMMAND_ERROR;
1893     goto out;
1894   }
1895
1896   if (cmd->argc != 2) {
1897     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1898         "Usage: /LEAVE <channel>");
1899     COMMAND_ERROR;
1900     goto out;
1901   }
1902
1903   if (cmd->argv[1][0] == '*') {
1904     if (!conn->current_channel) {
1905       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1906           "You are not on any channel");
1907       COMMAND_ERROR;
1908       goto out;
1909     }
1910     name = conn->current_channel->channel_name;
1911   } else {
1912     name = cmd->argv[1];
1913   }
1914
1915   /* Get the channel entry */
1916   channel = silc_client_get_channel(cmd->client, conn, name);
1917   if (!channel) {
1918     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1919         "You are not on that channel");
1920     COMMAND_ERROR;
1921     goto out;
1922   }
1923
1924   /* Remove us from channel */
1925   chu = silc_client_on_channel(channel, conn->local_entry);
1926   if (chu) {
1927     silc_hash_table_del(chu->client->channels, chu->channel);
1928     silc_hash_table_del(chu->channel->user_list, chu->client);
1929     silc_free(chu);
1930   }
1931
1932   /* Send LEAVE command to the server */
1933   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1934   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
1935                                           1, idp->data, idp->len);
1936   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
1937                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
1938   silc_buffer_free(buffer);
1939   silc_buffer_free(idp);
1940
1941   /* Notify application */
1942   COMMAND;
1943
1944   if (conn->current_channel == channel)
1945     conn->current_channel = NULL;
1946
1947   silc_client_del_channel(cmd->client, cmd->conn, channel);
1948
1949  out:
1950   silc_client_command_free(cmd);
1951 }
1952
1953 /* Command USERS. Requests the USERS of the clients joined on requested
1954    channel. */
1955
1956 SILC_CLIENT_CMD_FUNC(users)
1957 {
1958   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1959   SilcClientConnection conn = cmd->conn;
1960   SilcBuffer buffer;
1961   char *name;
1962
1963   if (!cmd->conn) {
1964     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1965     COMMAND_ERROR;
1966     goto out;
1967   }
1968
1969   if (cmd->argc != 2) {
1970     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1971         "Usage: /USERS <channel>");
1972     COMMAND_ERROR;
1973     goto out;
1974   }
1975
1976   if (cmd->argv[1][0] == '*') {
1977     if (!conn->current_channel) {
1978       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
1979           "You are not on any channel");
1980       COMMAND_ERROR;
1981       goto out;
1982     }
1983     name = conn->current_channel->channel_name;
1984   } else {
1985     name = cmd->argv[1];
1986   }
1987
1988   /* Send USERS command to the server */
1989   buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
1990                                           ++conn->cmd_ident, 1, 
1991                                           2, name, strlen(name));
1992   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1993                           NULL, 0, NULL, NULL, buffer->data, 
1994                           buffer->len, TRUE);
1995   silc_buffer_free(buffer);
1996
1997   /* Notify application */
1998   COMMAND;
1999
2000  out:
2001   silc_client_command_free(cmd);
2002 }
2003
2004 /* Command GETKEY. Used to fetch remote client's public key. */
2005
2006 SILC_CLIENT_CMD_FUNC(getkey)
2007 {
2008   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2009   SilcClientConnection conn = cmd->conn;
2010   SilcClient client = cmd->client;
2011   SilcClientEntry client_entry = NULL;
2012   SilcServerEntry server_entry = NULL;
2013   char *nickname = NULL;
2014   SilcBuffer idp, buffer;
2015
2016   SILC_LOG_DEBUG(("Start"));
2017
2018   if (!cmd->conn) {
2019     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2020     COMMAND_ERROR;
2021     goto out;
2022   }
2023
2024   if (cmd->argc < 2) {
2025     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
2026                      "Usage: /GETKEY <nickname or server name>");
2027     COMMAND_ERROR;
2028     goto out;
2029   }
2030
2031   /* Parse the typed nickname. */
2032   if (client->internal->params->nickname_parse)
2033     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2034   else
2035     nickname = strdup(cmd->argv[1]);
2036
2037   /* Find client entry */
2038   client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2039                                         FALSE);
2040   if (!client_entry) {
2041     /* Check whether user requested server actually */
2042     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2043
2044     if (!server_entry) {
2045       /* No. what ever user wants we don't have it, so resolve it. We
2046          will first try to resolve the client, and if that fails then
2047          we'll try to resolve the server. */
2048
2049       if (!cmd->pending) {
2050         /* This will send the IDENTIFY command for nickname */
2051         silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2052         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2053                                     conn->cmd_ident,  
2054                                     silc_client_command_getkey, 
2055                                     silc_client_command_dup(cmd));
2056         cmd->pending = 1;
2057         goto out;
2058       } else {
2059         SilcClientCommandReplyContext reply = 
2060           (SilcClientCommandReplyContext)context2;
2061         SilcCommandStatus status = silc_command_get_status(reply->payload);
2062         
2063         /* If nickname was not found, then resolve the server. */
2064         if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
2065           /* This sends the IDENTIFY command to resolve the server. */
2066           silc_client_command_register(client, SILC_COMMAND_IDENTIFY, 
2067                                        NULL, NULL,
2068                                        silc_client_command_reply_identify_i, 0,
2069                                        ++conn->cmd_ident);
2070           silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2071                                    conn->cmd_ident, 1, 
2072                                    2, cmd->argv[1], cmd->argv_lens[1]);
2073           silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
2074                                       conn->cmd_ident, 
2075                                       silc_client_command_getkey, 
2076                                       silc_client_command_dup(cmd));
2077           goto out;
2078         }
2079
2080         /* If server was not found, then we've resolved both nickname and
2081            server and did not find anybody. */
2082         if (status == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2083           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2084              silc_client_command_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2085           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
2086            silc_client_command_status_message(status));
2087           COMMAND_ERROR;
2088           goto out;
2089         }
2090
2091         COMMAND_ERROR;
2092         goto out;
2093       }
2094     }
2095
2096     idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2097   } else {
2098     idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2099   }
2100
2101   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
2102                                           1, idp->data, idp->len);
2103   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
2104                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2105   silc_buffer_free(buffer);
2106   silc_buffer_free(idp);
2107
2108   /* Notify application */
2109   COMMAND;
2110
2111  out:
2112   silc_free(nickname);
2113   silc_client_command_free(cmd);
2114 }
2115
2116 /* Register a new command indicated by the `command' to the SILC client.
2117    The `name' is optional command name.  If provided the command may be
2118    searched using the silc_client_command_find by that name.  The
2119    `command_function' is the function to be called when the command is
2120    executed, and the `command_reply_function' is the function to be
2121    called after the server has sent reply back to the command. 
2122
2123    The `ident' is optional identifier for the command.  If non-zero
2124    the `command_reply_function' for the command type `command' will be
2125    called only if the command reply sent by server includes the 
2126    command identifier `ident'. Application usually does not need it
2127    and set it to zero value. */
2128
2129 bool silc_client_command_register(SilcClient client,
2130                                   SilcCommand command,
2131                                   const char *name,
2132                                   SilcCommandCb command_function,
2133                                   SilcCommandCb command_reply_function,
2134                                   SilcUInt8 max_args,
2135                                   SilcUInt16 ident)
2136 {
2137   SilcClientCommand cmd;
2138
2139   cmd = silc_calloc(1, sizeof(*cmd));
2140   cmd->cmd = command;
2141   cmd->command = command_function;
2142   cmd->reply = command_reply_function;
2143   cmd->name = name ? strdup(name) : NULL;
2144   cmd->max_args = max_args;
2145   cmd->ident = ident;
2146
2147   silc_list_add(client->internal->commands, cmd);
2148
2149   return TRUE;
2150 }
2151
2152 /* Unregister a command indicated by the `command' with command function
2153    `command_function' and command reply function `command_reply_function'.
2154    Returns TRUE if the command was found and unregistered. */
2155
2156 bool silc_client_command_unregister(SilcClient client,
2157                                     SilcCommand command,
2158                                     SilcCommandCb command_function,
2159                                     SilcCommandCb command_reply_function,
2160                                     SilcUInt16 ident)
2161 {
2162   SilcClientCommand cmd;
2163
2164   silc_list_start(client->internal->commands);
2165   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2166     if (cmd->cmd == command && cmd->command == command_function &&
2167         cmd->reply == command_reply_function && cmd->ident == ident) {
2168       silc_list_del(client->internal->commands, cmd);
2169       silc_free(cmd->name);
2170       silc_free(cmd);
2171       return TRUE;
2172     }
2173   }
2174
2175   return FALSE;
2176 }
2177
2178 /* Private range commands, specific to this implementation (and compatible
2179    with SILC Server). */
2180
2181 /* CONNECT command. Connects the server to another server. */
2182
2183 SILC_CLIENT_CMD_FUNC(connect)
2184 {
2185   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2186   SilcClientConnection conn = cmd->conn;
2187   SilcBuffer buffer;
2188   unsigned char port[4];
2189   SilcUInt32 tmp;
2190
2191   if (!cmd->conn) {
2192     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2193     COMMAND_ERROR;
2194     goto out;
2195   }
2196
2197   if (cmd->argc < 2) {
2198     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2199         "Usage: /CONNECT <server> [<port>]");
2200     COMMAND_ERROR;
2201     goto out;
2202   }
2203
2204   if (cmd->argc == 3) {
2205     tmp = atoi(cmd->argv[2]);
2206     SILC_PUT32_MSB(tmp, port);
2207   }
2208
2209   if (cmd->argc == 3)
2210     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2, 
2211                                             1, cmd->argv[1], 
2212                                             strlen(cmd->argv[1]),
2213                                             2, port, 4);
2214   else
2215     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2216                                             1, cmd->argv[1], 
2217                                             strlen(cmd->argv[1]));
2218   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2219                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2220   silc_buffer_free(buffer);
2221
2222   /* Notify application */
2223   COMMAND;
2224
2225  out:
2226   silc_client_command_free(cmd);
2227 }
2228
2229
2230 /* CLOSE command. Close server connection to the remote server */
2231  
2232 SILC_CLIENT_CMD_FUNC(close)
2233 {
2234   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2235   SilcClientConnection conn = cmd->conn;
2236   SilcBuffer buffer;
2237   unsigned char port[4];
2238   SilcUInt32 tmp;
2239
2240   if (!cmd->conn) {
2241     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2242     COMMAND_ERROR;
2243     goto out;
2244   }
2245
2246   if (cmd->argc < 2) {
2247     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
2248         "Usage: /CLOSE <server> [<port>]");
2249     COMMAND_ERROR;
2250     goto out;
2251   }
2252
2253   if (cmd->argc == 3) {
2254     tmp = atoi(cmd->argv[2]);
2255     SILC_PUT32_MSB(tmp, port);
2256   }
2257
2258   if (cmd->argc == 3)
2259     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2, 
2260                                             1, cmd->argv[1], 
2261                                             strlen(cmd->argv[1]),
2262                                             2, port, 4);
2263   else
2264     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2265                                             1, cmd->argv[1], 
2266                                             strlen(cmd->argv[1]));
2267   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2268                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2269   silc_buffer_free(buffer);
2270
2271   /* Notify application */
2272   COMMAND;
2273
2274  out:
2275   silc_client_command_free(cmd);
2276 }
2277  
2278 /* SHUTDOWN command. Shutdowns the server. */
2279
2280 SILC_CLIENT_CMD_FUNC(shutdown)
2281 {
2282   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2283
2284   if (!cmd->conn) {
2285     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2286     COMMAND_ERROR;
2287     goto out;
2288   }
2289
2290   /* Send the command */
2291   silc_client_command_send(cmd->client, cmd->conn, 
2292                            SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2293
2294   /* Notify application */
2295   COMMAND;
2296
2297  out:
2298   silc_client_command_free(cmd);
2299 }
2300
2301 /* Register all default commands provided by the client library for the
2302    application. */
2303
2304 void silc_client_commands_register(SilcClient client)
2305 {
2306   silc_list_init(client->internal->commands, struct SilcClientCommandStruct, 
2307                  next);
2308
2309   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2310   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2311   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2312   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2313   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2314   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2315   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2316   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2317   SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
2318   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2319   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2320   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2321   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2322   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2323   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2324   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
2325   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
2326   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2327   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2328   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2329   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2330   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2331   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2332
2333   SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2334   SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2335   SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2336 }
2337
2338 /* Unregister all commands. */
2339
2340 void silc_client_commands_unregister(SilcClient client)
2341 {
2342   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2343   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2344   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2345   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2346   SILC_CLIENT_CMDU(list, LIST, "LIST");
2347   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2348   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2349   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2350   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2351   SILC_CLIENT_CMDU(info, INFO, "INFO");
2352   SILC_CLIENT_CMDU(ping, PING, "PING");
2353   SILC_CLIENT_CMDU(oper, OPER, "OPER");
2354   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2355   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2356   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2357   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2358   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2359   SILC_CLIENT_CMDU(kick, KICK, "KICK");
2360   SILC_CLIENT_CMDU(ban, BAN, "BAN");
2361   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2362   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2363   SILC_CLIENT_CMDU(users, USERS, "USERS");
2364   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2365
2366   SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2367   SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2368   SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");
2369 }