baseUrl = rtrim($baseUrl, '/'); $this->cookieFile = sys_get_temp_dir() . '/pleroma_admin_cookies_' . session_id() . '.txt'; } public function login($username, $password) { // Step 1: Initialise session by visiting admin page $adminPage = $this->baseUrl . '/pleroma/admin/'; $ch = curl_init($adminPage); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_USERAGENT => $this->userAgent, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEJAR => $this->cookieFile, CURLOPT_COOKIEFILE => $this->cookieFile, CURLOPT_TIMEOUT => 30 ]); curl_exec($ch); curl_close($ch); // Step 2: OAuth password grant (exact flow used by modern AdminFE) $oauthUrl = $this->baseUrl . '/oauth/token'; $postData = [ 'grant_type' => 'password', 'username' => $username, 'password' => $password, // These are the default AdminFE client credentials – work on nearly all instances 'client_id' => 'ECfTrzsW11bkG7simInOJf614Ee2sBhOlvE-69KDMzs', 'client_secret' => 'r5EbNudkfePUwvRmc7DbafP0yOXpUC4lvipSrU8QOpo', ]; $ch = curl_init($oauthUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($postData), CURLOPT_USERAGENT => $this->userAgent, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_COOKIEJAR => $this->cookieFile, CURLOPT_COOKIEFILE => $this->cookieFile, CURLOPT_HTTPHEADER => [ 'Content-Type: application/x-www-form-urlencoded', 'Accept: application/json' ], CURLOPT_TIMEOUT => 30 ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $data = json_decode($response, true); if ($httpCode === 200 && isset($data['access_token'])) { $_SESSION['bearer_token'] = $data['access_token']; return ['success' => true, 'message' => 'Admin OAuth login successful', 'token' => $data['access_token']]; } return ['success' => false, 'message' => 'Login failed', 'http_code' => $httpCode, 'response' => $response]; } public function getCookieFile() { return $this->cookieFile; } public function cleanup() { if (file_exists($this->cookieFile)) { unlink($this->cookieFile); } } } class PleromaEmojiUploader { private $baseUrl; private $bearerToken; private $maxFileSize = 1048576; // 1 MB private $maxDimensions = 256; // 256×256 px public function __construct($baseUrl, $bearerToken) { $this->baseUrl = rtrim($baseUrl, '/'); $this->bearerToken = $bearerToken; } private function validateFile($file, &$error) { $error = ''; if ($_FILES['emojiFile']['error'] !== UPLOAD_ERR_OK) { $error = 'Upload error code: ' . $_FILES['emojiFile']['error']; return false; } if (!is_uploaded_file($file['tmp_name'])) { $error = 'No file uploaded or not a valid uploaded file (common on mobile)'; return false; } if ($file['size'] > $this->maxFileSize) { $error = 'File too large (max 1 MB)'; return false; } $filename = pathinfo($file['name'], PATHINFO_FILENAME); if (!preg_match('/^[a-zA-Z0-9_]+$/', $filename)) { $error = 'Shortcode (filename without extension) may only contain letters, numbers and underscores'; return false; } $allowed = ['png', 'gif', 'webp', 'jpg', 'jpeg']; $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); if (!in_array($ext, $allowed)) { $error = 'Only PNG, GIF, WebP, JPG/JPEG allowed'; return false; } $info = getimagesize($file['tmp_name']); if (!$info || $info[0] > $this->maxDimensions || $info[1] > $this->maxDimensions) { $error = 'Image dimensions too large (max 256×256 px)'; return false; } return true; } public function uploadEmoji($packName, $uploadedFile) { $error = ''; if (!$this->validateFile($uploadedFile, $error)) { return ['success' => false, 'error' => $error]; } // Modern endpoint (2024–2026 Akkoma/Pleroma) $url = $this->baseUrl . '/api/v1/pleroma/emoji/packs/files?name=' . urlencode($packName); $mime = $uploadedFile['type']; if (empty($mime) || $mime === 'application/octet-stream') { // Fallback detection $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $uploadedFile['tmp_name']); finfo_close($finfo); } $postData = [ 'file' => new CURLFile($uploadedFile['tmp_name'], $mime, $uploadedFile['name']) ]; $ch = curl_init($url); $headers = [ 'Authorization: Bearer ' . $this->bearerToken, 'Accept: application/json' ]; curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $postData, CURLOPT_USERAGENT => 'Mozilla/5.0', CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => $headers, CURLOPT_TIMEOUT => 60 ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); // Added for debug curl_close($ch); $shortcode = pathinfo($uploadedFile['name'], PATHINFO_FILENAME); $result = [ 'success' => ($httpCode >= 200 && $httpCode < 300), 'http_code' => $httpCode, 'shortcode' => $shortcode, 'pack' => $packName, 'response' => $response, 'curl_error' => $curlError // Added for debug ]; return $result; } } ?> Pleroma/Akkoma Admin – Login + Emoji Upload (2026)
1. Admin Login
2. Emoji Uploader

🔐 Admin Login (OAuth)

⚠️ Administrators only!
Uses the same OAuth password flow as the official admin panel.
Please fill all fields.
'; } elseif (!filter_var($serverUrl, FILTER_VALIDATE_URL)) { echo '
Invalid URL.
'; } else { $auth = new PleromaAdminAuth($serverUrl); $result = $auth->login($username, $password); if ($result['success']) { $_SESSION['base_url'] = $serverUrl; $_SESSION['bearer_token'] = $result['token']; echo '
'; echo '

✅ Login Successful!

'; echo '

You are now authenticated.

'; if (!empty($result['token'])) { echo '
Bearer Token:
' . htmlspecialchars($result['token']) . '
'; } echo '

Switch to the Emoji Uploader tab.

'; echo '
'; } else { $auth->cleanup(); echo '
'; echo '

❌ Login Failed

'; echo '

' . htmlspecialchars($result['message']) . '

'; if (isset($result['http_code'])) echo '

HTTP Code: ' . $result['http_code'] . '

'; echo '
'; } } } ?>

🎨 Emoji Uploader

Requires successful admin login.
Pack must already exist (create it first in the web Admin → Settings → Emoji if needed).
uploadEmoji($_POST['packName'], $_FILES['emojiFile']); echo '
'; if ($uploadResult['success']) { echo '

✅ Emoji Uploaded Successfully!

'; echo '

Shortcode: :' . htmlspecialchars($uploadResult['shortcode']) . ':

'; echo '

Pack: ' . htmlspecialchars($uploadResult['pack']) . '

'; echo '

You may need to refresh the admin panel or reload emojis for it to appear immediately.

'; } else { echo '

❌ Upload Failed

'; } echo '

HTTP Code: ' . $uploadResult['http_code'] . '

'; if (!empty($uploadResult['curl_error'])) { echo '

cURL Error: ' . htmlspecialchars($uploadResult['curl_error']) . '

'; } if (!empty($uploadResult['response'])) { echo '
' . htmlspecialchars($uploadResult['response']) . '
'; } echo '
Debug Info:
' . htmlspecialchars($debugInfo) . '
'; echo '
'; } ?>
No active admin session.
Please log in first on the Login tab.