2 * Copyright (C) 2013 Intel Corporation
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 #include "localed-listener.h"
21 #include <syncevo/BoostHelper.h>
22 #include <syncevo/Logging.h>
23 #include <syncevo/util.h>
25 #include <boost/foreach.hpp>
26 #include <boost/algorithm/string/join.hpp>
27 #include <boost/algorithm/string/predicate.hpp>
32 static const char LOCALED_PATH[] = "/org/freedesktop/locale1";
33 static const char LOCALED_INTERFACE[] = "org.freedesktop.locale1";
34 static const char LOCALED_DESTINATION[] = "org.freedesktop.locale1";
35 static const char LOCALED_LOCALE_PROPERTY[] = "Locale";
38 * Must be a complete list, because we need to know which variables
39 * we have to unset if not set remotely.
41 * Localed intentionally does not support LC_ALL. As localed.c says:
42 * "We don't list LC_ALL here on purpose. People should be using LANG instead."
44 static const char * const LOCALED_ENV_VARS[] = {
60 static const char PROPERTIES_INTERFACE[] = "org.freedesktop.DBus.Properties";
61 static const char PROPERTIES_CHANGED_SIGNAL[] = "PropertiesChanged";
62 static const char PROPERTIES_GET[] = "Get";
64 LocaledListener::LocaledListener():
65 GDBusCXX::DBusRemoteObject(!strcmp(getEnv("SYNCEVOLUTION_LOCALED", ""), "none") ?
66 NULL : /* simulate missing localed */
67 GDBusCXX::dbus_get_bus_connection(!strcmp(getEnv("SYNCEVOLUTION_LOCALED", ""), "session") ?
68 "SESSION" : /* use our own localed stub */
69 "SYSTEM" /* use real localed */,
74 m_propertiesChanged(*this, PROPERTIES_CHANGED_SIGNAL),
75 m_propertiesGet(*this, PROPERTIES_GET)
77 if (getConnection()) {
78 m_propertiesChanged.activate(boost::bind(&LocaledListener::onPropertiesChange, this, _1, _2, _3));
80 SE_LOG_DEBUG(NULL, "localed: not activating, no connection");
85 boost::shared_ptr<LocaledListener> LocaledListener::create()
87 static boost::weak_ptr<LocaledListener> singleton;
88 boost::shared_ptr<LocaledListener> self = singleton.lock();
90 self.reset(new LocaledListener());
97 void LocaledListener::onPropertiesChange(const std::string &interface,
98 const Properties &properties,
99 const Invalidated &invalidated)
101 if (interface == LOCALED_INTERFACE) {
102 boost::function<void (const LocaleEnv &env)> result(boost::bind(&LocaledListener::emitLocaleEnv, m_self, _1));
103 BOOST_FOREACH (const Properties::value_type &entry, properties) {
104 if (entry.first == LOCALED_LOCALE_PROPERTY) {
105 const LocaleEnv *locale = boost::get<LocaleEnv>(&entry.second);
107 SE_LOG_DEBUG(NULL, "localed: got new Locale");
108 processLocaleProperty(*locale, "", false, result);
110 SE_LOG_DEBUG(NULL, "localed: got new Locale of invalid type?! Ignore.");
115 if (std::find(invalidated.begin(),
117 LOCALED_LOCALE_PROPERTY) != invalidated.end()) {
118 SE_LOG_DEBUG(NULL, "localed: Locale changed, need to get new value");
119 m_propertiesGet.start(std::string(LOCALED_INTERFACE),
120 std::string(LOCALED_LOCALE_PROPERTY),
121 boost::bind(&LocaledListener::processLocaleProperty, m_self,
125 SE_LOG_DEBUG(NULL, "localed: ignoring irrelevant property change");
129 void LocaledListener::processLocaleProperty(const LocaleVariant &variant,
130 const std::string &error,
132 const ProcessLocalePropCB_t &result)
134 SE_LOG_DEBUG(NULL, "localed: got Locale property: %s", error.empty() ? "<<successfully>>" : error.c_str());
135 const LocaleEnv *locale =
137 boost::get<LocaleEnv>(&variant) :
140 if (!locale && mustCall) {
141 SE_LOG_DEBUG(NULL, "localed: using current environment as fallback");
142 BOOST_FOREACH (const char *name, LOCALED_ENV_VARS) {
143 const char *value = getenv(name);
145 current.push_back(StringPrintf("%s=%s", name, value));
155 void LocaledListener::emitLocaleEnv(const LocaleEnv &env)
157 SE_LOG_DEBUG(NULL, "localed: got environment: %s",
158 boost::join(env, " ").c_str());
162 void LocaledListener::check(const boost::function<void (const LocaleEnv &env)> &result)
164 if (getConnection()) {
165 SE_LOG_DEBUG(NULL, "localed: get current Locale property");
166 m_propertiesGet.start(std::string(LOCALED_INTERFACE),
167 std::string(LOCALED_LOCALE_PROPERTY),
168 boost::bind(&LocaledListener::processLocaleProperty, m_self, _1, _2, true, result));
170 processLocaleProperty(LocaleVariant(), "no D-Bus connection", true, result);
174 void LocaledListener::setLocale(const LocaleEnv &locale)
176 bool modified = false;
177 BOOST_FOREACH (const char *name, LOCALED_ENV_VARS) {
178 const char *value = getenv(name);
179 std::string assignment = StringPrintf("%s=", name);
180 LocaleEnv::const_iterator instance = std::find_if(locale.begin(), locale.end(),
181 boost::bind(boost::starts_with<std::string, std::string>, _1, name));
182 const char *newvalue = instance != locale.end() ? instance->c_str() + assignment.size() : NULL;
183 if ((value && newvalue && strcmp(value, newvalue)) ||
184 (!value && newvalue)) {
186 setenv(name, newvalue, true);
187 SE_LOG_DEBUG(NULL, "localed: %s = %s -> %s", name, value ? value : "<none>", newvalue);
188 } else if (value && !newvalue) {
191 SE_LOG_DEBUG(NULL, "localed: %s = %s -> <none>", name, value);
194 SE_LOG_DEBUG(NULL, "localed: environment %s", modified ? "changed" : "unchanged");