fastsync.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  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 Array with the request parameters.
  133. * @return array {
  134. * @type string client Client identifier. Empty if not provided.
  135. * @type string user User identifier. Empty if not provided.
  136. * @type int foreground 1 if the sync is being made in the app
  137. * foreground, 0 otherwise.
  138. * @type string ip Client IP.
  139. * @type string os Client operating system identifier. Empty if not
  140. * found.
  141. * @type string browser Client browser identifier. Empty if not found.
  142. * @type string uagent Client user agent. Empty if not found.
  143. * @type string error Will contain ERR_CLIENT if the client was not
  144. * specified, empty otherwise.
  145. * }
  146. */
  147. function get_user_info($con, $get){
  148. $info = array();
  149. $error = "";
  150. $info["client"] = extract_param($con, $get, GET_CLIENT);
  151. if(strlen($info["client"]) == 0) {
  152. error_log("SYNC ERROR: Trying to sync with no client name.");
  153. $error = ERR_CLIENT;
  154. }
  155. $info["user"] = extract_param($con, $get, GET_USER);
  156. $info["foreground"] = (int) extract_param($con, $get, GET_FOREGROUND);
  157. if($info["foreground"] != 1){
  158. $info["foreground"] = 0;
  159. }
  160. $info["ip"] = get_user_ip();
  161. $browser_data = get_browser(null, true);
  162. $info["os"] = $browser_data['platform'];
  163. $info["browser"] = $browser_data['browser'];
  164. $info["uagent"] = $browser_data['browser_name_pattern'];
  165. $info["error"] = $error;
  166. return $info;
  167. }
  168. /**
  169. * Reads the table version in the client app.
  170. *
  171. * Reads the version of the tables reported by the user as GET parameters.
  172. * Those parameters must be the same as the table names listed in
  173. * {@see $tab_list}.
  174. *
  175. * @since 3.0.0
  176. * @global array $tab_list Array with the names of the tables to sync.
  177. * @param object $con Open database connection.
  178. * @param array $get Array with the request parameters.
  179. * @return array Integer array with the version of the tables reported in
  180. * the request, keyed with the table names. If no table
  181. * version was specified, the array will be empty.
  182. */
  183. function get_user_versions($con, $get){
  184. global $tab_list;
  185. $versions = array();
  186. foreach($tab_list as $tab){
  187. $versions[$tab] = intval(extract_param($con, $get, $tab));
  188. }
  189. return $versions;
  190. }
  191. /**
  192. * Reads the table version in the server.
  193. *
  194. * Reads from the database the version of the tables that sync with the
  195. * clients and have important data.
  196. *
  197. * @since 3.0.0
  198. * @global array $fast_tables Array with the names of the tables to sync.
  199. * @param object $con Open database connection.
  200. * @return array Integer array with the version of the tables in the
  201. * database, keyed with the table names.
  202. */
  203. function get_server_versions($con){
  204. global $fast_tables;
  205. $versions = array();
  206. $q = mysqli_query($con, "SELECT section, version FROM version;");
  207. while($r = mysqli_fetch_array($q)){
  208. if (in_array($r['section'], $fast_tables)){
  209. $versions[$r['section']] = "0";
  210. }
  211. else {
  212. $versions[$r['section']] = $r['version'];
  213. }
  214. }
  215. return $versions;
  216. }
  217. /**
  218. * Select the tables that need to be synced.
  219. *
  220. * Determines the tables in {@see $tab_list} that are out of sync between
  221. * the server and the client.
  222. *
  223. * @since 3.0.0
  224. * @global array $tab_list Array with the names of the tables that sync
  225. * whit clients.
  226. * @param object $con Open database connection.
  227. * @param array $user Integer array with the version of the tables reported
  228. * in the request, keyed with the table names.
  229. * @param array $server Integer array with the version of the tables in the
  230. * database, keyed with the table names.
  231. * @return array String array with the name of the tables present in $user whose
  232. * versions are lower than the ones in $server.
  233. */
  234. function select_tables($user, $server){
  235. global $tab_list;
  236. $tables = array();
  237. foreach($tab_list as $table){
  238. if (intval($user[$table]) < intval($server[$table]) || intval($server[$table]) == 0){
  239. array_push($tables, $table);
  240. }
  241. }
  242. return $tables;
  243. }
  244. /**
  245. * JSON-izes the versions of the tables to sync.
  246. *
  247. * Generates a JSON-formatted string with the versions of all the tables
  248. * to sync.
  249. *
  250. * @since 3.0.0
  251. * @global array $fast_tables Array with the names of the tables that sync
  252. * whit clients and have important data.
  253. * @global array $slow_tables Array with the names of the tables that sync
  254. * whit clients and have less important data.
  255. * @param object $con Open database connection.
  256. * @param array $tables String array with the names of the tables.
  257. * @return string JSON-formatted string with the version of the tables.
  258. * Empty string if no valid table names were passes in $tables.
  259. */
  260. function get_table_version($con, $tables){
  261. global $fast_tables;
  262. global $slow_tables;
  263. // Build query, showing only tables to sync
  264. $s = "SELECT section, version FROM version WHERE ";
  265. foreach($tables as $table){
  266. if (in_array($table, $fast_tables) == false){
  267. $s = $s . "section = '$table' OR ";
  268. }
  269. }
  270. $s = $s . "1 = 2 ";
  271. $s = $s . "UNION SELECT section, 0 AS version FROM version WHERE ";
  272. foreach($tables as $table){
  273. if (in_array($table, $fast_tables)){
  274. $s = $s . "section = '$table' OR ";
  275. }
  276. }
  277. $s = $s . "1 = 2;";
  278. $q = mysqli_query($con, $s);
  279. //If no rows, return
  280. if (mysqli_num_rows($q) == 0){
  281. return "";
  282. }
  283. //Create result array
  284. $str = "";
  285. $str = $str. "\"version\":[";
  286. while($r = mysqli_fetch_assoc($q)) {
  287. $str = $str . json_encode($r) . ",";
  288. }
  289. $str = rtrim($str,',');
  290. $str = $str . "],";
  291. return $str;
  292. }
  293. /**
  294. * JSON-izes the data in a table.
  295. *
  296. * Generates a JSON-formatted string with the data in a table. Inaccessible
  297. * or sensitive tables or fields are not returned.
  298. *
  299. * @since 3.0.0
  300. * @param object $con Open database connection.
  301. * @param string $table Table name.
  302. * @return string JSON-formatted string with the data in the table. Empty
  303. * string if $table was not a valid table name.
  304. */
  305. function get_table($con, $table){
  306. $year = date("Y");
  307. $table = strtolower($table);
  308. switch ($table){
  309. case TAB_ACTIVITY:
  310. $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;");
  311. break;
  312. case TAB_ALBUM:
  313. $q = mysqli_query($con, "SELECT id, permalink, title_es, title_en, title_eu, description_es, description_en, description_eu, open FROM album;");
  314. break;
  315. case TAB_PHOTO:
  316. $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;;");
  317. break;
  318. case TAB_POST:
  319. $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;");
  320. break;
  321. case TAB_SPONSOR:
  322. $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;");
  323. break;
  324. case TAB_SETTINGS:
  325. $q = mysqli_query($con, "SELECT name, value FROM settings;");
  326. break;
  327. case TAB_FESTIVAL_EVENT_GM:
  328. $q = mysqli_query($con, "SELECT * FROM festival_event_gm WHERE year(start) = $year AND interest >= 1;");
  329. break;
  330. case TAB_FESTIVAL:
  331. $q = mysqli_query($con, "SELECT * FROM festival WHERE year = $year;");
  332. break;
  333. case TAB_FESTIVAL_DAY:
  334. $q = mysqli_query($con, "SELECT * FROM festival_day WHERE year(date) = $year;");
  335. break;
  336. case TAB_FESTIVAL_OFFER:
  337. $q = mysqli_query($con, "SELECT * FROM festival_offer WHERE year = $year;");
  338. break;
  339. case TAB_FESTIVAL_EVENT_CITY:
  340. $q = mysqli_query($con, "SELECT * FROM festival_event_city WHERE year(start) = $year AND interest >= 1;");
  341. break;
  342. case TAB_ACTIVITY_IMAGE:
  343. $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;");
  344. break;
  345. case TAB_ACTIVITY_ITINERARY:
  346. $q = mysqli_query($con, "SELECT * FROM activity_itinerary WHERE year(start) = $year;");
  347. break;
  348. case TAB_PHOTO_ALBUM:
  349. $q = mysqli_query($con, "SELECT photo, album FROM photo_album, photo WHERE photo = photo.id AND year(uploaded) = $year;");
  350. break;
  351. case TAB_POST_IMAGE:
  352. $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;");
  353. //Other cases:
  354. default:
  355. $q = mysqli_query($con, "SELECT * FROM $table;");
  356. }
  357. //If no rows, return
  358. if (mysqli_num_rows($q) == 0){
  359. return "";
  360. }
  361. //Create result array
  362. $str = "";
  363. $str = $str. "\"$table\":[";
  364. while($r = mysqli_fetch_assoc($q)) {
  365. $str = $str . json_encode($r) . ",";
  366. }
  367. $str = rtrim($str,",");
  368. $str = $str . "]";
  369. return $str;
  370. }
  371. /**
  372. * Gets the data on the requested tables.
  373. *
  374. * Builds a JSON string with the data in all the requested tables.
  375. * Inaccessible or sensitive tables or fields are not returned.
  376. *
  377. * @since 3.0.0
  378. * @see get_table($con, $table)
  379. * @param object $con Open database connection.
  380. * @param array $tables String array with the names of the tables to sync.
  381. * @return string JSON-formatted string with the data in the requested
  382. * tables. Empty string if no valid table names were
  383. * provided in $tables.
  384. */
  385. function sync($con, $tables){
  386. $str = "";
  387. if(sizeof($tables) > 0){
  388. $str = "{" . get_table_version($con, $tables);
  389. foreach($tables as $table){
  390. $str = $str . get_table($con, $table) . ",";
  391. }
  392. $str = rtrim($str, ",");
  393. $str = $str . "}";
  394. $str = str_replace(",,", ",", $str);
  395. echo($str);
  396. return true;
  397. }
  398. return false;
  399. }
  400. /**
  401. * Gets the user IP address.
  402. *
  403. * @since 3.0.0
  404. * @return string User IP address.
  405. */
  406. function get_user_ip(){
  407. $client = @$_SERVER['HTTP_CLIENT_IP'];
  408. $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
  409. $remote = $_SERVER['REMOTE_ADDR'];
  410. if(filter_var($client, FILTER_VALIDATE_IP)){
  411. $ip = $client;
  412. }
  413. elseif(filter_var($forward, FILTER_VALIDATE_IP)){
  414. $ip = $forward;
  415. }
  416. else{
  417. $ip = $remote;
  418. }
  419. return $ip;
  420. }
  421. /**
  422. * Logs a request to the database.
  423. *
  424. * Creates an entry in the table 'sync' with the details of the request.
  425. *
  426. * @since 1.0.0
  427. * @param object $con Open database connection.
  428. * @param array $user {
  429. * @type string client Client identifier. Empty if not provided.
  430. * @type string user User identifier. Empty if not provided.
  431. * @type int foreground 1 if the sync is being made in the app
  432. * foreground, 0 otherwise.
  433. * @type string ip Client IP.
  434. * @type string os Client operating system identifier. Empty if not
  435. * found.
  436. * @type string browser Client browser identifier. Empty if not found.
  437. * @type string uagent Client user agent. Empty if not found.
  438. * }
  439. * @param int synced 1 if sync data was finally sent, 0 otherwise.
  440. */
  441. function log_sync($con, $user, $synced){
  442. 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]');");
  443. }
  444. /**
  445. * Logs a failed request to the database.
  446. *
  447. * Creates an entry in the table 'sync' with the details of the failed request.
  448. *
  449. * @since 1.0.0
  450. * @param object $con Open database connection.
  451. * @param array $user {
  452. * @type string client Client identifier. Empty if not provided.
  453. * @type string user User identifier. Empty if not provided.
  454. * @type int foreground 1 if the sync is being made in the app
  455. * foreground, 0 otherwise.
  456. * @type string ip Client IP.
  457. * @type string os Client operating system identifier. Empty if not
  458. * found.
  459. * @type string browser Client browser identifier. Empty if not found.
  460. * @type string uagent Client user agent. Empty if not found.
  461. * @type string error Error code.
  462. * }
  463. */
  464. function log_error($con, $user){
  465. 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]');");
  466. }
  467. // SCRIPT START
  468. // Connect to the database
  469. $con = startdb('rw');
  470. // Get info about the user
  471. $user = get_user_info($con, $_GET);
  472. if(strlen($user["error"]) > 0){
  473. log_error($con, $user);
  474. http_response_code(400);
  475. exit(-1);
  476. }
  477. // Get tables to sync
  478. $v_user = get_user_versions($con, $_GET);
  479. $v_server = get_server_versions($con);
  480. $tables = select_tables($v_user, $v_server);
  481. $synced = sync($con, $tables);
  482. //Log the sync in the database
  483. log_sync($con, $user, $synced);
  484. ?>