Imported Upstream version 1.3.99.5_20131030_SE_05e5911_SYSYNC_69de386
[platform/upstream/syncevolution.git] / src / dbus / server / localed-listener.cpp
1 /*
2  * Copyright (C) 2013 Intel Corporation
3  *
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.
8  *
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.
13  *
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
17  * 02110-1301  USA
18  */
19
20 #include "localed-listener.h"
21 #include <syncevo/BoostHelper.h>
22 #include <syncevo/Logging.h>
23 #include <syncevo/util.h>
24
25 #include <boost/foreach.hpp>
26 #include <boost/algorithm/string/join.hpp>
27 #include <boost/algorithm/string/predicate.hpp>
28 #include <algorithm>
29
30 SE_BEGIN_CXX
31
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";
36
37 /**
38  * Must be a complete list, because we need to know which variables
39  * we have to unset if not set remotely.
40  *
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."
43  */
44 static const char * const LOCALED_ENV_VARS[] = {
45     "LANG",
46     "LC_CTYPE",
47     "LC_NUMERIC",
48     "LC_TIME",
49     "LC_COLLATE",
50     "LC_MONETARY",
51     "LC_MESSAGES",
52     "LC_PAPER",
53     "LC_NAME",
54     "LC_ADDRESS",
55     "LC_TELEPHONE",
56     "LC_MEASUREMENT",
57     "LC_IDENTIFICATION"
58 };
59
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";
63
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 */,
70                                                                  NULL, false, NULL),
71                                LOCALED_PATH,
72                                PROPERTIES_INTERFACE,
73                                LOCALED_DESTINATION),
74     m_propertiesChanged(*this, PROPERTIES_CHANGED_SIGNAL),
75     m_propertiesGet(*this, PROPERTIES_GET)
76 {
77     if (getConnection()) {
78         m_propertiesChanged.activate(boost::bind(&LocaledListener::onPropertiesChange, this, _1, _2, _3));
79     } else {
80         SE_LOG_DEBUG(NULL, "localed: not activating, no connection");
81     }
82 };
83
84
85 boost::shared_ptr<LocaledListener> LocaledListener::create()
86 {
87     static boost::weak_ptr<LocaledListener> singleton;
88     boost::shared_ptr<LocaledListener> self = singleton.lock();
89     if (!self) {
90         self.reset(new LocaledListener());
91         self->m_self = self;
92         singleton = self;
93     }
94     return self;
95 };
96
97 void LocaledListener::onPropertiesChange(const std::string &interface,
98                                          const Properties &properties,
99                                          const Invalidated &invalidated)
100 {
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);
106                 if (locale) {
107                     SE_LOG_DEBUG(NULL, "localed: got new Locale");
108                     processLocaleProperty(*locale, "", false, result);
109                 } else {
110                     SE_LOG_DEBUG(NULL, "localed: got new Locale of invalid type?! Ignore.");
111                 }
112                 return;
113             }
114         }
115         if (std::find(invalidated.begin(),
116                       invalidated.end(),
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,
122                                               _1, _2, false,
123                                               result));
124         }
125         SE_LOG_DEBUG(NULL, "localed: ignoring irrelevant property change");
126     }
127 }
128
129 void LocaledListener::processLocaleProperty(const LocaleVariant &variant,
130                                             const std::string &error,
131                                             bool mustCall,
132                                             const ProcessLocalePropCB_t &result)
133 {
134     SE_LOG_DEBUG(NULL, "localed: got Locale property: %s", error.empty() ? "<<successfully>>" : error.c_str());
135     const LocaleEnv *locale =
136         error.empty() ?
137         boost::get<LocaleEnv>(&variant) :
138         NULL;
139     LocaleEnv current;
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);
144             if (value) {
145                 current.push_back(StringPrintf("%s=%s", name, value));
146             }
147         }
148         locale = &current;
149     }
150     if (locale) {
151         result(*locale);
152     }
153 }
154
155 void LocaledListener::emitLocaleEnv(const LocaleEnv &env)
156 {
157     SE_LOG_DEBUG(NULL, "localed: got environment: %s",
158                  boost::join(env, " ").c_str());
159     m_localeValues(env);
160 }
161
162 void LocaledListener::check(const boost::function<void (const LocaleEnv &env)> &result)
163 {
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));
169     } else {
170         processLocaleProperty(LocaleVariant(), "no D-Bus connection", true, result);
171     }
172 }
173
174 void LocaledListener::setLocale(const LocaleEnv &locale)
175 {
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)) {
185             modified = true;
186             setenv(name, newvalue, true);
187             SE_LOG_DEBUG(NULL, "localed: %s = %s -> %s", name, value ? value : "<none>", newvalue);
188         } else if (value && !newvalue) {
189             modified = true;
190             unsetenv(name);
191             SE_LOG_DEBUG(NULL, "localed: %s = %s -> <none>", name, value);
192         }
193     }
194     SE_LOG_DEBUG(NULL, "localed: environment %s", modified ? "changed" : "unchanged");
195     if (modified) {
196         m_localeChanged();
197     }
198 }
199
200 SE_END_CXX