HEX
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.4.30
System: Linux iZj6c1151k3ad370bosnmsZ 3.10.0-1160.76.1.el7.x86_64 #1 SMP Wed Aug 10 16:21:17 UTC 2022 x86_64
User: root (0)
PHP: 7.4.30
Disabled: NONE
Upload Files
File: /var/www/html/phpmyfaq/api.php
<?php

/**
 * The REST API
 *
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/.
 *
 * @package   phpMyFAQ
 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
 * @copyright 2009-2022 phpMyFAQ Team
 * @license   http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
 * @link      https://www.phpmyfaq.de
 * @since     2009-09-03
 */

const IS_VALID_PHPMYFAQ = null;

use phpMyFAQ\Attachment\AttachmentException;
use phpMyFAQ\Attachment\AttachmentFactory;
use phpMyFAQ\Category;
use phpMyFAQ\Comments;
use phpMyFAQ\Entity\CommentType;
use phpMyFAQ\Entity\FaqEntity;
use phpMyFAQ\Faq;
use phpMyFAQ\Faq\FaqMetaData;
use phpMyFAQ\Faq\FaqPermission;
use phpMyFAQ\Filter;
use phpMyFAQ\Helper\HttpHelper;
use phpMyFAQ\Helper\RegistrationHelper;
use phpMyFAQ\Language;
use phpMyFAQ\Language\Plurals;
use phpMyFAQ\News;
use phpMyFAQ\Permission\MediumPermission;
use phpMyFAQ\Question;
use phpMyFAQ\Search;
use phpMyFAQ\Search\SearchResultSet;
use phpMyFAQ\Services;
use phpMyFAQ\Strings;
use phpMyFAQ\Tags;
use phpMyFAQ\User\CurrentUser;
use phpMyFAQ\Utils;

//
// Bootstrapping
//
require 'src/Bootstrap.php';

//
// Send headers
//
$http = new HttpHelper();
$http->setContentType('application/json');
$http->fetchAllHeaders();
$http->addHeader();
//
// Set user permissions
//
$auth = false;

$action = Filter::filterInput(INPUT_GET, 'action', FILTER_UNSAFE_RAW);
$lang = Filter::filterInput(INPUT_GET, 'lang', FILTER_UNSAFE_RAW, 'en');
$categoryId = Filter::filterInput(INPUT_GET, 'categoryId', FILTER_VALIDATE_INT);
$recordId = Filter::filterInput(INPUT_GET, 'recordId', FILTER_VALIDATE_INT);
$tagId = Filter::filterInput(INPUT_GET, 'tagId', FILTER_VALIDATE_INT);

$faqUsername = Filter::filterInput(INPUT_POST, 'username', FILTER_UNSAFE_RAW);
$faqPassword = Filter::filterInput(INPUT_POST, 'password', FILTER_UNSAFE_RAW, FILTER_FLAG_NO_ENCODE_QUOTES);

//
// Get language (default: english)
//
$language = new Language($faqConfig);
$currentLanguage = $language->setLanguageByAcceptLanguage();

//
// Set language
//
if (Language::isASupportedLanguage($currentLanguage)) {
    require PMF_LANGUAGE_DIR . '/language_' . $currentLanguage . '.php';
} else {
    require PMF_LANGUAGE_DIR . '/language_en.php';
}
$faqConfig->setLanguage($language);

$plr = new Plurals($PMF_LANG);
Strings::init($currentLanguage);

//
// Set empty result
$result = [];

//
// Check if user is already authenticated
//
if (is_null($faqUsername) && is_null($faqPassword)) {
    $user = CurrentUser::getFromCookie($faqConfig);
    if (!$user instanceof CurrentUser) {
        $user = CurrentUser::getFromSession($faqConfig);
    }
    if ($user instanceof CurrentUser) {
        $auth = true;
    } else {
        $user = new CurrentUser($faqConfig);
    }
} else {
    $user = new CurrentUser($faqConfig);
}

//
// Get current user and group id - default: -1
//
if (!is_null($user) && $user instanceof CurrentUser) {
    $currentUser = $user->getUserId();
    if ($user->perm instanceof MediumPermission) {
        $currentGroups = $user->perm->getUserGroups($currentUser);
    } else {
        $currentGroups = [-1];
    }
    if (0 == count($currentGroups)) {
        $currentGroups = [-1];
    }
} else {
    $currentUser = -1;
    $currentGroups = [-1];
}

//
// Handle actions
//
switch ($action) {
    //
    // v2.1
    //
    case 'version':
        $result = $faqConfig->getVersion();
        break;

    case 'language':
        $result = $faqConfig->getLanguage()->getLanguage();
        break;

    case 'search':
        $user = new CurrentUser($faqConfig);
        $search = new Search($faqConfig);
        $search->setCategory(new Category($faqConfig));

        $faqPermission = new FaqPermission($faqConfig);
        $faqSearchResult = new SearchResultSet($user, $faqPermission, $faqConfig);

        $searchString = Filter::filterInput(INPUT_GET, 'q', FILTER_UNSAFE_RAW);
        try {
            $searchResults = $search->search($searchString, false);
            $faqSearchResult->reviewResultSet($searchResults);
            if ($faqSearchResult->getNumberOfResults() > 0) {
                $url = $faqConfig->getDefaultUrl() . 'index.php?action=faq&cat=%d&id=%d&artlang=%s';
                foreach ($faqSearchResult->getResultSet() as $data) {
                    $data->answer = html_entity_decode(strip_tags($data->answer), ENT_COMPAT, 'utf-8');
                    $data->answer = Utils::makeShorterText($data->answer, 12);
                    $data->link = sprintf($url, $data->category_id, $data->id, $data->lang);
                    $result[] = $data;
                }
            } else {
                $http->setStatus(404);
            }
        } catch (Exception $e) {
            $http->setStatus(400);
        }
        break;

    case 'categories':
        $category = new Category($faqConfig, $currentGroups, true);
        $category->setUser($currentUser);
        $category->setGroups($currentGroups);
        $category->setLanguage($currentLanguage);
        $result = array_values($category->getAllCategories());
        if (count($result) === 0) {
            $http->setStatus(404);
        }
        break;

    case 'tags':
        $tags = new Tags($faqConfig);
        $result = $tags->getPopularTagsAsArray(16);
        if (count($result) === 0) {
            $http->setStatus(404);
        }
        break;

    case 'open-questions':
        $questions = new Question($faqConfig);
        $result = $questions->getAllOpenQuestions();
        if (count($result) === 0) {
            $http->setStatus(404);
        }
        break;

    case 'searches':
        $search = new Search($faqConfig);
        $result = $search->getMostPopularSearches(7, true);
        if (count($result) === 0) {
            $http->setStatus(404);
        }
        break;

    case 'comments':
        $comment = new Comments($faqConfig);
        $result = $comment->getCommentsData($recordId, CommentType::FAQ);
        if (count($result) === 0) {
            $http->setStatus(404);
        }
        break;

    case 'attachments':
        $attachments = $result = [];
        try {
            $attachments = AttachmentFactory::fetchByRecordId($faqConfig, $recordId);
        } catch (AttachmentException $e) {
            $result = [];
        }
        foreach ($attachments as $attachment) {
            $result[] = [
                'filename' => $attachment->getFilename(),
                'url' => $faqConfig->getDefaultUrl() . $attachment->buildUrl(),
            ];
        }
        if (count($result) === 0) {
            $http->setStatus(404);
        }
        break;

    case 'news':
        $news = new News($faqConfig);
        $result = $news->getLatestData(false, true, true);
        if (count($result) === 0) {
            $http->setStatus(404);
        }
        break;


    case 'faqs':
        $filter = Filter::filterInput(INPUT_GET, 'filter', FILTER_UNSAFE_RAW);
        $faq = new Faq($faqConfig);
        $faq->setUser($currentUser);
        $faq->setGroups($currentGroups);

        // api/v2.1/faqs/:categoryId
        if (!is_null($categoryId)) {
            try {
                $result = $faq->getAllRecordsByCategoryId($categoryId);
            } catch (Exception $e) {
                $http->setStatus(400);
            }
        }

        // api/v2.1/faqs/tags/:tagId
        if (!is_null($tagId)) {
            $tags = new Tags($faqConfig);
            $recordIds = $tags->getFaqsByTagId($tagId);
            try {
                $result = $faq->getRecordsByIds($recordIds);
            } catch (Exception $e) {
                $http->setStatus(400);
            }
        }

        // api/v2.1/faqs/popular
        if ('popular' === $filter) {
            $result = array_values($faq->getTopTenData(PMF_NUMBER_RECORDS_TOPTEN));
        }

        // api/v2.1/faqs/latest
        if ('latest' === $filter) {
            $result = array_values($faq->getLatestData(PMF_NUMBER_RECORDS_LATEST));
        }

        // api/v2.1/faqs/sticky
        if ('sticky' === $filter) {
            $result = array_values($faq->getStickyRecordsData());
        }

        // api/v2.1/faqs
        if (is_null($categoryId) && is_null($tagId) && is_null($filter)) {
            $faq->getAllRecords(FAQ_SORTING_TYPE_CATID_FAQID, ['lang' => $currentLanguage]);
            $result = $faq->faqRecords;
        }

        if (count($result) === 0) {
            $http->setStatus(404);
        }
        break;


    case 'faq':
        //
        // GET
        //
        $filter = Filter::filterInput(INPUT_GET, 'filter', FILTER_UNSAFE_RAW);
        $faq = new Faq($faqConfig);
        $faq->setUser($currentUser);
        $faq->setGroups($currentGroups);

        if ($recordId > 0) {
            $faq->getRecord($recordId);
            $result = $faq->faqRecord;

            if (count($result) === 0 || $result['solution_id'] === 42) {
                $result = new stdClass();
                $http->setStatus(404);
            }

            if ('pdf' === $filter) {
                $service = new Services($faqConfig);
                $service->setFaqId($recordId);
                $service->setLanguage($currentLanguage);
                $service->setCategoryId($categoryId);

                $result = $service->getPdfApiLink();
            }
            break;
        }

        //
        // POST
        //
        if ($faqConfig->get('api.apiClientToken') !== $http->getClientApiToken()) {
            $http->setStatus(401);
            $result = [
                'stored' => false,
                'error' => 'X_PMF_Token not valid.'
            ];
        }

        $postData = json_decode(file_get_contents('php://input'), true);

        $languageCode = Filter::filterVar($postData['language'], FILTER_SANITIZE_SPECIAL_CHARS);
        $categoryId = Filter::filterVar($postData['category-id'], FILTER_VALIDATE_INT);
        $question = Filter::filterVar($postData['question'], FILTER_SANITIZE_SPECIAL_CHARS);
        $answer = Filter::filterVar($postData['answer'], FILTER_SANITIZE_SPECIAL_CHARS);
        $keywords = Filter::filterVar($postData['keywords'], FILTER_SANITIZE_SPECIAL_CHARS);
        $author = Filter::filterVar($postData['author'], FILTER_SANITIZE_SPECIAL_CHARS);
        $email = Filter::filterVar($postData['email'], FILTER_SANITIZE_EMAIL);
        $isActive = Filter::filterVar($postData['is-active'], FILTER_VALIDATE_BOOLEAN);
        $isSticky = Filter::filterVar($postData['is-sticky'], FILTER_VALIDATE_BOOLEAN);

        $categories = [ $categoryId ];

        $isActive = !is_null($isActive);
        $isSticky = !is_null($isSticky);

        $faqData = new FaqEntity();
        $faqData
            ->setLanguage($languageCode)
            ->setQuestion($question)
            ->setAnswer($answer)
            ->setKeywords($keywords)
            ->setAuthor($author)
            ->setEmail($email)
            ->setActive($isActive)
            ->setSticky($isSticky)
            ->setComment(false)
            ->setLinkState('')
            ->setNotes('');

        $faqId = $faq->create($faqData);

        $faqMetaData = new FaqMetaData($faqConfig);
        $faqMetaData
            ->setFaqId($faqId)
            ->setFaqLanguage($languageCode)
            ->setCategories($categories)
            ->save();

        $result = [
            'stored' => true
        ];
        break;

    case 'login':
        $currentUser = new CurrentUser($faqConfig);

        if ($currentUser->login($faqUsername, $faqPassword)) {
            if ($currentUser->getStatus() !== 'blocked') {
                $auth = true;
                $result = [
                    'loggedin' => true
                ];
            } else {
                $auth = false;
                $http->setStatus(400);
                $result = [
                    'loggedin' => false,
                    'error' => $PMF_LANG['ad_auth_fail']
                ];
            }
        } else {
            $auth = false;
            $http->setStatus(400);
            $result = [
                'loggedin' => false,
                'error' => $PMF_LANG['ad_auth_fail']
            ];
        }
        break;

    case 'register':
        if ($faqConfig->get('api.apiClientToken') !== $http->getClientApiToken()) {
            $http->setStatus(401);
            $result = [
                'registered' => false,
                'error' => 'X_PMF_Token not valid.'
            ];
            break;
        }

        $registration = new RegistrationHelper($faqConfig);

        $userName = Filter::filterInput(INPUT_POST, 'username', FILTER_UNSAFE_RAW);
        $fullName = Filter::filterInput(INPUT_POST, 'fullname', FILTER_UNSAFE_RAW);
        $email = Filter::filterInput(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
        $isVisible = Filter::filterInput(INPUT_POST, 'is-visible', FILTER_UNSAFE_RAW);
        $isVisible = $isVisible === 'true';

        if (!$registration->isDomainWhitelisted($email)) {
            $http->setStatus(400);
            $result = [
                'registered' => false,
                'error' => 'The domain is not whitelisted.'
            ];
            break;
        }

        if (!is_null($userName) && !is_null($fullName) && !is_null($email)) {
            $result = $registration->createUser($userName, $fullName, $email, $isVisible);
            $http->setStatus(200);
        } else {
            $http->setStatus(400);
            $result = [
                'registered' => false,
                'error' => $PMF_LANG['err_sendMail']
            ];
        }

        break;
}

//
// Check if FAQ should be secured
//
if (!$auth && $faqConfig->get('security.enableLoginOnly')) {
    $http->setStatus(403);
    $http->sendJsonWithHeaders([ 'error' => 'You are not allowed to view this content.' ]);
    exit();
}

$http->sendJsonWithHeaders($result);