[gcompris: 2/9] Sugar sharing and additional Journal integration (sugar part)



commit fbe4e69ea33034c8d046637312a2b73b8ebb1879
Author: Aleksey Lim <alsroot sugarlabs org>
Date:   Sat Mar 10 03:58:24 2012 +0000

    Sugar sharing and additional Journal integration (sugar part)

 src/gcompris/Makefile.am      |   11 +-
 src/gcompris/sugar.c          |  546 ++++++++++++------------
 src/gcompris/sugar.h          |   43 ++
 src/gcompris/sugar_cli.c      |  389 ++++++++++++++++
 src/gcompris/sugar_db.c       |  479 ++++++++++++++++++++
 src/gcompris/sugar_db.h       |   49 ++
 src/gcompris/sugar_gc.c       |  441 +++++++++++++++++++
 src/gcompris/sugar_gc.h       |   79 ++++
 src/gcompris/sugar_share.c    |  977 +++++++++++++++++++++++++++++++++++++++++
 src/gcompris/sugar_share.h    |  113 +++++
 src/gcompris/sugar_share.vala |  186 ++++++++
 src/gcompris/sugar_srv.c      |  160 +++++++
 12 files changed, 3199 insertions(+), 274 deletions(-)
---
diff --git a/src/gcompris/Makefile.am b/src/gcompris/Makefile.am
index cf87969..3b947ff 100644
--- a/src/gcompris/Makefile.am
+++ b/src/gcompris/Makefile.am
@@ -142,6 +142,15 @@ DONT_DIST_SOURCE = $(marshal_sources)
 
 if SUGAR
 INCLUDES += $(SUGAR_CFLAGS)
-gcompris_SOURCES += sugar.c
+gcompris_SOURCES += \
+	sugar.c sugar.h \
+	sugar_gc.c sugar_gc.h \
+	sugar_cli.c \
+	sugar_srv.c \
+	sugar_share.c sugar_share.h \
+	sugar_db.h
 gcompris_LDADD += $(SUGAR_LIBS)
+if USE_SQLITE
+gcompris_SOURCES += sugar_db.c
+endif
 endif
diff --git a/src/gcompris/sugar.c b/src/gcompris/sugar.c
index ba37868..596f3da 100644
--- a/src/gcompris/sugar.c
+++ b/src/gcompris/sugar.c
@@ -16,356 +16,356 @@
  *   along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <sugar/toolkit.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <gdk/gdkx.h>
+#include <dbus/dbus-glib.h>
+
+#include <polyol/toolkit.h>
 
 #include "gcompris.h"
 #include "gc_core.h"
 #include "bar.h"
 #include "score.h"
-
-static void bar_start(GtkContainer*, GooCanvas*);
-static void bar_set_level(GcomprisBoard*);
-static void bar_set_flags(const GComprisBarFlags);
-static void score_start(ScoreStyleList, guint, guint, guint);
-static void score_end();
-static void score_set(guint);
-static void help_clicked_cb(GtkToolButton*, gpointer);
-static void about_clicked_cb(GtkToolButton*, gpointer);
-static void level_clicked_cb(GtkToolButton*, gpointer);
-static void refresh_clicked_cb(GtkToolButton*, gpointer);
-static void zoom_clicked_cb(GtkToolButton*, gpointer);
-static void config_clicked_cb(GtkToolButton*, gpointer);
-static void back_clicked_cb(GtkToolButton*, gpointer);
-static void stop_clicked_cb(GtkToolButton*, gpointer);
-static GtkToolItem* level_widget_new();
-static GtkToolItem* score_widget_new();
-static GtkToolItem* expander_new();
-static GtkToolItem* separator_new();
-
-/* export sugar bar */
-Bar sugar_bar = {
-  bar_start,
-  bar_set_level,
-  NULL,
-  NULL,
-  bar_set_flags,
-  NULL
+#include "status.h"
+#include "sugar.h"
+#include "sugar_db.h"
+#include "sugar_share.h"
+
+#define JOURNAL_PREFIX "journal:"
+
+static const gchar *jobject_mime_types[][2] = {
+  {GC_MIME_TYPE "-wordprocessor", "/fun/wordprocessor"},
+  {GC_MIME_TYPE "-anim", "/fun/anim"},
+  {GC_MIME_TYPE "-draw", "/fun/draw"},
+  {NULL, NULL}
 };
 
-/* export sugar score */
-Score sugar_score = {
-  score_start,
-  score_end,
-  score_set
-  score_set_max,
-};
+static void notify_active_cb(GObject*);
+static void chooser_response_cb(GObject*, const gchar*);
 
-typedef struct {
-  const gchar *icon;
-  const gchar *label;
-  gpointer cb;
-  gpointer user_data;
-} Button;
-
-static Button buttons[] = {
-  { "emblem-question", N_("Help"), help_clicked_cb, NULL },
-  { "stock_home", N_("About"), about_clicked_cb, NULL },
-  { "go-previous-paired", N_("Previous level"), level_clicked_cb,
-      GINT_TO_POINTER(-1) },
-  { NULL, NULL, level_widget_new, NULL },
-  { "go-next-paired", N_("Next level"), level_clicked_cb,
-      GINT_TO_POINTER(+1) },
-  { "stock_refresh", N_("Refresh"), refresh_clicked_cb, NULL },
-  { "preferences-system", N_("Settings"), config_clicked_cb, NULL },
-  { NULL, NULL, expander_new, NULL },
-  { NULL, NULL, score_widget_new, NULL },
-  { NULL, NULL, expander_new, NULL },
-  { "view-fullscreen", N_("Zoom"), zoom_clicked_cb, NULL },
-  { NULL, NULL, separator_new, NULL },
-  { "go-previous",
-    /* TRANSLATORS: Back as in previous */
-    N_("Back"), back_clicked_cb, NULL },
-  { "activity-stop", N_("Stop"), stop_clicked_cb, NULL }
-};
+SugarActivity *activity;
+SugarJobject *board_jobject;
 
-enum {
-  BUTTON_HELP = 0,
-  BUTTON_ABOUT,
-  BUTTON_PREV,
-  BUTTON_LEVEL,
-  BUTTON_NEXT,
-  BUTTON_REFRESH,
-  BUTTON_CONFIG,
-  BUTTON_EXPANDER_1,
-  BUTTON_SCORE,
-  BUTTON_EXPANDER_2,
-  BUTTON_ZOOM,
-  BUTTON_SEPARATOR,
-  BUTTON_BACK,
-  BUTTON_STOP,
-  BUTTONS_COUNT
-};
+static SugarJobject *journal_file_object;
+static ImageSelectorCallBack choose_image_cb;
+static void * choose_image_user_context;
+static const gchar *jobject_mime_type;
 
-static gint16 buttons_mask;
-static GtkToolItem *button_widgets[BUTTONS_COUNT];
-static GtkToolbar *toolbar;
-static SugarToolkitToolText *level_widget;
-static SugarToolkitToolText *score_widget;
-static gint current_level = -1;
-static gint max_score;
+extern Peer srv_peer;
+extern Peer cli_peer;
+static Peer empty_peer;
+static Peer *peer = &empty_peer;
+static gboolean initial_activate = TRUE;
 
-static void
-bar_start(GtkContainer *workspace, GooCanvas *theCanvas)
-{
-  SugarToolkitToolbarBox *toolbox = sugar_toolkit_toolbar_box_new(-1);
-  gtk_box_pack_start(GTK_BOX(workspace), GTK_WIDGET(toolbox), FALSE, TRUE, 0);
-  toolbar = sugar_toolkit_toolbar_box_get_toolbar(toolbox);
-  gtk_widget_show(GTK_WIDGET(toolbar));
-  current_level = 1;
-}
+extern Bar sugar_bar;
+extern Score sugar_score;
 
-static void
-set_button(gint number, gboolean visible)
+void
+sugar_setup(int *argc, char ***argv)
 {
-  Button *button = &buttons[number];
-  GtkToolItem *item = button_widgets[number];
-
-  if (item == NULL) {
-    if (button->icon == NULL)
-      item = ((GtkToolItem* (*)(void))button->cb)();
-    else {
-      item = gtk_tool_button_new(NULL, button->label);
-      gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), button->icon);
-      g_signal_connect(item, "clicked", G_CALLBACK(button->cb),
-                button->user_data);
-    }
+  if (!sugar_init(argv, argc))
+    return;
 
-    g_object_ref_sink(item);
-    button_widgets[number] = item;
+  g_debug("Use sugar mode");
+
+  if (strcmp("net.gcompris.administration",
+              sugar_environ_get_bundle_id()) == 0)
+  {
+    /* There should be only one bundle_id for GCompris objects in
+       non-MODE_STANDALONE_ACTIVITY mode to let teacher via Administration
+       activity and students via GCompris activity share the same objects */
+    (*argv)[(*argc)++] = "-b";
+    (*argv)[(*argc)++] = "net.gcompris";
+    (*argv)[*argc] = NULL;
+    sugar_init(argv, argc);
   }
 
-  if (visible) {
-    if (gtk_widget_get_parent(GTK_WIDGET(item)) == NULL) {
-      int i;
-      int item_pos = 0;
-      for (i = 0; i < number; ++i)
-        if (buttons_mask & (1 << i))
-          ++item_pos;
-
-      gtk_widget_show(GTK_WIDGET(item));
-      gtk_toolbar_insert(toolbar, item, item_pos);
-      buttons_mask |= (1 << number);
-    }
-  } else {
-    if (gtk_widget_get_parent(GTK_WIDGET(item))) {
-      gtk_container_remove(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
-      buttons_mask &= ~(1 << number);
+  if (!g_thread_supported())
+    g_thread_init(NULL);
+  dbus_g_thread_init ();
+
+  sugar_environ_set_sync_dbus(TRUE);
+
+  const gchar *resumed_jobject_id = sugar_environ_get_object_id();
+  if (resumed_jobject_id)
+  {
+    SugarJobject *resumed_jobject = sugar_jobject_find(resumed_jobject_id);
+    if (resumed_jobject)
+    {
+      const gchar *mime_type = sugar_jobject_get_mime_type(resumed_jobject);
+      const gchar *(*i)[2];
+      for (i = jobject_mime_types; (*i)[0]; ++i)
+      {
+        if (strcmp((*i)[0], mime_type) == 0)
+        {
+          jobject_mime_type = (*i)[1];
+          g_debug("Start activity %s", jobject_mime_type);
+          break;
+        }
+      }
     }
   }
 }
 
-static void
-update_level()
+gboolean
+sugar_detected()
 {
-  gchar level_string[256];
-  snprintf(level_string, sizeof(level_string), "%d", current_level);
-  g_object_set(level_widget, "text", level_string, NULL);
+  return sugar_environ_get_initialized();
 }
 
-static void
-update_score(guint value)
+const gchar *
+sugar_jobject_root_menu(void)
 {
-  gchar score_string[256];
-  snprintf(score_string, sizeof(score_string), "%d/%d", value, max_score);
-  g_object_set(score_widget, "text", score_string, NULL);
+  return jobject_mime_type;
 }
 
-static GtkToolItem*
-level_widget_new()
+void
+sugar_setup_profile(const gchar *root, gboolean administration)
 {
-    g_assert(level_widget == NULL);
-    level_widget = sugar_toolkit_tool_text_new();
-    update_level();
-    return GTK_TOOL_ITEM(level_widget);
-}
+  if (!sugar_detected())
+    return;
 
-static GtkToolItem*
-score_widget_new()
-{
-    g_assert(score_widget == NULL);
-    score_widget = sugar_toolkit_tool_text_new();
-    return GTK_TOOL_ITEM(score_widget);
-}
+  activity = sugar_activity_new (TRUE, !jobject_mime_type);
 
-static GtkToolItem*
-separator_new()
-{
-  GtkToolItem *separator = gtk_separator_tool_item_new();
-  gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(separator), FALSE);
-  return separator;
-}
+  g_debug("Setup sugar profile root=%s administration=%d ",
+          root, administration);
 
-static GtkToolItem*
-expander_new()
-{
-  GtkToolItem *expander = gtk_separator_tool_item_new();
-  gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(expander), FALSE);
-  gtk_tool_item_set_expand(GTK_TOOL_ITEM(expander), TRUE);
-  return expander;
-}
+  SugarJobject *jobject = sugar_activity_get_jobject(activity);
 
-static void
-beep()
-{
-  gc_sound_play_ogg("sounds/bleep.wav", NULL);
-}
+  if (!sugar_activity_get_resumed(activity))
+  {
+    if (!jobject_mime_type)
+      sugar_jobject_set_mime_type(jobject, GC_MIME_TYPE);
+    else
+    {
+      gchar *activity_name = strrchr(root, '/') + 1;
+      gchar *mime = g_strdup_printf("%s-%s", GC_MIME_TYPE, activity_name);
+      sugar_jobject_set_mime_type(jobject, mime);
+      g_free(mime);
+    }
+  }
 
-static void
-help_clicked_cb(GtkToolButton *button, gpointer user_data)
-{
-  beep();
-  GcomprisBoard *board = gc_board_get_current();
-  gc_help_start(board);
+  if (!jobject_mime_type)
+  {
+    if (administration)
+      peer = &srv_peer;
+    else
+      peer = &cli_peer;
+  }
+
+  g_signal_connect(sugar_activity_get_shell(activity), "notify::active",
+          G_CALLBACK(notify_active_cb), NULL);
+  g_signal_connect(sugar_activity_get_journal(activity), "chooser-response",
+          G_CALLBACK(chooser_response_cb), NULL);
+
+  gc_bar_register(&sugar_bar);
+  gc_score_register(&sugar_score);
+
+  if (peer->construct)
+    peer->construct();
 }
 
-static void
-about_clicked_cb(GtkToolButton *button, gpointer user_data)
+void
+sugar_setup_x11()
 {
-  beep();
-  gc_about_start();
+  GtkWidget *window = gc_get_window();
+  Window xwindow = GDK_WINDOW_XWINDOW(window->window);
+  sugar_environ_set_window(GDK_DISPLAY(), xwindow);
 }
 
-static void
-level_clicked_cb(GtkToolButton *button, gpointer user_data)
+void
+sugar_cleanup()
 {
-  beep();
-
-  GcomprisBoard *board = gc_board_get_current();
-  if (board == NULL)
-      return;
-
-  gint delta = GPOINTER_TO_INT(user_data);
-  current_level += delta;
+  if (!sugar_detected())
+    return;
 
-  if (current_level > board->maxlevel)
-    current_level = 1;
-  else if (current_level < 1)
-    current_level = board->maxlevel;
+  g_debug("Cleanup sugar mode");
 
-  update_level();
+  if (peer->finalize)
+    peer->finalize();
 
-  if (board->plugin->set_level != NULL)
-    board->plugin->set_level(current_level);
+  if (board_jobject != NULL)
+    g_object_unref(G_OBJECT(board_jobject));
+  board_jobject = NULL;
 
-  gc_bar_play_level_voice(current_level);
-}
+  if(journal_file_object != NULL)
+    g_object_unref(G_OBJECT(journal_file_object));
+  journal_file_object = NULL;
 
-static void
-refresh_clicked_cb(GtkToolButton *button, gpointer user_data)
-{
-  beep();
-  GcomprisBoard *board = gc_board_get_current();
-  if(board && board->plugin->repeat != NULL)
-    board->plugin->repeat();
+  g_object_unref(G_OBJECT(activity));
+  activity = NULL;
 }
 
-static void
-zoom_clicked_cb(GtkToolButton *button, gpointer user_data)
+const char *
+sugar_load(void)
 {
-  beep();
-  GcomprisProperties *properties = gc_prop_get();
-  properties->zoom = (properties->zoom ? 0 : 1);
-  gc_update_canvas_zoom();
-}
+  if (!sugar_detected())
+    return NULL;
 
-static void
-config_clicked_cb(GtkToolButton *button, gpointer user_data)
-{
-  beep();
-  GcomprisBoard *board = gc_board_get_current();
-  if(board && board->plugin->config_start != NULL)
-    board->plugin->config_start(board, gc_profile_get_current());
-}
+  switch_board_jobject();
 
-static void
-back_clicked_cb(GtkToolButton *button, gpointer user_data)
-{
-  gc_board_stop();
-}
+  const char *file_path = sugar_jobject_get_file_path(board_jobject);
 
-static void
-stop_clicked_cb(GtkToolButton *button, gpointer user_data)
-{
-  // save zoom setting
-  GcomprisProperties *properties = gc_prop_get();
-  gc_prop_save(properties);
+  if (file_path != NULL)
+    g_debug("Load file from %s", sugar_jobject_get_uid(board_jobject));
 
-  gc_exit();
+  return file_path;
 }
 
-static void
-bar_set_flags(const GComprisBarFlags flags)
+void
+sugar_save(const char *path)
 {
-  GcomprisBoard *board = gc_board_get_current();
+  GError *error = NULL;
+  GArray *preview = NULL;
 
-  set_button(BUTTON_HELP, flags & GC_BAR_HELP || gc_help_has_board(board));
-  set_button(BUTTON_ABOUT, flags & GC_BAR_ABOUT);
+  if (!sugar_detected())
+    return;
 
-  set_button(BUTTON_PREV, flags & GC_BAR_LEVEL);
-  set_button(BUTTON_LEVEL, flags & GC_BAR_LEVEL);
-  set_button(BUTTON_NEXT, flags & GC_BAR_LEVEL);
+  switch_board_jobject();
 
-  set_button(BUTTON_REFRESH, flags & (GC_BAR_REPEAT | GC_BAR_REPEAT_ICON));
-  set_button(BUTTON_CONFIG, flags & GC_BAR_CONFIG);
+  preview = sugar_get_preview(&error);
+  if (preview != NULL)
+  {
+    sugar_jobject_set_preview(board_jobject, preview);
+    g_array_free(preview, FALSE);
+  } else {
+    g_warning("Cannot get preview: %s", error->message);
+    g_error_free(error);
+  }
 
-  set_button(BUTTON_EXPANDER_1, TRUE);
-  set_button(BUTTON_SCORE, max_score);
-  set_button(BUTTON_EXPANDER_2, TRUE);
+  sugar_jobject_write_file(board_jobject, path, TRUE);
 
-  set_button(BUTTON_ZOOM, TRUE);
-  set_button(BUTTON_SEPARATOR, TRUE);
-  set_button(BUTTON_BACK, board && board->previous_board);
-  set_button(BUTTON_STOP, TRUE);
+  if (peer->save_jobject)
+    peer->save_jobject(board_jobject);
+
+  g_warning("%p Saved %s file to journal entry %s", board_jobject, path,
+          sugar_jobject_get_uid(board_jobject));
 }
 
-static void
-score_start(ScoreStyleList style, guint x, guint y, guint max)
+void
+sugar_choose_image(ImageSelectorCallBack iscb, void *user_context)
 {
-  max_score = max;
-  set_button(BUTTON_SCORE, TRUE);
-  update_score(0);
+  choose_image_cb = iscb;
+  choose_image_user_context = user_context;
+  sugar_journal_choose_object(sugar_activity_get_journal(activity),
+          SUGAR_MIME_IMAGE);
 }
 
-static void
-score_end()
+gchar *
+sugar_get_journal_file(gchar *file_id)
 {
-  max_score = 0;
-  set_button(BUTTON_SCORE, FALSE);
+  if(!g_str_has_prefix(file_id, JOURNAL_PREFIX))
+    return file_id;
+
+  gchar *file_path = NULL;
+  const gchar *object_id = file_id + strlen(JOURNAL_PREFIX);
+
+  if(journal_file_object != NULL)
+    g_object_unref(G_OBJECT(journal_file_object));
+  journal_file_object = sugar_jobject_find(object_id);
+
+  if(journal_file_object == NULL)
+    g_warning("Cannot find jobject %s", object_id);
+  else
+  {
+    file_path = g_strdup(sugar_jobject_get_file_path(journal_file_object));
+    if(file_path == NULL)
+      g_warning("Cannot get file from jobject %s", object_id);
+  }
+
+  g_free(file_id);
+  return file_path;
 }
 
 static void
-score_set(guint value)
+notify_active_cb(GObject *sender)
 {
-  update_score(value);
+  SugarShell *shell = sugar_activity_get_shell(activity);
+  gboolean active = sugar_shell_get_active(shell);
+
+  g_debug("SugarShell.active=%d", active);
+
+  if(!active)
+    gc_sound_close();
+  else if(initial_activate == FALSE)
+    gc_sound_reopen();
+
+  initial_activate = FALSE;
 }
 
 static void
-score_set_max(guint max)
+chooser_response_cb(GObject *sender, const gchar *object_id)
 {
-  max_score = max;
+  g_debug("SugarShell.chooser_response_cb=%s", object_id);
+
+  if(choose_image_cb != NULL && object_id != NULL)
+  {
+    gchar file_id[256];
+    snprintf(file_id, sizeof(file_id), "%s%s", JOURNAL_PREFIX, object_id);
+    choose_image_cb(file_id, choose_image_user_context);
+    choose_image_cb = NULL;
+  }
 }
 
-static void
-bar_set_level(GcomprisBoard *board)
+gboolean
+switch_board_jobject()
 {
-  if (board == NULL)
-      return;
+  static GcomprisBoard *board = NULL;
+
+  if (board == gc_board_get_current())
+    return FALSE;
 
-  if (level_widget == NULL) {
-    g_message("in bar_set_level, level_item uninitialized : should not happen\n");
+  board = gc_board_get_current();
+  SugarJobject *new_board_jobject = NULL;
+
+  if (peer->get_jobject && is_activity_board())
+    new_board_jobject = peer->get_jobject(board);
+  else
+  {
+    new_board_jobject = sugar_activity_get_jobject(activity);
+    g_object_ref(G_OBJECT(new_board_jobject));
+  }
+
+  gboolean result = (new_board_jobject != board_jobject);
+  if (board_jobject)
+    g_object_unref(board_jobject);
+  board_jobject = new_board_jobject;
+
+  return result;
+}
+
+void
+save_profile(GKeyFile *profile)
+{
+  char tmp_file[] = "/tmp/GComprisXXXXXX";
+  int fd = mkstemp(tmp_file);
+  if (fd == -1)
+  {
+    g_warning("Cannot create temporary file to save");
     return;
   }
 
-  current_level = board->level;
-  update_level();
+  gsize size = 0;
+  gchar *profile_data = g_key_file_to_data(profile, &size, NULL);
+  gboolean success = (write(fd, profile_data, size) == size);
+  g_free(profile_data);
+  fchmod(fd, 0644);
+  close(fd);
+
+  if (success)
+    sugar_activity_write_file(activity, tmp_file, TRUE);
+  else
+  {
+    unlink(tmp_file);
+    g_warning("Cannot save profile to journal entry");
+  }
+}
+
+gboolean
+is_activity_board()
+{
+  GcomprisBoard *board = gc_board_get_current();
+  return board->name[0] != '\0' && strcmp("login", board->name) != 0 &&
+        strcmp("administration", board->name) != 0;
 }
diff --git a/src/gcompris/sugar.h b/src/gcompris/sugar.h
new file mode 100644
index 0000000..cb84e3b
--- /dev/null
+++ b/src/gcompris/sugar.h
@@ -0,0 +1,43 @@
+/* gcompris - sugar.h
+ *
+ * Copyright (C) 2010, Aleksey Lim
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SUGAR_H
+#define SUGAR_H
+
+#define GC_MIME_TYPE "application/x-gcompris"
+
+void save_profile(GKeyFile*);
+gboolean switch_board_jobject();
+gboolean is_activity_board();
+
+extern SugarActivity *activity;
+extern SugarJobject *board_jobject;
+
+typedef void (*Callback)(void);
+typedef SugarJobject *(*GetJobjectCallback)(GcomprisBoard*);
+typedef void (*SaveJobjectCallback)(SugarJobject*);
+
+typedef struct
+{
+  Callback construct;
+  Callback finalize;
+  GetJobjectCallback get_jobject;
+  SaveJobjectCallback save_jobject;
+} Peer;
+
+#endif
diff --git a/src/gcompris/sugar_cli.c b/src/gcompris/sugar_cli.c
new file mode 100644
index 0000000..bbfa08b
--- /dev/null
+++ b/src/gcompris/sugar_cli.c
@@ -0,0 +1,389 @@
+/* gcompris - sugar_cli.c
+ *
+ * Copyright (C) 2010, Aleksey Lim
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <polyol/toolkit.h>
+
+#include "gcompris.h"
+#include "gc_core.h"
+#include "status.h"
+#include "sugar.h"
+#include "sugar_share.h"
+
+static void construct();
+static void finalize();
+static void load_profile();
+static void setup_gc_profile();
+static void scope_changed_cb(SugarConnection*, SugarShareScope, const gchar*);
+static void channel_appeared_cb(SugarConnection*, SugarChannel*);
+static void buddy_appeared_cb(SugarChannel*, guint, const gchar*);
+static void buddy_disappeared_cb(SugarChannel*, guint, const gchar*);
+static void get_profile_from_server_cb(const char*, void*);
+static void load_board(gchar*, gint);
+static void finalize_delayed_start();
+static SugarJobject *get_jobject(GcomprisBoard*);
+static void save_jobject(SugarJobject*);
+
+static GKeyFile *profile;
+static AdministrationClient *client;
+static gboolean delayed_start;
+
+Peer cli_peer = {
+  construct,
+  finalize,
+  get_jobject,
+  save_jobject
+};
+
+gboolean
+sugar_delayed_start(void)
+{
+  return delayed_start;
+}
+
+void
+sugar_report(const gchar *date, guint duration, const gchar *user,
+        GcomprisBoard *board, GCBonusStatusList status, const gchar *comment)
+{
+  gchar *user_section = g_strdup_printf("user/%s", user);
+  gchar *report_section = g_strdup_printf("report/%s/%s", date, board->name);
+
+  if (client != NULL)
+    administration_client_report(client, date, duration, user,
+            board->name, board->level, board->sublevel, (int) status, comment);
+
+  g_key_file_set_string(profile, report_section, "date", date);
+  g_key_file_set_integer(profile, report_section, "duration", duration);
+  g_key_file_set_string(profile, report_section, "user", user_section);
+  g_key_file_set_string(profile, report_section, "board", board->name);
+  g_key_file_set_integer(profile, report_section, "level", board->level);
+  g_key_file_set_integer(profile, report_section, "sublevel", board->sublevel);
+  g_key_file_set_integer(profile, report_section, "status", (int)status);
+  g_key_file_set_string(profile, report_section, "comment", comment);
+
+  g_free(report_section);
+  g_free(user_section);
+}
+
+static void
+construct()
+{
+  profile = g_key_file_new();
+
+  SugarConnection *conn = sugar_activity_get_connection(activity);
+
+  g_signal_connect(conn, "scope-changed", G_CALLBACK(scope_changed_cb), NULL);
+  g_signal_connect(conn, "channel-appeared", G_CALLBACK(channel_appeared_cb),
+          NULL);
+
+  if (sugar_connection_get_scope(conn) == SUGAR_SHARE_SCOPE_PRIVATE ||
+          !sugar_connection_get_shared(conn) ||
+          sugar_connection_get_initiator(conn))
+    load_profile();
+  else
+    delayed_start = TRUE;
+}
+
+static void
+finalize()
+{
+  if (client != NULL)
+    administration_client_unref(client);
+  client = NULL;
+
+  if (!delayed_start)
+    save_profile(profile);
+
+  g_key_file_free(profile);
+  profile = NULL;
+}
+
+static SugarJobject *
+get_jobject(GcomprisBoard *board)
+{
+  SugarJobject *jobject = NULL;
+
+  gchar *object_id = g_key_file_get_string(profile, "jobjects",
+          board->name, NULL);
+  if (object_id != NULL)
+  {
+    jobject = sugar_jobject_find(object_id);
+    g_free(object_id);
+  }
+
+  if (jobject == NULL)
+  {
+    jobject = sugar_jobject_create();
+    gchar *bundle_id = g_strdup_printf("net.gcompris.%s", board->name);
+    sugar_jobject_set_activity(jobject, bundle_id);
+    gchar *mime = g_strdup_printf("%s-%s", GC_MIME_TYPE, board->name);
+    sugar_jobject_set_mime_type(jobject, mime);
+    g_free(bundle_id);
+    g_free(mime);
+  }
+
+  return jobject;
+}
+
+static void
+save_jobject(SugarJobject *jobject)
+{
+  g_key_file_set_string(profile, "jobjects", gc_board_get_current()->name,
+          sugar_jobject_get_uid(jobject));
+}
+
+static void
+scope_changed_cb(SugarConnection *conn, SugarShareScope prev_scope,
+        const gchar *error)
+{
+  if (sugar_connection_get_scope(conn) == SUGAR_SHARE_SCOPE_PRIVATE)
+  {
+    if (client != NULL)
+      administration_client_unref(client);
+    client = NULL;
+
+    if (delayed_start)
+    {
+      load_profile();
+      finalize_delayed_start();
+    }
+  }
+}
+
+static void
+channel_appeared_cb(SugarConnection *conn, SugarChannel *channel)
+{
+  g_signal_connect(channel, "buddy-appeared",
+          G_CALLBACK(buddy_appeared_cb), NULL);
+  g_signal_connect(channel, "buddy-disappeared",
+          G_CALLBACK(buddy_disappeared_cb), NULL);
+
+  g_debug ("Wait for server");
+}
+
+static void
+buddy_appeared_cb(SugarChannel *channel, guint buddy, const gchar *bus_name)
+{
+  if (client == NULL && sugar_channel_get_owner(channel) == buddy)
+  {
+    const gchar *address = sugar_channel_get_address(channel);
+    client = administration_client_new(address, bus_name,
+            get_profile_from_server_cb, NULL);
+
+    g_debug ("Connected to server %s", bus_name);
+
+    if (delayed_start)
+      administration_client_import_profile(client);
+  }
+}
+
+static void
+buddy_disappeared_cb(SugarChannel *channel, guint buddy, const gchar *bus_name)
+{
+  if (client != NULL && sugar_channel_get_owner(channel) == buddy)
+  {
+    g_debug ("Disconnected from server %s", bus_name);
+    administration_client_unref(client);
+    client = NULL;
+  }
+}
+
+static void
+get_profile_from_server_cb(const char* profile_data, void* user_data)
+{
+  if (profile_data != NULL)
+  {
+    GError *error;
+    if (!g_key_file_load_from_data(profile, profile_data, strlen(profile_data),
+                G_KEY_FILE_NONE, &error))
+    {
+      profile_data = NULL;
+      g_warning("Cannot import server profile: %s", error->message);
+      g_error_free(error);
+    }
+  }
+
+  if (profile_data == NULL)
+  {
+    SugarAlert *alert = SUGAR_ALERT(sugar_notify_alert_new(_("Sharing Error"),
+              _("Cannot retrieve remote data"), "emblem-warning", 7));
+    sugar_alert_bin_push(alert);
+
+    SugarConnection *conn = sugar_activity_get_connection(activity);
+    sugar_connection_set_scope(conn, SUGAR_SHARE_SCOPE_PRIVATE);
+  }
+
+  if (delayed_start)
+  {
+    if (profile_data == NULL)
+      load_profile();
+    else
+      setup_gc_profile();
+    finalize_delayed_start();
+  }
+}
+
+static void
+finalize_delayed_start()
+{
+  GcomprisProperties *properties = gc_prop_get();
+  GcomprisBoard *board;
+
+  g_debug("Delayed start");
+
+  delayed_start = FALSE;
+
+  gc_menu_load();
+  gc_status_close();
+
+  properties->menu_board = gc_menu_section_get("/");
+  if (properties->profile == NULL || properties->profile->group_ids == NULL)
+    board = gc_menu_section_get("/");
+  else
+    board = gc_menu_section_get("/login/login");
+  gc_board_play(board);
+}
+
+static void
+setup_gc_profile()
+{
+  int i, j;
+  gchar **board_files = g_key_file_get_string_list(profile,
+          "profile", "boards", NULL, NULL);
+
+  if (!board_files)
+      return;
+
+  GcomprisProfile *gc_profile = g_malloc0(sizeof(GcomprisProfile));
+  gc_prop_get()->profile = gc_profile;
+
+  gc_profile->profile_id = 0;
+  gc_profile->name = g_strdup("Sugar Profile");
+  gc_profile->directory = g_strdup(sugar_environ_get_activity_root());
+  gc_profile->description = g_strdup("Sugar Journal Object");
+  gc_profile->boards = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+          NULL, g_free);
+  gc_profile->groups = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+          NULL, (GDestroyNotify) gc_group_destroy);
+  gc_profile->config = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+          NULL, (GDestroyNotify) g_hash_table_unref);
+
+  load_board("common", -1);
+
+  gint board_id = 0;
+  g_hash_table_replace(gc_profile->boards,
+          GINT_TO_POINTER(++board_id), g_strdup("menu.xml"));
+  g_hash_table_replace(gc_profile->boards,
+          GINT_TO_POINTER(++board_id), g_strdup("login.xml"));
+  load_board("login.xml", board_id);
+
+  for (i = 0; board_files[i]; i++)
+  {
+    g_hash_table_replace(gc_profile->boards,
+            GINT_TO_POINTER(++board_id), board_files[i]);
+    load_board(board_files[i], board_id);
+  }
+
+  g_free(board_files);
+
+  gchar **sections = g_key_file_get_groups(profile, NULL);
+  if (!sections)
+    return;
+
+  for (i = 0; sections[i]; i++)
+  {
+    if (!g_str_has_prefix(sections[i], "group/"))
+      continue;
+
+    GcomprisGroup *group = g_malloc0(sizeof(GcomprisGroup));
+    group->group_id = g_key_file_get_integer(profile,
+            sections[i], "group_id", NULL);
+    group->name = g_key_file_get_string(profile,
+            sections[i], "name", NULL);
+    group->class_id = g_key_file_get_integer(profile,
+            sections[i], "class_id", NULL);
+    group->description = g_key_file_get_string(profile,
+            sections[i], "description", NULL);
+
+    int *group_id = g_malloc(sizeof(int));
+    *group_id = group->group_id;
+    gc_profile->group_ids = g_list_append(gc_profile->group_ids, group_id);
+
+    g_hash_table_insert(gc_profile->groups,
+            GINT_TO_POINTER(group->group_id), group);
+
+    gchar **users = g_key_file_get_string_list(profile,
+            sections[i], "users", NULL, NULL);
+    if (users)
+    {
+      for (j = 0; users[j]; j++)
+      {
+        GcomprisUser *user = g_malloc0(sizeof(GcomprisUser));
+
+        user->user_id = g_key_file_get_integer(profile,
+                users[j], "user_id", NULL);
+        user->login = g_key_file_get_string(profile,
+                users[j], "login", NULL);
+        user->lastname = g_key_file_get_string(profile,
+                users[j], "lastname", NULL);
+        user->firstname = g_key_file_get_string(profile,
+                users[j], "firstname", NULL);
+        user->birthdate = g_key_file_get_string(profile,
+                users[j], "birthdate", NULL);
+        user->class_id = g_key_file_get_integer(profile,
+                users[j], "class_id", NULL);
+
+        group->user_ids = g_list_append(group->user_ids, user);
+      }
+      g_strfreev(users);
+    }
+  }
+  g_strfreev(sections);
+}
+
+static void
+load_board(gchar *board_file, gint board_id)
+{
+  GHashTable *config = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+              g_free, g_free);
+
+  gchar *board_section = g_strdup_printf("board/%s", board_file);
+  gchar **keys = g_key_file_get_keys(profile, board_section, NULL, NULL);
+  if (keys)
+  {
+    gchar **key;
+    for (key = keys; *key; key++)
+      g_hash_table_replace(config, *key, g_strdup(g_key_file_get_string(
+                      profile, board_section, *key, NULL)));
+    g_free(keys);
+  }
+  g_free(board_section);
+
+  g_hash_table_replace(gc_prop_get()->profile->config,
+          GINT_TO_POINTER(board_id), config);
+}
+
+static void
+load_profile()
+{
+  SugarJobject *jobject = sugar_activity_get_jobject(activity);
+  const char *file_path = sugar_jobject_get_file_path(jobject);
+
+  if (file_path != NULL &&
+          g_key_file_load_from_file(profile, file_path, 0, NULL))
+      setup_gc_profile();
+}
diff --git a/src/gcompris/sugar_db.c b/src/gcompris/sugar_db.c
new file mode 100644
index 0000000..79dc911
--- /dev/null
+++ b/src/gcompris/sugar_db.c
@@ -0,0 +1,479 @@
+/* gcompris - sugar_db.c
+ *
+ * Copyright (C) 2010, Aleksey Lim
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <libgen.h>
+#include <glib.h>
+#include <sqlite3.h>
+
+#include "gcompris.h"
+
+#define SQL_EXEC(result, query, ...) \
+  { \
+    int _ncolumn; \
+    char *_query = sqlite3_mprintf((query), __VA_ARGS__); \
+    db_error = NULL; \
+    result = NULL; \
+    if (sqlite3_get_table(db, _query, &result, &db_count, &_ncolumn, \
+            &db_error) != SQLITE_OK) \
+      g_warning("SQL error in '%s': %s", _query, db_error); \
+    else if (db_count == 0 || (db_count == 1 && !result[_ncolumn])) \
+    { \
+      sqlite3_free_table(result); \
+      result = NULL; \
+    } \
+    sqlite3_free(_query); \
+  } \
+  for (; result; sqlite3_free_table(result), result = NULL)
+
+static void import_classes(GKeyFile*, gchar**);
+static void import_users(GKeyFile*, gchar**);
+static void import_groups(GKeyFile*, gint);
+static void import_config(GKeyFile*, gchar*, gint, gint);
+static void import_logs(GKeyFile*, gchar**);
+
+static sqlite3 *db;
+static char *db_error;
+static int db_count;
+
+void
+sugar_db_open()
+{
+  if (sqlite3_open(gc_prop_get()->database, &db) != SQLITE_OK)
+  {
+    g_warning("Cannot open SQL db");
+    db = NULL;
+  }
+}
+
+void
+sugar_db_close(int profile_id)
+{
+  if (db == NULL)
+    return;
+
+  if (profile_id != 1)
+  {
+    char **nop;
+
+    SQL_EXEC(nop, "DELETE FROM board_profile_conf WHERE profile_id=%d;",
+            profile_id);
+    SQL_EXEC(nop, "DELETE FROM activities_out WHERE out_id=%d;", profile_id);
+    SQL_EXEC(nop, "DELETE FROM profiles WHERE profile_id=%d;", profile_id);
+    SQL_EXEC(nop, "DELETE FROM logs;", NULL);
+
+    g_debug("Cleaned up sugar profile with id %d", profile_id);
+  }
+
+  sqlite3_close(db);
+}
+
+gint
+sugar_db_write(GKeyFile *profile)
+{
+  char **result, **nop;
+  int profile_id = 1;
+  int i;
+
+  if (db == NULL)
+    return 1;
+
+  SQL_EXEC(result, "SELECT MAX(profile_id) FROM profiles;", NULL)
+     profile_id = atoi(result[1]) + 1;
+
+  SQL_EXEC(nop, "INSERT INTO profiles (profile_id, name, profile_directory,"
+          "description) VALUES (%d, %Q, %Q, %Q);",
+          profile_id, "sugar", ".", "sugar");
+  if (db_error)
+    return 1;
+
+  gchar **sections = g_key_file_get_groups(profile, NULL);
+  if (!sections)
+    return profile_id;
+
+  g_debug("Import sugar profile with id %d", profile_id);
+
+  SQL_EXEC(nop, "BEGIN TRANSACTION", NULL);
+
+  SQL_EXEC(result, "SELECT board_id, filename FROM boards;", NULL)
+  {
+    gchar **include_boards = g_key_file_get_string_list(profile,
+            "profile", "boards", NULL, NULL);
+
+    for (i = db_count + 1; i-- > 1;)
+    {
+      gboolean exclude = include_boards != NULL;
+      gchar *board_file = basename(result[i * 2 + 1]);
+      gchar **include_file;
+
+      if (include_boards)
+        for (include_file = include_boards; *include_file; include_file++)
+          if (strcmp(board_file, *include_file) == 0)
+          {
+            exclude = FALSE;
+            break;
+          }
+
+      import_config(profile, board_file, profile_id, atoi(result[i * 2]));
+
+      if (exclude)
+        SQL_EXEC(nop, "INSERT INTO activities_out (board_id, out_id) "
+                "VALUES (%s, %d);", result[i * 2], profile_id);
+    }
+    g_strfreev(include_boards);
+  }
+
+  import_config(profile, "common", profile_id, -1);
+  import_classes(profile, sections);
+  import_users(profile, sections);
+  import_groups(profile, profile_id);
+  import_logs(profile, sections);
+
+  g_strfreev(sections);
+
+  SQL_EXEC(nop, "END TRANSACTION", NULL);
+
+  return profile_id;
+}
+
+void
+sugar_db_read(GKeyFile *profile, gint profile_id)
+{
+  if (db == NULL)
+    return;
+
+  gchar **result;
+  gint i, j;
+
+  SQL_EXEC(result, "SELECT filename FROM boards "
+          "WHERE board_id NOT IN "
+          "(SELECT board_id FROM activities_out WHERE out_id=%d);",
+          profile_id)
+  {
+    gchar *files[db_count];
+    for (i = db_count + 1; i-- > 1;)
+      files[i - 1] = basename(result[i]);
+    g_key_file_set_string_list(profile, "profile", "boards",
+            (const gchar* const*)files, db_count);
+  }
+
+  SQL_EXEC(result, "SELECT boards.filename, conf_key, conf_value "
+          "FROM board_profile_conf "
+          "LEFT JOIN boards ON boards.board_id = board_profile_conf.board_id "
+          "WHERE profile_id=%d", profile_id)
+  {
+    for (i = db_count + 1; i-- > 1;)
+    {
+      gchar *board_file;
+      if (result[i * 3])
+        board_file = basename(result[i * 3]);
+      else
+        board_file = "common";
+      gchar *board_section = g_strdup_printf("board/%s", board_file);
+      g_key_file_set_string(profile, board_section,
+              result[i * 3 + 1], result[i * 3 + 2]);
+      g_free(board_section);
+    }
+  }
+
+  SQL_EXEC(result, "SELECT class_id, name, teacher FROM class "
+          "WHERE class_id IN (SELECT class_id FROM groups "
+          "WHERE group_id IN (SELECT group_id FROM list_groups_in_profiles "
+          "WHERE profile_id=%d));", profile_id)
+  {
+    for (i = db_count + 1; i-- > 1;)
+    {
+      gchar *section = g_strdup_printf("class/%s", result[i * 3 + 1]);
+      g_key_file_set_string(profile, section, "class_id", result[i * 3]);
+      g_key_file_set_string(profile, section, "name", result[i * 3 + 1]);
+      g_key_file_set_string(profile, section, "teacher", result[i * 3 + 2]);
+      g_free(section);
+    }
+  }
+
+  SQL_EXEC(result, "SELECT user_id, login, lastname, firstname, birthdate, "
+          "'class/'||class.name FROM users "
+          "INNER JOIN class ON class.class_id = users.class_id "
+          "WHERE user_id IN (SELECT user_id FROM list_users_in_groups "
+          "WHERE group_id IN (SELECT group_id FROM list_groups_in_profiles "
+          "WHERE profile_id=%d));", profile_id)
+  {
+    for (i = db_count + 1; i-- > 1;)
+    {
+      gchar *section = g_strdup_printf("user/%s", result[i * 6 + 1]);
+      g_key_file_set_string(profile, section, "user_id", result[i * 6]);
+      g_key_file_set_string(profile, section, "login", result[i * 6 + 1]);
+      g_key_file_set_string(profile, section, "lastname", result[i * 6 + 2]);
+      g_key_file_set_string(profile, section, "firstname", result[i * 6 + 3]);
+      g_key_file_set_string(profile, section, "birthdate", result[i * 6 + 4]);
+      g_key_file_set_string(profile, section, "class", result[i * 6 + 5]);
+      g_free(section);
+    }
+  }
+
+  SQL_EXEC(result, "SELECT group_id, groups.name, 'class/'||class.name, "
+          "description, 'group/'||groups.name FROM groups "
+          "INNER JOIN class ON class.class_id = groups.class_id "
+          "WHERE group_id IN (SELECT group_id FROM list_groups_in_profiles "
+          "WHERE profile_id=%d);", profile_id)
+  {
+    int group_count = db_count;
+    gchar *group_sections[group_count];
+
+    for (i = db_count + 1; i-- > 1;)
+    {
+      group_sections[i - 1] = result[i * 5 + 4];
+
+      gchar *section = g_strdup_printf("group/%s", result[i * 5 + 1]);
+      g_key_file_set_string(profile, section, "group_id", result[i * 5]);
+      g_key_file_set_string(profile, section, "name", result[i * 5 + 1]);
+      g_key_file_set_string(profile, section, "class", result[i * 5 + 2]);
+      g_key_file_set_string(profile, section, "description", result[i * 5 + 3]);
+      gchar **users_result;
+      SQL_EXEC(users_result, "SELECT 'user/'||login FROM users "
+              "WHERE user_id IN (SELECT user_id FROM list_users_in_groups "
+              "WHERE group_id=%s);", result[i * 5])
+      {
+        gchar *users[db_count];
+        for (j = db_count + 1; j-- > 1;)
+          users[j - 1] = users_result[j];
+        g_key_file_set_string_list(profile, section, "users",
+                (const gchar* const*)users, db_count);
+      }
+      g_free(section);
+    }
+
+    g_key_file_set_string_list(profile, "profile", "groups",
+            (const gchar* const*)group_sections, group_count);
+  }
+}
+
+void
+sugar_db_read_reports(GKeyFile *profile)
+{
+  if (db == NULL)
+    return;
+
+  gchar **result;
+  gint i;
+
+  SQL_EXEC(result, "SELECT date, duration, 'user/'||login, boards.name, "
+          "level, sublevel, status, comment FROM logs "
+          "LEFT JOIN users ON users.user_id = logs.user_id "
+          "INNER JOIN boards ON boards.board_id = logs.board_id;", NULL)
+  {
+    for (i = db_count + 1; i-- > 1;)
+    {
+      gchar *section = g_strdup_printf("report/%s/%s",
+              result[i * 8], result[i * 8 + 3]);
+      g_key_file_set_string(profile, section, "date", result[i * 8]);
+      g_key_file_set_string(profile, section, "duration", result[i * 8 + 1]);
+      g_key_file_set_string(profile, section, "user", result[i * 8 + 2]);
+      g_key_file_set_string(profile, section, "board", result[i * 8 + 3]);
+      g_key_file_set_string(profile, section, "level", result[i * 8 + 4]);
+      g_key_file_set_string(profile, section, "sublevel", result[i * 8 + 5]);
+      g_key_file_set_string(profile, section, "status", result[i * 8 + 6]);
+      g_key_file_set_string(profile, section, "comment", result[i * 8 + 7]);
+      g_free(section);
+    }
+  }
+}
+
+void
+sugar_db_write_report(const char *date, gint duration, const char *user,
+        const char *board, gint level, gint sublevel, gint status,
+        const char *comment)
+{
+  if (db == NULL)
+    return;
+
+  gchar **nop;
+
+  SQL_EXEC(nop, "INSERT INTO logs (date, duration, user_id, board_id, level, "
+          "sublevel, status, comment) VALUES (%Q, %d, "
+          "(select user_id from users where login=%Q), "
+          "(select board_id from boards where name=%Q), %d, %d, %d, %Q);",
+          date, duration, user, board, level, sublevel, status, comment);
+}
+
+static void
+import_classes(GKeyFile *profile, gchar **sections)
+{
+  gchar **result, **nop, **klass;
+
+  for (klass = sections; *klass; klass++)
+  {
+    if (!g_str_has_prefix(*klass, "class/"))
+      continue;
+    int klass_id = -1;
+    SQL_EXEC(result, "SELECT class_id FROM class WHERE name=%Q;",
+            g_key_file_get_string(profile, *klass, "name", NULL))
+      klass_id = atoi(result[1]);
+    if (klass_id == -1)
+    {
+      klass_id = 1;
+      SQL_EXEC(result, "SELECT MAX(class_id) FROM class;", NULL)
+        klass_id = atoi(result[1]) + 1;
+      SQL_EXEC(nop, "INSERT INTO class (class_id, name, teacher, wholegroup_id)"
+              "VALUES (%d, %Q, %Q, %d);",
+              klass_id,
+              g_key_file_get_string(profile, *klass, "name", NULL),
+              g_key_file_get_string(profile, *klass, "teacher", NULL),
+              1);
+      if (!db_error)
+        g_debug("Imported class with id %d", klass_id);
+    }
+    g_key_file_set_integer(profile, *klass, "class_id", klass_id);
+  }
+}
+
+static void
+import_users(GKeyFile *profile, gchar **sections)
+{
+  gchar **result, **nop, **user;
+
+  for (user = sections; *user; user++)
+  {
+    if (!g_str_has_prefix(*user, "user/"))
+      continue;
+    int user_id = -1;
+    SQL_EXEC(result, "SELECT user_id FROM users WHERE login=%Q;",
+            g_key_file_get_string(profile, *user, "login", NULL))
+      user_id = atoi(result[1]);
+    if (user_id == -1)
+    {
+      user_id = 1;
+      SQL_EXEC(result, "SELECT MAX(user_id) FROM users;", NULL)
+        user_id = atoi(result[1]) + 1;
+      SQL_EXEC(nop, "INSERT INTO users (user_id, login, lastname, firstname, "
+              "birthdate, class_id) VALUES (%d, %Q, %Q, %Q, %Q, %d);",
+              user_id,
+              g_key_file_get_string(profile, *user, "login", NULL),
+              g_key_file_get_string(profile, *user, "lastname", NULL),
+              g_key_file_get_string(profile, *user, "firstname", NULL),
+              g_key_file_get_string(profile, *user, "birthdate", NULL),
+              g_key_file_get_integer(profile,
+                  g_key_file_get_string(profile, *user, "class", NULL),
+                  "class_id", NULL));
+      if (!db_error)
+        g_debug("Imported user with id %d", user_id);
+    }
+    g_key_file_set_integer(profile, *user, "user_id", user_id);
+  }
+}
+
+static void
+import_groups(GKeyFile *profile, gint profile_id)
+{
+  gchar **result, **nop, **group, **groups;
+
+  groups = g_key_file_get_string_list(profile, "profile", "groups", NULL, NULL);
+  if (groups == NULL)
+    return;
+
+  for (group = groups; *group; group++)
+  {
+    int group_id = -1;
+    SQL_EXEC(result, "SELECT group_id FROM groups WHERE name=%Q;",
+            g_key_file_get_string(profile, *group, "name", NULL))
+      group_id = atoi(result[1]);
+    if (group_id == -1)
+    {
+      group_id = 1;
+      SQL_EXEC(result, "SELECT MAX(group_id) FROM groups;", NULL)
+        group_id = atoi(result[1]) + 1;
+      SQL_EXEC(nop, "INSERT INTO groups (group_id, name, class_id, "
+              "description) VALUES (%d, %Q, %d, %Q);",
+              group_id,
+              g_key_file_get_string(profile, *group, "name", NULL),
+              g_key_file_get_integer(profile,
+                  g_key_file_get_string(profile, *group, "class", NULL),
+                  "class_id", NULL),
+              g_key_file_get_string(profile, *group, "description", NULL));
+      if (!db_error)
+        g_debug("Imported group with id %d", group_id);
+    }
+    gchar **users = g_key_file_get_string_list(profile, *group, "users",
+            NULL, NULL);
+    if (users)
+    {
+      gchar **user;
+      for (user = users; *user; user++)
+        SQL_EXEC(nop, "INSERT INTO list_users_in_groups (user_id, "
+                "group_id) VALUES (%d, %d);",
+                g_key_file_get_integer(profile, *user, "user_id", NULL),
+                group_id);
+      g_strfreev(users);
+    }
+    SQL_EXEC(nop, "INSERT INTO list_groups_in_profiles (profile_id, "
+            "group_id) VALUES (%d, %d);", profile_id, group_id);
+    g_key_file_set_integer(profile, *group, "group_id", group_id);
+  }
+
+  g_strfreev(groups);
+}
+
+static void
+import_config(GKeyFile *profile, gchar *board_file, gint profile_id,
+        gint board_id)
+{
+  gchar *board_section = g_strdup_printf("board/%s", board_file);
+  gchar **keys = g_key_file_get_keys(profile, board_section, NULL, NULL);
+  char **nop;
+
+  if (keys)
+  {
+    gchar **key;
+    for (key = keys; *key; key++)
+      SQL_EXEC(nop, "INSERT INTO board_profile_conf (profile_id, board_id, "
+              "conf_key, conf_value) VALUES (%d, %d, %Q, %Q);",
+              profile_id, board_id, *key,
+              g_key_file_get_string(profile, board_section, *key, NULL));
+    g_strfreev(keys);
+  }
+
+  g_free(board_section);
+}
+
+static void
+import_logs(GKeyFile *profile, gchar **sections)
+{
+  gchar **nop, **log;
+
+  SQL_EXEC(nop, "DELETE FROM logs;", NULL);
+
+  for (log = sections; *log; log++)
+  {
+    if (!g_str_has_prefix(*log, "report/"))
+      continue;
+
+    SQL_EXEC(nop, "INSERT INTO logs (date, duration, user_id, board_id, level, "
+            "sublevel, status, comment) VALUES (%Q, %Q, %d, "
+            "(select board_id from boards where name=%Q), %Q, %Q, %Q, %Q);",
+            g_key_file_get_string(profile, *log, "date", NULL),
+            g_key_file_get_string(profile, *log, "duration", NULL),
+            g_key_file_get_integer(profile,
+                g_key_file_get_string(profile, *log, "user", NULL),
+                "user_id", NULL),
+            g_key_file_get_string(profile, *log, "board", NULL),
+            g_key_file_get_string(profile, *log, "level", NULL),
+            g_key_file_get_string(profile, *log, "sublevel", NULL),
+            g_key_file_get_string(profile, *log, "status", NULL),
+            g_key_file_get_string(profile, *log, "comment", NULL));
+  }
+}
diff --git a/src/gcompris/sugar_db.h b/src/gcompris/sugar_db.h
new file mode 100644
index 0000000..4264bb1
--- /dev/null
+++ b/src/gcompris/sugar_db.h
@@ -0,0 +1,49 @@
+/* gcompris - sugar_db.h
+ *
+ * Copyright (C) 2010, Aleksey Lim
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SUGAR_DB_H
+#define SUGAR_DB_H
+
+#if defined(USE_SUGAR) && defined(USE_SQLITE)
+  void sugar_db_open(void);
+  void sugar_db_close(int profile_id);
+
+  /** Write profile data from key file to db */
+  gint sugar_db_write(GKeyFile *profile);
+
+  /** Read profile data from db and store it in key file */
+  void sugar_db_read(GKeyFile *profile, gint profile_id);
+
+  /** Write log entry to db */
+  void sugar_db_write_report (const char *date, gint duration,
+          const char *user, const char *board, gint level, gint sublevel,
+          gint status, const char *comment);
+
+  /** Read all log entries from db and store them in key file */
+  void sugar_db_read_reports (GKeyFile *profile);
+#else
+# define sugar_db_open()
+# define sugar_db_close(profile_id)
+# define sugar_db_write(config) 1
+# define sugar_db_read(config, profile_id)
+# define sugar_db_write_report(date, duration, user, board, level, sublevel, \
+        status, comment)
+# define sugar_db_read_reports(profile)
+#endif
+
+#endif
diff --git a/src/gcompris/sugar_gc.c b/src/gcompris/sugar_gc.c
new file mode 100644
index 0000000..a3ecfd6
--- /dev/null
+++ b/src/gcompris/sugar_gc.c
@@ -0,0 +1,441 @@
+/* gcompris - sugar_gc.c
+ *
+ * Copyright (C) 2010, Aleksey Lim
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <gdk/gdkx.h>
+
+#include <polyol/toolkit.h>
+
+#include "gcompris.h"
+#include "gc_core.h"
+#include "bar.h"
+#include "score.h"
+#include "status.h"
+#include "sugar.h"
+
+static void bar_start(GtkContainer*, GooCanvas*);
+static void bar_set_level(GcomprisBoard*);
+static void bar_set_flags(const GComprisBarFlags);
+static void score_start(ScoreStyleList, guint, guint, guint);
+static void score_end();
+static void score_set(guint);
+static void activity_clicked_cb(GtkToolButton*, gpointer);
+static void about_clicked_cb(GtkToolButton*, gpointer);
+static void level_clicked_cb(GtkToolButton*, gpointer);
+static void refresh_clicked_cb(GtkToolButton*, gpointer);
+static void zoom_clicked_cb(GtkToolButton*, gpointer);
+static void config_clicked_cb(GtkToolButton*, gpointer);
+static void back_clicked_cb(GtkToolButton*, gpointer);
+static void stop_clicked_cb(GtkToolButton*, gpointer);
+static GtkToolItem* level_widget_new();
+static GtkToolItem* score_widget_new();
+static GtkToolItem* expander_new();
+static GtkToolItem* separator_new();
+static GtkToolItem* activity_icon_new();
+static GtkToolItem* journal_title_new();
+static GtkToolItem* share_button_new();
+
+/* export sugar bar */
+Bar sugar_bar = {
+  bar_start,
+  bar_set_level,
+  NULL,
+  NULL,
+  bar_set_flags,
+  NULL
+};
+
+/* export sugar score */
+Score sugar_score = {
+  score_start,
+  score_end,
+  score_set
+};
+
+typedef struct {
+  const gchar *icon;
+  const gchar *label;
+  gpointer cb;
+  gpointer user_data;
+} Button;
+
+static Button buttons[] = {
+  { NULL, NULL, activity_icon_new, NULL },
+  { NULL, NULL, journal_title_new, NULL },
+  { NULL, NULL, share_button_new, NULL },
+  { "stock_home", N_("About"), about_clicked_cb, NULL },
+  { "stock_refresh", N_("Refresh"), refresh_clicked_cb, NULL },
+  { "preferences-system", N_("Settings"), config_clicked_cb, NULL },
+  { "go-previous-paired", N_("Previous level"), level_clicked_cb,
+      GINT_TO_POINTER(-1) },
+  { NULL, NULL, level_widget_new, NULL },
+  { "go-next-paired", N_("Next level"), level_clicked_cb,
+      GINT_TO_POINTER(+1) },
+  { NULL, NULL, expander_new, NULL },
+  { NULL, NULL, score_widget_new, NULL },
+  { NULL, NULL, expander_new, NULL },
+  { "view-fullscreen", N_("Zoom"), zoom_clicked_cb, NULL },
+  { NULL, NULL, separator_new, NULL },
+  { "go-previous",
+    /* TRANSLATORS: Back as in previous */
+    N_("Back"), back_clicked_cb, NULL },
+  { }
+};
+
+enum {
+  BUTTON_ACTIVITY_ICON = 0,
+  BUTTON_JOURNAL_TITLE,
+  BUTTON_SHARE,
+  BUTTON_ABOUT,
+  BUTTON_REFRESH,
+  BUTTON_CONFIG,
+  BUTTON_PREV,
+  BUTTON_LEVEL,
+  BUTTON_NEXT,
+  BUTTON_EXPANDER_1,
+  BUTTON_SCORE,
+  BUTTON_EXPANDER_2,
+  BUTTON_ZOOM,
+  BUTTON_SEPARATOR,
+  BUTTON_BACK,
+  BUTTON_STOP,
+  BUTTONS_COUNT
+};
+
+static GtkToolItem *button_widgets[BUTTONS_COUNT];
+static GtkToolbar *toolbar;
+static SugarToolText *level_widget;
+static SugarToolText *score_widget;
+static gint current_level = -1;
+static gint max_score;
+
+static void
+bar_start(GtkContainer *workspace, GooCanvas *theCanvas)
+{
+  GtkToolItem *item;
+
+  SugarToolbarBox *toolbox = sugar_toolbar_box_new();
+  gtk_widget_show(GTK_WIDGET(toolbox));
+  gtk_box_pack_start(GTK_BOX(workspace), GTK_WIDGET(toolbox), FALSE, TRUE, 0);
+
+  SugarAlertBin *alerts = sugar_alert_bin_new();
+  gtk_widget_show(GTK_WIDGET(alerts));
+  gtk_box_pack_start(GTK_BOX(workspace), GTK_WIDGET(alerts), FALSE, TRUE, 0);
+
+  toolbar = GTK_TOOLBAR(sugar_toolbar_box_get_toolbar(toolbox));
+
+  item = GTK_TOOL_ITEM(sugar_stop_button_new());
+  g_signal_connect(item, "clicked", G_CALLBACK(stop_clicked_cb), NULL);
+  g_object_ref_sink(item);
+  button_widgets[BUTTON_STOP] = item;
+
+  current_level = 1;
+}
+
+static void
+set_button(gint number, gboolean visible)
+{
+  Button *button = &buttons[number];
+  GtkToolItem *item = button_widgets[number];
+
+  if (visible && item == NULL) {
+    if (button->icon == NULL)
+      item = ((GtkToolItem* (*)(void))button->cb)();
+    else {
+      SugarToolButton *tool_button = sugar_tool_button_new();
+      sugar_tool_button_set_icon_name(tool_button, button->icon);
+      sugar_tool_button_set_tooltip(tool_button, _(button->label));
+      item = GTK_TOOL_ITEM(tool_button);
+      g_signal_connect(item, "clicked", G_CALLBACK(button->cb),
+                button->user_data);
+    }
+
+    g_object_ref_sink(item);
+    button_widgets[number] = item;
+  }
+
+  if (visible) {
+    if (gtk_widget_get_parent(GTK_WIDGET(item)) == NULL) {
+      int i;
+      int item_pos = 0;
+      for (i = 0; i < number; ++i)
+        if (button_widgets[i] != NULL &&
+                gtk_widget_get_parent(GTK_WIDGET(button_widgets[i])) != NULL)
+          ++item_pos;
+
+      gtk_widget_show(GTK_WIDGET(item));
+      gtk_toolbar_insert(toolbar, item, item_pos);
+    }
+  } else if (item) {
+    if (gtk_widget_get_parent(GTK_WIDGET(item)))
+      gtk_container_remove(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
+  }
+}
+
+static void
+update_level()
+{
+  gchar level_string[256];
+  snprintf(level_string, sizeof(level_string), "%d", current_level);
+  g_object_set(level_widget, "text", level_string, NULL);
+}
+
+static void
+update_score(guint value)
+{
+  gchar score_string[256];
+  snprintf(score_string, sizeof(score_string), "%d/%d", value, max_score);
+  g_object_set(score_widget, "text", score_string, NULL);
+}
+
+static GtkToolItem*
+level_widget_new()
+{
+  g_assert(level_widget == NULL);
+  level_widget = sugar_tool_text_new();
+  update_level();
+  return GTK_TOOL_ITEM(level_widget);
+}
+
+static GtkToolItem*
+score_widget_new()
+{
+  g_assert(score_widget == NULL);
+  score_widget = sugar_tool_text_new();
+  return GTK_TOOL_ITEM(score_widget);
+}
+
+static GtkToolItem*
+separator_new()
+{
+  GtkToolItem *separator = gtk_separator_tool_item_new();
+  gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(separator), FALSE);
+  return separator;
+}
+
+static GtkToolItem*
+expander_new()
+{
+  GtkToolItem *expander = gtk_separator_tool_item_new();
+  gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(expander), FALSE);
+  gtk_tool_item_set_expand(GTK_TOOL_ITEM(expander), TRUE);
+  return expander;
+}
+
+static void
+beep()
+{
+  gc_sound_play_ogg("sounds/bleep.wav", NULL);
+}
+
+static void
+activity_clicked_cb(GtkToolButton *button, gpointer user_data)
+{
+  beep();
+  GcomprisBoard *board = gc_board_get_current();
+  if (gc_help_has_board(board))
+    gc_help_start(board);
+}
+
+static void
+level_clicked_cb(GtkToolButton *button, gpointer user_data)
+{
+  beep();
+
+  GcomprisBoard *board = gc_board_get_current();
+  if (board == NULL)
+      return;
+
+  gint delta = GPOINTER_TO_INT(user_data);
+  current_level += delta;
+
+  if (current_level > board->maxlevel)
+    current_level = 1;
+  else if (current_level < 1)
+    current_level = board->maxlevel;
+
+  update_level();
+
+  if (board->plugin->set_level != NULL)
+    board->plugin->set_level(current_level);
+
+  gc_bar_play_level_voice(current_level);
+}
+
+static void
+refresh_clicked_cb(GtkToolButton *button, gpointer user_data)
+{
+  beep();
+  GcomprisBoard *board = gc_board_get_current();
+  if(board && board->plugin->repeat != NULL)
+    board->plugin->repeat();
+}
+
+static void
+zoom_clicked_cb(GtkToolButton *button, gpointer user_data)
+{
+  beep();
+  GcomprisProperties *properties = gc_prop_get();
+  properties->zoom = (properties->zoom ? 0 : 1);
+  gc_update_canvas_zoom();
+}
+
+static void
+config_clicked_cb(GtkToolButton *button, gpointer user_data)
+{
+  beep();
+  GcomprisBoard *board = gc_board_get_current();
+  if(board && board->plugin->config_start != NULL)
+    board->plugin->config_start(board, gc_profile_get_current());
+}
+
+static void
+back_clicked_cb(GtkToolButton *button, gpointer user_data)
+{
+  gc_board_stop();
+}
+
+static void
+stop_clicked_cb(GtkToolButton *button, gpointer user_data)
+{
+  // save zoom setting
+  GcomprisProperties *properties = gc_prop_get();
+  gc_prop_save(properties);
+
+  gc_exit();
+}
+
+static void
+bar_set_flags(const GComprisBarFlags flags)
+{
+  GcomprisBoard *board = gc_board_get_current();
+  gboolean sub_activity = board && board->previous_board;
+
+  if (switch_board_jobject() && flags & GC_BAR_JOURNAL)
+  {
+    set_button(BUTTON_JOURNAL_TITLE, FALSE);
+    if (button_widgets[BUTTON_JOURNAL_TITLE])
+      g_object_unref(G_OBJECT(button_widgets[BUTTON_JOURNAL_TITLE]));
+    button_widgets[BUTTON_JOURNAL_TITLE] = NULL;
+  }
+
+  set_button(BUTTON_ACTIVITY_ICON, FALSE);
+  if (button_widgets[BUTTON_ACTIVITY_ICON])
+    g_object_unref(G_OBJECT(button_widgets[BUTTON_ACTIVITY_ICON]));
+  button_widgets[BUTTON_ACTIVITY_ICON] = NULL;
+
+  set_button(BUTTON_ACTIVITY_ICON, TRUE);
+  set_button(BUTTON_JOURNAL_TITLE, flags & GC_BAR_JOURNAL);
+  set_button(BUTTON_SHARE, sugar_activity_get_connection(activity) != NULL &&
+          flags & GC_BAR_SHARE);
+  set_button(BUTTON_ABOUT, flags & GC_BAR_ABOUT);
+
+  set_button(BUTTON_PREV, flags & GC_BAR_LEVEL);
+  set_button(BUTTON_LEVEL, flags & GC_BAR_LEVEL);
+  set_button(BUTTON_NEXT, flags & GC_BAR_LEVEL);
+
+  set_button(BUTTON_REFRESH, flags & (GC_BAR_REPEAT | GC_BAR_REPEAT_ICON));
+  set_button(BUTTON_CONFIG, flags & GC_BAR_CONFIG);
+
+  set_button(BUTTON_EXPANDER_1, TRUE);
+  set_button(BUTTON_SCORE, max_score);
+  set_button(BUTTON_EXPANDER_2, TRUE);
+
+  set_button(BUTTON_ZOOM, strcmp("/", gc_prop_get()->root_menu) != 0 &&
+          strcmp("/login/login", gc_prop_get()->root_menu) != 0);
+  set_button(BUTTON_SEPARATOR, TRUE);
+
+  set_button(BUTTON_BACK, sub_activity);
+  set_button(BUTTON_STOP, !sub_activity);
+}
+
+static void
+score_start(ScoreStyleList style, guint x, guint y, guint max)
+{
+  max_score = max;
+  set_button(BUTTON_SCORE, TRUE);
+  update_score(0);
+}
+
+static void
+score_end()
+{
+  max_score = 0;
+  set_button(BUTTON_SCORE, FALSE);
+}
+
+static void
+score_set(guint value)
+{
+  update_score(value);
+}
+
+static void
+bar_set_level(GcomprisBoard *board)
+{
+  if (board == NULL)
+      return;
+
+  if (level_widget == NULL) {
+    g_message("in bar_set_level, level_widget == NULL should not happen\n");
+    return;
+  }
+
+  current_level = board->level;
+  update_level();
+}
+
+static GtkToolItem*
+activity_icon_new()
+{
+  SugarActivityButton *item = sugar_activity_button_new(board_jobject);
+  if (is_activity_board())
+  {
+    gchar *icon_file = g_strdup_printf("%s/%s",
+            gc_prop_get()->menu_dir, gc_board_get_current()->icon_name);
+    sugar_tool_button_set_icon_name(SUGAR_TOOL_BUTTON(item), icon_file);
+    g_free(icon_file);
+  }
+  g_signal_connect(item, "clicked", G_CALLBACK(activity_clicked_cb), NULL);
+  return GTK_TOOL_ITEM(item);
+}
+
+static GtkToolItem*
+journal_title_new()
+{
+  GtkToolItem *item;
+  item = GTK_TOOL_ITEM(sugar_title_entry_new(board_jobject));
+  return item;
+}
+
+static GtkToolItem*
+share_button_new()
+{
+  GtkToolItem *item;
+  SugarConnection *connection = sugar_activity_get_connection(activity);
+  item = GTK_TOOL_ITEM(sugar_share_button_new(connection));
+  return item;
+}
+
+static void
+about_clicked_cb(GtkToolButton *button, gpointer user_data)
+{
+  beep();
+  gc_about_start();
+}
diff --git a/src/gcompris/sugar_gc.h b/src/gcompris/sugar_gc.h
new file mode 100644
index 0000000..36071f5
--- /dev/null
+++ b/src/gcompris/sugar_gc.h
@@ -0,0 +1,79 @@
+/* gcompris - sugar.h
+ *
+ * Copyright (C) 2010, Aleksey Lim
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SUGAR_GC_H
+#define SUGAR_GC_H
+
+#ifdef USE_SUGAR
+  /** Initialize sugar libraries */
+  void sugar_setup(int *argc, char ***argv);
+
+  /** Was sugar mode detected on startup */
+  gboolean sugar_detected(void);
+
+  /** If sugar object restred from Journal, root menu to launch */
+  const gchar *sugar_jobject_root_menu(void);
+
+  /** Start is delayed until sugar retreaves remote data */
+  gboolean sugar_delayed_start(void);
+
+  /** Load sugar profile */
+  void sugar_setup_profile(const gchar *root, gboolean administration);
+
+  /** Setup X11 properties for main GC window */
+  void sugar_setup_x11();
+
+  /** Cleanup sugar related data */
+  void sugar_cleanup(void);
+
+  /** Get filename from current journal object */
+  const char *sugar_load(void);
+
+  /** Save file to current journal object */
+  void sugar_save(const char *path);
+
+  /** Start image chooser sugar dialog */
+  void sugar_choose_image(ImageSelectorCallBack iscb, void *user_context);
+
+  /** Get journal file by id previously returned by sugar_choose_image */
+  gchar *sugar_get_journal_file(gchar *file_id);
+
+  /** Get profile id imported to db from journal object */
+  gint sugar_get_profile_id(void);
+
+  /** Register a log report within sugar */
+  void sugar_report(const gchar *date, guint duration, const gchar *user,
+          GcomprisBoard *board, GCBonusStatusList status,
+          const gchar *comment);
+#else
+# define sugar_setup(argc, argv)
+# define sugar_detected() FALSE
+# define sugar_jobject_root_menu() FALSE
+# define sugar_delayed_start() FALSE
+# define sugar_setup_profile(root, administration)
+# define sugar_setup_x11()
+# define sugar_cleanup()
+# define sugar_load() NULL
+# define sugar_save(path)
+# define sugar_choose_image(iscb, user_context)
+# define sugar_get_journal_file(file_id) (file_id)
+# define sugar_get_profile_id() 1
+# define sugar_report(date, duration, user, board, status, comment)
+#endif
+
+#endif
diff --git a/src/gcompris/sugar_share.c b/src/gcompris/sugar_share.c
new file mode 100644
index 0000000..8e14a29
--- /dev/null
+++ b/src/gcompris/sugar_share.c
@@ -0,0 +1,977 @@
+/* sugar_share.c generated by valac, the Vala compiler
+ * generated from sugar_share.vala, do not modify */
+
+/* gcompris - sugar_share.vala
+ *
+ * Copyright (C) 2010, Aleksey Lim
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sugar_share.h"
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib.h>
+#include <gobject/gvaluecollector.h>
+#include <dbus/dbus.h>
+
+#define _g_free0(var) (var = (g_free (var), NULL))
+#define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL)))
+#define _dbus_g_connection_unref0(var) ((var == NULL) ? NULL : (var = (dbus_g_connection_unref (var), NULL)))
+#define _g_error_free0(var) ((var == NULL) ? NULL : (var = (g_error_free (var), NULL)))
+typedef struct _ParamSpecAdministrationClient ParamSpecAdministrationClient;
+typedef struct _ParamSpecAdministrationServer ParamSpecAdministrationServer;
+typedef struct _DBusObjectVTable _DBusObjectVTable;
+
+struct _AdministrationPrivate {
+	GetProfileCallback _GetProfile_cb;
+	gpointer _GetProfile_cb_target;
+	GDestroyNotify _GetProfile_cb_target_destroy_notify;
+	ReportCallback _Report_cb;
+	gpointer _Report_cb_target;
+	GDestroyNotify _Report_cb_target_destroy_notify;
+};
+
+struct _AdministrationClientPrivate {
+	DBusGProxy* _server;
+	DBusGConnection* _connection;
+	GotProfileCallback _GotProfile_cb;
+	gpointer _GotProfile_cb_target;
+	GDestroyNotify _GotProfile_cb_target_destroy_notify;
+};
+
+struct _ParamSpecAdministrationClient {
+	GParamSpec parent_instance;
+};
+
+struct _AdministrationServerPrivate {
+	Administration* _server;
+	DBusGConnection* _connection;
+};
+
+struct _ParamSpecAdministrationServer {
+	GParamSpec parent_instance;
+};
+
+struct _DBusObjectVTable {
+	void (*register_object) (DBusConnection*, const char*, void*);
+};
+
+
+static gpointer administration_parent_class = NULL;
+static gpointer administration_client_parent_class = NULL;
+static gpointer administration_server_parent_class = NULL;
+
+#define _PATH "/org/gcompris/Administration"
+#define _IFACE "org.gcompris.Administration"
+#define ADMINISTRATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_ADMINISTRATION, AdministrationPrivate))
+enum  {
+	ADMINISTRATION_DUMMY_PROPERTY
+};
+void administration_dbus_register_object (DBusConnection* connection, const char* path, void* object);
+void _administration_dbus_unregister (DBusConnection* connection, void* _user_data_);
+DBusHandlerResult administration_dbus_message (DBusConnection* connection, DBusMessage* message, void* object);
+static DBusHandlerResult _dbus_administration_introspect (Administration* self, DBusConnection* connection, DBusMessage* message);
+static DBusHandlerResult _dbus_administration_property_get_all (Administration* self, DBusConnection* connection, DBusMessage* message);
+static DBusHandlerResult _dbus_administration_GetProfile (Administration* self, DBusConnection* connection, DBusMessage* message);
+static DBusHandlerResult _dbus_administration_Report (Administration* self, DBusConnection* connection, DBusMessage* message);
+static void _dbus_administration_update_profile (GObject* _sender, DBusConnection* _connection);
+static void administration_finalize (GObject* obj);
+#define ADMINISTRATION_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_ADMINISTRATION_CLIENT, AdministrationClientPrivate))
+enum  {
+	ADMINISTRATION_CLIENT_DUMMY_PROPERTY
+};
+static void _administration_client_UpdateProfile_cb (AdministrationClient* self, DBusGProxy* sender);
+static void __administration_client_UpdateProfile_cb_dynamic_UpdateProfile_ (DBusGProxy* _sender, gpointer self);
+void _dynamic_UpdateProfile_conect (gpointer obj, const char * signal_name, GCallback handler, gpointer data);
+static void _administration_client_GetProfile_cb (AdministrationClient* self, const char* profile_data, GError* _error_);
+static void _dynamic_GetProfile0 (DBusGProxy* self, gpointer param1, void* param1_target, GError** error);
+static void _administration_client_Report_cb (AdministrationClient* self, GError* _error_);
+static void _dynamic_Report1 (DBusGProxy* self, const char* param1, gint param2, const char* param3, const char* param4, gint param5, gint param6, gint param7, const char* param8, gpointer param9, void* param9_target, GError** error);
+static void administration_client_finalize (AdministrationClient* obj);
+#define ADMINISTRATION_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_ADMINISTRATION_SERVER, AdministrationServerPrivate))
+enum  {
+	ADMINISTRATION_SERVER_DUMMY_PROPERTY
+};
+static void administration_server_finalize (AdministrationServer* obj);
+static void _vala_dbus_register_object (DBusConnection* connection, const char* path, void* object);
+static void _vala_dbus_unregister_object (gpointer connection, GObject* object);
+
+static const DBusObjectPathVTable _administration_dbus_path_vtable = {_administration_dbus_unregister, administration_dbus_message};
+static const _DBusObjectVTable _administration_dbus_vtable = {administration_dbus_register_object};
+
+
+Administration* administration_construct (GType object_type, GetProfileCallback GetProfile_cb, void* GetProfile_cb_target, ReportCallback Report_cb, void* Report_cb_target) {
+	Administration * self;
+	GetProfileCallback _tmp0_;
+	ReportCallback _tmp1_;
+	self = (Administration*) g_object_new (object_type, NULL);
+	self->priv->_GetProfile_cb = (_tmp0_ = GetProfile_cb, ((self->priv->_GetProfile_cb_target_destroy_notify == NULL) ? NULL : (self->priv->_GetProfile_cb_target_destroy_notify (self->priv->_GetProfile_cb_target), NULL), self->priv->_GetProfile_cb = NULL, self->priv->_GetProfile_cb_target = NULL, self->priv->_GetProfile_cb_target_destroy_notify = NULL), self->priv->_GetProfile_cb_target = GetProfile_cb_target, self->priv->_GetProfile_cb_target_destroy_notify = NULL, _tmp0_);
+	self->priv->_Report_cb = (_tmp1_ = Report_cb, ((self->priv->_Report_cb_target_destroy_notify == NULL) ? NULL : (self->priv->_Report_cb_target_destroy_notify (self->priv->_Report_cb_target), NULL), self->priv->_Report_cb = NULL, self->priv->_Report_cb_target = NULL, self->priv->_Report_cb_target_destroy_notify = NULL), self->priv->_Report_cb_target = Report_cb_target, self->priv->_Report_cb_target_destroy_notify = NULL, _tmp1_);
+	return self;
+}
+
+
+Administration* administration_new (GetProfileCallback GetProfile_cb, void* GetProfile_cb_target, ReportCallback Report_cb, void* Report_cb_target) {
+	return administration_construct (TYPE_ADMINISTRATION, GetProfile_cb, GetProfile_cb_target, Report_cb, Report_cb_target);
+}
+
+
+char* administration_GetProfile (Administration* self) {
+	char* result = NULL;
+	g_return_val_if_fail (self != NULL, NULL);
+	g_debug ("sugar_share.vala:53: GetProfile called on server");
+	result = self->priv->_GetProfile_cb (self->priv->_GetProfile_cb_target);
+	return result;
+}
+
+
+void administration_Report (Administration* self, const char* date, gint duration, const char* user, const char* board, gint level, gint sublevel, gint status, const char* comment) {
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (date != NULL);
+	g_return_if_fail (user != NULL);
+	g_return_if_fail (board != NULL);
+	g_return_if_fail (comment != NULL);
+	g_debug ("sugar_share.vala:62: Report called on server");
+	self->priv->_Report_cb (date, duration, user, board, level, sublevel, status, comment, self->priv->_Report_cb_target);
+}
+
+
+void _administration_dbus_unregister (DBusConnection* connection, void* _user_data_) {
+}
+
+
+static DBusHandlerResult _dbus_administration_introspect (Administration* self, DBusConnection* connection, DBusMessage* message) {
+	DBusMessage* reply;
+	DBusMessageIter iter;
+	GString* xml_data;
+	char** children;
+	int i;
+	reply = dbus_message_new_method_return (message);
+	dbus_message_iter_init_append (reply, &iter);
+	xml_data = g_string_new ("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\";>\n");
+	g_string_append (xml_data, "<node>\n<interface name=\"org.freedesktop.DBus.Introspectable\">\n  <method name=\"Introspect\">\n    <arg name=\"data\" direction=\"out\" type=\"s\"/>\n  </method>\n</interface>\n<interface name=\"org.freedesktop.DBus.Properties\">\n  <method name=\"Get\">\n    <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n    <arg name=\"propname\" direction=\"in\" type=\"s\"/>\n    <arg name=\"value\" direction=\"out\" type=\"v\"/>\n  </method>\n  <method name=\"Set\">\n    <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n    <arg name=\"propname\" direction=\"in\" type=\"s\"/>\n    <arg name=\"value\" direction=\"in\" type=\"v\"/>\n  </method>\n  <method name=\"GetAll\">\n    <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n    <arg name=\"props\" direction=\"out\" type=\"a{sv}\"/>\n  </method>\n</interface>\n<interface name=\"org.gcompris.Administration\">\n  <method name=\"GetProfile\">\n    <arg name=\"result\" type=\"s\" direction=\"o
 ut\"/>\n  </method>\n  <method name=\"Report\">\n    <arg name=\"date\" type=\"s\" direction=\"in\"/>\n    <arg name=\"duration\" type=\"i\" direction=\"in\"/>\n    <arg name=\"user\" type=\"s\" direction=\"in\"/>\n    <arg name=\"board\" type=\"s\" direction=\"in\"/>\n    <arg name=\"level\" type=\"i\" direction=\"in\"/>\n    <arg name=\"sublevel\" type=\"i\" direction=\"in\"/>\n    <arg name=\"status\" type=\"i\" direction=\"in\"/>\n    <arg name=\"comment\" type=\"s\" direction=\"in\"/>\n  </method>\n  <signal name=\"UpdateProfile\">\n  </signal>\n</interface>\n");
+	dbus_connection_list_registered (connection, g_object_get_data ((GObject *) self, "dbus_object_path"), &children);
+	for (i = 0; children[i]; i++) {
+		g_string_append_printf (xml_data, "<node name=\"%s\"/>\n", children[i]);
+	}
+	dbus_free_string_array (children);
+	g_string_append (xml_data, "</node>\n");
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &xml_data->str);
+	g_string_free (xml_data, TRUE);
+	if (reply) {
+		dbus_connection_send (connection, reply, NULL);
+		dbus_message_unref (reply);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	} else {
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+}
+
+
+static DBusHandlerResult _dbus_administration_property_get_all (Administration* self, DBusConnection* connection, DBusMessage* message) {
+	DBusMessage* reply;
+	DBusMessageIter iter, reply_iter, subiter;
+	char* interface_name;
+	const char* _tmp0_;
+	if (strcmp (dbus_message_get_signature (message), "s")) {
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+	dbus_message_iter_init (message, &iter);
+	reply = dbus_message_new_method_return (message);
+	dbus_message_iter_init_append (reply, &reply_iter);
+	dbus_message_iter_get_basic (&iter, &_tmp0_);
+	dbus_message_iter_next (&iter);
+	interface_name = g_strdup (_tmp0_);
+	if (strcmp (interface_name, "org.gcompris.Administration") == 0) {
+		dbus_message_iter_open_container (&reply_iter, DBUS_TYPE_ARRAY, "{sv}", &subiter);
+		dbus_message_iter_close_container (&reply_iter, &subiter);
+	} else {
+		dbus_message_unref (reply);
+		reply = NULL;
+	}
+	g_free (interface_name);
+	if (reply) {
+		dbus_connection_send (connection, reply, NULL);
+		dbus_message_unref (reply);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	} else {
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+}
+
+
+static DBusHandlerResult _dbus_administration_GetProfile (Administration* self, DBusConnection* connection, DBusMessage* message) {
+	DBusMessageIter iter;
+	GError* error;
+	char* result;
+	DBusMessage* reply;
+	const char* _tmp1_;
+	error = NULL;
+	if (strcmp (dbus_message_get_signature (message), "")) {
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+	dbus_message_iter_init (message, &iter);
+	result = administration_GetProfile (self);
+	reply = dbus_message_new_method_return (message);
+	dbus_message_iter_init_append (reply, &iter);
+	_tmp1_ = result;
+	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &_tmp1_);
+	_g_free0 (result);
+	if (reply) {
+		dbus_connection_send (connection, reply, NULL);
+		dbus_message_unref (reply);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	} else {
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+}
+
+
+static DBusHandlerResult _dbus_administration_Report (Administration* self, DBusConnection* connection, DBusMessage* message) {
+	DBusMessageIter iter;
+	GError* error;
+	char* date = NULL;
+	const char* _tmp2_;
+	gint duration = 0;
+	dbus_int32_t _tmp3_;
+	char* user = NULL;
+	const char* _tmp4_;
+	char* board = NULL;
+	const char* _tmp5_;
+	gint level = 0;
+	dbus_int32_t _tmp6_;
+	gint sublevel = 0;
+	dbus_int32_t _tmp7_;
+	gint status = 0;
+	dbus_int32_t _tmp8_;
+	char* comment = NULL;
+	const char* _tmp9_;
+	DBusMessage* reply;
+	error = NULL;
+	if (strcmp (dbus_message_get_signature (message), "sissiiis")) {
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+	dbus_message_iter_init (message, &iter);
+	dbus_message_iter_get_basic (&iter, &_tmp2_);
+	dbus_message_iter_next (&iter);
+	date = g_strdup (_tmp2_);
+	dbus_message_iter_get_basic (&iter, &_tmp3_);
+	dbus_message_iter_next (&iter);
+	duration = _tmp3_;
+	dbus_message_iter_get_basic (&iter, &_tmp4_);
+	dbus_message_iter_next (&iter);
+	user = g_strdup (_tmp4_);
+	dbus_message_iter_get_basic (&iter, &_tmp5_);
+	dbus_message_iter_next (&iter);
+	board = g_strdup (_tmp5_);
+	dbus_message_iter_get_basic (&iter, &_tmp6_);
+	dbus_message_iter_next (&iter);
+	level = _tmp6_;
+	dbus_message_iter_get_basic (&iter, &_tmp7_);
+	dbus_message_iter_next (&iter);
+	sublevel = _tmp7_;
+	dbus_message_iter_get_basic (&iter, &_tmp8_);
+	dbus_message_iter_next (&iter);
+	status = _tmp8_;
+	dbus_message_iter_get_basic (&iter, &_tmp9_);
+	dbus_message_iter_next (&iter);
+	comment = g_strdup (_tmp9_);
+	administration_Report (self, date, duration, user, board, level, sublevel, status, comment);
+	reply = dbus_message_new_method_return (message);
+	dbus_message_iter_init_append (reply, &iter);
+	_g_free0 (date);
+	_g_free0 (user);
+	_g_free0 (board);
+	_g_free0 (comment);
+	if (reply) {
+		dbus_connection_send (connection, reply, NULL);
+		dbus_message_unref (reply);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	} else {
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+}
+
+
+DBusHandlerResult administration_dbus_message (DBusConnection* connection, DBusMessage* message, void* object) {
+	DBusHandlerResult result;
+	result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	if (dbus_message_is_method_call (message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+		result = _dbus_administration_introspect (object, connection, message);
+	} else if (dbus_message_is_method_call (message, "org.freedesktop.DBus.Properties", "GetAll")) {
+		result = _dbus_administration_property_get_all (object, connection, message);
+	} else if (dbus_message_is_method_call (message, "org.gcompris.Administration", "GetProfile")) {
+		result = _dbus_administration_GetProfile (object, connection, message);
+	} else if (dbus_message_is_method_call (message, "org.gcompris.Administration", "Report")) {
+		result = _dbus_administration_Report (object, connection, message);
+	}
+	if (result == DBUS_HANDLER_RESULT_HANDLED) {
+		return result;
+	} else {
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+}
+
+
+static void _dbus_administration_update_profile (GObject* _sender, DBusConnection* _connection) {
+	const char * _path;
+	DBusMessage *_message;
+	DBusMessageIter _iter;
+	_path = g_object_get_data (_sender, "dbus_object_path");
+	_message = dbus_message_new_signal (_path, "org.gcompris.Administration", "UpdateProfile");
+	dbus_message_iter_init_append (_message, &_iter);
+	dbus_connection_send (_connection, _message, NULL);
+	dbus_message_unref (_message);
+}
+
+
+void administration_dbus_register_object (DBusConnection* connection, const char* path, void* object) {
+	if (!g_object_get_data (object, "dbus_object_path")) {
+		g_object_set_data (object, "dbus_object_path", g_strdup (path));
+		dbus_connection_register_object_path (connection, path, &_administration_dbus_path_vtable, object);
+		g_object_weak_ref (object, _vala_dbus_unregister_object, connection);
+	}
+	g_signal_connect (object, "update-profile", (GCallback) _dbus_administration_update_profile, connection);
+}
+
+
+static void administration_class_init (AdministrationClass * klass) {
+	administration_parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (AdministrationPrivate));
+	G_OBJECT_CLASS (klass)->finalize = administration_finalize;
+	g_signal_new ("update_profile", TYPE_ADMINISTRATION, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+	g_type_set_qdata (TYPE_ADMINISTRATION, g_quark_from_static_string ("DBusObjectVTable"), (void*) (&_administration_dbus_vtable));
+}
+
+
+static void administration_instance_init (Administration * self) {
+	self->priv = ADMINISTRATION_GET_PRIVATE (self);
+}
+
+
+static void administration_finalize (GObject* obj) {
+	Administration * self;
+	self = ADMINISTRATION (obj);
+	(self->priv->_GetProfile_cb_target_destroy_notify == NULL) ? NULL : (self->priv->_GetProfile_cb_target_destroy_notify (self->priv->_GetProfile_cb_target), NULL);
+	self->priv->_GetProfile_cb = NULL;
+	self->priv->_GetProfile_cb_target = NULL;
+	self->priv->_GetProfile_cb_target_destroy_notify = NULL;
+	(self->priv->_Report_cb_target_destroy_notify == NULL) ? NULL : (self->priv->_Report_cb_target_destroy_notify (self->priv->_Report_cb_target), NULL);
+	self->priv->_Report_cb = NULL;
+	self->priv->_Report_cb_target = NULL;
+	self->priv->_Report_cb_target_destroy_notify = NULL;
+	G_OBJECT_CLASS (administration_parent_class)->finalize (obj);
+}
+
+
+GType administration_get_type (void) {
+	static volatile gsize administration_type_id__volatile = 0;
+	if (g_once_init_enter (&administration_type_id__volatile)) {
+		static const GTypeInfo g_define_type_info = { sizeof (AdministrationClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) administration_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (Administration), 0, (GInstanceInitFunc) administration_instance_init, NULL };
+		GType administration_type_id;
+		administration_type_id = g_type_register_static (G_TYPE_OBJECT, "Administration", &g_define_type_info, 0);
+		g_once_init_leave (&administration_type_id__volatile, administration_type_id);
+	}
+	return administration_type_id__volatile;
+}
+
+
+static void __administration_client_UpdateProfile_cb_dynamic_UpdateProfile_ (DBusGProxy* _sender, gpointer self) {
+	_administration_client_UpdateProfile_cb (self, _sender);
+}
+
+
+void _dynamic_UpdateProfile_conect (gpointer obj, const char * signal_name, GCallback handler, gpointer data) {
+	dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (obj, "UpdateProfile", G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (obj, signal_name, handler, data, NULL);
+}
+
+
+AdministrationClient* administration_client_construct (GType object_type, const char* address, const char* name, GotProfileCallback GotProfile_cb, void* GotProfile_cb_target) {
+	GError * _inner_error_;
+	AdministrationClient* self;
+	GotProfileCallback _tmp0_;
+	DBusGProxy* _tmp3_;
+	g_return_val_if_fail (address != NULL, NULL);
+	g_return_val_if_fail (name != NULL, NULL);
+	_inner_error_ = NULL;
+	self = (AdministrationClient*) g_type_create_instance (object_type);
+	self->priv->_GotProfile_cb = (_tmp0_ = GotProfile_cb, ((self->priv->_GotProfile_cb_target_destroy_notify == NULL) ? NULL : (self->priv->_GotProfile_cb_target_destroy_notify (self->priv->_GotProfile_cb_target), NULL), self->priv->_GotProfile_cb = NULL, self->priv->_GotProfile_cb_target = NULL, self->priv->_GotProfile_cb_target_destroy_notify = NULL), self->priv->_GotProfile_cb_target = GotProfile_cb_target, self->priv->_GotProfile_cb_target_destroy_notify = NULL, _tmp0_);
+	{
+		DBusGConnection* _tmp1_;
+		DBusGConnection* _tmp2_;
+		_tmp1_ = dbus_g_connection_open (address, &_inner_error_);
+		if (_inner_error_ != NULL) {
+			goto __catch0_g_error;
+		}
+		self->priv->_connection = (_tmp2_ = _tmp1_, _dbus_g_connection_unref0 (self->priv->_connection), _tmp2_);
+	}
+	goto __finally0;
+	__catch0_g_error:
+	{
+		GError * _error_;
+		_error_ = _inner_error_;
+		_inner_error_ = NULL;
+		{
+			g_warning ("sugar_share.vala:88: Cannot open DBus connection: %s", _error_->message);
+			_g_error_free0 (_error_);
+			return self;
+		}
+	}
+	__finally0:
+	if (_inner_error_ != NULL) {
+		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+		g_clear_error (&_inner_error_);
+		return NULL;
+	}
+	self->priv->_server = (_tmp3_ = dbus_g_proxy_new_for_name (self->priv->_connection, name, _PATH, _IFACE), _g_object_unref0 (self->priv->_server), _tmp3_);
+	_dynamic_UpdateProfile_conect (self->priv->_server, "UpdateProfile", (GCallback) __administration_client_UpdateProfile_cb_dynamic_UpdateProfile_, self);
+	return self;
+}
+
+
+AdministrationClient* administration_client_new (const char* address, const char* name, GotProfileCallback GotProfile_cb, void* GotProfile_cb_target) {
+	return administration_client_construct (TYPE_ADMINISTRATION_CLIENT, address, name, GotProfile_cb, GotProfile_cb_target);
+}
+
+
+static void __administration_client_GetProfile_cb_cb (DBusGProxy* proxy, DBusGProxyCall* call, void* user_data) {
+	GError* error;
+	const char* profile_data;
+	error = NULL;
+	dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_STRING, &profile_data, G_TYPE_INVALID);
+	_administration_client_GetProfile_cb (user_data, profile_data, error);
+	administration_client_unref (user_data);
+}
+
+
+static void _dynamic_GetProfile0 (DBusGProxy* self, gpointer param1, void* param1_target, GError** error) {
+	dbus_g_proxy_begin_call (self, "GetProfile", __administration_client_GetProfile_cb_cb, administration_client_ref (param1_target), NULL, G_TYPE_INVALID, G_TYPE_INVALID);
+	if (*error) {
+		return;
+	}
+}
+
+
+void administration_client_import_profile (AdministrationClient* self) {
+	GError * _inner_error_;
+	g_return_if_fail (self != NULL);
+	_inner_error_ = NULL;
+	if (self->priv->_server == NULL) {
+		return;
+	}
+	g_debug ("sugar_share.vala:103: GetProfile called on client");
+	{
+		_dynamic_GetProfile0 (self->priv->_server, _administration_client_GetProfile_cb, self, &_inner_error_);
+		if (_inner_error_ != NULL) {
+			goto __catch1_g_error;
+		}
+	}
+	goto __finally1;
+	__catch1_g_error:
+	{
+		GError * _error_;
+		_error_ = _inner_error_;
+		_inner_error_ = NULL;
+		{
+			g_warning ("sugar_share.vala:108: Cannot get server profile: %s", _error_->message);
+			self->priv->_GotProfile_cb (NULL, self->priv->_GotProfile_cb_target);
+			_g_error_free0 (_error_);
+		}
+	}
+	__finally1:
+	if (_inner_error_ != NULL) {
+		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+		g_clear_error (&_inner_error_);
+		return;
+	}
+}
+
+
+static void __administration_client_Report_cb_cb (DBusGProxy* proxy, DBusGProxyCall* call, void* user_data) {
+	GError* error;
+	error = NULL;
+	dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+	_administration_client_Report_cb (user_data, error);
+	administration_client_unref (user_data);
+}
+
+
+static void _dynamic_Report1 (DBusGProxy* self, const char* param1, gint param2, const char* param3, const char* param4, gint param5, gint param6, gint param7, const char* param8, gpointer param9, void* param9_target, GError** error) {
+	dbus_g_proxy_begin_call (self, "Report", __administration_client_Report_cb_cb, administration_client_ref (param9_target), NULL, G_TYPE_STRING, param1, G_TYPE_INT, param2, G_TYPE_STRING, param3, G_TYPE_STRING, param4, G_TYPE_INT, param5, G_TYPE_INT, param6, G_TYPE_INT, param7, G_TYPE_STRING, param8, G_TYPE_INVALID, G_TYPE_INVALID);
+	if (*error) {
+		return;
+	}
+}
+
+
+void administration_client_report (AdministrationClient* self, const char* date, gint duration, const char* user, const char* board, gint level, gint sublevel, gint status, const char* comment) {
+	GError * _inner_error_;
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (date != NULL);
+	g_return_if_fail (user != NULL);
+	g_return_if_fail (board != NULL);
+	g_return_if_fail (comment != NULL);
+	_inner_error_ = NULL;
+	if (self->priv->_server == NULL) {
+		return;
+	}
+	g_debug ("sugar_share.vala:121: Send report");
+	_dynamic_Report1 (self->priv->_server, date, duration, user, board, level, sublevel, status, comment, _administration_client_Report_cb, self, &_inner_error_);
+	if (_inner_error_ != NULL) {
+		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+		g_clear_error (&_inner_error_);
+		return;
+	}
+}
+
+
+static void _administration_client_Report_cb (AdministrationClient* self, GError* _error_) {
+	g_return_if_fail (self != NULL);
+	if (_error_ != NULL) {
+		g_warning ("sugar_share.vala:129: Cannot send report to server: %s", _error_->message);
+	}
+}
+
+
+static void _administration_client_GetProfile_cb (AdministrationClient* self, const char* profile_data, GError* _error_) {
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (profile_data != NULL);
+	if (_error_ != NULL) {
+		g_warning ("sugar_share.vala:135: Cannot get server profile: %s", _error_->message);
+		self->priv->_GotProfile_cb (NULL, self->priv->_GotProfile_cb_target);
+	} else {
+		self->priv->_GotProfile_cb (profile_data, self->priv->_GotProfile_cb_target);
+	}
+}
+
+
+static void _administration_client_UpdateProfile_cb (AdministrationClient* self, DBusGProxy* sender) {
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (sender != NULL);
+	administration_client_import_profile (self);
+}
+
+
+static void value_administration_client_init (GValue* value) {
+	value->data[0].v_pointer = NULL;
+}
+
+
+static void value_administration_client_free_value (GValue* value) {
+	if (value->data[0].v_pointer) {
+		administration_client_unref (value->data[0].v_pointer);
+	}
+}
+
+
+static void value_administration_client_copy_value (const GValue* src_value, GValue* dest_value) {
+	if (src_value->data[0].v_pointer) {
+		dest_value->data[0].v_pointer = administration_client_ref (src_value->data[0].v_pointer);
+	} else {
+		dest_value->data[0].v_pointer = NULL;
+	}
+}
+
+
+static gpointer value_administration_client_peek_pointer (const GValue* value) {
+	return value->data[0].v_pointer;
+}
+
+
+static gchar* value_administration_client_collect_value (GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) {
+	if (collect_values[0].v_pointer) {
+		AdministrationClient* object;
+		object = collect_values[0].v_pointer;
+		if (object->parent_instance.g_class == NULL) {
+			return g_strconcat ("invalid unclassed object pointer for value type `", G_VALUE_TYPE_NAME (value), "'", NULL);
+		} else if (!g_value_type_compatible (G_TYPE_FROM_INSTANCE (object), G_VALUE_TYPE (value))) {
+			return g_strconcat ("invalid object type `", g_type_name (G_TYPE_FROM_INSTANCE (object)), "' for value type `", G_VALUE_TYPE_NAME (value), "'", NULL);
+		}
+		value->data[0].v_pointer = administration_client_ref (object);
+	} else {
+		value->data[0].v_pointer = NULL;
+	}
+	return NULL;
+}
+
+
+static gchar* value_administration_client_lcopy_value (const GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) {
+	AdministrationClient** object_p;
+	object_p = collect_values[0].v_pointer;
+	if (!object_p) {
+		return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
+	}
+	if (!value->data[0].v_pointer) {
+		*object_p = NULL;
+	} else if (collect_flags && G_VALUE_NOCOPY_CONTENTS) {
+		*object_p = value->data[0].v_pointer;
+	} else {
+		*object_p = administration_client_ref (value->data[0].v_pointer);
+	}
+	return NULL;
+}
+
+
+GParamSpec* param_spec_administration_client (const gchar* name, const gchar* nick, const gchar* blurb, GType object_type, GParamFlags flags) {
+	ParamSpecAdministrationClient* spec;
+	g_return_val_if_fail (g_type_is_a (object_type, TYPE_ADMINISTRATION_CLIENT), NULL);
+	spec = g_param_spec_internal (G_TYPE_PARAM_OBJECT, name, nick, blurb, flags);
+	G_PARAM_SPEC (spec)->value_type = object_type;
+	return G_PARAM_SPEC (spec);
+}
+
+
+gpointer value_get_administration_client (const GValue* value) {
+	g_return_val_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADMINISTRATION_CLIENT), NULL);
+	return value->data[0].v_pointer;
+}
+
+
+void value_set_administration_client (GValue* value, gpointer v_object) {
+	AdministrationClient* old;
+	g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADMINISTRATION_CLIENT));
+	old = value->data[0].v_pointer;
+	if (v_object) {
+		g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_ADMINISTRATION_CLIENT));
+		g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value)));
+		value->data[0].v_pointer = v_object;
+		administration_client_ref (value->data[0].v_pointer);
+	} else {
+		value->data[0].v_pointer = NULL;
+	}
+	if (old) {
+		administration_client_unref (old);
+	}
+}
+
+
+void value_take_administration_client (GValue* value, gpointer v_object) {
+	AdministrationClient* old;
+	g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADMINISTRATION_CLIENT));
+	old = value->data[0].v_pointer;
+	if (v_object) {
+		g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_ADMINISTRATION_CLIENT));
+		g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value)));
+		value->data[0].v_pointer = v_object;
+	} else {
+		value->data[0].v_pointer = NULL;
+	}
+	if (old) {
+		administration_client_unref (old);
+	}
+}
+
+
+static void administration_client_class_init (AdministrationClientClass * klass) {
+	administration_client_parent_class = g_type_class_peek_parent (klass);
+	ADMINISTRATION_CLIENT_CLASS (klass)->finalize = administration_client_finalize;
+	g_type_class_add_private (klass, sizeof (AdministrationClientPrivate));
+}
+
+
+static void administration_client_instance_init (AdministrationClient * self) {
+	self->priv = ADMINISTRATION_CLIENT_GET_PRIVATE (self);
+	self->ref_count = 1;
+}
+
+
+static void administration_client_finalize (AdministrationClient* obj) {
+	AdministrationClient * self;
+	self = ADMINISTRATION_CLIENT (obj);
+	_g_object_unref0 (self->priv->_server);
+	_dbus_g_connection_unref0 (self->priv->_connection);
+	(self->priv->_GotProfile_cb_target_destroy_notify == NULL) ? NULL : (self->priv->_GotProfile_cb_target_destroy_notify (self->priv->_GotProfile_cb_target), NULL);
+	self->priv->_GotProfile_cb = NULL;
+	self->priv->_GotProfile_cb_target = NULL;
+	self->priv->_GotProfile_cb_target_destroy_notify = NULL;
+}
+
+
+GType administration_client_get_type (void) {
+	static volatile gsize administration_client_type_id__volatile = 0;
+	if (g_once_init_enter (&administration_client_type_id__volatile)) {
+		static const GTypeValueTable g_define_type_value_table = { value_administration_client_init, value_administration_client_free_value, value_administration_client_copy_value, value_administration_client_peek_pointer, "p", value_administration_client_collect_value, "p", value_administration_client_lcopy_value };
+		static const GTypeInfo g_define_type_info = { sizeof (AdministrationClientClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) administration_client_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (AdministrationClient), 0, (GInstanceInitFunc) administration_client_instance_init, &g_define_type_value_table };
+		static const GTypeFundamentalInfo g_define_type_fundamental_info = { (G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE) };
+		GType administration_client_type_id;
+		administration_client_type_id = g_type_register_fundamental (g_type_fundamental_next (), "AdministrationClient", &g_define_type_info, &g_define_type_fundamental_info, 0);
+		g_once_init_leave (&administration_client_type_id__volatile, administration_client_type_id);
+	}
+	return administration_client_type_id__volatile;
+}
+
+
+gpointer administration_client_ref (gpointer instance) {
+	AdministrationClient* self;
+	self = instance;
+	g_atomic_int_inc (&self->ref_count);
+	return instance;
+}
+
+
+void administration_client_unref (gpointer instance) {
+	AdministrationClient* self;
+	self = instance;
+	if (g_atomic_int_dec_and_test (&self->ref_count)) {
+		ADMINISTRATION_CLIENT_GET_CLASS (self)->finalize (self);
+		g_type_free_instance ((GTypeInstance *) self);
+	}
+}
+
+
+AdministrationServer* administration_server_construct (GType object_type, const char* address, GetProfileCallback GetProfile_cb, void* GetProfile_cb_target, ReportCallback Report_cb, void* Report_cb_target) {
+	GError * _inner_error_;
+	AdministrationServer* self;
+	Administration* _tmp2_;
+	g_return_val_if_fail (address != NULL, NULL);
+	_inner_error_ = NULL;
+	self = (AdministrationServer*) g_type_create_instance (object_type);
+	{
+		DBusGConnection* _tmp0_;
+		DBusGConnection* _tmp1_;
+		_tmp0_ = dbus_g_connection_open (address, &_inner_error_);
+		if (_inner_error_ != NULL) {
+			goto __catch2_g_error;
+		}
+		self->priv->_connection = (_tmp1_ = _tmp0_, _dbus_g_connection_unref0 (self->priv->_connection), _tmp1_);
+	}
+	goto __finally2;
+	__catch2_g_error:
+	{
+		GError * _error_;
+		_error_ = _inner_error_;
+		_inner_error_ = NULL;
+		{
+			g_warning ("sugar_share.vala:165: Cannot open DBus connection: %s", _error_->message);
+			_g_error_free0 (_error_);
+			return self;
+		}
+	}
+	__finally2:
+	if (_inner_error_ != NULL) {
+		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+		g_clear_error (&_inner_error_);
+		return NULL;
+	}
+	self->priv->_server = (_tmp2_ = administration_new (GetProfile_cb, GetProfile_cb_target, Report_cb, Report_cb_target), _g_object_unref0 (self->priv->_server), _tmp2_);
+	_vala_dbus_register_object (dbus_g_connection_get_connection (self->priv->_connection), _PATH, (GObject*) self->priv->_server);
+	return self;
+}
+
+
+AdministrationServer* administration_server_new (const char* address, GetProfileCallback GetProfile_cb, void* GetProfile_cb_target, ReportCallback Report_cb, void* Report_cb_target) {
+	return administration_server_construct (TYPE_ADMINISTRATION_SERVER, address, GetProfile_cb, GetProfile_cb_target, Report_cb, Report_cb_target);
+}
+
+
+void administration_server_update_profile (AdministrationServer* self) {
+	g_return_if_fail (self != NULL);
+	if (self->priv->_server == NULL) {
+		return;
+	}
+	g_debug ("sugar_share.vala:179: Force to update students profiles");
+	g_signal_emit_by_name (self->priv->_server, "update-profile");
+}
+
+
+static void value_administration_server_init (GValue* value) {
+	value->data[0].v_pointer = NULL;
+}
+
+
+static void value_administration_server_free_value (GValue* value) {
+	if (value->data[0].v_pointer) {
+		administration_server_unref (value->data[0].v_pointer);
+	}
+}
+
+
+static void value_administration_server_copy_value (const GValue* src_value, GValue* dest_value) {
+	if (src_value->data[0].v_pointer) {
+		dest_value->data[0].v_pointer = administration_server_ref (src_value->data[0].v_pointer);
+	} else {
+		dest_value->data[0].v_pointer = NULL;
+	}
+}
+
+
+static gpointer value_administration_server_peek_pointer (const GValue* value) {
+	return value->data[0].v_pointer;
+}
+
+
+static gchar* value_administration_server_collect_value (GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) {
+	if (collect_values[0].v_pointer) {
+		AdministrationServer* object;
+		object = collect_values[0].v_pointer;
+		if (object->parent_instance.g_class == NULL) {
+			return g_strconcat ("invalid unclassed object pointer for value type `", G_VALUE_TYPE_NAME (value), "'", NULL);
+		} else if (!g_value_type_compatible (G_TYPE_FROM_INSTANCE (object), G_VALUE_TYPE (value))) {
+			return g_strconcat ("invalid object type `", g_type_name (G_TYPE_FROM_INSTANCE (object)), "' for value type `", G_VALUE_TYPE_NAME (value), "'", NULL);
+		}
+		value->data[0].v_pointer = administration_server_ref (object);
+	} else {
+		value->data[0].v_pointer = NULL;
+	}
+	return NULL;
+}
+
+
+static gchar* value_administration_server_lcopy_value (const GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) {
+	AdministrationServer** object_p;
+	object_p = collect_values[0].v_pointer;
+	if (!object_p) {
+		return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
+	}
+	if (!value->data[0].v_pointer) {
+		*object_p = NULL;
+	} else if (collect_flags && G_VALUE_NOCOPY_CONTENTS) {
+		*object_p = value->data[0].v_pointer;
+	} else {
+		*object_p = administration_server_ref (value->data[0].v_pointer);
+	}
+	return NULL;
+}
+
+
+GParamSpec* param_spec_administration_server (const gchar* name, const gchar* nick, const gchar* blurb, GType object_type, GParamFlags flags) {
+	ParamSpecAdministrationServer* spec;
+	g_return_val_if_fail (g_type_is_a (object_type, TYPE_ADMINISTRATION_SERVER), NULL);
+	spec = g_param_spec_internal (G_TYPE_PARAM_OBJECT, name, nick, blurb, flags);
+	G_PARAM_SPEC (spec)->value_type = object_type;
+	return G_PARAM_SPEC (spec);
+}
+
+
+gpointer value_get_administration_server (const GValue* value) {
+	g_return_val_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADMINISTRATION_SERVER), NULL);
+	return value->data[0].v_pointer;
+}
+
+
+void value_set_administration_server (GValue* value, gpointer v_object) {
+	AdministrationServer* old;
+	g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADMINISTRATION_SERVER));
+	old = value->data[0].v_pointer;
+	if (v_object) {
+		g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_ADMINISTRATION_SERVER));
+		g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value)));
+		value->data[0].v_pointer = v_object;
+		administration_server_ref (value->data[0].v_pointer);
+	} else {
+		value->data[0].v_pointer = NULL;
+	}
+	if (old) {
+		administration_server_unref (old);
+	}
+}
+
+
+void value_take_administration_server (GValue* value, gpointer v_object) {
+	AdministrationServer* old;
+	g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_ADMINISTRATION_SERVER));
+	old = value->data[0].v_pointer;
+	if (v_object) {
+		g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_ADMINISTRATION_SERVER));
+		g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value)));
+		value->data[0].v_pointer = v_object;
+	} else {
+		value->data[0].v_pointer = NULL;
+	}
+	if (old) {
+		administration_server_unref (old);
+	}
+}
+
+
+static void administration_server_class_init (AdministrationServerClass * klass) {
+	administration_server_parent_class = g_type_class_peek_parent (klass);
+	ADMINISTRATION_SERVER_CLASS (klass)->finalize = administration_server_finalize;
+	g_type_class_add_private (klass, sizeof (AdministrationServerPrivate));
+}
+
+
+static void administration_server_instance_init (AdministrationServer * self) {
+	self->priv = ADMINISTRATION_SERVER_GET_PRIVATE (self);
+	self->ref_count = 1;
+}
+
+
+static void administration_server_finalize (AdministrationServer* obj) {
+	AdministrationServer * self;
+	self = ADMINISTRATION_SERVER (obj);
+	_g_object_unref0 (self->priv->_server);
+	_dbus_g_connection_unref0 (self->priv->_connection);
+}
+
+
+GType administration_server_get_type (void) {
+	static volatile gsize administration_server_type_id__volatile = 0;
+	if (g_once_init_enter (&administration_server_type_id__volatile)) {
+		static const GTypeValueTable g_define_type_value_table = { value_administration_server_init, value_administration_server_free_value, value_administration_server_copy_value, value_administration_server_peek_pointer, "p", value_administration_server_collect_value, "p", value_administration_server_lcopy_value };
+		static const GTypeInfo g_define_type_info = { sizeof (AdministrationServerClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) administration_server_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (AdministrationServer), 0, (GInstanceInitFunc) administration_server_instance_init, &g_define_type_value_table };
+		static const GTypeFundamentalInfo g_define_type_fundamental_info = { (G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE) };
+		GType administration_server_type_id;
+		administration_server_type_id = g_type_register_fundamental (g_type_fundamental_next (), "AdministrationServer", &g_define_type_info, &g_define_type_fundamental_info, 0);
+		g_once_init_leave (&administration_server_type_id__volatile, administration_server_type_id);
+	}
+	return administration_server_type_id__volatile;
+}
+
+
+gpointer administration_server_ref (gpointer instance) {
+	AdministrationServer* self;
+	self = instance;
+	g_atomic_int_inc (&self->ref_count);
+	return instance;
+}
+
+
+void administration_server_unref (gpointer instance) {
+	AdministrationServer* self;
+	self = instance;
+	if (g_atomic_int_dec_and_test (&self->ref_count)) {
+		ADMINISTRATION_SERVER_GET_CLASS (self)->finalize (self);
+		g_type_free_instance ((GTypeInstance *) self);
+	}
+}
+
+
+static void _vala_dbus_register_object (DBusConnection* connection, const char* path, void* object) {
+	const _DBusObjectVTable * vtable;
+	vtable = g_type_get_qdata (G_TYPE_FROM_INSTANCE (object), g_quark_from_static_string ("DBusObjectVTable"));
+	if (vtable) {
+		vtable->register_object (connection, path, object);
+	} else {
+		g_warning ("Object does not implement any D-Bus interface");
+	}
+}
+
+
+static void _vala_dbus_unregister_object (gpointer connection, GObject* object) {
+	char* path;
+	path = g_object_steal_data ((GObject*) object, "dbus_object_path");
+	dbus_connection_unregister_object_path (connection, path);
+	g_free (path);
+}
+
+
+
+
diff --git a/src/gcompris/sugar_share.h b/src/gcompris/sugar_share.h
new file mode 100644
index 0000000..c5ca243
--- /dev/null
+++ b/src/gcompris/sugar_share.h
@@ -0,0 +1,113 @@
+/* sugar_share.h generated by valac, the Vala compiler, do not modify */
+
+
+#ifndef __SUGAR_SHARE_H__
+#define __SUGAR_SHARE_H__
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+
+#define TYPE_ADMINISTRATION (administration_get_type ())
+#define ADMINISTRATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_ADMINISTRATION, Administration))
+#define ADMINISTRATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_ADMINISTRATION, AdministrationClass))
+#define IS_ADMINISTRATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_ADMINISTRATION))
+#define IS_ADMINISTRATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_ADMINISTRATION))
+#define ADMINISTRATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_ADMINISTRATION, AdministrationClass))
+
+typedef struct _Administration Administration;
+typedef struct _AdministrationClass AdministrationClass;
+typedef struct _AdministrationPrivate AdministrationPrivate;
+
+#define TYPE_ADMINISTRATION_CLIENT (administration_client_get_type ())
+#define ADMINISTRATION_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_ADMINISTRATION_CLIENT, AdministrationClient))
+#define ADMINISTRATION_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_ADMINISTRATION_CLIENT, AdministrationClientClass))
+#define IS_ADMINISTRATION_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_ADMINISTRATION_CLIENT))
+#define IS_ADMINISTRATION_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_ADMINISTRATION_CLIENT))
+#define ADMINISTRATION_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_ADMINISTRATION_CLIENT, AdministrationClientClass))
+
+typedef struct _AdministrationClient AdministrationClient;
+typedef struct _AdministrationClientClass AdministrationClientClass;
+typedef struct _AdministrationClientPrivate AdministrationClientPrivate;
+
+#define TYPE_ADMINISTRATION_SERVER (administration_server_get_type ())
+#define ADMINISTRATION_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_ADMINISTRATION_SERVER, AdministrationServer))
+#define ADMINISTRATION_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_ADMINISTRATION_SERVER, AdministrationServerClass))
+#define IS_ADMINISTRATION_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_ADMINISTRATION_SERVER))
+#define IS_ADMINISTRATION_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_ADMINISTRATION_SERVER))
+#define ADMINISTRATION_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_ADMINISTRATION_SERVER, AdministrationServerClass))
+
+typedef struct _AdministrationServer AdministrationServer;
+typedef struct _AdministrationServerClass AdministrationServerClass;
+typedef struct _AdministrationServerPrivate AdministrationServerPrivate;
+
+typedef char* (*GetProfileCallback) (void* user_data);
+typedef void (*ReportCallback) (const char* date, gint duration, const char* user, const char* board, gint level, gint sublevel, gint status, const char* comment, void* user_data);
+typedef void (*GotProfileCallback) (const char* data, void* user_data);
+struct _Administration {
+	GObject parent_instance;
+	AdministrationPrivate * priv;
+};
+
+struct _AdministrationClass {
+	GObjectClass parent_class;
+};
+
+struct _AdministrationClient {
+	GTypeInstance parent_instance;
+	volatile int ref_count;
+	AdministrationClientPrivate * priv;
+};
+
+struct _AdministrationClientClass {
+	GTypeClass parent_class;
+	void (*finalize) (AdministrationClient *self);
+};
+
+struct _AdministrationServer {
+	GTypeInstance parent_instance;
+	volatile int ref_count;
+	AdministrationServerPrivate * priv;
+};
+
+struct _AdministrationServerClass {
+	GTypeClass parent_class;
+	void (*finalize) (AdministrationServer *self);
+};
+
+
+GType administration_get_type (void);
+Administration* administration_new (GetProfileCallback GetProfile_cb, void* GetProfile_cb_target, ReportCallback Report_cb, void* Report_cb_target);
+Administration* administration_construct (GType object_type, GetProfileCallback GetProfile_cb, void* GetProfile_cb_target, ReportCallback Report_cb, void* Report_cb_target);
+char* administration_GetProfile (Administration* self);
+void administration_Report (Administration* self, const char* date, gint duration, const char* user, const char* board, gint level, gint sublevel, gint status, const char* comment);
+gpointer administration_client_ref (gpointer instance);
+void administration_client_unref (gpointer instance);
+GParamSpec* param_spec_administration_client (const gchar* name, const gchar* nick, const gchar* blurb, GType object_type, GParamFlags flags);
+void value_set_administration_client (GValue* value, gpointer v_object);
+void value_take_administration_client (GValue* value, gpointer v_object);
+gpointer value_get_administration_client (const GValue* value);
+GType administration_client_get_type (void);
+AdministrationClient* administration_client_new (const char* address, const char* name, GotProfileCallback GotProfile_cb, void* GotProfile_cb_target);
+AdministrationClient* administration_client_construct (GType object_type, const char* address, const char* name, GotProfileCallback GotProfile_cb, void* GotProfile_cb_target);
+void administration_client_import_profile (AdministrationClient* self);
+void administration_client_report (AdministrationClient* self, const char* date, gint duration, const char* user, const char* board, gint level, gint sublevel, gint status, const char* comment);
+gpointer administration_server_ref (gpointer instance);
+void administration_server_unref (gpointer instance);
+GParamSpec* param_spec_administration_server (const gchar* name, const gchar* nick, const gchar* blurb, GType object_type, GParamFlags flags);
+void value_set_administration_server (GValue* value, gpointer v_object);
+void value_take_administration_server (GValue* value, gpointer v_object);
+gpointer value_get_administration_server (const GValue* value);
+GType administration_server_get_type (void);
+AdministrationServer* administration_server_new (const char* address, GetProfileCallback GetProfile_cb, void* GetProfile_cb_target, ReportCallback Report_cb, void* Report_cb_target);
+AdministrationServer* administration_server_construct (GType object_type, const char* address, GetProfileCallback GetProfile_cb, void* GetProfile_cb_target, ReportCallback Report_cb, void* Report_cb_target);
+void administration_server_update_profile (AdministrationServer* self);
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/gcompris/sugar_share.vala b/src/gcompris/sugar_share.vala
new file mode 100644
index 0000000..2c9a6e0
--- /dev/null
+++ b/src/gcompris/sugar_share.vala
@@ -0,0 +1,186 @@
+/* gcompris - sugar_share.vala
+ *
+ * Copyright (C) 2010, Aleksey Lim
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+private const string _PATH = "/org/gcompris/Administration";
+private const string _IFACE = "org.gcompris.Administration";
+
+public delegate string GetProfileCallback ();
+public delegate void ReportCallback (string date, int duration, string user,
+        string board, int level, int sublevel, int status, string comment);
+public delegate void GotProfileCallback (string? data);
+
+/**
+ * DBus service to interact between Administration teacher activity and
+ * students' GCompris sessions
+ */
+[DBus (name = "org.gcompris.Administration")]
+public class Administration : Object {
+    /**
+     * Teacher's profile was changed
+     *
+     * Used by students to know when new teacher's profile should be imported.
+     */
+    public signal void UpdateProfile ();
+
+    /**
+     * @param profile   teacher's profile
+     */
+    public Administration (GetProfileCallback GetProfile_cb,
+            ReportCallback Report_cb) {
+        _GetProfile_cb = GetProfile_cb;
+        _Report_cb = Report_cb;
+    }
+
+    /**
+     * Return teacher's profile
+     */
+    public string GetProfile () {
+        debug ("GetProfile called on server");
+        return _GetProfile_cb ();
+    }
+
+    /**
+     * Count student's report
+     */
+    public void Report (string date, int duration, string user, string board,
+            int level, int sublevel, int status, string comment) {
+        debug ("Report called on server");
+        _Report_cb (date, duration, user, board, level, sublevel, status,
+                comment);
+    }
+
+    private GetProfileCallback _GetProfile_cb;
+    private ReportCallback _Report_cb;
+}
+
+/**
+ * Highlevel client interface to Administration DBus service
+ */
+public class AdministrationClient {
+    /**
+     * Create an instance
+     *
+     * @param address   DBus connection address
+     * @param name      DBus name of the server
+     */
+    public AdministrationClient (string address, string name,
+            GotProfileCallback GotProfile_cb) {
+        _GotProfile_cb = GotProfile_cb;
+
+        try {
+            _connection = DBus.Bus.open (address);
+        } catch (Error error) {
+            warning ("Cannot open DBus connection: %s", error.message);
+            return;
+        }
+
+        _server = _connection.get_object (name, _PATH, _IFACE);
+        _server.UpdateProfile.connect (_UpdateProfile_cb);
+    }
+
+    /**
+     * Start importing profile from Administration server
+     */
+    public void import_profile () {
+        if (_server == null)
+            return;
+
+        debug ("GetProfile called on client");
+
+        try {
+            _server.GetProfile (_GetProfile_cb);
+        } catch (Error error) {
+            warning ("Cannot get server profile: %s", error.message);
+            _GotProfile_cb (null);
+        }
+    }
+
+    /**
+     * Send report to the server
+     */
+    public void report (string date, int duration, string user, string board,
+            int level, int sublevel, int status, string comment) {
+        if (_server == null)
+            return;
+
+        debug ("Send report");
+
+        _server.Report (date, duration, user, board, level, sublevel, status,
+                comment, _Report_cb);
+    }
+
+    private void _Report_cb (Error error) {
+        if (error != null) {
+            warning ("Cannot send report to server: %s", error.message);
+        }
+    }
+
+    private void _GetProfile_cb (string profile_data, Error error) {
+        if (error != null) {
+            warning ("Cannot get server profile: %s", error.message);
+            _GotProfile_cb (null);
+        } else {
+            _GotProfile_cb (profile_data);
+        }
+    }
+
+    private void _UpdateProfile_cb (dynamic DBus.Object sender) {
+        import_profile ();
+    }
+
+    private dynamic DBus.Object _server;
+    private DBus.Connection _connection;
+    private GotProfileCallback _GotProfile_cb;
+}
+
+/**
+ * Highlevel server object to handle Administration DBus service
+ */
+public class AdministrationServer {
+    /**
+     * Create an instance
+     *
+     * @param address   DBus connection address
+     */
+    public AdministrationServer (string address,
+            GetProfileCallback GetProfile_cb, ReportCallback Report_cb) {
+        try {
+            _connection = DBus.Bus.open (address);
+        } catch (Error error) {
+            warning ("Cannot open DBus connection: %s", error.message);
+            return;
+        }
+        _server = new Administration (GetProfile_cb, Report_cb);
+        _connection.register_object (_PATH, _server);
+    }
+
+    /**
+     * Emit remote UpdateProfile signal to force students update their profiles
+     */
+    public void update_profile () {
+        if (_server == null)
+            return;
+
+        debug ("Force to update students profiles");
+
+        _server.UpdateProfile ();
+    }
+
+    private dynamic Administration _server;
+    private DBus.Connection _connection;
+}
diff --git a/src/gcompris/sugar_srv.c b/src/gcompris/sugar_srv.c
new file mode 100644
index 0000000..0f300ee
--- /dev/null
+++ b/src/gcompris/sugar_srv.c
@@ -0,0 +1,160 @@
+/* gcompris - sugar_srv.c
+ *
+ * Copyright (C) 2010, Aleksey Lim
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <polyol/toolkit.h>
+
+#include "gcompris.h"
+#include "gc_core.h"
+#include "status.h"
+#include "sugar.h"
+#include "sugar_db.h"
+#include "sugar_share.h"
+
+static void construct();
+static void finalize();
+static void scope_changed_cb(SugarConnection*, SugarShareScope, const gchar*);
+static void channel_appeared_cb(SugarConnection*, SugarChannel*);
+static char* get_profile_request_cb(void*);
+static void report_cb (const char*, gint, const char*, const char*, gint, gint,
+        gint, const char*, void*);
+static void offer_failed_cb(SugarConnection*, const gchar *bus_name);
+
+static GKeyFile *export_profile;
+static gint imported_profile = 1;
+static AdministrationServer *server;
+static time_t last_db_mtime;
+
+Peer srv_peer = {
+  construct,
+  finalize,
+  NULL,
+  NULL
+};
+
+gint
+sugar_get_profile_id()
+{
+  return imported_profile;
+}
+
+static void
+construct()
+{
+  export_profile = g_key_file_new();
+
+  SugarConnection *conn = sugar_activity_get_connection(activity);
+
+  g_signal_connect(conn, "scope-changed", G_CALLBACK(scope_changed_cb), NULL);
+  g_signal_connect(conn, "channel-appeared", G_CALLBACK(channel_appeared_cb),
+          NULL);
+  g_signal_connect(conn, "offer-failed", G_CALLBACK(offer_failed_cb), NULL);
+
+  sugar_db_open();
+
+  GKeyFile *profile = g_key_file_new();
+  SugarJobject *jobject = sugar_activity_get_jobject(activity);
+  const char *file_path = sugar_jobject_get_file_path(jobject);
+
+  if (file_path)
+    g_key_file_load_from_file(profile, file_path, 0, NULL);
+  imported_profile = sugar_db_write(profile);
+
+  g_key_file_free(profile);
+}
+
+static void
+finalize()
+{
+  GKeyFile *profile = g_key_file_new();
+  sugar_db_read(profile, imported_profile);
+  sugar_db_read_reports(profile);
+  save_profile(profile);
+  g_key_file_free(profile);
+
+  if (server != NULL)
+    administration_server_unref(server);
+  server = NULL;
+
+  g_key_file_free(export_profile);
+  export_profile = NULL;
+
+  sugar_db_close(imported_profile);
+}
+
+static void
+scope_changed_cb(SugarConnection *conn, SugarShareScope prev_scope,
+        const gchar *error)
+{
+  if (sugar_connection_get_scope(conn) == SUGAR_SHARE_SCOPE_PRIVATE)
+  {
+    if (server != NULL)
+      administration_server_unref(server);
+    server = NULL;
+  }
+  else
+  {
+    sugar_connection_offer_channel(conn, "org.gcompris.Administration.Channel");
+    g_debug ("Sugar channel creation was initiated");
+  }
+}
+
+static void
+channel_appeared_cb(SugarConnection *conn, SugarChannel *channel)
+{
+  const gchar *address = sugar_channel_get_address(channel);
+  server = administration_server_new(address,
+          get_profile_request_cb, NULL, report_cb, NULL);
+
+  g_debug ("Server sugar channel is online");
+}
+
+static char*
+get_profile_request_cb(void* user_data)
+{
+  struct stat st = { };
+  if ((stat(gc_prop_get()->database, &st) == 0 && st.st_mtime != last_db_mtime)
+          || last_db_mtime == 0)
+  {
+    sugar_db_read(export_profile, imported_profile);
+    last_db_mtime = st.st_mtime;
+  }
+
+  return g_key_file_to_data(export_profile, NULL, NULL);
+}
+
+static void
+report_cb (const char *date, gint duration, const char *user, const char *board,
+    gint level, gint sublevel, gint status, const char *comment,
+    void *user_data)
+{
+  sugar_db_write_report(date, duration, user, board, level, sublevel, status,
+          comment);
+}
+
+static void
+offer_failed_cb(SugarConnection *conn, const gchar *bus_name)
+{
+  SugarAlert *alert = SUGAR_ALERT(sugar_notify_alert_new(_("Sharing Error"),
+            _("Cannot initiate sharing session"), "emblem-warning", 7));
+  sugar_alert_bin_push(alert);
+  sugar_connection_set_scope(conn, SUGAR_SHARE_SCOPE_PRIVATE);
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]