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)
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 ***********************************************************************/
15 #include <fc_config.h>
37 #include "stdinhand.h"
41 #define MAX_LEN_CHAT_NAME (2*MAX_LEN_NAME+10) /* for form_chat_name() names */
43 static 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)));
49 /****************************************************************************
50 Returns whether 'dest' is ignoring the 'sender' connection.
51 ****************************************************************************/
52 static inline bool conn_is_ignored(const struct connection
*sender
,
53 const struct connection
*dest
)
55 if (NULL
!= sender
&& NULL
!= dest
) {
56 return conn_pattern_list_match(dest
->server
.ignore_list
, sender
);
62 /**************************************************************************
63 Formulate a name for this connection, prefering the player name when
64 available and unambiguous (since this is the "standard" case), else
66 **************************************************************************/
67 static void form_chat_name(struct connection
*pconn
, char *buffer
, size_t len
)
69 struct player
*pplayer
= pconn
->playing
;
73 || strcmp(player_name(pplayer
), ANON_PLAYER_NAME
) == 0) {
74 fc_snprintf(buffer
, len
, "(%s)", pconn
->username
);
76 fc_snprintf(buffer
, len
, "%s", player_name(pplayer
));
80 /**************************************************************************
81 Send a chat message packet.
82 **************************************************************************/
83 static void send_chat_msg(struct connection
*pconn
,
84 const struct connection
*sender
,
85 const struct ft_color color
,
86 const char *format
, ...)
88 struct packet_chat_msg packet
;
91 va_start(args
, format
);
92 vpackage_chat_msg(&packet
, sender
, color
, format
, args
);
95 send_packet_chat_msg(pconn
, &packet
);
98 /**************************************************************************
99 Complain to sender that name was ambiguous.
100 'player_conn' is 0 for player names, 1 for connection names,
101 2 for attempt to send to an anonymous player.
102 **************************************************************************/
103 static void complain_ambiguous(struct connection
*pconn
, const char *name
,
106 switch(player_conn
) {
108 notify_conn(pconn
->self
, NULL
, E_CHAT_ERROR
, ftc_server
,
109 _("%s is an ambiguous player name-prefix."), name
);
112 notify_conn(pconn
->self
, NULL
, E_CHAT_ERROR
, ftc_server
,
113 _("%s is an ambiguous connection name-prefix."), name
);
116 notify_conn(pconn
->self
, NULL
, E_CHAT_ERROR
, ftc_server
,
117 _("%s is an anonymous name. Use connection name."), name
);
120 log_error("Unknown variant in %s(): %d.", __FUNCTION__
, player_conn
);
124 /**************************************************************************
125 Send private message to single connection.
126 **************************************************************************/
127 static void chat_msg_to_conn(struct connection
*sender
,
128 struct connection
*dest
, char *msg
)
130 char sender_name
[MAX_LEN_CHAT_NAME
], dest_name
[MAX_LEN_CHAT_NAME
];
132 form_chat_name(dest
, dest_name
, sizeof(dest_name
));
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."),
141 msg
= skip_leading_spaces(msg
);
142 form_chat_name(sender
, sender_name
, sizeof(sender_name
));
144 send_chat_msg(sender
, sender
, ftc_chat_private
,
145 "->*%s* %s", dest_name
, msg
);
147 if (sender
!= dest
) {
148 send_chat_msg(dest
, sender
, ftc_chat_private
,
149 "*%s* %s", sender_name
, msg
);
153 /**************************************************************************
154 Send private message to multi-connected player.
155 **************************************************************************/
156 static void chat_msg_to_player(struct connection
*sender
,
157 struct player
*pdest
, char *msg
)
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
);
164 msg
= skip_leading_spaces(msg
);
165 form_chat_name(sender
, sender_name
, sizeof(sender_name
));
167 /* Find the user of the player 'pdest'. */
168 conn_list_iterate(pdest
->connections
, pconn
) {
169 if (!pconn
->observer
) {
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."),
175 return; /* NB: stop here, don't send to observers. */
180 } conn_list_iterate_end
;
182 /* Repeat the message for the sender. */
183 send_chat_msg(sender
, sender
, ftc_chat_private
,
184 "->{%s} %s", player_name(pdest
), msg
);
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
);
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
) {
198 && !conn_is_ignored(sender
, pconn
)) {
199 send_packet_chat_msg(pconn
, &packet
);
201 } conn_list_iterate_end
;
202 if (NULL
!= sender
->playing
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
);
210 } conn_list_iterate_end
;
212 /* Add player to event cache. */
213 players
= event_cache_player_add(players
, sender
->playing
);
216 event_cache_add_for_players(&packet
, players
);
219 /**************************************************************************
220 Send private message to player allies.
221 **************************************************************************/
222 static void chat_msg_to_allies(struct connection
*sender
, char *msg
)
224 struct packet_chat_msg packet
;
225 struct event_cache_players
*players
= NULL
;
226 char sender_name
[MAX_LEN_CHAT_NAME
];
228 msg
= skip_leading_spaces(msg
);
229 form_chat_name(sender
, sender_name
, sizeof(sender_name
));
231 package_chat_msg(&packet
, sender
, ftc_chat_ally
,
232 _("%s to allies: %s"), sender_name
, msg
);
234 players_iterate(aplayer
) {
235 if (!pplayers_allied(sender
->playing
, aplayer
)) {
239 conn_list_iterate(aplayer
->connections
, pconn
) {
240 if (!conn_is_ignored(sender
, pconn
)) {
241 send_packet_chat_msg(pconn
, &packet
);
243 } conn_list_iterate_end
;
244 players
= event_cache_player_add(players
, aplayer
);
245 } players_iterate_end
;
247 /* Add to the event cache. */
248 event_cache_add_for_players(&packet
, players
);
251 /**************************************************************************
252 Send private message to all global observers.
253 **************************************************************************/
254 static void chat_msg_to_global_observers(struct connection
*sender
,
257 struct packet_chat_msg packet
;
258 char sender_name
[MAX_LEN_CHAT_NAME
];
260 msg
= skip_leading_spaces(msg
);
261 form_chat_name(sender
, sender_name
, sizeof(sender_name
));
263 package_chat_msg(&packet
, sender
, ftc_chat_ally
,
264 _("%s to global observers: %s"), sender_name
, msg
);
266 conn_list_iterate(game
.est_connections
, dest_conn
) {
267 if (conn_is_global_observer(dest_conn
)
268 && !conn_is_ignored(sender
, dest_conn
)) {
269 send_packet_chat_msg(dest_conn
, &packet
);
271 } conn_list_iterate_end
;
273 /* Add to the event cache. */
274 event_cache_add_for_global_observers(&packet
);
277 /**************************************************************************
278 Send private message to all connections.
279 **************************************************************************/
280 static void chat_msg_to_all(struct connection
*sender
, char *msg
)
282 struct packet_chat_msg packet
;
283 char sender_name
[MAX_LEN_CHAT_NAME
];
285 msg
= skip_leading_spaces(msg
);
286 form_chat_name(sender
, sender_name
, sizeof(sender_name
));
288 package_chat_msg(&packet
, sender
, ftc_chat_public
,
289 "<%s> %s", sender_name
, msg
);
290 con_write(C_COMMENT
, "%s", packet
.message
);
291 lsend_packet_chat_msg(game
.est_connections
, &packet
);
293 /* Add to the event cache. */
294 event_cache_add_for_all(&packet
);
297 /**************************************************************************
298 Handle a chat message packet from client:
299 1. Work out whether it is a server command and if so run it;
300 2. Otherwise work out whether it is directed to a single player, or
301 to a single connection, and send there. (For a player, send to
302 all clients connected as that player, in multi-connect case);
303 3. Or it may be intended for all allied players.
304 4. Else send to all connections (game.est_connections).
306 In case 2, there can sometimes be ambiguity between player and
307 connection names. By default this tries to match player name first,
308 and only if that fails tries to match connection name. User can
309 override this and specify connection only by using two colons ("::")
310 after the destination name/prefix, instead of one.
312 The message sent will name the sender, and via format differences
313 also indicates whether the recipient is either all connections, a
314 single connection, or multiple connections to a single player.
316 Message is also echoed back to sender (with different format),
317 avoiding sending both original and echo if sender is in destination
319 **************************************************************************/
320 void handle_chat_msg_req(struct connection
*pconn
, const char *message
)
322 char real_message
[MAX_LEN_MSG
], *cp
;
325 sz_strlcpy(real_message
, message
);
327 /* This loop to prevent players from sending multiple lines which can
329 for (cp
= real_message
; *cp
!= '\0'; cp
++) {
330 if (*cp
== '\n' || *cp
== '\r') {
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
342 if (real_message
[0] == SERVER_COMMAND_PREFIX
) {
343 /* pass it to the command parser, which will chop the prefix off */
344 (void) handle_stdin_input(pconn
, real_message
);
348 /* Send to allies command */
349 if (real_message
[0] == CHAT_ALLIES_PREFIX
) {
350 /* this won't work if we aren't attached to a player */
351 if (NULL
== pconn
->playing
&& !pconn
->observer
) {
352 notify_conn(pconn
->self
, NULL
, E_CHAT_ERROR
, ftc_server
,
353 _("You are not attached to a player."));
357 if (NULL
!= pconn
->playing
) {
358 chat_msg_to_allies(pconn
, real_message
+ 1);
360 chat_msg_to_global_observers(pconn
, real_message
+ 1);
365 /* Want to allow private messages with "player_name: message",
366 (or "connection_name: message"), including unambiguously
367 abbreviated player/connection name, but also want to allow
368 sensible use of ':' within messages, and _also_ want to
369 notice intended private messages with (eg) mis-spelt name.
373 If there is no ':', or ':' is first on line,
374 message is global (send to all players)
375 else if the ':' is double, try matching part before "::" against
376 connection names: for single match send to that connection,
377 for multiple matches complain, else goto heuristics below.
378 else try matching part before (single) ':' against player names:
379 for single match send to that player, for multiple matches
381 else try matching against connection names: for single match send
382 to that connection, for multiple matches complain
383 else if some heuristics apply (a space anywhere before first ':')
384 then treat as global message,
385 else complain (might be a typo-ed intended private message)
388 cp
= strchr(real_message
, CHAT_DIRECT_PREFIX
);
390 if (cp
&& (cp
!= &real_message
[0])) {
391 enum m_pre_result match_result_player
, match_result_conn
;
392 struct player
*pdest
= NULL
;
393 struct connection
*conn_dest
= NULL
;
394 char name
[MAX_LEN_NAME
];
397 (void) fc_strlcpy(name
, real_message
, MIN(sizeof(name
),
398 cp
- real_message
+ 1));
400 double_colon
= (*(cp
+1) == CHAT_DIRECT_PREFIX
);
402 conn_dest
= conn_by_user_prefix(name
, &match_result_conn
);
403 if (match_result_conn
== M_PRE_AMBIGUOUS
) {
404 complain_ambiguous(pconn
, name
, 1);
407 if (conn_dest
&& match_result_conn
< M_PRE_AMBIGUOUS
) {
408 chat_msg_to_conn(pconn
, conn_dest
, cp
+2);
413 pdest
= player_by_name_prefix(name
, &match_result_player
);
414 if (match_result_player
== M_PRE_AMBIGUOUS
) {
415 complain_ambiguous(pconn
, name
, 0);
418 if (pdest
&& strcmp(player_name(pdest
), ANON_PLAYER_NAME
) == 0) {
419 complain_ambiguous(pconn
, name
, 2);
422 if (pdest
&& match_result_player
< M_PRE_AMBIGUOUS
) {
423 chat_msg_to_player(pconn
, pdest
, cp
+ 1);
425 /* else try for connection name match before complaining */
427 conn_dest
= conn_by_user_prefix(name
, &match_result_conn
);
428 if (match_result_conn
== M_PRE_AMBIGUOUS
) {
429 complain_ambiguous(pconn
, name
, 1);
432 if (conn_dest
&& match_result_conn
< M_PRE_AMBIGUOUS
) {
433 chat_msg_to_conn(pconn
, conn_dest
, cp
+1);
436 if (pdest
&& match_result_player
< M_PRE_AMBIGUOUS
) {
437 /* Would have done something above if connected */
438 notify_conn(pconn
->self
, NULL
, E_CHAT_ERROR
, ftc_server
,
439 _("%s is not connected."), player_name(pdest
));
443 /* Didn't match; check heuristics to see if this is likely
444 * to be a global message
446 cpblank
= strchr(real_message
, ' ');
447 if (!cpblank
|| (cp
< cpblank
)) {
449 notify_conn(pconn
->self
, NULL
, E_CHAT_ERROR
, ftc_server
,
450 _("There is no connection by the name %s."), name
);
452 notify_conn(pconn
->self
, NULL
, E_CHAT_ERROR
, ftc_server
,
453 _("There is no player nor connection by the name %s."),
459 /* global message: */
460 chat_msg_to_all(pconn
, real_message
);