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 - 2001 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 #include "client_internal.h"
38
39 /* Client command reply list. */
40 SilcClientCommandReply silc_command_reply_list[] =
41 {
42   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
43   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
44   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
45   SILC_CLIENT_CMD_REPLY(nick, NICK),
46   SILC_CLIENT_CMD_REPLY(list, LIST),
47   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
48   SILC_CLIENT_CMD_REPLY(invite, INVITE),
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(shutdown, SHUTDOWN),
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 const SilcCommandStatusMessage silc_command_status_messages[] = {
71
72   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
73   { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
74   { STAT(NO_SUCH_SERVER),    "No such server" },
75   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
76   { STAT(NO_RECIPIENT),      "No recipient given" },
77   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
78   { STAT(WILDCARDS),         "Unknown command" },
79   { STAT(NO_CLIENT_ID),      "No Client ID given" },
80   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
81   { STAT(NO_SERVER_ID),      "No Server ID given" },
82   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
83   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
84   { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
85   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
86   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
87   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
88   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
89   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
90   { STAT(NOT_REGISTERED),    "You have not registered" },
91   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
92   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
93   { STAT(PERM_DENIED),       "Your host is not among the privileged" },
94   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
95   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
96   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
97   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
98   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
99   { STAT(UNKNOWN_MODE),    "Unknown mode" },
100   { STAT(NOT_YOU),         "Cannot change mode for other users" },
101   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
102   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
103   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
104   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
105   { STAT(BAD_NICKNAME),    "Bad nickname" },
106   { STAT(BAD_CHANNEL),     "Bad channel name" },
107   { STAT(AUTH_FAILED),     "Authentication failed" },
108   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
109
110   { 0, NULL }
111 };
112 /* Command reply operation that is called at the end of all command replys. 
113    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
114 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
115 #define ARGS cmd->client, cmd->sock->user_data, \
116              cmd->payload, TRUE, silc_command_get(cmd->payload), status
117
118 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
119 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
120   cmd->sock->user_data, cmd->payload, FALSE, \
121   silc_command_get(cmd->payload), status)
122
123 /* Process received command reply. */
124
125 void silc_client_command_reply_process(SilcClient client,
126                                        SilcSocketConnection sock,
127                                        SilcPacketContext *packet)
128 {
129   SilcBuffer buffer = packet->buffer;
130   SilcClientCommandReply *cmd;
131   SilcClientCommandReplyContext ctx;
132   SilcCommandPayload payload;
133   SilcCommand command;
134   unsigned short ident;
135
136   /* Get command reply payload from packet */
137   payload = silc_command_payload_parse(buffer);
138   if (!payload) {
139     /* Silently ignore bad reply packet */
140     SILC_LOG_DEBUG(("Bad command reply packet"));
141     return;
142   }
143   
144   /* Allocate command reply context. This must be free'd by the
145      command reply routine receiving it. */
146   ctx = silc_calloc(1, sizeof(*ctx));
147   ctx->client = client;
148   ctx->sock = sock;
149   ctx->payload = payload;
150   ctx->args = silc_command_get_args(ctx->payload);
151   ctx->packet = packet;
152   ident = silc_command_get_ident(ctx->payload);
153       
154   /* Check for pending commands and mark to be exeucted */
155   silc_client_command_pending_check(sock->user_data, ctx, 
156                                     silc_command_get(ctx->payload), ident);
157
158   /* Execute command reply */
159   command = silc_command_get(ctx->payload);
160   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
161     if (cmd->cmd == command)
162       break;
163
164   if (cmd == NULL || !cmd->cb) {
165     silc_free(ctx);
166     return;
167   }
168
169   cmd->cb(ctx);
170 }
171
172 /* Returns status message string */
173
174 char *silc_client_command_status_message(SilcCommandStatus status)
175 {
176   int i;
177
178   for (i = 0; silc_command_status_messages[i].message; i++) {
179     if (silc_command_status_messages[i].status == status)
180       break;
181   }
182
183   if (silc_command_status_messages[i].message == NULL)
184     return NULL;
185
186   return silc_command_status_messages[i].message;
187 }
188
189 /* Free command reply context and its internals. */
190
191 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
192 {
193   if (cmd) {
194     silc_command_free_payload(cmd->payload);
195     silc_free(cmd);
196   }
197 }
198
199 static void 
200 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
201                                      SilcCommandStatus status)
202 {
203   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
204   SilcClientID *client_id;
205   SilcIDCacheEntry id_cache = NULL;
206   SilcClientEntry client_entry = NULL;
207   int argc, len;
208   unsigned char *id_data, *tmp;
209   char *nickname = NULL, *username = NULL;
210   char *realname = NULL;
211   unsigned int idle = 0;
212   
213   argc = silc_argument_get_arg_num(cmd->args);
214
215   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
216   if (!id_data) {
217     COMMAND_REPLY_ERROR;
218     return;
219   }
220   
221   client_id = silc_id_payload_parse_id(id_data, len);
222   if (!client_id) {
223     COMMAND_REPLY_ERROR;
224     return;
225   }
226   
227   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
228   username = silc_argument_get_arg_type(cmd->args, 4, &len);
229   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
230   if (!nickname || !username || !realname) {
231     COMMAND_REPLY_ERROR;
232     return;
233   }
234
235   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
236   if (tmp)
237     SILC_GET32_MSB(idle, tmp);
238
239   /* Check if we have this client cached already. */
240   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
241                                    SILC_ID_CLIENT, &id_cache)) {
242     SILC_LOG_DEBUG(("Adding new client entry"));
243
244     client_entry = silc_calloc(1, sizeof(*client_entry));
245     client_entry->id = client_id;
246     silc_parse_nickname(nickname, &client_entry->nickname, 
247                         &client_entry->server, &client_entry->num);
248     client_entry->username = strdup(username);
249     if (realname)
250       client_entry->realname = strdup(realname);
251     
252     /* Add client to cache */
253     silc_idcache_add(conn->client_cache, client_entry->nickname,
254                      SILC_ID_CLIENT, client_id, (void *)client_entry, 
255                      TRUE, FALSE);
256   } else {
257     client_entry = (SilcClientEntry)id_cache->context;
258     if (client_entry->nickname)
259       silc_free(client_entry->nickname);
260     if (client_entry->server)
261       silc_free(client_entry->server);
262     if (client_entry->username)
263       silc_free(client_entry->username);
264     if (client_entry->realname)
265       silc_free(client_entry->realname);
266
267     SILC_LOG_DEBUG(("Updating client entry"));
268
269     silc_parse_nickname(nickname, &client_entry->nickname, 
270                         &client_entry->server, &client_entry->num);
271     client_entry->username = strdup(username);
272     if (realname)
273       client_entry->realname = strdup(realname);
274
275     id_cache->data = client_entry->nickname;
276     silc_idcache_sort_by_data(conn->client_cache);
277
278     silc_free(client_id);
279   }
280
281   /* Notify application */
282   if (!cmd->callback)
283     COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
284                    NULL, idle));
285 }
286
287 /* Received reply for WHOIS command. This maybe called several times
288    for one WHOIS command as server may reply with list of results. */
289
290 SILC_CLIENT_CMD_REPLY_FUNC(whois)
291 {
292   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
293   SilcCommandStatus status;
294   unsigned char *tmp;
295
296   SILC_LOG_DEBUG(("Start"));
297
298   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
299   SILC_GET16_MSB(status, tmp);
300   if (status != SILC_STATUS_OK && 
301       status != SILC_STATUS_LIST_START &&
302       status != SILC_STATUS_LIST_ITEM &&
303       status != SILC_STATUS_LIST_END) {
304     COMMAND_REPLY_ERROR;
305     goto out;
306   }
307
308   /* Display one whois reply */
309   if (status == SILC_STATUS_OK)
310     silc_client_command_reply_whois_save(cmd, status);
311
312   /* List */
313   if (status == SILC_STATUS_LIST_START ||
314       status == SILC_STATUS_LIST_ITEM ||
315       status == SILC_STATUS_LIST_END)
316     silc_client_command_reply_whois_save(cmd, status);
317
318   /* Pending callbacks are not executed if this was an list entry */
319   if (status != SILC_STATUS_OK &&
320       status != SILC_STATUS_LIST_END) {
321     silc_client_command_reply_free(cmd);
322     return;
323   }
324
325   /* Execute any pending command callbacks */
326   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
327
328  out:
329   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
330   silc_client_command_reply_free(cmd);
331 }
332
333 /* Received reply for WHOWAS command. */
334
335 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
336 {
337   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
338   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
339   SilcCommandStatus status;
340   SilcClientID *client_id;
341   SilcIDCacheEntry id_cache = NULL;
342   SilcClientEntry client_entry = NULL;
343   unsigned int len;
344   unsigned char *id_data, *tmp;
345   char *nickname, *username;
346   char *realname = NULL;
347
348   SILC_LOG_DEBUG(("Start"));
349
350   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
351   SILC_GET16_MSB(status, tmp);
352   if (status != SILC_STATUS_OK && 
353       status != SILC_STATUS_LIST_START &&
354       status != SILC_STATUS_LIST_ITEM &&
355       status != SILC_STATUS_LIST_END) {
356     COMMAND_REPLY_ERROR;
357     goto out;
358   }
359   
360   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
361   if (!id_data) {
362     COMMAND_REPLY_ERROR;
363     return;
364   }
365   
366   client_id = silc_id_payload_parse_id(id_data, len);
367   if (!client_id) {
368     COMMAND_REPLY_ERROR;
369     return;
370   }
371
372   /* Get the client entry, if exists */
373   if (silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
374                                   SILC_ID_CLIENT, &id_cache))
375     client_entry = (SilcClientEntry)id_cache->context;
376   silc_free(client_id);
377
378   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
379   username = silc_argument_get_arg_type(cmd->args, 4, &len);
380   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
381   if (!nickname || !username) {
382     COMMAND_REPLY_ERROR;
383     return;
384   }
385   /* Notify application. We don't save any history information to any
386      cache. Just pass the data to the application for displaying on 
387      the screen. */
388   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname));
389
390   /* Pending callbacks are not executed if this was an list entry */
391   if (status != SILC_STATUS_OK &&
392       status != SILC_STATUS_LIST_END) {
393     silc_client_command_reply_free(cmd);
394     return;
395   }
396
397   /* Execute any pending command callbacks */
398   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
399
400  out:
401   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
402   silc_client_command_reply_free(cmd);
403 }
404
405 static void 
406 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
407                                         SilcCommandStatus status)
408 {
409   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
410   SilcClientID *client_id;
411   SilcIDCacheEntry id_cache = NULL;
412   SilcClientEntry client_entry = NULL;
413   int argc, len;
414   unsigned char *id_data;
415   char *nickname = NULL, *username = NULL;
416   
417   argc = silc_argument_get_arg_num(cmd->args);
418
419   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
420   if (!id_data) {
421     COMMAND_REPLY_ERROR;
422     return;
423   }
424   
425   client_id = silc_id_payload_parse_id(id_data, len);
426   if (!client_id) {
427     COMMAND_REPLY_ERROR;
428     return;
429   }
430   
431   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
432   username = silc_argument_get_arg_type(cmd->args, 4, &len);
433
434   /* Check if we have this client cached already. */
435   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
436                                    SILC_ID_CLIENT, &id_cache)) {
437     SILC_LOG_DEBUG(("Adding new client entry"));
438
439     client_entry = silc_calloc(1, sizeof(*client_entry));
440     client_entry->id = client_id;
441     silc_parse_nickname(nickname, &client_entry->nickname, 
442                         &client_entry->server, &client_entry->num);
443     if (username)
444       client_entry->username = strdup(username);
445     
446     /* Add client to cache */
447     silc_idcache_add(conn->client_cache, client_entry->nickname,
448                      SILC_ID_CLIENT, client_id, (void *)client_entry, 
449                      TRUE, FALSE);
450   } else {
451     client_entry = (SilcClientEntry)id_cache->context;
452     if (client_entry->nickname)
453       silc_free(client_entry->nickname);
454     if (client_entry->server)
455       silc_free(client_entry->server);
456     if (username && client_entry->username)
457       silc_free(client_entry->username);
458     
459     SILC_LOG_DEBUG(("Updating client entry"));
460
461     silc_parse_nickname(nickname, &client_entry->nickname, 
462                         &client_entry->server, &client_entry->num);
463     
464     if (username)
465       client_entry->username = strdup(username);
466     
467     id_cache->data = client_entry->nickname;
468     silc_idcache_sort_by_data(conn->client_cache);
469     
470     silc_free(client_id);
471   }
472
473   /* Notify application */
474   COMMAND_REPLY((ARGS, client_entry, nickname, username));
475 }
476
477 /* Received reply for IDENTIFY command. This maybe called several times
478    for one IDENTIFY command as server may reply with list of results. 
479    This is totally silent and does not print anything on screen. */
480
481 SILC_CLIENT_CMD_REPLY_FUNC(identify)
482 {
483   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
484   SilcCommandStatus status;
485   unsigned char *tmp;
486
487   SILC_LOG_DEBUG(("Start"));
488
489   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
490   SILC_GET16_MSB(status, tmp);
491   if (status != SILC_STATUS_OK && 
492       status != SILC_STATUS_LIST_START &&
493       status != SILC_STATUS_LIST_ITEM &&
494       status != SILC_STATUS_LIST_END) {
495     COMMAND_REPLY_ERROR;
496     goto out;
497   }
498
499   /* Save one IDENTIFY entry */
500   if (status == SILC_STATUS_OK)
501     silc_client_command_reply_identify_save(cmd, status);
502
503   /* List */
504   if (status == SILC_STATUS_LIST_START ||
505       status == SILC_STATUS_LIST_ITEM ||
506       status == SILC_STATUS_LIST_END)
507     silc_client_command_reply_identify_save(cmd, status);
508
509   /* Pending callbacks are not executed if this was an list entry */
510   if (status != SILC_STATUS_OK &&
511       status != SILC_STATUS_LIST_END) {
512     silc_client_command_reply_free(cmd);
513     return;
514   }
515
516   /* Execute any pending command callbacks */
517   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
518
519  out:
520   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
521   silc_client_command_reply_free(cmd);
522 }
523
524 /* Received reply for command NICK. If everything went without errors
525    we just received our new Client ID. */
526
527 SILC_CLIENT_CMD_REPLY_FUNC(nick)
528 {
529   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
530   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
531   SilcCommandStatus status;
532   SilcIDPayload idp;
533   unsigned char *tmp;
534   unsigned int argc, len;
535
536   SILC_LOG_DEBUG(("Start"));
537
538   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
539   if (status != SILC_STATUS_OK) {
540     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
541              silc_client_command_status_message(status));
542     COMMAND_REPLY_ERROR;
543     goto out;
544   }
545
546   argc = silc_argument_get_arg_num(cmd->args);
547   if (argc < 2 || argc > 2) {
548     cmd->client->ops->say(cmd->client, conn, 
549                           "Cannot set nickname: bad reply to command");
550     COMMAND_REPLY_ERROR;
551     goto out;
552   }
553
554   /* Take received Client ID */
555   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
556   idp = silc_id_payload_parse_data(tmp, len);
557   if (!idp) {
558     COMMAND_REPLY_ERROR;
559     goto out;
560   }
561   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
562     
563   /* Notify application */
564   COMMAND_REPLY((ARGS, conn->local_entry));
565
566   /* Execute any pending command callbacks */
567   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
568
569  out:
570   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
571   silc_client_command_reply_free(cmd);
572 }
573
574 SILC_CLIENT_CMD_REPLY_FUNC(list)
575 {
576 }
577
578 /* Received reply to topic command. */
579
580 SILC_CLIENT_CMD_REPLY_FUNC(topic)
581 {
582   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
583   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
584   SilcCommandStatus status;
585   SilcChannelEntry channel;
586   SilcChannelID *channel_id = NULL;
587   SilcIDCacheEntry id_cache = NULL;
588   unsigned char *tmp;
589   char *topic;
590   unsigned int argc, len;
591
592   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
593   if (status != SILC_STATUS_OK) {
594     cmd->client->ops->say(cmd->client, conn,
595              "%s", silc_client_command_status_message(status));
596     COMMAND_REPLY_ERROR;
597     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
598     silc_client_command_reply_free(cmd);
599     return;
600   }
601
602   argc = silc_argument_get_arg_num(cmd->args);
603   if (argc < 1 || argc > 3) {
604     COMMAND_REPLY_ERROR;
605     goto out;
606   }
607
608   /* Take Channel ID */
609   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
610   if (!tmp)
611     goto out;
612
613   /* Take topic */
614   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
615   if (!topic)
616     goto out;
617
618   channel_id = silc_id_payload_parse_id(tmp, len);
619   if (!channel_id)
620     goto out;
621
622   /* Get the channel name */
623   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
624                                    SILC_ID_CHANNEL, &id_cache)) {
625     silc_free(channel_id);
626     COMMAND_REPLY_ERROR;
627     goto out;
628   }
629   
630   channel = (SilcChannelEntry)id_cache->context;
631
632   cmd->client->ops->say(cmd->client, conn, 
633                         "Topic on channel %s: %s", channel->channel_name,
634                         topic);
635
636   /* Notify application */
637   COMMAND_REPLY((ARGS, channel, topic));
638
639   /* Execute any pending command callbacks */
640   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
641
642  out:
643   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
644   silc_client_command_reply_free(cmd);
645 }
646
647 /* Received reply to invite command. */
648
649 SILC_CLIENT_CMD_REPLY_FUNC(invite)
650 {
651   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
652   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
653   SilcCommandStatus status;
654   unsigned char *tmp;
655
656   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
657   SILC_GET16_MSB(status, tmp);
658   if (status != SILC_STATUS_OK) {
659     cmd->client->ops->say(cmd->client, conn,
660              "%s", silc_client_command_status_message(status));
661     COMMAND_REPLY_ERROR;
662     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
663     silc_client_command_reply_free(cmd);
664     return;
665   }
666
667   /* Notify application */
668   COMMAND_REPLY((ARGS));
669
670   /* Execute any pending command callbacks */
671   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
672
673   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
674   silc_client_command_reply_free(cmd);
675 }
676  
677 SILC_CLIENT_CMD_REPLY_FUNC(kill)
678 {
679 }
680
681 /* Received reply to INFO command. We receive the server ID and some
682    information about the server user requested. */
683
684 SILC_CLIENT_CMD_REPLY_FUNC(info)
685 {
686   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
687   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
688   SilcClient client = cmd->client;
689   SilcCommandStatus status;
690   unsigned char *tmp;
691
692   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
693   SILC_GET16_MSB(status, tmp);
694   if (status != SILC_STATUS_OK) {
695     cmd->client->ops->say(cmd->client, conn,
696              "%s", silc_client_command_status_message(status));
697     COMMAND_REPLY_ERROR;
698     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
699     silc_client_command_reply_free(cmd);
700     return;
701   }
702
703   /* Get server ID */
704   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
705   if (!tmp)
706     goto out;
707
708   /* XXX save server id */
709
710   /* Get server info */
711   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
712   if (!tmp)
713     goto out;
714
715   client->ops->say(cmd->client, conn, "Info: %s", tmp);
716
717   /* Notify application */
718   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
719
720   /* Execute any pending command callbacks */
721   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
722
723  out:
724   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
725   silc_client_command_reply_free(cmd);
726 }
727
728 /* Received reply to PING command. The reply time is shown to user. */
729
730 SILC_CLIENT_CMD_REPLY_FUNC(ping)
731 {
732   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
733   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
734   SilcCommandStatus status;
735   void *id;
736   int i;
737   time_t diff, curtime;
738
739   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
740   if (status != SILC_STATUS_OK) {
741     cmd->client->ops->say(cmd->client, conn,
742              "%s", silc_client_command_status_message(status));
743     COMMAND_REPLY_ERROR;
744     goto out;
745   }
746
747   curtime = time(NULL);
748   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
749                       cmd->packet->src_id_type);
750   if (!id) {
751     COMMAND_REPLY_ERROR;
752     goto out;
753   }
754
755   for (i = 0; i < conn->ping_count; i++) {
756     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
757       diff = curtime - conn->ping[i].start_time;
758       cmd->client->ops->say(cmd->client, conn, 
759                             "Ping reply from %s: %d second%s", 
760                             conn->ping[i].dest_name, diff, 
761                             diff == 1 ? "" : "s");
762       
763       conn->ping[i].start_time = 0;
764       silc_free(conn->ping[i].dest_id);
765       conn->ping[i].dest_id = NULL;
766       silc_free(conn->ping[i].dest_name);
767       conn->ping[i].dest_name = NULL;
768       break;
769     }
770   }
771
772   silc_free(id);
773
774   /* Notify application */
775   COMMAND_REPLY((ARGS));
776
777   /* Execute any pending command callbacks */
778   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
779
780  out:
781   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
782   silc_client_command_reply_free(cmd);
783 }
784
785 /* Received reply for JOIN command. */
786
787 SILC_CLIENT_CMD_REPLY_FUNC(join)
788 {
789   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
790   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
791   SilcCommandStatus status;
792   SilcIDPayload idp = NULL;
793   SilcChannelEntry channel;
794   SilcIDCacheEntry id_cache = NULL;
795   SilcChannelUser chu;
796   unsigned int argc, mode, len, list_count;
797   char *topic, *tmp, *channel_name = NULL, *hmac;
798   SilcBuffer keyp, client_id_list, client_mode_list;
799   int i;
800
801   SILC_LOG_DEBUG(("Start"));
802
803   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
804   if (status != SILC_STATUS_OK) {
805     cmd->client->ops->say(cmd->client, conn,
806              "%s", silc_client_command_status_message(status));
807     COMMAND_REPLY_ERROR;
808     goto out;
809   }
810
811   argc = silc_argument_get_arg_num(cmd->args);
812   if (argc < 7 || argc > 14) {
813     cmd->client->ops->say(cmd->client, conn,
814              "Cannot join channel: Bad reply packet");
815     COMMAND_REPLY_ERROR;
816     goto out;
817   }
818
819   /* Get channel name */
820   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
821   if (!tmp) {
822     cmd->client->ops->say(cmd->client, conn, 
823                           "Cannot join channel: Bad reply packet");
824     COMMAND_REPLY_ERROR;
825     goto out;
826   }
827   channel_name = strdup(tmp);
828
829   /* Get Channel ID */
830   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
831   if (!tmp) {
832     cmd->client->ops->say(cmd->client, conn, 
833                           "Cannot join channel: Bad reply packet");
834     COMMAND_REPLY_ERROR;
835     silc_free(channel_name);
836     goto out;
837   }
838   idp = silc_id_payload_parse_data(tmp, len);
839   if (!idp) {
840     COMMAND_REPLY_ERROR;
841     silc_free(channel_name);
842     goto out;
843   }
844
845   /* Get channel mode */
846   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
847   if (tmp)
848     SILC_GET32_MSB(mode, tmp);
849   else
850     mode = 0;
851
852   /* Get channel key */
853   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
854   if (!tmp) {
855     silc_id_payload_free(idp);
856     silc_free(channel_name);
857     goto out;
858   }
859   keyp = silc_buffer_alloc(len);
860   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
861   silc_buffer_put(keyp, tmp, len);
862
863   /* Get topic */
864   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
865
866   /* Save received Channel ID. This actually creates the channel */
867   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
868                                        mode, idp);
869   silc_id_payload_free(idp);
870
871   /* Get hmac */
872   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
873   if (hmac) {
874     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
875       cmd->client->ops->say(cmd->client, conn, 
876                             "Cannot join channel: Unsupported HMAC `%s'",
877                             hmac);
878       COMMAND_REPLY_ERROR;
879       silc_free(channel_name);
880       goto out;
881     }
882   }
883
884   /* Get the list count */
885   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
886   if (!tmp)
887     goto out;
888   SILC_GET32_MSB(list_count, tmp);
889
890   /* Get Client ID list */
891   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
892   if (!tmp)
893     goto out;
894
895   client_id_list = silc_buffer_alloc(len);
896   silc_buffer_pull_tail(client_id_list, len);
897   silc_buffer_put(client_id_list, tmp, len);
898
899   /* Get client mode list */
900   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
901   if (!tmp)
902     goto out;
903
904   client_mode_list = silc_buffer_alloc(len);
905   silc_buffer_pull_tail(client_mode_list, len);
906   silc_buffer_put(client_mode_list, tmp, len);
907
908   /* Add clients we received in the reply to the channel */
909   for (i = 0; i < list_count; i++) {
910     unsigned short idp_len;
911     unsigned int mode;
912     SilcClientID *client_id;
913     SilcClientEntry client_entry;
914
915     /* Client ID */
916     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
917     idp_len += 4;
918     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
919     if (!client_id)
920       continue;
921
922     /* Mode */
923     SILC_GET32_MSB(mode, client_mode_list->data);
924
925     /* Check if we have this client cached already. */
926     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
927                                      SILC_ID_CLIENT, &id_cache)) {
928       /* No, we don't have it, add entry for it. */
929       client_entry = silc_calloc(1, sizeof(*client_entry));
930       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
931       silc_idcache_add(conn->client_cache, NULL, SILC_ID_CLIENT, 
932                        client_entry->id, (void *)client_entry, FALSE, FALSE);
933     } else {
934       /* Yes, we have it already */
935       client_entry = (SilcClientEntry)id_cache->context;
936     }
937
938     /* Join the client to the channel */
939     chu = silc_calloc(1, sizeof(*chu));
940     chu->client = client_entry;
941     chu->mode = mode;
942     silc_list_add(channel->clients, chu);
943     silc_free(client_id);
944
945     silc_buffer_pull(client_id_list, idp_len);
946     silc_buffer_pull(client_mode_list, 4);
947   }
948   silc_buffer_push(client_id_list, client_id_list->data - 
949                    client_id_list->head);
950   silc_buffer_push(client_mode_list, client_mode_list->data - 
951                    client_mode_list->head);
952
953   /* Save channel key */
954   silc_client_save_channel_key(conn, keyp, channel);
955
956   /* Notify application */
957   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
958                  NULL, topic, hmac, list_count, client_id_list, 
959                  client_mode_list));
960
961   /* Execute any pending command callbacks */
962   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
963
964   silc_buffer_free(keyp);
965   silc_buffer_free(client_id_list);
966   silc_buffer_free(client_mode_list);
967
968  out:
969   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
970   silc_client_command_reply_free(cmd);
971 }
972
973 /* Received reply for MOTD command */
974
975 SILC_CLIENT_CMD_REPLY_FUNC(motd)
976 {
977   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
978   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
979   SilcCommandStatus status;
980   unsigned int argc, i;
981   unsigned char *tmp;
982   char *motd = NULL, *cp, line[256];
983
984   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
985   SILC_GET16_MSB(status, tmp);
986   if (status != SILC_STATUS_OK) {
987     cmd->client->ops->say(cmd->client, conn,
988              "%s", silc_client_command_status_message(status));
989     COMMAND_REPLY_ERROR;
990     return;
991   }
992
993   argc = silc_argument_get_arg_num(cmd->args);
994   if (argc > 2) {
995     COMMAND_REPLY_ERROR;
996     goto out;
997   }
998
999   if (argc == 2) {
1000     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
1001     if (!motd) {
1002       COMMAND_REPLY_ERROR;
1003       goto out;
1004     }
1005
1006     i = 0;
1007     cp = motd;
1008     while(cp[i] != 0) {
1009       if (cp[i++] == '\n') {
1010         memset(line, 0, sizeof(line));
1011         strncat(line, cp, i - 1);
1012         cp += i;
1013         
1014         if (i == 2)
1015           line[0] = ' ';
1016         
1017         cmd->client->ops->say(cmd->client, conn, "%s", line);
1018         
1019         if (!strlen(cp))
1020           break;
1021         i = 0;
1022       }
1023     }
1024   }
1025
1026   /* Notify application */
1027   COMMAND_REPLY((ARGS, motd));
1028
1029   /* Execute any pending command callbacks */
1030   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1031
1032  out:
1033   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1034   silc_client_command_reply_free(cmd);
1035 }
1036
1037 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1038 {
1039 }
1040
1041 /* Received reply for CMODE command. */
1042
1043 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1044 {
1045   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1046   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1047   SilcCommandStatus status;
1048   unsigned char *tmp;
1049
1050   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1051   if (status != SILC_STATUS_OK) {
1052     cmd->client->ops->say(cmd->client, conn,
1053              "%s", silc_client_command_status_message(status));
1054     COMMAND_REPLY_ERROR;
1055     goto out;
1056   }
1057
1058   /* Get channel mode */
1059   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1060   if (!tmp) {
1061     COMMAND_REPLY_ERROR;
1062     goto out;
1063   }
1064
1065   /* Notify application */
1066   COMMAND_REPLY((ARGS, tmp));
1067
1068   /* Execute any pending command callbacks */
1069   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1070
1071  out:
1072   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1073   silc_client_command_reply_free(cmd);
1074 }
1075
1076 /* Received reply for CUMODE command */
1077
1078 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1079 {
1080   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1081   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1082   SilcCommandStatus status;
1083   SilcIDCacheEntry id_cache = NULL;
1084   SilcClientID *client_id;
1085   unsigned char *tmp, *id;
1086   unsigned int len;
1087   
1088   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1089   if (status != SILC_STATUS_OK) {
1090     cmd->client->ops->say(cmd->client, conn,
1091              "%s", silc_client_command_status_message(status));
1092     COMMAND_REPLY_ERROR;
1093     goto out;
1094   }
1095   
1096   /* Get channel mode */
1097   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1098   if (!tmp) {
1099     COMMAND_REPLY_ERROR;
1100     goto out;
1101   }
1102
1103   /* Get Client ID */
1104   id = silc_argument_get_arg_type(cmd->args, 3, &len);
1105   if (!id) {
1106     COMMAND_REPLY_ERROR;
1107     goto out;
1108   }
1109   client_id = silc_id_payload_parse_id(id, len);
1110   if (!client_id) {
1111     COMMAND_REPLY_ERROR;
1112     goto out;
1113   }
1114   
1115   /* Get client entry */
1116   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1117                                    SILC_ID_CLIENT, &id_cache)) {
1118     COMMAND_REPLY_ERROR;
1119     goto out;
1120   }
1121
1122   /* Notify application */
1123   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1124   silc_free(client_id);
1125   
1126   /* Execute any pending command callbacks */
1127   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1128
1129  out:
1130   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1131   silc_client_command_reply_free(cmd);
1132 }
1133
1134 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1135 {
1136   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1137   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1138   SilcCommandStatus status;
1139   unsigned char *tmp;
1140
1141   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1142   SILC_GET16_MSB(status, tmp);
1143   if (status != SILC_STATUS_OK) {
1144     cmd->client->ops->say(cmd->client, conn,
1145              "%s", silc_client_command_status_message(status));
1146     COMMAND_REPLY_ERROR;
1147     goto out;
1148   }
1149
1150   /* Notify application */
1151   COMMAND_REPLY((ARGS));
1152
1153   /* Execute any pending command callbacks */
1154   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1155
1156  out:
1157   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1158   silc_client_command_reply_free(cmd);
1159 }
1160
1161 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1162 {
1163   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1164   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1165   SilcCommandStatus status;
1166   unsigned char *tmp;
1167
1168   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1169   SILC_GET16_MSB(status, tmp);
1170   if (status != SILC_STATUS_OK) {
1171     cmd->client->ops->say(cmd->client, conn,
1172              "%s", silc_client_command_status_message(status));
1173     COMMAND_REPLY_ERROR;
1174     goto out;
1175   }
1176
1177   /* Notify application */
1178   COMMAND_REPLY((ARGS));
1179
1180   /* Execute any pending command callbacks */
1181   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1182
1183  out:
1184   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1185   silc_client_command_reply_free(cmd);
1186 }
1187
1188 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1189 {
1190   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1191   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1192   SilcCommandStatus status;
1193   unsigned char *tmp;
1194
1195   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1196   SILC_GET16_MSB(status, tmp);
1197   if (status != SILC_STATUS_OK) {
1198     cmd->client->ops->say(cmd->client, conn,
1199              "%s", silc_client_command_status_message(status));
1200     COMMAND_REPLY_ERROR;
1201     goto out;
1202   }
1203
1204   /* Notify application */
1205   COMMAND_REPLY((ARGS));
1206
1207   /* Execute any pending command callbacks */
1208   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1209
1210  out:
1211   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1212   silc_client_command_reply_free(cmd);
1213 }
1214
1215 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1216 {
1217   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1218   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1219   SilcCommandStatus status;
1220   unsigned char *tmp;
1221
1222   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1223   SILC_GET16_MSB(status, tmp);
1224   if (status != SILC_STATUS_OK) {
1225     cmd->client->ops->say(cmd->client, conn,
1226              "%s", silc_client_command_status_message(status));
1227     COMMAND_REPLY_ERROR;
1228     goto out;
1229   }
1230
1231   /* Notify application */
1232   COMMAND_REPLY((ARGS));
1233
1234   /* Execute any pending command callbacks */
1235   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1236
1237  out:
1238   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1239   silc_client_command_reply_free(cmd);
1240 }
1241
1242 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1243 {
1244   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1245   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1246   SilcCommandStatus status;
1247   unsigned char *tmp;
1248
1249   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1250   SILC_GET16_MSB(status, tmp);
1251   if (status != SILC_STATUS_OK) {
1252     cmd->client->ops->say(cmd->client, conn,
1253              "%s", silc_client_command_status_message(status));
1254     COMMAND_REPLY_ERROR;
1255     goto out;
1256   }
1257
1258   /* Notify application */
1259   COMMAND_REPLY((ARGS));
1260
1261   /* Execute any pending command callbacks */
1262   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1263
1264  out:
1265   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1266   silc_client_command_reply_free(cmd);
1267 }
1268  
1269 SILC_CLIENT_CMD_REPLY_FUNC(close)
1270 {
1271   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1272   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1273   SilcCommandStatus status;
1274   unsigned char *tmp;
1275
1276   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1277   SILC_GET16_MSB(status, tmp);
1278   if (status != SILC_STATUS_OK) {
1279     cmd->client->ops->say(cmd->client, conn,
1280              "%s", silc_client_command_status_message(status));
1281     COMMAND_REPLY_ERROR;
1282     goto out;
1283   }
1284
1285   /* Notify application */
1286   COMMAND_REPLY((ARGS));
1287
1288   /* Execute any pending command callbacks */
1289   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1290
1291  out:
1292   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1293   silc_client_command_reply_free(cmd);
1294 }
1295  
1296 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1297 {
1298   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1299   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1300   SilcCommandStatus status;
1301   unsigned char *tmp;
1302
1303   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1304   SILC_GET16_MSB(status, tmp);
1305   if (status != SILC_STATUS_OK) {
1306     cmd->client->ops->say(cmd->client, conn,
1307              "%s", silc_client_command_status_message(status));
1308     COMMAND_REPLY_ERROR;
1309     goto out;
1310   }
1311
1312   /* Notify application */
1313   COMMAND_REPLY((ARGS));
1314
1315   /* Execute any pending command callbacks */
1316   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1317
1318  out:
1319   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1320   silc_client_command_reply_free(cmd);
1321 }
1322  
1323 /* Reply to LEAVE command. */
1324
1325 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1326 {
1327   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1328   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1329   SilcCommandStatus status;
1330   unsigned char *tmp;
1331
1332   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1333   SILC_GET16_MSB(status, tmp);
1334   if (status != SILC_STATUS_OK) {
1335     cmd->client->ops->say(cmd->client, conn,
1336              "%s", silc_client_command_status_message(status));
1337     COMMAND_REPLY_ERROR;
1338     goto out;
1339   }
1340
1341   /* Notify application */
1342   COMMAND_REPLY((ARGS));
1343
1344   /* Execute any pending command callbacks */
1345   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1346
1347  out:
1348   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1349   silc_client_command_reply_free(cmd);
1350 }
1351
1352 /* Reply to USERS command. Received list of client ID's and theirs modes
1353    on the channel we requested. */
1354
1355 SILC_CLIENT_CMD_REPLY_FUNC(users)
1356 {
1357   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1358   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1359   SilcCommandStatus status;
1360   SilcIDCacheEntry id_cache = NULL;
1361   SilcChannelEntry channel;
1362   SilcChannelUser chu;
1363   SilcChannelID *channel_id = NULL;
1364   SilcBuffer client_id_list;
1365   SilcBuffer client_mode_list;
1366   unsigned char *tmp;
1367   unsigned int tmp_len, list_count;
1368   int i;
1369   unsigned char **res_argv = NULL;
1370   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1371
1372   SILC_LOG_DEBUG(("Start"));
1373
1374   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1375   SILC_GET16_MSB(status, tmp);
1376   if (status != SILC_STATUS_OK) {
1377     cmd->client->ops->say(cmd->client, conn,
1378              "%s", silc_client_command_status_message(status));
1379     COMMAND_REPLY_ERROR;
1380     goto out;
1381   }
1382
1383   /* Get channel ID */
1384   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1385   if (!tmp)
1386     goto out;
1387   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1388   if (!channel_id)
1389     goto out;
1390
1391   /* Get the list count */
1392   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1393   if (!tmp)
1394     goto out;
1395   SILC_GET32_MSB(list_count, tmp);
1396
1397   /* Get Client ID list */
1398   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1399   if (!tmp)
1400     goto out;
1401
1402   client_id_list = silc_buffer_alloc(tmp_len);
1403   silc_buffer_pull_tail(client_id_list, tmp_len);
1404   silc_buffer_put(client_id_list, tmp, tmp_len);
1405
1406   /* Get client mode list */
1407   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1408   if (!tmp)
1409     goto out;
1410
1411   client_mode_list = silc_buffer_alloc(tmp_len);
1412   silc_buffer_pull_tail(client_mode_list, tmp_len);
1413   silc_buffer_put(client_mode_list, tmp, tmp_len);
1414
1415   /* Get channel entry */
1416   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1417                                    SILC_ID_CHANNEL, &id_cache)) {
1418     COMMAND_REPLY_ERROR;
1419     goto out;
1420   }
1421   channel = (SilcChannelEntry)id_cache->context;
1422
1423   /* Remove old client list from channel. */
1424   silc_list_start(channel->clients);
1425   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1426     silc_list_del(channel->clients, chu);
1427     silc_free(chu);
1428   }
1429
1430   /* Cache the received Client ID's and modes. This cache expires
1431      whenever server sends notify message to channel. It means two things;
1432      some user has joined or leaved the channel. XXX! */
1433   for (i = 0; i < list_count; i++) {
1434     unsigned short idp_len;
1435     unsigned int mode;
1436     SilcClientID *client_id;
1437     SilcClientEntry client;
1438
1439     /* Client ID */
1440     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1441     idp_len += 4;
1442     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1443     if (!client_id)
1444       continue;
1445
1446     /* Mode */
1447     SILC_GET32_MSB(mode, client_mode_list->data);
1448
1449     /* Check if we have this client cached already. */
1450     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1451                                      SILC_ID_CLIENT, &id_cache)) {
1452       /* No we don't have it, query it from the server. Assemble argument
1453          table that will be sent fr the IDENTIFY command later. */
1454       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1455                               (res_argc + 1));
1456       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1457                                    (res_argc + 1));
1458       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1459                                     (res_argc + 1));
1460       res_argv[res_argc] = client_id_list->data;
1461       res_argv_lens[res_argc] = idp_len;
1462       res_argv_types[res_argc] = res_argc + 3;
1463       res_argc++;
1464     } else {
1465       /* Found the client, join it to the channel */
1466       client = (SilcClientEntry)id_cache->context;
1467       chu = silc_calloc(1, sizeof(*chu));
1468       chu->client = client;
1469       chu->mode = mode;
1470       silc_list_add(channel->clients, chu);
1471
1472       silc_free(client_id);
1473       id_cache = NULL;
1474     }
1475
1476     silc_buffer_pull(client_id_list, idp_len);
1477     silc_buffer_pull(client_mode_list, 4);
1478   }
1479
1480   /* Query the client information from server if the list included clients
1481      that we don't know about. */
1482   if (res_argc) {
1483     SilcBuffer res_cmd;
1484
1485     /* Send the IDENTIFY command to server */
1486     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1487                                           res_argc, res_argv, res_argv_lens,
1488                                           res_argv_types, ++conn->cmd_ident);
1489     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1490                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1491                             TRUE);
1492
1493     /* Register pending command callback. After we've received the IDENTIFY
1494        command reply we will reprocess this command reply by re-calling this
1495        USERS command reply callback. */
1496     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1497                                 NULL, silc_client_command_reply_users, cmd);
1498
1499     silc_buffer_free(res_cmd);
1500     if (channel_id)
1501       silc_free(channel_id);
1502
1503     silc_free(res_argv);
1504     silc_free(res_argv_lens);
1505     silc_free(res_argv_types);
1506     return;
1507   }
1508
1509   /* Notify application */
1510   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1511
1512   /* Execute any pending command callbacks */
1513   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1514
1515   silc_buffer_free(client_id_list);
1516   silc_buffer_free(client_mode_list);
1517
1518  out:
1519   if (channel_id)
1520     silc_free(channel_id);
1521   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1522   silc_client_command_reply_free(cmd);
1523 }