fastsync.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. <?php
  2. /**
  3. * Gasteizko Margolariak API v3 - Fast Sync
  4. *
  5. * Used to sync data with apps with persistent storage (i.e. no web apps).
  6. * This file is to be called directly from a URL request.
  7. *
  8. * It is intended to sync only the most important tables, as in an app
  9. * initial sync.
  10. *
  11. * @link https://margolariak.com/API/v3/help/
  12. *
  13. * @since 3.0.0
  14. */
  15. //Database section identifiers
  16. define('SEC_ALL', 'all');
  17. define('SEC_BLOG', 'blog');
  18. define('SEC_ACTIVITIES', 'activities');
  19. define('SEC_GALLERY', 'gallery');
  20. define('SEC_LABLANCA', 'lablanca');
  21. define('TAB_ACTIVITY', 'activity');
  22. define('TAB_ACTIVITY_IMAGE', 'activity_image');
  23. define('TAB_ACTIVITY_ITINERARY', 'activity_itinerary');
  24. define('TAB_ALBUM', 'album');
  25. define('TAB_FESTIVAL', 'festival');
  26. define('TAB_FESTIVAL_DAY', 'festival_day');
  27. define('TAB_FESTIVAL_EVENT_CITY', 'festival_event_city');
  28. define('TAB_FESTIVAL_EVENT_GM', 'festival_event_gm');
  29. define('TAB_FESTIVAL_OFFER', 'festival_offer');
  30. define('TAB_PEOPLE', 'people');
  31. define('TAB_PHOTO', 'photo');
  32. define('TAB_PHOTO_ALBUM', 'photo_album');
  33. define('TAB_PLACE', 'place');
  34. define('TAB_POST', 'post');
  35. define('TAB_POST_IMAGE', 'post_image');
  36. define('TAB_ROUTE', 'route');
  37. define('TAB_ROUTE_POINT', 'route_point');
  38. define('TAB_SETTINGS', 'settings');
  39. define('TAB_SPONSOR', 'sponsor');
  40. //$_GET valid parameters
  41. define('GET_CLIENT', 'client');
  42. define('GET_USER', 'user');
  43. define('GET_FOREGROUND', 'foreground');
  44. //Error messages
  45. define('ERR_CLIENT', 'CLIENT');
  46. /**
  47. * List of all tables that can be synced, sorted by priority / dependencies.
  48. *
  49. * @var string $tab_list
  50. */
  51. $tab_list = array(TAB_SETTINGS, TAB_PLACE, TAB_ROUTE_POINT,
  52. TAB_ROUTE, TAB_PEOPLE, TAB_FESTIVAL_EVENT_GM,
  53. TAB_FESTIVAL, TAB_FESTIVAL_DAY, TAB_FESTIVAL_OFFER,
  54. TAB_FESTIVAL_EVENT_CITY, TAB_ACTIVITY, TAB_ACTIVITY_IMAGE,
  55. TAB_ACTIVITY_ITINERARY, TAB_SPONSOR, TAB_ALBUM,
  56. TAB_PHOTO, TAB_PHOTO_ALBUM, TAB_POST,
  57. TAB_POST_IMAGE);
  58. /**
  59. * List of tables with content considered important.
  60. *
  61. * @var string $tab_list
  62. */
  63. $fast_tables = array(TAB_FESTIVAL_EVENT_GM, TAB_FESTIVAL, TAB_FESTIVAL_DAY,
  64. TAB_FESTIVAL_OFFER, TAB_FESTIVAL_EVENT_CITY, TAB_ACTIVITY,
  65. TAB_ACTIVITY_IMAGE, TAB_ACTIVITY_ITINERARY, TAB_PHOTO,
  66. TAB_PHOTO_ALBUM, TAB_POST, TAB_POST_IMAGE);
  67. /**
  68. * List of tables with not-so-much relevant data.
  69. *
  70. * @var string $tab_list
  71. */
  72. $slow_tables = array(TAB_SETTINGS, TAB_PLACE, TAB_ROUTE_POINT,
  73. TAB_ROUTE, TAB_PEOPLE, TAB_SPONSOR,
  74. TAB_ALBUM);
  75. /**
  76. * Initializes the MySQL database connection.
  77. *
  78. * Called at the beggining of the script. It connects to the database using the
  79. * parameters in the .htpasswd file. It also sets database and page encodings.
  80. *
  81. * @since 3.0.0
  82. * @return object Database connection.
  83. */
  84. function startdb(){
  85. //Include the db configuration file. It's somehow like this
  86. /*
  87. <?php
  88. $host = 'XXXX';
  89. $db_name = 'XXXX';
  90. $username_ro = 'XXXX';
  91. $username_rw = 'XXXX';
  92. $pass_ro = 'XXXX';
  93. $pass_rw = 'XXXX';
  94. ?>
  95. */
  96. include('../../.htpasswd');
  97. //Connect to to database
  98. $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
  99. //Set encoding options
  100. mysqli_set_charset($con, 'utf-8');
  101. header('Content-Type: text/html; charset=utf8');
  102. mysqli_query($con, 'SET NAMES utf8;');
  103. //Return the db connection
  104. return $con;
  105. }
  106. /**
  107. * Extracts a request parameter.
  108. *
  109. * Extracts the value of a parameter from the list of GET parameters,
  110. * sanitizing it to prevent SQL injections.
  111. *
  112. * @since 3.0.0
  113. * @param object $con Open database connection.
  114. * @param string $param Key of the parameter to retrieve.
  115. * @return string Value of the parameter or an empty string if it was not found.
  116. */
  117. function extract_param($con, $get, $param){
  118. if(isset($_GET[$param])){
  119. return mysqli_real_escape_string($con, $_GET[$param]);
  120. }
  121. else{
  122. return "";
  123. }
  124. }
  125. /**
  126. * Gets request info.
  127. *
  128. * Gets information about the request by reading it's parameters.
  129. *
  130. * @since 3.0.0
  131. * @param object $con Open database connection.
  132. * @param array $get Optional. Array with the request parameters. Default
  133. * is $_GET.
  134. * @return array {
  135. * @type string client Client identifier. Empty if not provided.
  136. * @type string user User identifier. Empty if not provided.
  137. * @type int foreground 1 if the sync is being made in the app
  138. * foreground, 0 otherwise.
  139. * @type string ip Client IP.
  140. * @type string os Client operating system identifier. Empty if not
  141. * found.
  142. * @type string browser Client browser identifier. Empty if not found.
  143. * @type string uagent Client user agent. Empty if not found.
  144. * @type string error Will contain ERR_CLIENT if the client was not
  145. * specified, empty otherwise.
  146. * }
  147. */
  148. function get_user_info($con, $get = $GET){
  149. $info = array();
  150. $error = "";
  151. $info["client"] = extract_param($con, $get, GET_CLIENT);
  152. if(strlen($info["client"]) == 0) {
  153. error_log("SYNC ERROR: Trying to sync with no client name.");
  154. $error = ERR_CLIENT;
  155. }
  156. $info["user"] = extract_param($con, $get, GET_USER);
  157. $info["foreground"] = (int) extract_param($con, $get, GET_FOREGROUND);
  158. if($info["foreground"] != 1){
  159. $info["foreground"] = 0;
  160. }
  161. $info["ip"] = get_user_ip();
  162. $browser_data = get_browser(null, true);
  163. $info["os"] = $browser_data['platform'];
  164. $info["browser"] = $browser_data['browser'];
  165. $info["uagent"] = $browser_data['browser_name_pattern'];
  166. $info["error"] = $error;
  167. return $info;
  168. }
  169. /**
  170. * Reads the table version in the client app.
  171. *
  172. * Reads the version of the tables reported by the user as GET parameters.
  173. * Those parameters must be the same as the table names listed in
  174. * {@see $tab_list}.
  175. *
  176. * @since 3.0.0
  177. * @global array $tab_list Array with the names of the tables to sync.
  178. * @param object $con Open database connection.
  179. * @param array $get Optional. Array with the request parameters. Default
  180. * is $_GET.
  181. * @return array Integer array with the version of the tables reported in
  182. * the request, keyed with the table names. If no table
  183. * version was specified, the array will be empty.
  184. */
  185. function get_user_versions($con, $get = $_GET){
  186. global $tab_list;
  187. $versions = array();
  188. foreach($tab_list as $tab){
  189. $versions[$tab] = intval(extract_param($con, $get, $tab));
  190. }
  191. return $versions;
  192. }
  193. /**
  194. * Reads the table version in the server.
  195. *
  196. * Reads from the database the version of the tables that sync with the
  197. * clients and have important data.
  198. *
  199. * @since 3.0.0
  200. * @global array $fast_tables Array with the names of the tables to sync.
  201. * @param object $con Open database connection.
  202. * @return array Integer array with the version of the tables in the
  203. * database, keyed with the table names.
  204. */
  205. function get_server_versions($con){
  206. global $fast_tables;
  207. $versions = array();
  208. $q = mysqli_query($con, "SELECT section, version FROM version;");
  209. while($r = mysqli_fetch_array($q)){
  210. if (in_array($r['section'], $fast_tables)){
  211. $versions[$r['section']] = "0";
  212. }
  213. else {
  214. $versions[$r['section']] = $r['version'];
  215. }
  216. }
  217. return $versions;
  218. }
  219. /**
  220. * Select the tables that need to be synced.
  221. *
  222. * Determines the tables in {@see $tab_list} that are out of sync between
  223. * the server and the client.
  224. *
  225. * @since 3.0.0
  226. * @global array $tab_list Array with the names of the tables that sync
  227. * whit clients.
  228. * @param object $con Open database connection.
  229. * @param array $user Integer array with the version of the tables reported
  230. * in the request, keyed with the table names.
  231. * @param array $server Integer array with the version of the tables in the
  232. * database, keyed with the table names.
  233. * @return array String array with the name of the tables present in $user whose
  234. * versions are lower than the ones in $server.
  235. */
  236. function select_tables($user, $server){
  237. global $tab_list;
  238. $tables = array();
  239. foreach($tab_list as $table){
  240. if (intval($user[$table]) < intval($server[$table]) || intval($server[$table]) == 0){
  241. array_push($tables, $table);
  242. }
  243. }
  244. return $tables;
  245. }
  246. /**
  247. * JSON-izes the versions of the tables to sync.
  248. *
  249. * Generates a JSON-formatted string with the versions of all the tables
  250. * to sync.
  251. *
  252. * @since 3.0.0
  253. * @global array $fast_tables Array with the names of the tables that sync
  254. * whit clients and have important data.
  255. * @global array $slow_tables Array with the names of the tables that sync
  256. * whit clients and have less important data.
  257. * @param object $con Open database connection.
  258. * @param array $tables String array with the names of the tables.
  259. * @return string JSON-formatted string with the version of the tables.
  260. * Empty string if no valid table names were passes in $tables.
  261. */
  262. function get_table_version($con, $tables){
  263. global $fast_tables;
  264. global $slow_tables;
  265. // Build query, showing only tables to sync
  266. $s = "SELECT section, version FROM version WHERE ";
  267. foreach($tables as $table){
  268. if (in_array($table, $fast_tables) == false){
  269. $s = $s . "section = '$table' OR ";
  270. }
  271. }
  272. $s = $s . "1 = 2 ";
  273. $s = $s . "UNION SELECT section, 0 AS version FROM version WHERE ";
  274. foreach($tables as $table){
  275. if (in_array($table, $fast_tables)){
  276. $s = $s . "section = '$table' OR ";
  277. }
  278. }
  279. $s = $s . "1 = 2;";
  280. $q = mysqli_query($con, $s);
  281. //If no rows, return
  282. if (mysqli_num_rows($q) == 0){
  283. return "";
  284. }
  285. //Create result array
  286. $str = "";
  287. $str = $str. "\"version\":[";
  288. while($r = mysqli_fetch_assoc($q)) {
  289. $str = $str . json_encode($r) . ",";
  290. }
  291. $str = rtrim($str,',');
  292. $str = $str . "],";
  293. return $str;
  294. }
  295. /**
  296. * JSON-izes the data in a table.
  297. *
  298. * Generates a JSON-formatted string with the data in a table. Inaccessible
  299. * or sensitive tables or fields are not returned.
  300. *
  301. * @since 3.0.0
  302. * @param object $con Open database connection.
  303. * @param string $table Table name.
  304. * @return string JSON-formatted string with the data in the table. Empty
  305. * string if $table was not a valid table name.
  306. */
  307. function get_table($con, $table){
  308. $year = date("Y");
  309. $table = strtolower($table);
  310. switch ($table){
  311. case TAB_ACTIVITY:
  312. $q = mysqli_query($con, "SELECT id, permalink, date, city, title_es, title_en, title_eu, text_es, text_eu, text_en, after_es, after_en, after_eu, price, inscription, max_people, album FROM activity WHERE visible = 1 AND year(date) = $year;");
  313. break;
  314. case TAB_ALBUM:
  315. $q = mysqli_query($con, "SELECT id, permalink, title_es, title_en, title_eu, description_es, description_en, description_eu, open FROM album;");
  316. break;
  317. case TAB_PHOTO:
  318. $q = mysqli_query($con, "SELECT photo.id AS id, file, permalink, title_es, title_en, title_eu, description_es, description_en, description_eu, uploaded, place, width, height, size, CONCAT(photo.username, user) AS username FROM photo, user WHERE user.id = photo.user AND approved = 1 AND year(uploaded) = $year;;");
  319. break;
  320. case TAB_POST:
  321. $q = mysqli_query($con, "SELECT post.id AS id, permalink, title_es, title_en, title_eu, text_es, text_en, text_eu, comments, username, dtime FROM post, user WHERE user.id = user AND visible = 1 AND year(dtime) = $year;");
  322. break;
  323. case TAB_SPONSOR:
  324. $q = mysqli_query($con, "SELECT id, name_es, name_en, name_eu, text_es, text_en, text_eu, image, address_es, address_en, address_eu, link, lat, lon FROM sponsor;");
  325. break;
  326. case TAB_SETTINGS:
  327. $q = mysqli_query($con, "SELECT name, value FROM settings;");
  328. break;
  329. case TAB_FESTIVAL_EVENT_GM:
  330. $q = mysqli_query($con, "SELECT * FROM festival_event_gm WHERE year(start) = $year AND interest >= 1;");
  331. break;
  332. case TAB_FESTIVAL:
  333. $q = mysqli_query($con, "SELECT * FROM festival WHERE year = $year;");
  334. break;
  335. case TAB_FESTIVAL_DAY:
  336. $q = mysqli_query($con, "SELECT * FROM festival_day WHERE year(date) = $year;");
  337. break;
  338. case TAB_FESTIVAL_OFFER:
  339. $q = mysqli_query($con, "SELECT * FROM festival_offer WHERE year = $year;");
  340. break;
  341. case TAB_FESTIVAL_EVENT_CITY:
  342. $q = mysqli_query($con, "SELECT * FROM festival_event_city WHERE year(start) = $year AND interest >= 1;");
  343. break;
  344. case TAB_ACTIVITY_IMAGE:
  345. $q = mysqli_query($con, "SELECT activity_image.id AS id, activity, image, idx FROM activity_image, activity WHERE activity = activity.id AND year(date) = $year;");
  346. break;
  347. case TAB_ACTIVITY_ITINERARY:
  348. $q = mysqli_query($con, "SELECT * FROM activity_itinerary WHERE year(start) = $year;");
  349. break;
  350. case TAB_PHOTO_ALBUM:
  351. $q = mysqli_query($con, "SELECT photo, album FROM photo_album, photo WHERE photo = photo.id AND year(uploaded) = $year;");
  352. break;
  353. case TAB_POST_IMAGE:
  354. $q = mysqli_query($con, "SELECT post_image.id AS id, post, image, idx FROM post_image, post WHERE post = post.id AND year(dtime) = $year;");
  355. //Other cases:
  356. default:
  357. $q = mysqli_query($con, "SELECT * FROM $table;");
  358. }
  359. //If no rows, return
  360. if (mysqli_num_rows($q) == 0){
  361. return "";
  362. }
  363. //Create result array
  364. $str = "";
  365. $str = $str. "\"$table\":[";
  366. while($r = mysqli_fetch_assoc($q)) {
  367. $str = $str . json_encode($r) . ",";
  368. }
  369. $str = rtrim($str,",");
  370. $str = $str . "]";
  371. return $str;
  372. }
  373. /**
  374. * Gets the data on the requested tables.
  375. *
  376. * Builds a JSON string with the data in all the requested tables.
  377. * Inaccessible or sensitive tables or fields are not returned.
  378. *
  379. * @since 3.0.0
  380. * @see get_table($con, $table)
  381. * @param object $con Open database connection.
  382. * @param array $tables String array with the names of the tables to sync.
  383. * @return string JSON-formatted string with the data in the requested
  384. * tables. Empty string if no valid table names were
  385. * provided in $tables.
  386. */
  387. function sync($con, $tables){
  388. $str = "";
  389. if(sizeof($tables) > 0){
  390. $str = "{" . get_table_version($con, $tables);
  391. foreach($tables as $table){
  392. $str = $str . get_table($con, $table) . ",";
  393. }
  394. $str = rtrim($str, ",");
  395. $str = $str . "}";
  396. $str = str_replace(",,", ",", $str);
  397. echo($str);
  398. return true;
  399. }
  400. return false;
  401. }
  402. /**
  403. * Gets the user IP address.
  404. *
  405. * @since 3.0.0
  406. * @return string User IP address.
  407. */
  408. function get_user_ip(){
  409. $client = @$_SERVER['HTTP_CLIENT_IP'];
  410. $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
  411. $remote = $_SERVER['REMOTE_ADDR'];
  412. if(filter_var($client, FILTER_VALIDATE_IP)){
  413. $ip = $client;
  414. }
  415. elseif(filter_var($forward, FILTER_VALIDATE_IP)){
  416. $ip = $forward;
  417. }
  418. else{
  419. $ip = $remote;
  420. }
  421. return $ip;
  422. }
  423. /**
  424. * Logs a request to the database.
  425. *
  426. * Creates an entry in the table 'sync' with the details of the request.
  427. *
  428. * @since 1.0.0
  429. * @param object $con Open database connection.
  430. * @param array $user {
  431. * @type string client Client identifier. Empty if not provided.
  432. * @type string user User identifier. Empty if not provided.
  433. * @type int foreground 1 if the sync is being made in the app
  434. * foreground, 0 otherwise.
  435. * @type string ip Client IP.
  436. * @type string os Client operating system identifier. Empty if not
  437. * found.
  438. * @type string browser Client browser identifier. Empty if not found.
  439. * @type string uagent Client user agent. Empty if not found.
  440. * }
  441. * @param int synced 1 if sync data was finally sent, 0 otherwise.
  442. */
  443. function log_sync($con, $user, $synced){
  444. mysqli_query($con, "INSERT INTO sync (client, user, fg, synced, ip, os, uagent) VALUES ('$user[client]', '$user[user]', $user[foreground], $synced, '$user[ip]', '$user[os]', '$user[uagent]');");
  445. }
  446. /**
  447. * Logs a failed request to the database.
  448. *
  449. * Creates an entry in the table 'sync' with the details of the failed request.
  450. *
  451. * @since 1.0.0
  452. * @param object $con Open database connection.
  453. * @param array $user {
  454. * @type string client Client identifier. Empty if not provided.
  455. * @type string user User identifier. Empty if not provided.
  456. * @type int foreground 1 if the sync is being made in the app
  457. * foreground, 0 otherwise.
  458. * @type string ip Client IP.
  459. * @type string os Client operating system identifier. Empty if not
  460. * found.
  461. * @type string browser Client browser identifier. Empty if not found.
  462. * @type string uagent Client user agent. Empty if not found.
  463. * @type string error Error code.
  464. * }
  465. */
  466. function log_error($con, $user){
  467. mysqli_query($con, "INSERT INTO sync (client, user, fg, error, ip, os, uagent) VALUES ('$user[client]', '$user[user]', $user[foreground], $user[error], '$user[ip]', '$user[os]', '$user[uagent]');");
  468. }
  469. // SCRIPT START
  470. // Connect to the database
  471. $con = startdb('rw');
  472. // Get info about the user
  473. $user = get_user_info($con, $_GET);
  474. if(strlen($user["error"]) > 0){
  475. log_error($con, $user);
  476. http_response_code(400);
  477. exit(-1);
  478. }
  479. // Get tables to sync
  480. $v_user = get_user_versions($con, $_GET);
  481. $v_server = get_server_versions($con);
  482. $tables = select_tables($v_user, $v_server);
  483. $synced = sync($con, $tables);
  484. //Log the sync in the database
  485. log_sync($con, $user, $synced);
  486. ?>