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