Fixed memory leak in JOIN command reply.
[silc.git] / apps / silcd / command_reply.c
1 /*
2
3   command_reply.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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "serverincludes.h"
23 #include "server_internal.h"
24 #include "command_reply.h"
25
26 /* All functions that call the COMMAND_CHECK_STATUS macros must have
27    out: and err: goto labels. */
28
29 #define COMMAND_CHECK_STATUS                                            \
30 do {                                                                    \
31   SILC_LOG_DEBUG(("Start"));                                            \
32   if (!silc_command_get_status(cmd->payload, &status, &error)) {        \
33     if (SILC_STATUS_IS_ERROR(status))                                   \
34       goto out;                                                         \
35     if (status == SILC_STATUS_LIST_END)                                 \
36       goto out;                                                         \
37     goto err;                                                           \
38   }                                                                     \
39 } while(0)
40
41 /* Server command reply list. Not all commands have reply function as
42    they are never sent by server. More maybe added later if need appears. */
43 SilcServerCommandReply silc_command_reply_list[] =
44 {
45   SILC_SERVER_CMD_REPLY(whois, WHOIS),
46   SILC_SERVER_CMD_REPLY(whowas, WHOWAS),
47   SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
48   SILC_SERVER_CMD_REPLY(info, INFO),
49   SILC_SERVER_CMD_REPLY(motd, MOTD),
50   SILC_SERVER_CMD_REPLY(join, JOIN),
51   SILC_SERVER_CMD_REPLY(stats, STATS),
52   SILC_SERVER_CMD_REPLY(users, USERS),
53   SILC_SERVER_CMD_REPLY(getkey, GETKEY),
54   SILC_SERVER_CMD_REPLY(list, LIST),
55
56   { NULL, 0 },
57 };
58
59 /* Process received command reply. */
60
61 void silc_server_command_reply_process(SilcServer server,
62                                        SilcSocketConnection sock,
63                                        SilcBuffer buffer)
64 {
65   SilcServerCommandReply *cmd;
66   SilcServerCommandReplyContext ctx;
67   SilcCommandPayload payload;
68   SilcCommand command;
69
70   SILC_LOG_DEBUG(("Start"));
71
72   /* Get command reply payload from packet */
73   payload = silc_command_payload_parse(buffer->data, buffer->len);
74   if (!payload) {
75     /* Silently ignore bad reply packet */
76     SILC_LOG_DEBUG(("Bad command reply packet"));
77     return;
78   }
79   
80   /* Allocate command reply context. This must be free'd by the
81      command reply routine receiving it. */
82   ctx = silc_calloc(1, sizeof(*ctx));
83   ctx->server = server;
84   ctx->sock = silc_socket_dup(sock);
85   ctx->payload = payload;
86   ctx->args = silc_command_get_args(ctx->payload);
87   ctx->ident = silc_command_get_ident(ctx->payload);
88   command = silc_command_get(ctx->payload);
89
90   /* Client is not allowed to send reply to all commands */
91   if (sock->type == SILC_SOCKET_TYPE_CLIENT &&
92       command != SILC_COMMAND_WHOIS) {
93     silc_server_command_reply_free(ctx);
94     return;
95   }
96       
97   /* Check for pending commands and mark to be exeucted */
98   ctx->callbacks = 
99     silc_server_command_pending_check(server, command,
100                                       ctx->ident, &ctx->callbacks_count);
101
102   /* Execute command reply */
103   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
104     if (cmd->cmd == command)
105       break;
106
107   if (cmd == NULL || !cmd->cb) {
108     silc_server_command_reply_free(ctx);
109     return;
110   }
111
112   cmd->cb(ctx, NULL);
113 }
114
115 /* Free command reply context and its internals. */
116
117 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
118 {
119   if (cmd) {
120     silc_command_payload_free(cmd->payload);
121     if (cmd->sock)
122       silc_socket_free(cmd->sock); /* Decrease the reference counter */
123     silc_free(cmd->callbacks);
124     silc_free(cmd);
125   }
126 }
127
128 static void 
129 silc_server_command_process_error(SilcServerCommandReplyContext cmd,
130                                   SilcStatus error)
131 {
132   SilcServer server = cmd->server;
133
134   /* If we received notify for invalid ID we'll remove the ID if we
135      have it cached. */
136   if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID &&
137       cmd->sock->type == SILC_SOCKET_TYPE_ROUTER) {
138     SilcClientEntry client;
139     SilcUInt32 tmp_len;
140     unsigned char *tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
141     if (tmp) {
142       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
143       if (client_id) {
144         SILC_LOG_DEBUG(("Received invalid client ID notification, deleting "
145                         "the entry from cache"));
146         client = silc_idlist_find_client_by_id(server->global_list, 
147                                                client_id, FALSE, NULL);
148         if (client) {
149           silc_server_remove_from_channels(server, NULL, client, TRUE, 
150                                            NULL, TRUE);
151           silc_idlist_del_data(client);
152           silc_idlist_del_client(server->global_list, client);
153         }
154         silc_free(client_id);
155       }
156     }
157   }
158 }
159
160 /* Caches the received WHOIS information. */
161
162 static char
163 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
164 {
165   SilcServer server = cmd->server;
166   unsigned char *tmp, *id_data, *umodes;
167   char *nickname, *username, *realname, *servername = NULL;
168   unsigned char *fingerprint;
169   SilcClientID *client_id;
170   SilcClientEntry client;
171   SilcIDCacheEntry cache = NULL;
172   char global = FALSE;
173   char *nick;
174   SilcUInt32 mode = 0, len, len2, id_len, flen;
175
176   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
177   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
178   username = silc_argument_get_arg_type(cmd->args, 4, &len);
179   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
180   if (!id_data || !nickname || !username || !realname)
181     return FALSE;
182
183   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
184   if (tmp)
185     SILC_GET32_MSB(mode, tmp);
186
187   client_id = silc_id_payload_parse_id(id_data, id_len, NULL);
188   if (!client_id)
189     return FALSE;
190
191   fingerprint = silc_argument_get_arg_type(cmd->args, 9, &flen);
192
193   /* Check if we have this client cached already. */
194
195   client = silc_idlist_find_client_by_id(server->local_list, client_id, 
196                                          FALSE, NULL);
197   if (!client) {
198     client = silc_idlist_find_client_by_id(server->global_list, client_id, 
199                                            FALSE, NULL);
200     global = TRUE;
201   }
202
203   if (!client) {
204     /* If router did not find such Client ID in its lists then this must
205        be bogus client or some router in the net is buggy. */
206     if (server->server_type != SILC_SERVER)
207       return FALSE;
208
209     /* Take hostname out of nick string if it includes it. */
210     silc_parse_userfqdn(nickname, &nick, &servername);
211
212     /* We don't have that client anywhere, add it. The client is added
213        to global list since server didn't have it in the lists so it must be 
214        global. */
215     client = silc_idlist_add_client(server->global_list, nick, 
216                                     strdup(username), 
217                                     strdup(realname), client_id, 
218                                     cmd->sock->user_data, NULL, 0);
219     if (!client) {
220       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
221       return FALSE;
222     }
223
224     client->data.status |= 
225       (SILC_IDLIST_STATUS_REGISTERED | SILC_IDLIST_STATUS_RESOLVED);
226     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
227     client->mode = mode;
228     client->servername = servername;
229   } else {
230     /* We have the client already, update the data */
231
232     SILC_LOG_DEBUG(("Updating client data"));
233
234     /* Take hostname out of nick string if it includes it. */
235     silc_parse_userfqdn(nickname, &nick, &servername);
236
237     /* Remove the old cache entry  */
238     silc_idcache_del_by_context(global ? server->global_list->clients :
239                                 server->local_list->clients, client);
240
241     silc_free(client->nickname);
242     silc_free(client->username);
243     silc_free(client->userinfo);
244     silc_free(client->servername);
245     
246     client->nickname = nick;
247     client->username = strdup(username);
248     client->userinfo = strdup(realname);
249     client->servername = servername;
250     client->mode = mode;
251     client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
252     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
253
254     /* Create new cache entry */
255     silc_idcache_add(global ? server->global_list->clients :
256                      server->local_list->clients, nick, client->id, 
257                      client, 0, NULL); 
258     silc_free(client_id);
259   }
260
261   /* Save channel list if it was sent to us */
262   if (server->server_type == SILC_SERVER) {
263     tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
264     umodes = silc_argument_get_arg_type(cmd->args, 10, &len2);
265     if (tmp && umodes) {
266       SilcBufferStruct channels_buf, umodes_buf;
267       silc_buffer_set(&channels_buf, tmp, len);
268       silc_buffer_set(&umodes_buf, umodes, len2);
269       silc_server_save_user_channels(server, cmd->sock, client, &channels_buf,
270                                      &umodes_buf);
271     } else {
272       silc_server_save_user_channels(server, cmd->sock, client, NULL, NULL);
273     }
274
275     /* If client is global and is not on any channel then add that we'll
276        expire the entry after a while. */
277     if (global) {
278       silc_idlist_find_client_by_id(server->global_list, client->id,
279                                     FALSE, &cache);
280       if (!silc_hash_table_count(client->channels))
281         cache->expire = time(NULL) + 300;
282       else
283         cache->expire = 0;
284     }
285   }
286
287   if (fingerprint && flen == sizeof(client->data.fingerprint))
288     memcpy(client->data.fingerprint, fingerprint, flen);
289
290   /* Take Requested Attributes if set. */
291   tmp = silc_argument_get_arg_type(cmd->args, 11, &len);
292   if (tmp) {
293     silc_free(client->attrs);
294     client->attrs = silc_memdup(tmp, len);
295     client->attrs_len = len;
296   }
297
298   return TRUE;
299 }
300
301 /* Handle requested attributes reply in WHOIS from client */
302
303 static char
304 silc_server_command_reply_whois_save_client(SilcServerCommandReplyContext cmd)
305 {
306   unsigned char *tmp;
307   SilcUInt32 len;
308   SilcClientEntry client = cmd->sock->user_data;
309
310   /* Take Requested Attributes if set. */
311   tmp = silc_argument_get_arg_type(cmd->args, 11, &len);
312   if (tmp && client) {
313     silc_free(client->attrs);
314     client->attrs = silc_memdup(tmp, len);
315     client->attrs_len = len;
316   }
317
318   client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
319
320   return TRUE;
321 }
322
323 /* Reiceved reply for WHOIS command. We sent the whois request to our
324    primary router, if we are normal server, and thus has now received reply
325    to the command. We will figure out what client originally sent us the
326    command and will send the reply to it.  If we are router we will figure
327    out who server sent us the command and send reply to that one. */
328
329 SILC_SERVER_CMD_REPLY_FUNC(whois)
330 {
331   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
332   SilcStatus status, error;
333
334   COMMAND_CHECK_STATUS;
335
336   if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT) {
337     if (!silc_server_command_reply_whois_save(cmd))
338       goto out;
339   } else {
340     if (!silc_server_command_reply_whois_save_client(cmd))
341       goto out;
342   }
343
344   /* Pending callbacks are not executed if this was an list entry */
345   if (status != SILC_STATUS_OK &&
346       status != SILC_STATUS_LIST_END) {
347     silc_server_command_reply_free(cmd);
348     return;
349   }
350
351  out:
352   silc_server_command_process_error(cmd, error);
353   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
354   silc_server_command_reply_free(cmd);
355   return;
356
357  err:
358   silc_server_command_process_error(cmd, error);
359   silc_server_command_reply_free(cmd);
360 }
361
362 /* Caches the received WHOWAS information for a short period of time. */
363
364 static char
365 silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
366 {
367   SilcServer server = cmd->server;
368   SilcUInt32 len, id_len;
369   unsigned char *id_data;
370   char *nickname, *username, *realname, *servername = NULL;
371   SilcClientID *client_id;
372   SilcClientEntry client;
373   SilcIDCacheEntry cache = NULL;
374   char *nick;
375   int global = FALSE;
376
377   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
378   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
379   username = silc_argument_get_arg_type(cmd->args, 4, &len);
380   if (!id_data || !nickname || !username)
381     return FALSE;
382
383   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
384
385   client_id = silc_id_payload_parse_id(id_data, id_len, NULL);
386   if (!client_id)
387     return FALSE;
388
389   /* Check if we have this client cached already. */
390
391   client = silc_idlist_find_client_by_id(server->local_list, client_id,
392                                          FALSE, &cache);
393   if (!client) {
394     client = silc_idlist_find_client_by_id(server->global_list, 
395                                            client_id, FALSE, &cache);
396     global = TRUE;
397   }
398
399   if (!client) {
400     /* If router did not find such Client ID in its lists then this must
401        be bogus client or some router in the net is buggy. */
402     if (server->server_type != SILC_SERVER)
403       return FALSE;
404
405     /* Take hostname out of nick string if it includes it. */
406     silc_parse_userfqdn(nickname, &nick, &servername);
407
408     /* We don't have that client anywhere, add it. The client is added
409        to global list since server didn't have it in the lists so it must be 
410        global. */
411     client = silc_idlist_add_client(server->global_list, nick,
412                                     strdup(username), strdup(realname), 
413                                     silc_id_dup(client_id, SILC_ID_CLIENT), 
414                                     cmd->sock->user_data, NULL,
415                                     SILC_ID_CACHE_EXPIRE_DEF);
416     if (!client) {
417       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
418       return FALSE;
419     }
420
421     client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
422     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
423     client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; 
424     client->servername = servername;
425   } else {
426     /* We have the client already, update the data */
427
428     /* Take hostname out of nick string if it includes it. */
429     silc_parse_userfqdn(nickname, &nick, &servername);
430
431     silc_free(client->nickname);
432     silc_free(client->username);
433     silc_free(client->servername);
434     
435     client->nickname = nick;
436     client->username = strdup(username);
437     client->servername = servername;
438     client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
439     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
440
441     /* Remove the old cache entry and create a new one */
442     silc_idcache_del_by_context(global ? server->global_list->clients :
443                                 server->local_list->clients, client);
444     silc_idcache_add(global ? server->global_list->clients :
445                      server->local_list->clients, nick, client->id, 
446                      client, 0, NULL);
447   }
448
449   /* If client is global and is not on any channel then add that we'll
450      expire the entry after a while. */
451   if (global) {
452     silc_idlist_find_client_by_id(server->global_list, client->id,
453                                   FALSE, &cache);
454     if (!silc_hash_table_count(client->channels))
455       cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
456     else
457       cache->expire = 0;
458   }
459
460   silc_free(client_id);
461
462   return TRUE;
463 }
464
465 /* Received reply for WHOWAS command. Cache the client information only for
466    a short period of time. */
467
468 SILC_SERVER_CMD_REPLY_FUNC(whowas)
469 {
470   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
471   SilcStatus status, error;
472
473   COMMAND_CHECK_STATUS;
474
475   if (!silc_server_command_reply_whowas_save(cmd))
476     goto out;
477
478   /* Pending callbacks are not executed if this was an list entry */
479   if (status != SILC_STATUS_OK &&
480       status != SILC_STATUS_LIST_END) {
481     silc_server_command_reply_free(cmd);
482     return;
483   }
484
485  out:
486   silc_server_command_process_error(cmd, error);
487   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
488   silc_server_command_reply_free(cmd);
489   return;
490
491  err:
492   silc_server_command_process_error(cmd, error);
493   silc_server_command_reply_free(cmd);
494 }
495
496 /* Caches the received IDENTIFY information. */
497
498 static char
499 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
500 {
501   SilcServer server = cmd->server;
502   SilcUInt32 len, id_len;
503   unsigned char *id_data;
504   char *name, *info;
505   SilcClientID *client_id = NULL;
506   SilcServerID *server_id = NULL;
507   SilcChannelID *channel_id = NULL;
508   SilcClientEntry client;
509   SilcServerEntry server_entry;
510   SilcChannelEntry channel;
511   char global = FALSE;
512   char *nick = NULL;
513   SilcIDPayload idp = NULL;
514   SilcIdType id_type;
515   int expire = 0;
516
517   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
518   if (!id_data)
519     return FALSE;
520   idp = silc_id_payload_parse(id_data, id_len);
521   if (!idp)
522     return FALSE;
523
524   name = silc_argument_get_arg_type(cmd->args, 3, &len);
525   info = silc_argument_get_arg_type(cmd->args, 4, &len);
526
527   id_type = silc_id_payload_get_type(idp);
528
529   switch (id_type) {
530   case SILC_ID_CLIENT:
531     client_id = silc_id_payload_get_id(idp);
532     if (!client_id)
533       goto error;
534
535     SILC_LOG_DEBUG(("Received client information"));
536
537     client = silc_idlist_find_client_by_id(server->local_list, 
538                                            client_id, FALSE, NULL);
539     if (!client) {
540       client = silc_idlist_find_client_by_id(server->global_list, client_id,
541                                              FALSE, NULL);
542       global = TRUE;
543     }
544     if (!client) {
545       /* If router did not find such Client ID in its lists then this must
546          be bogus client or some router in the net is buggy. */
547       if (server->server_type != SILC_SERVER)
548         goto error;
549
550       /* Take nickname */
551       if (name)
552         silc_parse_userfqdn(name, &nick, NULL);
553
554       /* We don't have that client anywhere, add it. The client is added
555          to global list since server didn't have it in the lists so it must be 
556          global. */
557       client = silc_idlist_add_client(server->global_list, nick, 
558                                       info ? strdup(info) : NULL, NULL,
559                                       client_id, cmd->sock->user_data,
560                                       NULL, time(NULL) + 300);
561       if (!client) {
562         SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
563         goto error;
564       }
565       client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
566       client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
567       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
568     } else {
569       /* We have the client already, update the data */
570       
571       SILC_LOG_DEBUG(("Updating client data"));
572       
573       /* Take nickname */
574       if (name) {
575         silc_parse_userfqdn(name, &nick, NULL);
576
577         /* Remove the old cache entry */
578         silc_idcache_del_by_context(global ? server->global_list->clients :
579                                     server->local_list->clients, client);
580
581         silc_free(client->nickname);
582         client->nickname = nick;
583       }
584       
585       if (info) {
586         silc_free(client->username);
587         client->username = strdup(info);
588       }
589
590       client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
591       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
592       
593       if (name) {
594         /* Add new cache entry */
595         silc_idcache_add(global ? server->global_list->clients :
596                          server->local_list->clients, nick, client->id, 
597                          client, expire, NULL);
598       }
599
600       /* If client is global and is not on any channel then add that we'll
601          expire the entry after a while. */
602       if (global && server->server_type == SILC_SERVER) {
603         SilcIDCacheEntry cache = NULL;
604         silc_idlist_find_client_by_id(server->global_list, client->id,
605                                       FALSE, &cache);
606         if (!silc_hash_table_count(client->channels))
607           cache->expire = time(NULL) + 300;
608         else
609           cache->expire = 0;
610       }
611
612       silc_free(client_id);
613     }
614
615     break;
616
617   case SILC_ID_SERVER:
618     if (!name)
619       goto error;
620
621     server_id = silc_id_payload_get_id(idp);
622     if (!server_id)
623       goto error;
624
625     SILC_LOG_DEBUG(("Received server information"));
626
627     server_entry = silc_idlist_find_server_by_id(server->local_list, 
628                                                  server_id, FALSE, NULL);
629     if (!server_entry)
630       server_entry = silc_idlist_find_server_by_id(server->global_list, 
631                                                    server_id, FALSE, NULL);
632     if (!server_entry) {
633       /* If router did not find such Server ID in its lists then this must
634          be bogus server or some router in the net is buggy. */
635       if (server->server_type != SILC_SERVER)
636         goto error;
637       
638       /* We don't have that server anywhere, add it. */
639       server_entry = silc_idlist_add_server(server->global_list, 
640                                             strdup(name), 0,
641                                             server_id, server->router,
642                                             SILC_PRIMARY_ROUTE(server));
643       if (!server_entry) {
644         silc_free(server_id);
645         goto error;
646       }
647       server_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
648       server_entry->data.status |= SILC_IDLIST_STATUS_RESOLVED;
649       server_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
650       server_id = NULL;
651     }
652
653     silc_free(server_id);
654     break;
655
656   case SILC_ID_CHANNEL:
657     if (!name)
658       goto error;
659
660     channel_id = silc_id_payload_get_id(idp);
661     if (!channel_id)
662       goto error;
663
664     SILC_LOG_DEBUG(("Received channel information"));
665
666     channel = silc_idlist_find_channel_by_name(server->local_list, 
667                                                name, NULL);
668     if (!channel)
669       channel = silc_idlist_find_channel_by_name(server->global_list, 
670                                                  name, NULL);
671     if (!channel) {
672       /* If router did not find such Channel ID in its lists then this must
673          be bogus channel or some router in the net is buggy. */
674       if (server->server_type != SILC_SERVER)
675         goto error;
676       
677       /* We don't have that channel anywhere, add it. */
678       channel = silc_idlist_add_channel(server->global_list, strdup(name),
679                                         SILC_CHANNEL_MODE_NONE, channel_id, 
680                                         server->router, NULL, NULL, 0);
681       if (!channel) {
682         silc_free(channel_id);
683         goto error;
684       }
685       channel_id = NULL;
686     }
687
688     silc_free(channel_id);
689     break;
690   }
691
692   silc_id_payload_free(idp);
693   return TRUE;
694
695  error:
696   silc_id_payload_free(idp);
697   return FALSE;
698 }
699
700 /* Received reply for forwarded IDENTIFY command. We have received the
701    requested identify information now and we will cache it. After this we
702    will call the pending command so that the requestee gets the information
703    after all. */
704
705 SILC_SERVER_CMD_REPLY_FUNC(identify)
706 {
707   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
708   SilcStatus status, error;
709
710   COMMAND_CHECK_STATUS;
711
712   if (!silc_server_command_reply_identify_save(cmd))
713     goto out;
714
715   /* Pending callbacks are not executed if this was an list entry */
716   if (status != SILC_STATUS_OK &&
717       status != SILC_STATUS_LIST_END) {
718     silc_server_command_reply_free(cmd);
719     return;
720   }
721
722  out:
723   silc_server_command_process_error(cmd, error);
724   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
725   silc_server_command_reply_free(cmd);
726   return;
727
728  err:
729   silc_server_command_process_error(cmd, error);
730   silc_server_command_reply_free(cmd);
731 }
732
733 /* Received reply fro INFO command. Cache the server and its information */
734
735 SILC_SERVER_CMD_REPLY_FUNC(info)
736 {
737   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
738   SilcServer server = cmd->server;
739   SilcStatus status, error;
740   SilcServerEntry entry;
741   SilcServerID *server_id;
742   SilcUInt32 tmp_len;
743   unsigned char *tmp, *name;
744
745   COMMAND_CHECK_STATUS;
746
747   /* Get Server ID */
748   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
749   if (!tmp)
750     goto out;
751   server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
752   if (!server_id)
753     goto out;
754
755   /* Get the name */
756   name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
757   if (tmp_len > 256)
758     goto out;
759
760   entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
761                                         FALSE, NULL);
762   if (!entry) {
763     entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
764                                           FALSE, NULL);
765     if (!entry) {
766       /* Add the server to global list */
767       server_id = silc_id_dup(server_id, SILC_ID_SERVER);
768       entry = silc_idlist_add_server(server->global_list, name, 0,
769                                      server_id, cmd->sock->user_data,
770                                      cmd->sock);
771       if (!entry) {
772         silc_free(server_id);
773         goto out;
774       }
775       entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
776     }
777   }
778
779   /* Get the info string */
780   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
781   if (tmp_len > 256)
782     tmp = NULL;
783
784   entry->server_info = tmp ? strdup(tmp) : NULL;
785
786  out:
787   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
788  err:
789   silc_server_command_reply_free(cmd);
790 }
791
792 /* Received reply fro MOTD command. */
793
794 SILC_SERVER_CMD_REPLY_FUNC(motd)
795 {
796   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
797   SilcServer server = cmd->server;
798   SilcStatus status, error;
799   SilcServerEntry entry = NULL;
800   SilcServerID *server_id;
801   SilcUInt32 tmp_len;
802   unsigned char *tmp;
803
804   COMMAND_CHECK_STATUS;
805
806   /* Get Server ID */
807   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
808   if (!tmp)
809     goto out;
810   server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
811   if (!server_id)
812     goto out;
813
814   entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
815                                         TRUE, NULL);
816   if (!entry) {
817     entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
818                                           TRUE, NULL);
819     if (!entry)
820       goto out;
821   }
822
823   /* Get the motd */
824   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
825   if (tmp_len > 256)
826     tmp = NULL;
827
828   entry->motd = tmp;
829
830  out:
831   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
832  err:
833   silc_server_command_reply_free(cmd);
834
835   if (entry)
836     entry->motd = NULL;
837 }
838
839 /* Received reply for forwarded JOIN command. Router has created or joined
840    the client to the channel. We save some channel information locally
841    for future use. */
842
843 SILC_SERVER_CMD_REPLY_FUNC(join)
844 {
845   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
846   SilcServer server = cmd->server;
847   SilcIDCacheEntry cache = NULL;
848   SilcStatus status, error;
849   SilcChannelID *id;
850   SilcClientID *client_id = NULL;
851   SilcChannelEntry entry;
852   SilcHmac hmac = NULL;
853   SilcUInt32 id_len, len, list_count;
854   unsigned char *id_string;
855   char *channel_name, *tmp;
856   SilcUInt32 mode, created;
857   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
858   SilcPublicKey founder_key = NULL;
859
860   COMMAND_CHECK_STATUS;
861
862   /* Get channel name */
863   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
864   if (!channel_name)
865     goto out;
866
867   /* Get channel ID */
868   id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
869   if (!id_string)
870     goto out;
871
872   /* Get client ID */
873   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
874   if (!tmp)
875     goto out;
876   client_id = silc_id_payload_parse_id(tmp, len, NULL);
877   if (!client_id)
878     goto out;
879
880   /* Get mode mask */
881   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
882   if (!tmp)
883     goto out;
884   SILC_GET32_MSB(mode, tmp);
885
886   /* Get created boolean value */
887   tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
888   if (!tmp)
889     goto out;
890   SILC_GET32_MSB(created, tmp);
891   if (created != 0 && created != 1)
892     goto out;
893
894   /* Get channel key */
895   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
896   if (tmp) {
897     keyp = silc_buffer_alloc(len);
898     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
899     silc_buffer_put(keyp, tmp, len);
900   }
901
902   /* Parse the Channel ID */
903   id = silc_id_payload_parse_id(id_string, id_len, NULL);
904   if (!id)
905     goto out;
906
907   /* Get hmac */
908   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
909   if (tmp) {
910     if (!silc_hmac_alloc(tmp, NULL, &hmac))
911       goto out;
912   }
913
914   /* Get the list count */
915   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
916   if (!tmp)
917     goto out;
918   SILC_GET32_MSB(list_count, tmp);
919
920   /* Get Client ID list */
921   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
922   if (!tmp)
923     goto out;
924
925   client_id_list = silc_buffer_alloc(len);
926   silc_buffer_pull_tail(client_id_list, len);
927   silc_buffer_put(client_id_list, tmp, len);
928
929   /* Get client mode list */
930   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
931   if (!tmp)
932     goto out;
933
934   client_mode_list = silc_buffer_alloc(len);
935   silc_buffer_pull_tail(client_mode_list, len);
936   silc_buffer_put(client_mode_list, tmp, len);
937
938   /* Get founder key */
939   tmp = silc_argument_get_arg_type(cmd->args, 15, &len);
940   if (tmp)
941     silc_pkcs_public_key_decode(tmp, len, &founder_key);
942
943   /* See whether we already have the channel. */
944   entry = silc_idlist_find_channel_by_name(server->local_list, 
945                                            channel_name, &cache);
946   if (!entry) {
947     /* Add new channel */
948
949     SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
950                     (created == 0 ? "existing" : "created"), channel_name,
951                     silc_id_render(id, SILC_ID_CHANNEL)));
952
953     /* If the channel is found from global list we must move it to the
954        local list. */
955     entry = silc_idlist_find_channel_by_name(server->global_list,
956                                              channel_name, &cache);
957     if (entry)
958       silc_idlist_del_channel(server->global_list, entry);
959
960     /* Add the channel to our local list. */
961     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
962                                     SILC_CHANNEL_MODE_NONE, id,
963                                     server->router, NULL, hmac, 0);
964     if (!entry) {
965       silc_free(id);
966       goto out;
967     }
968     hmac = NULL;
969     server->stat.my_channels++;
970     server->stat.channels++;
971   } else {
972     /* The entry exists. */
973
974     /* If ID has changed, then update it to the cache too. */
975     if (!SILC_ID_CHANNEL_COMPARE(entry->id, id))
976       silc_idlist_replace_channel_id(server->local_list, entry->id, id);
977
978     entry->disabled = FALSE;
979
980     /* Remove the founder auth data if the mode is not set but we have
981        them in the entry */
982     if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) {
983       silc_pkcs_public_key_free(entry->founder_key);
984       entry->founder_key = NULL;
985     }
986   }
987
988   if (founder_key) {
989     if (entry->founder_key)
990       silc_pkcs_public_key_free(entry->founder_key);
991     entry->founder_key = founder_key;
992     founder_key = NULL;
993   }
994
995   if (entry->hmac_name && (hmac || (!hmac && entry->hmac))) {
996     silc_free(entry->hmac_name);
997     entry->hmac_name = strdup(silc_hmac_get_name(hmac ? hmac : entry->hmac));
998   }
999
1000   /* Get the ban list */
1001   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
1002   if (tmp) {
1003     silc_free(entry->ban_list);
1004     entry->ban_list = silc_memdup(tmp, len);
1005   }
1006
1007   /* Get the invite list */
1008   tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
1009   if (tmp) {
1010     silc_free(entry->invite_list);
1011     entry->invite_list = silc_memdup(tmp, len);
1012   }
1013
1014   /* Get the topic */
1015   tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
1016   if (tmp) {
1017     silc_free(entry->topic);
1018     entry->topic = strdup(tmp);
1019   }
1020
1021   /* If channel was not created we know there is global users on the 
1022      channel. */
1023   entry->global_users = (created == 0 ? TRUE : FALSE);
1024
1025   /* If channel was just created the mask must be zero */
1026   if (!entry->global_users && mode) {
1027     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
1028                     "new channel, forcing it to zero", cmd->sock->hostname));
1029     mode = 0;
1030   }
1031
1032   /* Save channel mode */
1033   entry->mode = mode;
1034
1035   /* Save channel key */
1036   if (keyp) {
1037     if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
1038       silc_server_save_channel_key(server, keyp, entry);
1039     silc_buffer_free(keyp);
1040   }
1041
1042   /* Save the users to the channel */
1043   silc_server_save_users_on_channel(server, cmd->sock, entry, 
1044                                     client_id, client_id_list,
1045                                     client_mode_list, list_count);
1046   entry->users_resolved = TRUE;
1047
1048  out:
1049   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1050  err:
1051   if (hmac)
1052     silc_hmac_free(hmac);
1053   silc_free(client_id);
1054   silc_server_command_reply_free(cmd);
1055
1056   silc_pkcs_public_key_free(founder_key);
1057   if (client_id_list)
1058     silc_buffer_free(client_id_list);
1059   if (client_mode_list)
1060     silc_buffer_free(client_mode_list);
1061 }
1062
1063 /* Received reply to STATS command.  */
1064
1065 SILC_SERVER_CMD_REPLY_FUNC(stats)
1066 {
1067   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1068   SilcServer server = cmd->server;
1069   SilcStatus status, error;
1070   unsigned char *tmp;
1071   SilcUInt32 tmp_len;
1072   SilcBufferStruct buf;
1073
1074   COMMAND_CHECK_STATUS;
1075
1076   /* Get statistics structure */
1077   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1078   if (server->server_type == SILC_SERVER && tmp) {
1079     silc_buffer_set(&buf, tmp, tmp_len);
1080     silc_buffer_unformat(&buf,
1081                          SILC_STR_UI_INT(NULL),
1082                          SILC_STR_UI_INT(NULL),
1083                          SILC_STR_UI_INT(NULL),
1084                          SILC_STR_UI_INT(NULL),
1085                          SILC_STR_UI_INT(NULL),
1086                          SILC_STR_UI_INT(NULL),
1087                          SILC_STR_UI_INT(&server->stat.cell_clients),
1088                          SILC_STR_UI_INT(&server->stat.cell_channels),
1089                          SILC_STR_UI_INT(&server->stat.cell_servers),
1090                          SILC_STR_UI_INT(&server->stat.clients),
1091                          SILC_STR_UI_INT(&server->stat.channels),
1092                          SILC_STR_UI_INT(&server->stat.servers),
1093                          SILC_STR_UI_INT(&server->stat.routers),
1094                          SILC_STR_UI_INT(&server->stat.server_ops),
1095                          SILC_STR_UI_INT(&server->stat.router_ops),
1096                          SILC_STR_END);
1097   }
1098
1099  out:
1100   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
1101  err:
1102   silc_server_command_reply_free(cmd);
1103 }
1104
1105 SILC_SERVER_CMD_REPLY_FUNC(users)
1106 {
1107   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1108   SilcServer server = cmd->server;
1109   SilcStatus status, error;
1110   SilcChannelEntry channel;
1111   SilcChannelID *channel_id = NULL;
1112   SilcBuffer client_id_list;
1113   SilcBuffer client_mode_list;
1114   unsigned char *tmp;
1115   SilcUInt32 tmp_len;
1116   SilcUInt32 list_count;
1117
1118   COMMAND_CHECK_STATUS;
1119
1120   /* Get channel ID */
1121   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1122   if (!tmp)
1123     goto out;
1124   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1125   if (!channel_id)
1126     goto out;
1127
1128   /* Get channel entry */
1129   channel = silc_idlist_find_channel_by_id(server->local_list, 
1130                                            channel_id, NULL);
1131   if (!channel) {
1132     channel = silc_idlist_find_channel_by_id(server->global_list, 
1133                                              channel_id, NULL);
1134     if (!channel) {
1135       SilcBuffer idp;
1136
1137       if (server->server_type != SILC_SERVER)
1138         goto out;
1139
1140       idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1141       silc_server_send_command(server, SILC_PRIMARY_ROUTE(server),
1142                                SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
1143                                1, 5, idp->data, idp->len);
1144       silc_buffer_free(idp);
1145
1146       /* Register pending command callback. After we've received the channel
1147          information we will reprocess this command reply by re-calling this
1148          USERS command reply callback. */
1149       silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
1150                                   server->cmd_ident,
1151                                   silc_server_command_reply_users, cmd);
1152       return;
1153     }
1154   }
1155
1156   /* Get the list count */
1157   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1158   if (!tmp)
1159     goto out;
1160   SILC_GET32_MSB(list_count, tmp);
1161
1162   /* Get Client ID list */
1163   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1164   if (!tmp)
1165     goto out;
1166
1167   client_id_list = silc_buffer_alloc(tmp_len);
1168   silc_buffer_pull_tail(client_id_list, tmp_len);
1169   silc_buffer_put(client_id_list, tmp, tmp_len);
1170
1171   /* Get client mode list */
1172   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1173   if (!tmp)
1174     goto out;
1175
1176   client_mode_list = silc_buffer_alloc(tmp_len);
1177   silc_buffer_pull_tail(client_mode_list, tmp_len);
1178   silc_buffer_put(client_mode_list, tmp, tmp_len);
1179
1180   /* Save the users to the channel */
1181   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
1182                                     client_id_list, client_mode_list, 
1183                                     list_count);
1184
1185   channel->global_users = silc_server_channel_has_global(channel);
1186   channel->users_resolved = TRUE;
1187
1188   silc_buffer_free(client_id_list);
1189   silc_buffer_free(client_mode_list);
1190
1191  out:
1192   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1193   silc_free(channel_id);
1194  err:
1195   silc_server_command_reply_free(cmd);
1196 }
1197
1198 SILC_SERVER_CMD_REPLY_FUNC(getkey)
1199 {
1200   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1201   SilcServer server = cmd->server;
1202   SilcStatus status, error;
1203   SilcClientEntry client = NULL;
1204   SilcServerEntry server_entry = NULL;
1205   SilcClientID *client_id = NULL;
1206   SilcServerID *server_id = NULL;
1207   SilcSKEPKType type;
1208   unsigned char *tmp, *pk;
1209   SilcUInt32 len;
1210   SilcUInt16 pk_len;
1211   SilcIDPayload idp = NULL;
1212   SilcIdType id_type;
1213   SilcPublicKey public_key = NULL;
1214
1215   COMMAND_CHECK_STATUS;
1216
1217   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1218   if (!tmp)
1219     goto out;
1220   idp = silc_id_payload_parse(tmp, len);
1221   if (!idp)
1222     goto out;
1223
1224   /* Get the public key payload */
1225   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1226   if (!tmp)
1227     goto out;
1228
1229   /* Decode the public key */
1230
1231   SILC_GET16_MSB(pk_len, tmp);
1232   SILC_GET16_MSB(type, tmp + 2);
1233   pk = tmp + 4;
1234
1235   if (type != SILC_SKE_PK_TYPE_SILC)
1236     goto out;
1237
1238   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1239     goto out;
1240
1241   id_type = silc_id_payload_get_type(idp);
1242   if (id_type == SILC_ID_CLIENT) {
1243     client_id = silc_id_payload_get_id(idp);
1244
1245     client = silc_idlist_find_client_by_id(server->local_list, client_id,
1246                                            TRUE, NULL);
1247     if (!client) {
1248       client = silc_idlist_find_client_by_id(server->global_list, 
1249                                              client_id, TRUE, NULL);
1250       if (!client)
1251         goto out;
1252     }
1253
1254     client->data.public_key = public_key;
1255     public_key = NULL;
1256   } else if (id_type == SILC_ID_SERVER) {
1257     server_id = silc_id_payload_get_id(idp);
1258
1259     server_entry = silc_idlist_find_server_by_id(server->local_list, server_id,
1260                                                  TRUE, NULL);
1261     if (!server_entry) {
1262       server_entry = silc_idlist_find_server_by_id(server->global_list, 
1263                                                    server_id, TRUE, NULL);
1264       if (!server_entry)
1265         goto out;
1266     }
1267
1268     server_entry->data.public_key = public_key;
1269     public_key = NULL;
1270   } else {
1271     goto out;
1272   }
1273
1274  out:
1275   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1276   if (idp)
1277     silc_id_payload_free(idp);
1278   silc_free(client_id);
1279   silc_free(server_id);
1280   if (public_key)
1281     silc_pkcs_public_key_free(public_key);
1282  err:
1283   silc_server_command_reply_free(cmd);
1284 }
1285
1286 SILC_SERVER_CMD_REPLY_FUNC(list)
1287 {
1288   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1289   SilcServer server = cmd->server;
1290   SilcStatus status, error;
1291   SilcChannelID *channel_id = NULL;
1292   SilcChannelEntry channel;
1293   SilcIDCacheEntry cache;
1294   SilcUInt32 len;
1295   unsigned char *tmp, *name, *topic;
1296   SilcUInt32 usercount = 0;
1297   bool global_list = FALSE;
1298
1299   COMMAND_CHECK_STATUS;
1300
1301   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1302   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1303   if (!channel_id)
1304     goto out;
1305
1306   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1307   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
1308   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
1309   if (tmp)
1310     SILC_GET32_MSB(usercount, tmp);
1311
1312   /* Add the channel entry if we do not have it already */
1313   channel = silc_idlist_find_channel_by_name(server->local_list, 
1314                                              name, &cache);
1315   if (!channel) {
1316     channel = silc_idlist_find_channel_by_name(server->global_list, 
1317                                                name, &cache);
1318     global_list = TRUE;
1319   }
1320   if (!channel) {
1321     /* If router did not find such channel in its lists then this must
1322        be bogus channel or some router in the net is buggy. */
1323     if (server->server_type != SILC_SERVER)
1324       goto out;
1325     
1326     channel = silc_idlist_add_channel(server->global_list, strdup(name),
1327                                       SILC_CHANNEL_MODE_NONE, channel_id, 
1328                                       server->router, NULL, NULL, 
1329                                       time(NULL) + 60);
1330     if (!channel)
1331       goto out;
1332     channel_id = NULL;
1333   } else {
1334     /* Found, update expiry */
1335     if (global_list && server->server_type == SILC_SERVER)
1336       cache->expire = time(NULL) + 60;
1337   }
1338
1339   channel->user_count = usercount;
1340
1341   if (topic) {
1342     silc_free(channel->topic);
1343     channel->topic = strdup(topic);
1344   }
1345
1346   /* Pending callbacks are not executed if this was an list entry */
1347   if (status != SILC_STATUS_OK &&
1348       status != SILC_STATUS_LIST_END) {
1349     silc_server_command_reply_free(cmd);
1350     return;
1351   }
1352
1353   /* Now purge all old entries from the global list, otherwise we'll might
1354      have non-existent entries for long periods of time in the cache. */
1355   silc_idcache_purge(server->global_list->channels);
1356
1357  out:
1358   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
1359   silc_free(channel_id);
1360  err:
1361   silc_server_command_reply_free(cmd);
1362 }
1363
1364 SILC_SERVER_CMD_REPLY_FUNC(watch)
1365 {
1366   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1367   SilcStatus status, error;
1368
1369   COMMAND_CHECK_STATUS;
1370
1371  out:
1372   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1373  err:
1374   silc_server_command_reply_free(cmd);
1375 }