<?php
require_once('.contact_config.php');
/**
* @author Gernot WALZL
*/
class Uploader {
protected $config = null;
function __construct($config) {
$this->config = $config;
}
function to_bytes($str_val) {
$str_val = trim($str_val);
$modifier = strtoupper(substr($str_val, -1));
$bytes = (float)substr($str_val, 0, strlen($str_val)-1);
switch($modifier) {
case 'G':
$bytes *= 1024;
case 'M':
$bytes *= 1024;
case 'K':
$bytes *= 1024;
}
return $bytes;
}
function get_max_file_size() {
$max_upload_size = $this->to_bytes(ini_get('upload_max_filesize'));
$max_post_size = $this->to_bytes(ini_get('post_max_size'));
$max_file_size = $max_upload_size;
if ($max_upload_size > $max_post_size && $max_post_size != 0) {
$max_file_size = $max_post_size;
}
return $max_file_size;
}
function str_startswith($str, $sub) {
return ( substr($str, strlen($sub)) === $sub );
}
function str_endswith($str, $sub) {
return ( substr($str, -strlen($sub)) === $sub );
}
function has_allowed_ext($filename) {
$result = false;
foreach($this->config->allowed_exts as $ext) {
if ($this->str_endswith($filename, '.'.$ext)) {
$result = true;
break;
}
}
return $result;
}
function get_dst_dir($abs = false) {
$result = $this->config->dst_dir;
if ($abs) {
$result = dirname(__FILE__).'/'.$this->config->dst_dir;
}
return $result;
}
function get_ext($filename) {
$result = '';
if (strstr($filename, '.')) {
$exploded = explode('.', $filename);
$result = end($exploded);
}
return $result;
}
function get_valid_dst($filename) {
$result = '';
if (strlen($filename) == 0) {
$filename = 'unknown';
}
$basename = $filename;
$ext = $this->get_ext($filename);
if (!empty($ext)) {
$basename = substr($filename, 0, -strlen($ext)-1);
if (strlen($ext) > 8) {
$ext = substr($ext, 0, 8);
}
$ext = preg_replace('/[^a-zA-Z0-9\-]/', '_', $ext);
$ext = '.'.$ext;
}
if (strlen($basename) > 64) {
$basename = substr($basename, 0, 64);
}
$basename = preg_replace('/[^a-zA-Z0-9\-]/', '_', $basename);
$dst_dir = $this->get_dst_dir(true);
$dst_dir .= '/';
$result = $dst_dir.$basename.$ext;
if (file_exists($result)) {
$i = 1;
$result = $dst_dir.$basename.'.'.$i.$ext;
while (file_exists($result)) {
$i += 1;
$result = $dst_dir.$basename.'.'.$i.$ext;
}
}
return $result;
}
function compress_file($filename_src, $filename_dst) {
$result = false;
$zp = gzopen($filename_dst, 'wb');
if ($zp) {
$file = file_get_contents($filename_src);
if ($file) {
gzwrite($zp, $file);
$result = true;
}
gzclose($zp);
}
if ($result) {
unlink($filename_src);
}
return $result;
}
function handle_upload($filename_dst, $key, $compress = false) {
if (empty($filename_dst)) {
return false;
}
$result = false;
set_time_limit(3600); // 1 hour
if (is_uploaded_file($_FILES[$key]['tmp_name'])) {
if ($compress) {
if ($this->compress_file($_FILES[$key]['tmp_name'],
$filename_dst)) {
$result = true;
}
} else {
if (move_uploaded_file($_FILES[$key]['tmp_name'],
$filename_dst)) {
$result = true;
}
}
}
return $result;
}
/**
* https://www.php.net/manual/en/features.file-upload.errors.php
*/
function get_error_message($code) {
$msg = "";
switch ($code) {
case UPLOAD_ERR_OK:
$msg = "There is no error, the file uploaded with success.";
break;
case UPLOAD_ERR_INI_SIZE:
$msg = "The uploaded file exceeds the upload_max_filesize directive in php.ini.";
break;
case UPLOAD_ERR_FORM_SIZE:
$msg = "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.";
break;
case UPLOAD_ERR_PARTIAL:
$msg = "The uploaded file was only partially uploaded.";
break;
case UPLOAD_ERR_NO_FILE:
$msg = "No file was uploaded.";
break;
case UPLOAD_ERR_NO_TMP_DIR:
$msg = "Missing a temporary folder.";
break;
case UPLOAD_ERR_CANT_WRITE:
$msg = "Failed to write file to disk.";
break;
case UPLOAD_ERR_EXTENSION:
$msg = "A PHP extension stopped the file upload.";
break;
}
return $msg;
}
}
/**
* @author Gernot WALZL
*/
class MailValidator {
protected $config = null;
function __construct($config) {
$this->config = $config;
}
function is_valid_key($key) {
if ($key == hash('crc32b', date('YmdH')) ||
$key == hash('crc32b', date('YmdH', time()-3600))) {
return true;
}
return false;
}
function is_valid_name($name) {
if (strlen($name) <= 64 && !strpbrk($name, ":<>\n")) {
return true;
}
return false;
}
function is_valid_mail($mail) {
if (strlen($mail) <= 64 && !strpbrk($mail, ":<>\n") &&
strstr($mail, '@') && strstr($mail, '.')) {
return true;
}
return false;
}
function is_blocked($name, $mail, $msg) {
foreach ($this->config->blocked_addrs as $blocked_addr) {
if ($_SERVER['REMOTE_ADDR'] == $blocked_addr) {
return true;
}
}
foreach ($this->config->blocked_names as $blocked_name) {
if (strstr($name, $blocked_name)) {
return true;
}
}
foreach ($this->config->blocked_mails as $blocked_mail) {
if (strstr($mail, $blocked_mail)) {
return true;
}
}
foreach ($this->config->blocked_msgs as $blocked_msg) {
if (strstr($msg, $blocked_msg)) {
return true;
}
}
return false;
}
}
/**
* @author Gernot WALZL
*/
class ContactHTML {
function println($msg) {
print($msg."\n");
}
function print_head() {
$this->println('<!DOCTYPE html>');
$this->println('<html lang="en">');
$this->println('<head>');
$this->println('<meta charset="utf-8" />');
$this->println('<title>Contact Form</title>');
$this->println('</head>');
$this->println('<body>');
$this->println('<h1>Contact Form</h1>');
}
function print_foot() {
$this->println('</body>');
$this->println('</html>');
}
function print_form($name='', $mail='', $msg='', $max_file_size=0) {
$this->println('<form enctype="multipart/form-data" method="post" '.
'action="'.$_SERVER['REQUEST_URI'].'">');
if ($max_file_size) {
$this->println('<input type="hidden" name="MAX_FILE_SIZE" '.
'value="'.$max_file_size.'" />');
}
$this->println('<input type="hidden" id="key" name="key" '.
'value="'.hash('crc32b', date('YmdH')).'" />');
$this->println('<p><label for="name">Name:</label><br />');
$this->println('<input type="text" id="name" name="name" size="64" '.
'maxlength="64" value="'.htmlentities($name).'" /></p>');
$this->println('<p><label for="mail">Email:</label><br />');
$this->println('<input type="text" id="mail" name="mail" size="64" '.
'maxlength="64" value="'.htmlentities($mail).'" /></p>');
$this->println('<p><label for="msg">Message:</label><br />');
$this->println('<textarea id="msg" name="msg" cols="80" rows="10">'.
htmlentities($msg).'</textarea></p>');
$this->println('<p><label for="attachment">Attachment:</label><br />');
$this->println('<input type="file" id="attachment" '.
'name="attachment" /></p>');
$this->println('<input type="submit" value="Send" />');
$this->println('</form>');
}
function print_welcome() {
$this->println('<p>Please feel free to contact me.</p>');
}
function print_info_attachment_uploaded() {
$this->println('<p><strong>Info:</strong> '.
'Your attachment has been uploaded successfully.</p>');
}
function print_error_attachment($max_file_size) {
$mod = 1024;
$units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
for ($i = 0; $max_file_size > $mod; $i++) {
$max_file_size /= $mod;
}
$file_size_hr = round($max_file_size, 2).' '.$units[$i];
$this->println('<p><strong>Error:</strong> '.
'Attachment has not been uploaded.<br />');
$this->println('The file size is limited to '.$file_size_hr.'.</p>');
}
function print_info_msg_sent() {
$this->println('<p><strong>Info:</strong> '.
'Your message has been sent successfully.</p>');
}
function print_error_msg() {
$this->println('<p><strong>Error:</strong> '.
'Could not send your message.</p>');
$this->println('<ul>');
$this->println('<li>Fill out your name, email, and message.</li>');
$this->println('<li>Check your email address.</li>');
$this->println('<li>Your message may contain spam.</li>');
$this->println('</ul>');
}
}
$html_contact = new ContactHTML();
$cfg_upload = new UploadConfig();
$uploader = new Uploader($cfg_upload);
$key = '';
if (!empty($_POST['key'])) {
$key = trim($_POST['key']);
}
$name = '';
if (!empty($_POST['name'])) {
$name = trim($_POST['name']);
}
$mail = '';
if (!empty($_POST['mail'])) {
$mail = trim($_POST['mail']);
}
$msg = '';
if (!empty($_POST['msg'])) {
$msg = trim($_POST['msg']);
}
$attachment = false;
if (!empty($_FILES['attachment']['name'])) {
$attachment = true;
}
$sent = false;
$attachment_uploaded = false;
if (!empty($key) && !empty($name) && !empty($mail) &&
(!empty($msg) || $attachment)) {
$cfg_mail = new MailConfig();
$validator = new MailValidator($cfg_mail);
if ($validator->is_valid_key($key) &&
$validator->is_valid_name($name) &&
$validator->is_valid_mail($mail) &&
!$validator->is_blocked($name, $mail, $msg)) {
$headers = 'From: '.$name.' <'.$mail.'>';
$message = 'DATE = '.date(DATE_ATOM)."\n";
$message .= 'REMOTE_ADDR = '.$_SERVER['REMOTE_ADDR']."\n";
$message .= 'HTTP_REFERER = '.$_SERVER['HTTP_REFERER']."\n";
$message .= 'HTTP_USER_AGENT = '.$_SERVER['HTTP_USER_AGENT']."\n";
if (!empty($msg)) {
$message .= "\n";
$message .= $msg."\n";
}
if ($attachment) {
$filename = $_FILES['attachment']['name'];
$filename_dst = '';
$compress = false;
if ($uploader->has_allowed_ext($filename)) {
$filename_dst = $uploader->get_valid_dst($filename);
} else {
$filename_dst = $uploader->get_valid_dst($filename.'.gz');
$compress = true;
}
$message .= "\n";
$message .= "ATTACHMENT\n";
$message .= $filename."\n";
if ($uploader->handle_upload($filename_dst, 'attachment',
$compress)) {
$message .= $cfg_upload->link.$cfg_upload->dst_dir.'/'.
basename($filename_dst)."\n";
$attachment_uploaded = true;
} else {
$message .= 'Error: '.$uploader->get_error_message(
$_FILES['attachment']['error'])."\n";
}
}
if (mail($cfg_mail->to, $cfg_mail->subject, $message, $headers)) {
$sent = true;
}
}
}
//$html_contact->print_head();
$max_file_size = $uploader->get_max_file_size();
if (!empty($name) || !empty($mail) || !empty($msg) || $attachment) {
if ($sent) {
if ($attachment) {
if ($attachment_uploaded) {
$html_contact->print_info_attachment_uploaded();
} else {
$html_contact->print_error_attachment($max_file_size);
}
}
$html_contact->print_info_msg_sent();
$msg = '';
} else {
$html_contact->print_error_msg();
}
} else {
$html_contact->print_welcome();
}
$html_contact->print_form($name, $mail, $msg, $max_file_size);
//$html_contact->print_foot();
?>