# NPLIB.pl # Common routines for NewsPro scripts and addons. # Version: 3.7.5 build 29 # See newspro.cgi for license information. # Location of nsettings.cgi. # It is important that your nsettings.cgi is not visible over the Web. # One way of accomplishing it is to move it to a different directory, # and give it another name. You can set the following option to the new # path of nsettings.cgi. Use an absolute path (no trailing slash). # Example: # $nsettingspath = '/usr/home/me/securefiles/newspro-settings-5432.cgi'; $nsettingspath = ''; # Lifespan of cookies, in seconds. # 7776000 seconds is equal to 90 days. $cookieExpLength = 7776000; # END OF SETTINGS ########################################################### # Build number. DO NOT CHANGE! $nplibBuild = 7; require "nplang.pl"; @Abbrev_Week_Days = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); @Abbrev_Months = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'); # Load values for computing times $SEC = 1; $MIN = 60 * $SEC; $HR = 60 * $MIN; $DAY = 24 * $HR; $epoch = (localtime(2*$DAY))[5]; # Allow for bugs near localtime == 0. $YearFix = ((gmtime(946684800))[5] == 100) ? 100 : 0; # Get necessary localtime information into variables. sub BasicDateVars { my $thetime = shift; ($Second,$Minute,$Hour,$Month_Day, $Month,$Year,$Week_Day,$IsDST) = (localtime($thetime))[0,1,2,3,4,5,6,8]; $ActualMonth = $Month + 1; $Year = $Year + 1900; } sub GetTheDate { my $thetime = shift; unless ($thetime) { $thetime = time; if ($NPConfig{'TimeOffset'}) { $thetime += (3600 * $NPConfig{'TimeOffset'}); } } my $thedate = $NPConfig{'DateFormat'}; BasicDateVars($thetime); if ($IsDST == 1) { $Time_Zone = $NPConfig{'Daylight_Time_Zone'}; } else { $Time_Zone = $NPConfig{'Standard_Time_Zone'}; } if ($Second < 10) { $Second = "0$Second"; } if ($Minute < 10) { $Minute = "0$Minute"; } if ($Hour == 0) { if ($NPConfig{'12HourClock'}) {$Hour = "12";} $AMPM = "AM"; } elsif ($Hour < 12) { $AMPM = "AM"; } elsif ($Hour == 12) { $AMPM = "PM"; } else { if ($NPConfig{'12HourClock'}) {$Hour = $Hour - 12;} $AMPM = "PM"; } $Month_Name = $Months[$Month]; $Month_Number = $ActualMonth; $Weekday = $Week_Days[$Week_Day]; $Day = $Month_Day; $thedate =~ s/]+)>/${$1}/gi; return $thedate; } # DoGMTTime: Return date in GMT text format. Used for HTTP expiry dates # WARNING: Does not take time into account, always returns time of 00:00:00 (midnight) sub DoGMTTime { my $thetime = shift; my $thedate; my ($Second,$Minute,$Hour,$Month_Day, $Month,$Year,$Week_Day,$IsDST) = (localtime($thetime))[0,1,2,3,4,5,6,8]; if ($Month_Day < 10) { $Month_Day = "0$Month_Day"; } $Year = $Year + 1900; $thedate = "$Abbrev_Week_Days[$Week_Day], $Month_Day-$Abbrev_Months[$Month]-$Year 00:00:00 GMT"; return $thedate; } # YMDtoUNIX # Turns a date of form 1999-7-13 (July 13, 1999) into UNIX time. # Uses time of midnight. sub YMDtoUNIX { my ($textyear, $textmonth, $textday) = @_; my $unixtime; $textyear = $textyear - 1900; $textmonth = $textmonth - 1; $unixtime = timelocal(0,0,0, $textday, $textmonth, $textyear); return $unixtime; } # WriteConfigInfo: Saves %NPConfig settings to nsettings.cgi sub WriteConfigInfo { my $nsetpath = $nsettingspath; if ($abspath && !$nsetpath) { $nsetpath = "$abspath/nsettings.cgi"; } $nsetpath = 'nsettings.cgi' unless $nsetpath; NPopen('NPCFG', ">$nsetpath"); $NPConfig{'firsttime'} = "no"; foreach $key (keys %NPConfig) { $NPConfig{$key} =~ s/^\s+//; $NPConfig{$key} =~ s/\s+$//; $NPConfig{$key} =~ s/\n//g; $NPConfig{$key} =~ s/\r//g; if ($key ne '' && $NPConfig{$key} ne '') { print NPCFG "$key``x$NPConfig{$key}\n"; } } close(NPCFG); } # ReadConfigInfo: Reads configuration from nsettings.cgi into %NPConfig sub ReadConfigInfo { my $nsetpath = $nsettingspath; if ($abspath && !$nsetpath) { $nsetpath = "$abspath/nsettings.cgi"; } $nsetpath = 'nsettings.cgi' unless $nsetpath; NPopen('NPCFG', $nsetpath); my @npconfig = ; close(NPCFG); foreach $i (@npconfig) { ($varname, $varvalue) = split(/``x/, $i); $varvalue =~ s/^\s+//; $varvalue =~ s/\s+$//; $varvalue =~ s/\n//g; $NPConfig{$varname} = $varvalue; } } # GetDirInfo: Gets the absolute path to the current directory. sub GetDirInfo { eval 'use Cwd; $cwd = cwd();'; unless ($cwd) { $cwd = `pwd`; chomp $pwd; } $dirname = $cwd; return $cwd; } # GetScriptURL: Gets the full URL of newspro.cgi sub GetScriptURL { local ($ret, $perlwarn); $perlwarn = $^W; $^W = 0; $ret = 'http://' . $ENV{'SERVER_NAME'} . ($ENV{'SERVER_PORT'} != 80 ? ":$ENV{'SERVER_PORT'}" : '') . $ENV{'SCRIPT_NAME'}; $^W = $perlwarn; return $ret; } # header: A convenience that returns the full Content-type header. sub header { unless ($HeaderPrinted) { $HeaderPrinted = 1; return "Content-type: text/html\n\n"; } else { return ''; } } # ReadForm: Reads in POSTed form values to %in sub ReadForm { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); my @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; unless($in{$name}) {$in{$name} = $value;} else { $in{$name} .= "|x|$value"; } } } # GetCookies: Reads in HTTP Cookies to %Cookies sub GetCookies { # Load Encode/Decode Data @Cookie_Encode_Chars = ('\%', '\+', '\;', '\,', '\=', '\&', '\:\:', '\s'); %Cookie_Encode_Chars = ('\%', '%25', '\+', '%2B', '\;', '%3B', '\,', '%2C', '\=', '%3D', '\&', '%26', '\:\:', '%3A%3A', '\s', '+'); @Cookie_Decode_Chars = ('\+', '\%3A\%3A', '\%26', '\%3D', '\%2C', '\%3B', '\%2B', '\%25'); %Cookie_Decode_Chars = ('\+', ' ', '\%3A\%3A', '::', '\%26', '&', '\%3D', '=', '\%2C', ',', '\%3B', ';', '\%2B', '+', '\%25', '%'); local($cookie,$value); if ($ENV{'HTTP_COOKIE'}) { foreach (split(/; /,$ENV{'HTTP_COOKIE'})) { ($cookie,$value) = split(/=/); foreach $char (@Cookie_Decode_Chars) { $cookie =~ s/$char/$Cookie_Decode_Chars{$char}/g; $value =~ s/$char/$Cookie_Decode_Chars{$char}/g; } $Cookies{$cookie} = $value; } } } # SetCookies: Prints a HTTP header, setting any cookies passed to it. # WARNING: Must be run after GetCookies sub SetCookies { local(@cookies) = @_; local($cookie,$value,$char); print "Content-type: text/html\n"; while ( ($cookie,$value) = @cookies ) { foreach $char (@Cookie_Encode_Chars) { $cookie =~ s/$char/$Cookie_Encode_Chars{$char}/g; $value =~ s/$char/$Cookie_Encode_Chars{$char}/g; } print 'Set-Cookie: ' . $cookie . '=' . $value . ';'; $exptime = time + $cookieExpLength; $CookieExpires = DoGMTTime($exptime); print ' expires=' . $CookieExpires . ';'; print "\n"; shift(@cookies); shift(@cookies); } print "\n"; $HeaderPrinted = 1; } sub ClearCookies { print "Content-type: text/html\n"; print "Set-Cookie: uname=x; expires=Thu, 03-Feb-2000 00:00:00 GMT;\n"; print "Set-Cookie: pword=x; expires=Thu, 03-Feb-2000 00:00:00 GMT;\n\n"; $HeaderPrinted = 1; } sub query_string { return $ENV{'QUERY_STRING'}; } # Is this news item from a different date than the last one? sub isNewDate { my $isNewDate; my $mdy = "$Month_Day$Month$Year"; if ($isNewDate{$FileName} ne $mdy) { $isNewDate = 1; } else { $isNewDate = 0; } $isNewDate{$FileName} = $mdy; return $isNewDate; } ####### # NEWS DATABASE LOAD/SAVE ###### # Loads newsdat.txt, and reads news data into a variety of places. sub loadND { undef @NewsData; # Get form field information, add certain hard-coded ones (mainly date & time) &SubmitFormFields; my @ffields = @formfields; @ffield_add = ('newstext', 'newsid', 'newstime', 'newsrand', 'newscat'); if (@Addons_loadND_1) { &RunAddons(@Addons_loadND_1); } push(@ffields, @ffield_add); # Declare variables my $newsnum = 0; my $newsline; my $formfield; # Open newsdat.txt &NPopen('NEWSDAT', "$NPConfig{'admin_path'}/newsdat.txt"); $nd_age = (stat(NEWSDAT))[9]; # Begin line-by-line processing of newsdat.txt while () { # Remove ending newline character chomp($_); # Split news item into component variables, using npconfig.pl definition &SplitDataFile($_); # Split the news ID # into component variables. ($newstime, $newsrand, $newscat) = split(/\,/, $newsid); # Remove the news category from the news ID, so that the category can change. $newsid = join(',', $newstime, $newsrand, ''); # Check if we want to use TimeOffset if ($NPConfig{'TimeOffset'}) { $newstime += (3600 * $NPConfig{'TimeOffset'}); } if (@Addons_loadND_2) { &RunAddons(@Addons_loadND_2); } # Go through each defined form field. foreach $formfield (@ffields) { # Load that particular news variable into the appropriate place in @NewsData $NewsData[$newsnum]->{$formfield} = ${$formfield}; } # Establish a hash of news IDs $NewsID{$newsid} = $newsnum; # Increment $newsnum to continue the processing. $newsnum++; } if (@Addons_loadND_3) { &RunAddons(@Addons_loadND_3); } close(NEWSDAT); } # Saves newsdat.txt from @NewsData, which is set by &loadND sub saveND { my $ndentry; my $joinline; my $ff; NPopen('NEWSDAT', ">$NPConfig{'admin_path'}/newsdat.txt"); foreach $ndentry (@NewsData) { # Check if we want to use TimeOffset if ($NPConfig{'TimeOffset'}) { $ndentry->{'newstime'} -= (3600 * $NPConfig{'TimeOffset'}); } $ndentry->{'newsid'} = join(',', $ndentry->{'newstime'}, $ndentry->{'newsrand'}, $ndentry->{'newscat'}); unless ($ndentry eq "del") { foreach $ff (keys %{$ndentry}) { ${$ff} = $ndentry->{$ff}; } $joinline = &JoinDataFile; $joinline =~ s/\n//g; print NEWSDAT "$joinline\n"; } } close(NEWSDAT); } # Gets the news variables for a particular item. sub getNDvar { my $nn = shift; my $ff; foreach $ff (keys %{$NewsData[$nn]}) { ${$ff} = $NewsData[$nn]->{$ff}; } $newsdate = GetTheDate($newstime); } # Gets the news variables for a particular item. # Does not generate date information. sub getNDvar_nodate { my $nn = shift; my $ff; foreach $ff (keys %{$NewsData[$nn]}) { ${$ff} = $NewsData[$nn]->{$ff}; } } sub HTMLescape { my $text = shift; $text =~ s/&/&/g; $text =~ s//>/g; $text =~ s/"/"/g; return $text; } sub PrintSelectValues { my @values = @_; if ($values[0] eq 'same') { shift @values; foreach $i (@values) { print qq~ ~; } } else { my $count = 1; foreach $i (@values) { print qq~ ~; $count++ } } } # Mainly for debugging purposes. Defines a simple NPopen subroutine for cases when a full one isn't being included. sub MiniNPopen { my $npopendef = 'sub NPopen { my ($handle, $file) = @_; my $status = open($handle, $file); return $status; }'; eval $npopendef; } sub PastDaysTime { my $pastdays = shift; &BasicDateVars(time); my $enddate = &YMDtoUNIX($Year, $ActualMonth, $Month_Day); $enddate = $enddate + 86399; my $startdate = $enddate - ($pastdays * 86400); $startdate = $startdate + 1; return ($startdate, $enddate); } # Used to process some .tmpl (HTML Template files) sub ProcessTMPL { my $tmplpath = shift; my $tmplcontent = shift; my $tmpltitle = shift; unless ($tmplpath) { return 0; } unless ($TemplateCache{$tmplpath}) { NPopen(TMPLFILE, $tmplpath); while () { $TemplateCache{$tmplpath} .= $_; } close(TMPLFILE); } my $theresult = $TemplateCache{$tmplpath}; $theresult =~ s///g; $theresult =~ s/\{\[]+)>/${\(HTMLtoText(${$1}))}/g; $theresult =~ s/\{]+)\{[\'\"]([^\s\>\}\'\"]+)[\'\"]\}>/${\(HTMLtoText(${$1}{$2}))}/g; $theresult =~ s/\[]+)\[(\d+)\]>/${\(HTMLtoText(${$1}[$2]))}/g; $theresult =~ s/\{\[]+)>/${$1}/gi; $theresult =~ s/\{]+)\{[\'\"]([^\s\>\}\'\"]+)[\'\"]\}>/${$1}{$2}/g; $theresult =~ s/\[]+)\[(\d+)\]>/${$1}[$2]/g; $theresult =~ s//$tmplcontent/gi; $theresult =~ s//${\(HTMLtoText($tmplcontent))}/g; $theresult =~ s//$tmpltitle/gi; if (@Addons_ProcessTMPL) { &RunAddons(@Addons_ProcessTMPL); } return $theresult; } sub HTMLtoText { my $html = shift; $html =~ s/
/\n/gi; $html =~ s/<\/*(blockquote|ul|li|p)[^<>]*>/\n\n/gi; $html =~ s/\"]+)\"*[^>]*>([\s\S]+?)<\/a>/$2 (link: $1)/gi; $html =~ s/<[^>]+>//g; $html =~ s/\"/"/g; return $html; } # The following three subroutines are from the Perl Time::Local module. sub timegm { $ym = pack(C2, @_[5,4]); $cheat = $cheat{$ym} || &cheat; return -1 if $cheat<0 and $^O ne 'VMS'; $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAY; } sub timelocal { my $t = &timegm; my $tt = $t; my (@lt) = localtime($t); my (@gt) = gmtime($t); if ($t < $DAY and ($lt[5] >= 70 or $gt[5] >= 70 )) { # Wrap error, too early a date # Try a safer date $tt = $DAY; @lt = localtime($tt); @gt = gmtime($tt); } my $tzsec = ($gt[1] - $lt[1]) * $MIN + ($gt[2] - $lt[2]) * $HR; my($lday,$gday) = ($lt[7],$gt[7]); if($lt[5] > $gt[5]) { $tzsec -= $DAY; } elsif($gt[5] > $lt[5]) { $tzsec += $DAY; } else { $tzsec += ($gt[7] - $lt[7]) * $DAY; } $tzsec += $HR if($lt[8]); $time = $t + $tzsec; return -1 if $cheat<0 and $^O ne 'VMS'; @test = localtime($time + ($tt - $t)); $time -= $HR if $test[2] != $_[2]; $time; } sub cheat { $year = $_[5]; $year -= 1900 if $year > 1900; $month = $_[4]; NPdie("Month '$month' out of range 0..11") if $month > 11 || $month < 0; NPdie ("Day '$_[3]' out of range 1..31") if $_[3] > 31 || $_[3] < 1; NPdie("Hour '$_[2]' out of range 0..23") if $_[2] > 23 || $_[2] < 0; NPdie("Minute '$_[1]' out of range 0..59") if $_[1] > 59 || $_[1] < 0; NPdie("Second '$_[0]' out of range 0..59") if $_[0] > 59 || $_[0] < 0; $guess = $^T; @g = gmtime($guess); $year += $YearFix if $year < $epoch; $lastguess = ""; $counter = 0; while ($diff = $year - $g[5]) { NPdie("Can't handle date (".join(", ",@_).")") if ++$counter > 255; $guess += $diff * (363 * $DAY); @g = gmtime($guess); if (($thisguess = "@g") eq $lastguess){ return -1; #date beyond this machine's integer limit } $lastguess = $thisguess; } while ($diff = $month - $g[4]) { NPdie("Can't handle date (".join(", ",@_).")") if ++$counter > 255; $guess += $diff * (27 * $DAY); @g = gmtime($guess); if (($thisguess = "@g") eq $lastguess){ return -1; #date beyond this machine's integer limit } $lastguess = $thisguess; } @gfake = gmtime($guess-1); #still being skeptic if ("@gfake" eq $lastguess){ return -1; #date beyond this machine's integer limit } $g[3]--; $guess -= $g[0] * $SEC + $g[1] * $MIN + $g[2] * $HR + $g[3] * $DAY; $cheat{$ym} = $guess; } 1;