mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-07-04 23:13:14 +02:00
Add draft/multiline support with a default max-lines of 15 for known-users
and 7 for unknown-users (with max-bytes 5250 and 1500 respectively). This
allows pasting a short snippet of code, config file, text from a site, etc.
With multiline you have the guarantee that:
1) You will see the entire text with no delay between lines
2) You won't see another persons chat half-way through such a paste
3) For multiline supporting clients it is now clear that all the text
belongs to each other, which can make selecting/copying it easier.
This basically means short snippets/pastes like that can be completely on
IRC again. No need for a pastebin for it. Though, you may still need such
a service if you are pasting more lines.
Regarding the implementation in UnrealIRCd:
* Clients without multiline get individual fallback lines (concat lines
merged, blank lines skipped, as per spec). And we know that clients like
weechat - which does support multiline - also shows all lines and not
only a few plus snippet style "[.."]. That is another reason for only
allowing 15 lines by default and not something much more. Otherwise all
those clients would get a big wall of text, which just sucks.
* Spamfilter (also) runs on the full text of all lines together, so
splitting a phrase across lines does not evade spamfilter.
* Fakelag: a client can send the BATCH start+PRIVMSG (or NOTICE)+BATCH end
at full speed. We impose no fake lag there. Also, the multiline default
max-lines and max-bytes are lower than the example class::recvq of 8000,
so should be perfectly safe. If the entire BATCH is accepted then we
will impose fake-lag afterwards, with a cap of 15 seconds maximum.
If the BATCH is rejected, we impose half the fakelag plus 2sec.
* If the time between BATCH start and BATCH end is more than 15 seconds
then the BATCH is rejected (set::multiline::batch-timeout).
* The BATCH is atomic (either you see it all, or you see none of it):
* When the client sends it to server, it is buffered first.
* Only after the batch close the server indicates if it is accepted
or rejected. This has various reasons, two of them are: 1) The client
is going to send everything in one go anyway and not wait for a
response between each PRIVMSG, and 2) we can't do many checks in the
buffering stage and skip those after, that would cause a TOCTOU
problem (eg. a banned user still being able to speak).
* If any line gets rejected due to spamfilter or other case
(eg +c, +b ~text with block, etc etc), the entire batch is rejected
* Locally we deliver all or nothing (as said)
* S2S we buffer the batch as well, so if a server splits after having
received 10 lines out of 15, then clients will not see anything.
* We send max-lines and max-bytes, this is the hard upper limit.
* A multiline can still be limited more tight if:
* +f with 't' or 'm' restricts to fewer lines,
eg +f [5t]:15, which means max 5 lines per 15 seconds,
means the max accepted multiline is 5 for that channel.
* +F works the same, except that default +F normal does not
have a 't' at the moment and 'm' is very high (50) so
practically not limited by default.
* There will be a future +f flood subtype for some more control
TODO: we will send CAP NEW on unknown-users <-> known-users to
indicate the new max-lines value if you transition security groups
TODO: chat history does not yet include multiline batches.
This commit is contained in:
@@ -791,6 +791,7 @@ extern MODVAR int dontspread;
|
||||
extern MODVAR int labeled_response_inhibit;
|
||||
extern MODVAR int labeled_response_inhibit_end;
|
||||
extern MODVAR int labeled_response_force;
|
||||
extern MODVAR int echo_message_inhibit;
|
||||
|
||||
/* Efuncs */
|
||||
extern MODVAR void (*do_join)(Client *, int, const char **);
|
||||
@@ -880,6 +881,7 @@ extern MODVAR int (*is_services_but_not_ulined)(Client *client);
|
||||
extern MODVAR void (*parse_message_tags)(Client *cptr, char **str, MessageTag **mtag_list);
|
||||
extern MODVAR const char *(*mtags_to_string)(MessageTag *m, Client *acptr);
|
||||
extern MODVAR int (*can_send_to_channel)(Client *cptr, Channel *channel, const char **msgtext, const char **errmsg, SendType sendtyp, ClientContext *clictx);
|
||||
extern MODVAR int (*can_send_to_user)(Client *client, Client *target, const char **msgtext, const char **errmsg, SendType sendtype, ClientContext *clictx, int flags);
|
||||
extern MODVAR void (*broadcast_md_globalvar)(ModDataInfo *mdi, ModData *md);
|
||||
extern MODVAR void (*broadcast_md_globalvar_cmd)(Client *except, Client *sender, const char *varname, const char *value);
|
||||
extern MODVAR int (*tkl_ip_hash)(const char *ip);
|
||||
@@ -948,6 +950,7 @@ extern MODVAR int (*utf8_get_block_number)(const char *name);
|
||||
extern MODVAR void (*send_isupport)(Client *client);
|
||||
extern MODVAR void (*isupport_check_for_changes)(void);
|
||||
extern MODVAR int (*get_connections_from_ip)(Client *client);
|
||||
extern MODVAR int (*get_floodprot_channel_max_lines)(Channel *channel);
|
||||
/* /Efuncs */
|
||||
|
||||
/* TLS functions */
|
||||
@@ -987,6 +990,7 @@ extern int add_silence_default_handler(Client *client, const char *mask, int sen
|
||||
extern int del_silence_default_handler(Client *client, const char *mask);
|
||||
extern int is_silenced_default_handler(Client *client, Client *acptr);
|
||||
extern int get_connections_from_ip_default_handler(Client *client);
|
||||
extern int get_floodprot_channel_max_lines_default_handler(Channel *channel);
|
||||
extern void do_unreal_log_remote_deliver_default_handler(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized);
|
||||
extern int make_oper_default_handler(Client *client, const char *operblock_name, const char *operclass, ConfigItem_class *clientclass, long modes, const char *snomask, const char *vhost, const char *autojoin_channels);
|
||||
extern void webserver_send_response_default_handler(Client *client, int status, char *msg);
|
||||
@@ -1532,6 +1536,7 @@ extern Tag *add_tag(Client *client, const char *name, int value);
|
||||
extern void free_all_tags(Client *client);
|
||||
extern void del_tag(Client *client, const char *name);
|
||||
extern void bump_tag_serial(Client *client);
|
||||
extern int valid_batch_reference_tag(const char *ref);
|
||||
extern int valid_spamfilter_id(const char *s);
|
||||
extern void download_complete_dontcare(OutgoingWebRequest *request, OutgoingWebResponse *response);
|
||||
extern char *urlencode(const char *s, char *wbuf, int wlen);
|
||||
|
||||
@@ -534,6 +534,11 @@ typedef struct {
|
||||
#define MTAG_HANDLER_FLAGS_NONE 0x0
|
||||
/** This message-tag does not have a CAP REQ xx (eg: for "msgid") */
|
||||
#define MTAG_HANDLER_FLAGS_NO_CAP_NEEDED 0x1
|
||||
/** This tag should only appear on the first message of the
|
||||
* multiline fallback (for clients that don't support multiline).
|
||||
* Used by "msgid" and "+draft/reply".
|
||||
*/
|
||||
#define MTAG_HANDLER_FLAGS_FIRST_ONLY 0x2
|
||||
|
||||
/** Message Tag Handler */
|
||||
struct MessageTagHandler {
|
||||
@@ -2800,6 +2805,7 @@ enum EfunctionType {
|
||||
EFUNC_SEND_ISUPPORT,
|
||||
EFUNC_ISUPPORT_CHECK_FOR_CHANGES,
|
||||
EFUNC_GET_CONNECTIONS_FROM_IP,
|
||||
EFUNC_GET_FLOODPROT_CHANNEL_MAX_LINES,
|
||||
};
|
||||
|
||||
/* Module flags */
|
||||
|
||||
@@ -1457,7 +1457,10 @@ typedef enum FloodOption {
|
||||
FLD_CONVERSATIONS = 5, /**< max-concurrent-conversations */
|
||||
FLD_LAG_PENALTY = 6, /**< lag-penalty / lag-penalty-bytes */
|
||||
FLD_VHOST = 7, /**< vhost-flood */
|
||||
FLD_MULTILINE = 8, /**< multiline max-lines / max-bytes */
|
||||
} FloodOption;
|
||||
#define MULTILINE_MAX_CONFIGURABLE_LINES 200 /**< Maximum configurable max-lines for multiline */
|
||||
#define MULTILINE_MAX_CONFIGURABLE_BYTES 131072 /**< Maximum configurable max-bytes for multiline (128KB) */
|
||||
#define MAXFLOODOPTIONS 10
|
||||
|
||||
typedef struct TrafficStats TrafficStats;
|
||||
@@ -2662,6 +2665,9 @@ struct ConfigItem_badword {
|
||||
#define SKIP_CTCP 0x8
|
||||
#define CHECK_INVISIBLE 0x10
|
||||
|
||||
/* Flags for 'flags' in 'can_send_to_user' (and future: can_send_to_channel) */
|
||||
#define CAN_SEND_SKIP_SPAMFILTER 0x1
|
||||
|
||||
typedef struct GeoIPResult GeoIPResult;
|
||||
struct GeoIPResult {
|
||||
char *country_code;
|
||||
|
||||
Reference in New Issue
Block a user