AbstractProxy.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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. namespace PrivateBin\Proxy;
  12. use Exception;
  13. use PrivateBin\Configuration;
  14. use PrivateBin\Json;
  15. /**
  16. * AbstractProxy
  17. *
  18. * Forwards a URL for shortening and stores the result.
  19. */
  20. abstract class AbstractProxy
  21. {
  22. /**
  23. * error message
  24. *
  25. * @access private
  26. * @var string
  27. */
  28. private $_error = '';
  29. /**
  30. * shortened URL
  31. *
  32. * @access private
  33. * @var string
  34. */
  35. private $_url = '';
  36. /**
  37. * constructor
  38. *
  39. * initializes and runs the proxy class
  40. *
  41. * @access public
  42. * @param Configuration $conf
  43. * @param string $link
  44. */
  45. public function __construct(Configuration $conf, string $link)
  46. {
  47. if (!filter_var($link, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED & FILTER_FLAG_QUERY_REQUIRED)) {
  48. $this->_error = 'Invalid URL given.';
  49. return;
  50. }
  51. if (!str_starts_with($link, $conf->getKey('basepath') . '?') ||
  52. parse_url($link, PHP_URL_HOST) != parse_url($conf->getKey('basepath'), PHP_URL_HOST)
  53. ) {
  54. $this->_error = 'Trying to shorten a URL that isn\'t pointing at our instance.';
  55. return;
  56. }
  57. $proxyUrl = $this->_getProxyUrl($conf);
  58. if (empty($proxyUrl)) {
  59. $this->_error = 'Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.';
  60. $this->logErrorWithClassName($this->_error);
  61. return;
  62. }
  63. $data = file_get_contents($proxyUrl, false,
  64. stream_context_create(
  65. array(
  66. 'http' => $this->_getProxyPayload($conf, $link),
  67. )
  68. )
  69. );
  70. if ($data === false) {
  71. $http_response_header = $http_response_header ?? array();
  72. $statusCode = '';
  73. if (!empty($http_response_header) && preg_match('/HTTP\/\d+\.\d+\s+(\d+)/', $http_response_header[0], $matches)) {
  74. $statusCode = $matches[1];
  75. }
  76. $this->_error = 'Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.';
  77. $this->logErrorWithClassName($this->_error . ' Status code: ' . $statusCode);
  78. return;
  79. }
  80. try {
  81. $jsonData = Json::decode($data);
  82. } catch (Exception $e) {
  83. $this->_error = 'Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.';
  84. $this->logErrorWithClassName('Error calling proxy: ' . $e->getMessage());
  85. return;
  86. }
  87. $url = $this->_extractShortUrl($jsonData);
  88. if ($url === null || empty($url)) {
  89. $this->_error = 'Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.';
  90. $this->logErrorWithClassName('Error calling proxy: ' . $data);
  91. } else {
  92. $this->_url = $url;
  93. }
  94. }
  95. private function logErrorWithClassName(string $error)
  96. {
  97. error_log('[' . get_class($this) . '] ' . $error);
  98. }
  99. /**
  100. * Returns the (untranslated) error message
  101. *
  102. * @access public
  103. * @return string
  104. */
  105. public function getError()
  106. {
  107. return $this->_error;
  108. }
  109. /**
  110. * Returns the shortened URL
  111. *
  112. * @access public
  113. * @return string
  114. */
  115. public function getUrl()
  116. {
  117. return $this->_url;
  118. }
  119. /**
  120. * Returns true if any error has occurred
  121. *
  122. * @access public
  123. * @return bool
  124. */
  125. public function isError()
  126. {
  127. return !empty($this->_error);
  128. }
  129. /**
  130. * Abstract method to get the payload to send to the URL shortener
  131. *
  132. * @access protected
  133. * @param Configuration $conf
  134. * @param string $link
  135. * @return array
  136. */
  137. abstract protected function _getProxyPayload(Configuration $conf, string $link): array;
  138. /**
  139. * Abstract method to extract the shortUrl from the response
  140. *
  141. * @param array $data
  142. * @return ?string
  143. */
  144. abstract protected function _extractShortUrl(array $data): ?string;
  145. /**
  146. * Abstract method to get the proxy URL
  147. *
  148. * @param Configuration $conf
  149. * @return string
  150. */
  151. abstract protected function _getProxyUrl(Configuration $conf): string;
  152. }