updates.
[silc.git] / lib / silcclient / command_reply.c
1 /*
2
3   command_reply.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 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 /*
21  * Command reply functions are "the otherside" of the command functions.
22  * Reply to a command sent by server is handled by these functions.
23  *
24  * The arguments received from server are also passed to the calling
25  * application through command_reply client operation.  The arguments are
26  * exactly same and in same order as the server sent it.  However, ID's are
27  * not sent to the application.  Instead, corresponding ID entry is sent
28  * to the application.  For example, instead of sending Client ID the 
29  * corresponding SilcClientEntry is sent to the application.  The case is
30  * same with for example Channel ID's.  This way application has all the
31  * necessary data already in hand without redundant searching.  If ID is
32  * received but ID entry does not exist, NULL is sent.
33  */
34 /* $Id$ */
35
36 #include "clientlibincludes.h"
37
38 /* Client command reply list. */
39 SilcClientCommandReply silc_command_reply_list[] =
40 {
41   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
42   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
43   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
44   SILC_CLIENT_CMD_REPLY(nick, NICK),
45   SILC_CLIENT_CMD_REPLY(list, LIST),
46   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
47   SILC_CLIENT_CMD_REPLY(invite, INVITE),
48   SILC_CLIENT_CMD_REPLY(quit, QUIT),
49   SILC_CLIENT_CMD_REPLY(kill, KILL),
50   SILC_CLIENT_CMD_REPLY(info, INFO),
51   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
52   SILC_CLIENT_CMD_REPLY(ping, PING),
53   SILC_CLIENT_CMD_REPLY(oper, OPER),
54   SILC_CLIENT_CMD_REPLY(join, JOIN),
55   SILC_CLIENT_CMD_REPLY(motd, MOTD),
56   SILC_CLIENT_CMD_REPLY(umode, UMODE),
57   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
58   SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
59   SILC_CLIENT_CMD_REPLY(kick, KICK),
60   SILC_CLIENT_CMD_REPLY(restart, RESTART),
61   SILC_CLIENT_CMD_REPLY(close, CLOSE),
62   SILC_CLIENT_CMD_REPLY(die, DIE),
63   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
64   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
65   SILC_CLIENT_CMD_REPLY(users, USERS),
66
67   { NULL, 0 },
68 };
69
70 /* Status message structure. Messages are defined below. */
71 typedef struct {
72   SilcCommandStatus status;
73   char *message;
74 } SilcCommandStatusMessage;
75
76 /* Status messages returned by the server */
77 #define STAT(x) SILC_STATUS_ERR_##x
78 const SilcCommandStatusMessage silc_command_status_messages[] = {
79
80   { STAT(NO_SUCH_NICK),      "No such nickname" },
81   { STAT(NO_SUCH_CHANNEL),   "No such channel" },
82   { STAT(NO_SUCH_SERVER),    "No such server" },
83   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
84   { STAT(NO_RECIPIENT),      "No recipient given" },
85   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
86   { STAT(WILDCARDS),         "Unknown command" },
87   { STAT(NO_CLIENT_ID),      "No Client ID given" },
88   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
89   { STAT(NO_SERVER_ID),      "No Server ID given" },
90   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
91   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
92   { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
93   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
94   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
95   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
96   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
97   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
98   { STAT(NOT_REGISTERED),    "You have not registered" },
99   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
100   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
101   { STAT(PERM_DENIED),       "Your host is not among the privileged" },
102   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
103   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
104   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
105   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
106   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
107   { STAT(UNKNOWN_MODE),    "Unknown mode" },
108   { STAT(NOT_YOU),         "Cannot change mode for other users" },
109   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
110   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
111   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
112   { STAT(BAD_NICKNAME),    "Bad nickname" },
113   { STAT(BAD_CHANNEL),     "Bad channel name" },
114   { STAT(AUTH_FAILED),     "Authentication failed" },
115
116   { 0, NULL }
117 };
118
119 /* Command reply operation that is called at the end of all command replys. 
120    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
121 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
122 #define ARGS cmd->client, cmd->sock->user_data, \
123              cmd->payload, TRUE, silc_command_get(cmd->payload), status
124
125 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
126 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
127   cmd->sock->user_data, cmd->payload, FALSE, \
128   silc_command_get(cmd->payload), status)
129
130 /* Process received command reply. */
131
132 void silc_client_command_reply_process(SilcClient client,
133                                        SilcSocketConnection sock,
134                                        SilcPacketContext *packet)
135 {
136   SilcBuffer buffer = packet->buffer;
137   SilcClientCommandReply *cmd;
138   SilcClientCommandReplyContext ctx;
139   SilcCommandPayload payload;
140   SilcCommand command;
141   unsigned short ident;
142
143   /* Get command reply payload from packet */
144   payload = silc_command_payload_parse(buffer);
145   if (!payload) {
146     /* Silently ignore bad reply packet */
147     SILC_LOG_DEBUG(("Bad command reply packet"));
148     return;
149   }
150   
151   /* Allocate command reply context. This must be free'd by the
152      command reply routine receiving it. */
153   ctx = silc_calloc(1, sizeof(*ctx));
154   ctx->client = client;
155   ctx->sock = sock;
156   ctx->payload = payload;
157   ctx->args = silc_command_get_args(ctx->payload);
158   ctx->packet = packet;
159   ident = silc_command_get_ident(ctx->payload);
160       
161   /* Check for pending commands and mark to be exeucted */
162   silc_client_command_pending_check(sock->user_data, ctx, 
163                                     silc_command_get(ctx->payload), ident);
164
165   /* Execute command reply */
166   command = silc_command_get(ctx->payload);
167   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
168     if (cmd->cmd == command)
169       break;
170
171   if (cmd == NULL || !cmd->cb) {
172     silc_free(ctx);
173     return;
174   }
175
176   cmd->cb(ctx);
177 }
178
179 /* Returns status message string */
180
181 static char *
182 silc_client_command_status_message(SilcCommandStatus status)
183 {
184   int i;
185
186   for (i = 0; silc_command_status_messages[i].message; i++) {
187     if (silc_command_status_messages[i].status == status)
188       break;
189   }
190
191   if (silc_command_status_messages[i].message == NULL)
192     return NULL;
193
194   return silc_command_status_messages[i].message;
195 }
196
197 /* Free command reply context and its internals. */
198
199 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
200 {
201   if (cmd) {
202     silc_command_free_payload(cmd->payload);
203     silc_free(cmd);
204   }
205 }
206
207 static void 
208 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
209                                       SilcCommandStatus status)
210 {
211   char buf[256];
212   int argc, len;
213   unsigned char *id_data;
214   char *nickname = NULL, *username = NULL;
215   char *realname = NULL;
216   SilcClientID *client_id;
217   SilcIDCacheEntry id_cache = NULL;
218   SilcClientEntry client_entry = NULL;
219   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
220   
221   memset(buf, 0, sizeof(buf));
222   
223   argc = silc_argument_get_arg_num(cmd->args);
224
225   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
226   if (!id_data) {
227     COMMAND_REPLY_ERROR;
228     return;
229   }
230   
231   client_id = silc_id_payload_parse_id(id_data, len);
232   if (!client_id) {
233     COMMAND_REPLY_ERROR;
234     return;
235   }
236   
237   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
238   if (nickname) {
239     strncat(buf, nickname, len);
240     strncat(buf, " is ", 4);
241   }
242   
243   username = silc_argument_get_arg_type(cmd->args, 4, &len);
244   if (username) {
245     strncat(buf, username, len);
246   }
247   
248   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
249   if (realname) {
250     strncat(buf, " (", 2);
251     strncat(buf, realname, len);
252     strncat(buf, ")", 1);
253   }
254   
255   /* Check if we have this client cached already. */
256   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
257                                    SILC_ID_CLIENT, &id_cache)) {
258     client_entry = silc_calloc(1, sizeof(*client_entry));
259     client_entry->id = client_id;
260     silc_parse_nickname(nickname, &client_entry->nickname, 
261                         &client_entry->server, &client_entry->num);
262     client_entry->username = strdup(username);
263     if (realname)
264       client_entry->realname = strdup(realname);
265     
266     /* Add client to cache */
267     silc_idcache_add(conn->client_cache, client_entry->nickname,
268                      SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
269   } else {
270     client_entry = (SilcClientEntry)id_cache->context;
271     if (client_entry->nickname)
272       silc_free(client_entry->nickname);
273     if (client_entry->server)
274       silc_free(client_entry->server);
275     if (client_entry->username)
276       silc_free(client_entry->username);
277     if (client_entry->realname)
278       silc_free(client_entry->realname);
279
280     silc_parse_nickname(nickname, &client_entry->nickname, 
281                         &client_entry->server, &client_entry->num);
282     client_entry->username = strdup(username);
283     if (realname)
284       client_entry->realname = strdup(realname);
285
286     id_cache->data = client_entry->nickname;
287     silc_idcache_sort_by_data(conn->client_cache);
288
289     silc_free(client_id);
290   }
291
292   if (!cmd->callback)
293     cmd->client->ops->say(cmd->client, conn, "%s", buf);
294
295   /* Notify application */
296   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
297                  NULL, NULL));
298 }
299
300 /* Received reply for WHOIS command. This maybe called several times
301    for one WHOIS command as server may reply with list of results. */
302
303 SILC_CLIENT_CMD_REPLY_FUNC(whois)
304 {
305   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
306   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
307   SilcCommandStatus status;
308   unsigned char *tmp;
309
310   SILC_LOG_DEBUG(("Start"));
311
312   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
313   SILC_GET16_MSB(status, tmp);
314   if (status != SILC_STATUS_OK && 
315       status != SILC_STATUS_LIST_START &&
316       status != SILC_STATUS_LIST_ITEM &&
317       status != SILC_STATUS_LIST_END) {
318     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
319       /* Take nickname which may be provided */
320       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
321       if (tmp)
322         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
323                  silc_client_command_status_message(status));
324       else
325         cmd->client->ops->say(cmd->client, conn, "%s",
326                  silc_client_command_status_message(status));
327       COMMAND_REPLY_ERROR;
328       goto out;
329     } else {
330       cmd->client->ops->say(cmd->client, conn,
331                "%s", silc_client_command_status_message(status));
332       COMMAND_REPLY_ERROR;
333       goto out;
334     }
335   }
336
337   /* Display one whois reply */
338   if (status == SILC_STATUS_OK) {
339     silc_client_command_reply_whois_print(cmd, status);
340   }
341
342   /* XXX list should not be displayed untill all items has been received. */
343   if (status == SILC_STATUS_LIST_START) {
344     silc_client_command_reply_whois_print(cmd, status);
345   }
346
347   if (status == SILC_STATUS_LIST_ITEM) {
348     silc_client_command_reply_whois_print(cmd, status);
349   }
350
351   if (status == SILC_STATUS_LIST_END) {
352     silc_client_command_reply_whois_print(cmd, status);
353   }
354
355   /* Execute any pending command callbacks */
356   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
357
358  out:
359   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
360   silc_client_command_reply_free(cmd);
361 }
362
363 /* Received reply for WHOWAS command. */
364
365 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
366 {
367
368 }
369
370 /* Received reply for IDENTIFY command. This maybe called several times
371    for one IDENTIFY command as server may reply with list of results. 
372    This is totally silent and does not print anything on screen. */
373
374 SILC_CLIENT_CMD_REPLY_FUNC(identify)
375 {
376   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
377   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
378   SilcClientEntry client_entry;
379   SilcIDCacheEntry id_cache = NULL;
380   SilcCommandStatus status;
381   unsigned char *tmp;
382
383   SILC_LOG_DEBUG(("Start"));
384
385   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
386   SILC_GET16_MSB(status, tmp);
387   if (status != SILC_STATUS_OK) {
388     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
389       /* Take nickname which may be provided */
390       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
391       if (tmp)
392         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
393                  silc_client_command_status_message(status));
394       else
395         cmd->client->ops->say(cmd->client, conn, "%s",
396                  silc_client_command_status_message(status));
397       COMMAND_REPLY_ERROR;
398       goto out;
399     } else {
400       cmd->client->ops->say(cmd->client, conn,
401                "%s", silc_client_command_status_message(status));
402       COMMAND_REPLY_ERROR;
403       goto out;
404     }
405   }
406
407   /* Display one whois reply */
408   if (status == SILC_STATUS_OK) {
409     unsigned int len;
410     unsigned char *id_data;
411     char *nickname;
412     char *username;
413     SilcClientID *client_id;
414
415     id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
416     if (!id_data)
417       goto out;
418     client_id = silc_id_payload_parse_id(id_data, len);
419     if (!client_id)
420       goto out;
421
422     nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
423     username = silc_argument_get_arg_type(cmd->args, 4, NULL);
424
425     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
426                                      SILC_ID_CLIENT, &id_cache)) {
427       client_entry = silc_calloc(1, sizeof(*client_entry));
428       client_entry->id = client_id;
429       silc_parse_nickname(nickname, &client_entry->nickname, 
430                           &client_entry->server, &client_entry->num);
431       if (username)
432         client_entry->username = strdup(username);
433     
434       /* Add client to cache */
435       silc_idcache_add(conn->client_cache, client_entry->nickname,
436                        SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
437     } else {
438       client_entry = (SilcClientEntry)id_cache->context;
439       if (client_entry->nickname)
440         silc_free(client_entry->nickname);
441       if (client_entry->server)
442         silc_free(client_entry->server);
443       if (username && client_entry->username)
444         silc_free(client_entry->username);
445
446       silc_parse_nickname(nickname, &client_entry->nickname, 
447                           &client_entry->server, &client_entry->num);
448
449       if (username)
450         client_entry->username = strdup(username);
451
452       id_cache->data = client_entry->nickname;
453       silc_idcache_sort_by_data(conn->client_cache);
454     
455       silc_free(client_id);
456     }
457   }
458
459   if (status == SILC_STATUS_LIST_START) {
460
461   }
462
463   if (status == SILC_STATUS_LIST_END) {
464
465   }
466
467   /* Execute any pending command callbacks */
468   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
469
470  out:
471   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
472   silc_client_command_reply_free(cmd);
473 }
474
475 /* Received reply for command NICK. If everything went without errors
476    we just received our new Client ID. */
477
478 SILC_CLIENT_CMD_REPLY_FUNC(nick)
479 {
480   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
481   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
482   SilcCommandStatus status;
483   SilcIDPayload idp;
484   unsigned char *tmp;
485   unsigned int argc, len;
486
487   SILC_LOG_DEBUG(("Start"));
488
489   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
490   if (status != SILC_STATUS_OK) {
491     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
492              silc_client_command_status_message(status));
493     COMMAND_REPLY_ERROR;
494     goto out;
495   }
496
497   argc = silc_argument_get_arg_num(cmd->args);
498   if (argc < 2 || argc > 2) {
499     cmd->client->ops->say(cmd->client, conn, 
500                           "Cannot set nickname: bad reply to command");
501     COMMAND_REPLY_ERROR;
502     goto out;
503   }
504
505   /* Take received Client ID */
506   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
507   idp = silc_id_payload_parse_data(tmp, len);
508   if (!idp) {
509     COMMAND_REPLY_ERROR;
510     goto out;
511   }
512   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
513     
514   /* Notify application */
515   COMMAND_REPLY((ARGS, conn->local_entry));
516
517   /* Execute any pending command callbacks */
518   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
519
520  out:
521   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
522   silc_client_command_reply_free(cmd);
523 }
524
525 SILC_CLIENT_CMD_REPLY_FUNC(list)
526 {
527 }
528
529 /* Received reply to topic command. */
530
531 SILC_CLIENT_CMD_REPLY_FUNC(topic)
532 {
533   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
534   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
535   SilcCommandStatus status;
536   SilcChannelEntry channel;
537   SilcChannelID *channel_id = NULL;
538   SilcIDCacheEntry id_cache = NULL;
539   unsigned char *tmp;
540   char *topic;
541   unsigned int argc, len;
542
543   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
544   if (status != SILC_STATUS_OK) {
545     cmd->client->ops->say(cmd->client, conn,
546              "%s", silc_client_command_status_message(status));
547     COMMAND_REPLY_ERROR;
548     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
549     silc_client_command_reply_free(cmd);
550     return;
551   }
552
553   argc = silc_argument_get_arg_num(cmd->args);
554   if (argc < 1 || argc > 3) {
555     COMMAND_REPLY_ERROR;
556     goto out;
557   }
558
559   /* Take Channel ID */
560   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
561   if (!tmp)
562     goto out;
563
564   /* Take topic */
565   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
566   if (!topic)
567     goto out;
568
569   channel_id = silc_id_payload_parse_id(tmp, len);
570   if (!channel_id)
571     goto out;
572
573   /* Get the channel name */
574   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
575                                    SILC_ID_CHANNEL, &id_cache)) {
576     silc_free(channel_id);
577     COMMAND_REPLY_ERROR;
578     goto out;
579   }
580   
581   channel = (SilcChannelEntry)id_cache->context;
582
583   cmd->client->ops->say(cmd->client, conn, 
584                         "Topic on channel %s: %s", channel->channel_name,
585                         topic);
586
587   /* Notify application */
588   COMMAND_REPLY((ARGS, channel, topic));
589
590   /* Execute any pending command callbacks */
591   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
592
593  out:
594   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
595   silc_client_command_reply_free(cmd);
596 }
597
598 /* Received reply to invite command. */
599
600 SILC_CLIENT_CMD_REPLY_FUNC(invite)
601 {
602   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
603   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
604   SilcCommandStatus status;
605   unsigned char *tmp;
606
607   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
608   SILC_GET16_MSB(status, tmp);
609   if (status != SILC_STATUS_OK) {
610     cmd->client->ops->say(cmd->client, conn,
611              "%s", silc_client_command_status_message(status));
612     COMMAND_REPLY_ERROR;
613     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
614     silc_client_command_reply_free(cmd);
615     return;
616   }
617
618   /* Notify application */
619   COMMAND_REPLY((ARGS));
620
621   /* Execute any pending command callbacks */
622   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
623
624   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
625   silc_client_command_reply_free(cmd);
626 }
627  
628 SILC_CLIENT_CMD_REPLY_FUNC(quit)
629 {
630 }
631
632 SILC_CLIENT_CMD_REPLY_FUNC(kill)
633 {
634 }
635
636 /* Received reply to INFO command. We receive the server ID and some
637    information about the server user requested. */
638
639 SILC_CLIENT_CMD_REPLY_FUNC(info)
640 {
641   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
642   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
643   SilcClient client = cmd->client;
644   SilcCommandStatus status;
645   unsigned char *tmp;
646
647   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
648   SILC_GET16_MSB(status, tmp);
649   if (status != SILC_STATUS_OK) {
650     cmd->client->ops->say(cmd->client, conn,
651              "%s", silc_client_command_status_message(status));
652     COMMAND_REPLY_ERROR;
653     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
654     silc_client_command_reply_free(cmd);
655     return;
656   }
657
658   /* Get server ID */
659   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
660   if (!tmp)
661     goto out;
662
663   /* XXX save server id */
664
665   /* Get server info */
666   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
667   if (!tmp)
668     goto out;
669
670   client->ops->say(cmd->client, conn, "Info: %s", tmp);
671
672   /* Notify application */
673   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
674
675   /* Execute any pending command callbacks */
676   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
677
678  out:
679   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
680   silc_client_command_reply_free(cmd);
681 }
682
683 SILC_CLIENT_CMD_REPLY_FUNC(connect)
684 {
685 }
686
687 /* Received reply to PING command. The reply time is shown to user. */
688
689 SILC_CLIENT_CMD_REPLY_FUNC(ping)
690 {
691   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
692   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
693   SilcCommandStatus status;
694   void *id;
695   int i;
696   time_t diff, curtime;
697
698   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
699   if (status != SILC_STATUS_OK) {
700     cmd->client->ops->say(cmd->client, conn,
701              "%s", silc_client_command_status_message(status));
702     COMMAND_REPLY_ERROR;
703     goto out;
704   }
705
706   curtime = time(NULL);
707   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
708                       cmd->packet->src_id_type);
709   if (!id) {
710     COMMAND_REPLY_ERROR;
711     goto out;
712   }
713
714   for (i = 0; i < conn->ping_count; i++) {
715     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
716       diff = curtime - conn->ping[i].start_time;
717       cmd->client->ops->say(cmd->client, conn, 
718                             "Ping reply from %s: %d second%s", 
719                             conn->ping[i].dest_name, diff, 
720                             diff == 1 ? "" : "s");
721       
722       conn->ping[i].start_time = 0;
723       silc_free(conn->ping[i].dest_id);
724       conn->ping[i].dest_id = NULL;
725       silc_free(conn->ping[i].dest_name);
726       conn->ping[i].dest_name = NULL;
727       break;
728     }
729   }
730
731   silc_free(id);
732
733   /* Notify application */
734   COMMAND_REPLY((ARGS));
735
736   /* Execute any pending command callbacks */
737   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
738
739  out:
740   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
741   silc_client_command_reply_free(cmd);
742 }
743
744 SILC_CLIENT_CMD_REPLY_FUNC(oper)
745 {
746 }
747
748 /* Received reply for JOIN command. */
749
750 SILC_CLIENT_CMD_REPLY_FUNC(join)
751 {
752   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
753   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
754   SilcClient client = cmd->client;
755   SilcCommandStatus status;
756   SilcIDPayload idp = NULL;
757   unsigned int argc, mode, len;
758   char *topic, *tmp, *channel_name = NULL;
759   SilcBuffer keyp;
760
761   SILC_LOG_DEBUG(("Start"));
762
763   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
764   if (status != SILC_STATUS_OK) {
765     cmd->client->ops->say(cmd->client, conn,
766              "%s", silc_client_command_status_message(status));
767     COMMAND_REPLY_ERROR;
768     goto out;
769   }
770
771   argc = silc_argument_get_arg_num(cmd->args);
772   if (argc < 3 || argc > 9) {
773     cmd->client->ops->say(cmd->client, conn,
774              "Cannot join channel: Bad reply packet");
775     COMMAND_REPLY_ERROR;
776     goto out;
777   }
778
779   /* Get channel name */
780   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
781   if (!tmp) {
782     cmd->client->ops->say(cmd->client, conn, 
783                           "Cannot join channel: Bad reply packet");
784     COMMAND_REPLY_ERROR;
785     goto out;
786   }
787   channel_name = strdup(tmp);
788
789   /* Get Channel ID */
790   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
791   if (!tmp) {
792     cmd->client->ops->say(cmd->client, conn, 
793                           "Cannot join channel: Bad reply packet");
794     COMMAND_REPLY_ERROR;
795     silc_free(channel_name);
796     goto out;
797   }
798   idp = silc_id_payload_parse_data(tmp, len);
799   if (!idp) {
800     COMMAND_REPLY_ERROR;
801     silc_free(channel_name);
802     goto out;
803   }
804
805   /* Get channel mode */
806   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
807   if (tmp)
808     SILC_GET32_MSB(mode, tmp);
809   else
810     mode = 0;
811
812   /* Get channel key */
813   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
814   if (!tmp) {
815     silc_id_payload_free(idp);
816     silc_free(channel_name);
817     goto out;
818   }
819   keyp = silc_buffer_alloc(len);
820   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
821   silc_buffer_put(keyp, tmp, len);
822
823   /* Get topic */
824   topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
825
826   /* Save received Channel ID */
827   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
828                              mode, idp);
829   silc_id_payload_free(idp);
830
831   /* Save channel key */
832   silc_client_save_channel_key(conn, keyp, conn->current_channel);
833   silc_buffer_free(keyp);
834
835   if (topic)
836     client->ops->say(cmd->client, conn, 
837                      "Topic for %s: %s", channel_name, topic);
838
839   /* Notify application */
840   COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
841                  NULL, NULL, topic));
842
843   /* Execute any pending command callbacks */
844   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
845
846  out:
847   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
848   silc_client_command_reply_free(cmd);
849 }
850
851 /* Received reply for MOTD command */
852
853 SILC_CLIENT_CMD_REPLY_FUNC(motd)
854 {
855   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
856   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
857   SilcCommandStatus status;
858   unsigned int argc, i;
859   unsigned char *tmp;
860   char *motd = NULL, *cp, line[256];
861
862   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
863   SILC_GET16_MSB(status, tmp);
864   if (status != SILC_STATUS_OK) {
865     cmd->client->ops->say(cmd->client, conn,
866              "%s", silc_client_command_status_message(status));
867     COMMAND_REPLY_ERROR;
868     return;
869   }
870
871   argc = silc_argument_get_arg_num(cmd->args);
872   if (argc > 2) {
873     COMMAND_REPLY_ERROR;
874     goto out;
875   }
876
877   if (argc == 2) {
878     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
879     if (!motd) {
880       COMMAND_REPLY_ERROR;
881       goto out;
882     }
883
884     i = 0;
885     cp = motd;
886     while(cp[i] != 0) {
887       if (cp[i++] == '\n') {
888         memset(line, 0, sizeof(line));
889         strncat(line, cp, i - 1);
890         cp += i;
891         
892         if (i == 2)
893           line[0] = ' ';
894         
895         cmd->client->ops->say(cmd->client, conn, "%s", line);
896         
897         if (!strlen(cp))
898           break;
899         i = 0;
900       }
901     }
902   }
903
904   /* Notify application */
905   COMMAND_REPLY((ARGS, motd));
906
907   /* Execute any pending command callbacks */
908   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
909
910  out:
911   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
912   silc_client_command_reply_free(cmd);
913 }
914
915 SILC_CLIENT_CMD_REPLY_FUNC(umode)
916 {
917 }
918
919 /* Received reply for CMODE command. */
920
921 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
922 {
923   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
924   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
925   SilcCommandStatus status;
926   unsigned char *tmp;
927
928   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
929   if (status != SILC_STATUS_OK) {
930     cmd->client->ops->say(cmd->client, conn,
931              "%s", silc_client_command_status_message(status));
932     COMMAND_REPLY_ERROR;
933     goto out;
934   }
935
936   /* Get channel mode */
937   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
938   if (!tmp) {
939     COMMAND_REPLY_ERROR;
940     goto out;
941   }
942
943   /* Notify application */
944   COMMAND_REPLY((ARGS, tmp));
945
946   /* Execute any pending command callbacks */
947   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
948
949  out:
950   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
951   silc_client_command_reply_free(cmd);
952 }
953
954 /* Received reply for CUMODE command */
955
956 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
957 {
958   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
959   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
960   SilcCommandStatus status;
961   SilcIDCacheEntry id_cache = NULL;
962   SilcClientID *client_id;
963   unsigned char *tmp, *id;
964   unsigned int len;
965   
966   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
967   if (status != SILC_STATUS_OK) {
968     cmd->client->ops->say(cmd->client, conn,
969              "%s", silc_client_command_status_message(status));
970     COMMAND_REPLY_ERROR;
971     goto out;
972   }
973   
974   /* Get channel mode */
975   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
976   if (!tmp) {
977     COMMAND_REPLY_ERROR;
978     goto out;
979   }
980
981   /* Get Client ID */
982   id = silc_argument_get_arg_type(cmd->args, 3, &len);
983   if (!id) {
984     COMMAND_REPLY_ERROR;
985     goto out;
986   }
987   client_id = silc_id_payload_parse_id(id, len);
988   if (!client_id) {
989     COMMAND_REPLY_ERROR;
990     goto out;
991   }
992   
993   /* Get client entry */
994   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
995                                    SILC_ID_CLIENT, &id_cache)) {
996     COMMAND_REPLY_ERROR;
997     goto out;
998   }
999
1000   /* Notify application */
1001   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1002   silc_free(client_id);
1003   
1004   /* Execute any pending command callbacks */
1005   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1006
1007  out:
1008   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1009   silc_client_command_reply_free(cmd);
1010 }
1011
1012 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1013 {
1014 }
1015
1016 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1017 {
1018 }
1019  
1020 SILC_CLIENT_CMD_REPLY_FUNC(close)
1021 {
1022 }
1023  
1024 SILC_CLIENT_CMD_REPLY_FUNC(die)
1025 {
1026 }
1027  
1028 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1029 {
1030 }
1031
1032 /* Reply to LEAVE command. */
1033
1034 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1035 {
1036   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1037   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1038   SilcCommandStatus status;
1039   unsigned char *tmp;
1040
1041   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1042   SILC_GET16_MSB(status, tmp);
1043   if (status != SILC_STATUS_OK) {
1044     cmd->client->ops->say(cmd->client, conn,
1045              "%s", silc_client_command_status_message(status));
1046     COMMAND_REPLY_ERROR;
1047     goto out;
1048   }
1049
1050   /* Notify application */
1051   COMMAND_REPLY((ARGS));
1052
1053   /* Execute any pending command callbacks */
1054   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1055
1056  out:
1057   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1058   silc_client_command_reply_free(cmd);
1059 }
1060
1061 /* Reply to USERS command. Received list of client ID's and theirs modes
1062    on the channel we requested. */
1063
1064 SILC_CLIENT_CMD_REPLY_FUNC(users)
1065 {
1066   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1067   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1068   SilcCommandStatus status;
1069   SilcIDCacheEntry id_cache = NULL;
1070   SilcChannelEntry channel;
1071   SilcChannelUser chu;
1072   SilcChannelID *channel_id = NULL;
1073   SilcBuffer client_id_list;
1074   SilcBuffer client_mode_list;
1075   unsigned char *tmp;
1076   unsigned int tmp_len, list_count;
1077   int i;
1078   unsigned char **res_argv = NULL;
1079   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1080
1081   SILC_LOG_DEBUG(("Start"));
1082
1083   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1084   SILC_GET16_MSB(status, tmp);
1085   if (status != SILC_STATUS_OK) {
1086     cmd->client->ops->say(cmd->client, conn,
1087              "%s", silc_client_command_status_message(status));
1088     COMMAND_REPLY_ERROR;
1089     goto out;
1090   }
1091
1092   /* Get channel ID */
1093   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1094   if (!tmp)
1095     goto out;
1096   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1097   if (!channel_id)
1098     goto out;
1099
1100   /* Get the list count */
1101   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1102   if (!tmp)
1103     goto out;
1104   SILC_GET32_MSB(list_count, tmp);
1105
1106   /* Get Client ID list */
1107   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1108   if (!tmp)
1109     goto out;
1110
1111   client_id_list = silc_buffer_alloc(tmp_len);
1112   silc_buffer_pull_tail(client_id_list, tmp_len);
1113   silc_buffer_put(client_id_list, tmp, tmp_len);
1114
1115   /* Get client mode list */
1116   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1117   if (!tmp)
1118     goto out;
1119
1120   client_mode_list = silc_buffer_alloc(tmp_len);
1121   silc_buffer_pull_tail(client_mode_list, tmp_len);
1122   silc_buffer_put(client_mode_list, tmp, tmp_len);
1123
1124   /* Get channel entry */
1125   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1126                                    SILC_ID_CHANNEL, &id_cache)) {
1127     COMMAND_REPLY_ERROR;
1128     goto out;
1129   }
1130   channel = (SilcChannelEntry)id_cache->context;
1131
1132   /* Remove old client list from channel. */
1133   silc_list_start(channel->clients);
1134   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1135     silc_list_del(channel->clients, chu);
1136     silc_free(chu);
1137   }
1138
1139   /* Cache the received Client ID's and modes. This cache expires
1140      whenever server sends notify message to channel. It means two things;
1141      some user has joined or leaved the channel. XXX! */
1142   for (i = 0; i < list_count; i++) {
1143     unsigned short idp_len;
1144     unsigned int mode;
1145     SilcClientID *client_id;
1146     SilcClientEntry client;
1147
1148     /* Client ID */
1149     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1150     idp_len += 4;
1151     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1152     if (!client_id)
1153       continue;
1154
1155     /* Mode */
1156     SILC_GET32_MSB(mode, client_mode_list->data);
1157
1158     /* Check if we have this client cached already. */
1159     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1160                                      SILC_ID_CLIENT, &id_cache)) {
1161       /* No we don't have it, query it from the server. Assemble argument
1162          table that will be sent fr the IDENTIFY command later. */
1163       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1164                               (res_argc + 1));
1165       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1166                                    (res_argc + 1));
1167       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1168                                     (res_argc + 1));
1169       res_argv[res_argc] = client_id_list->data;
1170       res_argv_lens[res_argc] = idp_len;
1171       res_argv_types[res_argc] = res_argc + 3;
1172       res_argc++;
1173     } else {
1174       /* Found the client, join it to the channel */
1175       client = (SilcClientEntry)id_cache->context;
1176       chu = silc_calloc(1, sizeof(*chu));
1177       chu->client = client;
1178       chu->mode = mode;
1179       silc_list_add(channel->clients, chu);
1180
1181       silc_free(client_id);
1182       id_cache = NULL;
1183     }
1184
1185     silc_buffer_pull(client_id_list, idp_len);
1186     silc_buffer_pull(client_mode_list, 4);
1187   }
1188
1189   /* Query the client information from server if the list included clients
1190      that we don't know about. */
1191   if (res_argc) {
1192     SilcBuffer res_cmd;
1193
1194     /* Send the IDENTIFY command to server */
1195     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1196                                           res_argc, res_argv, res_argv_lens,
1197                                           res_argv_types, ++conn->cmd_ident);
1198     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1199                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1200                             TRUE);
1201
1202     /* Register pending command callback. After we've received the IDENTIFY
1203        command reply we will reprocess this command reply by re-calling this
1204        USERS command reply callback. */
1205     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1206                                 NULL, silc_client_command_reply_users, cmd);
1207
1208     silc_buffer_free(res_cmd);
1209     if (channel_id)
1210       silc_free(channel_id);
1211
1212     silc_free(res_argv);
1213     silc_free(res_argv_lens);
1214     silc_free(res_argv_types);
1215     return;
1216   }
1217
1218   /* We have all the clients on the channel cached now. Create a nice
1219      output for user interface and notify application. */
1220
1221   if (!cmd->callback) {
1222     /* Server has sent us USERS reply even when we haven't actually sent
1223        USERS command. This is normal behaviour when joining to a channel.
1224        Display some nice information on the user interface. */
1225     int k = 0, len1 = 0, len2 = 0;
1226     char *name_list = NULL;
1227
1228     silc_list_start(channel->clients);
1229     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1230       char *m, *n = chu->client->nickname;
1231       len2 = strlen(n);
1232       len1 += len2;
1233
1234       name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1235
1236       m = silc_client_chumode_char(chu->mode);
1237       if (m) {
1238         memcpy(name_list + (len1 - len2), m, strlen(m));
1239         len1 += strlen(m);
1240         silc_free(m);
1241       }
1242
1243       memcpy(name_list + (len1 - len2), n, len2);
1244       name_list[len1] = 0;
1245       
1246       if (k == silc_list_count(channel->clients) - 1)
1247         break;
1248       memcpy(name_list + len1, " ", 1);
1249       len1++;
1250       k++;
1251     }
1252
1253     cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1254                           channel->channel_name, name_list);
1255     silc_free(name_list);
1256   }
1257
1258   /* Notify application */
1259   COMMAND_REPLY((ARGS, channel, client_id_list->head,
1260                  client_mode_list->head));
1261
1262   /* Execute any pending command callbacks */
1263   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1264
1265   silc_buffer_free(client_id_list);
1266   silc_buffer_free(client_mode_list);
1267
1268  out:
1269   if (channel_id)
1270     silc_free(channel_id);
1271   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1272   silc_client_command_reply_free(cmd);
1273 }