diff --git a/configure.ac b/configure.ac index e20c425..b467e14 100644 --- a/configure.ac +++ b/configure.ac @@ -90,6 +90,7 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 dnl dbus-glib-1 dnl mojito-client dnl + libglade-2.0 >= 2.5.0 dnl gconf-2.0 gio-unix-2.0) @@ -291,6 +292,49 @@ PLUGIN_LT_LDFLAGS="-version-info $PLUGIN_LT_VERSION" AC_SUBST(PLUGIN_LT_VERSION) AC_SUBST(PLUGIN_LT_LDFLAGS) +#Clock/PolKit +PKG_CHECK_MODULES(CLOCK_MECHANISM, [ gthread-2.0 gio-2.0 dbus-glib-1 ]) +AC_SUBST(CLOCK_MECHANISM_CFLAGS) +AC_SUBST(CLOCK_MECHANISM_LIBS) + + +POLKIT_GNOME_REQUIRED=0.7 +POLKIT_DBUS_REQUIRED=0.7 +DBUS_GLIB_REQUIRED=0.71 +DBUS_REQUIRED=1.1.2 + +# PolicyKit detection; defaults to 'auto' (use it if it's available) +# +POLKIT_GNOME_CFLAGS= +POLKIT_GNOME_LIBS= +POLKIT_DBUS_CFLAGS= +POLKIT_DBUS_LIBS= +AC_ARG_ENABLE(polkit, AS_HELP_STRING([--enable-polkit],[Enable PolicyKit support (auto)]),enable_polkit=$enableval,enable_polkit=auto) +if test "x$enable_polkit" = "xno" ; then + HAVE_POLKIT=no +else + HAVE_POLKIT=no + PKG_CHECK_MODULES(POLKIT_GNOME, polkit-gnome >= $POLKIT_GNOME_REQUIRED dbus-1 >= $DBUS_REQUIRED, HAVE_POLKIT=yes, HAVE_POLKIT=no) + + if test "x$enable_polkit" = "xyes" -a "x$HAVE_POLKIT" = "xno" ; then + AC_MSG_ERROR(PolicyKit support explicity enabled but not available) + fi + + if test "x$HAVE_POLKIT" = "xyes" ; then + AC_DEFINE(HAVE_POLKIT, 1, [Defined if PolicyKit support is enabled]) + PKG_CHECK_MODULES(POLKIT_DBUS, polkit-dbus >= $POLKIT_DBUS_REQUIRED dbus-glib-1 >= $DBUS_GLIB_REQUIRED gobject-2.0) + AC_CHECK_PROG([POLKIT_POLICY_FILE_VALIDATE], + [polkit-policy-file-validate], [polkit-policy-file-validate]) + fi +fi +AM_CONDITIONAL(HAVE_POLKIT, test "x$HAVE_POLKIT" = "xyes") +AC_SUBST(POLKIT_GNOME_CFLAGS) +AC_SUBST(POLKIT_GNOME_LIBS) +AC_SUBST(POLKIT_DBUS_CFLAGS) +AC_SUBST(POLKIT_DBUS_LIBS) + + + SHAVE_INIT([build/autotools], [enable]) AC_OUTPUT([ diff --git a/src/Makefile.am b/src/Makefile.am index f87a92a..061343f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,12 +1,23 @@ SUBDIRS=tray effects +QUIET_GEN = $(Q:@=@echo ' GEN '$@;) + pkglibdir=$(MUTTER_PLUGIN_DIR) AM_CPPFLAGS = \ @MUTTER_PLUGIN_CFLAGS@ \ -DPLUGIN_LOCALEDIR=\"$(localedir)\" \ -DNBTK_CACHE=\"$(MUTTER_MOBLIN_THEME_DIR)/nbtk.cache\" \ - -DTHEMEDIR=\"$(MUTTER_MOBLIN_THEME_DIR)\" + -DTHEMEDIR=\"$(MUTTER_MOBLIN_THEME_DIR)\" \ + -DGLADEDIR=\""$(datadir)/mutter-moblin/glade"\" \ + $(CLOCK_MECHANISM_CFLAGS) \ + $(POLKIT_GNOME_CFLAGS) \ + $(POLKIT_DBUS_CFLAGS) -g -O0 + +gladedir = $(datadir)/mutter-moblin/glade +glade_DATA = \ + clock.glade + moblin_netbook_la_SOURCES = \ mnb-scale-group.c \ @@ -40,13 +51,76 @@ moblin_netbook_la_SOURCES = \ mnb-panel.c \ mnb-panel.h +if HAVE_POLKIT +moblin_netbook_la_SOURCES += set-timezone.c \ + set-timezone.h +endif + +moblin_clock_applet_mechanism_SOURCES = \ + moblin-clock-applet-mechanism.c \ + moblin-clock-applet-mechanism.h \ + moblin-clock-applet-mechanism-main.c \ + system-timezone.c \ + system-timezone.h + +moblin_clock_applet_mechanism_CFLAGS = \ + $(CLOCK_MECHANISM_CFLAGS) \ + $(POLKIT_GNOME_CFLAGS) \ + $(POLKIT_DBUS_CFLAGS) + +moblin_clock_applet_mechanism_LDADD = \ + $(CLOCK_MECHANISM_LIBS) \ + $(POLKIT_GNOME_LIBS) \ + $(POLKIT_DBUS_LIBS) + + +moblin-clock-applet-mechanism-glue.h: $(srcdir)/moblin-clock-applet-mechanism.xml + $(QUIET_GEN)dbus-binding-tool \ + --prefix=moblin_clock_applet_mechanism --mode=glib-server \ + --output=moblin-clock-applet-mechanism-glue.h \ + $(srcdir)/moblin-clock-applet-mechanism.xml + DBUS_GLUE = notification-manager-glue.h \ mnb-panel-dbus-glue.h \ - mnb-toolbar-dbus-glue.h + mnb-toolbar-dbus-glue.h \ + moblin-clock-applet-mechanism-glue.h DBUS_BINDINGS = mnb-panel-dbus-bindings.h \ mnb-toolbar-dbus-bindings.h +if HAVE_POLKIT +libexec_PROGRAMS = moblin-clock-applet-mechanism +endif + +dbus_servicesdir = $(datadir)/dbus-1/system-services +dbus_confdir = $(sysconfdir)/dbus-1/system.d +polkitdir = $(datadir)/PolicyKit/policy + +dbus_services_in_files = org.moblin.ClockApplet.Mechanism.service.in +polkit_in_files = org.moblin.clockapplet.mechanism.policy.in + +if HAVE_POLKIT +dbus_services_DATA = $(dbus_services_in_files:.service.in=.service) + +$(dbus_services_DATA): $(dbus_services_in_files) + $(QUIET_GEN)sed -e "s|\@LIBEXECDIR\@|$(libexecdir)|" $< > $@ + +dbus_conf_DATA = org.moblin.ClockApplet.Mechanism.conf + +@INTLTOOL_POLICY_RULE@ +polkit_DATA = $(polkit_in_files:.policy.in=.policy) + +check: + $(POLKIT_POLICY_FILE_VALIDATE) $(polkit_DATA) + +else +dbus_services_DATA = +dbus_conf_DATA = +polkit_DATA = +endif + + + %-glue.h: %.xml dbus-binding-tool --mode=glib-server --output=$@ --prefix=$(subst -,_,$*) $^ @@ -63,7 +137,10 @@ MARSHALS = marshal.c marshal.h moblin_netbook_la_LDFLAGS = $(PLUGIN_LT_LDFLAGS) -module -no-undefined moblin_netbook_la_LIBADD = @MUTTER_PLUGIN_LIBS@ \ tray/libtray.la \ - effects/libeffects.la + effects/libeffects.la \ + $(POLKIT_GNOME_LIBS) \ + $(POLKIT_DBUS_LIBS) + pkglib_LTLIBRARIES = moblin-netbook.la @@ -76,4 +153,14 @@ install-exec-hook: BUILT_SOURCES = $(DBUS_GLUE) $(DBUS_BINDINGS) $(MARSHALS) -CLEANFILES = $(BUILT_SOURCES) +EXTRA_DIST = glade_DATA \ + $(dbus_services_in_files) \ + org.moblin.ClockApplet.Mechanism.conf \ + $(polkit_in_files) \ + moblin-clock-applet-mechanism.xml + + +CLEANFILES = $(BUILT_SOURCES) \ + $(polkit_DATA) \ + $(dbus_services_DATA) + diff --git a/src/mnb-toolbar.c b/src/mnb-toolbar.c index 81d6c7f..98baef6 100644 --- a/src/mnb-toolbar.c +++ b/src/mnb-toolbar.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include "moblin-netbook.h" @@ -42,6 +44,7 @@ #include "mnb-toolbar-button.h" #include "mnb-drop-down.h" #include "mnb-switcher.h" +#include "set-timezone.h" /* for the launching API; * TODO -- refactor & namespace that @@ -65,6 +68,19 @@ #define MOBLIN_BOOT_COUNT_KEY "/desktop/moblin/myzone/boot_count" +typedef struct _ClockData { + GladeXML *glade_xml; + GtkWidget *set_time_window; + GtkWidget *calendar; + GtkWidget *hours_spin; + GtkWidget *minutes_spin; + GtkWidget *seconds_spin; + GtkWidget *set_time_button; + GtkWidget *cancel_button; + GtkWidget *current_time_label; + MnbToolbar *toolbar; +} ClockData; + #if 0 /* * TODO @@ -187,6 +203,9 @@ struct _MnbToolbarPrivate DBusGProxy *dbus_proxy; GSList *pending_panels; + + /* Clock Data */ + ClockData *cd; }; static void @@ -262,6 +281,10 @@ mnb_toolbar_finalize (GObject *object) MnbToolbarPrivate *priv = MNB_TOOLBAR (object)->priv; GSList *l; + if (priv->cd->set_time_window) + gtk_widget_destroy (priv->cd->set_time_window); + g_free (priv->cd); + l = priv->pending_panels; while (l) { @@ -1611,8 +1634,18 @@ static void mnb_toolbar_init (MnbToolbar *self) { MnbToolbarPrivate *priv; + char *filename; priv = self->priv = MNB_TOOLBAR_GET_PRIVATE (self); + priv->cd = g_new0(ClockData, 1); + priv->cd->toolbar = self; + + filename = g_build_filename (GLADEDIR, "clock.glade", NULL); + priv->cd->glade_xml = glade_xml_new (filename, NULL, NULL); + if (!priv->cd->glade_xml) + g_error ("%s/clock.glade not found; this installation is incorrect.", GLADEDIR); + + g_free (filename); if (g_getenv("MUTTER_DISABLE_PANEL_RESTART")) priv->no_autoloading = TRUE; @@ -1739,6 +1772,210 @@ mnb_toolbar_kbd_grab_notify_cb (MetaScreen *screen, mnb_toolbar_setup_kbd_grabs (toolbar); } + +static void +set_time_callback (ClockData *cd, GError *error) +{ + GtkWidget *window; + GtkWidget *dialog; + + if (error) { + dialog = gtk_message_dialog_new (NULL, + 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Failed to set the system time")); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message); + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + gtk_window_present (GTK_WINDOW (dialog)); + + g_error_free (error); + } + else + mnb_toolbar_update_time_date (cd->toolbar->priv); + + window = glade_xml_get_widget (cd->glade_xml, "set-time-window"); + gtk_widget_hide (window); +} + +static void +set_time (GtkWidget *widget, ClockData *cd) +{ + struct tm t; + time_t tim; + guint year, month, day; + + time (&tim); + /* sets t.isdst -- we could set it to -1 to have mktime() guess the + * right value , but we don't know if this works with all libc */ + localtime_r (&tim, &t); + + t.tm_sec = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (cd->seconds_spin)); + t.tm_min = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (cd->minutes_spin)); + t.tm_hour = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (cd->hours_spin)); + gtk_calendar_get_date (GTK_CALENDAR (cd->calendar), &year, &month, &day); + t.tm_year = year - 1900; + t.tm_mon = month; + t.tm_mday = day; + + tim = mktime (&t); + + set_system_time_async (tim, GDK_WINDOW_XWINDOW (cd->set_time_window->window), (GFunc)set_time_callback, cd, NULL); +} + + +static void +cancel_time_settings (GtkWidget *button, ClockData *cd) +{ + gtk_widget_hide (cd->set_time_window); + + // refresh_clock_timeout (cd); +} + +static gboolean +delete_time_settings (GtkWidget *widget, GdkEvent *event, gpointer data) +{ + cancel_time_settings (widget, data); + + return TRUE; +} + + +static void +wrap_cb (GtkSpinButton *spin, ClockData *cd) +{ + gdouble value; + gdouble min, max; + GtkSpinType direction; + + value = gtk_spin_button_get_value (spin); + gtk_spin_button_get_range (spin, &min, &max); + + if (value == min) + direction = GTK_SPIN_STEP_FORWARD; + else + direction = GTK_SPIN_STEP_BACKWARD; + + if (spin == (GtkSpinButton *) cd->seconds_spin) + gtk_spin_button_spin (GTK_SPIN_BUTTON (cd->minutes_spin), direction, 1.0); + else if (spin == (GtkSpinButton *) cd->minutes_spin) + gtk_spin_button_spin (GTK_SPIN_BUTTON (cd->hours_spin), direction, 1.0); + else { + guint year, month, day; + GDate *date; + + gtk_calendar_get_date (GTK_CALENDAR (cd->calendar), &year, &month, &day); + + date = g_date_new_dmy (day, month + 1, year); + + if (direction == GTK_SPIN_STEP_FORWARD) + g_date_add_days (date, 1); + else + g_date_subtract_days (date, 1); + + year = g_date_get_year (date); + month = g_date_get_month (date) - 1; + day = g_date_get_day (date); + + gtk_calendar_select_month (GTK_CALENDAR (cd->calendar), month, year); + gtk_calendar_select_day (GTK_CALENDAR (cd->calendar), day); + + g_date_free (date); + } +} + +static gboolean +output_cb (GtkSpinButton *spin, + gpointer data) +{ + GtkAdjustment *adj; + gchar *text; + int value; + + adj = gtk_spin_button_get_adjustment (spin); + value = (int) gtk_adjustment_get_value (adj); + text = g_strdup_printf ("%02d", value); + gtk_entry_set_text (GTK_ENTRY (spin), text); + g_free (text); + + return TRUE; +} + + +static void +ensure_time_settings_window_is_created (ClockData *cd) +{ + GtkWidget *cancel_button; + + if (cd->set_time_window) + return; + + cd->set_time_window = glade_xml_get_widget (cd->glade_xml, "set-time-window"); + gtk_window_set_position (cd->set_time_window, GTK_WIN_POS_CENTER); + g_signal_connect (cd->set_time_window, "delete_event", + G_CALLBACK (delete_time_settings), cd); + + cd->calendar = glade_xml_get_widget (cd->glade_xml, "calendar"); + cd->hours_spin = glade_xml_get_widget (cd->glade_xml, "hours_spin"); + cd->minutes_spin = glade_xml_get_widget (cd->glade_xml, "minutes_spin"); + cd->seconds_spin = glade_xml_get_widget (cd->glade_xml, "seconds_spin"); + gtk_entry_set_width_chars (GTK_ENTRY (cd->hours_spin), 2); + gtk_entry_set_width_chars (GTK_ENTRY (cd->minutes_spin), 2); + gtk_entry_set_width_chars (GTK_ENTRY (cd->seconds_spin), 2); + gtk_entry_set_alignment (GTK_ENTRY (cd->hours_spin), 1.0); + gtk_entry_set_alignment (GTK_ENTRY (cd->minutes_spin), 1.0); + gtk_entry_set_alignment (GTK_ENTRY (cd->seconds_spin), 1.0); + g_signal_connect (cd->seconds_spin, "wrapped", G_CALLBACK (wrap_cb), cd); + g_signal_connect (cd->minutes_spin, "wrapped", G_CALLBACK (wrap_cb), cd); + g_signal_connect (cd->hours_spin, "wrapped", G_CALLBACK (wrap_cb), cd); + + g_signal_connect (cd->minutes_spin, "output", G_CALLBACK (output_cb), cd); + g_signal_connect (cd->seconds_spin, "output", G_CALLBACK (output_cb), cd); + + cd->set_time_button = glade_xml_get_widget (cd->glade_xml, "set-time-button"); + g_signal_connect (cd->set_time_button, "clicked", G_CALLBACK (set_time), cd); + + cancel_button = glade_xml_get_widget (cd->glade_xml, "cancel-set-time-button"); + g_signal_connect (cancel_button, "clicked", G_CALLBACK (cancel_time_settings), cd); + + cd->current_time_label = glade_xml_get_widget (cd->glade_xml, "current_time_label"); +} + +static void +fill_time_settings_window (ClockData *cd) +{ + time_t now_t; + struct tm now; + + /* Fill the time settings */ + tzset (); + time (&now_t); + localtime_r (&now_t, &now); + + gtk_spin_button_set_value (GTK_SPIN_BUTTON (cd->seconds_spin), now.tm_sec); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (cd->minutes_spin), now.tm_min); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (cd->hours_spin), now.tm_hour); + + gtk_calendar_select_month (GTK_CALENDAR (cd->calendar), now.tm_mon, + now.tm_year + 1900); + gtk_calendar_select_day (GTK_CALENDAR (cd->calendar), now.tm_mday); +} + + +static void +date_clicked_cb (NbtkButton *button, MnbToolbar *mnb) +{ + ClockData *cd = mnb->priv->cd; + + ensure_time_settings_window_is_created (cd); + gtk_widget_show (cd->set_time_window); + + fill_time_settings_window (cd); + mnb_toolbar_hide (mnb); +} + static void mnb_toolbar_make_hint (MnbToolbar *toolbar) { @@ -1804,7 +2041,6 @@ mnb_toolbar_make_hint (MnbToolbar *toolbar) clutter_container_add_actor (CLUTTER_CONTAINER (overlay), bin); clutter_actor_hide (bin); - priv->hint = bin; } @@ -2071,7 +2307,7 @@ mnb_toolbar_constructed (GObject *self) MnbToolbarPrivate *priv = MNB_TOOLBAR (self)->priv; MutterPlugin *plugin = priv->plugin; ClutterActor *actor = CLUTTER_ACTOR (self); - ClutterActor *hbox; + ClutterActor *hbox, *anotherbox, *button; ClutterActor *background, *bg_texture; ClutterActor *lowlight; gint screen_width, screen_height; @@ -2096,6 +2332,13 @@ mnb_toolbar_constructed (GObject *self) } hbox = priv->hbox = clutter_group_new (); + button = (ClutterActor *)mnb_toolbar_button_new (); + nbtk_widget_set_tooltip_text (NBTK_WIDGET (button), _("Change date and time")); + clutter_actor_set_name (CLUTTER_ACTOR (button), "date-button"); + nbtk_button_set_toggle_mode (NBTK_BUTTON (button), FALSE); + + anotherbox = clutter_group_new (); + g_signal_connect (button, "clicked", G_CALLBACK(date_clicked_cb), self); g_object_set (self, "show-on-set-parent", FALSE, @@ -2138,22 +2381,29 @@ mnb_toolbar_constructed (GObject *self) clutter_actor_set_name (CLUTTER_ACTOR (priv->time), "time-label"); priv->date = nbtk_label_new (""); - clutter_actor_set_name (CLUTTER_ACTOR (priv->date), "date-label"); + clutter_actor_set_name (CLUTTER_ACTOR (priv->date), "date-label"); - clutter_container_add (CLUTTER_CONTAINER (hbox), + clutter_container_add (CLUTTER_CONTAINER (anotherbox), CLUTTER_ACTOR (priv->time), CLUTTER_ACTOR (priv->date), NULL); + clutter_container_add_actor (CLUTTER_CONTAINER (button), anotherbox); + clutter_container_add_actor (CLUTTER_CONTAINER (hbox), button); + clutter_actor_set_size (button, 192, BUTTON_HEIGHT); + clutter_actor_set_anchor_point_from_gravity (CLUTTER_ACTOR(button), + CLUTTER_GRAVITY_CENTER); + clutter_actor_set_position (CLUTTER_ACTOR (button), 192 / 2, 32 + 4); + mnb_toolbar_update_time_date (priv); clutter_actor_set_anchor_point_from_gravity (CLUTTER_ACTOR(priv->time), CLUTTER_GRAVITY_CENTER); - clutter_actor_set_position (CLUTTER_ACTOR (priv->time), 192 / 2, 16); + clutter_actor_set_position (CLUTTER_ACTOR (priv->time), 192/4, 16 - 10); clutter_actor_set_anchor_point_from_gravity (CLUTTER_ACTOR(priv->date), CLUTTER_GRAVITY_CENTER); - clutter_actor_set_position (CLUTTER_ACTOR (priv->date), 192 / 2, 48); + clutter_actor_set_position (CLUTTER_ACTOR (priv->date), 192/4, 48 - 10); nbtk_bin_set_alignment (NBTK_BIN (self), NBTK_ALIGN_LEFT, NBTK_ALIGN_TOP); nbtk_bin_set_child (NBTK_BIN (self), hbox); --- /dev/null 2008-12-03 11:26:56.000000000 +0530 +++ mutter-moblin/src/clock.glade 2009-08-12 08:55:03.000000000 +0530 @@ -0,0 +1,1046 @@ + + + + + + 5 + False + dialog + False + + + True + + + True + 3 + 6 + 6 + + + True + 6 + + + True + True + never + never + + + True + none + + + gtk-missing-image + + + + + + + 0 + + + + + True + 5 + 5 + 4 + 6 + 6 + + + True + 0 + <small><i>Type a city, region, or country name and then select a match from the pop-up.</i></small> + True + True + + + 1 + 4 + 1 + 2 + GTK_FILL + + + + + + True + + + + + + 1 + 4 + GTK_FILL + GTK_FILL + + + + + True + + + + + + 1 + 4 + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + 0 + Timezone: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + Location Name: + + + GTK_FILL + + + + + + True + True + + + + 1 + 2 + 3 + 4 + + + + + + True + North +South + + + 2 + 3 + 3 + 4 + GTK_FILL + GTK_FILL + + + + + True + 0 + <i>(optional)</i> + True + + + 3 + 4 + 3 + 4 + GTK_FILL + + + + + + True + 0 + <i>(optional)</i> + True + + + 3 + 4 + 4 + 5 + GTK_FILL + + + + + + True + East +West + + + 2 + 3 + 4 + 5 + GTK_FILL + GTK_FILL + + + + + True + True + + + + 1 + 2 + 4 + 5 + + + + + + True + 0 + Longitude: + + + 4 + 5 + GTK_FILL + + + + + + True + 6 + True + + + True + 0 + Latitude: + + + False + False + 0 + + + + + 3 + 4 + GTK_FILL + + + + + + + + 1 + + + + + 3 + + + + + + 1 + + + + + True + end + + + gtk-cancel + -6 + True + True + True + False + True + + + False + False + 0 + + + + + gtk-ok + -5 + True + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + + 5 + Time & Date + dialog + False + + + True + + + True + 0 + 7 + 6 + 7 + 7 + + + True + vertical + 12 + + + True + True + 2009 + 5 + 3 + + + 0 + + + + + True + 2 + 2 + 12 + 6 + + + True + + + True + True + + 23 0 23 1 12 0 + 1 + True + True + + + False + False + 0 + + + + + True + True + + 59 0 59 1 30 0 + 1 + True + True + + + False + False + 1 + + + + + True + True + + 59 0 59 1 30 0 + 1 + True + True + + + False + False + 2 + + + + + 1 + 2 + 1 + 2 + + + + + + True + 0 + 23:59:59 + + + 1 + 2 + + + + + + True + 0 + Time: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Current Time: + + + GTK_FILL + + + + + + False + False + 1 + + + + + + + False + False + 1 + + + + + True + True + end + + + gtk-cancel + True + True + True + False + True + + + False + False + 0 + + + + + Set System Time + True + True + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + + 5 + Clock Preferences + center + dialog + False + + + True + 2 + + + True + True + 5 + + + True + 12 + 18 + + + True + 6 + + + True + 0 + <b>Clock Options</b> + True + + + False + False + 0 + + + + + True + 12 + + + True + 13 + + + _12 hour format + True + True + False + True + True + + + False + False + 0 + + + + + _24 hour format + True + True + False + True + True + 12hr_radio + + + False + False + 1 + + + + + + + 1 + + + + + False + 0 + + + + + True + 6 + + + True + 0 + <b>Panel Display</b> + True + + + False + False + 0 + + + + + True + 12 + + + True + 6 + + + Show the _date + True + True + False + True + True + + + False + False + 0 + + + + + Show seco_nds + True + True + False + True + True + + + False + False + 1 + + + + + Show _weather + True + True + False + True + True + + + False + False + 2 + + + + + Show _temperature + True + True + False + True + True + + + False + False + 3 + + + + + + + 1 + + + + + 1 + + + + + + + True + General + + + False + tab + + + + + True + 12 + 18 + + + True + 6 + + + True + True + never + never + in + + + True + True + False + True + + + + + 0 + + + + + True + 6 + start + + + gtk-add + True + True + True + False + True + + + False + False + 0 + + + + + gtk-edit + True + True + True + False + True + + + False + False + 1 + + + + + gtk-remove + True + True + True + False + True + + + False + False + 2 + + + + + False + False + 1 + + + + + 0 + + + + + 1 + + + + + True + Locations + + + 1 + False + tab + + + + + True + 12 + 18 + + + True + 6 + + + True + 0 + <b>Display</b> + True + + + False + False + 0 + + + + + True + 12 + + + True + 4 + 2 + 12 + 6 + + + + 1 + 2 + 3 + 4 + GTK_FILL + GTK_FILL + + + + + 0 + _Visibility unit: + True + visibility_combo + + + 3 + 4 + GTK_FILL + + + + + + + 1 + 2 + 2 + 3 + GTK_FILL + GTK_FILL + + + + + 0 + _Pressure unit: + True + pressure_combo + + + 2 + 3 + GTK_FILL + + + + + + True + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + + + 1 + 2 + GTK_FILL + + + + + True + 0 + _Wind speed unit: + True + wind_speed_combo + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Temperature unit: + True + temperature_combo + + + GTK_FILL + + + + + + + + 1 + + + + + 0 + + + + + 2 + + + + + True + Weather + + + 2 + False + tab + + + + + 1 + + + + + True + end + + + gtk-help + -11 + True + True + True + False + True + + + False + False + 0 + + + + + Time _Settings + True + True + True + False + True + + + False + False + 1 + + + + + gtk-close + -7 + True + True + True + True + False + True + + + False + False + 2 + + + + + False + end + 0 + + + + + + --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/moblin-clock-applet-mechanism.c 2009-08-12 09:53:32.000000000 +0530 @@ -0,0 +1,611 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 David Zeuthen + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "system-timezone.h" + +#include "moblin-clock-applet-mechanism.h" +#include "moblin-clock-applet-mechanism-glue.h" + +static gboolean +do_exit (gpointer user_data) +{ + g_debug ("Exiting due to inactivity"); + exit (1); + return FALSE; +} + +static void +reset_killtimer (void) +{ + static guint timer_id = 0; + + if (timer_id > 0) { + g_source_remove (timer_id); + } + g_debug ("Setting killtimer to 30 seconds..."); + timer_id = g_timeout_add (30 * 1000, do_exit, NULL); +} + +struct GnomeClockAppletMechanismPrivate +{ + DBusGConnection *system_bus_connection; + DBusGProxy *system_bus_proxy; + PolKitContext *pol_ctx; +}; + +static void gnome_clock_applet_mechanism_finalize (GObject *object); + +G_DEFINE_TYPE (GnomeClockAppletMechanism, gnome_clock_applet_mechanism, G_TYPE_OBJECT) + +#define GNOME_CLOCK_APPLET_MECHANISM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNOME_CLOCK_APPLET_TYPE_MECHANISM, GnomeClockAppletMechanismPrivate)) + +GQuark +gnome_clock_applet_mechanism_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) { + ret = g_quark_from_static_string ("gnome_clock_applet_mechanism_error"); + } + + return ret; +} + + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +gnome_clock_applet_mechanism_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) + { + static const GEnumValue values[] = + { + ENUM_ENTRY (GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, "GeneralError"), + ENUM_ENTRY (GNOME_CLOCK_APPLET_MECHANISM_ERROR_NOT_PRIVILEGED, "NotPrivileged"), + ENUM_ENTRY (GNOME_CLOCK_APPLET_MECHANISM_ERROR_INVALID_TIMEZONE_FILE, "InvalidTimezoneFile"), + { 0, 0, 0 } + }; + + g_assert (GNOME_CLOCK_APPLET_MECHANISM_NUM_ERRORS == G_N_ELEMENTS (values) - 1); + + etype = g_enum_register_static ("GnomeClockAppletMechanismError", values); + } + + return etype; +} + + +static GObject * +gnome_clock_applet_mechanism_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GnomeClockAppletMechanism *mechanism; + + mechanism = GNOME_CLOCK_APPLET_MECHANISM (G_OBJECT_CLASS (gnome_clock_applet_mechanism_parent_class)->constructor ( + type, + n_construct_properties, + construct_properties)); + + return G_OBJECT (mechanism); +} + +static void +gnome_clock_applet_mechanism_class_init (GnomeClockAppletMechanismClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = gnome_clock_applet_mechanism_constructor; + object_class->finalize = gnome_clock_applet_mechanism_finalize; + + g_type_class_add_private (klass, sizeof (GnomeClockAppletMechanismPrivate)); + + dbus_g_object_type_install_info (GNOME_CLOCK_APPLET_TYPE_MECHANISM, &dbus_glib_moblin_clock_applet_mechanism_object_info); + + dbus_g_error_domain_register (GNOME_CLOCK_APPLET_MECHANISM_ERROR, NULL, GNOME_CLOCK_APPLET_MECHANISM_TYPE_ERROR); + +} + +static void +gnome_clock_applet_mechanism_init (GnomeClockAppletMechanism *mechanism) +{ + mechanism->priv = GNOME_CLOCK_APPLET_MECHANISM_GET_PRIVATE (mechanism); + +} + +static void +gnome_clock_applet_mechanism_finalize (GObject *object) +{ + GnomeClockAppletMechanism *mechanism; + + g_return_if_fail (object != NULL); + g_return_if_fail (GNOME_CLOCK_APPLET_IS_MECHANISM (object)); + + mechanism = GNOME_CLOCK_APPLET_MECHANISM (object); + + g_return_if_fail (mechanism->priv != NULL); + + g_object_unref (mechanism->priv->system_bus_proxy); + + G_OBJECT_CLASS (gnome_clock_applet_mechanism_parent_class)->finalize (object); +} + +static gboolean +pk_io_watch_have_data (GIOChannel *channel, GIOCondition condition, gpointer user_data) +{ + int fd; + PolKitContext *pk_context = user_data; + fd = g_io_channel_unix_get_fd (channel); + polkit_context_io_func (pk_context, fd); + return TRUE; +} + +static int +pk_io_add_watch (PolKitContext *pk_context, int fd) +{ + guint id = 0; + GIOChannel *channel; + channel = g_io_channel_unix_new (fd); + if (channel == NULL) + goto out; + id = g_io_add_watch (channel, G_IO_IN, pk_io_watch_have_data, pk_context); + if (id == 0) { + g_io_channel_unref (channel); + goto out; + } + g_io_channel_unref (channel); +out: + return id; +} + +static void +pk_io_remove_watch (PolKitContext *pk_context, int watch_id) +{ + g_source_remove (watch_id); +} + +static gboolean +register_mechanism (GnomeClockAppletMechanism *mechanism) +{ + GError *error = NULL; + + mechanism->priv->pol_ctx = polkit_context_new (); + polkit_context_set_io_watch_functions (mechanism->priv->pol_ctx, pk_io_add_watch, pk_io_remove_watch); + if (!polkit_context_init (mechanism->priv->pol_ctx, NULL)) { + g_critical ("cannot initialize libpolkit"); + goto error; + } + + error = NULL; + mechanism->priv->system_bus_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (mechanism->priv->system_bus_connection == NULL) { + if (error != NULL) { + g_critical ("error getting system bus: %s", error->message); + g_error_free (error); + } + goto error; + } + + dbus_g_connection_register_g_object (mechanism->priv->system_bus_connection, "/", + G_OBJECT (mechanism)); + + mechanism->priv->system_bus_proxy = dbus_g_proxy_new_for_name (mechanism->priv->system_bus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + + reset_killtimer (); + + return TRUE; + +error: + return FALSE; +} + + +GnomeClockAppletMechanism * +gnome_clock_applet_mechanism_new (void) +{ + GObject *object; + gboolean res; + + object = g_object_new (GNOME_CLOCK_APPLET_TYPE_MECHANISM, NULL); + + res = register_mechanism (GNOME_CLOCK_APPLET_MECHANISM (object)); + if (! res) { + g_object_unref (object); + return NULL; + } + + return GNOME_CLOCK_APPLET_MECHANISM (object); +} + +static gboolean +_check_polkit_for_action (GnomeClockAppletMechanism *mechanism, DBusGMethodInvocation *context, const char *action) +{ + const char *sender; + GError *error; + DBusError dbus_error; + PolKitCaller *pk_caller; + PolKitAction *pk_action; + PolKitResult pk_result; + + error = NULL; + + /* Check that caller is privileged */ + sender = dbus_g_method_get_sender (context); + dbus_error_init (&dbus_error); + pk_caller = polkit_caller_new_from_dbus_name ( + dbus_g_connection_get_connection (mechanism->priv->system_bus_connection), + sender, + &dbus_error); + if (pk_caller == NULL) { + error = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "Error getting information about caller: %s: %s", + dbus_error.name, dbus_error.message); + dbus_error_free (&dbus_error); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + pk_action = polkit_action_new (); + polkit_action_set_action_id (pk_action, action); + pk_result = polkit_context_is_caller_authorized (mechanism->priv->pol_ctx, pk_action, pk_caller, FALSE, NULL); + polkit_caller_unref (pk_caller); + polkit_action_unref (pk_action); + + if (pk_result != POLKIT_RESULT_YES) { + error = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_NOT_PRIVILEGED, + "%s %s <-- (action, result)", + action, + polkit_result_to_string_representation (pk_result)); + dbus_error_free (&dbus_error); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + return TRUE; +} + + +static gboolean +_set_time (GnomeClockAppletMechanism *mechanism, + const struct timeval *tv, + DBusGMethodInvocation *context) +{ + GError *error; + + if (!_check_polkit_for_action (mechanism, context, "org.moblin.clockapplet.mechanism.settime")) + return FALSE; + + if (settimeofday (tv, NULL) != 0) { + error = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "Error calling settimeofday({%lld,%lld}): %s", + (gint64) tv->tv_sec, (gint64) tv->tv_usec, + strerror (errno)); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + if (g_file_test ("/sbin/hwclock", + G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_EXECUTABLE)) { + int exit_status; + if (!g_spawn_command_line_sync ("/sbin/hwclock --systohc", NULL, NULL, &exit_status, &error)) { + GError *error2; + error2 = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "Error spawning /sbin/hwclock: %s", error->message); + g_error_free (error); + dbus_g_method_return_error (context, error2); + g_error_free (error2); + return FALSE; + } + if (WEXITSTATUS (exit_status) != 0) { + error = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "/sbin/hwclock returned %d", exit_status); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + } + + dbus_g_method_return (context); + return TRUE; +} + +static gboolean +_rh_update_etc_sysconfig_clock (DBusGMethodInvocation *context, const char *key, const char *value) +{ + /* On Red Hat / Fedora, the /etc/sysconfig/clock file needs to be kept in sync */ + if (g_file_test ("/etc/sysconfig/clock", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) { + char **lines; + int n; + gboolean replaced; + char *data; + gsize len; + GError *error; + + error = NULL; + + if (!g_file_get_contents ("/etc/sysconfig/clock", &data, &len, &error)) { + GError *error2; + error2 = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "Error reading /etc/sysconfig/clock file: %s", error->message); + g_error_free (error); + dbus_g_method_return_error (context, error2); + g_error_free (error2); + return FALSE; + } + replaced = FALSE; + lines = g_strsplit (data, "\n", 0); + g_free (data); + + for (n = 0; lines[n] != NULL; n++) { + if (g_str_has_prefix (lines[n], key)) { + g_free (lines[n]); + lines[n] = g_strdup_printf ("%s%s", key, value); + replaced = TRUE; + } + } + if (replaced) { + GString *str; + + str = g_string_new (NULL); + for (n = 0; lines[n] != NULL; n++) { + g_string_append (str, lines[n]); + if (lines[n + 1] != NULL) + g_string_append_c (str, '\n'); + } + data = g_string_free (str, FALSE); + len = strlen (data); + if (!g_file_set_contents ("/etc/sysconfig/clock", data, len, &error)) { + GError *error2; + error2 = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "Error updating /etc/sysconfig/clock: %s", error->message); + g_error_free (error); + dbus_g_method_return_error (context, error2); + g_error_free (error2); + g_free (data); + return FALSE; + } + g_free (data); + } + g_strfreev (lines); + } + + return TRUE; +} + +/* exported methods */ + +gboolean +moblin_clock_applet_mechanism_set_time (GnomeClockAppletMechanism *mechanism, + gint64 seconds_since_epoch, + DBusGMethodInvocation *context) +{ + struct timeval tv; + + reset_killtimer (); + g_debug ("SetTime(%lld) called", seconds_since_epoch); + + tv.tv_sec = (time_t) seconds_since_epoch; + tv.tv_usec = 0; + return _set_time (mechanism, &tv, context); +} + +gboolean +moblin_clock_applet_mechanism_adjust_time (GnomeClockAppletMechanism *mechanism, + gint64 seconds_to_add, + DBusGMethodInvocation *context) +{ + struct timeval tv; + + reset_killtimer (); + g_debug ("AdjustTime(%lld) called", seconds_to_add); + + if (gettimeofday (&tv, NULL) != 0) { + GError *error; + error = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "Error calling gettimeofday(): %s", strerror (errno)); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + tv.tv_sec += (time_t) seconds_to_add; + return _set_time (mechanism, &tv, context); +} + + +gboolean +moblin_clock_applet_mechanism_set_timezone (GnomeClockAppletMechanism *mechanism, + const char *zone_file, + DBusGMethodInvocation *context) +{ + GError *error; + + reset_killtimer (); + g_debug ("SetTimezone('%s') called", zone_file); + + if (!_check_polkit_for_action (mechanism, context, "org.moblin.clockapplet.mechanism.settimezone")) + return FALSE; + + error = NULL; + + if (!system_timezone_set_from_file (zone_file, &error)) { + GError *error2; + int code; + + if (error->code == SYSTEM_TIMEZONE_ERROR_INVALID_TIMEZONE_FILE) + code = GNOME_CLOCK_APPLET_MECHANISM_ERROR_INVALID_TIMEZONE_FILE; + else + code = GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL; + + error2 = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + code, "%s", error->message); + + g_error_free (error); + + dbus_g_method_return_error (context, error2); + g_error_free (error2); + + return FALSE; + } + + dbus_g_method_return (context); + return TRUE; +} + + + +gboolean +moblin_clock_applet_mechanism_get_hardware_clock_using_utc (GnomeClockAppletMechanism *mechanism, + DBusGMethodInvocation *context) +{ + char **lines; + char *data; + gsize len; + GError *error; + gboolean is_utc; + + error = NULL; + + if (!g_file_get_contents ("/etc/adjtime", &data, &len, &error)) { + GError *error2; + error2 = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "Error reading /etc/adjtime file: %s", error->message); + g_error_free (error); + dbus_g_method_return_error (context, error2); + g_error_free (error2); + return FALSE; + } + + lines = g_strsplit (data, "\n", 0); + g_free (data); + + if (g_strv_length (lines) < 3) { + error = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "Cannot parse /etc/adjtime"); + dbus_g_method_return_error (context, error); + g_error_free (error); + g_strfreev (lines); + return FALSE; + } + + if (strcmp (lines[2], "UTC") == 0) { + is_utc = TRUE; + } else if (strcmp (lines[2], "LOCAL") == 0) { + is_utc = FALSE; + } else { + error = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "Expected UTC or LOCAL at line 3 of /etc/adjtime; found '%s'", lines[2]); + dbus_g_method_return_error (context, error); + g_error_free (error); + g_strfreev (lines); + return FALSE; + } + g_strfreev (lines); + dbus_g_method_return (context, is_utc); + return TRUE; +} + +gboolean +moblin_clock_applet_mechanism_set_hardware_clock_using_utc (GnomeClockAppletMechanism *mechanism, + gboolean using_utc, + DBusGMethodInvocation *context) +{ + GError *error; + + error = NULL; + + if (!_check_polkit_for_action (mechanism, context, "org.moblin.clockapplet.mechanism.configurehwclock")) + return FALSE; + + if (g_file_test ("/sbin/hwclock", + G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_EXECUTABLE)) { + int exit_status; + char *cmd; + cmd = g_strdup_printf ("/sbin/hwclock %s --systohc", using_utc ? "--utc" : "--localtime"); + if (!g_spawn_command_line_sync (cmd, NULL, NULL, &exit_status, &error)) { + GError *error2; + error2 = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "Error spawning /sbin/hwclock: %s", error->message); + g_error_free (error); + dbus_g_method_return_error (context, error2); + g_error_free (error2); + g_free (cmd); + return FALSE; + } + g_free (cmd); + if (WEXITSTATUS (exit_status) != 0) { + error = g_error_new (GNOME_CLOCK_APPLET_MECHANISM_ERROR, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + "/sbin/hwclock returned %d", exit_status); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + if (!_rh_update_etc_sysconfig_clock (context, "UTC=", using_utc ? "true" : "false")) + return FALSE; + + } + dbus_g_method_return (context); + return TRUE; + +} --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/moblin-clock-applet-mechanism.h 2009-08-12 09:52:02.000000000 +0530 @@ -0,0 +1,89 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 David Zeuthen + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef GNOME_CLOCK_APPLET_MECHANISM_H +#define GNOME_CLOCK_APPLET_MECHANISM_H + +#include +#include + +G_BEGIN_DECLS + +#define GNOME_CLOCK_APPLET_TYPE_MECHANISM (gnome_clock_applet_mechanism_get_type ()) +#define GNOME_CLOCK_APPLET_MECHANISM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNOME_CLOCK_APPLET_TYPE_MECHANISM, GnomeClockAppletMechanism)) +#define GNOME_CLOCK_APPLET_MECHANISM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GNOME_CLOCK_APPLET_TYPE_MECHANISM, GnomeClockAppletMechanismClass)) +#define GNOME_CLOCK_APPLET_IS_MECHANISM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNOME_CLOCK_APPLET_TYPE_MECHANISM)) +#define GNOME_CLOCK_APPLET_IS_MECHANISM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GNOME_CLOCK_APPLET_TYPE_MECHANISM)) +#define GNOME_CLOCK_APPLET_MECHANISM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GNOME_CLOCK_APPLET_TYPE_MECHANISM, GnomeClockAppletMechanismClass)) + +typedef struct GnomeClockAppletMechanismPrivate GnomeClockAppletMechanismPrivate; + +typedef struct +{ + GObject parent; + GnomeClockAppletMechanismPrivate *priv; +} GnomeClockAppletMechanism; + +typedef struct +{ + GObjectClass parent_class; +} GnomeClockAppletMechanismClass; + +typedef enum +{ + GNOME_CLOCK_APPLET_MECHANISM_ERROR_GENERAL, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_NOT_PRIVILEGED, + GNOME_CLOCK_APPLET_MECHANISM_ERROR_INVALID_TIMEZONE_FILE, + GNOME_CLOCK_APPLET_MECHANISM_NUM_ERRORS +} GnomeClockAppletMechanismError; + +#define GNOME_CLOCK_APPLET_MECHANISM_ERROR gnome_clock_applet_mechanism_error_quark () + +GType gnome_clock_applet_mechanism_error_get_type (void); +#define GNOME_CLOCK_APPLET_MECHANISM_TYPE_ERROR (gnome_clock_applet_mechanism_error_get_type ()) + + +GQuark gnome_clock_applet_mechanism_error_quark (void); +GType gnome_clock_applet_mechanism_get_type (void); +GnomeClockAppletMechanism *gnome_clock_applet_mechanism_new (void); + +/* exported methods */ +gboolean moblin_clock_applet_mechanism_set_timezone (GnomeClockAppletMechanism *mechanism, + const char *zone_file, + DBusGMethodInvocation *context); + +gboolean moblin_clock_applet_mechanism_set_time (GnomeClockAppletMechanism *mechanism, + gint64 seconds_since_epoch, + DBusGMethodInvocation *context); + +gboolean moblin_clock_applet_mechanism_adjust_time (GnomeClockAppletMechanism *mechanism, + gint64 seconds_to_add, + DBusGMethodInvocation *context); + +gboolean moblin_clock_applet_mechanism_get_hardware_clock_using_utc (GnomeClockAppletMechanism *mechanism, + DBusGMethodInvocation *context); + +gboolean moblin_clock_applet_mechanism_set_hardware_clock_using_utc (GnomeClockAppletMechanism *mechanism, + gboolean using_utc, + DBusGMethodInvocation *context); + +G_END_DECLS + +#endif /* GNOME_CLOCK_APPLET_MECHANISM_H */ --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/moblin-clock-applet-mechanism-main.c 2009-08-12 09:41:54.000000000 +0530 @@ -0,0 +1,171 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 David Zeuthen + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + + +#include "moblin-clock-applet-mechanism.h" + +static DBusGProxy * +get_bus_proxy (DBusGConnection *connection) +{ + DBusGProxy *bus_proxy; + + bus_proxy = dbus_g_proxy_new_for_name (connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + return bus_proxy; +} + +#define BUS_NAME "org.moblin.ClockApplet.Mechanism" + +static gboolean +acquire_name_on_proxy (DBusGProxy *bus_proxy) +{ + GError *error; + guint result; + gboolean res; + gboolean ret; + + ret = FALSE; + + if (bus_proxy == NULL) { + goto out; + } + + error = NULL; + res = dbus_g_proxy_call (bus_proxy, + "RequestName", + &error, + G_TYPE_STRING, BUS_NAME, + G_TYPE_UINT, 0, + G_TYPE_INVALID, + G_TYPE_UINT, &result, + G_TYPE_INVALID); + if (! res) { + if (error != NULL) { + g_warning ("Failed to acquire %s: %s", BUS_NAME, error->message); + g_error_free (error); + } else { + g_warning ("Failed to acquire %s", BUS_NAME); + } + goto out; + } + + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + if (error != NULL) { + g_warning ("Failed to acquire %s: %s", BUS_NAME, error->message); + g_error_free (error); + } else { + g_warning ("Failed to acquire %s", BUS_NAME); + } + goto out; + } + + ret = TRUE; + + out: + return ret; +} + +static DBusGConnection * +get_system_bus (void) +{ + GError *error; + DBusGConnection *bus; + + error = NULL; + bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (bus == NULL) { + g_warning ("Couldn't connect to system bus: %s", error->message); + g_error_free (error); + } + return bus; +} + +int +main (int argc, char **argv) +{ + GMainLoop *loop; + GnomeClockAppletMechanism *mechanism; + DBusGProxy *bus_proxy; + DBusGConnection *connection; + int ret; + + ret = 1; + + if (! g_thread_supported ()) { + g_thread_init (NULL); + } + dbus_g_thread_init (); + g_type_init (); + + connection = get_system_bus (); + if (connection == NULL) { + goto out; + } + + bus_proxy = get_bus_proxy (connection); + if (bus_proxy == NULL) { + g_warning ("Could not construct bus_proxy object; bailing out"); + goto out; + } + + if (!acquire_name_on_proxy (bus_proxy) ) { + g_warning ("Could not acquire name; bailing out"); + goto out; + } + + mechanism = gnome_clock_applet_mechanism_new (); + + if (mechanism == NULL) { + goto out; + } + + loop = g_main_loop_new (NULL, FALSE); + + g_main_loop_run (loop); + + g_object_unref (mechanism); + g_main_loop_unref (loop); + ret = 0; + +out: + return ret; +} --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/moblin-clock-applet-mechanism.xml 2009-08-12 09:41:54.000000000 +0530 @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/org.moblin.ClockApplet.Mechanism.conf 2009-08-12 09:41:54.000000000 +0530 @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/org.moblin.ClockApplet.Mechanism.service.in 2009-08-12 09:41:54.000000000 +0530 @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.moblin.ClockApplet.Mechanism +Exec=@LIBEXECDIR@/moblin-clock-applet-mechanism +User=root --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/org.moblin.clockapplet.mechanism.policy.in 2009-08-12 09:41:54.000000000 +0530 @@ -0,0 +1,38 @@ + + + + + The Moblin Project + http://www.moblin.org/ + gnome-panel-clock + + + <_description>Change system time zone + <_message>Privileges are required to change the system time zone. + + no + auth_self_keep_always + + + + + <_description>Change system time + <_message>Privileges are required to change the system time. + + no + auth_self_keep_always + + + + + <_description>Configure hardware clock + <_message>Privileges are required to configure the hardware clock. + + no + auth_self_keep_always + + + + --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/set-timezone.c 2009-08-12 10:46:30.000000000 +0530 @@ -0,0 +1,437 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 David Zeuthen + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "set-timezone.h" + +#define CACHE_VALIDITY_SEC 2 + +static DBusGConnection * +get_session_bus (void) +{ + GError *error; + static DBusGConnection *bus = NULL; + + if (bus == NULL) { + error = NULL; + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (bus == NULL) { + g_warning ("Couldn't connect to session bus: %s", + error->message); + g_error_free (error); + } + } + + return bus; +} + +static DBusGConnection * +get_system_bus (void) +{ + GError *error; + static DBusGConnection *bus = NULL; + + if (bus == NULL) { + error = NULL; + bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (bus == NULL) { + g_warning ("Couldn't connect to system bus: %s", + error->message); + g_error_free (error); + } + } + + return bus; +} + +static gboolean +pk_io_watch_have_data (GIOChannel *channel, GIOCondition condition, gpointer user_data) +{ + int fd; + PolKitContext *pk_context = user_data; + fd = g_io_channel_unix_get_fd (channel); + polkit_context_io_func (pk_context, fd); + return TRUE; +} + +static int +pk_io_add_watch_fn (PolKitContext *pk_context, int fd) +{ + guint id = 0; + GIOChannel *channel; + channel = g_io_channel_unix_new (fd); + if (channel == NULL) + goto out; + id = g_io_add_watch (channel, G_IO_IN, pk_io_watch_have_data, pk_context); + if (id == 0) { + g_io_channel_unref (channel); + goto out; + } + g_io_channel_unref (channel); +out: + return id; +} + +static void +pk_io_remove_watch_fn (PolKitContext *pk_context, int watch_id) +{ + g_source_remove (watch_id); +} + +static PolKitContext * +get_pk_context (void) +{ + static PolKitContext *pk_context = NULL; + + if (pk_context == NULL) { + pk_context = polkit_context_new (); + polkit_context_set_io_watch_functions (pk_context, + pk_io_add_watch_fn, + pk_io_remove_watch_fn); + if (!polkit_context_init (pk_context, NULL)) { + polkit_context_unref (pk_context); + pk_context = NULL; + } + } + + return pk_context; +} + +static gint +can_do (const gchar *pk_action_id) +{ + DBusConnection *system_bus; + PolKitCaller *pk_caller; + PolKitAction *pk_action; + PolKitResult pk_result; + PolKitContext *pk_context; + DBusError dbus_error; + gint res = 0; + + pk_caller = NULL; + pk_action = NULL; + + system_bus = dbus_g_connection_get_connection (get_system_bus ()); + if (system_bus == NULL) + goto out; + + pk_context = get_pk_context (); + if (pk_context == NULL) + goto out; + + pk_action = polkit_action_new (); + polkit_action_set_action_id (pk_action, pk_action_id); + + dbus_error_init (&dbus_error); + pk_caller = polkit_caller_new_from_pid (system_bus, getpid (), &dbus_error); + if (pk_caller == NULL) { + fprintf (stderr, "cannot get caller from dbus name\n"); + goto out; + } + + pk_result = polkit_context_is_caller_authorized (pk_context, pk_action, pk_caller, FALSE, NULL); + + switch (pk_result) { + case POLKIT_RESULT_UNKNOWN: + case POLKIT_RESULT_NO: + res = 0; + break; + case POLKIT_RESULT_YES: + res = 2; + break; + default: + /* This covers all the POLKIT_RESULT_ONLY_VIA_[SELF|ADMIN]_AUTH_* cases as more of these + * may be added in the future. + */ + res = 1; + break; + } + +out: + if (pk_action != NULL) + polkit_action_unref (pk_action); + if (pk_caller != NULL) + polkit_caller_unref (pk_caller); + + return res; +} + +gint +can_set_system_timezone (void) +{ + static gboolean cache = FALSE; + static time_t last_refreshed = 0; + time_t now; + + time (&now); + if (ABS (now - last_refreshed) > CACHE_VALIDITY_SEC) { + cache = can_do ("org.moblin.clockapplet.mechanism.settimezone"); + last_refreshed = now; + } + + return cache; +} + +gint +can_set_system_time (void) +{ + static gboolean cache = FALSE; + static time_t last_refreshed = 0; + time_t now; + + time (&now); + if (ABS (now - last_refreshed) > CACHE_VALIDITY_SEC) { + cache = can_do ("org.moblin.clockapplet.mechanism.settime"); + last_refreshed = now; + } + + return cache; +} + +typedef struct { + gint ref_count; + gchar *call; + gint64 time; + gchar *filename; + guint transient_parent_xid; + GFunc callback; + gpointer data; + GDestroyNotify notify; +} SetTimeCallbackData; + +static void +free_data (gpointer d) +{ + SetTimeCallbackData *data = d; + + data->ref_count--; + if (data->ref_count == 0) { + if (data->notify) + data->notify (data->data); + g_free (data->filename); + g_free (data); + } +} + +static void set_time_async (SetTimeCallbackData *data); + +static void +auth_notify (DBusGProxy *proxy, + DBusGProxyCall *call, + void *user_data) +{ + SetTimeCallbackData *data = user_data; + GError *error = NULL; + gboolean gained_privilege; + + if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_BOOLEAN, &gained_privilege, G_TYPE_INVALID)) { + if (gained_privilege) + set_time_async (data); + } + else { + if (data->callback) + data->callback (data->data, error); + else + g_error_free (error); + } +} + +static void +do_auth_async (const gchar *action, + const gchar *result, + SetTimeCallbackData *data) +{ + DBusGConnection *bus; + DBusGProxy *proxy; + + g_debug ("helper refused; returned polkit_result='%s' and polkit_action='%s'", + result, action); + + /* Now ask the user for auth... */ + bus = get_session_bus (); + if (bus == NULL) + return; + + proxy = dbus_g_proxy_new_for_name (bus, + "org.gnome.PolicyKit", + "/org/gnome/PolicyKit/Manager", + "org.gnome.PolicyKit.Manager"); + + data->ref_count++; + dbus_g_proxy_begin_call_with_timeout (proxy, + "ShowDialog", + auth_notify, + data, free_data, + INT_MAX, + G_TYPE_STRING, action, + G_TYPE_UINT, data->transient_parent_xid, + G_TYPE_INVALID); +} + +static void +set_time_notify (DBusGProxy *proxy, + DBusGProxyCall *call, + void *user_data) +{ + SetTimeCallbackData *data = user_data; + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) { + if (data->callback) + data->callback (data->data, NULL); + } + else { + if (error->domain == DBUS_GERROR && + error->code == DBUS_GERROR_NO_REPLY) { + /* these errors happen because dbus doesn't + * use monotonic clocks + */ + g_warning ("ignoring no-reply error when setting time"); + g_error_free (error); + if (data->callback) + data->callback (data->data, NULL); + } + else if (dbus_g_error_has_name (error, "org.moblin.ClockApplet.Mechanism.NotPrivileged")) { + gchar **tokens; + + tokens = g_strsplit (error->message, " ", 2); + g_error_free (error); + if (g_strv_length (tokens) == 2) + do_auth_async (tokens[0], tokens[1], data); + else + g_warning ("helper return string malformed"); + g_strfreev (tokens); + } + else { + if (data->callback) + data->callback (data->data, error); + else + g_error_free (error); + } + } +} + +static void +set_time_async (SetTimeCallbackData *data) +{ + DBusGConnection *bus; + DBusGProxy *proxy; + + bus = get_system_bus (); + if (bus == NULL) + return; + + proxy = dbus_g_proxy_new_for_name (bus, + "org.moblin.ClockApplet.Mechanism", + "/", + "org.moblin.ClockApplet.Mechanism"); + + data->ref_count++; + if (strcmp (data->call, "SetTime") == 0) + dbus_g_proxy_begin_call_with_timeout (proxy, + "SetTime", + set_time_notify, + data, free_data, + INT_MAX, + /* parameters: */ + G_TYPE_INT64, data->time, + G_TYPE_INVALID, + /* return values: */ + G_TYPE_INVALID); + else + dbus_g_proxy_begin_call_with_timeout (proxy, + "SetTimezone", + set_time_notify, + data, free_data, + INT_MAX, + /* parameters: */ + G_TYPE_STRING, data->filename, + G_TYPE_INVALID, + /* return values: */ + G_TYPE_INVALID); +} + +void +set_system_time_async (gint64 time, + guint transient_parent_xid, + GFunc callback, + gpointer d, + GDestroyNotify notify) +{ + SetTimeCallbackData *data; + + if (time == -1) + return; + + data = g_new0 (SetTimeCallbackData, 1); + data->ref_count = 1; + data->call = "SetTime"; + data->time = time; + data->filename = NULL; + data->transient_parent_xid = transient_parent_xid; + data->callback = callback; + data->data = d; + data->notify = notify; + + set_time_async (data); + free_data (data); +} + +void +set_system_timezone_async (const gchar *filename, + guint transient_parent_xid, + GFunc callback, + gpointer d, + GDestroyNotify notify) +{ + SetTimeCallbackData *data; + + if (filename == NULL) + return; + + data = g_new0 (SetTimeCallbackData, 1); + data->ref_count = 1; + data->call = "SetTimezone"; + data->time = -1; + data->filename = g_strdup (filename); + data->transient_parent_xid = transient_parent_xid; + data->callback = callback; + data->data = d; + data->notify = notify; + + set_time_async (data); + free_data (data); +} --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/set-timezone.h 2009-08-12 09:41:54.000000000 +0530 @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 David Zeuthen + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef __SET_SYSTEM_TIMEZONE_H__ + +#include +#include + +gint can_set_system_timezone (void); + +gint can_set_system_time (void); + +void set_system_time_async (gint64 time, + guint transient_parent_xid, + GFunc callback, + gpointer data, + GDestroyNotify notify); + +void set_system_timezone_async (const gchar *filename, + guint transient_parent_xid, + GFunc callback, + gpointer data, + GDestroyNotify notify); + +#endif --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/system-timezone.c 2009-08-12 09:41:54.000000000 +0530 @@ -0,0 +1,1108 @@ +/* System timezone handling + * + * Copyright (C) 2008 Novell, Inc. + * + * Authors: Vincent Untz + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Some code is based on previous code in clock-location.c and on code from + * tz.c (shipped with version <= 2.22.0). Those files were under the same + * license, with those authors and copyrights: + * + * clock-location.c: + * ================ + * No header, but most of the work was done (AFAIK) by + * Federico Mena Quintero + * Matthias Clasen + * + * tz.c: + * ==== + * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2004 Sun Microsystems, Inc. + * + * Authors: Hans Petter Jansson + * additional functions by Erwann Chenede + * reworked by Vincent Untz + * + * Largely based on Michael Fulbright's work on Anaconda. + */ + +/* To compile a test program, do: + * $ gcc -DSYSTZ_GET_TEST -Wall -g -O0 -o system-timezone-get `pkg-config --cflags --libs glib-2.0 gobject-2.0 gio-2.0` system-timezone.c + * or: + * $ gcc -DSYSTZ_SET_TEST -Wall -g -O0 -o system-timezone-set `pkg-config --cflags --libs glib-2.0 gobject-2.0 gio-2.0` system-timezone.c + * + * You can then read the system timezone with: + * $ system-timezone-get + * and simulate a set of system timezone (in /tmp/etc/...) with: + * $ system-timezone-set "Europe/Paris" + */ + + +/* FIXME: it'd be nice to filter out the timezones that we might get when + * parsing config files that are not in zone.tab. Note that it's also wrong + * in some cases: eg, in tzdata2008b, Asia/Calcutta got renamed to + * Asia/Kolkata and the old name is not in zone.tab. */ + +#include +#include + +#include +#include +#include + +#include "system-timezone.h" + +/* Files that we look at and that should be monitored */ +#define CHECK_NB 5 +#ifndef SYSTZ_SET_TEST +#define ETC_TIMEZONE "/etc/timezone" +#define ETC_TIMEZONE_MAJ "/etc/TIMEZONE" +#define ETC_RC_CONF "/etc/rc.conf" +#define ETC_SYSCONFIG_CLOCK "/etc/sysconfig/clock" +#define ETC_CONF_D_CLOCK "/etc/conf.d/clock" +#define ETC_LOCALTIME "/etc/localtime" +#else +/* Filenames that will be writable for testing */ +#define TEST_PREFIX "/tmp/systz-test" +#define ETC_TIMEZONE TEST_PREFIX"/etc/timezone" +#define ETC_TIMEZONE_MAJ TEST_PREFIX"/etc/TIMEZONE" +#define ETC_RC_CONF TEST_PREFIX"/etc/rc.conf" +#define ETC_SYSCONFIG_CLOCK TEST_PREFIX"/etc/sysconfig/clock" +#define ETC_CONF_D_CLOCK TEST_PREFIX"/etc/conf.d/clock" +#define ETC_LOCALTIME TEST_PREFIX"/etc/localtime" +#endif /* SYSTZ_SET_TEST */ + +/* The first 4 characters in a timezone file, from tzfile.h */ +#define TZ_MAGIC "TZif" + +static char *files_to_check[CHECK_NB] = { + ETC_TIMEZONE, + ETC_TIMEZONE_MAJ, + ETC_SYSCONFIG_CLOCK, + ETC_CONF_D_CLOCK, + ETC_LOCALTIME +}; + +static GObject *systz_singleton = NULL; + +G_DEFINE_TYPE (SystemTimezone, system_timezone, G_TYPE_OBJECT) + +typedef struct { + char *tz; + char *env_tz; + GFileMonitor *monitors[CHECK_NB]; +} SystemTimezonePrivate; + +enum { + CHANGED, + LAST_SIGNAL +}; + +static guint system_timezone_signals[LAST_SIGNAL] = { 0 }; + +static GObject *system_timezone_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties); +static void system_timezone_finalize (GObject *obj); + +static void system_timezone_monitor_changed (GFileMonitor *handle, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + gpointer user_data); +static char *system_timezone_find (void); + +#define PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SYSTEM_TIMEZONE_TYPE, SystemTimezonePrivate)) + +SystemTimezone * +system_timezone_new (void) +{ + return g_object_new (SYSTEM_TIMEZONE_TYPE, NULL); +} + +const char * +system_timezone_get (SystemTimezone *systz) +{ + SystemTimezonePrivate *priv; + + g_return_val_if_fail (IS_SYSTEM_TIMEZONE (systz), NULL); + + priv = PRIVATE (systz); + return priv->tz; +} + +const char * +system_timezone_get_env (SystemTimezone *systz) +{ + SystemTimezonePrivate *priv; + + g_return_val_if_fail (IS_SYSTEM_TIMEZONE (systz), NULL); + + priv = PRIVATE (systz); + return priv->env_tz; +} + +static void +system_timezone_class_init (SystemTimezoneClass *class) +{ + GObjectClass *g_obj_class = G_OBJECT_CLASS (class); + + g_obj_class->constructor = system_timezone_constructor; + g_obj_class->finalize = system_timezone_finalize; + + system_timezone_signals[CHANGED] = + g_signal_new ("changed", + G_OBJECT_CLASS_TYPE (g_obj_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (SystemTimezoneClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + + g_type_class_add_private (class, sizeof (SystemTimezonePrivate)); +} + +static void +system_timezone_init (SystemTimezone *systz) +{ + int i; + SystemTimezonePrivate *priv = PRIVATE (systz); + + priv->tz = NULL; + priv->env_tz = NULL; + for (i = 0; i < CHECK_NB; i++) + priv->monitors[i] = NULL; +} + +static GObject * +system_timezone_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GObject *obj; + SystemTimezonePrivate *priv; + int i; + + /* This is a singleton, we don't need to have it per-applet */ + if (systz_singleton) + return g_object_ref (systz_singleton); + + obj = G_OBJECT_CLASS (system_timezone_parent_class)->constructor ( + type, + n_construct_properties, + construct_properties); + + priv = PRIVATE (obj); + + priv->tz = system_timezone_find (); + + priv->env_tz = g_strdup (g_getenv ("TZ")); + + for (i = 0; i < CHECK_NB; i++) { + GFile *file; + + file = g_file_new_for_path (files_to_check[i]); + priv->monitors[i] = g_file_monitor_file (file, + G_FILE_MONITOR_NONE, + NULL, NULL); + g_object_unref (file); + + if (priv->monitors[i]) + g_signal_connect (G_OBJECT (priv->monitors[i]), + "changed", + G_CALLBACK (system_timezone_monitor_changed), + obj); + } + + systz_singleton = obj; + + return systz_singleton; +} + +static void +system_timezone_finalize (GObject *obj) +{ + int i; + SystemTimezonePrivate *priv = PRIVATE (obj); + + if (priv->tz) { + g_free (priv->tz); + priv->tz = NULL; + } + + if (priv->env_tz) { + g_free (priv->env_tz); + priv->env_tz = NULL; + } + + for (i = 0; i < CHECK_NB; i++) { + g_object_unref (priv->monitors[i]); + priv->monitors[i] = NULL; + } + + G_OBJECT_CLASS (system_timezone_parent_class)->finalize (obj); + + g_assert (obj == systz_singleton); + + systz_singleton = NULL; +} + +static void +system_timezone_monitor_changed (GFileMonitor *handle, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + gpointer user_data) +{ + SystemTimezonePrivate *priv = PRIVATE (user_data); + char *new_tz; + + if (event != G_FILE_MONITOR_EVENT_CHANGED && + event != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT && + event != G_FILE_MONITOR_EVENT_DELETED && + event != G_FILE_MONITOR_EVENT_CREATED) + return; + + new_tz = system_timezone_find (); + + g_assert (priv->tz != NULL && new_tz != NULL); + + if (strcmp (priv->tz, new_tz) != 0) { + g_free (priv->tz); + priv->tz = new_tz; + + g_signal_emit (G_OBJECT (user_data), + system_timezone_signals[CHANGED], + 0, priv->tz); + } else + g_free (new_tz); +} + + +/* + * Code to deal with the system timezone on all distros. + * There's no dependency on the SystemTimezone GObject here. + * + * Here's what we know: + * + * + /etc/localtime contains the binary data of the timezone. + * It can be a symlink to the actual data file, a hard link to the data + * file, or just a copy. So we can determine the timezone with this + * (reading the symlink, comparing inodes, or comparing content). + * + * + However, most distributions also have the timezone setting + * configured somewhere else. This might be better to read it from there. + * + * Debian/Ubuntu/Gentoo (new): content of /etc/timezone + * Fedora/Mandriva: the ZONE key in /etc/sysconfig/clock + * openSUSE: the TIMEZONE key in /etc/sysconfig/clock + * Solaris/OpenSolaris: the TZ key in /etc/TIMEZONE + * Arch Linux: the TIMEZONE key in /etc/rc.conf + * Gentoo (old): the ZONE key in /etc/conf.d/clock + * + * FIXME: reading the system-tools-backends, it seems there's this too: + * Solaris: the TZ key in /etc/default/init + * /etc/TIMEZONE seems to be a link to /etc/default/init + * + * First, some functions to handle those system config files. + * + */ + +/* This works for Debian and derivatives (including Ubuntu), and new Gentoo */ +static char * +system_timezone_read_etc_timezone (void) +{ + FILE *etc_timezone; + GString *reading; + int c; + + etc_timezone = g_fopen (ETC_TIMEZONE, "r"); + if (!etc_timezone) + return NULL; + + reading = g_string_new (""); + + c = fgetc (etc_timezone); + /* only get the first line, we'll validate the value later */ + while (c != EOF && !g_ascii_isspace (c)) { + reading = g_string_append_c (reading, c); + c = fgetc (etc_timezone); + } + + fclose (etc_timezone); + + if (reading->str && reading->str[0] != '\0') + return g_string_free (reading, FALSE); + else + g_string_free (reading, TRUE); + + return NULL; +} + +static gboolean +system_timezone_write_etc_timezone (const char *tz, + GError **error) +{ + char *content; + GError *our_error; + gboolean retval; + + if (!g_file_test (ETC_TIMEZONE, G_FILE_TEST_IS_REGULAR)) + return TRUE; + + content = g_strdup_printf ("%s\n", tz); + + our_error = NULL; + retval = g_file_set_contents (ETC_TIMEZONE, content, -1, &our_error); + g_free (content); + + if (!retval) { + g_set_error (error, SYSTEM_TIMEZONE_ERROR, + SYSTEM_TIMEZONE_ERROR_GENERAL, + ETC_TIMEZONE" cannot be overwritten: %s", + our_error->message); + g_error_free (our_error); + } + + return retval; +} + + +/* Read a file that looks like a key-file (but there's no need for groups) + * and get the last value for a specific key */ +static char * +system_timezone_read_key_file (const char *filename, + const char *key) +{ + GIOChannel *channel; + char *key_eq; + char *line; + char *retval; + + if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) + return NULL; + + channel = g_io_channel_new_file (filename, "r", NULL); + if (!channel) + return NULL; + + key_eq = g_strdup_printf ("%s=", key); + retval = NULL; + + while (g_io_channel_read_line (channel, &line, NULL, + NULL, NULL) == G_IO_STATUS_NORMAL) { + if (g_str_has_prefix (line, key_eq)) { + char *value; + int len; + + value = line + strlen (key_eq); + g_strstrip (value); + + len = strlen (value); + + if (value[0] == '\"') { + if (value[len - 1] == '\"') { + if (retval) + g_free (retval); + + retval = g_strndup (value + 1, + len - 2); + } + } else { + if (retval) + g_free (retval); + + retval = g_strdup (line + strlen (key_eq)); + } + + g_strstrip (retval); + } + + g_free (line); + } + + g_free (key_eq); + g_io_channel_unref (channel); + + return retval; +} + +static gboolean +system_timezone_write_key_file (const char *filename, + const char *key, + const char *value, + GError **error) +{ + GError *our_error; + char *content; + gsize len; + char *key_eq; + char **lines; + gboolean replaced; + gboolean retval; + int n; + + if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) + return TRUE; + + our_error = NULL; + + if (!g_file_get_contents (filename, &content, &len, &our_error)) { + g_set_error (error, SYSTEM_TIMEZONE_ERROR, + SYSTEM_TIMEZONE_ERROR_GENERAL, + "%s cannot be read: %s", + filename, our_error->message); + g_error_free (our_error); + return FALSE; + } + + lines = g_strsplit (content, "\n", 0); + g_free (content); + + key_eq = g_strdup_printf ("%s=", key); + replaced = FALSE; + + for (n = 0; lines[n] != NULL; n++) { + if (g_str_has_prefix (lines[n], key_eq)) { + char *old_value; + gboolean use_quotes; + + old_value = lines[n] + strlen (key_eq); + g_strstrip (old_value); + use_quotes = old_value[0] == '\"'; + + g_free (lines[n]); + + if (use_quotes) + lines[n] = g_strdup_printf ("%s\"%s\"", + key_eq, value); + else + lines[n] = g_strdup_printf ("%s%s", + key_eq, value); + + replaced = TRUE; + } + } + + g_free (key_eq); + + if (!replaced) { + g_strfreev (lines); + return TRUE; + } + + content = g_strjoinv ("\n", lines); + g_strfreev (lines); + + retval = g_file_set_contents (filename, content, -1, &our_error); + g_free (content); + + if (!retval) { + g_set_error (error, SYSTEM_TIMEZONE_ERROR, + SYSTEM_TIMEZONE_ERROR_GENERAL, + "%s cannot be overwritten: %s", + filename, our_error->message); + g_error_free (our_error); + } + + return retval; +} + +/* This works for Solaris/OpenSolaris */ +static char * +system_timezone_read_etc_TIMEZONE (void) +{ + return system_timezone_read_key_file (ETC_TIMEZONE_MAJ, + "TZ"); +} + +static gboolean +system_timezone_write_etc_TIMEZONE (const char *tz, + GError **error) +{ + return system_timezone_write_key_file (ETC_TIMEZONE_MAJ, + "TZ", tz, error); +} + +/* This works for Fedora and Mandriva */ +static char * +system_timezone_read_etc_sysconfig_clock (void) +{ + return system_timezone_read_key_file (ETC_SYSCONFIG_CLOCK, + "ZONE"); +} + +static gboolean +system_timezone_write_etc_sysconfig_clock (const char *tz, + GError **error) +{ + return system_timezone_write_key_file (ETC_SYSCONFIG_CLOCK, + "ZONE", tz, error); +} + +/* This works for openSUSE */ +static char * +system_timezone_read_etc_sysconfig_clock_alt (void) +{ + return system_timezone_read_key_file (ETC_SYSCONFIG_CLOCK, + "TIMEZONE"); +} + +static gboolean +system_timezone_write_etc_sysconfig_clock_alt (const char *tz, + GError **error) +{ + return system_timezone_write_key_file (ETC_SYSCONFIG_CLOCK, + "TIMEZONE", tz, error); +} + +/* This works for old Gentoo */ +static char * +system_timezone_read_etc_conf_d_clock (void) +{ + return system_timezone_read_key_file (ETC_CONF_D_CLOCK, + "TIMEZONE"); +} + +static gboolean +system_timezone_write_etc_conf_d_clock (const char *tz, + GError **error) +{ + return system_timezone_write_key_file (ETC_CONF_D_CLOCK, + "TIMEZONE", tz, error); +} + +/* This works for Arch Linux */ +static char * +system_timezone_read_etc_rc_conf (void) +{ + return system_timezone_read_key_file (ETC_RC_CONF, + "TIMEZONE"); +} + +static gboolean +system_timezone_write_etc_rc_conf (const char *tz, + GError **error) +{ + return system_timezone_write_key_file (ETC_RC_CONF, + "TIMEZONE", tz, error); +} + +/* + * + * First, getting the timezone. + * + */ + +static char * +system_timezone_strip_path_if_valid (const char *filename) +{ + int skip; + + if (!filename || !g_str_has_prefix (filename, SYSTEM_ZONEINFODIR"/")) + return NULL; + + /* Timezone data files also live under posix/ and right/ for some + * reason. + * FIXME: make sure accepting those files is valid. I think "posix" is + * okay, not sure about "right" */ + if (g_str_has_prefix (filename, SYSTEM_ZONEINFODIR"/posix/")) + skip = strlen (SYSTEM_ZONEINFODIR"/posix/"); + else if (g_str_has_prefix (filename, SYSTEM_ZONEINFODIR"/right/")) + skip = strlen (SYSTEM_ZONEINFODIR"/right/"); + else + skip = strlen (SYSTEM_ZONEINFODIR"/"); + + return g_strdup (filename + skip); +} + +/* Read the soft symlink from /etc/localtime */ +static char * +system_timezone_read_etc_localtime_softlink (void) +{ + char *file; + char *tz; + + if (!g_file_test (ETC_LOCALTIME, G_FILE_TEST_IS_SYMLINK)) + return NULL; + + file = g_file_read_link (ETC_LOCALTIME, NULL); + tz = system_timezone_strip_path_if_valid (file); + g_free (file); + + return tz; +} + +typedef gboolean (*CompareFiles) (struct stat *a_stat, + struct stat *b_stat, + const char *a_content, + gsize a_content_len, + const char *b_filename); + +static char * +recursive_compare (struct stat *localtime_stat, + const char *localtime_content, + gsize localtime_content_len, + char *file, + CompareFiles compare_func) +{ + struct stat file_stat; + + if (g_stat (file, &file_stat) != 0) + return NULL; + + if (S_ISREG (file_stat.st_mode)) { + if (compare_func (localtime_stat, + &file_stat, + localtime_content, + localtime_content_len, + file)) + return system_timezone_strip_path_if_valid (file); + else + return NULL; + } else if (S_ISDIR (file_stat.st_mode)) { + GDir *dir = NULL; + char *ret = NULL; + const char *subfile = NULL; + char *subpath = NULL; + + dir = g_dir_open (file, 0, NULL); + if (dir == NULL) + return NULL; + + while ((subfile = g_dir_read_name (dir)) != NULL) { + subpath = g_build_filename (file, subfile, NULL); + + ret = recursive_compare (localtime_stat, + localtime_content, + localtime_content_len, + subpath, + compare_func); + + g_free (subpath); + + if (ret != NULL) + break; + } + + g_dir_close (dir); + + return ret; + } + + return NULL; +} + + +static gboolean +files_are_identical_inode (struct stat *a_stat, + struct stat *b_stat, + const char *a_content, + gsize a_content_len, + const char *b_filename) +{ + return (a_stat->st_ino == b_stat->st_ino); +} + + +/* Determine if /etc/localtime is a hard link to some file, by looking at + * the inodes */ +static char * +system_timezone_read_etc_localtime_hardlink (void) +{ + struct stat stat_localtime; + + if (g_stat (ETC_LOCALTIME, &stat_localtime) != 0) + return NULL; + + if (!S_ISREG (stat_localtime.st_mode)) + return NULL; + + return recursive_compare (&stat_localtime, + NULL, + 0, + SYSTEM_ZONEINFODIR, + files_are_identical_inode); +} + +static gboolean +files_are_identical_content (struct stat *a_stat, + struct stat *b_stat, + const char *a_content, + gsize a_content_len, + const char *b_filename) +{ + char *b_content = NULL; + gsize b_content_len = -1; + int cmp; + + if (a_stat->st_size != b_stat->st_size) + return FALSE; + + if (!g_file_get_contents (b_filename, + &b_content, &b_content_len, NULL)) + return FALSE; + + if (a_content_len != b_content_len) { + g_free (b_content); + return FALSE; + } + + cmp = memcmp (a_content, b_content, a_content_len); + g_free (b_content); + + return (cmp == 0); +} + +/* Determine if /etc/localtime is a copy of a timezone file */ +static char * +system_timezone_read_etc_localtime_content (void) +{ + struct stat stat_localtime; + char *localtime_content = NULL; + gsize localtime_content_len = -1; + char *retval; + + if (g_stat (ETC_LOCALTIME, &stat_localtime) != 0) + return NULL; + + if (!S_ISREG (stat_localtime.st_mode)) + return NULL; + + if (!g_file_get_contents (ETC_LOCALTIME, + &localtime_content, + &localtime_content_len, + NULL)) + return NULL; + + retval = recursive_compare (&stat_localtime, + localtime_content, + localtime_content_len, + SYSTEM_ZONEINFODIR, + files_are_identical_content); + + g_free (localtime_content); + + return retval; +} + +typedef char * (*GetSystemTimezone) (void); +/* The order of the functions here define the priority of the methods used + * to find the timezone. First method has higher priority. */ +static GetSystemTimezone get_system_timezone_methods[] = { + /* cheap and "more correct" than data from a config file */ + system_timezone_read_etc_localtime_softlink, + /* reading various config files */ + system_timezone_read_etc_timezone, + system_timezone_read_etc_sysconfig_clock, + system_timezone_read_etc_sysconfig_clock_alt, + system_timezone_read_etc_TIMEZONE, + system_timezone_read_etc_rc_conf, + /* reading deprecated config files */ + system_timezone_read_etc_conf_d_clock, + /* reading /etc/timezone directly. Expensive since we have to stat + * many files */ + system_timezone_read_etc_localtime_hardlink, + system_timezone_read_etc_localtime_content, + NULL +}; + +static gboolean +system_timezone_is_valid (const char *tz) +{ + const char *c; + + if (!tz) + return FALSE; + + for (c = tz; *c != '\0'; c++) { + if (!(g_ascii_isalnum (*c) || + *c == '/' || *c == '-' || *c == '_')) + return FALSE; + } + + return TRUE; +} + +static char * +system_timezone_find (void) +{ + char *tz; + int i; + + for (i = 0; get_system_timezone_methods[i] != NULL; i++) { + tz = get_system_timezone_methods[i] (); + + if (system_timezone_is_valid (tz)) + return tz; + + g_free (tz); + } + + return g_strdup ("UTC"); +} + +/* + * + * Now, setting the timezone. + * + */ + +static gboolean +system_timezone_is_zone_file_valid (const char *zone_file, + GError **error) +{ + GError *our_error; + GIOChannel *channel; + GIOStatus status; + char buffer[strlen (TZ_MAGIC)]; + gsize read; + + /* First, check the zone_file is properly rooted */ + if (!g_str_has_prefix (zone_file, SYSTEM_ZONEINFODIR"/")) { + g_set_error (error, SYSTEM_TIMEZONE_ERROR, + SYSTEM_TIMEZONE_ERROR_INVALID_TIMEZONE_FILE, + "Timezone file needs to be under "SYSTEM_ZONEINFODIR); + return FALSE; + } + + /* Second, check it's a regular file that exists */ + if (!g_file_test (zone_file, G_FILE_TEST_IS_REGULAR)) { + g_set_error (error, SYSTEM_TIMEZONE_ERROR, + SYSTEM_TIMEZONE_ERROR_INVALID_TIMEZONE_FILE, + "No such timezone file %s", zone_file); + return FALSE; + } + + /* Third, check that it's a tzfile (see tzfile(5)). The file has a 4 + * bytes header which is TZ_MAGIC. + * + * TODO: is there glibc API for this? */ + our_error = NULL; + channel = g_io_channel_new_file (zone_file, "r", &our_error); + if (!our_error) + status = g_io_channel_read_chars (channel, + buffer, strlen (TZ_MAGIC), + &read, &our_error); + if (channel) + g_io_channel_unref (channel); + + if (our_error) { + g_set_error (error, SYSTEM_TIMEZONE_ERROR, + SYSTEM_TIMEZONE_ERROR_INVALID_TIMEZONE_FILE, + "Timezone file %s cannot be read: %s", + zone_file, our_error->message); + g_error_free (our_error); + return FALSE; + } + + if (read != strlen (TZ_MAGIC) || strncmp (buffer, TZ_MAGIC, strlen (TZ_MAGIC)) != 0) { + g_set_error (error, SYSTEM_TIMEZONE_ERROR, + SYSTEM_TIMEZONE_ERROR_INVALID_TIMEZONE_FILE, + "%s is not a timezone file", + zone_file); + return FALSE; + } + + return TRUE; +} + +static gboolean +system_timezone_set_etc_timezone (const char *zone_file, + GError **error) +{ + GError *our_error; + char *content; + gsize len; + + if (!system_timezone_is_zone_file_valid (zone_file, error)) + return FALSE; + + /* If /etc/localtime is a symlink, write a symlink */ + if (g_file_test (ETC_LOCALTIME, G_FILE_TEST_IS_SYMLINK)) { + if (g_unlink (ETC_LOCALTIME) == 0 && + symlink (zone_file, ETC_LOCALTIME) == 0) + return TRUE; + + /* If we couldn't symlink the file, we'll just fallback on + * copying it */ + } + + /* Else copy the file to /etc/localtime. We explicitly avoid doing + * hard links since they break with different partitions */ + our_error = NULL; + if (!g_file_get_contents (zone_file, &content, &len, &our_error)) { + g_set_error (error, SYSTEM_TIMEZONE_ERROR, + SYSTEM_TIMEZONE_ERROR_GENERAL, + "Timezone file %s cannot be read: %s", + zone_file, our_error->message); + g_error_free (our_error); + return FALSE; + } + + if (!g_file_set_contents (ETC_LOCALTIME, content, len, &our_error)) { + g_set_error (error, SYSTEM_TIMEZONE_ERROR, + SYSTEM_TIMEZONE_ERROR_GENERAL, + ETC_LOCALTIME" cannot be overwritten: %s", + our_error->message); + g_error_free (our_error); + g_free (content); + return FALSE; + } + + g_free (content); + + return TRUE; +} + +typedef gboolean (*SetSystemTimezone) (const char *tz, + GError **error); +/* The order here does not matter too much: we'll try to change all files + * that already have a timezone configured. It matters in case of error, + * since the process will be stopped and the last methods won't be called. + * So we use the same order as in get_system_timezone_methods */ +static SetSystemTimezone set_system_timezone_methods[] = { + /* writing various config files if they exist and have the + * setting already present */ + system_timezone_write_etc_timezone, + system_timezone_write_etc_sysconfig_clock, + system_timezone_write_etc_sysconfig_clock_alt, + system_timezone_write_etc_TIMEZONE, + system_timezone_write_etc_rc_conf, + /* writing deprecated config files if they exist and have the + * setting already present */ + system_timezone_write_etc_conf_d_clock, + NULL +}; + +static gboolean +system_timezone_update_config (const char *tz, + GError **error) +{ + int i; + + for (i = 0; set_system_timezone_methods[i] != NULL; i++) { + if (!set_system_timezone_methods[i] (tz, error)) + return FALSE; + /* FIXME: maybe continue to change all config files if + * possible? */ + } + + return TRUE; +} + +gboolean +system_timezone_set_from_file (const char *zone_file, + GError **error) +{ + const char *tz; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + tz = zone_file + strlen (SYSTEM_ZONEINFODIR"/"); + + /* FIXME: is it right to return FALSE even when /etc/localtime was + * changed but not the config files? */ + return (system_timezone_set_etc_timezone (zone_file, error) && + system_timezone_update_config (tz, error)); +} + +gboolean +system_timezone_set (const char *tz, + GError **error) +{ + char *zone_file; + gboolean retval; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + zone_file = g_build_filename (SYSTEM_ZONEINFODIR, tz, NULL); + + /* FIXME: is it right to return FALSE even when /etc/localtime was + * changed but not the config files? */ + retval = system_timezone_set_etc_timezone (zone_file, error) && + system_timezone_update_config (tz, error); + + g_free (zone_file); + + return retval; +} + +GQuark +system_timezone_error_quark (void) +{ + static GQuark ret = 0; + + if (ret == 0) { + ret = g_quark_from_static_string ("system-timezone-error"); + } + + return ret; +} + +#ifdef SYSTZ_GET_TEST +int +main (int argc, + char **argv) +{ + char *tz; + + tz = system_timezone_find (); + g_print ("%s\n", tz); + g_free (tz); + + return 0; +} +#endif /* SYSTZ_GET_TEST */ + +#ifdef SYSTZ_SET_TEST +int +main (int argc, + char **argv) +{ + GError *error; + char *dirname; + + if (argc != 2) { + g_print ("Usage: %s TIMEZONE\n", argv[0]); + return 1; + } + + dirname = g_path_get_dirname (ETC_TIMEZONE); + g_mkdir_with_parents (dirname, 0755); + g_free (dirname); + dirname = g_path_get_dirname (ETC_SYSCONFIG_CLOCK); + g_mkdir_with_parents (dirname, 0755); + g_free (dirname); + dirname = g_path_get_dirname (ETC_CONF_D_CLOCK); + g_mkdir_with_parents (dirname, 0755); + g_free (dirname); + + error = NULL; + if (!system_timezone_set (argv[1], &error)) { + g_print ("%s\n", error->message); + g_error_free (error); + return 1; + } + + g_print (TEST_PREFIX" is the test directory where files " + "were written (feel free to populate this directory with " + "files that should be modified by this program).\n"); + + return 0; +} +#endif /* SYSTZ_SET_TEST */ --- /dev/null 2009-07-11 18:55:23.000000000 +0530 +++ mutter-moblin-0.32.120090807/src/system-timezone.h 2009-08-12 09:41:54.000000000 +0530 @@ -0,0 +1,83 @@ +/* System timezone handling + * + * Copyright (C) 2008 Novell, Inc. + * + * Authors: Vincent Untz + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __SYSTEM_TIMEZONE_H__ +#define __SYSTEM_TIMEZONE_H__ + +#include +#include + +G_BEGIN_DECLS + +#ifdef HAVE_SOLARIS +#define SYSTEM_ZONEINFODIR "/usr/share/lib/zoneinfo/tab" +#else +#define SYSTEM_ZONEINFODIR "/usr/share/zoneinfo" +#endif + + +#define SYSTEM_TIMEZONE_TYPE (system_timezone_get_type ()) +#define SYSTEM_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SYSTEM_TIMEZONE_TYPE, SystemTimezone)) +#define SYSTEM_TIMEZONE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), SYSTEM_TIMEZONE_TYPE, SystemTimezoneClass)) +#define IS_SYSTEM_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SYSTEM_TIMEZONE_TYPE)) +#define IS_SYSTEM_TIMEZONE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), SYSTEM_TIMEZONE_TYPE)) +#define SYSTEM_TIMEZONE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SYSTEM_TIMEZONE_TYPE, SystemTimezoneClass)) + +typedef struct +{ + GObject g_object; +} SystemTimezone; + +typedef struct +{ + GObjectClass g_object_class; + + void (* changed) (SystemTimezone *systz, + const char *tz); +} SystemTimezoneClass; + +GType system_timezone_get_type (void); + +SystemTimezone *system_timezone_new (void); + +const char *system_timezone_get (SystemTimezone *systz); +const char *system_timezone_get_env (SystemTimezone *systz); + +/* Functions to set the timezone. They won't be used by the applet, but + * by a program with more privileges */ + +#define SYSTEM_TIMEZONE_ERROR system_timezone_error_quark () +GQuark system_timezone_error_quark (void); + +typedef enum +{ + SYSTEM_TIMEZONE_ERROR_GENERAL, + SYSTEM_TIMEZONE_ERROR_INVALID_TIMEZONE_FILE, + SYSTEM_TIMEZONE_NUM_ERRORS +} SystemTimezoneError; + +gboolean system_timezone_set_from_file (const char *zone_file, + GError **error); +gboolean system_timezone_set (const char *tz, + GError **error); + +G_END_DECLS +#endif /* __SYSTEM_TIMEZONE_H__ */ --- mutter-moblin-0.32.120090807/po/de.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/de.po 2009-08-12 17:10:38.000000000 +0530 @@ -407,6 +407,10 @@ msgstr "Test" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "Bewege deinen Cursor zum oberen Bildschirmrand, um die Symbolleiste zu aktivieren." +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "Datum und Uhrzeit _anpassen" + #: ../status/src/mnb-im-status-row.c:103 #: ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742 --- mutter-moblin-0.32.120090807/po/es.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/es.po 2009-08-12 17:23:01.000000000 +0530 @@ -432,6 +432,10 @@ msgstr "prueba" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "Mueve el cursor hacia arriba de la pantalla para ver la barra de herramientas" +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "A_justar fecha y hora" + #: ../status/src/mnb-im-status-row.c:103 #: ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742 --- mutter-moblin-0.32.120090807/po/fi.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/fi.po 2009-08-12 17:11:54.000000000 +0530 @@ -400,6 +400,10 @@ msgstr "testi" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "Siirrä osoitin ruudun yläreunaan työkalurivin aktivoimiseksi." +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "_Aseta päiväys ja aika" + #: ../status/src/mnb-im-status-row.c:103 #: ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742 --- mutter-moblin-0.32.120090807/po/fr.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/fr.po 2009-08-12 17:12:36.000000000 +0530 @@ -402,6 +402,10 @@ msgstr "test" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "Pour activer la barre d'outils, placez votre curseur en haut de l'écran" +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "_Régler la date et l'heure" + #: ../status/src/mnb-im-status-row.c:103 #: ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742 --- mutter-moblin-0.32.120090807/po/hu.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/hu.po 2009-08-12 17:13:16.000000000 +0530 @@ -393,6 +393,10 @@ msgstr "" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "" +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "Dátum és idő _beállítása" + #: ../status/src/mnb-im-status-row.c:97 ../status/src/mnb-im-status-row.c:98 #: ../status/src/mnb-im-status-row.c:641 msgid "Offline" --- mutter-moblin-0.32.120090807/po/it.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/it.po 2009-08-12 17:14:02.000000000 +0530 @@ -327,6 +327,10 @@ msgstr "Nessuno selezionato" msgid "zones" msgstr "zone" +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "Re_gola data e ora" + #: ../src/moblin-netbook.c:415 msgid "Moblin Netbook Effects" msgstr "Effetti Moblin netbook" --- mutter-moblin-0.32.120090807/po/ja.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/ja.po 2009-08-12 17:14:38.000000000 +0530 @@ -397,6 +397,10 @@ msgstr "テスト" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "カーセルを画面の上に動かしてツールバーをアクティブにします" +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "日付と時刻の調整(_J)" + #: ../status/src/mnb-im-status-row.c:103 #: ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742 --- mutter-moblin-0.32.120090807/po/ko.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/ko.po 2009-08-12 17:15:14.000000000 +0530 @@ -397,6 +397,10 @@ msgstr "테스트" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "툴바를 활성화 하려면 컬서를 스크린 위쪽으로 움직이십시오. " +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "날짜 및 시간 조정(_J)" + #: ../status/src/mnb-im-status-row.c:103 #: ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742 --- mutter-moblin-0.32.120090807/po/pl.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/pl.po 2009-08-12 17:15:46.000000000 +0530 @@ -431,6 +431,10 @@ msgstr "test" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "Aby skorzystać z paska narzędzi, dojedź kursorem do górnej krawędzi" +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "Do_stosuj datę i czas" + # "Offline"? Masculine form unfortunately #: ../status/src/mnb-im-status-row.c:103 ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742 --- mutter-moblin-0.32.120090807/po/pt_BR.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/pt_BR.po 2009-08-12 17:17:22.000000000 +0530 @@ -397,6 +397,10 @@ msgstr "teste" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "Para ativar a barra de ferramentas, mova o cursor para cima" +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "A_justar data e hora" + #: ../status/src/mnb-im-status-row.c:103 #: ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742 --- mutter-moblin-0.32.120090807/po/sv.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/sv.po 2009-08-12 17:21:38.000000000 +0530 @@ -397,6 +397,10 @@ msgstr "testa" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "Aktivera listen, flytta din markör till översta delen av skärmen" +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "_Justera datum och tid" + #: ../status/src/mnb-im-status-row.c:103 #: ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742 --- mutter-moblin-0.32.120090807/po/zh_CN.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/zh_CN.po 2009-08-12 17:22:15.000000000 +0530 @@ -403,6 +403,10 @@ msgstr "测试" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "移动鼠标到屏幕顶部可以弹出工具栏" +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "调整日期和时间(_J)" + #: ../status/src/mnb-im-status-row.c:103 ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742 msgid "Offline" --- mutter-moblin-0.32.120090807/po/zh_TW.po 2009-08-12 17:23:48.000000000 +0530 +++ mutter-moblin-0.32.120090807/po/zh_TW.po 2009-08-12 17:22:44.000000000 +0530 @@ -397,6 +397,10 @@ msgstr "測試" msgid "To activate the toolbar, move your cursor to the top of the screen" msgstr "要激活工具欄,移動您的滑鼠到屏面上方" +#: ../src/mnb-toolbar.c:2343 +msgid "Ad_just Date & Time" +msgstr "調整日期及時間(_J)" + #: ../status/src/mnb-im-status-row.c:103 #: ../status/src/mnb-im-status-row.c:104 #: ../status/src/mnb-im-status-row.c:742