"divineomega/password_exposed": "^2.8",
"ezyang/htmlpurifier": "^4.7",
"friendica/json-ld": "^1.0",
+ "guzzlehttp/guzzle": "^6.5",
"league/html-to-markdown": "^4.8",
"level-2/dice": "^4",
"lightopenid/lightopenid": "dev-master",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "fd22bd8c29dcea3d6b6eeb117d79af52",
+ "content-hash": "6c141820d2160b278dffecc3e89f86e3",
"packages": [
{
"name": "asika/simple-console",
];
$likebuttons = [
- 'like' => null,
- 'dislike' => null,
- 'share' => null,
+ 'like' => null,
+ 'dislike' => null,
+ 'share' => null,
+ 'announce' => null,
];
if (DI::pConfig()->get(local_user(), 'system', 'hide_dislike')) {
if ($curlResponse->isSuccess()) {
// Convert the header fields into an array
$hdrs = [];
- $h = explode("\n", $curlResponse->getHeader());
+ $h = $curlResponse->getHeaders();
foreach ($h as $l) {
- $header = array_map('trim', explode(':', trim($l), 2));
- if (count($header) == 2) {
- list($k, $v) = $header;
- $hdrs[$k] = $v;
+ foreach ($l as $k => $v) {
+ if (empty($hdrs[$k])) {
+ $hdrs[$k] = $v;
+ }
+ $hdrs[$k] .= " " . $v;
}
}
$type = null;
}
$i = $curlResult->getBody();
- $type = $curlResult->getContentType();
- $type = Images::getMimeTypeByData($i, $mtch[1], $type);
+ $contType = $curlResult->getContentType();
+ $type = Images::getMimeTypeByData($i, $mtch[1], $contType);
if ($i) {
$Image = new Image($i, $type);
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Module\Register;
-use Friendica\Network\CurlResult;
+use Friendica\Network\IHTTPResult;
use Friendica\Protocol\Diaspora;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
/**
* Detect server type by using the nodeinfo data
*
- * @param string $url address of the server
- * @param CurlResult $curlResult
+ * @param string $url address of the server
+ * @param IHTTPResult $httpResult
+ *
* @return array Server data
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
- private static function fetchNodeinfo(string $url, CurlResult $curlResult)
+ private static function fetchNodeinfo(string $url, IHTTPResult $httpResult)
{
- if (!$curlResult->isSuccess()) {
+ if (!$httpResult->isSuccess()) {
return [];
}
- $nodeinfo = json_decode($curlResult->getBody(), true);
+ $nodeinfo = json_decode($httpResult->getBody(), true);
if (!is_array($nodeinfo) || empty($nodeinfo['links'])) {
return [];
if (!empty($image_url)) {
$ret = DI::httpRequest()->get($image_url, true);
$img_str = $ret->getBody();
- $type = $ret->getContentType();
+ $contType = $ret->getContentType();
} else {
$img_str = '';
+ $contType = '';
}
if ($quit_on_error && ($img_str == "")) {
return false;
}
- $type = Images::getMimeTypeByData($img_str, $image_url, $type);
+ $type = Images::getMimeTypeByData($img_str, $image_url, $contType);
$Image = new Image($img_str, $type);
if ($Image->isValid()) {
$curlResult = DI::httpRequest()->get($photo, true);
if ($curlResult->isSuccess()) {
$img_str = $curlResult->getBody();
- $type = $curlResult->getContentType();
+ $contType = $curlResult->getContentType();
} else {
$img_str = '';
- $type = '';
+ $contType = '';
}
- $type = Images::getMimeTypeByData($img_str, $photo, $type);
+ $type = Images::getMimeTypeByData($img_str, $photo, $contType);
$Image = new Image($img_str, $type);
if ($Image->isValid()) {
/**
* A content class for Curl call results
*/
-class CurlResult
+class CurlResult implements IHTTPResult
{
/**
* @var int HTTP return code or 0 if timeout or failure
*
* @param string $url optional URL
*
- * @return CurlResult a CURL with error response
+ * @return IHTTPResult a CURL with error response
* @throws InternalServerErrorException
*/
public static function createErrorCurl($url = '')
}
}
- /**
- * Gets the Curl Code
- *
- * @return string The Curl Code
- */
+ /** {@inheritDoc} */
public function getReturnCode()
{
return $this->returnCode;
}
- /**
- * Returns the Curl Content Type
- *
- * @return string the Curl Content Type
- */
+ /** {@inheritDoc} */
public function getContentType()
{
return $this->contentType;
}
- /**
- * Returns the Curl headers
- *
- * @param string $field optional header field. Return all fields if empty
- *
- * @return string the Curl headers or the specified content of the header variable
- */
- public function getHeader(string $field = '')
+ /** {@inheritDoc} */
+ public function getHeader($header)
{
- if (empty($field)) {
- return $this->header;
+ if (empty($header)) {
+ return [];
}
- $field = strtolower(trim($field));
+ $header = strtolower(trim($header));
$headers = $this->getHeaderArray();
- if (isset($headers[$field])) {
- return $headers[$field];
+ if (isset($headers[$header])) {
+ return $headers[$header];
}
- return '';
+ return [];
}
- /**
- * Check if a specified header exists
- *
- * @param string $field header field
- *
- * @return boolean "true" if header exists
- */
+ /** {@inheritDoc} */
+ public function getHeaders()
+ {
+ return $this->getHeaderArray();
+ }
+
+ /** {@inheritDoc} */
public function inHeader(string $field)
{
$field = strtolower(trim($field));
return array_key_exists($field, $headers);
}
- /**
- * Returns the Curl headers as an associated array
- *
- * @return array associated header array
- */
+ /** {@inheritDoc} */
public function getHeaderArray()
{
if (!empty($this->header_fields)) {
$parts = explode(':', $line);
$headerfield = strtolower(trim(array_shift($parts)));
$headerdata = trim(implode(':', $parts));
- $this->header_fields[$headerfield] = $headerdata;
+ if (empty($this->header_fields[$headerfield])) {
+ $this->header_fields[$headerfield] = [$headerdata];
+ } elseif (!in_array($headerdata, $this->header_fields[$headerfield])) {
+ $this->header_fields[$headerfield][] = $headerdata;
+ }
}
return $this->header_fields;
}
- /**
- * @return bool
- */
+ /** {@inheritDoc} */
public function isSuccess()
{
return $this->isSuccess;
}
- /**
- * @return string
- */
+ /** {@inheritDoc} */
public function getUrl()
{
return $this->url;
}
- /**
- * @return string
- */
+ /** {@inheritDoc} */
public function getRedirectUrl()
{
return $this->redirectUrl;
}
- /**
- * @return string
- */
+ /** {@inheritDoc} */
public function getBody()
{
return $this->body;
}
- /**
- * @return array
- */
- public function getInfo()
- {
- return $this->info;
- }
-
- /**
- * @return bool
- */
+ /** {@inheritDoc} */
public function isRedirectUrl()
{
return $this->isRedirectUrl;
}
- /**
- * @return int
- */
+ /** {@inheritDoc} */
public function getErrorNumber()
{
return $this->errorNumber;
}
- /**
- * @return string
- */
+ /** {@inheritDoc} */
public function getError()
{
return $this->error;
}
- /**
- * @return bool
- */
+ /** {@inheritDoc} */
public function isTimeout()
{
return $this->isTimeout;
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Network;
+
+use Friendica\Core\Logger;
+use Friendica\Core\System;
+use Friendica\Network\HTTPException\NotImplementedException;
+use GuzzleHttp\Psr7\Response;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * A content wrapper class for Guzzle call results
+ */
+class GuzzleResponse extends Response implements IHTTPResult, ResponseInterface
+{
+ /** @var string The URL */
+ private $url;
+ /** @var boolean */
+ private $isTimeout;
+ /** @var boolean */
+ private $isSuccess;
+ /**
+ * @var int the error number or 0 (zero) if no error
+ */
+ private $errorNumber;
+
+ /**
+ * @var string the error message or '' (the empty string) if no
+ */
+ private $error;
+
+ public function __construct(ResponseInterface $response, string $url, $errorNumber = 0, $error = '')
+ {
+ parent::__construct($response->getStatusCode(), $response->getHeaders(), $response->getBody(), $response->getProtocolVersion(), $response->getReasonPhrase());
+ $this->url = $url;
+ $this->error = $error;
+ $this->errorNumber = $errorNumber;
+
+ $this->checkSuccess();
+ }
+
+ private function checkSuccess()
+ {
+ $this->isSuccess = ($this->getStatusCode() >= 200 && $this->getStatusCode() <= 299) || $this->errorNumber == 0;
+
+ // Everything higher or equal 400 is not a success
+ if ($this->getReturnCode() >= 400) {
+ $this->isSuccess = false;
+ }
+
+ if (!$this->isSuccess) {
+ Logger::notice('http error', ['url' => $this->url, 'code' => $this->getReturnCode(), 'error' => $this->error, 'callstack' => System::callstack(20)]);
+ Logger::debug('debug', ['info' => $this->getHeaders()]);
+ }
+
+ if (!$this->isSuccess && $this->errorNumber == CURLE_OPERATION_TIMEDOUT) {
+ $this->isTimeout = true;
+ } else {
+ $this->isTimeout = false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public function getReturnCode()
+ {
+ return $this->getStatusCode();
+ }
+
+ /** {@inheritDoc} */
+ public function getContentType()
+ {
+ $contentTypes = $this->getHeader('Content-Type') ?? [];
+ return array_pop($contentTypes) ?? '';
+ }
+
+ /** {@inheritDoc} */
+ public function inHeader(string $field)
+ {
+ return $this->hasHeader($field);
+ }
+
+ /** {@inheritDoc} */
+ public function getHeaderArray()
+ {
+ return $this->getHeaders();
+ }
+
+ /** {@inheritDoc} */
+ public function isSuccess()
+ {
+ return $this->isSuccess;
+ }
+
+ /** {@inheritDoc} */
+ public function getUrl()
+ {
+ return $this->url;
+ }
+
+ /** {@inheritDoc} */
+ public function getRedirectUrl()
+ {
+ return $this->url;
+ }
+
+ /** {@inheritDoc} */
+ public function isRedirectUrl()
+ {
+ throw new NotImplementedException();
+ }
+
+ /** {@inheritDoc} */
+ public function getErrorNumber()
+ {
+ return $this->errorNumber;
+ }
+
+ /** {@inheritDoc} */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /** {@inheritDoc} */
+ public function isTimeout()
+ {
+ return $this->isTimeout;
+ }
+}
use Friendica\Core\System;
use Friendica\Util\Network;
use Friendica\Util\Profiler;
+use GuzzleHttp\Client;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Exception\TransferException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
use Psr\Log\LoggerInterface;
/**
/**
* {@inheritDoc}
- *
- * @param int $redirects The recursion counter for internal use - default 0
- *
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
- public function get(string $url, bool $binary = false, array $opts = [], int &$redirects = 0)
+ public function get(string $url, bool $binary = false, array $opts = [])
{
$stamp1 = microtime(true);
return CurlResult::createErrorCurl($url);
}
- $ch = @curl_init($url);
-
- if (($redirects > 8) || (!$ch)) {
- return CurlResult::createErrorCurl($url);
- }
+ $curlOptions = [];
- @curl_setopt($ch, CURLOPT_HEADER, true);
+ $curlOptions[CURLOPT_HEADER] = true;
if (!empty($opts['cookiejar'])) {
- curl_setopt($ch, CURLOPT_COOKIEJAR, $opts["cookiejar"]);
- curl_setopt($ch, CURLOPT_COOKIEFILE, $opts["cookiejar"]);
+ $curlOptions[CURLOPT_COOKIEJAR] = $opts["cookiejar"];
+ $curlOptions[CURLOPT_COOKIEFILE] = $opts["cookiejar"];
}
// These settings aren't needed. We're following the location already.
- // @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
- // @curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
+ // $curlOptions[CURLOPT_FOLLOWLOCATION] =true;
+ // $curlOptions[CURLOPT_MAXREDIRS] = 5;
if (!empty($opts['accept_content'])) {
- curl_setopt(
- $ch,
- CURLOPT_HTTPHEADER,
- ['Accept: ' . $opts['accept_content']]
- );
+ $curlOptions[CURLOPT_HTTPHEADER][] = ['Accept: ' . $opts['accept_content']];
}
if (!empty($opts['header'])) {
- curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['header']);
+ $curlOptions[CURLOPT_HTTPHEADER][] = $opts['header'];
}
- @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- @curl_setopt($ch, CURLOPT_USERAGENT, $this->getUserAgent());
+ $curlOptions[CURLOPT_RETURNTRANSFER] = true;
+ $curlOptions[CURLOPT_USERAGENT] = $this->getUserAgent();
$range = intval($this->config->get('system', 'curl_range_bytes', 0));
if ($range > 0) {
- @curl_setopt($ch, CURLOPT_RANGE, '0-' . $range);
+ $curlOptions[CURLOPT_RANGE] = '0-' . $range;
}
// Without this setting it seems as if some webservers send compressed content
// This seems to confuse curl so that it shows this uncompressed.
/// @todo We could possibly set this value to "gzip" or something similar
- curl_setopt($ch, CURLOPT_ENCODING, '');
+ $curlOptions[CURLOPT_ENCODING] = '';
if (!empty($opts['headers'])) {
- @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']);
+ $curlOptions[CURLOPT_HTTPHEADER][] = $opts['headers'];
}
if (!empty($opts['nobody'])) {
- @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']);
+ $curlOptions[CURLOPT_NOBODY] = $opts['nobody'];
}
- @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
+ $curlOptions[CURLOPT_CONNECTTIMEOUT] = 10;
if (!empty($opts['timeout'])) {
- @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']);
+ $curlOptions[CURLOPT_TIMEOUT] = $opts['timeout'];
} else {
$curl_time = $this->config->get('system', 'curl_timeout', 60);
- @curl_setopt($ch, CURLOPT_TIMEOUT, intval($curl_time));
+ $curlOptions[CURLOPT_TIMEOUT] = intval($curl_time);
}
// by default we will allow self-signed certs
// but you can override this
$check_cert = $this->config->get('system', 'verifyssl');
- @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
+ $curlOptions[CURLOPT_SSL_VERIFYPEER] = ($check_cert) ? true : false;
if ($check_cert) {
- @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
+ $curlOptions[CURLOPT_SSL_VERIFYHOST] = 2;
}
$proxy = $this->config->get('system', 'proxy');
if (!empty($proxy)) {
- @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
- @curl_setopt($ch, CURLOPT_PROXY, $proxy);
+ $curlOptions[CURLOPT_HTTPPROXYTUNNEL] = 1;
+ $curlOptions[CURLOPT_PROXY] = $proxy;
$proxyuser = $this->config->get('system', 'proxyuser');
if (!empty($proxyuser)) {
- @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuser);
+ $curlOptions[CURLOPT_PROXYUSERPWD] = $proxyuser;
}
}
if ($this->config->get('system', 'ipv4_resolve', false)) {
- curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+ $curlOptions[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
}
if ($binary) {
- @curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
+ $curlOptions[CURLOPT_BINARYTRANSFER] = 1;
}
- // don't let curl abort the entire application
- // if it throws any errors.
-
- $s = @curl_exec($ch);
- $curl_info = @curl_getinfo($ch);
-
- // Special treatment for HTTP Code 416
- // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/416
- if (($curl_info['http_code'] == 416) && ($range > 0)) {
- @curl_setopt($ch, CURLOPT_RANGE, '');
- $s = @curl_exec($ch);
- $curl_info = @curl_getinfo($ch);
- }
+ $logger = $this->logger;
- $curlResponse = new CurlResult($url, $s, $curl_info, curl_errno($ch), curl_error($ch));
+ $onRedirect = function(
+ RequestInterface $request,
+ ResponseInterface $response,
+ UriInterface $uri
+ ) use ($logger) {
+ $logger->notice('Curl redirect.', ['url' => $request->getUri(), 'to' => $uri]);
+ };
- if (!Network::isRedirectBlocked($url) && $curlResponse->isRedirectUrl()) {
- $redirects++;
- $this->logger->notice('Curl redirect.', ['url' => $url, 'to' => $curlResponse->getRedirectUrl()]);
- @curl_close($ch);
- return $this->get($curlResponse->getRedirectUrl(), $binary, $opts, $redirects);
+ $onHeaders = function (ResponseInterface $response) use ($opts) {
+ if (!empty($opts['content_length']) &&
+ $response->getHeaderLine('Content-Length') > $opts['content_length']) {
+ throw new TransferException('The file is too big!');
+ }
+ };
+
+ $client = new Client([
+ 'allow_redirect' => [
+ 'max' => 8,
+ 'on_redirect' => $onRedirect,
+ 'on_headers' => $onHeaders,
+ 'track_redirect' => true,
+ 'strict' => true,
+ 'referer' => true,
+ ],
+ 'curl' => $curlOptions
+ ]);
+
+ try {
+ $response = $client->get($url);
+ return new GuzzleResponse($response, $url);
+ } catch (TransferException $exception) {
+ if ($exception instanceof RequestException &&
+ $exception->hasResponse()) {
+ return new GuzzleResponse($exception->getResponse(), $url, $exception->getCode(), '');
+ } else {
+ return new CurlResult($url, '', ['http_code' => $exception->getCode()], $exception->getCode(), '');
+ }
+ } finally {
+ $this->profiler->saveTimestamp($stamp1, 'network');
}
-
- @curl_close($ch);
-
- $this->profiler->saveTimestamp($stamp1, 'network');
-
- return $curlResponse;
}
/**
'timeout' => $timeout,
'accept_content' => $accept_content,
'cookiejar' => $cookiejar
- ],
- $redirects
+ ]
);
}
* @param string $accept_content supply Accept: header with 'accept_content' as the value
* @param string $cookiejar Path to cookie jar file
*
- * @return CurlResult With all relevant information, 'body' contains the actual fetched content.
+ * @return IHTTPResult With all relevant information, 'body' contains the actual fetched content.
*/
public function fetchFull(string $url, bool $binary = false, int $timeout = 0, string $accept_content = '', string $cookiejar = '');
* 'nobody' => only return the header
* 'cookiejar' => path to cookie jar file
* 'header' => header array
+ * 'content_length' => int maximum File content length
*
- * @return CurlResult
+ * @return IHTTPResult
*/
public function get(string $url, bool $binary = false, array $opts = []);
* @param array $headers HTTP headers
* @param int $timeout The timeout in seconds, default system config value or 60 seconds
*
- * @return CurlResult The content
+ * @return IHTTPResult The content
*/
public function post(string $url, $params, array $headers = [], int $timeout = 0);
--- /dev/null
+<?php
+
+namespace Friendica\Network;
+
+use Psr\Http\Message\MessageInterface;
+
+/**
+ * Temporary class to map Friendica used variables based on PSR-7 HTTPResponse
+ */
+interface IHTTPResult
+{
+ /**
+ * Gets the Return Code
+ *
+ * @return string The Return Code
+ */
+ public function getReturnCode();
+
+ /**
+ * Returns the Content Type
+ *
+ * @return string the Content Type
+ */
+ public function getContentType();
+
+ /**
+ * Returns the headers
+ * @see MessageInterface::getHeader()
+ *
+ * @param string $header optional header field. Return all fields if empty
+ *
+ * @return string[] the headers or the specified content of the header variable
+ */
+ public function getHeader($header);
+
+ /**
+ * Returns all headers
+ * @see MessageInterface::getHeaders()
+ *
+ * @return string[][]
+ */
+ public function getHeaders();
+
+ /**
+ * Check if a specified header exists
+ * @see MessageInterface::hasHeader()
+ *
+ * @param string $field header field
+ *
+ * @return boolean "true" if header exists
+ */
+ public function inHeader(string $field);
+
+ /**
+ * Returns the headers as an associated array
+ * @see MessageInterface::getHeaders()
+ * @deprecated
+ *
+ * @return string[][] associated header array
+ */
+ public function getHeaderArray();
+
+ /**
+ * @return bool
+ */
+ public function isSuccess();
+
+ /**
+ * @return string
+ */
+ public function getUrl();
+
+ /**
+ * @return string
+ */
+ public function getRedirectUrl();
+
+ /**
+ * @see MessageInterface::getBody()
+ *
+ * @return string
+ */
+ public function getBody();
+
+ /**
+ * @return boolean
+ */
+ public function isRedirectUrl();
+
+ /**
+ * @return integer
+ */
+ public function getErrorNumber();
+
+ /**
+ * @return string
+ */
+ public function getError();
+
+ /**
+ * @return boolean
+ */
+ public function isTimeout();
+}
*/
private static function getHideStatus($url)
{
- $curlResult = DI::httpRequest()->get($url);
+ $curlResult = DI::httpRequest()->get($url, false, ['content_length' => 1000000]);
if (!$curlResult->isSuccess()) {
return false;
}
- // If the file is too large then exit
- if (($curlResult->getInfo()['download_content_length'] ?? 0) > 1000000) {
- return false;
- }
-
// If it isn't a HTML file then exit
if (($curlResult->getContentType() != '') && !strstr(strtolower($curlResult->getContentType()), 'html')) {
return false;
}
$sparkle = '';
$buttons = [
- 'like' => null,
- 'dislike' => null,
- 'share' => null,
+ 'like' => null,
+ 'dislike' => null,
+ 'share' => null,
+ 'announce' => null,
];
$dropping = false;
$pinned = '';
: false);
$shareable = in_array($conv->getProfileOwner(), [0, local_user()]) && $item['private'] != Item::PRIVATE;
+ $announceable = $shareable && in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER]);
$edpost = false;
$buttons['like'] = [DI::l10n()->t("I like this \x28toggle\x29") , DI::l10n()->t("like")];
$buttons['dislike'] = [DI::l10n()->t("I don't like this \x28toggle\x29"), DI::l10n()->t("dislike")];
if ($shareable) {
- $buttons['share'] = [DI::l10n()->t('Share this'), DI::l10n()->t('share')];
+ $buttons['share'] = [DI::l10n()->t('Share this'), DI::l10n()->t('Share')];
+ }
+ if ($announceable) {
+ $buttons['announce'] = [DI::l10n()->t('Announce this'), DI::l10n()->t('Announce')];
}
}
return -9; // timed out
}
- if (($curl_stat == 503) && stristr($postResult->getHeader(), 'retry-after')) {
+ if (($curl_stat == 503) && $postResult->inHeader('retry-after')) {
return -10;
}
return -9; // timed out
}
- if (($curl_stat == 503) && (stristr($postResult->getHeader(), 'retry-after'))) {
+ if (($curl_stat == 503) && $postResult->inHeader('retry-after')) {
return -10;
}
$xml = '';
- if (stristr($curlResult->getHeader(), 'Content-Type: application/atom+xml')) {
+ if ($curlResult->inHeader('Content-Type') &&
+ in_array('application/atom+xml', $curlResult->getHeader('Content-Type'))) {
$xml = $curlResult->getBody();
}
$xml = '';
- if (stristr($curlResult->getHeader(), 'Content-Type: application/atom+xml')) {
+ if ($curlResult->inHeader('Content-Type') &&
+ in_array('application/atom+xml', $curlResult->getHeader('Content-Type'))) {
Logger::log('Directly fetched XML for URI ' . $related_uri, Logger::DEBUG);
$xml = $curlResult->getBody();
}
return -1;
}
- if (($return_code == 503) && (stristr($postResult->getHeader(), 'retry-after'))) {
+ if (($return_code == 503) && $postResult->inHeader('retry-after')) {
return -1;
}
*
* @param string $image_data Image data
* @param string $filename File name (for guessing the type via the extension)
- * @param string $mime default mime type
+ * @param string $mimeType possible mime type
*
* @return string
* @throws \Exception
*/
- public static function getMimeTypeByData(string $image_data, string $filename = '', string $mime = '')
+ public static function getMimeTypeByData(string $image_data, string $filename = '', string $mimeType = '')
{
- if (substr($mime, 0, 6) == 'image/') {
- Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $mime]);
- return $mime;
+ if (substr($mimeType, 0, 6) == 'image/') {
+ Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $mimeType]);
+ return $mimeType;
}
$image = @getimagesizefromstring($image_data);
if (!empty($image['mime'])) {
- Logger::info('Mime type detected via data', ['filename' => $filename, 'default' => $mime, 'mime' => $image['mime']]);
+ Logger::info('Mime type detected via data', ['filename' => $filename, 'default' => $mimeType, 'mime' => $image['mime']]);
return $image['mime'];
}
return $siteinfo;
}
- $curlResult = DI::httpRequest()->get($url);
+ $curlResult = DI::httpRequest()->get($url, false, ['content_length' => 1000000]);
if (!$curlResult->isSuccess()) {
return $siteinfo;
}
- // If the file is too large then exit
- if (($curlResult->getInfo()['download_content_length'] ?? 0) > 1000000) {
- return $siteinfo;
- }
-
// If it isn't a HTML file then exit
if (($curlResult->getContentType() != '') && !strstr(strtolower($curlResult->getContentType()), 'html')) {
return $siteinfo;
}
- $header = $curlResult->getHeader();
$body = $curlResult->getBody();
if ($do_oembed) {
$charset = '';
// Look for a charset, first in headers
// Expected form: Content-Type: text/html; charset=ISO-8859-4
- if (preg_match('/charset=([a-z0-9-_.\/]+)/i', $header, $matches)) {
+ if (preg_match('/charset=([a-z0-9-_.\/]+)/i', $curlResult->getContentType(), $matches)) {
$charset = trim(trim(trim(array_pop($matches)), ';,'));
}
--- /dev/null
+<?php
+
+return [
+ 'http/2 200' => [''],
+ 'date' => ['Thu, 11 Oct 2018 18:43:54 GMT'],
+ 'content-type' => ['text/html; charset=utf-8'],
+ 'vary' => ['Accept-Encoding'],
+ 'server' => ['Mastodon'],
+ 'x-frame-options' => ['DENY', 'SAMEORIGIN'],
+ 'x-content-type-options' => ['nosniff'],
+ 'x-xss-protection' => ['1; mode=block'],
+ 'etag' => ['W/"706e6c48957e1d46ecf9d7597a7880af"'],
+ 'cache-control' => ['max-age=0, private, must-revalidate'],
+ 'set-cookie' => ['_mastodon_session=v3kcy%2FW3aZYBBvZUohuwksEKwzYIyEUlEuJ1KqTAfWPKvVQq%2F4UuJ39zp621VyfpQNlvY46TL%2FYutzXowSLYQBNFCJcrEiF04aU0TdtHls9zynMiyeHhoVgCijOXWXNt9%2FCmpQ49RkNEujkv9NaJ0cum32MCVZKjE9%2BMKmLM%2F8ZygZeLBGJ7sg%3D%3D--QGIiU0%2FpXc3Aym8F--he2iRRPePOdtEs3z%2BufSXg%3D%3D; path=/; secure; HttpOnly'],
+ 'x-request-id' => ['a0c0b8e7-cd60-4efa-b79b-cf1b0d5a0784'],
+ 'x-runtime' => ['0.049566'],
+ 'strict-transport-security' => ['max-age=31536000; includeSubDomains; preload'],
+ 'referrer-policy' => ['same-origin'],
+ 'content-security-policy' => ["frame-ancestors 'none'; script-src 'self'; object-src 'self'; img-src * data: blob:; media-src 'self' data:; font-src 'self' data: https://fonts.gstatic.com/; connect-src 'self' blob: wss://mastodonten.de"],
+];
--- /dev/null
+<?php
+
+return [
+ 'http/2 301' => [''],
+ 'date' => ['Thu, 11 Oct 2018 18:43:54 GMT'],
+ 'content-type' => ['text/html; charset=utf-8'],
+ 'vary' => ['Accept-Encoding'],
+ 'server' => ['Mastodon'],
+ 'location' => ['https://test.other/some/'],
+ 'x-frame-options' => ['DENY', 'SAMEORIGIN'],
+ 'x-content-type-options' => ['nosniff'],
+ 'x-xss-protection' => ['1; mode=block'],
+ 'etag' => ['W/"706e6c48957e1d46ecf9d7597a7880af"'],
+ 'cache-control' => ['max-age=0, private, must-revalidate'],
+ 'set-cookie' => ['_mastodon_session=v3kcy%2FW3aZYBBvZUohuwksEKwzYIyEUlEuJ1KqTAfWPKvVQq%2F4UuJ39zp621VyfpQNlvY46TL%2FYutzXowSLYQBNFCJcrEiF04aU0TdtHls9zynMiyeHhoVgCijOXWXNt9%2FCmpQ49RkNEujkv9NaJ0cum32MCVZKjE9%2BMKmLM%2F8ZygZeLBGJ7sg%3D%3D--QGIiU0%2FpXc3Aym8F--he2iRRPePOdtEs3z%2BufSXg%3D%3D; path=/; secure; HttpOnly'],
+ 'x-request-id' => ['a0c0b8e7-cd60-4efa-b79b-cf1b0d5a0784'],
+ 'x-runtime' => ['0.049566'],
+ 'strict-transport-security' => ['max-age=31536000; includeSubDomains; preload'],
+ 'referrer-policy' => ['same-origin'],
+ 'content-security-policy' => ["frame-ancestors 'none'; script-src 'self'; object-src 'self'; img-src * data: blob:; media-src 'self' data:; font-src 'self' data: https://fonts.gstatic.com/; connect-src 'self' blob: wss://mastodonten.de"],
+];
use Dice\Dice;
use Friendica\Core\Config\Cache;
use Friendica\DI;
-use Friendica\Network\CurlResult;
+use Friendica\Network\IHTTPResult;
use Friendica\Network\IHTTPRequest;
use Friendica\Test\MockedTest;
use Friendica\Test\Util\VFSTrait;
$this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
// Mocking the CURL Response
- $curlResult = \Mockery::mock(CurlResult::class);
- $curlResult
+ $IHTTPResult = \Mockery::mock(IHTTPResult::class);
+ $IHTTPResult
->shouldReceive('getReturnCode')
->andReturn('404');
- $curlResult
+ $IHTTPResult
->shouldReceive('getRedirectUrl')
->andReturn('');
- $curlResult
+ $IHTTPResult
->shouldReceive('getError')
->andReturn('test Error');
$networkMock
->shouldReceive('fetchFull')
->with('https://test/install/testrewrite')
- ->andReturn($curlResult);
+ ->andReturn($IHTTPResult);
$networkMock
->shouldReceive('fetchFull')
->with('http://test/install/testrewrite')
- ->andReturn($curlResult);
+ ->andReturn($IHTTPResult);
$this->dice->shouldReceive('create')
->with(IHTTPRequest::class)
$this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
// Mocking the failed CURL Response
- $curlResultF = \Mockery::mock(CurlResult::class);
- $curlResultF
+ $IHTTPResultF = \Mockery::mock(IHTTPResult::class);
+ $IHTTPResultF
->shouldReceive('getReturnCode')
->andReturn('404');
// Mocking the working CURL Response
- $curlResultW = \Mockery::mock(CurlResult::class);
- $curlResultW
+ $IHTTPResultW = \Mockery::mock(IHTTPResult::class);
+ $IHTTPResultW
->shouldReceive('getReturnCode')
->andReturn('204');
$networkMock
->shouldReceive('fetchFull')
->with('https://test/install/testrewrite')
- ->andReturn($curlResultF);
+ ->andReturn($IHTTPResultF);
$networkMock
->shouldReceive('fetchFull')
->with('http://test/install/testrewrite')
- ->andReturn($curlResultW);
+ ->andReturn($IHTTPResultW);
$this->dice->shouldReceive('create')
->with(IHTTPRequest::class)
public function testNormal()
{
$header = file_get_contents(__DIR__ . '/../../datasets/curl/about.head');
+ $headerArray = include(__DIR__ . '/../../datasets/curl/about.head.php');
$body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body');
$this->assertTrue($curlResult->isSuccess());
$this->assertFalse($curlResult->isTimeout());
$this->assertFalse($curlResult->isRedirectUrl());
- $this->assertSame($header, $curlResult->getHeader());
+ $this->assertSame($headerArray, $curlResult->getHeaders());
$this->assertSame($body, $curlResult->getBody());
$this->assertSame('text/html; charset=utf-8', $curlResult->getContentType());
$this->assertSame('https://test.local', $curlResult->getUrl());
public function testRedirect()
{
$header = file_get_contents(__DIR__ . '/../../datasets/curl/about.head');
+ $headerArray = include(__DIR__ . '/../../datasets/curl/about.head.php');
$body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body');
$this->assertTrue($curlResult->isSuccess());
$this->assertFalse($curlResult->isTimeout());
$this->assertTrue($curlResult->isRedirectUrl());
- $this->assertSame($header, $curlResult->getHeader());
+ $this->assertSame($headerArray, $curlResult->getHeaders());
$this->assertSame($body, $curlResult->getBody());
$this->assertSame('text/html; charset=utf-8', $curlResult->getContentType());
$this->assertSame('https://test.local/test/it', $curlResult->getUrl());
public function testTimeout()
{
$header = file_get_contents(__DIR__ . '/../../datasets/curl/about.head');
+ $headerArray = include(__DIR__ . '/../../datasets/curl/about.head.php');
$body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body');
$this->assertFalse($curlResult->isSuccess());
$this->assertTrue($curlResult->isTimeout());
$this->assertFalse($curlResult->isRedirectUrl());
- $this->assertSame($header, $curlResult->getHeader());
+ $this->assertSame($headerArray, $curlResult->getHeaders());
$this->assertSame($body, $curlResult->getBody());
$this->assertSame('text/html; charset=utf-8', $curlResult->getContentType());
$this->assertSame('https://test.local/test/it', $curlResult->getRedirectUrl());
public function testRedirectHeader()
{
$header = file_get_contents(__DIR__ . '/../../datasets/curl/about.redirect');
+ $headerArray = include(__DIR__ . '/../../datasets/curl/about.redirect.php');
$body = file_get_contents(__DIR__ . '/../../datasets/curl/about.body');
$this->assertTrue($curlResult->isSuccess());
$this->assertFalse($curlResult->isTimeout());
$this->assertTrue($curlResult->isRedirectUrl());
- $this->assertSame($header, $curlResult->getHeader());
+ $this->assertSame($headerArray, $curlResult->getHeaders());
$this->assertSame($body, $curlResult->getBody());
$this->assertSame('text/html; charset=utf-8', $curlResult->getContentType());
$this->assertSame('https://test.local/test/it?key=value', $curlResult->getUrl());
'url' => 'https://test.local'
]);
- $this->assertNotEmpty($curlResult->getHeader());
+ $this->assertNotEmpty($curlResult->getHeaders());
$this->assertEmpty($curlResult->getHeader('wrongHeader'));
}
}
<div class="wall-item-like-buttons" id="wall-item-like-buttons-{{$item.id}}">
<a href="#" class="icon like{{if $item.responses.like.self}} active{{/if}}" title="{{$item.vote.like.0}}" onclick="dolike({{$item.id}}, 'like'{{if $item.responses.like.self}}, true{{/if}}); return false"></a>
{{if $item.vote.dislike}}<a href="#" class="icon dislike{{if $item.responses.dislike.self}} active{{/if}}" title="{{$item.vote.dislike.0}}" onclick="dolike({{$item.id}},'dislike'{{if $item.responses.dislike.self}}, true{{/if}}); return false"></a>{{/if}}
- {{if $item.vote.share}}<a href="#" class="icon recycle wall-item-share-buttons" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"></a>{{/if}}
+ {{if $item.vote.announce}}<a href="#" class="icon recycle{{if $item.responses.announce.self}} active{{/if}}" title="{{$item.vote.announce.0}}" onclick="dolike({{$item.id}},'announce'{{if $item.responses.announce.self}}, true{{/if}}); return false"></a>{{/if}}
+ {{if $item.vote.share}}<a href="#" class="icon share wall-item-share-buttons" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"></a>{{/if}}
<img id="like-rotator-{{$item.id}}" class="like-rotator" src="images/rotator.gif" alt="{{$item.wait}}" title="{{$item.wait}}" style="display: none;" />
</div>
{{/if}}
{{* Button for sharing the item *}}
{{if $item.vote}}
- {{if $item.vote.share}}
+ {{if $item.vote.share OR $item.vote.announce}}
{{if $item.vote.like OR $item.vote.dislike OR $item.comment_html}}
<span role="presentation" class="separator"></span>
{{/if}}
- <button type="button" class="btn-link button-votes" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}});"><i class="fa fa-retweet" aria-hidden="true"></i> {{$item.vote.share.1}}</button>
+ {{/if}}
+ {{if $item.vote.announce}}
+ <button type="button" class="btn-link button-announces{{if $item.responses.announce.self}} active" aria-pressed="true{{/if}}" id="announce-{{$item.id}}" title="{{$item.vote.announce.0}}" onclick="doLikeAction({{$item.id}}, 'announce'{{if $item.responses.announce.self}}, true{{/if}});" data-toggle="button"><i class="fa fa-retweet" aria-hidden="true"></i> {{$item.vote.announce.1}}</button>
+ <span role="presentation" class="separator"></span>
+ {{/if}}
+ {{if $item.vote.share}}
+ <button type="button" class="btn-link button-votes" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}});"><i class="fa fa-share" aria-hidden="true"></i> {{$item.vote.share.1}}</button>
{{/if}}
{{/if}}
</div>
{{/if}}
+ {{* Button for announcing the item *}}
+ {{if $item.vote.announce}}
+<!-- <div class="btn-group" role="group">
+ <button type="button" class="btn btn-sm button-announces{{if $item.responses.announce.self}} active" aria-pressed="true{{/if}}" id="announce-{{$item.id}}" title="{{$item.vote.announce.0}}" onclick="doLikeAction({{$item.id}}, 'announce'{{if $item.responses.announce.self}}, true{{/if}});" data-toggle="button"><i class="fa fa-retweet-up" aria-hidden="true"></i></button>
+ </div>
+ -->
+ {{/if}}
+
{{* Button for sharing the item *}}
{{if $item.vote.share}}
<div class="btn-group" role="group">
- <button type="button" class="btn btn-sm button-votes" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}});"><i class="fa fa-retweet" aria-hidden="true"></i></button>
+ <button type="button" class="btn btn-sm button-votes" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}});"><i class="fa fa-share" aria-hidden="true"></i></button>
</div>
{{/if}}
{{if $item.vote.dislike}}
<a href="#" id="dislike-{{$item.id}}"{{if $item.responses.dislike.self}} class="active{{/if}}" title="{{$item.vote.dislike.0}}" onclick="dolike({{$item.id}}, 'dislike'{{if $item.responses.dislike.self}}, true{{/if}}); return false">{{$item.vote.dislike.1}}</a>
{{/if}}
+ {{if $item.vote.announce}}
+ <a href="#" id="announce-{{$item.id}}"{{if $item.responses.announce.self}} class="active{{/if}}" title="{{$item.vote.announce.0}}" onclick="dolike({{$item.id}}, 'announce'{{if $item.responses.announce.self}}, true{{/if}}); return false">{{$item.vote.announce.1}}</a>
+ {{/if}}
{{if $item.vote.share}}
<a href="#" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false">{{$item.vote.share.1}}</a>
{{/if}}
{{if $item.vote.dislike}}
<a href="#" class="icon dislike{{if $item.responses.dislike.self}} self{{/if}}" title="{{$item.vote.dislike.0}}" onclick="dolike({{$item.id}}, 'dislike'{{if $item.responses.dislike.self}}, true{{/if}}); return false"></a>
{{/if}}
+ {{if $item.vote.announce}}
+ <a href="#" class="icon recycle{{if $item.responses.announce.self}} self{{/if}}" title="{{$item.vote.announce.0}}" onclick="dolike({{$item.id}}, 'announce'{{if $item.responses.announce.self}}, true{{/if}}); return false"></a>
+ {{/if}}
{{if $item.vote.share}}
- <a href="#" class="icon recycle wall-item-share-buttons" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"></a> {{/if}}
+ <a href="#" class="icon share wall-item-share-buttons" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"></a> {{/if}}
<img id="like-rotator-{{$item.id}}" class="like-rotator" src="images/rotator.gif" alt="{{$item.wait}}" title="{{$item.wait}}" style="display: none;" />
</div>
{{/if}}
<a role="button" id="like-{{$item.id}}"{{if $item.responses.like.self}} class="active"{{/if}} title="{{$item.vote.like.0}}" onclick="dolike({{$item.id}}, 'like'{{if $item.responses.like.self}}, true{{/if}}); return false"><i class="icon-thumbs-up icon-large"><span class="sr-only">{{$item.vote.like.0}}</span></i></a>
{{/if}}{{if $item.vote.dislike}}
<a role="button" id="dislike-{{$item.id}}"{{if $item.responses.dislike.self}} class="active"{{/if}} title="{{$item.vote.dislike.0}}" onclick="dolike({{$item.id}}, 'dislike'{{if $item.responses.dislike.self}}, true{{/if}}); return false"><i class="icon-thumbs-down icon-large"><span class="sr-only">{{$item.vote.dislike.0}}</span></i></a>
+ {{/if}}{{if $item.vote.announce}}
+ <a role="button" id="announce-{{$item.id}}"{{if $item.responses.announce.self}} class="active"{{/if}} title="{{$item.vote.announce.0}}" onclick="dolike({{$item.id}}, 'announce'{{if $item.responses.announce.self}}, true{{/if}}); return false"><i class="icon-retweet icon-large"><span class="sr-only">{{$item.vote.dislike.0}}</span></i></a>
{{/if}}
{{if $item.vote.share}}
- <a role="button" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"><i class="icon-retweet icon-large"><span class="sr-only">{{$item.vote.share.0}}</span></i></a>
+ <a role="button" id="share-{{$item.id}}" title="{{$item.vote.share.0}}" onclick="jotShare({{$item.id}}); return false"><i class="icon-share icon-large"><span class="sr-only">{{$item.vote.share.0}}</span></i></a>
{{/if}}
{{/if}}