From ee7c8343ddd2ee3957d0d9c376c85874926e6d88 Mon Sep 17 00:00:00 2001 From: Alex Cabal Date: Sat, 11 May 2024 13:23:15 -0500 Subject: [PATCH] Convert some constants to enums --- config/sql/se/Payments.sql | 2 +- config/sql/se/PendingPayments.sql | 2 +- lib/Artwork.php | 36 +++---- lib/Constants.php | 23 ----- .../HttpMethodNotAllowedException.php | 5 + lib/Exceptions/InvalidHttpMethodException.php | 5 + lib/HttpInput.php | 98 ++++++++++++------- lib/HttpMethod.php | 9 ++ lib/HttpRequestType.php | 5 + lib/HttpVariableSource.php | 7 ++ lib/HttpVariableType.php | 8 ++ lib/Payment.php | 6 +- lib/PaymentProcessor.php | 4 + lib/Session.php | 2 +- scripts/ingest-fa-payments | 4 +- scripts/process-pending-payments | 5 +- www/artists/get.php | 2 +- www/artworks/edit.php | 2 +- www/artworks/get.php | 4 +- www/artworks/index.php | 12 +-- www/artworks/new.php | 2 +- www/artworks/post.php | 29 +++--- www/bulk-downloads/collection.php | 2 +- www/bulk-downloads/download.php | 2 +- www/bulk-downloads/get.php | 4 +- www/collections/get.php | 2 +- www/ebooks/author.php | 2 +- www/ebooks/download.php | 4 +- www/ebooks/ebook.php | 2 +- www/ebooks/index.php | 12 +-- www/feeds/atom/search.php | 2 +- www/feeds/collection.php | 4 +- www/feeds/download.php | 2 +- www/feeds/get.php | 4 +- www/feeds/opds/search.php | 2 +- www/feeds/rss/search.php | 2 +- www/manual/index.php | 2 +- www/newsletter/subscriptions/confirm.php | 2 +- www/newsletter/subscriptions/delete.php | 20 ++-- www/newsletter/subscriptions/get.php | 2 +- www/newsletter/subscriptions/post.php | 82 ++++++++-------- www/polls/get.php | 2 +- www/polls/votes/get.php | 2 +- www/polls/votes/index.php | 2 +- www/polls/votes/new.php | 2 +- www/polls/votes/post.php | 33 +++---- www/sessions/new.php | 4 +- www/sessions/post.php | 38 ++++--- www/settings/post.php | 4 +- www/webhooks/github.php | 13 +-- www/webhooks/postmark.php | 7 +- www/webhooks/zoho.php | 17 ++-- 52 files changed, 282 insertions(+), 268 deletions(-) create mode 100644 lib/Exceptions/HttpMethodNotAllowedException.php create mode 100644 lib/Exceptions/InvalidHttpMethodException.php create mode 100644 lib/HttpMethod.php create mode 100644 lib/HttpRequestType.php create mode 100644 lib/HttpVariableSource.php create mode 100644 lib/HttpVariableType.php create mode 100644 lib/PaymentProcessor.php diff --git a/config/sql/se/Payments.sql b/config/sql/se/Payments.sql index d67ce883..383d365f 100644 --- a/config/sql/se/Payments.sql +++ b/config/sql/se/Payments.sql @@ -2,7 +2,7 @@ CREATE TABLE `Payments` ( `PaymentId` int(10) unsigned NOT NULL AUTO_INCREMENT, `UserId` int(10) unsigned DEFAULT NULL, `Created` datetime NOT NULL, - `ChannelId` tinyint(4) unsigned NOT NULL, + `Processor` enum('fractured_atlas') NOT NULL, `TransactionId` varchar(80) NOT NULL, `Amount` decimal(7,2) unsigned NOT NULL, `Fee` decimal(7,2) unsigned NOT NULL DEFAULT 0.00, diff --git a/config/sql/se/PendingPayments.sql b/config/sql/se/PendingPayments.sql index b979487a..0ebe5cf9 100644 --- a/config/sql/se/PendingPayments.sql +++ b/config/sql/se/PendingPayments.sql @@ -1,6 +1,6 @@ CREATE TABLE `PendingPayments` ( `Created` datetime NOT NULL, - `ChannelId` tinyint(4) unsigned NOT NULL, + `Processor` enum('fractured_atlas') NOT NULL, `TransactionId` varchar(80) NOT NULL, `ProcessedOn` datetime DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/lib/Artwork.php b/lib/Artwork.php index cd18d966..8da1c357 100644 --- a/lib/Artwork.php +++ b/lib/Artwork.php @@ -654,7 +654,9 @@ class Artwork{ } /** - * @throws Exceptions\ValidationException + * @throws Exceptions\InvalidArtworkException + * @throws Exceptions\InvalidArtworkTagException + * @throws Exceptions\InvalidArtistException * @throws Exceptions\InvalidImageUploadException */ public function Create(?string $imagePath = null): void{ @@ -874,23 +876,23 @@ class Artwork{ $artwork = new Artwork(); $artwork->Artist = new Artist(); - $artwork->Artist->Name = HttpInput::Str(POST, 'artist-name'); - $artwork->Artist->DeathYear = HttpInput::Int(POST, 'artist-year-of-death'); + $artwork->Artist->Name = HttpInput::Str(HttpVariableSource::Post, 'artist-name'); + $artwork->Artist->DeathYear = HttpInput::Int(HttpVariableSource::Post, 'artist-year-of-death'); - $artwork->Name = HttpInput::Str(POST, 'artwork-name'); - $artwork->CompletedYear = HttpInput::Int(POST, 'artwork-year'); - $artwork->CompletedYearIsCirca = HttpInput::Bool(POST, 'artwork-year-is-circa') ?? false; - $artwork->Tags = HttpInput::Str(POST, 'artwork-tags') ?? []; - $artwork->Status = ArtworkStatus::tryFrom(HttpInput::Str(POST, 'artwork-status') ?? '') ?? ArtworkStatus::Unverified; - $artwork->EbookUrl = HttpInput::Str(POST, 'artwork-ebook-url'); - $artwork->IsPublishedInUs = HttpInput::Bool(POST, 'artwork-is-published-in-us') ?? false; - $artwork->PublicationYear = HttpInput::Int(POST, 'artwork-publication-year'); - $artwork->PublicationYearPageUrl = HttpInput::Str(POST, 'artwork-publication-year-page-url'); - $artwork->CopyrightPageUrl = HttpInput::Str(POST, 'artwork-copyright-page-url'); - $artwork->ArtworkPageUrl = HttpInput::Str(POST, 'artwork-artwork-page-url'); - $artwork->MuseumUrl = HttpInput::Str(POST, 'artwork-museum-url'); - $artwork->Exception = HttpInput::Str(POST, 'artwork-exception'); - $artwork->Notes = HttpInput::Str(POST, 'artwork-notes'); + $artwork->Name = HttpInput::Str(HttpVariableSource::Post, 'artwork-name'); + $artwork->CompletedYear = HttpInput::Int(HttpVariableSource::Post, 'artwork-year'); + $artwork->CompletedYearIsCirca = HttpInput::Bool(HttpVariableSource::Post, 'artwork-year-is-circa') ?? false; + $artwork->Tags = HttpInput::Str(HttpVariableSource::Post, 'artwork-tags') ?? []; + $artwork->Status = ArtworkStatus::tryFrom(HttpInput::Str(HttpVariableSource::Post, 'artwork-status') ?? '') ?? ArtworkStatus::Unverified; + $artwork->EbookUrl = HttpInput::Str(HttpVariableSource::Post, 'artwork-ebook-url'); + $artwork->IsPublishedInUs = HttpInput::Bool(HttpVariableSource::Post, 'artwork-is-published-in-us') ?? false; + $artwork->PublicationYear = HttpInput::Int(HttpVariableSource::Post, 'artwork-publication-year'); + $artwork->PublicationYearPageUrl = HttpInput::Str(HttpVariableSource::Post, 'artwork-publication-year-page-url'); + $artwork->CopyrightPageUrl = HttpInput::Str(HttpVariableSource::Post, 'artwork-copyright-page-url'); + $artwork->ArtworkPageUrl = HttpInput::Str(HttpVariableSource::Post, 'artwork-artwork-page-url'); + $artwork->MuseumUrl = HttpInput::Str(HttpVariableSource::Post, 'artwork-museum-url'); + $artwork->Exception = HttpInput::Str(HttpVariableSource::Post, 'artwork-exception'); + $artwork->Notes = HttpInput::Str(HttpVariableSource::Post, 'artwork-notes'); return $artwork; } diff --git a/lib/Constants.php b/lib/Constants.php index 967119b2..92f3c682 100644 --- a/lib/Constants.php +++ b/lib/Constants.php @@ -54,31 +54,8 @@ define('EMAIL_SMTP_USERNAME', get_cfg_var('se.secrets.postmark.username')); const EMAIL_SMTP_HOST = 'smtp.postmarkapp.com'; const EMAIL_POSTMARK_STREAM_BROADCAST = 'the-standard-ebooks-newsletter'; -const REST = 0; -const WEB = 1; - -const GET = 'GET'; -const POST = 'POST'; -const COOKIE = 'COOKIE'; -const SESSION = 'SESSION'; - -const HTTP_VAR_INT = 0; -const HTTP_VAR_STR = 1; -const HTTP_VAR_BOOL = 2; -const HTTP_VAR_DEC = 3; -const HTTP_VAR_ARRAY = 4; - -const HTTP_GET = 0; -const HTTP_POST = 1; -const HTTP_PATCH = 2; -const HTTP_PUT = 3; -const HTTP_DELETE = 4; -const HTTP_HEAD = 5; - const AVERAGE_READING_WORDS_PER_MINUTE = 275; -const PAYMENT_CHANNEL_FA = 0; - const FA_FEE_PERCENT = 0.87; const GITHUB_IGNORED_REPOS = ['tools', 'manual', 'web']; // If we get GitHub push requests featuring these repos, silently ignore instead of returning an error. diff --git a/lib/Exceptions/HttpMethodNotAllowedException.php b/lib/Exceptions/HttpMethodNotAllowedException.php new file mode 100644 index 00000000..1ee1e704 --- /dev/null +++ b/lib/Exceptions/HttpMethodNotAllowedException.php @@ -0,0 +1,5 @@ + $allowedHttpMethods An array containing a list of allowed HTTP methods, or null if any valid HTTP method is allowed. + * @param bool $throwException If true, in case of errors throw an exception; if false, in case of errors output HTTP 405 and exit the script immediately. + * @throws Exceptions\InvalidHttpMethodException If the HTTP method is not recognized. + * @throws Exceptions\HttpMethodNotAllowedException If the HTTP method is not in the list of allowed methods. + */ + public static function ValidateRequestMethod(?array $allowedHttpMethods = null, bool $throwException = false): HttpMethod{ + try{ + $requestMethod = HttpMethod::from($_POST['_method'] ?? $_SERVER['REQUEST_METHOD']); + if($allowedHttpMethods !== null){ + $isRequestMethodAllowed = false; + foreach($allowedHttpMethods as $allowedHttpMethod){ + if($requestMethod == $allowedHttpMethod){ + $isRequestMethodAllowed = true; + } + } - switch($method){ - case 'POST': - return HTTP_POST; - case 'PUT': - return HTTP_PUT; - case 'DELETE': - return HTTP_DELETE; - case 'PATCH': - return HTTP_PATCH; - case 'HEAD': - return HTTP_HEAD; + if(!$isRequestMethodAllowed){ + throw new Exceptions\HttpMethodNotAllowedException(); + } + } + } + catch(\ValueError | Exceptions\HttpMethodNotAllowedException $ex){ + if($throwException){ + if($ex instanceof \ValueError){ + throw new Exceptions\InvalidHttpMethodException(); + } + else{ + throw $ex; + } + } + else{ + if($allowedHttpMethods !== null){ + header('Allow: ' . implode(',', array_map(fn($httpMethod): string => $httpMethod->value, $allowedHttpMethods))); + } + http_response_code(405); + exit(); + } } - return HTTP_GET; + return $requestMethod; } public static function GetMaxPostSize(): int{ // bytes @@ -45,12 +69,12 @@ class HttpInput{ return false; } - public static function RequestType(): int{ - return preg_match('/\btext\/html\b/ius', $_SERVER['HTTP_ACCEPT'] ?? '') ? WEB : REST; + public static function RequestType(): HttpRequestType{ + return preg_match('/\btext\/html\b/ius', $_SERVER['HTTP_ACCEPT'] ?? '') ? HttpRequestType::Web : HttpRequestType::Rest; } - public static function Str(string $type, string $variable, bool $allowEmptyString = false): ?string{ - $var = self::GetHttpVar($variable, HTTP_VAR_STR, $type); + public static function Str(HttpVariableSource $set, string $variable, bool $allowEmptyString = false): ?string{ + $var = self::GetHttpVar($variable, HttpVariableType::String, $set); if(is_array($var)){ return null; @@ -63,50 +87,50 @@ class HttpInput{ return $var; } - public static function Int(string $type, string $variable): ?int{ - return self::GetHttpVar($variable, HTTP_VAR_INT, $type); + public static function Int(HttpVariableSource $set, string $variable): ?int{ + return self::GetHttpVar($variable, HttpVariableType::Integer, $set); } - public static function Bool(string $type, string $variable): ?bool{ - return self::GetHttpVar($variable, HTTP_VAR_BOOL, $type); + public static function Bool(HttpVariableSource $set, string $variable): ?bool{ + return self::GetHttpVar($variable, HttpVariableType::Boolean, $set); } - public static function Dec(string $type, string $variable): ?float{ - return self::GetHttpVar($variable, HTTP_VAR_DEC, $type); + public static function Dec(HttpVariableSource $set, string $variable): ?float{ + return self::GetHttpVar($variable, HttpVariableType::Decimal, $set); } /** * @param string $variable * @return array */ - public static function GetArray(string $variable): ?array{ - return self::GetHttpVar($variable, HTTP_VAR_ARRAY, GET); + public static function Array(HttpVariableSource $set, string $variable): ?array{ + return self::GetHttpVar($variable, HttpVariableType::Array, $set); } - private static function GetHttpVar(string $variable, int $type, string $set): mixed{ + private static function GetHttpVar(string $variable, HttpVariableType $type, HttpVariableSource $set): mixed{ $vars = []; switch($set){ - case GET: + case HttpVariableSource::Get: $vars = $_GET; break; - case POST: + case HttpVariableSource::Post: $vars = $_POST; break; - case COOKIE: + case HttpVariableSource::Cookie: $vars = $_COOKIE; break; - case SESSION: + case HttpVariableSource::Session: $vars = $_SESSION; break; } if(isset($vars[$variable])){ - if($type == HTTP_VAR_ARRAY && is_array($vars[$variable])){ + if($type == HttpVariableType::Array && is_array($vars[$variable])){ // We asked for an array, and we got one return $vars[$variable]; } - elseif($type !== HTTP_VAR_ARRAY && is_array($vars[$variable])){ + elseif($type !== HttpVariableType::Array && is_array($vars[$variable])){ // We asked for not an array, but we got an array return null; } @@ -115,9 +139,9 @@ class HttpInput{ } switch($type){ - case HTTP_VAR_STR: + case HttpVariableType::String: return $var; - case HTTP_VAR_INT: + case HttpVariableType::Integer: // Can't use ctype_digit because we may want negative integers if(is_numeric($var) && mb_strpos($var, '.') === false){ try{ @@ -128,14 +152,14 @@ class HttpInput{ } } break; - case HTTP_VAR_BOOL: + case HttpVariableType::Boolean: if($var === '0' || strtolower($var) == 'false' || strtolower($var) == 'off'){ return false; } else{ return true; } - case HTTP_VAR_DEC: + case HttpVariableType::Decimal: if(is_numeric($var)){ try{ return floatval($var); diff --git a/lib/HttpMethod.php b/lib/HttpMethod.php new file mode 100644 index 00000000..f06e4b8a --- /dev/null +++ b/lib/HttpMethod.php @@ -0,0 +1,9 @@ +UserId, $this->Created, $this->ChannelId, $this->TransactionId, $this->Amount, $this->Fee, $this->IsRecurring, $this->IsMatchingDonation]); + ', [$this->UserId, $this->Created, $this->Processor, $this->TransactionId, $this->Amount, $this->Fee, $this->IsRecurring, $this->IsMatchingDonation]); } catch(Exceptions\DuplicateDatabaseKeyException){ throw new Exceptions\PaymentExistsException(); diff --git a/lib/PaymentProcessor.php b/lib/PaymentProcessor.php new file mode 100644 index 00000000..b74389d3 --- /dev/null +++ b/lib/PaymentProcessor.php @@ -0,0 +1,4 @@ +ChannelId == PAYMENT_CHANNEL_FA){ + $pendingPayment->Processor = PaymentProcessor::from($pendingPayment->Processor); + if($pendingPayment->Processor == PaymentProcessor::FracturedAtlas){ $log->Write('Processing donation ' . $pendingPayment->TransactionId . ' ...'); if(Db::QueryInt(' @@ -121,7 +122,7 @@ try{ $payment = new Payment(); $payment->User = new User(); - $payment->ChannelId = $pendingPayment->ChannelId; + $payment->Processor = $pendingPayment->Processor; try{ // If the donation is via a foundation (like American Online Giving Foundation) then there will be a 'soft credit' element. if(sizeof($detailsRow->findElements(WebDriverBy::xpath('//th[normalize-space(.) = "Soft Credit Donor Info"]'))) > 0){ diff --git a/www/artists/get.php b/www/artists/get.php index 5bb500f1..9e0ce16c 100644 --- a/www/artists/get.php +++ b/www/artists/get.php @@ -14,7 +14,7 @@ if($isSubmitterView){ } try{ - $artworks = Library::GetArtworksByArtist(HttpInput::Str(GET, 'artist-url-name'), $filterArtworkStatus, $submitterUserId); + $artworks = Library::GetArtworksByArtist(HttpInput::Str(HttpVariableSource::Get, 'artist-url-name'), $filterArtworkStatus, $submitterUserId); if(sizeof($artworks) == 0){ throw new Exceptions\ArtistNotFoundException(); diff --git a/www/artworks/edit.php b/www/artworks/edit.php index d99efa7f..bd6a53be 100644 --- a/www/artworks/edit.php +++ b/www/artworks/edit.php @@ -13,7 +13,7 @@ try{ } if($artwork === null){ - $artwork = Artwork::GetByUrl(HttpInput::Str(GET, 'artist-url-name'), HttpInput::Str(GET, 'artwork-url-name')); + $artwork = Artwork::GetByUrl(HttpInput::Str(HttpVariableSource::Get, 'artist-url-name'), HttpInput::Str(HttpVariableSource::Get, 'artwork-url-name')); } if(!$artwork->CanBeEditedBy($GLOBALS['User'])){ diff --git a/www/artworks/get.php b/www/artworks/get.php index bc2a4d9b..2e13a652 100644 --- a/www/artworks/get.php +++ b/www/artworks/get.php @@ -3,11 +3,11 @@ use function Safe\session_unset; session_start(); -$saved = HttpInput::Bool(SESSION, 'artwork-saved') ?? false; +$saved = HttpInput::Bool(HttpVariableSource::Session, 'artwork-saved') ?? false; $exception = $_SESSION['exception'] ?? null; try{ - $artwork = Artwork::GetByUrl(HttpInput::Str(GET, 'artist-url-name'), HttpInput::Str(GET, 'artwork-url-name')); + $artwork = Artwork::GetByUrl(HttpInput::Str(HttpVariableSource::Get, 'artist-url-name'), HttpInput::Str(HttpVariableSource::Get, 'artwork-url-name')); $isReviewerView = $GLOBALS['User']->Benefits->CanReviewArtwork ?? false; $isAdminView = $GLOBALS['User']->Benefits->CanReviewOwnArtwork ?? false; diff --git a/www/artworks/index.php b/www/artworks/index.php index fefeaa8f..1dfcc52d 100644 --- a/www/artworks/index.php +++ b/www/artworks/index.php @@ -1,11 +1,11 @@ Benefits->CanUploadArtwork){ throw new Exceptions\InvalidPermissionsException(); } @@ -56,8 +52,8 @@ try{ } // PUTing an artwork - if($httpMethod == HTTP_PUT){ - $originalArtwork = Artwork::GetByUrl(HttpInput::Str(GET, 'artist-url-name'), HttpInput::Str(GET, 'artwork-url-name')); + if($httpMethod == HttpMethod::Put){ + $originalArtwork = Artwork::GetByUrl(HttpInput::Str(HttpVariableSource::Get, 'artist-url-name'), HttpInput::Str(HttpVariableSource::Get, 'artwork-url-name')); if(!$originalArtwork->CanBeEditedBy($GLOBALS['User'])){ throw new Exceptions\InvalidPermissionsException(); @@ -71,7 +67,7 @@ try{ $artwork->SubmitterUserId = $originalArtwork->SubmitterUserId; $artwork->Status = $originalArtwork->Status; // Overwrite any value got from POST because we need permission to change the status - $newStatus = ArtworkStatus::tryFrom(HttpInput::Str(POST, 'artwork-status') ?? ''); + $newStatus = ArtworkStatus::tryFrom(HttpInput::Str(HttpVariableSource::Post, 'artwork-status') ?? ''); if($newStatus !== null){ if($originalArtwork->Status != $newStatus && !$originalArtwork->CanStatusBeChangedBy($GLOBALS['User'])){ throw new Exceptions\InvalidPermissionsException(); @@ -105,14 +101,14 @@ try{ } // PATCHing a new artwork - if($httpMethod == HTTP_PATCH){ - $artwork = Artwork::GetByUrl(HttpInput::Str(GET, 'artist-url-name'), HttpInput::Str(GET, 'artwork-url-name')); + if($httpMethod == HttpMethod::Patch){ + $artwork = Artwork::GetByUrl(HttpInput::Str(HttpVariableSource::Get, 'artist-url-name'), HttpInput::Str(HttpVariableSource::Get, 'artwork-url-name')); $exceptionRedirectUrl = $artwork->Url; // We can PATCH the status, the ebook www filesystem path, or both. if(isset($_POST['artwork-status'])){ - $newStatus = ArtworkStatus::tryFrom(HttpInput::Str(POST, 'artwork-status') ?? ''); + $newStatus = ArtworkStatus::tryFrom(HttpInput::Str(HttpVariableSource::Post, 'artwork-status') ?? ''); if($newStatus !== null){ if($artwork->Status != $newStatus && !$artwork->CanStatusBeChangedBy($GLOBALS['User'])){ throw new Exceptions\InvalidPermissionsException(); @@ -125,7 +121,7 @@ try{ } if(isset($_POST['artwork-ebook-url'])){ - $newEbookUrl = HttpInput::Str(POST, 'artwork-ebook-url'); + $newEbookUrl = HttpInput::Str(HttpVariableSource::Post, 'artwork-ebook-url'); if($artwork->EbookUrl != $newEbookUrl && !$artwork->CanEbookUrlBeChangedBy($GLOBALS['User'])){ throw new Exceptions\InvalidPermissionsException(); } @@ -142,9 +138,6 @@ try{ header('Location: ' . $artwork->Url); } } -catch(Exceptions\InvalidRequestException){ - http_response_code(405); -} catch(Exceptions\LoginRequiredException){ Template::RedirectToLogin(); } @@ -154,9 +147,9 @@ catch(Exceptions\InvalidPermissionsException){ catch(Exceptions\ArtworkNotFoundException){ Template::Emit404(); } -catch(Exceptions\AppException $exception){ +catch(Exceptions\InvalidArtworkException | Exceptions\InvalidArtworkTagException | Exceptions\InvalidArtistException | Exceptions\InvalidImageUploadException | Exceptions\ArtworkNotFoundException $ex){ $_SESSION['artwork'] = $artwork; - $_SESSION['exception'] = $exception; + $_SESSION['exception'] = $ex; http_response_code(303); header('Location: ' . $exceptionRedirectUrl); diff --git a/www/bulk-downloads/collection.php b/www/bulk-downloads/collection.php index 9749a34d..ccf1eb2e 100644 --- a/www/bulk-downloads/collection.php +++ b/www/bulk-downloads/collection.php @@ -3,7 +3,7 @@ use function Safe\apcu_fetch; use function Safe\preg_replace; $canDownload = false; -$class = HttpInput::Str(GET, 'class'); +$class = HttpInput::Str(HttpVariableSource::Get, 'class'); if($class === null || ($class != 'authors' && $class != 'collections' && $class != 'subjects' && $class != 'months')){ Template::Emit404(); diff --git a/www/bulk-downloads/download.php b/www/bulk-downloads/download.php index a75d545e..63041e0a 100644 --- a/www/bulk-downloads/download.php +++ b/www/bulk-downloads/download.php @@ -1,7 +1,7 @@ IsConfirmed){ $subscription->Confirm(); diff --git a/www/newsletter/subscriptions/delete.php b/www/newsletter/subscriptions/delete.php index a8cd7485..7ed22590 100644 --- a/www/newsletter/subscriptions/delete.php +++ b/www/newsletter/subscriptions/delete.php @@ -1,27 +1,19 @@ Delete(); - if($requestType == REST){ + if($requestType == HttpRequestType::Rest){ exit(); } } -catch(Exceptions\InvalidRequestException){ - http_response_code(405); - exit(); -} catch(Exceptions\NewsletterSubscriptionNotFoundException){ - if($requestType == WEB){ + if($requestType == HttpRequestType::Web){ Template::Emit404(); } else{ diff --git a/www/newsletter/subscriptions/get.php b/www/newsletter/subscriptions/get.php index 7290f523..f81e0c4c 100644 --- a/www/newsletter/subscriptions/get.php +++ b/www/newsletter/subscriptions/get.php @@ -13,7 +13,7 @@ try{ $created = true; } else{ - $subscription = NewsletterSubscription::Get(HttpInput::Str(GET, 'uuid')); + $subscription = NewsletterSubscription::Get(HttpInput::Str(HttpVariableSource::Get, 'uuid')); if(isset($_SESSION['subscription-created']) && $_SESSION['subscription-created'] == $subscription->UserId){ $created = true; diff --git a/www/newsletter/subscriptions/post.php b/www/newsletter/subscriptions/post.php index 1ad584b6..663cffc5 100644 --- a/www/newsletter/subscriptions/post.php +++ b/www/newsletter/subscriptions/post.php @@ -2,63 +2,61 @@ use Ramsey\Uuid\Uuid; use function Safe\session_unset; -if(HttpInput::RequestMethod() != HTTP_POST){ - http_response_code(405); - exit(); -} - -session_start(); - -$requestType = HttpInput::RequestType(); - -$subscription = new NewsletterSubscription(); - -if(HttpInput::Str(POST, 'automationtest')){ - // A bot filled out this form field, which should always be empty. Pretend like we succeeded. - if($requestType == WEB){ - http_response_code(303); - $uuid = Uuid::uuid4(); - $subscription->User = new User(); - $subscription->User->Uuid = $uuid->toString(); - $_SESSION['subscription-created'] = 0; // 0 means 'bot' - header('Location: /newsletter/subscriptions/success'); - } - else{ - // Access via REST api; 201 CREATED with location - http_response_code(201); - header('Location: /newsletter/subscriptions/success'); - } - - exit(); -} - try{ - $subscription->User = new User(); - $subscription->User->Email = HttpInput::Str(POST, 'email'); - $subscription->IsSubscribedToNewsletter = HttpInput::Bool(POST, 'issubscribedtonewsletter') ?? false; - $subscription->IsSubscribedToSummary = HttpInput::Bool(POST, 'issubscribedtosummary') ?? false; + HttpInput::ValidateRequestMethod([HttpMethod::Post]); - $expectedCaptcha = HttpInput::Str(SESSION, 'captcha') ?? ''; - $receivedCaptcha = HttpInput::Str(POST, 'captcha'); + session_start(); + + $requestType = HttpInput::RequestType(); + + $subscription = new NewsletterSubscription(); + + if(HttpInput::Str(HttpVariableSource::Post, 'automationtest')){ + // A bot filled out this form field, which should always be empty. Pretend like we succeeded. + if($requestType == HttpRequestType::Web){ + http_response_code(303); + $uuid = Uuid::uuid4(); + $subscription->User = new User(); + $subscription->User->Uuid = $uuid->toString(); + $_SESSION['subscription-created'] = 0; // 0 means 'bot' + header('Location: /newsletter/subscriptions/success'); + } + else{ + // Access via HttpRequestType::Rest api; 201 CREATED with location + http_response_code(201); + header('Location: /newsletter/subscriptions/success'); + } + + exit(); + } + + + $subscription->User = new User(); + $subscription->User->Email = HttpInput::Str(HttpVariableSource::Post, 'email'); + $subscription->IsSubscribedToNewsletter = HttpInput::Bool(HttpVariableSource::Post, 'issubscribedtonewsletter') ?? false; + $subscription->IsSubscribedToSummary = HttpInput::Bool(HttpVariableSource::Post, 'issubscribedtosummary') ?? false; + + $expectedCaptcha = HttpInput::Str(HttpVariableSource::Session, 'captcha') ?? ''; + $receivedCaptcha = HttpInput::Str(HttpVariableSource::Post, 'captcha'); $subscription->Create($expectedCaptcha, $receivedCaptcha); session_unset(); - if($requestType == WEB){ + if($requestType == HttpRequestType::Web){ http_response_code(303); $_SESSION['subscription-created'] = $subscription->UserId; header('Location: /newsletter/subscriptions/success'); } else{ - // Access via REST api; 201 CREATED with location + // Access via HttpRequestType::Rest api; 201 CREATED with location http_response_code(201); header('Location: /newsletter/subscriptions/success'); } } catch(Exceptions\NewsletterSubscriptionExistsException){ // Subscription exists. - if($requestType == WEB){ + if($requestType == HttpRequestType::Web){ // If we're accessing from the web, update the subscription, // re-sending the confirmation email if the user isn't yet confirmed $existingSubscription = NewsletterSubscription::Get($subscription->User->Uuid); @@ -79,12 +77,12 @@ catch(Exceptions\NewsletterSubscriptionExistsException){ } } else{ - // Access via REST api; 409 CONFLICT + // Access via HttpRequestType::Rest api; 409 CONFLICT http_response_code(409); } } catch(Exceptions\InvalidNewsletterSubscription $ex){ - if($requestType == WEB){ + if($requestType == HttpRequestType::Web){ $_SESSION['subscription'] = $subscription; $_SESSION['exception'] = $ex; @@ -93,7 +91,7 @@ catch(Exceptions\InvalidNewsletterSubscription $ex){ header('Location: /newsletter/subscriptions/new'); } else{ - // Access via REST api; 422 Unprocessable Entity + // Access via HttpRequestType::Rest api; 422 Unprocessable Entity http_response_code(422); } } diff --git a/www/polls/get.php b/www/polls/get.php index dafdf815..d1cee61e 100644 --- a/www/polls/get.php +++ b/www/polls/get.php @@ -5,7 +5,7 @@ $poll = new Poll(); $canVote = true; // Allow non-logged-in users to see the 'vote' button try{ - $poll = Poll::GetByUrlName(HttpInput::Str(GET, 'pollurlname')); + $poll = Poll::GetByUrlName(HttpInput::Str(HttpVariableSource::Get, 'pollurlname')); if(!$poll->IsActive() && $poll->End !== null && $poll->End < new DateTimeImmutable()){ // If the poll ended, redirect to the results diff --git a/www/polls/votes/get.php b/www/polls/votes/get.php index 19f3bf68..6a455662 100644 --- a/www/polls/votes/get.php +++ b/www/polls/votes/get.php @@ -7,7 +7,7 @@ $vote = new PollVote(); $created = false; try{ - $vote = PollVote::Get(HttpInput::Str(GET, 'pollurlname'), HttpInput::Int(GET, 'userid')); + $vote = PollVote::Get(HttpInput::Str(HttpVariableSource::Get, 'pollurlname'), HttpInput::Int(HttpVariableSource::Get, 'userid')); if(isset($_SESSION['vote-created']) && $_SESSION['vote-created'] == $vote->UserId){ $created = true; diff --git a/www/polls/votes/index.php b/www/polls/votes/index.php index 3e3ebee8..12f6a3a4 100644 --- a/www/polls/votes/index.php +++ b/www/polls/votes/index.php @@ -2,7 +2,7 @@ $poll = new Poll(); try{ - $poll = Poll::GetByUrlName(HttpInput::Str(GET, 'pollurlname')); + $poll = Poll::GetByUrlName(HttpInput::Str(HttpVariableSource::Get, 'pollurlname')); } catch(Exceptions\AppException){ Template::Emit404(); diff --git a/www/polls/votes/new.php b/www/polls/votes/new.php index 7d0300b6..f17bff4a 100644 --- a/www/polls/votes/new.php +++ b/www/polls/votes/new.php @@ -19,7 +19,7 @@ try{ $vote->User = $GLOBALS['User']; } - $poll = Poll::GetByUrlName(HttpInput::Str(GET, 'pollurlname')); + $poll = Poll::GetByUrlName(HttpInput::Str(HttpVariableSource::Get, 'pollurlname')); try{ $vote = PollVote::Get($poll->UrlName, $GLOBALS['User']->UserId); diff --git a/www/polls/votes/post.php b/www/polls/votes/post.php index cde6a48f..ab13af4b 100644 --- a/www/polls/votes/post.php +++ b/www/polls/votes/post.php @@ -1,46 +1,43 @@ PollItemId = HttpInput::Int(POST, 'pollitemid'); + HttpInput::ValidateRequestMethod([HttpMethod::Post]); - $vote->Create(HttpInput::Str(POST, 'email')); + session_start(); + + $requestType = HttpInput::RequestType(); + + $vote = new PollVote(); + + $vote->PollItemId = HttpInput::Int(HttpVariableSource::Post, 'pollitemid'); + + $vote->Create(HttpInput::Str(HttpVariableSource::Post, 'email')); session_unset(); - if($requestType == WEB){ + if($requestType == HttpRequestType::Web){ $_SESSION['vote-created'] = $vote->UserId; http_response_code(303); header('Location: ' . $vote->Url); } else{ - // Access via REST api; 201 CREATED with location + // Access via HttpRequestType::Rest api; 201 CREATED with location http_response_code(201); header('Location: ' . $vote->Url); } } catch(Exceptions\InvalidPollVoteException $ex){ - if($requestType == WEB){ + if($requestType == HttpRequestType::Web){ $_SESSION['vote'] = $vote; $_SESSION['exception'] = $ex; // Access via form; 303 redirect to the form, which will emit a 422 Unprocessable Entity http_response_code(303); - header('Location: /polls/' . (HttpInput::Str(GET, 'pollurlname') ?? '') . '/votes/new'); + header('Location: /polls/' . (HttpInput::Str(HttpVariableSource::Get, 'pollurlname') ?? '') . '/votes/new'); } else{ - // Access via REST api; 422 Unprocessable Entity + // Access via HttpRequestType::Rest api; 422 Unprocessable Entity http_response_code(422); } } diff --git a/www/sessions/new.php b/www/sessions/new.php index 6179e1ac..3c135970 100644 --- a/www/sessions/new.php +++ b/www/sessions/new.php @@ -8,8 +8,8 @@ if($GLOBALS['User'] !== null){ exit(); } -$email = HttpInput::Str(SESSION, 'email'); -$redirect = HttpInput::Str(SESSION, 'redirect') ?? HttpInput::Str(GET, 'redirect'); +$email = HttpInput::Str(HttpVariableSource::Session, 'email'); +$redirect = HttpInput::Str(HttpVariableSource::Session, 'redirect') ?? HttpInput::Str(HttpVariableSource::Get, 'redirect'); $exception = $_SESSION['exception'] ?? null; $passwordRequired = false; diff --git a/www/sessions/post.php b/www/sessions/post.php index f291d91a..e2ee4249 100644 --- a/www/sessions/post.php +++ b/www/sessions/post.php @@ -1,40 +1,38 @@ Create($email, $password); - if($requestType == WEB){ + session_unset(); + + if($requestType == HttpRequestType::Web){ http_response_code(303); header('Location: ' . $redirect); } else{ - // Access via REST api; 201 CREATED with location + // Access via HttpRequestType::Rest api; 201 CREATED with location http_response_code(201); header('Location: ' . $session->Url); } } -catch(Exceptions\AppException $ex){ - if($requestType == WEB){ +catch(Exceptions\InvalidLoginException | Exceptions\PasswordRequiredException $ex){ + if($requestType == HttpRequestType::Web){ $_SESSION['email'] = $email; $_SESSION['redirect'] = $redirect; $_SESSION['exception'] = $ex; @@ -44,7 +42,7 @@ catch(Exceptions\AppException $ex){ header('Location: /sessions/new'); } else{ - // Access via REST api; 422 Unprocessable Entity + // Access via HttpRequestType::Rest api; 422 Unprocessable Entity http_response_code(422); } } diff --git a/www/settings/post.php b/www/settings/post.php index 0f90cce1..0fa87b0f 100644 --- a/www/settings/post.php +++ b/www/settings/post.php @@ -1,8 +1,8 @@ strtotime('+1 month'), 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']); diff --git a/www/webhooks/github.php b/www/webhooks/github.php index 0b46dffc..3e693675 100644 --- a/www/webhooks/github.php +++ b/www/webhooks/github.php @@ -8,16 +8,13 @@ use function Safe\shell_exec; // This script makes various calls to external scripts using exec() (and when called via Apache, as the www-data user). // These scripts are allowed using the /etc/sudoers.d/www-data file. Only the specific scripts // in that file may be executed by this script. - -$log = new Log(GITHUB_WEBHOOK_LOG_FILE_PATH); -$lastPushHashFlag = ''; - try{ - $log->Write('Received GitHub webhook.'); + $log = new Log(GITHUB_WEBHOOK_LOG_FILE_PATH); + $lastPushHashFlag = ''; - if(HttpInput::RequestMethod() != HTTP_POST){ - throw new Exceptions\WebhookException('Expected HTTP POST.'); - } + HttpInput::ValidateRequestMethod([HttpMethod::Post]); + + $log->Write('Received GitHub webhook.'); $post = file_get_contents('php://input'); diff --git a/www/webhooks/postmark.php b/www/webhooks/postmark.php index 3a433c42..ee33b917 100644 --- a/www/webhooks/postmark.php +++ b/www/webhooks/postmark.php @@ -5,17 +5,14 @@ use function Safe\curl_setopt; use function Safe\file_get_contents; use function Safe\json_decode; -$log = new Log(POSTMARK_WEBHOOK_LOG_FILE_PATH); - try{ + $log = new Log(POSTMARK_WEBHOOK_LOG_FILE_PATH); /** @var string $smtpUsername */ $smtpUsername = get_cfg_var('se.secrets.postmark.username'); $log->Write('Received Postmark webhook.'); - if(HttpInput::RequestMethod() != HTTP_POST){ - throw new Exceptions\WebhookException('Expected HTTP POST.'); - } + HttpInput::ValidateRequestMethod([HttpMethod::Post]); $apiKey = get_cfg_var('se.secrets.postmark.api_key'); diff --git a/www/webhooks/zoho.php b/www/webhooks/zoho.php index b73eba1d..95af5015 100644 --- a/www/webhooks/zoho.php +++ b/www/webhooks/zoho.php @@ -1,22 +1,17 @@ Write('Received Zoho webhook.'); + $log = new Log(ZOHO_WEBHOOK_LOG_FILE_PATH); - if(HttpInput::RequestMethod() != HTTP_POST){ - throw new Exceptions\WebhookException('Expected HTTP POST.'); - } + HttpInput::ValidateRequestMethod([HttpMethod::Post]); + + $log->Write('Received Zoho webhook.'); $post = file_get_contents('php://input'); @@ -39,11 +34,11 @@ try{ $transactionId = $matches[1]; Db::Query(' - INSERT into PendingPayments (Created, ChannelId, TransactionId) + INSERT into PendingPayments (Created, Processor, TransactionId) values (utc_timestamp(), ?, ?) - ', [PAYMENT_CHANNEL_FA, $transactionId]); + ', [PaymentProcessor::FracturedAtlas, $transactionId]); $log->Write('Donation ID: ' . $transactionId); }