updates.
[silc.git] / lib / silcclient / command_reply.c
1 /*
2
3   command_reply.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /*
21  * Command reply functions are "the otherside" of the command functions.
22  * Reply to a command sent by server is handled by these functions.
23  *
24  * The arguments received from server are also passed to the calling
25  * application through command_reply client operation.  The arguments are
26  * exactly same and in same order as the server sent it.  However, ID's are
27  * not sent to the application.  Instead, corresponding ID entry is sent
28  * to the application.  For example, instead of sending Client ID the 
29  * corresponding SilcClientEntry is sent to the application.  The case is
30  * same with for example Channel ID's.  This way application has all the
31  * necessary data already in hand without redundant searching.  If ID is
32  * received but ID entry does not exist, NULL is sent.
33  */
34 /* $Id$ */
35
36 #include "clientlibincludes.h"
37 #include "client_internal.h"
38
39 /* Client command reply list. */
40 SilcClientCommandReply silc_command_reply_list[] =
41 {
42   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
43   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
44   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
45   SILC_CLIENT_CMD_REPLY(nick, NICK),
46   SILC_CLIENT_CMD_REPLY(list, LIST),
47   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
48   SILC_CLIENT_CMD_REPLY(invite, INVITE),
49   SILC_CLIENT_CMD_REPLY(kill, KILL),
50   SILC_CLIENT_CMD_REPLY(info, INFO),
51   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
52   SILC_CLIENT_CMD_REPLY(ping, PING),
53   SILC_CLIENT_CMD_REPLY(oper, OPER),
54   SILC_CLIENT_CMD_REPLY(join, JOIN),
55   SILC_CLIENT_CMD_REPLY(motd, MOTD),
56   SILC_CLIENT_CMD_REPLY(umode, UMODE),
57   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
58   SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
59   SILC_CLIENT_CMD_REPLY(kick, KICK),
60   SILC_CLIENT_CMD_REPLY(restart, RESTART),
61   SILC_CLIENT_CMD_REPLY(close, CLOSE),
62   SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN),
63   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
64   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
65   SILC_CLIENT_CMD_REPLY(users, USERS),
66   SILC_CLIENT_CMD_REPLY(ban, BAN),
67
68   { NULL, 0 },
69 };
70
71 const SilcCommandStatusMessage silc_command_status_messages[] = {
72
73   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
74   { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
75   { STAT(NO_SUCH_SERVER),    "No such server" },
76   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
77   { STAT(NO_RECIPIENT),      "No recipient given" },
78   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
79   { STAT(WILDCARDS),         "Unknown command" },
80   { STAT(NO_CLIENT_ID),      "No Client ID given" },
81   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
82   { STAT(NO_SERVER_ID),      "No Server ID given" },
83   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
84   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
85   { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
86   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
87   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
88   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
89   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
90   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
91   { STAT(NOT_REGISTERED),    "You have not registered" },
92   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
93   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
94   { STAT(PERM_DENIED),       "Permission denied" },
95   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
96   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
97   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
98   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
99   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
100   { STAT(UNKNOWN_MODE),    "Unknown mode" },
101   { STAT(NOT_YOU),         "Cannot change mode for other users" },
102   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
103   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
104   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
105   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
106   { STAT(BAD_NICKNAME),    "Bad nickname" },
107   { STAT(BAD_CHANNEL),     "Bad channel name" },
108   { STAT(AUTH_FAILED),     "Authentication failed" },
109   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
110
111   { 0, NULL }
112 };
113 /* Command reply operation that is called at the end of all command replys. 
114    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
115 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
116 #define ARGS cmd->client, cmd->sock->user_data, \
117              cmd->payload, TRUE, silc_command_get(cmd->payload), status
118
119 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
120 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
121   cmd->sock->user_data, cmd->payload, FALSE, \
122   silc_command_get(cmd->payload), status)
123
124 /* Process received command reply. */
125
126 void silc_client_command_reply_process(SilcClient client,
127                                        SilcSocketConnection sock,
128                                        SilcPacketContext *packet)
129 {
130   SilcBuffer buffer = packet->buffer;
131   SilcClientCommandReply *cmd;
132   SilcClientCommandReplyContext ctx;
133   SilcCommandPayload payload;
134   SilcCommand command;
135   unsigned short ident;
136
137   /* Get command reply payload from packet */
138   payload = silc_command_payload_parse(buffer);
139   if (!payload) {
140     /* Silently ignore bad reply packet */
141     SILC_LOG_DEBUG(("Bad command reply packet"));
142     return;
143   }
144   
145   /* Allocate command reply context. This must be free'd by the
146      command reply routine receiving it. */
147   ctx = silc_calloc(1, sizeof(*ctx));
148   ctx->client = client;
149   ctx->sock = sock;
150   ctx->payload = payload;
151   ctx->args = silc_command_get_args(ctx->payload);
152   ctx->packet = packet;
153   ident = silc_command_get_ident(ctx->payload);
154       
155   /* Check for pending commands and mark to be exeucted */
156   silc_client_command_pending_check(sock->user_data, ctx, 
157                                     silc_command_get(ctx->payload), ident);
158
159   /* Execute command reply */
160   command = silc_command_get(ctx->payload);
161   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
162     if (cmd->cmd == command)
163       break;
164
165   if (cmd == NULL || !cmd->cb) {
166     silc_free(ctx);
167     return;
168   }
169
170   cmd->cb(ctx);
171 }
172
173 /* Returns status message string */
174
175 char *silc_client_command_status_message(SilcCommandStatus status)
176 {
177   int i;
178
179   for (i = 0; silc_command_status_messages[i].message; i++) {
180     if (silc_command_status_messages[i].status == status)
181       break;
182   }
183
184   if (silc_command_status_messages[i].message == NULL)
185     return NULL;
186
187   return silc_command_status_messages[i].message;
188 }
189
190 /* Free command reply context and its internals. */
191
192 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
193 {
194   if (cmd) {
195     silc_command_free_payload(cmd->payload);
196     silc_free(cmd);
197   }
198 }
199
200 static void 
201 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
202                                      SilcCommandStatus status)
203 {
204   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
205   SilcClientID *client_id;
206   SilcIDCacheEntry id_cache = NULL;
207   SilcClientEntry client_entry = NULL;
208   int argc, len;
209   unsigned char *id_data, *tmp;
210   char *nickname = NULL, *username = NULL;
211   char *realname = NULL;
212   unsigned int 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   unsigned int 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, len;
434   unsigned char *id_data;
435   char *nickname = NULL, *username = NULL;
436   
437   argc = silc_argument_get_arg_num(cmd->args);
438
439   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
440   if (!id_data) {
441     COMMAND_REPLY_ERROR;
442     return;
443   }
444   
445   client_id = silc_id_payload_parse_id(id_data, len);
446   if (!client_id) {
447     COMMAND_REPLY_ERROR;
448     return;
449   }
450   
451   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
452   username = silc_argument_get_arg_type(cmd->args, 4, &len);
453
454   /* Check if we have this client cached already. */
455   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
456                                    SILC_ID_CLIENT, &id_cache)) {
457     SILC_LOG_DEBUG(("Adding new client entry"));
458
459     client_entry = silc_calloc(1, sizeof(*client_entry));
460     client_entry->id = client_id;
461     silc_parse_nickname(nickname, &client_entry->nickname, 
462                         &client_entry->server, &client_entry->num);
463     if (username)
464       client_entry->username = strdup(username);
465     
466     /* Add client to cache */
467     silc_idcache_add(conn->client_cache, client_entry->nickname,
468                      strlen(client_entry->nickname),
469                      SILC_ID_CLIENT, client_id, (void *)client_entry, 
470                      TRUE, FALSE);
471   } else {
472     client_entry = (SilcClientEntry)id_cache->context;
473     if (client_entry->nickname)
474       silc_free(client_entry->nickname);
475     if (client_entry->server)
476       silc_free(client_entry->server);
477     if (username && client_entry->username)
478       silc_free(client_entry->username);
479     
480     SILC_LOG_DEBUG(("Updating client entry"));
481
482     silc_parse_nickname(nickname, &client_entry->nickname, 
483                         &client_entry->server, &client_entry->num);
484     
485     if (username)
486       client_entry->username = strdup(username);
487     
488     id_cache->data = client_entry->nickname;
489     id_cache->data_len = strlen(client_entry->nickname);
490     silc_idcache_sort_by_data(conn->client_cache);
491     
492     silc_free(client_id);
493   }
494
495   /* Notify application */
496   COMMAND_REPLY((ARGS, client_entry, nickname, username));
497 }
498
499 /* Received reply for IDENTIFY command. This maybe called several times
500    for one IDENTIFY command as server may reply with list of results. 
501    This is totally silent and does not print anything on screen. */
502
503 SILC_CLIENT_CMD_REPLY_FUNC(identify)
504 {
505   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
506   SilcCommandStatus status;
507   unsigned char *tmp;
508
509   SILC_LOG_DEBUG(("Start"));
510
511   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
512   SILC_GET16_MSB(status, tmp);
513   if (status != SILC_STATUS_OK && 
514       status != SILC_STATUS_LIST_START &&
515       status != SILC_STATUS_LIST_ITEM &&
516       status != SILC_STATUS_LIST_END) {
517     COMMAND_REPLY_ERROR;
518     goto out;
519   }
520
521   /* Save one IDENTIFY entry */
522   if (status == SILC_STATUS_OK)
523     silc_client_command_reply_identify_save(cmd, status);
524
525   /* List */
526   if (status == SILC_STATUS_LIST_START ||
527       status == SILC_STATUS_LIST_ITEM ||
528       status == SILC_STATUS_LIST_END)
529     silc_client_command_reply_identify_save(cmd, status);
530
531   /* Pending callbacks are not executed if this was an list entry */
532   if (status != SILC_STATUS_OK &&
533       status != SILC_STATUS_LIST_END) {
534     silc_client_command_reply_free(cmd);
535     return;
536   }
537
538   /* Execute any pending command callbacks */
539   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
540
541  out:
542   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
543   silc_client_command_reply_free(cmd);
544 }
545
546 /* Received reply for command NICK. If everything went without errors
547    we just received our new Client ID. */
548
549 SILC_CLIENT_CMD_REPLY_FUNC(nick)
550 {
551   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
552   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
553   SilcCommandStatus status;
554   SilcIDPayload idp;
555   unsigned char *tmp;
556   unsigned int argc, len;
557
558   SILC_LOG_DEBUG(("Start"));
559
560   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
561   if (status != SILC_STATUS_OK) {
562     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
563              silc_client_command_status_message(status));
564     COMMAND_REPLY_ERROR;
565     goto out;
566   }
567
568   argc = silc_argument_get_arg_num(cmd->args);
569   if (argc < 2 || argc > 2) {
570     cmd->client->ops->say(cmd->client, conn, 
571                           "Cannot set nickname: bad reply to command");
572     COMMAND_REPLY_ERROR;
573     goto out;
574   }
575
576   /* Take received Client ID */
577   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
578   idp = silc_id_payload_parse_data(tmp, len);
579   if (!idp) {
580     COMMAND_REPLY_ERROR;
581     goto out;
582   }
583   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
584     
585   /* Notify application */
586   COMMAND_REPLY((ARGS, conn->local_entry));
587
588   /* Execute any pending command callbacks */
589   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
590
591  out:
592   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
593   silc_client_command_reply_free(cmd);
594 }
595
596 /* Received reply to the LIST command. */
597
598 SILC_CLIENT_CMD_REPLY_FUNC(list)
599 {
600   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
601   SilcCommandStatus status;
602   unsigned char *tmp, *name, *topic;
603   unsigned int usercount = 0;
604
605   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
606   SILC_GET16_MSB(status, tmp);
607   if (status != SILC_STATUS_OK && 
608       status != SILC_STATUS_LIST_START &&
609       status != SILC_STATUS_LIST_ITEM &&
610       status != SILC_STATUS_LIST_END) {
611     COMMAND_REPLY_ERROR;
612     goto out;
613   }
614
615   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
616   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
617   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
618   if (tmp)
619     SILC_GET32_MSB(usercount, tmp);
620
621   /* Notify application */
622   COMMAND_REPLY((ARGS, NULL, name, topic, usercount));
623
624   /* Pending callbacks are not executed if this was an list entry */
625   if (status != SILC_STATUS_OK &&
626       status != SILC_STATUS_LIST_END) {
627     silc_client_command_reply_free(cmd);
628     return;
629   }
630
631   /* Execute any pending command callbacks */
632   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
633
634  out:
635   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LIST);
636   silc_client_command_reply_free(cmd);
637 }
638
639 /* Received reply to topic command. */
640
641 SILC_CLIENT_CMD_REPLY_FUNC(topic)
642 {
643   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
644   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
645   SilcCommandStatus status;
646   SilcChannelEntry channel;
647   SilcChannelID *channel_id = NULL;
648   SilcIDCacheEntry id_cache = NULL;
649   unsigned char *tmp;
650   char *topic;
651   unsigned int argc, len;
652
653   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
654   if (status != SILC_STATUS_OK) {
655     cmd->client->ops->say(cmd->client, conn,
656              "%s", silc_client_command_status_message(status));
657     COMMAND_REPLY_ERROR;
658     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
659     silc_client_command_reply_free(cmd);
660     return;
661   }
662
663   argc = silc_argument_get_arg_num(cmd->args);
664   if (argc < 1 || argc > 3) {
665     COMMAND_REPLY_ERROR;
666     goto out;
667   }
668
669   /* Take Channel ID */
670   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
671   if (!tmp)
672     goto out;
673
674   /* Take topic */
675   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
676   if (!topic)
677     goto out;
678
679   channel_id = silc_id_payload_parse_id(tmp, len);
680   if (!channel_id)
681     goto out;
682
683   /* Get the channel entry */
684   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
685                                    SILC_ID_CHANNEL, &id_cache)) {
686     silc_free(channel_id);
687     COMMAND_REPLY_ERROR;
688     goto out;
689   }
690   
691   channel = (SilcChannelEntry)id_cache->context;
692
693   cmd->client->ops->say(cmd->client, conn, 
694                         "Topic on channel %s: %s", channel->channel_name,
695                         topic);
696
697   /* Notify application */
698   COMMAND_REPLY((ARGS, channel, topic));
699
700   /* Execute any pending command callbacks */
701   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
702
703  out:
704   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
705   silc_client_command_reply_free(cmd);
706 }
707
708 /* Received reply to invite command. */
709
710 SILC_CLIENT_CMD_REPLY_FUNC(invite)
711 {
712   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
713   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
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_INVITE);
724     silc_client_command_reply_free(cmd);
725     return;
726   }
727
728   /* Notify application */
729   COMMAND_REPLY((ARGS));
730
731   /* Execute any pending command callbacks */
732   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
733
734   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
735   silc_client_command_reply_free(cmd);
736 }
737
738 /* Received reply to the KILL command. */
739  
740 SILC_CLIENT_CMD_REPLY_FUNC(kill)
741 {
742   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
743   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
744   SilcCommandStatus status;
745   unsigned char *tmp;
746
747   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
748   SILC_GET16_MSB(status, tmp);
749   if (status != SILC_STATUS_OK) {
750     cmd->client->ops->say(cmd->client, conn,
751              "%s", silc_client_command_status_message(status));
752     COMMAND_REPLY_ERROR;
753     goto out;
754   }
755
756   /* Notify application */
757   COMMAND_REPLY((ARGS));
758
759   /* Execute any pending command callbacks */
760   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
761
762  out:
763   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KILL);
764   silc_client_command_reply_free(cmd);
765 }
766
767 /* Received reply to INFO command. We receive the server ID and some
768    information about the server user requested. */
769
770 SILC_CLIENT_CMD_REPLY_FUNC(info)
771 {
772   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
773   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
774   SilcClient client = cmd->client;
775   SilcCommandStatus status;
776   unsigned char *tmp;
777
778   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
779   SILC_GET16_MSB(status, tmp);
780   if (status != SILC_STATUS_OK) {
781     cmd->client->ops->say(cmd->client, conn,
782              "%s", silc_client_command_status_message(status));
783     COMMAND_REPLY_ERROR;
784     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
785     silc_client_command_reply_free(cmd);
786     return;
787   }
788
789   /* Get server ID */
790   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
791   if (!tmp)
792     goto out;
793
794   /* XXX save server id */
795
796   /* Get server name */
797   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
798   if (!tmp)
799     goto out;
800
801   /* Get server info */
802   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
803   if (!tmp)
804     goto out;
805
806   client->ops->say(cmd->client, conn, "Info: %s", tmp);
807
808   /* Notify application */
809   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
810
811   /* Execute any pending command callbacks */
812   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
813
814  out:
815   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
816   silc_client_command_reply_free(cmd);
817 }
818
819 /* Received reply to PING command. The reply time is shown to user. */
820
821 SILC_CLIENT_CMD_REPLY_FUNC(ping)
822 {
823   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
824   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
825   SilcCommandStatus status;
826   void *id;
827   int i;
828   time_t diff, curtime;
829
830   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
831   if (status != SILC_STATUS_OK) {
832     cmd->client->ops->say(cmd->client, conn,
833              "%s", silc_client_command_status_message(status));
834     COMMAND_REPLY_ERROR;
835     goto out;
836   }
837
838   curtime = time(NULL);
839   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
840                       cmd->packet->src_id_type);
841   if (!id) {
842     COMMAND_REPLY_ERROR;
843     goto out;
844   }
845
846   for (i = 0; i < conn->ping_count; i++) {
847     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
848       diff = curtime - conn->ping[i].start_time;
849       cmd->client->ops->say(cmd->client, conn, 
850                             "Ping reply from %s: %d second%s", 
851                             conn->ping[i].dest_name, diff, 
852                             diff == 1 ? "" : "s");
853       
854       conn->ping[i].start_time = 0;
855       silc_free(conn->ping[i].dest_id);
856       conn->ping[i].dest_id = NULL;
857       silc_free(conn->ping[i].dest_name);
858       conn->ping[i].dest_name = NULL;
859       break;
860     }
861   }
862
863   silc_free(id);
864
865   /* Notify application */
866   COMMAND_REPLY((ARGS));
867
868   /* Execute any pending command callbacks */
869   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
870
871  out:
872   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
873   silc_client_command_reply_free(cmd);
874 }
875
876 /* Received reply for JOIN command. */
877
878 SILC_CLIENT_CMD_REPLY_FUNC(join)
879 {
880   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
881   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
882   SilcCommandStatus status;
883   SilcIDPayload idp = NULL;
884   SilcChannelEntry channel;
885   SilcIDCacheEntry id_cache = NULL;
886   SilcChannelUser chu;
887   unsigned int argc, mode, len, list_count;
888   char *topic, *tmp, *channel_name = NULL, *hmac;
889   SilcBuffer keyp, client_id_list, client_mode_list;
890   int i;
891
892   SILC_LOG_DEBUG(("Start"));
893
894   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
895   if (status != SILC_STATUS_OK) {
896     cmd->client->ops->say(cmd->client, conn,
897              "%s", silc_client_command_status_message(status));
898     COMMAND_REPLY_ERROR;
899     goto out;
900   }
901
902   argc = silc_argument_get_arg_num(cmd->args);
903   if (argc < 7 || argc > 14) {
904     cmd->client->ops->say(cmd->client, conn,
905              "Cannot join channel: Bad reply packet");
906     COMMAND_REPLY_ERROR;
907     goto out;
908   }
909
910   /* Get channel name */
911   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
912   if (!tmp) {
913     cmd->client->ops->say(cmd->client, conn, 
914                           "Cannot join channel: Bad reply packet");
915     COMMAND_REPLY_ERROR;
916     goto out;
917   }
918   channel_name = strdup(tmp);
919
920   /* Get Channel ID */
921   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
922   if (!tmp) {
923     cmd->client->ops->say(cmd->client, conn, 
924                           "Cannot join channel: Bad reply packet");
925     COMMAND_REPLY_ERROR;
926     silc_free(channel_name);
927     goto out;
928   }
929   idp = silc_id_payload_parse_data(tmp, len);
930   if (!idp) {
931     COMMAND_REPLY_ERROR;
932     silc_free(channel_name);
933     goto out;
934   }
935
936   /* Get channel mode */
937   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
938   if (tmp)
939     SILC_GET32_MSB(mode, tmp);
940   else
941     mode = 0;
942
943   /* Get channel key */
944   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
945   if (!tmp) {
946     silc_id_payload_free(idp);
947     silc_free(channel_name);
948     goto out;
949   }
950   keyp = silc_buffer_alloc(len);
951   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
952   silc_buffer_put(keyp, tmp, len);
953
954   /* Get topic */
955   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
956
957   /* Save received Channel ID. This actually creates the channel */
958   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
959                                        mode, idp);
960   silc_id_payload_free(idp);
961
962   /* Get hmac */
963   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
964   if (hmac) {
965     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
966       cmd->client->ops->say(cmd->client, conn, 
967                             "Cannot join channel: Unsupported HMAC `%s'",
968                             hmac);
969       COMMAND_REPLY_ERROR;
970       silc_free(channel_name);
971       goto out;
972     }
973   }
974
975   /* Get the list count */
976   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
977   if (!tmp)
978     goto out;
979   SILC_GET32_MSB(list_count, tmp);
980
981   /* Get Client ID list */
982   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
983   if (!tmp)
984     goto out;
985
986   client_id_list = silc_buffer_alloc(len);
987   silc_buffer_pull_tail(client_id_list, len);
988   silc_buffer_put(client_id_list, tmp, len);
989
990   /* Get client mode list */
991   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
992   if (!tmp)
993     goto out;
994
995   client_mode_list = silc_buffer_alloc(len);
996   silc_buffer_pull_tail(client_mode_list, len);
997   silc_buffer_put(client_mode_list, tmp, len);
998
999   /* Add clients we received in the reply to the channel */
1000   for (i = 0; i < list_count; i++) {
1001     unsigned short idp_len;
1002     unsigned int mode;
1003     SilcClientID *client_id;
1004     SilcClientEntry client_entry;
1005
1006     /* Client ID */
1007     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1008     idp_len += 4;
1009     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1010     if (!client_id)
1011       continue;
1012
1013     /* Mode */
1014     SILC_GET32_MSB(mode, client_mode_list->data);
1015
1016     /* Check if we have this client cached already. */
1017     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1018                                      SILC_ID_CLIENT, &id_cache)) {
1019       /* No, we don't have it, add entry for it. */
1020       client_entry = silc_calloc(1, sizeof(*client_entry));
1021       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
1022       silc_idcache_add(conn->client_cache, NULL, 0, SILC_ID_CLIENT, 
1023                        client_entry->id, (void *)client_entry, FALSE, FALSE);
1024     } else {
1025       /* Yes, we have it already */
1026       client_entry = (SilcClientEntry)id_cache->context;
1027     }
1028
1029     /* Join the client to the channel */
1030     chu = silc_calloc(1, sizeof(*chu));
1031     chu->client = client_entry;
1032     chu->mode = mode;
1033     silc_list_add(channel->clients, chu);
1034     silc_free(client_id);
1035
1036     silc_buffer_pull(client_id_list, idp_len);
1037     silc_buffer_pull(client_mode_list, 4);
1038   }
1039   silc_buffer_push(client_id_list, client_id_list->data - 
1040                    client_id_list->head);
1041   silc_buffer_push(client_mode_list, client_mode_list->data - 
1042                    client_mode_list->head);
1043
1044   /* Save channel key */
1045   silc_client_save_channel_key(conn, keyp, channel);
1046
1047   /* Notify application */
1048   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
1049                  NULL, topic, hmac, list_count, client_id_list, 
1050                  client_mode_list));
1051
1052   /* Execute any pending command callbacks */
1053   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1054
1055   silc_buffer_free(keyp);
1056   silc_buffer_free(client_id_list);
1057   silc_buffer_free(client_mode_list);
1058
1059  out:
1060   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
1061   silc_client_command_reply_free(cmd);
1062 }
1063
1064 /* Received reply for MOTD command */
1065
1066 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1067 {
1068   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1069   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1070   SilcCommandStatus status;
1071   unsigned int argc, i;
1072   unsigned char *tmp;
1073   char *motd = NULL, *cp, line[256];
1074
1075   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1076   SILC_GET16_MSB(status, tmp);
1077   if (status != SILC_STATUS_OK) {
1078     cmd->client->ops->say(cmd->client, conn,
1079              "%s", silc_client_command_status_message(status));
1080     COMMAND_REPLY_ERROR;
1081     return;
1082   }
1083
1084   argc = silc_argument_get_arg_num(cmd->args);
1085   if (argc > 3) {
1086     COMMAND_REPLY_ERROR;
1087     goto out;
1088   }
1089
1090   if (argc == 3) {
1091     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1092     if (!motd) {
1093       COMMAND_REPLY_ERROR;
1094       goto out;
1095     }
1096
1097     i = 0;
1098     cp = motd;
1099     while(cp[i] != 0) {
1100       if (cp[i++] == '\n') {
1101         memset(line, 0, sizeof(line));
1102         strncat(line, cp, i - 1);
1103         cp += i;
1104         
1105         if (i == 2)
1106           line[0] = ' ';
1107         
1108         cmd->client->ops->say(cmd->client, conn, "%s", line);
1109         
1110         if (!strlen(cp))
1111           break;
1112         i = 0;
1113       }
1114     }
1115   }
1116
1117   /* Notify application */
1118   COMMAND_REPLY((ARGS, motd));
1119
1120   /* Execute any pending command callbacks */
1121   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1122
1123  out:
1124   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1125   silc_client_command_reply_free(cmd);
1126 }
1127
1128 /* Received reply tot he UMODE command. Save the current user mode */
1129
1130 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1131 {
1132   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1133   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1134   SilcCommandStatus status;
1135   unsigned char *tmp;
1136   unsigned int mode;
1137
1138   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1139   SILC_GET16_MSB(status, tmp);
1140   if (status != SILC_STATUS_OK) {
1141     cmd->client->ops->say(cmd->client, conn,
1142              "%s", silc_client_command_status_message(status));
1143     COMMAND_REPLY_ERROR;
1144     goto out;
1145   }
1146
1147   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1148   if (!tmp) {
1149     COMMAND_REPLY_ERROR;
1150     goto out;
1151   }
1152
1153   SILC_GET32_MSB(mode, tmp);
1154   conn->local_entry->mode = mode;
1155
1156   /* Notify application */
1157   COMMAND_REPLY((ARGS, mode));
1158
1159   /* Execute any pending command callbacks */
1160   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1161
1162  out:
1163   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE);
1164   silc_client_command_reply_free(cmd);
1165 }
1166
1167 /* Received reply for CMODE command. */
1168
1169 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1170 {
1171   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1172   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1173   SilcCommandStatus status;
1174   unsigned char *tmp;
1175
1176   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1177   if (status != SILC_STATUS_OK) {
1178     cmd->client->ops->say(cmd->client, conn,
1179              "%s", silc_client_command_status_message(status));
1180     COMMAND_REPLY_ERROR;
1181     goto out;
1182   }
1183
1184   /* Get channel mode */
1185   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1186   if (!tmp) {
1187     COMMAND_REPLY_ERROR;
1188     goto out;
1189   }
1190
1191   /* Notify application */
1192   COMMAND_REPLY((ARGS, tmp));
1193
1194   /* Execute any pending command callbacks */
1195   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1196
1197  out:
1198   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1199   silc_client_command_reply_free(cmd);
1200 }
1201
1202 /* Received reply for CUMODE command */
1203
1204 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1205 {
1206   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1207   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1208   SilcCommandStatus status;
1209   SilcIDCacheEntry id_cache = NULL;
1210   SilcClientID *client_id;
1211   unsigned char *tmp, *id;
1212   unsigned int len;
1213   
1214   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1215   if (status != SILC_STATUS_OK) {
1216     cmd->client->ops->say(cmd->client, conn,
1217              "%s", silc_client_command_status_message(status));
1218     COMMAND_REPLY_ERROR;
1219     goto out;
1220   }
1221   
1222   /* Get channel mode */
1223   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1224   if (!tmp) {
1225     COMMAND_REPLY_ERROR;
1226     goto out;
1227   }
1228
1229   /* Get Client ID */
1230   id = silc_argument_get_arg_type(cmd->args, 3, &len);
1231   if (!id) {
1232     COMMAND_REPLY_ERROR;
1233     goto out;
1234   }
1235   client_id = silc_id_payload_parse_id(id, len);
1236   if (!client_id) {
1237     COMMAND_REPLY_ERROR;
1238     goto out;
1239   }
1240   
1241   /* Get client entry */
1242   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1243                                    SILC_ID_CLIENT, &id_cache)) {
1244     COMMAND_REPLY_ERROR;
1245     goto out;
1246   }
1247
1248   /* Notify application */
1249   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1250   silc_free(client_id);
1251   
1252   /* Execute any pending command callbacks */
1253   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1254
1255  out:
1256   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1257   silc_client_command_reply_free(cmd);
1258 }
1259
1260 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1261 {
1262   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1263   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1264   SilcCommandStatus status;
1265   unsigned char *tmp;
1266
1267   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1268   SILC_GET16_MSB(status, tmp);
1269   if (status != SILC_STATUS_OK) {
1270     cmd->client->ops->say(cmd->client, conn,
1271              "%s", silc_client_command_status_message(status));
1272     COMMAND_REPLY_ERROR;
1273     goto out;
1274   }
1275
1276   /* Notify application */
1277   COMMAND_REPLY((ARGS));
1278
1279   /* Execute any pending command callbacks */
1280   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1281
1282  out:
1283   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1284   silc_client_command_reply_free(cmd);
1285 }
1286
1287 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1288 {
1289   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1290   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1291   SilcCommandStatus status;
1292   unsigned char *tmp;
1293
1294   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1295   SILC_GET16_MSB(status, tmp);
1296   if (status != SILC_STATUS_OK) {
1297     cmd->client->ops->say(cmd->client, conn,
1298              "%s", silc_client_command_status_message(status));
1299     COMMAND_REPLY_ERROR;
1300     goto out;
1301   }
1302
1303   /* Notify application */
1304   COMMAND_REPLY((ARGS));
1305
1306   /* Execute any pending command callbacks */
1307   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1308
1309  out:
1310   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1311   silc_client_command_reply_free(cmd);
1312 }
1313
1314 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1315 {
1316   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1317   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1318   SilcCommandStatus status;
1319   unsigned char *tmp;
1320
1321   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1322   SILC_GET16_MSB(status, tmp);
1323   if (status != SILC_STATUS_OK) {
1324     cmd->client->ops->say(cmd->client, conn,
1325              "%s", silc_client_command_status_message(status));
1326     COMMAND_REPLY_ERROR;
1327     goto out;
1328   }
1329
1330   /* Notify application */
1331   COMMAND_REPLY((ARGS));
1332
1333   /* Execute any pending command callbacks */
1334   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1335
1336  out:
1337   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1338   silc_client_command_reply_free(cmd);
1339 }
1340
1341 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1342 {
1343   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1344   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1345   SilcCommandStatus status;
1346   unsigned char *tmp;
1347
1348   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1349   SILC_GET16_MSB(status, tmp);
1350   if (status != SILC_STATUS_OK) {
1351     cmd->client->ops->say(cmd->client, conn,
1352              "%s", silc_client_command_status_message(status));
1353     COMMAND_REPLY_ERROR;
1354     goto out;
1355   }
1356
1357   /* Notify application */
1358   COMMAND_REPLY((ARGS));
1359
1360   /* Execute any pending command callbacks */
1361   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1362
1363  out:
1364   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1365   silc_client_command_reply_free(cmd);
1366 }
1367
1368 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1369 {
1370   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1371   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1372   SilcCommandStatus status;
1373   unsigned char *tmp;
1374
1375   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1376   SILC_GET16_MSB(status, tmp);
1377   if (status != SILC_STATUS_OK) {
1378     cmd->client->ops->say(cmd->client, conn,
1379              "%s", silc_client_command_status_message(status));
1380     COMMAND_REPLY_ERROR;
1381     goto out;
1382   }
1383
1384   /* Notify application */
1385   COMMAND_REPLY((ARGS));
1386
1387   /* Execute any pending command callbacks */
1388   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1389
1390  out:
1391   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1392   silc_client_command_reply_free(cmd);
1393 }
1394  
1395 SILC_CLIENT_CMD_REPLY_FUNC(close)
1396 {
1397   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1398   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1399   SilcCommandStatus status;
1400   unsigned char *tmp;
1401
1402   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1403   SILC_GET16_MSB(status, tmp);
1404   if (status != SILC_STATUS_OK) {
1405     cmd->client->ops->say(cmd->client, conn,
1406              "%s", silc_client_command_status_message(status));
1407     COMMAND_REPLY_ERROR;
1408     goto out;
1409   }
1410
1411   /* Notify application */
1412   COMMAND_REPLY((ARGS));
1413
1414   /* Execute any pending command callbacks */
1415   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1416
1417  out:
1418   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1419   silc_client_command_reply_free(cmd);
1420 }
1421  
1422 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1423 {
1424   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1425   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1426   SilcCommandStatus status;
1427   unsigned char *tmp;
1428
1429   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1430   SILC_GET16_MSB(status, tmp);
1431   if (status != SILC_STATUS_OK) {
1432     cmd->client->ops->say(cmd->client, conn,
1433              "%s", silc_client_command_status_message(status));
1434     COMMAND_REPLY_ERROR;
1435     goto out;
1436   }
1437
1438   /* Notify application */
1439   COMMAND_REPLY((ARGS));
1440
1441   /* Execute any pending command callbacks */
1442   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1443
1444  out:
1445   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1446   silc_client_command_reply_free(cmd);
1447 }
1448  
1449 /* Reply to LEAVE command. */
1450
1451 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1452 {
1453   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1454   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1455   SilcCommandStatus status;
1456   unsigned char *tmp;
1457
1458   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1459   SILC_GET16_MSB(status, tmp);
1460   if (status != SILC_STATUS_OK) {
1461     cmd->client->ops->say(cmd->client, conn,
1462              "%s", silc_client_command_status_message(status));
1463     COMMAND_REPLY_ERROR;
1464     goto out;
1465   }
1466
1467   /* Notify application */
1468   COMMAND_REPLY((ARGS));
1469
1470   /* Execute any pending command callbacks */
1471   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1472
1473  out:
1474   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1475   silc_client_command_reply_free(cmd);
1476 }
1477
1478 /* Reply to USERS command. Received list of client ID's and theirs modes
1479    on the channel we requested. */
1480
1481 SILC_CLIENT_CMD_REPLY_FUNC(users)
1482 {
1483   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1484   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1485   SilcCommandStatus status;
1486   SilcIDCacheEntry id_cache = NULL;
1487   SilcChannelEntry channel;
1488   SilcChannelUser chu;
1489   SilcChannelID *channel_id = NULL;
1490   SilcBuffer client_id_list;
1491   SilcBuffer client_mode_list;
1492   unsigned char *tmp;
1493   unsigned int tmp_len, list_count;
1494   int i;
1495   unsigned char **res_argv = NULL;
1496   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1497
1498   SILC_LOG_DEBUG(("Start"));
1499
1500   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1501   SILC_GET16_MSB(status, tmp);
1502   if (status != SILC_STATUS_OK) {
1503     cmd->client->ops->say(cmd->client, conn,
1504              "%s", silc_client_command_status_message(status));
1505     COMMAND_REPLY_ERROR;
1506     goto out;
1507   }
1508
1509   /* Get channel ID */
1510   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1511   if (!tmp)
1512     goto out;
1513   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1514   if (!channel_id)
1515     goto out;
1516
1517   /* Get the list count */
1518   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1519   if (!tmp)
1520     goto out;
1521   SILC_GET32_MSB(list_count, tmp);
1522
1523   /* Get Client ID list */
1524   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1525   if (!tmp)
1526     goto out;
1527
1528   client_id_list = silc_buffer_alloc(tmp_len);
1529   silc_buffer_pull_tail(client_id_list, tmp_len);
1530   silc_buffer_put(client_id_list, tmp, tmp_len);
1531
1532   /* Get client mode list */
1533   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1534   if (!tmp)
1535     goto out;
1536
1537   client_mode_list = silc_buffer_alloc(tmp_len);
1538   silc_buffer_pull_tail(client_mode_list, tmp_len);
1539   silc_buffer_put(client_mode_list, tmp, tmp_len);
1540
1541   /* Get channel entry */
1542   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1543                                    SILC_ID_CHANNEL, &id_cache)) {
1544     COMMAND_REPLY_ERROR;
1545     goto out;
1546   }
1547   channel = (SilcChannelEntry)id_cache->context;
1548
1549   /* Remove old client list from channel. */
1550   silc_list_start(channel->clients);
1551   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1552     silc_list_del(channel->clients, chu);
1553     silc_free(chu);
1554   }
1555
1556   /* Cache the received Client ID's and modes. This cache expires
1557      whenever server sends notify message to channel. It means two things;
1558      some user has joined or leaved the channel. XXX! */
1559   for (i = 0; i < list_count; i++) {
1560     unsigned short idp_len;
1561     unsigned int mode;
1562     SilcClientID *client_id;
1563     SilcClientEntry client;
1564
1565     /* Client ID */
1566     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1567     idp_len += 4;
1568     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1569     if (!client_id)
1570       continue;
1571
1572     /* Mode */
1573     SILC_GET32_MSB(mode, client_mode_list->data);
1574
1575     /* Check if we have this client cached already. */
1576     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1577                                      SILC_ID_CLIENT, &id_cache)) {
1578       /* No we don't have it, query it from the server. Assemble argument
1579          table that will be sent fr the IDENTIFY command later. */
1580       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1581                               (res_argc + 1));
1582       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1583                                    (res_argc + 1));
1584       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1585                                     (res_argc + 1));
1586       res_argv[res_argc] = client_id_list->data;
1587       res_argv_lens[res_argc] = idp_len;
1588       res_argv_types[res_argc] = res_argc + 3;
1589       res_argc++;
1590     } else {
1591       /* Found the client, join it to the channel */
1592       client = (SilcClientEntry)id_cache->context;
1593       chu = silc_calloc(1, sizeof(*chu));
1594       chu->client = client;
1595       chu->mode = mode;
1596       silc_list_add(channel->clients, chu);
1597
1598       silc_free(client_id);
1599       id_cache = NULL;
1600     }
1601
1602     silc_buffer_pull(client_id_list, idp_len);
1603     silc_buffer_pull(client_mode_list, 4);
1604   }
1605
1606   /* Query the client information from server if the list included clients
1607      that we don't know about. */
1608   if (res_argc) {
1609     SilcBuffer res_cmd;
1610
1611     /* Send the IDENTIFY command to server */
1612     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1613                                           res_argc, res_argv, res_argv_lens,
1614                                           res_argv_types, ++conn->cmd_ident);
1615     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1616                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1617                             TRUE);
1618
1619     /* Register pending command callback. After we've received the IDENTIFY
1620        command reply we will reprocess this command reply by re-calling this
1621        USERS command reply callback. */
1622     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1623                                 NULL, silc_client_command_reply_users, cmd);
1624
1625     silc_buffer_free(res_cmd);
1626     if (channel_id)
1627       silc_free(channel_id);
1628
1629     silc_free(res_argv);
1630     silc_free(res_argv_lens);
1631     silc_free(res_argv_types);
1632     return;
1633   }
1634
1635   /* Notify application */
1636   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1637
1638   /* Execute any pending command callbacks */
1639   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1640
1641   silc_buffer_free(client_id_list);
1642   silc_buffer_free(client_mode_list);
1643
1644  out:
1645   if (channel_id)
1646     silc_free(channel_id);
1647   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1648   silc_client_command_reply_free(cmd);
1649 }
1650
1651 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1652 {
1653   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1654   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1655   SilcCommandStatus status;
1656   SilcIDCacheEntry id_cache = NULL;
1657   SilcChannelEntry channel;
1658   SilcChannelID *channel_id;
1659   unsigned char *tmp;
1660   unsigned int len;
1661
1662   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1663   SILC_GET16_MSB(status, tmp);
1664   if (status != SILC_STATUS_OK) {
1665     cmd->client->ops->say(cmd->client, conn,
1666              "%s", silc_client_command_status_message(status));
1667     COMMAND_REPLY_ERROR;
1668     goto out;
1669   }
1670
1671   /* Take Channel ID */
1672   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1673   if (!tmp)
1674     goto out;
1675
1676   channel_id = silc_id_payload_parse_id(tmp, len);
1677   if (!channel_id)
1678     goto out;
1679
1680   /* Get the channel entry */
1681   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1682                                    SILC_ID_CHANNEL, &id_cache)) {
1683     silc_free(channel_id);
1684     COMMAND_REPLY_ERROR;
1685     goto out;
1686   }
1687   
1688   channel = (SilcChannelEntry)id_cache->context;
1689
1690   /* Get the ban list */
1691   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1692
1693   /* Notify application */
1694   COMMAND_REPLY((ARGS, channel, tmp));
1695
1696   /* Execute any pending command callbacks */
1697   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1698
1699  out:
1700   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_BAN);
1701   silc_client_command_reply_free(cmd);
1702 }