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, 
1047                                          (void *)client_id, 
1048                                          NULL, NULL, 
1049                                          silc_hash_client_id_compare, NULL,
1050                                          &id_cache)) {
1051       /* No, we don't have it, add entry for it. */
1052       client_entry = silc_calloc(1, sizeof(*client_entry));
1053       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
1054       silc_idcache_add(conn->client_cache, NULL, client_entry->id, 
1055                        (void *)client_entry, FALSE);
1056     } else {
1057       /* Yes, we have it already */
1058       client_entry = (SilcClientEntry)id_cache->context;
1059     }
1060
1061     /* Join the client to the channel */
1062     chu = silc_calloc(1, sizeof(*chu));
1063     chu->client = client_entry;
1064     chu->mode = mode;
1065     silc_list_add(channel->clients, chu);
1066     silc_free(client_id);
1067
1068     silc_buffer_pull(client_id_list, idp_len);
1069     silc_buffer_pull(client_mode_list, 4);
1070   }
1071   silc_buffer_push(client_id_list, client_id_list->data - 
1072                    client_id_list->head);
1073   silc_buffer_push(client_mode_list, client_mode_list->data - 
1074                    client_mode_list->head);
1075
1076   /* Save channel key */
1077   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1078     silc_client_save_channel_key(conn, keyp, channel);
1079
1080   /* Client is now joined to the channel */
1081   channel->on_channel = TRUE;
1082
1083   /* Notify application */
1084   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, 
1085                  keyp ? keyp->head : NULL, NULL,
1086                  NULL, topic, hmac, list_count, client_id_list, 
1087                  client_mode_list));
1088
1089   /* Execute any pending command callbacks */
1090   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1091
1092   if (keyp)
1093     silc_buffer_free(keyp);
1094   silc_buffer_free(client_id_list);
1095   silc_buffer_free(client_mode_list);
1096
1097  out:
1098   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
1099   silc_client_command_reply_free(cmd);
1100 }
1101
1102 /* Received reply for MOTD command */
1103
1104 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1105 {
1106   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1107   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1108   SilcCommandStatus status;
1109   uint32 argc, i;
1110   unsigned char *tmp;
1111   char *motd = NULL, *cp, line[256];
1112
1113   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1114   SILC_GET16_MSB(status, tmp);
1115   if (status != SILC_STATUS_OK) {
1116     cmd->client->ops->say(cmd->client, conn,
1117              "%s", silc_client_command_status_message(status));
1118     COMMAND_REPLY_ERROR;
1119     return;
1120   }
1121
1122   argc = silc_argument_get_arg_num(cmd->args);
1123   if (argc > 3) {
1124     COMMAND_REPLY_ERROR;
1125     goto out;
1126   }
1127
1128   if (argc == 3) {
1129     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1130     if (!motd) {
1131       COMMAND_REPLY_ERROR;
1132       goto out;
1133     }
1134
1135     i = 0;
1136     cp = motd;
1137     while(cp[i] != 0) {
1138       if (cp[i++] == '\n') {
1139         memset(line, 0, sizeof(line));
1140         strncat(line, cp, i - 1);
1141         cp += i;
1142         
1143         if (i == 2)
1144           line[0] = ' ';
1145         
1146         cmd->client->ops->say(cmd->client, conn, "%s", line);
1147         
1148         if (!strlen(cp))
1149           break;
1150         i = 0;
1151       }
1152     }
1153   }
1154
1155   /* Notify application */
1156   COMMAND_REPLY((ARGS, motd));
1157
1158   /* Execute any pending command callbacks */
1159   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1160
1161  out:
1162   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1163   silc_client_command_reply_free(cmd);
1164 }
1165
1166 /* Received reply tot he UMODE command. Save the current user mode */
1167
1168 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1169 {
1170   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1171   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1172   SilcCommandStatus status;
1173   unsigned char *tmp;
1174   uint32 mode;
1175
1176   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1177   SILC_GET16_MSB(status, tmp);
1178   if (status != SILC_STATUS_OK) {
1179     cmd->client->ops->say(cmd->client, conn,
1180              "%s", silc_client_command_status_message(status));
1181     COMMAND_REPLY_ERROR;
1182     goto out;
1183   }
1184
1185   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1186   if (!tmp) {
1187     COMMAND_REPLY_ERROR;
1188     goto out;
1189   }
1190
1191   SILC_GET32_MSB(mode, tmp);
1192   conn->local_entry->mode = mode;
1193
1194   /* Notify application */
1195   COMMAND_REPLY((ARGS, mode));
1196
1197   /* Execute any pending command callbacks */
1198   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1199
1200  out:
1201   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE);
1202   silc_client_command_reply_free(cmd);
1203 }
1204
1205 /* Received reply for CMODE command. */
1206
1207 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1208 {
1209   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1210   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1211   SilcCommandStatus status;
1212   unsigned char *tmp;
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   /* Notify application */
1230   COMMAND_REPLY((ARGS, tmp));
1231
1232   /* Execute any pending command callbacks */
1233   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1234
1235  out:
1236   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1237   silc_client_command_reply_free(cmd);
1238 }
1239
1240 /* Received reply for CUMODE command */
1241
1242 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1243 {
1244   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1245   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1246   SilcCommandStatus status;
1247   SilcIDCacheEntry id_cache = NULL;
1248   SilcClientID *client_id;
1249   unsigned char *tmp, *id;
1250   uint32 len;
1251   
1252   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1253   if (status != SILC_STATUS_OK) {
1254     cmd->client->ops->say(cmd->client, conn,
1255              "%s", silc_client_command_status_message(status));
1256     COMMAND_REPLY_ERROR;
1257     goto out;
1258   }
1259   
1260   /* Get channel mode */
1261   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1262   if (!tmp) {
1263     COMMAND_REPLY_ERROR;
1264     goto out;
1265   }
1266
1267   /* Get Client ID */
1268   id = silc_argument_get_arg_type(cmd->args, 3, &len);
1269   if (!id) {
1270     COMMAND_REPLY_ERROR;
1271     goto out;
1272   }
1273   client_id = silc_id_payload_parse_id(id, len);
1274   if (!client_id) {
1275     COMMAND_REPLY_ERROR;
1276     goto out;
1277   }
1278   
1279   /* Get client entry */
1280   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
1281                                        NULL, NULL, 
1282                                        silc_hash_client_id_compare, NULL,
1283                                        &id_cache)) {
1284     COMMAND_REPLY_ERROR;
1285     goto out;
1286   }
1287
1288   /* Notify application */
1289   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1290   silc_free(client_id);
1291   
1292   /* Execute any pending command callbacks */
1293   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1294
1295  out:
1296   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1297   silc_client_command_reply_free(cmd);
1298 }
1299
1300 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1301 {
1302   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1303   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1304   SilcCommandStatus status;
1305   unsigned char *tmp;
1306
1307   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1308   SILC_GET16_MSB(status, tmp);
1309   if (status != SILC_STATUS_OK) {
1310     cmd->client->ops->say(cmd->client, conn,
1311              "%s", silc_client_command_status_message(status));
1312     COMMAND_REPLY_ERROR;
1313     goto out;
1314   }
1315
1316   /* Notify application */
1317   COMMAND_REPLY((ARGS));
1318
1319   /* Execute any pending command callbacks */
1320   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1321
1322  out:
1323   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1324   silc_client_command_reply_free(cmd);
1325 }
1326
1327 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1328 {
1329   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1330   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1331   SilcCommandStatus status;
1332   unsigned char *tmp;
1333
1334   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1335   SILC_GET16_MSB(status, tmp);
1336   if (status != SILC_STATUS_OK) {
1337     cmd->client->ops->say(cmd->client, conn,
1338              "%s", silc_client_command_status_message(status));
1339     COMMAND_REPLY_ERROR;
1340     goto out;
1341   }
1342
1343   /* Notify application */
1344   COMMAND_REPLY((ARGS));
1345
1346   /* Execute any pending command callbacks */
1347   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1348
1349  out:
1350   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1351   silc_client_command_reply_free(cmd);
1352 }
1353
1354 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1355 {
1356   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1357   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1358   SilcCommandStatus status;
1359   unsigned char *tmp;
1360
1361   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1362   SILC_GET16_MSB(status, tmp);
1363   if (status != SILC_STATUS_OK) {
1364     cmd->client->ops->say(cmd->client, conn,
1365              "%s", silc_client_command_status_message(status));
1366     COMMAND_REPLY_ERROR;
1367     goto out;
1368   }
1369
1370   /* Notify application */
1371   COMMAND_REPLY((ARGS));
1372
1373   /* Execute any pending command callbacks */
1374   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1375
1376  out:
1377   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1378   silc_client_command_reply_free(cmd);
1379 }
1380
1381 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1382 {
1383   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1384   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1385   SilcCommandStatus status;
1386   unsigned char *tmp;
1387
1388   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1389   SILC_GET16_MSB(status, tmp);
1390   if (status != SILC_STATUS_OK) {
1391     cmd->client->ops->say(cmd->client, conn,
1392              "%s", silc_client_command_status_message(status));
1393     COMMAND_REPLY_ERROR;
1394     goto out;
1395   }
1396
1397   /* Notify application */
1398   COMMAND_REPLY((ARGS));
1399
1400   /* Execute any pending command callbacks */
1401   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1402
1403  out:
1404   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1405   silc_client_command_reply_free(cmd);
1406 }
1407
1408 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1409 {
1410   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1411   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1412   SilcCommandStatus status;
1413   SilcIDCacheEntry id_cache = NULL;
1414   SilcChannelEntry channel;
1415   SilcChannelID *channel_id;
1416   unsigned char *tmp;
1417   uint32 len;
1418
1419   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1420   SILC_GET16_MSB(status, tmp);
1421   if (status != SILC_STATUS_OK) {
1422     cmd->client->ops->say(cmd->client, conn,
1423              "%s", silc_client_command_status_message(status));
1424     COMMAND_REPLY_ERROR;
1425     goto out;
1426   }
1427
1428   /* Take Channel ID */
1429   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1430   if (!tmp)
1431     goto out;
1432
1433   channel_id = silc_id_payload_parse_id(tmp, len);
1434   if (!channel_id)
1435     goto out;
1436
1437   /* Get the channel entry */
1438   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1439                                    &id_cache)) {
1440     silc_free(channel_id);
1441     COMMAND_REPLY_ERROR;
1442     goto out;
1443   }
1444   
1445   channel = (SilcChannelEntry)id_cache->context;
1446
1447   /* Get the ban list */
1448   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1449
1450   /* Notify application */
1451   COMMAND_REPLY((ARGS, channel, tmp));
1452
1453   /* Execute any pending command callbacks */
1454   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1455
1456  out:
1457   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_BAN);
1458   silc_client_command_reply_free(cmd);
1459 }
1460
1461 SILC_CLIENT_CMD_REPLY_FUNC(close)
1462 {
1463   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1464   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1465   SilcCommandStatus status;
1466   unsigned char *tmp;
1467
1468   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1469   SILC_GET16_MSB(status, tmp);
1470   if (status != SILC_STATUS_OK) {
1471     cmd->client->ops->say(cmd->client, conn,
1472              "%s", silc_client_command_status_message(status));
1473     COMMAND_REPLY_ERROR;
1474     goto out;
1475   }
1476
1477   /* Notify application */
1478   COMMAND_REPLY((ARGS));
1479
1480   /* Execute any pending command callbacks */
1481   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1482
1483  out:
1484   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1485   silc_client_command_reply_free(cmd);
1486 }
1487  
1488 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1489 {
1490   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1491   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1492   SilcCommandStatus status;
1493   unsigned char *tmp;
1494
1495   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1496   SILC_GET16_MSB(status, tmp);
1497   if (status != SILC_STATUS_OK) {
1498     cmd->client->ops->say(cmd->client, conn,
1499              "%s", silc_client_command_status_message(status));
1500     COMMAND_REPLY_ERROR;
1501     goto out;
1502   }
1503
1504   /* Notify application */
1505   COMMAND_REPLY((ARGS));
1506
1507   /* Execute any pending command callbacks */
1508   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1509
1510  out:
1511   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1512   silc_client_command_reply_free(cmd);
1513 }
1514  
1515 /* Reply to LEAVE command. */
1516
1517 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1518 {
1519   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1520   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1521   SilcCommandStatus status;
1522   unsigned char *tmp;
1523
1524   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1525   SILC_GET16_MSB(status, tmp);
1526   if (status != SILC_STATUS_OK) {
1527     cmd->client->ops->say(cmd->client, conn,
1528              "%s", silc_client_command_status_message(status));
1529     COMMAND_REPLY_ERROR;
1530     goto out;
1531   }
1532
1533   /* Notify application */
1534   COMMAND_REPLY((ARGS));
1535
1536   /* Execute any pending command callbacks */
1537   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1538
1539  out:
1540   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1541   silc_client_command_reply_free(cmd);
1542 }
1543
1544 /* Reply to USERS command. Received list of client ID's and theirs modes
1545    on the channel we requested. */
1546
1547 SILC_CLIENT_CMD_REPLY_FUNC(users)
1548 {
1549   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1550   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1551   SilcCommandStatus status;
1552   SilcIDCacheEntry id_cache = NULL;
1553   SilcChannelEntry channel;
1554   SilcChannelUser chu;
1555   SilcChannelID *channel_id = NULL;
1556   SilcBuffer client_id_list;
1557   SilcBuffer client_mode_list;
1558   unsigned char *tmp;
1559   uint32 tmp_len, list_count;
1560   int i;
1561   unsigned char **res_argv = NULL;
1562   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1563
1564   SILC_LOG_DEBUG(("Start"));
1565
1566   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1567   SILC_GET16_MSB(status, tmp);
1568   if (status != SILC_STATUS_OK) {
1569     cmd->client->ops->say(cmd->client, conn,
1570              "%s", silc_client_command_status_message(status));
1571     COMMAND_REPLY_ERROR;
1572     goto out;
1573   }
1574
1575   /* Get channel ID */
1576   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1577   if (!tmp)
1578     goto out;
1579   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1580   if (!channel_id)
1581     goto out;
1582
1583   /* Get the list count */
1584   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1585   if (!tmp)
1586     goto out;
1587   SILC_GET32_MSB(list_count, tmp);
1588
1589   /* Get Client ID list */
1590   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1591   if (!tmp)
1592     goto out;
1593
1594   client_id_list = silc_buffer_alloc(tmp_len);
1595   silc_buffer_pull_tail(client_id_list, tmp_len);
1596   silc_buffer_put(client_id_list, tmp, tmp_len);
1597
1598   /* Get client mode list */
1599   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1600   if (!tmp)
1601     goto out;
1602
1603   client_mode_list = silc_buffer_alloc(tmp_len);
1604   silc_buffer_pull_tail(client_mode_list, tmp_len);
1605   silc_buffer_put(client_mode_list, tmp, tmp_len);
1606
1607   /* Get channel entry */
1608   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1609                                    &id_cache)) {
1610     COMMAND_REPLY_ERROR;
1611     goto out;
1612   }
1613   channel = (SilcChannelEntry)id_cache->context;
1614
1615   /* Remove old client list from channel. */
1616   silc_list_start(channel->clients);
1617   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1618     silc_list_del(channel->clients, chu);
1619     silc_free(chu);
1620   }
1621
1622   /* Cache the received Client ID's and modes. This cache expires
1623      whenever server sends notify message to channel. It means two things;
1624      some user has joined or leaved the channel. XXX! */
1625   for (i = 0; i < list_count; i++) {
1626     uint16 idp_len;
1627     uint32 mode;
1628     SilcClientID *client_id;
1629     SilcClientEntry client;
1630
1631     /* Client ID */
1632     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1633     idp_len += 4;
1634     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1635     if (!client_id)
1636       continue;
1637
1638     /* Mode */
1639     SILC_GET32_MSB(mode, client_mode_list->data);
1640
1641     /* Check if we have this client cached already. */
1642     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1643                                          (void *)client_id, 
1644                                          NULL, NULL, 
1645                                          silc_hash_client_id_compare, NULL,
1646                                          &id_cache)) {
1647       /* No we don't have it, query it from the server. Assemble argument
1648          table that will be sent fr the IDENTIFY command later. */
1649       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1650                               (res_argc + 1));
1651       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1652                                    (res_argc + 1));
1653       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1654                                     (res_argc + 1));
1655       res_argv[res_argc] = client_id_list->data;
1656       res_argv_lens[res_argc] = idp_len;
1657       res_argv_types[res_argc] = res_argc + 3;
1658       res_argc++;
1659     } else {
1660       /* Found the client, join it to the channel */
1661       client = (SilcClientEntry)id_cache->context;
1662       chu = silc_calloc(1, sizeof(*chu));
1663       chu->client = client;
1664       chu->mode = mode;
1665       silc_list_add(channel->clients, chu);
1666
1667       silc_free(client_id);
1668       id_cache = NULL;
1669     }
1670
1671     silc_buffer_pull(client_id_list, idp_len);
1672     silc_buffer_pull(client_mode_list, 4);
1673   }
1674
1675   /* Query the client information from server if the list included clients
1676      that we don't know about. */
1677   if (res_argc) {
1678     SilcBuffer res_cmd;
1679
1680     /* Send the IDENTIFY command to server */
1681     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1682                                           res_argc, res_argv, res_argv_lens,
1683                                           res_argv_types, ++conn->cmd_ident);
1684     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1685                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1686                             TRUE);
1687
1688     /* Register pending command callback. After we've received the IDENTIFY
1689        command reply we will reprocess this command reply by re-calling this
1690        USERS command reply callback. */
1691     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1692                                 NULL, silc_client_command_reply_users, cmd);
1693
1694     silc_buffer_free(res_cmd);
1695     if (channel_id)
1696       silc_free(channel_id);
1697
1698     silc_free(res_argv);
1699     silc_free(res_argv_lens);
1700     silc_free(res_argv_types);
1701     return;
1702   }
1703
1704   /* Notify application */
1705   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1706
1707   /* Execute any pending command callbacks */
1708   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1709
1710   silc_buffer_free(client_id_list);
1711   silc_buffer_free(client_mode_list);
1712
1713  out:
1714   if (channel_id)
1715     silc_free(channel_id);
1716   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1717   silc_client_command_reply_free(cmd);
1718 }
1719
1720 /* Received command reply to GETKEY command. WE've received the remote
1721    client's public key. */
1722
1723 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1724 {
1725   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1726   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1727   SilcCommandStatus status;
1728   SilcIDCacheEntry id_cache;
1729   SilcIDPayload idp = NULL;
1730   SilcClientID *client_id = NULL;
1731   SilcClientEntry client_entry;
1732   SilcSKEPKType type;
1733   unsigned char *tmp, *pk;
1734   uint32 len;
1735   uint16 pk_len;
1736   SilcIdType id_type;
1737   SilcPublicKey public_key = NULL;
1738
1739   SILC_LOG_DEBUG(("Start"));
1740
1741   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1742   SILC_GET16_MSB(status, tmp);
1743   if (status != SILC_STATUS_OK) {
1744     cmd->client->ops->say(cmd->client, conn,
1745              "%s", silc_client_command_status_message(status));
1746     COMMAND_REPLY_ERROR;
1747     goto out;
1748   }
1749
1750   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1751   if (!tmp)
1752     goto out;
1753   idp = silc_id_payload_parse_data(tmp, len);
1754   if (!idp)
1755     goto out;
1756
1757   /* Get the public key payload */
1758   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1759   if (!tmp)
1760     goto out;
1761
1762   /* Decode the public key */
1763
1764   SILC_GET16_MSB(pk_len, tmp);
1765   SILC_GET16_MSB(type, tmp + 2);
1766   pk = tmp + 4;
1767
1768   if (type != SILC_SKE_PK_TYPE_SILC)
1769     goto out;
1770
1771   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1772     goto out;
1773
1774   id_type = silc_id_payload_get_type(idp);
1775   if (id_type == SILC_ID_CLIENT) {
1776     client_id = silc_id_payload_get_id(idp);
1777
1778     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1779                                          (void *)client_id, 
1780                                          NULL, NULL, 
1781                                          silc_hash_client_id_compare, NULL,
1782                                          &id_cache))
1783       goto out;
1784
1785     client_entry = (SilcClientEntry)id_cache->context;
1786
1787     /* Notify application */
1788     COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1789   } else if (id_type == SILC_ID_SERVER) {
1790     /* XXX we don't have server entries at all */
1791     goto out;
1792   } else {
1793     goto out;
1794   }
1795
1796  out:
1797   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY);
1798   if (idp)
1799     silc_id_payload_free(idp);
1800   if (public_key)
1801     silc_pkcs_public_key_free(public_key);
1802   silc_free(client_id);
1803   silc_client_command_reply_free(cmd);
1804 }