. /** * @package plagiarism_pchkorg * @category plagiarism * @copyright PlagiarismCheck.org, https://plagiarismcheck.org/ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Class provider HTTP-API methods. */ class plagiarism_pchkorg_api_provider { /** * Auth token. * * @var string */ private $token; /** * Url of api. * * @var string */ private $endpoint; /** * Last api error. * * @var string|null */ private $lasterror; /** * Fetch last api error. * * @return mixed */ public function get_last_error() { return $this->lasterror; } /** * Setup last api error. * * @param mixed $lasterror */ public function set_last_error($lasterror) { $this->lasterror = $lasterror; } /** * Constructor for api provider. * * @param $token * @param string $endpoint */ public function __construct($token, $endpoint = 'https://plagiarismcheck.org') { $this->token = $token; $this->endpoint = $endpoint; } /** * Send text for originality check. * * @param $authorhash * @param $cousereid * @param $assignmentid * @param $submissionid * @param $attachmentid * @param $content * @param $mime * @param $filename * * @return |null */ public function send_group_text($authorhash, $cousereid, $assignmentid, $submissionid, $attachmentid, $content, $mime, $filename) { $boundary = sprintf('PLAGCHECKBOUNDARY-%s', uniqid(time())); $curl = new curl(); $response = $curl->post( $this->endpoint . '/lms/moodle/check-text/', $this->get_body_for_group( $boundary, $authorhash, $cousereid, $assignmentid, $submissionid, $attachmentid, $content, $mime, $filename), array( 'CURLOPT_RETURNTRANSFER' => true, 'CURLOPT_FOLLOWLOCATION' => true, 'CURLOPT_SSL_VERIFYHOST' => false, 'CURLOPT_SSL_VERIFYPEER' => false, 'CURLOPT_HTTPHEADER' => array( 'X-API-TOKEN: ' . $this->generate_api_token(), 'Content-Type: multipart/form-data; boundary=' . $boundary ), ) ); $id = null; if ($json = json_decode($response)) { if (isset($json->message)) { $this->set_last_error($json->message); return null; } if (isset($json->success) && $json->success) { $id = $json->data->text->id; } } return $id; } /** * Build HTTP body of request. * * @param $boundary * @param $authorhash * @param $cousereid * @param $assignmentid * @param $submissionid * @param $attachmentid * @param $content * @param $mime * @param $filename * @return string */ private function get_body_for_group($boundary, $authorhash, $cousereid, $assignmentid, $submissionid, $attachmentid, $content, $mime, $filename) { $eol = "\r\n"; $body = ''; $body .= $this->get_part('token', $this->token, $boundary); $body .= $this->get_part('hash', $authorhash, $boundary); $body .= $this->get_part('course_id', $cousereid, $boundary); $body .= $this->get_part('assignment_id', $assignmentid, $boundary); $body .= $this->get_part('submission_id', $submissionid, $boundary); $body .= $this->get_part('attachment_id', $attachmentid, $boundary); $body .= $this->get_part('filename', $filename, $boundary); $body .= $this->get_part('language', 'en', $boundary); $body .= $this->get_part('skip_english_words_validation', '1', $boundary); $body .= $this->get_part('skip_percentage_words_validation', '1', $boundary); $body .= $this->get_file_part('content', $content, $mime, $filename, $boundary); $body .= '--' . $boundary . '--' . $eol; return $body; } /** * Send text to the service for check. * * @param $content * @param $mime * @param $filename * @return |null */ public function send_text($content, $mime, $filename) { $boundary = sprintf('PLAGCHECKBOUNDARY-%s', uniqid(time())); $curl = new curl(); $response = $curl->post( $this->endpoint . '/api/v1/text', $this->get_body($boundary, $content, $mime, $filename), array( 'CURLOPT_RETURNTRANSFER' => true, 'CURLOPT_FOLLOWLOCATION' => true, 'CURLOPT_SSL_VERIFYHOST' => false, 'CURLOPT_SSL_VERIFYPEER' => false, 'CURLOPT_POST' => true, 'CURLOPT_HTTPHEADER' => array( 'X-API-TOKEN: ' . $this->generate_api_token(), 'Content-Type: multipart/form-data; boundary=' . $boundary ), ) ); $id = null; if ($json = json_decode($response)) { if (isset($json->message)) { $this->set_last_error($json->message); return null; } if (isset($json->success) && $json->success) { $id = $json->data->text->id; } } return $id; } /** * * Method send information to service thar agreement had been accepted. * Method will be called only for personal account type. * */ public function save_accepted_agreement() { $curl = new curl(); $curl->post( $this->endpoint . '/api/v1/agreement/create/moodle-plugin/2019-04-11/', '', array( 'CURLOPT_RETURNTRANSFER' => true, 'CURLOPT_FOLLOWLOCATION' => true, 'CURLOPT_SSL_VERIFYHOST' => false, 'CURLOPT_SSL_VERIFYPEER' => false, 'CURLOPT_POST' => true, 'CURLOPT_HTTPHEADER' => array( 'X-API-TOKEN: ' . $this->generate_api_token(), ), ) ); } /** * Build part of HTTP body. * * @param $name * @param $value * @param $boundary * @return string */ private function get_part($name, $value, $boundary) { $eol = "\r\n"; $part = '--' . $boundary . $eol; $part .= 'Content-Disposition: form-data; name="' . $name . '"' . $eol . $eol; $part .= $value . $eol; return $part; } /** * Build part of HTTP body. This part contains file. * * @param $name * @param $value * @param $mime * @param $filename * @param $boundary * @return string */ private function get_file_part($name, $value, $mime, $filename, $boundary) { $eol = "\r\n"; $part = '--' . $boundary . $eol; $part .= 'Content-Disposition: form-data; name="' . $name . '"; filename="' . $filename . '";' . $eol; $part .= 'Content-Type: ' . $mime . $eol; $part .= 'Content-Length: ' . strlen($value) . $eol . $eol; $part .= $value . $eol; return $part; } /** * Build body for http-request. * * @param $boundary * @param $content * @param $mime * @param $filename * @return string */ private function get_body($boundary, $content, $mime, $filename) { $eol = "\r\n"; $body = ''; $body .= $this->get_part('language', 'en', $boundary); $body .= $this->get_part('skip_english_words_validation', '1', $boundary); $body .= $this->get_part('skip_percentage_words_validation', '1', $boundary); $body .= $this->get_file_part('text', $content, $mime, $filename, $boundary); $body .= '--' . $boundary . '--' . $eol; return $body; } /** * Convert user email to sha256 salted hash. * * @param $email * @return string */ public function user_email_to_hash($email) { // We don't send raw user email to the service. return hash('sha256', $this->token . $email); } /** * Check type of service account. * There are two types of accounts: personal and group. * * @return bool */ public function is_group_token() { return 'G-' === \substr($this->token, 0, 2); } /** * Check that user belongs to group when it is group account. * * @param string $email * @return bool */ public function is_group_member($email = '') { if (!$this->is_group_token()) { return true; } static $resultmap = array(); if (!array_key_exists($email, $resultmap)) { $resultmap[$email] = false; $curl = new curl(); $response = $curl->post($this->endpoint . '/lms/moodle/is-group-member/', array( 'token' => $this->token, 'hash' => $this->user_email_to_hash($email) ), array( 'CURLOPT_RETURNTRANSFER' => true, 'CURLOPT_FOLLOWLOCATION' => true, 'CURLOPT_SSL_VERIFYHOST' => false, 'CURLOPT_SSL_VERIFYPEER' => false, // The maximum number of seconds to allow cURL functions to execute. 'CURLOPT_TIMEOUT' => 2 )); if ($json = json_decode($response)) { if (true == $json->is_member) { $resultmap[$email] = true; } } } return $resultmap[$email]; } /** * Check status of document. * If document has been checked, state is 5. * * @param $textid * @return object|null */ public function check_text($textid) { $curl = new curl(); $response = $curl->get($this->endpoint . '/api/v1/text/' . $textid, array(), array( 'CURLOPT_RETURNTRANSFER' => true, 'CURLOPT_FOLLOWLOCATION' => true, 'CURLOPT_SSL_VERIFYHOST' => false, 'CURLOPT_SSL_VERIFYPEER' => false, 'CURLOPT_POST' => false, 'CURLOPT_HTTPHEADER' => array( 'X-API-TOKEN: ' . $this->generate_api_token(), 'Content-Type: application/x-www-form-urlencoded' ), )); if ($json = json_decode($response)) { if (isset($json->data) && 5 == $json->data->state) { return $json->data->report; } } return null; } /** * Build url for the api. * * @param $id * @return string */ public function get_report_action($id) { return "{$this->endpoint}/lms/public-report/{$id}/"; } /** * Generate token for API auth. * * @return string */ public function generate_api_token() { global $USER; if ($this->is_group_token()) { return $this->token . '::' . hash('sha256', $this->token . $USER->email); } return $this->token; } /** * List of supported mime. * * @param $mime * @return bool */ public function is_supported_mime($mime) { return in_array($mime, array( 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/rtf', 'application/vnd.oasis.opendocument.text', 'text/plain', 'plain/text', 'application/pdf', ), true); } /** * Return maximum size of document. * * @return int */ public function get_max_filesize() { return 20 * 1048576; } }