[evolution-data-server] Allow getting system timezone information in libecal
- From: Milan Crha <mcrha src gnome org>
- To: svn-commits-list gnome org
- Subject: [evolution-data-server] Allow getting system timezone information in libecal
- Date: Fri, 24 Apr 2009 12:39:34 -0400 (EDT)
commit 3ea36b63cda3076fcf15ca8df692a7fb5b3c6bc8
Author: Milan Crha <mcrha redhat com>
Date: Fri Apr 24 18:37:46 2009 +0200
Allow getting system timezone information in libecal
** Part of fix for bug #381132
* libecal/Makefile.am:
* libecal/e-cal-system-timezone.h:
* libecal/e-cal-system-timezone.c:
New files for obtaining system timezone location,
based on gnome-panel's clock-applet system-timezone.c.
* libecal/e-cal-util.h: (e_cal_util_get_system_timezone_location),
(e_cal_util_get_system_timezone):
* libecal/e-cal-util.c: (): (e_cal_util_get_system_timezone_location),
(e_cal_util_get_system_timezone):
New API functions to obtain system timezone.
---
calendar/ChangeLog | 15 +
calendar/libecal/Makefile.am | 2 +
calendar/libecal/e-cal-system-timezone.c | 428 ++++++++++++++++++++++++++++++
calendar/libecal/e-cal-system-timezone.h | 27 ++
calendar/libecal/e-cal-util.c | 35 +++
calendar/libecal/e-cal-util.h | 4 +-
6 files changed, 510 insertions(+), 1 deletions(-)
diff --git a/calendar/ChangeLog b/calendar/ChangeLog
index d11c391..6394d6b 100644
--- a/calendar/ChangeLog
+++ b/calendar/ChangeLog
@@ -1,5 +1,20 @@
2009-04-24 Milan Crha <mcrha redhat com>
+ ** Part of fix for bug #381132
+
+ * libecal/Makefile.am:
+ * libecal/e-cal-system-timezone.h:
+ * libecal/e-cal-system-timezone.c:
+ New files for obtaining system timezone location,
+ based on gnome-panel's clock-applet system-timezone.c.
+ * libecal/e-cal-util.h: (e_cal_util_get_system_timezone_location),
+ (e_cal_util_get_system_timezone):
+ * libecal/e-cal-util.c: (): (e_cal_util_get_system_timezone_location),
+ (e_cal_util_get_system_timezone):
+ New API functions to obtain system timezone.
+
+2009-04-24 Milan Crha <mcrha redhat com>
+
** Part of fix for bug #478692
* backends/caldav/create-account.c: Removed unused file.
diff --git a/calendar/libecal/Makefile.am b/calendar/libecal/Makefile.am
index 9addcf6..8e2255c 100644
--- a/calendar/libecal/Makefile.am
+++ b/calendar/libecal/Makefile.am
@@ -46,6 +46,8 @@ libecal_1_2_la_SOURCES = \
e-cal-recur.c \
e-cal-time-util.c \
e-cal-check-timezones.c \
+ e-cal-system-timezone.c \
+ e-cal-system-timezone.h \
e-cal-util.c \
e-cal-view.c \
e-cal-view-listener.c \
diff --git a/calendar/libecal/e-cal-system-timezone.c b/calendar/libecal/e-cal-system-timezone.c
new file mode 100644
index 0000000..cfee41f
--- /dev/null
+++ b/calendar/libecal/e-cal-system-timezone.c
@@ -0,0 +1,428 @@
+/* Evolution calendar system timezone functions
+ * Based on gnome-panel's clock-applet system-timezone.c file.
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <string.h>
+#include "e-cal-system-timezone.h"
+
+#ifdef HAVE_SOLARIS
+#define SYSTEM_ZONEINFODIR "/usr/share/lib/zoneinfo/tab"
+#else
+#define SYSTEM_ZONEINFODIR "/usr/share/zoneinfo"
+#endif
+
+#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"
+
+#define TZ_MAGIC "TZif"
+
+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;
+}
+
+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;
+}
+
+/* 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;
+}
+
+/* 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");
+}
+
+/* This works for openSUSE */
+static char *
+system_timezone_read_etc_sysconfig_clock_alt (void)
+{
+ return system_timezone_read_key_file (ETC_SYSCONFIG_CLOCK,
+ "TIMEZONE");
+}
+
+/* This works for Solaris/OpenSolaris */
+static char *
+system_timezone_read_etc_TIMEZONE (void)
+{
+ return system_timezone_read_key_file (ETC_TIMEZONE_MAJ,
+ "TZ");
+}
+
+/* This works for Arch Linux */
+static char *
+system_timezone_read_etc_rc_conf (void)
+{
+ return system_timezone_read_key_file (ETC_RC_CONF,
+ "TIMEZONE");
+}
+
+/* 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");
+}
+
+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 NULL;
+}
+
+/**
+ * e_cal_system_timezone_get_location:
+ *
+ * Returns system timezone location string, NULL on an error.
+ * Returned pointer should be freed with g_free().
+ **/
+char *
+e_cal_system_timezone_get_location (void)
+{
+ return system_timezone_find ();
+}
diff --git a/calendar/libecal/e-cal-system-timezone.h b/calendar/libecal/e-cal-system-timezone.h
new file mode 100644
index 0000000..ea286a9
--- /dev/null
+++ b/calendar/libecal/e-cal-system-timezone.h
@@ -0,0 +1,27 @@
+/* Evolution calendar system timezone functions
+ * Based on gnome-panel's clock-applet system-timezone.c file.
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef E_CAL_SYSTEM_TIMEZONE_H
+#define E_CAL_SYSTEM_TIMEZONE_H
+
+#include <libical/ical.h>
+
+char *e_cal_system_timezone_get_location (void);
+
+#endif
diff --git a/calendar/libecal/e-cal-util.c b/calendar/libecal/e-cal-util.c
index f4f03c5..a80c1f5 100644
--- a/calendar/libecal/e-cal-util.c
+++ b/calendar/libecal/e-cal-util.c
@@ -26,6 +26,7 @@
#include <glib/gi18n-lib.h>
#include "libedataserver/e-data-server-util.h"
#include "e-cal-util.h"
+#include "e-cal-system-timezone.h"
@@ -1139,3 +1140,37 @@ e_cal_util_remove_instances (icalcomponent *icalcomp,
icalrecur_iterator_free (iter);
}
}
+
+/**
+ * e_cal_util_get_system_timezone_location:
+ *
+ * Returns system timezone location string, NULL on an error.
+ * Returned pointer should be freed with g_free().
+ **/
+char *
+e_cal_util_get_system_timezone_location (void)
+{
+ return e_cal_system_timezone_get_location ();
+}
+
+/**
+ * e_cal_util_get_system_timezone:
+ *
+ * Returns icaltimezone object of the system timezone. NULL on an error.
+ * Returned pointer is part of the built-in timezones, thus do not free it.
+ **/
+icaltimezone *
+e_cal_util_get_system_timezone (void)
+{
+ char *location;
+ icaltimezone *zone;
+
+ location = e_cal_system_timezone_get_location ();
+ g_return_val_if_fail (location != NULL, NULL);
+
+ zone = icaltimezone_get_builtin_timezone (location);
+
+ g_free (location);
+
+ return zone;
+}
diff --git a/calendar/libecal/e-cal-util.h b/calendar/libecal/e-cal-util.h
index a369b59..8bd5ee1 100644
--- a/calendar/libecal/e-cal-util.h
+++ b/calendar/libecal/e-cal-util.h
@@ -135,7 +135,9 @@ void e_cal_util_remove_instances (icalcomponent *icalcomp,
struct icaltimetype rid,
CalObjModType mod);
+char *e_cal_util_get_system_timezone_location (void);
+icaltimezone *e_cal_util_get_system_timezone (void);
+
G_END_DECLS
#endif
-
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]