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