<?php if (!defined('PmWiki')) exit();

/*	=== MultiUpload ===
 *	Copyright 2007 Eemeli Aro <eemeli.aro@tkk.fi>
 *
 *	Uploads multiple files at once by packing them in a zip
 *	archive & extracting them on the server.
 *
 *	Developed and tested using the PmWiki 2.2.0-beta series.
 *	Requires PHP 5 ZipArchive support:
 *		http://php.net/manual/en/ref.zip.php
 *
 *	To install, add the following line to your configuration file :
		include_once("$FarmD/cookbook/multiupload.php");
 *
 *	For more information, please see the online documentation at
 *		http://www.pmwiki.org/wiki/Cookbook/MultiUpload
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License,
 *	Version 2, as published by the Free Software Foundation.
 *	http://www.gnu.org/copyleft/gpl.html
 */

$RecipeInfo['MultiUpload']['Version'] = '2007-11-27';

if ( !class_exists('ZipArchive') ) Abort('PHP 5 <a href=\'http://php.net/manual/en/ref.zip.php\'>ZipArchive</a> support required for multiupload.php cookbook recipe');

SDV($PageUploadFmt,array("
  <div id='wikiupload'>
  <h2 class='wikiaction'>$[Attachments for] {\$FullName}</h2>
  <h3>\$UploadResult</h3>
  <form enctype='multipart/form-data' action='{\$PageUrl}' method='post'>
  <input type='hidden' name='n' value='{\$FullName}' />
  <input type='hidden' name='action' value='postmultiupload' />
  <table border='0'>
    <tr><td align='right'>$[File to upload:]</td><td><input name='uploadfile' type='file' /></td></tr>
    <tr><td align='right'>$[Name attachment as:]</td>
      <td><input type='text' name='upname' value='\$UploadName' /><input type='submit' value=' $[Upload] ' /></td></tr>
    <tr><td></td><td><label>Extract archive contents <input type='checkbox' name='extract' /></label></td></tr></table></form></div>",
  'wiki:$[{$SiteGroup}/UploadQuickReference]'));

SDVA( $HandleActions, array( 'postmultiupload' => 'HandlePostMultiUpload' ) );
SDV( $HandleAuth['postmultiupload'], $HandleAuth['postupload'] );

XLSDV( 'en', array( 'ULfailextract' => 'error extracting file' ) );

function HandlePostMultiUpload($pagename, $auth = 'upload') {
	global $UploadFileFmt, $LastModFile, $EnableUploadVersions, $Now;
	if ( !$_REQUEST['extract'] ) {
		HandlePostUpload( $pagename, $auth );
		return;
	}
	$page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT);
	if (!$page) Abort("?cannot upload to $pagename");
	$zipfile = $_FILES['uploadfile'];
	$zname = $zipfile['name'];
	$upreport = '{$PageUrl}?action=upload&uprname';
	$result = UploadVerifyArchive( $zipfile );
	if ( $result != '' ) {
		Redirect( $pagename, "$upreport=$zname&$result" );
		return;
	}

	$za = new ZipArchive;
	$res = $za->open($zipfile['tmp_name']);
	if ($res !== TRUE) Abort("?error opening archive $zname: ".ZipArchiveErrorStr($res) );
	$filedir = FmtPageName( "$UploadFileFmt", $pagename );
	mkdirp($filedir);

	for ( $i = 0; $i < $za->numFiles; ++$i ) {
		$stat = $za->statIndex($i);
		$name = MakeUploadName( $pagename, preg_replace( '#^.*/([^/]*)$#', '$1', $stat['name'] ) );
		if ( $name == '' ) continue;
		$filepath = "$filedir/$name";
		$result = UploadVerifyArchiveEntry( $stat, $filepath );
		if ( $result != '' ) {
			Redirect( $pagename, "$upreport=$zname/$name&$result" );
			return;
		}
		if ( IsEnabled( $EnableUploadVersions, 0 ) ) @rename( $filepath, "$filepath,$Now" );
		if ( !( $za->renameIndex( $i, $name ) && $za->extractTo( $filedir, $name ) ) ) {
			Redirect( $pagename, "$upreport=$zname/$name&upresult=failextract" );
			return;
		}
		fixperms( $filepath, 0444 );
	}
	if ($LastModFile) { touch($LastModFile); fixperms($LastModFile); }
	Redirect( $pagename, "$upreport=$zname&upresult=success" );
}

function UploadVerifyArchive( $uploadfile ) {
	global $UploadExtSize;
	preg_match( '/\\.([^.\\/]+)$/', $uploadfile['name'], $match ); $ext = @$match[1];
	$maxsize = $UploadExtSize[$ext];
	if ( $maxsize <= 0 ) return "upresult=badtype&upext=$ext";
	if ($uploadfile['size']>$maxsize) 
		return "upresult=toobigext&upext=$ext&upmax=$maxsize";
	switch (@$uploadfile['error']) {
		case 1: return 'upresult=toobig';
		case 2: return 'upresult=toobig';
		case 3: return 'upresult=partial';
		case 4: return 'upresult=nofile';
	}
	if (!is_uploaded_file($uploadfile['tmp_name'])) return 'upresult=nofile';
	return '';
}

function UploadVerifyArchiveEntry( $stat, $filepath ) {
	global $EnableUploadOverwrite, $UploadExtSize, $UploadPrefixQuota, $UploadDirQuota, $UploadDir;
	if ( !$EnableUploadOverwrite && file_exists($filepath) ) return 'upresult=exists';
	preg_match( '/\\.([^.\\/]+)$/', $filepath, $match ); $ext = @$match[1];
	$maxsize = $UploadExtSize[$ext];
	if ( $maxsize <= 0 ) return "upresult=badtype&upext=$ext";
	if ( $stat['size'] > $maxsize ) return "upresult=toobigext&upext=$ext&upmax=$maxsize";
	$filedir = preg_replace('#/[^/]*$#','',$filepath);
	if ( $UploadPrefixQuota && ( dirsize($filedir) - @filesize($filepath) + $stat['size'] ) > $UploadPrefixQuota )
		return 'upresult=pquota';
	if ( $UploadDirQuota && ( dirsize($UploadDir) - @filesize($filepath) + $stat['size'] ) > $UploadDirQuota )
		return 'upresult=tquota';
	return '';
}

function ZipArchiveErrorStr($i) {
	switch($i) {
		case constant('ZIPARCHIVE::ER_OK'): return "No error.";
		case constant('ZIPARCHIVE::ER_MULTIDISK'): return "Multi-disk zip archives not supported.";
		case constant('ZIPARCHIVE::ER_RENAME'): return "Renaming temporary file failed.";
		case constant('ZIPARCHIVE::ER_CLOSE'): return "Closing zip archive failed";
		case constant('ZIPARCHIVE::ER_SEEK'): return "Seek error";
		case constant('ZIPARCHIVE::ER_READ'): return "Read error";
		case constant('ZIPARCHIVE::ER_WRITE'): return "Write error";
		case constant('ZIPARCHIVE::ER_CRC'): return "CRC error";
		case constant('ZIPARCHIVE::ER_ZIPCLOSED'): return "Containing zip archive was closed";
		case constant('ZIPARCHIVE::ER_NOENT'): return "No such file.";
		case constant('ZIPARCHIVE::ER_EXISTS'): return "File already exists";
		case constant('ZIPARCHIVE::ER_OPEN'): return "Can't open file";
		case constant('ZIPARCHIVE::ER_TMPOPEN'): return "Failure to create temporary file.";
		case constant('ZIPARCHIVE::ER_ZLIB'): return "Zlib error";
		case constant('ZIPARCHIVE::ER_MEMORY'): return "Memory allocation failure";
		case constant('ZIPARCHIVE::ER_CHANGED'): return "Entry $i has been changed";
		case constant('ZIPARCHIVE::ER_COMPNOTSUPP'): return "Compression method not supported.";
		case constant('ZIPARCHIVE::ER_EOF'): return "Premature EOF";
		case constant('ZIPARCHIVE::ER_INVAL'): return "Invalid argument";
		case constant('ZIPARCHIVE::ER_NOZIP'): return "Not a zip archive";
		case constant('ZIPARCHIVE::ER_INTERNAL'): return "Internal error";
		case constant('ZIPARCHIVE::ER_INCONS'): return "Zip archive inconsistent";
		case constant('ZIPARCHIVE::ER_REMOVE'): return "Can't remove file";
		case constant('ZIPARCHIVE::ER_DELETED'): return "Entry has been deleted";
	}
	return FALSE;
}