index.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <?php
  2. /* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
  3. * Copyright (C) 2016 Laurent Destailleur <eldy@users.sourceforge.net>
  4. * Copyright (C) 2017 Regis Houssin <regis.houssin@inodbox.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. /**
  20. * \defgroup api Module DolibarrApi
  21. * \brief API loader
  22. * Search files htdocs/<module>/class/api_<module>.class.php
  23. * \file htdocs/api/index.php
  24. */
  25. use Luracast\Restler\Format\UploadFormat;
  26. if (!defined('NOCSRFCHECK')) define('NOCSRFCHECK', '1'); // Do not check anti CSRF attack test
  27. if (!defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); // Do not check anti POST attack test
  28. if (!defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1'); // If there is no need to load and show top and left menu
  29. if (!defined('NOREQUIREHTML')) define('NOREQUIREHTML', '1'); // If we don't need to load the html.form.class.php
  30. if (!defined('NOREQUIREAJAX')) define('NOREQUIREAJAX', '1'); // Do not load ajax.lib.php library
  31. if (!defined("NOLOGIN")) define("NOLOGIN", '1'); // If this page is public (can be called outside logged session)
  32. // Force entity if a value is provided into HTTP header. Otherwise, will use the entity of user of token used.
  33. if (!empty($_SERVER['HTTP_DOLAPIENTITY'])) define("DOLENTITY", (int) $_SERVER['HTTP_DOLAPIENTITY']);
  34. $res = 0;
  35. if (!$res && file_exists("../main.inc.php")) $res = include '../main.inc.php';
  36. if (!$res) die("Include of main fails");
  37. require_once DOL_DOCUMENT_ROOT.'/includes/restler/framework/Luracast/Restler/AutoLoader.php';
  38. call_user_func(function () {
  39. $loader = Luracast\Restler\AutoLoader::instance();
  40. spl_autoload_register($loader);
  41. return $loader;
  42. });
  43. require_once DOL_DOCUMENT_ROOT.'/api/class/api.class.php';
  44. require_once DOL_DOCUMENT_ROOT.'/api/class/api_access.class.php';
  45. require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
  46. $url = $_SERVER['PHP_SELF'];
  47. if (preg_match('/api\/index\.php$/', $url)) { // sometimes $_SERVER['PHP_SELF'] is 'api\/index\.php' instead of 'api\/index\.php/explorer.php' or 'api\/index\.php/method'
  48. $url = $_SERVER['PHP_SELF'].$_SERVER['PATH_INFO'];
  49. }
  50. // Fix for some NGINX setups (this should not be required even with NGINX, however setup of NGINX are often mysterious and this may help is such cases)
  51. if (!empty($conf->global->MAIN_NGINX_FIX))
  52. {
  53. $url = (isset($_SERVER['SCRIPT_URI']) && $_SERVER["SCRIPT_URI"] !== null) ? $_SERVER["SCRIPT_URI"] : $_SERVER['PHP_SELF'];
  54. }
  55. // Enable and test if module Api is enabled
  56. if (empty($conf->global->MAIN_MODULE_API))
  57. {
  58. $langs->load("admin");
  59. dol_syslog("Call Dolibarr API interfaces with module REST disabled");
  60. print $langs->trans("WarningModuleNotActive", 'Api').'.<br><br>';
  61. print $langs->trans("ToActivateModule");
  62. exit;
  63. }
  64. // Test if explorer is not disabled
  65. if (preg_match('/api\/index\.php\/explorer/', $url) && !empty($conf->global->API_EXPLORER_DISABLED))
  66. {
  67. $langs->load("admin");
  68. dol_syslog("Call Dolibarr API interfaces with module REST disabled");
  69. print $langs->trans("WarningAPIExplorerDisabled").'.<br><br>';
  70. exit;
  71. }
  72. // This 2 lines are usefull only if we want to exclude some Urls from the explorer
  73. //use Luracast\Restler\Explorer;
  74. //Explorer::$excludedPaths = array('/categories');
  75. // Analyze URLs
  76. // index.php/explorer do a redirect to index.php/explorer/
  77. // index.php/explorer/ called by swagger to build explorer page
  78. // index.php/explorer/.../....png|.css|.js called by swagger for resources to build explorer page
  79. // index.php/explorer/resources.json called by swagger to get list of all services
  80. // index.php/explorer/resources.json/xxx called by swagger to get detail of services xxx
  81. // index.php/xxx called by any REST client to run API
  82. $reg = array();
  83. preg_match('/index\.php\/([^\/]+)(.*)$/', $url, $reg);
  84. // .../index.php/categories?sortfield=t.rowid&sortorder=ASC
  85. // When in production mode, a file api/temp/routes.php is created with the API available of current call.
  86. // But, if we set $refreshcache to false, so it may have only one API in the routes.php file if we make a call for one API without
  87. // using the explorer. And when we make another call for another API, the API is not into the api/temp/routes.php and a 404 is returned.
  88. // So we force refresh to each call.
  89. $refreshcache = (empty($conf->global->API_PRODUCTION_DO_NOT_ALWAYS_REFRESH_CACHE) ? true : false);
  90. if (!empty($reg[1]) && $reg[1] == 'explorer' && ($reg[2] == '/swagger.json' || $reg[2] == '/swagger.json/root' || $reg[2] == '/resources.json' || $reg[2] == '/resources.json/root'))
  91. {
  92. $refreshcache = true;
  93. }
  94. $api = new DolibarrApi($db, '', $refreshcache);
  95. //var_dump($api->r->apiVersionMap);
  96. // Enable the Restler API Explorer.
  97. // See https://github.com/Luracast/Restler-API-Explorer for more info.
  98. $api->r->addAPIClass('Luracast\\Restler\\Explorer');
  99. $api->r->setSupportedFormats('JsonFormat', 'XmlFormat', 'UploadFormat'); // 'YamlFormat'
  100. $api->r->addAuthenticationClass('DolibarrApiAccess', '');
  101. // Define accepted mime types
  102. UploadFormat::$allowedMimeTypes = array('image/jpeg', 'image/png', 'text/plain', 'application/octet-stream');
  103. // Restrict API to some IPs
  104. if (!empty($conf->global->API_RESTRICT_ON_IP))
  105. {
  106. $allowedip = explode(' ', $conf->global->API_RESTRICT_ON_IP);
  107. $ipremote = getUserRemoteIP();
  108. if (!in_array($ipremote, $allowedip))
  109. {
  110. dol_syslog('Remote ip is '.$ipremote.', not into list '.$conf->global->API_RESTRICT_ON_IP);
  111. print 'APIs are not allowed from the IP '.$ipremote;
  112. header('HTTP/1.1 503 API not allowed from your IP '.$ipremote);
  113. //print $conf->global->API_RESTRICT_ON_IP;
  114. exit(0);
  115. }
  116. }
  117. // Call Explorer file for all APIs definitions (this part is slow)
  118. if (!empty($reg[1]) && $reg[1] == 'explorer' && ($reg[2] == '/swagger.json' || $reg[2] == '/swagger.json/root' || $reg[2] == '/resources.json' || $reg[2] == '/resources.json/root'))
  119. {
  120. // Scan all API files to load them
  121. $listofapis = array();
  122. $modulesdir = dolGetModulesDirs();
  123. foreach ($modulesdir as $dir)
  124. {
  125. // Search available module
  126. dol_syslog("Scan directory ".$dir." for module descriptor files, then search for API files");
  127. $handle = @opendir(dol_osencode($dir));
  128. if (is_resource($handle))
  129. {
  130. while (($file = readdir($handle)) !== false)
  131. {
  132. $regmod = array();
  133. if (is_readable($dir.$file) && preg_match("/^mod(.*)\.class\.php$/i", $file, $regmod))
  134. {
  135. $module = strtolower($regmod[1]);
  136. $moduledirforclass = getModuleDirForApiClass($module);
  137. $modulenameforenabled = $module;
  138. if ($module == 'propale') { $modulenameforenabled = 'propal'; }
  139. if ($module == 'supplierproposal') { $modulenameforenabled = 'supplier_proposal'; }
  140. if ($module == 'ficheinter') { $modulenameforenabled = 'ficheinter'; }
  141. dol_syslog("Found module file ".$file." - module=".$module." - modulenameforenabled=".$modulenameforenabled." - moduledirforclass=".$moduledirforclass);
  142. // Defined if module is enabled
  143. $enabled = true;
  144. if (empty($conf->$modulenameforenabled->enabled)) $enabled = false;
  145. if ($enabled)
  146. {
  147. // If exists, load the API class for enable module
  148. // Search files named api_<object>.class.php into /htdocs/<module>/class directory
  149. // @todo : use getElementProperties() function ?
  150. $dir_part = dol_buildpath('/'.$moduledirforclass.'/class/');
  151. $handle_part = @opendir(dol_osencode($dir_part));
  152. if (is_resource($handle_part))
  153. {
  154. while (($file_searched = readdir($handle_part)) !== false)
  155. {
  156. if ($file_searched == 'api_access.class.php') continue;
  157. $regapi = array();
  158. if (is_readable($dir_part.$file_searched) && preg_match("/^api_(.*)\.class\.php$/i", $file_searched, $regapi))
  159. {
  160. $classname = ucwords($regapi[1]);
  161. $classname = str_replace('_', '', $classname);
  162. require_once $dir_part.$file_searched;
  163. if (class_exists($classname.'Api'))
  164. {
  165. //dol_syslog("Found API by index.php: classname=".$classname."Api for module ".$dir." into ".$dir_part.$file_searched);
  166. $listofapis[strtolower($classname.'Api')] = $classname.'Api';
  167. } elseif (class_exists($classname))
  168. {
  169. //dol_syslog("Found API by index.php: classname=".$classname." for module ".$dir." into ".$dir_part.$file_searched);
  170. $listofapis[strtolower($classname)] = $classname;
  171. } else {
  172. dol_syslog("We found an api_xxx file (".$file_searched.") but class ".$classname." does not exists after loading file", LOG_WARNING);
  173. }
  174. }
  175. }
  176. }
  177. }
  178. }
  179. }
  180. }
  181. }
  182. // Sort the classes before adding them to Restler.
  183. // The Restler API Explorer shows the classes in the order they are added and it's a mess if they are not sorted.
  184. asort($listofapis);
  185. foreach ($listofapis as $apiname => $classname)
  186. {
  187. $api->r->addAPIClass($classname, $apiname);
  188. }
  189. //var_dump($api->r);
  190. }
  191. // Call one APIs or one definition of an API
  192. $regbis = array();
  193. if (!empty($reg[1]) && ($reg[1] != 'explorer' || ($reg[2] != '/swagger.json' && $reg[2] != '/resources.json' && preg_match('/^\/(swagger|resources)\.json\/(.+)$/', $reg[2], $regbis) && $regbis[2] != 'root')))
  194. {
  195. $module = $reg[1];
  196. if ($module == 'explorer') // If we call page to explore details of a service
  197. {
  198. $module = $regbis[2];
  199. }
  200. $module = strtolower($module);
  201. $moduledirforclass = getModuleDirForApiClass($module);
  202. // Load a dedicated API file
  203. dol_syslog("Load a dedicated API file module=".$module." moduledirforclass=".$moduledirforclass);
  204. $tmpmodule = $module;
  205. if ($tmpmodule != 'api')
  206. $tmpmodule = preg_replace('/api$/i', '', $tmpmodule);
  207. $classfile = str_replace('_', '', $tmpmodule);
  208. if ($module == 'supplierproposals')
  209. $classfile = 'supplier_proposals';
  210. if ($module == 'supplierorders')
  211. $classfile = 'supplier_orders';
  212. if ($module == 'supplierinvoices')
  213. $classfile = 'supplier_invoices';
  214. if ($module == 'ficheinter')
  215. $classfile = 'interventions';
  216. if ($module == 'interventions')
  217. $classfile = 'interventions';
  218. $dir_part_file = dol_buildpath('/'.$moduledirforclass.'/class/api_'.$classfile.'.class.php', 0, 2);
  219. $classname = ucwords($module);
  220. dol_syslog('Search api file /'.$moduledirforclass.'/class/api_'.$classfile.'.class.php => dir_part_file='.$dir_part_file.' classname='.$classname);
  221. $res = false;
  222. if ($dir_part_file)
  223. $res = include_once $dir_part_file;
  224. if (!$res) {
  225. dol_syslog('Failed to make include_once '.$dir_part_file, LOG_WARNING);
  226. print 'API not found (failed to include API file)';
  227. header('HTTP/1.1 501 API not found (failed to include API file)');
  228. exit(0);
  229. }
  230. if (class_exists($classname))
  231. $api->r->addAPIClass($classname);
  232. }
  233. //var_dump($api->r->apiVersionMap);
  234. //exit;
  235. // Call API (we suppose we found it).
  236. // The handle will use the file api/temp/routes.php to get data to run the API. If the file exists and the entry for API is not found, it will return 404.
  237. $api->r->handle();