You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1085 lines
29 KiB

<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id$
//this script may only be included - so its better to die if called directly.
if (strpos($_SERVER['SCRIPT_NAME'], basename(__FILE__)) !== false) {
header('location: index.php');
exit;
}
/**
* GZIP stuff.
*
* Note that we use gzopen()/gzwrite() instead of gzcompress() even if
* gzcompress() is available. Gzcompress() puts out data with
* different headers --- in particular it includes an "adler-32"
* checksum rather than a "CRC32" checksum. Since we need the CRC-32
* checksum, and since not all PHP's have gzcompress(), we'll just
* stick with gzopen().
*/
function gzip_cleanup()
{
global $gzip_tmpfile;
if ($gzip_tmpfile) {
@unlink($gzip_tmpfile);
}
}
function gzip_tempnam()
{
global $gzip_tmpfile;
if (! $gzip_tmpfile) {
//FIXME: does this work on non-unix machines?
$gzip_tmpfile = tempnam('/tmp', 'wkzip');
register_shutdown_function('gzip_cleanup');
}
return $gzip_tmpfile;
}
function gzip_compress($data)
{
$filename = gzip_tempnam();
if (! ($fp = gzopen($filename, 'wb'))) {
trigger_error(sprintf('%s failed', 'gzopen'), E_USER_ERROR);
}
gzwrite($fp, $data, strlen($data));
if (! gzclose($fp)) {
trigger_error(sprintf('%s failed', 'gzclose'), E_USER_ERROR);
}
$size = filesize($filename);
if (! ($fp = fopen($filename, 'rb'))) {
trigger_error(sprintf('%s failed', 'fopen'), E_USER_ERROR);
}
if (! ($z = fread($fp, $size)) || strlen($z) != $size) {
trigger_error(sprintf('%s failed', 'fread'), E_USER_ERROR);
}
if (! fclose($fp)) {
trigger_error(sprintf('%s failed', 'fclose'), E_USER_ERROR);
}
unlink($filename);
return $z;
}
function gzip_uncompress($data)
{
$filename = gzip_tempnam();
if (! ($fp = fopen($filename, 'wb'))) {
trigger_error(sprintf('%s failed', 'fopen'), E_USER_ERROR);
}
fwrite($fp, $data, strlen($data));
if (! fclose($fp)) {
trigger_error(sprintf('%s failed', 'fclose'), E_USER_ERROR);
}
if (! ($fp = gzopen($filename, 'rb'))) {
trigger_error(sprintf('%s failed', 'gzopen'), E_USER_ERROR);
}
$unz = '';
while ($buf = gzread($fp, 4096)) {
$unz .= $buf;
}
if (! gzclose($fp)) {
trigger_error(sprintf("%s failed", 'gzclose'), E_USER_ERROR);
}
unlink($filename);
return $unz;
}
/**
* CRC32 computation. Hacked from Info-zip's zip-2.3 source code.
*/
function zip_crc32($str, $crc = 0)
{
static $zip_crc_table;
if (empty($zip_crc_table)) {
/* NOTE: The range of PHP ints seems to be -0x80000000 to 0x7fffffff.
* So, had to munge these constants.
*/
$zip_crc_table = [
0x00000000,
0x77073096,
-0x11f19ed4,
-0x66f6ae46,
0x076dc419,
0x706af48f,
-0x169c5acb,
-0x619b6a5d,
0x0edb8832,
0x79dcb8a4,
-0x1f2a16e2,
-0x682d2678,
0x09b64c2b,
0x7eb17cbd,
-0x1847d2f9,
-0x6f40e26f,
0x1db71064,
0x6ab020f2,
-0x0c468eb8,
-0x7b41be22,
0x1adad47d,
0x6ddde4eb,
-0x0b2b4aaf,
-0x7c2c7a39,
0x136c9856,
0x646ba8c0,
-0x029d0686,
-0x759a3614,
0x14015c4f,
0x63066cd9,
-0x05f0c29d,
-0x72f7f20b,
0x3b6e20c8,
0x4c69105e,
-0x2a9fbe1c,
-0x5d988e8e,
0x3c03e4d1,
0x4b04d447,
-0x2df27a03,
-0x5af54a95,
0x35b5a8fa,
0x42b2986c,
-0x2444362a,
-0x534306c0,
0x32d86ce3,
0x45df5c75,
-0x2329f231,
-0x542ec2a7,
0x26d930ac,
0x51de003a,
-0x3728ae80,
-0x402f9eea,
0x21b4f4b5,
0x56b3c423,
-0x30456a67,
-0x47425af1,
0x2802b89e,
0x5f058808,
-0x39f3264e,
-0x4ef416dc,
0x2f6f7c87,
0x58684c11,
-0x3e9ee255,
-0x4999d2c3,
0x76dc4190,
0x01db7106,
-0x672ddf44,
-0x102aefd6,
0x71b18589,
0x06b6b51f,
-0x60401b5b,
-0x17472bcd,
0x7807c9a2,
0x0f00f934,
-0x69f65772,
-0x1ef167e8,
0x7f6a0dbb,
0x086d3d2d,
-0x6e9b9369,
-0x199ca3ff,
0x6b6b51f4,
0x1c6c6162,
-0x7a9acf28,
-0x0d9dffb2,
0x6c0695ed,
0x1b01a57b,
-0x7df70b3f,
-0x0af03ba9,
0x65b0d9c6,
0x12b7e950,
-0x74414716,
-0x03467784,
0x62dd1ddf,
0x15da2d49,
-0x732c830d,
-0x042bb39b,
0x4db26158,
0x3ab551ce,
-0x5c43ff8c,
-0x2b44cf1e,
0x4adfa541,
0x3dd895d7,
-0x5b2e3b93,
-0x2c290b05,
0x4369e96a,
0x346ed9fc,
-0x529877ba,
-0x259f4730,
0x44042d73,
0x33031de5,
-0x55f5b3a1,
-0x22f28337,
0x5005713c,
0x270241aa,
-0x41f4eff0,
-0x36f3df7a,
0x5768b525,
0x206f85b3,
-0x46992bf7,
-0x319e1b61,
0x5edef90e,
0x29d9c998,
-0x4f2f67de,
-0x3828574c,
0x59b33d17,
0x2eb40d81,
-0x4842a3c5,
-0x3f459353,
-0x12477ce0,
-0x65404c4a,
0x03b6e20c,
0x74b1d29a,
-0x152ab8c7,
-0x622d8851,
0x04db2615,
0x73dc1683,
-0x1c9cf4ee,
-0x6b9bc47c,
0x0d6d6a3e,
0x7a6a5aa8,
-0x1bf130f5,
-0x6cf60063,
0x0a00ae27,
0x7d079eb1,
-0x0ff06cbc,
-0x78f75c2e,
0x1e01f268,
0x6906c2fe,
-0x089da8a3,
-0x7f9a9835,
0x196c3671,
0x6e6b06e7,
-0x012be48a,
-0x762cd420,
0x10da7a5a,
0x67dd4acc,
-0x06462091,
-0x71411007,
0x17b7be43,
0x60b08ed5,
-0x29295c18,
-0x5e2e6c82,
0x38d8c2c4,
0x4fdff252,
-0x2e44980f,
-0x5943a899,
0x3fb506dd,
0x48b2364b,
-0x27f2d426,
-0x50f5e4b4,
0x36034af6,
0x41047a60,
-0x209f103d,
-0x579820ab,
0x316e8eef,
0x4669be79,
-0x349e4c74,
-0x43997ce6,
0x256fd2a0,
0x5268e236,
-0x33f3886b,
-0x44f4b8fd,
0x220216b9,
0x5505262f,
-0x3a45c442,
-0x4d42f4d8,
0x2bb45a92,
0x5cb36a04,
-0x3d280059,
-0x4a2f30cf,
0x2cd99e8b,
0x5bdeae1d,
-0x649b3d50,
-0x139c0dda,
0x756aa39c,
0x026d930a,
-0x63f6f957,
-0x14f1c9c1,
0x72076785,
0x05005713,
-0x6a40b57e,
-0x1d4785ec,
0x7bb12bae,
0x0cb61b38,
-0x6d2d7165,
-0x1a2a41f3,
0x7cdcefb7,
0x0bdbdf21,
-0x792c2d2c,
-0x0e2b1dbe,
0x68ddb3f8,
0x1fda836e,
-0x7e41e933,
-0x0946d9a5,
0x6fb077e1,
0x18b74777,
-0x77f7a51a,
-0x00f09590,
0x66063bca,
0x11010b5c,
-0x709a6101,
-0x079d5197,
0x616bffd3,
0x166ccf45,
-0x5ff51d88,
-0x28f22d12,
0x4e048354,
0x3903b3c2,
-0x5898d99f,
-0x2f9fe909,
0x4969474d,
0x3e6e77db,
-0x512e95b6,
-0x2629a524,
0x40df0b66,
0x37d83bf0,
-0x564351ad,
-0x2144613b,
0x47b2cf7f,
0x30b5ffe9,
-0x42420de4,
-0x35453d76,
0x53b39330,
0x24b4a3a6,
-0x452fc9fb,
-0x3228f96d,
0x54de5729,
0x23d967bf,
-0x4c9985d2,
-0x3b9eb548,
0x5d681b02,
0x2a6f2b94,
-0x4bf441c9,
-0x3cf3715f,
0x5a05df1b,
0x2d02ef8d
];
}
$crc = ~$crc;
for ($i = 0, $istrlen_str = strlen($str); $i < $istrlen_str; $i++) {
$crc = ($zip_crc_table[($crc ^ ord($str[$i])) & 0xff] ^ (($crc >> 8) & 0xffffff));
}
return ~$crc;
}
define('GZIP_MAGIC', "\037\213");
define('GZIP_DEFLATE', 010);
function zip_deflate($content)
{
// Compress content, and suck information from gzip header.
$z = gzip_compress($content);
// Suck OS type byte from gzip header. FIXME: this smells bad.
extract(unpack("a2magic/Ccomp_type/Cflags/@9/Cos_type", $z));
if ($magic != GZIP_MAGIC) {
trigger_error(sprintf('Bad %s', 'gzip magic'), E_USER_ERROR);
}
if ($comp_type != GZIP_DEFLATE) {
trigger_error(sprintf('Bad %s', 'gzip comp type'), E_USER_ERROR);
}
if (($flags & 0x3e) != 0) {
trigger_error(sprintf('Bad %s', sprintf('flags (0x%02x)', $flags)), E_USER_ERROR);
}
$gz_header_len = 10;
$gz_data_len = strlen($z) - $gz_header_len - 8;
if ($gz_data_len < 0) {
trigger_error('not enough gzip output?', E_USER_ERROR);
}
extract(unpack('Vcrc32', substr($z, $gz_header_len + $gz_data_len)));
return [
substr($z, $gz_header_len, $gz_data_len), // gzipped data
$crc32, // crc
$os_type // OS type
];
}
function zip_inflate($data, $crc32, $uncomp_size)
{
if (! function_exists('gzopen')) {
global $request;
$request->finish(_("Can't inflate data: zlib support not enabled in this PHP"));
}
// Reconstruct gzip header and ungzip the data.
$mtime = time(); //(Bogus mtime)
return gzip_uncompress(pack("a2CxV@10", GZIP_MAGIC, GZIP_DEFLATE, $mtime) . $data . pack('VV', $crc32, $uncomp_size));
}
function unixtime2dostime($unix_time)
{
if ($unix_time % 1) {
$unix_time++; // Round up to even seconds.
}
list($year, $month, $mday, $hour, $min, $sec) = explode(' ', TikiLib::date_format('%Y %m %e %H %M %S', $unix_time));
if ($year < 1980) {
list($year, $month, $mday, $hour, $min, $sec) = [
1980,
1,
1,
0,
0,
0
];
}
$dosdate = (($year - 1980) << 9) | ($month << 5) | $mday;
$dostime = ($hour << 11) | ($min << 5) | ($sec >> 1);
return [
$dosdate,
$dostime
];
}
function dostime2unixtime($dosdate, $dostime)
{
$mday = $dosdate & 0x1f;
$month = ($dosdate >> 5) & 0x0f;
$year = 1980 + (($dosdate >> 9) & 0x7f);
$sec = ($dostime & 0x1f) * 2;
$min = ($dostime >> 5) & 0x3f;
$hour = ($dostime >> 11) & 0x1f;
return TikiLib::make_time($hour, $min, $sec, $month, $mday, $year);
}
/**
* Class for zipfile creation.
*/
define('ZIP_DEFLATE', GZIP_DEFLATE);
define('ZIP_STORE', 0);
define('ZIP_CENTHEAD_MAGIC', "PK\001\002");
define('ZIP_LOCHEAD_MAGIC', "PK\003\004");
define('ZIP_ENDDIR_MAGIC', "PK\005\006");
class ZipWriter
{
public function __construct($comment = '', $zipname = 'archive.zip')
{
$this->comment = $comment;
$this->nfiles = 0;
$this->dir = ''; // "Central directory block"
$this->offset = 0; // Current file position.
$zipname = addslashes($zipname);
header("Content-Type: application/zip; name=\"$zipname\"");
header("Content-Disposition: attachment; filename=\"$zipname\"");
}
public function addRegularFile($filename, $content, $attrib = false)
{
if (! $attrib) {
$attrib = [];
}
$size = strlen($content);
if (function_exists('gzopen')) {
list($data, $crc32, $os_type) = zip_deflate($content);
if (strlen($data) < $size) {
$content = $data; // Use compressed data.
$comp_type = ZIP_DEFLATE;
} else {
unset($crc32); // force plain store.
}
}
if (! isset($crc32)) {
$comp_type = ZIP_STORE;
$crc32 = zip_crc32($content);
}
if (! empty($attrib['write_protected'])) {
$atx = (0100444 << 16) | 1; // S_IFREG + read permissions to
} // everybody.
else {
$atx = (0100644 << 16); // Add owner write perms.
}
$ati = $attrib['is_ascii'] ? 1 : 0;
if (empty($attrib['mtime'])) {
$attrib['mtime'] = time();
}
list($mod_date, $mod_time) = unixtime2dostime($attrib['mtime']);
// Construct parts common to "Local file header" and "Central
// directory file header."
if (! isset($attrib['extra_field'])) {
$attrib['extra_field'] = '';
}
if (! isset($attrib['file_comment'])) {
$attrib['file_comment'] = '';
}
$head = pack(
"vvvvvVVVvv",
20, // Version needed to extract (FIXME: is this right?)
0, // Gen purp bit flag
$comp_type,
$mod_time,
$mod_date,
$crc32,
strlen($content),
$size,
strlen($filename),
strlen($attrib['extra_field'])
);
// Construct the "Local file header"
$lheader = ZIP_LOCHEAD_MAGIC . $head . $filename . $attrib['extra_field'];
// Construct the "central directory file header"
$this->dir .= pack("a4CC", ZIP_CENTHEAD_MAGIC, 23, $os_type);
$this->dir .= $head;
$this->dir .= pack(
"vvvVV",
strlen($attrib['file_comment']),
0, // Disk number start
$ati, // Internal file attributes
$atx, // External file attributes
$this->offset // Relative offset of local header
);
$this->dir .= $filename . $attrib['extra_field'] . $attrib['file_comment'];
// Output the "Local file header" and file contents.
echo $lheader;
echo $content;
$this->offset += strlen($lheader) + strlen($content);
$this->nfiles++;
}
public function finish()
{
// Output the central directory
echo $this->dir;
// Construct the "End of central directory record"
echo ZIP_ENDDIR_MAGIC;
echo pack(
"vvvvVVv",
0, // Number of this disk.
0, // Number of disk with start of c dir
$this->nfiles, // Number entries on this disk
$this->nfiles, // Number entries
strlen($this->dir), // Size of central directory
$this->offset, // Offset of central directory
strlen($this->comment)
);
echo $this->comment;
}
}
/**
* Class for reading zip files.
*
* BUGS:
*
* Many of the ExitWiki()'s should probably be warn()'s (eg. CRC mismatch).
*
* Only a subset of zip formats is recognized. (I think that
* unsupported formats will be recognized as such rather than silently
* munged.)
*
* We don't read the central directory. This means we don't see the
* file attributes (text? read-only?), or file comments.
*
* Right now we ignore the file mod date and time, since we don't need it.
*/
class ZipReader
{
public function __construct($zipfile)
{
if (! is_string($zipfile)) {
$this->fp = $zipfile; // File already open
} elseif (! ($this->fp = fopen($zipfile, "rb"))) {
trigger_error(sprintf(_("Can't open zip file '%s' for reading"), $zipfile), E_USER_ERROR);
}
}
public function _read($nbytes)
{
$chunk = fread($this->fp, $nbytes);
if (strlen($chunk) != $nbytes) {
trigger_error(_("Unexpected EOF in zip file"), E_USER_ERROR);
}
return $chunk;
}
public function done()
{
fclose($this->fp);
return false;
}
public function readFile()
{
$head = $this->_read(30);
extract(
unpack(
'a4magic/vreq_version/vflags/vcomp_type'
. '/vmod_time/vmod_date'
. '/Vcrc32/Vcomp_size/Vuncomp_size'
. '/vfilename_len/vextrafld_len',
$head
)
);
//FIXME: we should probably check $req_version.
$attrib['mtime'] = dostime2unixtime($mod_date, $mod_time);
if ($magic != ZIP_LOCHEAD_MAGIC) {
if ($magic != ZIP_CENTHEAD_MAGIC) {
// FIXME: better message?
ExitWiki(sprintf('Bad header type: %s', $magic));
}
return $this->done();
}
if (($flags & 0x21) != 0) {
ExitWiki('Encryption and/or zip patches not supported.');
}
if (($flags & 0x08) != 0) {
// FIXME: better message?
ExitWiki('Postponed CRC not yet supported.');
}
$filename = $this->_read($filename_len);
if ($extrafld_len != 0) {
$attrib['extra_field'] = $this->_read($extrafld_len);
}
$data = $this->_read($comp_size);
if ($comp_type == ZIP_DEFLATE) {
$data = zip_inflate($data, $crc32, $uncomp_size);
} elseif ($comp_type == ZIP_STORE) {
$crc = zip_crc32($data);
if ($crc32 != $crc) {
ExitWiki(sprintf('CRC mismatch %x != %x', $crc, $crc32));
}
} else {
ExitWiki(sprintf('Compression method %s unsupported', $comp_method));
}
if (strlen($data) != $uncomp_size) {
ExitWiki(sprintf('Uncompressed size mismatch %d != %d', strlen($data), $uncomp_size));
}
return [
$filename,
$data,
$attrib
];
}
}
/**
* Routines for Mime mailification of pages.
*/
//FIXME: these should go elsewhere (libmime?).
/**
* Routines for quoted-printable en/decoding.
*/
function QuotedPrintableEncode($string)
{
// Quote special characters in line.
$quoted = '';
while ($string) {
// The complicated regexp is to force quoting of trailing spaces.
preg_match('/^([ !-<>-~]*)(?:([!-<>-~]$)|(.))/s', $string, $match);
$quoted .= $match[1] . $match[2];
if (! empty($match[3])) {
$quoted .= sprintf('=%02X', ord($match[3]));
}
$string = substr($string, strlen($match[0]));
}
// Split line.
// This splits the line (preferably after white-space) into lines
// which are no longer than 76 chars (after adding trailing '=' for
// soft line break, but before adding \r\n.)
return preg_replace('/(?=.{77})(.{10,74}[ \t]|.{71,73}[^=][^=])/s', "\\1=\r\n", $quoted);
}
function QuotedPrintableDecode($string)
{
// Eliminate soft line-breaks.
$string = preg_replace('/=[ \t\r]*\n/', '', $string);
return quoted_printable_decode($string);
}
define('MIME_TOKEN_REGEXP', "[-!#-'*+.0-9A-Z^-~]+");
function MimeContentTypeHeader($type, $subtype, $params)
{
$header = "Content-Type: $type/$subtype";
reset($params);
foreach ($params as $key => $val) {
//FIXME: what about non-ascii printables in $val?
if (! preg_match('/^' . MIME_TOKEN_REGEXP . '$/', $val)) {
$val = '"' . addslashes($val) . '"';
}
$header .= ";\r\n $key=$val";
}
return "$header\r\n";
}
function MimeMultipart($parts)
{
global $mime_multipart_count;
// The string "=_" can not occur in quoted-printable encoded data.
$boundary = "=_multipart_boundary_" . ++$mime_multipart_count;
$head = MimeContentTypeHeader('multipart', 'mixed', ['boundary' => $boundary]);
$sep = "\r\n--$boundary\r\n";
return $head . $sep . implode($sep, $parts) . "\r\n--${boundary}--\r\n";
}
/**
* For reference see:
* http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html
* http://www.faqs.org/rfcs/rfc2045.html
* (RFC 1521 has been superceeded by RFC 2045 & others).
*
* Also see http://www.faqs.org/rfcs/rfc2822.html
*
*
* Notes on content-transfer-encoding.
*
* "7bit" means short lines of US-ASCII.
* "8bit" means short lines of octets with (possibly) the high-order bit set.
* "binary" means lines are not necessarily short enough for SMTP
* transport, and non-ASCII characters may be present.
*
* Only "7bit", "quoted-printable", and "base64" are universally safe
* for transport via e-mail. (Though many MTAs can/will be configured to
* automatically convert encodings to a safe type if they receive
* mail encoded in '8bit' and/or 'binary' encodings.
*/
function MimeifyPageRevision($page)
{
//$page = $revision->getPage();
// FIXME: add 'hits' to $params
$params = [
'pagename' => $page['pageName'],
'flags' => '',
'author' => $page['user'],
'version' => $page['version'],
'lastmodified' => $page['lastModif']
];
$params['author_id'] = $page['ip'];
$params['summary'] = $page['comment'];
if (isset($page['hits'])) {
$params['hits'] = $page['hits'];
}
$params['description'] = $page['description'];
$params['charset'] = 'utf-8';
// Non-US-ASCII is not allowed in Mime headers (at least not without
// special handling) --- so we urlencode all parameter values.
foreach ($params as $key => $val) {
$params[$key] = rawurlencode($val);
}
$out = MimeContentTypeHeader('application', 'x-tikiwiki', $params);
$out .= sprintf("Content-Transfer-Encoding: %s\r\n", 'binary');
$out .= "\r\n";
$lines = explode("\n", $page["data"]);
foreach ($lines as $line) {
// This is a dirty hack to allow saving binary text files. See above.
$line = rtrim($line);
$out .= "$line\r\n";
}
return $out;
}
/**
* Routines for parsing Mime-ified phpwiki pages.
*/
function ParseRFC822Headers(&$string)
{
if (preg_match("/^From (.*)\r?\n/", $string, $match)) {
$headers['from '] = preg_replace('/^\s+|\s+$/', '', $match[1]);
$string = substr($string, strlen($match[0]));
}
while (preg_match('/^([!-9;-~]+) [ \t]* : [ \t]* ' . '( .* \r?\n (?: [ \t] .* \r?\n)* )/x', $string, $match)) {
$headers[strtolower($match[1])] = preg_replace('/^\s+|\s+$/', '', $match[2]);
$string = substr($string, strlen($match[0]));
}
if (empty($headers)) {
return false;
}
if (! preg_match("/^\r?\n/", $string, $match)) {
// No blank line after headers.
return false;
}
$string = substr($string, strlen($match[0]));
return $headers;
}
function ParseMimeContentType($string)
{
// FIXME: Remove (RFC822 style comments).
// Get type/subtype
if (! preg_match(':^\s*(' . MIME_TOKEN_REGEXP . ')\s*' . '/' . '\s*(' . MIME_TOKEN_REGEXP . ')\s*:x', $string, $match)) {
ExitWiki(sprintf("Bad %s", 'MIME content-type'));
}
$type = strtolower($match[1]);
$subtype = strtolower($match[2]);
$string = substr($string, strlen($match[0]));
$param = [];
while (preg_match('/^;\s*(' . MIME_TOKEN_REGEXP . ')\s*=\s*' . '(?:(' . MIME_TOKEN_REGEXP . ')|"((?:[^"\\\\]|\\.)*)") \s*/sx', $string, $match)) {
//" <--kludge for brain-dead syntax coloring
if (strlen($match[2])) {
$val = $match[2];
} else {
$val = preg_replace('/[\\\\](.)/s', '\\1', $match[3]);
}
$param[strtolower($match[1])] = $val;
$string = substr($string, strlen($match[0]));
}
return [$type, $subtype,$param];
}
function ParseMimeMultipart($data, $boundary)
{
if (! $boundary) {
ExitWiki('No boundary?');
}
$boundary = preg_quote($boundary);
while (preg_match("/^(|.*?\n)--$boundary((?:--)?)[^\n]*\n/s", $data, $match)) {
$data = substr($data, strlen($match[0]));
if (! isset($parts)) {
$parts = []; // First time through: discard leading chaff
} else {
if ($content = ParseMimeifiedPages($match[1])) {
foreach ($content as $p) {
$parts[] = $p;
}
}
}
if ($match[2]) {
return $parts; // End boundary found.
}
}
ExitWiki('No end boundary?');
}
function GenerateFootnotesFromRefs($params)
{
$footnotes = [];
reset($params);
foreach ($params as $p => $reference) {
if (preg_match('/^ref([1-9][0-9]*)$/', $p, $m)) {
$footnotes[$m[1]] = sprintf(_("[%d] See [%s]"), $m[1], rawurldecode($reference));
}
}
if (count($footnotes) > 0) {
ksort($footnotes);
return "-----\n" . "!" . _("References") . "\n" . join("\n%%%\n", $footnotes) . "\n";
} else {
return '';
}
}
// Convert references in meta-data to footnotes.
// Only zip archives generated by phpwiki 1.2.x or earlier should have
// references.
function ParseMimeifiedPages($data)
{
if (! ($headers = ParseRFC822Headers($data)) || empty($headers['content-type'])) {
//trigger_error( sprintf(_("Can't find %s"),'content-type header'),
// E_USER_WARNING );
return false;
}
$typeheader = $headers['content-type'];
if (! (list($type, $subtype, $params) = ParseMimeContentType($typeheader))) {
trigger_error(sprintf("Can't parse %s: (%s)", 'content-type', $typeheader), E_USER_WARNING);
return false;
}
if ("$type/$subtype" == 'multipart/mixed') {
return ParseMimeMultipart($data, $params['boundary']);
} elseif ("$type/$subtype" != 'application/x-phpwiki') {
trigger_error(sprintf("Bad %s", "content-type: $type/$subtype"), E_USER_WARNING);
return false;
}
// FIXME: more sanity checking?
$page = [];
$pagedata = [];
$versiondata = [];
foreach ($params as $key => $value) {
if (empty($value)) {
continue;
}
$value = rawurldecode($value);
switch ($key) {
case 'pagename':
case 'version':
$page[$key] = $value;
break;
case 'flags':
if (preg_match('/PAGE_LOCKED/', $value)) {
$pagedata['locked'] = 'yes';
}
break;
case 'created':
case 'hits':
$pagedata[$key] = $value;
break;
case 'lastmodified':
$versiondata['mtime'] = $value;
break;
case 'author':
case 'author_id':
case 'summary':
case 'markup':
$versiondata[$key] = $value;
break;
}
}
// FIXME: do we need to try harder to find a pagename if we
// haven't got one yet?
if (! isset($versiondata['author'])) {
global $request;
$user = $request->getUser();
$versiondata['author'] = $user->getId(); //FIXME:?
}
$encoding = strtolower($headers['content-transfer-encoding']);
if ($encoding == 'quoted-printable') {
$data = QuotedPrintableDecode($data);
} elseif ($encoding && $encoding != 'binary') {
ExitWiki(sprintf("Unknown %s", 'encoding type: $encoding'));
}
$data .= GenerateFootnotesFromRefs($params);
$page['content'] = preg_replace('/[ \t\r]*\n/', "\n", chop($data));
$page['pagedata'] = $pagedata;
$page['versiondata'] = $versiondata;
return [$page];
}
// Local Variables:
// mode: php
// tab-width: 8
// c-basic-offset: 4
// c-hanging-comment-ender-p: nil
// indent-tabs-mode: nil
// End: