277 lines
9.4 KiB
PHP
277 lines
9.4 KiB
PHP
<?php
|
|
namespace YTKNS;
|
|
|
|
use Exception;
|
|
|
|
class UploadNotFoundException extends Exception {};
|
|
class UploadCreationFailedException extends Exception {};
|
|
|
|
final class Upload {
|
|
private const ID_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
|
|
public function getId(): string {
|
|
return $this->upload_id;
|
|
}
|
|
|
|
public function getPath(): string {
|
|
return YTKNS_UPLOADS . '/' . $this->getId();
|
|
}
|
|
public function getUrl(): string {
|
|
return 'https://' . Config::get('domain.main') . '/uploads/' . $this->getId();
|
|
}
|
|
|
|
public function getUserId(): int {
|
|
return $this->user_id;
|
|
}
|
|
public function setUserId(int $userId): void {
|
|
$this->user_id = $userId;
|
|
}
|
|
|
|
public function getType(): string {
|
|
return $this->upload_type ?? 'text/plain';
|
|
}
|
|
|
|
public function getName(): string {
|
|
return $this->upload_name ?? '';
|
|
}
|
|
|
|
public function getHash(): string {
|
|
return $this->upload_hash ?? str_pad('', 64, '0');
|
|
}
|
|
|
|
public function getUser(): User {
|
|
return User::byId($this->getUserId());
|
|
}
|
|
|
|
public function getUseCount(): int {
|
|
return $this->upload_use_count ?? 0;
|
|
}
|
|
|
|
public function getCreated(): int {
|
|
return $this->upload_created;
|
|
}
|
|
|
|
public function getlastUsed(): ?int {
|
|
return $this->upload_last_used;
|
|
}
|
|
|
|
public function getDeleted(): ?int {
|
|
return $this->upload_deleted;
|
|
}
|
|
|
|
public function getDMCA(): ?int {
|
|
return $this->upload_dmca;
|
|
}
|
|
|
|
public function delete(bool $hard): void {
|
|
if($hard) {
|
|
if(is_file($this->getPath()))
|
|
unlink($this->getPath());
|
|
|
|
if($this->getDMCA() < 1) {
|
|
$delete = DB::prepare('
|
|
DELETE FROM `ytkns_uploads`
|
|
WHERE `upload_id` = :id
|
|
');
|
|
$delete->bindValue('id', $this->getId());
|
|
$delete->execute();
|
|
}
|
|
} else {
|
|
$delete = DB::prepare('
|
|
UPDATE `ytkns_uploads`
|
|
SET `upload_deleted` = NOW()
|
|
WHERE `upload_id` = :id
|
|
');
|
|
$delete->bindValue('id', $this->getId());
|
|
$delete->execute();
|
|
}
|
|
}
|
|
|
|
public function toJson(bool $asString = false) {
|
|
$uploadInfo = [
|
|
'id' => $this->getId(),
|
|
'name' => $this->getName(),
|
|
'type' => $this->getType(),
|
|
'user' => $this->getUserId(),
|
|
'uses' => $this->getUseCount(),
|
|
'hash' => $this->getHash(),
|
|
'created' => $this->getCreated(),
|
|
'last_used' => $this->getLastUsed(),
|
|
'deleted' => $this->getDeleted(),
|
|
'dmca' => $this->getDMCA(),
|
|
];
|
|
|
|
if($asString)
|
|
$uploadInfo = json_encode($uploadInfo);
|
|
|
|
return $uploadInfo;
|
|
}
|
|
|
|
public static function generateId(int $length = 16): string {
|
|
$token = random_bytes($length);
|
|
$chars = strlen(self::ID_CHARS);
|
|
|
|
for($i = 0; $i < $length; $i++)
|
|
$token[$i] = self::ID_CHARS[ord($token[$i]) % $chars];
|
|
|
|
return $token;
|
|
}
|
|
|
|
public static function create(User $user, string $fileName, string $fileType, string $fileHash): self {
|
|
$id = self::generateId();
|
|
$create = DB::prepare('
|
|
INSERT INTO `ytkns_uploads` (
|
|
`upload_id`, `user_id`, `upload_ip`, `upload_name`, `upload_type`, `upload_hash`
|
|
) VALUES (
|
|
:id, :user, INET6_ATON(:ip), :name, :type, UNHEX(:hash)
|
|
)
|
|
');
|
|
$create->bindValue('id', $id);
|
|
$create->bindValue('user', $user->getId());
|
|
$create->bindValue('ip', $_SERVER['REMOTE_ADDR']);
|
|
$create->bindValue('name', $fileName);
|
|
$create->bindValue('type', $fileType);
|
|
$create->bindValue('hash', $fileHash);
|
|
$create->execute();
|
|
|
|
try {
|
|
return self::byId($id);
|
|
} catch(UploadNotFoundException $ex) {
|
|
throw new UploadCreationFailedException;
|
|
}
|
|
}
|
|
|
|
public static function byId(string $id): self {
|
|
$getUpload = DB::prepare('
|
|
SELECT `upload_id`, `user_id`, `upload_use_count`, `upload_name`, `upload_type`,
|
|
UNIX_TIMESTAMP(`upload_created`) AS `upload_created`,
|
|
UNIX_TIMESTAMP(`upload_last_used`) AS `upload_last_used`,
|
|
UNIX_TIMESTAMP(`upload_deleted`) AS `upload_deleted`,
|
|
UNIX_TIMESTAMP(`upload_dmca`) AS `upload_dmca`,
|
|
INET6_NTOA(`upload_ip`) AS `upload_ip`,
|
|
LOWER(HEX(`upload_hash`)) AS `upload_hash`
|
|
FROM `ytkns_uploads`
|
|
WHERE `upload_id` = :id
|
|
AND `upload_deleted` IS NULL
|
|
');
|
|
$getUpload->bindValue('id', $id);
|
|
$upload = $getUpload->execute() ? $getUpload->fetchObject(self::class) : false;
|
|
|
|
if(!$upload)
|
|
throw new UploadNotFoundException;
|
|
|
|
return $upload;
|
|
}
|
|
|
|
public static function byHash(string $hash): ?self {
|
|
$getUpload = DB::prepare('
|
|
SELECT `upload_id`, `user_id`, `upload_use_count`, `upload_name`, `upload_type`,
|
|
UNIX_TIMESTAMP(`upload_created`) AS `upload_created`,
|
|
UNIX_TIMESTAMP(`upload_last_used`) AS `upload_last_used`,
|
|
UNIX_TIMESTAMP(`upload_deleted`) AS `upload_deleted`,
|
|
UNIX_TIMESTAMP(`upload_dmca`) AS `upload_dmca`,
|
|
INET6_NTOA(`upload_ip`) AS `upload_ip`,
|
|
LOWER(HEX(`upload_hash`)) AS `upload_hash`
|
|
FROM `ytkns_uploads`
|
|
WHERE `upload_hash` = UNHEX(:hash)
|
|
AND `upload_deleted` IS NULL
|
|
');
|
|
$getUpload->bindValue('hash', $hash);
|
|
$upload = $getUpload->execute() ? $getUpload->fetchObject(self::class) : false;
|
|
return $upload ? $upload : null;
|
|
}
|
|
|
|
public static function deleted(): array {
|
|
$getDeleted = DB::prepare('
|
|
SELECT `upload_id`, `user_id`, `upload_use_count`, `upload_name`, `upload_type`,
|
|
UNIX_TIMESTAMP(`upload_created`) AS `upload_created`,
|
|
UNIX_TIMESTAMP(`upload_last_used`) AS `upload_last_used`,
|
|
UNIX_TIMESTAMP(`upload_deleted`) AS `upload_deleted`,
|
|
UNIX_TIMESTAMP(`upload_dmca`) AS `upload_dmca`,
|
|
INET6_NTOA(`upload_ip`) AS `upload_ip`,
|
|
LOWER(HEX(`upload_hash`)) AS `upload_hash`
|
|
FROM `ytkns_uploads`
|
|
WHERE `upload_deleted` IS NOT NULL
|
|
OR `upload_dmca` IS NOT NULL
|
|
');
|
|
if(!$getDeleted->execute())
|
|
return [];
|
|
|
|
$deleted = [];
|
|
|
|
while($upload = $getDeleted->fetchObject(self::class))
|
|
$deleted[] = $upload;
|
|
|
|
return $deleted;
|
|
}
|
|
|
|
public static function purgeOrphans(): void {
|
|
DB::exec('
|
|
UPDATE `ytkns_uploads`
|
|
SET `upload_deleted` = NOW()
|
|
WHERE `upload_use_count` < 1
|
|
AND (`upload_created` + INTERVAL 1 DAY) < NOW()
|
|
AND `upload_dmca` IS NULL
|
|
');
|
|
|
|
$orphans = self::deleted();
|
|
|
|
foreach($orphans as $orphan)
|
|
$orphan->delete(true);
|
|
}
|
|
|
|
public static function resync(array $uploadFields): void {
|
|
if(empty($uploadFields))
|
|
return;
|
|
|
|
$effectNames = array_keys($uploadFields);
|
|
$fetchEffectsWhereIn = range(0, count($effectNames) - 1);
|
|
array_walk($fetchEffectsWhereIn, function(&$i, $k, $v) { $i = $v . $i; }, ':field_');
|
|
|
|
$fetchEffects = DB::prepare(sprintf('
|
|
SELECT `effect_name`, `effect_params`
|
|
FROM `ytkns_zones_effects`
|
|
WHERE `effect_name` IN (%s)
|
|
', implode(', ', $fetchEffectsWhereIn)));
|
|
|
|
foreach($fetchEffectsWhereIn as $index => $name)
|
|
$fetchEffects->bindValue($name, $effectNames[$index]);
|
|
|
|
$effectsWithUploads = $fetchEffects->execute() ? $fetchEffects->fetchAll(\PDO::FETCH_OBJ) : false;
|
|
$effectsWithUploads = $effectsWithUploads ? $effectsWithUploads : [];
|
|
|
|
$uploadUseCounts = [];
|
|
|
|
foreach($effectsWithUploads as $effectWithUploads) {
|
|
if(!array_key_exists($effectWithUploads->effect_name, $uploadFields))
|
|
continue;
|
|
|
|
$effectParams = json_decode($effectWithUploads->effect_params, true);
|
|
|
|
foreach($uploadFields[$effectWithUploads->effect_name] as $uploadField) {
|
|
if(empty($uploadId = $effectParams[$uploadField]))
|
|
continue;
|
|
|
|
if(!isset($uploadUseCounts[$uploadId]))
|
|
$uploadUseCounts[$uploadId] = 1;
|
|
else
|
|
$uploadUseCounts[$uploadId] += 1;
|
|
}
|
|
}
|
|
|
|
$updateUploadUseCount = DB::prepare('
|
|
UPDATE `ytkns_uploads`
|
|
SET `upload_use_count` = :count
|
|
WHERE `upload_id` = :id
|
|
');
|
|
|
|
DB::exec('UPDATE `ytkns_uploads` SET `upload_use_count` = 0');
|
|
|
|
foreach($uploadUseCounts as $uploadId => $uploadUseCount) {
|
|
$updateUploadUseCount->bindValue('id', $uploadId);
|
|
$updateUploadUseCount->bindValue('count', $uploadUseCount);
|
|
$updateUploadUseCount->execute();
|
|
}
|
|
}
|
|
}
|