Freeciv-3.1
Loading...
Searching...
No Matches
handchat.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18#include <stdarg.h>
19#include <stdio.h>
20#include <string.h>
21
22/* utility */
23#include "fcintl.h"
24#include "log.h"
25#include "shared.h"
26#include "support.h"
27
28/* common */
29#include "chat.h"
30#include "game.h"
31#include "packets.h"
32#include "player.h"
33
34/* server */
35#include "console.h"
36#include "notify.h"
37#include "stdinhand.h"
38
39#include "handchat.h"
40
41#define MAX_LEN_CHAT_NAME (2*MAX_LEN_NAME+10) /* for form_chat_name() names */
42
43static void send_chat_msg(struct connection *pconn,
44 const struct connection *sender,
45 const struct ft_color color,
46 const char *format, ...)
47 fc__attribute((__format__ (__printf__, 4, 5)));
48
49/**********************************************************************/
52static inline bool conn_is_ignored(const struct connection *sender,
53 const struct connection *dest)
54{
55 if (NULL != sender && NULL != dest) {
56 return conn_pattern_list_match(dest->server.ignore_list, sender);
57 } else {
58 return FALSE;
59 }
60}
61
62/**********************************************************************/
67static void form_chat_name(struct connection *pconn, char *buffer, size_t len)
68{
69 struct player *pplayer = pconn->playing;
70
71 if (!pplayer
72 || pconn->observer
73 || strcmp(player_name(pplayer), ANON_PLAYER_NAME) == 0) {
74 fc_snprintf(buffer, len, "(%s)", pconn->username);
75 } else {
76 fc_snprintf(buffer, len, "%s", player_name(pplayer));
77 }
78}
79
80/**********************************************************************/
83static void send_chat_msg(struct connection *pconn,
84 const struct connection *sender,
85 const struct ft_color color,
86 const char *format, ...)
87{
88 struct packet_chat_msg packet;
89 va_list args;
90
91 va_start(args, format);
92 vpackage_chat_msg(&packet, sender, color, format, args);
93 va_end(args);
94
95 send_packet_chat_msg(pconn, &packet);
96}
97
98/**********************************************************************/
103static void complain_ambiguous(struct connection *pconn, const char *name,
104 int player_conn)
105{
106 switch (player_conn) {
107 case 0:
108 notify_conn(pconn->self, NULL, E_CHAT_ERROR, ftc_server,
109 _("%s is an ambiguous player name-prefix."), name);
110 break;
111 case 1:
112 notify_conn(pconn->self, NULL, E_CHAT_ERROR, ftc_server,
113 _("%s is an ambiguous connection name-prefix."), name);
114 break;
115 case 2:
116 notify_conn(pconn->self, NULL, E_CHAT_ERROR, ftc_server,
117 _("%s is an anonymous name. Use connection name."), name);
118 break;
119 default:
120 log_error("Unknown variant in %s(): %d.", __FUNCTION__, player_conn);
121 }
122}
123
124/**********************************************************************/
127static void chat_msg_to_conn(struct connection *sender,
128 struct connection *dest, char *msg)
129{
130 char sender_name[MAX_LEN_CHAT_NAME], dest_name[MAX_LEN_CHAT_NAME];
131
132 form_chat_name(dest, dest_name, sizeof(dest_name));
133
134 if (conn_is_ignored(sender, dest)) {
135 send_chat_msg(sender, NULL, ftc_warning,
136 _("You cannot send messages to %s; you are ignored."),
137 dest_name);
138 return;
139 }
140
141 msg = skip_leading_spaces(msg);
142 form_chat_name(sender, sender_name, sizeof(sender_name));
143
144 send_chat_msg(sender, sender, ftc_chat_private,
145 "->*%s* %s", dest_name, msg);
146
147 if (sender != dest) {
148 send_chat_msg(dest, sender, ftc_chat_private,
149 "*%s* %s", sender_name, msg);
150 }
151}
152
153/**********************************************************************/
156static void chat_msg_to_player(struct connection *sender,
157 struct player *pdest, char *msg)
158{
159 struct packet_chat_msg packet;
160 char sender_name[MAX_LEN_CHAT_NAME];
161 struct connection *dest = NULL; /* The 'pdest' user. */
162 struct event_cache_players *players = event_cache_player_add(NULL, pdest);
163
164 msg = skip_leading_spaces(msg);
165 form_chat_name(sender, sender_name, sizeof(sender_name));
166
167 /* Find the user of the player 'pdest'. */
168 conn_list_iterate(pdest->connections, pconn) {
169 if (!pconn->observer) {
170 /* Found it! */
171 if (conn_is_ignored(sender, pconn)) {
172 send_chat_msg(sender, NULL, ftc_warning,
173 _("You cannot send messages to %s; you are ignored."),
174 player_name(pdest));
175 return; /* NB: stop here, don't send to observers. */
176 }
177 dest = pconn;
178 break;
179 }
181
182 /* Repeat the message for the sender. */
183 send_chat_msg(sender, sender, ftc_chat_private,
184 "->{%s} %s", player_name(pdest), msg);
185
186 /* Send the message to destination. */
187 if (NULL != dest && dest != sender) {
188 send_chat_msg(dest, sender, ftc_chat_private,
189 "{%s} %s", sender_name, msg);
190 }
191
192 /* Send the message to player observers. */
193 package_chat_msg(&packet, sender, ftc_chat_private,
194 "{%s -> %s} %s", sender_name, player_name(pdest), msg);
195 conn_list_iterate(pdest->connections, pconn) {
196 if (pconn != dest
197 && pconn != sender
198 && !conn_is_ignored(sender, pconn)) {
199 send_packet_chat_msg(pconn, &packet);
200 }
202 if (NULL != sender->playing
203 && !sender->observer
204 && sender->playing != pdest) {
205 /* The sender is another player. */
206 conn_list_iterate(sender->playing->connections, pconn) {
207 if (pconn != sender && !conn_is_ignored(sender, pconn)) {
208 send_packet_chat_msg(pconn, &packet);
209 }
211
212 /* Add player to event cache. */
213 players = event_cache_player_add(players, sender->playing);
214 }
215
216 event_cache_add_for_players(&packet, players);
217}
218
219/**********************************************************************/
222static void chat_msg_to_allies(struct connection *sender, char *msg)
223{
224 struct packet_chat_msg packet;
225 struct event_cache_players *players = NULL;
226 char sender_name[MAX_LEN_CHAT_NAME];
227
228 msg = skip_leading_spaces(msg);
229 form_chat_name(sender, sender_name, sizeof(sender_name));
230
231 package_chat_msg(&packet, sender, ftc_chat_ally,
232 _("%s to allies: %s"), sender_name, msg);
233
234 players_iterate(aplayer) {
235 if (!pplayers_allied(sender->playing, aplayer)) {
236 continue;
237 }
238
239 conn_list_iterate(aplayer->connections, pconn) {
240 if (!conn_is_ignored(sender, pconn)) {
241 send_packet_chat_msg(pconn, &packet);
242 }
244 players = event_cache_player_add(players, aplayer);
246
247 /* Add to the event cache. */
248 event_cache_add_for_players(&packet, players);
249}
250
251/**********************************************************************/
254static void chat_msg_to_global_observers(struct connection *sender,
255 char *msg)
256{
257 struct packet_chat_msg packet;
258 char sender_name[MAX_LEN_CHAT_NAME];
259
260 msg = skip_leading_spaces(msg);
261 form_chat_name(sender, sender_name, sizeof(sender_name));
262
263 package_chat_msg(&packet, sender, ftc_chat_ally,
264 _("%s to global observers: %s"), sender_name, msg);
265
267 if (conn_is_global_observer(dest_conn)
268 && !conn_is_ignored(sender, dest_conn)) {
269 send_packet_chat_msg(dest_conn, &packet);
270 }
272
273 /* Add to the event cache. */
275}
276
277/**********************************************************************/
280static void chat_msg_to_all(struct connection *sender, char *msg)
281{
282 struct packet_chat_msg packet;
283 char sender_name[MAX_LEN_CHAT_NAME];
284
285 msg = skip_leading_spaces(msg);
286 form_chat_name(sender, sender_name, sizeof(sender_name));
287
288 package_chat_msg(&packet, sender, ftc_chat_public,
289 "<%s> %s", sender_name, msg);
290 con_write(C_COMMENT, "%s", packet.message);
292
293 /* Add to the event cache. */
295}
296
297/**********************************************************************/
320void handle_chat_msg_req(struct connection *pconn, const char *message)
321{
322 char real_message[MAX_LEN_MSG], *cp;
323 bool double_colon;
324
325 sz_strlcpy(real_message, message);
326
327 /* This loop to prevent players from sending multiple lines which can
328 * be abused */
329 for (cp = real_message; *cp != '\0'; cp++) {
330 if (*cp == '\n' || *cp == '\r') {
331 *cp = '\0';
332 break;
333 }
334 }
335
336 /* Server commands are prefixed with '/', which is an obvious
337 but confusing choice: even before this feature existed,
338 novice players were trying /who, /nick etc.
339 So consider this an incentive for IRC support,
340 or change it in chat.h - rp
341 */
342 if (real_message[0] == SERVER_COMMAND_PREFIX) {
343 /* Pass it to the command parser, which will chop the prefix off */
344 handle_stdin_input(pconn, real_message);
345
346 return;
347 }
348
349 /* Send to allies command */
350 if (real_message[0] == CHAT_ALLIES_PREFIX) {
351 /* this won't work if we aren't attached to a player */
352 if (NULL == pconn->playing && !pconn->observer) {
353 notify_conn(pconn->self, NULL, E_CHAT_ERROR, ftc_server,
354 _("You are not attached to a player."));
355 return;
356 }
357
358 if (NULL != pconn->playing) {
359 chat_msg_to_allies(pconn, real_message + 1);
360 } else {
361 chat_msg_to_global_observers(pconn, real_message + 1);
362 }
363 return;
364 }
365
366 /* Want to allow private messages with "player_name: message",
367 (or "connection_name: message"), including unambiguously
368 abbreviated player/connection name, but also want to allow
369 sensible use of ':' within messages, and _also_ want to
370 notice intended private messages with (eg) mis-spelt name.
371
372 Approach:
373
374 If there is no ':', or ':' is first on line,
375 message is global (send to all players)
376 else if the ':' is double, try matching part before "::" against
377 connection names: for single match send to that connection,
378 for multiple matches complain, else goto heuristics below.
379 else try matching part before (single) ':' against player names:
380 for single match send to that player, for multiple matches
381 complain
382 else try matching against connection names: for single match send
383 to that connection, for multiple matches complain
384 else if some heuristics apply (a space anywhere before first ':')
385 then treat as global message,
386 else complain (might be a typo-ed intended private message)
387 */
388
389 cp = strchr(real_message, CHAT_DIRECT_PREFIX);
390
391 if (cp && (cp != &real_message[0])) {
392 enum m_pre_result match_result_player, match_result_conn;
393 struct player *pdest = NULL;
394 struct connection *conn_dest = NULL;
395 char name[MAX_LEN_NAME];
396 char *cpblank;
397
398 (void) fc_strlcpy(name, real_message, MIN(sizeof(name),
399 cp - real_message + 1));
400
401 double_colon = (*(cp+1) == CHAT_DIRECT_PREFIX);
402 if (double_colon) {
403 conn_dest = conn_by_user_prefix(name, &match_result_conn);
404 if (match_result_conn == M_PRE_AMBIGUOUS) {
405 complain_ambiguous(pconn, name, 1);
406 return;
407 }
408 if (conn_dest && match_result_conn < M_PRE_AMBIGUOUS) {
409 chat_msg_to_conn(pconn, conn_dest, cp+2);
410 return;
411 }
412 } else {
413 /* single colon */
414 pdest = player_by_name_prefix(name, &match_result_player);
415 if (match_result_player == M_PRE_AMBIGUOUS) {
416 complain_ambiguous(pconn, name, 0);
417 return;
418 }
419 if (pdest && strcmp(player_name(pdest), ANON_PLAYER_NAME) == 0) {
420 complain_ambiguous(pconn, name, 2);
421 return;
422 }
423 if (pdest && match_result_player < M_PRE_AMBIGUOUS) {
424 chat_msg_to_player(pconn, pdest, cp + 1);
425 return;
426 /* else try for connection name match before complaining */
427 }
428 conn_dest = conn_by_user_prefix(name, &match_result_conn);
429 if (match_result_conn == M_PRE_AMBIGUOUS) {
430 complain_ambiguous(pconn, name, 1);
431 return;
432 }
433 if (conn_dest && match_result_conn < M_PRE_AMBIGUOUS) {
434 chat_msg_to_conn(pconn, conn_dest, cp+1);
435 return;
436 }
437 if (pdest && match_result_player < M_PRE_AMBIGUOUS) {
438 /* Would have done something above if connected */
439 notify_conn(pconn->self, NULL, E_CHAT_ERROR, ftc_server,
440 _("%s is not connected."), player_name(pdest));
441 return;
442 }
443 }
444 /* Didn't match; check heuristics to see if this is likely
445 * to be a global message
446 */
447 cpblank = strchr(real_message, ' ');
448 if (!cpblank || (cp < cpblank)) {
449 if (double_colon) {
450 notify_conn(pconn->self, NULL, E_CHAT_ERROR, ftc_server,
451 _("There is no connection by the name %s."), name);
452 } else {
453 notify_conn(pconn->self, NULL, E_CHAT_ERROR, ftc_server,
454 _("There is no player nor connection by the name %s."),
455 name);
456 }
457 return;
458 }
459 }
460 /* global message: */
461 chat_msg_to_all(pconn, real_message);
462}
#define CHAT_DIRECT_PREFIX
Definition chat.h:31
#define CHAT_ALLIES_PREFIX
Definition chat.h:30
#define SERVER_COMMAND_PREFIX
Definition chat.h:28
struct connection * conn_by_user_prefix(const char *user_name, enum m_pre_result *result)
Definition connection.c:397
bool conn_is_global_observer(const struct connection *pconn)
Definition connection.c:750
bool conn_pattern_list_match(const struct conn_pattern_list *plist, const struct connection *pconn)
Definition connection.c:847
#define conn_list_iterate(connlist, pconn)
Definition connection.h:113
#define conn_list_iterate_end
Definition connection.h:115
void con_write(enum rfc_status rfc_status, const char *message,...)
Definition console.c:203
@ C_COMMENT
Definition console.h:37
#define MAX_LEN_NAME
Definition fc_types.h:66
#define _(String)
Definition fcintl.h:67
const struct ft_color ftc_chat_private
const struct ft_color ftc_server
const struct ft_color ftc_warning
const struct ft_color ftc_chat_public
const struct ft_color ftc_chat_ally
struct civ_game game
Definition game.c:57
static void static bool conn_is_ignored(const struct connection *sender, const struct connection *dest)
Definition handchat.c:52
static void complain_ambiguous(struct connection *pconn, const char *name, int player_conn)
Definition handchat.c:103
static void chat_msg_to_player(struct connection *sender, struct player *pdest, char *msg)
Definition handchat.c:156
static void form_chat_name(struct connection *pconn, char *buffer, size_t len)
Definition handchat.c:67
#define MAX_LEN_CHAT_NAME
Definition handchat.c:41
static void send_chat_msg(struct connection *pconn, const struct connection *sender, const struct ft_color color, const char *format,...) fc__attribute((__format__(__printf__
Definition handchat.c:83
static void chat_msg_to_all(struct connection *sender, char *msg)
Definition handchat.c:280
static void chat_msg_to_allies(struct connection *sender, char *msg)
Definition handchat.c:222
static void chat_msg_to_global_observers(struct connection *sender, char *msg)
Definition handchat.c:254
void handle_chat_msg_req(struct connection *pconn, const char *message)
Definition handchat.c:320
static void chat_msg_to_conn(struct connection *sender, struct connection *dest, char *msg)
Definition handchat.c:127
const char * name
Definition inputfile.c:127
#define log_error(message,...)
Definition log.h:103
void package_chat_msg(struct packet_chat_msg *packet, const struct connection *sender, const struct ft_color color, const char *format,...)
Definition notify.c:123
void event_cache_add_for_players(const struct packet_chat_msg *packet, struct event_cache_players *players)
Definition notify.c:671
struct event_cache_players * event_cache_player_add(struct event_cache_players *players, const struct player *pplayer)
Definition notify.c:703
void notify_conn(struct conn_list *dest, const struct tile *ptile, enum event_type event, const struct ft_color color, const char *format,...)
Definition notify.c:238
void event_cache_add_for_global_observers(const struct packet_chat_msg *packet)
Definition notify.c:630
void vpackage_chat_msg(struct packet_chat_msg *packet, const struct connection *sender, const struct ft_color color, const char *format, va_list vargs)
Definition notify.c:104
void event_cache_add_for_all(const struct packet_chat_msg *packet)
Definition notify.c:619
#define MAX_LEN_MSG
Definition packets.h:43
void lsend_packet_chat_msg(struct conn_list *dest, const struct packet_chat_msg *packet)
int send_packet_chat_msg(struct connection *pc, const struct packet_chat_msg *packet)
int len
Definition packhand.c:125
struct player * player_by_name_prefix(const char *name, enum m_pre_result *result)
Definition player.c:911
const char * player_name(const struct player *pplayer)
Definition player.c:886
bool pplayers_allied(const struct player *pplayer, const struct player *pplayer2)
Definition player.c:1381
#define players_iterate_end
Definition player.h:535
#define players_iterate(_pplayer)
Definition player.h:530
#define ANON_PLAYER_NAME
Definition player.h:43
char * skip_leading_spaces(char *s)
Definition shared.c:387
#define MIN(x, y)
Definition shared.h:55
m_pre_result
Definition shared.h:207
@ M_PRE_AMBIGUOUS
Definition shared.h:210
bool handle_stdin_input(struct connection *caller, char *str)
Definition stdinhand.c:4362
struct conn_list * est_connections
Definition game.h:97
Definition colors.h:20
struct player * playing
Definition connection.h:156
struct conn_list * self
Definition connection.h:168
bool observer
Definition connection.h:152
char username[MAX_LEN_NAME]
Definition connection.h:169
char message[MAX_LEN_MSG]
struct conn_list * connections
Definition player.h:298
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:969
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:787
#define sz_strlcpy(dest, src)
Definition support.h:167
#define fc__attribute(x)
Definition support.h:89
#define FALSE
Definition support.h:47