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