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 - 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(names, NAMES),
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   SilcClientCommandReplyContext ctx;
138   SilcCommandPayload payload;
139
140   /* Get command reply payload from packet */
141   payload = silc_command_payload_parse(buffer);
142   if (!payload) {
143     /* Silently ignore bad reply packet */
144     SILC_LOG_DEBUG(("Bad command reply packet"));
145     return;
146   }
147   
148   /* Allocate command reply context. This must be free'd by the
149      command reply routine receiving it. */
150   ctx = silc_calloc(1, sizeof(*ctx));
151   ctx->client = client;
152   ctx->sock = sock;
153   ctx->payload = payload;
154   ctx->args = silc_command_get_args(ctx->payload);
155   ctx->packet = packet;
156       
157   /* Check for pending commands and mark to be exeucted */
158   SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
159   
160   /* Execute command reply */
161   SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
162 }
163
164 /* Returns status message string */
165
166 static char *
167 silc_client_command_status_message(SilcCommandStatus status)
168 {
169   int i;
170
171   for (i = 0; silc_command_status_messages[i].message; i++) {
172     if (silc_command_status_messages[i].status == status)
173       break;
174   }
175
176   if (silc_command_status_messages[i].message == NULL)
177     return NULL;
178
179   return silc_command_status_messages[i].message;
180 }
181
182 /* Free command reply context and its internals. */
183
184 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
185 {
186   if (cmd) {
187     silc_command_free_payload(cmd->payload);
188     silc_free(cmd);
189   }
190 }
191
192 static void 
193 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
194                                       SilcCommandStatus status)
195 {
196   char buf[256];
197   int argc, len;
198   unsigned char *id_data;
199   char *nickname = NULL, *username = NULL;
200   char *realname = NULL;
201   SilcClientID *client_id;
202   SilcIDCacheEntry id_cache = NULL;
203   SilcClientEntry client_entry = NULL;
204   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
205   
206   memset(buf, 0, sizeof(buf));
207   
208   argc = silc_argument_get_arg_num(cmd->args);
209
210   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
211   if (!id_data) {
212     COMMAND_REPLY_ERROR;
213     return;
214   }
215   
216   client_id = silc_id_payload_parse_id(id_data, len);
217   
218   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
219   if (nickname) {
220     strncat(buf, nickname, len);
221     strncat(buf, " is ", 4);
222   }
223   
224   username = silc_argument_get_arg_type(cmd->args, 4, &len);
225   if (username) {
226     strncat(buf, username, len);
227   }
228   
229   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
230   if (realname) {
231     strncat(buf, " (", 2);
232     strncat(buf, realname, len);
233     strncat(buf, ")", 1);
234   }
235   
236   /* Check if we have this client cached already. */
237   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
238                                    SILC_ID_CLIENT, &id_cache)) {
239     client_entry = silc_calloc(1, sizeof(*client_entry));
240     client_entry->id = client_id;
241     silc_parse_nickname(nickname, &client_entry->nickname, 
242                         &client_entry->server, &client_entry->num);
243     client_entry->username = strdup(username);
244     if (realname)
245       client_entry->realname = strdup(realname);
246     
247     /* Add client to cache */
248     silc_idcache_add(conn->client_cache, client_entry->nickname,
249                      SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
250   } else {
251     client_entry = (SilcClientEntry)id_cache->context;
252     if (client_entry->nickname)
253       silc_free(client_entry->nickname);
254     if (client_entry->server)
255       silc_free(client_entry->server);
256     if (client_entry->username)
257       silc_free(client_entry->username);
258     if (client_entry->realname)
259       silc_free(client_entry->realname);
260
261     silc_parse_nickname(nickname, &client_entry->nickname, 
262                         &client_entry->server, &client_entry->num);
263     client_entry->username = strdup(username);
264     if (realname)
265       client_entry->realname = strdup(realname);
266
267     id_cache->data = client_entry->nickname;
268
269     silc_free(client_id);
270   }
271
272   if (!cmd->callback)
273     cmd->client->ops->say(cmd->client, conn, "%s", buf);
274
275   /* Notify application */
276   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
277                  NULL, NULL));
278 }
279
280 /* Received reply for WHOIS command. This maybe called several times
281    for one WHOIS command as server may reply with list of results. */
282
283 SILC_CLIENT_CMD_REPLY_FUNC(whois)
284 {
285   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
286   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
287   SilcCommandStatus status;
288   unsigned char *tmp;
289
290   SILC_LOG_DEBUG(("Start"));
291
292   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
293   SILC_GET16_MSB(status, tmp);
294   if (status != SILC_STATUS_OK && 
295       status != SILC_STATUS_LIST_START &&
296       status != SILC_STATUS_LIST_ITEM &&
297       status != SILC_STATUS_LIST_END) {
298     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
299       /* Take nickname which may be provided */
300       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
301       if (tmp)
302         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
303                  silc_client_command_status_message(status));
304       else
305         cmd->client->ops->say(cmd->client, conn, "%s",
306                  silc_client_command_status_message(status));
307       COMMAND_REPLY_ERROR;
308       goto out;
309     } else {
310       cmd->client->ops->say(cmd->client, conn,
311                "%s", silc_client_command_status_message(status));
312       COMMAND_REPLY_ERROR;
313       goto out;
314     }
315   }
316
317   /* Display one whois reply */
318   if (status == SILC_STATUS_OK) {
319     silc_client_command_reply_whois_print(cmd, status);
320   }
321
322   /* XXX list should not be displayed untill all items has been received. */
323   if (status == SILC_STATUS_LIST_START) {
324     silc_client_command_reply_whois_print(cmd, status);
325   }
326
327   if (status == SILC_STATUS_LIST_ITEM) {
328     silc_client_command_reply_whois_print(cmd, status);
329   }
330
331   if (status == SILC_STATUS_LIST_END) {
332     silc_client_command_reply_whois_print(cmd, status);
333   }
334
335   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
336
337  out:
338   silc_client_command_reply_free(cmd);
339 }
340
341 /* Received reply for WHOWAS command. */
342
343 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
344 {
345
346 }
347
348 /* Received reply for IDENTIFY command. This maybe called several times
349    for one IDENTIFY command as server may reply with list of results. 
350    This is totally silent and does not print anything on screen. */
351
352 SILC_CLIENT_CMD_REPLY_FUNC(identify)
353 {
354   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
355   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
356   SilcClientEntry client_entry;
357   SilcIDCacheEntry id_cache = NULL;
358   SilcCommandStatus status;
359   unsigned char *tmp;
360
361   SILC_LOG_DEBUG(("Start"));
362
363   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
364   SILC_GET16_MSB(status, tmp);
365   if (status != SILC_STATUS_OK) {
366     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
367       /* Take nickname which may be provided */
368       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
369       if (tmp)
370         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
371                  silc_client_command_status_message(status));
372       else
373         cmd->client->ops->say(cmd->client, conn, "%s",
374                  silc_client_command_status_message(status));
375       COMMAND_REPLY_ERROR;
376       goto out;
377     } else {
378       cmd->client->ops->say(cmd->client, conn,
379                "%s", silc_client_command_status_message(status));
380       COMMAND_REPLY_ERROR;
381       goto out;
382     }
383   }
384
385   /* Display one whois reply */
386   if (status == SILC_STATUS_OK) {
387     unsigned int len;
388     unsigned char *id_data;
389     char *nickname;
390     char *username;
391     SilcClientID *client_id;
392
393     id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
394     if (!id_data)
395       goto out;
396     client_id = silc_id_payload_parse_id(id_data, len);
397
398     nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
399     username = silc_argument_get_arg_type(cmd->args, 4, NULL);
400
401     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
402                                      SILC_ID_CLIENT, &id_cache)) {
403       client_entry = silc_calloc(1, sizeof(*client_entry));
404       client_entry->id = client_id;
405       silc_parse_nickname(nickname, &client_entry->nickname, 
406                           &client_entry->server, &client_entry->num);
407       if (username)
408         client_entry->username = strdup(username);
409     
410       /* Add client to cache */
411       silc_idcache_add(conn->client_cache, client_entry->nickname,
412                        SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
413     } else {
414       client_entry = (SilcClientEntry)id_cache->context;
415       if (client_entry->nickname)
416         silc_free(client_entry->nickname);
417       if (client_entry->server)
418         silc_free(client_entry->server);
419       if (username && client_entry->username)
420         silc_free(client_entry->username);
421
422       silc_parse_nickname(nickname, &client_entry->nickname, 
423                           &client_entry->server, &client_entry->num);
424
425       if (username)
426         client_entry->username = strdup(username);
427
428       id_cache->data = client_entry->nickname;
429
430       silc_free(client_id);
431     }
432   }
433
434   if (status == SILC_STATUS_LIST_START) {
435
436   }
437
438   if (status == SILC_STATUS_LIST_END) {
439
440   }
441
442   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
443
444  out:
445   silc_client_command_reply_free(cmd);
446 }
447
448 /* Received reply for command NICK. If everything went without errors
449    we just received our new Client ID. */
450
451 SILC_CLIENT_CMD_REPLY_FUNC(nick)
452 {
453   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
454   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
455   SilcCommandStatus status;
456   SilcIDPayload idp;
457   unsigned char *tmp;
458   unsigned int argc, len;
459
460   SILC_LOG_DEBUG(("Start"));
461
462   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
463   if (status != SILC_STATUS_OK) {
464     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
465              silc_client_command_status_message(status));
466     COMMAND_REPLY_ERROR;
467     goto out;
468   }
469
470   argc = silc_argument_get_arg_num(cmd->args);
471   if (argc < 2 || argc > 2) {
472     cmd->client->ops->say(cmd->client, conn, 
473                           "Cannot set nickname: bad reply to command");
474     COMMAND_REPLY_ERROR;
475     goto out;
476   }
477
478   /* Take received Client ID */
479   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
480   idp = silc_id_payload_parse_data(tmp, len);
481   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
482     
483   /* Notify application */
484   COMMAND_REPLY((ARGS, conn->local_entry));
485
486  out:
487   silc_client_command_reply_free(cmd);
488 }
489
490 SILC_CLIENT_CMD_REPLY_FUNC(list)
491 {
492 }
493
494 /* Received reply to topic command. */
495
496 SILC_CLIENT_CMD_REPLY_FUNC(topic)
497 {
498   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
499   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
500   SilcCommandStatus status;
501   SilcChannelEntry channel;
502   SilcChannelID *channel_id = NULL;
503   SilcIDCacheEntry id_cache = NULL;
504   unsigned char *tmp;
505   char *topic;
506   unsigned int argc, len;
507
508   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
509   if (status != SILC_STATUS_OK) {
510     cmd->client->ops->say(cmd->client, conn,
511              "%s", silc_client_command_status_message(status));
512     COMMAND_REPLY_ERROR;
513     silc_client_command_reply_free(cmd);
514     return;
515   }
516
517   argc = silc_argument_get_arg_num(cmd->args);
518   if (argc < 1 || argc > 3) {
519     COMMAND_REPLY_ERROR;
520     goto out;
521   }
522
523   /* Take Channel ID */
524   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
525   if (!tmp)
526     goto out;
527
528   /* Take topic */
529   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
530   if (!topic)
531     goto out;
532
533   channel_id = silc_id_payload_parse_id(tmp, len);
534
535   /* Get the channel name */
536   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
537                                    SILC_ID_CHANNEL, &id_cache)) {
538     silc_free(channel_id);
539     COMMAND_REPLY_ERROR;
540     goto out;
541   }
542   
543   channel = (SilcChannelEntry)id_cache->context;
544
545   cmd->client->ops->say(cmd->client, conn, 
546                         "Topic on channel %s: %s", channel->channel_name,
547                         topic);
548
549   /* Notify application */
550   COMMAND_REPLY((ARGS, channel, topic));
551
552  out:
553   silc_client_command_reply_free(cmd);
554 }
555
556 /* Received reply to invite command. */
557
558 SILC_CLIENT_CMD_REPLY_FUNC(invite)
559 {
560   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
561   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
562   SilcCommandStatus status;
563   unsigned char *tmp;
564
565   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
566   SILC_GET16_MSB(status, tmp);
567   if (status != SILC_STATUS_OK) {
568     cmd->client->ops->say(cmd->client, conn,
569              "%s", silc_client_command_status_message(status));
570     COMMAND_REPLY_ERROR;
571     silc_client_command_reply_free(cmd);
572     return;
573   }
574
575   /* Notify application */
576   COMMAND_REPLY((ARGS));
577
578   silc_client_command_reply_free(cmd);
579 }
580  
581 SILC_CLIENT_CMD_REPLY_FUNC(quit)
582 {
583 }
584
585 SILC_CLIENT_CMD_REPLY_FUNC(kill)
586 {
587 }
588
589 /* Received reply to INFO command. We receive the server ID and some
590    information about the server user requested. */
591
592 SILC_CLIENT_CMD_REPLY_FUNC(info)
593 {
594   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
595   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
596   SilcClient client = cmd->client;
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   /* Get server ID */
611   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
612   if (!tmp)
613     goto out;
614
615   /* XXX save server id */
616
617   /* Get server info */
618   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
619   if (!tmp)
620     goto out;
621
622   client->ops->say(cmd->client, conn, "Info: %s", tmp);
623
624   /* Notify application */
625   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
626
627  out:
628   silc_client_command_reply_free(cmd);
629 }
630
631 SILC_CLIENT_CMD_REPLY_FUNC(connect)
632 {
633 }
634
635 /* Received reply to PING command. The reply time is shown to user. */
636
637 SILC_CLIENT_CMD_REPLY_FUNC(ping)
638 {
639   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
640   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
641   SilcCommandStatus status;
642   void *id;
643   int i;
644   time_t diff, curtime;
645
646   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
647   if (status != SILC_STATUS_OK) {
648     cmd->client->ops->say(cmd->client, conn,
649              "%s", silc_client_command_status_message(status));
650     COMMAND_REPLY_ERROR;
651     goto out;
652   }
653
654   curtime = time(NULL);
655   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
656
657   for (i = 0; i < conn->ping_count; i++) {
658     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
659       diff = curtime - conn->ping[i].start_time;
660       cmd->client->ops->say(cmd->client, conn, 
661                             "Ping reply from %s: %d second%s", 
662                             conn->ping[i].dest_name, diff, 
663                             diff == 1 ? "" : "s");
664
665       conn->ping[i].start_time = 0;
666       silc_free(conn->ping[i].dest_id);
667       conn->ping[i].dest_id = NULL;
668       silc_free(conn->ping[i].dest_name);
669       conn->ping[i].dest_name = NULL;
670
671       /* Notify application */
672       COMMAND_REPLY((ARGS));
673       break;
674     }
675   }
676
677   silc_free(id);
678
679  out:
680   silc_client_command_reply_free(cmd);
681 }
682
683 SILC_CLIENT_CMD_REPLY_FUNC(oper)
684 {
685 }
686
687 /* Received reply for JOIN command. */
688
689 SILC_CLIENT_CMD_REPLY_FUNC(join)
690 {
691   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
692   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
693   SilcClient client = cmd->client;
694   SilcCommandStatus status;
695   SilcIDPayload idp;
696   unsigned int argc, mode, len;
697   char *topic, *tmp, *channel_name;
698
699   SILC_LOG_DEBUG(("Start"));
700
701   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
702   if (status != SILC_STATUS_OK) {
703     cmd->client->ops->say(cmd->client, conn,
704              "%s", silc_client_command_status_message(status));
705     COMMAND_REPLY_ERROR;
706     goto out;
707   }
708
709   argc = silc_argument_get_arg_num(cmd->args);
710   if (argc < 3 || argc > 5) {
711     cmd->client->ops->say(cmd->client, conn,
712              "Cannot join channel: Bad reply packet");
713     COMMAND_REPLY_ERROR;
714     goto out;
715   }
716
717   /* Get channel name */
718   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
719   if (!tmp) {
720     cmd->client->ops->say(cmd->client, conn, 
721                           "Cannot join channel: Bad reply packet");
722     COMMAND_REPLY_ERROR;
723     goto out;
724   }
725   channel_name = strdup(tmp);
726
727   /* Get Channel ID */
728   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
729   if (!tmp) {
730     cmd->client->ops->say(cmd->client, conn, 
731                           "Cannot join channel: Bad reply packet");
732     COMMAND_REPLY_ERROR;
733     goto out;
734   }
735   idp = silc_id_payload_parse_data(tmp, len);
736
737   /* Get channel mode */
738   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
739   if (tmp)
740     SILC_GET32_MSB(mode, tmp);
741   else
742     mode = 0;
743
744   /* Get topic */
745   topic = silc_argument_get_arg_type(cmd->args, 5, NULL);
746
747   /* Save received Channel ID */
748   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
749                              mode, idp);
750   silc_id_payload_free(idp);
751
752   if (topic)
753     client->ops->say(cmd->client, conn, 
754                      "Topic for %s: %s", channel_name, topic);
755
756   /* Notify application */
757   COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
758                  NULL, NULL, topic));
759
760  out:
761   silc_client_command_reply_free(cmd);
762 }
763
764 /* Received reply for MOTD command */
765
766 SILC_CLIENT_CMD_REPLY_FUNC(motd)
767 {
768   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
769   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
770   SilcCommandStatus status;
771   unsigned int argc, i;
772   unsigned char *tmp;
773   char *motd = NULL, *cp, line[256];
774
775   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
776   SILC_GET16_MSB(status, tmp);
777   if (status != SILC_STATUS_OK) {
778     cmd->client->ops->say(cmd->client, conn,
779              "%s", silc_client_command_status_message(status));
780     COMMAND_REPLY_ERROR;
781     return;
782   }
783
784   argc = silc_argument_get_arg_num(cmd->args);
785   if (argc > 2) {
786     COMMAND_REPLY_ERROR;
787     goto out;
788   }
789
790   if (argc == 2) {
791     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
792     if (!motd) {
793       COMMAND_REPLY_ERROR;
794       goto out;
795     }
796
797     i = 0;
798     cp = motd;
799     while(cp[i] != 0) {
800       if (cp[i++] == '\n') {
801         memset(line, 0, sizeof(line));
802         strncat(line, cp, i - 1);
803         cp += i;
804         
805         if (i == 2)
806           line[0] = ' ';
807         
808         cmd->client->ops->say(cmd->client, conn, "%s", line);
809         
810         if (!strlen(cp))
811           break;
812         i = 0;
813       }
814     }
815   }
816
817   /* Notify application */
818   COMMAND_REPLY((ARGS, motd));
819
820  out:
821   silc_client_command_reply_free(cmd);
822 }
823
824 SILC_CLIENT_CMD_REPLY_FUNC(umode)
825 {
826 }
827
828 /* Received reply for CMODE command. */
829
830 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
831 {
832   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
833   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
834   SilcCommandStatus status;
835   unsigned char *tmp;
836
837   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
838   if (status != SILC_STATUS_OK) {
839     cmd->client->ops->say(cmd->client, conn,
840              "%s", silc_client_command_status_message(status));
841     COMMAND_REPLY_ERROR;
842     goto out;
843   }
844
845   /* Get channel mode */
846   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
847   if (!tmp) {
848     COMMAND_REPLY_ERROR;
849     goto out;
850   }
851
852   /* Notify application */
853   COMMAND_REPLY((ARGS, tmp));
854
855  out:
856   silc_client_command_reply_free(cmd);
857 }
858
859 /* Received reply for CUMODE command */
860
861 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
862 {
863   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
864   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
865   SilcCommandStatus status;
866   SilcIDCacheEntry id_cache = NULL;
867   SilcClientID *client_id;
868   unsigned char *tmp, *id;
869   unsigned int len;
870   
871   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
872   if (status != SILC_STATUS_OK) {
873     cmd->client->ops->say(cmd->client, conn,
874              "%s", silc_client_command_status_message(status));
875     COMMAND_REPLY_ERROR;
876     goto out;
877   }
878   
879   /* Get channel mode */
880   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
881   if (!tmp) {
882     COMMAND_REPLY_ERROR;
883     goto out;
884   }
885
886   /* Get Client ID */
887   id = silc_argument_get_arg_type(cmd->args, 3, &len);
888   if (!id) {
889     COMMAND_REPLY_ERROR;
890     goto out;
891   }
892   client_id = silc_id_payload_parse_id(id, len);
893   
894   /* Get client entry */
895   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
896                                    SILC_ID_CLIENT, &id_cache)) {
897     COMMAND_REPLY_ERROR;
898     goto out;
899   }
900
901   /* Notify application */
902   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
903   silc_free(client_id);
904   
905  out:
906   silc_client_command_reply_free(cmd);
907 }
908
909 SILC_CLIENT_CMD_REPLY_FUNC(kick)
910 {
911 }
912
913 SILC_CLIENT_CMD_REPLY_FUNC(restart)
914 {
915 }
916  
917 SILC_CLIENT_CMD_REPLY_FUNC(close)
918 {
919 }
920  
921 SILC_CLIENT_CMD_REPLY_FUNC(die)
922 {
923 }
924  
925 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
926 {
927 }
928
929 /* Reply to LEAVE command. */
930
931 SILC_CLIENT_CMD_REPLY_FUNC(leave)
932 {
933   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
934   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
935   SilcCommandStatus status;
936   unsigned char *tmp;
937
938   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
939   SILC_GET16_MSB(status, tmp);
940   if (status != SILC_STATUS_OK) {
941     cmd->client->ops->say(cmd->client, conn,
942              "%s", silc_client_command_status_message(status));
943     COMMAND_REPLY_ERROR;
944     return;
945   }
946
947   /* Notify application */
948   COMMAND_REPLY((ARGS));
949
950   silc_client_command_reply_free(cmd);
951 }
952
953 /* Reply to NAMES command. Received list of client names on the channel 
954    we requested. */
955
956 SILC_CLIENT_CMD_REPLY_FUNC(names)
957 {
958   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
959   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
960   SilcCommandStatus status;
961   SilcIDCacheEntry id_cache = NULL;
962   SilcChannelEntry channel;
963   SilcChannelUser chu;
964   SilcChannelID *channel_id = NULL;
965   SilcBuffer client_id_list;
966   SilcBuffer client_mode_list;
967   unsigned char *tmp;
968   char *name_list, *cp;
969   int i, k, len1, len2, list_count = 0;
970
971   SILC_LOG_DEBUG(("Start"));
972
973   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
974   SILC_GET16_MSB(status, tmp);
975   if (status != SILC_STATUS_OK) {
976     cmd->client->ops->say(cmd->client, conn,
977              "%s", silc_client_command_status_message(status));
978     COMMAND_REPLY_ERROR;
979     goto out;
980   }
981
982   /* Get channel ID */
983   tmp = silc_argument_get_arg_type(cmd->args, 2, &len1);
984   if (!tmp) {
985     cmd->client->ops->say(cmd->client, conn, 
986                           "Cannot Channel ID: Bad reply packet");
987     COMMAND_REPLY_ERROR;
988     goto out;
989   }
990   channel_id = silc_id_payload_parse_id(tmp, len1);
991
992   /* Get the name list of the channel */
993   name_list = silc_argument_get_arg_type(cmd->args, 3, &len1);
994   if (!name_list) {
995     cmd->client->ops->say(cmd->client, conn, 
996                           "Cannot get user list: Bad reply packet");
997     COMMAND_REPLY_ERROR;
998     goto out;
999   }
1000
1001   /* Get Client ID list */
1002   tmp = silc_argument_get_arg_type(cmd->args, 4, &len2);
1003   if (!tmp) {
1004     cmd->client->ops->say(cmd->client, conn, 
1005                           "Cannot get user list: Bad reply packet");
1006     COMMAND_REPLY_ERROR;
1007     goto out;
1008   }
1009
1010   client_id_list = silc_buffer_alloc(len2);
1011   silc_buffer_pull_tail(client_id_list, len2);
1012   silc_buffer_put(client_id_list, tmp, len2);
1013
1014   /* Get client mode list */
1015   tmp = silc_argument_get_arg_type(cmd->args, 5, &len2);
1016   if (!tmp) {
1017     cmd->client->ops->say(cmd->client, conn, 
1018                           "Cannot get user list: Bad reply packet");
1019     COMMAND_REPLY_ERROR;
1020     goto out;
1021   }
1022
1023   client_mode_list = silc_buffer_alloc(len2);
1024   silc_buffer_pull_tail(client_mode_list, len2);
1025   silc_buffer_put(client_mode_list, tmp, len2);
1026
1027   /* Get the channel name */
1028   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1029                                    SILC_ID_CHANNEL, &id_cache)) {
1030     COMMAND_REPLY_ERROR;
1031     goto out;
1032   }
1033   
1034   channel = (SilcChannelEntry)id_cache->context;
1035
1036   /* Remove commas from list */
1037   for (i = 0; i < len1; i++)
1038     if (name_list[i] == ',') {
1039       name_list[i] = ' ';
1040       list_count++;
1041     }
1042   list_count++;
1043
1044   /* Remove old client list from channel. */
1045   silc_list_start(channel->clients);
1046   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1047     silc_list_del(channel->clients, chu);
1048     silc_free(chu);
1049   }
1050
1051   /* Cache the received name list, client ID's and modes. This cache expires
1052      whenever server sends notify message to channel. It means two things;
1053      some user has joined or leaved the channel. */
1054   cp = name_list;
1055   for (i = 0; i < list_count; i++) {
1056     int nick_len = strcspn(name_list, " ");
1057     unsigned short idp_len;
1058     unsigned int mode;
1059     char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname));
1060     SilcClientID *client_id;
1061     SilcClientEntry client;
1062
1063     memcpy(nickname, name_list, nick_len);
1064     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1065     idp_len += 4;
1066     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1067     silc_buffer_pull(client_id_list, idp_len);
1068     
1069     SILC_GET32_MSB(mode, client_mode_list->data);
1070     silc_buffer_pull(client_mode_list, 4);
1071
1072     /* Check if we have this client cached already. */
1073     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1074                                      SILC_ID_CLIENT, &id_cache)) {
1075       client = silc_calloc(1, sizeof(*client));
1076       client->id = client_id;
1077       silc_parse_nickname(nickname, &client->nickname, &client->server, 
1078                           &client->num);
1079       silc_free(nickname);
1080
1081       /* Add client to cache */
1082       silc_idcache_add(conn->client_cache, client->nickname, SILC_ID_CLIENT,
1083                        client_id, (void *)client, TRUE);
1084     } else {
1085       client = (SilcClientEntry)id_cache->context;
1086       silc_free(client_id);
1087       silc_free(nickname);
1088       id_cache = NULL;
1089     }
1090
1091     chu = silc_calloc(1, sizeof(*chu));
1092     chu->client = client;
1093     chu->mode = mode;
1094     silc_list_add(channel->clients, chu);
1095
1096     name_list += nick_len + 1;
1097   }
1098
1099   name_list = cp;
1100   for (i = 0; i < list_count; i++) {
1101     int c = 0;
1102     int nick_len = strcspn(name_list, " ");
1103     char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname));
1104     memcpy(nickname, name_list, nick_len);
1105
1106     silc_list_start(channel->clients);
1107     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1108       if (!strncmp(chu->client->nickname, nickname, 
1109                    strlen(chu->client->nickname))) {
1110         char t[8];
1111         
1112         if (!c) {
1113           c++;
1114           continue;
1115         }
1116         
1117         memset(t, 0, sizeof(t));
1118         chu->client->nickname = silc_calloc(strlen(nickname) + 8, 1);
1119         snprintf(t, sizeof(t), "[%d]", c++);
1120         strncat(chu->client->nickname, t, strlen(t));
1121         strncat(chu->client->nickname, nickname, strlen(nickname));
1122       }
1123     }
1124
1125     silc_free(nickname);
1126   }
1127
1128   /* XXX hmm... actually it is applications business to display this
1129      information. We should just pass (as we do) the data to application and
1130      let it to parse it and display it the way it wants. */
1131   if (cmd->callback) {
1132     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1133                           channel->channel_name);
1134     
1135     silc_list_start(channel->clients);
1136     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1137       SilcClientEntry e = chu->client;
1138       char *m, tmp[80], line[80];
1139
1140       memset(line, 0, sizeof(line));
1141       memset(tmp, 0, sizeof(tmp));
1142       m = silc_client_chumode_char(chu->mode);
1143
1144       strcat(line, " ");
1145       strcat(line, e->nickname);
1146       strcat(line, e->server ? "@" : "");
1147
1148       len1 = 0;
1149       if (e->server)
1150         len1 = strlen(e->server);
1151       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1152
1153       len1 = strlen(line);
1154       if (len1 >= 30) {
1155         memset(&line[29], 0, len1 - 29);
1156       } else {
1157         for (i = 0; i < 30 - len1 - 1; i++)
1158           strcat(line, " ");
1159       }
1160
1161       strcat(line, "  H");
1162       strcat(tmp, m ? m : "");
1163       strcat(line, tmp);
1164
1165       if (strlen(tmp) < 5)
1166         for (i = 0; i < 5 - strlen(tmp); i++)
1167           strcat(line, " ");
1168
1169       strcat(line, e->username ? e->username : "");
1170
1171       cmd->client->ops->say(cmd->client, conn, "%s", line);
1172
1173       if (m)
1174         silc_free(m);
1175     }
1176
1177   } else {
1178     name_list = NULL;
1179     len1 = 0;
1180     k = 0;
1181
1182     silc_list_start(channel->clients);
1183     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1184       char *m, *n = chu->client->nickname;
1185       len2 = strlen(n);
1186       len1 += len2;
1187
1188       name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1189
1190       m = silc_client_chumode_char(chu->mode);
1191       if (m) {
1192         memcpy(name_list + (len1 - len2), m, strlen(m));
1193         len1 += strlen(m);
1194         silc_free(m);
1195       }
1196
1197       memcpy(name_list + (len1 - len2), n, len2);
1198       name_list[len1] = 0;
1199       
1200       if (k == silc_list_count(channel->clients) - 1)
1201         break;
1202       memcpy(name_list + len1, " ", 1);
1203       len1++;
1204       k++;
1205     }
1206
1207     cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1208                           channel->channel_name, name_list);
1209     silc_free(name_list);
1210   }
1211
1212   name_list = silc_argument_get_arg_type(cmd->args, 3, &len1);
1213
1214   /* Notify application */
1215   COMMAND_REPLY((ARGS, channel, name_list, client_id_list->head,
1216                  client_mode_list->head));
1217
1218   silc_buffer_free(client_id_list);
1219   silc_buffer_free(client_mode_list);
1220
1221  out:
1222   if (channel_id)
1223     silc_free(channel_id);
1224   silc_client_command_reply_free(cmd);
1225 }