shr3dd3r
62b7b68976
Мне это всё расписывать что-ли? Смотрите в содержание коммита, мне феерически индифферентно
347 lines
10 KiB
PHP
347 lines
10 KiB
PHP
<?php
|
|
// Create new post
|
|
|
|
|
|
|
|
// Includes
|
|
if (isset($IS_FRONTEND) && $IS_FRONTEND) {
|
|
require_once("api/_auth.php");
|
|
require_once("api/_utils.php");
|
|
require_once("api/_errorslist.php");
|
|
require_once("api/_types.php");
|
|
require_once("api/user/index.php");
|
|
require_once("api/tags/index.php");
|
|
} else {
|
|
require_once("../_auth.php");
|
|
require_once("../_utils.php");
|
|
require_once("../_errorslist.php");
|
|
require_once("../_types.php");
|
|
require_once("../user/index.php");
|
|
require_once("../tags/index.php");
|
|
}
|
|
|
|
|
|
|
|
// Functions
|
|
|
|
/*
|
|
* FUNCTION
|
|
* Parse tags from raw string
|
|
*/
|
|
function Post_ParseRawTagString (string $str): ReturnT {
|
|
global $Config;
|
|
|
|
$allowedSymbols = $Config["posting"]["tags"]["allowed_syms"];
|
|
$maxTagLength = $Config["posting"]["tags"]["max_single_length"];
|
|
$strLength = strlen($str);
|
|
$currLen = 0;
|
|
$currTag = "";
|
|
$result = array();
|
|
|
|
for ($i = 0; $i <= $strLength; ++$i) {
|
|
if ($i === $strLength || $str[$i] === ",") {
|
|
if ($currLen > 0) {
|
|
$result[] = $currTag;
|
|
$currLen = 0;
|
|
$currTag = "";
|
|
} else {
|
|
return new ReturnT(err_code: E_UIN_BADARGS, err_desc: "syntax error at index $i while trying to parse tags");
|
|
}
|
|
} elseif (!IntlChar::isspace($str[$i])) {
|
|
$currTag .= $str[$i];
|
|
if (++$currLen > $maxTagLength)
|
|
return new ReturnT(err_code: E_UIN_BADARGS, err_desc: "tag too large: $currTag");
|
|
}
|
|
}
|
|
|
|
$preg_str = "/[^" . $allowedSymbols . "]/";
|
|
|
|
foreach ($result as $tag) {
|
|
if (preg_match($preg_str, $tag))
|
|
return new ReturnT(err_code: E_UIN_BADARGS, err_desc: "only allowed symbols in tags are: " . $allowedSymbols);
|
|
elseif (!$tag)
|
|
unset($tag);
|
|
}
|
|
|
|
return new ReturnT(data: $result);
|
|
}
|
|
|
|
/*
|
|
* FUNCTION
|
|
* Check if image size properties are valid
|
|
*/
|
|
function Post_ImgResIsValid (int $x, int $y): bool {
|
|
global $Config;
|
|
|
|
return ($x <= $Config["media"]["max_pic_res"]["x"])
|
|
&& ($y <= $Config["media"]["max_pic_res"]["y"])
|
|
&& (Utils_GetRatio($x, $y) <= $Config["media"]["max_pic_res"]["ratio"]);
|
|
}
|
|
|
|
/*
|
|
* FUNCTION
|
|
* Create preview version of image
|
|
*/
|
|
function Post_CreatePreviewFromImage (string $src, string $dst): ReturnT {
|
|
$img = null;
|
|
|
|
// Reading image from source path
|
|
switch (mime_content_type($src)) {
|
|
case "image/jpeg":
|
|
$img = imagecreatefromjpeg($src);
|
|
break;
|
|
case "image/png":
|
|
$img = imagecreatefrompng($src);
|
|
break;
|
|
default:
|
|
return new ReturnT(err_code: E_UIN_FILETYPE, err_desc: "invalid mime type");
|
|
}
|
|
|
|
// Saving it as LQ JPEG
|
|
$saveResult = imagejpeg($img, $dst, 30);
|
|
|
|
if (!$saveResult) {
|
|
if (file_exists($dst)) // $src isnt our responsibility, $dst is
|
|
unlink($dst);
|
|
return new ReturnT(err_code: E_UNS_UNEXPECTED, err_desc: "failed to create preview");
|
|
}
|
|
|
|
// Check if preview is bigger or same size as original...
|
|
if (filesize($src) < filesize($dst))
|
|
unlink($dst); // ...then we can just delete preview. Frontend will use preview only when it exist
|
|
|
|
return new ReturnT(data: true);
|
|
}
|
|
|
|
/*
|
|
* FUNCTION
|
|
* Stores image and returns paths to picture and its preview
|
|
*/
|
|
function Post_StoreImage (string $path): ReturnT {
|
|
global $Config, $IS_FRONTEND;
|
|
|
|
// Original extension
|
|
$ext = "";
|
|
if (mime_content_type($path) === "image/jpeg")
|
|
$ext = "jpg";
|
|
elseif (mime_content_type($path) === "image/png")
|
|
$ext = "png";
|
|
else
|
|
return new ReturnT(err_code: E_UNS_UNEXPECTED, err_desc: "failed to determine correctly mime type of image");
|
|
|
|
// Paths
|
|
$fileName = strval(time()) . "_" . Utils_GenerateRandomString(4);
|
|
// Defining base dir
|
|
$baseDir = "";
|
|
if (isset($IS_FRONTEND) && $IS_FRONTEND)
|
|
$baseDir = "./";
|
|
else
|
|
$baseDir = "../../";
|
|
$targetDir = $baseDir . $Config["media"]["pics_path"];
|
|
$targetPath = Utils_JoinPaths($targetDir, $fileName . "." . $ext);
|
|
|
|
// Moving original picture
|
|
$moveResult = move_uploaded_file($path, $targetPath);
|
|
if (!$moveResult)
|
|
return new ReturnT(err_code: E_UNS_UNEXPECTED, err_desc: "failed to move uploaded file");
|
|
|
|
// Creating preview file
|
|
if ($Config["media"]["previews_enabled"]) {
|
|
$previewDir = $baseDir . $Config["media"]["prevs_path"];
|
|
$previewPath = Utils_JoinPaths($previewDir, $fileName . ".jpg");
|
|
$res = Post_CreatePreviewFromImage($targetPath, $previewPath);
|
|
if ($res->IsError()) // $path and $targetPath arent our responsibility, neither is $previewPath
|
|
return $res;
|
|
if (!file_exists($previewPath)) // If no preview was created - then just nullify path
|
|
$previewPath = null;
|
|
}
|
|
|
|
return new ReturnT(data: array(
|
|
"preview" => $previewPath,
|
|
"picture" => $targetPath
|
|
));
|
|
}
|
|
|
|
/*
|
|
* FUNCTION
|
|
* Create single publication
|
|
*/
|
|
function Post_Create (
|
|
int $author_id,
|
|
string $tags,
|
|
string $pic_path,
|
|
?string $title = null,
|
|
?string $prev_path = null,
|
|
bool $comms_enabled = false,
|
|
bool $edit_lock = false
|
|
): ReturnT {
|
|
global $db;
|
|
|
|
$result = null;
|
|
|
|
// Performing SQL query
|
|
$s = $db->prepare("INSERT INTO posts (author_id,tags,title,pic_path,preview_path,comments_enabled,edit_lock) VALUES (?,?,?,?,?,?,?)");
|
|
$s->bind_param("issssii", $author_id, $tags, $title, $pic_path, $prev_path, $comms_enabled, $edit_lock);
|
|
|
|
if ($s->execute() === false)
|
|
return new ReturnT(err_code: E_DBE_INSERTFAIL, err_desc: "failed to create post record in DB");
|
|
|
|
$result = true;
|
|
|
|
return new ReturnT(data: $result);
|
|
}
|
|
|
|
|
|
|
|
// Methods
|
|
|
|
/*
|
|
* METHOD
|
|
* Create single publication
|
|
* Request fields:
|
|
* tags - list of tags, should be delimited by comma
|
|
* title - optional title for post
|
|
* Files fields:
|
|
* pic - id of file object in $_FILES variable
|
|
*/
|
|
function Post_Create_Method (array $req, array $files): ReturnT {
|
|
global $Config, $LOGGED_IN, $THIS_USER;
|
|
|
|
$tags = null;
|
|
$pic_path = null;
|
|
$title = null;
|
|
$prev_path = null;
|
|
$comms_enabled = false; // TODO: support for this option at post creation
|
|
|
|
// Input sanity checks
|
|
|
|
// Check if user is authenticated
|
|
if (!$LOGGED_IN)
|
|
return new ReturnT(err_code: E_AUT_NOTAUTHED, err_desc: "you must be logged in to create posts");
|
|
|
|
// Check if there are necessary input
|
|
if (!isset($req["tags"]) || !isset($files["pic"]) || is_array($files["pic"]["error"]) || $files["pic"]["error"] === UPLOAD_ERR_NO_FILE)
|
|
return new ReturnT(err_code: E_UIN_INSUFARGS, err_desc: "tags and picture are necessary");
|
|
|
|
// Check tags
|
|
// If raw string length not fits into limit
|
|
$tagsLen = strlen($req["tags"]);
|
|
$tagsMaxLen = $Config["posting"]["tags"]["max_raw_input_str_length"];
|
|
if ($tagsLen > $tagsMaxLen)
|
|
return new ReturnT(err_code: E_UIN_BADARGS, err_desc: "tags string length exceeds limit: " . $tagsMaxLen);
|
|
elseif ($tagsLen < 1)
|
|
return new ReturnT(err_code: E_UIN_BADARGS, err_desc: "tags cant be empty");
|
|
// Check if supplied string is ASCII
|
|
if (!Utils_IsAscii($req["tags"]))
|
|
return new ReturnT(err_code: E_UIN_BADARGS, err_desc: "tags must be ASCII-only");
|
|
// Parsing tags
|
|
$parsedTags = Post_ParseRawTagString($req["tags"]);
|
|
if ($parsedTags->IsError())
|
|
return $parsedTags;
|
|
$parsedTags = $parsedTags->GetData();
|
|
// Check if tags are approved
|
|
foreach ($parsedTags as $singleTag) {
|
|
if (!Tags_IsTagApproved($singleTag))
|
|
return new ReturnT(err_code: E_UIN_BADARGS, err_desc: "tag \"$singleTag\" is not approved");
|
|
}
|
|
// Concatenating parsed tags to single comma-separated string
|
|
$tags = implode(",", $parsedTags);
|
|
|
|
// Check user role TODO: add rate-limiting, instead of this
|
|
if (User_HasRole($THIS_USER, "newbie")->GetData())
|
|
return new ReturnT(err_code: E_ACS_INSUFROLE, err_desc: "newbies cant create posts");
|
|
|
|
// Checking title
|
|
if (isset($req["title"])) {
|
|
// Title length
|
|
$maxTitleLen = $Config["posting"]["title"]["max_length"];
|
|
$realTitleLen = strlen($req["title"]);
|
|
if ($realTitleLen > $maxTitleLen)
|
|
return new ReturnT(err_code: E_UIN_BADARGS, err_desc: "title length exceeds maximum value");
|
|
// Cleaning off all bad symbols (no script injection allowed here) TODO: move to function
|
|
for ($i = 0; $i < $realTitleLen; ++$i) {
|
|
switch ($req["title"][$i]) {
|
|
case "<":
|
|
$title .= "<";
|
|
break;
|
|
case ">":
|
|
$title .= ">";
|
|
break;
|
|
case "/":
|
|
$title .= "/";
|
|
break;
|
|
case "\\":
|
|
$title .= "\";
|
|
break;
|
|
case "?":
|
|
$title .= "?";
|
|
break;
|
|
case "&":
|
|
$title .= "&";
|
|
break;
|
|
case "\n":
|
|
$title .= "<br>";
|
|
break;
|
|
case "\t":
|
|
$title .= " ";
|
|
break;
|
|
default:
|
|
$title .= $req["title"][$i];
|
|
}
|
|
}
|
|
if (strlen($title) > $maxTitleLen)
|
|
return new ReturnT(err_code: E_UIN_BADARGS, err_desc: "title length exceeds maximum value after escaping");
|
|
}
|
|
|
|
// Check image properties
|
|
// If error happened while uploading
|
|
if ($files["pic"]["error"] !== UPLOAD_ERR_OK)
|
|
return new ReturnT(err_code: E_UIN_FAIL2UPLD, err_desc: "error while uploading file: " . strval($files["pic"]["error"]));
|
|
// If size is too large
|
|
if ($files["pic"]["size"] > $Config["media"]["max_pic_size"])
|
|
return new ReturnT(err_code: E_UIN_FILE2LARGE, err_desc: "picture size is too large");
|
|
$TmpFilePath = $files["pic"]["tmp_name"];
|
|
// If file mime type is not in list of allowed
|
|
if (!in_array(mime_content_type($TmpFilePath), $Config["media"]["allowed_mimetypes"]))
|
|
return new ReturnT(err_code: E_UIN_FILETYPE, err_desc: "picture mime type is invalid");
|
|
// Check if resolution is bigger than allowed or have unacceptable aspect ratio
|
|
list($SzX, $SzY, $Type, $Attr) = getimagesize($TmpFilePath);
|
|
if (!Post_ImgResIsValid($SzX, $SzY))
|
|
return new ReturnT(err_code: E_UIN_IMGBADRES, err_desc: "image with that resolution or aspect ratio cant be accepted");
|
|
|
|
// Actions
|
|
|
|
// Copy picture to storage folder
|
|
$res = Post_StoreImage($TmpFilePath);
|
|
if ($res->IsError()) { // $TmpFilePath seemingly isnt our responsibility, BUT, only we know how and what to cleanup
|
|
unlink($TmpFilePath);
|
|
return $res;
|
|
}
|
|
$res = $res->GetData();
|
|
$pic_path = $res["picture"];
|
|
$prev_path = $res["preview"];
|
|
|
|
$res = Post_Create($THIS_USER, $tags, $pic_path, $title, $prev_path, $comms_enabled, false);
|
|
if ($res->IsError()) { // Cleaning up all processed pics
|
|
unlink($pic_path);
|
|
if ($prev_path)
|
|
unlink($prev_path);
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
|
|
|
|
if (Utils_ThisFileIsRequested(__FILE__)) {
|
|
require_once("../_json.php");
|
|
|
|
$result = Post_Create_Method($_POST, $_FILES);
|
|
|
|
// Checking result
|
|
if ($result->IsError())
|
|
$result->ThrowJSONError();
|
|
else
|
|
JSON_ReturnData(["success" => $result->GetData()]);
|
|
}
|
|
|
|
?>
|