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