**/ namespace Federator\Cache; /** * Caching class using redis */ class RedisCache implements Cache { /** * config data * * @var array $config */ private $config; /** * connection to redis open flag * * @var bool $connected */ private $connected = false; /** * connection handle * * @var \Redis $redis */ private $redis; /** * user cache time to live in secods * * @var int $userTTL */ private $userTTL; /** * public key cache time to live in secods * * @var int $publicKeyPemTTL */ private $publicKeyPemTTL; /** * constructor */ public function __construct() { $config = parse_ini_file('../rediscache.ini'); if ($config !== false) { $this->config = $config; $this->userTTL = array_key_exists('userttl', $config) ? intval($config['userttl'], 10) : 60; $this->publicKeyPemTTL = array_key_exists('publickeypemttl', $config) ? intval($config['publickeypemttl'], 10) : 3600; } } /** * connect to redis * @return void */ private function connect() { $this->redis = new \Redis(); $this->redis->pconnect($this->config['host'], intval($this->config['port'], 10)); // @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal $this->redis->auth([$this->config['username'], $this->config['password']]); // Set the Redis backend for Resque $redisUrl = sprintf( 'redis://%s:%s@%s:%d', urlencode($this->config['username']), urlencode($this->config['password']), $this->config['host'], intval($this->config['port'], 10) ); \Resque::setBackend($redisUrl); } /** * create key from session and user * * @param string $prefix prefix to create name spaces * @param string $input key name * @return string key */ private static function createKey($prefix, $input) { return $prefix . '_' . md5($input); } /** * get followers of given user * * @param string $id user id @unused-param * @return \Federator\Data\FedUser[]|false */ public function getFollowersByUser($id) { error_log("rediscache::getFollowersByUser not implemented"); return false; } /** * get following of given user * * @param string $id user id @unused-param * @return \Federator\Data\FedUser[]|false */ public function getFollowingByUser($id) { error_log("rediscache::getFollowingByUser not implemented"); return false; } /** * Convert jsonData to Activity format * * @param array $jsonData the json data from our platfrom @unused-param * @param string $articleId the original id of the article (if applicable) * (used to identify the article in the remote system) @unused-param * @return \Federator\Data\ActivityPub\Common\Activity|false */ public function jsonToActivity(array $jsonData, &$articleId) { error_log("rediscache::jsonToActivity not implemented"); return false; } /** * get posts by given user * * @param string $id user id @unused-param * @param int $min min timestamp @unused-param * @param int $max max timestamp @unused-param * @param int $limit limit results @unused-param * @return \Federator\Data\ActivityPub\Common\Activity[]|false */ public function getRemotePostsByUser($id, $min, $max, $limit) { error_log("rediscache::getRemotePostsByUser not implemented"); return false; } /** * get statistics from remote system * * @return \Federator\Data\Stats|false */ public function getRemoteStats() { if (!$this->connected) { $this->connect(); } $key = 'm_stats'; $data = $this->redis->get($key); if ($data === false) { return false; } $stats = \Federator\Data\Stats::createFromJson($data); return $stats; } /** * get remote user by given name * * @param string $_name user/profile name * @return \Federator\Data\User | false */ public function getRemoteUserByName(string $_name) { if (!$this->connected) { $this->connect(); } $key = self::createKey('u', $_name); $data = $this->redis->get($key); if ($data === false) { return false; } $user = \Federator\Data\User::createFromJson($data); return $user; } /** * get remote federation user by given name * * @param string $_name user/profile name * @return \Federator\Data\FedUser | false */ public function getRemoteFedUserByName(string $_name) { if (!$this->connected) { $this->connect(); } $key = self::createKey('u', $_name); $data = $this->redis->get($key); if ($data === false) { return false; } $user = \Federator\Data\FedUser::createFromJson($data); return $user; } /** * get remote user by given session * * @param string $_session session id * @param string $_user user/profile name * @return \Federator\Data\User | false */ public function getRemoteUserBySession($_session, $_user) { if (!$this->connected) { $this->connect(); } $key = self::createKey('s', $_session . $_user); $data = $this->redis->get($key); if ($data === false) { return false; } $user = \Federator\Data\User::createFromJson($data); return $user; } /** * Retrieve the public key for a given keyId * * @param string $keyId The keyId (e.g., actor URL + #main-key) * @return string|false The cached public key PEM or false if not found */ public function getPublicKey(string $keyId) { if (!$this->connected) { $this->connect(); } $key = self::createKey('publickey', $keyId); return $this->redis->get($key); } /** * save remote followers by user * * @param string $user user name @unused-param * @param \Federator\Data\FedUser[]|false $followers user followers @unused-param * @return void */ public function saveFollowersByUser($user, $followers) { error_log("rediscache::saveFollowersByUser not implemented"); } /** * save remote following for user * * @param string $user user name @unused-param * @param \Federator\Data\FedUser[]|false $following user following @unused-param * @return void */ public function saveFollowingByUser($user, $following) { error_log("rediscache::saveFollowingByUser not implemented"); } /** * save remote posts by user * * @param string $user user name @unused-param * @param int $min min timestamp @unused-param * @param int $max max timestamp @unused-param * @param int $limit limit results @unused-param * @param \Federator\Data\ActivityPub\Common\APObject[]|false $posts user posts @unused-param * @return void */ public function saveRemotePostsByUser($user, $min, $max, $limit, $posts) { error_log("rediscache::saveRemotePostsByUser not implemented"); } /** * save remote stats * * @param \Federator\Data\Stats $stats stats to save * @return void */ public function saveRemoteStats($stats) { if (!$this->connected) { $this->connect(); } $key = 'm_stats'; $serialized = $stats->toJson(); $this->redis->setEx($key, $this->config['statsttl'], $serialized); } /** * save remote user by name * * @param string $_name user/profile name * @param \Federator\Data\User $user user data * @return void */ public function saveRemoteUserByName($_name, $user) { if (!$this->connected) { $this->connect(); } $key = self::createKey('u', $_name); $serialized = $user->toJson(); $this->redis->setEx($key, $this->userTTL, $serialized); } /** * save remote federation user by given name * * @param string $_name user/profile name * @param \Federator\Data\FedUser $user user data * @return void */ public function saveRemoteFedUserByName(string $_name, \Federator\Data\FedUser $user) { $key = self::createKey('u', $_name); $serialized = $user->toJson(); $this->redis->setEx($key, $this->userTTL, $serialized); } /** * save remote user by given session * * @param string $_session session id * @param string $_user user/profile name * @param \Federator\Data\User $user user data * @return void */ public function saveRemoteUserBySession($_session, $_user, $user) { $key = self::createKey('s', $_session . $_user); $serialized = $user->toJson(); $this->redis->setEx($key, $this->userTTL, $serialized); } /** * Save the public key for a given keyId * * @param string $keyId The keyId (e.g., actor URL + #main-key) * @param string $publicKeyPem The public key PEM to cache * @return void */ public function savePublicKey(string $keyId, string $publicKeyPem) { if (!$this->connected) { $this->connect(); } $key = self::createKey('publickey', $keyId); $this->redis->setEx($key, $this->publicKeyPemTTL, $publicKeyPem); } /** * send target-friendly json from ActivityPub activity * * @param \Federator\Data\FedUser $sender the user of the sender @unused-param * @param \Federator\Data\ActivityPub\Common\Activity $activity the activity @unused-param * @return boolean did we successfully send the activity? */ public function sendActivity($sender, $activity) { return false; } /** * check if the headers include a valid signature * * @param string[] $headers the headers @unused-param * @throws \Federator\Exceptions\PermissionDenied * @return string|\Federator\Exceptions\PermissionDenied */ public function checkSignature($headers) { return new \Federator\Exceptions\PermissionDenied("RedisCache: no signature check"); } } namespace Federator; /** * Function to initialize plugin * * @param \Federator\Main $main main instance * @return void */ function rediscache_load($main) { $rc = new Cache\RedisCache(); $main->setCache($rc); }