diff options
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/nickserv.pl | 453 |
1 files changed, 208 insertions, 245 deletions
diff --git a/scripts/nickserv.pl b/scripts/nickserv.pl index 02a5983..ff2738d 100644 --- a/scripts/nickserv.pl +++ b/scripts/nickserv.pl @@ -32,19 +32,20 @@ $VERSION = "1.11"; name => 'nickserv.pl', description => 'This script will authorize you into NickServ.', license => 'GNU General Public License', - url => 'http://irssi.hauwaerts.be/nickserv.pl', + url => 'https://github.com/irssi/scripts.irssi.org/blob/master/scripts/nickserv.pl', + changed => 'Di 17. Jan 19:32:45 CET 2017', ); +my $irssidir = Irssi::get_irssi_dir(); + my @nickservnet = (); -my $nickservnet_file = "nickserv.networks"; +my $nickservnet_file = "$irssidir/nickserv.networks"; my @nickservauth = (); -my $nickservauth_file = "nickserv.auth"; +my $nickservauth_file = "$irssidir/nickserv.auth"; my @nickservpostcmd = (); -my $nickservpostcmd_file = "nickserv.postcmd"; - -my $irssidir = Irssi::get_irssi_dir(); +my $nickservpostcmd_file = "$irssidir/nickserv.postcmd"; my $help = <<EOF; @@ -62,7 +63,7 @@ addnick: Add a new nickname into the NickServ list. addpostcmd: Add a new post auth command for nickname into the NickServ list. delnet: Delete a network from the NickServ list. delnick: Delete a nickname from the NickServ list. -delpostcmd: Deletes all post auth command for nickame. +delpostcmd: Deletes all post auth commands for the given nickame. listnet: Display the contents of the NickServ network list. listnick: Display the contents of the NickServ nickname list. listpostcmd: Display the contents of the NickServ postcmd list. @@ -87,12 +88,12 @@ Irssi::theme_register([ 'nickserv_delusage', '%R>>%n %_NickServ:%_ Insufficient parameters: Usage "%_/NICKSERV delnet ircnet%_".', 'nickserv_delnickusage', '%R>>%n %_NickServ:%_ Insufficient parameters: Usage "%_/NICKSERV delnick ircnet nickname%_".', 'nickserv_delpostcmdusage', '%R>>%n %_NickServ:%_ Insufficient parameters: Usage "%_/NICKSERV delpostcmd ircnet nickname%_".', - 'nickserv_delled', '%R>>%n %_NickServ:%_ Deleted %_$0%_ and his nicknames from the NickServ ircnet list.', - 'nickserv_delled_nick', '%R>>%n %_NickServ:%_ Deleted %_$1%_ from the NickServ list on $0.', + 'nickserv_delled', '%R>>%n %_NickServ:%_ Deleted %_$0%_ and it\'s nicknames and post commands from the NickServ ircnet list.', + 'nickserv_delled_nick', '%R>>%n %_NickServ:%_ Deleted %_$1%_ and it\'s post commands from the NickServ list on $0.', 'nickserv_delled_postcmd', '%R>>%n %_NickServ:%_ Deleted all entries for %_$1%_ from the NickServ postcmd list on $0.', 'nickserv_nfound', '%R>>%n %_NickServ:%_ The NickServ ircnet %_$0%_ could not be found.', 'nickserv_nfound_nick', '%R>>%n %_NickServ:%_ The NickServ nickname %_$0%_ could not be found on $1.', - 'nickserv_nfound_postcmd', '%R>>%n %_NickServ:%_ The NickServ nickname %_$0%_ could not be found on $1.', + 'nickserv_nfound_postcmd', '%R>>%n %_NickServ:%_ The NickServ post commands for nickname %_$1%_ could not be found on $0.', 'nickserv_usage', '%R>>%n %_NickServ:%_ Insufficient parameters: Use "%_/NICKSERV help%_" for further instructions.', 'nickserv_no_net', '%R>>%n %_NickServ:%_ Unknown Irssi ircnet %_$0%_.', 'nickserv_wrong_host', '%R>>%n %_NickServ:%_ Malformed services hostname %_$0%_.', @@ -123,177 +124,163 @@ sub load_nickservnet { my ($file) = @_; - @nickservnet = (); - - if (-e $file) { - local *F; - open(F, "<", $file); - local $/ = "\n"; - - while (<F>) { - chop; - my $new_nsnet = new_nickserv_network(split("\t")); - - if (($new_nsnet->{name} ne "") && ($new_nsnet->{host} ne "")) { - push(@nickservnet, $new_nsnet); - } - } - - close(F); - } + @nickservnet = load_file($file, sub { + my $new_nsnet = new_nickserv_network(@_); + return undef if ($new_nsnet->{name} eq "" || $new_nsnet->{host} eq ""); + return $new_nsnet; + }); } sub save_nickservnet { - my ($file) = @_; - - if (-e $file) { - local *F; - open(F, ">", $file); - - for (my $n = 0; $n < @nickservnet; ++$n) { - print(F join("\t", $nickservnet[$n]->{name}, $nickservnet[$n]->{host}) . "\n"); - } - - close(F); - } else { - create_network_file($file); - save_nickservnet($file); - } -} - -sub create_network_file { - - my ($file) = @_; - - open(F, ">", $file) or die "Can't create $file. Reason: $!"; + save_file($nickservnet_file, \@nickservnet, \&nickservnet_as_list); } sub new_nickserv_network { - my $nsnet = {}; + return { + name => shift, + host => shift + }; +} + +sub nickservnet_as_list { - $nsnet->{name} = shift; - $nsnet->{host} = shift; + my $nickserv_net = shift; - return $nsnet; + return ( + $nickserv_net->{name}, + $nickserv_net->{host} + ); } sub load_nickservnick { my ($file) = @_; - @nickservauth = (); + @nickservauth = load_file($file, sub { + my $new_nsnick = new_nickserv_nick(@_); - if (-e $file) { - local *F; - open(F, "<" ,$file); - local $/ = "\n"; + return undef if ($new_nsnick->{ircnet} eq "" || $new_nsnick->{nick} eq "" || $new_nsnick->{pass} eq ""); + return $new_nsnick; + }); +} - while (<F>) { - chop; - my $new_nsnick = new_nickserv_nick(split("\t")); - - if (($new_nsnick->{ircnet} ne "") && ($new_nsnick->{nick} ne "") && ($new_nsnick->{pass} ne "")) { - push(@nickservauth, $new_nsnick); - } - } - - close(F); - } +sub save_nickservnick { + + save_file($nickservauth_file, \@nickservauth, \&nickserv_nick_as_list); } -sub load_nickservpostcmd { - my ($file) = @_; +sub new_nickserv_nick { - @nickservpostcmd = (); + return { + ircnet => shift, + nick => shift, + pass => shift + }; +} - if (-e $file) { - local *F; - open(F, "<" ,$file); - local $/ = "\n"; +sub nickserv_nick_as_list { - while (<F>) { - chop; - my $new_postcmd = new_postcmd(split("\t")); - - if (($new_postcmd->{ircnet} ne "") && ($new_postcmd->{nick} ne "") && ($new_postcmd->{postcmd} ne "")) { - push(@nickservpostcmd, $new_postcmd); - } - } - - close(F); - } + my $nickserv_nick = shift; + return ( + $nickserv_nick->{ircnet}, + $nickserv_nick->{nick}, + $nickserv_nick->{pass} + ); } -sub save_nickservnick { +sub load_nickservpostcmd { + my ($file) = @_; - if (-e $file) { - local *F; - open(F, ">", $file); + @nickservpostcmd = load_file($file, sub { + my $new_postcmd = new_postcmd(@_); - for (my $n = 0; $n < @nickservauth; ++$n) { - print(F join("\t", $nickservauth[$n]->{ircnet}, $nickservauth[$n]->{nick}, $nickservauth[$n]->{pass}) . "\n"); - } - - close(F); - } else { - create_save_file($file); - save_nickservnick($file); - } + return undef if ($new_postcmd->{ircnet} eq "" || $new_postcmd->{nick} eq "" || $new_postcmd->{postcmd} eq ""); + return $new_postcmd; + }); } sub save_nickservpostcmd { - my ($file) = @_; - if (-e $file) { - local *F; - open(F, ">", $file); + save_file($nickservpostcmd_file, \@nickservpostcmd, \&postcmd_as_list); +} - for (my $n = 0; $n < @nickservpostcmd; ++$n) { - print(F join("\t", $nickservpostcmd[$n]->{ircnet}, $nickservpostcmd[$n]->{nick}, $nickservpostcmd[$n]->{postcmd}) . "\n"); - } - - close(F); - } else { - create_save_file($file); - save_nickservpostcmd($file); - } +sub new_postcmd { + + return { + ircnet => shift, + nick => shift, + postcmd => shift + }; } -sub create_save_file { - my ($file) = @_; - my $umask = umask 0077; # save old umask - open(F, ">", $file) or die "Can't create $file. Reason: $!"; - close(F); - umask $umask; +sub postcmd_as_list { + my $postcmd = shift; + + return ( + $postcmd->{ircnet}, + $postcmd->{nick}, + $postcmd->{postcmd} + ); } -sub new_nickserv_nick { +# file: filename to be read +# parse_line_fn: receives array of entries of a single line as input, should +# return parsed data object or undef in the data is incomplete +# returns: parsed data array +sub load_file { + + my ($file, $parse_line_fn) = @_; + my @parsed_data = (); - my $nsnick = {}; + if (-e $file) { + open(my $fh, "<", $file); + local $/ = "\n"; + + while (<$fh>) { + chomp; + my $data = $parse_line_fn->(split("\t")); + push(@parsed_data, $data) if $data; + } - $nsnick->{ircnet} = shift; - $nsnick->{nick} = shift; - $nsnick->{pass} = shift; + close($fh); + } - return $nsnick; + return @parsed_data; } -sub new_postcmd { +# file: filename to be written, is created accessable only by the user +# data_ref: array ref of data entries +# serialize_fn: receives a data reference and should return an array or tuples +# for that data that will be serialized into one line +sub save_file { + + my ($file, $data_ref, $serialize_fn) = @_; + + create_private_file($file) unless -e $file; + + open(my $fh, ">", $file) or die "Can't create $file. Reason: $!"; - my $nspostcmd = {}; + for my $data (@$data_ref) { + print($fh join("\t", $serialize_fn->($data)), "\n"); + } + + close($fh); +} - $nspostcmd->{ircnet} = shift; - $nspostcmd->{nick} = shift; - $nspostcmd->{postcmd} = shift; +sub create_private_file { - return $nspostcmd; + my ($file) = @_; + my $umask = umask 0077; # save old umask + open(my $fh, ">", $file) or die "Can't create $file. Reason: $!"; + close($fh); + umask $umask; } sub add_nickname { - + my ($network, $nickname, $password) = split(" ", $_[0], 3); my ($correct_network, $correct_nickname); @@ -301,7 +288,7 @@ sub add_nickname { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_usage_nickname'); return; } - + if ($network) { if (!already_loaded_net($network)) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_not_loaded_net', $network); @@ -310,7 +297,7 @@ sub add_nickname { $correct_network = 1; } } - + if ($nickname) { if (already_loaded_nick($nickname, $network)) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_loaded_nick', $nickname, $network); @@ -319,17 +306,17 @@ sub add_nickname { $correct_nickname = 1; } } - + if ($correct_network && $correct_nickname) { push(@nickservauth, new_nickserv_nick($network, $nickname, $password)); - save_nickservnick("$irssidir/$nickservauth_file"); - + save_nickservnick(); + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'saved_nickname', $network, $nickname); } } sub add_postcmd { - + my ($network, $nickname, $postcmd) = split(" ", $_[0], 3); my ($correct_network, $correct_nickname); @@ -337,7 +324,7 @@ sub add_postcmd { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_usage_postcmd'); return; } - + if ($network) { if (!already_loaded_net($network)) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_not_loaded_net', $network); @@ -346,7 +333,7 @@ sub add_postcmd { $correct_network = 1; } } - + if ($nickname) { if (!already_loaded_nick($nickname, $network)) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_not_loaded_nick', $nickname, $network); @@ -355,28 +342,28 @@ sub add_postcmd { $correct_nickname = 1; } } - + if ($correct_network && $correct_nickname) { push(@nickservpostcmd, new_postcmd($network, $nickname, $postcmd)); - save_nickservpostcmd("$irssidir/$nickservpostcmd_file"); - + save_nickservpostcmd(); + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'saved_postcmd', $network, $nickname, $postcmd); } } sub add_network { - + my ($network, $hostname) = split(" ", $_[0], 2); my ($correct_net, $correct_host); - + if ($network eq "" || $hostname eq "") { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_usage_network'); return; } - + if ($network) { my ($ircnet) = Irssi::chatnet_find($network); - + if (!$ircnet) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_no_net', $network); return; @@ -387,7 +374,7 @@ sub add_network { $correct_net = 1; } } - + if ($hostname) { if ($hostname !~ /^[.+a-zA-Z0-9_-]{1,}@[.+a-zA-Z0-9_-]{1,}$/) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_wrong_host', $hostname); @@ -396,11 +383,11 @@ sub add_network { $correct_host = 1; } } - + if ($correct_net && $correct_host) { push(@nickservnet, new_nickserv_network($network, $hostname)); - save_nickservnet("$irssidir/$nickservnet_file"); - + save_nickservnet(); + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'saved_network', $network, $hostname); } } @@ -408,56 +395,32 @@ sub add_network { sub already_loaded_net { my ($ircnet) = @_; - my $loaded = check_loaded_net($ircnet); - - if ($loaded > -1) { - return 1; - } - - return 0; -} - -sub check_loaded_net { - - my ($ircnet) = @_; $ircnet = lc($ircnet); - for (my $loaded = 0; $loaded < @nickservnet; ++$loaded) { - return $loaded if (lc($nickservnet[$loaded]->{name}) eq $ircnet); + for my $loaded (@nickservnet) { + return 1 if (lc($loaded->{name}) eq $ircnet); } - - return -1; + + return 0; } sub already_loaded_nick { - my ($nickname, $network) = @_; - my $loaded = check_loaded_nick($nickname, $network); - - if ($loaded > -1) { - return 1; - } - - return 0 -} -sub check_loaded_nick { - - my ($nickname, $network) = @_; - $nickname = lc($nickname); $network = lc($network); - - for (my $loaded = 0; $loaded < @nickservauth; ++$loaded) { - return $loaded if (lc($nickservauth[$loaded]->{nick}) eq $nickname && lc ($nickservauth[$loaded]->{ircnet}) eq $network); + + for my $loaded (@nickservauth) { + return 1 if (lc($loaded->{nick}) eq $nickname && + lc($loaded->{ircnet}) eq $network); } - - return -1; + + return 0; } sub list_net { - + if (@nickservnet == 0) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'network_empty'); } else { @@ -470,7 +433,7 @@ sub list_net { } sub list_nick { - + if (@nickservauth == 0) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickname_empty'); } else { @@ -483,7 +446,7 @@ sub list_nick { } sub list_postcmd { - + if (@nickservpostcmd == 0) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'postcmd_empty'); } else { @@ -496,7 +459,7 @@ sub list_postcmd { } sub nickserv_notice { - + my ($server, $data, $nick, $address) = @_; my ($target, $text) = $data =~ /^(\S*)\s:(.*)/; @@ -505,14 +468,14 @@ sub nickserv_notice { if ($text =~ /^(?:If this is your nickname, type|Please identify via|Type) \/msg NickServ (?i:identify)/ || $text =~ /^This nickname is registered and protected. If it is your/ || $text =~ /This nickname is registered\. Please choose a different nickname/) { my $password = get_password($server->{tag}, $server->{nick}); - + if ($password == -1) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'password_request', $server->{tag}); Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_nfound_nick', $server->{nick}, $server->{tag}); Irssi::signal_stop(); return; } - + Irssi::signal_stop(); Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'password_request', $server->{tag}); $server->command("^MSG NickServ IDENTIFY $password"); @@ -564,34 +527,38 @@ sub nickserv_notice { } sub run_postcmds { - my ($server, $tag, $nick) = @_; - return unless scalar @nickservpostcmd; # there's nothing to save + my ($server, $ircnet, $nick) = @_; + return if @nickservpostcmd == 0; for my $cmd (@nickservpostcmd) { - if ($tag eq $cmd->{ircnet} && $nick eq $cmd->{nick} && $cmd->{postcmd}) { + if ($ircnet eq $cmd->{ircnet} && + $nick eq $cmd->{nick} && + $cmd->{postcmd}) { $server->command($cmd->{postcmd}); } } } sub is_nickserv { - + my ($net, $host) = @_; for (my $loaded = 0; $loaded < @nickservnet; ++$loaded) { - return 1 if (lc($nickservnet[$loaded]->{name}) eq lc($net) && lc($nickservnet[$loaded]->{host}) eq lc($host)); + return 1 if (lc($nickservnet[$loaded]->{name}) eq lc($net) && + lc($nickservnet[$loaded]->{host}) eq lc($host)); } return 0; } sub get_password { - + my ($ircnet, $nick) = @_; - + for (my $loaded = 0; $loaded < @nickservauth; ++$loaded) { - return $nickservauth[$loaded]->{pass} if (lc($nickservauth[$loaded]->{ircnet}) eq lc($ircnet) && lc($nickservauth[$loaded]->{nick}) eq lc($nick)); + return $nickservauth[$loaded]->{pass} if (lc($nickservauth[$loaded]->{ircnet}) eq lc($ircnet) && + lc($nickservauth[$loaded]->{nick}) eq lc($nick)); } - + return -1; } @@ -610,47 +577,54 @@ sub del_network { $ircnetindex = 1; } } - + if ($ircnetindex) { @nickservnet = grep {lc($_->{name}) ne lc($ircnet)} @nickservnet; @nickservauth = grep {lc($_->{ircnet}) ne lc($ircnet)} @nickservauth; + @nickservpostcmd = grep {lc($_->{ircnet}) ne lc($ircnet)} @nickservpostcmd; Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_delled', $ircnet); - save_nickservnet("$irssidir/$nickservnet_file"); - save_nickservnick("$irssidir/$nickservauth_file"); + save_nickservnet(); + save_nickservnick(); + save_nickservpostcmd(); } else { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_nfound', $ircnet); } } sub del_nickname { - + my ($ircnet, $nickname) = split(" ", $_[0], 2); my ($nickindex); - + if ($ircnet eq "" || $nickname eq "") { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_delnickusage'); return; } for (my $index = 0; $index < @nickservauth; ++$index) { - if (lc($nickservauth[$index]->{ircnet}) eq lc($ircnet) && lc($nickservauth[$index]->{nick}) eq lc($nickname)) { + if (lc($nickservauth[$index]->{ircnet}) eq lc($ircnet) && + lc($nickservauth[$index]->{nick}) eq lc($nickname)) { $nickindex = splice(@nickservauth, $index, 1); - } + } } if ($nickindex) { + @nickservpostcmd = grep {lc($_->{ircnet}) ne lc($ircnet) || + lc($_->{nick}) ne lc($nickname)} + @nickservpostcmd; + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_delled_nick', $ircnet, $nickname); - save_nickservnick("$irssidir/$nickservauth_file"); + save_nickservnick(); + save_nickservpostcmd(); } else { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_nfound_nick', $ircnet, $nickname); } } sub del_postcmd { - + my ($ircnet, $nickname) = split(" ", $_[0], 2); - my ($nickindex); - + if ($ircnet eq "" || $nickname eq "") { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_delpostcmdusage'); return; @@ -662,17 +636,17 @@ sub del_postcmd { if ($size_before != $size_after) { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_delled_postcmd', $ircnet, $nickname); - save_nickservpostcmd("$irssidir/$nickservpostcmd_file"); + save_nickservpostcmd(); } else { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_nfound_postcmd', $ircnet, $nickname); } } sub nickserv_runsub { - + my ($data, $server, $item) = @_; $data =~ s/\s+$//g; - + if ($data) { Irssi::command_runsub('nickserv', $data, $server, $item); } else { @@ -680,42 +654,31 @@ sub nickserv_runsub { } } -load_nickservnet("$irssidir/$nickservnet_file"); -load_nickservnick("$irssidir/$nickservauth_file"); -load_nickservpostcmd("$irssidir/$nickservpostcmd_file"); +load_nickservnet($nickservnet_file); +load_nickservnick($nickservauth_file); +load_nickservpostcmd($nickservpostcmd_file); Irssi::command_bind('nickserv', 'nickserv_runsub'); Irssi::command_bind('ns', 'nickserv_runsub'); -Irssi::command_bind('nickserv addnet', 'add_network'); -Irssi::command_bind('ns addnet', 'add_network'); - -Irssi::command_bind('nickserv addnick', 'add_nickname'); -Irssi::command_bind('ns addnick', 'add_nickname'); - -Irssi::command_bind('nickserv addpostcmd', 'add_postcmd'); -Irssi::command_bind('ns addpostcmd', 'add_postcmd'); - -Irssi::command_bind('nickserv listnet', 'list_net'); -Irssi::command_bind('ns listnet', 'list_net'); - -Irssi::command_bind('nickserv listnick', 'list_nick'); -Irssi::command_bind('ns listnick', 'list_nick'); - -Irssi::command_bind('nickserv listpostcmd', 'list_postcmd'); -Irssi::command_bind('ns listpostcmd', 'list_postcmd'); - -Irssi::command_bind('nickserv delnet', 'del_network'); -Irssi::command_bind('ns delnet', 'del_network'); - -Irssi::command_bind('nickserv delnick', 'del_nickname'); -Irssi::command_bind('ns delnick', 'del_nickname'); - -Irssi::command_bind('nickserv delpostcmd', 'del_postcmd'); -Irssi::command_bind('ns delpostcmd', 'del_postcmd'); - Irssi::command_bind('nickserv help' => sub { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_help', $help) }); Irssi::command_bind('ns help' => sub { Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_help', $help) }); +# "command binding" -> "function name" mapping +for my $cmd (( + ['addnet' => 'add_network'], + ['addnick' => 'add_nickname'], + ['addpostcmd' => 'add_postcmd'], + ['listnet' => 'list_net'], + ['listnick' => 'list_nick'], + ['listpostcmd' => 'list_postcmd'], + ['delnet' => 'del_network'], + ['delnick' => 'del_nickname'], + ['delpostcmd' => 'del_postcmd'], +)) { + Irssi::command_bind("nickserv $cmd->[0]", $cmd->[1]); + Irssi::command_bind("ns $cmd->[0]", $cmd->[1]); +} + Irssi::signal_add('event notice', 'nickserv_notice'); Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'nickserv_loaded', $IRSSI{name}, $VERSION, $IRSSI{authors}); |
