## $Id: newgroup.pl 4932 2001-07-19 00:32:56Z rra $ ## ## newgroup control message handler. ## ## Copyright 2001 by Marco d'Itri ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. use strict; sub control_newgroup { my ($par, $sender, $replyto, $site, $action, $log, $approved, $headers, $body) = @_; my ($groupname, $modflag) = @$par; $modflag ||= ''; my $modcmd = $modflag eq 'moderated' ? 'm' : 'y'; my $errmsg; $errmsg= local_checkgroupname($groupname) if defined &local_checkgroupname; if ($errmsg) { $errmsg = checkgroupname($groupname) if $errmsg eq 'DONE'; } if ($errmsg) { if ($log) { logger($log, "skipping newgroup ($errmsg)", $headers, $body); } else { logmsg("skipping newgroup ($errmsg)"); } return; } # Scan active to see what sort of change we are making. open(ACTIVE, $inn::active) or logdie("Cannot open $inn::active: $!"); my @oldgroup; while () { next unless /^(\Q$groupname\E)\s\d+\s\d+\s(\w)/; @oldgroup = split /\s+/; last; } close ACTIVE; my $status; if (@oldgroup) { if ($oldgroup[3] eq 'm' and $modflag ne 'moderated') { $status = 'made unmoderated'; } elsif ($oldgroup[3] ne 'm' and $modflag eq 'moderated') { $status = 'made moderated'; } else { $status = 'no change'; } } elsif (not $approved) { $status = 'unapproved'; } else { $status = 'created'; } if ($action eq 'mail' and $status !~ /no change|unapproved/) { my $mail = sendmail("newgroup $groupname $modcmd $sender"); print $mail <$tempfile") or logdie("Cannot open $tempfile: $!"); my $olddesc = ''; while () { if (/^\Q$name\E\s+(.*)/) { $olddesc = $1; next; } print TEMPFILE $_; } print TEMPFILE "$name\t$desc\n"; close TEMPFILE; close NEWSGROUPS; # install the modified file only if the description has changed if ($desc ne $olddesc) { rename($tempfile, $inn::newsgroups) or logdie("Cannot rename $tempfile: $!"); } else { unlink($tempfile); } unlink("$inn::locks/LOCK.newsgroups", $tempfile); } # Check the group name. This is partially derived from C News. # Some checks are commented out if I think they're too strict or # language-dependent. Your mileage may vary. sub checkgroupname { local $_ = shift; # whole-name checking return 'Empty group name' if /^$/; return 'Whitespace in group name' if /\s/; return 'unsafe group name' if /[\`\/:;]/; return 'Bad dots in group name' if /^\./ or /\.$/ or /\.\./; # return 'Group name does not begin/end with alphanumeric' # if (/^[a-zA-Z0-9].+[a-zA-Z0-9]$/; return 'Group name begins in control. or junk.' if /^(?:junk|control)\./; # return 'Group name too long' if length $_ > 128; my @components = split(/\./); # prevent alt.a.b.c.d.e.f.g.w.x.y.z... return 'Too many components' if $#components > 9; # per-component checking for (my $i = 0; $i <= $#components; $i++) { local $_ = $components[$i]; return 'all-numeric name component' if /^[0-9]+$/; # return 'name component starts with non-alphanumeric' if /^[a-zA-Z0-9]/; # return 'name component does not contain letter' if not /[a-zA-Z]/; return "`all' or `ctl' used as name component" if /^(?:all|ctl)$/; # return 'name component longer than 30 characters' if length $_ > 30; # return 'uppercase letter(s) in name' if /[A-Z]/; return 'illegal character(s) in name' if /[^a-z0-9+_\-.]/; # sigh, c++ etc must be allowed return 'repeated punctuation in name' if /--|__|\+\+./; # return 'repeated component(s) in name' if ($i + 2 <= $#components # and $_ eq $components[$i + 1] and $_ eq $components[$i + 2]); } return ''; } 1;