/** * Right before the response is sent to the client, check if any HTTP cache control headers * have explicity been set for this response. If not, apply the configured defaults. * * @param EventInterface $event The event instance */ public function setHeaders(EventInterface $event) { $method = $event->getRequest()->getMethod(); // Obviously we shouldn't bother doing any HTTP caching logic for non-GET/HEAD requests if ($method !== 'GET' && $method !== 'HEAD') { return; } $response = $event->getResponse(); $headers = $event->getResponse()->headers; // Imbo defaults to 'public' as cache-control value - if it has changed from this value, // assume the resource requested has explicitly defined its own caching rules and fall back if ($headers->get('Cache-Control') !== 'public') { return; } // Get configured HTTP cache defaults from configuration, then apply them $config = $event->getConfig()['httpCacheHeaders']; if (isset($config['maxAge'])) { $response->setMaxAge((int) $config['maxAge']); } if (isset($config['sharedMaxAge'])) { $response->setSharedMaxAge($config['sharedMaxAge']); } if (isset($config['public']) && $config['public']) { $response->setPublic(); } else { if (isset($config['public'])) { $response->setPrivate(); } } if (isset($config['mustRevalidate']) && $config['mustRevalidate']) { $headers->addCacheControlDirective('must-revalidate'); } }
/** * Add resources to a group * * @param EventInterface $event The current event */ public function addGroup(EventInterface $event) { $accessControl = $event->getAccessControl(); if (!$accessControl instanceof MutableAdapterInterface) { throw new ResourceException('Access control adapter is immutable', 405); } $request = $event->getRequest(); $route = $request->getRoute(); $groupName = $route->get('group'); $group = $accessControl->getGroup($groupName); $groupExists = !empty($group); $resources = json_decode($request->getContent(), true); if (!is_array($resources)) { throw new ResourceException('Invalid data. Array of resource strings is expected', 400); } foreach ($resources as $resource) { if (!is_string($resource)) { throw new ResourceException('Invalid value in the resources array. Only strings are allowed', 400); } } if ($groupExists) { $accessControl->updateResourceGroup($groupName, $resources); } else { $accessControl->addResourceGroup($groupName, $resources); } $response = $event->getResponse(); $response->setStatusCode($groupExists ? 200 : 201); }
/** * Handle GET requests * * @param EventInterface $event The current event */ public function get(EventInterface $event) { $response = $event->getResponse(); $response->setMaxAge(0)->setPrivate(); $response->headers->addCacheControlDirective('no-store'); $event->getManager()->trigger('db.stats.load'); }
/** * Transform images * * @param EventInterface $event The current event */ public function transform(EventInterface $event) { $request = $event->getRequest(); $image = $event->getResponse()->getModel(); $eventManager = $event->getManager(); $presets = $event->getConfig()['transformationPresets']; // Fetch transformations specifed in the query and transform the image foreach ($request->getTransformations() as $transformation) { if (isset($presets[$transformation['name']])) { // Preset foreach ($presets[$transformation['name']] as $name => $params) { if (is_int($name)) { // No hardcoded params, use the ones from the request $name = $params; $params = $transformation['params']; } else { // Some hardcoded params. Merge with the ones from the request, making the // hardcoded params overwrite the ones from the request $params = array_replace($transformation['params'], $params); } $eventManager->trigger('image.transformation.' . strtolower($name), array('image' => $image, 'params' => $params)); } } else { // Regular transformation $eventManager->trigger('image.transformation.' . strtolower($transformation['name']), array('image' => $image, 'params' => $transformation['params'])); } } }
/** * Handle GET requests * * @param EventInterface $event The current event */ public function get(EventInterface $event) { $response = $event->getResponse(); $database = $event->getDatabase(); $storage = $event->getStorage(); $databaseStatus = $database->getStatus(); $storageStatus = $storage->getStatus(); if (!$databaseStatus || !$storageStatus) { if (!$databaseStatus && !$storageStatus) { $message = 'Database and storage error'; } else { if (!$storageStatus) { $message = 'Storage error'; } else { $message = 'Database error'; } } $response->setStatusCode(503, $message); } $response->setMaxAge(0)->setPrivate(); $response->headers->addCacheControlDirective('no-store'); $statusModel = new Model\Status(); $statusModel->setDate(new DateTime('now', new DateTimeZone('UTC')))->setDatabaseStatus($databaseStatus)->setStorageStatus($storageStatus); $response->setModel($statusModel); }
/** * Send the response * * @param EventInterface $event The current event */ public function send(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); // Vary on public key header. Public key specified in query and URL path doesn't have to be // taken into consideration, since they will have varying URLs $response->setVary('X-Imbo-PublicKey', false); // Optionally mark this response as not modified $response->isNotModified($request); // Inject a possible image identifier into the response headers $imageIdentifier = null; if ($image = $request->getImage()) { // The request has an image. This means that an image was just added. // Get the image identifier from the image model $imageIdentifier = $image->getImageIdentifier(); } else { if ($identifier = $request->getImageIdentifier()) { // An image identifier exists in the request URI, use that $imageIdentifier = $identifier; } } if ($imageIdentifier) { $response->headers->set('X-Imbo-ImageIdentifier', $imageIdentifier); } $response->send(); }
/** * {@inheritdoc} */ public function checkAccessToken(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $query = $request->query; $eventName = $event->getName(); if (($eventName === 'image.get' || $eventName === 'image.head') && $this->isWhitelisted($request)) { // All transformations in the request are whitelisted. Skip the access token check return; } // If the response has a short URL header, we can skip the access token check if ($response->headers->has('X-Imbo-ShortUrl')) { return; } if (!$query->has('accessToken')) { throw new RuntimeException('Missing access token', 400); } $token = $query->get('accessToken'); // First the the raw un-encoded URI, then the URI as is $uris = array($request->getRawUri(), $request->getUriAsIs()); $privateKeys = $event->getUserLookup()->getPrivateKeys($request->getPublicKey()) ?: []; foreach ($uris as $uri) { // Remove the access token from the query string as it's not used to generate the HMAC $uri = rtrim(preg_replace('/(?<=(\\?|&))accessToken=[^&]+&?/', '', $uri), '&?'); foreach ($privateKeys as $privateKey) { $correctToken = hash_hmac('sha256', $uri, $privateKey); if ($correctToken === $token) { return; } } } throw new RuntimeException('Incorrect access token', 400); }
/** * Choose an image variation based on the transformations and the original size of the image * * @param EventInterface $event The current event */ public function chooseVariation(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $publicKey = $request->getPublicKey(); $imageIdentifier = $request->getImageIdentifier(); // Fetch the original width / height of the image to use for ratio calculations $image = $response->getModel(); $imageWidth = $image->getWidth(); $imageHeight = $image->getHeight(); // Fetch the transformations from the request and find the max width used in the set $transformations = $request->getTransformations(); if (!$transformations) { // No transformations in the request return; } $maxWidth = $this->getMaxWidth($imageWidth, $imageHeight, $transformations); if (!$maxWidth) { // No need to use a variation based on the set of transformations return; } // Fetch the index of the transformation that decided the max width, and the width itself list($transformationIndex, $maxWidth) = each($maxWidth); if ($maxWidth >= $imageWidth) { // The width is the same or above the original, use the original return; } // WE HAVE A WINNER! Find the best variation. The width of the variation is the first // available one above the $maxWidth value $variation = $this->database->getBestMatch($publicKey, $imageIdentifier, $maxWidth); if (!$variation) { // Could not find any :( return; } // Now that we have a variation we can use we need to adjust some of the transformation // parameters. $event->getManager()->trigger('image.transformations.adjust', ['transformationIndex' => $transformationIndex, 'ratio' => $imageWidth / $variation['width']]); // Fetch the image variation blob from the storage adapter $imageBlob = $this->storage->getImageVariation($publicKey, $imageIdentifier, $variation['width']); if (!$imageBlob) { // The image blob does not exist in the storage, which it should. Trigger an error and // return trigger_error('Image variation storage is not in sync with the image variation database', E_USER_WARNING); return; } // Set some data that the storage operations listener usually sets, since that will be // skipped since we have an image variation $lastModified = $event->getStorage()->getLastModified($publicKey, $imageIdentifier); $response->setLastModified($lastModified); // Update the model $model = $response->getModel(); $model->setBlob($imageBlob)->setWidth($variation['width'])->setHeight($variation['height']); // Set a HTTP header that informs the user agent on which image variation that was used in // the transformations $response->headers->set('X-Imbo-ImageVariation', $variation['width'] . 'x' . $variation['height']); // Stop the propagation of this event $event->stopPropagation(); $event->getManager()->trigger('image.loaded'); }
/** * Handle POST requests * * @param EventInterface $event The current event */ public function post(EventInterface $event) { $request = $event->getRequest(); $event->getManager()->trigger('db.metadata.update', ['metadata' => json_decode($request->getContent(), true)]); $model = new Model\Metadata(); $model->setData($event->getDatabase()->getMetadata($request->getUser(), $request->getImageIdentifier())); $event->getResponse()->setModel($model); }
/** * Add the HashTwo header to the response * * @param EventInterface $event The current event */ public function addHeader(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $user = $request->getUser(); $imageIdentifier = $response->getModel()->getImageIdentifier(); $response->headers->set($this->header, ['imbo;image;' . $user . ';' . $imageIdentifier, 'imbo;user;' . $user]); }
/** * Add the HashTwo header to the response * * @param EventInterface $event The current event */ public function addHeader(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $publicKey = $request->getPublicKey(); $imageIdentifier = $response->getModel()->getImageIdentifier(); $response->headers->set($this->header, array('imbo;image;' . $publicKey . ';' . $imageIdentifier, 'imbo;user;' . $publicKey)); }
public function get(EventInterface $event) { $model = new ListModel(); $model->setContainer('foo'); $model->setEntry('bar'); $model->setList([1, 2, 3]); $event->getResponse()->setModel($model); }
/** * Handle POST requests * * @param EventInterface */ public function addImage(EventInterface $event) { $event->getManager()->trigger('db.image.insert'); $event->getManager()->trigger('storage.image.insert'); $request = $event->getRequest(); $response = $event->getResponse(); $image = $request->getImage(); $model = new Model\ArrayModel(); $model->setData(['imageIdentifier' => $image->getImageIdentifier(), 'width' => $image->getWidth(), 'height' => $image->getHeight(), 'extension' => $image->getExtension()]); $response->setModel($model); }
/** * Handle GET requests * * @param EventInterface $event The current event */ public function get(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $response->setStatusCode(200, 'Hell Yeah'); $baseUrl = $request->getSchemeAndHttpHost() . $request->getBaseUrl(); $model = new Model\ArrayModel(); $model->setData(array('version' => Version::VERSION, 'urls' => array('site' => 'http://www.imbo-project.org', 'source' => 'https://github.com/imbo/imbo', 'issues' => 'https://github.com/imbo/imbo/issues', 'docs' => 'http://docs.imbo-project.org'), 'endpoints' => array('status' => $baseUrl . '/status', 'stats' => $baseUrl . '/stats', 'user' => $baseUrl . '/users/{publicKey}', 'images' => $baseUrl . '/users/{publicKey}/images', 'image' => $baseUrl . '/users/{publicKey}/images/{imageIdentifier}', 'globalShortImageUrl' => $baseUrl . '/s/{id}', 'metadata' => $baseUrl . '/users/{publicKey}/images/{imageIdentifier}/metadata', 'shortImageUrls' => $baseUrl . '/users/{publicKey}/images/{imageIdentifier}/shorturls', 'shortImageUrl' => $baseUrl . '/users/{publicKey}/images/{imageIdentifier}/shorturls/{id}'))); $response->setModel($model); // Prevent caching $response->setMaxAge(0)->setPrivate(); $response->headers->addCacheControlDirective('no-store'); }
/** * {@inheritdoc} */ public function checkAccessToken(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $query = $request->query; $eventName = $event->getName(); $config = $event->getConfig(); if (($eventName === 'image.get' || $eventName === 'image.head') && $this->isWhitelisted($request)) { // All transformations in the request are whitelisted. Skip the access token check return; } // If the response has a short URL header, we can skip the access token check if ($response->headers->has('X-Imbo-ShortUrl')) { return; } if (!$query->has('accessToken')) { throw new RuntimeException('Missing access token', 400); } $token = $query->get('accessToken'); // First the the raw un-encoded URI, then the URI as is $uris = [$request->getRawUri(), $request->getUriAsIs()]; $privateKey = $event->getAccessControl()->getPrivateKey($request->getPublicKey()); // append uris with [] expanded or [0] reduced $uris[] = $this->getUnescapedAlternativeURL($request->getRawUri()); $uris[] = $this->getEscapedAlternativeURL($request->getRawUri()); // See if we should modify the protocol for the incoming request $protocol = $config['authentication']['protocol']; if ($protocol === 'both') { $uris = array_reduce($uris, function ($dest, $uri) { $baseUrl = preg_replace('#^https?#', '', $uri); $dest[] = 'http' . $baseUrl; $dest[] = 'https' . $baseUrl; return $dest; }, []); } else { if (in_array($protocol, ['http', 'https'])) { $uris = array_map(function ($uri) use($protocol) { return preg_replace('#^https?#', $protocol, $uri); }, $uris); } } foreach ($uris as $uri) { // Remove the access token from the query string as it's not used to generate the HMAC $uri = rtrim(preg_replace('/(?<=(\\?|&))accessToken=[^&]+&?/', '', $uri), '&?'); $correctToken = hash_hmac('sha256', $uri, $privateKey); if ($correctToken === $token) { return; } } throw new RuntimeException('Incorrect access token', 400); }
/** * {@inheritdoc} */ public function authenticate(EventInterface $event) { $response = $event->getResponse(); $request = $event->getRequest(); // Whether or not the authentication info is in the request headers $fromHeaders = $request->headers->has('x-imbo-authenticate-timestamp') && $request->headers->has('x-imbo-authenticate-signature'); // Fetch timestamp header, fallback to query param $timestamp = $request->headers->get('x-imbo-authenticate-timestamp', $request->query->get('timestamp')); if (!$timestamp) { $exception = new RuntimeException('Missing authentication timestamp', 400); $exception->setImboErrorCode(Exception::AUTH_MISSING_PARAM); } else { if (!$this->timestampIsValid($timestamp)) { $exception = new RuntimeException('Invalid timestamp: ' . $timestamp, 400); $exception->setImboErrorCode(Exception::AUTH_INVALID_TIMESTAMP); } else { if ($this->timestampHasExpired($timestamp)) { $exception = new RuntimeException('Timestamp has expired: ' . $timestamp, 400); $exception->setImboErrorCode(Exception::AUTH_TIMESTAMP_EXPIRED); } } } if (isset($exception)) { throw $exception; } // Fetch signature header, fallback to query param $signature = $request->headers->get('x-imbo-authenticate-signature', $request->query->get('signature')); if (!$signature) { $exception = new RuntimeException('Missing authentication signature', 400); $exception->setImboErrorCode(Exception::AUTH_MISSING_PARAM); } if (isset($exception)) { throw $exception; } $publicKey = $request->getPublicKey(); $privateKeys = $event->getUserLookup()->getPrivateKeys($publicKey, UserLookupInterface::MODE_READ_WRITE) ?: []; $url = $request->getRawUri(); if (!$fromHeaders) { // Remove the signature and timestamp from the query parameters as they are not used // when generating the HMAC $url = rtrim(preg_replace('/(?<=(\\?|&))(signature|timestamp)=[^&]+&?/', '', $url), '&?'); } // Add the URL used for auth to the response headers $response->headers->set('X-Imbo-AuthUrl', $url); if (!$this->signatureIsValid($request->getMethod(), $url, $publicKey, $privateKeys, $timestamp, $signature)) { $exception = new RuntimeException('Signature mismatch', 400); $exception->setImboErrorCode(Exception::AUTH_SIGNATURE_MISMATCH); throw $exception; } }
/** * Store metadata in the cache * * @param EventInterface $event The event instance */ public function storeInCache(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $cacheKey = $this->getCacheKey($request->getPublicKey(), $request->getImageIdentifier()); // Store the response in the cache for later use if ($response->getStatusCode() === 200) { $metadata = array(); if ($model = $response->getModel()) { $metadata = $model->getData(); } $this->cache->set($cacheKey, array('lastModified' => $response->getLastModified(), 'metadata' => $metadata)); } }
/** * Set the correct ETag for the response * * @param EventInterface $event The current event */ public function setETag(EventInterface $event) { $response = $event->getResponse(); $request = $event->getRequest(); $routesWithETags = ['user' => true, 'images' => true, 'image' => true, 'metadata' => true, 'globalshorturl' => true]; $currentRoute = (string) $request->getRoute(); if (!isset($routesWithETags[$currentRoute])) { // The current route does not use ETags return; } if ($response->isOk()) { $response->setETag('"' . md5($response->getContent()) . '"'); } }
/** * Fetch an image via a short URL * * @param EventInterface $event */ public function getImage(EventInterface $event) { $request = $event->getRequest(); $route = $request->getRoute(); $params = $event->getDatabase()->getShortUrlParams($route->get('shortUrlId')); if (!$params) { throw new ResourceException('Image not found', 404); } $route->set('publicKey', $params['publicKey']); $route->set('imageIdentifier', $params['imageIdentifier']); $route->set('extension', $params['extension']); $request->query = new ParameterBag($params['query']); $event->getResponse()->headers->set('X-Imbo-ShortUrl', $request->getUri()); $event->getManager()->trigger('image.get'); }
/** * Insert an image * * @param EventInterface $event An event instance */ public function insertImage(EventInterface $event) { $request = $event->getRequest(); $user = $request->getUser(); $image = $request->getImage(); $imageIdentifier = $image->getImageIdentifier(); $blob = $image->getBlob(); try { $exists = $event->getStorage()->imageExists($user, $imageIdentifier); $event->getStorage()->store($user, $imageIdentifier, $blob); } catch (StorageException $e) { $event->getDatabase()->deleteImage($user, $imageIdentifier); throw $e; } $event->getResponse()->setStatusCode($exists ? 200 : 201); }
/** * Inject the image blob from the image model into the shared imagick instance * * @param EventInterface $event The event instance */ public function readImageBlob(EventInterface $event) { if ($event->hasArgument('image')) { // The image has been specified as an argument to the event $image = $event->getArgument('image'); } else { if ($event->getName() === 'images.post') { // The image is found in the request $image = $event->getRequest()->getImage(); } else { // The image is found in the response $image = $event->getResponse()->getModel(); } } // Inject the image blob $this->imagick->readImageBlob($image->getBlob()); }
/** * Delete a single short URL * * @param EventInterface $event */ public function deleteShortUrl(EventInterface $event) { $database = $event->getDatabase(); $request = $event->getRequest(); $publicKey = $request->getPublicKey(); $imageIdentifier = $request->getImageIdentifier(); $shortUrlId = $request->getRoute()->get('shortUrlId'); if (!($params = $database->getShortUrlParams($shortUrlId))) { throw new ResourceException('ShortURL not found', 404); } if ($params['publicKey'] !== $publicKey || $params['imageIdentifier'] !== $imageIdentifier) { throw new ResourceException('ShortURL not found', 404); } $database->deleteShortUrls($publicKey, $imageIdentifier, $shortUrlId); $model = new ArrayModel(); $model->setData(array('id' => $shortUrlId)); $event->getResponse()->setModel($model); }
/** * Handle GET and HEAD requests * * @param EventInterface */ public function getImage(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $eventManager = $event->getManager(); $publicKey = $request->getPublicKey(); $imageIdentifier = $request->getImageIdentifier(); $image = new Model\Image(); $image->setImageIdentifier($imageIdentifier)->setPublicKey($publicKey); $response->setModel($image); // Load image details from database $eventManager->trigger('db.image.load'); // Set a long max age as the image itself won't change $response->setMaxAge(31536000); // Custom Imbo headers, based on original $response->headers->add(['X-Imbo-OriginalMimeType' => $image->getMimeType(), 'X-Imbo-OriginalWidth' => $image->getWidth(), 'X-Imbo-OriginalHeight' => $image->getHeight(), 'X-Imbo-OriginalFileSize' => $image->getFilesize(), 'X-Imbo-OriginalExtension' => $image->getExtension()]); // Trigger loading of the image $eventManager->trigger('storage.image.load'); // Trigger possible image transformations $eventManager->trigger('image.transform'); }
/** * Add or update public key * * @param EventInterface $event The current event */ public function setKey(EventInterface $event) { $acl = $event->getAccessControl(); if (!$acl instanceof MutableAdapterInterface) { throw new ResourceException('Access control adapter is immutable', 405); } $request = $event->getRequest(); $data = json_decode($request->getContent(), true); if (!isset($data['privateKey'])) { throw new InvalidArgumentException('No privateKey provided', 400); } $publicKey = $request->getRoute()->get('publickey'); $privateKey = $data['privateKey']; $keyExists = $acl->publicKeyExists($publicKey); if ($keyExists) { $acl->updatePrivateKey($publicKey, $privateKey); } else { $acl->addKeyPair($publicKey, $privateKey); } $event->getResponse()->setStatusCode($keyExists ? 200 : 201); }
/** * Get access rules for the specified public key * * @param EventInterface $event The current event */ public function getRules(EventInterface $event) { $request = $event->getRequest(); $publicKey = $request->getRoute()->get('publickey'); $accessControl = $event->getAccessControl(); $keyExists = $accessControl->publicKeyExists($publicKey); if (!$keyExists) { throw new RuntimeException('Public key not found', 404); } $accessList = $accessControl->getAccessListForPublicKey($publicKey); if ($request->query->has('expandGroups')) { foreach ($accessList as &$rule) { if (!isset($rule['group'])) { continue; } $rule['resources'] = $accessControl->getGroup($rule['group']); } } $model = new AccessRulesModel(); $model->setRules($accessList); $event->getResponse()->setModel($model); }
/** * Get an access control rule specified by ID * * @param EventInterface $event The current event */ public function getRule(EventInterface $event) { $acl = $event->getAccessControl(); $request = $event->getRequest(); $publicKey = $request->getRoute()->get('publickey'); $accessRuleId = $request->getRoute()->get('accessRuleId'); $keyExists = $acl->publicKeyExists($publicKey); if (!$keyExists) { throw new RuntimeException('Public key not found', 404); } $accessRule = $acl->getAccessRule($publicKey, $accessRuleId); if (!$accessRule) { throw new RuntimeException('Access rule not found', 404); } $model = new AccessRuleModel(); $model->setId($accessRule['id'])->setUsers($accessRule['users']); if (isset($accessRule['group'])) { $model->setGroup($accessRule['group']); } if (isset($accessRule['resources'])) { $model->setResources($accessRule['resources']); } $event->getResponse()->setModel($model); }
/** * Response send hook * * @param EventInterface $event The current event */ public function format(EventInterface $event) { $response = $event->getResponse(); $model = $response->getModel(); if ($response->getStatusCode() === 204 || !$model) { // No content to write return; } $request = $event->getRequest(); // If we are dealing with an image we want to trigger an event that handles a possible // conversion if ($model instanceof Model\Image) { $eventManager = $event->getManager(); if ($this->extensionsToMimeType[$this->formatter] !== $model->getMimeType()) { $eventManager->trigger('image.transformation.convert', array('image' => $model, 'params' => array('type' => $this->formatter))); } // Finished transforming the image $eventManager->trigger('image.transformed', array('image' => $model)); $formattedData = $model->getBlob(); $contentType = $model->getMimeType(); } else { // Create an instance of the formatter $formatter = $this->formatters[$this->formatter]; $formattedData = $formatter->format($model); $contentType = $formatter->getContentType(); } if ($contentType === 'application/json') { foreach (array('callback', 'jsonp', 'json') as $validParam) { if ($request->query->has($validParam)) { $formattedData = sprintf("%s(%s)", $request->query->get($validParam), $formattedData); break; } } } $response->headers->add(array('Content-Type' => $contentType, 'Content-Length' => strlen($formattedData))); if ($request->getMethod() !== 'HEAD') { $response->setContent($formattedData); } }
/** * Load stats * * @param EventInterface $event An event instance */ public function loadStats(EventInterface $event) { $response = $event->getResponse(); $database = $event->getDatabase(); $userLookup = $event->getUserLookup(); $publicKeys = $userLookup->getPublicKeys(); $users = array(); foreach ($publicKeys as $key) { $users[$key] = array('numImages' => $database->getNumImages($key), 'numBytes' => $database->getNumBytes($key)); } $statsModel = new Model\Stats(); $statsModel->setUsers($users); $response->setModel($statsModel); }
/** * Using the configured image identifier generator, attempt to generate a unique image * identifier for the given image until we either have found a unique ID or we hit the maximum * allowed attempts. * * @param EventInterface $event The current event * @param Image $image The event to generate the image identifier for * @return string * @throws ImageException */ private function generateImageIdentifier(EventInterface $event, Image $image) { $database = $event->getDatabase(); $config = $event->getConfig(); $user = $event->getRequest()->getUser(); $imageIdentifierGenerator = $config['imageIdentifierGenerator']; if (is_callable($imageIdentifierGenerator) && !$imageIdentifierGenerator instanceof GeneratorInterface) { $imageIdentifierGenerator = $imageIdentifierGenerator(); } if ($imageIdentifierGenerator->isDeterministic()) { return $imageIdentifierGenerator->generate($image); } // Continue generating image identifiers until we get one that does not already exist $maxAttempts = 100; $attempts = 0; do { $imageIdentifier = $imageIdentifierGenerator->generate($image); $attempts++; } while ($attempts < $maxAttempts && $database->imageExists($user, $imageIdentifier)); // Did we reach our max attempts limit? if ($attempts === $maxAttempts) { $e = new ImageException('Failed to generate unique image identifier', 503); $e->setImboErrorCode(Exception::IMAGE_IDENTIFIER_GENERATION_FAILED); // Tell the client it's OK to retry later $event->getResponse()->headers->set('Retry-After', 1); throw $e; } return $imageIdentifier; }
/** * Update image data * * @param Imbo\EventListener\ListenerInterface $event The current event */ public function set(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $imageIdentifier = $request->getImageIdentifier(); // The imageIdentifier was not part of the URL if (!$imageIdentifier) { $responseData = $response->getModel()->getData(); if (!isset($responseData['imageIdentifier'])) { return; } $imageIdentifier = $responseData['imageIdentifier']; } // Get image information $image = $this->getImageData($event, $imageIdentifier); // Pass image data to the search backend $this->backend->set($request->getUser(), $imageIdentifier, ['user' => $request->getUser(), 'size' => $image->getFilesize(), 'extension' => $image->getExtension(), 'mime' => $image->getMimeType(), 'metadata' => $image->getMetadata(), 'added' => $image->getAddedDate()->getTimestamp(), 'updated' => $image->getUpdatedDate()->getTimestamp(), 'width' => $image->getWidth(), 'height' => $image->getHeight()]); }