*/
namespace Friendica\Util;
-use Friendica\Core\Config;
+use Friendica\Database\DBA;
use Friendica\Core\Logger;
+use Friendica\DI;
use Friendica\Model\User;
use Friendica\Model\APContact;
/**
- * @brief Implements HTTP Signatures per draft-cavage-http-signatures-07.
+ * Implements HTTP Signatures per draft-cavage-http-signatures-07.
*
* Ported from Hubzilla: https://framagit.org/hubzilla/core/blob/master/Zotlabs/Web/HTTPSig.php
*
{
// See draft-cavage-http-signatures-08
/**
- * @brief Verifies a magic request
+ * Verifies a magic request
*
* @param $key
*
}
/**
- * @brief
- *
* @param array $head
* @param string $prvkey
* @param string $keyid (optional, default 'Key')
}
/**
- * @brief
- *
* @param array $head
* @param string $prvkey
* @param string $alg (optional) default 'sha256'
}
/**
- * @brief
- *
* @param string $header
* @return array associate array with
* - \e string \b keyID
}
/**
- * @brief
- *
* @param string $header
* @param string $prvkey (optional), if not set use site private key
*
$iv = $key = $alg = $data = null;
if (!$prvkey) {
- $prvkey = Config::get('system', 'prvkey');
+ $prvkey = DI::config()->get('system', 'prvkey');
}
$matches = [];
*/
/**
- * @brief Transmit given data to a target for a user
+ * Transmit given data to a target for a user
*
* @param array $data Data that is about to be send
* @param string $target The URL of the inbox
Logger::log('Transmit to ' . $target . ' returned ' . $return_code, Logger::DEBUG);
- return ($return_code >= 200) && ($return_code <= 299);
+ $success = ($return_code >= 200) && ($return_code <= 299);
+
+ self::setInboxStatus($target, $success);
+
+ return $success;
+ }
+
+ /**
+ * Set the delivery status for a given inbox
+ *
+ * @param string $url The URL of the inbox
+ * @param boolean $success Transmission status
+ */
+ static private function setInboxStatus($url, $success)
+ {
+ $now = DateTimeFormat::utcNow();
+
+ $status = DBA::selectFirst('inbox-status', [], ['url' => $url]);
+ if (!DBA::isResult($status)) {
+ DBA::insert('inbox-status', ['url' => $url, 'created' => $now]);
+ $status = DBA::selectFirst('inbox-status', [], ['url' => $url]);
+ }
+
+ if ($success) {
+ $fields = ['success' => $now];
+ } else {
+ $fields = ['failure' => $now];
+ }
+
+ if ($status['failure'] > DBA::NULL_DATETIME) {
+ $new_previous_stamp = strtotime($status['failure']);
+ $old_previous_stamp = strtotime($status['previous']);
+
+ // Only set "previous" with at least one day difference.
+ // We use this to assure to not accidentally archive too soon.
+ if (($new_previous_stamp - $old_previous_stamp) >= 86400) {
+ $fields['previous'] = $status['failure'];
+ }
+ }
+
+ if (!$success) {
+ if ($status['success'] <= DBA::NULL_DATETIME) {
+ $stamp1 = strtotime($status['created']);
+ } else {
+ $stamp1 = strtotime($status['success']);
+ }
+
+ $stamp2 = strtotime($now);
+ $previous_stamp = strtotime($status['previous']);
+
+ // Archive the inbox when there had been failures for five days.
+ // Additionally ensure that at least one previous attempt has to be in between.
+ if ((($stamp2 - $stamp1) >= 86400 * 5) && ($previous_stamp > $stamp1)) {
+ $fields['archive'] = true;
+ }
+ } else {
+ $fields['archive'] = false;
+ }
+
+ DBA::update('inbox-status', $fields, ['url' => $url]);
}
/**
- * @brief Fetches JSON data for a user
+ * Fetches JSON data for a user
*
* @param string $request request url
* @param integer $uid User id of the requester
}
/**
- * @brief Fetches raw data for a user
+ * Fetches raw data for a user
*
* @param string $request request url
* @param integer $uid User id of the requester
$curl_opts = $opts;
$curl_opts['header'] = $headers;
- $curlResult = Network::curl($request, false, $redirects, $curl_opts);
+ $curlResult = Network::curl($request, false, $curl_opts);
$return_code = $curlResult->getReturnCode();
Logger::log('Fetched for user ' . $uid . ' from ' . $request . ' returned ' . $return_code, Logger::DEBUG);
}
/**
- * @brief Gets a signer from a given HTTP request
+ * Gets a signer from a given HTTP request
*
* @param $content
* @param $http_headers
return false;
}
+ $hasGoodSignedContent = false;
+
// Check the digest when it is part of the signed data
- if (in_array('digest', $sig_block['headers'])) {
+ if (!empty($content) && in_array('digest', $sig_block['headers'])) {
$digest = explode('=', $headers['digest'], 2);
if ($digest[0] === 'SHA-256') {
$hashalg = 'sha256';
if (!empty($hashalg) && base64_encode(hash($hashalg, $content, true)) != $digest[1]) {
return false;
}
+
+ $hasGoodSignedContent = true;
}
// Check if the signed date field is in an acceptable range
Logger::log("Header date '" . $headers['date'] . "' is with " . $diff . " seconds out of the 300 second frame. The signature is invalid.");
return false;
}
+ $hasGoodSignedContent = true;
}
// Check the content-length when it is part of the signed data
}
}
+ // Ensure that the authentication had been done with some content
+ // Without this check someone could authenticate with fakeable data
+ if (!$hasGoodSignedContent) {
+ return false;
+ }
+
return $key['url'];
}
/**
- * @brief fetches a key for a given id and actor
+ * fetches a key for a given id and actor
*
* @param $id
* @param $actor