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