_prefix = $options['prefix']; } if (is_array($options) && array_key_exists('uniformacl', $options)) { $this->_uniformacl = $options['uniformacl']; } $this->_client = class_exists('StorageClientStub', false) ? new \StorageClientStub(array()) : new StorageClient(array('suppressKeyFileNotice' => true)); if (isset($bucket)) { $this->_bucket = $this->_client->bucket($bucket); } } /** * returns the google storage object key for $pasteid in $this->_bucket. * * @access private * @param $pasteid string to get the key for * @return string */ private function _getKey($pasteid) { if ($this->_prefix != '') { return $this->_prefix . '/' . $pasteid; } return $pasteid; } /** * Uploads the payload in the $this->_bucket under the specified key. * The entire payload is stored as a JSON document. The metadata is replicated * as the GCS object's metadata except for the fields attachment, attachmentname * and salt. * * @param $key string to store the payload under * @param $payload array to store * @return bool true if successful, otherwise false. */ private function _upload($key, $payload) { $metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array(); unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']); foreach ($metadata as $k => $v) { $metadata[$k] = strval($v); } try { $data = array( 'name' => $key, 'chunkSize' => 262144, 'metadata' => array( 'content-type' => 'application/json', 'metadata' => $metadata, ), ); if (!$this->_uniformacl) { $data['predefinedAcl'] = 'private'; } $this->_bucket->upload(Json::encode($payload), $data); } catch (Exception $e) { error_log('failed to upload ' . $key . ' to ' . $this->_bucket->name() . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); return false; } return true; } /** * @inheritDoc */ public function create($pasteid, array $paste) { if ($this->exists($pasteid)) { return false; } return $this->_upload($this->_getKey($pasteid), $paste); } /** * @inheritDoc */ public function read($pasteid) { try { $o = $this->_bucket->object($this->_getKey($pasteid)); $data = $o->downloadAsString(); return Json::decode($data); } catch (NotFoundException $e) { return false; } catch (Exception $e) { error_log('failed to read ' . $pasteid . ' from ' . $this->_bucket->name() . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); return false; } } /** * @inheritDoc */ public function delete($pasteid) { $name = $this->_getKey($pasteid); try { foreach ($this->_bucket->objects(array('prefix' => $name . '/discussion/')) as $comment) { try { $this->_bucket->object($comment->name())->delete(); } catch (NotFoundException $e) { // ignore if already deleted. } } } catch (NotFoundException $e) { // there are no discussions associated with the paste } try { $this->_bucket->object($name)->delete(); } catch (NotFoundException $e) { // ignore if already deleted } } /** * @inheritDoc */ public function exists($pasteid) { $o = $this->_bucket->object($this->_getKey($pasteid)); return $o->exists(); } /** * @inheritDoc */ public function createComment($pasteid, $parentid, $commentid, array $comment) { if ($this->existsComment($pasteid, $parentid, $commentid)) { return false; } $key = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid; return $this->_upload($key, $comment); } /** * @inheritDoc */ public function readComments($pasteid) { $comments = array(); $prefix = $this->_getKey($pasteid) . '/discussion/'; try { foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) { $comment = JSON::decode($this->_bucket->object($key->name())->downloadAsString()); $comment['id'] = basename($key->name()); $slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']); $comments[$slot] = $comment; } } catch (NotFoundException $e) { // no comments found } return $comments; } /** * @inheritDoc */ public function existsComment($pasteid, $parentid, $commentid) { $name = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid; $o = $this->_bucket->object($name); return $o->exists(); } /** * @inheritDoc */ public function purgeValues($namespace, $time) { $path = 'config/' . $namespace; try { foreach ($this->_bucket->objects(array('prefix' => $path)) as $object) { $name = $object->name(); if (strlen($name) > strlen($path) && substr($name, strlen($path), 1) !== '/') { continue; } $info = $object->info(); if (key_exists('metadata', $info) && key_exists('value', $info['metadata'])) { $value = $info['metadata']['value']; if (is_numeric($value) && intval($value) < $time) { try { $object->delete(); } catch (NotFoundException $e) { // deleted by another instance. } } } } } catch (NotFoundException $e) { // no objects in the bucket yet } } /** * For GoogleCloudStorage, the value will also be stored in the metadata for the * namespaces traffic_limiter and purge_limiter. * @inheritDoc */ public function setValue($value, $namespace, $key = '') { if ($key === '') { $key = 'config/' . $namespace; } else { $key = 'config/' . $namespace . '/' . $key; } $metadata = array('namespace' => $namespace); if ($namespace != 'salt') { $metadata['value'] = strval($value); } try { $data = array( 'name' => $key, 'chunkSize' => 262144, 'metadata' => array( 'content-type' => 'application/json', 'metadata' => $metadata, ), ); if (!$this->_uniformacl) { $data['predefinedAcl'] = 'private'; } $this->_bucket->upload($value, $data); } catch (Exception $e) { error_log('failed to set key ' . $key . ' to ' . $this->_bucket->name() . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); return false; } return true; } /** * @inheritDoc */ public function getValue($namespace, $key = '') { if ($key === '') { $key = 'config/' . $namespace; } else { $key = 'config/' . $namespace . '/' . $key; } try { $o = $this->_bucket->object($key); return $o->downloadAsString(); } catch (NotFoundException $e) { return ''; } } /** * @inheritDoc */ protected function _getExpiredPastes($batchsize) { $expired = array(); $now = time(); $prefix = $this->_prefix; if ($prefix != '') { $prefix .= '/'; } try { foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $object) { $metadata = $object->info()['metadata']; if ($metadata != null && array_key_exists('expire_date', $metadata)) { $expire_at = intval($metadata['expire_date']); if ($expire_at != 0 && $expire_at < $now) { array_push($expired, basename($object->name())); } } if (count($expired) > $batchsize) { break; } } } catch (NotFoundException $e) { // no objects in the bucket yet } return $expired; } /** * @inheritDoc */ public function getAllPastes() { $pastes = array(); $prefix = $this->_prefix; if ($prefix != '') { $prefix .= '/'; } try { foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $object) { $candidate = substr($object->name(), strlen($prefix)); if (strpos($candidate, '/') === false) { $pastes[] = $candidate; } } } catch (NotFoundException $e) { // no objects in the bucket yet } return $pastes; } }