auth = $auth; $this->config = $config; $this->db = $db; $this->helper = $helper; $this->language = $language; $this->request = $request; $this->template = $template; $this->user = $user; $this->phpbb_container = $phpbb_container; $this->php_ext = $php_ext; $this->root_path = $root_path; $this->users_table = $users_table; $this->jobs_table = $jobs_table; } /** * @return \Symfony\Component\HttpFoundation\Response A Symfony Response object */ public function execute() { $this->start = date('Y-m-d H:i:s'); // https://symfony.com/doc/current/components/http_foundation.html#streaming-a-response $streamedResponse = new StreamedResponse(); $streamedResponse->headers->set('X-Accel-Buffering', 'no'); $streamedResponse->setCallback(function () { echo 'Processing'; flush(); }); $streamedResponse->send(); $this->job_id = utf8_clean_string($this->request->variable('job_id', '', true)); if (empty($this->job_id)) { return new JsonResponse('job_id not provided'); } $this->load_job(); if (empty($this->job)) { return new JsonResponse('job_id not found in the database'); } if (!empty($this->job['status'])) { return new JsonResponse('job_id already completed with ' . $this->job['status']); } $this->log = array('start' => $this->start); try { $this->cfg = json_decode($this->job['config']); } catch (\Exception $e) { $this->cfg = null; $this->log['exception'] = $e->getMessage(); } if (empty($this->cfg)) { $this->job['status'] = 'fail'; $this->log['error'] = 'config not provided'; $set = [ 'status' => $this->job['status'], 'log' => json_encode($this->log) ]; $this->job_update($set); $this->post_update($this->job); return new JsonResponse($this->log); } return $this->process(); } protected function load_job() { $where = [ 'job_id' => $this->job_id ]; $sql = 'SELECT j.job_id, j.ailabs_user_id, j.status, j.attempts, j.post_mode, j.post_id, j.forum_id, j.poster_id, j.poster_name, j.request, j.response, j.log, j.ref, j.response_message_id, c.config, c.template, u.username as ailabs_username, p.topic_id, p.post_subject, p.post_text, p.post_time, f.forum_name ' . 'FROM ' . $this->jobs_table . ' j ' . 'JOIN ' . $this->users_table . ' c ON c.user_id = j.ailabs_user_id ' . 'JOIN ' . USERS_TABLE . ' u ON u.user_id = j.ailabs_user_id ' . 'JOIN ' . POSTS_TABLE . ' p ON p.post_id = j.post_id ' . 'JOIN ' . FORUMS_TABLE . ' f ON f.forum_id = j.forum_id ' . 'WHERE ' . $this->db->sql_build_array('SELECT', $where); $result = $this->db->sql_query($sql); $this->job = $this->db->sql_fetchrow($result); $this->db->sql_freeresult($result); if (!empty($this->job) && !empty($this->job['request'])) $this->job['request'] = utf8_decode_ncr($this->job['request']); } protected function process() { return new JsonResponse($this->log); } /** * Parse message template * @param string $template */ protected function replace_vars($job, resultParse $resultParse) { $images = null; $attachments = null; if (!empty($resultParse->images)) { $images = []; $attachments = []; $ind = 0; foreach ($resultParse->images as $item) { array_push($images, '[img]' . $item . '[/img]' . PHP_EOL . PHP_EOL); array_push($attachments, '[attachment=' . $ind . '][/attachment]' . PHP_EOL); $ind = $ind + 1; } $images = implode("", $images); $attachments = implode("", $attachments); } $tokens = array( '{post_id}' => $job['post_id'], '{request}' => $job['request'], '{info}' => $resultParse->info, '{response}' => $resultParse->message, '{images}' => $images, '{attachments}' => $attachments, '{poster_id}' => $job['poster_id'], '{poster_name}' => $job['poster_name'], '{ailabs_username}' => $job['ailabs_username'], ); return str_ireplace(array_keys($tokens), array_values($tokens), $job['template']); } protected function job_update($set) { $where = ['job_id' => $this->job_id]; $sql = 'UPDATE ' . $this->jobs_table . ' SET ' . $this->db->sql_build_array('UPDATE', $set) . ' WHERE ' . $this->db->sql_build_array('SELECT', $where); $result = $this->db->sql_query($sql); $this->db->sql_freeresult($result); } protected function log_flush() { $set = ['log' => json_encode($this->log)]; $this->job_update($set); } protected function post_update($job) { /* [ job_id: , ailabs_user_id: , ailabs_username: , response_time: , status: , response_post_id: ] */ $data = array( 'job_id' => $job['job_id'], 'ailabs_user_id' => $job['ailabs_user_id'], 'ailabs_username' => $job['ailabs_username'], 'status' => $job['status'], 'response_time' => empty($job['response_time']) ? time() : $job['response_time'], ); if (!empty($job['response_post_id'])) { $data['response_post_id'] = $job['response_post_id']; } $where = [ 'post_id' => $job['post_id'] ]; $set = '\'' . json_encode($data) . ',\''; $concat = $this->db->sql_concatenate('post_ailabs_data', $set); $sql = 'UPDATE ' . POSTS_TABLE . ' SET post_ailabs_data = ' . $concat . ' WHERE ' . $this->db->sql_build_array('SELECT', $where); $result = $this->db->sql_query($sql); $this->db->sql_freeresult($result); } protected function post_response($job, $response) { // Prep posting $poll = $uid = $bitfield = $options = ''; generate_text_for_storage($response, $uid, $bitfield, $options, true, true, true); // For some reason uid is not calculated by generate_text_for_storage if (empty($uid)) { $message_parser = new \parse_message($response); $message_parser->parse(true, true, true); $uid = $message_parser->bbcode_uid; } $data = array( 'poster_id' => $job['ailabs_user_id'], // General Posting Settings 'forum_id' => $job['forum_id'], // The forum ID in which the post will be placed. (int) 'topic_id' => $job['topic_id'], // Post a new topic or in an existing one? Set to 0 to create a new one, if not, specify your topic ID here instead. 'icon_id' => false, // The Icon ID in which the post will be displayed with on the viewforum, set to false for icon_id. (int) // Defining Post Options 'enable_bbcode' => true, // Enable BBcode in this post. (bool) 'enable_smilies' => true, // Enabe smilies in this post. (bool) 'enable_urls' => true, // Enable self-parsing URL links in this post. (bool) 'enable_sig' => true, // Enable the signature of the poster to be displayed in the post. (bool) // Message Body 'message' => $response, // Your text you wish to have submitted. It should pass through generate_text_for_storage() before this. (string) 'message_md5' => md5($response), // The md5 hash of your message 'post_checksum' => md5($response), // The md5 hash of your message // Values from generate_text_for_storage() 'bbcode_bitfield' => $bitfield, // Value created from the generate_text_for_storage() function. 'bbcode_uid' => $uid, // Value created from the generate_text_for_storage() function. // Other Options 'post_edit_locked' => 0, // Disallow post editing? 1 = Yes, 0 = No 'topic_title' => $job['post_subject'], 'notify_set' => true, // (bool) 'notify' => true, // (bool) 'post_time' => 0, // Set a specific time, use 0 to let submit_post() take care of getting the proper time (int) 'forum_name' => $job['forum_name'], // For identifying the name of the forum in a notification email. (string) // Indexing 'enable_indexing' => true, // Allow indexing the post? (bool) // 3.0.6 ); // Post as designated user and then switch back to original one $actual_user_id = $this->user->data['user_id']; $this->switch_user($job['ailabs_user_id']); $post_subject = ((strpos($job['post_subject'], 'Re: ') !== 0) ? 'Re: ' : '') . censor_text($job['post_subject']); include($this->root_path . 'includes/functions_posting.' . $this->php_ext); submit_post('reply', $post_subject, $job['ailabs_username'], POST_NORMAL, $poll, $data); $this->switch_user($actual_user_id); return $data; } /** * Switch to the AI Labs user * @param int $new_user_id * @return bool */ protected function switch_user($new_user_id) { if ($this->user->data['user_id'] == $new_user_id) { // Nothing to do return true; } $sql = 'SELECT * FROM ' . USERS_TABLE . ' WHERE user_id = ' . (int) $new_user_id; $result = $this->db->sql_query($sql); $row = $this->db->sql_fetchrow($result); $this->db->sql_freeresult($result); $row['is_registered'] = true; $this->user->data = array_merge($this->user->data, $row); $this->auth->acl($this->user->data); return true; } /* Inspired by https://www.phpbb.com/community/viewtopic.php?t=2556226 */ protected function attach_to_post($isUrl, $urlOrFilename, $postId, $topicId = 0, $forumId = 0, $userId = 0, $realFileName = '', $comment = '', $dispatchEvent = true) { if ( getType($isUrl) != 'boolean' || getType($urlOrFilename) != 'string' || getType($postId) != 'integer' || getType($comment) != 'string' || getType($dispatchEvent) != 'boolean' || getType($topicId) != 'integer' || getType($forumId) != 'integer' || getType($userId) != 'integer' ) throw 'Type Missmatch'; if ($postId <= 0) throw 'Post ID cannot be zero!'; // if not given, get missing IDs if ($topicId == 0 || $forumId == 0 || $userId == 0) { $idRow = $this->db->sql_fetchrow($this->db->sql_query('SELECT post_id, topic_id, forum_id, poster_id FROM ' . POSTS_TABLE . ' WHERE post_id = ' . $postId)); if (!$idRow) return ['POST_NOT_EXISTS']; $topicId = intval($idRow['topic_id']); $forumId = intval($idRow['topic_id']); $userId = intval($idRow['poster_id']); } // get required classes $upload = $this->phpbb_container->get('files.upload'); $cache = $this->phpbb_container->get('cache'); $attach = $this->phpbb_container->get('attachment.upload'); // load file from remote location to local server $upload->set_disallowed_content([]); $extensions = $cache->obtain_attach_extensions($forumId); $extensionArr = array_keys($extensions['_allowed_']); $upload->set_allowed_extensions($extensionArr); $tempFile = null; if ($isUrl) { $upload->set_allowed_extensions($extensionArr); $tempFile = $upload->handle_upload('files.types.remote', $urlOrFilename); } else { // TODO: There seems to be some kind of bug where phpBB unable to get extension for local file array_push($extensionArr, ''); $upload->set_allowed_extensions($extensionArr); $local_filedata = array(); $tempFile = $upload->handle_upload('files.types.local', $urlOrFilename, $local_filedata); } if (count($tempFile->error) > 0) { $ext = '.'; if (strrpos($urlOrFilename, '.') !== false) $ext = substr($urlOrFilename, strrpos($urlOrFilename, '.') + 1); if ($tempFile->error[0] == 'URL_INVALID' && !in_array($ext, $extensionArr)) return ['FILE_EXTENSION_NOT_ALLOWED', 'EXTENSION=.' . htmlspecialchars($ext)]; return $tempFile->error; } $realFileNameExt = $isUrl ? '.' . $tempFile->get('extension') : ''; $realFileName = $realFileName == '' ? $tempFile->get('realname') : htmlspecialchars($realFileName) . $realFileNameExt; $tempFileData = [ 'realname' => $realFileName, 'size' => $tempFile->get('filesize'), 'type' => $tempFile->get('mimetype'), ]; // create attachment from temp file if (!function_exists('create_thumbnail')) require($this->root_path . 'includes/functions_posting.php'); $attachFileName = $isUrl ? $tempFile->get('filename') : $urlOrFilename; $attachmentFileData = $attach->upload('', $forumId, true, $attachFileName, false, $tempFileData); if (!$attachmentFileData['post_attach']) return ['FILE_ATTACH_ERROR', $realFileName, $attachFileName, $tempFileData]; if (count($attachmentFileData['error']) > 0) return $attachmentFileData['error']; $sql_ary = array( 'physical_filename' => $attachmentFileData['physical_filename'], 'attach_comment' => $comment, 'real_filename' => $attachmentFileData['real_filename'], 'extension' => $attachmentFileData['extension'], 'mimetype' => $attachmentFileData['mimetype'], 'filesize' => $attachmentFileData['filesize'], 'filetime' => $attachmentFileData['filetime'], 'thumbnail' => $attachmentFileData['thumbnail'], 'is_orphan' => 0, 'in_message' => 0, 'poster_id' => $userId, 'post_msg_id' => $postId, 'topic_id' => $topicId, ); if ($dispatchEvent) { $dispatcher = $this->phpbb_container->get('dispatcher'); // Same exact code as in https://github.com/phpbb/phpbb/blob/master/phpBB/includes/message_parser.php // See core.modify_attachment_sql_ary_on_submit https://area51.phpbb.com/docs/dev/3.3.x/extensions/events_list.html $vars_array = array('sql_ary'); extract($dispatcher->trigger_event('core.modify_attachment_sql_ary_on_submit', compact($vars_array))); } $this->db->sql_query('INSERT INTO ' . ATTACHMENTS_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary)); $newAttachmentID = intval($this->db->sql_nextid()); if ($newAttachmentID == 0) return ['SQL_ATTACHMENT_INSERT_ERROR']; $this->db->sql_query('UPDATE ' . POSTS_TABLE . ' SET post_attachment = 1 WHERE post_id = ' . $postId); return ['SUCCESS', $newAttachmentID, $postId]; } protected function image_filename($ind) { return 'ailabs_' . $this->job['ailabs_user_id'] . '_' . $this->job_id . '_' . $ind; } protected function save_base64_to_temp_file($base64, $ind, $ext = '.png') { $temp_dir_path = sys_get_temp_dir(); if (substr($temp_dir_path, -1) != DIRECTORY_SEPARATOR) $temp_dir_path .= DIRECTORY_SEPARATOR; $filename = $temp_dir_path . $this->image_filename($ind) . $ext; $handle = fopen($filename, 'wb'); fwrite($handle, base64_decode($base64)); fclose($handle); return $filename; } protected function trim_words($inputString, $numWords) { $words = explode(' ', $inputString); if (count($words) <= $numWords) { return $inputString; } $trimmedWords = array_slice($words, 0, $numWords); return implode(' ', $trimmedWords) . '...'; //'…'; } }