mambax7/gwiki

View on GitHub
ajaxfileedit.php

Summary

Maintainability
D
1 day
Test Coverage
<?php
/**
 * ajaxfileedit.php - backend upload attachments and update file info
 *
 * @copyright  Copyright © 2013 geekwright, LLC. All rights reserved.
 * @license    gwiki/docs/license.txt  GNU General Public License (GPL)
 * @since      1.0
 * @author     Richard Griffith <richard@geekwright.com>
 * @package    gwiki
 */

use XoopsModules\Gwiki;
use XoopsModules\Gwiki\Helper;

require_once \dirname(__DIR__, 2) . '/mainfile.php';
$xoopsLogger->activated = false;
// provide error logging for our sanity in debugging ajax use (won't see xoops logger)
restore_error_handler();
error_reporting(-1);

$dir = basename(__DIR__);
//require_once XOOPS_ROOT_PATH . '/modules/' . $dir . '/class/GwikiPage.php';
$helper = Helper::getInstance();
global $wikiPage;
$wikiPage = new Gwiki\GwikiPage();

$uploadpath = XOOPS_ROOT_PATH . "/uploads/{$dir}/";
$uploadurl  = XOOPS_URL . "/uploads/{$dir}/";

$newfile  = ($_SERVER['HTTP_GW_FILENAME'] ?? false);
$jsondata = ($_SERVER['HTTP_GW_JSONDATA'] ?? false);

// initialize whitelist
$whitelist = [];
$wlconfig  = $helper->getConfig('attach_ext_whitelist');
//$wlconfig='txt,pdf,doc,docx,xls,rtf,zip';

// populate whitelist
if (!empty($wlconfig)) {
    $whitelist = explode(',', $wlconfig);
}

/**
 * @param $filename
 *
 * @return array|bool
 */
function getExtensionInfo($filename)
{
    global $whitelist;

    $fi = [];

    // these choices are just from our icon set - nothing magic, just ext => filename - .png
    $icons = [
        'aac'  => 'aac',
        'aiff' => 'aiff',
        'ai'   => 'ai',
        'avi'  => 'avi',
        'bmp'  => 'bmp',
        'c'    => 'c',
        'cpp'  => 'cpp',
        'css'  => 'css',
        'dat'  => 'dat',
        'dmg'  => 'dmg',
        'doc'  => 'doc',
        'docx' => 'doc',
        'dot'  => 'dotx',
        'dotx' => 'dotx',
        'dwg'  => 'dwg',
        'dxf'  => 'dxf',
        'eps'  => 'eps',
        'exe'  => 'exe',
        'flv'  => 'flv',
        'gif'  => 'gif',
        'h'    => 'h',
        'hpp'  => 'hpp',
        'htm'  => 'html',
        'html' => 'html',
        'ics'  => 'ics',
        'iso'  => 'iso',
        'java' => 'java',
        'jpe'  => 'jpg',
        'jpeg' => 'jpg',
        'jpg'  => 'jpg',
        'key'  => 'key',
        'mid'  => 'mid',
        'mp3'  => 'mp3',
        'mp4'  => 'mp4',
        'mpg'  => 'mpg',
        'odf'  => 'odf',
        'ods'  => 'ods',
        'odt'  => 'odt',
        'otp'  => 'otp',
        'ots'  => 'ots',
        'ott'  => 'ott',
        'pdf'  => 'pdf',
        'php'  => 'php',
        'png'  => 'png',
        'ppt'  => 'ppt',
        'psd'  => 'psd',
        'py'   => 'py',
        'qt'   => 'qt',
        'rar'  => 'rar',
        'rb'   => 'rb',
        'rtf'  => 'rtf',
        'sql'  => 'sql',
        'tga'  => 'tga',
        'tgz'  => 'tgz',
        'tif'  => 'tiff',
        'tiff' => 'tiff',
        'txt'  => 'txt',
        'wav'  => 'wav',
        'xls'  => 'xls',
        'xlsx' => 'xlsx',
        'xml'  => 'xml',
        'yml'  => 'yml',
        'zip'  => 'zip',
    ];
    // Also have files '_blank' '_page'

    $path_parts = pathinfo($filename);
    // =$path_parts['dirname'], "\n";
    $fi['file_name'] = $path_parts['basename'];
    if (!isset($path_parts['extension'])) {
        $ext = '';
    } else {
        $ext = mb_strtolower($path_parts['extension']);
    }
    //=$path_parts['filename'];

    // if no name, or not on whitelist reject
    if (empty($path_parts['filename']) || false === array_search($ext, $whitelist, true)) {
        return false;
    }

    if (empty($ext)) {
        $fi['file_icon'] = '_blank';
    } else {
        if (empty($icons[$ext])) {
            $fi['file_icon'] = '_blank';
        } else {
            $fi['file_icon'] = $icons[$ext];
        }
    }

    return $fi;
}

/**
 * @param $filename
 *
 * @return array
 */
function getFileInfo($filename)
{
    $fi = [];

    if (function_exists('finfo_open')) {
        $finfo           = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
        $fi['file_type'] = finfo_file($finfo, $filename);
        finfo_close($finfo);
    } else {
        $fi['file_type'] = mime_content_type($filename);
    }
    $fi['file_size'] = filesize($filename);

    return $fi;
}

/**
 * @param $string
 *
 * @return string
 */
function cleaner($string)
{
    $string = stripcslashes($string);
    $string = html_entity_decode($string);
    $string = strip_tags($string); // DANGER -- kills wiki text
    $string = trim($string);
    $string = stripslashes($string);

    return $string;
}

/**
 * @param $uid
 *
 * @return string
 */
function getUserName($uid)
{
    global $xoopsConfig;

    $uid = (int)$uid;

    if ($uid > 0) {
        /** @var \XoopsMemberHandler $memberHandler */
        $memberHandler = xoops_getHandler('member');
        $user          = $memberHandler->getUser($uid);
        if (is_object($user)) {
            return '<a href="' . XOOPS_URL . "/userinfo.php?uid=$uid\">" . htmlspecialchars($user->getVar('uname'), ENT_QUOTES) . '</a>';
        }
    }

    return $xoopsConfig['anonymous'];
}

/**
 * @param $input
 *
 * @return mixed
 */
function deleteData(&$input)
{
    global $xoopsDB, $uploadpath, $wikiPage;

    $q_file_id = (int)$input['file_id'];
    // use keyword in delete so we know id and edit authority are connected
    $q_keyword = $wikiPage->escapeForDB($input['page']);

    // look up the name and delete the image file
    $sql = 'SELECT file_path FROM ' . $xoopsDB->prefix('gwiki_page_files') . " where file_id='{$q_file_id}' AND keyword = '{$q_keyword}' ";

    $result = $xoopsDB->query($sql);
    if ($result) {
        $rows = $xoopsDB->getRowsNum($result);
        if ($rows) {
            $myrow = $xoopsDB->fetchArray($result);
            if (!empty($myrow['file_path'])) {
                $oldfilename = $uploadpath . '/' . $myrow['file_path'];
                unlink($oldfilename);
            }
        }
    }

    // delete the row
    $sql = 'DELETE FROM ' . $xoopsDB->prefix('gwiki_page_files') . " where file_id='{$q_file_id}' AND keyword = '{$q_keyword}' ";

    $result = $xoopsDB->queryF($sql);
    $cnt    = $xoopsDB->getAffectedRows();
    if ($cnt) {
        $input['message'] = _MD_GWIKI_AJAX_FILEEDIT_DEL_OK;
    }

    return $result;
}

/**
 * @param $input
 *
 * @return mixed
 */
function updateData(&$input)
{
    global $xoopsDB, $xoopsUser, $wikiPage;

    $q_file_id          = (int)$input['file_id'];
    $q_keyword          = $wikiPage->escapeForDB($input['page']);
    $q_file_name        = $wikiPage->escapeForDB($input['file_name']);
    $q_file_icon        = $wikiPage->escapeForDB($input['file_icon']);
    $q_file_type        = $wikiPage->escapeForDB($input['file_type']);
    $q_file_description = $wikiPage->escapeForDB($input['file_description']);
    //  file_path only changed by image upload
    $q_file_size       = (int)$input['file_size'];
    $q_file_path       = empty($input['file_path']) ? '' : $wikiPage->escapeForDB($input['file_path']);
    $q_file_uid        = $xoopsUser ? $xoopsUser->getVar('uid') : 0;
    $input['file_uid'] = $q_file_uid;
    if (0 === (int)$input['file_upload_date']) {
        $input['file_upload_date'] = time();
    }
    $q_file_upload_date = $input['file_upload_date'];

    $sql = 'UPDATE ' . $xoopsDB->prefix('gwiki_page_files') . ' SET ';
    $sql .= " file_name = '{$q_file_name}', ";
    $sql .= " file_icon = '{$q_file_icon}', ";
    $sql .= " file_type = '{$q_file_type}', ";
    $sql .= " file_size = '{$q_file_size}', ";
    $sql .= " file_path = '{$q_file_path}', ";
    $sql .= " file_uid  = '{$q_file_uid}', ";
    //  $sql.= " file_uid  = '{$q_file_upload_date}', ";
    $sql .= " file_description = '{$q_file_description}' ";
    $sql .= " where file_id = '{$q_file_id}' AND keyword = '{$q_keyword}' ";

    $result = $xoopsDB->queryF($sql);
    if (!$result) {
        header('Status: 500 Internal Error - Database Error');
        $out['message'] === $xoopsDB->error();
        echo json_encode($out);
        exit;
    }
    $cnt = $xoopsDB->getAffectedRows();
    if (!$cnt) {
        $input['message'] = _MD_GWIKI_AJAX_FILEEDIT_NOT_DEFINED;
    } else {
        $input['message'] = _MD_GWIKI_AJAX_FILEEDIT_UPD_OK;
    }

    //file_id, keyword, file_name, file_path, file_type, file_icon, file_size, file_upload_date, file_description, file_uid

    if ($result && !$cnt && !empty($q_file_path)) { // database is OK but nothing to update - require_once file_path
        $sql              = 'INSERT INTO ' . $xoopsDB->prefix('gwiki_page_files');
        $sql              .= ' (keyword, file_name, file_path, file_type, file_icon, file_size, file_upload_date, file_description, file_uid) ';
        $sql              .= " values ('{$q_keyword}', '{$q_file_name}', '{$q_file_path}', '{$q_file_type}', '{$q_file_icon}', '{$q_file_size}', $q_file_upload_date, '{$q_file_description}', '{$q_file_uid}' )";
        $result           = $xoopsDB->queryF($sql);
        $input['file_id'] = $xoopsDB->getInsertId();
        $input['message'] = _MD_GWIKI_AJAX_FILEEDIT_ADD_OK;
    }

    return $input['file_id'];
}

/**
 * @param $newfile
 * @param $input
 *
 * @return mixed
 */
function updateFile($newfile, &$input)
{
    global $uploadpath, $xoopsDB;
    // For now, images are stored in individual directories for each page.
    // We can change the directory distribution later, as the entire path
    // relative to /uploads/gwiki/ ($relpath) is stored in the database.

    // We get rid of any colons in the page name in case the filesystem has
    // issues with them. (undescore is illegal in page name, so it stays unique.)
    $relpath  = 'pages/' . str_replace(':', '_', $input['page']) . '/file/';
    $ourpath  = $uploadpath . $relpath;
    $oldUmask = umask(0);
    @mkdir($ourpath, 0755, true);
    umask($oldUmask);
    $tempfn = tempnam($ourpath, 'WIKIFILE_');
    $image  = file_get_contents('php://input');
    file_put_contents($tempfn, $image);

    $filename = $ourpath . $newfile;
    if (empty($input['file_name'])) {
        $input['file_name'] = $justfn;
    }

    $fi = getFileInfo($tempfn);
    foreach ($fi as $k => $v) {
        $input[$k] = $v;
    }

    $input['file_name'] = $newfile;
    $input['file_path'] = $relpath . $newfile;

    rename($tempfn, $filename);
    chmod($filename, 0644);
    $q_file_id = (int)$input['file_id'];
    $sql       = 'SELECT file_path FROM ' . $xoopsDB->prefix('gwiki_page_files') . " where file_id='{$q_file_id}' ";

    $result = $xoopsDB->query($sql);
    if ($result) {
        $rows = $xoopsDB->getRowsNum($result);
        if ($rows) {
            $myrow = $xoopsDB->fetchArray($result);
            if (!empty($myrow['file_path'])) {
                $oldfilename = $uploadpath . '/' . $myrow['file_path'];
                unlink($oldfilename);
            }
            // update
        }
        // new row
    }
    $input['file_upload_date'] = time();

    return updateData($input);
}

if (!$jsondata) {
    header('Status: 500 Internal Error - No Data Passed');
    exit;
}
$input = json_decode($jsondata, true);
//file_put_contents ( XOOPS_ROOT_PATH.'/uploads/debug.txt', print_r($input,true));

if (!empty($input['file_id'])) {
    $q_file_id = (int)$input['file_id'];
    $sql       = 'SELECT * FROM ' . $xoopsDB->prefix('gwiki_page_files') . " where file_id = '{$q_file_id}' ";
    $result    = $xoopsDB->query($sql);
    $row       = $xoopsDB->fetchArray($result);
    if ($row) {
        $input['page'] = $row['keyword'];
        foreach ($row as $k => $v) {
            if (!isset($input[$k])) {
                $input[$k] = $v;
            }
        }
    }
}

if (empty($input['page'])) {
    header('Status: 500 Internal Error - No Page');
    exit;
}
$input['page'] = mb_strtolower($wikiPage->normalizeKeyword($input['page']));
$pageX         = $wikiPage->getPage($input['page']);
$mayEdit       = $wikiPage->checkEdit();

if (!$mayEdit) {
    header('Status: 403 Forbidden - No Permission');
    if (!$mayEdit) {
        $out['message'] = _MD_GWIKI_AJAX_FILEEDIT_NO_AUTH;
    }
    echo json_encode($out);
    exit;
}

$q_newfile = $wikiPage->escapeForDB($newfile);
$q_keyword = $wikiPage->escapeForDB($input['page']);

$sql = 'SELECT file_id FROM ' . $xoopsDB->prefix('gwiki_page_files') . " where file_name = '{$q_newfile}' AND keyword='{$q_keyword}' ";

$result = $xoopsDB->query($sql);
$row    = $xoopsDB->fetchArray($result);
if ($row) {
    if ($input['file_id'] !== $row['file_id']) {
        header('Status: 500 Internal Error - Duplicate File Name');
        $out['message'] = _MD_GWIKI_AJAX_FILEEDIT_DUPLICATE;
        echo json_encode($out);
        exit;
    }
}

/*
 * This creates issues if page being edited has not been saved yet, so let's not be anal about it
    if (!$pageX) {
        header("Status: 403 Forbidden - No Page");
        if(!$pageX) $out['message']='Page does not exist';
        echo json_encode($out);
        exit;
    }
*/

if ($newfile) {
    $fi = getExtensionInfo($newfile);
    if (false === $fi) {
        header('Status: 403 Forbidden - Bad File Type');
        $out['message'] = _MD_GWIKI_AJAX_FILEEDIT_BAD_TYPE;
        echo json_encode($out);
        exit;
    }
    foreach ($fi as $k => $v) {
        $input[$k] = $v;
    }
    $input['file_id'] = updateFile($newfile, $input);
    if ($input['file_id']) {
        $input['message']  = 'Attachment Saved';
        $input['link']     = $uploadurl . $input['file_path'];
        $input['iconlink'] = XOOPS_URL . '/modules/' . $dir . '/assets/icons/48px/' . $input['file_icon'] . '.png';
        $input['userlink'] = getUserName($input['file_uid']);
        $input['size']     = number_format($input['file_size']);
        $input['date']     = date($wikiPage->dateFormat, $input['file_upload_date']);
    }
} else {
    if (!empty($input['op']) && 'delete' === $input['op']) {
        deleteData($input);
    } else {
        updateData($input);
    }
}
echo json_encode($input);
exit;