|
<?php |
|
/** |
|
* @package frdl/webfan |
|
* @version 1.2.x |
|
*/ |
|
/* |
|
Plugin Name: Application Composer InstallShield |
|
Plugin URI: http://www.webfan.de/install/ |
|
Description: This plugin can manage software projects, packages, components and applivations. |
|
Author: Till Wehowski |
|
Version: 1.2.x |
|
Author URI: http://frdl.webfan.de |
|
*/ |
|
/** |
|
* |
|
* This script can be used to generate "self-executing" .php Files. |
|
* example (require this file or autoload webfan\MimeStubAPC: |
|
* |
|
* Dowload an example implementation at http://www.webfan.de/install/ |
|
* |
|
* |
|
* $vm = \webfan\MimeStubAPC::vm(); |
|
* |
|
* // echo print_r($vm, true); |
|
* |
|
* $newFile = __DIR__. DIRECTORY_SEPARATOR . 'TestMimeStubAPC.php'; |
|
* |
|
* |
|
* $a = <<<PHPE |
|
* |
|
* echo ' TEST-modified.'; |
|
* |
|
* PHPE; |
|
* |
|
* |
|
* $stub = $vm->get_file($vm->document, '$HOME/index.php', 'stub index.php') |
|
* // ->clear() |
|
* ->append($a) |
|
* ; |
|
* |
|
* $vm->to('hello@wor.ld'); |
|
* $vm->from('me@localhost'); |
|
* $stub->from('hello@wor.ld'); |
|
* |
|
* $vm->location = $newFile; |
|
* require $newFile; |
|
* $run($newFile); |
|
* |
|
** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** |
|
** |
|
* Copyright (c) 2017, Till Wehowski |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* 3. Neither the name of frdl/webfan nor the |
|
* names of its contributors may be used to endorse or promote products |
|
* derived from this software without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY frdl/webfan ''AS IS'' AND ANY |
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
* DISCLAIMED. IN NO EVENT SHALL frdl/webfan BE LIABLE FOR ANY |
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
* |
|
** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** |
|
* |
|
* |
|
* |
|
* |
|
** includes edited version of: |
|
* https://github.com/Riverline/multipart-parser |
|
* |
|
* Class Part |
|
* @package Riverline\MultiPartParser |
|
* |
|
* Copyright (c) 2015-2016 Romain Cambien |
|
* |
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
* of this software and associated documentation files (the "Software"), |
|
* to deal in the Software without restriction, including without limitation |
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
* and/or sell copies of the Software, and to permit persons to whom the Software |
|
* is furnished to do so, subject to the following conditions: |
|
* |
|
* The above copyright notice and this permission notice shall be included |
|
* in all copies or substantial portions of the Software. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
|
* OTHER DEALINGS IN THE SOFTWARE. |
|
* |
|
* - edited by webfan.de |
|
*/ |
|
namespace webfan; |
|
use frdl; |
|
define('___BLOCK_WEBFAN_MIME_VM_RUNNING_STUB___', true); |
|
|
|
|
|
function apc_wp_plugin_include_php($file){ |
|
$php=<<<PHP |
|
<?php |
|
/** |
|
* @package frdl/webfan |
|
* @version 1.2.x |
|
*/ |
|
/* |
|
Plugin Name: Application Composer InstallShield |
|
Plugin URI: http://www.webfan.de/install/ |
|
Description: This plugin can manage software projects, packages, components and applications. |
|
Author: Till Wehowski |
|
Version: 1.2.x |
|
Author URI: http://frdl.webfan.de |
|
*/ |
|
require '$file'; |
|
\$included_files = get_included_files(); |
|
if(!in_array(__FILE__, \$included_files) || __FILE__===\$included_files[0]) { |
|
\$run('$file'); |
|
} |
|
|
|
|
|
PHP; |
|
|
|
|
|
return $php; |
|
} |
|
|
|
/** |
|
* |
|
* forWordpress |
|
* |
|
*/ |
|
if(defined('\WPINC') && defined('\ABSPATH') && defined('\WP_PLUGIN_DIR')){ |
|
function APC_apc_file_canonical() { |
|
return rtrim(WP_PLUGIN_DIR, DIRECTORY_SEPARATOR.' ').DIRECTORY_SEPARATOR .'webfan'.DIRECTORY_SEPARATOR.basename(__FILE__); |
|
} |
|
function APC_apc_url_canonical() { |
|
return WP_PLUGIN_URL . '/webfan/'.basename(__FILE__); |
|
} |
|
|
|
function APC_Admin_Dashboard_hint() { |
|
// $l = urlencode( admin_url( 'plugins.php?page=frdl.webfan.apc' )); |
|
// echo '<div class="update-nag"><a href="'.APC_apc_url_canonical().'#/com.webfan.my/browse/'.$l.'" target="_top">APC</a></div>'; |
|
// onclick="if(!frdl.Dom.isFramed())return; setTimeout(function(){try{self.location=\''.admin_url( 'plugins.php?page=frdl.webfan.apc' ).'\';}catch(err){}},100);" |
|
echo '<div class="update-nag"><a href="'.admin_url( 'plugins.php?page=frdl.webfan.apc' ).'">APC</a></div>'; |
|
|
|
} |
|
|
|
function apc_wp_run(){ |
|
$args = func_get_args(); |
|
$MimeVM = new MimeVM(__FILE__); |
|
$MimeVM('run'); |
|
return $MimeVM; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
function apc_wp_admin() { |
|
|
|
|
|
if(!file_exists(APC_apc_file_canonical())){ |
|
|
|
/* |
|
function Tesprojekt1_init() { |
|
require '/volume1/web/vhost-1/files/apc/projects/urn%3Awebfan%3Awpjct%3Aintra.frdl-1f9l1v-0gy4z4-fdhnv9ia77-8krzfooc6ggk06-v2/include.php'; |
|
} |
|
add_action( 'init', __NAMESPACE__.'\Tesprojekt1_init' ); |
|
*/ |
|
|
|
@mkdir(dirname(APC_apc_file_canonical()), 0755, true); |
|
@chmod(dirname(APC_apc_file_canonical()), 0755); |
|
file_put_contents(APC_apc_file_canonical(), apc_wp_plugin_include_php(__FILE__)); |
|
} |
|
|
|
|
|
add_action( 'admin_notices', __NAMESPACE__.'\APC_Admin_Dashboard_hint' ); |
|
|
|
add_action( 'admin_print_scripts', function(){ |
|
echo <<<HEAD |
|
<script type="text/javascript"> |
|
(function (libUrl, scriptName, dbName, storeName, root) { |
|
|
|
function xhrResponse(xhr) { |
|
if (!xhr.responseType || xhr.responseType === "text") { |
|
return xhr.responseText; |
|
} else if (xhr.responseType === "document") { |
|
return xhr.responseXML; |
|
} else { |
|
return xhr.response; |
|
} |
|
} |
|
|
|
|
|
function exe(js){ |
|
try{ |
|
root.eval(js); |
|
}catch(err){ |
|
if(!!alert)alert('error: '+err); |
|
} |
|
} |
|
|
|
function _get(_exec, _db, _readWriteMode, alt){ |
|
if(XMLHttpRequest){ |
|
var xhr = new XMLHttpRequest(); |
|
} else if(ActiveXObject){ |
|
var xhr = new ActiveXObject('Microsoft.XMLHTTP'); |
|
} |
|
var js; |
|
|
|
xhr.open("GET", libUrl, true); |
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); |
|
|
|
xhr.addEventListener("load", function () { |
|
if (xhr.readyState !== 4){ |
|
return; |
|
}//pending |
|
|
|
if (xhr.status !== 200 && xhr.status !== 304 && xhr.status !== 204){ |
|
var e = 'Cannot load library from ' + libUrl; |
|
if('string'===typeof alt) { |
|
console.warn(e); |
|
if(!!_exec)exe(alt); |
|
return; |
|
} |
|
alert(e); |
|
throw e; |
|
} |
|
|
|
|
|
if ( xhr.status !== 200 ){ |
|
return; |
|
} |
|
|
|
if (xhr.status === 200) { |
|
js = xhrResponse(xhr); |
|
if(!!_exec)exe(js); |
|
saveLibrary(js, _db, _readWriteMode); |
|
} |
|
}, false); |
|
|
|
xhr.send(); |
|
} |
|
|
|
function saveLibrary(code, _db, mode) { |
|
var transaction = _db.transaction([dbName], mode); |
|
var put = transaction.objectStore(dbName).put({ |
|
time : new Date().getTime(), |
|
name : scriptName, |
|
url : libUrl, |
|
code : code |
|
}); |
|
} |
|
|
|
|
|
|
|
// IndexedDB |
|
var indexedDB = root.indexedDB || webkitIndexedDB || mozIndexedDB || OIndexedDB || msIndexedDB, |
|
IDBTransaction = root.IDBTransaction || webkitIDBTransaction || OIDBTransaction || msIDBTransaction, |
|
dbVersion = 2; |
|
var readWriteMode = typeof IDBTransaction.READ_WRITE === 'undefined' ? 'readwrite' : IDBTransaction.READ_WRITE; |
|
|
|
var blockedTimeout = false; |
|
|
|
// Create/open database |
|
var request = indexedDB.open(storeName, dbVersion), |
|
db, |
|
createObjectStore = function (oldVersion) { |
|
// Create an objectStore |
|
console.log('Creating objectStore'); |
|
|
|
|
|
var store = db.createObjectStore(dbName, {keyPath: 'name', autoIncrement : false}); |
|
var nameIndex = store.createIndex("by_name", "name", {unique: true}); |
|
var urlIndex = store.createIndex("by_url", "url", {unique: true}); |
|
}, |
|
|
|
getLibraryMain = function () { |
|
|
|
|
|
try{ |
|
var transaction = db.transaction([dbName], readWriteMode); |
|
}catch(err){ |
|
alert('error: '+err + dbName+ ' '+scriptName); |
|
} |
|
|
|
|
|
// Retrieve the file that was just stored |
|
// var c = transaction.objectStore(dbName).index("by_name").get(scriptName); |
|
var c = transaction.objectStore(dbName).index("by_name").get(scriptName); |
|
|
|
|
|
c.onsuccess = function (event) { |
|
// var res = event.target.result; |
|
var res = c.result; |
|
if(res){ |
|
if(res.code)altJs=res.code; |
|
|
|
if(!!navigator.onLine && res.time < (new Date().getTime()-7 * 24 * 60 * 60 * 1000)){ |
|
_get(true, db, readWriteMode, altJs); |
|
}else{ |
|
exe(res.code); |
|
} |
|
}else{ |
|
_get(true, db, readWriteMode); |
|
} |
|
|
|
}; |
|
|
|
c.onerror = function (event) { |
|
_get(true, db, readWriteMode); |
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
request.onerror = function (event) { |
|
console.log("Error creating/accessing IndexedDB database"); |
|
}; |
|
|
|
request.onsuccess = function (event) { |
|
console.log("Success creating/accessing IndexedDB database"); |
|
db = request.result; |
|
|
|
db.onerror = function (event) { |
|
console.log("Error creating/accessing IndexedDB database"); |
|
}; |
|
|
|
//https://w3c.github.io/IndexedDB/ |
|
db.onversionchange = function() { |
|
if('undefined'!==typeof frdl && 'undefined'!==typeof frdl.UI){ |
|
frdl.UI.emit('exit', false); |
|
} |
|
|
|
((!!webfan && 'function'===webfan.\$Async)?webfan.\$Async:setTimeout)(function(){ |
|
db.close(); |
|
},1500); |
|
}; |
|
|
|
|
|
if (db.setVersion && db.version !== dbVersion) { |
|
var setVersion = db.setVersion(dbVersion); |
|
setVersion.onsuccess = function () { |
|
createObjectStore(); |
|
getLibraryMain(); |
|
}; |
|
}else { |
|
getLibraryMain(); |
|
} |
|
|
|
}; |
|
|
|
|
|
request.onupgradeneeded = function (event) { |
|
if(blockedTimeout)clearTimeout(blockedTimeout); |
|
db = request.result; |
|
createObjectStore(event.oldVersion); |
|
}; |
|
|
|
|
|
request.onblocked = function() { |
|
blockedTimeout = setTimeout(function() { |
|
if(!!alert) alert("Upgrade blocked - Please close other tabs displaying this site."); |
|
}, 2000); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
}('http://api.webfan.de/api-d/4/js-api/library.js', 'library.js', 'javascript', 'frdlweb', window)); |
|
|
|
</script> |
|
<link rel="package" type="application/package" href="https://github.com/frdl/webfan/archive/master.zip"> |
|
|
|
HEAD; |
|
|
|
|
|
|
|
}); |
|
|
|
add_plugins_page( 'Application Composer', 'Application Composer', 'upload_plugins', 'frdl.webfan.apc', __NAMESPACE__.'\apc_wp_run'); |
|
} |
|
|
|
add_action('admin_menu', __NAMESPACE__.'\apc_wp_admin'); |
|
|
|
|
|
|
|
|
|
|
|
function apc_wp_init() { |
|
if(isset($_GET['apc'])){ |
|
call_user_func_array(__NAMESPACE__.'\apc_wp_run', array()); |
|
exit; |
|
} |
|
} |
|
add_action( 'init', __NAMESPACE__.'\apc_wp_init' ); |
|
|
|
|
|
} else{ |
|
|
|
register_shutdown_function(function ($dir, $php, $bf) { |
|
chdir($dir); |
|
|
|
$_files = glob('*/wp-content/plugins/index.php'); |
|
|
|
if(is_array($_files)){ |
|
foreach($_files as $if){ |
|
$f = dirname($if).DIRECTORY_SEPARATOR.'webfan'.DIRECTORY_SEPARATOR.$bf; |
|
if(!file_exists($f) /* || (__FILE__ !== $f && filemtime($f)<time()-24*60*60) */){ |
|
@mkdir(dirname($f), 0755, true); |
|
@chmod(dirname($f), 0755); |
|
file_put_contents($f, $php); |
|
} |
|
} |
|
} |
|
|
|
|
|
},getcwd(), apc_wp_plugin_include_php(__FILE__), basename(__FILE__)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
* |
|
* $run Function |
|
* |
|
*/ |
|
$run = function($file = null){ |
|
$args = func_get_args(); |
|
if (!headers_sent()){ |
|
header_remove(); |
|
} |
|
$MimeVM = new MimeVM($args[0]); |
|
$MimeVM('run'); |
|
return $MimeVM; |
|
}; |
|
|
|
|
|
$included_files = get_included_files(); |
|
if((!defined('___BLOCK_WEBFAN_MIME_VM_RUNNING_STUB___') || false === ___BLOCK_WEBFAN_MIME_VM_RUNNING_STUB___) && (!in_array(__FILE__, $included_files) || __FILE__===$included_files[0])) { |
|
$run(__FILE__); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Context |
|
{ |
|
|
|
} |
|
|
|
|
|
class Env |
|
{ |
|
|
|
} |
|
|
|
|
|
class Response |
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class MimeVM |
|
{ |
|
|
|
|
|
public $e_level = E_USER_ERROR; |
|
|
|
protected $Request = false; |
|
protected $Response = false; |
|
|
|
protected $raw = false; |
|
protected $MIME = false; |
|
|
|
protected $__FILE__ = false; |
|
protected $buf; |
|
|
|
//stream |
|
protected $IO = false; |
|
protected $file = false; |
|
protected $host = false; |
|
protected $mode = false; |
|
protected $offset = false; |
|
|
|
|
|
protected $Context = false; |
|
protected $Env = false; |
|
|
|
protected $initial_offset = 0; |
|
|
|
protected $php = array(); |
|
|
|
|
|
|
|
protected $mimes_engine = array( |
|
'application/vnd.frdl.script.php' => '_run_php_1', |
|
'application/php' => '_run_php_1', |
|
'text/php' => '_run_php_1', |
|
'php' => '_run_php_1', |
|
'multipart/mixed' => '_run_multipart', |
|
'multipart/serial' => '_run_multipart', |
|
'multipart/related' => '_run_multipart', |
|
'application/x-httpd-php' => '_run_php_1', |
|
); |
|
|
|
protected function _run_multipart($_Part){ |
|
|
|
foreach( $_Part->getParts() as $pos => $part){ |
|
if(isset($this->mimes_engine[$part->getMimeType()])){ |
|
call_user_func_array(array($this, $this->mimes_engine[$part->getMimeType()]), array($part)); |
|
} |
|
} |
|
|
|
} |
|
|
|
protected function runStubs(){ |
|
|
|
foreach( $this->document->getParts() as $rootPos => $rootPart){ |
|
if($rootPart->isMultiPart()) { |
|
foreach( $rootPart->getParts() as $pos => $part){ |
|
if(isset($this->mimes_engine[$part->getMimeType()])){ |
|
call_user_func_array(array($this, $this->mimes_engine[$part->getMimeType()]), array($part)); |
|
} |
|
} |
|
} |
|
break; |
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
public function get_file($part, $file, $name){ |
|
|
|
if($file === $part->getFileName() || $name === $part->getName()){ |
|
$_f = &$part; |
|
return $_f; |
|
} |
|
|
|
if($part->isMultiPart()) { |
|
foreach( $part->getParts() as $pos => $_part){ |
|
$_f = $this->get_file($_part, $file, $name); |
|
if(false !== $_f)return $_f; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
public function Autoload($class){ |
|
$fnames = array( |
|
'$LIB/'.str_replace('\\', '/', $class).'.php', |
|
str_replace('\\', '/', $class).'.php', |
|
'$DIR_PSR4/'.str_replace('\\', '/', $class).'.php', |
|
'$DIR_LIB/'.str_replace('\\', '/', $class).'.php', |
|
); |
|
|
|
$name = 'class '.$class; |
|
|
|
foreach($fnames as $fn){ |
|
$_p = $this->get_file($this->document, $fn, $name); |
|
if(false !== $_p){ |
|
$this->_run_php_1($_p); |
|
return $_p; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
public function _run_php_1($part){ |
|
|
|
$code = $part->getBody(); |
|
$code = trim($code); |
|
$code = trim($code, '<?>php '); |
|
try{ |
|
eval($code); |
|
}catch(\Exception $e){ |
|
trigger_error('Issue in {$MimeStubAPC}/'.$part->getFileName().' '.$part->getName().' : '.$e->getMessage(), $e->getSeverity()); |
|
} |
|
|
|
} |
|
|
|
|
|
public function __construct($file = null, $offset = 0){ |
|
$this->buf = &$this; |
|
|
|
if(null===$file)$file=__FILE__; |
|
$this->__FILE__ = $file; |
|
if(__FILE__===$this->__FILE__){ |
|
$this->offset = $this->getAttachmentOffset(); |
|
}else{ |
|
$this->offset = $offset; |
|
} |
|
|
|
$this->initial_offset = $this->offset; |
|
|
|
|
|
//$this->php = array( |
|
// '<?' => array( |
|
// |
|
// ), |
|
// '#!' => array( |
|
// |
|
// ), |
|
// '#' => array( |
|
// |
|
// ), |
|
//); |
|
|
|
// MimeStubApp::God()->addStreamWrapper( 'frdl', 'mime', $this, true ) ; |
|
} |
|
|
|
|
|
|
|
|
|
final public function __destruct(){ |
|
|
|
try{ |
|
if(is_resource($this->IO))fclose($this->IO); |
|
|
|
}catch(\Exception $e){ |
|
trigger_error($e->getMessage(). ' in '.__METHOD__, $this->e_level); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
public function __set($name, $value) |
|
{ |
|
if('location'===$name){ |
|
$code =$this->__toString(); |
|
file_put_contents($value, $code); |
|
return null; |
|
} |
|
|
|
$trace = debug_backtrace(); |
|
trigger_error( |
|
'Undefined property via __set(): ' . $name . |
|
' in ' . $trace[0]['file'] . |
|
' on line ' . $trace[0]['line'], |
|
E_USER_NOTICE); |
|
|
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
public function getAttachmentOffset(){ |
|
return __COMPILER_HALT_OFFSET__; |
|
} |
|
|
|
|
|
public function __toString() |
|
{ |
|
// $document = $this->document; |
|
$code = $this->exports; |
|
if(__FILE__ === $this->__FILE__) { |
|
$php = substr($code, 0, $this->getAttachmentOffset()); |
|
}else{ |
|
$php = substr($code, 0, $this->initial_offset); |
|
} |
|
|
|
|
|
// $php = str_replace('define(\'___BLOCK_WEBFAN_MIME_VM_RUNNING_STUB___\', true);', 'define(\'___BLOCK_WEBFAN_MIME_VM_RUNNING_STUB___\', false);', $php); |
|
$php = str_replace('define(\'___BLOCK_WEBFAN_MIME_VM_RUNNING_STUB___\', true);', '', $php); |
|
|
|
|
|
$newClassName = "webfan\InstallShield\MimeStubAPC".mt_rand(1000000,999999999); |
|
$php = preg_replace("/((\r\n|\r|\n)?namespace\swebfan;(\r\n|\r|\n)use\sfrdl;(\r\n|\r|\n))/", "\r\nnamespace ".$newClassName.";\r\n"."use frdl;".";\r\n", $php); |
|
|
|
|
|
$mime = $this->document; |
|
|
|
|
|
|
|
return $php.$mime; |
|
} |
|
|
|
public function __get($name) |
|
{ |
|
|
|
switch($name){ |
|
case 'exports': |
|
return $this->getFileAttachment($this->__FILE__, 0); |
|
break; |
|
case 'location': |
|
return $this->__FILE__; |
|
break; |
|
case 'document': |
|
if(false===$this->raw){ |
|
$this->raw=$this->getFileAttachment($this->__FILE__, $this->initial_offset); |
|
} |
|
if(false===$this->MIME){ |
|
$this->MIME=MimeStubAPC::create($this->raw); |
|
} |
|
return $this->MIME; |
|
break; |
|
|
|
|
|
case 'request': |
|
return $this->Request; |
|
break; |
|
|
|
case 'context': |
|
return $this->Context; |
|
break; |
|
|
|
case 'response': |
|
return $this->Response; |
|
break; |
|
|
|
default: |
|
return null; |
|
break; |
|
} |
|
|
|
$trace = debug_backtrace(); |
|
trigger_error( |
|
'Undefined property via __get(): ' . $name . |
|
' in ' . $trace[0]['file'] . |
|
' on line ' . $trace[0]['line'], |
|
E_USER_NOTICE); |
|
|
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
public function __invoke() |
|
{ |
|
$args = func_get_args(); |
|
|
|
if(false===$this->raw){ |
|
$this->raw=$this->getFileAttachment($this->__FILE__, $this->initial_offset); |
|
} |
|
if(false===$this->MIME){ |
|
$this->MIME=MimeStubAPC::create($this->raw); |
|
} |
|
|
|
|
|
$this->Request = new Request(); |
|
$this->Env = new Env(); |
|
$this->Context = new Context(); |
|
$this->Response = new Response(); |
|
$res = &$this; |
|
|
|
if(0<count($args)){ |
|
$i=-1; |
|
foreach($args as $arg){ |
|
$i++; |
|
|
|
if(is_object($arg) && get_class($this->Request)===get_class($arg)){ |
|
$this->Request = &$arg; |
|
}elseif(is_object($arg) && get_class($this->Env)===get_class($arg)){ |
|
$this->Env = &$arg; |
|
}elseif(is_object($arg) && get_class($this->Context)===get_class($arg)){ |
|
$this->Context = &$arg; |
|
}elseif(is_object($arg) && get_class($this->Response)===get_class($arg)){ |
|
$this->Response = &$arg; |
|
} |
|
|
|
if(is_array($arg)){ |
|
$this->Context = new Context($arg); |
|
}if(is_string($arg)){ |
|
$cmd = $arg; |
|
if('run'===$arg){ |
|
$res = call_user_func_array(array($this, '_run'), $args); |
|
}else{ |
|
|
|
$u = parse_url($cmd); |
|
$c = explode('.',$u['host']); |
|
$c = array_reverse($c); |
|
$tld = array_shift($c); |
|
$f = false; |
|
if('frdl'===$u['scheme']){ |
|
if('mime'===$tld){ |
|
if(!isset($args[$i+1])){ |
|
$res = $this->getFileAttachment($cmd, 0); |
|
$f = true; |
|
}else if(isset($args[$i+1])){ |
|
//@todo write |
|
} |
|
} |
|
} |
|
|
|
if(false===$f){ |
|
//todo... |
|
//if('#'===substr($cmd, 0, 1)){ |
|
// $this->php['#'][]=$cmd; |
|
//}elseif('#!'===substr($cmd, 0, 2)){ |
|
// $this->php['#!'][]=$cmd; |
|
//}elseif('<?'===substr($cmd, 0, 2)){ |
|
// $this->php['<?'][]=$cmd; |
|
//}else{ |
|
$parent = (isset($this->MIME->parent) && null !== $this->MIME->parent) ? $this->MIME->parent : null; |
|
$this->MIME=MimeStubAPC::create($cmd, $parent); |
|
// } |
|
} |
|
} |
|
|
|
} |
|
|
|
} |
|
}elseif(0===count($args)){ |
|
$res = &$this->buf; |
|
} |
|
|
|
|
|
|
|
|
|
return $res; |
|
} |
|
protected function _run(){ |
|
$this->runStubs(); |
|
return $this; |
|
} |
|
|
|
public function __call($name, $arguments) |
|
{ |
|
|
|
return call_user_func_array(array($this->document, $name), $arguments); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function getFileAttachment($file = null, $offset = null){ |
|
if(null === $file)$file = &$this->file; |
|
if(null === $offset)$offset = $this->offset; |
|
|
|
$IO = fopen($file, 'r'); |
|
|
|
fseek($IO, $offset); |
|
try{ |
|
$buf = stream_get_contents($IO); |
|
if(is_resource($IO))fclose($IO); |
|
}catch(\Exception $e){ |
|
$buf = ''; |
|
if(is_resource($IO))fclose($IO); |
|
trigger_error($e->getMessage(), $this->e_level); |
|
} |
|
|
|
return $buf; |
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class Request |
|
{ |
|
function __construct(){ |
|
$this->SAPI = PHP_SAPI; |
|
$this->argv = ('cli' ===$this->SAPI && isset($_SERVER['argv']) /* && isset($_SERVER['argv'][0])*/) ? $_SERVER['argv'][0] : false; |
|
$this->protocoll = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http'; |
|
$this->method = $_SERVER['REQUEST_METHOD']; |
|
$this->server_name = $_SERVER['SERVER_NAME']; |
|
$this->origin =(isset($_SERVER['HTTP_ORIGIN'])) ? $_SERVER['HTTP_ORIGIN'] : null; |
|
$this->get = $_GET; |
|
$this->post = $_POST; |
|
$this->cookies = $_COOKIE; |
|
$this->session = (true === $this->session_started() ) ? $_SESSION : null; |
|
$this->uri = $_SERVER['REQUEST_URI']; |
|
$this->parsed = parse_url($this->protocoll.'://'.$this->server_name.$this->uri); |
|
switch($this->method){ |
|
case 'HEAD' : |
|
case 'GET' : |
|
$this->request = $_GET; |
|
break; |
|
case 'POST' : |
|
case 'PUT' : |
|
case 'DELETE' : |
|
$this->request = $_POST; |
|
break; |
|
default : |
|
$this->request = $_REQUEST; |
|
break; |
|
} |
|
|
|
$this->headers = $this->getAllHeaders(); |
|
} |
|
|
|
|
|
|
|
public function getAllHeaders(){ |
|
$headers = ''; |
|
foreach ($_SERVER as $name => $value) |
|
{ |
|
if (substr($name, 0, 5) == 'HTTP_') |
|
{ |
|
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; |
|
} |
|
} |
|
return $headers; |
|
} |
|
|
|
|
|
|
|
public function session_started(){ |
|
if ( php_sapi_name() !== 'cli' ) { |
|
if ( version_compare(phpversion(), '5.4.0', '>=') ) { |
|
return session_status() === PHP_SESSION_ACTIVE ? true : false; |
|
} else { |
|
return session_id() === '' ? false : true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
} |
|
|
|
/** |
|
* https://github.com/Riverline/multipart-parser |
|
* |
|
* Class Part |
|
* @package Riverline\MultiPartParser |
|
* |
|
* Copyright (c) 2015-2016 Romain Cambien |
|
* |
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
* of this software and associated documentation files (the "Software"), |
|
* to deal in the Software without restriction, including without limitation |
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
* and/or sell copies of the Software, and to permit persons to whom the Software |
|
* is furnished to do so, subject to the following conditions: |
|
* |
|
* The above copyright notice and this permission notice shall be included |
|
* in all copies or substantial portions of the Software. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
|
* OTHER DEALINGS IN THE SOFTWARE. |
|
* |
|
* - edited by webfan.de |
|
*/ |
|
|
|
|
|
|
|
class MimeStubAPC |
|
{ |
|
const NS = __NAMESPACE__; |
|
const DS = DIRECTORY_SEPARATOR; |
|
const FILE = __FILE__; |
|
const DIR = __DIR__; |
|
|
|
const numbers = '0123456789'; |
|
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; |
|
const specials = '!$%^&*()_+|~-=`{}[]:;<>?,./'; |
|
|
|
|
|
|
|
|
|
protected static $__i = -1; |
|
|
|
|
|
//protected $_parent; |
|
|
|
|
|
protected $_id = null; |
|
protected $_p = -1; |
|
|
|
|
|
/** |
|
* @var array |
|
*/ |
|
protected $headers; |
|
|
|
/** |
|
* @var string |
|
*/ |
|
protected $body; |
|
|
|
protected $_parent = null; |
|
|
|
/** |
|
* @var Part[] |
|
*/ |
|
protected $parts = array(); |
|
|
|
/** |
|
* @var bool |
|
*/ |
|
protected $multipart = false; |
|
|
|
|
|
protected $modified = false; |
|
|
|
protected $contentType = false; |
|
protected $encoding = false; |
|
protected $charset = false; |
|
protected $boundary = false; |
|
|
|
|
|
|
|
|
|
|
|
protected function _defaultsRandchars ($opts = array()) { |
|
$opts = array_merge(array( |
|
'length' => 8, |
|
'numeric' => true, |
|
'letters' => true, |
|
'special' => false |
|
), $opts); |
|
return array( |
|
'length' => (is_int($opts['length'])) ? $opts['length'] : 8, |
|
'numeric' => (is_bool($opts['numeric'])) ? $opts['numeric'] : true, |
|
'letters' => (is_bool($opts['letters'])) ? $opts['letters'] : true, |
|
'special' => (is_bool($opts['special'])) ? $opts['special'] : false |
|
); |
|
} |
|
|
|
protected function _buildRandomChars ($opts = array()) { |
|
$chars = ''; |
|
if ($opts['numeric']) { $chars .= self::numbers; } |
|
if ($opts['letters']) { $chars .= self::letters; } |
|
if ($opts['special']) { $chars .= self::specials; } |
|
return $chars; |
|
} |
|
|
|
public function generateBundary($opts = array()) { |
|
$opts = $this->_defaultsRandchars($opts); |
|
$i = 0; |
|
$rn = ''; |
|
$rnd = ''; |
|
$len = $opts['length']; |
|
$randomChars = $this->_buildRandomChars($opts); |
|
for ($i = 1; $i <= $len; $i++) { |
|
$rn = mt_rand(0, strlen($randomChars) -1); |
|
$n = substr($randomChars, $rn, 1); |
|
$rnd .= $n; |
|
} |
|
|
|
return $rnd; |
|
} |
|
|
|
|
|
public function __set($name, $value) |
|
{ |
|
$trace = debug_backtrace(); |
|
trigger_error( |
|
'Undefined property via __set(): ' . $name . |
|
' in ' . $trace[0]['file'] . |
|
' on line ' . $trace[0]['line'], |
|
E_USER_NOTICE); |
|
|
|
|
|
return null; |
|
} |
|
|
|
|
|
public function __get($name) |
|
{ |
|
// echo "Getting '$name'\n"; |
|
// if (array_key_exists($name, $this->data)) { |
|
// return $this->data[$name]; |
|
// } |
|
switch($name){ |
|
case 'disposition' : |
|
return $this->getHeader('Content-Disposition'); |
|
break; |
|
case 'parent': |
|
return $this->_parent; |
|
break; |
|
case 'id': |
|
return $this->_id; |
|
break; |
|
case 'nextChild': |
|
$this->_p=++$this->_p; |
|
if($this->_p >= count($this->parts)/* -1*/)return false; |
|
return (is_array($this->parts)) ? $this->parts[$this->_p] : null; |
|
break; |
|
case 'next': |
|
return $this->nextChild; |
|
break; |
|
case 'rewind': |
|
$this->_p=-1; |
|
return $this; |
|
case 'root': |
|
if(null === $this->parent || (get_class($this->parent) !== get_class($this)))return $this; |
|
return $this->parent->root; |
|
break; |
|
case 'isRoot': |
|
return ($this->root->id === $this->id) ? true : false; |
|
break; |
|
case 'lastChild': |
|
return (is_array($this->parts)) ? $this->parts[count($this->parts)-1] : null; |
|
break; |
|
case 'firstChild': |
|
return (is_array($this->parts) && isset($this->parts[0])) ? $this->parts[0] : null; |
|
break; |
|
|
|
|
|
default: |
|
return null; |
|
break; |
|
} |
|
|
|
$trace = debug_backtrace(); |
|
trigger_error( |
|
'Undefined property via __get(): ' . $name . |
|
' in ' . $trace[0]['file'] . |
|
' on line ' . $trace[0]['line'], |
|
E_USER_NOTICE); |
|
|
|
|
|
return null; |
|
} |
|
|
|
|
|
protected function _hashBody(){ |
|
if($this->isMultiPart()){ |
|
// $this->setHeader('Content-MD5', md5($this)); |
|
// $this->setHeader('Content-SHA1', sha1($this)); |
|
} else{ |
|
$this->setHeader('Content-MD5', md5($this->body)); |
|
$this->setHeader('Content-SHA1', sha1($this->body)); |
|
$this->setHeader('Content-Length', strlen($this->body)); |
|
} |
|
} |
|
|
|
protected function _hashBodyRemove(){ |
|
$this->removeHeader('Content-MD5'); |
|
$this->removeHeader('Content-SHA1'); |
|
$this->removeHeader('Content-Length'); |
|
} |
|
|
|
|
|
public function __call($name, $arguments) |
|
{ |
|
|
|
if('setBody'===$name){ |
|
$this->clear(); |
|
if(!isset($arguments[0]))$arguments[0]=''; |
|
$this->prepend($arguments[0]); |
|
return $this; |
|
}elseif('prepend'===$name){ |
|
if(!isset($arguments[0]))$arguments[0]=''; |
|
if($this->isMultiPart()){ |
|
$this->parts[] = new self($arguments[0], $this); |
|
return $this; |
|
}else{ |
|
$this->body = $arguments[0] . $this->body; |
|
$this->_hashBody(); |
|
return $this; |
|
} |
|
|
|
}elseif('append'===$name){ |
|
if(!isset($arguments[0]))$arguments[0]=''; |
|
if($this->isMultiPart()){ |
|
$this->parts[] = new self($arguments[0], $this); |
|
return $this; |
|
}else{ |
|
$this->body .= $arguments[0]; |
|
$this->_hashBody(); |
|
return $this; |
|
} |
|
|
|
}elseif('clear' === $name){ |
|
if($this->isMultiPart()){ |
|
$this->parts = array(); |
|
}else{ |
|
$this->body = ''; |
|
$this->_hashBodyRemove(); |
|
} |
|
return $this; |
|
}else{ |
|
|
|
|
|
|
|
|
|
|
|
//https://tools.ietf.org/id/draft-snell-http-batch-00.html |
|
foreach(array('from', 'to', 'cc', 'bcc', 'sender', 'subject', 'reply-to'/* ->{'reply-to'} */, 'in-reply-to', |
|
'message-id') as $_header){ |
|
if($_header===$name){ |
|
if(0===count($arguments)){ |
|
return $this->getHeader($_header, null); |
|
}elseif(null===$arguments[0]){ |
|
$this->removeHeader($_header); |
|
}elseif(isset($arguments[0]) && is_string($arguments[0])){ |
|
$this->setHeader($_header, $arguments[0]); |
|
} |
|
return $this; |
|
} |
|
} |
|
|
|
|
|
} |
|
//else |
|
|
|
|
|
// Note: value of $name is case sensitive. |
|
$trace = debug_backtrace(); |
|
trigger_error( |
|
'Undefined property via __call(): ' . $name . |
|
' in ' . $trace[0]['file'] . |
|
' on line ' . $trace[0]['line'], |
|
E_USER_NOTICE); |
|
|
|
|
|
return null; |
|
} |
|
|
|
/** As of PHP 5.3.0 */ |
|
public static function __callStatic($name, $arguments) |
|
{ |
|
|
|
if('run'===$name){ |
|
return call_user_func_array('run', $arguments); |
|
} |
|
|
|
|
|
if('vm'===$name){ |
|
if(0===count($arguments)){ |
|
return new MimeVM(); |
|
}elseif(1===count($arguments)){ |
|
return new MimeVM($arguments[0]); |
|
}elseif(2===count($arguments)){ |
|
return new MimeVM($arguments[0], $arguments[1]); |
|
} |
|
// return call_user_func_array(array(webfan\MimeVM, '__construct'), $arguments); |
|
return new MimeVM(); |
|
} |
|
|
|
|
|
|
|
if('create'===$name){ |
|
if(!isset($arguments[0]))$arguments[0]=''; |
|
if(!isset($arguments[1]))$arguments[1]=null; |
|
return new self($arguments[0], $arguments[1]); |
|
} |
|
// Note: value of $name is case sensitive. |
|
$trace = debug_backtrace(); |
|
trigger_error( |
|
'Undefined property via __callStatic(): ' . $name . |
|
' in ' . $trace[0]['file'] . |
|
' on line ' . $trace[0]['line'], |
|
E_USER_NOTICE); |
|
|
|
|
|
return null; |
|
} |
|
|
|
public function getContentType() |
|
{ |
|
$this->contentType=$this->getMimeType(); |
|
return $this->contentType; |
|
} |
|
|
|
|
|
public function headerName($headName) |
|
{ |
|
$headName = str_replace('-', ' ', $headName); |
|
$headName = ucwords($headName); |
|
return preg_replace("/\s+/", "\s", str_replace(' ', '-', $headName)); |
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
* @param string $input A base64 encoded string |
|
* |
|
* @return string A decoded string |
|
*/ |
|
public static function urlsafeB64Decode($input) |
|
{ |
|
$remainder = strlen($input) % 4; |
|
if ($remainder) { |
|
$padlen = 4 - $remainder; |
|
$input .= str_repeat('=', $padlen); |
|
} |
|
return base64_decode(strtr($input, '-_', '+/')); |
|
} |
|
|
|
/** |
|
* @param string $input Anything really |
|
* |
|
* @return string The base64 encode of what you passed in |
|
*/ |
|
public static function urlsafeB64Encode($input) |
|
{ |
|
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); |
|
} |
|
|
|
|
|
|
|
public static function strip_body($s,$s1,$s2=false,$offset=0, $_trim = true) { |
|
/* |
|
* http://php.net/manual/en/function.strpos.php#75146 |
|
*/ |
|
|
|
// if( $s2 === false ) { $s2 = $s1; } |
|
if( $s2 === false ) { $s2 = $s1.'--'; } |
|
$result = array(); |
|
$result_2 = array(); |
|
$L1 = strlen($s1); |
|
$L2 = strlen($s2); |
|
|
|
if( $L1==0 || $L2==0 ) { |
|
return false; |
|
} |
|
|
|
do { |
|
$pos1 = strpos($s,$s1,$offset); |
|
|
|
if( $pos1 !== false ) { |
|
$pos1 += $L1; |
|
|
|
$pos2 = strpos($s,$s2,$pos1); |
|
|
|
if( $pos2 !== false ) { |
|
$key_len = $pos2 - $pos1; |
|
|
|
$this_key = substr($s,$pos1,$key_len); |
|
if(true===$_trim){ |
|
$this_key = trim($this_key); |
|
} |
|
|
|
if( !array_key_exists($this_key,$result) ) { |
|
$result[$this_key] = array(); |
|
} |
|
|
|
$result[$this_key][] = $pos1; |
|
$result_2[] = array( |
|
'pos' => $pos1, |
|
'content' => $this_key |
|
); |
|
|
|
$offset = $pos2 + $L2; |
|
} else { |
|
$pos1 = false; |
|
} |
|
} |
|
} while($pos1 !== false ); |
|
|
|
return array( |
|
'pindex' => $result_2, |
|
'cindex' => $result |
|
); |
|
} |
|
|
|
|
|
/** |
|
* MultiPart constructor. |
|
* @param string $content |
|
* @throws \InvalidArgumentException |
|
*/ |
|
protected function __construct($content, &$parent = null) |
|
{ |
|
$this->_id = ++self::$__i; |
|
$this->_parent = $parent; |
|
|
|
// Split headers and body |
|
$splits = preg_split('/(\r?\n){2}/', $content, 2); |
|
|
|
if (count($splits) < 2) { |
|
throw new \InvalidArgumentException("Content is not valid, can't split headers and content"); |
|
} |
|
|
|
list ($headers, $body) = $splits; |
|
|
|
// Regroup multiline headers |
|
$currentHeader = ''; |
|
$headerLines = array(); |
|
foreach (preg_split('/\r?\n/', $headers) as $line) { |
|
if (empty($line)) { |
|
continue; |
|
} |
|
if (preg_match('/^\h+(.+)/', $line, $matches)) { |
|
// Multi line header |
|
$currentHeader .= ' '.$matches[1]; |
|
} else { |
|
if (!empty($currentHeader)) { |
|
$headerLines[] = $currentHeader; |
|
} |
|
$currentHeader = trim($line); |
|
} |
|
} |
|
|
|
if (!empty($currentHeader)) { |
|
$headerLines[] = $currentHeader; |
|
} |
|
|
|
// Parse headers |
|
$this->headers = array(); |
|
foreach ($headerLines as $line) { |
|
$lineSplit = explode(':', $line, 2); |
|
if (2 === count($lineSplit)) { |
|
list($key, $value) = $lineSplit; |
|
// Decode value |
|
$value = mb_decode_mimeheader(trim($value)); |
|
} else { |
|
// Bogus header |
|
$key = $lineSplit[0]; |
|
$value = ''; |
|
} |
|
// Case-insensitive key |
|
$key = strtolower($key); |
|
if (!isset($this->headers[$key])) { |
|
$this->headers[$key] = $value; |
|
} else { |
|
if (!is_array($this->headers[$key])) { |
|
$this->headers[$key] = (array)$this->headers[$key]; |
|
} |
|
$this->headers[$key][] = $value; |
|
} |
|
} |
|
|
|
// Is MultiPart ? |
|
$contentType = $this->getHeader('Content-Type'); |
|
$this->contentType=$contentType; |
|
if ('multipart' === strstr(self::getHeaderValue($contentType), '/', true)) { |
|
// MultiPart ! |
|
$this->multipart = true; |
|
$boundary = self::getHeaderOption($contentType, 'boundary'); |
|
$this->boundary=$boundary; |
|
|
|
if (null === $boundary) { |
|
throw new \InvalidArgumentException("Can't find boundary in content type"); |
|
} |
|
|
|
$separator = '--'.preg_quote($boundary, '/'); |
|
|
|
if (0 === preg_match('/'.$separator.'\r?\n(.+?)\r?\n'.$separator.'--/s', $body, $matches) |
|
|| preg_last_error() !== PREG_NO_ERROR |
|
) { |
|
$bodyParts = self::strip_body($body,$separator."",$separator."--",0); |
|
if(1 !== count($bodyParts['pindex'])){ |
|
throw new \InvalidArgumentException("Can't find multi-part content"); |
|
} |
|
$bodyStr = $bodyParts['pindex'][0]['content']; |
|
unset($bodyParts); |
|
}else{ |
|
$bodyStr = $matches[1]; |
|
} |
|
|
|
|
|
|
|
|
|
$parts = preg_split('/\r?\n'.$separator.'\r?\n/', $bodyStr); |
|
unset($bodyStr); |
|
|
|
foreach ($parts as $part) { |
|
//$this->parts[] = new self($part, $this); |
|
$this->append($part); |
|
} |
|
} else { |
|
|
|
// Decode |
|
$encoding = $this->getEcoding(); |
|
switch ($encoding) { |
|
case 'base64': |
|
$body = $this->urlsafeB64Decode($body); |
|
break; |
|
case 'quoted-printable': |
|
$body = quoted_printable_decode($body); |
|
break; |
|
} |
|
|
|
// Convert to UTF-8 ( Not if binary or 7bit ( aka Ascii ) ) |
|
if (!in_array($encoding, array('binary', '7bit'))) { |
|
// Charset |
|
$charset = self::getHeaderOption($contentType, 'charset'); |
|
if (null === $charset) { |
|
// Try to detect |
|
$charset = mb_detect_encoding($body) ?: 'utf-8'; |
|
} |
|
$this->charset=$charset; |
|
|
|
// Only convert if not UTF-8 |
|
if ('utf-8' !== strtolower($charset)) { |
|
$body = mb_convert_encoding($body, 'utf-8', $charset); |
|
} |
|
} |
|
|
|
$this->body = $body; |
|
} |
|
} |
|
|
|
|
|
|
|
public function __toString() |
|
{ |
|
$boundary = $this->getBoundary($this->isMultiPart()); |
|
$s=''; |
|
foreach($this->headers as $hname => $hvalue){ |
|
$s.= $this->headerName($hname).': '. $this->getHeader($hname) /*$hvalue*/."\r\n"; |
|
} |
|
|
|
$s.= "\r\n" ; |
|
if ($this->isMultiPart()) $s.= "--" ; |
|
$s.= $boundary ; |
|
if ($this->isMultiPart()) $s.= "\r\n" ; |
|
|
|
|
|
if ($this->isMultiPart()) { |
|
foreach ($this->parts as $part) { |
|
$s.= (get_class($this) === get_class($part)) ? $part : $part->__toString() . "\r\n" ; |
|
} |
|
$s.= "\r\n"."--" . $boundary . '--'; |
|
}else{ |
|
|
|
$s.= $this->getBody(true, $encoding); |
|
} |
|
|
|
if (null!==$this->parent && $this->parent->isMultiPart() && $this->parent->lastChild->id !== $this->id){ |
|
$s.= "\r\n" . "--" .$this->parent->getBoundary() . "\r\n"; |
|
} |
|
return $s; |
|
} |
|
|
|
public function getEcoding() |
|
{ |
|
$this->encoding=strtolower($this->getHeader('Content-Transfer-Encoding')); |
|
return $this->encoding; |
|
} |
|
|
|
public function getCharset() |
|
{ |
|
// return $this->charset; |
|
$charset = self::getHeaderOption($this->getMimeType(), 'charset'); |
|
if(!is_string($charset)) { |
|
// Try to detect |
|
$charset = mb_detect_encoding($this->body) ?: 'utf-8'; |
|
} |
|
$this->charset=$charset; |
|
return $this->charset; |
|
} |
|
|
|
|
|
public function setBoundary($boundary = null, $opts = array()) |
|
{ |
|
$this->mod(); |
|
|
|
if(null===$boundary){ |
|
$size = 8; |
|
if(4 < count($this->parts))$size = 32; |
|
if(6 < count($this->parts))$size = 40; |
|
if(8 < count($this->parts))$size = 64; |
|
if(10 <= count($this->parts))$size = 70; |
|
$opt = array( |
|
'length' => $size |
|
); |
|
|
|
|
|
$options = array_merge($opt, $opts); |
|
$boundary = $this->generateBundary($options); |
|
} |
|
|
|
$this->boundary =$boundary; |
|
$this->setHeaderOption('Content-Type', $this->boundary, 'boundary'); |
|
} |
|
|
|
|
|
public function getBoundary($generate = true) |
|
{ |
|
$this->boundary = self::getHeaderOption($this->getHeader('Content-Type'), 'boundary'); |
|
if(true === $generate && $this->isMultiPart() |
|
&& (!is_string($this->boundary) || 0===strlen(trim($this->boundary))) |
|
){ |
|
$this->setBoundary(); |
|
} |
|
return $this->boundary; |
|
} |
|
/** |
|
* @param string $key |
|
* @param mixed $default |
|
* @return mixed |
|
*/ |
|
public function mod() |
|
{ |
|
$this->modified = true; |
|
return $this; |
|
} |
|
|
|
public function setHeader($key, $value) |
|
{ |
|
$this->mod(); |
|
$key = strtolower($key); |
|
$this->headers[$key]=$value; |
|
|
|
// echo print_r($this->headers, true); |
|
|
|
return $this; |
|
} |
|
|
|
public function removeHeader($key) |
|
{ |
|
$this->mod(); |
|
unset($this->headers[$key]); |
|
return $this; |
|
} |
|
|
|
public function setHeaderOption($headerName, $value = null, $opt = null) |
|
{ |
|
$this->mod(); |
|
$old_header_value = $this->getHeader($headerName); |
|
|
|
|
|
if(null===$opt && null !==$value){ |
|
$this->headers[$headerName]=$value; |
|
}else if(null !==$opt && null !==$value){ |
|
list($headerValue,$options) = self::parseHeaderContent($old_header_value); |
|
$options[$opt]=$value; |
|
$new_header_value = $headerValue; |
|
// $new_header_value=''; |
|
foreach($options as $o => $v){ |
|
$new_header_value .= ';'.$o.'='.$v.''; |
|
} |
|
|
|
$this->setHeader($headerName, $new_header_value); |
|
} |
|
|
|
|
|
return $this; |
|
} |
|
|
|
|
|
|
|
/** |
|
* @return bool |
|
*/ |
|
public function isMultiPart() |
|
{ |
|
return $this->multipart; |
|
} |
|
|
|
/** |
|
* @return string |
|
* @throws \LogicException if is multipart |
|
*/ |
|
public function getBody($reEncode = false, &$encoding = null) |
|
{ |
|
if ($this->isMultiPart()) { |
|
throw new \LogicException("MultiPart content, there aren't body"); |
|
} else { |
|
$body = $this->body; |
|
|
|
if(true===$reEncode){ |
|
$encoding = $this->getEcoding(); |
|
switch ($encoding) { |
|
case 'base64': |
|
$body = $this->urlsafeB64Encode($body); |
|
break; |
|
case 'quoted-printable': |
|
$body = quoted_printable_encode($body); |
|
break; |
|
} |
|
|
|
// Convert to UTF-8 ( Not if binary or 7bit ( aka Ascii ) ) |
|
if (!in_array($encoding, array('binary', '7bit'))) { |
|
// back de-/encode |
|
if ( 'utf-8' !== strtolower(self::getHeaderOption($this->getMimeType(), 'charset')) |
|
&& 'utf-8' === mb_detect_encoding($body)) { |
|
$body = mb_convert_encoding($body, self::getHeaderOption($this->getMimeType(), 'charset'), 'utf-8'); |
|
}elseif ( 'utf-8' === strtolower(self::getHeaderOption($this->getMimeType(), 'charset')) |
|
&& 'utf-8' !== mb_detect_encoding($body)) { |
|
$body = mb_convert_encoding($body, 'utf-8', mb_detect_encoding($body)); |
|
} |
|
} |
|
} |
|
|
|
|
|
return $body; |
|
} |
|
} |
|
|
|
/** |
|
* @return array |
|
*/ |
|
public function getHeaders() |
|
{ |
|
return $this->headers; |
|
} |
|
|
|
/** |
|
* @param string $key |
|
* @param mixed $default |
|
* @return mixed |
|
*/ |
|
public function getHeader($key, $default = null) |
|
{ |
|
// Case-insensitive key |
|
$key = strtolower($key); |
|
if (isset($this->headers[$key])) { |
|
return $this->headers[$key]; |
|
} else { |
|
return $default; |
|
} |
|
} |
|
|
|
/** |
|
* @param string $content |
|
* @return array |
|
*/ |
|
static protected function parseHeaderContent($content) |
|
{ |
|
$parts = explode(';', $content); |
|
$headerValue = array_shift($parts); |
|
$options = array(); |
|
// Parse options |
|
foreach ($parts as $part) { |
|
if (!empty($part)) { |
|
$partSplit = explode('=', $part, 2); |
|
if (2 === count($partSplit)) { |
|
list ($key, $value) = $partSplit; |
|
$options[trim($key)] = trim($value, ' "'); |
|
} else { |
|
// Bogus option |
|
$options[$partSplit[0]] = ''; |
|
} |
|
} |
|
} |
|
|
|
return array($headerValue, $options); |
|
} |
|
|
|
/** |
|
* @param string $header |
|
* @return string |
|
*/ |
|
static public function getHeaderValue($header) |
|
{ |
|
list($value) = self::parseHeaderContent($header); |
|