Paste
Pasted as PHP by registered user atmaca ( 1 month ago )
<?php
define('THIS_SCRIPT', 'ft_story_upload');
require_once('./global.php');
/*
FT Story Upload
- Dosya Upload:
* JPG / PNG / GIF (max 5 MB)
* MP4 (max 15 MB)
- YouTube Bağlantısı
* Sadece youtube.com / youtu.be / youtube-nocookie.com domainleri
* DB: mediatype = 4, filepath = "yt:VIDEOID"
- Görünürlük:
1 = Forum Üyeleri
3 = Sadece Arkadaşlar
Geliştirici: atmaca
*/
// ------------------------- Helpers -------------------------
function ft_utf8ize($mixed, $fromCharset)
{
if (is_array($mixed))
{
foreach ($mixed as $k => $v)
{
$mixed[$k] = ft_utf8ize($v, $fromCharset);
}
return $mixed;
}
if (is_string($mixed))
{
if (!strcasecmp($fromCharset, 'utf-8'))
{
return $mixed;
}
if (function_exists('mb_convert_encoding'))
{
return @mb_convert_encoding($mixed, 'UTF-8', $fromCharset);
}
return utf8_encode($mixed);
}
return $mixed;
}
function ft_json_exit($arr)
{
global $stylevar;
$fromCharset = 'UTF-8';
if (!empty($stylevar['charset']))
{
$fromCharset = $stylevar['charset'];
}
$json = json_encode($arr, JSON_UNESCAPED_UNICODE);
if ($json === false)
{
$arr = ft_utf8ize($arr, $fromCharset);
$json = json_encode($arr, JSON_UNESCAPED_UNICODE);
}
if (function_exists('ob_get_length') && ob_get_length())
{
@ob_clean();
}
header('Content-Type: application/json; charset=UTF-8');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
if ($json === false)
{
echo json_encode(array(
'ok' => 0,
'error' => 'JSON üretilemedi.',
'code' => json_last_error(),
));
exit;
}
echo $json;
exit;
}
function ft_upload_error_message($code)
{
$map = array(
1 => 'Dosya çok büyük (php.ini upload_max_filesize).',
2 => 'Dosya çok büyük (form MAX_FILE_SIZE).',
3 => 'Dosya kısmen yüklendi.',
4 => 'Dosya seçilmedi.',
6 => 'Geçici klasör (tmp) bulunamadı.',
7 => 'Dosya diske yazılamadı.',
8 => 'Bir PHP eklentisi yüklemeyi durdurdu.',
);
return isset($map[$code]) ? $map[$code] : ('Yükleme hatası (kod: ' . intval($code) . ').');
}
function ft_fail($code, $ajax)
{
$msg = $code;
if (is_string($code))
{
if (strpos($code, 'upload_hata_') === 0)
{
$err = intval(substr($code, strlen('upload_hata_')));
$msg = ft_upload_error_message($err);
}
else
{
$map = array(
'bad_token' => 'Oturum doğrulaması başarısız. Sayfayı yenileyip tekrar deneyin.',
'dosya_gelmedi' => 'Dosya seçilmedi.',
'dosya_5mb_ustu' => 'Görsel boyutu 5 MB sınırını aşıyor.',
'video_15mb_ustu' => 'Video boyutu 15 MB sınırını aşıyor.',
'gecersiz_resim' => 'Geçersiz resim dosyası.',
'yalnizca_jpg_png_gif' => 'Sadece JPG / PNG / GIF kabul ediliyor.',
'yalnizca_mp4' => 'Sadece MP4 video kabul ediliyor.',
'yalnizca_youtube_link' => 'Sadece YouTube bağlantıları kabul ediliyor.',
'upload_klasoru_yazilamaz' => 'Sunucuda yükleme klasörü yazılabilir değil.',
'dosya_tasinamadi' => 'Dosya sunucuya kaydedilemedi.',
'not_logged_in' => 'Bu işlem için oturum açmalısınız.',
);
if (isset($map[$code]))
{
$msg = $map[$code];
}
}
}
if ($ajax)
{
ft_json_exit(array(
'ok' => 0,
'error' => $msg,
'code' => $code,
));
}
die($msg);
}
/* Visibility normalize helper */
function ft_normalize_visibility($value)
{
$value = intval($value);
// Sadece 1 = kayıtlı üyeler, 3 = arkadaşlar
if ($value === 3)
{
return 3;
}
return 1;
}
/* Strong random token */
function ft_token_hex($bytes = 16)
{
$bytes = max(8, intval($bytes));
if (function_exists('random_bytes'))
{
try {
return bin2hex(random_bytes($bytes));
} catch (Exception $e) {}
}
if (function_exists('openssl_random_pseudo_bytes'))
{
$b = openssl_random_pseudo_bytes($bytes);
if ($b !== false)
{
return bin2hex($b);
}
}
// Son çare
return substr(md5(uniqid('', true)), 0, $bytes * 2);
}
/* Quick MP4 signature check for octet-stream cases */
function ft_looks_like_mp4($path)
{
$fh = @fopen($path, 'rb');
if (!$fh) return false;
$head = @fread($fh, 128);
@fclose($fh);
// MP4 container’da genellikle "ftyp" bulunur
return ($head !== false && strpos($head, 'ftyp') !== false);
}
/* Parse YouTube URL -> 11-char video id (only allowed domains) */
function ft_parse_youtube_id($url)
{
$url = trim((string)$url);
if ($url === '') return false;
$p = @parse_url($url);
if (!$p || empty($p['host'])) return false;
$host = strtolower($p['host']);
$host = preg_replace('/^www\./', '', $host);
$allowed = array('youtube.com', 'm.youtube.com', 'youtu.be', 'youtube-nocookie.com');
if (!in_array($host, $allowed, true)) return false;
$path = isset($p['path']) ? trim($p['path'], '/') : '';
$id = '';
if ($host === 'youtu.be')
{
// youtu.be/VIDEOID
$parts = explode('/', $path);
$id = isset($parts[0]) ? $parts[0] : '';
}
else
{
// youtube.com/watch?v=VIDEOID
if (!empty($p['query']))
{
parse_str($p['query'], $q);
if (!empty($q['v'])) $id = (string)$q['v'];
}
// youtube.com/shorts/VIDEOID veya /embed/VIDEOID
if (!$id && preg_match('~^(shorts|embed)/([A-Za-z0-9_-]{11})~', $path, $m))
{
$id = $m[2];
}
}
if (!preg_match('~^[A-Za-z0-9_-]{11}$~', $id)) return false;
return $id;
}
// ------------------------- Input -------------------------
$vbulletin->input->clean_array_gpc('p', array(
'do' => TYPE_STR, // upload | youtube
'securitytoken' => TYPE_STR,
'ajax' => TYPE_UINT,
'visibility' => TYPE_UINT,
'youtube_url' => TYPE_STR, // do=youtube için
'text_body' => TYPE_STR, // Metin hikaye için
'bg_id' => TYPE_UINT, // Metin hikaye için
'confirm_censor' => TYPE_UINT,
));
$do = $vbulletin->GPC['do'];
$ajax = ($vbulletin->GPC['ajax'] ? true : false);
if (!$vbulletin->userinfo['userid'])
{
if ($ajax)
{
ft_fail('not_logged_in', true);
}
print_no_permission();
}
$db =& $vbulletin->db;
// Ayarlar (MVP)
$MAX_IMG_BYTES = 5 * 1024 * 1024; // 5 MB
$MAX_VID_BYTES = 15 * 1024 * 1024; // 15 MB
$EXPIRE_SECS = 24 * 60 * 60; // 24 saat
$UPLOAD_DIR = DIR . '/ft_story_uploads';
$UPLOAD_URL = 'ft_story_uploads';
// ------------------------- Token verify -------------------------
function ft_verify_token_or_fail($ajax)
{
global $vbulletin;
$token = $vbulletin->GPC['securitytoken'];
$raw = (!empty($vbulletin->userinfo['securitytoken_raw']) ? $vbulletin->userinfo['securitytoken_raw'] : '');
if (!$token || $token === 'guest')
{
ft_fail('bad_token', $ajax);
}
if (!verify_security_token($token, $raw))
{
ft_fail('bad_token', $ajax);
}
}
// ------------------------- Main actions -------------------------
if ($_SERVER['REQUEST_METHOD'] === 'POST')
{
// dosya upload
if ($do === 'upload')
{
ft_verify_token_or_fail($ajax);
if (empty($_FILES['storyfile']) || !is_uploaded_file($_FILES['storyfile']['tmp_name']))
{
ft_fail('dosya_gelmedi', $ajax);
}
$f = $_FILES['storyfile'];
if (!empty($f['error']))
{
ft_fail('upload_hata_' . intval($f['error']), $ajax);
}
$tmp = $f['tmp_name'];
// ext + type detect
$ext = strtolower(pathinfo((string)$f['name'], PATHINFO_EXTENSION));
$is_video = ($ext === 'mp4');
$mediatype = 1;
$width = 0;
$height = 0;
if ($is_video)
{
if ($f['size'] <= 0 || $f['size'] > $MAX_VID_BYTES)
{
ft_fail('video_15mb_ustu', $ajax);
}
$mime = '';
if (function_exists('finfo_open'))
{
$fi = @finfo_open(FILEINFO_MIME_TYPE);
if ($fi)
{
$mime = @finfo_file($fi, $tmp);
@finfo_close($fi);
}
}
// bazı sunucular octet-stream döndürebilir -> imza kontrolü
if ($mime && $mime !== 'video/mp4')
{
if ($mime === 'application/octet-stream' && ft_looks_like_mp4($tmp))
{
// kabul
}
else
{
ft_fail('yalnizca_mp4', $ajax);
}
}
$mediatype = 2;
$ext = 'mp4';
}
else
{
// IMAGE
if ($f['size'] <= 0 || $f['size'] > $MAX_IMG_BYTES)
{
ft_fail('dosya_5mb_ustu', $ajax);
}
$img = @getimagesize($tmp);
if (!$img || empty($img[2]))
{
ft_fail('gecersiz_resim', $ajax);
}
// Sadece JPG/PNG/GIF
$allowed = array(
IMAGETYPE_JPEG => 'jpg',
IMAGETYPE_PNG => 'png',
IMAGETYPE_GIF => 'gif',
);
$type = intval($img[2]);
if (!isset($allowed[$type]))
{
ft_fail('yalnizca_jpg_png_gif', $ajax);
}
$ext = $allowed[$type];
$width = intval($img[0]);
$height = intval($img[1]);
}
if (!is_dir($UPLOAD_DIR))
{
@mkdir($UPLOAD_DIR, 0755, true);
}
if (!is_dir($UPLOAD_DIR) || !is_writable($UPLOAD_DIR))
{
ft_fail('upload_klasoru_yazilamaz', $ajax);
}
$userid = intval($vbulletin->userinfo['userid']);
$visibility = ft_normalize_visibility($vbulletin->GPC['visibility']);
// Daha güçlü ve daha az tahmin edilebilir isim
$token = ft_token_hex(16); // 32 hex
$fname = 's_' . TIMENOW . '_' . $token . '.' . $ext;
$dest_abs = $UPLOAD_DIR . '/' . $fname;
$dest_rel = $UPLOAD_URL . '/' . $fname;
if (!move_uploaded_file($tmp, $dest_abs))
{
ft_fail('dosya_tasinamadi', $ajax);
}
@chmod($dest_abs, 0644);
$dateline = TIMENOW;
$expiretime = TIMENOW + $EXPIRE_SECS;
$db->query_write("
INSERT INTO ft_story (userid, dateline, expiretime, state, privacy, visibility)
VALUES ($userid, $dateline, $expiretime, 1, 0, $visibility)
");
$storyid = intval($db->insert_id());
$filepath_sql = $db->escape_string($dest_rel);
$db->query_write("
INSERT INTO ft_story_media (storyid, mediatype, filepath, thumbpath, filesize, width, height, dateline)
VALUES ($storyid, " . intval($mediatype) . ", '$filepath_sql', '', " . intval($f['size']) . ", " . intval($width) . ", " . intval($height) . ", $dateline)
");
// kullanıcı adı + avatar
$u = $db->query_first("
SELECT u.username, u.avatarid, u.avatarrevision, a.avatarpath
FROM user u
LEFT JOIN avatar a ON (a.avatarid = u.avatarid)
WHERE u.userid = $userid
LIMIT 1
");
$username = (!empty($u['username']) ? $u['username'] : $vbulletin->userinfo['username']);
if (!empty($u['avatarpath']))
{
$avatar = $u['avatarpath'];
}
else
{
$avatar = 'image.php?u=' . $userid . '&dateline=' . intval($u['avatarrevision']);
}
// AJAX ise JSON dön
if ($ajax)
{
ft_json_exit(array(
'ok' => 1,
'storyid' => $storyid,
'url' => $dest_rel,
'visibility' => $visibility,
'mediatype' => $mediatype,
'userid' => $userid,
'username' => $username,
'avatar' => $avatar,
'story' => array(
'storyid' => $storyid,
'userid' => $userid,
'username' => $username,
'avatar' => $avatar,
'url' => $dest_rel,
'image' => $dest_rel,
'visibility' => $visibility,
'mediatype' => $mediatype,
),
));
}
// Non-AJAX: basit çıktı
echo 'OK';
exit;
}
// YouTube link story
if ($do === 'youtube')
{
ft_verify_token_or_fail($ajax);
$ytid = ft_parse_youtube_id($vbulletin->GPC['youtube_url']);
if (!$ytid)
{
ft_fail('yalnizca_youtube_link', $ajax);
}
$userid = intval($vbulletin->userinfo['userid']);
$visibility = ft_normalize_visibility($vbulletin->GPC['visibility']);
$dateline = TIMENOW;
$expiretime = TIMENOW + $EXPIRE_SECS;
$db->query_write("
INSERT INTO ft_story (userid, dateline, expiretime, state, privacy, visibility)
VALUES ($userid, $dateline, $expiretime, 1, 0, $visibility)
");
$storyid = intval($db->insert_id());
// filepath içine "yt:ID" saklıyoruz (viewer/json tarafı bunu yorumlayacak)
$filepath_sql = $db->escape_string('yt:' . $ytid);
$db->query_write("
INSERT INTO ft_story_media (storyid, mediatype, filepath, thumbpath, filesize, width, height, dateline)
VALUES ($storyid, 4, '$filepath_sql', '', 0, 0, 0, $dateline)
");
$u = $db->query_first("
SELECT u.username, u.avatarid, u.avatarrevision, a.avatarpath
FROM user u
LEFT JOIN avatar a ON (a.avatarid = u.avatarid)
WHERE u.userid = $userid
LIMIT 1
");
$username = (!empty($u['username']) ? $u['username'] : $vbulletin->userinfo['username']);
if (!empty($u['avatarpath']))
{
$avatar = $u['avatarpath'];
}
else
{
$avatar = 'image.php?u=' . $userid . '&dateline=' . intval($u['avatarrevision']);
}
if ($ajax)
{
ft_json_exit(array(
'ok' => 1,
'storyid' => $storyid,
'visibility' => $visibility,
'mediatype' => 4,
'youtube_id' => $ytid,
'userid' => $userid,
'username' => $username,
'avatar' => $avatar,
'story' => array(
'storyid' => $storyid,
'userid' => $userid,
'username' => $username,
'avatar' => $avatar,
'visibility' => $visibility,
'mediatype' => 4,
'youtube_id' => $ytid,
),
));
}
echo 'OK';
exit;
}
// Text story
if ($do === 'text')
{
ft_verify_token_or_fail($ajax);
$db->query_write("SET NAMES utf8mb4");
// Metin + bg
$text = trim((string)$vbulletin->GPC['text_body']);
$bg = intval($vbulletin->GPC['bg_id']);
if ($bg < 1 || $bg > 12) $bg = 1;
if ($text === '')
{
ft_fail('bos_metin', $ajax);
}
// uzunluk limiti (MVP)
if (function_exists('mb_strlen')) {
if (mb_strlen($text, 'UTF-8') > 240) ft_fail('metin_cok_uzun', $ajax);
} else {
if (strlen($text) > 240) ft_fail('metin_cok_uzun', $ajax);
}
// --- Censor check ---
$confirm = intval($vbulletin->GPC['confirm_censor']);
if (function_exists('fetch_censored_text'))
{
$censored = fetch_censored_text($text);
// Sansür uygulanacaksa ilk istekte uyar, kaydetme
if ($censored !== $text && !$confirm)
{
ft_json_exit(array(
'ok' => 0,
'code' => 'censor_warning',
'error' => 'Metin yasaklı sözcük içeriyor. Devam ederseniz sansür uygulanacak.'
));
}
// Kullanıcı onayladıysa sansürlü metni kaydet
if ($censored !== $text && $confirm)
{
$text = $censored;
}
}
// emoji için bu request'te utf8mb4
$db->query_write("SET NAMES utf8mb4");
$userid = intval($vbulletin->userinfo['userid']);
$visibility = ft_normalize_visibility($vbulletin->GPC['visibility']);
$dateline = TIMENOW;
$expiretime = TIMENOW + $EXPIRE_SECS;
$db->query_write("
INSERT INTO ft_story (userid, dateline, expiretime, state, privacy, visibility)
VALUES ($userid, $dateline, $expiretime, 1, 0, $visibility)
");
$storyid = intval($db->insert_id());
// media kaydı (mediatype=3)
$db->query_write("
INSERT INTO ft_story_media (storyid, mediatype, filepath, thumbpath, filesize, width, height, dateline)
VALUES ($storyid, 3, '', '', 0, 0, 0, $dateline)
");
// text kaydı (utf8mb4 tabloda)
$text_sql = $db->escape_string($text);
$db->query_write("
INSERT INTO ft_story_text (storyid, text_body, bg_id, dateline)
VALUES ($storyid, '$text_sql', " . intval($bg) . ", $dateline)
");
// avatar/username
$u = $db->query_first("
SELECT u.username, u.avatarid, u.avatarrevision, a.avatarpath
FROM user u
LEFT JOIN avatar a ON (a.avatarid = u.avatarid)
WHERE u.userid = $userid
LIMIT 1
");
$username = (!empty($u['username']) ? $u['username'] : $vbulletin->userinfo['username']);
$avatar = (!empty($u['avatarpath']))
? $u['avatarpath']
: ('image.php?u=' . $userid . '&dateline=' . intval($u['avatarrevision']));
if ($ajax)
{
ft_json_exit(array(
'ok' => 1,
'storyid' => $storyid,
'visibility' => $visibility,
'mediatype' => 3,
'text_body' => $text,
'bg_id' => $bg,
'userid' => $userid,
'username' => $username,
'avatar' => $avatar,
'story' => array(
'storyid' => $storyid,
'userid' => $userid,
'username' => $username,
'avatar' => $avatar,
'visibility' => $visibility,
'mediatype' => 3,
'text_body' => $text,
'bg_id' => $bg,
),
));
}
echo 'OK';
exit;
}
// POST ama bilinmeyen action
ft_fail('gecersiz_islem', $ajax);
}
Revise this Paste