Prechádzať zdrojové kódy

Implementing Sync API v3.

Iñigo Valentin 9 rokov pred
rodič
commit
e31444b186

+ 311 - 0
www/API/v2/comment.php

@@ -0,0 +1,311 @@
+<?php
+    // Gasteizko Margolariak API v1 //
+    
+    //Posible comment target
+    define('TARGET_PHOTO', 'photo');
+    define('TARGET_POST', 'post');
+    define('TARGET_ACTIVITY', 'activity');
+    
+    //Default target
+    define('DEF_TARGET', TARGET_ALL);
+        
+    //$_GET valid parameters
+    define('GET_CLIENT', 'client');
+    define('GET_USER', 'user');
+    define('GET_TARGET', 'target');
+    define('GET_ID', 'id');
+    define('GET_PERMALINK', 'permalink');
+    define('GET_TEXT', 'text');
+    define('GET_USERNAME', 'username');
+    
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+        <?php
+            $host = 'XXXX';
+            $db_name = 'XXXX';
+            $username_ro = 'XXXX';
+            $username_rw = 'XXXX';
+            $pass_ro = 'XXXX';
+            $pass_rw = 'XXXX';
+        ?>
+        */
+        include('../../.htpasswd');
+        
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+        
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+        
+        //Return the db connection
+        return $con;
+    }
+    
+    //Connect to the database
+    $con = startdb('rw');
+    
+    //Get data from URL
+    $client = mysqli_real_escape_string($con, $_GET[GET_CLIENT]);
+    $user = mysqli_real_escape_string($con, $_GET[GET_USER]);
+    $target = strtolower(mysqli_real_escape_string($con, $_GET[GET_TARGET]));
+    $id = strtolower(mysqli_real_escape_string($con, $_GET[GET_ID]));
+    $permalink = strtolower(mysqli_real_escape_string($con, $_GET[GET_PERMALINK]));
+    $ink = strtolower(mysqli_real_escape_string($con, $_GET[GET_PERMALINK]));
+    $permalink = strtolower(mysqli_real_escape_string($con, $_GET[GET_PERMALINK]));
+    $username = mysqli_real_escape_string($con, $_GET[GET_USERNAME]);
+    $text = mysqli_real_escape_string($con, $_GET[GET_TEXT]);
+    
+    //Validate data
+    if (strlen($client) < 1){
+        //Bad request
+        http_response_code(400);
+        exit();
+    }
+    if (strlen($user) < 1){
+        $user = '';
+    }
+    if (strlen($target) < 1){
+        //Bad request
+        http_response_code(400);
+        exit();
+    }
+    if ($target != TARGET_PHOTO && $target != TARGET_POST && $target != TARGET_ACTIVITY){
+        //Bad request
+        http_response_code(400);
+        exit();
+    }
+    if (strlen($username) < 1){
+        //Bad request
+        http_response_code(400);
+        exit();
+    }
+    
+    
+    //Check id and/or permalink. Several cases:
+    
+    //1st case: id and permalink empty: Error.
+    if (strlen($id) < 1 && strlen($permalink) < 1){
+        //Bad request
+        http_response_code(400);
+        exit();
+    }
+    
+    //2nd case: Comment for post, permalink and no id.
+    elseif ($target == TARGET_POST && strlen($id) < 1 && strlen($permalink) >= 1){
+    
+        //Check if post exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM post WHERE visible = 1 AND permalink = '$permalink';");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //3rd case: Comment for post, id and no permalink.
+    elseif ($target == TARGET_POST && strlen($id) >= 1 && strlen($permalink) < 1){
+    
+        //Check if post exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM post WHERE visible = 1 AND id = $id;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //4th case: Comment for post, permalink and id.
+    elseif ($target == TARGET_POST && strlen($id) >= 1 && strlen($permalink) >= 1){
+    
+        //Check if post exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM post WHERE visible = 1 AND permalink = '$permalink' AND id = $id ;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //5th case: Comment for photo, permalink and no id.
+    elseif ($target == TARGET_PHOTO && strlen($id) < 1 && strlen($permalink) >= 1){
+    
+        //Check if photo exists.
+        $q = mysqli_query($con, "SELECT id FROM photo WHERE approved = 1 AND permalink = '$permalink';");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+            $item_id = $r['id'];
+        }
+    }
+    
+    //6th case: Comment for photo, id and no permalink.
+    elseif ($target == TARGET_PHOTO && strlen($id) >= 1 && strlen($permalink) < 1){
+    
+        //Check if photo exists.
+        $q = mysqli_query($con, "SELECT id FROM photo WHERE approved = 1 AND id = $id;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+            $item_id = $r['id'];
+        }
+    }
+    
+    //7th case: Comment for photo, permalink and id.
+    elseif ($target == TARGET_PHOTO && strlen($id) >= 1 && strlen($permalink) >= 1){
+    
+        //Check if photo exists.
+        $q = mysqli_query($con, "SELECT id FROM photo WHERE approved = 1 AND permalink = '$permalink' AND id = $id;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+            $item_id = $r['id'];
+        }
+    }
+    
+    //8nd case: Comment for activity, permalink and no id.
+    elseif ($target == TARGET_ACTIVITY && strlen($id) < 1 && strlen($permalink) >= 1){
+    
+        //Check if activity exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM activity WHERE visible = 1 AND permalink = '$permalink';");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //9rd case: Comment for activity, id and no permalink.
+    elseif ($target == TARGET_ACTIVITY && strlen($id) >= 1 && strlen($permalink) < 1){
+    
+        //Check if activity exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM activity WHERE visible = 1 AND id = $id;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //10th case: Comment for activity, permalink and id.
+    elseif ($target == TARGET_ACTIVITY && strlen($id) >= 1 && strlen($permalink) >= 1){
+    
+        //Check if activity exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM activity WHERE visible = 1 AND permalink = '$permalink' AND id = $id ;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //If code gets here, there were no errors. Build query.
+    $query = "INSERT INTO ";
+    $section = "";
+    switch ($target){
+        case TARGET_POST:
+            $query = $query . "post_comment (post";
+            $section = "blog";
+            break;
+        case TARGET_PHOTO:
+            $query = $query . "photo_comment (photo";
+            $section = "gallery";
+            break;
+        case TARGET_ACTIVITY:
+            $query = $query . "activity_comment (activity";
+            $section = "activity";
+            break;
+    }
+    $query = $query . ", text, username, app) VALUES ($item_id, \"$text\", \"$username\", \"client\");";
+    //echo($query);
+    mysqli_query($con, $query);
+    mysqli_query($con, "UPDATE version SET version = version + 1 WHERE section = '$section';");
+
+?>

+ 56 - 0
www/API/v2/help/comment.php

@@ -0,0 +1,56 @@
+<?php 
+    $http_host = $_SERVER['HTTP_HOST']; 
+    $v = 1;
+?>
+
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
+        <meta charset="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
+        <title>Gasteizko Margolariak API v<?php echo($v); ?> Documentation</title>
+        <link rel="shortcut icon" href="<?php echo "http://$http_host/img/logo/favicon.ico";?>">
+        <!-- CSS files -->
+        <style>
+            <?php 
+                include("../../../css/ui.css"); 
+                include("../../../css/index.css");
+            ?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media="(max-width : 990px)">
+            <?php 
+                include("../../../css/m/ui.css"); 
+                include("../../../css/m/index.css");
+            ?>
+        </style>
+        <!-- Script files -->
+        <script type="text/javascript">
+            <?php include("../../../script/ui.js"); ?>
+        </script>
+        <!-- Meta tags -->
+        <link rel="canonical" href="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <link rel="author" href="<?php echo "http://$http_host"; ?>"/>
+        <link rel="publisher" href="<?php echo "http://$http_host"; ?>"/>
+        <meta name="description" content="<?php echo $lng['index_description'];?>"/>
+        <meta property="og:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation"/>
+        <meta property="og:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta property="og:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta property="og:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta property="og:site_name" content="Gasteizko Margolariak"/>
+        <meta property="og:type" content="website"/>
+        <meta property="og:locale" content="en"/>
+        <meta name="twitter:card" content="summary"/>
+        <meta name="twitter:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation""/>
+        <meta name="twitter:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta name="twitter:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta name="twitter:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta name="robots" content="index follow"/>
+    </head>
+    <body>
+        <?php include("toolbar.php"); ?>
+        <div id="content">
+        </div>
+    </body>
+</body>

+ 56 - 0
www/API/v2/help/index.php

@@ -0,0 +1,56 @@
+<?php 
+    $http_host = $_SERVER['HTTP_HOST']; 
+    $v = 1;
+?>
+
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
+        <meta charset="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
+        <title>Gasteizko Margolariak API v<?php echo($v); ?> Documentation</title>
+        <link rel="shortcut icon" href="<?php echo "http://$http_host/img/logo/favicon.ico";?>">
+        <!-- CSS files -->
+        <style>
+            <?php 
+                include("../../../css/ui.css"); 
+                include("../../../css/index.css");
+            ?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media="(max-width : 990px)">
+            <?php 
+                include("../../../css/m/ui.css"); 
+                include("../../../css/m/index.css");
+            ?>
+        </style>
+        <!-- Script files -->
+        <script type="text/javascript">
+            <?php include("../../../script/ui.js"); ?>
+        </script>
+        <!-- Meta tags -->
+        <link rel="canonical" href="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <link rel="author" href="<?php echo "http://$http_host"; ?>"/>
+        <link rel="publisher" href="<?php echo "http://$http_host"; ?>"/>
+        <meta name="description" content="<?php echo $lng['index_description'];?>"/>
+        <meta property="og:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation"/>
+        <meta property="og:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta property="og:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta property="og:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta property="og:site_name" content="Gasteizko Margolariak"/>
+        <meta property="og:type" content="website"/>
+        <meta property="og:locale" content="en"/>
+        <meta name="twitter:card" content="summary"/>
+        <meta name="twitter:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation""/>
+        <meta name="twitter:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta name="twitter:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta name="twitter:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta name="robots" content="index follow"/>
+    </head>
+    <body>
+        <?php include("toolbar.php"); ?>
+        <div id="content">
+        </div>
+    </body>
+</body>

+ 56 - 0
www/API/v2/help/sync.php

@@ -0,0 +1,56 @@
+<?php 
+    $http_host = $_SERVER['HTTP_HOST']; 
+    $v = 1;
+?>
+
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
+        <meta charset="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
+        <title>Gasteizko Margolariak API v<?php echo($v); ?> Documentation</title>
+        <link rel="shortcut icon" href="<?php echo "http://$http_host/img/logo/favicon.ico";?>">
+        <!-- CSS files -->
+        <style>
+            <?php 
+                include("../../../css/ui.css"); 
+                include("../../../css/index.css");
+            ?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media="(max-width : 990px)">
+            <?php 
+                include("../../../css/m/ui.css"); 
+                include("../../../css/m/index.css");
+            ?>
+        </style>
+        <!-- Script files -->
+        <script type="text/javascript">
+            <?php include("../../../script/ui.js"); ?>
+        </script>
+        <!-- Meta tags -->
+        <link rel="canonical" href="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <link rel="author" href="<?php echo "http://$http_host"; ?>"/>
+        <link rel="publisher" href="<?php echo "http://$http_host"; ?>"/>
+        <meta name="description" content="<?php echo $lng['index_description'];?>"/>
+        <meta property="og:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation"/>
+        <meta property="og:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta property="og:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta property="og:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta property="og:site_name" content="Gasteizko Margolariak"/>
+        <meta property="og:type" content="website"/>
+        <meta property="og:locale" content="en"/>
+        <meta name="twitter:card" content="summary"/>
+        <meta name="twitter:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation""/>
+        <meta name="twitter:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta name="twitter:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta name="twitter:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta name="robots" content="index follow"/>
+    </head>
+    <body>
+        <?php include("toolbar.php"); ?>
+        <div id="content">
+        </div>
+    </body>
+</body>

+ 27 - 0
www/API/v2/help/toolbar.php

@@ -0,0 +1,27 @@
+ <div id="header" class="desktop">
+    <div id="header_content">
+        <img src="/img/logo/logo-api.png"/>
+        <div id="header_menu">
+            <table>
+                <tr>
+                    <td><a href="http://<?php echo($http_host . "API/help/V$v/"); ?>">API documentation</a></td>
+                    <td><a href="http://<?php echo($http_host . "API/help/V$v/sync/"); ?>">Sync</a></td>
+                    <td><a href="http://<?php echo($http_host . "API/help/V$v/comment/"); ?>">Comment</a></td>
+                    <td><a href="http://<?php echo($http_host); ?>/">Main page</a></td>
+                </tr>
+            </table>
+        </div>
+    </div>
+</div>
+<div id="header_m" class="mobile">
+    <img src="/img/logo/logo-api.png" onClick='toggleMobileMenu();' id='mobile_logo'/>
+    <div id="header_menu_m">
+        <div id='header_m_title' onClick='openMobileMenu();' class='pointer'><span><img src='http://<?php echo $http_host; ?>/img/misc/slid-menu.png'/>&nbsp;&nbsp;&nbsp;&nbsp;<?php echo $cur_section; ?></span></div>
+        <div class='header_m_link'><a href="http://<?php echo($http_host . "API/help/V$v/"); ?>">API documentation</a></div>
+        <div class='header_m_link'><a href="http://<?php echo($http_host . "API/help/V$v/sync/"); ?>">Sync</a></div>
+        <div class='header_m_link'><a href="http://<?php echo($http_host . "API/help/V$v/comment/"); ?>">Comment</a></div>
+        <div class='header_m_link'><a href="http://<?php echo($http_host); ?>/">Main page</a></div>
+        <div id='header_m_slider' onClick='closeMobileMenu();' class='pointer'><span><img src='http://<?php echo $http_host; ?>/img/misc/slid-top.png'/></span></div>
+    </div><br/><br/>
+</div>
+

+ 87 - 0
www/API/v2/location.php

@@ -0,0 +1,87 @@
+<?php
+    // Gasteizko Margolariak API v1 //
+        
+    //List of available data formatting
+    define('FOR_JSON', 'json');
+    
+    //Default info format
+    define('DEF_FORMAT', FOR_JSON);
+    
+    //Errors
+    define('ERR_FORMAT', '-FORMAT:');
+    
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+        <?php
+            $host = 'XXXX';
+            $db_name = 'XXXX';
+            $username_ro = 'XXXX';
+            $username_rw = 'XXXX';
+            $pass_ro = 'XXXX';
+            $pass_rw = 'XXXX';
+        ?>
+        */
+        include('../../.htpasswd');
+        
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+        
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+        
+        //Return the db connection
+        return $con;
+    }
+    
+    //Connect to the database
+    $con = startdb('rw');
+    
+    //Get data from the url
+    $format = strtolower(mysqli_real_escape_string($con, $_GET[GET_FORMAT]));
+    
+    //Validate input
+    if (strlen($format) < 1){
+        $format = DEF_FORMAT;
+    }
+    if ($format != FOR_JSON){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_FORMAT . mysqli_real_escape_string($con, $_GET[GET_FORMAT]);
+        error_log($error);
+        exit(-1);
+    }
+    
+    //Get location
+    $q = mysqli_query($con, "SELECT lat, lon, dtime FROM location WHERE action <> 'F' AND lat IS NOT null AND lon IS NOT null AND dtime > NOW() - INTERVAL 30 MINUTE ORDER BY dtime DESC LIMIT 1;");
+    if (mysqli_num_rows($q) > 0){
+        $r = mysqli_fetch_array($q);
+        switch ($format){
+            case FOR_JSON:
+                echo("[{\"lat\":\"$r[lat]\",\"lon\":\"$r[lon]\",\"dtime\":\"$r[dtime]\"}]");
+                break;
+            default:
+                http_response_code(400);
+                $error = $error . ERR_FORMAT . mysqli_real_escape_string($con, $_GET[GET_FORMAT]);
+                error_log($error);
+                exit(-2);
+        }
+    }
+    else{
+        //No content
+        http_response_code(204);
+        exit(0);
+    }
+?>

+ 130 - 0
www/API/v2/notifications.php

@@ -0,0 +1,130 @@
+<?php
+    // Gasteizko Margolariak API v1 //
+
+    //List of available data formatting
+    define('FOR_JSON', 'json');
+
+    //Default info format
+    define('DEF_FORMAT', FOR_JSON);
+
+    //Posible notification target
+    define('TARGET_ALL', 'all');
+    define('TARGET_GM', 'gm');
+
+    //Default target
+    define('DEF_TARGET', TARGET_ALL);
+
+    //$_GET valid parameters
+    define('GET_CLIENT', 'client');
+    define('GET_USER', 'user');
+    define('GET_TARGET', 'target');
+    define('GET_FORMAT', 'json');
+
+    //Error messages
+    define('ERR_TARGET', '-TARGET:');
+    define('ERR_FORMAT', '-FORMAT:');
+
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+        <?php
+            $host = 'XXXX';
+            $db_name = 'XXXX';
+            $username_ro = 'XXXX';
+            $username_rw = 'XXXX';
+            $pass_ro = 'XXXX';
+            $pass_rw = 'XXXX';
+        ?>
+        */
+        include('../../.htpasswd');
+        
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+        
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+        
+        //Return the db connection
+        return $con;
+    }
+    
+    function show_notifications($con, $target = DEF_TARGET, $format = DEF_FORMAT){
+        if ($target == TARGET_GM){
+            $query = "SELECT id, title_es, title_en, title_eu, text_es, text_en, text_eu, dtime, internal AS gm, duration, action, 0 AS seen FROM notification WHERE internal = 1 AND dtime > NOW() - INTERVAL duration MINUTE ORDER BY dtime DESC";
+        }
+        else{
+            $query = "SELECT id, title_es, title_en, title_eu, text_es, text_en, text_eu, dtime, internal AS gm, duration, action, 0 AS seen FROM notification WHERE dtime > NOW() - INTERVAL duration MINUTE ORDER BY dtime DESC";
+        }
+        $q = mysqli_query($con, $query);
+        if (mysqli_num_rows($q) == 0){
+            http_response_code(204);
+        }
+        else{
+            switch ($format){
+                case FOR_JSON:
+                    //Create result array
+                    $rows = array();
+                    while($r = mysqli_fetch_assoc($q)) {
+                        $rows[] = $r;
+                    }
+                    return(json_encode($rows));
+                    break;
+            }
+        }
+    }
+    
+    //Connect to the database
+    $con = startdb('rw');
+    
+    //Get data from URL
+    $client = mysqli_real_escape_string($con, $_GET[GET_CLIENT]);
+    $user = mysqli_real_escape_string($con, $_GET[GET_USER]);
+    $target = strtolower(mysqli_real_escape_string($con, $_GET[GET_TARGET]));
+    $format = strtolower(mysqli_real_escape_string($con, $_GET[GET_FORMAT]));
+    
+    //Initialize some variables
+    $error = '';
+    
+    //Validate data
+    if (strlen($client) < 1){
+        $client = '';
+    }
+    if (strlen($user) < 1){
+        $user = '';
+    }
+    if (strlen($target) < 1){
+        $target = DEF_TARGET;
+    }
+    if ($target != TARGET_ALL && $action != TARGET_GM){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_TARGET . mysqli_real_escape_string($con, $_GET[GET_TARGET]);
+    }
+    if (strlen($format) < 1){
+        $format = DEF_FORMAT;
+    }
+    if ($format != FOR_JSON){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_FORMAT . mysqli_real_escape_string($con, $_GET[GET_FORMAT]);
+    }
+
+
+    //If there has not been an error, procede
+    if (strlen($error) == 0){
+        echo(show_notifications($con, $target, $format));
+    }
+
+?>

+ 160 - 0
www/API/v2/sendlocation.php

@@ -0,0 +1,160 @@
+<?php
+
+    // $_GET valid parameters
+    define('GET_USER', 'user');
+    define('GET_PASS', 'pass');
+    define('GET_ACTION', 'action');
+    define('GET_LAT', 'lat');
+    define('GET_LON', 'lon');
+
+    // Valid values
+    define('ACTION_START', 'start');
+    define('ACTION_REFRESH', 'refresh');
+    define('ACTION_STOP', 'stop');
+    $actions = [ACTION_START, ACTION_REFRESH, ACTION_STOP];
+
+    // Error messages
+    define('ERR_USER', '-USER:');
+    define('ERR_ACTION', '-ACTION:');
+    define('ERR_LOCATION', '-TITLE:');
+
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+         <?php
+          $host = 'XXXX';
+          $db_name = 'XXXX';
+          $username_ro = 'XXXX';
+          $username_rw = 'XXXX';
+          $pass_ro = 'XXXX';
+          $pass_rw = 'XXXX';
+         ?>
+        */
+        include('../../.htpasswd');
+
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+
+        //Return the db connection
+        return $con;
+    }
+
+    $con = startdb('rw');
+    $error = "";
+
+    //Get fields
+    $user = mysqli_real_escape_string($con, $_GET[GET_USER]);
+    $pass = mysqli_real_escape_string($con, $_GET[GET_PASS]);
+    $lat = mysqli_real_escape_string($con, $_GET[GET_LAT]);
+    $lon = mysqli_real_escape_string($con, $_GET[GET_LON]);
+    $action = mysqli_real_escape_string($con, $_GET[GET_ACTION]);
+
+    //Validate user
+    $q = mysqli_query($con, "SELECT id FROM user WHERE (lower(username) = lower('$user') OR lower(email) = lower('$user')) AND password = sha1(concat('$pass', sha1(salt)))");
+    if (mysqli_num_rows($q) == 0){
+        error_log(":SECURITY: Reporting location with wrong credentials (IP $_SERVER[REMOTE_ADDR])");
+        http_response_code(403); // Forbidden
+        $error = $error . ERR_USER . mysqli_real_escape_string($con, $_GET[GET_USER]);
+        error_log($error);
+        exit(-1);
+    }
+    // Get id
+    $r = mysqli_fetch_array($q);
+    $uid = $r['id'];
+
+    //Validate fields
+    if (!in_array($action, $actions)){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_ACTION . $action;
+        error_log($error);
+        exit(-2);
+    }
+    if (is_numeric($lat) == false || is_numeric($lon) == false){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_LOCATION . '($lat, $lon)';
+        error_log($error);
+        exit(-3);
+    }
+    if (strlen($lat) == 0 xor strlen($lon) == 0){
+        // Only one coordinate.
+        http_response_code(400); // Bad request
+        $error = $error . ERR_LOCATION . '($lat, $lon)';
+        error_log($error);
+        exit(-4);
+    }
+    if (strlen($lat) != 0 && ($lat < -90.0 || $lat > 90.0)){
+        // Invalid latitude
+        http_response_code(400); // Bad request
+        $error = $error . ERR_LOCATION . '(Lat: $lat)';
+        error_log($error);
+        exit(-5);
+    }
+    if (strlen($lon) != 0 && ($lon < -180.0 || $lon > 180.0)){
+        // Invalid longitude
+        http_response_code(400); // Bad request
+        $error = $error . ERR_LOCATION . '(Lon: $lat)';
+        error_log($error);
+        exit(-6);
+    }
+
+    // Discern action
+    switch ($action){
+        case ACTION_START:
+            // Insert
+            mysqli_query($con, "INSERT INTO location (lat, lon, action, user) VALUES ($lat, $lon, 'S', $uid);");
+            break;
+        case ACTION_REFRESH:
+            // Look for start node.
+            $q = mysqli_query($con, "SELECT id, start, action FROM location WHERE user = $uid AND dtime > NOW() - INTERVAL 30 MINUTE ORDER BY dtime DESC LIMIT 1;");
+            if (mysqli_num_rows($q) == 0){
+                // No recent reports. Start anew.
+                mysqli_query($con, "INSERT INTO location (lat, lon, action, user) VALUES ($lat, $lon, 'S', $uid);");
+            }
+            else{
+                $r = mysqli_fetch_array($q);
+                if ($r['action'] == 'F'){
+                    // Previous track was stoped. Start anew.
+                    mysqli_query($con, "INSERT INTO location (lat, lon, action, user) VALUES ($lat, $lon, 'S', $uid);");
+                }
+                else{
+                    // Continue track.
+                    $s = $r['id'];
+                    mysqli_query($con, "INSERT INTO location (lat, lon, action, user, start) VALUES ($lat, $lon, 'R', $uid, $s);");
+                }
+            }
+            break;
+        case ACTION_STOP:
+            // Look for start node.
+            $q = mysqli_query($con, "SELECT id, start FROM location WHERE user = $uid AND dtime > NOW() - INTERVAL 30 MINUTE ORDER BY dtime DESC LIMIT 1;");
+            if (mysqli_num_rows($q) > 0){
+                $r = mysqli_fetch_array($q);
+                if ($r['action'] != 'F'){
+                    // Finish track.
+                    $s = $r['start'];
+                    if (strlen($lat) > 0 && strlen($lon) > 0){
+                        mysqli_query($con, "INSERT INTO location (lat, lon, action, user, start) VALUES ($lat, $lon, 'F', $uid, $s);");
+                    }
+                    else{
+                        mysqli_query($con, "INSERT INTO location (action, user, start) VALUES ('F', $uid, $s);");
+                    }
+                }
+            }
+            break;
+    }
+    http_response_code(204); // No content.
+?>

+ 168 - 0
www/API/v2/sendnotification.php

@@ -0,0 +1,168 @@
+<?php
+    //include("../functions.php");
+    //$con = startdb('rw');
+
+    // $_GET valid parameters
+    define('GET_USER', 'user');
+    define('GET_PASS', 'pass');
+    define('GET_TITLE_ES', 'title_es');
+    define('GET_TITLE_EN', 'title_en');
+    define('GET_TITLE_EU', 'title_eu');
+    define('GET_TEXT_ES', 'text_es');
+    define('GET_TEXT_EN', 'text_en');
+    define('GET_TEXT_EU', 'text_eu');
+    define('GET_DURATION', 'duration');
+    define('GET_ACTION', 'action');
+    define('GET_PERM', 'permalink');
+    define('GET_ID', 'id');
+    define('GET_GM', 'gm');
+
+    // Valid values
+    define('ACTION_TEXT', 'mensaje');
+    define('ACTION_BLOG', 'blog');
+    define('ACTION_ACTIVITIES', 'actividades');
+    define('ACTION_GALLERY', 'galeria');
+    define('ACTION_LOCALIZATION', 'localizacion');
+    define('ACTION_LABLANCA', 'lablanca');
+    define('ACTION_SCHEDULE', 'programa');
+    define('ACTION_GM_SCHEDULE', 'gprograma');
+    define('ACTION_US', 'nosotros');
+    $actions = [ACTION_TEXT, ACTION_BLOG, ACTION_ACTIVITIES, ACTION_GALLERY, ACTION_LOCALIZATION, ACTION_LABLANCA, ACTION_SCHEDULE, ACTION_GM_SCHEDULE, ACTION_US];
+
+    // Default values
+    define('DEF_GM', 0);
+    define('DEF_ACTION', ACTION_TEXT);
+
+    // Error messages
+    define('ERR_USER', '-USER:');
+    define('ERR_ACTION', '-ACTION:');
+    define('ERR_TITLE', '-TITLE:');
+    define('ERR_TEXT', '-TEXT:');
+    define('ERR_DURATION', '-DURATION:');
+    define('ERR_GM', '-GM:');
+    define('ERR_PERM', '-PERM:');
+    define('ERR_ID', '-ID:');
+
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+         <?php
+          $host = 'XXXX';
+          $db_name = 'XXXX';
+          $username_ro = 'XXXX';
+          $username_rw = 'XXXX';
+          $pass_ro = 'XXXX';
+          $pass_rw = 'XXXX';
+         ?>
+        */
+        include('../../.htpasswd');
+
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+
+        //Return the db connection
+        return $con;
+    }
+
+    $con = startdb();
+
+    // Get fields
+    $user = mysqli_real_escape_string($con, $_GET[GET_USER]);
+    $pass = mysqli_real_escape_string($con, $_GET[GET_PASS]);
+    $title_es = urldecode(mysqli_real_escape_string($con, $_GET[GET_TITLE_ES]));
+    $title_en = urldecode(mysqli_real_escape_string($con, $_GET[GET_TITLE_EN]));
+    $title_eu = urldecode(mysqli_real_escape_string($con, $_GET[GET_TITLE_EU]));
+    $text_es = urldecode(mysqli_real_escape_string($con, $_GET[GET_TEXT_ES]));
+    $text_en = urldecode(mysqli_real_escape_string($con, $_GET[GET_TEXT_EN]));
+    $text_eu = urldecode(mysqli_real_escape_string($con, $_GET[GET_TEXT_EU]));
+    $duration = mysqli_real_escape_string($con, $_GET[GET_DURATION]);
+    $action = mysqli_real_escape_string($con, $_GET[GET_ACTION]);
+    $id = mysqli_real_escape_string($con, $_GET[GET_ID]);
+    $perm = mysqli_real_escape_string($con, $_GET[GET_PERM]);
+    $gm = mysqli_real_escape_string($con, $_GET[GET_GM]);
+
+    // Error control
+    $error = "";
+
+    // Validate user/pass
+    $q = mysqli_query($con, "SELECT id FROM user WHERE (lower(username) = lower('$user') OR lower(email) = lower('$user')) AND password = sha1(concat('$pass', sha1(salt)))");
+    if (mysqli_num_rows($q) == 0){
+        error_log(":SECURITY: Reporting location with wrong credentials (IP $_SERVER[REMOTE_ADDR])");
+        http_response_code(403); // Forbidden
+        $error = $error . ERR_USER . mysqli_real_escape_string($con, $_GET[GET_USER]);
+        error_log($error);
+        exit(-1);
+    }
+    $r = mysqli_fetch_array($q);
+    $uid = $r['id'];
+
+    //Validate fields
+    if (strlen($title_es) == 0){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_TITLE . $title_es;
+        error_log($error);
+        exit(-2);
+    }
+
+    if (strlen($text_es) == 0){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_TEXT . $text_es;
+        error_log($error);
+        exit(-3);
+    }
+
+    if (is_numeric($duration) == false || $duration < 1 && $duration > 48 * 60){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_DURATION . $duration;
+        error_log($error);
+        exit(-4);
+    }
+
+    if (!in_array($action, $actions)){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_ACTION . $action;
+        error_log($error);
+        exit(-5);
+    }
+
+    //Handle translations
+    if (strlen($title_en) == 0){
+        $title_en = $title_es;
+    }
+    if (strlen($title_eu) == 0){
+        $title_eu = $title_es;
+    }
+    if (strlen($text_en) == 0){
+        $text_en = $text_es;
+    }
+    if (strlen($text_eu) == 0){
+        $text_eu = $text_es;
+    }
+
+    //Insert
+    if (strlen($error) == 0){
+        error_log("INSERT INTO notification (user, title_es, title_en, title_eu, text_es, text_en, text_eu, action, duration) VALUES ($uid, '$title_es', '$title_en', '$title_eu', '$text_es', '$text_en', '$text_eu', '$action', $duration);");
+        mysqli_query($con, "INSERT INTO notification (user, title_es, title_en, title_eu, text_es, text_en, text_eu, action, duration) VALUES ($uid, '$title_es', '$title_en', '$title_eu', '$text_es', '$text_en', '$text_eu', '$action', $duration);");
+        http_response_code(204); // No content;
+        exit(0);
+    }
+    else{
+        error_log($error);
+        exit(-6);
+    }
+?>

+ 399 - 0
www/API/v2/sync.php

@@ -0,0 +1,399 @@
+ <?php
+    // Gasteizko Margolariak API v1 //
+        
+    //List of available data formatting
+    define('FOR_JSON', 'json');
+    
+    //Default info format
+    define('DEF_FORMAT', FOR_JSON);
+
+    //Database section identifiers
+    define('SEC_ALL', 'all');
+    define('SEC_BLOG', 'blog');
+    define('SEC_ACTIVITIES', 'activities');
+    define('SEC_GALLERY', 'gallery');
+    define('SEC_LABLANCA', 'lablanca');
+    
+    //Posible actions
+    define('ACTION_SYNC', 'sync');
+    define('ACTION_VERSION', 'version');
+    
+    //Default action
+    define('DEF_ACTION', ACTION_SYNC);
+    
+    //Output keys
+    define('KEY_VERSION', 'version');
+    define('KEY_DATA', 'data');
+    
+    //$_GET valid parameters
+    define('GET_CLIENT', 'client');
+    define('GET_USER', 'user');
+    define('GET_ACTION', 'action');
+    define('GET_SECTION', 'section');
+    define('GET_VERSION', 'version');
+    define('GET_FOREGROUND', 'foreground');
+    define('GET_FORMAT', 'json');
+    
+    //Error messages
+    define('ERR_ACTION', '-ACTION:');
+    define('ERR_SECTION', '-SECTION:');
+    define('ERR_VERSION', '-VERSION:');
+    define('ERR_FOREGROUND', '-FOREGROUND:');
+    define('ERR_FORMAT', '-FORMAT:');
+    
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+        <?php
+            $host = 'XXXX';
+            $db_name = 'XXXX';
+            $username_ro = 'XXXX';
+            $username_rw = 'XXXX';
+            $pass_ro = 'XXXX';
+            $pass_rw = 'XXXX';
+        ?>
+        */
+        include('../../.htpasswd');
+        
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+        
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+        
+        //Return the db connection
+        return $con;
+    }    
+    
+    /****************************************************
+    * Gives the version of one or more of the sections  *
+    * of the database, or the global section.           * 
+    *                                                   *
+    * @params:                                          *
+    *    con: (MySQL server connection) RO mode enough. *
+    *    section: (string): 'blog', 'activities',       *
+    *             'gallery', 'lablanca', 'global'. None *
+    *             to get them all.                      *
+    * @return: (int): The version of the db section.    *
+     ****************************************************/
+    function get_version($con, $section = SEC_ALL){
+        if ($section == SEC_ALL){
+        $q = mysqli_query($con, "SELECT SUM(version) AS version FROM version;");
+        }
+        else{
+            $q = mysqli_query($con, "SELECT version FROM version WHERE section = '$section';");
+        }
+        if (mysqli_num_rows($q) == 0){
+            //'Bad request' status code
+            http_response_code(400);
+            return 0;
+        }
+        else{
+            $r = mysqli_fetch_array($q);
+            return($r['version']);
+        }
+    }
+    
+    /****************************************************
+    * Gives the version of every section of the db.     *
+    *                                                   *
+    * @params:                                          *
+    *    con: (MySQL server connection) RO mode enough. *
+    * @return: (Assoc. array): The versions of the db.  *
+     ****************************************************/
+    function get_all_versions($con){
+        $v = array();
+        $v[] = [SEC_ALL => get_version($con, SEC_ALL)];
+        $v[] = [SEC_BLOG => get_version($con, SEC_BLOG)];
+        $v[] = [SEC_ACTIVITIES => get_version($con, SEC_ACTIVITIES)];
+        $v[] = [SEC_GALLERY => get_version($con, SEC_GALLERY)];
+        $v[] = [SEC_LABLANCA => get_version($con, SEC_LABLANCA)];
+        return $v;
+    }
+    
+    /****************************************************
+    * Prepares the info of the database or a portion of *
+    * it in the selected format.                        * 
+    *                                                   *
+    * @params:                                          *
+    *    con: (MySQL server connection) RO mode enough. *
+    *    section: (string): 'blog', 'activities',       *
+    *             'gallery', 'lablanca', 'global'. None *
+    *             to get them all.                      *
+    *    format: (int): 1: json (default).              *
+    * @return: (String): data in the desired format.    *
+     ****************************************************/
+    function sync($con, $section = SEC_ALL, $format = DEF_FORMAT){
+        
+        switch ($section){
+            case SEC_BLOG:
+                $tables = [ 'post', 'post_comment', 'post_image', 'post_tag' ];
+                break;
+            case SEC_ACTIVITIES:
+                $tables = [ 'activity', 'activity_itinerary', 'activity_comment', 'activity_image', 'activity_tag', 'activity_itinerary', 'album', 'photo', 'photo_album', 'photo_comment', 'place' ];
+                break;
+            case SEC_GALLERY:
+                $tables = [ 'album', 'photo', 'photo_album', 'photo_comment', 'place' ];
+                break;
+            case SEC_LABLANCA:
+                $tables = [ 'festival', 'festival_day', 'festival_event', 'festival_event_image', 'festival_offer', 'place', 'people' ];
+                break;
+            case SEC_ALL:
+                $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', 'people', 'post', 'post_comment', 'post_image', 'post_tag', 'settings', 'sponsor' ];
+                break;
+            default:
+                //'Bad request' staus code
+                http_response_code(400);
+                return;
+        }
+        
+        $v = array();
+        if ($section == SEC_ALL){
+            $v = get_all_versions($con);
+        }
+        else{
+            $v[] = [ $section => get_version($con, $section) ];
+        }
+        
+        switch ($format){
+            case FOR_JSON:
+                $db = array();
+                foreach($tables as $table){
+                    $req_table = get_table($con, $table, $format);
+                    if ($req_table != -1){
+                        $db[] = [ $table => $req_table];
+                    }
+                }
+                $data = array();
+                $data[] = [ KEY_VERSION => $v ];
+                $data[] = [ KEY_DATA => $db];
+                return(json_encode($data));
+                break;
+        }
+        
+    }
+    
+    /****************************************************
+    * Echoes the contents of a table from the database. *
+    * Inaccessible or sensitive tables or fields are    *
+    * not printed.                                      *
+    *                                                   *
+    * @params:                                          *
+    *    con: (MySQL server connection) RO mode enough. *
+    *    table (string): The name of the table.         *
+    * @return: (Assoc Array): Data in the table.        *
+     ****************************************************/
+    function get_table($con, $table){
+        $table = strtolower($table);
+        switch ($table){
+            case "activity":
+                $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;");
+                break;
+            case "activity_comment":
+                $q = mysqli_query($con, "SELECT id, activity, text, dtime, username, lang FROM activity_comment WHERE approved = 1;");
+                break;
+            case "album":
+                $q = mysqli_query($con, "SELECT id, permalink, title_es, title_en, title_eu, description_es, description_en, description_eu, open FROM album;");
+                break;
+            case "photo":
+                $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;");
+                break;
+            case "post":
+                $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;");
+                break;
+            case "post_comment":
+                $q = mysqli_query($con, "SELECT post_comment.id AS id, post, text, dtime, username, lang FROM post_comment WHERE approved = 1;");
+                break;
+            case "photo_comment":
+                $q = mysqli_query($con, "SELECT photo_comment.id AS id, post, text, dtime, username, lang FROM photo_comment WHERE approved = 1;");
+                break;
+            case "sponsor":
+                $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;");
+                break;
+            case "settings":
+                $q = mysqli_query($con, "SELECT name, value FROM settings");
+                break;
+                
+            //Other cases: 
+            default:
+                //If the table is a public one and has not been listed above, all of its fields are public.
+                if (in_array($table, ['activity_image', 'activity_itinerary', 'activity_tag', 'festival', 'festival_day', 'festival_event', 'festival_event_image', 'festival_offer', 'photo_album', 'place', 'people', 'post_image', 'post_tag'])){
+                    $q = mysqli_query($con, "SELECT * FROM $table;");
+                }
+                //If forbidden table
+                else{
+                    //'Forbidden' status code
+                    //TODO: I dont want execution stopping here
+                    http_response_code(403);
+                    return;
+                }
+        }
+        
+        //If no rows, return
+        if (mysqli_num_rows($q) == 0){
+            return -1;
+        }
+        
+        //Create result array
+        $rows = array();
+        while($r = mysqli_fetch_assoc($q)) {
+            $rows[] = $r;
+        }
+        return $rows;
+    }
+    
+    
+    /****************************************************
+    * gets the IP address of the client.                *
+    *                                                   *
+    * @return: (String): Client IP address.             *
+     ****************************************************/
+    function get_user_ip(){
+        $client  = @$_SERVER['HTTP_CLIENT_IP'];
+        $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
+        $remote  = $_SERVER['REMOTE_ADDR'];
+        if(filter_var($client, FILTER_VALIDATE_IP)){
+            $ip = $client;
+        }
+        elseif(filter_var($forward, FILTER_VALIDATE_IP)){
+            $ip = $forward;
+        }
+        else{
+            $ip = $remote;
+        }
+        return $ip;
+    }
+    
+    /****************************************************
+    * Registers the request in the database.            *
+    *                                                   *
+    * @params:                                          *
+    *    con: (MySQL server connection) RO mode enough. *
+    *    client: (string): The client identifier.       *
+    *    user: (string): A unique end user identifier.  *
+    *    action: (string): Requested action.            *
+    *    section: (string): Requested database section. *
+    *    version: (int): Version of the client db.      *
+    *    new_version: (int): Returned version.          *
+    *    foreground: (int): 1 for fg syncs, 0 for bg.   *
+    *    format: (string): Requested format.            *
+    *    error: (string): Error message to store.       *
+    ****************************************************/
+    function log_sync($con, $client, $user, $action, $section, $version, $new_version, $foreground, $format, $error){
+        $ip = get_user_ip();
+        $browser_data = get_browser(null, true);
+        $os = $browser_data['platform'];
+        $browser = $browser_data['browser'];
+        $uagent = $browser_data['browser_name_pattern'];
+        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');");
+        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');");
+    }
+    
+    //Connect to the database
+    $con = startdb('rw');
+    
+    //Get data from URL
+    $client = mysqli_real_escape_string($con, $_GET[GET_CLIENT]);
+    $user = mysqli_real_escape_string($con, $_GET[GET_USER]);
+    $action = strtolower(mysqli_real_escape_string($con, $_GET[GET_ACTION]));
+    $section = strtolower(mysqli_real_escape_string($con, $_GET[GET_SECTION]));
+    $version = (int) mysqli_real_escape_string($con, $_GET[GET_VERSION]);
+    $foregroud = (int) mysqli_real_escape_string($con, $_GET[GET_FOREGROUND]);
+    $format = strtolower(mysqli_real_escape_string($con, $_GET[GET_FORMAT]));
+    
+    //Initialize some variables
+    $error = '';
+    $new_version = -1;
+    
+    //Validate data
+    if (strlen($client) < 1){
+        $client = '';
+    }
+    if (strlen($user) < 1){
+        $user = '';
+    }
+    if (strlen($action) < 1){
+        $action = DEF_ACTION;
+    }
+    if ($action != ACTION_SYNC && $action != ACTION_VERSION){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_ACTION . mysqli_real_escape_string($con, $_GET[GET_ACTION]);
+    }
+    if (strlen($section) > 0 && $section != SEC_ALL && $section != SEC_BLOG && $section != SEC_ACTIVITIES && $section != SEC_GALLERY && $section != SEC_LABLANCA ){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_SECTION . mysqli_real_escape_string($con, $_GET[GET_SECTION]);
+    }
+    if (strlen($section) == 0){
+        $section = SEC_ALL;
+    }
+    if (strlen($version) == 0){
+        $version = -1;
+    }
+    if (is_int($version) == false){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_VERSION . mysqli_real_escape_string($con, $_GET[GET_VERSION]);
+        $version = -1;
+    }
+    if (strlen($foregroud) < 1){
+        $foreground = 1;
+    }
+    if ($foreground != 0 && $foregroud != 0){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_FOREGROUND . mysqli_real_escape_string($con, $_GET[GET_BACKGROUND]);
+    }
+    if (strlen($format) < 1){
+        $format = DEF_FORMAT;
+    }
+    if ($format != FOR_JSON){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_FORMAT . mysqli_real_escape_string($con, $_GET[GET_FORMAT]);
+    }
+    
+    //If there has not been an error, procede
+    if (strlen($error) == 0){
+        //If the client just needs to know the version number
+        if ($action == ACTION_VERSION){
+            $new_version = get_version($con, $section);
+            echo ($new_version);
+        }
+        //If the clients wants to actually perform a sync
+        else{
+            //If the client version is up to date, send a no content status
+            $new_version = get_version($con, $section);
+            if ($version >= $new_version){
+                //No content
+                http_response_code(204);
+            }
+            //If the client needs an update
+            else{
+                $out = sync($con, $section);
+                $out = str_replace('\"', '\u0022', $out);
+                $out = str_replace(':""', ':null', $out);
+                $out = "{\"sync\":$out}";
+                echo($out);
+            }    
+        }
+    }
+    
+    //Log the sync in the database
+    log_sync($con, $client, $user, $action, $section, $version, $new_version, $foreground, $format, $error);
+    
+    
+?>

+ 311 - 0
www/API/v3/comment.php

@@ -0,0 +1,311 @@
+<?php
+    // Gasteizko Margolariak API v1 //
+    
+    //Posible comment target
+    define('TARGET_PHOTO', 'photo');
+    define('TARGET_POST', 'post');
+    define('TARGET_ACTIVITY', 'activity');
+    
+    //Default target
+    define('DEF_TARGET', TARGET_ALL);
+        
+    //$_GET valid parameters
+    define('GET_CLIENT', 'client');
+    define('GET_USER', 'user');
+    define('GET_TARGET', 'target');
+    define('GET_ID', 'id');
+    define('GET_PERMALINK', 'permalink');
+    define('GET_TEXT', 'text');
+    define('GET_USERNAME', 'username');
+    
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+        <?php
+            $host = 'XXXX';
+            $db_name = 'XXXX';
+            $username_ro = 'XXXX';
+            $username_rw = 'XXXX';
+            $pass_ro = 'XXXX';
+            $pass_rw = 'XXXX';
+        ?>
+        */
+        include('../../.htpasswd');
+        
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+        
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+        
+        //Return the db connection
+        return $con;
+    }
+    
+    //Connect to the database
+    $con = startdb('rw');
+    
+    //Get data from URL
+    $client = mysqli_real_escape_string($con, $_GET[GET_CLIENT]);
+    $user = mysqli_real_escape_string($con, $_GET[GET_USER]);
+    $target = strtolower(mysqli_real_escape_string($con, $_GET[GET_TARGET]));
+    $id = strtolower(mysqli_real_escape_string($con, $_GET[GET_ID]));
+    $permalink = strtolower(mysqli_real_escape_string($con, $_GET[GET_PERMALINK]));
+    $ink = strtolower(mysqli_real_escape_string($con, $_GET[GET_PERMALINK]));
+    $permalink = strtolower(mysqli_real_escape_string($con, $_GET[GET_PERMALINK]));
+    $username = mysqli_real_escape_string($con, $_GET[GET_USERNAME]);
+    $text = mysqli_real_escape_string($con, $_GET[GET_TEXT]);
+    
+    //Validate data
+    if (strlen($client) < 1){
+        //Bad request
+        http_response_code(400);
+        exit();
+    }
+    if (strlen($user) < 1){
+        $user = '';
+    }
+    if (strlen($target) < 1){
+        //Bad request
+        http_response_code(400);
+        exit();
+    }
+    if ($target != TARGET_PHOTO && $target != TARGET_POST && $target != TARGET_ACTIVITY){
+        //Bad request
+        http_response_code(400);
+        exit();
+    }
+    if (strlen($username) < 1){
+        //Bad request
+        http_response_code(400);
+        exit();
+    }
+    
+    
+    //Check id and/or permalink. Several cases:
+    
+    //1st case: id and permalink empty: Error.
+    if (strlen($id) < 1 && strlen($permalink) < 1){
+        //Bad request
+        http_response_code(400);
+        exit();
+    }
+    
+    //2nd case: Comment for post, permalink and no id.
+    elseif ($target == TARGET_POST && strlen($id) < 1 && strlen($permalink) >= 1){
+    
+        //Check if post exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM post WHERE visible = 1 AND permalink = '$permalink';");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //3rd case: Comment for post, id and no permalink.
+    elseif ($target == TARGET_POST && strlen($id) >= 1 && strlen($permalink) < 1){
+    
+        //Check if post exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM post WHERE visible = 1 AND id = $id;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //4th case: Comment for post, permalink and id.
+    elseif ($target == TARGET_POST && strlen($id) >= 1 && strlen($permalink) >= 1){
+    
+        //Check if post exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM post WHERE visible = 1 AND permalink = '$permalink' AND id = $id ;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //5th case: Comment for photo, permalink and no id.
+    elseif ($target == TARGET_PHOTO && strlen($id) < 1 && strlen($permalink) >= 1){
+    
+        //Check if photo exists.
+        $q = mysqli_query($con, "SELECT id FROM photo WHERE approved = 1 AND permalink = '$permalink';");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+            $item_id = $r['id'];
+        }
+    }
+    
+    //6th case: Comment for photo, id and no permalink.
+    elseif ($target == TARGET_PHOTO && strlen($id) >= 1 && strlen($permalink) < 1){
+    
+        //Check if photo exists.
+        $q = mysqli_query($con, "SELECT id FROM photo WHERE approved = 1 AND id = $id;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+            $item_id = $r['id'];
+        }
+    }
+    
+    //7th case: Comment for photo, permalink and id.
+    elseif ($target == TARGET_PHOTO && strlen($id) >= 1 && strlen($permalink) >= 1){
+    
+        //Check if photo exists.
+        $q = mysqli_query($con, "SELECT id FROM photo WHERE approved = 1 AND permalink = '$permalink' AND id = $id;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+            $item_id = $r['id'];
+        }
+    }
+    
+    //8nd case: Comment for activity, permalink and no id.
+    elseif ($target == TARGET_ACTIVITY && strlen($id) < 1 && strlen($permalink) >= 1){
+    
+        //Check if activity exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM activity WHERE visible = 1 AND permalink = '$permalink';");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //9rd case: Comment for activity, id and no permalink.
+    elseif ($target == TARGET_ACTIVITY && strlen($id) >= 1 && strlen($permalink) < 1){
+    
+        //Check if activity exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM activity WHERE visible = 1 AND id = $id;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //10th case: Comment for activity, permalink and id.
+    elseif ($target == TARGET_ACTIVITY && strlen($id) >= 1 && strlen($permalink) >= 1){
+    
+        //Check if activity exists...
+        $q = mysqli_query($con, "SELECT id, comments FROM activity WHERE visible = 1 AND permalink = '$permalink' AND id = $id ;");
+        if (mysqli_num_rows($q) == 0){
+            //Bad request
+            http_response_code(400);
+            exit();
+        }
+        else{
+        
+            //... and if it does, check if can be commented.
+            $r = mysqli_fetch_array($q);
+            $item_id = $r['id'];
+            if ($r['comments'] != 1){
+                //'Forbidden' status code
+                http_response_code(403);
+                exit();
+            }
+        }
+    }
+    
+    //If code gets here, there were no errors. Build query.
+    $query = "INSERT INTO ";
+    $section = "";
+    switch ($target){
+        case TARGET_POST:
+            $query = $query . "post_comment (post";
+            $section = "blog";
+            break;
+        case TARGET_PHOTO:
+            $query = $query . "photo_comment (photo";
+            $section = "gallery";
+            break;
+        case TARGET_ACTIVITY:
+            $query = $query . "activity_comment (activity";
+            $section = "activity";
+            break;
+    }
+    $query = $query . ", text, username, app) VALUES ($item_id, \"$text\", \"$username\", \"client\");";
+    //echo($query);
+    mysqli_query($con, $query);
+    mysqli_query($con, "UPDATE version SET version = version + 1 WHERE section = '$section';");
+
+?>

+ 56 - 0
www/API/v3/help/comment.php

@@ -0,0 +1,56 @@
+<?php 
+    $http_host = $_SERVER['HTTP_HOST']; 
+    $v = 1;
+?>
+
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
+        <meta charset="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
+        <title>Gasteizko Margolariak API v<?php echo($v); ?> Documentation</title>
+        <link rel="shortcut icon" href="<?php echo "http://$http_host/img/logo/favicon.ico";?>">
+        <!-- CSS files -->
+        <style>
+            <?php 
+                include("../../../css/ui.css"); 
+                include("../../../css/index.css");
+            ?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media="(max-width : 990px)">
+            <?php 
+                include("../../../css/m/ui.css"); 
+                include("../../../css/m/index.css");
+            ?>
+        </style>
+        <!-- Script files -->
+        <script type="text/javascript">
+            <?php include("../../../script/ui.js"); ?>
+        </script>
+        <!-- Meta tags -->
+        <link rel="canonical" href="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <link rel="author" href="<?php echo "http://$http_host"; ?>"/>
+        <link rel="publisher" href="<?php echo "http://$http_host"; ?>"/>
+        <meta name="description" content="<?php echo $lng['index_description'];?>"/>
+        <meta property="og:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation"/>
+        <meta property="og:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta property="og:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta property="og:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta property="og:site_name" content="Gasteizko Margolariak"/>
+        <meta property="og:type" content="website"/>
+        <meta property="og:locale" content="en"/>
+        <meta name="twitter:card" content="summary"/>
+        <meta name="twitter:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation""/>
+        <meta name="twitter:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta name="twitter:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta name="twitter:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta name="robots" content="index follow"/>
+    </head>
+    <body>
+        <?php include("toolbar.php"); ?>
+        <div id="content">
+        </div>
+    </body>
+</body>

+ 56 - 0
www/API/v3/help/index.php

@@ -0,0 +1,56 @@
+<?php 
+    $http_host = $_SERVER['HTTP_HOST']; 
+    $v = 1;
+?>
+
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
+        <meta charset="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
+        <title>Gasteizko Margolariak API v<?php echo($v); ?> Documentation</title>
+        <link rel="shortcut icon" href="<?php echo "http://$http_host/img/logo/favicon.ico";?>">
+        <!-- CSS files -->
+        <style>
+            <?php 
+                include("../../../css/ui.css"); 
+                include("../../../css/index.css");
+            ?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media="(max-width : 990px)">
+            <?php 
+                include("../../../css/m/ui.css"); 
+                include("../../../css/m/index.css");
+            ?>
+        </style>
+        <!-- Script files -->
+        <script type="text/javascript">
+            <?php include("../../../script/ui.js"); ?>
+        </script>
+        <!-- Meta tags -->
+        <link rel="canonical" href="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <link rel="author" href="<?php echo "http://$http_host"; ?>"/>
+        <link rel="publisher" href="<?php echo "http://$http_host"; ?>"/>
+        <meta name="description" content="<?php echo $lng['index_description'];?>"/>
+        <meta property="og:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation"/>
+        <meta property="og:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta property="og:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta property="og:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta property="og:site_name" content="Gasteizko Margolariak"/>
+        <meta property="og:type" content="website"/>
+        <meta property="og:locale" content="en"/>
+        <meta name="twitter:card" content="summary"/>
+        <meta name="twitter:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation""/>
+        <meta name="twitter:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta name="twitter:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta name="twitter:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta name="robots" content="index follow"/>
+    </head>
+    <body>
+        <?php include("toolbar.php"); ?>
+        <div id="content">
+        </div>
+    </body>
+</body>

+ 56 - 0
www/API/v3/help/sync.php

@@ -0,0 +1,56 @@
+<?php 
+    $http_host = $_SERVER['HTTP_HOST']; 
+    $v = 1;
+?>
+
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
+        <meta charset="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
+        <title>Gasteizko Margolariak API v<?php echo($v); ?> Documentation</title>
+        <link rel="shortcut icon" href="<?php echo "http://$http_host/img/logo/favicon.ico";?>">
+        <!-- CSS files -->
+        <style>
+            <?php 
+                include("../../../css/ui.css"); 
+                include("../../../css/index.css");
+            ?>
+        </style>
+        <!-- CSS for mobile version -->
+        <style media="(max-width : 990px)">
+            <?php 
+                include("../../../css/m/ui.css"); 
+                include("../../../css/m/index.css");
+            ?>
+        </style>
+        <!-- Script files -->
+        <script type="text/javascript">
+            <?php include("../../../script/ui.js"); ?>
+        </script>
+        <!-- Meta tags -->
+        <link rel="canonical" href="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <link rel="author" href="<?php echo "http://$http_host"; ?>"/>
+        <link rel="publisher" href="<?php echo "http://$http_host"; ?>"/>
+        <meta name="description" content="<?php echo $lng['index_description'];?>"/>
+        <meta property="og:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation"/>
+        <meta property="og:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta property="og:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta property="og:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta property="og:site_name" content="Gasteizko Margolariak"/>
+        <meta property="og:type" content="website"/>
+        <meta property="og:locale" content="en"/>
+        <meta name="twitter:card" content="summary"/>
+        <meta name="twitter:title" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation""/>
+        <meta name="twitter:description" content="Gasteizko Margolariak API v<?php echo($v); ?> Documentation - Index page"/>
+        <meta name="twitter:image" content="<?php echo "http://$http_host/img/logo/logo-api.png";?>"/>
+        <meta name="twitter:url" content="<?php echo "http://$http_host/API/help/V$v"; ?>"/>
+        <meta name="robots" content="index follow"/>
+    </head>
+    <body>
+        <?php include("toolbar.php"); ?>
+        <div id="content">
+        </div>
+    </body>
+</body>

+ 27 - 0
www/API/v3/help/toolbar.php

@@ -0,0 +1,27 @@
+ <div id="header" class="desktop">
+    <div id="header_content">
+        <img src="/img/logo/logo-api.png"/>
+        <div id="header_menu">
+            <table>
+                <tr>
+                    <td><a href="http://<?php echo($http_host . "API/help/V$v/"); ?>">API documentation</a></td>
+                    <td><a href="http://<?php echo($http_host . "API/help/V$v/sync/"); ?>">Sync</a></td>
+                    <td><a href="http://<?php echo($http_host . "API/help/V$v/comment/"); ?>">Comment</a></td>
+                    <td><a href="http://<?php echo($http_host); ?>/">Main page</a></td>
+                </tr>
+            </table>
+        </div>
+    </div>
+</div>
+<div id="header_m" class="mobile">
+    <img src="/img/logo/logo-api.png" onClick='toggleMobileMenu();' id='mobile_logo'/>
+    <div id="header_menu_m">
+        <div id='header_m_title' onClick='openMobileMenu();' class='pointer'><span><img src='http://<?php echo $http_host; ?>/img/misc/slid-menu.png'/>&nbsp;&nbsp;&nbsp;&nbsp;<?php echo $cur_section; ?></span></div>
+        <div class='header_m_link'><a href="http://<?php echo($http_host . "API/help/V$v/"); ?>">API documentation</a></div>
+        <div class='header_m_link'><a href="http://<?php echo($http_host . "API/help/V$v/sync/"); ?>">Sync</a></div>
+        <div class='header_m_link'><a href="http://<?php echo($http_host . "API/help/V$v/comment/"); ?>">Comment</a></div>
+        <div class='header_m_link'><a href="http://<?php echo($http_host); ?>/">Main page</a></div>
+        <div id='header_m_slider' onClick='closeMobileMenu();' class='pointer'><span><img src='http://<?php echo $http_host; ?>/img/misc/slid-top.png'/></span></div>
+    </div><br/><br/>
+</div>
+

+ 87 - 0
www/API/v3/location.php

@@ -0,0 +1,87 @@
+<?php
+    // Gasteizko Margolariak API v1 //
+        
+    //List of available data formatting
+    define('FOR_JSON', 'json');
+    
+    //Default info format
+    define('DEF_FORMAT', FOR_JSON);
+    
+    //Errors
+    define('ERR_FORMAT', '-FORMAT:');
+    
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+        <?php
+            $host = 'XXXX';
+            $db_name = 'XXXX';
+            $username_ro = 'XXXX';
+            $username_rw = 'XXXX';
+            $pass_ro = 'XXXX';
+            $pass_rw = 'XXXX';
+        ?>
+        */
+        include('../../.htpasswd');
+        
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+        
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+        
+        //Return the db connection
+        return $con;
+    }
+    
+    //Connect to the database
+    $con = startdb('rw');
+    
+    //Get data from the url
+    $format = strtolower(mysqli_real_escape_string($con, $_GET[GET_FORMAT]));
+    
+    //Validate input
+    if (strlen($format) < 1){
+        $format = DEF_FORMAT;
+    }
+    if ($format != FOR_JSON){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_FORMAT . mysqli_real_escape_string($con, $_GET[GET_FORMAT]);
+        error_log($error);
+        exit(-1);
+    }
+    
+    //Get location
+    $q = mysqli_query($con, "SELECT lat, lon, dtime FROM location WHERE action <> 'F' AND lat IS NOT null AND lon IS NOT null AND dtime > NOW() - INTERVAL 30 MINUTE ORDER BY dtime DESC LIMIT 1;");
+    if (mysqli_num_rows($q) > 0){
+        $r = mysqli_fetch_array($q);
+        switch ($format){
+            case FOR_JSON:
+                echo("[{\"lat\":\"$r[lat]\",\"lon\":\"$r[lon]\",\"dtime\":\"$r[dtime]\"}]");
+                break;
+            default:
+                http_response_code(400);
+                $error = $error . ERR_FORMAT . mysqli_real_escape_string($con, $_GET[GET_FORMAT]);
+                error_log($error);
+                exit(-2);
+        }
+    }
+    else{
+        //No content
+        http_response_code(204);
+        exit(0);
+    }
+?>

+ 130 - 0
www/API/v3/notifications.php

@@ -0,0 +1,130 @@
+<?php
+    // Gasteizko Margolariak API v1 //
+
+    //List of available data formatting
+    define('FOR_JSON', 'json');
+
+    //Default info format
+    define('DEF_FORMAT', FOR_JSON);
+
+    //Posible notification target
+    define('TARGET_ALL', 'all');
+    define('TARGET_GM', 'gm');
+
+    //Default target
+    define('DEF_TARGET', TARGET_ALL);
+
+    //$_GET valid parameters
+    define('GET_CLIENT', 'client');
+    define('GET_USER', 'user');
+    define('GET_TARGET', 'target');
+    define('GET_FORMAT', 'json');
+
+    //Error messages
+    define('ERR_TARGET', '-TARGET:');
+    define('ERR_FORMAT', '-FORMAT:');
+
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+        <?php
+            $host = 'XXXX';
+            $db_name = 'XXXX';
+            $username_ro = 'XXXX';
+            $username_rw = 'XXXX';
+            $pass_ro = 'XXXX';
+            $pass_rw = 'XXXX';
+        ?>
+        */
+        include('../../.htpasswd');
+        
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+        
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+        
+        //Return the db connection
+        return $con;
+    }
+    
+    function show_notifications($con, $target = DEF_TARGET, $format = DEF_FORMAT){
+        if ($target == TARGET_GM){
+            $query = "SELECT id, title_es, title_en, title_eu, text_es, text_en, text_eu, dtime, internal AS gm, duration, action, 0 AS seen FROM notification WHERE internal = 1 AND dtime > NOW() - INTERVAL duration MINUTE ORDER BY dtime DESC";
+        }
+        else{
+            $query = "SELECT id, title_es, title_en, title_eu, text_es, text_en, text_eu, dtime, internal AS gm, duration, action, 0 AS seen FROM notification WHERE dtime > NOW() - INTERVAL duration MINUTE ORDER BY dtime DESC";
+        }
+        $q = mysqli_query($con, $query);
+        if (mysqli_num_rows($q) == 0){
+            http_response_code(204);
+        }
+        else{
+            switch ($format){
+                case FOR_JSON:
+                    //Create result array
+                    $rows = array();
+                    while($r = mysqli_fetch_assoc($q)) {
+                        $rows[] = $r;
+                    }
+                    return(json_encode($rows));
+                    break;
+            }
+        }
+    }
+    
+    //Connect to the database
+    $con = startdb('rw');
+    
+    //Get data from URL
+    $client = mysqli_real_escape_string($con, $_GET[GET_CLIENT]);
+    $user = mysqli_real_escape_string($con, $_GET[GET_USER]);
+    $target = strtolower(mysqli_real_escape_string($con, $_GET[GET_TARGET]));
+    $format = strtolower(mysqli_real_escape_string($con, $_GET[GET_FORMAT]));
+    
+    //Initialize some variables
+    $error = '';
+    
+    //Validate data
+    if (strlen($client) < 1){
+        $client = '';
+    }
+    if (strlen($user) < 1){
+        $user = '';
+    }
+    if (strlen($target) < 1){
+        $target = DEF_TARGET;
+    }
+    if ($target != TARGET_ALL && $action != TARGET_GM){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_TARGET . mysqli_real_escape_string($con, $_GET[GET_TARGET]);
+    }
+    if (strlen($format) < 1){
+        $format = DEF_FORMAT;
+    }
+    if ($format != FOR_JSON){
+        //Bad request
+        http_response_code(400);
+        $error = $error . ERR_FORMAT . mysqli_real_escape_string($con, $_GET[GET_FORMAT]);
+    }
+
+
+    //If there has not been an error, procede
+    if (strlen($error) == 0){
+        echo(show_notifications($con, $target, $format));
+    }
+
+?>

+ 160 - 0
www/API/v3/sendlocation.php

@@ -0,0 +1,160 @@
+<?php
+
+    // $_GET valid parameters
+    define('GET_USER', 'user');
+    define('GET_PASS', 'pass');
+    define('GET_ACTION', 'action');
+    define('GET_LAT', 'lat');
+    define('GET_LON', 'lon');
+
+    // Valid values
+    define('ACTION_START', 'start');
+    define('ACTION_REFRESH', 'refresh');
+    define('ACTION_STOP', 'stop');
+    $actions = [ACTION_START, ACTION_REFRESH, ACTION_STOP];
+
+    // Error messages
+    define('ERR_USER', '-USER:');
+    define('ERR_ACTION', '-ACTION:');
+    define('ERR_LOCATION', '-TITLE:');
+
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+         <?php
+          $host = 'XXXX';
+          $db_name = 'XXXX';
+          $username_ro = 'XXXX';
+          $username_rw = 'XXXX';
+          $pass_ro = 'XXXX';
+          $pass_rw = 'XXXX';
+         ?>
+        */
+        include('../../.htpasswd');
+
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+
+        //Return the db connection
+        return $con;
+    }
+
+    $con = startdb('rw');
+    $error = "";
+
+    //Get fields
+    $user = mysqli_real_escape_string($con, $_GET[GET_USER]);
+    $pass = mysqli_real_escape_string($con, $_GET[GET_PASS]);
+    $lat = mysqli_real_escape_string($con, $_GET[GET_LAT]);
+    $lon = mysqli_real_escape_string($con, $_GET[GET_LON]);
+    $action = mysqli_real_escape_string($con, $_GET[GET_ACTION]);
+
+    //Validate user
+    $q = mysqli_query($con, "SELECT id FROM user WHERE (lower(username) = lower('$user') OR lower(email) = lower('$user')) AND password = sha1(concat('$pass', sha1(salt)))");
+    if (mysqli_num_rows($q) == 0){
+        error_log(":SECURITY: Reporting location with wrong credentials (IP $_SERVER[REMOTE_ADDR])");
+        http_response_code(403); // Forbidden
+        $error = $error . ERR_USER . mysqli_real_escape_string($con, $_GET[GET_USER]);
+        error_log($error);
+        exit(-1);
+    }
+    // Get id
+    $r = mysqli_fetch_array($q);
+    $uid = $r['id'];
+
+    //Validate fields
+    if (!in_array($action, $actions)){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_ACTION . $action;
+        error_log($error);
+        exit(-2);
+    }
+    if (is_numeric($lat) == false || is_numeric($lon) == false){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_LOCATION . '($lat, $lon)';
+        error_log($error);
+        exit(-3);
+    }
+    if (strlen($lat) == 0 xor strlen($lon) == 0){
+        // Only one coordinate.
+        http_response_code(400); // Bad request
+        $error = $error . ERR_LOCATION . '($lat, $lon)';
+        error_log($error);
+        exit(-4);
+    }
+    if (strlen($lat) != 0 && ($lat < -90.0 || $lat > 90.0)){
+        // Invalid latitude
+        http_response_code(400); // Bad request
+        $error = $error . ERR_LOCATION . '(Lat: $lat)';
+        error_log($error);
+        exit(-5);
+    }
+    if (strlen($lon) != 0 && ($lon < -180.0 || $lon > 180.0)){
+        // Invalid longitude
+        http_response_code(400); // Bad request
+        $error = $error . ERR_LOCATION . '(Lon: $lat)';
+        error_log($error);
+        exit(-6);
+    }
+
+    // Discern action
+    switch ($action){
+        case ACTION_START:
+            // Insert
+            mysqli_query($con, "INSERT INTO location (lat, lon, action, user) VALUES ($lat, $lon, 'S', $uid);");
+            break;
+        case ACTION_REFRESH:
+            // Look for start node.
+            $q = mysqli_query($con, "SELECT id, start, action FROM location WHERE user = $uid AND dtime > NOW() - INTERVAL 30 MINUTE ORDER BY dtime DESC LIMIT 1;");
+            if (mysqli_num_rows($q) == 0){
+                // No recent reports. Start anew.
+                mysqli_query($con, "INSERT INTO location (lat, lon, action, user) VALUES ($lat, $lon, 'S', $uid);");
+            }
+            else{
+                $r = mysqli_fetch_array($q);
+                if ($r['action'] == 'F'){
+                    // Previous track was stoped. Start anew.
+                    mysqli_query($con, "INSERT INTO location (lat, lon, action, user) VALUES ($lat, $lon, 'S', $uid);");
+                }
+                else{
+                    // Continue track.
+                    $s = $r['id'];
+                    mysqli_query($con, "INSERT INTO location (lat, lon, action, user, start) VALUES ($lat, $lon, 'R', $uid, $s);");
+                }
+            }
+            break;
+        case ACTION_STOP:
+            // Look for start node.
+            $q = mysqli_query($con, "SELECT id, start FROM location WHERE user = $uid AND dtime > NOW() - INTERVAL 30 MINUTE ORDER BY dtime DESC LIMIT 1;");
+            if (mysqli_num_rows($q) > 0){
+                $r = mysqli_fetch_array($q);
+                if ($r['action'] != 'F'){
+                    // Finish track.
+                    $s = $r['start'];
+                    if (strlen($lat) > 0 && strlen($lon) > 0){
+                        mysqli_query($con, "INSERT INTO location (lat, lon, action, user, start) VALUES ($lat, $lon, 'F', $uid, $s);");
+                    }
+                    else{
+                        mysqli_query($con, "INSERT INTO location (action, user, start) VALUES ('F', $uid, $s);");
+                    }
+                }
+            }
+            break;
+    }
+    http_response_code(204); // No content.
+?>

+ 168 - 0
www/API/v3/sendnotification.php

@@ -0,0 +1,168 @@
+<?php
+    //include("../functions.php");
+    //$con = startdb('rw');
+
+    // $_GET valid parameters
+    define('GET_USER', 'user');
+    define('GET_PASS', 'pass');
+    define('GET_TITLE_ES', 'title_es');
+    define('GET_TITLE_EN', 'title_en');
+    define('GET_TITLE_EU', 'title_eu');
+    define('GET_TEXT_ES', 'text_es');
+    define('GET_TEXT_EN', 'text_en');
+    define('GET_TEXT_EU', 'text_eu');
+    define('GET_DURATION', 'duration');
+    define('GET_ACTION', 'action');
+    define('GET_PERM', 'permalink');
+    define('GET_ID', 'id');
+    define('GET_GM', 'gm');
+
+    // Valid values
+    define('ACTION_TEXT', 'mensaje');
+    define('ACTION_BLOG', 'blog');
+    define('ACTION_ACTIVITIES', 'actividades');
+    define('ACTION_GALLERY', 'galeria');
+    define('ACTION_LOCALIZATION', 'localizacion');
+    define('ACTION_LABLANCA', 'lablanca');
+    define('ACTION_SCHEDULE', 'programa');
+    define('ACTION_GM_SCHEDULE', 'gprograma');
+    define('ACTION_US', 'nosotros');
+    $actions = [ACTION_TEXT, ACTION_BLOG, ACTION_ACTIVITIES, ACTION_GALLERY, ACTION_LOCALIZATION, ACTION_LABLANCA, ACTION_SCHEDULE, ACTION_GM_SCHEDULE, ACTION_US];
+
+    // Default values
+    define('DEF_GM', 0);
+    define('DEF_ACTION', ACTION_TEXT);
+
+    // Error messages
+    define('ERR_USER', '-USER:');
+    define('ERR_ACTION', '-ACTION:');
+    define('ERR_TITLE', '-TITLE:');
+    define('ERR_TEXT', '-TEXT:');
+    define('ERR_DURATION', '-DURATION:');
+    define('ERR_GM', '-GM:');
+    define('ERR_PERM', '-PERM:');
+    define('ERR_ID', '-ID:');
+
+    /****************************************************
+    * This function is called from almost everywhere at *
+    * the beggining of the page. It initializes the     *
+    * session variables, connect to the db, enabling    *
+    * the variable $con for futher use everywhere in    *
+    * the php code, and populates the arrays $user      *
+    * and $permission, with info about the user.        *
+    *                                                   *
+    * @return: (db connection): The connection handler. *
+    ****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+         <?php
+          $host = 'XXXX';
+          $db_name = 'XXXX';
+          $username_ro = 'XXXX';
+          $username_rw = 'XXXX';
+          $pass_ro = 'XXXX';
+          $pass_rw = 'XXXX';
+         ?>
+        */
+        include('../../.htpasswd');
+
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+
+        //Return the db connection
+        return $con;
+    }
+
+    $con = startdb();
+
+    // Get fields
+    $user = mysqli_real_escape_string($con, $_GET[GET_USER]);
+    $pass = mysqli_real_escape_string($con, $_GET[GET_PASS]);
+    $title_es = urldecode(mysqli_real_escape_string($con, $_GET[GET_TITLE_ES]));
+    $title_en = urldecode(mysqli_real_escape_string($con, $_GET[GET_TITLE_EN]));
+    $title_eu = urldecode(mysqli_real_escape_string($con, $_GET[GET_TITLE_EU]));
+    $text_es = urldecode(mysqli_real_escape_string($con, $_GET[GET_TEXT_ES]));
+    $text_en = urldecode(mysqli_real_escape_string($con, $_GET[GET_TEXT_EN]));
+    $text_eu = urldecode(mysqli_real_escape_string($con, $_GET[GET_TEXT_EU]));
+    $duration = mysqli_real_escape_string($con, $_GET[GET_DURATION]);
+    $action = mysqli_real_escape_string($con, $_GET[GET_ACTION]);
+    $id = mysqli_real_escape_string($con, $_GET[GET_ID]);
+    $perm = mysqli_real_escape_string($con, $_GET[GET_PERM]);
+    $gm = mysqli_real_escape_string($con, $_GET[GET_GM]);
+
+    // Error control
+    $error = "";
+
+    // Validate user/pass
+    $q = mysqli_query($con, "SELECT id FROM user WHERE (lower(username) = lower('$user') OR lower(email) = lower('$user')) AND password = sha1(concat('$pass', sha1(salt)))");
+    if (mysqli_num_rows($q) == 0){
+        error_log(":SECURITY: Reporting location with wrong credentials (IP $_SERVER[REMOTE_ADDR])");
+        http_response_code(403); // Forbidden
+        $error = $error . ERR_USER . mysqli_real_escape_string($con, $_GET[GET_USER]);
+        error_log($error);
+        exit(-1);
+    }
+    $r = mysqli_fetch_array($q);
+    $uid = $r['id'];
+
+    //Validate fields
+    if (strlen($title_es) == 0){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_TITLE . $title_es;
+        error_log($error);
+        exit(-2);
+    }
+
+    if (strlen($text_es) == 0){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_TEXT . $text_es;
+        error_log($error);
+        exit(-3);
+    }
+
+    if (is_numeric($duration) == false || $duration < 1 && $duration > 48 * 60){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_DURATION . $duration;
+        error_log($error);
+        exit(-4);
+    }
+
+    if (!in_array($action, $actions)){
+        http_response_code(400); // Bad request
+        $error = $error . ERR_ACTION . $action;
+        error_log($error);
+        exit(-5);
+    }
+
+    //Handle translations
+    if (strlen($title_en) == 0){
+        $title_en = $title_es;
+    }
+    if (strlen($title_eu) == 0){
+        $title_eu = $title_es;
+    }
+    if (strlen($text_en) == 0){
+        $text_en = $text_es;
+    }
+    if (strlen($text_eu) == 0){
+        $text_eu = $text_es;
+    }
+
+    //Insert
+    if (strlen($error) == 0){
+        error_log("INSERT INTO notification (user, title_es, title_en, title_eu, text_es, text_en, text_eu, action, duration) VALUES ($uid, '$title_es', '$title_en', '$title_eu', '$text_es', '$text_en', '$text_eu', '$action', $duration);");
+        mysqli_query($con, "INSERT INTO notification (user, title_es, title_en, title_eu, text_es, text_en, text_eu, action, duration) VALUES ($uid, '$title_es', '$title_en', '$title_eu', '$text_es', '$text_en', '$text_eu', '$action', $duration);");
+        http_response_code(204); // No content;
+        exit(0);
+    }
+    else{
+        error_log($error);
+        exit(-6);
+    }
+?>

+ 356 - 0
www/API/v3/sync.php

@@ -0,0 +1,356 @@
+ <?php
+    // Gasteizko Margolariak API v1 //
+
+    //Database section identifiers
+    define('SEC_ALL', 'all');
+    define('SEC_BLOG', 'blog');
+    define('SEC_ACTIVITIES', 'activities');
+    define('SEC_GALLERY', 'gallery');
+    define('SEC_LABLANCA', 'lablanca');
+
+    define('TAB_ACTIVITY', 'activity');
+    define('TAB_ACTIVITY_COMMENT', 'activity_comment');
+    define('TAB_ACTIVITY_IMAGE', 'activity_image');
+    define('TAB_ACTIVITY_ITINERARY', 'activity_itinerary');
+    define('TAB_ACTIVITY_TAG', 'activity_tag');
+    define('TAB_ALBUM', 'album');
+    define('TAB_FESTIVAL', 'festival');
+    define('TAB_FESTIVAL_DAY', 'festival_day');
+    define('TAB_FESTIVAL_EVENT_CITY', 'festival_event_city');
+    define('TAB_FESTIVAL_EVENT_GM', 'festival_event_gm');
+    define('TAB_FESTIVAL_OFFER', 'festival_offer');
+    define('TAB_PEOPLE', 'people');
+    define('TAB_PHOTO', 'photo');
+    define('TAB_PHOTO_ALBUM', 'photo_album');
+    define('TAB_PHOTO_COMMENT', 'photo_comment');
+    define('TAB_PLACE', 'place');
+    define('TAB_POST', 'post');
+    define('TAB_POST_COMMENT', 'post_comment');
+    define('TAB_POST_IMAGE', 'post_image');
+    define('TAB_POST_TAG', 'post_tag');
+    define('TAB_ROUTE', 'route');
+    define('TAB_ROUTE_POINT', 'route_point');
+    define('TAB_SETTINGS', 'settings');
+    define('TAB_SPONSOR', 'sponsor');
+    
+    //Posible actions
+    define('ACTION_SYNC', 'sync');
+    define('ACTION_VERSION', 'version');
+    
+    //Default action
+    define('DEF_ACTION', ACTION_SYNC);
+    
+    //Output keys
+    define('KEY_VERSION', 'version');
+    define('KEY_DATA', 'data');
+    
+    //$_GET valid parameters
+    define('GET_CLIENT', 'client');
+    define('GET_USER', 'user');
+    define('GET_FOREGROUND', 'foreground');
+    
+    //Error messages
+    define('ERR_CLIENT', 'CLIENT');
+
+    //List of all tables to sync, sorted by priority.
+    $tab_list = [TAB_SETTINGS, TAB_PLACE, TAB_ROUTE_POINT, TAB_ROUTE, TAB_PEOPLE, TAB_FESTIVAL_EVENT_GM, TAB_FESTIVAL, TAB_FESTIVAL_DAY, TAB_FESTIVAL_OFFER, TAB_FESTIVAL_EVENT_CITY, TAB_ACTIVITY, TAB_ACTIVITY_IMAGE, TAB_ACTIVITY_ITINERARY, TAB_SPONSOR, TAB_ALBUM, TAB_PHOTO, TAB_PHOTO_ALBUM, TAB_POST, TAB_POST_IMAGE, TAB_PHOTO_COMMENT, TAB_POST_COMMENT, TAB_ACTIVITY_COMMENT, TAB_ACTIVITY_TAG, TAB_POST_TAG];
+    
+    /****************************************************
+     * This function is called from almost everywhere at *
+     * the beggining of the page. It initializes the     *
+     * session variables, connect to the db, enabling    *
+     * the variable $con for futher use everywhere in    *
+     * the php code, and populates the arrays $user      *
+     * and $permission, with info about the user.        *
+     *                                                   *
+     * @return: (db connection): The connection handler. *
+     *****************************************************/
+    function startdb(){
+        //Include the db configuration file. It's somehow like this
+        /*
+        <?php
+            $host = 'XXXX';
+            $db_name = 'XXXX';
+            $username_ro = 'XXXX';
+            $username_rw = 'XXXX';
+            $pass_ro = 'XXXX';
+            $pass_rw = 'XXXX';
+        ?>
+        */
+        include('../../.htpasswd');
+
+        //Connect to to database
+        $con = mysqli_connect($host, $username_rw, $pass_rw, $db_name);
+
+        //Set encoding options
+        mysqli_set_charset($con, 'utf-8');
+        header('Content-Type: text/html; charset=utf8');
+        mysqli_query($con, 'SET NAMES utf8;');
+
+        //Return the db connection
+        return $con;
+    }
+    /*****************************************************
+     * Selects the value of a parameter from the list of *
+     * GET arguments. It also sanitizes it to prevent    *
+     * SQL injections.                                   *
+     *                                                   *
+     * @params:                                          *
+     *    get: (string array) Contains the GET           *
+     *         parameters.                               *
+     *    param: (string) Name of the parameter.         *
+     * @return: (string): Value of the parameter or an   *
+     *          empty string if it was not passed.       *
+     *****************************************************/
+    function extract_param($con, $get, $param){
+        if(isset($_GET[$param])){
+            return mysqli_real_escape_string($con, $_GET[$param]);
+        }
+        else{
+            return "";
+        }
+    }
+
+    /*****************************************************
+     * Gets information about the API call and the       *
+     * assocciated client. If some mandatory parameter   *
+     * is not provided, a error log entry is registered  *
+     *                                                   *
+     * @params:                                          *
+     *    get: (string array) Contains the GET           *
+     *         parameters.                               *
+     * @return: (string array): Array with the keys      *
+     *           'client', 'user', 'foreground', 'ip',   *
+     *           'os', 'browser', 'uagent' and 'error'.  *
+     *           'error' will contain the key of a       *
+     *           mandatory value if it has not been      *
+     *           provided, or will be empty if there     *
+     *           were no problem.                        *
+     *****************************************************/
+    function get_user_info($con, $get){
+        $info = array();
+        $error = "";
+        $info["client"] = extract_param($con, $get, GET_CLIENT);
+        if(strlen($info["client"]) == 0) {
+            error_log("SYNC ERROR: Trying to sync with no client name.");
+            $error = ERR_CLIENT;
+        }
+        $info["user"] = extract_param($con, $get, GET_USER);
+        $info["foregronud"] = (int) extract_param($con, $get, GET_FOREGROUND);
+        if($info["foreground"] != 1){
+            $info["foreground"] = 0;
+        }
+        $info["ip"] = get_user_ip();
+        $browser_data = get_browser(null, true);
+        $info["os"] = $browser_data['platform'];
+        $info["browser"] = $browser_data['browser'];
+        $info["uagent"] = $browser_data['browser_name_pattern'];
+        $info["error"] = $error;
+    }
+
+    /*****************************************************
+     * Reads the version of the tables reported by the   *
+     * user as GET parameters.                           *
+     *                                                   *
+     * @params:                                          *
+     *    get: (string array) Contains the GET           *
+     *         parameters.                               *
+     * @return: (int array): Array with the version of   *
+     *           the tables in the user app, keyed with  *
+     *           the table names.                        *
+     *****************************************************/
+    function get_user_versions($con, $get){
+        global $tab_list;
+        
+        $versions = array();
+        foreach($tab_list as $tab){
+            $versions[$tab] = intval(extract_param($con, $get, $tab));
+        }
+        return $versions;
+    }
+
+    /*****************************************************
+     * Reads the version of the tables reported by the   *
+     * user as GET parameters.                           *
+     *                                                   *
+     * @params:                                          *
+     *    con: (MySQL server connection) RO mode enough. *
+     *         parameters.                               *
+     * @return: (int array): Array with the version of   *
+     *           the tables in the server, keyed with    *
+     *           the table names.                        *
+     *****************************************************/
+    function get_server_versions($con){
+        $versions = array();
+        $q = mysqli_query($con, "SELECT section, version FROM version;");
+        while($r = mysqli_fetch_array($q)){
+            $versions[$r['section']] = $r['version'];
+        }
+        return $versions;
+    }
+
+    /*****************************************************
+     * Select the tables that need to be synced.         *
+     *                                                   *
+     * @params:                                          *
+     *    user: (int array) Versions of tables in the    *
+     *          user app.                                *
+     *    server: (int array) Versions of tables in the  *
+     *            server.                                *
+     * @return: (string array): Array with the names of  *
+     *           the tables that need to be synced.      *
+     *****************************************************/
+    function select_tables($user, $server){
+        global $tab_list;
+        $tables = array();
+        foreach($tab_list as $table){
+            if ($user[$table] < $server[$table]){
+                array_push($tables, $table);
+            }
+        }
+    }
+
+    /****************************************************
+     * Echoes the contents of a table from the database. *
+     * Inaccessible or sensitive tables or fields are    *
+     * not printed.                                      *
+     *                                                   *
+     * @params:                                          *
+     *    con: (MySQL server connection) RO mode enough. *
+     *    table (string): The name of the table.         *
+     * @return: (Assoc Array): Data in the table.        *
+     ****************************************************/
+    function get_table($con, $table){
+        $table = strtolower($table);
+        switch ($table){
+            case TAB_ACTIVITY:
+                $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;");
+                break;
+            case TAB_ACTIVITY_COMMENT:
+                $q = mysqli_query($con, "SELECT id, activity, text, dtime, username, lang FROM activity_comment WHERE approved = 1;");
+                break;
+            case TAB_ALBUM:
+                $q = mysqli_query($con, "SELECT id, permalink, title_es, title_en, title_eu, description_es, description_en, description_eu, open FROM album;");
+                break;
+            case TAB_PHOTO:
+                $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;");
+                break;
+            case TAB_POST:
+                $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;");
+                break;
+            case TAB_POST_COMMENT:
+                $q = mysqli_query($con, "SELECT post_comment.id AS id, post, text, dtime, username, lang FROM post_comment WHERE approved = 1;");
+                break;
+            case TAB_PHOTO_COMMENT:
+                $q = mysqli_query($con, "SELECT photo_comment.id AS id, post, text, dtime, username, lang FROM photo_comment WHERE approved = 1;");
+                break;
+            case TAB_SPONSOR:
+                $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;");
+                break;
+            case TAB_SETTINGS:
+                $q = mysqli_query($con, "SELECT name, value FROM settings;");
+                break;
+
+            //Other cases:
+            default:
+                $q = mysqli_query($con, "SELECT * FROM $table;");
+        }
+
+        //If no rows, return
+        if (mysqli_num_rows($q) == 0){
+            return "";
+        }
+
+        //Create result array
+        $str = "";
+        $str = $str. "\"$table\":[";
+        while($r = mysqli_fetch_assoc($q)) {
+            $str = $str . json_encode($r) . ",";
+        }
+        $str = rtrim($str,',');
+        $str = $str . "]";
+        return $str;
+    }
+
+    /*****************************************************
+     * Prints out required tables.                       *
+     *                                                   *
+     * @return: (String): Client IP address.             *
+     *****************************************************/
+    function sync($con, $tables){
+        $str = "";
+        if(sizeof($tables) > 0){
+            $str = "{" . get_table('version');
+            foreach($tables as $table){
+                $str = $str . get_table($con, $table) . ",";
+            }
+            $str = rtrim($str,',');
+            $str = $str . "}";
+            echo($str);
+            return true;
+        }
+        return false;
+    }
+
+    /*****************************************************
+     * Gets the IP address of the client.                *
+     *                                                   *
+     * @return: (String): Client IP address.             *
+     *****************************************************/
+    function get_user_ip(){
+        $client  = @$_SERVER['HTTP_CLIENT_IP'];
+        $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
+        $remote  = $_SERVER['REMOTE_ADDR'];
+        if(filter_var($client, FILTER_VALIDATE_IP)){
+            $ip = $client;
+        }
+        elseif(filter_var($forward, FILTER_VALIDATE_IP)){
+            $ip = $forward;
+        }
+        else{
+            $ip = $remote;
+        }
+        return $ip;
+    }
+    
+    /****************************************************
+    * Registers the request in the database.            *
+    *                                                   *
+    * @params:                                          *
+    *    con: (MySQL server connection) RO mode enough. *
+    *    client: (string): The client identifier.       *
+    *    user: (string): A unique end user identifier.  *
+    *    action: (string): Requested action.            *
+    *    section: (string): Requested database section. *
+    *    version: (int): Version of the client db.      *
+    *    new_version: (int): Returned version.          *
+    *    foreground: (int): 1 for fg syncs, 0 for bg.   *
+    *    format: (string): Requested format.            *
+    *    error: (string): Error message to store.       *
+    ****************************************************/
+    function log_sync($con, $user, $synced){
+        // TODO: implement once the sync table has been reworked.
+        //mysqli_query($con, "INSERT INTO sync (client, user, fg, synced, ip, os, uagent) VALUES ('$client', '$user', '$action', '$section', $version, $new_version, $foreground, '$format', '$error', '$ip', '$os', '$uagent');");
+    }
+
+
+
+    // Connect to the database
+    $con = startdb('rw');
+
+    // Get info about the user
+    $user = get_user_info($con, $_GET);
+    if(strlen($user["error"]) > 0){
+        http_response_code(400);
+        exit(-1);
+    }
+
+    // Get tables to sync
+    $v_user = get_user_versions($con, $_GET);
+    $v_server = get_server_versions($con);
+    $tables = select_tables($v_user, $v_server);
+    $synced = sync($con, $tables);
+
+    //Log the sync in the database
+    log_sync($con, $user, $synced);
+?>