5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2001 Pekka Riikonen
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.
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.
22 #include "serverincludes.h"
23 #include "server_internal.h"
24 #include "command_reply.h"
26 /* All functions that call the COMMAND_CHECK_STATUS or the
27 COMMAND_CHECK_STATUS_LIST macros must have out: goto label. */
29 #define COMMAND_CHECK_STATUS \
31 SILC_LOG_DEBUG(("Start")); \
32 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
33 if (status != SILC_STATUS_OK) \
37 #define COMMAND_CHECK_STATUS_LIST \
39 SILC_LOG_DEBUG(("Start")); \
40 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
41 if (status != SILC_STATUS_OK && \
42 status != SILC_STATUS_LIST_START && \
43 status != SILC_STATUS_LIST_ITEM && \
44 status != SILC_STATUS_LIST_END) \
48 /* Server command reply list. Not all commands have reply function as
49 they are never sent by server. More maybe added later if need appears. */
50 SilcServerCommandReply silc_command_reply_list[] =
52 SILC_SERVER_CMD_REPLY(whois, WHOIS),
53 SILC_SERVER_CMD_REPLY(whowas, WHOWAS),
54 SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
55 SILC_SERVER_CMD_REPLY(info, INFO),
56 SILC_SERVER_CMD_REPLY(motd, MOTD),
57 SILC_SERVER_CMD_REPLY(join, JOIN),
58 SILC_SERVER_CMD_REPLY(users, USERS),
63 /* Process received command reply. */
65 void silc_server_command_reply_process(SilcServer server,
66 SilcSocketConnection sock,
69 SilcServerCommandReply *cmd;
70 SilcServerCommandReplyContext ctx;
71 SilcCommandPayload payload;
75 SILC_LOG_DEBUG(("Start"));
77 /* Get command reply payload from packet */
78 payload = silc_command_payload_parse(buffer);
80 /* Silently ignore bad reply packet */
81 SILC_LOG_DEBUG(("Bad command reply packet"));
85 /* Allocate command reply context. This must be free'd by the
86 command reply routine receiving it. */
87 ctx = silc_calloc(1, sizeof(*ctx));
89 ctx->sock = silc_socket_dup(sock);
90 ctx->payload = payload;
91 ctx->args = silc_command_get_args(ctx->payload);
92 ident = silc_command_get_ident(ctx->payload);
94 /* Check for pending commands and mark to be exeucted */
95 silc_server_command_pending_check(server, ctx,
96 silc_command_get(ctx->payload), ident);
98 /* Execute command reply */
99 command = silc_command_get(ctx->payload);
100 for (cmd = silc_command_reply_list; cmd->cb; cmd++)
101 if (cmd->cmd == command)
104 if (cmd == NULL || !cmd->cb) {
105 silc_server_command_reply_free(ctx);
112 /* Free command reply context and its internals. */
114 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
117 silc_command_free_payload(cmd->payload);
119 silc_socket_free(cmd->sock); /* Decrease the reference counter */
124 /* Caches the received WHOIS information. */
127 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
129 SilcServer server = cmd->server;
130 unsigned char *tmp, *id_data;
131 char *nickname, *username, *realname;
132 SilcClientID *client_id;
133 SilcClientEntry client;
134 SilcIDCacheEntry cache = NULL;
137 uint32 mode = 0, len, id_len;
139 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
140 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
141 username = silc_argument_get_arg_type(cmd->args, 4, &len);
142 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
143 if (!id_data || !nickname || !username || !realname) {
144 SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
145 nickname ? nickname : "",
146 username ? username : "",
147 realname ? realname : ""));
151 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
153 SILC_GET32_MSB(mode, tmp);
155 client_id = silc_id_payload_parse_id(id_data, id_len);
159 /* Check if we have this client cached already. */
161 client = silc_idlist_find_client_by_id(server->local_list, client_id,
164 client = silc_idlist_find_client_by_id(server->global_list,
170 /* If router did not find such Client ID in its lists then this must
171 be bogus client or some router in the net is buggy. */
172 if (server->server_type == SILC_ROUTER)
175 /* Take hostname out of nick string if it includes it. */
176 if (strchr(nickname, '@')) {
177 int len = strcspn(nickname, "@");
178 nick = silc_calloc(len + 1, sizeof(char));
179 memcpy(nick, nickname, len);
181 nick = strdup(nickname);
184 /* We don't have that client anywhere, add it. The client is added
185 to global list since server didn't have it in the lists so it must be
187 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
189 strdup(realname), client_id,
190 cmd->sock->user_data, NULL);
194 client->data.registered = TRUE;
197 /* We have the client already, update the data */
199 SILC_LOG_DEBUG(("Updating client data"));
201 /* Take hostname out of nick string if it includes it. */
202 if (strchr(nickname, '@')) {
203 int len = strcspn(nickname, "@");
204 nick = silc_calloc(len + 1, sizeof(char));
205 memcpy(nick, nickname, len);
207 nick = strdup(nickname);
210 if (client->nickname)
211 silc_free(client->nickname);
212 if (client->username)
213 silc_free(client->username);
214 if (client->userinfo)
215 silc_free(client->userinfo);
217 client->nickname = nick;
218 client->username = strdup(username);
219 client->userinfo = strdup(realname);
224 cache->data_len = strlen(nick);
225 silc_idcache_sort_by_data(global ? server->global_list->clients :
226 server->local_list->clients);
229 silc_free(client_id);
235 /* Reiceved reply for WHOIS command. We sent the whois request to our
236 primary router, if we are normal server, and thus has now received reply
237 to the command. We will figure out what client originally sent us the
238 command and will send the reply to it. If we are router we will figure
239 out who server sent us the command and send reply to that one. */
241 SILC_SERVER_CMD_REPLY_FUNC(whois)
243 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
244 SilcCommandStatus status;
246 COMMAND_CHECK_STATUS_LIST;
248 if (!silc_server_command_reply_whois_save(cmd))
251 /* Pending callbacks are not executed if this was an list entry */
252 if (status != SILC_STATUS_OK &&
253 status != SILC_STATUS_LIST_END) {
254 silc_server_command_reply_free(cmd);
259 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
260 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
261 silc_server_command_reply_free(cmd);
264 /* Caches the received WHOWAS information for a short period of time. */
267 silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
269 SilcServer server = cmd->server;
271 unsigned char *id_data;
272 char *nickname, *username, *realname;
273 SilcClientID *client_id;
274 SilcClientEntry client;
275 SilcIDCacheEntry cache = NULL;
279 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
280 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
281 username = silc_argument_get_arg_type(cmd->args, 4, &len);
282 if (!id_data || !nickname || !username)
285 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
287 client_id = silc_id_payload_parse_id(id_data, id_len);
291 /* Check if we have this client cached already. */
293 client = silc_idlist_find_client_by_id(server->local_list, client_id,
296 client = silc_idlist_find_client_by_id(server->global_list,
302 /* If router did not find such Client ID in its lists then this must
303 be bogus client or some router in the net is buggy. */
304 if (server->server_type == SILC_ROUTER)
307 /* Take hostname out of nick string if it includes it. */
308 if (strchr(nickname, '@')) {
309 int len = strcspn(nickname, "@");
310 nick = silc_calloc(len + 1, sizeof(char));
311 memcpy(nick, nickname, len);
313 nick = strdup(nickname);
316 /* We don't have that client anywhere, add it. The client is added
317 to global list since server didn't have it in the lists so it must be
319 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
320 strdup(username), strdup(realname),
321 silc_id_dup(client_id, SILC_ID_CLIENT),
322 cmd->sock->user_data, NULL);
326 client->data.registered = FALSE;
327 client = silc_idlist_find_client_by_id(server->global_list,
329 cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
331 /* We have the client already, update the data */
333 /* Take hostname out of nick string if it includes it. */
334 if (strchr(nickname, '@')) {
335 int len = strcspn(nickname, "@");
336 nick = silc_calloc(len + 1, sizeof(char));
337 memcpy(nick, nickname, len);
339 nick = strdup(nickname);
342 if (client->nickname)
343 silc_free(client->nickname);
344 if (client->username)
345 silc_free(client->username);
347 client->nickname = nick;
348 client->username = strdup(username);
352 cache->data_len = strlen(nick);
353 silc_idcache_sort_by_data(global ? server->global_list->clients :
354 server->local_list->clients);
358 silc_free(client_id);
363 /* Received reply for WHOWAS command. Cache the client information only for
364 a short period of time. */
366 SILC_SERVER_CMD_REPLY_FUNC(whowas)
368 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
369 SilcCommandStatus status;
371 COMMAND_CHECK_STATUS_LIST;
373 if (!silc_server_command_reply_whowas_save(cmd))
376 /* Pending callbacks are not executed if this was an list entry */
377 if (status != SILC_STATUS_OK &&
378 status != SILC_STATUS_LIST_END) {
379 silc_server_command_reply_free(cmd);
384 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
385 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS);
386 silc_server_command_reply_free(cmd);
389 /* Caches the received IDENTIFY information. */
392 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
394 SilcServer server = cmd->server;
396 unsigned char *id_data;
397 char *nickname, *username;
398 SilcClientID *client_id;
399 SilcClientEntry client;
400 SilcIDCacheEntry cache = NULL;
404 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
405 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
406 username = silc_argument_get_arg_type(cmd->args, 4, &len);
410 client_id = silc_id_payload_parse_id(id_data, id_len);
414 /* Check if we have this client cached already. */
416 client = silc_idlist_find_client_by_id(server->local_list, client_id,
419 client = silc_idlist_find_client_by_id(server->global_list,
425 /* If router did not find such Client ID in its lists then this must
426 be bogus client or some router in the net is buggy. */
427 if (server->server_type == SILC_ROUTER)
430 /* Take hostname out of nick string if it includes it. */
432 if (strchr(nickname, '@')) {
433 int len = strcspn(nickname, "@");
434 nick = silc_calloc(len + 1, sizeof(char));
435 memcpy(nick, nickname, len);
437 nick = strdup(nickname);
441 /* We don't have that client anywhere, add it. The client is added
442 to global list since server didn't have it in the lists so it must be
444 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
445 username ? strdup(username) : NULL, NULL,
446 client_id, cmd->sock->user_data, NULL);
447 client->data.registered = TRUE;
449 /* We have the client already, update the data */
451 SILC_LOG_DEBUG(("Updating client data"));
453 /* Take hostname out of nick string if it includes it. */
455 if (strchr(nickname, '@')) {
456 int len = strcspn(nickname, "@");
457 nick = silc_calloc(len + 1, sizeof(char));
458 memcpy(nick, nickname, len);
460 nick = strdup(nickname);
464 if (nickname && client->nickname)
465 silc_free(client->nickname);
468 client->nickname = nick;
470 if (username && client->username) {
471 silc_free(client->username);
472 client->username = strdup(username);
475 if (nickname && cache) {
477 cache->data_len = strlen(nick);
478 silc_idcache_sort_by_data(global ? server->global_list->clients :
479 server->local_list->clients);
482 silc_free(client_id);
488 /* Received reply for forwarded IDENTIFY command. We have received the
489 requested identify information now and we will cache it. After this we
490 will call the pending command so that the requestee gets the information
493 SILC_SERVER_CMD_REPLY_FUNC(identify)
495 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
496 SilcCommandStatus status;
498 COMMAND_CHECK_STATUS_LIST;
500 if (!silc_server_command_reply_identify_save(cmd))
503 /* Pending callbacks are not executed if this was an list entry */
504 if (status != SILC_STATUS_OK &&
505 status != SILC_STATUS_LIST_END) {
506 silc_server_command_reply_free(cmd);
511 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
512 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
513 silc_server_command_reply_free(cmd);
516 /* Received reply fro INFO command. Cache the server and its information */
518 SILC_SERVER_CMD_REPLY_FUNC(info)
520 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
521 SilcServer server = cmd->server;
522 SilcCommandStatus status;
523 SilcServerEntry entry;
524 SilcServerID *server_id;
526 unsigned char *tmp, *name;
528 COMMAND_CHECK_STATUS;
531 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
534 server_id = silc_id_payload_parse_id(tmp, tmp_len);
539 name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
543 entry = silc_idlist_find_server_by_id(server->local_list, server_id, NULL);
545 entry = silc_idlist_find_server_by_id(server->global_list, server_id,
548 /* Add the server to global list */
549 server_id = silc_id_dup(server_id, SILC_ID_SERVER);
550 entry = silc_idlist_add_server(server->global_list, name, 0,
551 server_id, NULL, NULL);
553 silc_free(server_id);
559 /* Get the info string */
560 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
564 entry->server_info = tmp ? strdup(tmp) : NULL;
567 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
568 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
569 silc_server_command_reply_free(cmd);
572 /* Received reply fro MOTD command. */
574 SILC_SERVER_CMD_REPLY_FUNC(motd)
576 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
577 SilcServer server = cmd->server;
578 SilcCommandStatus status;
579 SilcServerEntry entry = NULL;
580 SilcServerID *server_id;
584 COMMAND_CHECK_STATUS;
587 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
590 server_id = silc_id_payload_parse_id(tmp, tmp_len);
594 entry = silc_idlist_find_server_by_id(server->local_list, server_id, NULL);
596 entry = silc_idlist_find_server_by_id(server->global_list, server_id,
603 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
610 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
611 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
612 silc_server_command_reply_free(cmd);
618 /* Received reply for forwarded JOIN command. Router has created or joined
619 the client to the channel. We save some channel information locally
622 SILC_SERVER_CMD_REPLY_FUNC(join)
624 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
625 SilcServer server = cmd->server;
626 SilcIDCacheEntry cache = NULL;
627 SilcCommandStatus status;
629 SilcClientID *client_id = NULL;
630 SilcChannelEntry entry;
631 SilcHmac hmac = NULL;
632 uint32 id_len, len, list_count;
633 unsigned char *id_string;
634 char *channel_name, *tmp;
635 uint32 mode, created;
636 SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
638 COMMAND_CHECK_STATUS;
640 /* Get channel name */
641 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
646 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
651 tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
654 client_id = silc_id_payload_parse_id(tmp, len);
659 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
662 SILC_GET32_MSB(mode, tmp);
664 /* Get created boolean value */
665 tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
668 SILC_GET32_MSB(created, tmp);
669 if (created != 0 && created != 1)
672 /* Get channel key */
673 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
675 keyp = silc_buffer_alloc(len);
676 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
677 silc_buffer_put(keyp, tmp, len);
680 id = silc_id_payload_parse_id(id_string, id_len);
685 tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
687 if (!silc_hmac_alloc(tmp, NULL, &hmac))
691 /* Get the list count */
692 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
695 SILC_GET32_MSB(list_count, tmp);
697 /* Get Client ID list */
698 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
702 client_id_list = silc_buffer_alloc(len);
703 silc_buffer_pull_tail(client_id_list, len);
704 silc_buffer_put(client_id_list, tmp, len);
706 /* Get client mode list */
707 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
711 client_mode_list = silc_buffer_alloc(len);
712 silc_buffer_pull_tail(client_mode_list, len);
713 silc_buffer_put(client_mode_list, tmp, len);
715 /* See whether we already have the channel. */
716 entry = silc_idlist_find_channel_by_name(server->local_list,
717 channel_name, &cache);
719 /* Add new channel */
721 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
722 (created == 0 ? "existing" : "created"), channel_name,
723 silc_id_render(id, SILC_ID_CHANNEL)));
725 /* Add the channel to our local list. */
726 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
727 SILC_CHANNEL_MODE_NONE, id,
728 server->router, NULL, hmac);
734 /* The entry exists. */
736 silc_free(cache->id);
738 cache->id = entry->id;
740 /* Remove the founder auth data if the mode is not set but we have
742 if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) {
743 silc_pkcs_public_key_free(entry->founder_key);
744 if (entry->founder_passwd) {
745 silc_free(entry->founder_passwd);
746 entry->founder_passwd = NULL;
751 if (entry->hmac_name && hmac) {
752 silc_free(entry->hmac_name);
753 entry->hmac_name = strdup(hmac->hmac->name);
756 /* Get the ban list */
757 tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
760 silc_free(entry->ban_list);
761 entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list));
762 memcpy(entry->ban_list, tmp, len);
765 /* Get the invite list */
766 tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
768 if (entry->invite_list)
769 silc_free(entry->invite_list);
770 entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list));
771 memcpy(entry->invite_list, tmp, len);
775 tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
778 silc_free(entry->topic);
779 entry->topic = strdup(tmp);
782 /* If channel was not created we know there is global users on the
784 entry->global_users = (created == 0 ? TRUE : FALSE);
786 /* If channel was just created the mask must be zero */
787 if (!entry->global_users && mode) {
788 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
789 "new channel, forcing it to zero", cmd->sock->hostname));
793 /* Save channel mode */
796 /* Save channel key */
797 if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
798 silc_server_save_channel_key(server, keyp, entry);
800 silc_buffer_free(keyp);
802 /* Save the users to the channel */
803 silc_server_save_users_on_channel(server, cmd->sock, entry,
804 client_id, client_id_list,
805 client_mode_list, list_count);
808 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
809 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
811 silc_free(client_id);
812 silc_server_command_reply_free(cmd);
815 silc_buffer_free(client_id_list);
816 if (client_mode_list)
817 silc_buffer_free(client_mode_list);
820 SILC_SERVER_CMD_REPLY_FUNC(users)
822 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
823 SilcServer server = cmd->server;
824 SilcCommandStatus status;
825 SilcChannelEntry channel;
826 SilcChannelID *channel_id = NULL;
827 SilcBuffer client_id_list;
828 SilcBuffer client_mode_list;
833 COMMAND_CHECK_STATUS;
836 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
839 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
843 /* Get channel entry */
844 channel = silc_idlist_find_channel_by_id(server->local_list,
847 channel = silc_idlist_find_channel_by_id(server->global_list,
853 /* Get the list count */
854 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
857 SILC_GET32_MSB(list_count, tmp);
859 /* Get Client ID list */
860 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
864 client_id_list = silc_buffer_alloc(tmp_len);
865 silc_buffer_pull_tail(client_id_list, tmp_len);
866 silc_buffer_put(client_id_list, tmp, tmp_len);
868 /* Get client mode list */
869 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
873 client_mode_list = silc_buffer_alloc(tmp_len);
874 silc_buffer_pull_tail(client_mode_list, tmp_len);
875 silc_buffer_put(client_mode_list, tmp, tmp_len);
877 /* Save the users to the channel */
878 silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
879 client_id_list, client_mode_list,
882 silc_buffer_free(client_id_list);
883 silc_buffer_free(client_mode_list);
886 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
887 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
889 silc_free(channel_id);
890 silc_server_command_reply_free(cmd);