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(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   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 = NULL;
696   unsigned int argc, mode, len;
697   char *topic, *tmp, *channel_name = NULL;
698   SilcBuffer keyp;
699
700   SILC_LOG_DEBUG(("Start"));
701
702   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
703   if (status != SILC_STATUS_OK) {
704     cmd->client->ops->say(cmd->client, conn,
705              "%s", silc_client_command_status_message(status));
706     COMMAND_REPLY_ERROR;
707     goto out;
708   }
709
710   argc = silc_argument_get_arg_num(cmd->args);
711   if (argc < 3 || argc > 9) {
712     cmd->client->ops->say(cmd->client, conn,
713              "Cannot join channel: Bad reply packet");
714     COMMAND_REPLY_ERROR;
715     goto out;
716   }
717
718   /* Get channel name */
719   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
720   if (!tmp) {
721     cmd->client->ops->say(cmd->client, conn, 
722                           "Cannot join channel: Bad reply packet");
723     COMMAND_REPLY_ERROR;
724     goto out;
725   }
726   channel_name = strdup(tmp);
727
728   /* Get Channel ID */
729   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
730   if (!tmp) {
731     cmd->client->ops->say(cmd->client, conn, 
732                           "Cannot join channel: Bad reply packet");
733     COMMAND_REPLY_ERROR;
734     silc_free(channel_name);
735     goto out;
736   }
737   idp = silc_id_payload_parse_data(tmp, len);
738
739   /* Get channel mode */
740   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
741   if (tmp)
742     SILC_GET32_MSB(mode, tmp);
743   else
744     mode = 0;
745
746   /* Get channel key */
747   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
748   if (!tmp) {
749     silc_id_payload_free(idp);
750     silc_free(channel_name);
751     goto out;
752   }
753   keyp = silc_buffer_alloc(len);
754   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
755   silc_buffer_put(keyp, tmp, len);
756
757   /* Get topic */
758   topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
759
760   /* Save received Channel ID */
761   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
762                              mode, idp);
763   silc_id_payload_free(idp);
764
765   /* Save channel key */
766   silc_client_save_channel_key(conn, keyp, conn->current_channel);
767   silc_buffer_free(keyp);
768
769   if (topic)
770     client->ops->say(cmd->client, conn, 
771                      "Topic for %s: %s", channel_name, topic);
772
773   /* Notify application */
774   COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
775                  NULL, NULL, topic));
776
777  out:
778   silc_client_command_reply_free(cmd);
779 }
780
781 /* Received reply for MOTD command */
782
783 SILC_CLIENT_CMD_REPLY_FUNC(motd)
784 {
785   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
786   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
787   SilcCommandStatus status;
788   unsigned int argc, i;
789   unsigned char *tmp;
790   char *motd = NULL, *cp, line[256];
791
792   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
793   SILC_GET16_MSB(status, tmp);
794   if (status != SILC_STATUS_OK) {
795     cmd->client->ops->say(cmd->client, conn,
796              "%s", silc_client_command_status_message(status));
797     COMMAND_REPLY_ERROR;
798     return;
799   }
800
801   argc = silc_argument_get_arg_num(cmd->args);
802   if (argc > 2) {
803     COMMAND_REPLY_ERROR;
804     goto out;
805   }
806
807   if (argc == 2) {
808     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
809     if (!motd) {
810       COMMAND_REPLY_ERROR;
811       goto out;
812     }
813
814     i = 0;
815     cp = motd;
816     while(cp[i] != 0) {
817       if (cp[i++] == '\n') {
818         memset(line, 0, sizeof(line));
819         strncat(line, cp, i - 1);
820         cp += i;
821         
822         if (i == 2)
823           line[0] = ' ';
824         
825         cmd->client->ops->say(cmd->client, conn, "%s", line);
826         
827         if (!strlen(cp))
828           break;
829         i = 0;
830       }
831     }
832   }
833
834   /* Notify application */
835   COMMAND_REPLY((ARGS, motd));
836
837  out:
838   silc_client_command_reply_free(cmd);
839 }
840
841 SILC_CLIENT_CMD_REPLY_FUNC(umode)
842 {
843 }
844
845 /* Received reply for CMODE command. */
846
847 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
848 {
849   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
850   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
851   SilcCommandStatus status;
852   unsigned char *tmp;
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   /* Get channel mode */
863   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
864   if (!tmp) {
865     COMMAND_REPLY_ERROR;
866     goto out;
867   }
868
869   /* Notify application */
870   COMMAND_REPLY((ARGS, tmp));
871
872  out:
873   silc_client_command_reply_free(cmd);
874 }
875
876 /* Received reply for CUMODE command */
877
878 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
879 {
880   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
881   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
882   SilcCommandStatus status;
883   SilcIDCacheEntry id_cache = NULL;
884   SilcClientID *client_id;
885   unsigned char *tmp, *id;
886   unsigned int len;
887   
888   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
889   if (status != SILC_STATUS_OK) {
890     cmd->client->ops->say(cmd->client, conn,
891              "%s", silc_client_command_status_message(status));
892     COMMAND_REPLY_ERROR;
893     goto out;
894   }
895   
896   /* Get channel mode */
897   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
898   if (!tmp) {
899     COMMAND_REPLY_ERROR;
900     goto out;
901   }
902
903   /* Get Client ID */
904   id = silc_argument_get_arg_type(cmd->args, 3, &len);
905   if (!id) {
906     COMMAND_REPLY_ERROR;
907     goto out;
908   }
909   client_id = silc_id_payload_parse_id(id, len);
910   
911   /* Get client entry */
912   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
913                                    SILC_ID_CLIENT, &id_cache)) {
914     COMMAND_REPLY_ERROR;
915     goto out;
916   }
917
918   /* Notify application */
919   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
920   silc_free(client_id);
921   
922  out:
923   silc_client_command_reply_free(cmd);
924 }
925
926 SILC_CLIENT_CMD_REPLY_FUNC(kick)
927 {
928 }
929
930 SILC_CLIENT_CMD_REPLY_FUNC(restart)
931 {
932 }
933  
934 SILC_CLIENT_CMD_REPLY_FUNC(close)
935 {
936 }
937  
938 SILC_CLIENT_CMD_REPLY_FUNC(die)
939 {
940 }
941  
942 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
943 {
944 }
945
946 /* Reply to LEAVE command. */
947
948 SILC_CLIENT_CMD_REPLY_FUNC(leave)
949 {
950   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
951   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
952   SilcCommandStatus status;
953   unsigned char *tmp;
954
955   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
956   SILC_GET16_MSB(status, tmp);
957   if (status != SILC_STATUS_OK) {
958     cmd->client->ops->say(cmd->client, conn,
959              "%s", silc_client_command_status_message(status));
960     COMMAND_REPLY_ERROR;
961     return;
962   }
963
964   /* Notify application */
965   COMMAND_REPLY((ARGS));
966
967   silc_client_command_reply_free(cmd);
968 }
969
970 /* Reply to USERS command. Received list of client ID's and theirs modes
971    on the channel we requested. */
972
973 SILC_CLIENT_CMD_REPLY_FUNC(users)
974 {
975   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
976   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
977   SilcCommandStatus status;
978   SilcIDCacheEntry id_cache = NULL;
979   SilcChannelEntry channel;
980   SilcChannelUser chu;
981   SilcChannelID *channel_id = NULL;
982   SilcBuffer client_id_list;
983   SilcBuffer client_mode_list;
984   unsigned char *tmp;
985   unsigned int tmp_len;
986   int i, k, len1, len2, list_count;
987   unsigned char **res_argv = NULL;
988   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
989   char *name_list, *cp;
990
991   SILC_LOG_DEBUG(("Start"));
992
993   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
994   SILC_GET16_MSB(status, tmp);
995   if (status != SILC_STATUS_OK) {
996     cmd->client->ops->say(cmd->client, conn,
997              "%s", silc_client_command_status_message(status));
998     COMMAND_REPLY_ERROR;
999     goto out;
1000   }
1001
1002   /* Get channel ID */
1003   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1004   if (!tmp)
1005     goto out;
1006   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1007
1008   /* Get the list count */
1009   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1010   if (!tmp)
1011     goto out;
1012   SILC_GET32_MSB(list_count, tmp);
1013
1014   /* Get Client ID list */
1015   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1016   if (!tmp)
1017     goto out;
1018
1019   client_id_list = silc_buffer_alloc(tmp_len);
1020   silc_buffer_pull_tail(client_id_list, tmp_len);
1021   silc_buffer_put(client_id_list, tmp, tmp_len);
1022
1023   /* Get client mode list */
1024   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1025   if (!tmp)
1026     goto out;
1027
1028   client_mode_list = silc_buffer_alloc(tmp_len);
1029   silc_buffer_pull_tail(client_mode_list, tmp_len);
1030   silc_buffer_put(client_mode_list, tmp, tmp_len);
1031
1032   /* Get channel entry */
1033   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1034                                    SILC_ID_CHANNEL, &id_cache)) {
1035     COMMAND_REPLY_ERROR;
1036     goto out;
1037   }
1038   channel = (SilcChannelEntry)id_cache->context;
1039
1040   /* Remove old client list from channel. */
1041   silc_list_start(channel->clients);
1042   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1043     silc_list_del(channel->clients, chu);
1044     silc_free(chu);
1045   }
1046
1047   /* Cache the received Client ID's and modes. This cache expires
1048      whenever server sends notify message to channel. It means two things;
1049      some user has joined or leaved the channel. XXX! */
1050   for (i = 0; i < list_count; i++) {
1051     unsigned short idp_len;
1052     unsigned int mode;
1053     SilcClientID *client_id;
1054     SilcClientEntry client;
1055
1056     /* Client ID */
1057     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1058     idp_len += 4;
1059     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1060     silc_buffer_pull(client_id_list, idp_len);
1061
1062     /* Mode */
1063     SILC_GET32_MSB(mode, client_mode_list->data);
1064     silc_buffer_pull(client_mode_list, 4);
1065
1066     /* Check if we have this client cached already. */
1067     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1068                                      SILC_ID_CLIENT, &id_cache)) {
1069       /* No we don't have it, query it from the server. Assemble argument
1070          table that will be sent fr the IDENTIFY command later. */
1071       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1072                               (res_argc + 1));
1073       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1074                                    (res_argc + 1));
1075       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1076                                     (res_argc + 1));
1077       res_argv[res_argc] = client_id_list->data;
1078       res_argv_lens[res_argc] = idp_len;
1079       res_argv_types[res_argc] = res_argc + 3;
1080       res_argc++;
1081     } else {
1082       /* Found the client, join it to the channel */
1083       client = (SilcClientEntry)id_cache->context;
1084       chu = silc_calloc(1, sizeof(*chu));
1085       chu->client = client;
1086       chu->mode = mode;
1087       silc_list_add(channel->clients, chu);
1088
1089       silc_free(client_id);
1090       id_cache = NULL;
1091     }
1092   }
1093
1094   /* Query the client information from server if the list included clients
1095      that we don't know about. */
1096   if (res_argc) {
1097 #if 0
1098     SilcBuffer res_cmd;
1099
1100     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1101                                           res_argc, res_argv, res_argv_lens,
1102                                           res_argv_types, 0);
1103     silc_client_packet_send(cmd->client, cmd->conn->sock,
1104                             SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
1105                             buffer->data, buffer->len, TRUE);
1106     goto out;
1107 #endif
1108   }
1109
1110   name_list = cp;
1111   for (i = 0; i < list_count; i++) {
1112     int c = 0;
1113     int nick_len = strcspn(name_list, " ");
1114     char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname));
1115     memcpy(nickname, name_list, nick_len);
1116
1117     silc_list_start(channel->clients);
1118     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1119       if (!strncmp(chu->client->nickname, nickname, 
1120                    strlen(chu->client->nickname))) {
1121         char t[8];
1122         
1123         if (!c) {
1124           c++;
1125           continue;
1126         }
1127         
1128         memset(t, 0, sizeof(t));
1129         chu->client->nickname = silc_calloc(strlen(nickname) + 8, 1);
1130         snprintf(t, sizeof(t), "[%d]", c++);
1131         strncat(chu->client->nickname, t, strlen(t));
1132         strncat(chu->client->nickname, nickname, strlen(nickname));
1133       }
1134     }
1135
1136     silc_free(nickname);
1137   }
1138
1139   /* XXX hmm... actually it is applications business to display this
1140      information. We should just pass (as we do) the data to application and
1141      let it to parse it and display it the way it wants. */
1142   if (cmd->callback) {
1143     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1144                           channel->channel_name);
1145     
1146     silc_list_start(channel->clients);
1147     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1148       SilcClientEntry e = chu->client;
1149       char *m, tmp[80], line[80];
1150
1151       memset(line, 0, sizeof(line));
1152       memset(tmp, 0, sizeof(tmp));
1153       m = silc_client_chumode_char(chu->mode);
1154
1155       strcat(line, " ");
1156       strcat(line, e->nickname);
1157       strcat(line, e->server ? "@" : "");
1158
1159       len1 = 0;
1160       if (e->server)
1161         len1 = strlen(e->server);
1162       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1163
1164       len1 = strlen(line);
1165       if (len1 >= 30) {
1166         memset(&line[29], 0, len1 - 29);
1167       } else {
1168         for (i = 0; i < 30 - len1 - 1; i++)
1169           strcat(line, " ");
1170       }
1171
1172       strcat(line, "  H");
1173       strcat(tmp, m ? m : "");
1174       strcat(line, tmp);
1175
1176       if (strlen(tmp) < 5)
1177         for (i = 0; i < 5 - strlen(tmp); i++)
1178           strcat(line, " ");
1179
1180       strcat(line, e->username ? e->username : "");
1181
1182       cmd->client->ops->say(cmd->client, conn, "%s", line);
1183
1184       if (m)
1185         silc_free(m);
1186     }
1187
1188   } else {
1189     name_list = NULL;
1190     len1 = 0;
1191     k = 0;
1192
1193     silc_list_start(channel->clients);
1194     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1195       char *m, *n = chu->client->nickname;
1196       len2 = strlen(n);
1197       len1 += len2;
1198
1199       name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1200
1201       m = silc_client_chumode_char(chu->mode);
1202       if (m) {
1203         memcpy(name_list + (len1 - len2), m, strlen(m));
1204         len1 += strlen(m);
1205         silc_free(m);
1206       }
1207
1208       memcpy(name_list + (len1 - len2), n, len2);
1209       name_list[len1] = 0;
1210       
1211       if (k == silc_list_count(channel->clients) - 1)
1212         break;
1213       memcpy(name_list + len1, " ", 1);
1214       len1++;
1215       k++;
1216     }
1217
1218     cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1219                           channel->channel_name, name_list);
1220     silc_free(name_list);
1221   }
1222
1223   name_list = silc_argument_get_arg_type(cmd->args, 3, &len1);
1224
1225   /* Notify application */
1226   COMMAND_REPLY((ARGS, channel, name_list, client_id_list->head,
1227                  client_mode_list->head));
1228
1229   silc_buffer_free(client_id_list);
1230   silc_buffer_free(client_mode_list);
1231
1232  out:
1233   if (channel_id)
1234     silc_free(channel_id);
1235   silc_client_command_reply_free(cmd);
1236 }