<?php

# PLUGIN PREVIEW BY TEXTPATTERN.INFO

if (txpinterface === 'public') {
    
register_callback('ext_file_attach''comconnect.deliver');

    
// Register tags if necessary.
    
if (class_exists('\Textpattern\Tag\Registry')) {
        
Txp::get('\Textpattern\Tag\Registry')
            ->
register('com_connect_file');
    }
}

/**
 * Callback hook for com_connect to handle attaching the file.
 *
 * @param  string $evt     Textpattern event
 * @param  string $stp     Textpattern step (action)
 * @param  array  $payload Delivery content, passed in from com_connect
 */
function ext_file_attach($evt$stp, &$payload)
{
    global 
$com_connect_error;

    
$file_attached false;

    foreach (
$payload['fields'] as $key => $value) {
        if (
strpos($key'ext_file_') === 0) {
            
$file_size $value['size'];
            
$file_type $value['type'];
            
$file_name $value['name'];
            
$file_temp $value['tmp_name'];
            
$file_error $value['error'];
            
$field current($value);
            
$out '';

            
// Check for errors.
            // This is rarely triggered unfortunately because most browsers validate file sizes and types,
            // throwing empty arrays or just silently failing on our behalf. Grrr.
            // Not only that, com_connect doesn't know what to do with any error strings at the moment so it'd just
            // report a generic 'sorry' message.
            
if ($file_error 0) {
                switch (
$file_error) {
                    case 
1:
                    case 
2:
                        
$max ext_file_max();
                        
$com_connect_error[] = gTxt('com_connect_maxval_warning', array('{field}' => $hlabel'{value}' => $max));
                        
$out 'comconnect.fail';
                        break;
                    case 
3:
                        
// File only partially uploaded.
                        
$out 'comconnect.fail';
                        break;
                    case 
4:
                        
// No file uploaded: no worries, ignore it. Field is probably not required.
                        
break;
                    case 
6:
                        
// Missing temporary folder.
                        
$out 'comconnect.fail';
                        break;
                }

                return 
$out;
            } else {
                
$handle fopen($file_temp'r');
                
$content fread($handle$file_size);
                
fclose($handle);

                
// Only one file can be attached per message.
                
$encoded_content chunk_split(base64_encode($content));
                
$file_attached true;
            }

            
// TODO: delete temp file or does PHP do it?
            
break;
        }
    }

    if (
$file_attached) {
        
$fileBoundary md5('boundary1');
        
$textBoundary md5('boundary2');
        
$sep PHP_EOL;

        
$payload['headers']['MIME-Version'] = '1.0';
        
$payload['headers']['content_type'] = 'multipart/mixed; boundary=' $fileBoundary;
        
$payload['body'] = '--' $fileBoundary $sep
            
'Content-Type: multipart/alternative; boundary=' $textBoundary $sep
            
'--' $textBoundary $sep
            
'Content-Type: text/plain; charset=utf-8' $sep
            
$payload['body'] . $sep
            
'--' $textBoundary '--' $sep
            
'--' $fileBoundary $sep
            
'Content-Type:' $file_type '; '
                
'name="' $file_name '"' $sep
            
'Content-Transfer-Encoding:base64' $sep
            
'Content-Disposition:attachment; '
                
'filename="' $file_name '"' $sep
            
'X-Attachment-Id:' rand(10009000) . $sep $sep
            
$encoded_content $sep
            
'--' $fileBoundary '--';
    }

    
// Back to com_connect to mail out the modified content
    
return;
}

/**
 * Tag: Render a file input field.
 *
 * @param  array  $atts Tag attributes
 * @return string HTML
 */
function com_connect_file($atts)
{
    global 
$com_connect_error$com_connect_submit$com_connect_flags;

    
$max_upload_size ext_file_max();

    
extract(com_connect_lAtts(array(
        
'accept'         => '',
        
'break'          => br,
        
'class'          => 'comFile',
        
'html_form'      => $com_connect_flags['this_form'],
        
'isError'        => '',
        
'label'          => gTxt('com_connect_file'),
        
'label_position' => 'before',
        
'max'            => $max_upload_size,
        
'min'            => 0,
        
'placeholder'    => '',
        
'required'       => $com_connect_flags['required'],
        
'type'           => 'file',
    ), 
$atts));

    
$doctype get_pref('doctype''xhtml');

    if (empty(
$name)) {
        
$name com_connect_label2name($label);
    }

    if (
$com_connect_submit) {
        
$hlabel txpspecialchars($label);

        if (
array_key_exists($name$_FILES)) {
            
$fileInfo $_FILES[$name];
            
$acceptableTypes do_list($accept);

            if (
$fileInfo['size'] && ($fileInfo['size'] > $max)) {
                
$com_connect_error[] = gTxt('com_connect_maxval_warning', array('{field}' => $hlabel'{value}' => $max));
                
$isError "errorElement";
            } elseif (
$accept && $fileInfo['name'] !== '') {
                
$isOK false;

                foreach (
$acceptableTypes as $acceptable) {
                    if (
strpos($acceptable'.') === 0) {
                        
// It's a file extension check.
                        
if (strpos($fileInfo['name'], $acceptable) !== false) {
                            
$isOK true;
                            break;
                        }
                    } else {
                        
// It's a MIME type check.
                        
if (in_array($fileInfo['type'], $acceptableTypes)) {
                            
$isOK true;
                            break;
                        }
                    }
                }

                if (
$isOK) {
                    
com_connect_store('ext_file_' $name$label$fileInfo);
                } else {
                    
$com_connect_error[] = gTxt('ext_file_invalid_type', array('{field}' => $hlabel));
                    
$isError "errorElement";
                }
            } else {
                
com_connect_store('ext_file_' $name$label$fileInfo);
            }
        } elseif (
$required && empty($_FILES)) {
            
$com_connect_error[] = gTxt('com_connect_maxval_warning', array('{field}' => $hlabel'{value}' => $max));
            
$isError "errorElement";
        } elseif (
$required) {
            
$com_connect_error[] = gTxt('com_connect_field_missing', array('{field}' => $hlabel));
            
$isError "errorElement";
        }
    }

    
// Core attributes.
    
$attr com_connect_build_atts(array(
        
'accept' => $accept,
        
'id'     => (isset($id) ? $id $name),
        
'name'   => $name,
        
'type'   => $type,
    ));

    if (
$min) {
        
$attr['minlength'] = 'minlength="' intval($min) . '"';
    }

    if (
$max) {
        
$attr['maxlength'] = 'maxlength="' intval($max) . '"';
    }

    
// HTML5 attributes.
    
$required = ($required) ? 'required' '';

    if (
$doctype !== 'xhtml') {
        
$attr += com_connect_build_atts(array(
            
'form'         => $html_form,
            
'placeholder'  => $placeholder,
            
'required'     => $required,
        ));
    }

    
// Global attributes.
    
$attr += com_connect_build_atts($com_connect_globals$atts);

    
$classes = array();

    foreach (array(
$class, ($required 'comRequired' ''), $isError) as $cls) {
        if (
$cls) {
            
$classes[] = $cls;
        }
    }

    
$classStr = ($classes ' class="' implode(' '$classes) . '"' '');
    
$labelStr '<label for="' $name '"' $classStr '>' txpspecialchars($label) . '</label>';

    return (
$label_position === 'before' $labelStr $break '') .
        
'<input' $classStr . ($attr ' ' implode(' '$attr) : '') . ' />' .
        (
$label_position === 'after' $break $labelStr '') .
        
script_js(<<<EOJS
function com_ext_attach_handler() {
    var b = document.getElementById('
{$html_form}');
    b.setAttribute("enctype", "multipart/form-data");
}

if (document.readyState != 'loading') {
    com_ext_attach_handler();
} else if (document.addEventListener) {
    document.addEventListener('DOMContentLoaded', com_ext_attach_handler);
} else document.attachEvent('onreadystatechange', function() {
    if (document.readyState=='interactive') {
        com_ext_attach_handler()();
    }
});
EOJS
);
}

// Returns a file size limit in bytes.
function ext_file_max()
{
    
$max_size = -1;

    if (
$max_size 0) {
        
// Start with post_max_size.
        
$max_size ext_file_parse_size(ini_get('post_max_size'));

        
// If upload_max_size is less, then reduce. Except if
        // zero, which indicates no limit.
        
$upload_max ext_file_parse_size(ini_get('upload_max_filesize'));

        if (
$upload_max && $upload_max $max_size) {
            
$max_size $upload_max;
        }

        
// If Textpattern's file_max_upload_size is less, then reduce. Except if
        // zero, which indicates no limit.
        
$upload_max get_pref('file_max_upload_size');

        if (
$upload_max && $upload_max $max_size) {
            
$max_size $upload_max;
        }
    }

    return 
$max_size;
}

/**
 * Convert a size value with suffix (K, M, G, T, etc) to bytes.
 */
function ext_file_parse_size($size)
{
    
$unit preg_replace('/[^bkmgtpezy]/i'''$size); // Remove the non-unit characters from the size.
    
$size preg_replace('/[^0-9\.\,]/'''$size); // Remove the non-numeric characters from the size.

   
if ($unit) {
        
// Find the position of the unit in the ordered string which is
        // the power of magnitude to multiply a kilobyte by.
        
return round($size pow(1024stripos('bkmgtpezy'$unit[0])));
    } else {
        return 
round($size);
    }
}