X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=plugins%2FOStatus%2Flib%2Fsalmon.php;h=2f5772a84497eac45d0cf86cdb73faea50899301;hb=1d15145993a00d1db1057dacf71f3783cd16c119;hp=b5f178cc6a2e4cec2f191dad33d96ed39aa9a4ad;hpb=59be4b8cae992476628b23c2976d335e4f704c89;p=quix0rs-gnu-social.git diff --git a/plugins/OStatus/lib/salmon.php b/plugins/OStatus/lib/salmon.php index b5f178cc6a..2f5772a844 100644 --- a/plugins/OStatus/lib/salmon.php +++ b/plugins/OStatus/lib/salmon.php @@ -28,62 +28,138 @@ */ class Salmon { + const REL_SALMON = 'salmon'; + const REL_MENTIONED = 'mentioned'; + + // XXX: these are deprecated + const NS_REPLIES = "http://salmon-protocol.org/ns/salmon-replies"; + const NS_MENTIONS = "http://salmon-protocol.org/ns/salmon-mention"; + /** * Sign and post the given Atom entry as a Salmon message. * - * @fixme pass through the actor for signing? + * Side effects: may generate a keypair on-demand for the given user, + * which can be very slow on some systems. * * @param string $endpoint_uri - * @param string $xml + * @param string $xml string representation of payload + * @param Profile $actor local user profile whose keys to sign with * @return boolean success */ - public function post($endpoint_uri, $xml) + public function post($endpoint_uri, $xml, $actor) { if (empty($endpoint_uri)) { return false; } - if (!common_config('ostatus', 'skip_signatures')) { - $xml = $this->createMagicEnv($xml); - } - - $headers = array('Content-Type: application/atom+xml'); + foreach ($this->formatClasses() as $class) { + try { + $envelope = $this->createMagicEnv($xml, $actor, $class); + } catch (Exception $e) { + common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage()); + return false; + } + + $headers = array('Content-Type: application/magic-envelope+xml'); + + try { + $client = new HTTPClient(); + $client->setBody($envelope); + $response = $client->post($endpoint_uri, $headers); + } catch (HTTP_Request2_Exception $e) { + common_log(LOG_ERR, "Salmon ($class) post to $endpoint_uri failed: " . $e->getMessage()); + continue; + } + if ($response->getStatus() != 200) { + common_log(LOG_ERR, "Salmon ($class) at $endpoint_uri returned status " . + $response->getStatus() . ': ' . $response->getBody()); + continue; + } - try { - $client = new HTTPClient(); - $client->setBody($xml); - $response = $client->post($endpoint_uri, $headers); - } catch (HTTP_Request2_Exception $e) { - common_log(LOG_ERR, "Salmon post to $endpoint_uri failed: " . $e->getMessage()); - return false; - } - if ($response->getStatus() != 200) { - common_log(LOG_ERR, "Salmon at $endpoint_uri returned status " . - $response->getStatus() . ': ' . $response->getBody()); - return false; + // Success! + return true; } - return true; + return false; } - public function createMagicEnv($text) - { - $magic_env = new MagicEnvelope(); + /** + * List the magic envelope signature class variants in the order we try them. + * Multiples are needed for backwards-compat with StatusNet prior to 0.9.7, + * which used a draft version of the magic envelope spec. + */ + protected function formatClasses() { + return array('MagicEnvelope', 'MagicEnvelopeCompat'); + } - // TODO: Should probably be getting the signer uri as an argument? - $signer_uri = $magic_env->getAuthor($text); + /** + * Encode the given string as a signed MagicEnvelope XML document, + * using the keypair for the given local user profile. + * + * Side effects: will create and store a keypair on-demand if one + * hasn't already been generated for this user. This can be very slow + * on some systems. + * + * @param string $text XML fragment to sign, assumed to be Atom + * @param Profile $actor Profile of a local user to use as signer + * @param string $class to override the magic envelope signature version, pass a MagicEnvelope subclass here + * + * @return string XML string representation of magic envelope + * + * @throws Exception on bad profile input or key generation problems + * @fixme if signing fails, this seems to return the original text without warning. Is there a reason for this? + */ + public function createMagicEnv($text, $actor, $class='MagicEnvelope') + { + $magic_env = new $class(); - $env = $magic_env->signMessage($text, 'application/atom+xml', $signer_uri); + $user = User::staticGet('id', $actor->id); + if ($user->id) { + // Use local key + $magickey = Magicsig::staticGet('user_id', $user->id); + if (!$magickey) { + // No keypair yet, let's generate one. + $magickey = new Magicsig(); + $magickey->generate($user->id); + } + } else { + // TRANS: Exception. + throw new Exception(_m('Salmon invalid actor for signing.')); + } - return $magic_env->unfold($env); + try { + $env = $magic_env->signMessage($text, 'application/atom+xml', $magickey->toString()); + } catch (Exception $e) { + return $text; + } + return $magic_env->toXML($env); } - - public function verifyMagicEnv($dom) + /** + * Check if the given magic envelope is well-formed and correctly signed. + * Needs to have network access to fetch public keys over the web. + * Both current and back-compat signature formats will be checked. + * + * Side effects: exceptions and caching updates may occur during network + * fetches. + * + * @param string $text XML fragment of magic envelope + * @return boolean + * + * @throws Exception on bad profile input or key generation problems + * @fixme could hit fatal errors or spew output on invalid XML + */ + public function verifyMagicEnv($text) { - $magic_env = new MagicEnvelope(); - - $env = $magic_env->fromDom($dom); + foreach ($this->formatClasses() as $class) { + $magic_env = new $class(); + + $env = $magic_env->parse($text); + + if ($magic_env->verify($env)) { + return true; + } + } - return $magic_env->verify($env); + return false; } }