fastsync.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. <?php
  2. // Gasteizko Margolariak API v3 //
  3. //Database section identifiers
  4. define('SEC_ALL', 'all');
  5. define('SEC_BLOG', 'blog');
  6. define('SEC_ACTIVITIES', 'activities');
  7. define('SEC_GALLERY', 'gallery');
  8. define('SEC_LABLANCA', 'lablanca');
  9. define('TAB_ACTIVITY', 'activity');
  10. define('TAB_ACTIVITY_IMAGE', 'activity_image');
  11. define('TAB_ACTIVITY_ITINERARY', 'activity_itinerary');
  12. define('TAB_ALBUM', 'album');
  13. define('TAB_FESTIVAL', 'festival');
  14. define('TAB_FESTIVAL_DAY', 'festival_day');
  15. define('TAB_FESTIVAL_EVENT_CITY', 'festival_event_city');
  16. define('TAB_FESTIVAL_EVENT_GM', 'festival_event_gm');
  17. define('TAB_FESTIVAL_OFFER', 'festival_offer');
  18. define('TAB_PEOPLE', 'people');
  19. define('TAB_PHOTO', 'photo');
  20. define('TAB_PHOTO_ALBUM', 'photo_album');
  21. define('TAB_PLACE', 'place');
  22. define('TAB_POST', 'post');
  23. define('TAB_POST_IMAGE', 'post_image');
  24. define('TAB_ROUTE', 'route');
  25. define('TAB_ROUTE_POINT', 'route_point');
  26. define('TAB_SETTINGS', 'settings');
  27. define('TAB_SPONSOR', 'sponsor');
  28. //$_GET valid parameters
  29. define('GET_CLIENT', 'client');
  30. define('GET_USER', 'user');
  31. define('GET_FOREGROUND', 'foreground');
  32. //Error messages
  33. define('ERR_CLIENT', 'CLIENT');
  34. //List of all tables to sync, sorted by priority.
  35. $tab_list = array(TAB_SETTINGS, TAB_PLACE, TAB_ROUTE_POINT,
  36. TAB_ROUTE, TAB_PEOPLE, TAB_FESTIVAL_EVENT_GM,
  37. TAB_FESTIVAL, TAB_FESTIVAL_DAY, TAB_FESTIVAL_OFFER,
  38. TAB_FESTIVAL_EVENT_CITY, TAB_ACTIVITY, TAB_ACTIVITY_IMAGE,
  39. TAB_ACTIVITY_ITINERARY, TAB_SPONSOR, TAB_ALBUM,
  40. TAB_PHOTO, TAB_PHOTO_ALBUM, TAB_POST,
  41. TAB_POST_IMAGE);
  42. $fast_tables = array(TAB_FESTIVAL_EVENT_GM, TAB_FESTIVAL, TAB_FESTIVAL_DAY,
  43. TAB_FESTIVAL_OFFER, TAB_FESTIVAL_EVENT_CITY, TAB_ACTIVITY,
  44. TAB_ACTIVITY_IMAGE, TAB_ACTIVITY_ITINERARY, TAB_PHOTO,
  45. TAB_PHOTO_ALBUM, TAB_POST, TAB_POST_IMAGE);
  46. $slow_tables = array(TAB_SETTINGS, TAB_PLACE, TAB_ROUTE_POINT,
  47. TAB_ROUTE, TAB_PEOPLE, TAB_SPONSOR,
  48. TAB_ALBUM);
  49. /*****************************************************
  50. * This function is called from almost everywhere at *
  51. * the beggining of the page. It initializes the *
  52. * session variables and connects to the db. *
  53. * *
  54. * @return: (MySQL server connection): The *
  55. * connection handler. *
  56. ****************************************************/
  57. function startdb(){
  58. //Include the db configuration file. It's somehow like this
  59. /*
  60. <?php
  61. $host = 'XXXX';
  62. $db_name = 'XXXX';
  63. $username_ro = 'XXXX';
  64. $username_rw = 'XXXX';
  65. $pass_ro = 'XXXX';
  66. $pass_rw = 'XXXX';
  67. ?>
  68. */
  69. include('../../.htpasswd');
  70. //Connect to to database
  71. $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
  72. //Set encoding options
  73. mysqli_set_charset($con, 'utf-8');
  74. header('Content-Type: text/html; charset=utf8');
  75. mysqli_query($con, 'SET NAMES utf8;');
  76. //Return the db connection
  77. return $con;
  78. }
  79. /*****************************************************
  80. * Selects the value of a parameter from the list of *
  81. * GET arguments. It also sanitizes it to prevent *
  82. * SQL injections. *
  83. * *
  84. * @params: *
  85. * con: (MySQL server connection) Db connector. *
  86. * get: (string array) Contains the GET *
  87. * parameters. *
  88. * param: (string) Name of the parameter. *
  89. * @return: (string): Value of the parameter or an *
  90. * empty string if it was not passed. *
  91. *****************************************************/
  92. function extract_param($con, $get, $param){
  93. if(isset($_GET[$param])){
  94. return mysqli_real_escape_string($con, $_GET[$param]);
  95. }
  96. else{
  97. return "";
  98. }
  99. }
  100. /*****************************************************
  101. * Gets information about the API call and the *
  102. * assocciated client. If some mandatory parameter *
  103. * is not provided, a error log entry is registered *
  104. * *
  105. * @params: *
  106. * con: (MySQL server connection) Db connector. *
  107. * get: (string array) Contains the GET *
  108. * parameters. *
  109. * @return: (string array): Array with the keys *
  110. * 'client', 'user', 'foreground', 'ip', *
  111. * 'os', 'browser', 'uagent' and 'error'. *
  112. * 'error' will contain the key of a *
  113. * mandatory value if it has not been *
  114. * provided, or will be empty if there *
  115. * were no problem. *
  116. *****************************************************/
  117. function get_user_info($con, $get){
  118. $info = array();
  119. $error = "";
  120. $info["client"] = extract_param($con, $get, GET_CLIENT);
  121. if(strlen($info["client"]) == 0) {
  122. error_log("SYNC ERROR: Trying to sync with no client name.");
  123. $error = ERR_CLIENT;
  124. }
  125. $info["user"] = extract_param($con, $get, GET_USER);
  126. $info["foreground"] = (int) extract_param($con, $get, GET_FOREGROUND);
  127. if($info["foreground"] != 1){
  128. $info["foreground"] = 0;
  129. }
  130. $info["ip"] = get_user_ip();
  131. $browser_data = get_browser(null, true);
  132. $info["os"] = $browser_data['platform'];
  133. $info["browser"] = $browser_data['browser'];
  134. $info["uagent"] = $browser_data['browser_name_pattern'];
  135. $info["error"] = $error;
  136. return $info;
  137. }
  138. /*****************************************************
  139. * Reads the version of the tables reported by the *
  140. * user as GET parameters. *
  141. * *
  142. * @params: *
  143. * con: (MySQL server connection) Db connector. *
  144. * get: (string array) Contains the GET *
  145. * parameters. *
  146. * @return: (int array): Array with the version of *
  147. * the tables in the user app, keyed with *
  148. * the table names. *
  149. *****************************************************/
  150. function get_user_versions($con, $get){
  151. global $tab_list;
  152. $versions = array();
  153. foreach($tab_list as $tab){
  154. $versions[$tab] = intval(extract_param($con, $get, $tab));
  155. }
  156. return $versions;
  157. }
  158. /*****************************************************
  159. * Reads the version of the tables reported by the *
  160. * user as GET parameters. *
  161. * *
  162. * @params: *
  163. * con: (MySQL server connection) RO mode enough. *
  164. * parameters. *
  165. * @return: (int array): Array with the version of *
  166. * the tables in the server, keyed with *
  167. * the table names. *
  168. *****************************************************/
  169. function get_server_versions($con){
  170. global $fast_tables;
  171. $versions = array();
  172. $q = mysqli_query($con, "SELECT section, version FROM version;");
  173. while($r = mysqli_fetch_array($q)){
  174. if (in_array($r['section'], $fast_tables)){
  175. $versions[$r['section']] = "0";
  176. }
  177. else {
  178. $versions[$r['section']] = $r['version'];
  179. }
  180. }
  181. return $versions;
  182. }
  183. /*****************************************************
  184. * Select the tables that need to be synced. *
  185. * *
  186. * @params: *
  187. * user: (int array) Versions of tables in the *
  188. * user app. *
  189. * server: (int array) Versions of tables in the *
  190. * server. *
  191. * @return: (string array): Array with the names of *
  192. * the tables that need to be synced. *
  193. *****************************************************/
  194. function select_tables($user, $server){
  195. global $tab_list;
  196. $tables = array();
  197. foreach($tab_list as $table){
  198. if (intval($user[$table]) < intval($server[$table]) || intval($server[$table]) == 0){
  199. array_push($tables, $table);
  200. }
  201. }
  202. return $tables;
  203. }
  204. /*****************************************************
  205. * Formats the contents of ther 'versions' table, *
  206. * Only for the tables that will be synced. *
  207. * *
  208. * @params: *
  209. * con: (MySQL server connection) Db connector. *
  210. * tables (String array): List of table. *
  211. * @return: (Assoc Array): Data in the table. *
  212. ****************************************************/
  213. function get_table_version($con, $tables){
  214. global $fast_tables;
  215. global $slow_tables;
  216. // Build query, showing only tables to sync
  217. $s = "SELECT section, version FROM version WHERE ";
  218. foreach($tables as $table){
  219. if (in_array($table, $fast_tables) == false){
  220. $s = $s . "section = '$table' OR ";
  221. }
  222. }
  223. $s = $s . "1 = 2 ";
  224. $s = $s . "UNION SELECT section, 0 AS version FROM version WHERE ";
  225. foreach($tables as $table){
  226. if (in_array($table, $fast_tables)){
  227. $s = $s . "section = '$table' OR ";
  228. }
  229. }
  230. $s = $s . "1 = 2;";
  231. $q = mysqli_query($con, $s);
  232. //If no rows, return
  233. if (mysqli_num_rows($q) == 0){
  234. return "";
  235. }
  236. //Create result array
  237. $str = "";
  238. $str = $str. "\"version\":[";
  239. while($r = mysqli_fetch_assoc($q)) {
  240. $str = $str . json_encode($r) . ",";
  241. }
  242. $str = rtrim($str,',');
  243. $str = $str . "],";
  244. return $str;
  245. }
  246. /*****************************************************
  247. * Formats the contents of a table in the database. *
  248. * Inaccessible or sensitive tables or fields are *
  249. * not printed. *
  250. * *
  251. * @params: *
  252. * con: (MySQL server connection) RO mode enough. *
  253. * table (string): The name of the table. *
  254. * @return: (Assoc Array): Data in the table. *
  255. ****************************************************/
  256. function get_table($con, $table){
  257. $year = date("Y");
  258. $table = strtolower($table);
  259. switch ($table){
  260. case TAB_ACTIVITY:
  261. $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;");
  262. break;
  263. case TAB_ALBUM:
  264. $q = mysqli_query($con, "SELECT id, permalink, title_es, title_en, title_eu, description_es, description_en, description_eu, open FROM album;");
  265. break;
  266. case TAB_PHOTO:
  267. $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;;");
  268. break;
  269. case TAB_POST:
  270. $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;");
  271. break;
  272. case TAB_SPONSOR:
  273. $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;");
  274. break;
  275. case TAB_SETTINGS:
  276. $q = mysqli_query($con, "SELECT name, value FROM settings;");
  277. break;
  278. case TAB_FESTIVAL_EVENT_GM:
  279. $q = mysqli_query($con, "SELECT * FROM festival_event_gm WHERE year(start) = $year AND interest >= 1;");
  280. break;
  281. case TAB_FESTIVAL:
  282. $q = mysqli_query($con, "SELECT * FROM festival WHERE year = $year;");
  283. break;
  284. case TAB_FESTIVAL_DAY:
  285. $q = mysqli_query($con, "SELECT * FROM festival_day WHERE year(date) = $year;");
  286. break;
  287. case TAB_FESTIVAL_OFFER:
  288. $q = mysqli_query($con, "SELECT * FROM festival_offer WHERE year = $year;");
  289. break;
  290. case TAB_FESTIVAL_EVENT_CITY:
  291. $q = mysqli_query($con, "SELECT * FROM festival_event_city WHERE year(start) = $year AND interest >= 1;");
  292. break;
  293. case TAB_ACTIVITY_IMAGE:
  294. $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;");
  295. break;
  296. case TAB_ACTIVITY_ITINERARY:
  297. $q = mysqli_query($con, "SELECT * FROM activity_itinerary WHERE year(start) = $year;");
  298. break;
  299. case TAB_PHOTO_ALBUM:
  300. $q = mysqli_query($con, "SELECT photo, album FROM photo_album, photo WHERE photo = photo.id AND year(uploaded) = $year;");
  301. break;
  302. case TAB_POST_IMAGE:
  303. $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;");
  304. //Other cases:
  305. default:
  306. $q = mysqli_query($con, "SELECT * FROM $table;");
  307. }
  308. //If no rows, return
  309. if (mysqli_num_rows($q) == 0){
  310. return "";
  311. }
  312. //Create result array
  313. $str = "";
  314. $str = $str. "\"$table\":[";
  315. while($r = mysqli_fetch_assoc($q)) {
  316. $str = $str . json_encode($r) . ",";
  317. }
  318. $str = rtrim($str,",");
  319. $str = $str . "]";
  320. return $str;
  321. }
  322. /*****************************************************
  323. * Prints out required tables. *
  324. * *
  325. * @params: *
  326. * con: (MySQL server connection) Db connector. *
  327. * tables: (String array) List of tables to sync. *
  328. * @return: (String): Client IP address. *
  329. *****************************************************/
  330. function sync($con, $tables){
  331. $str = "";
  332. if(sizeof($tables) > 0){
  333. $str = "{" . get_table_version($con, $tables);
  334. foreach($tables as $table){
  335. $str = $str . get_table($con, $table) . ",";
  336. }
  337. $str = rtrim($str, ",");
  338. $str = $str . "}";
  339. $str = str_replace(",,", ",", $str);
  340. echo($str);
  341. return true;
  342. }
  343. return false;
  344. }
  345. /*****************************************************
  346. * Gets the IP address of the client. *
  347. * *
  348. * @return: (String): Client IP address. *
  349. *****************************************************/
  350. function get_user_ip(){
  351. $client = @$_SERVER['HTTP_CLIENT_IP'];
  352. $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
  353. $remote = $_SERVER['REMOTE_ADDR'];
  354. if(filter_var($client, FILTER_VALIDATE_IP)){
  355. $ip = $client;
  356. }
  357. elseif(filter_var($forward, FILTER_VALIDATE_IP)){
  358. $ip = $forward;
  359. }
  360. else{
  361. $ip = $remote;
  362. }
  363. return $ip;
  364. }
  365. /*****************************************************
  366. * Registers the request in the database. *
  367. * *
  368. * @params: *
  369. * con: (MySQL server connection) RO mode enough. *
  370. * user: (String array): Array with, at least, *
  371. * the keys 'client', 'user', 'foreground', *
  372. * 'ip', 'os', 'browser', 'uagent', with *
  373. * info about the calling app. *
  374. * synced: (Int): 1 if a sync content was sent, 0 *
  375. * otherwise. *
  376. *****************************************************/
  377. function log_sync($con, $user, $synced){
  378. 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]');");
  379. }
  380. /*****************************************************
  381. * Registers a failed request in the database. *
  382. * *
  383. * @params: *
  384. * con: (MySQL server connection) RO mode enough. *
  385. * user: (String array): Array with, at least, *
  386. * the keys 'client', 'user', 'foreground', *
  387. * 'ip', 'os', 'browser', 'uagent', and *
  388. * 'error', with info about the calling *
  389. * app. The 'error' key will contain an *
  390. * error description. *
  391. *****************************************************/
  392. function log_error($con, $user){
  393. 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]');");
  394. }
  395. // Connect to the database
  396. $con = startdb('rw');
  397. // Get info about the user
  398. $user = get_user_info($con, $_GET);
  399. if(strlen($user["error"]) > 0){
  400. log_error($con, $user);
  401. http_response_code(400);
  402. exit(-1);
  403. }
  404. // Get tables to sync
  405. $v_user = get_user_versions($con, $_GET);
  406. $v_server = get_server_versions($con);
  407. $tables = select_tables($v_user, $v_server);
  408. $synced = sync($con, $tables);
  409. //Log the sync in the database
  410. log_sync($con, $user, $synced);
  411. ?>