Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
AbstractProxy
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
5 / 5
15
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
1 / 1
11
 logErrorWithClassName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 _getProxyPayload
n/a
0 / 0
n/a
0 / 0
0
 _extractShortUrl
n/a
0 / 0
n/a
0 / 0
0
 _getProxyUrl
n/a
0 / 0
n/a
0 / 0
0
1<?php declare(strict_types=1);
2/**
3 * PrivateBin
4 *
5 * a zero-knowledge paste bin
6 *
7 * @link      https://github.com/PrivateBin/PrivateBin
8 * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
9 * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
10 */
11
12namespace PrivateBin\Proxy;
13
14use JsonException;
15use PrivateBin\Configuration;
16use PrivateBin\Json;
17
18/**
19 * AbstractProxy
20 *
21 * Forwards a URL for shortening and stores the result.
22 */
23abstract class AbstractProxy
24{
25    /**
26     * error message
27     *
28     * @access private
29     * @var    string
30     */
31    private $_error = '';
32
33    /**
34     * proxy URL
35     *
36     * @access protected
37     * @var    string
38     */
39    protected $_proxyUrl = '';
40
41    /**
42     * shortened URL
43     *
44     * @access private
45     * @var    string
46     */
47    private $_url = '';
48
49    /**
50     * constructor
51     *
52     * initializes and runs the proxy class
53     *
54     * @access public
55     * @param Configuration $conf
56     * @param string $link
57     */
58    public function __construct(Configuration $conf, string $link)
59    {
60        if (!filter_var($link, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED | FILTER_FLAG_QUERY_REQUIRED)) {
61            $this->_error = 'Invalid URL given.';
62            return;
63        }
64
65        if (!str_starts_with($link, $conf->getKey('basepath') . '?') ||
66            parse_url($link, PHP_URL_HOST) !== parse_url($conf->getKey('basepath'), PHP_URL_HOST)
67        ) {
68            $this->_error = 'Trying to shorten a URL that isn\'t pointing at our instance.';
69            return;
70        }
71
72        $this->_proxyUrl = $this->_getProxyUrl($conf);
73
74        if (empty($this->_proxyUrl)) {
75            $this->_error = 'Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.';
76            $this->logErrorWithClassName($this->_error);
77            return;
78        }
79
80        $data = file_get_contents($this->_proxyUrl, false,
81            stream_context_create(
82                [
83                    'http' => $this->_getProxyPayload($conf, $link),
84                ]
85            )
86        );
87
88        if ($data === false) {
89            $http_response_header = $http_response_header ?? [];
90            $statusCode           = '';
91            if (!empty($http_response_header) && preg_match('/HTTP\/\d+\.\d+\s+(\d+)/', $http_response_header[0], $matches)) {
92                $statusCode = $matches[1];
93            }
94            $this->_error = 'Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.';
95            $this->logErrorWithClassName($this->_error . ' Status code: ' . $statusCode);
96            return;
97        }
98
99        try {
100            $jsonData = Json::decode($data);
101        } catch (JsonException $e) {
102            $this->_error = 'Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.';
103            $this->logErrorWithClassName('Error calling proxy: ' . $e->getMessage());
104            return;
105        }
106
107        $url = $this->_extractShortUrl($jsonData);
108
109        if ($url === null || empty($url)) {
110            $this->_error = 'Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.';
111            $this->logErrorWithClassName('Error calling proxy: ' . $data);
112        } else {
113            $this->_url = $url;
114        }
115    }
116
117    private function logErrorWithClassName(string $error)
118    {
119        error_log('[' . get_class($this) . '] ' . $error);
120    }
121
122    /**
123     * Returns the (untranslated) error message
124     *
125     * @access public
126     * @return string
127     */
128    public function getError()
129    {
130        return $this->_error;
131    }
132
133    /**
134     * Returns the shortened URL
135     *
136     * @access public
137     * @return string
138     */
139    public function getUrl()
140    {
141        return $this->_url;
142    }
143
144    /**
145     * Returns true if any error has occurred
146     *
147     * @access public
148     * @return bool
149     */
150    public function isError()
151    {
152        return !empty($this->_error);
153    }
154
155    /**
156     * Abstract method to get the payload to send to the URL shortener
157     *
158     * @access protected
159     * @param Configuration $conf
160     * @param string $link
161     * @return array
162     */
163    abstract protected function _getProxyPayload(Configuration $conf, string $link): array;
164
165    /**
166     * Abstract method to extract the shortUrl from the response
167     *
168     * @param array $data
169     * @return ?string
170     */
171    abstract protected function _extractShortUrl(array $data): ?string;
172
173    /**
174     * Abstract method to get the proxy URL
175     *
176     * @param Configuration $conf
177     * @return string
178     */
179    abstract protected function _getProxyUrl(Configuration $conf): string;
180}