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