<?php

/***************************************************************************\
 *  SPIP, Systeme de publication pour l'internet                           *
 *                                                                         *
 *  Copyright (c) 2001-2016                                                *
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
\***************************************************************************/

if (!defined("_ECRIRE_INC_VERSION")) return;

include_spip('inc/getdocument');
include_spip('inc/documents');
include_spip('inc/choisir_mode_document'); // compat core
include_spip('inc/renseigner_document');

/**
 * Ajouter des documents
 *
 * @param int $id_document
 *   document a remplacer, ou pour une vignette, l'id_document de maman
 *   0 ou 'new' pour une insertion
 * @param  $files
 *   tableau de taleau de propriete pour chaque document a inserer
 * @param  $objet
 *   objet auquel associer le document
 * @param  $id_objet
 *   id_objet
 * @param  $mode
 *   mode par defaut si pas precise pour le document
 * @return array
 *   liste des id_documents inseres
 */
function action_ajouter_documents_dist($id_document, $files, $objet, $id_objet, $mode){
	$ajouter_un_document = charger_fonction('ajouter_un_document','action');
	$ajoutes = array();

	// on ne peut mettre qu'un seul document a la place d'un autre ou en vignette d'un autre
	if (intval($id_document)){
		$ajoutes[] = $ajouter_un_document($id_document, reset($files), $objet, $id_objet, $mode);
	}
	else
		foreach($files as $file){
			$ajoutes[] = $ajouter_un_document('new', $file, $objet, $id_objet, $mode);
		}
	return $ajoutes;
}

/**
 * Ajouter un document (au format $_FILES)
 *
 * http://code.spip.net/@ajouter_un_document
 *
 * @param int $id_document
 *   document a remplacer, ou pour une vignette, l'id_document de maman
 *   0 ou 'new' pour une insertion
 * @param array $file
 *   proprietes au format $_FILE etendu :
 *     string tmp_name : source sur le serveur
 *     string name : nom du fichier envoye
 *     bool titrer : donner ou non un titre a partir du nom du fichier
 *     bool distant : pour utiliser une source distante sur internet
 *     string mode : vignette|image|documents|choix
 * @param string $objet
 *   objet auquel associer le document
 * @param int $id_objet
 *   id_objet
 * @param string $mode
 *   mode par defaut si pas precise pour le document
 * @return array|bool|int|mixed|string|unknown
 * 	 si int : l'id_document ajouté (opération réussie)
 *   si string : une erreur s'est produit, la chaine est le message d'erreur
 *  
 */
function action_ajouter_un_document_dist($id_document, $file, $objet, $id_objet, $mode) {

	$source = $file['tmp_name'];
	$nom_envoye = $file['name'];

	// passer en minuscules le nom du fichier, pour eviter les collisions
	// si le file system fait la difference entre les deux il ne detectera
	// pas que Toto.pdf et toto.pdf
	// et on aura une collision en cas de changement de file system
	$file['name'] = strtolower(translitteration($file['name']));
	
	// Pouvoir definir dans mes_options.php que l'on veut titrer tous les documents par d?faut
	if (!defined('_TITRER_DOCUMENTS')) { define('_TITRER_DOCUMENTS', false); }

	$titrer = isset($file['titrer'])?$file['titrer']:_TITRER_DOCUMENTS;
	$mode = ((isset($file['mode']) AND $file['mode'])?$file['mode']:$mode);

	include_spip('inc/modifier');
	if (isset($file['distant']) AND $file['distant'] AND !in_array($mode,array('choix','auto','image','document'))) {
		include_spip('inc/distant');
		$file['tmp_name'] = _DIR_RACINE . copie_locale($source);
		$source = $file['tmp_name'];
		unset($file['distant']);
	}

	// Documents distants : pas trop de verifications bloquantes, mais un test
	// via une requete HEAD pour savoir si la ressource existe (non 404), si le
	// content-type est connu, et si possible recuperer la taille, voire plus.
	if (isset($file['distant']) AND $file['distant']) {
		include_spip('inc/distant');
		if (is_array($a = renseigner_source_distante($source))) {

			$champs = $a;
			# NB: dans les bonnes conditions (fichier autorise et pas trop gros)
			# $a['fichier'] est une copie locale du fichier

			unset($champs['type_image']);
		}
		// on ne doit plus arriver ici, car l'url distante a ete verifiee a la saisie !
		else {
			spip_log("Echec du lien vers le document $source, abandon");
			return $a; // message d'erreur
		}
	}
	else { // pas distant

		$champs = array(
			'distant' => 'non'
		);
		
		$type_image = ''; // au pire
		$champs['titre'] = '';
		if ($titrer){
			$titre = substr($nom_envoye,0, strrpos($nom_envoye, ".")); // Enlever l'extension du nom du fichier
			$titre = preg_replace(',[[:punct:][:space:]]+,u', ' ', $titre);
			$champs['titre'] = preg_replace(',\.([^.]+)$,', '', $titre);
		}

		if (!is_array($fichier = fixer_fichier_upload($file, $mode)))
			return is_string($fichier)?$fichier:_T("medias:erreur_upload_type_interdit",array('nom'=>$file['name']));
		
		$champs['inclus'] = $fichier['inclus'];
		$champs['extension'] = $fichier['extension'];
		$champs['fichier'] = $fichier['fichier'];

		/**
		 * Récupère les informations du fichier
		 * -* largeur
		 * -* hauteur
		 * -* type_image
		 * -* taille
		 * -* ses metadonnées si une fonction de metadatas/ est présente
		 */
		$infos = renseigner_taille_dimension_image($champs['fichier'],$champs['extension']);
		if (is_string($infos))
			return $infos; // c'est un message d'erreur !
		
		$champs = array_merge($champs,$infos);

		// Si mode == 'choix', fixer le mode image/document
		if (in_array($mode,array('choix','auto'))) {
			$choisir_mode_document = charger_fonction('choisir_mode_document','inc');
			$mode = $choisir_mode_document($champs, $champs['inclus'] == 'image', $objet);
		}
		$champs['mode'] = $mode;

		if (($test = verifier_taille_document_acceptable($champs))!==true){
			spip_unlink($champs['fichier']);
			return $test; // erreur sur les dimensions du fichier
		}

		unset($champs['type_image']);
		unset($champs['inclus']);
		$champs['fichier'] = set_spip_doc($champs['fichier']);
	}

	// si le media est pas renseigne, le faire, en fonction de l'extension
	if (!isset($champs['media'])){
		$champs['media'] = sql_getfetsel('media_defaut','spip_types_documents','extension='.sql_quote($champs['extension']));
	}
	
	// lier le parent si necessaire
	if ($id_objet=intval($id_objet) AND $objet)
		$champs['parents'][] = "$objet|$id_objet";

	// "mettre a jour un document" si on lui
	// passe un id_document
	if ($id_document=intval($id_document)){
		unset($champs['titre']); // garder le titre d'origine
		unset($champs['date']); // garder la date d'origine
		unset($champs['descriptif']); // garder la desc d'origine
		// unset($a['distant']); # on peut remplacer un doc statique par un doc distant
		// unset($a['mode']); # on peut remplacer une image par un document ?
	}

	include_spip('action/editer_document');
	// Installer le document dans la base
	if (!$id_document){
		if ($id_document = document_inserer())
			spip_log ("ajout du document ".$file['tmp_name']." ".$file['name']."  (M '$mode' T '$objet' L '$id_objet' D '$id_document')",'medias');
		else
			spip_log ("Echec insert_document() du document ".$file['tmp_name']." ".$file['name']."  (M '$mode' T '$objet' L '$id_objet' D '$id_document')",'medias'._LOG_ERREUR);
	}
	if (!$id_document)
		return _T('medias:erreur_insertion_document_base',array('fichier'=>"<em>".$file['name']."</em>"));
	
	document_modifier($id_document,$champs);

	// permettre aux plugins de faire des modifs a l'ajout initial
	// ex EXIF qui tourne les images si necessaire
	// Ce plugin ferait quand même mieux de se placer dans metadata/jpg.php
	pipeline('post_edition',
		array(
			'args' => array(
				'table' => 'spip_documents', // compatibilite
				'table_objet' => 'documents',
				'spip_table_objet' => 'spip_documents',
				'type' =>'document',
				'id_objet' => $id_document,
				'champs' => array_keys($champs),
				'serveur' => '', // serveur par defaut, on ne sait pas faire mieux pour le moment
				'action' => 'ajouter_document',
				'operation' => 'ajouter_document', // compat <= v2.0
			),
			'data' => $champs
		)
	);

	return $id_document ;
}


/**
 * Corrige l'extension du fichier dans quelques cas particuliers
 * (a passer dans ecrire/base/typedoc)
 * A noter : une extension 'pdf ' passe dans la requete de controle
 * mysql> SELECT * FROM spip_types_documents WHERE extension="pdf ";
 *
 * @param string $ext
 * @return string
 */
function corriger_extension($ext) {
	$ext = preg_replace(',[^a-z0-9],i', '', $ext);
	switch ($ext) {
		case 'htm':
			$ext='html';
			break;
		case 'jpeg':
			$ext='jpg';
			break;
		case 'tiff':
			$ext='tif';
			break;
		case 'aif':
			$ext='aiff';
			break;
		case 'mpeg':
			$ext='mpg';
			break;
	}
	return $ext;
}

/**
 * Verifie la possibilite d'uploader une extension
 * renvoie un tableau descriptif si l'extension est acceptee
 * avec un index 'autozip' si il faut zipper
 * false ou message d'erreur si l'extension est refusee
 * Verifie aussi si l'extension est autorisee pour le mode demande
 * si on connait le mode a ce moment la
 * 
 */
function verifier_upload_autorise($source, $mode=''){
	$infos = array('fichier'=>$source);
	$res = false;
	if (preg_match(",\.([a-z0-9]+)(\?.*)?$,i", $source, $match)
	  AND $ext = $match[1]){
		
	  $ext = corriger_extension(strtolower($ext));
		if ($res = sql_fetsel("extension,inclus,media_defaut as media", "spip_types_documents", "extension=" . sql_quote($ext) . " AND upload='oui'"))
			$infos = array_merge($infos,$res);
	}
	if (!$res){
		if ($res = sql_fetsel("extension,inclus,media_defaut as media", "spip_types_documents", "extension='zip' AND upload='oui'")){
			$infos = array_merge($infos,$res);
			$res['autozip'] = true;
		}
	}
	if ($mode AND $res){
		// verifier en fonction du mode si une fonction est proposee
		if ($verifier_document_mode = charger_fonction("verifier_document_mode_".$mode,"inc",true)){
			$check = $verifier_document_mode($infos); // true ou message d'erreur sous forme de chaine
			if ($check!==true)
				$res = $check;
		}
	}

	if (!$res OR is_string($res)) {
		spip_log("Upload $source interdit ($res)",_LOG_INFO_IMPORTANTE);
	}

	return $res;
}


/**
 * tester le type de document :
 * - interdit a l'upload ?
 * - quelle extension dans spip_types_documents ?
 * - est-ce "inclus" comme une image ?
 * 
 * le zipper si necessaire
 * 
 * @param array $file //format $_FILES
 * @return array
 */
function fixer_fichier_upload($file, $mode=''){

	if (is_array($row=verifier_upload_autorise($file['name'], $mode))) {
		if (!isset($row['autozip'])){
			$row['fichier'] = copier_document($row['extension'], $file['name'], $file['tmp_name']);
			
			/**
			 * On vérifie que le fichier a une taille
			 * si non, on le supprime et on affiche une erreur
			 */
			if($row['fichier'] && (!$taille = @intval(filesize(get_spip_doc($row['fichier']))))) { 
				spip_log ("Echec copie du fichier ".$file['tmp_name']." (taille de fichier indéfinie)");
				spip_unlink(get_spip_doc($row['fichier']));
				return _T('medias:erreur_copie_fichier',array('nom'=> $file['tmp_name']));
			}else 
				return $row;
		}
		// creer un zip comme demande
		// pour encapsuler un fichier dont l'extension n'est pas supportee
		else{
		
			unset($row['autozip']);

			$ext = 'zip';
			if (!$tmp_dir = tempnam(_DIR_TMP, 'tmp_upload'))
				return false;

			spip_unlink($tmp_dir);
			@mkdir($tmp_dir);

			include_spip('inc/charsets');
			$tmp = $tmp_dir.'/'.translitteration($file['name']);

			$file['name'] .= '.'.$ext; # conserver l'extension dans le nom de fichier, par exemple toto.js => toto.js.zip

			// deplacer le fichier tmp_name dans le dossier tmp
			deplacer_fichier_upload($file['tmp_name'], $tmp, true);

			include_spip('inc/pclzip');
			$source = _DIR_TMP . basename($tmp_dir) . '.'.$ext;
			$archive = new PclZip($source);

			$v_list = $archive->create($tmp,
					PCLZIP_OPT_REMOVE_PATH, $tmp_dir,
					PCLZIP_OPT_ADD_PATH, '');

			effacer_repertoire_temporaire($tmp_dir);
			if (!$v_list) {
				spip_log("Echec creation du zip ");
				return false;
			}

			$row['fichier']  = copier_document($row['extension'], $file['name'], $source);
			spip_unlink($source);
			
			/**
			 * On vérifie que le fichier zip créé a une taille
			 * si non, on le supprime et on affiche une erreur
			 */
			if($row['fichier'] && (!$taille = @intval(filesize(get_spip_doc($row['fichier']))))) { 
				spip_log ("Echec copie du fichier ".$file['tmp_name']." (taille de fichier indéfinie)");
				spip_unlink(get_spip_doc($row['fichier']));
				return _T('medias:erreur_copie_fichier',array('nom'=> $file['tmp_name']));
			}else 
				return $row;
		}
	}
	else
		return $row; // retourner le message d'erreur
}

/**
 * Verifier si le fichier respecte les contraintes de tailles
 * 
 * @param  array $infos
 * @return bool|mixed|string
 */
function verifier_taille_document_acceptable($infos){
	
	// si ce n'est pas une image
	if (!$infos['type_image']) {
		if (_DOC_MAX_SIZE > 0
		 AND $infos['taille'] > _DOC_MAX_SIZE*1024)
			return _T('medias:info_doc_max_poids', array('maxi' => taille_en_octets(_DOC_MAX_SIZE*1024), 'actuel' => taille_en_octets($infos['taille'])));
	}
	
	// si c'est une image
	else {

		if (_IMG_MAX_SIZE > 0
		 AND $infos['taille'] > _IMG_MAX_SIZE*1024)
			return _T('medias:info_image_max_poids', array('maxi' => taille_en_octets(_IMG_MAX_SIZE*1024), 'actuel' => taille_en_octets($infos['taille'])));
	
		if (_IMG_MAX_WIDTH * _IMG_MAX_HEIGHT
		 AND ($infos['largeur'] > _IMG_MAX_WIDTH
		 OR $infos['hauteur'] > _IMG_MAX_HEIGHT))

			return _T('medias:info_logo_max_taille',
					array(
					'maxi' =>
						_T('info_largeur_vignette',
							array('largeur_vignette' => _IMG_MAX_WIDTH,
							'hauteur_vignette' => _IMG_MAX_HEIGHT)),
					'actuel' =>
						_T('info_largeur_vignette',
							array('largeur_vignette' => $infos['largeur'],
							'hauteur_vignette' => $infos['hauteur']))
				));
	}

	// verifier en fonction du mode si une fonction est proposee
	if ($verifier_document_mode = charger_fonction("verifier_document_mode_".$infos['mode'],"inc",true))
		return $verifier_document_mode($infos);

	return true;
}


?>
