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