update.
[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;
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 /* Received reply to the KILL command. */
678  
679 SILC_CLIENT_CMD_REPLY_FUNC(kill)
680 {
681   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
682   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
683   SilcCommandStatus status;
684   unsigned char *tmp;
685
686   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
687   SILC_GET16_MSB(status, tmp);
688   if (status != SILC_STATUS_OK) {
689     cmd->client->ops->say(cmd->client, conn,
690              "%s", silc_client_command_status_message(status));
691     COMMAND_REPLY_ERROR;
692     goto out;
693   }
694
695   /* Notify application */
696   COMMAND_REPLY((ARGS));
697
698   /* Execute any pending command callbacks */
699   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
700
701  out:
702   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KILL);
703   silc_client_command_reply_free(cmd);
704 }
705
706 /* Received reply to INFO command. We receive the server ID and some
707    information about the server user requested. */
708
709 SILC_CLIENT_CMD_REPLY_FUNC(info)
710 {
711   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
712   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
713   SilcClient client = cmd->client;
714   SilcCommandStatus status;
715   unsigned char *tmp;
716
717   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
718   SILC_GET16_MSB(status, tmp);
719   if (status != SILC_STATUS_OK) {
720     cmd->client->ops->say(cmd->client, conn,
721              "%s", silc_client_command_status_message(status));
722     COMMAND_REPLY_ERROR;
723     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
724     silc_client_command_reply_free(cmd);
725     return;
726   }
727
728   /* Get server ID */
729   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
730   if (!tmp)
731     goto out;
732
733   /* XXX save server id */
734
735   /* Get server info */
736   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
737   if (!tmp)
738     goto out;
739
740   client->ops->say(cmd->client, conn, "Info: %s", tmp);
741
742   /* Notify application */
743   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
744
745   /* Execute any pending command callbacks */
746   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
747
748  out:
749   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
750   silc_client_command_reply_free(cmd);
751 }
752
753 /* Received reply to PING command. The reply time is shown to user. */
754
755 SILC_CLIENT_CMD_REPLY_FUNC(ping)
756 {
757   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
758   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
759   SilcCommandStatus status;
760   void *id;
761   int i;
762   time_t diff, curtime;
763
764   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
765   if (status != SILC_STATUS_OK) {
766     cmd->client->ops->say(cmd->client, conn,
767              "%s", silc_client_command_status_message(status));
768     COMMAND_REPLY_ERROR;
769     goto out;
770   }
771
772   curtime = time(NULL);
773   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
774                       cmd->packet->src_id_type);
775   if (!id) {
776     COMMAND_REPLY_ERROR;
777     goto out;
778   }
779
780   for (i = 0; i < conn->ping_count; i++) {
781     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
782       diff = curtime - conn->ping[i].start_time;
783       cmd->client->ops->say(cmd->client, conn, 
784                             "Ping reply from %s: %d second%s", 
785                             conn->ping[i].dest_name, diff, 
786                             diff == 1 ? "" : "s");
787       
788       conn->ping[i].start_time = 0;
789       silc_free(conn->ping[i].dest_id);
790       conn->ping[i].dest_id = NULL;
791       silc_free(conn->ping[i].dest_name);
792       conn->ping[i].dest_name = NULL;
793       break;
794     }
795   }
796
797   silc_free(id);
798
799   /* Notify application */
800   COMMAND_REPLY((ARGS));
801
802   /* Execute any pending command callbacks */
803   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
804
805  out:
806   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
807   silc_client_command_reply_free(cmd);
808 }
809
810 /* Received reply for JOIN command. */
811
812 SILC_CLIENT_CMD_REPLY_FUNC(join)
813 {
814   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
815   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
816   SilcCommandStatus status;
817   SilcIDPayload idp = NULL;
818   SilcChannelEntry channel;
819   SilcIDCacheEntry id_cache = NULL;
820   SilcChannelUser chu;
821   unsigned int argc, mode, len, list_count;
822   char *topic, *tmp, *channel_name = NULL, *hmac;
823   SilcBuffer keyp, client_id_list, client_mode_list;
824   int i;
825
826   SILC_LOG_DEBUG(("Start"));
827
828   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
829   if (status != SILC_STATUS_OK) {
830     cmd->client->ops->say(cmd->client, conn,
831              "%s", silc_client_command_status_message(status));
832     COMMAND_REPLY_ERROR;
833     goto out;
834   }
835
836   argc = silc_argument_get_arg_num(cmd->args);
837   if (argc < 7 || argc > 14) {
838     cmd->client->ops->say(cmd->client, conn,
839              "Cannot join channel: Bad reply packet");
840     COMMAND_REPLY_ERROR;
841     goto out;
842   }
843
844   /* Get channel name */
845   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
846   if (!tmp) {
847     cmd->client->ops->say(cmd->client, conn, 
848                           "Cannot join channel: Bad reply packet");
849     COMMAND_REPLY_ERROR;
850     goto out;
851   }
852   channel_name = strdup(tmp);
853
854   /* Get Channel ID */
855   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
856   if (!tmp) {
857     cmd->client->ops->say(cmd->client, conn, 
858                           "Cannot join channel: Bad reply packet");
859     COMMAND_REPLY_ERROR;
860     silc_free(channel_name);
861     goto out;
862   }
863   idp = silc_id_payload_parse_data(tmp, len);
864   if (!idp) {
865     COMMAND_REPLY_ERROR;
866     silc_free(channel_name);
867     goto out;
868   }
869
870   /* Get channel mode */
871   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
872   if (tmp)
873     SILC_GET32_MSB(mode, tmp);
874   else
875     mode = 0;
876
877   /* Get channel key */
878   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
879   if (!tmp) {
880     silc_id_payload_free(idp);
881     silc_free(channel_name);
882     goto out;
883   }
884   keyp = silc_buffer_alloc(len);
885   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
886   silc_buffer_put(keyp, tmp, len);
887
888   /* Get topic */
889   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
890
891   /* Save received Channel ID. This actually creates the channel */
892   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
893                                        mode, idp);
894   silc_id_payload_free(idp);
895
896   /* Get hmac */
897   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
898   if (hmac) {
899     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
900       cmd->client->ops->say(cmd->client, conn, 
901                             "Cannot join channel: Unsupported HMAC `%s'",
902                             hmac);
903       COMMAND_REPLY_ERROR;
904       silc_free(channel_name);
905       goto out;
906     }
907   }
908
909   /* Get the list count */
910   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
911   if (!tmp)
912     goto out;
913   SILC_GET32_MSB(list_count, tmp);
914
915   /* Get Client ID list */
916   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
917   if (!tmp)
918     goto out;
919
920   client_id_list = silc_buffer_alloc(len);
921   silc_buffer_pull_tail(client_id_list, len);
922   silc_buffer_put(client_id_list, tmp, len);
923
924   /* Get client mode list */
925   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
926   if (!tmp)
927     goto out;
928
929   client_mode_list = silc_buffer_alloc(len);
930   silc_buffer_pull_tail(client_mode_list, len);
931   silc_buffer_put(client_mode_list, tmp, len);
932
933   /* Add clients we received in the reply to the channel */
934   for (i = 0; i < list_count; i++) {
935     unsigned short idp_len;
936     unsigned int mode;
937     SilcClientID *client_id;
938     SilcClientEntry client_entry;
939
940     /* Client ID */
941     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
942     idp_len += 4;
943     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
944     if (!client_id)
945       continue;
946
947     /* Mode */
948     SILC_GET32_MSB(mode, client_mode_list->data);
949
950     /* Check if we have this client cached already. */
951     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
952                                      SILC_ID_CLIENT, &id_cache)) {
953       /* No, we don't have it, add entry for it. */
954       client_entry = silc_calloc(1, sizeof(*client_entry));
955       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
956       silc_idcache_add(conn->client_cache, NULL, SILC_ID_CLIENT, 
957                        client_entry->id, (void *)client_entry, FALSE, FALSE);
958     } else {
959       /* Yes, we have it already */
960       client_entry = (SilcClientEntry)id_cache->context;
961     }
962
963     /* Join the client to the channel */
964     chu = silc_calloc(1, sizeof(*chu));
965     chu->client = client_entry;
966     chu->mode = mode;
967     silc_list_add(channel->clients, chu);
968     silc_free(client_id);
969
970     silc_buffer_pull(client_id_list, idp_len);
971     silc_buffer_pull(client_mode_list, 4);
972   }
973   silc_buffer_push(client_id_list, client_id_list->data - 
974                    client_id_list->head);
975   silc_buffer_push(client_mode_list, client_mode_list->data - 
976                    client_mode_list->head);
977
978   /* Save channel key */
979   silc_client_save_channel_key(conn, keyp, channel);
980
981   /* Notify application */
982   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
983                  NULL, topic, hmac, list_count, client_id_list, 
984                  client_mode_list));
985
986   /* Execute any pending command callbacks */
987   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
988
989   silc_buffer_free(keyp);
990   silc_buffer_free(client_id_list);
991   silc_buffer_free(client_mode_list);
992
993  out:
994   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
995   silc_client_command_reply_free(cmd);
996 }
997
998 /* Received reply for MOTD command */
999
1000 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1001 {
1002   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1003   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1004   SilcCommandStatus status;
1005   unsigned int argc, i;
1006   unsigned char *tmp;
1007   char *motd = NULL, *cp, line[256];
1008
1009   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1010   SILC_GET16_MSB(status, tmp);
1011   if (status != SILC_STATUS_OK) {
1012     cmd->client->ops->say(cmd->client, conn,
1013              "%s", silc_client_command_status_message(status));
1014     COMMAND_REPLY_ERROR;
1015     return;
1016   }
1017
1018   argc = silc_argument_get_arg_num(cmd->args);
1019   if (argc > 2) {
1020     COMMAND_REPLY_ERROR;
1021     goto out;
1022   }
1023
1024   if (argc == 2) {
1025     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
1026     if (!motd) {
1027       COMMAND_REPLY_ERROR;
1028       goto out;
1029     }
1030
1031     i = 0;
1032     cp = motd;
1033     while(cp[i] != 0) {
1034       if (cp[i++] == '\n') {
1035         memset(line, 0, sizeof(line));
1036         strncat(line, cp, i - 1);
1037         cp += i;
1038         
1039         if (i == 2)
1040           line[0] = ' ';
1041         
1042         cmd->client->ops->say(cmd->client, conn, "%s", line);
1043         
1044         if (!strlen(cp))
1045           break;
1046         i = 0;
1047       }
1048     }
1049   }
1050
1051   /* Notify application */
1052   COMMAND_REPLY((ARGS, motd));
1053
1054   /* Execute any pending command callbacks */
1055   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1056
1057  out:
1058   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1059   silc_client_command_reply_free(cmd);
1060 }
1061
1062 /* Received reply tot he UMODE command. Save the current user mode */
1063
1064 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1065 {
1066   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1067   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1068   SilcCommandStatus status;
1069   unsigned char *tmp;
1070   unsigned int mode;
1071
1072   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1073   SILC_GET16_MSB(status, tmp);
1074   if (status != SILC_STATUS_OK) {
1075     cmd->client->ops->say(cmd->client, conn,
1076              "%s", silc_client_command_status_message(status));
1077     COMMAND_REPLY_ERROR;
1078     goto out;
1079   }
1080
1081   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1082   if (!tmp) {
1083     COMMAND_REPLY_ERROR;
1084     goto out;
1085   }
1086
1087   SILC_GET32_MSB(mode, tmp);
1088   conn->local_entry->mode = mode;
1089
1090   /* Notify application */
1091   COMMAND_REPLY((ARGS, mode));
1092
1093   /* Execute any pending command callbacks */
1094   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1095
1096  out:
1097   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE);
1098   silc_client_command_reply_free(cmd);
1099 }
1100
1101 /* Received reply for CMODE command. */
1102
1103 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1104 {
1105   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1106   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1107   SilcCommandStatus status;
1108   unsigned char *tmp;
1109
1110   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1111   if (status != SILC_STATUS_OK) {
1112     cmd->client->ops->say(cmd->client, conn,
1113              "%s", silc_client_command_status_message(status));
1114     COMMAND_REPLY_ERROR;
1115     goto out;
1116   }
1117
1118   /* Get channel mode */
1119   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1120   if (!tmp) {
1121     COMMAND_REPLY_ERROR;
1122     goto out;
1123   }
1124
1125   /* Notify application */
1126   COMMAND_REPLY((ARGS, tmp));
1127
1128   /* Execute any pending command callbacks */
1129   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1130
1131  out:
1132   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1133   silc_client_command_reply_free(cmd);
1134 }
1135
1136 /* Received reply for CUMODE command */
1137
1138 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1139 {
1140   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1141   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1142   SilcCommandStatus status;
1143   SilcIDCacheEntry id_cache = NULL;
1144   SilcClientID *client_id;
1145   unsigned char *tmp, *id;
1146   unsigned int len;
1147   
1148   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1149   if (status != SILC_STATUS_OK) {
1150     cmd->client->ops->say(cmd->client, conn,
1151              "%s", silc_client_command_status_message(status));
1152     COMMAND_REPLY_ERROR;
1153     goto out;
1154   }
1155   
1156   /* Get channel mode */
1157   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1158   if (!tmp) {
1159     COMMAND_REPLY_ERROR;
1160     goto out;
1161   }
1162
1163   /* Get Client ID */
1164   id = silc_argument_get_arg_type(cmd->args, 3, &len);
1165   if (!id) {
1166     COMMAND_REPLY_ERROR;
1167     goto out;
1168   }
1169   client_id = silc_id_payload_parse_id(id, len);
1170   if (!client_id) {
1171     COMMAND_REPLY_ERROR;
1172     goto out;
1173   }
1174   
1175   /* Get client entry */
1176   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1177                                    SILC_ID_CLIENT, &id_cache)) {
1178     COMMAND_REPLY_ERROR;
1179     goto out;
1180   }
1181
1182   /* Notify application */
1183   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1184   silc_free(client_id);
1185   
1186   /* Execute any pending command callbacks */
1187   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1188
1189  out:
1190   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1191   silc_client_command_reply_free(cmd);
1192 }
1193
1194 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1195 {
1196   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1197   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1198   SilcCommandStatus status;
1199   unsigned char *tmp;
1200
1201   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1202   SILC_GET16_MSB(status, tmp);
1203   if (status != SILC_STATUS_OK) {
1204     cmd->client->ops->say(cmd->client, conn,
1205              "%s", silc_client_command_status_message(status));
1206     COMMAND_REPLY_ERROR;
1207     goto out;
1208   }
1209
1210   /* Notify application */
1211   COMMAND_REPLY((ARGS));
1212
1213   /* Execute any pending command callbacks */
1214   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1215
1216  out:
1217   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1218   silc_client_command_reply_free(cmd);
1219 }
1220
1221 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1222 {
1223   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1224   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1225   SilcCommandStatus status;
1226   unsigned char *tmp;
1227
1228   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1229   SILC_GET16_MSB(status, tmp);
1230   if (status != SILC_STATUS_OK) {
1231     cmd->client->ops->say(cmd->client, conn,
1232              "%s", silc_client_command_status_message(status));
1233     COMMAND_REPLY_ERROR;
1234     goto out;
1235   }
1236
1237   /* Notify application */
1238   COMMAND_REPLY((ARGS));
1239
1240   /* Execute any pending command callbacks */
1241   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1242
1243  out:
1244   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1245   silc_client_command_reply_free(cmd);
1246 }
1247
1248 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1249 {
1250   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1251   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1252   SilcCommandStatus status;
1253   unsigned char *tmp;
1254
1255   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1256   SILC_GET16_MSB(status, tmp);
1257   if (status != SILC_STATUS_OK) {
1258     cmd->client->ops->say(cmd->client, conn,
1259              "%s", silc_client_command_status_message(status));
1260     COMMAND_REPLY_ERROR;
1261     goto out;
1262   }
1263
1264   /* Notify application */
1265   COMMAND_REPLY((ARGS));
1266
1267   /* Execute any pending command callbacks */
1268   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1269
1270  out:
1271   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1272   silc_client_command_reply_free(cmd);
1273 }
1274
1275 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1276 {
1277   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1278   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1279   SilcCommandStatus status;
1280   unsigned char *tmp;
1281
1282   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1283   SILC_GET16_MSB(status, tmp);
1284   if (status != SILC_STATUS_OK) {
1285     cmd->client->ops->say(cmd->client, conn,
1286              "%s", silc_client_command_status_message(status));
1287     COMMAND_REPLY_ERROR;
1288     goto out;
1289   }
1290
1291   /* Notify application */
1292   COMMAND_REPLY((ARGS));
1293
1294   /* Execute any pending command callbacks */
1295   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1296
1297  out:
1298   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1299   silc_client_command_reply_free(cmd);
1300 }
1301
1302 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1303 {
1304   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1305   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1306   SilcCommandStatus status;
1307   unsigned char *tmp;
1308
1309   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1310   SILC_GET16_MSB(status, tmp);
1311   if (status != SILC_STATUS_OK) {
1312     cmd->client->ops->say(cmd->client, conn,
1313              "%s", silc_client_command_status_message(status));
1314     COMMAND_REPLY_ERROR;
1315     goto out;
1316   }
1317
1318   /* Notify application */
1319   COMMAND_REPLY((ARGS));
1320
1321   /* Execute any pending command callbacks */
1322   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1323
1324  out:
1325   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1326   silc_client_command_reply_free(cmd);
1327 }
1328  
1329 SILC_CLIENT_CMD_REPLY_FUNC(close)
1330 {
1331   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1332   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1333   SilcCommandStatus status;
1334   unsigned char *tmp;
1335
1336   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1337   SILC_GET16_MSB(status, tmp);
1338   if (status != SILC_STATUS_OK) {
1339     cmd->client->ops->say(cmd->client, conn,
1340              "%s", silc_client_command_status_message(status));
1341     COMMAND_REPLY_ERROR;
1342     goto out;
1343   }
1344
1345   /* Notify application */
1346   COMMAND_REPLY((ARGS));
1347
1348   /* Execute any pending command callbacks */
1349   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1350
1351  out:
1352   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1353   silc_client_command_reply_free(cmd);
1354 }
1355  
1356 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1357 {
1358   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1359   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1360   SilcCommandStatus status;
1361   unsigned char *tmp;
1362
1363   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1364   SILC_GET16_MSB(status, tmp);
1365   if (status != SILC_STATUS_OK) {
1366     cmd->client->ops->say(cmd->client, conn,
1367              "%s", silc_client_command_status_message(status));
1368     COMMAND_REPLY_ERROR;
1369     goto out;
1370   }
1371
1372   /* Notify application */
1373   COMMAND_REPLY((ARGS));
1374
1375   /* Execute any pending command callbacks */
1376   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1377
1378  out:
1379   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1380   silc_client_command_reply_free(cmd);
1381 }
1382  
1383 /* Reply to LEAVE command. */
1384
1385 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1386 {
1387   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1388   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1389   SilcCommandStatus status;
1390   unsigned char *tmp;
1391
1392   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1393   SILC_GET16_MSB(status, tmp);
1394   if (status != SILC_STATUS_OK) {
1395     cmd->client->ops->say(cmd->client, conn,
1396              "%s", silc_client_command_status_message(status));
1397     COMMAND_REPLY_ERROR;
1398     goto out;
1399   }
1400
1401   /* Notify application */
1402   COMMAND_REPLY((ARGS));
1403
1404   /* Execute any pending command callbacks */
1405   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1406
1407  out:
1408   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1409   silc_client_command_reply_free(cmd);
1410 }
1411
1412 /* Reply to USERS command. Received list of client ID's and theirs modes
1413    on the channel we requested. */
1414
1415 SILC_CLIENT_CMD_REPLY_FUNC(users)
1416 {
1417   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1418   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1419   SilcCommandStatus status;
1420   SilcIDCacheEntry id_cache = NULL;
1421   SilcChannelEntry channel;
1422   SilcChannelUser chu;
1423   SilcChannelID *channel_id = NULL;
1424   SilcBuffer client_id_list;
1425   SilcBuffer client_mode_list;
1426   unsigned char *tmp;
1427   unsigned int tmp_len, list_count;
1428   int i;
1429   unsigned char **res_argv = NULL;
1430   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1431
1432   SILC_LOG_DEBUG(("Start"));
1433
1434   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1435   SILC_GET16_MSB(status, tmp);
1436   if (status != SILC_STATUS_OK) {
1437     cmd->client->ops->say(cmd->client, conn,
1438              "%s", silc_client_command_status_message(status));
1439     COMMAND_REPLY_ERROR;
1440     goto out;
1441   }
1442
1443   /* Get channel ID */
1444   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1445   if (!tmp)
1446     goto out;
1447   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1448   if (!channel_id)
1449     goto out;
1450
1451   /* Get the list count */
1452   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1453   if (!tmp)
1454     goto out;
1455   SILC_GET32_MSB(list_count, tmp);
1456
1457   /* Get Client ID list */
1458   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1459   if (!tmp)
1460     goto out;
1461
1462   client_id_list = silc_buffer_alloc(tmp_len);
1463   silc_buffer_pull_tail(client_id_list, tmp_len);
1464   silc_buffer_put(client_id_list, tmp, tmp_len);
1465
1466   /* Get client mode list */
1467   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1468   if (!tmp)
1469     goto out;
1470
1471   client_mode_list = silc_buffer_alloc(tmp_len);
1472   silc_buffer_pull_tail(client_mode_list, tmp_len);
1473   silc_buffer_put(client_mode_list, tmp, tmp_len);
1474
1475   /* Get channel entry */
1476   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1477                                    SILC_ID_CHANNEL, &id_cache)) {
1478     COMMAND_REPLY_ERROR;
1479     goto out;
1480   }
1481   channel = (SilcChannelEntry)id_cache->context;
1482
1483   /* Remove old client list from channel. */
1484   silc_list_start(channel->clients);
1485   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1486     silc_list_del(channel->clients, chu);
1487     silc_free(chu);
1488   }
1489
1490   /* Cache the received Client ID's and modes. This cache expires
1491      whenever server sends notify message to channel. It means two things;
1492      some user has joined or leaved the channel. XXX! */
1493   for (i = 0; i < list_count; i++) {
1494     unsigned short idp_len;
1495     unsigned int mode;
1496     SilcClientID *client_id;
1497     SilcClientEntry client;
1498
1499     /* Client ID */
1500     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1501     idp_len += 4;
1502     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1503     if (!client_id)
1504       continue;
1505
1506     /* Mode */
1507     SILC_GET32_MSB(mode, client_mode_list->data);
1508
1509     /* Check if we have this client cached already. */
1510     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1511                                      SILC_ID_CLIENT, &id_cache)) {
1512       /* No we don't have it, query it from the server. Assemble argument
1513          table that will be sent fr the IDENTIFY command later. */
1514       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1515                               (res_argc + 1));
1516       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1517                                    (res_argc + 1));
1518       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1519                                     (res_argc + 1));
1520       res_argv[res_argc] = client_id_list->data;
1521       res_argv_lens[res_argc] = idp_len;
1522       res_argv_types[res_argc] = res_argc + 3;
1523       res_argc++;
1524     } else {
1525       /* Found the client, join it to the channel */
1526       client = (SilcClientEntry)id_cache->context;
1527       chu = silc_calloc(1, sizeof(*chu));
1528       chu->client = client;
1529       chu->mode = mode;
1530       silc_list_add(channel->clients, chu);
1531
1532       silc_free(client_id);
1533       id_cache = NULL;
1534     }
1535
1536     silc_buffer_pull(client_id_list, idp_len);
1537     silc_buffer_pull(client_mode_list, 4);
1538   }
1539
1540   /* Query the client information from server if the list included clients
1541      that we don't know about. */
1542   if (res_argc) {
1543     SilcBuffer res_cmd;
1544
1545     /* Send the IDENTIFY command to server */
1546     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1547                                           res_argc, res_argv, res_argv_lens,
1548                                           res_argv_types, ++conn->cmd_ident);
1549     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1550                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1551                             TRUE);
1552
1553     /* Register pending command callback. After we've received the IDENTIFY
1554        command reply we will reprocess this command reply by re-calling this
1555        USERS command reply callback. */
1556     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1557                                 NULL, silc_client_command_reply_users, cmd);
1558
1559     silc_buffer_free(res_cmd);
1560     if (channel_id)
1561       silc_free(channel_id);
1562
1563     silc_free(res_argv);
1564     silc_free(res_argv_lens);
1565     silc_free(res_argv_types);
1566     return;
1567   }
1568
1569   /* Notify application */
1570   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1571
1572   /* Execute any pending command callbacks */
1573   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1574
1575   silc_buffer_free(client_id_list);
1576   silc_buffer_free(client_mode_list);
1577
1578  out:
1579   if (channel_id)
1580     silc_free(channel_id);
1581   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1582   silc_client_command_reply_free(cmd);
1583 }