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