[evolution-data-server] Allow getting system timezone information in libecal



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]