Google Calendar events on the SyncML device (28.06.2016)
Get Google Calendar events on the SyncML compatible phone.

After Google stoped it’s SyncML service in 2013 there’s no free simple solution to synchronize the Calendar with SyncML device (like Nokia s40 in my case). Another reason to setup own server is outdated technology so last online sync services may go down soon.
We will install Funambol SyncML server on linux and use Google Calendar PHP API to import Calendar events to Funambol mysql database.

Funambol installation instruction:

Install Funambol
wget -O funambol-10.0.3-x64.bin https://sourceforge.net/projects/funambol/files/bundle/v10/funambol-10.0.3-x64.bin/download
chmod +x funambol-10.0.3-x64.bin
./funambol-10.0.3-x64.bin

Download and insert mysql-connector-java:
wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.39.tar.gz
tar -zxvf mysql-connector-java-5.1.39.tar.gz
cp ./mysql-connector-java-5.1.39/mysql-connector-java-5.1.39-bin.jar  /opt/Funambol/tools/jre-1.6.0/jre/lib/ext/
cp ./mysql-connector-java-5.1.39/mysql-connector-java-5.1.39-bin.jar  /opt/Funambol/pim-listener/lib/
cp ./mysql-connector-java-5.1.39/mysql-connector-java-5.1.39-bin.jar  /opt/Funambol/inbox-listener/lib/

Change /opt/Funambol/ds-server/install.properties:
dbms=mysql
jdbc.classpath=/opt/Funambol/tools/jre-1.6.0/lib/ext/mysql-connector-java-5.1.14-bin.jar
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/funambol?characterEncoding=UTF-8
jdbc.user=funambol
jdbc.password=funambol

Create mysql database:
mysql> create database funambol;
mysql> GRANT ALL PRIVILEGES ON funambol.* TO 'funambol'@'localhost'  IDENTIFIED BY 'funambol';
/opt/Funambol/bin/install

Make init script to start Funambol as service (founded here). We don’t actually need all services from the instruction, also it’s memory consuming, so we’ll start only funambol-server.
/etc/init.d/funambol:
#!/bin/sh

cd `dirname $0`
FUNAMBOL_HOME=`(cd /opt/Funambol ; pwd)`
DS_SERVER_HOME=$FUNAMBOL_HOME/ds-server

if [ ! -d $FUNAMBOL_HOME/config ]; then
#
# maybe we are in Funambol/tool/bin
#
FUNAMBOL_HOME=$FUNAMBOL_HOME/..
DS_SERVER_HOME=$FUNAMBOL_HOME/ds-server
fi

# Setting the JAVA_HOME to the JRE in the bundle if not set or if not correctly set
if [ -z "$JAVA_HOME" ]; then
export JAVA_HOME=$FUNAMBOL_HOME/tools/jre-1.5.0/jre
else
if [ ! -f "$JAVA_HOME/bin/java" ]; then
export JAVA_HOME=$FUNAMBOL_HOME/tools/jre-1.5.0/jre
fi
fi

if [ -z "$JAVA_HOME" ]; then
echo “Please, set JAVA_HOME before running this script.”
exit 1
fi

if [ ! -f "$JAVA_HOME/bin/java" ]
then
echo “Please set JAVA_HOME to the path of a valid jre.”
exit;
fi

export J2EE_HOME=${FUNAMBOL_HOME}/tools/tomcat
export CATALINA_HOME=${FUNAMBOL_HOME}/tools/tomcat

cd ${FUNAMBOL_HOME}

export LANG=en_US.utf-8

cd ${J2EE_HOME}/bin

case $1 in
start)

# Run DS Server
#
sh $FUNAMBOL_HOME/bin/funambol-server start > /dev/null
;;
stop)

# Shutdown Tomcat
#
sh $FUNAMBOL_HOME/bin/funambol-server stop > /dev/null
;;
*)
echo “usage: $0 [start|stop]”
;;
esac

Now we should see Web Demo client on http://yourserver.name:8080/ (admin/sa, guest/guest) and can synchronize a cellphone with following parameters:
Server name: http://yourserver.name:8080/funambol/ds
Username: guest
Password: guest
Sync what: Contacts, Calendar, Tasks, Notes (as needed)
Databases names:
Contacts: card
Calendar: event
Tasks: task
Notes: note

Get Google Calendar PHP SDK:
git clone -b v1-master https://github.com/google/google-api-php-client.git

Here is little changed quick start example of getting 10 future events from default calendar (read this instruction). Added import data to funambol database.
Before using this script we should create an application with appropriate scope and place client_secret.json file to our directory.
<?
$db_server_name = "localhost";
$db_username = "user";
$db_password = "password";
$db_name = "funambol";

$conn = new mysqli($db_server_name, $db_username, $db_password, $db_name);
if ($conn->connect_error) {
  die("Connection failed: " . $conn->connect_error);
}
$conn->query("SET NAMES 'utf8'");

function add_event($dstart, $dend, $subject, $body, $gid, $all_day) {
  global $conn;
  $sql = "SELECT * FROM fnbl_pim_calendar WHERE gid='".$gid."'";
  $result = $conn->query($sql);
  if ($result->num_rows > 0) {
    //update
    $sql = "UPDATE fnbl_pim_calendar
      SET
        userid = 'guest',
        categories = 'MEETING',
        last_update = '".time()."000',
        status = 'N',
        type = 1,
        rec_type = -1,
        all_day = '".$all_day."',
        dstart = '".$dstart."',
        dend = '".$dend."',
        subject = '".$subject."',
        body = '".$body."'
      WHERE gid = '".$gid."'";
    $result = $conn->query($sql);
  }
  else {
    //create
    $sql = "INSERT INTO fnbl_pim_calendar
      SET
        userid = 'guest',
        categories = 'MEETING',
        last_update = '".time()."000',
        status = 'N',
        type = 1,
        rec_type = -1,
        all_day = '".$all_day."',
        dstart = '".$dstart."',
        dend = '".$dend."',
        subject = '".$subject."',
        body = '".$body."',
        gid = '".$gid."'";
    if(!$result = $conn->query($sql))
      printf("Errormessage: %s\n", $conn->error);
  }
}

set_include_path(get_include_path() . PATH_SEPARATOR . './google-api-php-client/src');

require_once './google-api-php-client/src/Google/autoload.php';


define('APPLICATION_NAME', 'Google Calendar API PHP Quickstart');
define('CREDENTIALS_PATH', './calendar-php-quickstart.json');
define('CLIENT_SECRET_PATH', './client_secret.json');
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/calendar-php-quickstart.json
define('SCOPES', implode(' ', array(
  Google_Service_Calendar::CALENDAR_READONLY)
));

if (php_sapi_name() != 'cli') {
  throw new Exception('This application must be run on the command line.');
}

/**
* Returns an authorized API client.
* @return Google_Client the authorized client object
*/
function getClient() {
  $client = new Google_Client();
  $client->setApplicationName(APPLICATION_NAME);
  $client->setScopes(SCOPES);
  $client->setAuthConfigFile(CLIENT_SECRET_PATH);
  $client->setAccessType('offline');

  // Load previously authorized credentials from a file.
  $credentialsPath = expandHomeDirectory(CREDENTIALS_PATH);
  if (file_exists($credentialsPath)) {
    $accessToken = file_get_contents($credentialsPath);
  } else {
    // Request authorization from the user.
    $authUrl = $client->createAuthUrl();
    printf("Open the following link in your browser:\n%s\n", $authUrl);
    print 'Enter verification code: ';
    $authCode = trim(fgets(STDIN));

    // Exchange authorization code for an access token.
    $accessToken = $client->authenticate($authCode);

    // Store the credentials to disk.
    if(!file_exists(dirname($credentialsPath))) {
      mkdir(dirname($credentialsPath), 0700, true);
    }
    file_put_contents($credentialsPath, $accessToken);
    printf("Credentials saved to %s\n", $credentialsPath);
  }
  $client->setAccessToken($accessToken);

  // Refresh the token if it's expired.
  if ($client->isAccessTokenExpired()) {
    $client->refreshToken($client->getRefreshToken());
    file_put_contents($credentialsPath, $client->getAccessToken());
  }
  return $client;
}

/**
* Expands the home directory alias '~' to the full path.
* @param string $path the path to expand.
* @return string the expanded path.
*/
function expandHomeDirectory($path) {
  $homeDirectory = getenv('HOME');
  if (empty($homeDirectory)) {
    $homeDirectory = getenv("HOMEDRIVE") . getenv("HOMEPATH");
  }
  return str_replace('~', realpath($homeDirectory), $path);
}

// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Calendar($client);

// Print the next 10 events on the user's calendar.
$calendarId = 'primary';
$optParams = array(
  'maxResults' => 10,
  'orderBy' => 'startTime',
  'singleEvents' => TRUE,
  'timeMin' => date('c'),
);
$results = $service->events->listEvents($calendarId, $optParams);

if (count($results->getItems()) == 0) {
  print "No upcoming events found.\n";
} else {
  print "Upcoming events:\n";
  foreach ($results->getItems() as $event) {
  $start_date = $event->start->date; //The date, in the format "yyyy-mm-dd", if this is an all-day event.
  $end_date = $event->end->date;
  $start_time = $end_time = "";
  $all_day = 1;
    if($event->start->dateTime) {
    $all_day = 0;
    $start = explode("T",$event->start->dateTime);
    $start_date = $start[0];
    $start_time = explode("+", $start[1]);
    $start_time = $start_time[0];
  }
    if($event->end->dateTime) {
    $end = explode("T",$event->end->dateTime);
    $end_date = $end[0];
    $end_time = explode("+", $end[1]);
    $end_time = $end_time[0];
  }
  if(!$end_date) $end_date = $start_date;
  
  $dstart = $start_date." ".$start_time;
  $dend = $end_date." ".$end_time;
  $subject = $event->getSummary();
  $body = $event->getDescription() ? $event->getDescription() : " ";
  $gid = $event->getId();
  //echo $dstart, $dend, $subject, $body, $gid, "\n";
      add_event($dstart, $dend, $subject, $body, $gid, $all_day);
  }
}
?>

After executing the script we can see added events in Funambol web interface and sync it to our phone.
adm, PHP
comments powered by Disqus
JavaScript (13)
PHP (11)
Brainfuck (8)
adm (8)
Joomla (4)
Canvas (3)
answers (2)
API (2)
CMS (2)
Modx (2)
jQuery (1)
Ajax (1)
SQL (1)
Shell (1)
batch (1)
10-6