Code auditing weekend results and fixes committing.
[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 - 2000 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
38 /* Client command reply list. */
39 SilcClientCommandReply silc_command_reply_list[] =
40 {
41   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
42   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
43   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
44   SILC_CLIENT_CMD_REPLY(nick, NICK),
45   SILC_CLIENT_CMD_REPLY(list, LIST),
46   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
47   SILC_CLIENT_CMD_REPLY(invite, INVITE),
48   SILC_CLIENT_CMD_REPLY(quit, QUIT),
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(die, DIE),
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 /* Status message structure. Messages are defined below. */
71 typedef struct {
72   SilcCommandStatus status;
73   char *message;
74 } SilcCommandStatusMessage;
75
76 /* Status messages returned by the server */
77 #define STAT(x) SILC_STATUS_ERR_##x
78 const SilcCommandStatusMessage silc_command_status_messages[] = {
79
80   { STAT(NO_SUCH_NICK),      "No such nickname" },
81   { STAT(NO_SUCH_CHANNEL),   "No such channel" },
82   { STAT(NO_SUCH_SERVER),    "No such server" },
83   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
84   { STAT(NO_RECIPIENT),      "No recipient given" },
85   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
86   { STAT(WILDCARDS),         "Unknown command" },
87   { STAT(NO_CLIENT_ID),      "No Client ID given" },
88   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
89   { STAT(NO_SERVER_ID),      "No Server ID given" },
90   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
91   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
92   { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
93   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
94   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
95   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
96   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
97   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
98   { STAT(NOT_REGISTERED),    "You have not registered" },
99   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
100   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
101   { STAT(PERM_DENIED),       "Your host is not among the privileged" },
102   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
103   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
104   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
105   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
106   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
107   { STAT(UNKNOWN_MODE),    "Unknown mode" },
108   { STAT(NOT_YOU),         "Cannot change mode for other users" },
109   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
110   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
111   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
112   { STAT(BAD_NICKNAME),    "Bad nickname" },
113   { STAT(BAD_CHANNEL),     "Bad channel name" },
114   { STAT(AUTH_FAILED),     "Authentication failed" },
115
116   { 0, NULL }
117 };
118
119 /* Command reply operation that is called at the end of all command replys. 
120    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
121 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
122 #define ARGS cmd->client, cmd->sock->user_data, \
123              cmd->payload, TRUE, silc_command_get(cmd->payload), status
124
125 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
126 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
127   cmd->sock->user_data, cmd->payload, FALSE, \
128   silc_command_get(cmd->payload), status)
129
130 /* Process received command reply. */
131
132 void silc_client_command_reply_process(SilcClient client,
133                                        SilcSocketConnection sock,
134                                        SilcPacketContext *packet)
135 {
136   SilcBuffer buffer = packet->buffer;
137   SilcClientCommandReply *cmd;
138   SilcClientCommandReplyContext ctx;
139   SilcCommandPayload payload;
140   SilcCommand command;
141   unsigned short ident;
142
143   /* Get command reply payload from packet */
144   payload = silc_command_payload_parse(buffer);
145   if (!payload) {
146     /* Silently ignore bad reply packet */
147     SILC_LOG_DEBUG(("Bad command reply packet"));
148     return;
149   }
150   
151   /* Allocate command reply context. This must be free'd by the
152      command reply routine receiving it. */
153   ctx = silc_calloc(1, sizeof(*ctx));
154   ctx->client = client;
155   ctx->sock = sock;
156   ctx->payload = payload;
157   ctx->args = silc_command_get_args(ctx->payload);
158   ctx->packet = packet;
159   ident = silc_command_get_ident(ctx->payload);
160       
161   /* Check for pending commands and mark to be exeucted */
162   silc_client_command_pending_check(sock->user_data, ctx, 
163                                     silc_command_get(ctx->payload), ident);
164
165   /* Execute command reply */
166   command = silc_command_get(ctx->payload);
167   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
168     if (cmd->cmd == command)
169       break;
170
171   if (cmd == NULL || !cmd->cb) {
172     silc_free(ctx);
173     return;
174   }
175
176   cmd->cb(ctx);
177 }
178
179 /* Returns status message string */
180
181 static char *
182 silc_client_command_status_message(SilcCommandStatus status)
183 {
184   int i;
185
186   for (i = 0; silc_command_status_messages[i].message; i++) {
187     if (silc_command_status_messages[i].status == status)
188       break;
189   }
190
191   if (silc_command_status_messages[i].message == NULL)
192     return NULL;
193
194   return silc_command_status_messages[i].message;
195 }
196
197 /* Free command reply context and its internals. */
198
199 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
200 {
201   if (cmd) {
202     silc_command_free_payload(cmd->payload);
203     silc_free(cmd);
204   }
205 }
206
207 static void 
208 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
209                                       SilcCommandStatus status)
210 {
211   char buf[256];
212   int argc, len;
213   unsigned char *id_data;
214   char *nickname = NULL, *username = NULL;
215   char *realname = NULL;
216   SilcClientID *client_id;
217   SilcIDCacheEntry id_cache = NULL;
218   SilcClientEntry client_entry = NULL;
219   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
220   
221   memset(buf, 0, sizeof(buf));
222   
223   argc = silc_argument_get_arg_num(cmd->args);
224
225   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
226   if (!id_data) {
227     COMMAND_REPLY_ERROR;
228     return;
229   }
230   
231   client_id = silc_id_payload_parse_id(id_data, len);
232   if (!client_id) {
233     COMMAND_REPLY_ERROR;
234     return;
235   }
236   
237   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
238   if (nickname) {
239     strncat(buf, nickname, len);
240     strncat(buf, " is ", 4);
241   }
242   
243   username = silc_argument_get_arg_type(cmd->args, 4, &len);
244   if (username) {
245     strncat(buf, username, len);
246   }
247   
248   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
249   if (realname) {
250     strncat(buf, " (", 2);
251     strncat(buf, realname, len);
252     strncat(buf, ")", 1);
253   }
254   
255   /* Check if we have this client cached already. */
256   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
257                                    SILC_ID_CLIENT, &id_cache)) {
258     client_entry = silc_calloc(1, sizeof(*client_entry));
259     client_entry->id = client_id;
260     silc_parse_nickname(nickname, &client_entry->nickname, 
261                         &client_entry->server, &client_entry->num);
262     client_entry->username = strdup(username);
263     if (realname)
264       client_entry->realname = strdup(realname);
265     
266     /* Add client to cache */
267     silc_idcache_add(conn->client_cache, client_entry->nickname,
268                      SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
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
280     silc_parse_nickname(nickname, &client_entry->nickname, 
281                         &client_entry->server, &client_entry->num);
282     client_entry->username = strdup(username);
283     if (realname)
284       client_entry->realname = strdup(realname);
285
286     id_cache->data = client_entry->nickname;
287
288     silc_free(client_id);
289   }
290
291   if (!cmd->callback)
292     cmd->client->ops->say(cmd->client, conn, "%s", buf);
293
294   /* Notify application */
295   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
296                  NULL, NULL));
297 }
298
299 /* Received reply for WHOIS command. This maybe called several times
300    for one WHOIS command as server may reply with list of results. */
301
302 SILC_CLIENT_CMD_REPLY_FUNC(whois)
303 {
304   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
305   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
306   SilcCommandStatus status;
307   unsigned char *tmp;
308
309   SILC_LOG_DEBUG(("Start"));
310
311   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
312   SILC_GET16_MSB(status, tmp);
313   if (status != SILC_STATUS_OK && 
314       status != SILC_STATUS_LIST_START &&
315       status != SILC_STATUS_LIST_ITEM &&
316       status != SILC_STATUS_LIST_END) {
317     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
318       /* Take nickname which may be provided */
319       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
320       if (tmp)
321         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
322                  silc_client_command_status_message(status));
323       else
324         cmd->client->ops->say(cmd->client, conn, "%s",
325                  silc_client_command_status_message(status));
326       COMMAND_REPLY_ERROR;
327       goto out;
328     } else {
329       cmd->client->ops->say(cmd->client, conn,
330                "%s", silc_client_command_status_message(status));
331       COMMAND_REPLY_ERROR;
332       goto out;
333     }
334   }
335
336   /* Display one whois reply */
337   if (status == SILC_STATUS_OK) {
338     silc_client_command_reply_whois_print(cmd, status);
339   }
340
341   /* XXX list should not be displayed untill all items has been received. */
342   if (status == SILC_STATUS_LIST_START) {
343     silc_client_command_reply_whois_print(cmd, status);
344   }
345
346   if (status == SILC_STATUS_LIST_ITEM) {
347     silc_client_command_reply_whois_print(cmd, status);
348   }
349
350   if (status == SILC_STATUS_LIST_END) {
351     silc_client_command_reply_whois_print(cmd, status);
352   }
353
354   /* Execute any pending command callbacks */
355   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
356
357  out:
358   silc_client_command_reply_free(cmd);
359 }
360
361 /* Received reply for WHOWAS command. */
362
363 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
364 {
365
366 }
367
368 /* Received reply for IDENTIFY command. This maybe called several times
369    for one IDENTIFY command as server may reply with list of results. 
370    This is totally silent and does not print anything on screen. */
371
372 SILC_CLIENT_CMD_REPLY_FUNC(identify)
373 {
374   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
375   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
376   SilcClientEntry client_entry;
377   SilcIDCacheEntry id_cache = NULL;
378   SilcCommandStatus status;
379   unsigned char *tmp;
380
381   SILC_LOG_DEBUG(("Start"));
382
383   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
384   SILC_GET16_MSB(status, tmp);
385   if (status != SILC_STATUS_OK) {
386     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
387       /* Take nickname which may be provided */
388       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
389       if (tmp)
390         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
391                  silc_client_command_status_message(status));
392       else
393         cmd->client->ops->say(cmd->client, conn, "%s",
394                  silc_client_command_status_message(status));
395       COMMAND_REPLY_ERROR;
396       goto out;
397     } else {
398       cmd->client->ops->say(cmd->client, conn,
399                "%s", silc_client_command_status_message(status));
400       COMMAND_REPLY_ERROR;
401       goto out;
402     }
403   }
404
405   /* Display one whois reply */
406   if (status == SILC_STATUS_OK) {
407     unsigned int len;
408     unsigned char *id_data;
409     char *nickname;
410     char *username;
411     SilcClientID *client_id;
412
413     id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
414     if (!id_data)
415       goto out;
416     client_id = silc_id_payload_parse_id(id_data, len);
417     if (!client_id)
418       goto out;
419
420     nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
421     username = silc_argument_get_arg_type(cmd->args, 4, NULL);
422
423     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
424                                      SILC_ID_CLIENT, &id_cache)) {
425       client_entry = silc_calloc(1, sizeof(*client_entry));
426       client_entry->id = client_id;
427       silc_parse_nickname(nickname, &client_entry->nickname, 
428                           &client_entry->server, &client_entry->num);
429       if (username)
430         client_entry->username = strdup(username);
431     
432       /* Add client to cache */
433       silc_idcache_add(conn->client_cache, client_entry->nickname,
434                        SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
435     } else {
436       client_entry = (SilcClientEntry)id_cache->context;
437       if (client_entry->nickname)
438         silc_free(client_entry->nickname);
439       if (client_entry->server)
440         silc_free(client_entry->server);
441       if (username && client_entry->username)
442         silc_free(client_entry->username);
443
444       silc_parse_nickname(nickname, &client_entry->nickname, 
445                           &client_entry->server, &client_entry->num);
446
447       if (username)
448         client_entry->username = strdup(username);
449
450       id_cache->data = client_entry->nickname;
451
452       silc_free(client_id);
453     }
454   }
455
456   if (status == SILC_STATUS_LIST_START) {
457
458   }
459
460   if (status == SILC_STATUS_LIST_END) {
461
462   }
463
464   /* Execute any pending command callbacks */
465   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
466
467  out:
468   silc_client_command_reply_free(cmd);
469 }
470
471 /* Received reply for command NICK. If everything went without errors
472    we just received our new Client ID. */
473
474 SILC_CLIENT_CMD_REPLY_FUNC(nick)
475 {
476   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
477   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
478   SilcCommandStatus status;
479   SilcIDPayload idp;
480   unsigned char *tmp;
481   unsigned int argc, len;
482
483   SILC_LOG_DEBUG(("Start"));
484
485   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
486   if (status != SILC_STATUS_OK) {
487     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
488              silc_client_command_status_message(status));
489     COMMAND_REPLY_ERROR;
490     goto out;
491   }
492
493   argc = silc_argument_get_arg_num(cmd->args);
494   if (argc < 2 || argc > 2) {
495     cmd->client->ops->say(cmd->client, conn, 
496                           "Cannot set nickname: bad reply to command");
497     COMMAND_REPLY_ERROR;
498     goto out;
499   }
500
501   /* Take received Client ID */
502   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
503   idp = silc_id_payload_parse_data(tmp, len);
504   if (!idp) {
505     COMMAND_REPLY_ERROR;
506     goto out;
507   }
508   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
509     
510   /* Notify application */
511   COMMAND_REPLY((ARGS, conn->local_entry));
512
513   /* Execute any pending command callbacks */
514   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_NICK);
515
516  out:
517   silc_client_command_reply_free(cmd);
518 }
519
520 SILC_CLIENT_CMD_REPLY_FUNC(list)
521 {
522 }
523
524 /* Received reply to topic command. */
525
526 SILC_CLIENT_CMD_REPLY_FUNC(topic)
527 {
528   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
529   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
530   SilcCommandStatus status;
531   SilcChannelEntry channel;
532   SilcChannelID *channel_id = NULL;
533   SilcIDCacheEntry id_cache = NULL;
534   unsigned char *tmp;
535   char *topic;
536   unsigned int argc, len;
537
538   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
539   if (status != SILC_STATUS_OK) {
540     cmd->client->ops->say(cmd->client, conn,
541              "%s", silc_client_command_status_message(status));
542     COMMAND_REPLY_ERROR;
543     silc_client_command_reply_free(cmd);
544     return;
545   }
546
547   argc = silc_argument_get_arg_num(cmd->args);
548   if (argc < 1 || argc > 3) {
549     COMMAND_REPLY_ERROR;
550     goto out;
551   }
552
553   /* Take Channel ID */
554   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
555   if (!tmp)
556     goto out;
557
558   /* Take topic */
559   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
560   if (!topic)
561     goto out;
562
563   channel_id = silc_id_payload_parse_id(tmp, len);
564   if (!channel_id)
565     goto out;
566
567   /* Get the channel name */
568   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
569                                    SILC_ID_CHANNEL, &id_cache)) {
570     silc_free(channel_id);
571     COMMAND_REPLY_ERROR;
572     goto out;
573   }
574   
575   channel = (SilcChannelEntry)id_cache->context;
576
577   cmd->client->ops->say(cmd->client, conn, 
578                         "Topic on channel %s: %s", channel->channel_name,
579                         topic);
580
581   /* Notify application */
582   COMMAND_REPLY((ARGS, channel, topic));
583
584   /* Execute any pending command callbacks */
585   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_TOPIC);
586
587  out:
588   silc_client_command_reply_free(cmd);
589 }
590
591 /* Received reply to invite command. */
592
593 SILC_CLIENT_CMD_REPLY_FUNC(invite)
594 {
595   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
596   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
597   SilcCommandStatus status;
598   unsigned char *tmp;
599
600   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
601   SILC_GET16_MSB(status, tmp);
602   if (status != SILC_STATUS_OK) {
603     cmd->client->ops->say(cmd->client, conn,
604              "%s", silc_client_command_status_message(status));
605     COMMAND_REPLY_ERROR;
606     silc_client_command_reply_free(cmd);
607     return;
608   }
609
610   /* Notify application */
611   COMMAND_REPLY((ARGS));
612
613   /* Execute any pending command callbacks */
614   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INVITE);
615
616   silc_client_command_reply_free(cmd);
617 }
618  
619 SILC_CLIENT_CMD_REPLY_FUNC(quit)
620 {
621 }
622
623 SILC_CLIENT_CMD_REPLY_FUNC(kill)
624 {
625 }
626
627 /* Received reply to INFO command. We receive the server ID and some
628    information about the server user requested. */
629
630 SILC_CLIENT_CMD_REPLY_FUNC(info)
631 {
632   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
633   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
634   SilcClient client = cmd->client;
635   SilcCommandStatus status;
636   unsigned char *tmp;
637
638   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
639   SILC_GET16_MSB(status, tmp);
640   if (status != SILC_STATUS_OK) {
641     cmd->client->ops->say(cmd->client, conn,
642              "%s", silc_client_command_status_message(status));
643     COMMAND_REPLY_ERROR;
644     silc_client_command_reply_free(cmd);
645     return;
646   }
647
648   /* Get server ID */
649   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
650   if (!tmp)
651     goto out;
652
653   /* XXX save server id */
654
655   /* Get server info */
656   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
657   if (!tmp)
658     goto out;
659
660   client->ops->say(cmd->client, conn, "Info: %s", tmp);
661
662   /* Notify application */
663   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
664
665   /* Execute any pending command callbacks */
666   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INFO);
667
668  out:
669   silc_client_command_reply_free(cmd);
670 }
671
672 SILC_CLIENT_CMD_REPLY_FUNC(connect)
673 {
674 }
675
676 /* Received reply to PING command. The reply time is shown to user. */
677
678 SILC_CLIENT_CMD_REPLY_FUNC(ping)
679 {
680   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
681   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
682   SilcCommandStatus status;
683   void *id;
684   int i;
685   time_t diff, curtime;
686
687   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
688   if (status != SILC_STATUS_OK) {
689     cmd->client->ops->say(cmd->client, conn,
690              "%s", silc_client_command_status_message(status));
691     COMMAND_REPLY_ERROR;
692     goto out;
693   }
694
695   curtime = time(NULL);
696   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
697                       cmd->packet->src_id_type);
698   if (!id) {
699     COMMAND_REPLY_ERROR;
700     goto out;
701   }
702
703   for (i = 0; i < conn->ping_count; i++) {
704     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
705       diff = curtime - conn->ping[i].start_time;
706       cmd->client->ops->say(cmd->client, conn, 
707                             "Ping reply from %s: %d second%s", 
708                             conn->ping[i].dest_name, diff, 
709                             diff == 1 ? "" : "s");
710
711       conn->ping[i].start_time = 0;
712       silc_free(conn->ping[i].dest_id);
713       conn->ping[i].dest_id = NULL;
714       silc_free(conn->ping[i].dest_name);
715       conn->ping[i].dest_name = NULL;
716
717       /* Notify application */
718       COMMAND_REPLY((ARGS));
719       break;
720     }
721   }
722
723   silc_free(id);
724
725   /* Execute any pending command callbacks */
726   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_PING);
727
728  out:
729   silc_client_command_reply_free(cmd);
730 }
731
732 SILC_CLIENT_CMD_REPLY_FUNC(oper)
733 {
734 }
735
736 /* Received reply for JOIN command. */
737
738 SILC_CLIENT_CMD_REPLY_FUNC(join)
739 {
740   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
741   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
742   SilcClient client = cmd->client;
743   SilcCommandStatus status;
744   SilcIDPayload idp = NULL;
745   unsigned int argc, mode, len;
746   char *topic, *tmp, *channel_name = NULL;
747   SilcBuffer keyp;
748
749   SILC_LOG_DEBUG(("Start"));
750
751   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
752   if (status != SILC_STATUS_OK) {
753     cmd->client->ops->say(cmd->client, conn,
754              "%s", silc_client_command_status_message(status));
755     COMMAND_REPLY_ERROR;
756     goto out;
757   }
758
759   argc = silc_argument_get_arg_num(cmd->args);
760   if (argc < 3 || argc > 9) {
761     cmd->client->ops->say(cmd->client, conn,
762              "Cannot join channel: Bad reply packet");
763     COMMAND_REPLY_ERROR;
764     goto out;
765   }
766
767   /* Get channel name */
768   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
769   if (!tmp) {
770     cmd->client->ops->say(cmd->client, conn, 
771                           "Cannot join channel: Bad reply packet");
772     COMMAND_REPLY_ERROR;
773     goto out;
774   }
775   channel_name = strdup(tmp);
776
777   /* Get Channel ID */
778   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
779   if (!tmp) {
780     cmd->client->ops->say(cmd->client, conn, 
781                           "Cannot join channel: Bad reply packet");
782     COMMAND_REPLY_ERROR;
783     silc_free(channel_name);
784     goto out;
785   }
786   idp = silc_id_payload_parse_data(tmp, len);
787   if (!idp) {
788     COMMAND_REPLY_ERROR;
789     silc_free(channel_name);
790     goto out;
791   }
792
793   /* Get channel mode */
794   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
795   if (tmp)
796     SILC_GET32_MSB(mode, tmp);
797   else
798     mode = 0;
799
800   /* Get channel key */
801   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
802   if (!tmp) {
803     silc_id_payload_free(idp);
804     silc_free(channel_name);
805     goto out;
806   }
807   keyp = silc_buffer_alloc(len);
808   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
809   silc_buffer_put(keyp, tmp, len);
810
811   /* Get topic */
812   topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
813
814   /* Save received Channel ID */
815   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
816                              mode, idp);
817   silc_id_payload_free(idp);
818
819   /* Save channel key */
820   silc_client_save_channel_key(conn, keyp, conn->current_channel);
821   silc_buffer_free(keyp);
822
823   if (topic)
824     client->ops->say(cmd->client, conn, 
825                      "Topic for %s: %s", channel_name, topic);
826
827   /* Notify application */
828   COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
829                  NULL, NULL, topic));
830
831   /* Execute any pending command callbacks */
832   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
833
834  out:
835   silc_client_command_reply_free(cmd);
836 }
837
838 /* Received reply for MOTD command */
839
840 SILC_CLIENT_CMD_REPLY_FUNC(motd)
841 {
842   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
843   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
844   SilcCommandStatus status;
845   unsigned int argc, i;
846   unsigned char *tmp;
847   char *motd = NULL, *cp, line[256];
848
849   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
850   SILC_GET16_MSB(status, tmp);
851   if (status != SILC_STATUS_OK) {
852     cmd->client->ops->say(cmd->client, conn,
853              "%s", silc_client_command_status_message(status));
854     COMMAND_REPLY_ERROR;
855     return;
856   }
857
858   argc = silc_argument_get_arg_num(cmd->args);
859   if (argc > 2) {
860     COMMAND_REPLY_ERROR;
861     goto out;
862   }
863
864   if (argc == 2) {
865     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
866     if (!motd) {
867       COMMAND_REPLY_ERROR;
868       goto out;
869     }
870
871     i = 0;
872     cp = motd;
873     while(cp[i] != 0) {
874       if (cp[i++] == '\n') {
875         memset(line, 0, sizeof(line));
876         strncat(line, cp, i - 1);
877         cp += i;
878         
879         if (i == 2)
880           line[0] = ' ';
881         
882         cmd->client->ops->say(cmd->client, conn, "%s", line);
883         
884         if (!strlen(cp))
885           break;
886         i = 0;
887       }
888     }
889   }
890
891   /* Notify application */
892   COMMAND_REPLY((ARGS, motd));
893
894   /* Execute any pending command callbacks */
895   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_MOTD);
896
897  out:
898   silc_client_command_reply_free(cmd);
899 }
900
901 SILC_CLIENT_CMD_REPLY_FUNC(umode)
902 {
903 }
904
905 /* Received reply for CMODE command. */
906
907 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
908 {
909   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
910   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
911   SilcCommandStatus status;
912   unsigned char *tmp;
913
914   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
915   if (status != SILC_STATUS_OK) {
916     cmd->client->ops->say(cmd->client, conn,
917              "%s", silc_client_command_status_message(status));
918     COMMAND_REPLY_ERROR;
919     goto out;
920   }
921
922   /* Get channel mode */
923   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
924   if (!tmp) {
925     COMMAND_REPLY_ERROR;
926     goto out;
927   }
928
929   /* Notify application */
930   COMMAND_REPLY((ARGS, tmp));
931
932   /* Execute any pending command callbacks */
933   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CMODE);
934
935  out:
936   silc_client_command_reply_free(cmd);
937 }
938
939 /* Received reply for CUMODE command */
940
941 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
942 {
943   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
944   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
945   SilcCommandStatus status;
946   SilcIDCacheEntry id_cache = NULL;
947   SilcClientID *client_id;
948   unsigned char *tmp, *id;
949   unsigned int len;
950   
951   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
952   if (status != SILC_STATUS_OK) {
953     cmd->client->ops->say(cmd->client, conn,
954              "%s", silc_client_command_status_message(status));
955     COMMAND_REPLY_ERROR;
956     goto out;
957   }
958   
959   /* Get channel mode */
960   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
961   if (!tmp) {
962     COMMAND_REPLY_ERROR;
963     goto out;
964   }
965
966   /* Get Client ID */
967   id = silc_argument_get_arg_type(cmd->args, 3, &len);
968   if (!id) {
969     COMMAND_REPLY_ERROR;
970     goto out;
971   }
972   client_id = silc_id_payload_parse_id(id, len);
973   if (!client_id) {
974     COMMAND_REPLY_ERROR;
975     goto out;
976   }
977   
978   /* Get client entry */
979   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
980                                    SILC_ID_CLIENT, &id_cache)) {
981     COMMAND_REPLY_ERROR;
982     goto out;
983   }
984
985   /* Notify application */
986   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
987   silc_free(client_id);
988   
989   /* Execute any pending command callbacks */
990   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CUMODE);
991
992  out:
993   silc_client_command_reply_free(cmd);
994 }
995
996 SILC_CLIENT_CMD_REPLY_FUNC(kick)
997 {
998 }
999
1000 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1001 {
1002 }
1003  
1004 SILC_CLIENT_CMD_REPLY_FUNC(close)
1005 {
1006 }
1007  
1008 SILC_CLIENT_CMD_REPLY_FUNC(die)
1009 {
1010 }
1011  
1012 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1013 {
1014 }
1015
1016 /* Reply to LEAVE command. */
1017
1018 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1019 {
1020   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1021   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1022   SilcCommandStatus status;
1023   unsigned char *tmp;
1024
1025   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1026   SILC_GET16_MSB(status, tmp);
1027   if (status != SILC_STATUS_OK) {
1028     cmd->client->ops->say(cmd->client, conn,
1029              "%s", silc_client_command_status_message(status));
1030     COMMAND_REPLY_ERROR;
1031     return;
1032   }
1033
1034   /* Notify application */
1035   COMMAND_REPLY((ARGS));
1036
1037   /* Execute any pending command callbacks */
1038   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_LEAVE);
1039
1040   silc_client_command_reply_free(cmd);
1041 }
1042
1043 /* Reply to USERS command. Received list of client ID's and theirs modes
1044    on the channel we requested. */
1045
1046 SILC_CLIENT_CMD_REPLY_FUNC(users)
1047 {
1048   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1049   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1050   SilcCommandStatus status;
1051   SilcIDCacheEntry id_cache = NULL;
1052   SilcChannelEntry channel;
1053   SilcChannelUser chu;
1054   SilcChannelID *channel_id = NULL;
1055   SilcBuffer client_id_list;
1056   SilcBuffer client_mode_list;
1057   unsigned char *tmp;
1058   unsigned int tmp_len, list_count;
1059   int i;
1060   unsigned char **res_argv = NULL;
1061   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1062
1063   SILC_LOG_DEBUG(("Start"));
1064
1065   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1066   SILC_GET16_MSB(status, tmp);
1067   if (status != SILC_STATUS_OK) {
1068     cmd->client->ops->say(cmd->client, conn,
1069              "%s", silc_client_command_status_message(status));
1070     COMMAND_REPLY_ERROR;
1071     goto out;
1072   }
1073
1074   /* Get channel ID */
1075   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1076   if (!tmp)
1077     goto out;
1078   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1079   if (!channel_id)
1080     goto out;
1081
1082   /* Get the list count */
1083   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1084   if (!tmp)
1085     goto out;
1086   SILC_GET32_MSB(list_count, tmp);
1087
1088   /* Get Client ID list */
1089   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1090   if (!tmp)
1091     goto out;
1092
1093   client_id_list = silc_buffer_alloc(tmp_len);
1094   silc_buffer_pull_tail(client_id_list, tmp_len);
1095   silc_buffer_put(client_id_list, tmp, tmp_len);
1096
1097   /* Get client mode list */
1098   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1099   if (!tmp)
1100     goto out;
1101
1102   client_mode_list = silc_buffer_alloc(tmp_len);
1103   silc_buffer_pull_tail(client_mode_list, tmp_len);
1104   silc_buffer_put(client_mode_list, tmp, tmp_len);
1105
1106   /* Get channel entry */
1107   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1108                                    SILC_ID_CHANNEL, &id_cache)) {
1109     COMMAND_REPLY_ERROR;
1110     goto out;
1111   }
1112   channel = (SilcChannelEntry)id_cache->context;
1113
1114   /* Remove old client list from channel. */
1115   silc_list_start(channel->clients);
1116   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1117     silc_list_del(channel->clients, chu);
1118     silc_free(chu);
1119   }
1120
1121   /* Cache the received Client ID's and modes. This cache expires
1122      whenever server sends notify message to channel. It means two things;
1123      some user has joined or leaved the channel. XXX! */
1124   for (i = 0; i < list_count; i++) {
1125     unsigned short idp_len;
1126     unsigned int mode;
1127     SilcClientID *client_id;
1128     SilcClientEntry client;
1129
1130     /* Client ID */
1131     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1132     idp_len += 4;
1133     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1134     if (!client_id)
1135       continue;
1136
1137     /* Mode */
1138     SILC_GET32_MSB(mode, client_mode_list->data);
1139
1140     /* Check if we have this client cached already. */
1141     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1142                                      SILC_ID_CLIENT, &id_cache)) {
1143       /* No we don't have it, query it from the server. Assemble argument
1144          table that will be sent fr the IDENTIFY command later. */
1145       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1146                               (res_argc + 1));
1147       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1148                                    (res_argc + 1));
1149       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1150                                     (res_argc + 1));
1151       res_argv[res_argc] = client_id_list->data;
1152       res_argv_lens[res_argc] = idp_len;
1153       res_argv_types[res_argc] = res_argc + 3;
1154       res_argc++;
1155     } else {
1156       /* Found the client, join it to the channel */
1157       client = (SilcClientEntry)id_cache->context;
1158       chu = silc_calloc(1, sizeof(*chu));
1159       chu->client = client;
1160       chu->mode = mode;
1161       silc_list_add(channel->clients, chu);
1162
1163       silc_free(client_id);
1164       id_cache = NULL;
1165     }
1166
1167     silc_buffer_pull(client_id_list, idp_len);
1168     silc_buffer_pull(client_mode_list, 4);
1169   }
1170
1171   /* Query the client information from server if the list included clients
1172      that we don't know about. */
1173   if (res_argc) {
1174     SilcBuffer res_cmd;
1175
1176     /* Send the IDENTIFY command to server */
1177     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1178                                           res_argc, res_argv, res_argv_lens,
1179                                           res_argv_types, ++conn->cmd_ident);
1180     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1181                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1182                             TRUE);
1183
1184     /* Register pending command callback. After we've received the IDENTIFY
1185        command reply we will reprocess this command reply by re-calling this
1186        USERS command reply callback. */
1187     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1188                                 silc_client_command_reply_users, cmd);
1189
1190     silc_buffer_free(res_cmd);
1191     if (channel_id)
1192       silc_free(channel_id);
1193
1194     silc_free(res_argv);
1195     silc_free(res_argv_lens);
1196     silc_free(res_argv_types);
1197     return;
1198   }
1199
1200   /* We have all the clients on the channel cached now. Create a nice
1201      output for user interface and notify application. */
1202
1203   if (!cmd->callback) {
1204     /* Server has sent us USERS reply even when we haven't actually sent
1205        USERS command. This is normal behaviour when joining to a channel.
1206        Display some nice information on the user interface. */
1207     int k = 0, len1 = 0, len2 = 0;
1208     char *name_list = NULL;
1209
1210     silc_list_start(channel->clients);
1211     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1212       char *m, *n = chu->client->nickname;
1213       len2 = strlen(n);
1214       len1 += len2;
1215
1216       name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1217
1218       m = silc_client_chumode_char(chu->mode);
1219       if (m) {
1220         memcpy(name_list + (len1 - len2), m, strlen(m));
1221         len1 += strlen(m);
1222         silc_free(m);
1223       }
1224
1225       memcpy(name_list + (len1 - len2), n, len2);
1226       name_list[len1] = 0;
1227       
1228       if (k == silc_list_count(channel->clients) - 1)
1229         break;
1230       memcpy(name_list + len1, " ", 1);
1231       len1++;
1232       k++;
1233     }
1234
1235     cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1236                           channel->channel_name, name_list);
1237     silc_free(name_list);
1238   }
1239
1240   /* Notify application */
1241   COMMAND_REPLY((ARGS, channel, client_id_list->head,
1242                  client_mode_list->head));
1243
1244   /* Execute any pending command callbacks */
1245   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
1246
1247   silc_buffer_free(client_id_list);
1248   silc_buffer_free(client_mode_list);
1249
1250  out:
1251   if (channel_id)
1252     silc_free(channel_id);
1253   silc_client_command_reply_free(cmd);
1254 }