Open Journal Systems  3.3.0
WebService.inc.php
1 <?php
2 
17 define('WEBSERVICE_RETRIES', 2);
18 define('WEBSERVICE_MICROSECONDS_BEFORE_RETRY', 100000);
19 
20 define('WEBSERVICE_RESPONSE_OK', 200);
21 define('WEBSERVICE_RESPONSE_CREATED', 201);
22 
23 import('lib.pkp.classes.webservice.WebServiceRequest');
24 
25 class WebService {
27  var $_authUsername;
29  var $_authPassword;
31  var $_sslVersion;
32 
35 
36  //
37  // Setters and Getters
38  //
43  function setAuthUsername($authUsername) {
44  $this->_authUsername = str_replace(':', '', $authUsername);
45  }
46 
51  function setAuthPassword($authPassword) {
52  $this->_authPassword = $authPassword;
53  }
54 
59  function setSslVersion($sslVersion) {
60  $this->_sslVersion = $sslVersion;
61  }
62 
67  function getLastResponseStatus() {
69  }
70 
71 
72  //
73  // Public API
74  //
80  function &call(&$webServiceRequest) {
81  assert(is_a($webServiceRequest, 'WebServiceRequest'));
82 
83  $usePut = false;
84  switch($webServiceRequest->getMethod()) {
85  case 'PUT':
86  $usePut = true;
87  case 'POST':
88  if ($webServiceRequest->getAsync()) {
89  $result = $this->_callPostWebServiceAsync($webServiceRequest, $usePut);
90  } else {
91  $result = $this->_callPostWebService($webServiceRequest, $usePut);
92  }
93  break;
94 
95  case 'GET':
96  $result = $this->_callGetWebService($webServiceRequest);
97  break;
98 
99  default:
100  // TODO: implement DELETE
101  assert(false);
102  }
103 
104  // Catch web service errors
105  $nullVar = null;
106  if (!$result) return $nullVar;
107 
108  if ($this->_lastResponseStatus >= 400 && $this->_lastResponseStatus <= 599) {
109  return $nullVar;
110  }
111 
112  // Clean the result
113  $result = stripslashes($result);
114 
115  return $result;
116  }
117 
118 
119  //
120  // Private helper methods
121  //
128  function _callPostWebService($webServiceRequest, $usePut = false) {
129  $url = $webServiceRequest->getUrl();
130  $postOptions = $webServiceRequest->getParams();
131 
132  $useProxySettings = $webServiceRequest->getUseProxySettings();
133 
134  import('lib.pkp.classes.helpers.PKPCurlHelper');
135  $ch = PKPCurlHelper::getCurlObject($url, $useProxySettings);
136 
137  $headers = array('Accept: ' . $webServiceRequest->getAccept());
138  foreach($webServiceRequest->getHeaders() as $header => $content) {
139  $headers[] = $header . ': ' . $content;
140  }
141 
142  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
143  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
144  if ($usePut) {
145  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
146  } else {
147  curl_setopt($ch, CURLOPT_POST, 1);
148  }
149 
150  // Bug #8518 safety work-around
151  if (is_array($postOptions)) foreach ($postOptions as $paramValue) {
152  if ($paramValue[0] == '@') die('CURL parameters may not begin with @.');
153  }
154 
155  curl_setopt($ch, CURLOPT_POSTFIELDS, $postOptions);
156 
157  // Set up basic authentication if required.
158  $this->_authenticateRequest($ch);
159  $this->_checkSSL($ch, $url);
160 
161  // Relax timeout a little bit for slow servers
162  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
163 
164  // POST to the web service
165  for ($retries = 0; $retries < WEBSERVICE_RETRIES; $retries++) {
166  if (($result = @curl_exec($ch)) !== false) break;
167 
168  // Wait for a short interval before trying again
169  usleep(WEBSERVICE_MICROSECONDS_BEFORE_RETRY);
170  }
171  if (curl_errno($ch)) {
172  trigger_error(curl_error($ch), E_USER_ERROR);
173  }
174 
175  $this->_lastResponseStatus = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
176 
177  curl_close($ch);
178  return $result;
179  }
180 
186  function _callGetWebService(&$webServiceRequest) {
187  // Prepare the request URL
188  $url = $webServiceRequest->getUrl();
189  $queryString = '';
190  foreach($webServiceRequest->getParams() as $key => $values) {
191  // GET requests can contain repeated parameter keys.
192  if (is_scalar($values)) $values = array($values);
193  foreach($values as $value) {
194  if (empty($queryString)) {
195  $queryString = '?';
196  } else {
197  $queryString .= '&';
198  }
199  $queryString .= urlencode($key).'='.urlencode($value);
200  }
201  }
202  $url = $url.$queryString;
203 
204  $useProxySettings = $webServiceRequest->getUseProxySettings();
205 
206  import('lib.pkp.classes.helpers.PKPCurlHelper');
207  $ch = PKPCurlHelper::getCurlObject($url, $useProxySettings);
208 
209  $headers = $this->_buildHeaders($webServiceRequest);
210  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
211  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
212 
213  // Set up basic authentication if required.
214  $this->_authenticateRequest($ch);
215  $this->_checkSSL($ch, $url);
216 
217  // Relax timeout a little bit for slow servers
218  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
219 
220  // GET from the web service
221  for ($retries = 0; $retries < WEBSERVICE_RETRIES; $retries++) {
222  if (($result = @curl_exec($ch)) !== false) break;
223 
224  // Wait for a short interval before trying again
225  usleep(WEBSERVICE_MICROSECONDS_BEFORE_RETRY);
226  }
227  if (curl_errno($ch)) {
228  trigger_error(curl_error($ch), E_USER_ERROR);
229  }
230 
231  $this->_lastResponseStatus = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
232 
233  curl_close($ch);
234  return $result;
235  }
236 
250  function _callPostWebServiceAsync($webServiceRequest, $usePut = false) {
251  // Parse the request URL.
252  $url = $webServiceRequest->getUrl();
253  $urlParts = parse_url($url);
254 
255  // Authentication.
256  $username = $this->_authUsername;
257  if (!is_null($username)) {
258  $password = $this->_authPassword;
259  $webServiceRequest->setHeader('Authorization', 'Basic ' . base64_encode("$username:$password"));
260  }
261 
262  // Headers
263  if (!$webServiceRequest->hasHeader('Content-Type')) {
264  // Our default content type for async POST requests is XML.
265  $webServiceRequest->setHeader('Content-Type', 'text/xml; charset=utf-8');
266  }
267  $headers = $this->_buildHeaders($webServiceRequest);
268 
269  // We expect raw payload.
270  $payload = $webServiceRequest->getParams();
271  assert(is_string($payload));
272 
273  // Open the socket.
274  $fp = fsockopen(
275  $urlParts['host'],
276  isset($urlParts['port'])?$urlParts['port']:80,
277  $errno, $errstr, 30
278  );
279 
280  if (!$fp) {
281  return false;
282  } else {
283  $path = $urlParts['path'] . (isset($urlParts['query']) ? '?' . $urlParts['query'] : '');
284  $host = $urlParts['host'] . ':' . (isset($urlParts['port']) ? $urlParts['port'] : '80');
285  $out = ($usePut ? "PUT " : "POST ") . $path . " HTTP/1.1\r\n";
286  $out.= "Host: " . $host . "\r\n";
287  foreach ($headers as $header) {
288  $out.= "$header\r\n";
289  }
290  $out.= "Content-Length: " . strlen($payload) . "\r\n";
291  $out.= "Connection: Close\r\n\r\n";
292  $out .= $payload;
293  fwrite($fp, $out);
294 
295  // We close the connection before we get the
296  // response. This only works if the web service
297  // reads the whole content at once and is immune
298  // to the client prematurely closing the connection.
299  fclose($fp);
300  return '';
301  }
302  }
303 
308  function _authenticateRequest(&$ch) {
309  $username = $this->_authUsername;
310  if (!is_null($username)) {
311  $password = $this->_authPassword;
312  curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
313  curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
314  }
315  }
316 
322  function _checkSSL($ch, $url) {
323  if (substr($url, 0, 6) == 'https:') {
324  $sslVersion = isset($this->_sslVersion) ? $this->_sslVersion : CURL_SSLVERSION_DEFAULT;
325  curl_setopt($ch, CURLOPT_SSLVERSION, $sslVersion);
326  }
327  }
328 
336  function _buildHeaders(&$webServiceRequest) {
337  $headers = array('Accept: ' . $webServiceRequest->getAccept());
338  foreach($webServiceRequest->getHeaders() as $header => $content) {
339  // Remove colons and spaces from the header name.
340  $header = str_replace(array(' ', ':'), array('', ''), $header);
341  $headerLine = $header . ': ' . $content;
342  // Remove CR/LF from the header line to avoid CRLF attacks.
343  // (We do not allow folded header content).
344  $headerLine = str_replace(array("\n", "\r"), array('', ''), $headerLine);
345  $headers[] = $headerLine;
346  }
347  return $headers;
348  }
349 }
WebService\getLastResponseStatus
getLastResponseStatus()
Definition: WebService.inc.php:79
WebService\setAuthUsername
setAuthUsername($authUsername)
Definition: WebService.inc.php:55
WebService
Abstract base class for a web service.
Definition: WebService.inc.php:25
PKPCurlHelper\getCurlObject
static getCurlObject($url=null, $useProxySettings=true)
Definition: PKPCurlHelper.inc.php:23
WebService\_authenticateRequest
_authenticateRequest(&$ch)
Definition: WebService.inc.php:320
WebService\call
& call(&$webServiceRequest)
Definition: WebService.inc.php:92
WebService\$_authUsername
$_authUsername
Definition: WebService.inc.php:30
WebService\setAuthPassword
setAuthPassword($authPassword)
Definition: WebService.inc.php:63
WebService\setSslVersion
setSslVersion($sslVersion)
Definition: WebService.inc.php:71
WebService\_checkSSL
_checkSSL($ch, $url)
Definition: WebService.inc.php:334
WebService\$_lastResponseStatus
$_lastResponseStatus
Definition: WebService.inc.php:46
WebService\_callPostWebService
_callPostWebService($webServiceRequest, $usePut=false)
Definition: WebService.inc.php:140
WebService\_callPostWebServiceAsync
_callPostWebServiceAsync($webServiceRequest, $usePut=false)
Definition: WebService.inc.php:262
WebService\$_sslVersion
$_sslVersion
Definition: WebService.inc.php:40
WebService\_buildHeaders
_buildHeaders(&$webServiceRequest)
Definition: WebService.inc.php:348
WebService\$_authPassword
$_authPassword
Definition: WebService.inc.php:35
WebService\_callGetWebService
_callGetWebService(&$webServiceRequest)
Definition: WebService.inc.php:198