The SILC Project

source navigation ]
identifier search ]
freetext search ]
file search ]

silc/silcd/command.c

  1 /*
  2 
  3   command.c
  4 
  5   Author: Pekka Riikonen <priikone@silcnet.org>
  6 
  7   Copyright (C) 1997 - 2005 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: command.c,v 1.269 2005/04/23 13:32:24 priikone Exp $ */
 20 
 21 #include "serverincludes.h"
 22 #include "server_internal.h"
 23 
 24 static int silc_server_is_registered(SilcServer server,
 25                                      SilcSocketConnection sock,
 26                                      SilcServerCommandContext cmd,
 27                                      SilcCommand command);
 28 static void
 29 silc_server_command_send_status_reply(SilcServerCommandContext cmd,
 30                                       SilcCommand command,
 31                                       SilcStatus status,
 32                                       SilcStatus error);
 33 static void
 34 silc_server_command_send_status_data(SilcServerCommandContext cmd,
 35                                      SilcCommand command,
 36                                      SilcStatus status,
 37                                      SilcStatus error,
 38                                      SilcUInt32 arg_type,
 39                                      const unsigned char *arg,
 40                                      SilcUInt32 arg_len);
 41 static bool
 42 silc_server_command_pending_error_check(SilcServerCommandContext cmd,
 43                                         SilcServerCommandReplyContext cmdr,
 44                                         SilcCommand command);
 45 SILC_TASK_CALLBACK(silc_server_command_process_timeout);
 46 
 47 /* Server command list. */
 48 SilcServerCommand silc_command_list[] =
 49 {
 50   SILC_SERVER_CMD(whois, WHOIS, SILC_CF_LAG | SILC_CF_REG),
 51   SILC_SERVER_CMD(whowas, WHOWAS, SILC_CF_LAG | SILC_CF_REG),
 52   SILC_SERVER_CMD(identify, IDENTIFY, SILC_CF_LAG | SILC_CF_REG),
 53   SILC_SERVER_CMD(nick, NICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
 54   SILC_SERVER_CMD(list, LIST, SILC_CF_LAG_STRICT | SILC_CF_REG),
 55   SILC_SERVER_CMD(topic, TOPIC, SILC_CF_LAG | SILC_CF_REG),
 56   SILC_SERVER_CMD(invite, INVITE, SILC_CF_LAG | SILC_CF_REG),
 57   SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG),
 58   SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG_STRICT | SILC_CF_REG | SILC_CF_OPER),
 59   SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG),
 60   SILC_SERVER_CMD(stats, STATS, SILC_CF_LAG | SILC_CF_REG),
 61   SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG),
 62   SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
 63   SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG_STRICT | SILC_CF_REG),
 64   SILC_SERVER_CMD(motd, MOTD, SILC_CF_LAG | SILC_CF_REG),
 65   SILC_SERVER_CMD(umode, UMODE, SILC_CF_LAG | SILC_CF_REG),
 66   SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG_STRICT | SILC_CF_REG),
 67   SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
 68   SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
 69   SILC_SERVER_CMD(ban, BAN, SILC_CF_LAG_STRICT | SILC_CF_REG),
 70   SILC_SERVER_CMD(detach, DETACH, SILC_CF_LAG_STRICT | SILC_CF_REG),
 71   SILC_SERVER_CMD(watch, WATCH, SILC_CF_LAG | SILC_CF_REG),
 72   SILC_SERVER_CMD(silcoper, SILCOPER,
 73                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
 74   SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
 75   SILC_SERVER_CMD(users, USERS, SILC_CF_LAG | SILC_CF_REG),
 76   SILC_SERVER_CMD(getkey, GETKEY, SILC_CF_LAG | SILC_CF_REG),
 77 
 78   SILC_SERVER_CMD(connect, PRIV_CONNECT,
 79                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
 80   SILC_SERVER_CMD(close, PRIV_CLOSE,
 81                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
 82   SILC_SERVER_CMD(shutdown, PRIV_SHUTDOWN, SILC_CF_LAG | SILC_CF_REG |
 83                   SILC_CF_OPER),
 84 
 85   { NULL, 0 },
 86 };
 87 
 88 /* Performs several checks to the command. It first checks whether this
 89    command was called as pending command callback. If it was then it checks
 90    whether error occurred in the command reply where the pending command
 91    callback was called.
 92 
 93    It also checks that the requested command includes correct amount
 94    of arguments. */
 95 #define SILC_SERVER_COMMAND_CHECK(command, context, min, max)                \
 96 do {                                                                         \
 97   SilcUInt32 _argc;                                                          \
 98                                                                              \
 99   if (silc_server_command_pending_error_check(cmd, context2, command)) {     \
100     SILC_LOG_DEBUG(("Error occurred in command reply, command not called")); \
101     silc_server_command_free(cmd);                                           \
102     return;                                                                  \
103   }                                                                          \
104                                                                              \
105   _argc = silc_argument_get_arg_num(cmd->args);                              \
106   if (_argc < min) {                                                         \
107     SILC_LOG_DEBUG(("Not enough parameters in command"));                    \
108     silc_server_command_send_status_reply(cmd, command,                      \
109                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS, \
110                                           0);                                \
111     silc_server_command_free(cmd);                                           \
112     return;                                                                  \
113   }                                                                          \
114   if (_argc > max) {                                                         \
115     SILC_LOG_DEBUG(("Too many parameters in command"));                      \
116     silc_server_command_send_status_reply(cmd, command,                      \
117                                           SILC_STATUS_ERR_TOO_MANY_PARAMS,   \
118                                           0);                                \
119     silc_server_command_free(cmd);                                           \
120     return;                                                                  \
121   }                                                                          \
122 } while(0)
123 
124 /* Returns TRUE if the connection is registered. Unregistered connections
125    usually cannot send commands hence the check. */
126 
127 static int silc_server_is_registered(SilcServer server,
128                                      SilcSocketConnection sock,
129                                      SilcServerCommandContext cmd,
130                                      SilcCommand command)
131 {
132   SilcIDListData idata = (SilcIDListData)sock->user_data;
133 
134   if (!idata)
135     return FALSE;
136 
137   if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
138     return TRUE;
139 
140   silc_server_command_send_status_reply(cmd, command,
141                                         SILC_STATUS_ERR_NOT_REGISTERED, 0);
142   return FALSE;
143 }
144 
145 /* Internal context to hold data when executed command with timeout. */
146 typedef struct {
147   SilcServerCommandContext ctx;
148   SilcServerCommand *cmd;
149 } *SilcServerCommandTimeout;
150 
151 /* Timeout callback to process commands with timeout for client. Client's
152    commands are always executed with timeout. */
153 
154 SILC_TASK_CALLBACK(silc_server_command_process_timeout)
155 {
156   SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
157   SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
158 
159   if (!client) {
160     SILC_LOG_DEBUG(("Client entry is invalid"));
161     silc_server_command_free(timeout->ctx);
162     silc_free(timeout);
163     return;
164   }
165 
166   /* Update access time */
167   client->last_command = time(NULL);
168 
169   if (!(timeout->cmd->flags & SILC_CF_REG)) {
170     SILC_LOG_DEBUG(("Calling %s command",
171                     silc_get_command_name(timeout->cmd->cmd)));
172     timeout->cmd->cb(timeout->ctx, NULL);
173   } else if (silc_server_is_registered(timeout->ctx->server,
174                                        timeout->ctx->sock,
175                                        timeout->ctx,
176                                        timeout->cmd->cmd)) {
177     SILC_LOG_DEBUG(("Calling %s command",
178                     silc_get_command_name(timeout->cmd->cmd)));
179     timeout->cmd->cb(timeout->ctx, NULL);
180   } else {
181     SILC_LOG_DEBUG(("Client is not registered"));
182     silc_server_command_free(timeout->ctx);
183   }
184 
185   silc_free(timeout);
186 }
187 
188 /* Processes received command packet. */
189 
190 void silc_server_command_process(SilcServer server,
191                                  SilcSocketConnection sock,
192                                  SilcPacketContext *packet)
193 {
194   SilcServerCommandContext ctx;
195   SilcServerCommand *cmd;
196   SilcCommand command;
197 
198   /* Allocate command context. This must be free'd by the
199      command routine receiving it. */
200   ctx = silc_server_command_alloc();
201   ctx->server = server;
202   ctx->sock = silc_socket_dup(sock);
203   ctx->packet = silc_packet_context_dup(packet); /* Save original packet */
204 
205   /* Parse the command payload in the packet */
206   ctx->payload = silc_command_payload_parse(packet->buffer->data,
207                                             packet->buffer->len);
208   if (!ctx->payload) {
209     SILC_LOG_ERROR(("Bad command payload, dropped (%s:%d [%s])",
210                    sock->hostname, sock->port,
211                    (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
212                     sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
213                     sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
214                     "Router")));
215     silc_packet_context_free(packet);
216     silc_socket_free(ctx->sock);
217     silc_free(ctx);
218     return;
219   }
220   ctx->args = silc_command_get_args(ctx->payload);
221 
222   /* Get the command */
223   command = silc_command_get(ctx->payload);
224   for (cmd = silc_command_list; cmd->cb; cmd++)
225     if (cmd->cmd == command)
226       break;
227 
228   if (!cmd || !cmd->cb) {
229     SILC_LOG_DEBUG(("Unknown command %d", command));
230     silc_server_command_send_status_reply(ctx, command,
231                                           SILC_STATUS_ERR_UNKNOWN_COMMAND, 0);
232     silc_server_command_free(ctx);
233     return;
234   }
235 
236   /* Execute client's commands always with timeout.  Normally they are
237      executed with zero (0) timeout but if client is sending command more
238      frequently than once in 2 seconds, then the timeout may be 0 to 2
239      seconds. */
240   if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
241     SilcClientEntry client = (SilcClientEntry)sock->user_data;
242     SilcServerCommandTimeout timeout;
243     int fast;
244 
245     if (!client) {
246       SILC_LOG_DEBUG(("Client entry is invalid"));
247       silc_server_command_free(ctx);
248       return;
249     }
250 
251     timeout = silc_calloc(1, sizeof(*timeout));
252     timeout->ctx = ctx;
253     timeout->cmd = cmd;
254 
255     if (client->last_command && (time(NULL) - client->last_command) < 2) {
256       client->fast_command++;
257       fast = FALSE;
258     } else {
259       if (client->fast_command - 2 <= 0)
260         client->fast_command = 0;
261       else
262         client->fast_command -= 2;
263       fast = TRUE;
264     }
265 
266     if (!fast && ((cmd->flags & SILC_CF_LAG_STRICT) ||
267                   (client->fast_command > 5 && cmd->flags & SILC_CF_LAG)))
268       silc_schedule_task_add(server->schedule, sock->sock,
269                              silc_server_command_process_timeout, timeout,
270                              (client->fast_command < 3 ? 0 :
271                               2 - (time(NULL) - client->last_command)),
272                              (client->fast_command < 3 ? 200000 : 0),
273                              SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
274     else
275       silc_schedule_task_add(server->schedule, sock->sock,
276                              silc_server_command_process_timeout, timeout,
277                              0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
278     return;
279   }
280 
281   /* Execute for server */
282 
283   if (!(cmd->flags & SILC_CF_REG)) {
284     SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
285     cmd->cb(ctx, NULL);
286   } else if (silc_server_is_registered(server, sock, ctx, cmd->cmd)) {
287     SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
288     cmd->cb(ctx, NULL);
289   } else {
290     SILC_LOG_DEBUG(("Server is not registered"));
291     silc_server_command_free(ctx);
292   }
293 }
294 
295 /* Allocate Command Context */
296 
297 SilcServerCommandContext silc_server_command_alloc()
298 {
299   SilcServerCommandContext ctx = silc_calloc(1, sizeof(*ctx));
300   ctx->users++;
301   return ctx;
302 }
303 
304 /* Free's the command context allocated before executing the command */
305 
306 void silc_server_command_free(SilcServerCommandContext ctx)
307 {
308   ctx->users--;
309   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
310                   ctx->users));
311   if (ctx->users < 1) {
312     if (ctx->payload)
313       silc_command_payload_free(ctx->payload);
314     if (ctx->packet)
315       silc_packet_context_free(ctx->packet);
316     if (ctx->sock)
317       silc_socket_free(ctx->sock); /* Decrease reference counter */
318     silc_free(ctx);
319   }
320 }
321 
322 /* Duplicate Command Context by adding reference counter. The context won't
323    be free'd untill it hits zero. */
324 
325 SilcServerCommandContext
326 silc_server_command_dup(SilcServerCommandContext ctx)
327 {
328   ctx->users++;
329   SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
330                   ctx->users));
331   return ctx;
332 }
333 
334 /* Timeout for pending command.  If reply to pending command never arrives
335    this is called to free resources. */
336 
337 SILC_TASK_CALLBACK(silc_server_command_pending_timeout)
338 {
339   SilcServer server = app_context;
340   SilcServerCommandPending *reply = context;
341   SilcServerCommandReplyContext cmdr;
342   SilcBuffer tmpreply;
343   int i;
344 
345   SILC_LOG_DEBUG(("Timeout pending command"));
346 
347   /* Allocate temporary and bogus command reply context */
348   cmdr = silc_calloc(1, sizeof(*cmdr));
349   cmdr->server = server;
350   cmdr->ident = reply->ident;
351 
352   /* Check for pending commands and mark to be exeucted */
353   cmdr->callbacks =
354     silc_server_command_pending_check(server, reply->reply_cmd,
355                                       reply->ident, &cmdr->callbacks_count);
356 
357   /* Create bogus command reply with an error inside */
358   tmpreply =
359     silc_command_reply_payload_encode_va(reply->reply_cmd ? reply->reply_cmd :
360                                          SILC_COMMAND_RESERVED,
361                                          SILC_STATUS_ERR_TIMEDOUT, 0,
362                                          reply->ident, 0);
363   cmdr->payload = silc_command_payload_parse(tmpreply->data, tmpreply->len);
364   silc_buffer_free(tmpreply);
365 
366   /* Call all callbacks. Same as SILC_SERVER_PENDING_EXEC macro. */
367   for (i = 0; i < cmdr->callbacks_count; i++)
368     if (cmdr->callbacks[i].callback)
369       (*cmdr->callbacks[i].callback)(cmdr->callbacks[i].context, cmdr);
370 
371   silc_server_command_pending_del(server, reply->reply_cmd, reply->ident);
372   silc_server_command_reply_free(cmdr);
373 }
374 
375 /* Add new pending command to be executed when reply to a command has been
376    received. The `reply_cmd' is the command that will call the `callback'
377    with `context' when reply has been received.  It can be SILC_COMMAND_NONE
378    to match any command with the `ident'.  If `ident' is non-zero
379    the `callback' will be executed when received reply with command
380    identifier `ident'. If there already exists pending command for the
381    specified command, ident, callback and context this function has no
382    effect. */
383 
384 bool silc_server_command_pending(SilcServer server,
385                                  SilcCommand reply_cmd,
386                                  SilcUInt16 ident,
387                                  SilcCommandCb callback,
388                                  void *context)
389 {
390   return silc_server_command_pending_timed(server, reply_cmd, ident, callback,
391                                            context, 0);
392 }
393 
394 /* Same as silc_server_command_pending with specific timeout for pending
395    commands.  If the `timeout' is zero default timeout is used. */
396 
397 bool silc_server_command_pending_timed(SilcServer server,
398                                        SilcCommand reply_cmd,
399                                        SilcUInt16 ident,
400                                        SilcCommandCb callback,
401                                        void *context,
402                                        SilcUInt16 timeout)
403 {
404   SilcServerCommandPending *reply;
405 
406   /* Check whether identical pending already exists for same command,
407      ident, callback and callback context. If it does then it would be
408      error to register it again. */
409   silc_dlist_start(server->pending_commands);
410   while ((reply = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
411     if (reply->reply_cmd == reply_cmd && reply->ident == ident &&
412         reply->callback == callback && reply->context == context)
413       return FALSE;
414   }
415 
416   reply = silc_calloc(1, sizeof(*reply));
417   reply->reply_cmd = reply_cmd;
418   reply->ident = ident;
419   reply->context = context;
420   reply->callback = callback;
421   reply->timeout =
422     silc_schedule_task_add(server->schedule, 0,
423                            silc_server_command_pending_timeout, reply,
424                            timeout ? timeout : 12, 0,
425                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
426   silc_dlist_add(server->pending_commands, reply);
427 
428   return TRUE;
429 }
430 
431 /* Deletes pending command by reply command type. */
432 
433 void silc_server_command_pending_del(SilcServer server,
434                                      SilcCommand reply_cmd,
435                                      SilcUInt16 ident)
436 {
437   SilcServerCommandPending *r;
438 
439   silc_dlist_start(server->pending_commands);
440   while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
441     if ((r->reply_cmd == reply_cmd || (r->reply_cmd == SILC_COMMAND_NONE &&
442                                        r->reply_check))
443         && r->ident == ident) {
444       silc_dlist_del(server->pending_commands, r);
445       if (r->timeout)
446         silc_schedule_task_del(server->schedule, r->timeout);
447       silc_free(r);
448     }
449   }
450 }
451 
452 /* Checks for pending commands and marks callbacks to be called from
453    the command reply function. Returns TRUE if there were pending command. */
454 
455 SilcServerCommandPendingCallbacks
456 silc_server_command_pending_check(SilcServer server,
457                                   SilcCommand command,
458                                   SilcUInt16 ident,
459                                   SilcUInt32 *callbacks_count)
460 {
461   SilcServerCommandPending *r;
462   SilcServerCommandPendingCallbacks callbacks = NULL;
463   int i = 0;
464 
465   silc_dlist_start(server->pending_commands);
466   while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
467     if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
468         && r->ident == ident) {
469       callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
470       callbacks[i].context = r->context;
471       callbacks[i].callback = r->callback;
472       r->reply_check = TRUE;
473       i++;
474     }
475   }
476 
477   *callbacks_count = i;
478   return callbacks;
479 }
480 
481 /* Sends simple status message as command reply packet */
482 
483 static void
484 silc_server_command_send_status_reply(SilcServerCommandContext cmd,
485                                       SilcCommand command,
486                                       SilcStatus status,
487                                       SilcStatus error)
488 {
489   SilcBuffer buffer;
490 
491   /* Statistics */
492   cmd->server->stat.commands_sent++;
493 
494   SILC_LOG_DEBUG(("Sending command status %d", status));
495 
496   buffer =
497     silc_command_reply_payload_encode_va(command, status, error,
498                                          silc_command_get_ident(cmd->payload),
499                                          0);
500   silc_server_packet_send(cmd->server, cmd->sock,
501                           SILC_PACKET_COMMAND_REPLY, 0,
502                           buffer->data, buffer->len, FALSE);
503   silc_buffer_free(buffer);
504 }
505 
506 /* Sends command status reply with one extra argument. The argument
507    type must be sent as argument. */
508 
509 static void
510 silc_server_command_send_status_data(SilcServerCommandContext cmd,
511                                      SilcCommand command,
512                                      SilcStatus status,
513                                      SilcStatus error,
514                                      SilcUInt32 arg_type,
515                                      const unsigned char *arg,
516                                      SilcUInt32 arg_len)
517 {
518   SilcBuffer buffer;
519 
520   /* Statistics */
521   cmd->server->stat.commands_sent++;
522 
523   SILC_LOG_DEBUG(("Sending command status %d", status));
524 
525   buffer =
526     silc_command_reply_payload_encode_va(command, status, 0,
527                                          silc_command_get_ident(cmd->payload),
528                                          1, arg_type, arg, arg_len);
529   silc_server_packet_send(cmd->server, cmd->sock,
530                           SILC_PACKET_COMMAND_REPLY, 0,
531                           buffer->data, buffer->len, FALSE);
532   silc_buffer_free(buffer);
533 }
534 
535 static void
536 silc_server_command_send_status_data2(SilcServerCommandContext cmd,
537                                       SilcCommand command,
538                                       SilcStatus status,
539                                       SilcStatus error,
540                                       SilcUInt32 arg_type1,
541                                       const unsigned char *arg1,
542                                       SilcUInt32 arg_len1,
543                                       SilcUInt32 arg_type2,
544                                       const unsigned char *arg2,
545                                       SilcUInt32 arg_len2)
546 {
547   SilcBuffer buffer;
548 
549   /* Statistics */
550   cmd->server->stat.commands_sent++;
551 
552   SILC_LOG_DEBUG(("Sending command status %d", status));
553 
554   buffer =
555     silc_command_reply_payload_encode_va(command, status, 0,
556                                          silc_command_get_ident(cmd->payload),
557                                          2, arg_type1, arg1, arg_len1,
558                                          arg_type2, arg2, arg_len2);
559   silc_server_packet_send(cmd->server, cmd->sock,
560                           SILC_PACKET_COMMAND_REPLY, 0,
561                           buffer->data, buffer->len, FALSE);
562   silc_buffer_free(buffer);
563 }
564 
565 /* This function can be called to check whether in the command reply
566    an error occurred. This function has no effect if this is called
567    when the command function was not called as pending command callback.
568    This returns TRUE if error had occurred. */
569 
570 static bool
571 silc_server_command_pending_error_check(SilcServerCommandContext cmd,
572                                         SilcServerCommandReplyContext cmdr,
573                                         SilcCommand command)
574 {
575   if (!cmd->pending || !cmdr)
576     return FALSE;
577 
578   if (!silc_command_get_status(cmdr->payload, NULL, NULL)) {
579     SilcBuffer buffer;
580 
581     /* Statistics */
582     cmd->server->stat.commands_sent++;
583 
584     /* Send the same command reply payload */
585     silc_command_set_command(cmdr->payload, silc_command_get(cmd->payload));
586     silc_command_set_ident(cmdr->payload,
587                            silc_command_get_ident(cmd->payload));
588     buffer = silc_command_payload_encode_payload(cmdr->payload);
589     silc_server_packet_send(cmd->server, cmd->sock,
590                             SILC_PACKET_COMMAND_REPLY, 0,
591                             buffer->data, buffer->len, FALSE);
592     silc_buffer_free(buffer);
593     return TRUE;
594   }
595 
596   return FALSE;
597 }
598 
599 /* Server side of command WHOIS. */
600 
601 SILC_SERVER_CMD_FUNC(whois)
602 {
603   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
604   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 256);
605   silc_server_query_command(cmd->server, SILC_COMMAND_WHOIS, cmd);
606   silc_server_command_free(cmd);
607 }
608 
609 /* Server side of command WHOWAS. */
610 
611 SILC_SERVER_CMD_FUNC(whowas)
612 {
613   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
614   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
615   silc_server_query_command(cmd->server, SILC_COMMAND_WHOWAS, cmd);
616   silc_server_command_free(cmd);
617 }
618 
619 /* Server side of command IDENTIFY. */
620 
621 SILC_SERVER_CMD_FUNC(identify)
622 {
623   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
624   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 256);
625   silc_server_query_command(cmd->server, SILC_COMMAND_IDENTIFY, cmd);
626   silc_server_command_free(cmd);
627 }
628 
629 /* Server side of command NICK. Sets nickname for user. Setting
630    nickname causes generation of a new client ID for the client. The
631    new client ID is sent to the client after changing the nickname. */
632 
633 SILC_SERVER_CMD_FUNC(nick)
634 {
635   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
636   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
637   SilcServer server = cmd->server;
638   SilcBuffer nidp, oidp = NULL;
639   SilcClientID *new_id;
640   SilcUInt32 nick_len;
641   unsigned char *nick, *nickc = NULL;
642   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
643 
644   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !client)
645     goto out;
646 
647   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
648 
649   /* Get nickname */
650   nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len);
651   if (!nick) {
652     silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
653                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
654     goto out;
655   }
656 
657   /* Truncate over long nicks */
658   if (nick_len > 128) {
659     nick[128] = '\0';
660     nick_len = 128;
661   }
662 
663   /* Check for valid nickname string.  This is cached, original is saved
664      in the client context. */
665   nickc = silc_identifier_check(nick, nick_len, SILC_STRING_UTF8, 128, NULL);
666   if (!nickc) {
667     silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
668                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
669     goto out;
670   }
671 
672   /* Check for same nickname */
673   if (strlen(client->nickname) == nick_len &&
674       !memcmp(client->nickname, nick, nick_len)) {
675     nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
676     silc_free(nickc);
677     goto send_reply;
678   }
679 
680   /* Create new Client ID */
681   if (!silc_id_create_client_id(cmd->server, cmd->server->id,
682                                 cmd->server->rng,
683                                 cmd->server->md5hash,
684                                 nickc, strlen(nickc), &new_id)) {
685     silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
686                                           SILC_STATUS_ERR_BAD_NICKNAME, 0);
687     silc_free(nickc);
688     goto out;
689   }
690 
691   /* Send notify about nickname change to our router. We send the new
692      ID and ask to replace it with the old one. If we are router the
693      packet is broadcasted. Send NICK_CHANGE notify. */
694   silc_server_send_notify_nick_change(server, SILC_PRIMARY_ROUTE(server),
695                                       SILC_BROADCAST(server), client->id,
696                                       new_id, nick);
697 
698   /* Check if anyone is watching the old nickname */
699   if (server->server_type == SILC_ROUTER)
700     silc_server_check_watcher_list(server, client, nick,
701                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
702 
703   oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
704 
705   /* Remove old cache entry */
706   silc_idcache_del_by_context(server->local_list->clients, client);
707 
708   silc_free(client->id);
709   client->id = new_id;
710 
711   silc_free(client->nickname);
712   client->nickname = strdup(nick);
713 
714   /* Update client cache */
715   silc_idcache_add(server->local_list->clients, nickc,
716                    client->id, (void *)client, 0, NULL);
717 
718   nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
719 
720   /* Send NICK_CHANGE notify to the client's channels */
721   silc_server_send_notify_on_channels(server, NULL, client,
722                                       SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
723                                       oidp->data, oidp->len,
724                                       nidp->data, nidp->len,
725                                       client->nickname,
726                                       strlen(client->nickname));
727 
728   /* Check if anyone is watching the new nickname */
729   if (server->server_type == SILC_ROUTER)
730     silc_server_check_watcher_list(server, client, NULL,
731                                    SILC_NOTIFY_TYPE_NICK_CHANGE);
732 
733  send_reply:
734   /* Send the new Client ID as reply command back to client */
735   silc_server_send_command_reply(cmd->server, cmd->sock,
736                                  SILC_COMMAND_NICK,
737                                  SILC_STATUS_OK, 0, ident, 2,
738                                  2, nidp->data, nidp->len,
739                                  3, nick, nick_len);
740   silc_buffer_free(nidp);
741   if (oidp)
742     silc_buffer_free(oidp);
743 
744  out:
745   silc_server_command_free(cmd);
746 }
747 
748 /* Sends the LIST command reply */
749 
750 static void
751 silc_server_command_list_send_reply(SilcServerCommandContext cmd,
752                                     SilcChannelEntry *lch,
753                                     SilcUInt32 lch_count,
754                                     SilcChannelEntry *gch,
755                                     SilcUInt32 gch_count)
756 {
757   int i, k;
758   SilcBuffer idp;
759   SilcChannelEntry entry;
760   SilcStatus status;
761   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
762   char *topic;
763   unsigned char usercount[4];
764   SilcUInt32 users;
765   int valid_lcount = 0, valid_rcount = 0;
766 
767   for (i = 0; i < lch_count; i++) {
768     if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET)
769       lch[i] = NULL;
770     else
771       valid_lcount++;
772   }
773   for (i = 0; i < gch_count; i++) {
774     if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET)
775       gch[i] = NULL;
776     else
777       valid_rcount++;
778   }
779 
780   if (!lch_count && !gch_count) {
781     silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
782                                           SILC_STATUS_OK, 0);
783     return;
784   }
785 
786   status = SILC_STATUS_OK;
787   if ((lch_count + gch_count) > 1)
788     status = SILC_STATUS_LIST_START;
789 
790   /* Local list */
791   for (i = 0, k = 0; i < lch_count; i++) {
792     entry = lch[i];
793     if (!entry)
794       continue;
795 
796     if (k >= 1)
797       status = SILC_STATUS_LIST_ITEM;
798     if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount)
799       status = SILC_STATUS_LIST_END;
800 
801     idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
802 
803     if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
804       topic = "*private*";
805       memset(usercount, 0, sizeof(usercount));
806     } else {
807       topic = entry->topic;
808       users = silc_hash_table_count(entry->user_list);
809       SILC_PUT32_MSB(users, usercount);
810     }
811 
812     /* Send the reply */
813     silc_server_send_command_reply(cmd->server, cmd->sock, SILC_COMMAND_LIST,
814                                    status, 0, ident, 4,
815                                    2, idp->data, idp->len,
816                                    3, entry->channel_name,
817                                    strlen(entry->channel_name),
818                                    4, topic, topic ? strlen(topic) : 0,
819                                    5, usercount, 4);
820     silc_buffer_free(idp);
821     k++;
822   }
823 
824   /* Global list */
825   for (i = 0, k = 0; i < gch_count; i++) {
826     entry = gch[i];
827     if (!entry)
828       continue;
829 
830     if (k >= 1)
831       status = SILC_STATUS_LIST_ITEM;
832     if (valid_rcount > 1 && k == valid_rcount - 1)
833       status = SILC_STATUS_LIST_END;
834 
835     idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
836 
837     if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
838       topic = "*private*";
839       memset(usercount, 0, sizeof(usercount));
840     } else {
841       topic = entry->topic;
842       users = entry->user_count;
843       SILC_PUT32_MSB(users, usercount);
844     }
845 
846     /* Send the reply */
847     silc_server_send_command_reply(cmd->server, cmd->sock, SILC_COMMAND_LIST,
848                                    status, 0, ident, 4,
849                                    2, idp->data, idp->len,
850                                    3, entry->channel_name,
851                                    strlen(entry->channel_name),
852                                    4, topic, topic ? strlen(topic) : 0,
853                                    5, usercount, 4);
854     silc_buffer_free(idp);
855     k++;
856   }
857 }
858 
859 /* Server side of LIST command. This lists the channel of the requested
860    server. Secret channels are not listed. */
861 
862 SILC_SERVER_CMD_FUNC(list)
863 {
864   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
865   SilcServer server = cmd->server;
866   SilcChannelID *channel_id = NULL;
867   unsigned char *tmp;
868   SilcUInt32 tmp_len;
869   SilcChannelEntry *lchannels = NULL, *gchannels = NULL;
870   SilcUInt32 lch_count = 0, gch_count = 0;
871 
872   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
873 
874   /* If we are normal server, send the command to router, since we
875      want to know all channels in the network. */
876   if (!cmd->pending && server->server_type != SILC_ROUTER &&
877       !server->standalone) {
878     SilcBuffer tmpbuf;
879     SilcUInt16 old_ident;
880 
881     /* Statistics */
882     cmd->server->stat.commands_sent++;
883 
884     old_ident = silc_command_get_ident(cmd->payload);
885     silc_command_set_ident(cmd->payload, ++server->cmd_ident);
886     tmpbuf = silc_command_payload_encode_payload(cmd->payload);
887     silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
888                             SILC_PACKET_COMMAND, cmd->packet->flags,
889                             tmpbuf->data, tmpbuf->len, TRUE);
890 
891     /* Reprocess this packet after received reply from router */
892     silc_server_command_pending(server, SILC_COMMAND_LIST,
893                                 silc_command_get_ident(cmd->payload),
894                                 silc_server_command_list,
895                                 silc_server_command_dup(cmd));
896     cmd->pending = TRUE;
897     silc_command_set_ident(cmd->payload, old_ident);
898     silc_buffer_free(tmpbuf);
899     goto out;
900   }
901 
902   /* Get Channel ID */
903   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
904   if (tmp) {
905     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
906     if (!channel_id) {
907       silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
908                                             SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
909       goto out;
910     }
911   }
912 
913   /* Get the channels from local list */
914   lchannels = silc_idlist_get_channels(server->local_list, channel_id,
915                                        &lch_count);
916 
917   /* Get the channels from global list */
918   gchannels = silc_idlist_get_channels(server->global_list, channel_id,
919                                        &gch_count);
920 
921   /* Send the reply */
922   silc_server_command_list_send_reply(cmd, lchannels, lch_count,
923                                       gchannels, gch_count);
924 
925   silc_free(lchannels);
926   silc_free(gchannels);
927 
928  out:
929   silc_server_command_free(cmd);
930 }
931 
932 /* Server side of TOPIC command. Sets topic for channel and/or returns
933    current topic to client. */
934 
935 SILC_SERVER_CMD_FUNC(topic)
936 {
937   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
938   SilcServer server = cmd->server;
939   SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
940   SilcChannelID *channel_id;
941   SilcChannelEntry channel;
942   SilcChannelClientEntry chl;
943   SilcBuffer idp;
944   unsigned char *tmp;
945   SilcUInt32 argc, tmp_len;
946   SilcUInt16 ident = silc_command_get_ident(cmd->payload);
947 
948   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT || !client)
949     goto out;
950 
951   SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_TOPIC, cmd, 1, 2);
952 
953   argc = silc_argument_get_arg_num(cmd->args);
954 
955   /* Get Channel ID */
956   tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
957   if (!tmp) {
958     silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
959                                           SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
960     goto out;
961   }
962   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
963   if (!channel_id) {
964     silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
965                                           SILC_STATUS_ERR_NO_CHANNEL_ID, 0);
966     goto out;
967   }
968 
969   /* Check whether the channel exists */
970   channel = silc_idlist_find_channel_by_id(server->local_list,
971                                            channel_id, NULL);
972   if (!channel) {
973     channel = silc_idlist_find_channel_by_id(server->global_list,
974                                              channel_id, NULL);
975     if (!channel) {
976       silc_server_command_send_status_data(cmd, SILC_COMMAND_TOPIC,
977                                            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
978                                            0, 2, tmp, tmp_len);
979       goto out;
980     }
981   }
982 
983   if (argc > 1) {
984     /* Get the topic */
985     tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
986     if (!tmp) {
987       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
988                                             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
989                                             0);
990       goto out;
991     }
992 
993     if (strlen(tmp) > 256) {
994       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
995                                             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
996                                             0);
997       goto out;
998     }
999 
1000     if (!silc_utf8_valid(tmp, strlen(tmp))) {
1001       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
1002                                             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS,
1003                                             0);
1004       goto out;
1005     }
1006 
1007     /* See whether the client is on channel and has rights to change topic */
1008     if (!silc_server_client_on_channel(client, channel, &chl)) {
1009       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
1010       silc_server_command_send_status_data(cmd, SILC_COMMAND_TOPIC,
1011                                            SILC_STATUS_ERR_NOT_ON_CHANNEL,
1012                                            0, 2, tmp, tmp_len);
1013       goto out;
1014     }
1015 
1016     if (channel->mode & SILC_CHANNEL_MODE_TOPIC &&
1017         !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
1018         !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
1019       tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
1020       silc_server_command_send_status_data(cmd, SILC_COMMAND_TOPIC,
1021                                            SILC_STATUS_ERR_NO_CHANNEL_PRIV,
1022                                            0, 2, tmp, tmp_len);
1023       goto out;
1024     }
1025 
1026     if (!channel->topic || strcmp(channel->topic, tmp)) {
1027       /* Set the topic for channel */
1028       silc_free(channel->topic);
1029       channel->topic = strdup(tmp);
1030 
1031       /* Send TOPIC_SET notify type to the network */
1032       silc_server_send_notify_topic_set(server, SILC_PRIMARY_ROUTE(server),
1033                                         SILC_BROADCAST(server), channel,
1034                                         client->id, SILC_ID_CLIENT,
1035                                         channel->topic);
1036 
1037       /* Send notify about topic change to all clients on the channel */
1038       idp = silc_id_payload_encode(client-&g