<?php // -*- php -*-
// Copyright (C) 2012, Wolfgang Scherer, <Wolfgang.Scherer at gmx.de>
// Sponsored by WIEDENMANN SEILE GMBH, http://www.wiedenmannseile.de
//
// This file is part of Wiedenmann Vacation.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>,
// or write to <Wolfgang.Scherer at gmx.de>

// $_REQUEST['_DEBUG_'] = 1;
// $_REQUEST['_DEBUG_TEST_'] = 1;
require_once(dirname(__FILE__) . '/../lib/config.php');
require_once(dirname(__FILE__) . '/../lib/language.php');

// --------------------------------------------------
// |||:sec:||| Benutzer
// --------------------------------------------------

function get_users()
{
    global $PASSWD_FILE;
    global $USER_ID_MIN;
    global $USER_ID_MAX;
    global $ALLOWED_USERS;
    global $INVALID_USERS;
    global $HOME_PFX;

    $passwd = file_get_contents($PASSWD_FILE);
    $lines = explode("\n", $passwd);
    $users = Array();
    //var_dump($lines);
    foreach ($lines as $line) {
        if (empty($line)) {
            continue;
        }
        $fields = explode(':', $line);
        $user = $fields[0];
        $uid = $fields[2];
        $home = $fields[5];

        // uncoditionally accept allowed users
        if (!in_array($user, $ALLOWED_USERS)) {
            if ($uid < $USER_ID_MIN || $uid > $USER_ID_MAX) {
                // skip users with a user id outside the allowed range
                continue;
            }
            $home_pos = strpos($home, $HOME_PFX);
            if ($home_pos === false || $home_pos > 0) {
                // skip users without a HOME directory
                continue;
            }
            if (in_array($user, $INVALID_USERS)) {
                // skip invalid users
                continue;
            }
        }
        $users[$user] = Array($user, $home);
    }
    ksort($users);
    return $users;
}

function make_htpasswd()
{
    global $SHADOW_FILE;
    global $HTPASSWD_FILE;
    $users = get_users();
    $user_names = array_keys($users);
    $shadow = file_get_contents($SHADOW_FILE);
    $lines = explode("\n", $shadow);
    $pw_ent = Array();
    //var_dump($lines);
    foreach ($lines as $line) {
        if (empty($line)) {
            continue;
        }
        $fields = explode(':', $line);
        $user = $fields[0];
        $pass = $fields[1];
        if (in_array($user, $user_names)) {
            $pw_ent[] = sprintf('%s:%s', $user, $pass);
        }
    }
    $htpwd = fopen($HTPASSWD_FILE, 'w');
    fwrite($htpwd, implode($pw_ent, "\n"));
    fclose($htpwd);
}

// --------------------------------------------------
// |||:sec:||| substitute_elements
// --------------------------------------------------

// |:check:| syntax error on PHP 5.2.x (mecki)
// class substitute_elements_check
// {
//     const _doc_ = <<<'DOC'
// Markierungen in String ersetzen.

// function substitute_elements ($string, $substitutions=array(), $keep_unknown=NULL, $pfx='@', $sfx=NULL)

// Falls eine Markierung gefunden wurde, die nicht in $substitutions
// enthalten ist, wird sie durch einen leeren String ersetzt, falls
// $keep_unknown == false, andernfalls wird die Markierung
// unverändert beibehalten.

// Ist $keep_unknown == NULL (Standardwert), dann wird es auf
// isset($_REQUEST['_DEBUG_']) gesetzt. Im Debug-Modus werden die
// Markierungen also beibehalten im Normal-Modus nicht.
// DOC;
//     const _check_ = <<<'CHECK'
// global $BRNL;

// $string = 'Eine @Ersetzung@ an @dieser@ Stelle.' . $BRNL;

// $substitutions = array(
//     'Ersetzung' => 'neue Sache',
//     );

// echo substitute_elements($string, $substitutions, True);
// echo substitute_elements($string, $substitutions, False);
// CHECK;
//     static $_check_output_;
// }

function substitute_elements (
    $string, $substitutions=array(), $keep_unknown=NULL, $pfx='@', $sfx=NULL)
{
    if ( !isset($keep_unknown) )
    {
        $keep_unknown = isset($_REQUEST['_DEBUG_']);
    }

    if ( !isset($sfx) )
    {
        $sfx = $pfx;
    }

    $subst_rx = $pfx . '([^' . substr($sfx, 0, 1) . ']+)' .$sfx;

    $parts = preg_split('/' . $subst_rx . '/', $string);
    //hline();
    //$d->c()->kva($parts)->p();

    $subst_keys = Array();
    preg_match_all('/' . $subst_rx . '/', $string,
                   $subst_keys, PREG_PATTERN_ORDER);
    $subst_keys = $subst_keys[1];
    //hline();
    //$d->c()->kva($subst_keys)->p();

    $result = Array();
    $indx = 0;
    foreach ($parts as $part)
    {
        // pdkv('// $part '."$indx", $part);
        $result[] = $part;
        if ( array_key_exists($indx, $subst_keys))
        {
            $subst_key = $subst_keys[$indx];
            if (array_key_exists($subst_key, $substitutions))
            {
                $result[] = $substitutions[$subst_key];
            }
            else if ( $keep_unknown )
            {
                $result[] = $pfx . $subst_key . $sfx;
            }
            // pdkv('// $subst_key', $subst_key);
        }
        $indx += 1;
    }
    $result = implode('', $result);
    return $result;
}

// --------------------------------------------------
// |||:sec:||| Language Support
// --------------------------------------------------

if (!array_key_exists($LANGUAGE, $LC_LANGUAGES)) {
    $LANGUAGE = 'en';
}
$LC_MESSAGES_EN = $LC_LANGUAGES['en'];
$LC_MESSAGES = $LC_LANGUAGES[$LANGUAGE];

if ( True && isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
{
    $accept_language = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
    $accept_language = preg_replace('/;[^,;]*/', '', $accept_language);
    $preferred_languages = explode(',', $accept_language);
    // if (isset($_REQUEST['_DEBUG_'])) {
    //     var_dump($preferred_languages);
    // }
    foreach ($preferred_languages as $plang) {
        if (array_key_exists($plang, $LC_LANGUAGES)) {
            $LC_MESSAGES = $LC_LANGUAGES[$plang];
            break;
        }
    }
}

function get_text($text)
{
    global $LC_MESSAGES_EN;
    global $LC_MESSAGES;
    if ( !array_key_exists($text, $LC_MESSAGES))
    {
        if ( array_key_exists($text, $LC_MESSAGES_EN) )
        {
            return $LC_MESSAGES_EN[$text];
        }
        return $text;
    }
    return $LC_MESSAGES[$text];
}

// --------------------------------------------------
// |||:sec:||| Überschrift/Fehlermeldung
// --------------------------------------------------

function shl($message)
{
    return sprintf('<h3>%s</h3>', $message);
}

function sshl($message)
{
    return sprintf('<h4>%s</h4>', $message);
}

function hl($message)
{
    echo shl($message)."\n";
}

function error_msg($message)
{
    echo sprintf('<div class="error"><strong>%s</strong></div>'."\n", $message);
}

// --------------------------------------------------
// |||:sec:||| vaccation(1) message
// --------------------------------------------------

// 2.2. Header Fields
//    Header fields are lines beginning with a field name, followed by a
//    colon (":"), followed by a field body, and terminated by CRLF.  A
//    field name MUST be composed of printable US-ASCII characters (i.e.,
//    characters that have values between 33 and 126, inclusive), except
//    colon.  A field body may be composed of printable US-ASCII characters
//    as well as the space (SP, ASCII value 32) and horizontal tab (HTAB,
//    ASCII value 9) characters (together known as the white space
//    characters, WSP).  A field body MUST NOT include CR and LF except
//    when used in "folding" and "unfolding", as described in section
//    2.2.3.  All field bodies MUST conform to the syntax described in
//    sections 3 and 4 of this specification.

function message_split($message)
{
    $lines = explode("\n", ltrim($message));
    $headers = Array();
    $body = '';
    $check_from = True;
    $current_field = '';
    while (True) {
        $line = array_shift($lines);
        $tline = trim($line);
        if (empty($tline)) {
            break;
        }
        if ($check_from) {
            $check_from = False;
            if ( preg_match('/^From /', $line) ) {
                continue;
            }
        }
        $matches = Array();
        if ( preg_match('/(^[!-~]+):/', $line, $matches)) {
            $current_field = strtolower($matches[1]);
            $headers[$current_field] = $line;
            continue;
        }
        if (!empty($current_field))
        {
            $headers[$current_field] .= "\n" . $line;
            continue;
        }
        $headers[] = $line;
    }
    $body = implode($lines, "\n");

    global $_debug;
    if ( $_debug ) {
        echo "<pre>\n";
        echo "--------------------------------------------------\n";
        echo "headers\n";
        echo "--------------------------------------------------\n";
        var_dump($headers);
        echo "--------------------------------------------------\n";
        echo "body\n";
        echo "--------------------------------------------------\n";
        var_dump($body);
        echo "</pre>\n";
    }

    return Array($headers, $body);
}

function message_join($headers, $body)
{
    return sprintf("%s\n\n%s", implode($headers, "\n"), $body);
}

function header_set(&$headers, $header)
{
    $matches = Array();
    if (preg_match('/^([!-~]+):/', $header, $matches)) {
        $headers[$matches[1]] = trim($header);
    }
}

function header_field_body(&$headers, $field_name)
{
    if (array_key_exists($field_name, $headers)) {
        $field = $headers[$field_name];
        $field_body = preg_replace('/^[^:]+: */', '', $field);
    } else {
        $field_body = '';
    }
    return $field_body;
}

function header_clean(&$headers)
{
    global $HEADER_REMOVE;
    $clean_headers = Array();
    foreach ($headers as $field_name => $field) {
        if ( substr($field_name, 0 , 2) == 'x-' )
        {
            continue;
        }
        if (in_array($field_name, $HEADER_REMOVE))
        {
            continue;
        }
        $clean_headers[$field_name] = $field;
    }
    return $clean_headers;
}

if (!function_exists("quoted_printable_encode")) {
    // only available with PHP >= 5.3.0
    if (function_exists("imap_8bit")) {
        function quoted_printable_encode($string) {
            return imap_8bit($string);
        }
    } else {
        /**
         * Process a string to fit the requirements of RFC2045 section 6.7. Note that
         * this works, but replaces more characters than the minimum set. For readability
         * the spaces and CRLF pairs aren't encoded though.
         */
        function quoted_printable_encode($string) {
            $string = str_replace(array('%20', '%0D%0A', '%'), array(' ', "\r\n", '='), rawurlencode($string));
            $string = preg_replace('/[^\r\n]{73}[^=\r\n]{2}/', "$0=\r\n", $string);
            return $string;
        }
    }
}

// there should be no need ... quoted_printable_decode is around since PHP4
if (!function_exists("quoted_printable_decode")) {
    if (function_exists("imap_qprint")) {
        echo "imap_qprint";
        function quoted_printable_decode($string) {
            return imap_qprint($string);
        }
    }
}

// Difference between Q-encoding and quoted-printable

// The ASCII codes for the question mark ("?") and equals sign ("=")
// may not be represented directly as they are used to delimit the
// encoded-word. The ASCII code for space may not be represented
// directly because it could cause older parsers to split up the
// encoded word undesirably. To make the encoding smaller and easier
// to read the underscore is used to represent the ASCII code for
// space creating the side effect that underscore cannot be
// represented directly. Use of encoded words in certain parts of
// headers imposes further restrictions on which characters may be
// represented directly.

function Q_encode ($string)
{
    $estring = quoted_printable_encode($string);
    if ( $estring != $string ) {
        $estring = preg_replace('/[?]/', '=3F', $estring);
        $estring = preg_replace('/[_]/', '=5F', $estring);
        $estring = preg_replace('/[ ]/', '_', $estring);
        $estring = sprintf('=?utf-8?Q?%s?=', $estring);
    }
    return $estring;
}

function Q_decode ($string)
{
    $sdecoded = Array();
    $matches = Array();
    preg_match_all('/=[?]utf-8[?]Q[?]|[?]=/', $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
    $indx = 0;
    $limit = count($matches);
    $start = 0;
    for ( $indx = 0; $indx<$limit; ) {
        $match = $matches[$indx][0][0];
        $offset = $matches[$indx][0][1];
        $indx += 1;
        if ( $match != "=?utf-8?Q?") {
            continue;
        }
        $sdecoded[] = substr($string, $start, $offset - $start);
        $start = $offset + strlen($match);

        if ( $indx>=$limit ) {
            break;
        }

        $match1 = $matches[$indx][0][0];
        $offset1 = $matches[$indx][0][1];
        $indx += 1;
        if ( $match != "?=") {
            // |:todo:| echo "warning: second match is not `?='\n";/*  */
        }

        $epart = substr($string, $start, $offset1 - $start);
        $epart = preg_replace('/[_]/', ' ', $epart);
        $sdecoded[] = quoted_printable_decode($epart);
        $start = $offset1 + strlen($match1);
    }
    $sdecoded[] = substr($string, $start);
    return implode($sdecoded, '');
}

function subject_quoted_printable_encode ($subject)
{
    $parts = preg_split('/[$]SUBJECT/', $subject);
    $indx = 0;
    $partsq = Array();
    foreach ($parts as $part) {
        if ( $indx > 0 ) {
            $partsq[] = '$SUBJECT';
        }
        $indx += 1;
        if ( empty($part) ) {
            continue;
        }
        $partsq[] = Q_encode($part);
    }
    return implode($partsq, '');
}

function subject_quoted_printable_decode ($subject)
{
    return Q_decode($subject);
}

function vacation_split($message, $decode)
{
    $messagea = message_split($message);
    $headers = $messagea[0];
    $body = $messagea[1];
    $subject = header_field_body($headers, 'subject');
    if ( $decode ) {
        $subject = subject_quoted_printable_decode($subject);
        $body = quoted_printable_decode($body);
    }
    return Array($subject, $headers, $body);
}

function vacation_find_forward($headers) {
    if ( array_key_exists('x-wsv-forward-to', $headers) )
    {
        $header = $headers['x-wsv-forward-to'];
        $matches = Array();
        if (preg_match('/^[^:]+: *(.*)/', $header, $matches)) {
            return $matches[1];
        }
    }
    return '';
}

function vacation_join($subject, $body, $headers=Null)
{
    $subject = trim($subject);
    $subject = subject_quoted_printable_encode($subject);
    $subject = 'Subject: ' . $subject;
    if (!isset($headers)) {
        $headers = Array();
    }
    header_set($headers, $subject);
    header_set($headers, 'Content-Type: text/plain;'."\n"
        .'  charset="utf-8"');
    header_set($headers, 'Content-Transfer-Encoding: quoted-printable');
    $headers = implode($headers, "\n");
    if (empty($body)) {
        $body = '';
    }
    $body = quoted_printable_encode($body);
    $body = preg_replace('/=24SUBJECT/', '$SUBJECT', $body);
    return sprintf("%s\n\n%s\n",
                   $headers, $body);
}

//
// :ide-menu: Emacs IDE Menu - Buffer @BUFFER@
// . M-x `eIDE-menu' ()(eIDE-menu "z")
// :ide: COMPILE: PHP _DEBUG_=2 _DEBUG_TEST_=2
// . (compile (concat "php " (file-name-nondirectory (buffer-file-name)) " _DEBUG_=2 _DEBUG_TEST_=2"))

// :ide: QUO: $this->
// . (insert "$this->" )

// :ide: COMPILE: PHP w/o args
// . (compile (concat "php " (file-name-nondirectory (buffer-file-name)) ""))

// :ide: COMPILE: PHP _DEBUG_=1 _DEBUG_TEST_=1
// . (compile (concat "php " (file-name-nondirectory (buffer-file-name)) " _DEBUG_=1 _DEBUG_TEST_=1"))

//
// Local Variables:
// mode: php
// End:
?>
