Skip to content

Commit

Permalink
11.39: new server method ->strings_from_cmodes() generates one or mor…
Browse files Browse the repository at this point in the history
…e strings based on named modes. #101.             new configuration options channels:max_modes_per_line and channels:max_modes_per_sline determine how many modes can fit in an outgoing MODE message.
  • Loading branch information
cooper committed Jul 21, 2016
1 parent 2cb208e commit 6eca514
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 4 deletions.
3 changes: 3 additions & 0 deletions INDEV
Original file line number Diff line number Diff line change
Expand Up @@ -3350,3 +3350,6 @@ CHANGES:
38. added new channel method ->handle_modes() which is a low-level applicator of named modes. #77. #101.
status mode blocks should now push user objects to the parameter list. the former [UID, NICK] format is deprecated.
rather than constantly converting between UIDs/nick and objects, mode blocks are always passed objects from now on.

39. new server method ->strings_from_cmodes() generates one or more strings based on named modes. #101.
new configuration options channels:max_modes_per_line and channels:max_modes_per_sline determine how many modes can fit in an outgoing MODE message.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
11.38
11.39
2 changes: 2 additions & 0 deletions etc/default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
automodes = '+ntqo +user +user' # set these modes as users enter channel
invite_must_exist = off # restrict INVITE to existing channels
only_ops_invite = off # restrict INVITE to chanops
max_modes_per_line = 5 # max modes per outgoing MODE message
max_modes_per_sline = 10 # same as above, except for servers

[ channels: fantasy ]

Expand Down
2 changes: 1 addition & 1 deletion modules/ircd.module/channel.module/channel.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"no_bless" : 1,
"package" : "channel",
"preserve_sym" : 1,
"version" : "11.38"
"version" : "11.39"
}
2 changes: 1 addition & 1 deletion modules/ircd.module/server.module/server.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"no_bless" : 1,
"package" : "server",
"preserve_sym" : 1,
"version" : "11.38"
"version" : "11.39"
}
154 changes: 153 additions & 1 deletion modules/ircd.module/server.module/server.pm
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use feature 'switch';
use parent 'Evented::Object';

use utils qw(col v conf notice);
use Scalar::Util qw(looks_like_number);
use Scalar::Util qw(looks_like_number blessed);

our ($api, $mod, $me, $pool);

Expand Down Expand Up @@ -458,6 +458,158 @@ sub cmode_string_difference {
return join ' ', $final_str, @final_p;
}

# convert cmodes to one or more string
#
# $modes changes in the form used by ->do_modes()
# $over_protocol whether to spit out UIDs or nicks
# $split whether to split it up into multiple strings
# $organize whether to alphabetize the output
# $skip_checks for internal use; whether to skip certain sanity checks
#
sub strings_from_cmodes {
my ($server, $modes, $over_protocol, $split, $organize, $skip_checks) = @_;
my @changes;

# $modes has to be an arrayref.
if (!ref $modes || ref $modes ne 'ARRAY') {
L(
"Modes are not in the proper arrayref form; ".
"mode stringification aborted"
);
return;
}

# $modes has to have an even number of elements.
if (@$modes % 2) {
L("Odd number of elements passed; mode stringification aborted");
return;
}

# add each mode to the resultant.
MODE: while (my ($name, $param) = splice @$modes, 0, 2) {

# extract the state.
my $state = 1;
my $first = \substr($name, 0, 1);
if ($$first eq '-' || $$first eq '+') {
$state = $$first eq '+';
$$first = '';
}

# $skip_checks is enabled when the modes were generated internally.
# this is to improve efficiency when we're already certain it's valid.
unless ($skip_checks) {

# find the mode type.
my $type = $me->cmode_type($name);
if (!defined $type || $type == -1) {
L("Mode '$name' is not known to this server; skipped");
next MODE;
}

# if the mode requires a parameter but one was not provided,
# we have no choice but to skip this.
#
# these are returned by ->cmode_takes_parameter: NOT mode types
# 1 = always takes param
# 2 = takes param, but valid if there isn't,
# such as list modes like +b for viewing
#
my $takes = $me->cmode_takes_parameter($name, $state) || 0;
if (!defined $param && $takes == 1) {
L("Mode '$name' is missing a parameter; skipped");
next MODE;
}

}

push @changes, [ $state, $name, $param ];
}

# if $organize is specified, put positive changes first and alphabetize.
@changes = sort {
my $sort;
$sort = -1 if $a->[0] && !$b->[0];
$sort = 1 if !$a->[0] && $b->[0];
$sort // $a->[1] cmp $b->[1]
} @changes if $organize;


# determine how much we can jam into one string.
my $limit =
!$split ? 'inf' : # no limit
$split == 1 ? conf('channels',
$over_protocol ? 'max_modes_per_sline' : 'max_modes_per_line'
) : # config
$split; # custom amount specified as $split

# stringify.
my @strings;
my ($str, @params) = '';
my $current_state = -1;
my $letters = 0;
CHANGE: foreach my $change (@changes) {
my ($state, $name, $param) = @$change;

# start a new string.
if ($letters >= $limit) {
$str = join ' ', $str, @params;
push @strings, $str;
($str, $letters, $current_state, @params) = ('', 0, -1);
}

# the state has changed.
if ($state != $current_state) {
$str .= $state ? '+' : '-';
$current_state = $state;
}

# add the letter.
my $letter = $server->cmode_letter($name) or next CHANGE;
$str .= $letter;
$letters++;

# push the parameter.
push @params, $server->_stringify_cmode_parameter(
$param, $state, $over_protocol
) if defined $param;

}
if (length $str) {
$str = join ' ', $str, @params;
push @strings, $str;
}

return $split && wantarray ? @strings : $strings[0];
}

sub _stringify_cmode_parameter {
my ($server, $param, $state, $over_protocol) = @_;

# already a string.
return $param if !blessed $param;

# for users, use UID or nickname.
if ($param->isa('user')) {
return $server->user_to_uid($param) if $over_protocol;
return $param->name;
}

# for servers, use an SID or name.
if ($param->isa('server')) {
# FIXME: the below is not protocol-safe
return $param->id if $over_protocol;
return $param->name;
}

# fallback to name if available.
if ($param->can('name')) {
return $param->name;
}

return $param;
}

sub is_local { shift == $me }

# sub DESTROY {
Expand Down

0 comments on commit 6eca514

Please sign in to comment.