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