sync.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. <?php
  2. // Gasteizko Margolariak API v1 //
  3. //List of available data formatting
  4. define('FOR_JSON', 'json');
  5. //Default info format
  6. define('DEF_FORMAT', FOR_JSON);
  7. //Database section identifiers
  8. define('SEC_ALL', 'all');
  9. define('SEC_BLOG', 'blog');
  10. define('SEC_ACTIVITIES', 'activities');
  11. define('SEC_GALLERY', 'gallery');
  12. define('SEC_LABLANCA', 'lablanca');
  13. //Posible actions
  14. define('ACTION_SYNC', 'sync');
  15. define('ACTION_VERSION', 'version');
  16. //Default action
  17. define('DEF_ACTION', ACTION_SYNC);
  18. //Output keys
  19. define('KEY_VERSION', 'version');
  20. define('KEY_DATA', 'data');
  21. //$_GET valid parameters
  22. define('GET_CLIENT', 'client');
  23. define('GET_USER', 'user');
  24. define('GET_ACTION', 'action');
  25. define('GET_SECTION', 'section');
  26. define('GET_VERSION', 'version');
  27. define('GET_FOREGROUND', 'foreground');
  28. define('GET_FORMAT', 'json');
  29. //Error messages
  30. define('ERR_ACTION', '-ACTION:');
  31. define('ERR_SECTION', '-SECTION:');
  32. define('ERR_VERSION', '-VERSION:');
  33. define('ERR_FOREGROUND', '-FOREGROUND:');
  34. define('ERR_FORMAT', '-FORMAT:');
  35. /****************************************************
  36. * This function is called from almost everywhere at *
  37. * the beggining of the page. It initializes the *
  38. * session variables, connect to the db, enabling *
  39. * the variable $con for futher use everywhere in *
  40. * the php code, and populates the arrays $user *
  41. * and $permission, with info about the user. *
  42. * *
  43. * @return: (db connection): The connection handler. *
  44. ****************************************************/
  45. function startdb(){
  46. //Include the db configuration file. It's somehow like this
  47. /*
  48. <?php
  49. $host = 'XXXX';
  50. $db_name = 'XXXX';
  51. $username_ro = 'XXXX';
  52. $username_rw = 'XXXX';
  53. $pass_ro = 'XXXX';
  54. $pass_rw = 'XXXX';
  55. ?>
  56. */
  57. include('../../.htpasswd');
  58. //Connect to to database
  59. $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
  60. //Set encoding options
  61. mysqli_set_charset($con, 'utf-8');
  62. header('Content-Type: text/html; charset=utf8');
  63. mysqli_query($con, 'SET NAMES utf8;');
  64. //Return the db connection
  65. return $con;
  66. }
  67. /****************************************************
  68. * Gives the version of one or more of the sections *
  69. * of the database, or the global section. *
  70. * *
  71. * @params: *
  72. * con: (MySQL server connection) RO mode enough. *
  73. * section: (string): 'blog', 'activities', *
  74. * 'gallery', 'lablanca', 'global'. None *
  75. * to get them all. *
  76. * @return: (int): The version of the db section. *
  77. ****************************************************/
  78. function get_version($con, $section = SEC_ALL){
  79. if ($section == SEC_ALL){
  80. $q = mysqli_query($con, "SELECT SUM(version) AS version FROM version;");
  81. }
  82. else{
  83. $q = mysqli_query($con, "SELECT version FROM version WHERE section = '$section';");
  84. }
  85. if (mysqli_num_rows($q) == 0){
  86. //'Bad request' status code
  87. http_response_code(400);
  88. return 0;
  89. }
  90. else{
  91. $r = mysqli_fetch_array($q);
  92. return($r['version']);
  93. }
  94. }
  95. /****************************************************
  96. * Gives the version of every section of the db. *
  97. * *
  98. * @params: *
  99. * con: (MySQL server connection) RO mode enough. *
  100. * @return: (Assoc. array): The versions of the db. *
  101. ****************************************************/
  102. function get_all_versions($con){
  103. $v = array();
  104. $v[] = [SEC_ALL => get_version($con, SEC_ALL)];
  105. $v[] = [SEC_BLOG => get_version($con, SEC_BLOG)];
  106. $v[] = [SEC_ACTIVITIES => get_version($con, SEC_ACTIVITIES)];
  107. $v[] = [SEC_GALLERY => get_version($con, SEC_GALLERY)];
  108. $v[] = [SEC_LABLANCA => get_version($con, SEC_LABLANCA)];
  109. return $v;
  110. }
  111. /****************************************************
  112. * Prepares the info of the database or a portion of *
  113. * it in the selected format. *
  114. * *
  115. * @params: *
  116. * con: (MySQL server connection) RO mode enough. *
  117. * section: (string): 'blog', 'activities', *
  118. * 'gallery', 'lablanca', 'global'. None *
  119. * to get them all. *
  120. * format: (int): 1: json (default). *
  121. * @return: (String): data in the desired format. *
  122. ****************************************************/
  123. function sync($con, $section = SEC_ALL, $format = DEF_FORMAT){
  124. switch ($section){
  125. case SEC_BLOG:
  126. $tables = [ 'post', 'post_comment', 'post_image', 'post_tag' ];
  127. break;
  128. case SEC_ACTIVITIES:
  129. $tables = [ 'activity', 'activity_itinerary', 'activity_comment', 'activity_image', 'activity_tag', 'activity_itinerary', 'album', 'photo', 'photo_album', 'photo_comment', 'place' ];
  130. break;
  131. case SEC_GALLERY:
  132. $tables = [ 'album', 'photo', 'photo_album', 'photo_comment', 'place' ];
  133. break;
  134. case SEC_LABLANCA:
  135. $tables = [ 'festival', 'festival_day', 'festival_event', 'festival_event_image', 'festival_offer', 'place', 'people' ];
  136. break;
  137. case SEC_ALL:
  138. $tables = [ 'activity', 'activity_itinerary', 'activity_comment', 'activity_image', 'activity_tag', 'album', 'photo', 'festival', 'festival_day', 'festival_event', 'festival_event_image', 'festival_offer', 'photo_album', 'place', 'post', 'post_comment', 'post_image', 'post_tag', 'settings', 'sponsor' ];
  139. break;
  140. default:
  141. //'Bad request' staus code
  142. http_response_code(400);
  143. return;
  144. }
  145. $v = array();
  146. if ($section == SEC_ALL){
  147. $v = get_all_versions($con);
  148. }
  149. else{
  150. $v[] = [ $section => get_version($con, $section) ];
  151. }
  152. switch ($format){
  153. case FOR_JSON:
  154. $db = array();
  155. foreach($tables as $table){
  156. $req_table = get_table($con, $table, $format);
  157. if ($req_table != -1){
  158. $db[] = [ $table => $req_table];
  159. }
  160. }
  161. $data = array();
  162. $data[] = [ KEY_VERSION => $v ];
  163. $data[] = [ KEY_DATA => $db];
  164. return(json_encode($data));
  165. break;
  166. }
  167. }
  168. /****************************************************
  169. * Echoes the contents of a table from the database. *
  170. * Inaccessible or sensitive tables or fields are *
  171. * not printed. *
  172. * *
  173. * @params: *
  174. * con: (MySQL server connection) RO mode enough. *
  175. * table (string): The name of the table. *
  176. * @return: (Assoc Array): Data in the table. *
  177. ****************************************************/
  178. function get_table($con, $table){
  179. $table = strtolower($table);
  180. switch ($table){
  181. case "activity":
  182. $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;");
  183. break;
  184. case "activity_comment":
  185. $q = mysqli_query($con, "SELECT activity_comment.id AS id, activity, text, dtime, CONCAT(user.username, user) AS user, lang FROM activity_comment, user WHERE activity_comment.user = user.id AND approved = 1;");
  186. break;
  187. case "album":
  188. $q = mysqli_query($con, "SELECT id, permalink, title_es, title_en, title_eu, description_es, description_en, description_eu, open FROM album;");
  189. break;
  190. case "photo":
  191. $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;");
  192. break;
  193. case "post":
  194. $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;");
  195. break;
  196. case "post_comment":
  197. $q = mysqli_query($con, "SELECT post_comment.id AS id, post, text, dtime, CONCAT(user.username, user) AS user, lang FROM post_comment, user WHERE post_comment.user = user.id AND approved = 1;");
  198. break;
  199. case "sponsor":
  200. $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;");
  201. break;
  202. case "settings":
  203. $q = mysqli_query($con, "SELECT name, value FROM settings");
  204. break;
  205. //Other cases:
  206. default:
  207. //If the table is a public one and has not been listed above, all of its fields are public.
  208. if (in_array($table, ['activity_image', 'activity_itinerary', 'activity_tag', 'festival', 'festival_day', 'festival_event', 'festival_event_image', 'festival_offer', 'photo_album', 'place', 'post_image', 'post_tag'])){
  209. $q = mysqli_query($con, "SELECT * FROM $table;");
  210. }
  211. //If forbidden table
  212. else{
  213. //'Forbidden' status code
  214. //TODO: I dont want execution stopping here
  215. http_response_code(403);
  216. return;
  217. }
  218. }
  219. //If no rows, return
  220. if (mysqli_num_rows($q) == 0){
  221. return -1;
  222. }
  223. //Create result array
  224. $rows = array();
  225. while($r = mysqli_fetch_assoc($q)) {
  226. $rows[] = $r;
  227. }
  228. return $rows;
  229. }
  230. /****************************************************
  231. * gets the IP address of the client. *
  232. * *
  233. * @return: (String): Client IP address. *
  234. ****************************************************/
  235. function get_user_ip(){
  236. $client = @$_SERVER['HTTP_CLIENT_IP'];
  237. $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
  238. $remote = $_SERVER['REMOTE_ADDR'];
  239. if(filter_var($client, FILTER_VALIDATE_IP)){
  240. $ip = $client;
  241. }
  242. elseif(filter_var($forward, FILTER_VALIDATE_IP)){
  243. $ip = $forward;
  244. }
  245. else{
  246. $ip = $remote;
  247. }
  248. return $ip;
  249. }
  250. /****************************************************
  251. * Registers the request in the database. *
  252. * *
  253. * @params: *
  254. * con: (MySQL server connection) RO mode enough. *
  255. * client: (string): The client identifier. *
  256. * user: (string): A unique end user identifier. *
  257. * action: (string): Requested action. *
  258. * section: (string): Requested database section. *
  259. * version: (int): Version of the client db. *
  260. * new_version: (int): Returned version. *
  261. * foreground: (int): 1 for fg syncs, 0 for bg. *
  262. * format: (string): Requested format. *
  263. * error: (string): Error message to store. *
  264. ****************************************************/
  265. function log_sync($con, $client, $user, $action, $section, $version, $new_version, $foreground, $format, $error){
  266. $ip = get_user_ip();
  267. $browser_data = get_browser(null, true);
  268. $os = $browser_data['platform'];
  269. $browser = $browser_data['browser'];
  270. $uagent = $browser_data['browser_name_pattern'];
  271. mysqli_query($con, "INSERT INTO sync (client, user, action, section, version_from, version_to, fg, format, error, ip, os, uagent) VALUES ('$client', '$user', '$action', '$section', $version, $new_version, $foreground, '$format', '$error', '$ip', '$os', '$uagent');");
  272. error_log("INSERT INTO sync (client, user, action, section, version_from, version_to, fg, format, error, ip, os, uagent) VALUES ('$client', '$user', '$action', '$section', $version, $new_version, $foreground, '$format', '$error', '$ip', '$os', '$uagent');");
  273. }
  274. //Connect to the database
  275. $con = startdb('rw');
  276. //Get data from URL
  277. $client = mysqli_real_escape_string($con, $_GET[GET_CLIENT]);
  278. $user = mysqli_real_escape_string($con, $_GET[GET_USER]);
  279. $action = strtolower(mysqli_real_escape_string($con, $_GET[GET_ACTION]));
  280. $section = strtolower(mysqli_real_escape_string($con, $_GET[GET_SECTION]));
  281. $version = (int) mysqli_real_escape_string($con, $_GET[GET_VERSION]);
  282. $foregroud = (int) mysqli_real_escape_string($con, $_GET[GET_FOREGROUND]);
  283. $format = strtolower(mysqli_real_escape_string($con, $_GET[GET_FORMAT]));
  284. //Initialize some variables
  285. $error = '';
  286. $new_version = -1;
  287. //Validate data
  288. if (strlen($client) < 1){
  289. $client = '';
  290. }
  291. if (strlen($user) < 1){
  292. $user = '';
  293. }
  294. if (strlen($action) < 1){
  295. $action = DEF_ACTION;
  296. }
  297. if ($action != ACTION_SYNC && $action != ACTION_VERSION){
  298. //Bad request
  299. http_response_code(400);
  300. $error = $error . ERR_ACTION . mysqli_real_escape_string($con, $_GET[GET_ACTION]);
  301. }
  302. if (strlen($section) > 0 && $section != SEC_ALL && $section != SEC_BLOG && $section != SEC_ACTIVITIES && $section != SEC_GALLERY && $section != SEC_LABLANCA ){
  303. //Bad request
  304. http_response_code(400);
  305. $error = $error . ERR_SECTION . mysqli_real_escape_string($con, $_GET[GET_SECTION]);
  306. }
  307. if (strlen($section) == 0){
  308. $section = SEC_ALL;
  309. }
  310. if (strlen($version) == 0){
  311. $version = -1;
  312. }
  313. if (is_int($version) == false){
  314. //Bad request
  315. http_response_code(400);
  316. $error = $error . ERR_VERSION . mysqli_real_escape_string($con, $_GET[GET_VERSION]);
  317. $version = -1;
  318. }
  319. if (strlen($foregroud) < 1){
  320. $foreground = 1;
  321. }
  322. if ($foreground != 0 && $foregroud != 0){
  323. //Bad request
  324. http_response_code(400);
  325. $error = $error . ERR_FOREGROUND . mysqli_real_escape_string($con, $_GET[GET_BACKGROUND]);
  326. }
  327. if (strlen($format) < 1){
  328. $format = DEF_FORMAT;
  329. }
  330. if ($format != FOR_JSON){
  331. //Bad request
  332. http_response_code(400);
  333. $error = $error . ERR_FORMAT . mysqli_real_escape_string($con, $_GET[GET_FORMAT]);
  334. }
  335. //If there has not been an error, procede
  336. if (strlen($error) == 0){
  337. //If the client just needs to know the version number
  338. if ($action == ACTION_VERSION){
  339. $new_version = get_version($con, $section);
  340. echo ($new_version);
  341. }
  342. //If the clients wants to actually perform a sync
  343. else{
  344. //If the client version is up to date, send a no content status
  345. $new_version = get_version($con, $section);
  346. if ($version >= $new_version){
  347. //No content
  348. http_response_code(204);
  349. }
  350. //If the client needs an update
  351. else{
  352. $out = sync($con, $section);
  353. $out = str_replace('\"', '\u0022', $out);
  354. $out = str_replace(':""', ':null', $out);
  355. $out = "{\"sync\":$out}";
  356. echo($out);
  357. }
  358. }
  359. }
  360. //Log the sync in the database
  361. log_sync($con, $client, $user, $action, $section, $version, $new_version, $foreground, $format, $error);
  362. ?>