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