Changed channel->clients to SilcList.
[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   SilcChannelUser chu;
959   SilcChannelID *channel_id = NULL;
960   SilcBuffer client_id_list;
961   SilcBuffer client_mode_list;
962   unsigned char *tmp;
963   char *name_list, *cp;
964   int i, k, len1, len2, list_count = 0;
965
966   SILC_LOG_DEBUG(("Start"));
967
968   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
969   SILC_GET16_MSB(status, tmp);
970   if (status != SILC_STATUS_OK) {
971     cmd->client->ops->say(cmd->client, conn,
972              "%s", silc_client_command_status_message(status));
973     COMMAND_REPLY_ERROR;
974     goto out;
975   }
976
977   /* Get channel ID */
978   tmp = silc_argument_get_arg_type(cmd->args, 2, &len1);
979   if (!tmp) {
980     cmd->client->ops->say(cmd->client, conn, 
981                           "Cannot Channel ID: Bad reply packet");
982     COMMAND_REPLY_ERROR;
983     goto out;
984   }
985   channel_id = silc_id_payload_parse_id(tmp, len1);
986
987   /* Get the name list of the channel */
988   name_list = silc_argument_get_arg_type(cmd->args, 3, &len1);
989   if (!name_list) {
990     cmd->client->ops->say(cmd->client, conn, 
991                           "Cannot get user list: Bad reply packet");
992     COMMAND_REPLY_ERROR;
993     goto out;
994   }
995
996   /* Get Client ID list */
997   tmp = silc_argument_get_arg_type(cmd->args, 4, &len2);
998   if (!tmp) {
999     cmd->client->ops->say(cmd->client, conn, 
1000                           "Cannot get user list: Bad reply packet");
1001     COMMAND_REPLY_ERROR;
1002     goto out;
1003   }
1004
1005   client_id_list = silc_buffer_alloc(len2);
1006   silc_buffer_pull_tail(client_id_list, len2);
1007   silc_buffer_put(client_id_list, tmp, len2);
1008
1009   /* Get client mode list */
1010   tmp = silc_argument_get_arg_type(cmd->args, 5, &len2);
1011   if (!tmp) {
1012     cmd->client->ops->say(cmd->client, conn, 
1013                           "Cannot get user list: Bad reply packet");
1014     COMMAND_REPLY_ERROR;
1015     goto out;
1016   }
1017
1018   client_mode_list = silc_buffer_alloc(len2);
1019   silc_buffer_pull_tail(client_mode_list, len2);
1020   silc_buffer_put(client_mode_list, tmp, len2);
1021
1022   /* Get the channel name */
1023   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1024                                    SILC_ID_CHANNEL, &id_cache)) {
1025     COMMAND_REPLY_ERROR;
1026     goto out;
1027   }
1028   
1029   channel = (SilcChannelEntry)id_cache->context;
1030
1031   /* Remove commas from list */
1032   for (i = 0; i < len1; i++)
1033     if (name_list[i] == ',') {
1034       name_list[i] = ' ';
1035       list_count++;
1036     }
1037   list_count++;
1038
1039   /* Remove old client list from channel. */
1040   silc_list_start(channel->clients);
1041   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1042     silc_list_del(channel->clients, chu);
1043     silc_free(chu);
1044   }
1045
1046   /* Cache the received name list, client ID's and modes. This cache expires
1047      whenever server sends notify message to channel. It means two things;
1048      some user has joined or leaved the channel. */
1049   cp = name_list;
1050   for (i = 0; i < list_count; i++) {
1051     int nick_len = strcspn(name_list, " ");
1052     unsigned short idp_len;
1053     unsigned int mode;
1054     char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname));
1055     SilcClientID *client_id;
1056     SilcClientEntry client;
1057
1058     memcpy(nickname, name_list, nick_len);
1059     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1060     idp_len += 4;
1061     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1062     silc_buffer_pull(client_id_list, idp_len);
1063     
1064     SILC_GET32_MSB(mode, client_mode_list->data);
1065     silc_buffer_pull(client_mode_list, 4);
1066
1067     /* Check if we have this client cached already. */
1068     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1069                                      SILC_ID_CLIENT, &id_cache)) {
1070       client = silc_calloc(1, sizeof(*client));
1071       client->id = client_id;
1072       silc_parse_nickname(nickname, &client->nickname, &client->server, 
1073                           &client->num);
1074       silc_free(nickname);
1075
1076       /* Add client to cache */
1077       silc_idcache_add(conn->client_cache, client->nickname, SILC_ID_CLIENT,
1078                        client_id, (void *)client, TRUE);
1079     } else {
1080       client = (SilcClientEntry)id_cache->context;
1081       silc_free(client_id);
1082       silc_free(nickname);
1083       id_cache = NULL;
1084     }
1085
1086     chu = silc_calloc(1, sizeof(*chu));
1087     chu->client = client;
1088     chu->mode = mode;
1089     silc_list_add(channel->clients, chu);
1090
1091     name_list += nick_len + 1;
1092   }
1093
1094   name_list = cp;
1095   for (i = 0; i < list_count; i++) {
1096     int c = 0;
1097     int nick_len = strcspn(name_list, " ");
1098     char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname));
1099     memcpy(nickname, name_list, nick_len);
1100
1101     silc_list_start(channel->clients);
1102     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1103       if (!strncmp(chu->client->nickname, nickname, 
1104                    strlen(chu->client->nickname))) {
1105         char t[8];
1106         
1107         if (!c) {
1108           c++;
1109           continue;
1110         }
1111         
1112         memset(t, 0, sizeof(t));
1113         chu->client->nickname = silc_calloc(strlen(nickname) + 8, 1);
1114         snprintf(t, sizeof(t), "[%d]", c++);
1115         strncat(chu->client->nickname, t, strlen(t));
1116         strncat(chu->client->nickname, nickname, strlen(nickname));
1117       }
1118     }
1119
1120     silc_free(nickname);
1121   }
1122
1123   /* XXX hmm... actually it is applications business to display this
1124      information. We should just pass (as we do) the data to application and
1125      let it to parse it and display it the way it wants. */
1126   if (cmd->callback) {
1127     cmd->client->ops->say(cmd->client, conn, "Users on %s", 
1128                           channel->channel_name);
1129     
1130     silc_list_start(channel->clients);
1131     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1132       SilcClientEntry e = chu->client;
1133       char *m, tmp[80], line[80];
1134
1135       memset(line, 0, sizeof(line));
1136       memset(tmp, 0, sizeof(tmp));
1137       m = silc_client_chumode_char(chu->mode);
1138
1139       strcat(line, " ");
1140       strcat(line, e->nickname);
1141       strcat(line, e->server ? "@" : "");
1142
1143       len1 = 0;
1144       if (e->server)
1145         len1 = strlen(e->server);
1146       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1147
1148       len1 = strlen(line);
1149       if (len1 >= 30) {
1150         memset(&line[29], 0, len1 - 29);
1151       } else {
1152         for (i = 0; i < 30 - len1 - 1; i++)
1153           strcat(line, " ");
1154       }
1155
1156       strcat(line, "  H");
1157       strcat(tmp, m ? m : "");
1158       strcat(line, tmp);
1159
1160       if (strlen(tmp) < 5)
1161         for (i = 0; i < 5 - strlen(tmp); i++)
1162           strcat(line, " ");
1163
1164       strcat(line, e->username ? e->username : "");
1165
1166       cmd->client->ops->say(cmd->client, conn, "%s", line);
1167
1168       if (m)
1169         silc_free(m);
1170     }
1171
1172   } else {
1173     name_list = NULL;
1174     len1 = 0;
1175     k = 0;
1176
1177     silc_list_start(channel->clients);
1178     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1179       char *m, *n = chu->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(chu->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 == silc_list_count(channel->clients) - 1)
1196         break;
1197       memcpy(name_list + len1, " ", 1);
1198       len1++;
1199       k++;
1200     }
1201
1202     cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1203                           channel->channel_name, name_list);
1204     silc_free(name_list);
1205   }
1206
1207   name_list = silc_argument_get_arg_type(cmd->args, 3, &len1);
1208
1209   /* Notify application */
1210   COMMAND_REPLY((ARGS, channel, name_list, client_id_list->head,
1211                  client_mode_list->head));
1212
1213   silc_buffer_free(client_id_list);
1214   silc_buffer_free(client_mode_list);
1215
1216  out:
1217   if (channel_id)
1218     silc_free(channel_id);
1219   silc_client_command_reply_free(cmd);
1220 }