api.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Bart Visscher <bartv@thisnet.nl>
  6. * @author Bernhard Posselt <dev@bernhard-posselt.com>
  7. * @author Björn Schießle <bjoern@schiessle.org>
  8. * @author Christoph Wurst <christoph@owncloud.com>
  9. * @author Joas Schilling <coding@schilljs.com>
  10. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  11. * @author Lukas Reschke <lukas@statuscode.ch>
  12. * @author Michael Gapczynski <GapczynskiM@gmail.com>
  13. * @author Morris Jobke <hey@morrisjobke.de>
  14. * @author Robin Appelman <robin@icewind.nl>
  15. * @author Roeland Jago Douma <roeland@famdouma.nl>
  16. * @author Thomas Müller <thomas.mueller@tmit.eu>
  17. * @author Tom Needham <tom@owncloud.com>
  18. * @author Vincent Petry <pvince81@owncloud.com>
  19. *
  20. * @license AGPL-3.0
  21. *
  22. * This code is free software: you can redistribute it and/or modify
  23. * it under the terms of the GNU Affero General Public License, version 3,
  24. * as published by the Free Software Foundation.
  25. *
  26. * This program is distributed in the hope that it will be useful,
  27. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  29. * GNU Affero General Public License for more details.
  30. *
  31. * You should have received a copy of the GNU Affero General Public License, version 3,
  32. * along with this program. If not, see <http://www.gnu.org/licenses/>
  33. *
  34. */
  35. use OCP\API;
  36. use OCP\AppFramework\Http;
  37. class OC_API {
  38. /**
  39. * api actions
  40. */
  41. protected static $actions = array();
  42. /**
  43. * registers an api call
  44. * @param string $method the http method
  45. * @param string $url the url to match
  46. * @param callable $action the function to run
  47. * @param string $app the id of the app registering the call
  48. * @param int $authLevel the level of authentication required for the call
  49. * @param array $defaults
  50. * @param array $requirements
  51. */
  52. public static function register($method, $url, $action, $app,
  53. $authLevel = API::USER_AUTH,
  54. $defaults = array(),
  55. $requirements = array()) {
  56. $name = strtolower($method).$url;
  57. $name = str_replace(array('/', '{', '}'), '_', $name);
  58. if(!isset(self::$actions[$name])) {
  59. $oldCollection = OC::$server->getRouter()->getCurrentCollection();
  60. OC::$server->getRouter()->useCollection('ocs');
  61. OC::$server->getRouter()->create($name, $url)
  62. ->method($method)
  63. ->defaults($defaults)
  64. ->requirements($requirements)
  65. ->action('OC_API', 'call');
  66. self::$actions[$name] = array();
  67. OC::$server->getRouter()->useCollection($oldCollection);
  68. }
  69. self::$actions[$name][] = array('app' => $app, 'action' => $action, 'authlevel' => $authLevel);
  70. }
  71. /**
  72. * respond to a call
  73. * @param \OC\OCS\Result $result
  74. * @param string $format the format xml|json
  75. */
  76. public static function respond($result, $format='xml') {
  77. $request = \OC::$server->getRequest();
  78. // Send 401 headers if unauthorised
  79. if($result->getStatusCode() === API::RESPOND_UNAUTHORISED) {
  80. // If request comes from JS return dummy auth request
  81. if($request->getHeader('X-Requested-With') === 'XMLHttpRequest') {
  82. header('WWW-Authenticate: DummyBasic realm="Authorisation Required"');
  83. } else {
  84. header('WWW-Authenticate: Basic realm="Authorisation Required"');
  85. }
  86. http_response_code(401);
  87. }
  88. foreach($result->getHeaders() as $name => $value) {
  89. header($name . ': ' . $value);
  90. }
  91. $meta = $result->getMeta();
  92. $data = $result->getData();
  93. if (self::isV2($request)) {
  94. $statusCode = self::mapStatusCodes($result->getStatusCode());
  95. if (!is_null($statusCode)) {
  96. $meta['statuscode'] = $statusCode;
  97. http_response_code($statusCode);
  98. }
  99. }
  100. self::setContentType($format);
  101. $body = self::renderResult($format, $meta, $data);
  102. echo $body;
  103. }
  104. /**
  105. * @param XMLWriter $writer
  106. */
  107. private static function toXML($array, $writer) {
  108. foreach($array as $k => $v) {
  109. if ($k[0] === '@') {
  110. $writer->writeAttribute(substr($k, 1), $v);
  111. continue;
  112. } else if (is_numeric($k)) {
  113. $k = 'element';
  114. }
  115. if(is_array($v)) {
  116. $writer->startElement($k);
  117. self::toXML($v, $writer);
  118. $writer->endElement();
  119. } else {
  120. $writer->writeElement($k, $v);
  121. }
  122. }
  123. }
  124. /**
  125. * @return string
  126. */
  127. public static function requestedFormat() {
  128. $formats = array('json', 'xml');
  129. $format = !empty($_GET['format']) && in_array($_GET['format'], $formats) ? $_GET['format'] : 'xml';
  130. return $format;
  131. }
  132. /**
  133. * Based on the requested format the response content type is set
  134. * @param string $format
  135. */
  136. public static function setContentType($format = null) {
  137. $format = is_null($format) ? self::requestedFormat() : $format;
  138. if ($format === 'xml') {
  139. header('Content-type: text/xml; charset=UTF-8');
  140. return;
  141. }
  142. if ($format === 'json') {
  143. header('Content-Type: application/json; charset=utf-8');
  144. return;
  145. }
  146. header('Content-Type: application/octet-stream; charset=utf-8');
  147. }
  148. /**
  149. * @param \OCP\IRequest $request
  150. * @return bool
  151. */
  152. protected static function isV2(\OCP\IRequest $request) {
  153. $script = $request->getScriptName();
  154. return substr($script, -11) === '/ocs/v2.php';
  155. }
  156. /**
  157. * @param integer $sc
  158. * @return int
  159. */
  160. public static function mapStatusCodes($sc) {
  161. switch ($sc) {
  162. case API::RESPOND_NOT_FOUND:
  163. return Http::STATUS_NOT_FOUND;
  164. case API::RESPOND_SERVER_ERROR:
  165. return Http::STATUS_INTERNAL_SERVER_ERROR;
  166. case API::RESPOND_UNKNOWN_ERROR:
  167. return Http::STATUS_INTERNAL_SERVER_ERROR;
  168. case API::RESPOND_UNAUTHORISED:
  169. // already handled for v1
  170. return null;
  171. case 100:
  172. return Http::STATUS_OK;
  173. }
  174. // any 2xx, 4xx and 5xx will be used as is
  175. if ($sc >= 200 && $sc < 600) {
  176. return $sc;
  177. }
  178. return Http::STATUS_BAD_REQUEST;
  179. }
  180. /**
  181. * @param string $format
  182. * @return string
  183. */
  184. public static function renderResult($format, $meta, $data) {
  185. $response = array(
  186. 'ocs' => array(
  187. 'meta' => $meta,
  188. 'data' => $data,
  189. ),
  190. );
  191. if ($format == 'json') {
  192. return OC_JSON::encode($response);
  193. }
  194. $writer = new XMLWriter();
  195. $writer->openMemory();
  196. $writer->setIndent(true);
  197. $writer->startDocument();
  198. self::toXML($response, $writer);
  199. $writer->endDocument();
  200. return $writer->outputMemory(true);
  201. }
  202. }