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