#!/usr/bin/perl # AdServer (c) RZ-Online GmbH by jo 1999-2009 # Version 1.0 erste eingesetzte Version 7/1999 # Version 1.1 mit dynamischer Verteilung über die Laufzeit bei def. MaxViews # Version 1.2 mit 'strftime'-Option in Link und Bild-Source # Version 1.3 serverunabhängig (.basic.conf-File eingeführt) 9/2000 # Version 1.4 multiprozessfähig, mit Logbuch 10/2000 # Version 1.5 mit Cookie-Sperrliste für PopUps etc. 11/2002 # Version 1.5.1 *alle* Pfade per Konfigurationsdatei einstellbar 2/2003 # Version 1.6 in den Feldern "Name" "Link" und "Bild" kann jetzt die # Zeichenfolge #a-b stehen. Sie wird durch Zufallszahlen von # a bis b ersetzt. # Version 1.7 mit Templates für teilweise (Flash) oder komplette Anzeigendefinition # Version 1.8 Templates auch per SSI-Kommando-Parameter nutzbar # Version 1.9 AdServer kann per shared memory (IPC::Cache) nachfolgenden Aufrufen eine Botschaft # hinterlassen, um z.B. zum Banner passende Skyscraper ("Wallpaper") zu platzieren (uid) # Dazu muss der Sperrvermerk (LOCKT) bei beiden Anzeigen ein Sternchen sein "*". # Cookie-Sperrliste suspendiert, weil Cookies nur mit IFrames genutzt werden können. # Fallback von Superbannern auf 468/234er Paare eingebaut: # (a) Auto Fallback mangels 728er Banner (b) Forced Fallback via Dummy-Anzeige _falback\d*_ # Version 1.9.1 Fallbacks mangels Bedarf auskommenmtiert. (12/08) # Version 1.9.2 adKeyRules zum Umschreiben abgelaufener adKeys eingebaut (1/09) use lib '/usr/online/lib'; use Time::Local; use Time::HiRes qw(gettimeofday); use POSIX qw(strftime); use Logbook; use Jo qw(ReadBasicConfig LockFileT UnlockFile TimeStr GetPID UnEscape); use CGI::Fast; use IPC::Cache; use strict; # Diese globalen Variablen können in 'adserver.basic.conf' im Programmpfad überladen werden my $counterDir; # Verzeichnis der Zähl-Dateien für Views und Clicks. my $configFile; # Konfigurationsdatei my $configTempFile; # nobody's Tabelle ;-) my $baseURL; # URL des WebServers my $cgiURL; # Script-URI dieses Scripts my $adServerLog; # Logbuch-Datei my $transGIF; # transparentes GIF für Dummies # Ende der überladbaren Variablen my $defBlock="ANZEIGEN"; # dieser Block ist obligatorisch. my $userBlock="LASTUSER"; # dieser Block ist obligatorisch. my $adKeys='ADKEYS'; # dieser Block ist obligatorisch. my $makroBlock="RESSORTMAKROS"; # dieser Block ist optional. my $httpHTMLHeader="content-type: text/html \n\n"; my (@anzeigen,%blocks,%makros,%tab,%axel,$now,$nextUpdate,$updatePending); my ($lastUser,$fallbackAdKey)=('??','nationalnews'); my ($log,$cache,%idList,%adKeyRules); # %blocks sind die Blöcke in der Definitionsdatei. # @anzeigen ist die Liste der Anzeigen, also ein Abbild der Zeilen im Block [ANZEIGEN]. # %tab ist ein Hash von Arrays: der Ressort-Streulisten. Diese sind Arrays mit Anzeigen-Indizes (Anzahl=Gewichtung). # %axel ist eine Hilfstabelle mit Anzeigennamen und zugehörigen Indizes zum schnelleren Zugriff auf die Anzeigen via Namen. # $now ist die aktuelle Uhrzeit, die bei jedem FCGI-Schleifendurchlauf ermittelt wird. # $nextUpdate ist die Zeit des nächsten Tabellen- (%tab) Updates. Die Tabelle wird ca. alle 2 Minuten neu aufgebaut. # $lastUser ist der letzte User, der schrieb. Wird wg. unterschiedlichen Domains vom JS übergeben. # [trenner][Anzahl] wird vor Anzeigen[gruppe] gesetzt my @prolog = ( ["",""], ["",""] ); my @epilog = ( ["",""], ["","
"] ); # dahinter " my @einleitung = ( ["
","",""], ["","",""]); # [trenner][Anzahl] wird vor jeder einzelnen Anzeige gesetzt my @ausleitung = ( ["

","


","


"], ["","",""] ); # dahinter my @dummyGrafik= ("",""); my $prg=$0; $prg=~ s/.+?\/([\w-]+\.(:?pl|fcg))$/$1/; my $updateTime=120; my $debug= ($0=~ /\.pl$/) || 0; $SIG{USR1} = sub { $updatePending++ }; sub JavaScript { print "\n"; } sub Log { if (open DAT,">>$counterDir/update.log") { if (LockFileT(\*DAT,0.67)) { print DAT TimeStr()," ",sprintf("%8.8s ",$$),join("\n",@_),"\n"; UnlockFile(\*DAT); } close DAT } } sub EraseNumFromArray { my ($a,$item)=@_; my $i=0; my $j=-1; while ($i<=$#{$a}+1) { # läuft eins über die Arraygrenze hinaus! if ($j>=0) { if (@{$a}[$i] != $item) { splice(@{$a},$j,$i-$j); $i=$j; $j=-1; } } elsif (@{$a}[$i] == $item) { $j=$i } $i++; } } sub Escape2 { my $s=$_[0]; $s=~s/(\;|\,)/sprintf("\$%2.2lx",ord($1))/ge; return $s; } sub UnEscape2 { my $s=$_[0]; $s=~s/\$([0-9a-fA-F]{2})/pack("c",hex($1))/ge; return $s; } sub Signed { return 1 if $_[0] eq "R4Ts95tsa"; print $httpHTMLHeader; JavaScript('alert("Ungültiger Funktionsaufruf des AdServers!");'); return 0; } sub ValidAnz { # testet auf Gültigkeit einer Anzeige my ($index,$anzeige)=@_; my $a=$anzeigen[$index]; my ($startT,$endT); return 0 unless (($a->{NAME} =~ /^_fallback[\w\-]*_$/) or ( ($a->{WERT}>0) and ($a->{PATH}) and ((!$a->{MAXVIEWS}) or ($a->{VIEWS}+$a->{_VIEWS} < $a->{MAXVIEWS})) and (($a->{LINK}) or ($a->{NAME}=~/#\d+-\d+/) or (exists $blocks{$a->{NAME}})) and (($a->{BILD}) or ($a->{NAME}=~/#\d+-\d+/) or (exists $blocks{$a->{NAME}})) ) ); # skip, weil kein Wert, kein Ressort, # Maxviews überschritten, # weder Link noch Block, weder Bild noch Block. # Achtung: hier keine Prüfung bei Random-Block-Anzeigen! if ($a->{STARTT}) { my ($mday,$mon,$year)=split /\./,$a->{STARTT},3; $year-=1900 if $year>1900; $startT=timelocal(0,0,0,$mday,--$mon,$year); return 0 if $now < $startT; } # skip, weil Starttermin noch nicht erreicht if ($a->{ENDT}) { my ($mday,$mon,$year)=split /\./,$a->{ENDT}; $year-=1900 if $year>1900; $endT=timelocal(59,59,23,$mday,--$mon,$year); return 0 if $now > $endT; } # skip, weil Endetermin überschritten if (($startT) and ($endT) and ($a->{MAXVIEWS})) { my $T=$endT-$startT; # gesamte Anzeigen-Laufzeit in Sek. my $t=$now-$startT; # verstrichene " my $e=$a->{MAXVIEWS} * $t / $T; # rechnerische Views bisher return ($a->{VIEWS}+$a->{_VIEWS} > $e) ? -1 : 1; } return 1 } sub ReadCounter { my $name=$_[0]; my ($views,$clicks); if (open(DAT,"<$counterDir/$name.counter")) { ($views,$clicks)=; close DAT; } return (int $views,int $clicks); } sub WriteAllCounters { my $res=1; for (@anzeigen) { if ((($_->{_VIEWS}) or ($_->{_CLICKS})) and ((open(DAT,"+<$counterDir/".$_->{NAME}.".counter")) or (open(DAT,"+>$counterDir/".$_->{NAME}.".counter")))) { if (LockFileT(\*DAT,0.67)) { ($_->{VIEWS},$_->{CLICKS})=; $_->{VIEWS}+=$_->{_VIEWS}; $_->{CLICKS}+=$_->{_CLICKS}; $_->{_VIEWS}=0; $_->{_CLICKS}=0; seek DAT,0,0; print DAT $_->{VIEWS}."\n".$_->{CLICKS}."\n"; UnlockFile(\*DAT); } else { $res=0; } close DAT; } } return $res; } sub Update { my $verbose=$_[0]; $updatePending=0; my $res=ReadConfig() || "o.k."; $log->Write("Update($verbose) with RES=$res (PIDs=".join(',',GetPID()).")"); print $httpHTMLHeader; if ($res ne "o.k.") { JavaScript('alert("FEHLER - AdServer NICHT neu initialisiert:\n'.$res.'");','parent.location.reload(true);') if $verbose; return $res } MakeTable(); JavaScript('alert("AdServer neu initialisiert.");','parent.location.reload(true);') if $verbose; return ""; } sub MakeTable { my ($a,$i,$v,$k,@r); my $index=-1; undef %tab; foreach $a(@anzeigen) { $index++; next if ValidAnz($index)<=0; foreach $v(split /\s*,\s*/,$a->{PATH}) { if ((exists $makros{$v}) and (defined $makros{$v}{RUBRIKEN})) { # Ressort-Makros auflösen for (split /\s*,\s*/,$makros{$v}{RUBRIKEN}) { push @r,$_ } } else { push @r,$v } } for (@r) { push @{$tab{$_}},$index # aktuellen Anzeigenindex einmal merken # Streutabelle jetzt in "putAd" jo 22.11.02 } undef @r; } } sub ReadConfig { return "Zaehlerdatei(en) nicht geschrieben!" if (@anzeigen) and (!WriteAllCounters()); undef %blocks; undef %makros; undef @anzeigen; undef %axel; my $anzLabel; open DAT,"<$configFile" or return "Konfiguration \"$configFile\" nicht gefunden!"; while () { if (s/^\s*\[(\w+)\]/$1/) { # * $anzLabel=$1 } elsif (($anzLabel) and (/\S+/) and not (/^\#/)) { s/[\s\n]*$//; push @{$blocks{$anzLabel}},$_ } } close DAT; return "Konfiguration unvollständig: '$defBlock' Marke fehlt!" unless exists $blocks{$defBlock}; while (my $s=shift @{$blocks{$defBlock}}) { my ($label,$wert,$ressorts,$link,$bild,$startT,$endT,$lockT,$maxViews)=split /\s*;\s*/,$s; $wert=99 if $wert>99; # Maximalgewichtung 99 my ($v,$k)=ReadCounter($label); $link="$baseURL$link" if ($link) and ($link!~ /^https?:\/\//) and ($link!~ /^TEMPLATE\s*=/); $bild="$baseURL$bild" if ($bild) and ($bild!~ /^https?:\/\//) and ($bild!~ /^ADKEY\s*=/); push @anzeigen,{ NAME=>$label, WERT=>$wert, PATH=>$ressorts, LINK=>UnEscape2($link), BILD=>UnEscape2($bild), STARTT=>$startT, ENDT=>$endT, LOCKT=>$lockT, MAXVIEWS=>$maxViews, VIEWS=>$v, CLICKS=>$k, _VIEWS=>0, _CLICKS=>0 }; if ($label=~ s/#(\d+)-(\d+)$//) { # Random-Anweisung für die Hilfstabelle entfernen my ($start,$end)=($1,$2); for (my $i=$start; $i<=$end; $i++) { $axel{$label.$i}=$#anzeigen; # Hilfstabelle (Accelerator) aufbauen } } else { $axel{$label}=$#anzeigen; # Hilfstabelle (Accelerator) aufbauen } } undef $blocks{$defBlock}; my $i=0; while (my $s=shift @{$blocks{$makroBlock}}) { my ($k,$v)=split /\s*=\s*/,$s; $makros{$k}{RUBRIKEN}=$v; $makros{$k}{NUM}=$i++; } undef $blocks{$makroBlock}; $lastUser=shift @{$blocks{$userBlock}}; undef $blocks{$userBlock}; $fallbackAdKey=shift @{$blocks{$adKeys}}; my $adKeyList=shift @{$blocks{$adKeys}}; my @a=split /\s*,\s*/,$adKeyList; while (@a) { my ($k,$v)=(shift @a,shift @a); $adKeyRules{$k} = $v || '' }; undef $blocks{$adKeys}; return ""; } sub WriteConfig { # schreibt temporäre Version auf /var/www/inter my (@zeilen,$s,$skip); # Wrapper kopiert diese open DAT,"<$configFile" or return "Konfiguration $configFile nicht gefunden!"; @zeilen=; close DAT; open DAT,">$configTempFile" or return "Konfiguration $configTempFile nicht zum Schreiben geöffnet!"; for (@zeilen) { next if ($skip) and not (/^\s*\[(\w+)\]/); # weiter bis neue Gruppe beginnt $skip=0; print DAT $_; if (/^\s*\[$userBlock\]/) { print DAT "$lastUser\n\n"; $skip++; } elsif (/^\s*\[$defBlock\]/) { foreach $s(@anzeigen) { print DAT join(";",$s->{NAME},$s->{WERT},$s->{PATH},Escape2($s->{LINK}),Escape2($s->{BILD}),$s->{STARTT},$s->{ENDT},$s->{LOCKT},$s->{MAXVIEWS}),"\n"; } print DAT "\n"; $skip++; } } close DAT; return ""; } #sub EraseNumFromTabs { # alternativ könnte MakeTable() benutzt werden ;-) # my $num=$_[0]; # for (values %tab) { EraseNumFromArray($_,$num) } # for (keys %axel) { undef $axel{$_} if $axel{$_}==$num } z.Zt. nicht erwünscht #} sub BuildTab { my ($adTab,$cookie)=@_; # adTab ist eine Liste mit Anzeigenindizes my (@tmp,$i,$j); # tmp wird die gefilterte und gewichtete Streutabelle AD: foreach $i (@{$adTab}) { # Cookie basierte Sperre suspendiert, weil der AdServer ohne iframes keine Cookies setzen kann, jo 22.11.05 # if ($anzeigen[$i]->{LOCKT}) { # my $ad=$anzeigen[$i]->{NAME}; # for (split /\s*;\s*/,$cookie) { next AD if /^$ad=/ } # } if (($anzeigen[$i]->{NAME} !~ /skyscraper$/) or ($anzeigen[$i]->{LOCKT} ne '*')) { for ($j=0; $j<$anzeigen[$i]->{WERT}; $j++) { push @tmp,$i } } } @{$adTab}=@tmp; return $#tmp+1; } sub HashValue { my $s=shift; my $y=0; for (my $i=0; $i[0]; } else { push @buffer,$prolog[$trenner][$anzahl>1] } my $nowgenau=scalar gettimeofday(); $nowgenau=~ s/\.//; $nowgenau=substr($nowgenau,0,12); my $hashUID=HashValue($uniqueID); if (($uniqueID) and (not $anzeige) and ($path =~ /skyscraper$/i) and ($anzeige=$cache->get($uniqueID))) { $cache->set($uniqueID,undef); $log->Write("Wallpaper-Match (Sky): Name=$anzeige UID=$uniqueID",3) } for ($i=0; $i<$anzahl; $i++) { if ($anzeige) { # Anzeigenname bekannt (Festplatzierung) $n=$axel{$anzeige}; unless ((defined $n) and (ValidAnz($n,$anzeige))) { # Anzeige nicht vorhanden / noch (nicht gültig) $log->Write("Keine festplatzierte Anzeige '$anzeige' verfügbar (Dokument=$filename, \$n=$n)"); next } } elsif (($path) and (exists($tab{$path}))) { @tmp=@{$tab{$path}}; # temporäres Array zuweisen BuildTab(\@tmp,$cookie); # Anzeigen nach Cookie-Sperrliste ausblenden, Rest nach Gewicht expandieren } else { # if ($path =~ /^(\w+)728$/) { # $log->Write('Superbanner gegen 468er+234er austauschen (keine Superbanner definiert)',3); # PutAd($1.'468',$src,2,'',$trenner,$filename,$cookie,$adKey,$addTemplate,$uniqueID); # return # } else { $log->Write("Keine Anzeige für Pfad '$path' verfügbar (Dokument=$filename [$ENV{HTTP_REFERER}])",3); # } next # Tabelle ist leer, keine Anzeige vorgesehen } unless ($anzeige) { $n=$#tmp; if ($n<0) { # if ($path =~ /^(\w+)728$/) { # PutAd($1.'468',$src,2,'',$trenner,$filename,$cookie,$adKey,$addTemplate,$uniqueID); # $log->Write("Superbanner gegen 468+234-Banner ausgetauscht (keine Anzeige für '$path')",3); # return # } else { $log->Write("Keine passende Anzeige verfügbar (Dokument=$filename)",2); # } next # keine Anzeige (mehr) verfügbar } $n=$tmp[int(rand 1+$n)]; # if (($anzeigen[$n]->{NAME} =~ /^_fallback[\w\-]*_$/) and ($path =~ /^(\w+)728$/)) { # PutAd($1.'468',$src,2,'',$trenner,$filename,$cookie,$adKey,$addTemplate,$uniqueID); # $log->Write("Superbanner gegen 468+234-Banner ausgetauscht (fallback erzwungen)",3); # return # } } if (($uniqueID) and ($anzeigen[$n]->{LOCKT} eq '*') and ($anzeigen[$n]->{NAME} =~ /^(.+?)(?:banner|\d+)$/i)) { $cache->set($uniqueID,$1.'skyscraper'); $log->Write("PUT Wallpaper-Banner: $anzeigen[$n]->{NAME} UID=$uniqueID",3) } my $l=($anzahl<2) ? 0 : ($i) ? 2 : 1; push @buffer,$einleitung[$trenner][$l]; my $link=$anzeigen[$n]->{LINK}; my $randNum; if ($link) { if ($link !~ s/^TEMPLATE\s*=//) { # Normalfall: verlinktes Banner my ($src,$masse1,$masse2,$alt)=split(/ +/,$anzeigen[$n]->{BILD},4); $masse1=~ s/^(width|height)=(\d+)/$1=\"$2\"/; $masse2=~ s/^(width|height)=(\d+)/$1=\"$2\"/; $alt="alt=\"Klick!\"" unless $alt=~ /alt\s*=/i; $link=~ s/#(\d+)-(\d+)/$randNum || ($randNum=($1+int(rand $2+1-$1)))/e; $src=~ s/#(\d+)-(\d+)/$randNum || ($randNum=($1+int(rand $2+1-$1)))/e; $link=strftime $link,localtime; $src=strftime $src,localtime; $link=~ s/([\?\&])/'%'.sprintf("%2.2lx",ord $1)/eg; # codieren, falls Parameter in der URL vorhanden sind [7.12.99 jo] push @buffer,""; } else { # Bild in universalem TEMPLATE-Block (aus adserver.conf-Datei) einbauen my @a=@{$blocks{$link}}; my $subst=$anzeigen[$n]->{BILD}; if ((!$subst) or ($subst =~ /^ADKEY(?:\s*=\s*(.+))?$/i)) { if ($adKey) { $subst=$adKey # AdKey aus Anzeigenaufruf (Parameterübergabe [1]) } else { $subst=($1) ? $1 : $fallbackAdKey; # Defaults aus Anzeigendefinition [2] oder Fallback [3] aus DEFAULTADKEY der .config } } $adKey=$adKeyRules{$adKey} if %adKeyRules and (defined $adKeyRules{$adKey}); # neu eingefügt, um abgelaufene AdKeys überschreiben zu können JO 8.1.09 map { # Platzhalter im Block durch Code (aus Feld 'BILD' oder Aufruf-Parameter 'adKey') ersetzen s/%%SRC/$subst/g; s/%%NAME/$anzeigen[$n]->{NAME}/g; s/%%TIMESTAMP/$nowgenau/g; s/%%DOC/$baseURL$filename/g; s/%%ENV\{(\w+)\}/$ENV{$1}/g; s/%%UID/$uniqueID/g; s/%%HASHUID/$hashUID/g; } @a; push @buffer,join("\n",@a); } } else { my $name=$anzeigen[$n]->{NAME}; $name=~ s/#(\d+)-(\d+)/$randNum || ($randNum=($1+int(rand $2+1-$1)))/e; # $log->Write("Block '$name' ausgelost",3); my @a=@{$blocks{$name}}; map { s/%%SRC/$src/g; s/%%TIMESTAMP/$nowgenau/g; s/%%DOC/$baseURL$filename/g; s/%%ENV\{(\w+)\}/$ENV{$1}/g; s/%%UID/$uniqueID/g; s/%%HASHUID/$hashUID/g; } @a; # Platzhalter im Block durch Code ersetzen push @buffer,join("\n",@a); # push @buffer,$dummyGrafik[$path=~ /468$/ || 0] if $anzeigen[$n]->{LOCKT}; zur Zeit suspendiert, jo 22.11.05 } push @buffer,$ausleitung[$trenner][$l]; $anzeigen[$n]->{_VIEWS}++; EraseNumFromArray(\@tmp,$n) if @tmp; if (($trenner) and ($anzahl>1)) { if ($path=~ /468$/) { $path=~ s/468$/234/ } else { $path=~ s/234$/468/ } } } if ($addTemplate) { push @buffer,$blocks{$addTemplate}->[1]; } else { push @buffer,$epilog[$trenner][$anzahl>1] } # print "Set-Cookie: ",$anzeigen[$n]->{NAME},"=set;EXPIRES=",scalar gmtime(time()+$anzeigen[$n]->{LOCKT}*60.),";PATH=/;\n" if $anzeigen[$n]->{LOCKT}; suspendiert, jo 22.11.05 print $httpHTMLHeader,join("\n",@buffer) } sub Click { my ($link,$filename,$doc)=@_; my $n=0; $n=$axel{$filename}; if (defined $n) { if ($link) { $link=~ s/%([\da-f]{2})/chr(hex($1))/eg; # decodieren 7.12.99 jo } else { $link=$anzeigen[$n]->{LINK} # hinterlegten Link nutzen, falls kein anderer mitgeteilt (DEFAULT) 28.11.06 jo } $anzeigen[$n]->{_CLICKS}++; $log->Write("Klick auf Anzeige \"$filename\" LINK=$link DOC=$doc",2); if ($link) { print "Location: $link\n\n\n" } else { print "$httpHTMLHeader\n" } } else # nur bei Ablauf/Streichung einer Anzeige denkbar, seltener Fall { print $httpHTMLHeader,"[Anzeige '$filename' nicht gefunden]\n" } } sub PutData { my $reload=$_[0]; my ($k,$v,@zeilen,@a); my $i=0; for (@anzeigen) { $v=ValidAnz($i++); if ($v>0) { $k="!" } elsif ($v<0) { $k="*" } else { $k=" " } push @zeilen,join ";",$_->{NAME},$_->{WERT},$_->{PATH},Escape2($_->{LINK}),Escape2($_->{BILD}),$_->{STARTT},$_->{ENDT},$_->{LOCKT},$_->{MAXVIEWS},$_->{VIEWS}+$_->{_VIEWS},$_->{CLICKS}+$_->{_CLICKS},$k; } push @a,"var a=new Array();"; $i=0; for (sort @zeilen) { push @a,"a[",$i++,"]=\"$_\";" } push @a,"var r=new Array();"; $i=0; my @tmp=sort { $makros{$a}{NUM} <=> $makros{$b}{NUM} } keys %makros; foreach $k(@tmp) { push @a,"r[",$i++,"]=\"$k\";"; for (split /\s*,\s*/,$makros{$k}{RUBRIKEN}) { push @a,"r[",$i++,"]=\"$_\";"; } } if ($reload) { push @a,"parent.ads_main.Reloaded();" } else { push @a,"parent.ads_main.Init();" } print $httpHTMLHeader; JavaScript(@a); } sub GetData { my $s=$_[0]; my (@a,%t,$label,$wert,$ressorts,$link,$bild,$startT,$endT,$lockT,$maxViews,$v,$k); for (@anzeigen) { # Views und Klicks in %t zwischenspeichern $t{$_->{NAME}} = { VIEWS=>$_->{VIEWS}, CLICKS=>$_->{CLICKS}, _VIEWS=>$_->{_VIEWS}, _CLICKS=>$_->{_CLICKS} } } undef @anzeigen; undef %axel; # Array und Zugriffstabelle wird komplett neu aufgebaut @a=split(/\|/,$$s); for (@a) { ($label,$wert,$ressorts,$link,$bild,$startT,$endT,$lockT,$maxViews)=split /;/; next if $label eq "(neu)"; push @anzeigen,{ NAME=>$label, WERT=>$wert, PATH=>$ressorts, LINK=>UnEscape2($link), BILD=>UnEscape2($bild), STARTT=>$startT, ENDT=>$endT, LOCKT=>$lockT, MAXVIEWS=>$maxViews, VIEWS=>(exists $t{$label}) ? $t{$label}->{VIEWS} : 0, CLICKS=>(exists $t{$label}) ? $t{$label}->{CLICKS} : 0, _VIEWS=>(exists $t{$label}) ? $t{$label}->{_VIEWS} : 0, _CLICKS=>(exists $t{$label}) ? $t{$label}->{_CLICKS} : 0 }; $axel{$label}=$#anzeigen; } undef @a; MakeTable(); $s=WriteConfig(); if ($s) { print $httpHTMLHeader; JavaScript('alert("ACHTUNG!\n'.$s.'");') } else { PutData(1) } } sub GetLastMod { my ($s,$m,$h,$d,$mon,$y)=localtime((stat $configFile)[9]); $mon++; $y+=1900; $s=~s/(^\d$)/0$1/; $m=~s/(^\d$)/0$1/; return "$d.$mon.$y $h:$m:$s von $lastUser"; } sub LastMod { my ($s,$m,$h,$d,$mon,$y)=localtime((stat $configFile)[9]); $mon++; $y+=1900; $s=~s/(^\d$)/0$1/; $m=~s/(^\d$)/0$1/; print $httpHTMLHeader; JavaScript("parent.ads_main.lastMod=\"".GetLastMod()."\";","parent.ads_main.CheckLastMod();"); } sub PrintError { my $error=shift; print $httpHTMLHeader; print "FEHLER - $error\n"; $log->Write($error); } MAIN: { umask 002; ReadBasicConfig(sub{eval $_[0]}); # Optionale Datei zum Ändern der Standard-Konfiguration my $error=ReadConfig(); $cache = new IPC::Cache({ namespace => 'RZOAdServer', expires_in => 15 }); $log=Logbook->new($adServerLog,1,1,2+$debug,10000); srand(time+$$); $log->Write("*** $0 launched (debug=$debug, cache=".($cache!=0).") ***",2); while (my $q=new CGI::Fast) { $now=time(); $error=Update(0) if $updatePending; my $cmd=$q->param('cmd'); if ($now>=$nextUpdate) { # ca. alle zwei Minuten: $nextUpdate=(WriteAllCounters()) ? $now+$updateTime+int(rand 30) : $now+1+int(rand 30) ; # Zaehler wegschreiben MakeTable(); # Tabelle neu aufbauen $log->Write('Mem info before Purge:'.IPC::Cache::SIZE(),2) if $debug; IPC::Cache::PURGE(); # shared Memory aufräumen $log->Write('Mem info after Purge:'.IPC::Cache::SIZE(),2) if $debug; } if (!$cmd) { if ($error) { PrintError($error) } else { if ($q->param('oldMode')) { print $q->header # wird nicht mehr unterstützt } else { PutAd($q->param('path') || '',$q->param('src') || '', $q->param('anzahl') || 1,$q->param('anzeige') || '', $q->param('blank') || 0,$q->param('doc') || $ENV{'DOCUMENT_URI'} || '', $q->param('cookie') || $ENV{'HTTP_COOKIE'}, $q->param('adKey') || '',$q->param('template') || '',$q->param('uid') || '') } } } elsif ($cmd eq 'CLICK') { if ($error) { PrintError($error); } else { Click($q->param('url') || "",$q->param('anzeige'),$q->param('doc') || $ENV{'DOCUMENT_URI'}) } } elsif (($cmd eq 'GETDATA') && (Signed($q->param('sgn')))) { PutData(); } elsif (($cmd eq 'PUTDATA') && (Signed($q->param('sgn')))) { $lastUser=$q->param('user'); GetData(\$q->param('data')); $log->Write("Got new data (master)"); sleep 1; local $SIG{USR1} = 'IGNORE'; kill 'USR1',GetPID(); } elsif ($cmd eq 'LASTMOD') { LastMod() } elsif (($cmd eq 'UPDATE') && (Signed($q->param('sgn')))) { $error=Update(1); local $SIG{USR1} = 'IGNORE'; kill 'USR1', GetPID(); } } $log->Write("DOWN"); }