/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
/* Copyright (C) 2005 Carlos Garnacho
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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.
*
* Authors: Carlos Garnacho Parro <carlosg@gnome.org>
*/
#include <dbus/dbus.h>
#include <glib-object.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include "oobs-object.h"
#include "oobs-object-private.h"
#include "oobs-timeconfig.h"
#include "utils.h"
#define TIME_CONFIG_REMOTE_OBJECT "TimeConfig"
#define OOBS_TIME_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), OOBS_TYPE_TIME_CONFIG, OobsTimeConfigPrivate))
typedef struct _OobsTimeConfigPrivate OobsTimeConfigPrivate;
struct _OobsTimeConfigPrivate
{
gboolean time_is_set;
GTimeVal time;
gchar *timezone;
};
static void oobs_time_config_class_init (OobsTimeConfigClass *class);
static void oobs_time_config_init (OobsTimeConfig *config);
static void oobs_time_config_finalize (GObject *object);
static void oobs_time_config_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void oobs_time_config_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void oobs_time_config_update (OobsObject *object);
static void oobs_time_config_commit (OobsObject *object);
enum
{
PROP_0,
PROP_UNIX_TIME,
PROP_TIMEZONE
};
G_DEFINE_TYPE (OobsTimeConfig, oobs_time_config, OOBS_TYPE_OBJECT);
static void
oobs_time_config_class_init (OobsTimeConfigClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
OobsObjectClass *oobs_object_class = OOBS_OBJECT_CLASS (class);
object_class->set_property = oobs_time_config_set_property;
object_class->get_property = oobs_time_config_get_property;
object_class->finalize = oobs_time_config_finalize;
oobs_object_class->commit = oobs_time_config_commit;
oobs_object_class->update = oobs_time_config_update;
g_object_class_install_property (object_class,
PROP_UNIX_TIME,
g_param_spec_long ("unix-time",
"Unix time",
"Current unix time for the computer",
0,
G_MAXLONG,
0,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_TIMEZONE,
g_param_spec_string ("timezone",
"Timezone",
"Timezone for the computer",
NULL,
G_PARAM_READWRITE));
g_type_class_add_private (object_class,
sizeof (OobsTimeConfigPrivate));
}
static void
oobs_time_config_init (OobsTimeConfig *config)
{
OobsTimeConfigPrivate *priv;
g_return_if_fail (OOBS_IS_TIME_CONFIG (config));
priv = OOBS_TIME_CONFIG_GET_PRIVATE (config);
priv->time_is_set = FALSE;
config->_priv = priv;
}
static void
oobs_time_config_finalize (GObject *object)
{
OobsTimeConfigPrivate *priv;
g_return_if_fail (OOBS_IS_TIME_CONFIG (object));
priv = OOBS_TIME_CONFIG (object)->_priv;
if (priv && priv->timezone)
g_free (priv->timezone);
if (G_OBJECT_CLASS (oobs_time_config_parent_class)->finalize)
(* G_OBJECT_CLASS (oobs_time_config_parent_class)->finalize) (object);
}
static void
oobs_time_config_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
OobsTimeConfigPrivate *priv;
g_return_if_fail (OOBS_IS_TIME_CONFIG (object));
priv = OOBS_TIME_CONFIG (object)->_priv;
switch (prop_id)
{
case PROP_UNIX_TIME:
priv->time.tv_sec = g_value_get_long (value);
priv->time.tv_usec = 0;
priv->time_is_set = TRUE;
break;
case PROP_TIMEZONE:
g_free (priv->timezone);
priv->timezone = g_value_dup_string (value);
break;
}
}
static void
oobs_time_config_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
OobsTimeConfigPrivate *priv;
GTimeVal tv;
g_return_if_fail (OOBS_IS_TIME_CONFIG (object));
priv = OOBS_TIME_CONFIG (object)->_priv;
switch (prop_id)
{
case PROP_UNIX_TIME:
if (!priv->time_is_set)
{
/* return the current time */
/* FIXME: using local information instead
of backend data, this avoids latency, but
won't work when remote configuration is
possible */
g_get_current_time (&tv);
}
else
{
/* once set, the time configuration will
stop getting the current time */
tv.tv_sec = priv->time.tv_sec;
tv.tv_usec = priv->time.tv_usec;
}
g_value_set_long (value, tv.tv_sec);
break;
case PROP_TIMEZONE:
g_value_set_string (value, priv->timezone);
break;
}
}
static void
oobs_time_config_update (OobsObject *object)
{
OobsTimeConfigPrivate *priv;
DBusMessage *reply;
DBusMessageIter iter;
const gchar *timezone;
priv = OOBS_TIME_CONFIG (object)->_priv;
reply = _oobs_object_get_dbus_message (object);
/* First of all, free the previous config */
g_free (priv->timezone);
/* FIXME: skip time & date settings, at the moment
* we rely on the local configuration, this has
* to change when we allow remote configuration
*/
dbus_message_iter_init (reply, &iter);
dbus_message_iter_next (&iter);
dbus_message_iter_next (&iter);
dbus_message_iter_next (&iter);
dbus_message_iter_next (&iter);
dbus_message_iter_next (&iter);
dbus_message_iter_next (&iter);
timezone = utils_get_string (&iter);
priv->timezone = (timezone) ? g_strdup (timezone) : NULL;
}
static void
oobs_time_config_commit (OobsObject *object)
{
OobsTimeConfigPrivate *priv;
DBusMessage *message;
DBusMessageIter iter;
gint year, month, day, hour, minute, second;
priv = OOBS_TIME_CONFIG (object)->_priv;
message = _oobs_object_get_dbus_message (object);
oobs_time_config_get_utc_time (OOBS_TIME_CONFIG (object),
&year, &month, &day,
&hour, &minute, &second);
dbus_message_iter_init_append (message, &iter);
dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &year);
dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &month);
dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &day);
dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &hour);
dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &minute);
dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &second);
utils_append_string (&iter, priv->timezone);
/* we've just synchronized with the system
* date, so we don't need this anymore
*/
priv->time_is_set = FALSE;
}
/**
* oobs_time_config_get:
* @session: An #OobsSession.
*
* Returns the #OobsTimeConfig singleton, this object
* represents the date and time configuration.
*
* Return Value: the singleton #OobsTimeConfig.
**/
OobsObject*
oobs_time_config_get (OobsSession *session)
{
static OobsObject *object = NULL;
g_return_val_if_fail (OOBS_IS_SESSION (session), NULL);
if (!object)
{
object = g_object_new (OOBS_TYPE_TIME_CONFIG,
"remote-object", TIME_CONFIG_REMOTE_OBJECT,
"session", session,
NULL);
oobs_object_update (object);
}
return object;
}
static gboolean
is_leap_year (gint year)
{
return ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
}
static gboolean
date_is_sane (gint year,
gint month,
gint day,
gint hour,
gint minute,
gint second)
{
static const gint month_lengths[2][12] = {
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
gint month_days;
gboolean is_leap;
g_return_val_if_fail ((year >= 1), FALSE);
g_return_val_if_fail ((month >= 0 && month <= 11), FALSE);
is_leap = is_leap_year (year);
month_days = month_lengths [is_leap][month];
g_return_val_if_fail ((day >= 1 && day <= month_days), FALSE);
g_return_val_if_fail ((hour >= 0 && hour <= 23), FALSE);
g_return_val_if_fail ((minute >= 0 && minute <= 59), FALSE);
g_return_val_if_fail ((second >= 0 && second <= 59), FALSE);
return TRUE;
}
/**
* oobs_time_config_get_unix_time:
* @config: An #OobsTimeConfig.
*
* Returns the time, measured in seconds, since the "epoch" (1970-01-01T00:00:00Z).
*
* Return Value: The unix time of the system.
**/
glong
oobs_time_config_get_unix_time (OobsTimeConfig *config)
{
glong unix_time;
g_return_val_if_fail (OOBS_IS_TIME_CONFIG (config), 0);
g_object_get (G_OBJECT (config), "unix-time", &unix_time, NULL);
return unix_time;
}
/**
* oobs_time_config_set_unix_time:
* @config: An #OobsTimeConfig.
* @unix_time: new Unix time.
*
* This function sets the #config time to be #unix_time. #unix_time
* is measured in seconds, since the "epoch" (1970-01-01T00:00:00Z).
**/
void
oobs_time_config_set_unix_time (OobsTimeConfig *config, glong unix_time)
{
g_return_if_fail (OOBS_IS_TIME_CONFIG (config));
g_object_set (G_OBJECT (config), "unix-time", unix_time, NULL);
}
static void
real_get_time (OobsTimeConfig *config,
gboolean use_utc,
gint *year,
gint *month,
gint *day,
gint *hour,
gint *minute,
gint *second)
{
glong unix_time;
struct tm *tm;
g_return_if_fail (OOBS_IS_TIME_CONFIG (config));
unix_time = oobs_time_config_get_unix_time (config);
if (use_utc)
tm = gmtime (&unix_time);
else
tm = localtime (&unix_time);
if (year)
*year = tm->tm_year + 1900;
if (month)
*month = tm->tm_mon;
if (day)
*day = tm->tm_mday;
if (hour)
*hour = tm->tm_hour;
if (minute)
*minute = tm->tm_min;
if (second)
*second = tm->tm_sec;
}
/**
* oobs_time_config_get_time:
* @config: An #OobsTimeConfig.
* @year: gint pointer to store the year, or #NULL.
* @month: gint pointer to store the month, or #NULL.
* @day: gint pointer to store the day, or #NULL.
* @hour: gint pointer to store the hour, or #NULL.
* @minute: gint pointer to store the minute, or #NULL.
* @second: gint pointer to store the second, or #NULL.
*
* Gets the system time and date in human readable values.
**/
void
oobs_time_config_get_time (OobsTimeConfig *config,
gint *year,
gint *month,
gint *day,
gint *hour,
gint *minute,
gint *second)
{
real_get_time (config, FALSE,
year, month, day,
hour, minute, second);
}
/**
* oobs_time_config_get_utc_time:
* @config: An #OobsTimeConfig.
* @year: gint pointer to store the year, or #NULL.
* @month: gint pointer to store the month, or #NULL.
* @day: gint pointer to store the day, or #NULL.
* @hour: gint pointer to store the hour, or #NULL.
* @minute: gint pointer to store the minute, or #NULL.
* @second: gint pointer to store the second, or #NULL.
*
* Gets the system time and date in human readable values (UTC).
**/
void
oobs_time_config_get_utc_time (OobsTimeConfig *config,
gint *year,
gint *month,
gint *day,
gint *hour,
gint *minute,
gint *second)
{
real_get_time (config, TRUE,
year, month, day,
hour, minute, second);
}
static glong
get_utc_unix_time (struct tm *tm)
{
gchar *tz;
glong unix_time;
tz = getenv ("TZ");
setenv ("TZ", "", 1);
tzset ();
unix_time = mktime (tm);
if (tz)
setenv ("TZ", tz, 1);
else
unsetenv ("TZ");
tzset ();
return unix_time;
}
static void
real_set_time (OobsTimeConfig *config,
gboolean use_utc,
gint year,
gint month,
gint day,
gint hour,
gint minute,
gint second)
{
struct tm tm = { 0, };
glong unix_time;
g_return_if_fail (OOBS_IS_TIME_CONFIG (config));
g_return_if_fail (date_is_sane (year, month, day, hour, minute, second));
tm.tm_year = year - 1900;
tm.tm_mon = month;
tm.tm_mday = day;
tm.tm_hour = hour;
tm.tm_min = minute;
tm.tm_sec = second;
tm.tm_isdst = -1;
if (use_utc)
unix_time = get_utc_unix_time (&tm);
else
unix_time = mktime (&tm);
oobs_time_config_set_unix_time (config, unix_time);
}
/**
* oobs_time_config_set_time:
* @config: An #OobsTimeConfig.
* @year: year.
* @month: month.
* @day: day.
* @hour: hour.
* @minute: minute.
* @second: second.
*
* Sets the time and date of #config to be the specified in the parameters.
**/
void
oobs_time_config_set_time (OobsTimeConfig *config,
gint year,
gint month,
gint day,
gint hour,
gint minute,
gint second)
{
real_set_time (config, FALSE,
year, month, day,
hour, minute, second);
}
/**
* oobs_time_config_set_utc_time:
* @config: An #OobsTimeConfig.
* @year: year.
* @month: month.
* @day: day.
* @hour: hour.
* @minute: minute.
* @second: second.
*
* Sets the time and date of @config to be the specified in the parameters (assuming they're in UTC).
**/
void
oobs_time_config_set_utc_time (OobsTimeConfig *config,
gint year,
gint month,
gint day,
gint hour,
gint minute,
gint second)
{
real_set_time (config, TRUE,
year, month, day,
hour, minute, second);
}
/**
* oobs_time_config_get_timezone:
* @config: An #OobsTimeConfig.
*
* Returns the timezone set for #config.
*
* Return Value: A pointer to the timezone as a string or #NULL.
* This string must not be freed, modified or stored.
**/
G_CONST_RETURN gchar*
oobs_time_config_get_timezone (OobsTimeConfig *config)
{
OobsTimeConfigPrivate *priv;
g_return_val_if_fail (OOBS_IS_TIME_CONFIG (config), NULL);
priv = config->_priv;
return priv->timezone;
}
/**
* oobs_time_config_set_timezone:
* @config: An #OobsTimeConfig.
* @timezone: A new timezone for #config.
*
* Sets the timezone of #config to be #timezone,
* overwriting the previous one.
**/
void
oobs_time_config_set_timezone (OobsTimeConfig *config,
const gchar *timezone)
{
g_return_if_fail (OOBS_IS_TIME_CONFIG (config));
g_object_set (G_OBJECT (config), "timezone", timezone, NULL);
}
syntax highlighted by Code2HTML, v. 0.9.1