<?php

// diese Seite stellt Orga-Funktionen für die eh1005-Seite zur Verfügung
// (Topics: ~ heißt geplant, - heißt im Bau, + heißt implementiert.)
// + diese Seite liegt im öffentlichen Serverbereich, dieser darf *.php nicht als plain/text ausliefern ;)
// + diese Seite verlangt zwingend https
// + diese Seite fragt ein Authentifikationspasswort ab
// + die Authentifikation läuft nach einer gewissen Zeitspanne (Konstante) ab
// + es dürfen von dieser Seite aus keine anderen orga-spezifischen Seiten aufgerufen werden,
//   da dies eventuell eine Umgehungsmöglichkeit der Authentifizierung zur Folge haben könnte

/*
Struktur dieser Seite:
	+ write HTTP header
	+ recall session
	+ check, defuse and shorten input variables
	+ Prüfung auf https
	 = OK => continue
	 = FAIL => Session-Zwangslogout, Abbruchmeldung und Schlußverarbeitung
	+ Prüfung auf gültigen Loginstatus, inkl. Timeout
	 = OK => continue
	 = FAIL => Authentifizierungsformular, Abbruchmeldung und Schlußverarbeitung
	+ Prüfung auf Wartungsstatus
	 = OK => continue
	 = FAIL => Abbruchmeldung und Schlußverarbeitung
	+ Login
	 = OK => continue
	 = FAIL => Authentifizierungsformular, Abbruchmeldung und Schlußverarbeitung
	+ Verarbeitung der Submit-Aktionen (POST), Anzeige der einzelnen Orga-Formulare
*/


/*
known bugs / todo
  
	- Script rennt in einen Loop-of-Death, wenn man auf einen Button klickt, während die Seite (z.B. wegen dem Fahrplan-Editor) noch nicht fertig aufgebaut war
	 -> Session hängt sich auf.
    
	Dieses Problem taucht auf webtest.hamburg.ccc.de auf, ist aber lokal (auf Rainers Notebook) nicht reproduzierbar.
	 -> php-Version oder "nur" Config-Problem? (deadlock-timeout?)
*/

// this script needs following additional server modules for PHP
// - Database: (e.g. pgsql)

// write HTTP header (anti-cache)
header('Expires: Sun,	31 Dec 1989	23:59:59 GMT');
header('Last-Modified: ' . gmdate('D,	d	M	Y	H:i:s')	.	'	GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0');
header('Pragma:	no-cache');

// recall session
session_cache_limiter('nocache');
session_cache_expire(1);
session_start();

// define general server constants
define( "SERVER_SITE_MAINTENANCE", false );               // maintenance state, set to TRUE to lock this page temporarily
define( "SERVER_LOCALHOST_IP", "127.0.0.1" );             // localhost IP, which does not need authentication
define( "SERVER_PROTOCOL_HTTPS", "HTTPS" );               // HTTPS protocol name

// define server file constants
define( "SERVER_FILE_TEMPLATE", "template.shtml" );       // template file
define( "SERVER_FILE_HEADER", "ssi_site_header.html" );   // SSI file for site header
define( "SERVER_FILE_INTRO", "ssi_site_intro.html" );     // SSI file for site intro
define( "SERVER_FILE_EXTRO", "ssi_site_extro.html" );     // SSI file for site extro
define( "SERVER_FILE_AUTOGEN", "ssi_site_autogen.html" ); // SSI file for site autogeneration warning

define( "SERVER_FILE_ANMELDUNGSLISTE", "anmeldung-data/anmeldung.csv" ); // path and file for registered participants

// pages of Kassenliste will be printed and can be arranged horizontally
// each column contains as much rows as defined by KASSENLISTE_COLUMN_ROWS, after each column of KASSENLISTE_PAGE_COLUMNS a page break will be initiated     
define( "KASSENLISTE_COLUMN_ROWS", 61 );      // number of rows per page in kassenliste, pages will be printed horizontally
define( "KASSENLISTE_PAGE_COLUMNS", 3 );      // number of columns per page in kassenliste, pages will contain those number printed horizontally

// define server file constants
define( "ORGA_DAYS_BREAKFAST", 3 ); //

// define display constants
define( "DISPLAY_TEXT_NONE", " " );
define( "DISPLAY_VALUE_SEPARATOR", chr(9));

// define auth constants
define( "AUTH_TIMEOUT_MINUTES", 20 );                  // idle timeout for login session
define( "AUTH_SERVER_PASS", "[Attrakt.Chaos]*9" );     // login password, [todo:] should be outsourced to an external, secured data file

// init references to $_SESSION variables ($sxxx)
$sbolauthloggedin = & $_SESSION['bolauthloginok'];
$sintauthprevtime = & $_SESSION['intauthprevtime'];

// init session variables
if ( !isset($sbolauthloggedin) ) $sbolauthloggedin = false;
if ( !isset($sintauthprevtime) ) $sintauthprevtime = time();

// init references to $_POST variables ($pxxx)
$pstrauth = & $_POST['txtauth'];
$pbollogin = & $_POST['cmdlogin'];
$pbollogout = & $_POST['cmdlogout'];
$pbolshowanmeldungen = & $_POST['cmdshowanmeldungen'];
$pbolshowkassenliste = & $_POST['cmdshowkassenliste'];

// init post variables (only for mandatory variables, ignore variables submitted by special forms!)
if ( !isset($pstrauth) ) $pstrauth = '';

// init instance variables

$eintritt = array ( 'Normal' => 42,
                    'Mitglied' => 35,
                    'Engel' => 35,
                    'Ermaeszigt' => 35 );

$intnow = time();

$strmsg = '';                     // (status/error/etc.) message to user
$bolauthabort = false;            // auth check will be aborted: show authentication page
$bolauthlogoutnow = false;        // user will be logged out

$strdbfields = array();
$strdbsql = '';
$strdbsql2 = '';
$strdbrow = '';

$strweekdayname = array (1 => 'Sonntag',
                         2 => 'Montag',
                         3 => 'Dienstag',
                         4 => 'Mittwoch',
                         5 => 'Donnerstag',
                         6 => 'Freitag',
                         7 => 'Samstag');

$strvalue = '';
$intindex = 0;
$intcolrotate = 1;
$strinput = '';
$stroutput = '';
$intfileid = 0;
$intcount = 0;

$strauth = '';
$bollogin = false;
$bollogout = false;
$bolshowanmeldungen = false;
$bolshowkassenliste = false;

$strtablehead = '';
$intcolrot = 0;
$boleof = false;
$strdbfields = '';

$bolok = false;                   // action feedback, triggers confirmation if true or warning if false

// function library

function addmsg ($straddmsg)
{
   // adds $straddmsg to $strmsg
   global $strmsg;
   if ( $straddmsg != '' )
   {
      $strmsg = $strmsg . '<li>' . $straddmsg . '</li>';
   }
}

function showmsg ()
{
   // shows messages in $strmsg if available
   global $strmsg, $bolok;
   if ( $strmsg != '' )
   {
      echo'
      <div class="announcement">
         <h1 class="';
         if ( $bolok )
         {
            echo 'confirmation';
         }
         else
         {
         echo 'warning';
         }
         echo '">Feedback</h1>
         <ul>' . $strmsg . '</ul>
      </div>';
      $strmsg = '';
      $bolok = false;
   }
}

// check input variables
//$bollogin = isset( $pbollogin ); // does not work in IE when submitted from within a text field by pressing Return key, reason unknown
$bollogin = ( isset( $pbollogin ) || $pstrauth != '' );  //workaround for IE problem, see line above
$bollogout = ( isset( $pbollogout ) || $bollogin ); //also fire a logout before (re-)login
$bolshowanmeldungen = ( isset( $pbolshowanmeldungen ) );
$bolshowkassenliste = ( isset( $pbolshowkassenliste ) );

// defuse input variables
$strauth = trim( htmlentities( $pstrauth, ENT_QUOTES ));

// shorten input variables
if ( strlen( $strauth ) > 255 ) $strauth = substr( $strauth, 0, 255 ); //cut it, do not modify


$strserverremoteaddr = htmlspecialchars( $_SERVER['REMOTE_ADDR'], ENT_QUOTES );
if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] )) $strserverremoteaddr = htmlspecialchars( $_SERVER['HTTP_X_FORWARDED_FOR'], ENT_QUOTES ); 
$strserverprotohttps = htmlspecialchars( $_SERVER['HTTPS'], ENT_QUOTES );
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] )) $strserverprotohttps = htmlspecialchars( $_SERVER['HTTP_X_FORWARDED_PROTO'], ENT_QUOTES ); 

// localhost darf immer
if ( $strserverremoteaddr != constant("SERVER_LOCALHOST_IP") )
{
   // check protocol
   // check secure connection
   // possible results:
   // - secure => continue
   // - insecure => abort processing and show error message ($strmsg)
   if ( strtolower( $strserverprotohttps ) != 'https' && strtolower( $strserverprotohttps ) != 'on' )
   {
      // insecure connection -> abort
      if ( !$bolauthlogoutnow && $sbolauthloggedin )
      {
         // active login over insecure connection -> force logout
         $bolauthlogoutnow = true;
         addmsg ( 'Du wirst ausgeloggt, da Deine Verbindung unsicher (unverschl&uuml;sselt) geworden ist.' );
      }
      addmsg ( '<a href="https://' . $_SERVER['HTTP_HOST'] . $_SERVER["PHP_SELF"] . '">Dein Verbindungsprotokoll ist HTTP. Bitte verwende f&uuml;r den Orga-Foo Verschl&uuml;sselung mittels HTTPS.</a>' );
      $bolauthabort = true;
      $bolok = false;
   }
   
   // check auth timeout
   // possible results:
   // - auth active => continue
   // - auth timeout => force logout ($bolauthlogoutnow), prepare message ($strmsg) and continue

   if ( $sbolauthloggedin && !$bolauthlogoutnow && ( $intnow > ( $sintauthprevtime + 60 * constant("AUTH_TIMEOUT_MINUTES") )))
    {
       // write message on user authentication expired -> also fire a logout ($bolauthlogoutnow)
       // echo '<div class="warning">Du wurdest ausgeloggt wegen mehr als ' . constant("AUTH_TIMEOUT_MINUTES") . ' Minuten Inaktivit&auml;t.</div><br>';
       $bolauthlogoutnow = true;
       addmsg ( 'Du wirst ausgeloggt wegen mehr als ' . constant("AUTH_TIMEOUT_MINUTES") . ' Minuten Inaktivit&auml;t.' );
    }
   // restart timeout
   $sintauthprevtime = $intnow;
}
else
{
   // localhost ist immer eingeloggt
   $sbolauthloggedin = true;
}

if ( $bolauthlogoutnow || $bollogout )
{
   // logout
   if ( $bolauthlogoutnow )
   {
      addmsg ( 'Du wurdest automatisch ausgeloggt. Bitte gegebenenfalls erneut einloggen.' );
   }
   $sbolauthloggedin = false;
}

// check maintenance state
if ( constant("SERVER_SITE_MAINTENANCE") )
{
   // cancel because of maintenance
   addmsg ( 'Die Orga-Seite ist zurzeit wegen Wartungsarbeiten deaktiviert. Bitte sp&auml;ter wiederkommen. Danke.' );
   $bolauthabort = true;
}

// Well, Checkpoint.
// Now a defined and normal state of checking has been reached.
// Possible states are now:
// - $sbolauthloggedin = true: user has successfully authenticated and is still logged in
// - $sbolauthloggedin = false: user is either not (yet) authenticated or authentication has been expired
// If there were any messages on the way up to here, they are stored as list items (<li>) in variable $strmsg.


// check auth phrase if user tries to login
if ( !$bolauthabort && $bollogin && $strauth > '' )
{
   if ( $strauth == constant("AUTH_SERVER_PASS") )
   {
      // login accepted
      $sbolauthloggedin = true;
   }
   else
   {
      // login failed
      $sbolauthloggedin = false;
      addmsg ( 'Das klappt so nicht. Vermutlich hast Du Dich vertippt.' );
   }
}

// prepare orga page
// assume that all ssi files are there. No, I won't check _that_ explicitely.
// If they ain't there, the user gets a partial page. ...so what? ;o)

readfile( constant("SERVER_FILE_HEADER") );
echo'
<!-- begin of specific page header -->
   <title>Easterhegg - Orga</title>
<!-- end of specific page header -->';

readfile( constant("SERVER_FILE_INTRO") );
echo'
<!-- begin of specific page content -->
';

echo
'
    <div id="main">
    
      <h1>interne Orga</h1>

';

// show msg, if available
if ( $strmsg != '' )
{
   echo'
   <div class="announcement">
      <h1 class="warning">Authentifizierungshinweis:</h1>
      <ul>' . $strmsg . '</ul>
   </div>';
   $strmsg = '';
}

if ( !$sbolauthloggedin )
{
   // show authentication page and then die

   if ( !$bolauthabort )
   {
      // show login form
      echo'
      <div class="chapter">
         <h1>*knock-knock*</h1>
         <form id="loginform" name="loginform" action="' . $_SERVER["PHP_SELF"] . '" method="post">
            <input name="txtauth" type="password">
            <input name="cmdlogin" type="submit" value="Lass mich rein!">
         </form>
      </div>';
  }

// close page and stoprun.
echo'
  
<!-- end of specific page content -->

';
readfile( constant("SERVER_FILE_EXTRO") );

die();
}

// Well, Checkpoint.
// Now the user is proofed to be successfully logged in. (All others are wiped out.)
// From here on the user is authorized to enjoy all the following features.
// default sequence within each chapter:
// 1. headline
// 2. submit action
// 3. showmsg();
// 4. user forms
// 5. showmsg();
// 6. five empty rows to separate from next chapter ;)

// show logout chapter
echo'
<div class="chapter">
   <h1>Logout</h1>
</div>';
showmsg();
echo '
<div>
   <form id="logoutform" name="logoutform" action="' . $_SERVER["PHP_SELF"] . '" method="post">
      <p>
         <input name="cmdlogout" type="submit" value="Habe fertig!" />
      </p>
   </form>
</div>';
showmsg();


// show anmeldungen
echo'
<a name="anmeldungen"></a>
<div class="chapter">
	<h1>Anmeldungen anzeigen</h1>
  <form	id="showanmeldungenform" name="showanmeldungen" action="' . $_SERVER["PHP_SELF"] . '#anmeldungen" method="post">
	  <p>
	     <input name="cmdshowanmeldungen" type="submit" value="Anmeldungen anzeigen" />
	  </p>
  </form>
</div>';

if ( $bolshowanmeldungen )
{
  // Anmeldungen anzeigen
  // hmm, das hier musste jetzt mal schnell gehen, schöner oder gar effizienter Code ist das natürlich absolut nicht ;-)
  
  $strinput = file_get_contents( constant("SERVER_FILE_ANMELDUNGSLISTE") );
  $strinput = htmlentities( $strinput, ENT_QUOTES );
  $strinput = str_replace( chr(13), '', $strinput );
  $strinput = str_replace( chr(10), '</td></tr><tr><td class="db">', $strinput );
  $strinput = str_replace( chr(9), '</td><td class="db">', $strinput );
  $stroutput = '<table class="db"><tr class="dbhead"><td class="db">' . $strinput . '</td></tr></table>';
  $stroutput = str_replace( '<tr><td class="db"></td></tr>', '', $stroutput );
	
  $strinput = file( constant("SERVER_FILE_ANMELDUNGSLISTE") );

  $intsumanmeldungen = 0;
  $intsumeinnahmen = 0;
	$intsumwurst = 0;
	$intsumkaese = 0;
	$intsummarmelade = 0;
	$intsumobst = 0;
	$intsummuesli = 0;
	$strangelswithoutgarmentsize = '';
  $intsumgarmentsize = array ( '?' => 0, 
                              '4XS' => 0,
                              '3XS' => 0,
                              'XXS' => 0,
                              'XS' => 0,
                              'S' => 0,
                              'M' => 0,
                              'L' => 0,
                              'XL' => 0,
                              'XXL' => 0,
                              '3XL' => 0,
                              '4XL' => 0);
	foreach ($strinput as $intlinenum => $strline) {
	  if ( $intlinenum != 0 && strlen( $strline ) > 0)
	  {
	  	list($strnick, $strstatus, $stremail, $intwurst, $intkaese, $intmarmelade, $intobst, $intmuesli, $strtimestamp, $strgarmentsize) = explode(chr(9), $strline);

	    $intsumanmeldungen = $intsumanmeldungen + 1;
	    $intsumeinnahmen = $intsumeinnahmen + $eintritt[$strstatus];
	    
	  	$intsumwurst = $intsumwurst + $intwurst;
	  	$intsumkaese = $intsumkaese + $intkaese;
	  	$intsummarmelade = $intsummarmelade + $intmarmelade;
	  	$intsumobst = $intsumobst + $intobst;
	  	$intsummuesli = $intsummuesli + $intmuesli;
	  	
	  	$strgarmentsize = trim( $strgarmentsize );
	  	if ( $strgarmentsize != '' )
	  	{
        if ( array_key_exists( $strgarmentsize, $intsumgarmentsize ))	{ $intsumgarmentsize[$strgarmentsize] = $intsumgarmentsize[$strgarmentsize] + 1; }
  	  	elseif( $strgarmentsize > '' ) { $intsumgarmentsize['?'] = $intsumgarmentsize['?'] + 1; }
	  	}
	  	elseif ( $strstatus == 'Engel' )
      {
        $strangelswithoutgarmentsize = $strangelswithoutgarmentsize . ', &quot;' . $strnick . '&quot;';
      } 
		}
	}
  $strangelswithoutgarmentsize = substr( $strangelswithoutgarmentsize, 2 );

  echo '<div class="box">';
	echo '<span class="topic">nach Status:</span>';
	echo '<span class="line">Normal: ' . substr_count( $stroutput, '>Normal<' ) . '</span>';
	echo '<span class="line">Mitglieder: ' . substr_count( $stroutput, '>Mitglied<' ) . '</span>';
	echo '<span class="line">Engel: ' . substr_count( $stroutput, '>Engel<' ) . '</span>';
	echo '<span class="line">Erm&auml;&szlig;igt: ' . substr_count( $stroutput, '>Ermaeszigt<' ) . '</span>';
	echo '<hr />';
	echo '<span class="line important">Gesamt: ' . $intsumanmeldungen . '</span>';
	echo '<span class="line important">Einnahmen: ' . $intsumeinnahmen . ' Euro</span>';
  echo '</div>';

  echo '<div class="box">';
	echo '<span class="topic">nach Futter: pro Tag (' . constant("ORGA_DAYS_BREAKFAST") . ' Tage)</span>';
	echo '<span class="line">Wurst: ' . $intsumwurst . ' (' . ( constant("ORGA_DAYS_BREAKFAST") * $intsumwurst ) . ')' . '</span>';
	echo '<span class="line">K&auml;se: ' . $intsumkaese . ' (' . ( constant("ORGA_DAYS_BREAKFAST") * $intsumkaese ) . ')' . '</span>';
	echo '<span class="line">Marmelade: ' . $intsummarmelade . ' (' . ( constant("ORGA_DAYS_BREAKFAST") * $intsummarmelade ) . ')' . '</span>';
	echo '<span class="line">Obst: ' . $intsumobst . ' (' . ( constant("ORGA_DAYS_BREAKFAST") * $intsumobst ) . ')' . '</span>';
	echo '<span class="line">M&uuml;sli: ' . $intsummuesli . ' (' . ( constant("ORGA_DAYS_BREAKFAST") * $intsummuesli ) . ')' . '</span>';
	echo '<hr />';
	echo '<span class="line important">Br&ouml;tchen gesamt: ' . ( $intsumwurst + $intsumkaese + $intsummarmelade ) . ' (' . ( 4 * ( $intsumwurst + $intsumkaese + $intsummarmelade ) ) . ')' . '</span>';
  echo '</div>';

  echo '<div class="box">';
	echo '<span class="topic">nach Konfektionsgr&ouml;&szlig;e:</span>';
	foreach ($intsumgarmentsize as $strgarmentsize => $intgarmentsize)
  {
    if ( $intgarmentsize > 0 ) echo '<span class="line">' . $strgarmentsize . ': ' . $intgarmentsize . '</span>';
	}
	echo '<hr />';
	echo '<span class="line important">T-Shirts gesamt: ' . ( array_sum( $intsumgarmentsize ) ) . '</span>';
	echo '<span class="line important">Engel ohne T-Shirt-Gr&ouml;&szlig;e: ' . (( $strangelswithoutgarmentsize != '') ? $strangelswithoutgarmentsize : '(keine)' ) . '</span>';
  echo '</div>';

  echo '<div class="box">' . $stroutput . '</div>';
	
}
showmsg();


// show kassenliste
echo'
<a name="kassenliste"></a>
<div class="chapter">
	<h1>Kassenliste anzeigen</h1>
  <form	id="showkassenlisteform" name="showkassenliste" action="' . $_SERVER["PHP_SELF"] . '#kassenliste" method="post">
	  <p>
	     <input name="cmdshowkassenliste" type="submit" value="Kassenliste anzeigen" />
	  </p>
  </form>
</div>';

if ( $bolshowkassenliste )
{
  // Kassenliste anzeigen
  // hmm, das hier musste jetzt mal schnell gehen, schöner oder gar effizienter Code ist das natürlich absolut nicht ;-)
  
  $strinput = file( constant("SERVER_FILE_ANMELDUNGSLISTE") );
  array_splice( $strinput, 0, 1 );  // remove first line (header)
  natcasesort( $strinput );         // sort by nick (first column)

  $stroutput = '';
  $intinputnum = 0;
	foreach ($strinput as $intlinenum => $strline)
	{
	  if ( strlen( $strline ) > 0)
	  {
      $intinputnum = $intinputnum + 1;
      $bolendofpage = ((($intinputnum % ( constant( "KASSENLISTE_PAGE_COLUMNS" ) * constant( "KASSENLISTE_COLUMN_ROWS" ))) == 0) or ($intinputnum == count($strinput)) );
      $bolendofcolumn = ((($intinputnum % constant( "KASSENLISTE_COLUMN_ROWS" )) == 0) or $bolendofpage );
      $bolbeginofpage = (($intinputnum % ( constant( "KASSENLISTE_PAGE_COLUMNS" ) * constant( "KASSENLISTE_COLUMN_ROWS" ))) == 1 );
      $bolbeginofcolumn = (( $intinputnum % ( constant( "KASSENLISTE_COLUMN_ROWS" )) == 1) or $bolbeginofpage );
	  	list($strnick, $strstatus, $stremail, $intwurst, $intkaese, $intmarmelade, $intobst, $intmuesli, $strtimestamp) = explode(chr(9), $strline);

	  	$strnick = htmlentities( $strnick, ENT_QUOTES );
	  	
	  	//addmsg($strnick.': '.$intlinenum.'/'.$intinputnum.'='.($bolendofcolumn?'-C':'').($bolendofpage?'-P':'').($bolbeginofpage?'+P':'').($bolbeginofcolumn?'+C':''));
	  	
	  	if ( $bolbeginofpage )
	  	{
	  	  $stroutput = $stroutput . '<div class="box">';
	  	}

	  	if ( $bolbeginofcolumn )
	  	{
	  	  $stroutput = $stroutput . '<table class="db column"><tr class="dbhead"><td class="db">Status</td><td class="db">Eintritt</td><td class="db">Nick</td>';
	  	}

  		$stroutput = $stroutput . chr(13) . '<tr><td class="db">' . $strstatus . ' &nbsp;</td><td class="db ';
			switch ( $strstatus )
			{
				case 'Ermaeszigt':
				case 'Engel':
				    $stroutput = $stroutput . 'left';
				    break;
				case 'Mitglied':
				    $stroutput = $stroutput . 'center';
				    break;
				case 'Normal':
				    $stroutput = $stroutput . 'right';
				    break;
			}
			$stroutput = $stroutput . '">' . $eintritt[$strstatus] . '</td><td class="db">' . $strnick . '</td></tr>';

	  	if ( $bolendofcolumn )
	  	{
	  	  $stroutput = $stroutput . '</table>';
	  	}
	  	
	  	if ( $bolendofpage )
	  	{
	  	  $stroutput = $stroutput . '<div class="line"></div></div>';
	  	}
		}
	}
	
	echo $stroutput;
}
showmsg();


// close page and stoprun.
echo
'
<!-- end of specific page content -->

';
readfile( constant("SERVER_FILE_EXTRO") );

die();

?>