<?php

function cms_do_stage($stage = '') {
    global $cms;
    
    if (
        // Not exists stage
        !isset($cms['stages'][$stage]) ||
        !is_array($cms['stages'][$stage]) || 

        // disable warnings if stage disabled
        isset($cms['stages'][$stage]['disabled']) &&
        $cms['stages'][$stage]['disabled'] == true
    ) return;
    
    ksort($cms['stages'][$stage]);
    
    foreach ($cms['stages'][$stage] as $nice => $value) {
        # skip non numeric keys
        if (is_array($cms['stages'][$stage][$nice])) {
            foreach ( $cms['stages'][$stage][$nice] as $function => $enabled ) {
                if ( $enabled ) {
                    $function();
                }
            }
        }
    }
}

function cms_save_config() {
    global $cms;
    @$r = file_put_contents($cms['config_file'], '<?php
$cms["config"] = '.var_export($cms['config'], true).";\n", LOCK_EX);
    /* apache2 ?
    if ($r) {
        clearstatcache();
        if (function_exists('opcache_invalidate')) { opcache_invalidate($cms['config_file']); }
        include($cms['config_file']);
    }*/
    //error_log( var_export( debug_backtrace(), true ) );
    return $r;
}

// Создание нового этапа
// Вторым параметром указываем к какому перейти после этого
// Если ничего не указываем, то CMS завершит работу после него
function cms_add_stage($stage, $next = '') {
    global $cms;
    $cms['stages'][$stage]['next'] = $next;
}

// Вставка этапа
// Вставляет новый этап, между текущим и следующим
// Вторым параметром можно переназначить следующий
function cms_insert_stage($stage, $next = '') {
    global $cms;
    if ($next === '') $next = $cms['stages'][$cms['stage']]['next'];
    $cms['stages'][$stage]['next'] = $next;
}

// Добавление функции в этап
// По умолчанию приоритет 10
// Увеличивая приоритет, отодвигаем функцию в конец, уменьшая - вперед
function cms_add_function($stage, $function, $nice = 10) {
    global $cms;
    $cms['stages'][$stage][$nice][$function] = true;
    if ( !isset( $cms['stages'][$stage]['next'] ) ) { $cms['stages'][$stage]['next'] = ''; } /* fix warning */
}

// Удаление функции из этапа
/*
function cms_remove_function($stage, $function) {
    global $cms;
    foreach ($cms['stages'][$stage] as $nice => $value) {
        foreach ($cms['stages'][$stage][$nice] as $fn) {
            if ($fn === $function) {
                unset($cms['stages'][$stage][$nice][$fn]);
            }
        }
    }
}
*/

function callback2( $matches ) {
    global $images;
    $n = count($images);
    $images[$n] = array('ext'=>$matches[1], 'base64'=>$matches[2]);
    return "cid:$n.".$matches[1];
}

function callback1( $matches ) {
    $r2 = preg_replace_callback('/data:image\/(\w+);base64,(.+)/', "callback2", $matches[1]);
    return '<img src="'.$r2.'"';
}

// $data['to_name']
// $data['to_email']
// $data['subject']
// $data['email_body']
// $data['from_name']
// $data['from_email']
// $data['reply_name']
// $data['reply_to']
// $data['files']
// $data['type'] (text/html or text/plain(default))
// $data['sender'] if empty, then noreply@$_SERVER['SERVER_NAME']
// $data['bcc']
/* Возвращает пустую строку, если отправка успешна или строку ошибки, если нет. */
function cms_email($data) {
    global $images;

    $to_name = @trim($data['to_name']);
    if (!empty($to_name)) {
        $to = '=?utf-8?b?'.base64_encode($to_name).'?= <'.$data['to_email'].'>';
    } else {
        $to = $data['to_email'];
    }

    $subject = '=?utf-8?b?'.base64_encode($data['subject']).'?=';

    $from_name = @trim($data['from_name']);
    if (!empty($from_name)) {
        $from = '=?utf-8?b?'.base64_encode($from_name).'?= <'.$data['from_email'].'>';
    } else {
        $from = $data['from_email'];
    }

    if (empty($data['reply_to'])) {
        $reply = $from;
    } else {
        $reply = $data['reply_to'];
        if (!empty($data['reply_name'])) {
            $reply = '=?utf-8?b?'.base64_encode($data['reply_name']).'?= <'.$data['reply_to'].'>';
        }
    }

    $boundary = md5(uniqid(time()-25));
    $boundary_id = md5(uniqid(time()));

    if (empty($data['sender'])) { $sender = "noreply@{$_SERVER['SERVER_NAME']}"; } else { $sender = $data['sender']; }

    $headers = "Sender: $sender\r\nFrom: $from\r\nReply-To: $reply\r\nContent-Type: multipart/mixed; boundary=\"$boundary\"\r\n";

    if (!empty($data['bcc'])) $headers .= "Bcc: {$data['bcc']}\r\n";

    $images = array();
    $body2 = preg_replace_callback('/<img +src="([^"]+)"/', "callback1", $data['email_body'] );

    if ( empty($data['type']) ) {
        $type = 'text/plain';
    } else {
        $type = $data['type'];
    }

    $body_all = "--$boundary\r\nContent-Type: multipart/related; boundary=\"$boundary_id\"\r\n\r\n";
    $body_all .= "--$boundary_id\r\nContent-Type: $type; charset=utf-8;\r\nContent-Transfer-Encoding: base64\r\n\r\n" . chunk_split(base64_encode($body2) . "\r\n\r\n");

    foreach($images as $ind=>$image) {
        $body_all .= "--$boundary_id\r\nContent-Type: image/".$image['ext']."; name=\"$ind.".$image['ext']."\"\r\nContent-ID: <$ind.".$image['ext'].">\r\nContent-Disposition: inline; filename=\"$ind.".$image['ext']."\"\r\nContent-transfer-encoding: base64\r\n\r\n".chunk_split($image['base64'])."\r\n\r\n";
    }

    if (isset($data['files']) && is_array($data['files']))
    foreach($data['files'] as $file) {
        $mime = mime_content_type($file);
        $name = preg_replace("/.*\//u", "", $file);
        $body_all .= "--$boundary_id\r\nContent-Type: $mime; name=\"$name\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"$name\"\r\n\r\n".chunk_split(base64_encode(file_get_contents($file)))."\r\n\r\n";
    }

    $body_all .= "--$boundary_id--\r\n\r\n";
    $body_all .= "--$boundary--\r\n\r\n";

    if ( mail($to, $subject, $body_all, $headers, "-f{$data['from_email']}") ) {
        return '';
    } else {
        return 'error mail() function';
    }
}

// $data['to_name']
// $data['to_email']
// $data['subject']
// $data['email_body']
// $data['from_name']
// $data['from_email']
// $data['reply_name']
// $data['reply_to']
// $data['files']
// $data['type'] (text/plain(default) or text/html)
// $data['sender'] if empty, then noreply@$_SERVER['SERVER_NAME']
// $data['bcc']
// $data['smtp_host']
// $data['smtp_port']
// $data['smtp_login']
// $data['smtp_password']
/* Возвращает пустую строку, если отправка успешна или строку ошибки, если нет. */
function cms_smtp_email($data) {
    global $images, $cms;

    $to_name = @trim($data['to_name']);
    if (!empty($to_name)) {
        $to = '=?utf-8?b?'.base64_encode($to_name).'?= <'.$data['to_email'].'>';
    } else {
        $to = $data['to_email'];
    }

    $from_name = @trim($data['from_name']);
    if (!empty($from_name)) {
        $from = '=?utf-8?b?'.base64_encode($from_name).'?= <'.$data['from_email'].'>';
    } else {
        $from = $data['from_email'];
    }

    if (empty($data['reply_to'])) {
        $reply = $from;
    } else {
        $reply = $data['reply_to'];
        if (!empty($data['reply_name'])) {
            $reply = '=?utf-8?b?'.base64_encode($data['reply_name']).'?= <'.$data['reply_to'].'>';
        }
    }

    $boundary = md5(uniqid(time()-25));
    $boundary_id = md5(uniqid(time()));

    if (empty($data['sender'])) {
        //$sender = "noreply@{$_SERVER['SERVER_NAME']}";
        $sender = $data['from_email'];
    } else {
        $sender = $data['sender'];
    }
    
    if (empty($data['return_path'])) {
        //$return_path = "noreply@{$_SERVER['SERVER_NAME']}";
        $return_path = $data['from_email'];
    } else {
        $return_path = $data['return_path'];
    }


    /* start headers */
    $SEND  = "Date: ".date("r")."\r\n";
    $SEND .= "Sender: $sender\r\n";
    $SEND .= "Return-Path: $return_path\r\n";
    $SEND .= "Reply-To: {$reply}\r\n";
    $SEND .= "From: {$from}\r\n";
    $SEND .= "To: {$to}\r\n";
    if (!empty($data['bcc'])) {$SEND .= "Bcc: {$data['bcc']}\r\n";}
	$SEND .= 'Subject: =?utf-8?b?'.base64_encode($data['subject'])."?=\r\n";
    $SEND .= "Content-Type: multipart/mixed; boundary=\"$boundary\"\r\n";
    $SEND .= "\r\n";
    /* end headers */

    /* Move images to appendix */
    $images = array();
    $body2 = preg_replace_callback('/<img +src="([^"]+)"/', "callback1", $data['email_body'] );

    if ( empty( $data['type'] ) ) {
        $type = 'text/plain';
    } else {
        $type = $data['type'];
    }

    $SEND .= "--{$boundary}\r\nContent-Type: multipart/related; boundary=\"{$boundary_id}\"\r\n\r\n";
    $SEND .= "--{$boundary_id}\r\nContent-Type: {$type}; charset=utf-8;\r\nContent-Transfer-Encoding: base64\r\n\r\n" . chunk_split(base64_encode($body2) . "\r\n\r\n");

    foreach( $images as $ind=>$image ) {
        $SEND .= "--{$boundary_id}\r\nContent-Type: image/{$image['ext']}; name=\"{$ind}.{$image['ext']}\"\r\nContent-ID: <{$ind}.{$image['ext']}>\r\nContent-Disposition: inline; filename=\"{$ind}.{$image['ext']}\"\r\nContent-transfer-encoding: base64\r\n\r\n".chunk_split($image['base64'])."\r\n\r\n";
    }

    if (isset($data['files']) && is_array($data['files'])) {
        foreach($data['files'] as $file) {
            $mime = mime_content_type($file);
            $name = preg_replace("/.*\//u", "", $file);
            $SEND .= "--{$boundary_id}\r\n";
            $SEND .= "Content-Type: {$mime}; name=\"{$name}\"\r\n";
            $SEND .= "Content-Transfer-Encoding: base64\r\n";
            $SEND .= "Content-Disposition: attachment; filename=\"{$name}\"\r\n\r\n";
            $SEND .= chunk_split(base64_encode(file_get_contents($file)))."\r\n\r\n";
        }
    }

    $SEND .= "--{$boundary_id}--\r\n\r\n";
    $SEND .= "--{$boundary}--\r\n\r\n";

    $debug_file = $cms['cms_dir'].'/smtp_debug.txt';
    cms_debug( $debug_file, "\n---" );

    /* Соединение */
    cms_debug( $debug_file, "fsockopen {$data['smtp_host']} {$data['smtp_port']}" );
    $socket = fsockopen( $data['smtp_host'], $data['smtp_port'], $errno, $errstr, 10 ); /* 10 sec timeout */
    if( $socket === false ) {
        cms_debug( $debug_file, "fsockopen error {$errno} {$errstr}" );
        return "{$errno} {$errstr}";
    }
    cms_debug( $debug_file, "fsockopen ok" );

    stream_set_timeout( $socket, 3 );
 
    /* Прием данных после соединения */
    $ok = false;
    while ( $r = fgets( $socket, 1000 ) ) {
        cms_debug( $debug_file, "SERVER: {$r}" );
        if ( substr( $r, 0, 3 ) === "220" ) { $ok = true; }
        if ( substr( $r, 3, 1 ) === " " ) { break; }
    }
	if ( $ok === false ) {
        return $r;
    }

    /* Приветствие */
    cms_debug( $debug_file, "ME: EHLO {$_SERVER['SERVER_NAME']}" );
    fputs( $socket, "EHLO {$_SERVER['SERVER_NAME']}\r\n");

    /* Прием данных после приветствия */
    $ok = false;
    while ( $r = fgets( $socket, 1000 ) ) {
        cms_debug( $debug_file, "SERVER: {$r}" );
        if ( preg_match( '/250.AUTH/', $r ) ) { $ok = true; }
        if ( substr( $r, 3, 1 ) === " " ) { break; }
    }
    if ( $ok === false ) {
        fclose( $socket );
        cms_debug( $debug_file, "Сервер не приветствует нас." );
		return $r."\nСервер не приветствует нас.";
    }
    
    /* Отправка команды "Хочу авторизоваться" */
    cms_debug( $debug_file, "ME: AUTH LOGIN" );
	fputs( $socket, "AUTH LOGIN\r\n" );

    /* Прием данных после отправки команды авторизации */
    $ok = false;
    while ( $r = fgets( $socket, 1000 ) ) {
        cms_debug( $debug_file, "SERVER: {$r}" );
        if ( substr( $r, 0, 3 ) === "334" ) { $ok = true; }
        if ( substr( $r, 3, 1 ) === " " ) { break; }
    }
	if ( $ok === false ) {
		fclose( $socket );
        cms_debug( $debug_file, "Не могу найти ответ на запрос авторизаци." );
		return "Не могу найти ответ на запрос авторизаци.";
    }
    
    /* Отправка логина */
    cms_debug( $debug_file, "ME: base64_encode( smtp_login )" );
	fputs( $socket, base64_encode( $data['smtp_login'] )."\r\n" );

    /* Прием ответа после отправки логина */
    $ok = false;
    while ( $r = fgets( $socket, 1000 ) ) {
        cms_debug( $debug_file, "SERVER: {$r}" );
        if ( substr( $r, 0, 3 ) === "334" ) { $ok = true; }
        if ( substr( $r, 3, 1 ) === " " ) { break; }
    }
	if ( $ok === false ) {
		fclose( $socket );
        cms_debug( $debug_file, "Логин авторизации не был принят сервером!" );
		return "Логин авторизации не был принят сервером!";
    }

    /* Отправка пароля */
    cms_debug( $debug_file, "ME: base64_encode( smtp_password )" );
	fputs( $socket, base64_encode( $data['smtp_password'])."\r\n" );

    /* Прием ответа после отправки пароля */
    $ok = false;
    while ( $r = fgets( $socket, 1000 ) ) {
        cms_debug( $debug_file, "SERVER: {$r}" );
        if ( substr( $r, 0, 3 ) === "235" ) { $ok = true; }
        if ( substr( $r, 3, 1 ) === " " ) { break; }
    }
	if ( $ok === false ) {
		fclose( $socket );
        cms_debug( $debug_file, "Пароль не был принят сервером как верный!" );
		return "Пароль не был принят сервером как верный!";
    }

    /* Отправка От кого */    
    cms_debug( $debug_file, "ME: MAIL FROM: <{$data['from_email']}>" );
	fputs( $socket, "MAIL FROM: <{$data['from_email']}>\r\n" );

    /* Прием ответа после От кого */
    $ok = false;
    while ( $r = fgets( $socket, 1000 ) ) {
        cms_debug( $debug_file, "SERVER: {$r}" );
        if ( substr( $r, 0, 3 ) === "250" ) { $ok = true; }
        if ( substr( $r, 3, 1 ) === " " ) { break; }
    }
	if ( $ok === false ) {
		fclose( $socket );
        cms_debug( $debug_file, "Не могу отправить комманду MAIL FROM" );
		return "Не могу отправить комманду MAIL FROM";
    }

    /* Отправка получателей */
    cms_debug( $debug_file, "ME: RCPT TO: <{$data['to_email']}>" );
	fputs( $socket, "RCPT TO: <{$data['to_email']}>\r\n" );

    /* Прием ответа после отправки получателей */
    $ok = false;
    while ( $r = fgets( $socket, 1000 ) ) {
        cms_debug( $debug_file, "SERVER: {$r}" );
        if ( substr( $r, 0, 3 ) === "250" ) { $ok = true; }
        if ( substr( $r, 3, 1 ) === " " ) { break; }
    }
	if ( $ok === false ) {
		fclose( $socket );
        cms_debug( $debug_file, "Не могу отправить комманду RCPT TO" );
		return "Не могу отправить комманду RCPT TO";
    }

    /* Скрытые копии */
    if ( !empty( $data['bcc'] ) ) {
        /* Отправка получателей скрытой копии */
        cms_debug( $debug_file, "ME (bcc): RCPT TO: <{$data['bcc']}>" );
        fputs( $socket, "RCPT TO: {$data['bcc']}\r\n" );

        /* Прием ответа после отправки получателей скрытой копии */
        $ok = false;
        while ( $r = fgets( $socket, 1000 ) ) {
            cms_debug( $debug_file, "SERVER: {$r}" );
            if ( substr( $r, 0, 3 ) === "250" ) { $ok = true; }
            if ( substr( $r, 3, 1 ) === " " ) { break; }
        }
        if ( $ok === false ) {
            fclose( $socket );
            cms_debug( $debug_file, "Не могу отправить комманду RCPT TO (bcc)" );
            return "Не могу отправить комманду RCPT TO (bcc)";
        }
    }
    
    /* Отправка команды перед телом письма */
    cms_debug( $debug_file, "ME: DATA" );
	fputs( $socket, "DATA\r\n" );

    /* Прием ответа, готов ли сервер принимать тело письма */
    $ok = false;
    while ( $r = fgets( $socket, 1000 ) ) {
        cms_debug( $debug_file, "SERVER: {$r}" );
        if ( substr( $r, 0, 3 ) === "354" ) { $ok = true; }
        if ( substr( $r, 3, 1 ) === " " ) { break; }
    }
    if ( $ok === false ) {
        fclose( $socket );
        cms_debug( $debug_file, "Не могу отправить комманду DATA" );
        return "Не могу отправить комманду DATA";
    }

    /* Отправка тела письма */
    cms_debug( $debug_file, "ME SEND DATA" );
	fputs( $socket, "{$SEND}\r\n.\r\n" );

    /* Прием ответа после отправки тела письма */
    $ok = false;
    while ( $r = fgets( $socket, 1000 ) ) {
        cms_debug( $debug_file, "SERVER: {$r}" );
        if ( substr( $r, 0, 3 ) === "250" ) { $ok = true; }
        if ( substr( $r, 3, 1 ) === " " ) { break; }
    }
    if ( $ok === false ) {
        fclose( $socket );
        cms_debug( $debug_file, "Не могу отправить тело письма." );
        return "Не могу отправить тело письма.";
    }

    /* Отправка команды выход и закрытие соединения */
    cms_debug( $debug_file, "ME: QUIT\n" );
	fputs( $socket, "QUIT\r\n" );
	fclose( $socket );
	return "";
}

function cms_debug( $debug_file, $string ) {
    global $cms;
    if ( empty( $cms['config']['debug'] ) ) {
        return;
    }
    if ( !file_exists( $debug_file ) || filesize( $debug_file ) > 16384 ) {
        file_put_contents( $debug_file, '' );
    }
    if ( substr( $string, -1 ) !== "\n" ) { $string .= "\n"; }
    file_put_contents( $debug_file, $string, FILE_APPEND );
}

function cms_readfile($file) {
    if (file_exists($file)) {
        /*if (ob_get_level()) {
            ob_end_clean();
        }*/
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="'.basename($file).'"');
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file));
        @ob_clean();
        flush();
        if ( $fd = fopen( $file, 'rb' ) ) {
            while ( !feof( $fd ) ) {
              print fread( $fd, 1024 );
            }
            return fclose( $fd );
        } else {
            return false;
        }
        //return readfile($file); mem exhausted
    } else {
        return false;
    }
}

function __( $string, $module = "admin.mod.php" ) {
    global $cms;
    if ( !isset( $cms['config'] ) ) return $string;
    // Load lang file
    if ( !isset( $cms["lang"][$module][ $cms['config']['locale'] ] ) ) {
        $file = $cms['cms_dir'].'/lang/'.$cms['config']['locale'].'/'.$module;
        if ( file_exists( $file ) ) {
            include_once( $file );
        } else {
            $cms["lang"][$module][ $cms['config']['locale'] ] = array();
        }
    }
    // Translate
    if ( isset( $cms["lang"][$module][ $cms['config']['locale'] ][$string] ) ) {
        return $cms["lang"][$module][ $cms['config']['locale'] ][$string];
    } else {
        return $string;
    }
}

function cms_translit( $string ) {
    global $cms;
    $tr1 = strtr( $string, $cms['tr'] );
    $tr2 = strtr( $tr1, array(' '=>'-') );
    $tr3 = preg_replace('/[^-A-Za-z0-9_]+/u', '', $tr2);
    $tr4 = trim($tr3, '-_');
    return $tr4;
}

function cms_translit_file( $string ) {
    global $cms;
    $tr1 = strtr( $string, $cms['tr'] );
    $tr2 = strtr( $tr1, array(' '=>'_') );
    $tr3 = preg_replace('/[^-A-Za-z0-9_]+/u', '', $tr2);
    return $tr3;
}

function cms_asort( &$array ) {
    if ( !is_array( $array ) ) return false;
    return uasort( $array, 'cms_uasort' );
}

function cms_uasort( $a, $b ) {
    if ( !isset($a['sort']) || !isset($b['sort']) ) {
        return 0;
    }
    if ( $a['sort'] <= $b['sort'] ) {
        return -1;
    } else {
        return 1;
    }
}

function recurse_rm($src) /* bool */ {
    if ( !file_exists( $src ) ) {
        return false;
    }
    $dir = opendir( $src );
    while( false !== ( $file = readdir( $dir ) ) ) {
        if ( ( $file != '.' ) && ( $file != '..' ) ) {
            if ( is_dir( $src.'/'.$file ) ) {
                recurse_rm( $src.'/'.$file );
            } else {
                if ( !unlink( $src.'/'.$file ) ) {
                    return false;
                }
            }
        }
    }
    closedir( $dir );
    return rmdir( $src );
}
