201 lines
7.8 KiB
C++
201 lines
7.8 KiB
C++
/*
|
|
* Copyright (C) 2013 Intel Corporation
|
|
*
|
|
* This library 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.1 of the License, or (at your option) version 3.
|
|
*
|
|
* This library 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 library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
#include "localed-listener.h"
|
|
#include <syncevo/BoostHelper.h>
|
|
#include <syncevo/Logging.h>
|
|
#include <syncevo/util.h>
|
|
|
|
#include <boost/foreach.hpp>
|
|
#include <boost/algorithm/string/join.hpp>
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <algorithm>
|
|
|
|
SE_BEGIN_CXX
|
|
|
|
static const char LOCALED_PATH[] = "/org/freedesktop/locale1";
|
|
static const char LOCALED_INTERFACE[] = "org.freedesktop.locale1";
|
|
static const char LOCALED_DESTINATION[] = "org.freedesktop.locale1";
|
|
static const char LOCALED_LOCALE_PROPERTY[] = "Locale";
|
|
|
|
/**
|
|
* Must be a complete list, because we need to know which variables
|
|
* we have to unset if not set remotely.
|
|
*
|
|
* Localed intentionally does not support LC_ALL. As localed.c says:
|
|
* "We don't list LC_ALL here on purpose. People should be using LANG instead."
|
|
*/
|
|
static const char * const LOCALED_ENV_VARS[] = {
|
|
"LANG",
|
|
"LC_CTYPE",
|
|
"LC_NUMERIC",
|
|
"LC_TIME",
|
|
"LC_COLLATE",
|
|
"LC_MONETARY",
|
|
"LC_MESSAGES",
|
|
"LC_PAPER",
|
|
"LC_NAME",
|
|
"LC_ADDRESS",
|
|
"LC_TELEPHONE",
|
|
"LC_MEASUREMENT",
|
|
"LC_IDENTIFICATION"
|
|
};
|
|
|
|
static const char PROPERTIES_INTERFACE[] = "org.freedesktop.DBus.Properties";
|
|
static const char PROPERTIES_CHANGED_SIGNAL[] = "PropertiesChanged";
|
|
static const char PROPERTIES_GET[] = "Get";
|
|
|
|
LocaledListener::LocaledListener():
|
|
GDBusCXX::DBusRemoteObject(!strcmp(getEnv("SYNCEVOLUTION_LOCALED", ""), "none") ?
|
|
NULL : /* simulate missing localed */
|
|
GDBusCXX::dbus_get_bus_connection(!strcmp(getEnv("SYNCEVOLUTION_LOCALED", ""), "session") ?
|
|
"SESSION" : /* use our own localed stub */
|
|
"SYSTEM" /* use real localed */,
|
|
NULL, false, NULL),
|
|
LOCALED_PATH,
|
|
PROPERTIES_INTERFACE,
|
|
LOCALED_DESTINATION),
|
|
m_propertiesChanged(*this, PROPERTIES_CHANGED_SIGNAL),
|
|
m_propertiesGet(*this, PROPERTIES_GET)
|
|
{
|
|
if (getConnection()) {
|
|
m_propertiesChanged.activate(boost::bind(&LocaledListener::onPropertiesChange, this, _1, _2, _3));
|
|
} else {
|
|
SE_LOG_DEBUG(NULL, "localed: not activating, no connection");
|
|
}
|
|
};
|
|
|
|
|
|
boost::shared_ptr<LocaledListener> LocaledListener::create()
|
|
{
|
|
static boost::weak_ptr<LocaledListener> singleton;
|
|
boost::shared_ptr<LocaledListener> self = singleton.lock();
|
|
if (!self) {
|
|
self.reset(new LocaledListener());
|
|
self->m_self = self;
|
|
singleton = self;
|
|
}
|
|
return self;
|
|
};
|
|
|
|
void LocaledListener::onPropertiesChange(const std::string &interface,
|
|
const Properties &properties,
|
|
const Invalidated &invalidated)
|
|
{
|
|
if (interface == LOCALED_INTERFACE) {
|
|
boost::function<void (const LocaleEnv &env)> result(boost::bind(&LocaledListener::emitLocaleEnv, m_self, _1));
|
|
BOOST_FOREACH (const Properties::value_type &entry, properties) {
|
|
if (entry.first == LOCALED_LOCALE_PROPERTY) {
|
|
const LocaleEnv *locale = boost::get<LocaleEnv>(&entry.second);
|
|
if (locale) {
|
|
SE_LOG_DEBUG(NULL, "localed: got new Locale");
|
|
processLocaleProperty(*locale, "", false, result);
|
|
} else {
|
|
SE_LOG_DEBUG(NULL, "localed: got new Locale of invalid type?! Ignore.");
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (std::find(invalidated.begin(),
|
|
invalidated.end(),
|
|
LOCALED_LOCALE_PROPERTY) != invalidated.end()) {
|
|
SE_LOG_DEBUG(NULL, "localed: Locale changed, need to get new value");
|
|
m_propertiesGet.start(std::string(LOCALED_INTERFACE),
|
|
std::string(LOCALED_LOCALE_PROPERTY),
|
|
boost::bind(&LocaledListener::processLocaleProperty, m_self,
|
|
_1, _2, false,
|
|
result));
|
|
}
|
|
SE_LOG_DEBUG(NULL, "localed: ignoring irrelevant property change");
|
|
}
|
|
}
|
|
|
|
void LocaledListener::processLocaleProperty(const LocaleVariant &variant,
|
|
const std::string &error,
|
|
bool mustCall,
|
|
const ProcessLocalePropCB_t &result)
|
|
{
|
|
SE_LOG_DEBUG(NULL, "localed: got Locale property: %s", error.empty() ? "<<successfully>>" : error.c_str());
|
|
const LocaleEnv *locale =
|
|
error.empty() ?
|
|
boost::get<LocaleEnv>(&variant) :
|
|
NULL;
|
|
LocaleEnv current;
|
|
if (!locale && mustCall) {
|
|
SE_LOG_DEBUG(NULL, "localed: using current environment as fallback");
|
|
BOOST_FOREACH (const char *name, LOCALED_ENV_VARS) {
|
|
const char *value = getenv(name);
|
|
if (value) {
|
|
current.push_back(StringPrintf("%s=%s", name, value));
|
|
}
|
|
}
|
|
locale = ¤t;
|
|
}
|
|
if (locale) {
|
|
result(*locale);
|
|
}
|
|
}
|
|
|
|
void LocaledListener::emitLocaleEnv(const LocaleEnv &env)
|
|
{
|
|
SE_LOG_DEBUG(NULL, "localed: got environment: %s",
|
|
boost::join(env, " ").c_str());
|
|
m_localeValues(env);
|
|
}
|
|
|
|
void LocaledListener::check(const boost::function<void (const LocaleEnv &env)> &result)
|
|
{
|
|
if (getConnection()) {
|
|
SE_LOG_DEBUG(NULL, "localed: get current Locale property");
|
|
m_propertiesGet.start(std::string(LOCALED_INTERFACE),
|
|
std::string(LOCALED_LOCALE_PROPERTY),
|
|
boost::bind(&LocaledListener::processLocaleProperty, m_self, _1, _2, true, result));
|
|
} else {
|
|
processLocaleProperty(LocaleVariant(), "no D-Bus connection", true, result);
|
|
}
|
|
}
|
|
|
|
void LocaledListener::setLocale(const LocaleEnv &locale)
|
|
{
|
|
bool modified = false;
|
|
BOOST_FOREACH (const char *name, LOCALED_ENV_VARS) {
|
|
const char *value = getenv(name);
|
|
std::string assignment = StringPrintf("%s=", name);
|
|
LocaleEnv::const_iterator instance = std::find_if(locale.begin(), locale.end(),
|
|
boost::bind(boost::starts_with<std::string, std::string>, _1, name));
|
|
const char *newvalue = instance != locale.end() ? instance->c_str() + assignment.size() : NULL;
|
|
if ((value && newvalue && strcmp(value, newvalue)) ||
|
|
(!value && newvalue)) {
|
|
modified = true;
|
|
setenv(name, newvalue, true);
|
|
SE_LOG_DEBUG(NULL, "localed: %s = %s -> %s", name, value ? value : "<none>", newvalue);
|
|
} else if (value && !newvalue) {
|
|
modified = true;
|
|
unsetenv(name);
|
|
SE_LOG_DEBUG(NULL, "localed: %s = %s -> <none>", name, value);
|
|
}
|
|
}
|
|
SE_LOG_DEBUG(NULL, "localed: environment %s", modified ? "changed" : "unchanged");
|
|
if (modified) {
|
|
m_localeChanged();
|
|
}
|
|
}
|
|
|
|
SE_END_CXX
|