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