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