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