$fullsize = CoreUtils::getFullsizeURL($StashItem['id'], 'sta.sh'); if (!is_string($fullsize)) { if ($fullsize === 404) { $Database->where('provider', 'sta.sh')->where('id', $StashItem['id'])->delete('deviation_cache'); $Database->where('preview', $StashItem['preview'])->orWhere('fullsize', $StashItem['fullsize'])->update('requests', array('fullsize' => null, 'preview' => null)); $Database->where('preview', $StashItem['preview'])->orWhere('fullsize', $StashItem['fullsize'])->update('reservations', array('fullsize' => null, 'preview' => null)); Response::fail('The original image has been deleted from Sta.sh', array('rmdirect' => true)); } else { throw new Exception("Code {$fullsize}; Could not find the URL"); } } } catch (Exception $e) { Response::fail('Error while finding URL: ' . $e->getMessage()); } // Check image availability if (!!DeviantArt::isImageAvailable($fullsize)) { Response::fail("The specified image doesn't seem to exist. Please verify that you can reach the URL below and try again.<br><a href='{$fullsize}' target='_blank'>{$fullsize}</a>"); } if (!$Database->where('id', $Post->id)->update("{$thing}s", array('fullsize' => $fullsize))) { Response::dbError(); } Response::done(array('fullsize' => $fullsize)); } } } } } $type = (new Input('what', function ($value) { if (!in_array($value, Posts::$TYPES)) { return Input::ERROR_INVALID; }
<p><?="$YouHave ".(!$AwaitCount?'no':"<strong>$AwaitCount</strong>")?> image<?=$AwaitCount!==1?'s':''?> waiting to be submited to and/or approved by the group<?= !$AwaitCount ? '.' : ", listed below.".( $sameUser ? "Please submit $them to the group gallery as soon as possible to have $them spot-checked for any issues. As stated in the rules, the goal is to add finished images to the group gallery, making $them easier to find for everyone.".( $AwaitCount>10 ? " You seem to have a large number of images that have not been approved yet, please submit them to the group soon if you haven't already." : '' ) :'' ).'</p><p>You can click the <strong class="color-green"><span class="typcn typcn-tick"></span> Check</strong> button below the '.CoreUtils::makePlural('image',$AwaitCount).' in case we forgot to click it ourselves after accepting it.'?></p> <? if ($AwaitCount){ ?> <ul id="awaiting-deviations"><? foreach ($AwaitingApproval as $Post){ $deviation = DeviantArt::getCachedSubmission($Post->deviation_id); $url = "http://{$deviation['provider']}/{$deviation['id']}"; unset($_); $postLink = $Post->toLink($_); $postAnchor = $Post->toAnchor(null, $_); $checkBtn = Permission::sufficient('member') ? "<button class='green typcn typcn-tick check'>Check</button>" : ''; echo <<<HTML <li id="{$Post->getID()}"> <div class="image deviation"> <a href="$url" target="_blank"> <img src="{$deviation['preview']}" alt="{$deviation['title']}"> </a> </div> <span class="label"><a href="$url" target="_blank">{$deviation['title']}</a></span> <em>Posted under $postAnchor</em>
<h2><span class='typcn typcn-info-large'></span>Additional notes</h2> <p id="notes"><?=Appearances::getNotesHTML($Appearance, NOWRAP, NOTE_TEXT_ONLY)?></p> </section> <? } if (!empty($Appearance['cm_favme'])){ $preview = Appearances::getCMPreviewURL($Appearance); ?> <section class="approved-cutie-mark"> <h2>Recommended cutie mark vector</h2> <?=Permission::sufficient('staff')&&!isset($Appearance['cm_dir'])?CoreUtils::notice('fail','Missing CM orientation, falling back to <strong>Tail-Head</strong>. Please edit the appaearance and provide an orientation!'):''?> <a id="pony-cm" href="http://fav.me/<?=$Appearance['cm_favme']?>" style="background-image:url('<?=Appearances::getCMPreviewSVGURL($Appearance['id'])?>')"> <div class="img cm-dir-<?=$Appearance['cm_dir']===CM_DIR_HEAD_TO_TAIL?'ht':'th'?>" style="background-image:url('<?=CoreUtils::aposEncode($preview)?>')"></div> </a> <p class="aside">This is only an illustration, the body shape & colors are <strong>not</strong> guaranteed to reflect the actual design.</p> <p>The image above links to the vector made by <?php $Vector = DeviantArt::getCachedSubmission($Appearance['cm_favme']); echo Users::get($Vector['author'],'name','name, avatar_url')->getProfileLink(User::LINKFORMAT_FULL); ?> and shows which way the cutie mark should be facing.</p> </section> <? } ?> <section class="color-list"> <h2 class="admin">Color groups</h2> <div class="admin"> <button class="darkblue typcn typcn-arrow-unsorted reorder-cgs">Re-order groups</button> <button class="green typcn typcn-plus create-cg">Create group</button> </div> <? if ($placehold = Appearances::getPendingPlaceholderFor($Appearance)) echo $placehold; else { ?> <ul id="colors" class="colors"><?php $CGs = ColorGroups::get($Appearance['id']);
use App\Cookie; use App\CSRFProtection; use App\DeviantArt; use App\Exceptions\CURLRequestException; use App\Permission; use App\Response; use App\Users; use App\Models\User; /** @var $signedIn bool */ if (!$signedIn) { Response::success("You've already signed out"); } CSRFProtection::protect(); if (isset($_REQUEST['unlink'])) { try { DeviantArt::request('https://www.deviantart.com/oauth2/revoke', null, array('token' => $currentUser->Session['access'])); } catch (CURLRequestException $e) { Response::fail("Coulnd not revoke the site's access: {$e->getMessage()} (HTTP {$e->getCode()})"); } } if (isset($_REQUEST['unlink']) || isset($_REQUEST['everywhere'])) { $col = 'user'; $val = $currentUser->id; $username = Users::validateName('username', null, true); if (isset($username)) { if (!Permission::sufficient('staff') || isset($_REQUEST['unlink'])) { Response::fail(); } /** @var $TargetUser User */ $TargetUser = $Database->where('name', $username)->getOne('users', 'id,name'); if (empty($TargetUser)) {
/** * Check authentication cookie and set global */ static function authenticate() { global $Database, $signedIn, $currentUser, $Color, $color; CSRFProtection::detect(); if (!POST_REQUEST && isset($_GET['CSRF_TOKEN'])) { HTTP::redirect(CSRFProtection::removeParamFromURL($_SERVER['REQUEST_URI'])); } if (!Cookie::exists('access')) { return; } $authKey = Cookie::get('access'); if (!empty($authKey)) { if (!preg_match(new RegExp('^[a-f\\d]+$', 'iu'), $authKey)) { $oldAuthKey = $authKey; $authKey = bin2hex($authKey); $Database->where('token', sha1($oldAuthKey))->update('sessions', array('token' => sha1($authKey))); Cookie::set('access', $authKey, time() + Time::$IN_SECONDS['year'], Cookie::HTTPONLY); } $currentUser = Users::get(sha1($authKey), 'token'); } if (!empty($currentUser)) { if ($currentUser->role === 'ban') { $Database->where('id', $currentUser->id)->delete('sessions'); } else { if (strtotime($currentUser->Session['expires']) < time()) { $tokenvalid = false; try { DeviantArt::getToken($currentUser->Session['refresh'], 'refresh_token'); $tokenvalid = true; } catch (CURLRequestException $e) { $Database->where('id', $currentUser->Session['id'])->delete('sessions'); trigger_error("Session refresh failed for {$currentUser->name} ({$currentUser->id}) | {$e->getMessage()} (HTTP {$e->getCode()})", E_USER_WARNING); } } else { $tokenvalid = true; } if ($tokenvalid) { $signedIn = true; if (time() - strtotime($currentUser->Session['lastvisit']) > Time::$IN_SECONDS['minute']) { $lastVisitTS = date('c'); if ($Database->where('id', $currentUser->Session['id'])->update('sessions', array('lastvisit' => $lastVisitTS))) { $currentUser->Session['lastvisit'] = $lastVisitTS; } } $_PrefersColour = array('Pirill-Poveniy' => true, 'itv-canterlot' => true); if (isset($_PrefersColour[$currentUser->name])) { $Color = 'Colour'; $color = 'colour'; } } } } else { Cookie::delete('access', Cookie::HTTPONLY); } }
/** * Requests or refreshes an Access Token * $type defaults to 'authorization_code' * * @param string $code * @param null|string $type * * @return User|void */ static function getToken(string $code, string $type = null) { global $Database, $http_response_header; if (empty($type) || !in_array($type, array('authorization_code', 'refresh_token'))) { $type = 'authorization_code'; } $URL_Start = 'https://www.deviantart.com/oauth2/token?client_id=' . DA_CLIENT . '&client_secret=' . DA_SECRET . "&grant_type={$type}"; switch ($type) { case "authorization_code": $json = DeviantArt::request("{$URL_Start}&code={$code}" . OAUTH_REDIRECT_URI, false); break; case "refresh_token": $json = DeviantArt::request("{$URL_Start}&refresh_token={$code}", false); break; } if (empty($json)) { if (Cookie::exists('access')) { $Database->where('access', Cookie::get('access'))->delete('sessions'); Cookie::delete('access', Cookie::HTTPONLY); } HTTP::redirect("/da-auth?error=server_error&error_description={$http_response_header[0]}"); } if (empty($json['status'])) { HTTP::redirect("/da-auth?error={$json['error']}&error_description={$json['error_description']}"); } $userdata = DeviantArt::request('user/whoami', $json['access_token']); /** @var $User Models\User */ $User = $Database->where('id', $userdata['userid'])->getOne('users'); if (isset($User->role) && $User->role === 'ban') { $_GET['error'] = 'user_banned'; $BanReason = $Database->where('target', $User->id)->orderBy('entryid', 'ASC')->getOne('log__banish'); if (!empty($BanReason)) { $_GET['error_description'] = $BanReason['reason']; } return; } $UserID = strtolower($userdata['userid']); $UserData = array('name' => $userdata['username'], 'avatar_url' => URL::makeHttps($userdata['usericon'])); $AuthData = array('access' => $json['access_token'], 'refresh' => $json['refresh_token'], 'expires' => date('c', time() + intval($json['expires_in'])), 'scope' => $json['scope']); $cookie = bin2hex(random_bytes(64)); $AuthData['token'] = sha1($cookie); $browser = CoreUtils::detectBrowser(); foreach ($browser as $k => $v) { if (!empty($v)) { $AuthData[$k] = $v; } } if (empty($User)) { $MoreInfo = array('id' => $UserID, 'role' => 'user'); $makeDev = !$Database->has('users'); if ($makeDev) { $MoreInfo['id'] = strtoupper($MoreInfo['id']); } $Insert = array_merge($UserData, $MoreInfo); $Database->insert('users', $Insert); $User = new User($Insert); if ($makeDev) { $User->updateRole('developer'); } } else { $Database->where('id', $UserID)->update('users', $UserData); } if (empty($makeDev) && !empty($User) && Permission::insufficient('member', $User->role) && $User->isClubMember()) { $User->updateRole('member'); } if ($type === 'refresh_token') { $Database->where('refresh', $code)->update('sessions', $AuthData); } else { $Database->where('user', $User->id)->where('scope', $AuthData['scope'], '!=')->delete('sessions'); $Database->insert('sessions', array_merge($AuthData, array('user' => $UserID))); } $Database->rawQuery("DELETE FROM sessions WHERE \"user\" = ? && lastvisit <= NOW() - INTERVAL '1 MONTH'", array($UserID)); Cookie::set('access', $cookie, time() + Time::$IN_SECONDS['year'], Cookie::HTTPONLY); return $User ?? null; }
private function _getDirectUrl($id) { switch ($this->provider) { case 'imgur': $this->fullsize = "https://i.imgur.com/{$id}.png"; $this->preview = "https://i.imgur.com/{$id}m.png"; self::_checkImageAllowed($this->fullsize); break; case 'derpibooru': $Data = @file_get_contents("http://derpibooru.org/{$id}.json"); if (empty($Data)) { throw new \Exception('The requested image could not be found on Derpibooru'); } $Data = JSON::decode($Data); if (isset($Data['duplicate_of'])) { return $this->_getDirectUrl($Data['duplicate_of']); } if (!isset($Data['is_rendered'])) { error_log("Invalid Derpibooru response for ID {$id}\n" . var_export($Data, true)); throw new \Exception('Derpibooru returned an invalid API response. This issue has been logged, please <a class="send-feedback">remind us</a> to take a look.'); } if (!$Data['is_rendered']) { throw new \Exception('The image was found but it hasn\'t been rendered yet. Please wait for it to render and try again shortly.'); } $this->fullsize = $Data['representations']['full']; $this->preview = $Data['representations']['small']; self::_checkImageAllowed($this->fullsize, $Data['mime_type']); break; case 'puush': $path = "http://puu.sh/{$id}"; $image = @file_get_contents($path); if (empty($image) || $image === 'That puush could not be found.') { throw new \Exception('The requested image could not be found on Puu.sh'); } if ($image === 'You do not have access to view that puush.') { throw new \Exception('The requested image is a private Puu.sh and the token is missing from the URL'); } self::_checkImageAllowed($path); $this->fullsize = $this->preview = $path; break; case 'dA': case 'fav.me': case 'sta.sh': if ($this->provider === 'dA') { $id = 'd' . base_convert($id, 10, 36); $this->provider = 'fav.me'; } try { $CachedDeviation = DeviantArt::getCachedSubmission($id, $this->provider); if (!DeviantArt::isImageAvailable($CachedDeviation['preview'])) { $preview = CoreUtils::aposEncode($CachedDeviation['preview']); throw new \Exception("The preview image appears to be unavailable. Please make sure <a href='{$preview}'>this link</a> works and try again, or re-submit the deviation if this persists."); } if (!DeviantArt::isImageAvailable($CachedDeviation['fullsize'])) { $fullsize = CoreUtils::aposEncode($CachedDeviation['fullsize']); throw new \Exception("The submission appears to be unavailable. Please make sure <a href='{$fullsize}'>this link</a> works and try again, or re-submit the deviation if this persists."); } } catch (CURLRequestException $e) { if ($e->getCode() === 404) { throw new \Exception('The requested image could not be found'); } throw new \Exception($e->getMessage()); } if (empty($CachedDeviation)) { throw new \Exception("{$this->provider} submission information could not be fetched for {$id}"); } $this->preview = $CachedDeviation['preview']; $this->fullsize = $CachedDeviation['fullsize']; $this->title = $CachedDeviation['title']; $this->author = $CachedDeviation['author']; self::_checkImageAllowed($this->preview); self::_checkImageAllowed($this->fullsize); break; case 'lightshot': $page = @file_get_contents("http://prntscr.com/{$id}"); if (empty($page)) { throw new \Exception('The requested page could not be found'); } if (!preg_match(new RegExp('<img\\s+class="image__pic[^"]*"\\s+src="http://i\\.imgur\\.com/([A-Za-z\\d]+)\\.'), $page, $_match)) { throw new \Exception('The requested image could not be found'); } $this->provider = 'imgur'; $this->_getDirectUrl($_match[1]); break; default: throw new \Exception("The image could not be retrieved due to a missing handler for the provider \"{$this->provider}\""); } $this->preview = URL::makeHttps($this->preview); $this->fullsize = URL::makeHttps($this->fullsize); $this->id = $id; }
switch ($do ?? null) { case "colorguide": if (!empty($Appearance)) { $sprite = Appearances::getSpriteURL($Appearance['id']); if ($sprite) { $ThumbImage = $sprite; } $Description = 'Show accurate colors for "' . $Appearance['label'] . '" from the MLP-VectorClub\'s Official Color Guide'; } break; case "s": if (!empty($LinkedPost)) { if (!$LinkedPost->isFinished) { $ThumbImage = $LinkedPost->preview; } else { $finishdeviation = DeviantArt::getCachedSubmission($LinkedPost->deviation_id); if (!empty($finishdeviation['preview'])) { $ThumbImage = $finishdeviation['preview']; } } $Title = $LinkedPost->label; if ($LinkedPost->isRequest) { $Description = 'A request'; } else { $_user = Users::get($LinkedPost->reserved_by, 'id', 'name'); $Description = 'A reservation' . (!empty($_user->name) ? " by {$_user->name}" : ''); } $Description .= ' on the MLP-VectorClub\'s website'; } break; }
/** * List ltem generator function for request & reservation generators * * @param Post $Post * @param bool $view_only Only show the "View" button * @param bool $cachebust_url Append a random string to the image URL to force a re-fetch * * @return string */ static function getLi(Post $Post, bool $view_only = false, bool $cachebust_url = false) : string { $finished = !empty($Post->deviation_id); $isRequest = $Post->isRequest; $type = $isRequest ? 'request' : 'reservation'; $ID = "{$type}-{$Post->id}"; $alt = !empty($Post->label) ? CoreUtils::aposEncode($Post->label) : ''; $postlink = (new Episode($Post))->formatURL() . "#{$ID}"; $ImageLink = $view_only ? $postlink : $Post->fullsize; $cachebust = $cachebust_url ? '?t=' . time() : ''; $Image = "<div class='image screencap'><a href='{$ImageLink}'><img src='{$Post->preview}{$cachebust}' alt='{$alt}'></a></div>"; $post_label = self::_getPostLabel($Post); $permalink = "<a href='{$postlink}'>" . Time::tag($Post->posted) . '</a>'; $posted_at = '<em class="post-date">'; if ($isRequest) { global $signedIn, $currentUser; $isRequester = $signedIn && $Post->requested_by === $currentUser->id; $isReserver = $signedIn && $Post->reserved_by === $currentUser->id; $overdue = Permission::sufficient('member') && $Post->isOverdue(); $posted_at .= "Requested {$permalink}"; if ($signedIn && (Permission::sufficient('staff') || $isRequester || $isReserver)) { $posted_at .= ' by ' . ($isRequester ? "<a href='/@{$currentUser->name}'>You</a>" : Users::get($Post->requested_by)->getProfileLink()); } } else { $overdue = false; $posted_at .= "Reserved {$permalink}"; } $posted_at .= "</em>"; $hide_reserved_status = !isset($Post->reserved_by) || $overdue && !$isReserver; if (!empty($Post->reserved_by)) { $Post->Reserver = Users::get($Post->reserved_by); $reserved_by = $overdue && !$isReserver ? ' by ' . $Post->Reserver->getProfileLink() : ''; $reserved_at = $isRequest && !empty($Post->reserved_at) && !($hide_reserved_status && Permission::insufficient('staff')) ? "<em class='reserve-date'>Reserved <strong>" . Time::tag($Post->reserved_at) . "</strong>{$reserved_by}</em>" : ''; if ($finished) { $approved = !empty($Post->lock); $Deviation = DeviantArt::getCachedSubmission($Post->deviation_id, 'fav.me', true); if (empty($Deviation)) { $ImageLink = $view_only ? $postlink : "http://fav.me/{$Post->deviation_id}"; $Image = "<div class='image deviation error'><a href='{$ImageLink}'>Preview unavailable<br><small>Click to view</small></a></div>"; } else { $alt = CoreUtils::aposEncode($Deviation['title']); $ImageLink = $view_only ? $postlink : "http://fav.me/{$Deviation['id']}"; $Image = "<div class='image deviation'><a href='{$ImageLink}'><img src='{$Deviation['preview']}{$cachebust}' alt='{$alt}'>"; if ($approved) { $Image .= "<span class='typcn typcn-tick' title='This submission has been accepted into the group gallery'></span>"; } $Image .= "</a></div>"; } if (Permission::sufficient('staff')) { $finished_at = !empty($Post->finished_at) ? "<em class='finish-date'>Finished <strong>" . Time::tag($Post->finished_at) . "</strong></em>" : ''; $locked_at = ''; if ($approved) { global $Database; $LogEntry = $Database->rawQuerySingle("SELECT l.timestamp\n\t\t\t\t\t\t\tFROM log__post_lock pl\n\t\t\t\t\t\t\tLEFT JOIN log l ON l.reftype = 'post_lock' && l.refid = pl.entryid\n\t\t\t\t\t\t\tWHERE type = ? && id = ?\n\t\t\t\t\t\t\tORDER BY pl.entryid ASC\n\t\t\t\t\t\t\tLIMIT 1", array($type, $Post->id)); $locked_at = $approved ? "<em class='approve-date'>Approved <strong>" . Time::tag(strtotime($LogEntry['timestamp'])) . "</strong></em>" : ''; } $Image .= $post_label . $posted_at . $reserved_at . $finished_at . $locked_at; if (!empty($Post->fullsize)) { $Image .= "<a href='{$Post->fullsize}' class='original color-green' target='_blank'><span class='typcn typcn-link'></span> Original image</a>"; } } } else { $Image .= $post_label . $posted_at . $reserved_at; } } else { $Image .= $post_label . $posted_at; } if ($overdue && (Permission::sufficient('staff') || $isReserver)) { $Image .= self::CONTESTABLE; } if ($hide_reserved_status) { $Post->Reserver = false; } return "<li id='{$ID}'>{$Image}" . self::_getPostActions($Post, $isRequest, $view_only ? $postlink : false) . '</li>'; }
/** * Retruns CM preview image link * * @param array $Appearance * * @return string */ static function getCMPreviewURL($Appearance) { return $Appearance['cm_preview'] ?? DeviantArt::getCachedSubmission($Appearance['cm_favme'])['preview']; }
use App\UserPrefs; if (!isset($_GET['error']) && (empty($_GET['code']) || empty($_GET['state']))) { $_GET['error'] = 'unauthorized_client'; } if (isset($_GET['error'])) { $err = $_GET['error']; if (isset($_GET['error_description'])) { $errdesc = $_GET['error_description']; } global $signedIn; if ($signedIn) { HTTP::redirect($_GET['state']); } Episodes::loadPage(); } $currentUser = DeviantArt::getToken($_GET['code']); $signedIn = !empty($currentUser); if (isset($_GET['error'])) { $err = $_GET['error']; if (isset($_GET['error_description'])) { $errdesc = $_GET['error_description']; } if ($err === 'user_banned') { $errdesc .= "\n\nIf you'd like to appeal your ban, please <a href='http://mlp-vectorclub.deviantart.com/notes/'>send the group a note</a>."; } Episodes::loadPage(); } if (preg_match(new RegExp('^[a-z\\d]+$', 'i'), $_GET['state'], $_match)) { $confirm = str_replace('{{CODE}}', $_match[0], file_get_contents(INCPATH . 'views/loginConfrim.html')); $confirm = str_replace('{{USERID}}', Permission::sufficient('developer') || UserPrefs::get('p_disable_ga') ? '' : $currentUser->id, $confirm); die($confirm);