Welcome, guest! Login / Register - Why register?
Psst.. new poll here.
Psst.. new forums here.

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

Your Name: Code Language: