•Each release branch of PHP is fully supported for two years from its initial stable release https://www.php.net/supported-versions.php
PHP Backup: https://ry3yr.github.io/php.zip
Run php server locally (android): https://apkcombo.com/de/palapa-web-server/com.alfanla.android.pws/download/apk#urusai.social/@alcea/111761622206017944#i.ibb.co/cx175BB/Screenshot-20240115-165447-Opera.png
Install ComposerPackagesManually(Windows) https://m.youtube.com/watch?v=j8kpwzE6ju0
https://alcea-wisteria.de/PHP/composer-packages/
___________________________________________________________________________________________________________________
_____https://stackoverflow.com/questions/40545795/how-do-i-install-composer-php-packages-without-composer------
*php-install
html file writer
----------------------
Insert Content into File
Insert Content into File
= htmlspecialchars($message) ?>
Steam Wishlist / PriceDropCheck
-------------------------------
checkprices.php
--
[
'timeout' => 15,
'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\r\n"
]
]);
$response = @file_get_contents($url, false, $context);
$data = json_decode($response, true);
if (isset($data[$appId]['data'])) {
$gameData = $data[$appId]['data'];
if (isset($gameData['price_overview'])) {
return [
'final_formatted' => $gameData['price_overview']['final_formatted'],
'final' => $gameData['price_overview']['final'],
'discount_percent' => $gameData['price_overview']['discount_percent'],
'initial_formatted' => $gameData['price_overview']['initial_formatted'],
'initial' => $gameData['price_overview']['initial']
];
} elseif (isset($gameData['is_free']) && $gameData['is_free']) {
return [
'final_formatted' => 'Free',
'final' => 0,
'discount_percent' => 0,
'initial_formatted' => 'Free',
'initial' => 0
];
}
}
return null;
}
// Function to convert price string to numeric value
function priceToNumeric($priceString) {
if ($priceString === 'Free') return 0;
// Remove currency symbols and commas, keep only numbers and decimal separator
$cleanPrice = preg_replace('/[^\d,\.]/', '', $priceString);
// Handle European format (comma as decimal separator)
if (strpos($cleanPrice, ',') !== false && strpos($cleanPrice, '.') !== false) {
// If both comma and dot exist, comma is likely decimal separator
$cleanPrice = str_replace('.', '', $cleanPrice); // Remove thousand separators
$cleanPrice = str_replace(',', '.', $cleanPrice); // Convert comma to decimal point
} elseif (strpos($cleanPrice, ',') !== false) {
// Only comma exists - check if it's decimal or thousand separator
$parts = explode(',', $cleanPrice);
if (strlen(end($parts)) === 2) {
// Last part is 2 digits, likely decimal
$cleanPrice = str_replace(',', '.', $cleanPrice);
} else {
// Comma as thousand separator
$cleanPrice = str_replace(',', '', $cleanPrice);
}
}
return floatval($cleanPrice);
}
// Function to compare prices and detect drops
function comparePrices($storedPrice, $currentPrice) {
$storedNumeric = priceToNumeric($storedPrice);
$currentNumeric = priceToNumeric($currentPrice['final_formatted']);
if ($storedNumeric === 0 && $currentNumeric === 0) {
return ['status' => 'same', 'change' => 0, 'percent' => 0];
}
if ($storedNumeric > $currentNumeric) {
$change = $storedNumeric - $currentNumeric;
$percent = ($change / $storedNumeric) * 100;
return [
'status' => 'drop',
'change' => $change,
'percent' => round($percent, 1),
'old_price' => $storedPrice,
'new_price' => $currentPrice['final_formatted']
];
} elseif ($storedNumeric < $currentNumeric) {
$change = $currentNumeric - $storedNumeric;
$percent = ($change / $storedNumeric) * 100;
return [
'status' => 'increase',
'change' => $change,
'percent' => round($percent, 1),
'old_price' => $storedPrice,
'new_price' => $currentPrice['final_formatted']
];
} else {
return ['status' => 'same', 'change' => 0, 'percent' => 0];
}
}
// Function to format currency
function formatCurrency($amount, $currency = 'EUR') {
$symbols = ['EUR' => '€', 'USD' => '$', 'GBP' => '£'];
$symbol = $symbols[$currency] ?? $currency;
return number_format($amount, 2, ',', '.') . $symbol;
}
// Main execution
$jsonFile = 'games_data.json';
$results = [];
if (file_exists($jsonFile)) {
$data = json_decode(file_get_contents($jsonFile), true);
if (!empty($data)) {
echo "
Steam Price Checker
Steam Price Checker
Checking prices for " . count($data) . " games...
Currency: EUR | Last updated: " . date('Y-m-d H:i:s') . "
";
$totalGames = count($data);
$priceDrops = 0;
$priceIncreases = 0;
$noChanges = 0;
$errors = 0;
foreach ($data as $index => $game) {
echo "
";
echo "Checking: {$game['title']} ...";
echo "
";
// Flush output to show progress
flush();
ob_flush();
$appId = extractAppIdFromUrl($game['url']);
$currentPrice = fetchCurrentPrice($appId, 'EUR', 'en');
if ($currentPrice) {
$comparison = comparePrices($game['price'], $currentPrice);
$statusClass = $comparison['status'] . '-change';
if ($comparison['status'] === 'drop') $priceDrops++;
elseif ($comparison['status'] === 'increase') $priceIncreases++;
else $noChanges++;
echo "";
flush();
ob_flush();
} else {
$errors++;
echo "";
flush();
ob_flush();
}
// Small delay to avoid hitting rate limits
usleep(500000); // 0.5 second delay
}
// Final statistics
echo "
Summary
Total Games: {$totalGames}
Price Drops: {$priceDrops}
Price Increases: {$priceIncreases}
No Changes: {$noChanges}
Errors: {$errors}
";
echo "
";
} else {
echo "No game data found in the JSON file.
";
}
} else {
echo "JSON file not found: {$jsonFile}
";
}
?>
addtolist.php
--
$deckCompatibility,
'deck_result' => $deckResult
];
}
}
return [
'deck_compat' => 'Not Available',
'deck_result' => 'No Result Data'
];
}
// Function to format bytes into human-readable format (MB/GB)
function formatFileSize($bytes) {
if (!is_numeric($bytes) || $bytes <= 0) return 'Size Not Available';
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 1) . ' GB';
} elseif ($bytes >= 1048576) {
return number_format($bytes / 1048576, 1) . ' MB';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 1) . ' KB';
} else {
return $bytes . ' bytes';
}
}
// NEW: Improved function to fetch filesize with better error handling and reliable sources
function fetchFileSize($appId) {
$filesize = 'Size Not Available';
// Method 1: Try SteamDB API first (most reliable for file sizes)
$steamDbUrl = "https://steamdb.info/api/GetAppSize/?appid={$appId}";
$context = stream_context_create([
'http' => [
'timeout' => 10,
'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\r\n"
]
]);
$steamDbResponse = @file_get_contents($steamDbUrl, false, $context);
if ($steamDbResponse !== false) {
$steamDbData = json_decode($steamDbResponse, true);
if (isset($steamDbData['success']) && $steamDbData['success'] && isset($steamDbData['data']['size'])) {
$size = $steamDbData['data']['size'];
if ($size > 0) {
return formatFileSize($size);
}
}
}
// Method 2: Try Steam API with multiple size fields
$apiUrl = "https://store.steampowered.com/api/appdetails?appids={$appId}";
$response = @file_get_contents($apiUrl, false, $context);
if ($response !== false) {
$data = json_decode($response, true);
if (isset($data[$appId]['data'])) {
$gameData = $data[$appId]['data'];
// Check multiple possible size fields
$sizeFields = ['size_on_disk', 'content_size', 'package_size', 'install_size'];
foreach ($sizeFields as $field) {
if (isset($gameData[$field]) && $gameData[$field] > 0) {
return formatFileSize($gameData[$field]);
}
}
// Check for PC requirements which might contain size info
if (isset($gameData['pc_requirements']['minimum'])) {
$requirements = $gameData['pc_requirements']['minimum'];
if (preg_match('/(\d+(?:\.\d+)?)\s*GB|(\d+(?:\.\d+)?)\s*gigabyte/i', $requirements, $matches)) {
$size = $matches[1] ?: $matches[2];
if ($size > 0) {
return number_format(floatval($size), 1) . ' GB';
}
}
if (preg_match('/(\d+(?:\.\d+)?)\s*MB|(\d+(?:\.\d+)?)\s*megabyte/i', $requirements, $matches)) {
$size = $matches[1] ?: $matches[2];
if ($size > 0) {
return number_format(floatval($size), 1) . ' MB';
}
}
}
}
}
// Method 3: Try scraping the store page for size information
$storeUrl = "https://store.steampowered.com/app/{$appId}/";
$storePage = @file_get_contents($storeUrl, false, $context);
if ($storePage !== false) {
// Look for size patterns in system requirements
$sizePatterns = [
'/(\d+(?:\.\d+)?)\s*GB.*storage/i',
'/(\d+(?:\.\d+)?)\s*GB.*space/i',
'/(\d+(?:\.\d+)?)\s*gigabyte/i',
'/(\d+(?:\.\d+)?)\s*MB.*storage/i',
'/(\d+(?:\.\d+)?)\s*MB.*space/i',
'/(\d+(?:\.\d+)?)\s*megabyte/i',
'/storage.*?(\d+(?:\.\d+)?)\s*GB/i',
'/space.*?(\d+(?:\.\d+)?)\s*GB/i'
];
foreach ($sizePatterns as $pattern) {
if (preg_match($pattern, $storePage, $matches)) {
$size = floatval($matches[1]);
if ($size > 0) {
if (stripos($pattern, 'GB') !== false || stripos($pattern, 'gigabyte') !== false) {
return number_format($size, 1) . ' GB';
} else {
// Convert MB to GB if it's a large number
if ($size > 1000) {
return number_format($size / 1024, 1) . ' GB';
}
return number_format($size, 1) . ' MB';
}
}
}
}
}
return $filesize;
}
// Function to get price and filesize using Steam API
function fetchGameDetailsFromSteamApi($appId, $apiKey, $cc = 'USD', $lang = 'en') {
$url = "https://store.steampowered.com/api/appdetails?appids={$appId}&cc={$cc}&l={$lang}&key={$apiKey}";
$context = stream_context_create([
'http' => [
'timeout' => 15,
'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\r\n"
]
]);
$response = @file_get_contents($url, false, $context);
$data = json_decode($response, true);
$price = 'Price Not Available';
$filesize = fetchFileSize($appId); // Use our improved filesize function
if (isset($data[$appId]['data'])) {
$gameData = $data[$appId]['data'];
// Get price
if (isset($gameData['price_overview'])) {
$price = decodeUnicode($gameData['price_overview']['final_formatted']);
} elseif (isset($gameData['is_free']) && $gameData['is_free']) {
$price = 'Free';
}
}
return [
'price' => $price,
'filesize' => $filesize
];
}
// Function to check if the URL already exists in the JSON data
function isDuplicate($url, $data) {
foreach ($data as $item) {
if ($item['url'] === $url) {
return true;
}
}
return false;
}
// Handle form submission
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['url'])) {
$url = trim($_POST['url']);
if (filter_var($url, FILTER_VALIDATE_URL)) {
$title = extractTitleFromUrl($url);
// Extract the app ID from the URL
preg_match('/\/app\/(\d+)\//', $url, $matches);
$appId = $matches[1] ?? null;
if ($appId) {
$apiKey = isset($_GET['api_key']) ? $_GET['api_key'] : DEFAULT_STEAM_API_KEY;
$cc = isset($_POST['currency']) ? $_POST['currency'] : 'USD';
$lang = isset($_POST['language']) ? $_POST['language'] : 'en';
// Fetch data
$gameDetails = fetchGameDetailsFromSteamApi($appId, $apiKey, $cc, $lang);
$deckDetails = fetchSteamDeckCompatibility($appId);
$currentDate = date('Y-m-d H:i:s');
$gameData = [
'url' => $url,
'title' => $title,
'price' => $gameDetails['price'],
'steamDeckCompatibility' => $deckDetails['deck_compat'],
'filesize' => $gameDetails['filesize'],
'deck_result' => $deckDetails['deck_result'],
'date' => $currentDate
];
$jsonFile = 'games_data.json';
$data = file_exists($jsonFile) ? json_decode(file_get_contents($jsonFile), true) : [];
if (!isDuplicate($url, $data)) {
$data[] = $gameData;
file_put_contents($jsonFile, json_encode($data, JSON_PRETTY_PRINT));
echo "Data for {$title} has been added to the JSON file.
";
echo "
Price: {$gameDetails['price']} | Size: {$gameDetails['filesize']} | Deck Compatibility: {$deckDetails['deck_compat']}
";
} else {
echo "Data for {$title} already exists. No duplicate added.
";
}
} else {
echo "Could not extract App ID from the URL.
";
}
} else {
echo "Invalid URL. Please enter a valid URL.
";
}
}
?>
Steam Game Logger
Steam Game Logger
Improved Filesize Detection:
• Primary: SteamDB API (most reliable)
• Secondary: Steam API with multiple size fields
• Fallback: Store page system requirements parsing
• Better timeout handling and user agents
Currency Code:
(e.g., USD, EUR, GBP)
Language:
(e.g., en, fr, de)
Steam Game URL:
Add Game to List
";
echo "Recent Entries: ";
$recent = array_slice($data, -3); // Show last 3 entries
foreach ($recent as $entry) {
echo "{$entry['title']} - Price: {$entry['price']} - Size: {$entry['filesize']} - Deck: {$entry['steamDeckCompatibility']}
";
}
echo "";
}
}
?>
NintendoNews parser (exclude ninteverything )
--------------
]*>.*? ]*src=["\']([^"\']+)["\'][^>]*>.*?]*class=["\']summary[^"\']*["\']>(.*?)
)?.*?data-timestamp=["\']([^"\']+)["\']#si';
if (preg_match_all($pattern, $html, $matches, PREG_SET_ORDER)) {
foreach ($matches as $m) {
$link = trim($m[2]);
$blocked = false;
foreach ($blocked_domains as $d) {
if (stripos($link, $d) !== false) { $blocked = true; break; }
}
if ($blocked) continue;
$host = parse_url($link, PHP_URL_HOST);
$badge = strtoupper(preg_replace('/^www\./','',$host));
$articles[] = [
'image' => normalize_image_url($m[1], $base_url),
'link' => $link,
'title' => trim(htmlspecialchars_decode($m[3])),
'excerpt' => isset($m[5]) ? trim(htmlspecialchars_decode($m[5])) : '',
'badge' => $badge,
'date' => isset($m[6]) ? $m[6] : ''
];
}
}
return $articles;
}
$base_url = 'https://nintendonews.com/news';
$blocked_domains = ['nintendolife.de','nintendoeverything.com'];
$per_page = 10;
$page_param = isset($_GET['page']) ? max(1,intval($_GET['page'])) : 1;
$autopage = isset($_GET['autopage']);
$url = $base_url . '?page=' . $page_param;
$html = fetch_url_content($url);
$all_articles = $html ? parse_articles_regex($html, $base_url, $blocked_domains) : [];
$total_articles = count($all_articles);
$total_pages = 100;
$current_page = $page_param;
$articles_to_show = $all_articles;
if ($autopage && isset($_GET['ajax'])) {
header('Content-Type: application/json');
echo json_encode([
'articles' => $articles_to_show,
'current' => $current_page,
'total' => $total_pages
]);
exit;
}
?>
Nintendo News
Latest Nintendo News
=htmlspecialchars($a['date'])?>
=htmlspecialchars($a['excerpt'])?>
=htmlspecialchars($a['badge'])?>
Page =$current_page?>
Fedi is Missing
------------------------
filereport.php
--
[
'timeout' => 15,
'header' => "User-Agent: FediversePostFetcher/1.0\r\nAccept: application/json\r\n"
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
]);
// If we have a status ID but no username, fetch the post to get the username
if ($status_id && !$username) {
// Try different API endpoints for different platforms
$api_urls = [
"https://$domain/api/v1/statuses/$status_id", // Mastodon/Pleroma standard
"https://$domain/objects/$status_id", // Akkoma alternative
];
$post_data = null;
foreach ($api_urls as $api_url) {
$response = @file_get_contents($api_url, false, $context);
if ($response !== false) {
$post_data = json_decode($response, true);
if ($post_data) break;
}
}
if (!$post_data) {
die("Error fetching post data from the server.");
}
// Extract username from post data - ensure we get the full @user@domain format
if (isset($post_data['account']['acct'])) {
$acct = $post_data['account']['acct'];
// Check if the acct already contains a domain
if (strpos($acct, '@') === false) {
// If not, add the domain
$username = '@' . $acct . '@' . $domain;
} else {
$username = '@' . $acct;
}
} else if (isset($post_data['account']['username'])) {
$acct = $post_data['account']['username'];
// Try to get the full acct (username@domain) if possible
if (isset($post_data['account']['url'])) {
$account_url = parse_url($post_data['account']['url']);
if (isset($account_url['host']) && $account_url['host'] !== $domain) {
$username = '@' . $acct . '@' . $account_url['host'];
} else {
$username = '@' . $acct . '@' . $domain;
}
} else {
$username = '@' . $acct . '@' . $domain;
}
} else {
die("Unable to extract user information from post.");
}
} else if ($username) {
// If we already have a username from the URL, ensure it's in the full @user@domain format
$username_parts = explode('@', $username);
if (count($username_parts) === 2) {
// Only username part is present, add domain
$username = $username . '@' . $domain;
}
}
// Now get the user's latest post using the username
// Extract just the username part without @ for lookup
$lookup_username = substr($username, 1); // Remove the leading @
$lookup_username_parts = explode('@', $lookup_username);
$local_username = $lookup_username_parts[0];
// Try different lookup endpoints
$lookup_urls = [
"https://$domain/api/v1/accounts/lookup?acct=" . urlencode($lookup_username), // Standard
"https://$domain/users/" . urlencode($local_username) . ".json", // Akkoma alternative
];
$account_data = null;
foreach ($lookup_urls as $lookup_url) {
$response = @file_get_contents($lookup_url, false, $context);
if ($response !== false) {
$account_data = json_decode($response, true);
if ($account_data) break;
}
}
$user_id = null;
$user_acct = $lookup_username; // Default to the full username we already have
if ($account_data) {
if (isset($account_data['id'])) {
$user_id = $account_data['id'];
}
// Get the proper account identifier
if (isset($account_data['acct'])) {
$acct = $account_data['acct'];
// Check if the acct already contains a domain
if (strpos($acct, '@') === false) {
// If not, add the domain
$user_acct = $acct . '@' . $domain;
} else {
$user_acct = $acct;
}
}
}
$latest_post_url = null;
$created_at = null;
// If we couldn't get user ID through lookup, try to get statuses directly
if (!$user_id) {
// Try different statuses endpoints
$statuses_urls = [
"https://$domain/users/$local_username/outbox?page=true&limit=1", // ActivityPub
"https://$domain/api/v1/accounts/$local_username/statuses?limit=1", // Mastodon API
"https://$domain/@$local_username.json?limit=1", // Alternative
];
$statuses = null;
foreach ($statuses_urls as $statuses_url) {
$response = @file_get_contents($statuses_url, false, $context);
if ($response !== false) {
$data = json_decode($response, true);
// Handle different response formats
if (isset($data['orderedItems'])) {
// ActivityPub format
if (!empty($data['orderedItems']) && isset($data['orderedItems'][0]['published'])) {
$created_at = $data['orderedItems'][0]['published'];
if (isset($data['orderedItems'][0]['id'])) {
$latest_post_url = $data['orderedItems'][0]['id'];
}
$statuses = [['created_at' => $created_at]];
break;
}
} else if (is_array($data) && !empty($data) && isset($data[0]['created_at'])) {
// Standard API format
$statuses = $data;
$created_at = $data[0]['created_at'];
if (isset($data[0]['url'])) {
$latest_post_url = $data[0]['url'];
}
break;
}
}
}
if (!$statuses || empty($statuses)) {
die("No posts found for the user.");
}
} else {
// Get statuses using the user ID
$statuses_url = "https://$domain/api/v1/accounts/$user_id/statuses?limit=1";
$response = @file_get_contents($statuses_url, false, $context);
if ($response === false) {
die("Error fetching user's latest post.");
}
$statuses = json_decode($response, true);
if (empty($statuses)) {
die("No posts found for the user.");
}
$created_at = $statuses[0]['created_at'];
if (isset($statuses[0]['url'])) {
$latest_post_url = $statuses[0]['url'];
}
}
// If we don't have a post URL, try to construct one
if (!$latest_post_url && isset($statuses[0]['id'])) {
$post_id = $statuses[0]['id'];
$latest_post_url = "https://$domain/@$user_acct/$post_id";
}
// Format the date to yyyymmdd
$date = date('Ymd', strtotime($created_at));
// Prepare the data for saving to the JSON file
$entry = [
'username' => "@$user_acct",
'date' => $date,
'post_url' => $latest_post_url
];
// Load the existing JSON data
$filename = 'fedimissing.json';
$json_data = [];
if (file_exists($filename)) {
$json_content = file_get_contents($filename);
if ($json_content !== false) {
$json_data = json_decode($json_content, true);
}
if (!is_array($json_data)) {
$json_data = [];
}
}
// Check if the user already exists
$user_exists = false;
foreach ($json_data as $existing_entry) {
if ($existing_entry['username'] === "@$user_acct") {
$user_exists = true;
break;
}
}
if ($user_exists) {
die("User @$user_acct already exists in the JSON file.");
}
// Prepend the new entry to the array
array_unshift($json_data, $entry);
// Save the updated data back to the JSON file
if (file_put_contents($filename, json_encode($json_data, JSON_PRETTY_PRINT))) {
echo "Latest post date saved successfully: @$user_acct $date";
if ($latest_post_url) {
echo " Post URL: $latest_post_url ";
}
} else {
echo "Error saving to JSON file. Please check file permissions.";
}
} else {
// Display the HTML form
?>
Fediverse Post Fetcher
Fetch Latest Post From Fediverse User
This tool will always get the date of the user's NEWEST post, regardless of which post URL you provide.
Enter Fediverse URL:
Get Newest Post Date
Supported formats:
https://mastodon.social/@username
https://instance.tld/@username/1234567890 (any post URL)
https://instance.tld/notice/ABC123 (Pleroma/Akkoma)
https://instance.tld/objects/ABC123 (Akkoma)
https://instance.tld/users/username (Akkoma)
Note: Some instances may have API limitations or require authentication.
index.html
--
Fediverse Missing Posts
post.php
--
Missing Fedi person: (update) ';
foreach ($missingPersons as $person) {
$username = htmlspecialchars($person['username']);
$postUrl = htmlspecialchars($person['post_url']);
$date = $person['date'];
// Calculate days missing
$today = new DateTime();
$missingDate = DateTime::createFromFormat('Ymd', $date);
if ($missingDate) {
$interval = $today->diff($missingDate);
$daysMissing = $interval->days;
$daysText = $daysMissing == 1 ? 'day' : 'days';
} else {
$daysMissing = 'unknown';
$daysText = 'days';
}
$htmlContent .= "$username - $daysMissing $daysText missing ";
}
// Prepare API request
$apiEndpoint = rtrim($instanceUrl, '/') . '/api/v1/statuses';
$postData = [
'status' => $htmlContent,
'content_type' => 'text/html',
'visibility' => 'public'
];
// Headers for Akkoma API
$headers = [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json'
];
// Initialize cURL
$ch = curl_init();
if (!empty($oldPostUrl)) {
// Edit existing post - extract status ID from URL
$urlParts = parse_url($oldPostUrl);
$pathParts = explode('/', trim($urlParts['path'], '/'));
$statusId = end($pathParts);
curl_setopt_array($ch, [
CURLOPT_URL => $apiEndpoint . '/' . $statusId,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => json_encode($postData),
CURLOPT_HTTPHEADER => $headers
]);
$action = "editing";
} else {
// Create new post
curl_setopt_array($ch, [
CURLOPT_URL => $apiEndpoint,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($postData),
CURLOPT_HTTPHEADER => $headers
]);
$action = "posting";
}
// Execute API request
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_error($ch)) {
http_response_code(500);
echo "Error: cURL error - " . curl_error($ch);
curl_close($ch);
exit;
}
curl_close($ch);
// Handle response
if ($httpCode >= 200 && $httpCode < 300) {
$responseData = json_decode($response, true);
$postUrl = $responseData['url'] ?? $oldPostUrl;
echo "Successfully $action post: " . $postUrl . "\n";
echo "Generated content:\n\n";
echo $htmlContent;
} else {
http_response_code($httpCode);
echo "Error: API returned status code $httpCode\n";
echo "Response: " . $response;
}
?>
updatereport.php
--
[
'timeout' => 15,
'header' => "User-Agent: FediversePostUpdater/1.0\r\nAccept: application/json\r\n"
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
]);
// Iterate over all entries
foreach ($json_data as &$entry) {
$username = $entry['username']; // e.g. @user@domain
$parts = explode('@', $username);
if (count($parts) < 3) {
echo "Skipping invalid username format: $username ";
continue;
}
$local_user = $parts[1];
$domain = $parts[2];
// Look up account to get ID
$lookup_urls = [
"https://$domain/api/v1/accounts/lookup?acct=" . urlencode("$local_user@$domain"),
"https://$domain/users/" . urlencode($local_user) . ".json",
];
$account_data = null;
foreach ($lookup_urls as $lookup_url) {
$response = @file_get_contents($lookup_url, false, $context);
if ($response !== false) {
$account_data = json_decode($response, true);
if ($account_data) break;
}
}
$user_id = null;
if ($account_data && isset($account_data['id'])) {
$user_id = $account_data['id'];
}
$latest_post_url = null;
$created_at = null;
if ($user_id) {
// Get statuses by user ID
$statuses_url = "https://$domain/api/v1/accounts/$user_id/statuses?limit=1";
$response = @file_get_contents($statuses_url, false, $context);
if ($response !== false) {
$statuses = json_decode($response, true);
if (is_array($statuses) && !empty($statuses)) {
$created_at = $statuses[0]['created_at'];
if (isset($statuses[0]['url'])) {
$latest_post_url = $statuses[0]['url'];
}
}
}
}
if (!$created_at) {
echo "No posts found for $username ";
continue;
}
// Format date
$date = date('Ymd', strtotime($created_at));
// Only update if date is newer
if ($date > $entry['date']) {
$entry['date'] = $date;
$entry['post_url'] = $latest_post_url;
echo "Updated $username → $date ($latest_post_url) ";
} else {
echo "No new posts for $username (latest: {$entry['date']}) ";
}
}
// Save updated JSON
if (file_put_contents($filename, json_encode($json_data, JSON_PRETTY_PRINT))) {
echo " JSON file updated successfully.";
} else {
echo " Error saving JSON file. Check permissions.";
}
?>
Fedi Follow List Creator
----------------
index.html
--
adduser
Fediverse Posts Browser
saveuser.php
--
false, 'message' => 'Invalid URL format'];
}
$domain = $parsedUrl['host'];
$username = null;
// Check if it's a direct @user@domain style URL (like Mastodon)
if (preg_match('#/@([^/@]+)(?:@[^/]+)?/(?:posts/)?\d+#', $parsedUrl['path'] ?? '', $matches)) {
$username = $matches[1];
}
// Check for notice/status IDs (like Pleroma/Akkoma)
else if (preg_match('#/notice/([^/?]+)#', $parsedUrl['path'] ?? '', $matches)) {
$statusId = $matches[1];
$username = resolveUsernameFromNotice($domain, $statusId);
if (!$username) {
// Prompt for username if resolution fails
if (isset($_POST['username']) && !empty($_POST['username'])) {
$username = $_POST['username'];
} else {
return [
'success' => false,
'needs_username' => true,
'url' => $url,
'domain' => $domain,
'message' => 'Could not resolve username automatically. Please enter it below.'
];
}
}
} else {
return ['success' => false, 'message' => 'URL format not recognized: ' . ($parsedUrl['path'] ?? 'no path')];
}
// Try to fetch profile picture
$pfpUrl = null;
if (!isset($_POST['pfp_url'])) {
$pfpUrl = fetchProfilePicture($domain, $username);
} else if (!empty($_POST['pfp_url'])) {
$pfpUrl = $_POST['pfp_url'];
}
// If we couldn't get the pfp URL and it wasn't provided, prompt for it
if (!$pfpUrl && !isset($_POST['pfp_url'])) {
return [
'success' => false,
'needs_pfp' => true,
'url' => $url,
'domain' => $domain,
'username' => $username,
'message' => 'Could not fetch profile picture automatically. Please enter the URL below.'
];
}
// Save to JSON
$userIdentifier = "$username@$domain";
$success = saveToJson($userIdentifier, $url, $pfpUrl);
if ($success) {
return ['success' => true, 'user' => $userIdentifier, 'url' => $url, 'pfp' => $pfpUrl];
} else {
return ['success' => false, 'message' => 'Failed to save to JSON file'];
}
}
function resolveUsernameFromNotice($domain, $statusId) {
// Try to fetch the status information using ActivityPub or Mastodon API
$apiUrl = "https://$domain/api/v1/statuses/$statusId";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_USERAGENT, 'Fediverse-User-Resolver/1.0');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200 && $response) {
$data = json_decode($response, true);
if (isset($data['account']['acct'])) {
return $data['account']['acct'];
}
if (isset($data['account']['username'])) {
return $data['account']['username'];
}
}
return null;
}
function fetchProfilePicture($domain, $username) {
// Try to fetch user information using Mastodon API
$apiUrl = "https://$domain/api/v1/accounts/lookup?acct=$username";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_USERAGENT, 'Fediverse-User-Resolver/1.0');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200 && $response) {
$data = json_decode($response, true);
if (isset($data['avatar'])) {
return $data['avatar'];
}
}
return null;
}
function saveToJson($userIdentifier, $url, $pfpUrl) {
$filename = 'fediusers.json';
$data = [];
// Read existing data if file exists
if (file_exists($filename)) {
$existingData = file_get_contents($filename);
if ($existingData) {
$data = json_decode($existingData, true) ?: [];
}
}
// Check if this user already exists
$exists = false;
foreach ($data as $entry) {
if ($entry['user'] === $userIdentifier) {
$exists = true;
break;
}
}
// Add new entry if it doesn't exist
if (!$exists) {
$data[] = [
'user' => $userIdentifier,
'url' => $url,
'pfp' => $pfpUrl,
'added' => date('Y-m-d H:i:s')
];
// Write back to file
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
return file_put_contents($filename, $json) !== false;
}
return true; // Already exists, consider it a success
}
?>
Fediverse User Saver
Fediverse User Saver
Username:
Save User
Profile Picture URL:
Save User
Error:
Fediverse Post URL:
Process URL
Supported formats:
Mastodon: https://mastodon.example/@username/123456789
Pleroma/Akkoma: https://instance.example/notice/ABC123DEF456
fediusers.json
--
[
{
"user": "alcea@alceawis.com",
"url": "https://alceawis.com/notice/AxWUaYZukUOVrhcSrQ",
"pfp": "https://alceawis.com/uploads/7ec207e13c920294e202c8007828db61de0e0e72fa3dd8f0dc6e94783d38a4c5.png",
"added": "2025-08-25 09:17:58"
},
{
"user": "Samb@mastodon.gamedev.place",
"url": "https://mastodon.gamedev.place/@Samb/112933762886991797",
"pfp": "https://cdn.masto.host/mastodongamedevplace/accounts/avatars/109/270/815/057/921/135/original/8e1fecc5ec4ed0c6.jpg",
"added": "2025-08-25 09:18:09"
},
{
"user": "dr_muesli@woof.tech",
"url": "https://woof.tech/@dr_muesli/115056046900787616",
"pfp": "https://cdn.woof.tech/accounts/avatars/113/820/856/641/199/543/original/f17907d049782ede.png",
"added": "2025-08-25 09:18:15"
}
]
EmojiShorthandDownloader (Zip & single dld)
----------------------
💾 Save Emoji List
$emoji['url'],
'shortcode' => $emoji['shortcode']
];
}
return $emojis;
}
}
// If still no content, return null
if (!$html) {
return null;
}
// Use DOMDocument to parse the HTML and extract emoji data
$dom = new DOMDocument();
@$dom->loadHTML($html);
$emojis = [];
$emojiElements = $dom->getElementsByTagName('div');
foreach ($emojiElements as $element) {
$imgElement = $element->getElementsByTagName('img');
$ddElement = $element->getElementsByTagName('dd');
// Check if we have the necessary elements
if ($imgElement->length > 0 && $ddElement->length > 0) {
$url = $imgElement->item(0)->getAttribute('src');
$shortcode = trim($ddElement->item(0)->nodeValue);
$emojis[] = ['url' => $url, 'shortcode' => $shortcode];
}
}
return $emojis;
}
// Function to download and cache emoji
function cache_emoji($url, $shortcode) {
$extension = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION);
if (empty($extension)) {
$extension = 'png'; // default extension
}
$filename = preg_replace('/[^a-zA-Z0-9_-]/', '', $shortcode) . '.' . $extension;
$cache_dir = __DIR__ . '/emoji_cache/';
if (!file_exists($cache_dir)) {
mkdir($cache_dir, 0755, true);
}
$cache_path = $cache_dir . $filename;
// If not cached or cache is older than 1 day
if (!file_exists($cache_path) || (time() - filemtime($cache_path) > 86400)) {
$image_data = fetch_via_curl($url);
if ($image_data) {
file_put_contents($cache_path, $image_data);
}
}
if (file_exists($cache_path)) {
return [
'path' => $cache_path,
'filename' => $filename,
'mime' => mime_content_type($cache_path)
];
}
return false;
}
$filtered = [];
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Handle emoji download
if (isset($_POST['download_single'])) {
$url = $_POST['url'];
$shortcode = $_POST['shortcode'];
$cached = cache_emoji($url, $shortcode);
if ($cached) {
header('Content-Type: ' . $cached['mime']);
header('Content-Disposition: attachment; filename="' . $cached['filename'] . '"');
readfile($cached['path']);
exit;
} else {
header("HTTP/1.0 404 Not Found");
echo "Failed to download emoji";
exit;
}
}
// Handle ZIP download
if (isset($_POST['download_all'])) {
$instance = $_POST['instance'];
$keyword = $_POST['keyword'];
// Fetch emojis again to ensure we have fresh data
$emojis = fetch_emojis($instance);
if ($emojis !== null) {
$filtered = [];
foreach ($emojis as $emoji) {
if (empty($keyword) || stripos($emoji['shortcode'], $keyword) !== false) {
$filtered[] = $emoji;
}
}
}
if (!empty($filtered)) {
$zip = new ZipArchive();
$zip_filename = 'emojis_' . time() . '.zip';
$zip_path = sys_get_temp_dir() . '/' . $zip_filename;
if ($zip->open($zip_path, ZipArchive::CREATE) === TRUE) {
foreach ($filtered as $emoji) {
$cached = cache_emoji($emoji['url'], $emoji['shortcode']);
if ($cached) {
$zip->addFile($cached['path'], $cached['filename']);
}
}
$zip->close();
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . $zip_filename . '"');
header('Content-Length: ' . filesize($zip_path));
readfile($zip_path);
unlink($zip_path);
exit;
}
}
header("HTTP/1.0 404 Not Found");
echo "Failed to create ZIP file";
exit;
}
// Handle the form submission for searching
$instance = isset($_POST['instance']) ? trim($_POST['instance']) : '';
$keyword = isset($_POST['keyword']) ? trim($_POST['keyword']) : '';
if (empty($instance)) {
$error = "Instance URL is required.";
} else {
// Fetch emojis from the instance URL
$emojis = fetch_emojis($instance);
if ($emojis === null) {
$error = "Failed to fetch from $instance";
} else {
// Filter emojis by keyword
foreach ($emojis as $emoji) {
if (empty($keyword) || stripos($emoji['shortcode'], $keyword) !== false) {
$filtered[] = $emoji;
}
}
}
}
}
?>
Emoji Fetcher
Emoji Fetcher
= htmlspecialchars($error) ?>
Instance URL (e.g. blahaj.zone):
Keyword:
Submit
Matched Emojis (= count($filtered) ?>)
Download All as ZIP
:= htmlspecialchars($emoji['shortcode']) ?>:
Download
Pixiv fetch user artwork stats (via api)
---------------------
singlefetch.php
--
$apiUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
'Referer: https://www.pixiv.net/',
// Uncomment if accessing private/NSFW works (replace XXX with your PHPSESSID):
// 'Cookie: PHPSESSID=XXX;',
],
CURLOPT_ENCODING => 'gzip',
CURLOPT_SSL_VERIFYPEER => false, // Disable if SSL issues occur
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
return ['error' => true, 'message' => "HTTP {$httpCode} - API request failed"];
}
return json_decode($response, true);
}
// Fetch artwork data
$artworkData = fetchPixivData($artworkId);
// Handle errors
if (isset($artworkData['error']) && $artworkData['error']) {
die("Error: " . ($artworkData['message'] ?? 'Failed to fetch artwork'));
}
// Extract data
$title = $artworkData['body']['title'] ?? 'No Title';
$imageUrl = $artworkData['body']['urls']['original'] ?? $artworkData['body']['urls']['regular'] ?? null;
$views = $artworkData['body']['viewCount'] ?? 0;
$bookmarks = $artworkData['body']['bookmarkCount'] ?? 0;
$likes = $artworkData['body']['likeCount'] ?? 0;
$comments = $artworkData['body']['commentCount'] ?? 0;
$artworkUrl = $defaultBaseUrl . $artworkId;
echo ''
. htmlspecialchars($title) . ' ';
if ($imageUrl) {
$proxyImageUrl = "https://i.pixiv.re/" . basename($imageUrl);
echo ''
. ' ';
}
echo sprintf(
'Views: %s | Bookmarks: %s | Likes: %s | Comments: %s',
htmlspecialchars($views),
htmlspecialchars($bookmarks),
htmlspecialchars($likes),
htmlspecialchars($comments)
);
?>
fetchfive.php
--
Cea ';
echo 'Alcea ';
echo 'PixivFE instances ';
/**
* Gets the latest 5 artwork IDs from Pixiv user profile
*/
function getPixivArtworkIds($userId, $limit = 5) {
$apiUrl = "https://www.pixiv.net/ajax/user/{$userId}/profile/all";
$context = stream_context_create([
'http' => [
'header' => "User-Agent: Mozilla/5.0\r\nReferer: https://www.pixiv.net/"
],
'ssl' => [
'verify_peer' => false
]
]);
$response = @file_get_contents($apiUrl, false, $context);
if (!$response) return [];
$data = json_decode($response, true);
if (empty($data['body']['illusts'])) return [];
// Get the most recent artwork IDs
$artworkIds = array_keys($data['body']['illusts']);
return array_slice($artworkIds, 0, $limit);
}
// Configuration: Default userId to Alcea's if not specified
$userId = $_GET['userId'] ?? '75406576'; // Default: Alcea
// Get the latest 5 artwork IDs
$artworkIds = getPixivArtworkIds($userId, 5);
// Loop through artwork IDs and fetch their data via the custom URL
foreach ($artworkIds as $artworkId) {
// Generate the full URL to fetch the artwork
$singlefetchUrl = "https://alceawis.de/other/extra/fetchdata/singlefetch.php?url=" . urlencode("https://www.pixiv.net/artworks/{$artworkId}");
// Fetch the output from the generated URL
$output = @file_get_contents($singlefetchUrl, false, stream_context_create([
'http' => ['timeout' => 5]
]));
// Display the result or an error message if loading failed
echo $output ?: "Failed to load artwork: {$artworkId} ";
}
?>
Pixiv image gallery
------------------
";
if ($page > 1) {
echo "<< Previous ";
}
echo "Page {$page} of {$totalPages} ";
if ($page < $totalPages) {
echo "Next >> ";
}
echo "";
// Start container for inline artworks
echo "";
// Display current batch of artworks in one line
foreach ($currentArtworkIds as $artId) {
$artLink = "https://www.pixiv.net/artworks/{$artId}";
$artImageUrl = "https://embed.pixiv.net/artwork.php?illust_id={$artId}";
// Fetch details only for the current artwork
$html = @file_get_contents($artLink);
$artTitle = "Untitled";
if ($html !== false) {
$dom = new DOMDocument();
@$dom->loadHTML($html);
$titleTag = $dom->getElementsByTagName('title')->item(0);
$artTitle = $titleTag ? $titleTag->textContent : "Untitled";
$artTitle = rtrim($artTitle, ' - pixiv');
}
echo "
";
$safeTitle = htmlspecialchars(addslashes($artTitle), ENT_QUOTES);
$safeImageUrl = htmlspecialchars($artImageUrl, ENT_QUOTES);
echo "
";
echo "
";
echo "
";
}
echo "
"; // Close artworks-container
// Display navigation buttons again at bottom
echo "";
if ($page > 1) {
echo "<< Previous ";
}
echo "Page {$page} of {$totalPages} ";
if ($page < $totalPages) {
echo "Next >> ";
}
echo "
";
// Display total count
echo "Total artworks: {$totalArtworks}
";
// Display all links in details section
echo "";
echo "Click to view all artwork links ";
echo "";
foreach ($artworkIds as $artId) {
echo "https://www.pixiv.net/artworks/{$artId}\n";
}
echo " ";
echo " ";
} else {
echo "No artworks found for the given user.
";
}
}
// Get parameters from URL
$userId = isset($_GET['userId']) ? $_GET['userId'] : '75406576'; // Default user ID
$random = isset($_GET['random']);
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Add user ID input form
echo '';
echo '
';
echo 'Pixiv User ID: ';
echo ' ';
echo ' ';
echo ' ';
echo '';
// Display artworks
getPixivUserArt($userId, $random, $page);
?>
×
Uptime Checker
--------------------
check.php
--
['mode' => 'response_code'],
'alceawis.com' => ['mode' => 'file_check'],
'alcea-wisteria.de' => ['mode' => 'response_code']
];
// cURL request with timeout and response code check
function curlGetResponseCode($url, $timeout = 30) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
]);
curl_exec($ch);
$responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_errno($ch);
curl_close($ch);
// Timeout or other error? Treat as 404
if ($error) {
return 404;
}
return $responseCode ?: 404;
}
function checkFileExistence($url, $filename) {
$fullUrl = $url . '/' . $filename;
$responseCode = curlGetResponseCode($fullUrl, 30);
return $responseCode === 200;
}
foreach ($domains as $domain => $config) {
$date = date('Y-m-d H:i:s');
$mode = $config['mode'];
$responseData = [
'date' => $date,
'mode' => $mode,
'domainname' => $domain,
'response_code' => 0
];
if ($mode === 'file_check') {
$exists = checkFileExistence("https://$domain", 'favicon.png') ||
checkFileExistence("https://$domain", 'index.html');
$responseData['response_code'] = $exists ? 200 : 404;
}
if ($mode === 'response_code') {
$responseData['response_code'] = curlGetResponseCode("https://$domain", 30);
}
$filename = "$domain.json";
$existingData = [];
if (file_exists($filename)) {
$json = file_get_contents($filename);
$decoded = json_decode($json, true);
if (is_array($decoded) && array_keys($decoded) === range(0, count($decoded) - 1)) {
$existingData = $decoded;
}
}
$existingData[] = $responseData;
file_put_contents($filename, json_encode($existingData, JSON_PRETTY_PRINT));
echo "Test result for $domain saved/updated in $filename\n";
}
?>
status.html
--
Domain Uptime Monitor
sync.php
--
'ftp.alcea-wisteria.de',
'user' => 'add@alcea-wisteria.de',
'pass' => '', // Replace with actual password
'remoteDir' => 'PHP/0demo/2025-08-04-UptimeCheck',
'localDir' => '/storage/emulated/0/pws/www/2025-08-04-UptimeCheck',
'successMessage' => 'Successfully uploaded to alcea-wisteria.de'
],
[
'host' => 'ftp.alceawis.de',
'user' => 'add@alceawis.de',
'pass' => '', // Replace with actual password
'remoteDir' => 'alceawis.de/other/extra/fetchdata/2025-08-04-UptimeCheck',
'localDir' => '/storage/emulated/0/pws/www/2025-08-04-UptimeCheck',
'successMessage' => 'Successfully uploaded to alceawis.de'
]
];
// Files to be uploaded
$files = [
'alceawis.com.json',
'alceawis.de.json',
'alcea-wisteria.de.json'
];
foreach ($servers as $server) {
foreach ($files as $file) {
$localFile = "{$server['localDir']}/$file";
$remoteFile = "{$server['remoteDir']}/$file";
// Check if local file exists
if (!file_exists($localFile)) {
echo "Local file not found: $localFile\n";
continue;
}
// Open local file
$fp = fopen($localFile, 'r');
if (!$fp) {
echo "Failed to open local file: $localFile\n";
continue;
}
// Initialize cURL session
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "ftp://{$server['host']}/$remoteFile");
curl_setopt($ch, CURLOPT_USERPWD, "{$server['user']}:{$server['pass']}");
curl_setopt($ch, CURLOPT_UPLOAD, true);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localFile));
curl_setopt($ch, CURLOPT_VERBOSE, true); // Optional: shows transfer details
// Execute the file upload
$result = curl_exec($ch);
if (!$result) {
echo "Upload failed for $file to {$server['host']}: " . curl_error($ch) . "\n";
} else {
echo "{$server['successMessage']}: $file\n";
}
// Clean up
curl_close($ch);
fclose($fp);
}
}
?>
Execute multi/different vps .sh via SSH ..
-------------------------
Load Akkoma Script
Load Nginx Script
";
$formSubmitted = true;
} else {
echo "Failed to move the uploaded file. ";
exit(1);
}
} else {
echo "Upload error code: " . $_FILES['ppkFile']['error'] . " ";
exit(1);
}
}
if ($formSubmitted && file_exists($uploadedKey)) {
chmod($uploadedKey, 0600);
// Escape the remote script path safely for bash on remote (no extra quotes)
$escapedRemoteScriptPath = escapeshellcmd($remoteScriptPath);
// Compose SSH command
$sshCommand = "ssh -i " . escapeshellarg($uploadedKey) .
" -o StrictHostKeyChecking=no " . escapeshellarg($remoteServer) .
" 'bash " . $escapedRemoteScriptPath . " > $logFile 2>&1'";
exec($sshCommand, $output, $returnVar);
echo $returnVar === 0 ? "Remote script executed successfully. " : "Remote script failed to execute. ";
// Get the log output from remote
$logContents = shell_exec("ssh -i " . escapeshellarg($uploadedKey) .
" -o StrictHostKeyChecking=no " . escapeshellarg($remoteServer) .
" 'cat $logFile'");
echo $logContents ? "" . htmlspecialchars($logContents) . " " : "No log output. ";
// Cleanup
if (file_exists($uploadedKey)) {
unlink($uploadedKey);
echo "File deleted! ";
}
}
?>
Upload Key and Run
Upload OpenSSH Private Key
Choose your private key:
Remote script path:
Upload & Run
Execute remote vps .sh via ssh and openSSHKey and report back (here: check letsencrypt status)
-------------------------
";
$formSubmitted = true;
} else {
echo "Failed to move the uploaded file. ";
exit(1);
}
} else {
echo "Upload error code: " . $_FILES['ppkFile']['error'] . " ";
exit(1);
}
}
if ($formSubmitted && file_exists($uploadedKey)) {
chmod($uploadedKey, 0600);
$sshCommand = "ssh -i $uploadedKey -o StrictHostKeyChecking=no $remoteServer 'bash $remoteScriptPath > $logFile 2>&1'";
exec($sshCommand, $output, $returnVar);
echo $returnVar === 0 ? "Remote script executed successfully. " : "Remote script failed to execute. ";
$logContents = shell_exec("ssh -i $uploadedKey -o StrictHostKeyChecking=no $remoteServer 'cat $logFile'");
echo $logContents ? "" . htmlspecialchars($logContents) . " " : "No log output. ";
// Cleanup
if (file_exists($uploadedKey)) {
unlink($uploadedKey);
echo "File deleted! ";
}
}
?>
Upload Key and Run
Upload OpenSSH Private Key
Choose your private key:
Upload & Run
Folder md5 Hashes (track dir&indir changes)
---------------------
isDir() && !$file->isDot()) {
$subfolderPath = $file->getPathname();
$subdirHash = hashFolderContents($subfolderPath);
$subdirs[] = $file->getFilename() . ' - ' . $subdirHash;
}
}
sort($subdirs); // Sort alphabetically by folder name
return $subdirs;
}
function hashFolderContents($folder) {
$items = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($folder, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$relativePath = substr($file->getPathname(), strlen($folder));
$items[] = $relativePath . $file->getSize() . $file->getMTime();
}
}
sort($items); // Ensure consistent sorting for hashing
return md5(implode('', $items));
}
// === Function to load existing hashes from file
function loadExistingHashes($file) {
$hashes = [];
$lines = file($file, FILE_IGNORE_NEW_LINES);
foreach ($lines as $line) {
list($folderName, $hash) = explode(' - ', $line);
$hashes[$folderName] = $hash;
}
return $hashes;
}
// === Function to generate comparison data
function compareHashes($subdirectories, $existingHashes) {
$totalFolders = count($subdirectories);
$missingFolders = 0;
$changedFolders = 0;
$comparisonResult = [];
foreach ($subdirectories as $line) {
list($folderName, $currentHash) = explode(' - ', $line);
if (isset($existingHashes[$folderName])) {
if ($existingHashes[$folderName] !== $currentHash) {
$comparisonResult[$folderName] = ['status' => 'changed', 'hash' => $currentHash];
$changedFolders++;
} else {
$comparisonResult[$folderName] = ['status' => 'same', 'hash' => $currentHash];
}
} else {
$comparisonResult[$folderName] = ['status' => 'missing', 'hash' => $currentHash];
$missingFolders++;
}
}
return [
'total' => $totalFolders,
'missing' => $missingFolders,
'changed' => $changedFolders,
'comparison' => $comparisonResult
];
}
$comparisonData = compareHashes($subdirectories, $existingHashes);
?>
Directory Hashes
📁 Subdirectory MD5 Hashes
Subdirectory Name
MD5 Hash
Status
$data): ?>
💾 Save to folder.md5hash.txt
✅ File saved: folder.md5hash.txt
Summary
Total Folders: Missing Folders: Changed Folders:
Fediverse Instance Diagnostic Checker
------------------
$emoji $label: $extra";
}
function discover_actor_url($domain, $username = null) {
$base = "https://$domain";
// If username is not provided, try to infer from domain (before first dot)
if ($username === null) {
$username = explode('.', $domain)[0];
}
$guesses = [
"$base/$username",
"$base/users/$username",
"$base/users/admin",
"$base/@$username",
];
if ($domain === "mastodon.social") {
$guesses[] = "$base/users/gargron";
}
foreach ($guesses as $url) {
[$actor, $code, $raw] = fetch_json($url, "application/activity+json");
if ($actor && isset($actor['id'])) {
return [$url, $actor];
}
}
return [null, null];
}
?>
Fediverse Instance Checker
🌐 Fediverse Instance Checker
Enter instance domain (e.g. mastodon.social):
🔍 Checking $domain ";
// Nodeinfo
$nodeinfo_url = get_nodeinfo_url($domain);
if ($nodeinfo_url) {
[$nodeinfo, $code] = fetch_json($nodeinfo_url);
display_result("NodeInfo (2.0)", true, "Found");
$software = $nodeinfo['software']['name'] ?? 'Unknown';
$version = $nodeinfo['software']['version'] ?? 'Unknown';
$users = $nodeinfo['usage']['users']['total'] ?? 'n/a';
$statuses = $nodeinfo['usage']['localPosts'] ?? 'n/a';
$start_time = $nodeinfo['metadata']['start_time'] ?? null;
echo "Software: $software
";
echo "Version: $version
";
echo "Users: $users
";
echo "Statuses: $statuses
";
if ($start_time) {
$since = (new DateTime())->diff(new DateTime($start_time));
echo "Online since: $start_time (~{$since->days} days)
";
}
} else {
display_result("NodeInfo", false, "Not found");
}
// Webfinger
[$webfinger, $code] = fetch_json("$base/.well-known/webfinger?resource=acct:example@$domain");
display_result("Webfinger", $code === 200, "HTTP $code");
// Instance info
[$instance, $code] = fetch_json("$base/api/v1/instance");
display_result("Instance Metadata", is_array($instance), $instance['uri'] ?? '');
// Emojis
[$emojis, $code] = fetch_json("$base/api/v1/custom_emojis");
display_result("Custom Emojis", is_array($emojis), is_array($emojis) ? count($emojis) . " found" : "");
// Peers
[$peers, $code] = fetch_json("$base/api/v1/instance/peers");
display_result("Federation Peers", is_array($peers), is_array($peers) ? count($peers) . " connected" : "");
// ActivityPub endpoints
echo "📬 ActivityPub Endpoint Discovery ";
list($actor_url, $actor_json) = discover_actor_url($domain);
if (!$actor_json) {
display_result("Actor Discovery", false, "No actor found via smart guessing.");
} else {
display_result("Actor Found", true, $actor_json['preferredUsername'] ?? $actor_json['id']);
// INBOX
$inbox_url = $actor_json['inbox'] ?? null;
if ($inbox_url) {
$headers = @get_headers($inbox_url, 1);
$inbox_status = is_array($headers) && preg_match("/2\d\d|405|403/", $headers[0]);
display_result("Inbox", $inbox_status, "URL: $inbox_url");
} else {
display_result("Inbox", false);
}
// OUTBOX
$outbox_url = $actor_json['outbox'] ?? null;
if ($outbox_url) {
[$outbox_json, $outbox_code] = fetch_json($outbox_url, "application/activity+json");
$valid = $outbox_code === 200 && isset($outbox_json['type']) && stripos($outbox_json['type'], 'OrderedCollection') !== false;
display_result("Outbox", $valid, "URL: $outbox_url");
// Statuses
if (isset($outbox_json['orderedItems'][0]['id'])) {
$status_url = $outbox_json['orderedItems'][0]['id'];
[$status_json, $status_code] = fetch_json($status_url, "application/activity+json");
$valid_status = $status_code === 200 && isset($status_json['type']);
display_result("Statuses", $valid_status, "1st post: $status_url");
} else {
display_result("Statuses", false, "No recent status found");
}
} else {
display_result("Outbox", false);
display_result("Statuses", false, "No outbox to check");
}
}
// Heuristic
echo "Instance Type Guess ";
if (isset($software)) {
if (stripos($software, 'akkoma') !== false) {
echo "This is likely an Akkoma instance.
";
} elseif (stripos($software, 'mastodon') !== false) {
echo "This is likely a Mastodon instance.
";
} elseif (stripos($software, 'pleroma') !== false) {
echo "This is likely a Pleroma instance.
";
} else {
echo "Software type: $software
";
}
}
endif;
?>
.htaccess redirect url only if requested from Domain xy
------------------------
# Match /alceawis/status/* URLs,
RewriteCond %{REQUEST_URI} ^/alceawis/status/.+$
RewriteCond %{HTTP_REFERER} ^https?://(www\.)?alceawis\.com [NC]
RewriteRule ^alceawis/status/(.+)$ /_https://alceawis.com/alceawis/status/$1 [R=301,L]
ActiPub Post JSON fetch
----------------------------
Fetch Mastodon Post ActivityPub Data
Fetch Mastodon Post ActivityPub Data
Mastodon Post URL:
Fetch Data
= $errorMessage ?>
ActivityPub JSON-LD Response:
= $response ?>
Password protect php file SECURE (put at file start!)(extrasecure-hashed pass + logout)
----------------------
true,'httponly' => true,'samesite' => 'Strict']);
session_start();
$session_lifetime = 3 * 60; // Session timeout
$correct_username = "admin";
$correct_password_hash = '$2y$10$S9naYnE6MqP14zVRfIsig.A98jBGzEImaGLgKVuiSGKpI7yr7XeGG'; // password: 4869
//echo password_hash("pass", PASSWORD_DEFAULT); //generate
if (isset($_GET['logout'])) {
session_unset();
session_destroy();
header("Location: " . $_SERVER['PHP_SELF']);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['heartbeat'])) {
$_SESSION['last_activity'] = time();
exit;
}
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if ($username === $correct_username && password_verify($password, $correct_password_hash)) {
session_regenerate_id(true);
$_SESSION['logged_in'] = true;
$_SESSION['last_activity'] = time();
header("Location: " . $_SERVER['PHP_SELF']);
exit;
} else {
$error = "Invalid username or password.";
}
}
?>
$error"; ?>
Username:
Password:
Login
$session_lifetime) {
session_unset();
session_destroy();
header("Location: " . $_SERVER['PHP_SELF']);
exit;
}
$_SESSION['last_activity'] = time(); // Refresh session activity
$remaining_time = ($_SESSION['last_activity'] + $session_lifetime) - time();
?>
Welcome, you are logged in!
Logout
Mastodon Remote Follow (via APIKey)
----------------------------------
"$remoteUser@$remoteDomain"]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $accessToken"]);
$response = curl_exec($ch);
curl_close($ch);
if ($response === false) {
return false;
}
$data = json_decode($response, true);
// If data has "id", assume fetched successfully
return isset($data['id']);
}
$yourInstance = $_POST['your_instance'] ?? '';
$accessToken = $_POST['access_token'] ?? '';
$remoteUserRaw = $_POST['remote_user'] ?? '';
$result = '';
$error = '';
$offerFetch = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$yourInstance = rtrim(trim($yourInstance), '/');
$accessToken = trim($accessToken);
$remoteUserRaw = trim($remoteUserRaw);
list($remoteUser, $remoteDomain) = resolveAcct($remoteUserRaw);
if ($yourInstance === '' || $accessToken === '' || $remoteUser === '' || $remoteDomain === '') {
$error = "Your Instance URL, Access Token, and Remote User (user@domain) are required.";
} else {
$ch = curl_init();
// Search using just the username (remoteUser)
$searchUrl = $yourInstance . '/api/v2/search?q=' . urlencode($remoteUser) . '&type=accounts&limit=10';
curl_setopt($ch, CURLOPT_URL, $searchUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $accessToken"]);
$searchResponse = curl_exec($ch);
if ($searchResponse === false) {
$error = "Search failed: " . curl_error($ch);
} else {
$searchData = json_decode($searchResponse, true);
$foundUser = null;
foreach ($searchData['accounts'] ?? [] as $account) {
if (strcasecmp($account['acct'] ?? '', "$remoteUser@$remoteDomain") === 0) {
$foundUser = $account;
break;
}
if (domainFromUrl($account['url'] ?? '') === $remoteDomain) {
$acctUser = explode('@', $account['acct'])[0];
if (strcasecmp($acctUser, $remoteUser) === 0) {
$foundUser = $account;
break;
}
}
}
if (!$foundUser) {
// Offer remote fetch option
$offerFetch = true;
$error = "Remote user not found or not federated yet. You can try to fetch the remote user first.";
} else {
// Verify credentials (get own user id)
$verifyUrl = $yourInstance . '/api/v1/accounts/verify_credentials';
curl_setopt($ch, CURLOPT_URL, $verifyUrl);
curl_setopt($ch, CURLOPT_HTTPGET, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $accessToken"]);
$verifyResp = curl_exec($ch);
if ($verifyResp === false) {
$error = "Verify credentials failed: " . curl_error($ch);
} else {
$myData = json_decode($verifyResp, true);
if (isset($myData['id']) && $myData['id'] == $foundUser['id']) {
$error = "Cannot follow your own account.";
} else {
$userId = $foundUser['id'];
$followUrl = $yourInstance . "/api/v1/accounts/$userId/follow";
curl_setopt($ch, CURLOPT_URL, $followUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $accessToken"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$followResponse = curl_exec($ch);
if ($followResponse === false) {
$error = "Follow failed: " . curl_error($ch);
} else {
$result = $followResponse;
}
}
}
}
}
curl_close($ch);
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['remote_fetch'])) {
// User requested remote fetch
list($remoteUser, $remoteDomain) = resolveAcct($remoteUserRaw);
if ($yourInstance !== '' && $accessToken !== '' && $remoteUser !== '' && $remoteDomain !== '') {
if (remoteFetch($yourInstance, $accessToken, $remoteUser, $remoteDomain)) {
$result = "Remote user fetched successfully. Please try following again.";
$offerFetch = false;
} else {
$error = "Remote fetch failed.";
}
} else {
$error = "Invalid data for remote fetch.";
}
}
?>
Remote Follow via Mastodon API
Remote Follow on Mastodon
Your Mastodon Instance URL:
Your Access Token:
Remote User (user@domain or @user@domain):
Follow User
Error
= htmlspecialchars($error) ?>
Fetch Remote User
Response
= htmlspecialchars($result) ?>
Mail client (with broken send)
-----------------
mailclient.php
--
[debug
] IONOS (?)
Error: ' . imap_last_error();
} else {
$folders = imap_list($mailbox, "{" . $customImapHost . "}", "*");
$selectedMailbox = @imap_open($mailboxString . $selectedFolder, $username, $password);
if ($selectedMailbox) {
$emails = imap_search($selectedMailbox, 'ALL', SE_UID);
$emailData = [];
if ($emails) {
rsort($emails);
foreach (array_slice($emails, 0, 30) as $emailNumber) {
$header = imap_headerinfo($selectedMailbox, $emailNumber);
$body = imap_fetchbody($selectedMailbox, $emailNumber, 1);
$decodedBody = imap_qprint($body);
$emailData[] = [
'uid' => $emailNumber,
'from' => $header->fromaddress ?? '',
'subject' => $header->subject ?? '',
'date' => $header->date ?? '',
'body' => htmlspecialchars(substr($decodedBody, 0, 300)) . '...',
'fullBody' => htmlspecialchars($decodedBody),
];
}
}
imap_close($selectedMailbox);
}
imap_close($mailbox);
}
}
?>
Mail Client
send.php
--
Debug Info Received Parameters ";
foreach ($_POST as $key => $value) {
if (stripos($key, 'pass') !== false) {
echo "$key: " . str_repeat('*', strlen($value)) . "\n";
} else {
echo "$key: $value\n";
}
}
echo " ";
// SMTP Mail Function
function sendSMTPMail($smtpHost, $smtpPort, $encryption, $username, $password, $to, $subject, $body) {
$newline = "\r\n";
$timeout = 30;
$contextOptions = [];
// Set encryption socket
$remoteSocket = "$smtpHost:$smtpPort";
if (stripos($encryption, 'ssl') !== false) {
$remoteSocket = "ssl://$smtpHost:$smtpPort";
} elseif (stripos($encryption, 'tls') !== false) {
$contextOptions['ssl'] = ['crypto_method' => STREAM_CRYPTO_METHOD_TLS_CLIENT];
}
$context = stream_context_create($contextOptions);
$socket = @stream_socket_client($remoteSocket, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $context);
if (!$socket) {
return "Connection failed: $errstr ($errno)";
}
$sendCmd = function($cmd, $expectCode = 250) use ($socket, $newline) {
if ($cmd !== null) fwrite($socket, $cmd . $newline);
$response = '';
while ($line = fgets($socket, 515)) {
$response .= $line;
if (preg_match('/^\d{3} /', $line)) break;
}
$code = (int)substr($response, 0, 3);
if ($code !== $expectCode && !in_array($expectCode, [0, $code])) {
throw new Exception("SMTP error ($code): $response");
}
return $response;
};
try {
$sendCmd(null, 220);
$sendCmd("EHLO localhost");
if (stripos($encryption, 'tls') !== false) {
$sendCmd("STARTTLS", 220);
if (!stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
throw new Exception("Failed to enable TLS encryption.");
}
$sendCmd("EHLO localhost");
}
$sendCmd("AUTH LOGIN", 334);
$sendCmd(base64_encode($username), 334);
$sendCmd(base64_encode($password), 235);
$sendCmd("MAIL FROM: <$username>");
$sendCmd("RCPT TO: <$to>");
$sendCmd("DATA", 354);
$headers = "From: <$username>$newline";
$headers .= "To: <$to>$newline";
$headers .= "Subject: $subject$newline";
$headers .= "MIME-Version: 1.0$newline";
$headers .= "Content-Type: text/plain; charset=UTF-8$newline";
$headers .= $newline;
$message = $headers . $body . $newline . ".";
$sendCmd($message, 250);
$sendCmd("QUIT", 221);
fclose($socket);
return "Email sent successfully.";
} catch (Exception $e) {
fclose($socket);
return "Error: " . $e->getMessage();
}
}
// Handle POST form
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
$to = $_POST['to'] ?? '';
$subject = $_POST['subject'] ?? '';
$body = $_POST['body'] ?? '';
$smtpHost = $_POST['smtpHost'] ?? 'smtp.ionos.de';
$smtpPort = (int)($_POST['smtpPort'] ?? 465);
$smtpEncryption = $_POST['smtpEncryption'] ?? 'SSL';
if ($username && $password && $to && $subject && $body) {
$result = sendSMTPMail($smtpHost, $smtpPort, $smtpEncryption, $username, $password, $to, $subject, $body);
} else {
$result = "Missing required fields.";
}
echo "Result $result
Go Back ";
}
?>
debug.php
--
";
echo "🔍 SMTP Diagnostics for: $smtp_host (user: $username)\n";
echo "_______________________________\n";
// ✅ 1. Ping test
echo "\n📶 Pinging host $smtp_host ...\n";
$ping_result = null;
if (stripos(PHP_OS, 'WIN') === 0) {
$ping_result = shell_exec("ping -n 1 " . escapeshellarg($smtp_host));
$success = strpos($ping_result, 'TTL=') !== false;
} else {
$ping_result = shell_exec("ping -c 1 " . escapeshellarg($smtp_host));
$success = strpos($ping_result, '1 received') !== false || strpos($ping_result, 'bytes from') !== false;
}
echo $success ? "✅ Ping successful.\n" : "⚠️ Ping failed or blocked (not always critical).\n";
// ✅ 2. Define SMTP ports and expected encryption
$ports = [
25 => 'tls',
465 => 'ssl',
587 => 'tls'
];
// ✅ 3. Loop through each port
foreach ($ports as $port => $expected_encryption) {
echo "\n🌐 Testing port $port (expected encryption: $expected_encryption)\n";
// TCP connectivity test
echo "🔌 Testing TCP connection to $smtp_host:$port ... ";
$fp = @fsockopen($smtp_host, $port, $errno, $errstr, 5);
if (!$fp) {
echo "❌ Connection failed: $errstr ($errno)\n";
continue;
} else {
echo "✅ TCP connection successful.\n";
fclose($fp);
}
// PHPMailer test
echo "📤 Sending test email using PHPMailer on port $port...\n";
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = $smtp_host;
$mail->SMTPAuth = true;
$mail->Username = $username;
$mail->Password = $password;
$mail->Port = $port;
if ($port == 465) {
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
} else {
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
}
$mail->setFrom($username, 'SMTP Debug');
$mail->addAddress($username);
$mail->Subject = "SMTP Test via port $port";
$mail->Body = "This is a test email sent at " . date('Y-m-d H:i:s') . " via port $port.";
$mail->send();
echo "✅ Email sent successfully on port $port\n";
} catch (Exception $e) {
echo "❌ PHPMailer error on port $port: " . $mail->ErrorInfo . "\n";
}
}
echo "\n✅ Diagnostics complete.\n";
S/FTP & WebDAV Uploader
----------------------
upload.php
--
[
"method"=>"HEAD",
"header"=>"Authorization: Basic ".base64_encode("$username:$password"),
"ignore_errors"=>true
]];
$ctxCheck = stream_context_create($optsCheck);
@file_get_contents($url,false,$ctxCheck);
if (isset($http_response_header)) {
foreach($http_response_header as $line){
if (preg_match('#HTTP/\d+\.\d+\s+(\d+)#', $line, $m)){
$code = intval($m[1]);
if($code >= 200 && $code < 300){
$debug[] = "WebDAV upload canceled: '$remoteFile' already exists.";
return false;
}
break;
}
}
}
// 2. Upload
$data = file_get_contents($localFile);
$opts = ["http"=>[
"method"=>"PUT",
"header"=>"Authorization: Basic ".base64_encode("$username:$password")."\r\nContent-Length: ".strlen($data),
"content"=>$data,
"ignore_errors"=>true
]];
$ctx = stream_context_create($opts);
$res = @file_get_contents($url,false,$ctx);
if ($res === false) {
$debug[] = "WebDAV upload failed: Cannot reach server or unauthorized.";
return false;
}
if (isset($http_response_header) && is_array($http_response_header)) {
$statusLine = $http_response_header[0];
preg_match('#HTTP/\d+\.\d+\s+(\d+)#', $statusLine, $matches);
$code = isset($matches[1]) ? intval($matches[1]) : 0;
if ($code >= 200 && $code < 300) {
$debug[] = "WebDAV upload successful: $remoteFile (HTTP $code)";
return true;
} else {
$debug[] = "WebDAV upload failed: HTTP $code - " . $statusLine;
return false;
}
}
$debug[] = "WebDAV upload unknown result.";
return false;
}
function buildUrl($protocol,$server,$path) {
$scheme=($protocol==='webdav')?'https':$protocol;
return "$scheme://$server$path";
}
function listDirectory($protocol, $server, $username, $password, $path, &$debug) {
$files=[];
if ($protocol==='ftp') {
$conn=@ftp_connect($server);
if($conn&&@ftp_login($conn,$username,$password)){
ftp_pasv($conn,true);
if(@ftp_chdir($conn,$path)){
$raw=ftp_rawlist($conn,".");
foreach($raw as $line){
$parts=preg_split("/\s+/",$line,9);
if(count($parts)===9){
$files[]=['name'=>$parts[8],'type'=>$parts[0][0]==='d'?'dir':'file'];
}
}
}
ftp_close($conn);
}
} elseif($protocol==='sftp') {
$conn=@ssh2_connect($server);
if($conn&&@ssh2_auth_password($conn,$username,$password)){
$sftp=ssh2_sftp($conn);
$dh=@opendir("ssh2.sftp://$sftp$path");
if($dh){while(($f=readdir($dh))!==false){if($f==='.'||$f==='..')continue;$fp="ssh2.sftp://$sftp$path$f";$files[]=['name'=>$f,'type'=>is_dir($fp)?'dir':'file'];}closedir($dh);}
}
} elseif($protocol==='webdav') {
$url=buildUrl($protocol,$server,$path);
$opts=["http"=>["method"=>"PROPFIND","header"=>"Authorization: Basic ".base64_encode("$username:$password")."\r\nDepth: 1","content"=>""]];
$ctx=stream_context_create($opts);
$res=@file_get_contents($url,false,$ctx);
if($res!==false){preg_match_all('/(.*?)<\/d:href>/i',$res,$m);foreach($m[1] as $href){$nm=basename(parse_url($href,PHP_URL_PATH));if($nm&&$nm!==basename($path)){$files[]=['name'=>$nm,'type'=>(substr($href,-1)==='/')?'dir':'file'];}}}
}
return $files;
}
// -- FORM VALUES --
$username = getQuery('username');
$password = getQuery('password');
$server = getQuery('server');
$path = getQuery('path', '/');
$protocol = getQuery('protocol', 'ftp');
$uploadType = getQuery('upload_type', 'url');
$anonymous = getQuery('anonymous', '') ? true : false;
if ($anonymous) {
$username = 'anonymous';
$password = 'anonymous@domain.com';
}
$isDir = substr($path, -1) === '/';
$files = [];
$debug = [];
// -- HANDLE UPLOAD --
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($uploadType==='file' && !empty($_FILES['upload_file']['tmp_name'])) {
$file = $_FILES['upload_file'];
$remoteFile = basename($file['name']);
$tmpFile = $file['tmp_name'];
if($protocol==='ftp') ftpUpload($server,$username,$password,$path,$tmpFile,$remoteFile,$debug);
elseif($protocol==='sftp') sftpUpload($server,$username,$password,$path,$tmpFile,$remoteFile,$debug);
elseif($protocol==='webdav') webdavUpload($server,$username,$password,$path,$tmpFile,$remoteFile,$debug);
} elseif($uploadType==='url' && !empty($_POST['upload_url'])) {
$urlFile = $_POST['upload_url'];
$remoteFile = basename(parse_url($urlFile, PHP_URL_PATH));
$tmpFile = tempnam(sys_get_temp_dir(), 'up');
if(file_put_contents($tmpFile, file_get_contents($urlFile))===false){
$debug[] = "Download from URL failed: $urlFile";
} else {
if($protocol==='ftp') ftpUpload($server,$username,$password,$path,$tmpFile,$remoteFile,$debug);
elseif($protocol==='sftp') sftpUpload($server,$username,$password,$path,$tmpFile,$remoteFile,$debug);
elseif($protocol==='webdav') webdavUpload($server,$username,$password,$path,$tmpFile,$remoteFile,$debug);
}
unlink($tmpFile);
}
if(!empty($debug)){
echo "";
foreach($debug as $d) echo htmlspecialchars($d) . " ";
echo "
";
}
}
// -- HTML FORM --
echo '
Upload type:
File
URL
Anonymous Login
Username:
Password:
Server:
Path:
Protocol:
FTP
SFTP
WebDAV
Select file to upload:
Enter URL to upload:
';
// -- Directory listing --
if ($server && $isDir) {
$files = listDirectory($protocol, $server, $username, $password, $path, $debug);
if ($path !== '/') {
$parent = rtrim($path, '/');
$parent = substr($parent, 0, strrpos($parent, '/') + 1);
echo '.. (Parent directory) ';
}
echo "";
foreach ($files as $file) {
$name = $file['name'];
$type = $file['type'];
$urlPath = $path . $name . ($type === 'dir' ? '/' : '');
echo '';
if ($type === 'dir') {
echo "📁 " . htmlspecialchars($name) . " ";
} else {
$downloadUrl = "download.php?protocol=" . urlencode($protocol) .
"&server=" . urlencode($server) .
"&path=" . urlencode($urlPath) .
"&username=" . urlencode($username) .
"&password=" . urlencode($password) .
"&anonymous=" . ($anonymous?1:0);
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
if (in_array($ext, ['mp3','mp4','png','jpg','jpeg','gif'])) {
echo "📄 " . htmlspecialchars($name) . " ";
} else {
echo "📄 " . htmlspecialchars($name) . " ";
}
}
echo " ";
}
echo " ";
}
?>
download.php
--
'audio/mpeg',
'mp4' => 'video/mp4',
'png' => 'image/png',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif'
);
$contentType = isset($mime[$ext]) ? $mime[$ext] : 'application/octet-stream';
// Build remote URL
$url = ($protocol === 'webdav' ? 'https' : $protocol) . "://$username:$password@$server$path";
// Stream to browser
header("Content-Type: $contentType");
header('Content-Disposition: inline; filename="' . basename($path) . '"');
readfile($url);
exit;
?>
streamtplink.php
--
$size - 1) $end = $size - 1;
$length = $end - $start + 1;
$status = 206;
}
// Set headers
if (function_exists('http_response_code')) {
http_response_code($status);
} else {
header($_SERVER["SERVER_PROTOCOL"] . " " . $status);
}
header("Content-Type: video/mp4");
header("Accept-Ranges: bytes");
header("Content-Length: $length");
header('Content-Disposition: inline; filename="' . basename($remoteFile) . '"');
if ($status === 206) {
header("Content-Range: bytes $start-$end/$size");
}
// Open a temporary local stream for output buffering
// Use ftp_nb_fget to fetch from FTP in chunks and output directly
$tmpStream = fopen('php://output', 'wb');
if (!$tmpStream) {
ftp_close($ftp);
http_response_code(500);
echo "Failed to open output stream.";
exit;
}
// ftp_nb_fget doesn't support offset, so we have to download full or partial file manually
// We'll download full file into php://output but skip bytes before $start by reading and discarding
// Unfortunately, FTP protocol itself doesn't support partial retrieval starting at offset
// so we have to download and skip bytes, or use ftp_raw to send REST command (some FTP servers support it)
// We'll try REST command
ftp_raw($ftp, "TYPE I"); // Switch to binary mode
// Attempt REST command for offset
$restResp = ftp_raw($ftp, "REST $start");
if (strpos(implode("\n", $restResp), '350') === false) {
// REST not supported or failed - fallback to downloading entire file and skipping in PHP
$start = 0; // ignore range start - will download entire file
$length = $size;
$status = 200;
}
$ret = ftp_nb_fget($ftp, $tmpStream, $remoteFile, FTP_BINARY);
$bytesSent = 0;
while ($ret == FTP_MOREDATA) {
flush();
$ret = ftp_nb_continue($ftp);
}
fclose($tmpStream);
ftp_close($ftp);
}
// If cached file does NOT exist, do caching download with progress page and play button
if (!file_exists($cacheFile)) {
// Show progress bar page with play button, video, speed and ETR display and direct play button
echo 'Downloading... ';
echo "Downloading and caching video file... ";
echo '
Direct Play
Play Now
';
echo '';
echo 'Speed: 0 KB/s | Estimated time remaining: calculating...
';
echo ' ';
echo '';
flush();
// Connect FTP
$ftp = ftp_connect($server);
if (!$ftp || !ftp_login($ftp, $username, $password)) {
http_response_code(500);
echo "FTP connection failed.
";
exit;
}
ftp_pasv($ftp, true);
// Get file size for progress calculation
$ftpSize = ftp_size($ftp, $relativePath);
if ($ftpSize <= 0) {
echo "Failed to get remote file size.