Implement custom rate limits
authorShane Synan <digitalcircuit36939@gmail.com>
Mon, 5 Sep 2016 21:26:44 +0000 (16:26 -0500)
committerManuel Nickschas <sputnick@quassel-irc.org>
Tue, 13 Sep 2016 22:07:58 +0000 (00:07 +0200)
Add custom rate limits flag and settings.
> In database, add 'usecustommessagerate', 'messagerateburstsize',
'messageratedelay', and 'unlimitedmessagerate' to 'network' table,
defaulting to the values already used.  This modifies schema,
requiring a version bump, changes to migration scripts, etc.
Use 4 upgrade scripts as SQLite does not support adding multiple
columns at once.
> In protocol, add 'useCustomMessageRate', 'msgRateBurstSize',
'msgRateMessageDelay', and 'unlimitedMessageRate' to Network,
configuring custom rate limits.

Add 'CustomRateLimits' feature flag so the client can tell users
about needing to upgrade their core to get this feature.

If rate limits set to unlimited, disable the token bucket timer and
send messages immediately.  Otherwise, modify the existing token
bucket variables according to the new limits.  The defaults are
unchanged.

Add "Use Custom Rate Limits" checkbox to network "Connection" tab
with an explanatory tooltip warning against setting too low of rate
limits.  Tooltip is modified and checkbox disabled if the core does
not support the feature.

Testing:
> Database schema handling
SQLite new install - works
SQLite upgrade - works
SQLite migrate to Postgres - works
Postgres new install - works
Postgres upgrade - works

> Client/core connections
New client, new core - works
New client, old core - works, custom rates grayed out
Old client, new core - works, custom rates reset + disabled on edit
(Unfortunately there's no way to use a boolean and tell if the client
 is old, or actually disabling custom rate limits.  Would need the
 work-in-progress ClientFeature system 'esainane' designed)

192 files changed:
src/common/network.cpp
src/common/network.h
src/common/quassel.h
src/core/SQL/PostgreSQL/18/insert_network.sql [deleted file]
src/core/SQL/PostgreSQL/18/migrate_write_network.sql [deleted file]
src/core/SQL/PostgreSQL/19/delete_backlog_by_uid.sql [moved from src/core/SQL/PostgreSQL/18/delete_backlog_by_uid.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_backlog_for_buffer.sql [moved from src/core/SQL/PostgreSQL/18/delete_backlog_for_buffer.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_backlog_for_network.sql [moved from src/core/SQL/PostgreSQL/18/delete_backlog_for_network.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_buffer_for_bufferid.sql [moved from src/core/SQL/PostgreSQL/18/delete_buffer_for_bufferid.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_buffers_by_uid.sql [moved from src/core/SQL/PostgreSQL/18/delete_buffers_by_uid.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_buffers_for_network.sql [moved from src/core/SQL/PostgreSQL/18/delete_buffers_for_network.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_identity.sql [moved from src/core/SQL/PostgreSQL/18/delete_identity.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_ircservers_for_network.sql [moved from src/core/SQL/PostgreSQL/18/delete_ircservers_for_network.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_network.sql [moved from src/core/SQL/PostgreSQL/18/delete_network.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_networks_by_uid.sql [moved from src/core/SQL/PostgreSQL/18/delete_networks_by_uid.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_nicks.sql [moved from src/core/SQL/PostgreSQL/18/delete_nicks.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/delete_quasseluser.sql [moved from src/core/SQL/PostgreSQL/18/delete_quasseluser.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/insert_buffer.sql [moved from src/core/SQL/PostgreSQL/18/insert_buffer.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/insert_identity.sql [moved from src/core/SQL/PostgreSQL/18/insert_identity.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/insert_message.sql [moved from src/core/SQL/PostgreSQL/18/insert_message.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/insert_network.sql [new file with mode: 0644]
src/core/SQL/PostgreSQL/19/insert_nick.sql [moved from src/core/SQL/PostgreSQL/18/insert_nick.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/insert_quasseluser.sql [moved from src/core/SQL/PostgreSQL/18/insert_quasseluser.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/insert_sender.sql [moved from src/core/SQL/PostgreSQL/18/insert_sender.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/insert_server.sql [moved from src/core/SQL/PostgreSQL/18/insert_server.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/insert_user_setting.sql [moved from src/core/SQL/PostgreSQL/18/insert_user_setting.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/migrate_write_backlog.sql [moved from src/core/SQL/PostgreSQL/18/migrate_write_backlog.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/migrate_write_buffer.sql [moved from src/core/SQL/PostgreSQL/18/migrate_write_buffer.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/migrate_write_identity.sql [moved from src/core/SQL/PostgreSQL/18/migrate_write_identity.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/migrate_write_identity_nick.sql [moved from src/core/SQL/PostgreSQL/18/migrate_write_identity_nick.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/migrate_write_ircserver.sql [moved from src/core/SQL/PostgreSQL/18/migrate_write_ircserver.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/migrate_write_network.sql [new file with mode: 0644]
src/core/SQL/PostgreSQL/19/migrate_write_quasseluser.sql [moved from src/core/SQL/PostgreSQL/18/migrate_write_quasseluser.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/migrate_write_sender.sql [moved from src/core/SQL/PostgreSQL/18/migrate_write_sender.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/migrate_write_usersetting.sql [moved from src/core/SQL/PostgreSQL/18/migrate_write_usersetting.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_authuser.sql [moved from src/core/SQL/PostgreSQL/18/select_authuser.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_bufferByName.sql [moved from src/core/SQL/PostgreSQL/18/select_bufferByName.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_bufferExists.sql [moved from src/core/SQL/PostgreSQL/18/select_bufferExists.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_buffer_by_id.sql [moved from src/core/SQL/PostgreSQL/18/select_buffer_by_id.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_buffer_lastseen_messages.sql [moved from src/core/SQL/PostgreSQL/18/select_buffer_lastseen_messages.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_buffer_markerlinemsgids.sql [moved from src/core/SQL/PostgreSQL/18/select_buffer_markerlinemsgids.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_buffers.sql [moved from src/core/SQL/PostgreSQL/18/select_buffers.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_buffers_for_network.sql [moved from src/core/SQL/PostgreSQL/18/select_buffers_for_network.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_checkidentity.sql [moved from src/core/SQL/PostgreSQL/18/select_checkidentity.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_connected_networks.sql [moved from src/core/SQL/PostgreSQL/18/select_connected_networks.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_identities.sql [moved from src/core/SQL/PostgreSQL/18/select_identities.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_internaluser.sql [moved from src/core/SQL/PostgreSQL/18/select_internaluser.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_messages.sql [moved from src/core/SQL/PostgreSQL/18/select_messages.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_messagesAll.sql [moved from src/core/SQL/PostgreSQL/18/select_messagesAll.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_messagesAllNew.sql [moved from src/core/SQL/PostgreSQL/18/select_messagesAllNew.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_messagesNewerThan.sql [moved from src/core/SQL/PostgreSQL/18/select_messagesNewerThan.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_messagesRange.sql [moved from src/core/SQL/PostgreSQL/18/select_messagesRange.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_networkExists.sql [moved from src/core/SQL/PostgreSQL/18/select_networkExists.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_network_awaymsg.sql [moved from src/core/SQL/PostgreSQL/18/select_network_awaymsg.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_network_usermode.sql [moved from src/core/SQL/PostgreSQL/18/select_network_usermode.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_networks_for_user.sql [moved from src/core/SQL/SQLite/19/select_networks_for_user.sql with 60% similarity]
src/core/SQL/PostgreSQL/19/select_nicks.sql [moved from src/core/SQL/PostgreSQL/18/select_nicks.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_persistent_channels.sql [moved from src/core/SQL/PostgreSQL/18/select_persistent_channels.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_senderid.sql [moved from src/core/SQL/PostgreSQL/18/select_senderid.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_servers_for_network.sql [moved from src/core/SQL/PostgreSQL/18/select_servers_for_network.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_user_setting.sql [moved from src/core/SQL/PostgreSQL/18/select_user_setting.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/select_userid.sql [moved from src/core/SQL/PostgreSQL/18/select_userid.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_000_quasseluser.sql [moved from src/core/SQL/PostgreSQL/18/setup_000_quasseluser.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_010_sender.sql [moved from src/core/SQL/PostgreSQL/18/setup_010_sender.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_020_identity.sql [moved from src/core/SQL/PostgreSQL/18/setup_020_identity.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_030_identity_nick.sql [moved from src/core/SQL/PostgreSQL/18/setup_030_identity_nick.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_040_network.sql [moved from src/core/SQL/PostgreSQL/18/setup_040_network.sql with 79% similarity]
src/core/SQL/PostgreSQL/19/setup_050_buffer.sql [moved from src/core/SQL/PostgreSQL/18/setup_050_buffer.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_060_backlog.sql [moved from src/core/SQL/PostgreSQL/18/setup_060_backlog.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_070_coreinfo.sql [moved from src/core/SQL/PostgreSQL/18/setup_070_coreinfo.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_080_ircservers.sql [moved from src/core/SQL/PostgreSQL/18/setup_080_ircservers.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_090_backlog_idx.sql [moved from src/core/SQL/PostgreSQL/18/setup_090_backlog_idx.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_100_user_setting.sql [moved from src/core/SQL/PostgreSQL/18/setup_100_user_setting.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_110_alter_sender_seq.sql [moved from src/core/SQL/PostgreSQL/18/setup_110_alter_sender_seq.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/setup_120_alter_messageid_seq.sql [moved from src/core/SQL/PostgreSQL/18/setup_120_alter_messageid_seq.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_backlog_bufferid.sql [moved from src/core/SQL/PostgreSQL/18/update_backlog_bufferid.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_buffer_lastseen.sql [moved from src/core/SQL/PostgreSQL/18/update_buffer_lastseen.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_buffer_markerlinemsgid.sql [moved from src/core/SQL/PostgreSQL/18/update_buffer_markerlinemsgid.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_buffer_name.sql [moved from src/core/SQL/PostgreSQL/18/update_buffer_name.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_buffer_persistent_channel.sql [moved from src/core/SQL/PostgreSQL/18/update_buffer_persistent_channel.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_buffer_set_channel_key.sql [moved from src/core/SQL/PostgreSQL/18/update_buffer_set_channel_key.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_identity.sql [moved from src/core/SQL/PostgreSQL/18/update_identity.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_network.sql [moved from src/core/SQL/PostgreSQL/18/update_network.sql with 79% similarity]
src/core/SQL/PostgreSQL/19/update_network_connected.sql [moved from src/core/SQL/PostgreSQL/18/update_network_connected.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_network_set_awaymsg.sql [moved from src/core/SQL/PostgreSQL/18/update_network_set_awaymsg.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_network_set_usermode.sql [moved from src/core/SQL/PostgreSQL/18/update_network_set_usermode.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_user_setting.sql [moved from src/core/SQL/PostgreSQL/18/update_user_setting.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_username.sql [moved from src/core/SQL/PostgreSQL/18/update_username.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/update_userpassword.sql [moved from src/core/SQL/PostgreSQL/18/update_userpassword.sql with 100% similarity]
src/core/SQL/PostgreSQL/19/upgrade_000_alter_network_add_ratelimit_usecustom.sql [new file with mode: 0644]
src/core/SQL/PostgreSQL/19/upgrade_001_alter_network_add_ratelimit_burstsize.sql [new file with mode: 0644]
src/core/SQL/PostgreSQL/19/upgrade_002_alter_network_add_ratelimit_delay.sql [new file with mode: 0644]
src/core/SQL/PostgreSQL/19/upgrade_003_alter_network_add_ratelimit_unlimited.sql [new file with mode: 0644]
src/core/SQL/SQLite/19/insert_network.sql [deleted file]
src/core/SQL/SQLite/20/delete_backlog_by_uid.sql [moved from src/core/SQL/SQLite/19/delete_backlog_by_uid.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_backlog_for_buffer.sql [moved from src/core/SQL/SQLite/19/delete_backlog_for_buffer.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_backlog_for_network.sql [moved from src/core/SQL/SQLite/19/delete_backlog_for_network.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_buffer_for_bufferid.sql [moved from src/core/SQL/SQLite/19/delete_buffer_for_bufferid.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_buffers_by_uid.sql [moved from src/core/SQL/SQLite/19/delete_buffers_by_uid.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_buffers_for_network.sql [moved from src/core/SQL/SQLite/19/delete_buffers_for_network.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_identity.sql [moved from src/core/SQL/SQLite/19/delete_identity.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_ircservers_for_network.sql [moved from src/core/SQL/SQLite/19/delete_ircservers_for_network.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_network.sql [moved from src/core/SQL/SQLite/19/delete_network.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_networks_by_uid.sql [moved from src/core/SQL/SQLite/19/delete_networks_by_uid.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_nicks.sql [moved from src/core/SQL/SQLite/19/delete_nicks.sql with 100% similarity]
src/core/SQL/SQLite/20/delete_quasseluser.sql [moved from src/core/SQL/SQLite/19/delete_quasseluser.sql with 100% similarity]
src/core/SQL/SQLite/20/insert_buffer.sql [moved from src/core/SQL/SQLite/19/insert_buffer.sql with 100% similarity]
src/core/SQL/SQLite/20/insert_identity.sql [moved from src/core/SQL/SQLite/19/insert_identity.sql with 100% similarity]
src/core/SQL/SQLite/20/insert_message.sql [moved from src/core/SQL/SQLite/19/insert_message.sql with 100% similarity]
src/core/SQL/SQLite/20/insert_network.sql [new file with mode: 0644]
src/core/SQL/SQLite/20/insert_nick.sql [moved from src/core/SQL/SQLite/19/insert_nick.sql with 100% similarity]
src/core/SQL/SQLite/20/insert_quasseluser.sql [moved from src/core/SQL/SQLite/19/insert_quasseluser.sql with 100% similarity]
src/core/SQL/SQLite/20/insert_sender.sql [moved from src/core/SQL/SQLite/19/insert_sender.sql with 100% similarity]
src/core/SQL/SQLite/20/insert_server.sql [moved from src/core/SQL/SQLite/19/insert_server.sql with 100% similarity]
src/core/SQL/SQLite/20/insert_user_setting.sql [moved from src/core/SQL/SQLite/19/insert_user_setting.sql with 100% similarity]
src/core/SQL/SQLite/20/migrate_read_backlog.sql [moved from src/core/SQL/SQLite/19/migrate_read_backlog.sql with 100% similarity]
src/core/SQL/SQLite/20/migrate_read_buffer.sql [moved from src/core/SQL/SQLite/19/migrate_read_buffer.sql with 100% similarity]
src/core/SQL/SQLite/20/migrate_read_identity.sql [moved from src/core/SQL/SQLite/19/migrate_read_identity.sql with 100% similarity]
src/core/SQL/SQLite/20/migrate_read_identity_nick.sql [moved from src/core/SQL/SQLite/19/migrate_read_identity_nick.sql with 100% similarity]
src/core/SQL/SQLite/20/migrate_read_ircserver.sql [moved from src/core/SQL/SQLite/19/migrate_read_ircserver.sql with 100% similarity]
src/core/SQL/SQLite/20/migrate_read_network.sql [moved from src/core/SQL/SQLite/19/migrate_read_network.sql with 74% similarity]
src/core/SQL/SQLite/20/migrate_read_quasseluser.sql [moved from src/core/SQL/SQLite/19/migrate_read_quasseluser.sql with 100% similarity]
src/core/SQL/SQLite/20/migrate_read_sender.sql [moved from src/core/SQL/SQLite/19/migrate_read_sender.sql with 100% similarity]
src/core/SQL/SQLite/20/migrate_read_usersetting.sql [moved from src/core/SQL/SQLite/19/migrate_read_usersetting.sql with 100% similarity]
src/core/SQL/SQLite/20/select_authuser.sql [moved from src/core/SQL/SQLite/19/select_authuser.sql with 100% similarity]
src/core/SQL/SQLite/20/select_bufferByName.sql [moved from src/core/SQL/SQLite/19/select_bufferByName.sql with 100% similarity]
src/core/SQL/SQLite/20/select_bufferExists.sql [moved from src/core/SQL/SQLite/19/select_bufferExists.sql with 100% similarity]
src/core/SQL/SQLite/20/select_buffer_by_id.sql [moved from src/core/SQL/SQLite/19/select_buffer_by_id.sql with 100% similarity]
src/core/SQL/SQLite/20/select_buffer_lastseen_messages.sql [moved from src/core/SQL/SQLite/19/select_buffer_lastseen_messages.sql with 100% similarity]
src/core/SQL/SQLite/20/select_buffer_markerlinemsgids.sql [moved from src/core/SQL/SQLite/19/select_buffer_markerlinemsgids.sql with 100% similarity]
src/core/SQL/SQLite/20/select_buffers.sql [moved from src/core/SQL/SQLite/19/select_buffers.sql with 100% similarity]
src/core/SQL/SQLite/20/select_buffers_for_merge.sql [moved from src/core/SQL/SQLite/19/select_buffers_for_merge.sql with 100% similarity]
src/core/SQL/SQLite/20/select_buffers_for_network.sql [moved from src/core/SQL/SQLite/19/select_buffers_for_network.sql with 100% similarity]
src/core/SQL/SQLite/20/select_checkidentity.sql [moved from src/core/SQL/SQLite/19/select_checkidentity.sql with 100% similarity]
src/core/SQL/SQLite/20/select_connected_networks.sql [moved from src/core/SQL/SQLite/19/select_connected_networks.sql with 100% similarity]
src/core/SQL/SQLite/20/select_identities.sql [moved from src/core/SQL/SQLite/19/select_identities.sql with 100% similarity]
src/core/SQL/SQLite/20/select_internaluser.sql [moved from src/core/SQL/SQLite/19/select_internaluser.sql with 100% similarity]
src/core/SQL/SQLite/20/select_messages.sql [moved from src/core/SQL/SQLite/19/select_messages.sql with 100% similarity]
src/core/SQL/SQLite/20/select_messagesAll.sql [moved from src/core/SQL/SQLite/19/select_messagesAll.sql with 100% similarity]
src/core/SQL/SQLite/20/select_messagesAllNew.sql [moved from src/core/SQL/SQLite/19/select_messagesAllNew.sql with 100% similarity]
src/core/SQL/SQLite/20/select_messagesNewerThan.sql [moved from src/core/SQL/SQLite/19/select_messagesNewerThan.sql with 100% similarity]
src/core/SQL/SQLite/20/select_messagesNewestK.sql [moved from src/core/SQL/SQLite/19/select_messagesNewestK.sql with 100% similarity]
src/core/SQL/SQLite/20/select_networkExists.sql [moved from src/core/SQL/SQLite/19/select_networkExists.sql with 100% similarity]
src/core/SQL/SQLite/20/select_network_awaymsg.sql [moved from src/core/SQL/SQLite/19/select_network_awaymsg.sql with 100% similarity]
src/core/SQL/SQLite/20/select_network_usermode.sql [moved from src/core/SQL/SQLite/19/select_network_usermode.sql with 100% similarity]
src/core/SQL/SQLite/20/select_networks_for_user.sql [moved from src/core/SQL/PostgreSQL/18/select_networks_for_user.sql with 60% similarity]
src/core/SQL/SQLite/20/select_nicks.sql [moved from src/core/SQL/SQLite/19/select_nicks.sql with 100% similarity]
src/core/SQL/SQLite/20/select_persistent_channels.sql [moved from src/core/SQL/SQLite/19/select_persistent_channels.sql with 100% similarity]
src/core/SQL/SQLite/20/select_servers_for_network.sql [moved from src/core/SQL/SQLite/19/select_servers_for_network.sql with 100% similarity]
src/core/SQL/SQLite/20/select_user_setting.sql [moved from src/core/SQL/SQLite/19/select_user_setting.sql with 100% similarity]
src/core/SQL/SQLite/20/select_userid.sql [moved from src/core/SQL/SQLite/19/select_userid.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_000_quasseluser.sql [moved from src/core/SQL/SQLite/19/setup_000_quasseluser.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_010_sender.sql [moved from src/core/SQL/SQLite/19/setup_010_sender.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_020_network.sql [moved from src/core/SQL/SQLite/19/setup_020_network.sql with 78% similarity]
src/core/SQL/SQLite/20/setup_030_buffer.sql [moved from src/core/SQL/SQLite/19/setup_030_buffer.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_040_buffer_idx.sql [moved from src/core/SQL/SQLite/19/setup_040_buffer_idx.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_050_buffer_cname_idx.sql [moved from src/core/SQL/SQLite/19/setup_050_buffer_cname_idx.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_060_backlog.sql [moved from src/core/SQL/SQLite/19/setup_060_backlog.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_070_coreinfo.sql [moved from src/core/SQL/SQLite/19/setup_070_coreinfo.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_080_ircservers.sql [moved from src/core/SQL/SQLite/19/setup_080_ircservers.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_090_backlog_idx.sql [moved from src/core/SQL/SQLite/19/setup_090_backlog_idx.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_100_backlog_idx2.sql [moved from src/core/SQL/SQLite/19/setup_100_backlog_idx2.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_110_buffer_user_idx.sql [moved from src/core/SQL/SQLite/19/setup_110_buffer_user_idx.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_120_user_setting.sql [moved from src/core/SQL/SQLite/19/setup_120_user_setting.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_130_identity.sql [moved from src/core/SQL/SQLite/19/setup_130_identity.sql with 100% similarity]
src/core/SQL/SQLite/20/setup_140_identity_nick.sql [moved from src/core/SQL/SQLite/19/setup_140_identity_nick.sql with 100% similarity]
src/core/SQL/SQLite/20/update_backlog_bufferid.sql [moved from src/core/SQL/SQLite/19/update_backlog_bufferid.sql with 100% similarity]
src/core/SQL/SQLite/20/update_buffer_lastseen.sql [moved from src/core/SQL/SQLite/19/update_buffer_lastseen.sql with 100% similarity]
src/core/SQL/SQLite/20/update_buffer_markerlinemsgid.sql [moved from src/core/SQL/SQLite/19/update_buffer_markerlinemsgid.sql with 100% similarity]
src/core/SQL/SQLite/20/update_buffer_name.sql [moved from src/core/SQL/SQLite/19/update_buffer_name.sql with 100% similarity]
src/core/SQL/SQLite/20/update_buffer_persistent_channel.sql [moved from src/core/SQL/SQLite/19/update_buffer_persistent_channel.sql with 100% similarity]
src/core/SQL/SQLite/20/update_buffer_set_channel_key.sql [moved from src/core/SQL/SQLite/19/update_buffer_set_channel_key.sql with 100% similarity]
src/core/SQL/SQLite/20/update_identity.sql [moved from src/core/SQL/SQLite/19/update_identity.sql with 100% similarity]
src/core/SQL/SQLite/20/update_network.sql [moved from src/core/SQL/SQLite/19/update_network.sql with 79% similarity]
src/core/SQL/SQLite/20/update_network_connected.sql [moved from src/core/SQL/SQLite/19/update_network_connected.sql with 100% similarity]
src/core/SQL/SQLite/20/update_network_set_awaymsg.sql [moved from src/core/SQL/SQLite/19/update_network_set_awaymsg.sql with 100% similarity]
src/core/SQL/SQLite/20/update_network_set_usermode.sql [moved from src/core/SQL/SQLite/19/update_network_set_usermode.sql with 100% similarity]
src/core/SQL/SQLite/20/update_user_setting.sql [moved from src/core/SQL/SQLite/19/update_user_setting.sql with 100% similarity]
src/core/SQL/SQLite/20/update_username.sql [moved from src/core/SQL/SQLite/19/update_username.sql with 100% similarity]
src/core/SQL/SQLite/20/update_userpassword.sql [moved from src/core/SQL/SQLite/19/update_userpassword.sql with 100% similarity]
src/core/SQL/SQLite/20/upgrade_000_alter_network_add_ratelimit_usecustom.sql [new file with mode: 0644]
src/core/SQL/SQLite/20/upgrade_001_alter_network_add_ratelimit_burstsize.sql [new file with mode: 0644]
src/core/SQL/SQLite/20/upgrade_002_alter_network_add_ratelimit_delay.sql [new file with mode: 0644]
src/core/SQL/SQLite/20/upgrade_003_alter_network_add_ratelimit_unlimited.sql [new file with mode: 0644]
src/core/abstractsqlstorage.h
src/core/corenetwork.cpp
src/core/corenetwork.h
src/core/postgresqlstorage.cpp
src/core/sql.qrc
src/core/sqlitestorage.cpp
src/qtui/settingspages/networkssettingspage.cpp
src/qtui/settingspages/networkssettingspage.ui

index b1e4519..4e14af4 100644 (file)
@@ -50,6 +50,10 @@ Network::Network(const NetworkId &networkid, QObject *parent)
     _autoReconnectInterval(60),
     _autoReconnectRetries(10),
     _unlimitedReconnectRetries(false),
+    _useCustomMessageRate(false),
+    _messageRateBurstSize(5),
+    _messageRateDelay(2200),
+    _unlimitedMessageRate(false),
     _codecForServer(0),
     _codecForEncoding(0),
     _codecForDecoding(0),
@@ -112,6 +116,10 @@ NetworkInfo Network::networkInfo() const
     info.autoReconnectRetries = autoReconnectRetries();
     info.unlimitedReconnectRetries = unlimitedReconnectRetries();
     info.rejoinChannels = rejoinChannels();
+    info.useCustomMessageRate = useCustomMessageRate();
+    info.messageRateBurstSize = messageRateBurstSize();
+    info.messageRateDelay = messageRateDelay();
+    info.unlimitedMessageRate = unlimitedMessageRate();
     return info;
 }
 
@@ -138,6 +146,15 @@ void Network::setNetworkInfo(const NetworkInfo &info)
     if (info.autoReconnectRetries != autoReconnectRetries()) setAutoReconnectRetries(info.autoReconnectRetries);
     if (info.unlimitedReconnectRetries != unlimitedReconnectRetries()) setUnlimitedReconnectRetries(info.unlimitedReconnectRetries);
     if (info.rejoinChannels != rejoinChannels()) setRejoinChannels(info.rejoinChannels);
+    // Custom rate limiting
+    if (info.useCustomMessageRate != useCustomMessageRate())
+        setUseCustomMessageRate(info.useCustomMessageRate);
+    if (info.messageRateBurstSize != messageRateBurstSize())
+        setMessageRateBurstSize(info.messageRateBurstSize);
+    if (info.messageRateDelay != messageRateDelay())
+        setMessageRateDelay(info.messageRateDelay);
+    if (info.unlimitedMessageRate != unlimitedMessageRate())
+        setUnlimitedMessageRate(info.unlimitedMessageRate);
 }
 
 
@@ -676,6 +693,56 @@ void Network::setRejoinChannels(bool rejoin)
 }
 
 
+void Network::setUseCustomMessageRate(bool useCustomRate)
+{
+    if (_useCustomMessageRate != useCustomRate) {
+        _useCustomMessageRate = useCustomRate;
+        SYNC(ARG(useCustomRate))
+        emit configChanged();
+        emit useCustomMessageRateSet(_useCustomMessageRate);
+    }
+}
+
+
+void Network::setMessageRateBurstSize(quint32 burstSize)
+{
+    if (burstSize < 1) {
+        // Can't go slower than one message at a time
+        qWarning() << "Received invalid setMessageRateBurstSize data, cannot have zero message "
+                      "burst size!" << burstSize;
+        return;
+    }
+    if (_messageRateBurstSize != burstSize) {
+        _messageRateBurstSize = burstSize;
+        SYNC(ARG(burstSize))
+        emit configChanged();
+        emit messageRateBurstSizeSet(_messageRateBurstSize);
+    }
+}
+
+
+void Network::setMessageRateDelay(quint32 messageDelay)
+{
+    if (_messageRateDelay != messageDelay) {
+        _messageRateDelay = messageDelay;
+        SYNC(ARG(messageDelay))
+        emit configChanged();
+        emit messageRateDelaySet(_messageRateDelay);
+    }
+}
+
+
+void Network::setUnlimitedMessageRate(bool unlimitedRate)
+{
+    if (_unlimitedMessageRate != unlimitedRate) {
+        _unlimitedMessageRate = unlimitedRate;
+        SYNC(ARG(unlimitedRate))
+        emit configChanged();
+        emit unlimitedMessageRateSet(_unlimitedMessageRate);
+    }
+}
+
+
 void Network::addSupport(const QString &param, const QString &value)
 {
     if (!_supports.contains(param)) {
@@ -1001,7 +1068,11 @@ NetworkInfo::NetworkInfo()
     autoReconnectInterval(60),
     autoReconnectRetries(20),
     unlimitedReconnectRetries(false),
-    rejoinChannels(true)
+    rejoinChannels(true),
+    useCustomMessageRate(false),
+    messageRateBurstSize(5),
+    messageRateDelay(2200),
+    unlimitedMessageRate(false)
 {
 }
 
@@ -1028,6 +1099,11 @@ bool NetworkInfo::operator==(const NetworkInfo &other) const
     if (autoReconnectRetries != other.autoReconnectRetries) return false;
     if (unlimitedReconnectRetries != other.unlimitedReconnectRetries) return false;
     if (rejoinChannels != other.rejoinChannels) return false;
+    // Custom rate limiting
+    if (useCustomMessageRate != other.useCustomMessageRate) return false;
+    if (messageRateBurstSize != other.messageRateBurstSize) return false;
+    if (messageRateDelay != other.messageRateDelay) return false;
+    if (unlimitedMessageRate != other.unlimitedMessageRate) return false;
     return true;
 }
 
@@ -1061,6 +1137,11 @@ QDataStream &operator<<(QDataStream &out, const NetworkInfo &info)
     i["AutoReconnectRetries"] = info.autoReconnectRetries;
     i["UnlimitedReconnectRetries"] = info.unlimitedReconnectRetries;
     i["RejoinChannels"] = info.rejoinChannels;
+    // Custom rate limiting
+    i["UseCustomMessageRate"] = info.useCustomMessageRate;
+    i["MessageRateBurstSize"] = info.messageRateBurstSize;
+    i["MessageRateDelay"] = info.messageRateDelay;
+    i["UnlimitedMessageRate"] = info.unlimitedMessageRate;
     out << i;
     return out;
 }
@@ -1090,6 +1171,11 @@ QDataStream &operator>>(QDataStream &in, NetworkInfo &info)
     info.autoReconnectRetries = i["AutoReconnectRetries"].toInt();
     info.unlimitedReconnectRetries = i["UnlimitedReconnectRetries"].toBool();
     info.rejoinChannels = i["RejoinChannels"].toBool();
+    // Custom rate limiting
+    info.useCustomMessageRate = i["UseCustomMessageRate"].toBool();
+    info.messageRateBurstSize = i["MessageRateBurstSize"].toUInt();
+    info.messageRateDelay = i["MessageRateDelay"].toUInt();
+    info.unlimitedMessageRate = i["UnlimitedMessageRate"].toBool();
     return in;
 }
 
@@ -1103,7 +1189,12 @@ QDebug operator<<(QDebug dbg, const NetworkInfo &i)
     << " useSasl = " << i.useSasl << " saslAccount = " << i.saslAccount << " saslPassword = " << i.saslPassword
     << " useAutoReconnect = " << i.useAutoReconnect << " autoReconnectInterval = " << i.autoReconnectInterval
     << " autoReconnectRetries = " << i.autoReconnectRetries << " unlimitedReconnectRetries = " << i.unlimitedReconnectRetries
-    << " rejoinChannels = " << i.rejoinChannels << ")";
+    << " rejoinChannels = " << i.rejoinChannels
+    << " useCustomMessageRate = " << i.useCustomMessageRate
+    << " messageRateBurstSize = " << i.messageRateBurstSize
+    << " messageRateDelay = " << i.messageRateDelay
+    << " unlimitedMessageRate = " << i.unlimitedMessageRate
+    << ")";
     return dbg.space();
 }
 
index bbcd268..969c3b9 100644 (file)
@@ -74,6 +74,11 @@ class Network : public SyncableObject
     Q_PROPERTY(quint16 autoReconnectRetries READ autoReconnectRetries WRITE setAutoReconnectRetries)
     Q_PROPERTY(bool unlimitedReconnectRetries READ unlimitedReconnectRetries WRITE setUnlimitedReconnectRetries)
     Q_PROPERTY(bool rejoinChannels READ rejoinChannels WRITE setRejoinChannels)
+    // Custom rate limiting
+    Q_PROPERTY(bool useCustomMessageRate READ useCustomMessageRate WRITE setUseCustomMessageRate)
+    Q_PROPERTY(quint32 msgRateBurstSize READ messageRateBurstSize WRITE setMessageRateBurstSize)
+    Q_PROPERTY(quint32 msgRateMessageDelay READ messageRateDelay WRITE setMessageRateDelay)
+    Q_PROPERTY(bool unlimitedMessageRate READ unlimitedMessageRate WRITE setUnlimitedMessageRate)
 
 public :
         enum ConnectionState {
@@ -207,6 +212,44 @@ public :
     inline bool unlimitedReconnectRetries() const { return _unlimitedReconnectRetries; }
     inline bool rejoinChannels() const { return _rejoinChannels; }
 
+    // Custom rate limiting
+
+    /**
+     * Gets whether or not custom rate limiting is used
+     *
+     * @return True if custom rate limiting is enabled, otherwise false.
+     */
+    inline bool useCustomMessageRate() const { return _useCustomMessageRate; }
+
+    /**
+     * Gets maximum number of messages to send without any delays
+     *
+     * @return
+     * @parblock
+     * Maximum number of messages to send without any delays.  A value of 1 disables message
+     * bursting.
+     * @endparblock
+     */
+    inline quint32 messageRateBurstSize() const { return _messageRateBurstSize; }
+
+    /**
+     * Gets the delay between messages after the maximum number of undelayed messages have been sent
+     *
+     * @return
+     * @parblock
+     * Delay in milliseconds between messages after the maximum number of undelayed messages have
+     * been sent.
+     * @endparblock
+     */
+    inline quint32 messageRateDelay() const { return _messageRateDelay; }
+
+    /**
+     * Gets whether or not all rate limiting is disabled, e.g. for IRC bridges
+     *
+     * @return If true, disable rate limiting, otherwise apply configured limits.
+     */
+    inline bool unlimitedMessageRate() const { return _unlimitedMessageRate; }
+
     NetworkInfo networkInfo() const;
     void setNetworkInfo(const NetworkInfo &);
 
@@ -297,6 +340,48 @@ public slots:
     void setUnlimitedReconnectRetries(bool);
     void setRejoinChannels(bool);
 
+    // Custom rate limiting
+
+    /**
+     * Sets whether or not custom rate limiting is used.
+     *
+     * Setting limits too low may get you disconnected from the server!
+     *
+     * @param[in] useCustomRate If true, use custom rate limits, otherwise use Quassel defaults.
+     */
+    void setUseCustomMessageRate(bool useCustomRate);
+
+    /**
+     * Sets maximum number of messages to send without any delays
+     *
+     * @param[in] burstSize
+     * @parblock
+     * Maximum number of messages to send without any delays.  A value of 1 disables message
+     * bursting.  Cannot be less than 1 as sending 0 messages at a time accomplishes nothing.
+     * @endparblock
+     */
+    void setMessageRateBurstSize(quint32 burstSize);
+
+    /**
+     * Sets the delay between messages after the maximum number of undelayed messages have been sent
+     *
+     * @param[in] messageDelay
+     * @parblock
+     * Delay in milliseconds between messages after the maximum number of undelayed messages have
+     * been sent.
+     * @endparblock
+     */
+    void setMessageRateDelay(quint32 messageDelay);
+
+    /**
+     * Sets whether or not all rate limiting is disabled, e.g. for IRC bridges
+     *
+     * Don't use with most normal networks.
+     *
+     * @param[in] unlimitedRate If true, disable rate limiting, otherwise apply configured limits.
+     */
+    void setUnlimitedMessageRate(bool unlimitedRate);
+
     void setCodecForServer(const QByteArray &codecName);
     void setCodecForEncoding(const QByteArray &codecName);
     void setCodecForDecoding(const QByteArray &codecName);
@@ -431,6 +516,44 @@ signals:
 //   void unlimitedReconnectRetriesSet(bool);
 //   void rejoinChannelsSet(bool);
 
+    // Custom rate limiting (can drive other slots)
+
+    /**
+     * Signals enabling or disabling custom rate limiting
+     *
+     * @see Network::useCustomMessageRate()
+     *
+     * @param[out] useCustomRate
+     */
+    void useCustomMessageRateSet(const bool useCustomRate);
+
+    /**
+     * Signals a change in maximum number of messages to send without any delays
+     *
+     * @see Network::messageRateBurstSize()
+     *
+     * @param[out] burstSize
+     */
+    void messageRateBurstSizeSet(const quint32 burstSize);
+
+    /**
+     * Signals a change in delay between messages after the max. undelayed messages have been sent
+     *
+     * @see Network::messageRateDelay()
+     *
+     * @param[out] messageDelay
+     */
+    void messageRateDelaySet(const quint32 messageDelay);
+
+    /**
+     * Signals enabling or disabling all rate limiting
+     *
+     * @see Network::unlimitedMessageRate()
+     *
+     * @param[out] unlimitedRate
+     */
+    void unlimitedMessageRateSet(const bool unlimitedRate);
+
 //   void codecForServerSet(const QByteArray &codecName);
 //   void codecForEncodingSet(const QByteArray &codecName);
 //   void codecForDecodingSet(const QByteArray &codecName);
@@ -524,6 +647,12 @@ private:
     bool _unlimitedReconnectRetries;
     bool _rejoinChannels;
 
+    // Custom rate limiting
+    bool _useCustomMessageRate;         /// If true, use custom rate limits, otherwise use defaults
+    quint32 _messageRateBurstSize;      /// Maximum number of messages to send without any delays
+    quint32 _messageRateDelay;          /// Delay in ms. for messages when max. burst messages sent
+    bool _unlimitedMessageRate;         /// If true, disable rate limiting, otherwise apply limits
+
     QTextCodec *_codecForServer;
     QTextCodec *_codecForEncoding;
     QTextCodec *_codecForDecoding;
@@ -572,6 +701,12 @@ struct NetworkInfo {
     bool unlimitedReconnectRetries;
     bool rejoinChannels;
 
+    // Custom rate limiting
+    bool useCustomMessageRate;         /// If true, use custom rate limits, otherwise use defaults
+    quint32 messageRateBurstSize;      /// Maximum number of messages to send without any delays
+    quint32 messageRateDelay;          /// Delay in ms. for messages when max. burst messages sent
+    bool unlimitedMessageRate;         /// If true, disable rate limiting, otherwise apply limits
+
     bool operator==(const NetworkInfo &other) const;
     bool operator!=(const NetworkInfo &other) const;
 };
index b705d59..3305009 100644 (file)
@@ -73,8 +73,9 @@ public:
         PasswordChange = 0x0010,
         CapNegotiation = 0x0020,           /// IRCv3 capability negotiation, account tracking
         VerifyServerSSL = 0x0040,          /// IRC server SSL validation
+        CustomRateLimits = 0x0080,         /// IRC server custom message rate limits
 
-        NumFeatures = 0x0040
+        NumFeatures = 0x0080
     };
     Q_DECLARE_FLAGS(Features, Feature)
 
diff --git a/src/core/SQL/PostgreSQL/18/insert_network.sql b/src/core/SQL/PostgreSQL/18/insert_network.sql
deleted file mode 100644 (file)
index 56fbef8..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-INSERT INTO network (userid, networkname, identityid, servercodec, encodingcodec, decodingcodec, userandomserver, perform, useautoidentify, autoidentifyservice, autoidentifypassword, useautoreconnect, autoreconnectinterval, autoreconnectretries, unlimitedconnectretries, rejoinchannels, usesasl, saslaccount, saslpassword)
-VALUES (:userid, :networkname, :identityid, :servercodec, :encodingcodec, :decodingcodec, :userandomserver, :perform, :useautoidentify, :autoidentifyservice, :autoidentifypassword, :useautoreconnect, :autoreconnectinterval, :autoreconnectretries, :unlimitedconnectretries, :rejoinchannels, :usesasl, :saslaccount, :saslpassword)
-RETURNING networkid
diff --git a/src/core/SQL/PostgreSQL/18/migrate_write_network.sql b/src/core/SQL/PostgreSQL/18/migrate_write_network.sql
deleted file mode 100644 (file)
index 45576ee..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-INSERT INTO network (networkid, userid, networkname, identityid, encodingcodec, decodingcodec, servercodec, userandomserver, perform, useautoidentify, autoidentifyservice, autoidentifypassword, useautoreconnect, autoreconnectinterval, autoreconnectretries, unlimitedconnectretries, rejoinchannels, connected, usermode, awaymessage, attachperform, detachperform, usesasl, saslaccount, saslpassword)
-VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
diff --git a/src/core/SQL/PostgreSQL/19/insert_network.sql b/src/core/SQL/PostgreSQL/19/insert_network.sql
new file mode 100644 (file)
index 0000000..d91f23c
--- /dev/null
@@ -0,0 +1,12 @@
+INSERT INTO network (userid, networkname, identityid, servercodec, encodingcodec, decodingcodec,
+                     userandomserver, perform, useautoidentify, autoidentifyservice,
+                     autoidentifypassword, useautoreconnect, autoreconnectinterval,
+                     autoreconnectretries, unlimitedconnectretries, rejoinchannels, usesasl,
+                     saslaccount, saslpassword, usecustomessagerate, messagerateburstsize,
+                     messageratedelay, unlimitedmessagerate)
+VALUES (:userid, :networkname, :identityid, :servercodec, :encodingcodec, :decodingcodec,
+        :userandomserver, :perform, :useautoidentify, :autoidentifyservice, :autoidentifypassword,
+        :useautoreconnect, :autoreconnectinterval, :autoreconnectretries, :unlimitedconnectretries,
+        :rejoinchannels, :usesasl, :saslaccount, :saslpassword, :usecustomessagerate,
+        :messagerateburstsize, :messageratedelay, :unlimitedmessagerate)
+RETURNING networkid
diff --git a/src/core/SQL/PostgreSQL/19/migrate_write_network.sql b/src/core/SQL/PostgreSQL/19/migrate_write_network.sql
new file mode 100644 (file)
index 0000000..979370f
--- /dev/null
@@ -0,0 +1,8 @@
+INSERT INTO network (networkid, userid, networkname, identityid, encodingcodec, decodingcodec,
+                     servercodec, userandomserver, perform, useautoidentify, autoidentifyservice,
+                     autoidentifypassword, useautoreconnect, autoreconnectinterval,
+                     autoreconnectretries, unlimitedconnectretries, rejoinchannels, connected,
+                     usermode, awaymessage, attachperform, detachperform, usesasl, saslaccount,
+                     saslpassword, usecustomessagerate, messagerateburstsize, messageratedelay,
+                     unlimitedmessagerate)
+VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
@@ -1,6 +1,7 @@
 SELECT networkid, networkname, identityid, servercodec, encodingcodec, decodingcodec,
        userandomserver, perform, useautoidentify, autoidentifyservice, autoidentifypassword,
-       useautoreconnect, autoreconnectinterval, autoreconnectretries, unlimitedconnectretries, rejoinchannels,
-       usesasl, saslaccount, saslpassword
+       useautoreconnect, autoreconnectinterval, autoreconnectretries, unlimitedconnectretries,
+       rejoinchannels, usesasl, saslaccount, saslpassword, usecustomessagerate,
+       messagerateburstsize, messageratedelay, unlimitedmessagerate
 FROM network
 WHERE userid = :userid
@@ -24,5 +24,9 @@ CREATE TABLE network (
        awaymessage varchar(256), -- away message to restore (empty if not away)
        attachperform text, -- perform list for on attach
        detachperform text, -- perform list for on detach
+       usecustomessagerate boolean NOT NULL DEFAULT FALSE,  -- Custom rate limiting
+       messagerateburstsize INTEGER NOT NULL DEFAULT 5,     -- Maximum messages at once
+       messageratedelay INTEGER NOT NULL DEFAULT 2200,      -- Delay between future messages (milliseconds)
+       unlimitedmessagerate boolean NOT NULL DEFAULT FALSE, -- Disable rate limits
        UNIQUE (userid, networkname)
 )
@@ -13,6 +13,10 @@ useautoreconnect = :useautoreconnect,
 autoreconnectinterval = :autoreconnectinterval,
 autoreconnectretries = :autoreconnectretries,
 unlimitedconnectretries = :unlimitedconnectretries,
+usecustomessagerate = :usecustomessagerate,
+messagerateburstsize = :messagerateburstsize,
+messageratedelay = :messageratedelay,
+unlimitedmessagerate = :unlimitedmessagerate,
 rejoinchannels = :rejoinchannels,
 usesasl = :usesasl,
 saslaccount = :saslaccount,
diff --git a/src/core/SQL/PostgreSQL/19/upgrade_000_alter_network_add_ratelimit_usecustom.sql b/src/core/SQL/PostgreSQL/19/upgrade_000_alter_network_add_ratelimit_usecustom.sql
new file mode 100644 (file)
index 0000000..6b84d19
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE network ADD COLUMN usecustomessagerate boolean NOT NULL DEFAULT FALSE
diff --git a/src/core/SQL/PostgreSQL/19/upgrade_001_alter_network_add_ratelimit_burstsize.sql b/src/core/SQL/PostgreSQL/19/upgrade_001_alter_network_add_ratelimit_burstsize.sql
new file mode 100644 (file)
index 0000000..5ad4f7c
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE network ADD COLUMN messagerateburstsize INTEGER NOT NULL DEFAULT 5
diff --git a/src/core/SQL/PostgreSQL/19/upgrade_002_alter_network_add_ratelimit_delay.sql b/src/core/SQL/PostgreSQL/19/upgrade_002_alter_network_add_ratelimit_delay.sql
new file mode 100644 (file)
index 0000000..ea96477
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE network ADD COLUMN messageratedelay INTEGER NOT NULL DEFAULT 2200
diff --git a/src/core/SQL/PostgreSQL/19/upgrade_003_alter_network_add_ratelimit_unlimited.sql b/src/core/SQL/PostgreSQL/19/upgrade_003_alter_network_add_ratelimit_unlimited.sql
new file mode 100644 (file)
index 0000000..fa943a1
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE network ADD COLUMN unlimitedmessagerate boolean NOT NULL DEFAULT FALSE
diff --git a/src/core/SQL/SQLite/19/insert_network.sql b/src/core/SQL/SQLite/19/insert_network.sql
deleted file mode 100644 (file)
index 4d9ff01..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-INSERT INTO network (userid, networkname, identityid, servercodec, encodingcodec, decodingcodec, userandomserver,
-                     perform, useautoidentify, autoidentifyservice, autoidentifypassword, useautoreconnect, autoreconnectinterval, autoreconnectretries, unlimitedconnectretries, rejoinchannels, usesasl, saslaccount, saslpassword)
-VALUES (:userid, :networkname, :identityid, :servercodec, :encodingcodec, :decodingcodec, :userandomserver,
-        :perform, :useautoidentify, :autoidentifyservice, :autoidentifypassword, :useautoreconnect, :autoreconnectinterval, :autoreconnectretries, :unlimitedconnectretries, :rejoinchannels, :usesasl, :saslaccount, :saslpassword)
diff --git a/src/core/SQL/SQLite/20/insert_network.sql b/src/core/SQL/SQLite/20/insert_network.sql
new file mode 100644 (file)
index 0000000..ec5a212
--- /dev/null
@@ -0,0 +1,11 @@
+INSERT INTO network (userid, networkname, identityid, servercodec, encodingcodec, decodingcodec,
+                     userandomserver, perform, useautoidentify, autoidentifyservice,
+                     autoidentifypassword, useautoreconnect, autoreconnectinterval,
+                     autoreconnectretries, unlimitedconnectretries, rejoinchannels, usesasl,
+                     saslaccount, saslpassword, usecustomessagerate, messagerateburstsize,
+                     messageratedelay, unlimitedmessagerate)
+VALUES (:userid, :networkname, :identityid, :servercodec, :encodingcodec, :decodingcodec,
+        :userandomserver, :perform, :useautoidentify, :autoidentifyservice, :autoidentifypassword,
+        :useautoreconnect, :autoreconnectinterval, :autoreconnectretries, :unlimitedconnectretries,
+        :rejoinchannels, :usesasl, :saslaccount, :saslpassword, :usecustomessagerate,
+        :messagerateburstsize, :messageratedelay, :unlimitedmessagerate)
@@ -2,5 +2,6 @@ SELECT networkid, userid, networkname, identityid, encodingcodec, decodingcodec,
        userandomserver, perform, useautoidentify, autoidentifyservice, autoidentifypassword,
        useautoreconnect, autoreconnectinterval, autoreconnectretries, unlimitedconnectretries,
        rejoinchannels, connected, usermode, awaymessage, attachperform, detachperform,
-       usesasl, saslaccount, saslpassword
+       usesasl, saslaccount, saslpassword, usecustomessagerate, messagerateburstsize,
+       messageratedelay, unlimitedmessagerate
 FROM network
@@ -1,6 +1,7 @@
 SELECT networkid, networkname, identityid, servercodec, encodingcodec, decodingcodec,
        userandomserver, perform, useautoidentify, autoidentifyservice, autoidentifypassword,
-       useautoreconnect, autoreconnectinterval, autoreconnectretries, unlimitedconnectretries, rejoinchannels,
-       usesasl, saslaccount, saslpassword
+       useautoreconnect, autoreconnectinterval, autoreconnectretries, unlimitedconnectretries,
+       rejoinchannels, usesasl, saslaccount, saslpassword, usecustomessagerate,
+       messagerateburstsize, messageratedelay, unlimitedmessagerate
 FROM network
 WHERE userid = :userid
similarity index 78%
rename from src/core/SQL/SQLite/19/setup_020_network.sql
rename to src/core/SQL/SQLite/20/setup_020_network.sql
index efa1f8d..60417a7 100644 (file)
@@ -24,5 +24,9 @@ CREATE TABLE network (
        awaymessage TEXT, -- away message to restore (empty if not away)
        attachperform TEXT, -- perform list for on attach
        detachperform TEXT, -- perform list for on detach
+       usecustomessagerate INTEGER NOT NULL DEFAULT 0,  -- BOOL - Custom rate limiting
+       messagerateburstsize INTEGER NOT NULL DEFAULT 5, -- Maximum messages at once
+       messageratedelay INTEGER NOT NULL DEFAULT 2200,  -- Delay between future messages (milliseconds)
+       unlimitedmessagerate INTEGER NOT NULL DEFAULT 0, -- BOOL - Disable rate limits
        UNIQUE (userid, networkname)
 )
similarity index 79%
rename from src/core/SQL/SQLite/19/update_network.sql
rename to src/core/SQL/SQLite/20/update_network.sql
index 84c8195..00e9f83 100644 (file)
@@ -14,6 +14,10 @@ autoreconnectinterval = :autoreconnectinterval,
 autoreconnectretries = :autoreconnectretries,
 unlimitedconnectretries = :unlimitedconnectretries,
 rejoinchannels = :rejoinchannels,
+usecustomessagerate = :usecustomessagerate,
+messagerateburstsize = :messagerateburstsize,
+messageratedelay = :messageratedelay,
+unlimitedmessagerate = :unlimitedmessagerate,
 usesasl = :usesasl,
 saslaccount = :saslaccount,
 saslpassword = :saslpassword
diff --git a/src/core/SQL/SQLite/20/upgrade_000_alter_network_add_ratelimit_usecustom.sql b/src/core/SQL/SQLite/20/upgrade_000_alter_network_add_ratelimit_usecustom.sql
new file mode 100644 (file)
index 0000000..6cde931
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE network ADD COLUMN usecustomessagerate INTEGER NOT NULL DEFAULT 0
diff --git a/src/core/SQL/SQLite/20/upgrade_001_alter_network_add_ratelimit_burstsize.sql b/src/core/SQL/SQLite/20/upgrade_001_alter_network_add_ratelimit_burstsize.sql
new file mode 100644 (file)
index 0000000..5ad4f7c
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE network ADD COLUMN messagerateburstsize INTEGER NOT NULL DEFAULT 5
diff --git a/src/core/SQL/SQLite/20/upgrade_002_alter_network_add_ratelimit_delay.sql b/src/core/SQL/SQLite/20/upgrade_002_alter_network_add_ratelimit_delay.sql
new file mode 100644 (file)
index 0000000..ea96477
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE network ADD COLUMN messageratedelay INTEGER NOT NULL DEFAULT 2200
diff --git a/src/core/SQL/SQLite/20/upgrade_003_alter_network_add_ratelimit_unlimited.sql b/src/core/SQL/SQLite/20/upgrade_003_alter_network_add_ratelimit_unlimited.sql
new file mode 100644 (file)
index 0000000..4380e45
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE network ADD COLUMN unlimitedmessagerate INTEGER NOT NULL DEFAULT 0
index 06d19b7..0ae1404 100644 (file)
@@ -188,6 +188,12 @@ public:
         int autoreconnectretries;
         bool unlimitedconnectretries;
         bool rejoinchannels;
+        // Custom rate limiting
+        bool usecustommessagerate;
+        int messagerateburstsize;
+        int messageratedelay;
+        bool unlimitedmessagerate;
+        // ...
         bool connected;
         QString usermode;
         QString awaymessage;
index 76f430c..6f2c285 100644 (file)
@@ -72,7 +72,7 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session)
     connect(&_autoReconnectTimer, SIGNAL(timeout()), this, SLOT(doAutoReconnect()));
     connect(&_autoWhoTimer, SIGNAL(timeout()), this, SLOT(sendAutoWho()));
     connect(&_autoWhoCycleTimer, SIGNAL(timeout()), this, SLOT(startAutoWhoCycle()));
-    connect(&_tokenBucketTimer, SIGNAL(timeout()), this, SLOT(fillBucketAndProcessQueue()));
+    connect(&_tokenBucketTimer, SIGNAL(timeout()), this, SLOT(checkTokenBucket()));
 
     connect(&socket, SIGNAL(connected()), this, SLOT(socketInitialized()));
     connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
@@ -84,6 +84,13 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session)
 #endif
     connect(this, SIGNAL(newEvent(Event *)), coreSession()->eventManager(), SLOT(postEvent(Event *)));
 
+    // Custom rate limiting
+    // These react to the user changing settings in the client
+    connect(this, SIGNAL(useCustomMessageRateSet(bool)), SLOT(updateRateLimiting()));
+    connect(this, SIGNAL(messageRateBurstSizeSet(quint32)), SLOT(updateRateLimiting()));
+    connect(this, SIGNAL(messageRateDelaySet(quint32)), SLOT(updateRateLimiting()));
+    connect(this, SIGNAL(unlimitedMessageRateSet(bool)), SLOT(updateRateLimiting()));
+
     // IRCv3 capability handling
     // These react to CAP messages from the server
     connect(this, SIGNAL(capAdded(QString)), this, SLOT(serverCapAdded(QString)));
@@ -301,12 +308,18 @@ void CoreNetwork::userInput(BufferInfo buf, QString msg)
 
 void CoreNetwork::putRawLine(const QByteArray s, const bool prepend)
 {
-    if (_tokenBucket > 0) {
+    if (_tokenBucket > 0 || (_skipMessageRates && _msgQueue.size() == 0)) {
+        // If there's tokens remaining, ...
+        // Or rate limits don't apply AND no messages are in queue (to prevent out-of-order), ...
+        // Send the message now.
         writeToSocket(s);
     } else {
+        // Otherwise, queue the message for later
         if (prepend) {
+            // Jump to the start, skipping other messages
             _msgQueue.prepend(s);
         } else {
+            // Add to back, waiting in order
             _msgQueue.append(s);
         }
     }
@@ -529,11 +542,10 @@ void CoreNetwork::socketInitialized()
 
     socket.setSocketOption(QAbstractSocket::KeepAliveOption, true);
 
-    // TokenBucket to avoid sending too much at once
-    _messageDelay = 2200;  // this seems to be a safe value (2.2 seconds delay)
-    _burstSize = 5;
-    _tokenBucket = _burstSize; // init with a full bucket
-    _tokenBucketTimer.start(_messageDelay);
+    // Update the TokenBucket with specified rate-limiting settings
+    updateRateLimiting();
+    // Fill up the token bucket as we're connecting from scratch
+    resetTokenBucket();
 
     // Request capabilities as per IRCv3.2 specifications
     // Older servers should ignore this; newer servers won't downgrade to RFC1459
@@ -916,6 +928,85 @@ void CoreNetwork::setPingInterval(int interval)
     _pingTimer.setInterval(interval * 1000);
 }
 
+
+/******** Custom Rate Limiting ********/
+
+void CoreNetwork::updateRateLimiting()
+{
+    // Always reset the delay and token bucket (safe-guard against accidentally starting the timer)
+
+    if (useCustomMessageRate()) {
+        // Custom message rates enabled.  Let's go for it!
+
+        _messageDelay = messageRateDelay();
+
+        _burstSize = messageRateBurstSize();
+        if (_burstSize < 1) {
+            qWarning() << "Invalid messageRateBurstSize data, cannot have zero message burst size!"
+                       << _burstSize;
+            // Can't go slower than one message at a time
+            _burstSize = 1;
+        }
+
+        if (_tokenBucket > _burstSize) {
+            // Don't let the token bucket exceed the maximum
+            _tokenBucket = _burstSize;
+            // To fill up the token bucket, use resetRateLimiting().  Don't do that here, otherwise
+            // changing the rate-limit settings while connected to a server will incorrectly reset
+            // the token bucket.
+        }
+
+        // Toggle the timer according to whether or not rate limiting is enabled
+        // If we're here, useCustomMessageRate is true.  Thus, the logic becomes
+        // _skipMessageRates = (useCustomMessageRate && unlimitedMessageRate)
+        _skipMessageRates = unlimitedMessageRate();
+        if (_skipMessageRates) {
+            // If the message queue already contains messages, they need sent before disabling the
+            // timer.  Set the timer to a rapid pace and let it disable itself.
+            if (_msgQueue.size() > 0) {
+                qDebug() << "Outgoing message queue contains messages while disabling rate "
+                            "limiting.  Sending remaining queued messages...";
+                // Promptly run the timer again to clear the messages.  Rate limiting is disabled,
+                // so nothing should cause this to block.. in theory.  However, don't directly call
+                // fillBucketAndProcessQueue() in order to keep it on a separate thread.
+                //
+                // TODO If testing shows this isn't needed, it can be simplified to a direct call.
+                // Hesitant to change it without a wide variety of situations to verify behavior.
+                _tokenBucketTimer.start(100);
+            } else {
+                // No rate limiting, disable the timer
+                _tokenBucketTimer.stop();
+            }
+        } else {
+            // Rate limiting enabled, enable the timer
+            _tokenBucketTimer.start(_messageDelay);
+        }
+    } else {
+        // Custom message rates disabled.  Go for the default.
+
+        _skipMessageRates = false;   // Enable rate-limiting by default
+        // TokenBucket to avoid sending too much at once
+        _messageDelay = 2200;      // This seems to be a safe value (2.2 seconds delay)
+        _burstSize = 5;            // 5 messages at once
+        if (_tokenBucket > _burstSize) {
+            // Don't let the token bucket exceed the maximum
+            _tokenBucket = _burstSize;
+            // To fill up the token bucket, use resetRateLimiting().  Don't do that here, otherwise
+            // changing the rate-limit settings while connected to a server will incorrectly reset
+            // the token bucket.
+        }
+        // Rate limiting enabled, enable the timer
+        _tokenBucketTimer.start(_messageDelay);
+    }
+}
+
+void CoreNetwork::resetTokenBucket()
+{
+    // Fill up the token bucket to the maximum
+    _tokenBucket = _burstSize;
+}
+
+
 /******** IRCv3 Capability Negotiation ********/
 
 void CoreNetwork::serverCapAdded(const QString &capability)
@@ -1274,12 +1365,30 @@ void CoreNetwork::sslErrors(const QList<QSslError> &sslErrors)
 
 #endif  // HAVE_SSL
 
+void CoreNetwork::checkTokenBucket()
+{
+    if (_skipMessageRates) {
+        if (_msgQueue.size() == 0) {
+            // Message queue emptied; stop the timer and bail out
+            _tokenBucketTimer.stop();
+            return;
+        }
+        // Otherwise, we're emptying the queue, continue on as normal
+    }
+
+    // Process whatever messages are pending
+    fillBucketAndProcessQueue();
+}
+
+
 void CoreNetwork::fillBucketAndProcessQueue()
 {
+    // If there's less tokens than burst size, refill the token bucket by 1
     if (_tokenBucket < _burstSize) {
         _tokenBucket++;
     }
 
+    // As long as there's tokens available and messages remaining, sending messages from the queue
     while (_msgQueue.size() > 0 && _tokenBucket > 0) {
         writeToSocket(_msgQueue.takeFirst());
     }
@@ -1290,7 +1399,10 @@ void CoreNetwork::writeToSocket(const QByteArray &data)
 {
     socket.write(data);
     socket.write("\r\n");
-    _tokenBucket--;
+    if (!_skipMessageRates) {
+        // Only subtract from the token bucket if message rate limiting is enabled
+        _tokenBucket--;
+    }
 }
 
 
index 906f60a..509df19 100644 (file)
@@ -268,6 +268,34 @@ public slots:
     bool cipherUsesCBC(const QString &target);
 #endif
 
+    // Custom rate limiting (can be connected to signals)
+
+    /**
+     * Update rate limiting according to Network configuration
+     *
+     * Updates the token bucket and message queue timer according to the network configuration, such
+     * as on first load, or after changing settings.
+     *
+     * Calling this will reset any ongoing queue delays.  If messages exist in the queue when rate
+     * limiting is disabled, messages will be quickly sent (100 ms) with new messages queued to send
+     * until the queue is cleared.
+     *
+     * @see Network::useCustomMessageRate()
+     * @see Network::messageRateBurstSize()
+     * @see Network::messageRateDelay()
+     * @see Network::unlimitedMessageRate()
+     */
+    void updateRateLimiting();
+
+    /**
+     * Resets the token bucket up to the maximum
+     *
+     * Call this if the connection's been reset after calling updateRateLimiting() if needed.
+     *
+     * @see CoreNetwork::updateRateLimiting()
+     */
+    void resetTokenBucket();
+
     // IRCv3 capability negotiation (can be connected to signals)
 
     /**
@@ -381,6 +409,23 @@ private slots:
     void sslErrors(const QList<QSslError> &errors);
 #endif
 
+    /**
+     * Check the message token bucket
+     *
+     * If rate limiting is disabled and the message queue is empty, this disables the token bucket
+     * timer.  Otherwise, a queued message will be sent.
+     *
+     * @see CoreNetwork::fillBucketAndProcessQueue()
+     */
+    void checkTokenBucket();
+
+    /**
+     * Top up token bucket and send as many queued messages as possible
+     *
+     * If there's any room for more tokens, add to the token bucket.  Separately, if there's any
+     * messages to send, send until there's no more tokens or the queue is empty, whichever comes
+     * first.
+     */
     void fillBucketAndProcessQueue();
 
     void writeToSocket(const QByteArray &data);
@@ -463,10 +508,12 @@ private:
     const int maxCapRequestLength = 100;
 
     QTimer _tokenBucketTimer;
-    int _messageDelay;      // token refill speed in ms
-    int _burstSize;         // size of the token bucket
-    int _tokenBucket;       // the virtual bucket that holds the tokens
-    QList<QByteArray> _msgQueue;
+    // No need for int type as one cannot travel into the past (at least not yet, Doc)
+    quint32 _messageDelay;       /// Token refill speed in ms
+    quint32 _burstSize;          /// Size of the token bucket
+    quint32 _tokenBucket;        /// The virtual bucket that holds the tokens
+    QList<QByteArray> _msgQueue; /// Queue of messages waiting to be sent
+    bool _skipMessageRates;      /// If true, skip all message rate limits
 
     QString _requestedUserModes; // 2 strings separated by a '-' character. first part are requested modes to add, the second to remove
 
index 5790478..d6b8244 100644 (file)
@@ -697,6 +697,11 @@ void PostgreSqlStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &inf
     query.bindValue(":autoreconnectretries", info.autoReconnectRetries);
     query.bindValue(":unlimitedconnectretries", info.unlimitedReconnectRetries);
     query.bindValue(":rejoinchannels", info.rejoinChannels);
+    // Custom rate limiting
+    query.bindValue(":usecustomessagerate", info.useCustomMessageRate);
+    query.bindValue(":messagerateburstsize", info.messageRateBurstSize);
+    query.bindValue(":messageratedelay", info.messageRateDelay);
+    query.bindValue(":unlimitedmessagerate", info.unlimitedMessageRate);
     if (info.networkId.isValid())
         query.bindValue(":networkid", info.networkId.toInt());
 }
@@ -843,6 +848,11 @@ QList<NetworkInfo> PostgreSqlStorage::networks(UserId user)
         net.useSasl = networksQuery.value(16).toBool();
         net.saslAccount = networksQuery.value(17).toString();
         net.saslPassword = networksQuery.value(18).toString();
+        // Custom rate limiting
+        net.useCustomMessageRate = networksQuery.value(19).toBool();
+        net.messageRateBurstSize = networksQuery.value(20).toUInt();
+        net.messageRateDelay = networksQuery.value(21).toUInt();
+        net.unlimitedMessageRate = networksQuery.value(22).toBool();
 
         serversQuery.bindValue(":networkid", net.networkId.toInt());
         safeExec(serversQuery);
@@ -1927,6 +1937,11 @@ bool PostgreSqlMigrationWriter::writeMo(const NetworkMO &network)
     bindValue(22, network.usesasl);
     bindValue(23, network.saslaccount);
     bindValue(24, network.saslpassword);
+    // Custom rate limiting
+    bindValue(25, network.usecustommessagerate);
+    bindValue(26, network.messagerateburstsize);
+    bindValue(27, network.messageratedelay);
+    bindValue(28, network.unlimitedmessagerate);
     return exec();
 }
 
index 01b4555..be7a843 100644 (file)
     <file>./SQL/SQLite/17/upgrade_001_alter_network_add_sasl.sql</file>
     <file>./SQL/SQLite/17/upgrade_002_alter_network_add_sasl.sql</file>
     <file>./SQL/SQLite/18/upgrade_000_alter_quasseluser_add_passwordversion.sql</file>
-    <file>./SQL/SQLite/19/delete_backlog_by_uid.sql</file>
-    <file>./SQL/SQLite/19/delete_backlog_for_buffer.sql</file>
-    <file>./SQL/SQLite/19/delete_backlog_for_network.sql</file>
-    <file>./SQL/SQLite/19/delete_buffer_for_bufferid.sql</file>
-    <file>./SQL/SQLite/19/delete_buffers_by_uid.sql</file>
-    <file>./SQL/SQLite/19/delete_buffers_for_network.sql</file>
-    <file>./SQL/SQLite/19/delete_identity.sql</file>
-    <file>./SQL/SQLite/19/delete_ircservers_for_network.sql</file>
-    <file>./SQL/SQLite/19/delete_networks_by_uid.sql</file>
-    <file>./SQL/SQLite/19/delete_network.sql</file>
-    <file>./SQL/SQLite/19/delete_nicks.sql</file>
-    <file>./SQL/SQLite/19/delete_quasseluser.sql</file>
-    <file>./SQL/SQLite/19/insert_buffer.sql</file>
-    <file>./SQL/SQLite/19/insert_identity.sql</file>
-    <file>./SQL/SQLite/19/insert_message.sql</file>
-    <file>./SQL/SQLite/19/insert_network.sql</file>
-    <file>./SQL/SQLite/19/insert_nick.sql</file>
-    <file>./SQL/SQLite/19/insert_quasseluser.sql</file>
-    <file>./SQL/SQLite/19/insert_sender.sql</file>
-    <file>./SQL/SQLite/19/insert_server.sql</file>
-    <file>./SQL/SQLite/19/insert_user_setting.sql</file>
-    <file>./SQL/SQLite/19/migrate_read_backlog.sql</file>
-    <file>./SQL/SQLite/19/migrate_read_buffer.sql</file>
-    <file>./SQL/SQLite/19/migrate_read_identity_nick.sql</file>
-    <file>./SQL/SQLite/19/migrate_read_identity.sql</file>
-    <file>./SQL/SQLite/19/migrate_read_ircserver.sql</file>
-    <file>./SQL/SQLite/19/migrate_read_network.sql</file>
-    <file>./SQL/SQLite/19/migrate_read_quasseluser.sql</file>
-    <file>./SQL/SQLite/19/migrate_read_sender.sql</file>
-    <file>./SQL/SQLite/19/migrate_read_usersetting.sql</file>
-    <file>./SQL/SQLite/19/select_authuser.sql</file>
-    <file>./SQL/SQLite/19/select_buffer_by_id.sql</file>
-    <file>./SQL/SQLite/19/select_bufferByName.sql</file>
-    <file>./SQL/SQLite/19/select_bufferExists.sql</file>
-    <file>./SQL/SQLite/19/select_buffer_lastseen_messages.sql</file>
-    <file>./SQL/SQLite/19/select_buffer_markerlinemsgids.sql</file>
-    <file>./SQL/SQLite/19/select_buffers_for_merge.sql</file>
-    <file>./SQL/SQLite/19/select_buffers_for_network.sql</file>
-    <file>./SQL/SQLite/19/select_buffers.sql</file>
-    <file>./SQL/SQLite/19/select_checkidentity.sql</file>
-    <file>./SQL/SQLite/19/select_connected_networks.sql</file>
-    <file>./SQL/SQLite/19/select_identities.sql</file>
-    <file>./SQL/SQLite/19/select_internaluser.sql</file>
-    <file>./SQL/SQLite/19/select_messagesAllNew.sql</file>
-    <file>./SQL/SQLite/19/select_messagesAll.sql</file>
-    <file>./SQL/SQLite/19/select_messagesNewerThan.sql</file>
-    <file>./SQL/SQLite/19/select_messagesNewestK.sql</file>
-    <file>./SQL/SQLite/19/select_messages.sql</file>
-    <file>./SQL/SQLite/19/select_network_awaymsg.sql</file>
-    <file>./SQL/SQLite/19/select_networkExists.sql</file>
-    <file>./SQL/SQLite/19/select_networks_for_user.sql</file>
-    <file>./SQL/SQLite/19/select_network_usermode.sql</file>
-    <file>./SQL/SQLite/19/select_nicks.sql</file>
-    <file>./SQL/SQLite/19/select_persistent_channels.sql</file>
-    <file>./SQL/SQLite/19/select_servers_for_network.sql</file>
-    <file>./SQL/SQLite/19/select_userid.sql</file>
-    <file>./SQL/SQLite/19/select_user_setting.sql</file>
-    <file>./SQL/SQLite/19/setup_000_quasseluser.sql</file>
-    <file>./SQL/SQLite/19/setup_010_sender.sql</file>
-    <file>./SQL/SQLite/19/setup_020_network.sql</file>
-    <file>./SQL/SQLite/19/setup_030_buffer.sql</file>
-    <file>./SQL/SQLite/19/setup_040_buffer_idx.sql</file>
-    <file>./SQL/SQLite/19/setup_050_buffer_cname_idx.sql</file>
-    <file>./SQL/SQLite/19/setup_060_backlog.sql</file>
-    <file>./SQL/SQLite/19/setup_070_coreinfo.sql</file>
-    <file>./SQL/SQLite/19/setup_080_ircservers.sql</file>
-    <file>./SQL/SQLite/19/setup_090_backlog_idx.sql</file>
-    <file>./SQL/SQLite/19/setup_100_backlog_idx2.sql</file>
-    <file>./SQL/SQLite/19/setup_110_buffer_user_idx.sql</file>
-    <file>./SQL/SQLite/19/setup_120_user_setting.sql</file>
-    <file>./SQL/SQLite/19/setup_130_identity.sql</file>
-    <file>./SQL/SQLite/19/setup_140_identity_nick.sql</file>
-    <file>./SQL/SQLite/19/update_backlog_bufferid.sql</file>
-    <file>./SQL/SQLite/19/update_buffer_lastseen.sql</file>
-    <file>./SQL/SQLite/19/update_buffer_markerlinemsgid.sql</file>
-    <file>./SQL/SQLite/19/update_buffer_name.sql</file>
-    <file>./SQL/SQLite/19/update_buffer_persistent_channel.sql</file>
-    <file>./SQL/SQLite/19/update_buffer_set_channel_key.sql</file>
-    <file>./SQL/SQLite/19/update_identity.sql</file>
-    <file>./SQL/SQLite/19/update_network_connected.sql</file>
-    <file>./SQL/SQLite/19/update_network_set_awaymsg.sql</file>
-    <file>./SQL/SQLite/19/update_network_set_usermode.sql</file>
-    <file>./SQL/SQLite/19/update_network.sql</file>
-    <file>./SQL/SQLite/19/update_username.sql</file>
-    <file>./SQL/SQLite/19/update_userpassword.sql</file>
-    <file>./SQL/SQLite/19/update_user_setting.sql</file>
     <file>./SQL/SQLite/19/upgrade_000_alter_ircserver_add_sslverify.sql</file>
+    <file>./SQL/SQLite/20/delete_backlog_by_uid.sql</file>
+    <file>./SQL/SQLite/20/delete_backlog_for_buffer.sql</file>
+    <file>./SQL/SQLite/20/delete_backlog_for_network.sql</file>
+    <file>./SQL/SQLite/20/delete_buffer_for_bufferid.sql</file>
+    <file>./SQL/SQLite/20/delete_buffers_by_uid.sql</file>
+    <file>./SQL/SQLite/20/delete_buffers_for_network.sql</file>
+    <file>./SQL/SQLite/20/delete_identity.sql</file>
+    <file>./SQL/SQLite/20/delete_ircservers_for_network.sql</file>
+    <file>./SQL/SQLite/20/delete_networks_by_uid.sql</file>
+    <file>./SQL/SQLite/20/delete_network.sql</file>
+    <file>./SQL/SQLite/20/delete_nicks.sql</file>
+    <file>./SQL/SQLite/20/delete_quasseluser.sql</file>
+    <file>./SQL/SQLite/20/insert_buffer.sql</file>
+    <file>./SQL/SQLite/20/insert_identity.sql</file>
+    <file>./SQL/SQLite/20/insert_message.sql</file>
+    <file>./SQL/SQLite/20/insert_network.sql</file>
+    <file>./SQL/SQLite/20/insert_nick.sql</file>
+    <file>./SQL/SQLite/20/insert_quasseluser.sql</file>
+    <file>./SQL/SQLite/20/insert_sender.sql</file>
+    <file>./SQL/SQLite/20/insert_server.sql</file>
+    <file>./SQL/SQLite/20/insert_user_setting.sql</file>
+    <file>./SQL/SQLite/20/migrate_read_backlog.sql</file>
+    <file>./SQL/SQLite/20/migrate_read_buffer.sql</file>
+    <file>./SQL/SQLite/20/migrate_read_identity_nick.sql</file>
+    <file>./SQL/SQLite/20/migrate_read_identity.sql</file>
+    <file>./SQL/SQLite/20/migrate_read_ircserver.sql</file>
+    <file>./SQL/SQLite/20/migrate_read_network.sql</file>
+    <file>./SQL/SQLite/20/migrate_read_quasseluser.sql</file>
+    <file>./SQL/SQLite/20/migrate_read_sender.sql</file>
+    <file>./SQL/SQLite/20/migrate_read_usersetting.sql</file>
+    <file>./SQL/SQLite/20/select_authuser.sql</file>
+    <file>./SQL/SQLite/20/select_buffer_by_id.sql</file>
+    <file>./SQL/SQLite/20/select_bufferByName.sql</file>
+    <file>./SQL/SQLite/20/select_bufferExists.sql</file>
+    <file>./SQL/SQLite/20/select_buffer_lastseen_messages.sql</file>
+    <file>./SQL/SQLite/20/select_buffer_markerlinemsgids.sql</file>
+    <file>./SQL/SQLite/20/select_buffers_for_merge.sql</file>
+    <file>./SQL/SQLite/20/select_buffers_for_network.sql</file>
+    <file>./SQL/SQLite/20/select_buffers.sql</file>
+    <file>./SQL/SQLite/20/select_checkidentity.sql</file>
+    <file>./SQL/SQLite/20/select_connected_networks.sql</file>
+    <file>./SQL/SQLite/20/select_identities.sql</file>
+    <file>./SQL/SQLite/20/select_internaluser.sql</file>
+    <file>./SQL/SQLite/20/select_messagesAllNew.sql</file>
+    <file>./SQL/SQLite/20/select_messagesAll.sql</file>
+    <file>./SQL/SQLite/20/select_messagesNewerThan.sql</file>
+    <file>./SQL/SQLite/20/select_messagesNewestK.sql</file>
+    <file>./SQL/SQLite/20/select_messages.sql</file>
+    <file>./SQL/SQLite/20/select_network_awaymsg.sql</file>
+    <file>./SQL/SQLite/20/select_networkExists.sql</file>
+    <file>./SQL/SQLite/20/select_networks_for_user.sql</file>
+    <file>./SQL/SQLite/20/select_network_usermode.sql</file>
+    <file>./SQL/SQLite/20/select_nicks.sql</file>
+    <file>./SQL/SQLite/20/select_persistent_channels.sql</file>
+    <file>./SQL/SQLite/20/select_servers_for_network.sql</file>
+    <file>./SQL/SQLite/20/select_userid.sql</file>
+    <file>./SQL/SQLite/20/select_user_setting.sql</file>
+    <file>./SQL/SQLite/20/setup_000_quasseluser.sql</file>
+    <file>./SQL/SQLite/20/setup_010_sender.sql</file>
+    <file>./SQL/SQLite/20/setup_020_network.sql</file>
+    <file>./SQL/SQLite/20/setup_030_buffer.sql</file>
+    <file>./SQL/SQLite/20/setup_040_buffer_idx.sql</file>
+    <file>./SQL/SQLite/20/setup_050_buffer_cname_idx.sql</file>
+    <file>./SQL/SQLite/20/setup_060_backlog.sql</file>
+    <file>./SQL/SQLite/20/setup_070_coreinfo.sql</file>
+    <file>./SQL/SQLite/20/setup_080_ircservers.sql</file>
+    <file>./SQL/SQLite/20/setup_090_backlog_idx.sql</file>
+    <file>./SQL/SQLite/20/setup_100_backlog_idx2.sql</file>
+    <file>./SQL/SQLite/20/setup_110_buffer_user_idx.sql</file>
+    <file>./SQL/SQLite/20/setup_120_user_setting.sql</file>
+    <file>./SQL/SQLite/20/setup_130_identity.sql</file>
+    <file>./SQL/SQLite/20/setup_140_identity_nick.sql</file>
+    <file>./SQL/SQLite/20/update_backlog_bufferid.sql</file>
+    <file>./SQL/SQLite/20/update_buffer_lastseen.sql</file>
+    <file>./SQL/SQLite/20/update_buffer_markerlinemsgid.sql</file>
+    <file>./SQL/SQLite/20/update_buffer_name.sql</file>
+    <file>./SQL/SQLite/20/update_buffer_persistent_channel.sql</file>
+    <file>./SQL/SQLite/20/update_buffer_set_channel_key.sql</file>
+    <file>./SQL/SQLite/20/update_identity.sql</file>
+    <file>./SQL/SQLite/20/update_network_connected.sql</file>
+    <file>./SQL/SQLite/20/update_network_set_awaymsg.sql</file>
+    <file>./SQL/SQLite/20/update_network_set_usermode.sql</file>
+    <file>./SQL/SQLite/20/update_network.sql</file>
+    <file>./SQL/SQLite/20/update_username.sql</file>
+    <file>./SQL/SQLite/20/update_userpassword.sql</file>
+    <file>./SQL/SQLite/20/update_user_setting.sql</file>
+    <file>./SQL/SQLite/20/upgrade_000_alter_network_add_ratelimit_usecustom.sql</file>
+    <file>./SQL/SQLite/20/upgrade_001_alter_network_add_ratelimit_burstsize.sql</file>
+    <file>./SQL/SQLite/20/upgrade_002_alter_network_add_ratelimit_delay.sql</file>
+    <file>./SQL/SQLite/20/upgrade_003_alter_network_add_ratelimit_unlimited.sql</file>
     <file>./SQL/PostgreSQL/15/upgrade_000_alter_buffer_add_markerlinemsgid.sql</file>
     <file>./SQL/PostgreSQL/16/upgrade_000_alter_network_add_sasl.sql</file>
     <file>./SQL/PostgreSQL/17/upgrade_000_alter_quasseluser_add_passwordversion.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_backlog_by_uid.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_backlog_for_buffer.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_backlog_for_network.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_buffer_for_bufferid.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_buffers_by_uid.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_buffers_for_network.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_identity.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_ircservers_for_network.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_networks_by_uid.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_network.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_nicks.sql</file>
-    <file>./SQL/PostgreSQL/18/delete_quasseluser.sql</file>
-    <file>./SQL/PostgreSQL/18/insert_buffer.sql</file>
-    <file>./SQL/PostgreSQL/18/insert_identity.sql</file>
-    <file>./SQL/PostgreSQL/18/insert_message.sql</file>
-    <file>./SQL/PostgreSQL/18/insert_network.sql</file>
-    <file>./SQL/PostgreSQL/18/insert_nick.sql</file>
-    <file>./SQL/PostgreSQL/18/insert_quasseluser.sql</file>
-    <file>./SQL/PostgreSQL/18/insert_sender.sql</file>
-    <file>./SQL/PostgreSQL/18/insert_server.sql</file>
-    <file>./SQL/PostgreSQL/18/insert_user_setting.sql</file>
-    <file>./SQL/PostgreSQL/18/migrate_write_backlog.sql</file>
-    <file>./SQL/PostgreSQL/18/migrate_write_buffer.sql</file>
-    <file>./SQL/PostgreSQL/18/migrate_write_identity_nick.sql</file>
-    <file>./SQL/PostgreSQL/18/migrate_write_identity.sql</file>
-    <file>./SQL/PostgreSQL/18/migrate_write_ircserver.sql</file>
-    <file>./SQL/PostgreSQL/18/migrate_write_network.sql</file>
-    <file>./SQL/PostgreSQL/18/migrate_write_quasseluser.sql</file>
-    <file>./SQL/PostgreSQL/18/migrate_write_sender.sql</file>
-    <file>./SQL/PostgreSQL/18/migrate_write_usersetting.sql</file>
-    <file>./SQL/PostgreSQL/18/select_authuser.sql</file>
-    <file>./SQL/PostgreSQL/18/select_buffer_by_id.sql</file>
-    <file>./SQL/PostgreSQL/18/select_bufferByName.sql</file>
-    <file>./SQL/PostgreSQL/18/select_bufferExists.sql</file>
-    <file>./SQL/PostgreSQL/18/select_buffer_lastseen_messages.sql</file>
-    <file>./SQL/PostgreSQL/18/select_buffer_markerlinemsgids.sql</file>
-    <file>./SQL/PostgreSQL/18/select_buffers_for_network.sql</file>
-    <file>./SQL/PostgreSQL/18/select_buffers.sql</file>
-    <file>./SQL/PostgreSQL/18/select_checkidentity.sql</file>
-    <file>./SQL/PostgreSQL/18/select_connected_networks.sql</file>
-    <file>./SQL/PostgreSQL/18/select_identities.sql</file>
-    <file>./SQL/PostgreSQL/18/select_internaluser.sql</file>
-    <file>./SQL/PostgreSQL/18/select_messagesAllNew.sql</file>
-    <file>./SQL/PostgreSQL/18/select_messagesAll.sql</file>
-    <file>./SQL/PostgreSQL/18/select_messagesNewerThan.sql</file>
-    <file>./SQL/PostgreSQL/18/select_messagesRange.sql</file>
-    <file>./SQL/PostgreSQL/18/select_messages.sql</file>
-    <file>./SQL/PostgreSQL/18/select_network_awaymsg.sql</file>
-    <file>./SQL/PostgreSQL/18/select_networkExists.sql</file>
-    <file>./SQL/PostgreSQL/18/select_networks_for_user.sql</file>
-    <file>./SQL/PostgreSQL/18/select_network_usermode.sql</file>
-    <file>./SQL/PostgreSQL/18/select_nicks.sql</file>
-    <file>./SQL/PostgreSQL/18/select_persistent_channels.sql</file>
-    <file>./SQL/PostgreSQL/18/select_senderid.sql</file>
-    <file>./SQL/PostgreSQL/18/select_servers_for_network.sql</file>
-    <file>./SQL/PostgreSQL/18/select_userid.sql</file>
-    <file>./SQL/PostgreSQL/18/select_user_setting.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_000_quasseluser.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_010_sender.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_020_identity.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_030_identity_nick.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_040_network.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_050_buffer.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_060_backlog.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_070_coreinfo.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_080_ircservers.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_090_backlog_idx.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_100_user_setting.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_110_alter_sender_seq.sql</file>
-    <file>./SQL/PostgreSQL/18/setup_120_alter_messageid_seq.sql</file>
-    <file>./SQL/PostgreSQL/18/update_backlog_bufferid.sql</file>
-    <file>./SQL/PostgreSQL/18/update_buffer_lastseen.sql</file>
-    <file>./SQL/PostgreSQL/18/update_buffer_markerlinemsgid.sql</file>
-    <file>./SQL/PostgreSQL/18/update_buffer_name.sql</file>
-    <file>./SQL/PostgreSQL/18/update_buffer_persistent_channel.sql</file>
-    <file>./SQL/PostgreSQL/18/update_buffer_set_channel_key.sql</file>
-    <file>./SQL/PostgreSQL/18/update_identity.sql</file>
-    <file>./SQL/PostgreSQL/18/update_network_connected.sql</file>
-    <file>./SQL/PostgreSQL/18/update_network_set_awaymsg.sql</file>
-    <file>./SQL/PostgreSQL/18/update_network_set_usermode.sql</file>
-    <file>./SQL/PostgreSQL/18/update_network.sql</file>
-    <file>./SQL/PostgreSQL/18/update_username.sql</file>
-    <file>./SQL/PostgreSQL/18/update_userpassword.sql</file>
-    <file>./SQL/PostgreSQL/18/update_user_setting.sql</file>
     <file>./SQL/PostgreSQL/18/upgrade_000_alter_ircserver_add_sslverify.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_backlog_by_uid.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_backlog_for_buffer.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_backlog_for_network.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_buffer_for_bufferid.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_buffers_by_uid.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_buffers_for_network.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_identity.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_ircservers_for_network.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_networks_by_uid.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_network.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_nicks.sql</file>
+    <file>./SQL/PostgreSQL/19/delete_quasseluser.sql</file>
+    <file>./SQL/PostgreSQL/19/insert_buffer.sql</file>
+    <file>./SQL/PostgreSQL/19/insert_identity.sql</file>
+    <file>./SQL/PostgreSQL/19/insert_message.sql</file>
+    <file>./SQL/PostgreSQL/19/insert_network.sql</file>
+    <file>./SQL/PostgreSQL/19/insert_nick.sql</file>
+    <file>./SQL/PostgreSQL/19/insert_quasseluser.sql</file>
+    <file>./SQL/PostgreSQL/19/insert_sender.sql</file>
+    <file>./SQL/PostgreSQL/19/insert_server.sql</file>
+    <file>./SQL/PostgreSQL/19/insert_user_setting.sql</file>
+    <file>./SQL/PostgreSQL/19/migrate_write_backlog.sql</file>
+    <file>./SQL/PostgreSQL/19/migrate_write_buffer.sql</file>
+    <file>./SQL/PostgreSQL/19/migrate_write_identity_nick.sql</file>
+    <file>./SQL/PostgreSQL/19/migrate_write_identity.sql</file>
+    <file>./SQL/PostgreSQL/19/migrate_write_ircserver.sql</file>
+    <file>./SQL/PostgreSQL/19/migrate_write_network.sql</file>
+    <file>./SQL/PostgreSQL/19/migrate_write_quasseluser.sql</file>
+    <file>./SQL/PostgreSQL/19/migrate_write_sender.sql</file>
+    <file>./SQL/PostgreSQL/19/migrate_write_usersetting.sql</file>
+    <file>./SQL/PostgreSQL/19/select_authuser.sql</file>
+    <file>./SQL/PostgreSQL/19/select_buffer_by_id.sql</file>
+    <file>./SQL/PostgreSQL/19/select_bufferByName.sql</file>
+    <file>./SQL/PostgreSQL/19/select_bufferExists.sql</file>
+    <file>./SQL/PostgreSQL/19/select_buffer_lastseen_messages.sql</file>
+    <file>./SQL/PostgreSQL/19/select_buffer_markerlinemsgids.sql</file>
+    <file>./SQL/PostgreSQL/19/select_buffers_for_network.sql</file>
+    <file>./SQL/PostgreSQL/19/select_buffers.sql</file>
+    <file>./SQL/PostgreSQL/19/select_checkidentity.sql</file>
+    <file>./SQL/PostgreSQL/19/select_connected_networks.sql</file>
+    <file>./SQL/PostgreSQL/19/select_identities.sql</file>
+    <file>./SQL/PostgreSQL/19/select_internaluser.sql</file>
+    <file>./SQL/PostgreSQL/19/select_messagesAllNew.sql</file>
+    <file>./SQL/PostgreSQL/19/select_messagesAll.sql</file>
+    <file>./SQL/PostgreSQL/19/select_messagesNewerThan.sql</file>
+    <file>./SQL/PostgreSQL/19/select_messagesRange.sql</file>
+    <file>./SQL/PostgreSQL/19/select_messages.sql</file>
+    <file>./SQL/PostgreSQL/19/select_network_awaymsg.sql</file>
+    <file>./SQL/PostgreSQL/19/select_networkExists.sql</file>
+    <file>./SQL/PostgreSQL/19/select_networks_for_user.sql</file>
+    <file>./SQL/PostgreSQL/19/select_network_usermode.sql</file>
+    <file>./SQL/PostgreSQL/19/select_nicks.sql</file>
+    <file>./SQL/PostgreSQL/19/select_persistent_channels.sql</file>
+    <file>./SQL/PostgreSQL/19/select_senderid.sql</file>
+    <file>./SQL/PostgreSQL/19/select_servers_for_network.sql</file>
+    <file>./SQL/PostgreSQL/19/select_userid.sql</file>
+    <file>./SQL/PostgreSQL/19/select_user_setting.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_000_quasseluser.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_010_sender.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_020_identity.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_030_identity_nick.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_040_network.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_050_buffer.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_060_backlog.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_070_coreinfo.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_080_ircservers.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_090_backlog_idx.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_100_user_setting.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_110_alter_sender_seq.sql</file>
+    <file>./SQL/PostgreSQL/19/setup_120_alter_messageid_seq.sql</file>
+    <file>./SQL/PostgreSQL/19/update_backlog_bufferid.sql</file>
+    <file>./SQL/PostgreSQL/19/update_buffer_lastseen.sql</file>
+    <file>./SQL/PostgreSQL/19/update_buffer_markerlinemsgid.sql</file>
+    <file>./SQL/PostgreSQL/19/update_buffer_name.sql</file>
+    <file>./SQL/PostgreSQL/19/update_buffer_persistent_channel.sql</file>
+    <file>./SQL/PostgreSQL/19/update_buffer_set_channel_key.sql</file>
+    <file>./SQL/PostgreSQL/19/update_identity.sql</file>
+    <file>./SQL/PostgreSQL/19/update_network_connected.sql</file>
+    <file>./SQL/PostgreSQL/19/update_network_set_awaymsg.sql</file>
+    <file>./SQL/PostgreSQL/19/update_network_set_usermode.sql</file>
+    <file>./SQL/PostgreSQL/19/update_network.sql</file>
+    <file>./SQL/PostgreSQL/19/update_username.sql</file>
+    <file>./SQL/PostgreSQL/19/update_userpassword.sql</file>
+    <file>./SQL/PostgreSQL/19/update_user_setting.sql</file>
+    <file>./SQL/PostgreSQL/19/upgrade_000_alter_network_add_ratelimit_usecustom.sql</file>
+    <file>./SQL/PostgreSQL/19/upgrade_001_alter_network_add_ratelimit_burstsize.sql</file>
+    <file>./SQL/PostgreSQL/19/upgrade_002_alter_network_add_ratelimit_delay.sql</file>
+    <file>./SQL/PostgreSQL/19/upgrade_003_alter_network_add_ratelimit_unlimited.sql</file>
 </qresource>
 </RCC>
index c8f57d1..53601e0 100644 (file)
@@ -658,6 +658,11 @@ void SqliteStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info)
     query.bindValue(":autoreconnectretries", info.autoReconnectRetries);
     query.bindValue(":unlimitedconnectretries", info.unlimitedReconnectRetries ? 1 : 0);
     query.bindValue(":rejoinchannels", info.rejoinChannels ? 1 : 0);
+    // Custom rate limiting
+    query.bindValue(":usecustomessagerate", info.useCustomMessageRate ? 1 : 0);
+    query.bindValue(":messagerateburstsize", info.messageRateBurstSize);
+    query.bindValue(":messageratedelay", info.messageRateDelay);
+    query.bindValue(":unlimitedmessagerate", info.unlimitedMessageRate ? 1 : 0);
     if (info.networkId.isValid())
         query.bindValue(":networkid", info.networkId.toInt());
 }
@@ -854,6 +859,11 @@ QList<NetworkInfo> SqliteStorage::networks(UserId user)
                 net.useSasl = networksQuery.value(16).toInt() == 1 ? true : false;
                 net.saslAccount = networksQuery.value(17).toString();
                 net.saslPassword = networksQuery.value(18).toString();
+                // Custom rate limiting
+                net.useCustomMessageRate = networksQuery.value(19).toInt() == 1 ? true : false;
+                net.messageRateBurstSize = networksQuery.value(20).toUInt();
+                net.messageRateDelay = networksQuery.value(21).toUInt();
+                net.unlimitedMessageRate = networksQuery.value(22).toInt() == 1 ? true : false;
 
                 serversQuery.bindValue(":networkid", net.networkId.toInt());
                 safeExec(serversQuery);
@@ -1887,6 +1897,11 @@ bool SqliteMigrationReader::readMo(NetworkMO &network)
     network.usesasl = value(22).toInt() == 1 ? true : false;
     network.saslaccount = value(23).toString();
     network.saslpassword = value(24).toString();
+    // Custom rate limiting
+    network.usecustommessagerate = value(25).toInt() == 1 ? true : false;
+    network.messagerateburstsize = value(26).toInt();
+    network.messageratedelay = value(27).toUInt();
+    network.unlimitedmessagerate = value(28).toInt() == 1 ? true : false;
     return true;
 }
 
index 7976cca..5ac2795 100644 (file)
@@ -104,6 +104,15 @@ NetworksSettingsPage::NetworksSettingsPage(QWidget *parent)
     connect(ui.reconnectRetries, SIGNAL(valueChanged(int)), this, SLOT(widgetHasChanged()));
     connect(ui.unlimitedRetries, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
     connect(ui.rejoinOnReconnect, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
+
+    // Core features can change during a reconnect.  Always connect these here, delaying testing for
+    // the core feature flag in load().
+    connect(ui.useCustomMessageRate, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
+    connect(ui.messageRateBurstSize, SIGNAL(valueChanged(int)), this, SLOT(widgetHasChanged()));
+    connect(ui.messageRateDelay, SIGNAL(valueChanged(double)), this, SLOT(widgetHasChanged()));
+    connect(ui.unlimitedMessageRate, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
+
+    // Add additional widgets here
     //connect(ui., SIGNAL(), this, SLOT(widgetHasChanged()));
     //connect(ui., SIGNAL(), this, SLOT(widgetHasChanged()));
 
@@ -158,10 +167,38 @@ void NetworksSettingsPage::save()
 void NetworksSettingsPage::load()
 {
     reset();
+
+    // Handle UI dependent on core feature flags here
+    if (Client::coreFeatures() & Quassel::CustomRateLimits) {
+        // Custom rate limiting supported, allow toggling
+        ui.useCustomMessageRate->setEnabled(true);
+        // Reset tooltip to default.
+        ui.useCustomMessageRate->setToolTip(QString("%1").arg(
+                                          tr("<p>Override default message rate limiting.</p>"
+                                             "<p><b>Setting limits too low may get you disconnected"
+                                             " from the server!</b></p>")));
+        // If changed, update the message below!
+    } else {
+        // Custom rate limiting not supported, disallow toggling
+        ui.useCustomMessageRate->setEnabled(false);
+        // Split up the message to allow re-using translations:
+        // [Original tool-tip]
+        // [Bold 'does not support feature' message]
+        // [Specific version needed and feature details]
+        ui.useCustomMessageRate->setToolTip(QString("%1<br/><b>%2</b><br/>%3").arg(
+                                          tr("<p>Override default message rate limiting.</p>"
+                                             "<p><b>Setting limits too low may get you disconnected"
+                                             " from the server!</b></p>"),
+                                          tr("Your Quassel core does not support this feature"),
+                                          tr("You need a Quassel core v0.13.0 or newer in order to "
+                                          "modify message rate limits.")));
+    }
+
     foreach(NetworkId netid, Client::networkIds()) {
         clientNetworkAdded(netid);
     }
     ui.networkList->setCurrentRow(0);
+
     setChangedState(false);
 }
 
@@ -526,6 +563,14 @@ void NetworksSettingsPage::displayNetwork(NetworkId id)
         ui.reconnectRetries->setValue(info.autoReconnectRetries);
         ui.unlimitedRetries->setChecked(info.unlimitedReconnectRetries);
         ui.rejoinOnReconnect->setChecked(info.rejoinChannels);
+        // Custom rate limiting
+        ui.unlimitedMessageRate->setChecked(info.unlimitedMessageRate);
+        // Set 'ui.useCustomMessageRate' after 'ui.unlimitedMessageRate' so if the latter is
+        // disabled, 'ui.messageRateDelayFrame' will remain disabled.
+        ui.useCustomMessageRate->setChecked(info.useCustomMessageRate);
+        ui.messageRateBurstSize->setValue(info.messageRateBurstSize);
+        // Convert milliseconds (integer) into seconds (double)
+        ui.messageRateDelay->setValue(info.messageRateDelay / 1000.0f);
     }
     else {
         // just clear widgets
@@ -575,6 +620,12 @@ void NetworksSettingsPage::saveToNetworkInfo(NetworkInfo &info)
     info.autoReconnectRetries = ui.reconnectRetries->value();
     info.unlimitedReconnectRetries = ui.unlimitedRetries->isChecked();
     info.rejoinChannels = ui.rejoinOnReconnect->isChecked();
+    // Custom rate limiting
+    info.useCustomMessageRate = ui.useCustomMessageRate->isChecked();
+    info.messageRateBurstSize = ui.messageRateBurstSize->value();
+    // Convert seconds (double) into milliseconds (integer)
+    info.messageRateDelay = static_cast<quint32>((ui.messageRateDelay->value() * 1000));
+    info.unlimitedMessageRate = ui.unlimitedMessageRate->isChecked();
 }
 
 
index 5fa1c46..5b93160 100644 (file)
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>510</width>
-    <height>505</height>
+    <height>553</height>
    </rect>
   </property>
   <property name="windowTitle">
               </property>
               <property name="icon">
                <iconset>
-                <normaloff/>
-               </iconset>
+                <normaloff>.</normaloff>.</iconset>
               </property>
              </widget>
             </item>
@@ -373,7 +372,7 @@ Note that Quassel IRC automatically rejoins channels, so /join will rarely be ne
          <attribute name="title">
           <string>Connection</string>
          </attribute>
-         <layout class="QVBoxLayout" name="verticalLayout_8">
+         <layout class="QVBoxLayout" name="verticalLayout_9">
           <item>
            <widget class="QGroupBox" name="autoReconnect">
             <property name="enabled">
@@ -496,6 +495,154 @@ Note that Quassel IRC automatically rejoins channels, so /join will rarely be ne
             </layout>
            </widget>
           </item>
+          <item>
+           <widget class="QGroupBox" name="useCustomMessageRate">
+            <property name="enabled">
+             <bool>true</bool>
+            </property>
+            <property name="toolTip">
+             <string notr="true">Tooltip not yet loaded - to modify tooltip, edit NetworksSettingsPage::load()</string>
+            </property>
+            <property name="title">
+             <string>Use Custom Rate Limits</string>
+            </property>
+            <property name="checkable">
+             <bool>true</bool>
+            </property>
+            <property name="checked">
+             <bool>false</bool>
+            </property>
+            <layout class="QVBoxLayout" name="verticalLayout_8">
+             <item>
+              <layout class="QHBoxLayout" name="horizontalLayout_6">
+               <item>
+                <widget class="QLabel" name="label_11">
+                 <property name="text">
+                  <string>Max. messages at once:</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <widget class="QSpinBox" name="messageRateBurstSize">
+                 <property name="toolTip">
+                  <string>Maximum number of messages to send without any delays</string>
+                 </property>
+                 <property name="minimum">
+                  <number>1</number>
+                 </property>
+                 <property name="maximum">
+                  <number>999</number>
+                 </property>
+                 <property name="value">
+                  <number>5</number>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <widget class="QCheckBox" name="unlimitedMessageRate">
+                 <property name="toolTip">
+                  <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disable all rate limiting, e.g. for IRC bridges.&lt;/p&gt;&lt;p&gt;Don't use with most normal networks.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+                 </property>
+                 <property name="text">
+                  <string>Unlimited</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_5">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>40</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+              </layout>
+             </item>
+             <item>
+              <widget class="QFrame" name="messageRateDelayFrame">
+               <property name="frameShape">
+                <enum>QFrame::NoFrame</enum>
+               </property>
+               <property name="frameShadow">
+                <enum>QFrame::Plain</enum>
+               </property>
+               <property name="lineWidth">
+                <number>1</number>
+               </property>
+               <layout class="QVBoxLayout" name="verticalLayout_10">
+                <property name="leftMargin">
+                 <number>0</number>
+                </property>
+                <property name="topMargin">
+                 <number>0</number>
+                </property>
+                <property name="rightMargin">
+                 <number>0</number>
+                </property>
+                <property name="bottomMargin">
+                 <number>0</number>
+                </property>
+                <item>
+                 <layout class="QHBoxLayout" name="horizontalLayout_5">
+                  <item>
+                   <widget class="QLabel" name="label_10">
+                    <property name="text">
+                     <string>Wait</string>
+                    </property>
+                   </widget>
+                  </item>
+                  <item>
+                   <widget class="QDoubleSpinBox" name="messageRateDelay">
+                    <property name="toolTip">
+                     <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Delay between messages after the maximum number of undelayed messages have been sent.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+                    </property>
+                    <property name="suffix">
+                     <string> s</string>
+                    </property>
+                    <property name="minimum">
+                     <double>0.010000000000000</double>
+                    </property>
+                    <property name="singleStep">
+                     <double>0.100000000000000</double>
+                    </property>
+                    <property name="value">
+                     <double>2.200000000000000</double>
+                    </property>
+                   </widget>
+                  </item>
+                  <item>
+                   <widget class="QLabel" name="label_13">
+                    <property name="text">
+                     <string>between future messages</string>
+                    </property>
+                   </widget>
+                  </item>
+                  <item>
+                   <spacer name="horizontalSpacer_4">
+                    <property name="orientation">
+                     <enum>Qt::Horizontal</enum>
+                    </property>
+                    <property name="sizeHint" stdset="0">
+                     <size>
+                      <width>40</width>
+                      <height>20</height>
+                     </size>
+                    </property>
+                   </spacer>
+                  </item>
+                 </layout>
+                </item>
+               </layout>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
           <item>
            <spacer name="verticalSpacer_3">
             <property name="orientation">
@@ -504,7 +651,7 @@ Note that Quassel IRC automatically rejoins channels, so /join will rarely be ne
             <property name="sizeHint" stdset="0">
              <size>
               <width>20</width>
-              <height>79</height>
+              <height>1</height>
              </size>
             </property>
            </spacer>
@@ -810,8 +957,16 @@ Unless you *really* know what you do, leave this as ISO-8859-1!</string>
   <tabstop>reconnectRetries</tabstop>
   <tabstop>unlimitedRetries</tabstop>
   <tabstop>rejoinOnReconnect</tabstop>
+  <tabstop>useCustomMessageRate</tabstop>
+  <tabstop>messageRateBurstSize</tabstop>
+  <tabstop>unlimitedMessageRate</tabstop>
+  <tabstop>messageRateDelay</tabstop>
+  <tabstop>autoIdentify</tabstop>
   <tabstop>autoIdentifyService</tabstop>
   <tabstop>autoIdentifyPassword</tabstop>
+  <tabstop>sasl</tabstop>
+  <tabstop>saslAccount</tabstop>
+  <tabstop>saslPassword</tabstop>
   <tabstop>useCustomEncodings</tabstop>
   <tabstop>sendEncoding</tabstop>
   <tabstop>recvEncoding</tabstop>
@@ -835,5 +990,37 @@ Unless you *really* know what you do, leave this as ISO-8859-1!</string>
     </hint>
    </hints>
   </connection>
+  <connection>
+   <sender>unlimitedMessageRate</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>messageRateBurstSize</receiver>
+   <slot>setDisabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>332</x>
+     <y>392</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>249</x>
+     <y>393</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>unlimitedMessageRate</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>messageRateDelayFrame</receiver>
+   <slot>setDisabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>332</x>
+     <y>392</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>262</x>
+     <y>428</y>
+    </hint>
+   </hints>
+  </connection>
  </connections>
 </ui>