Lomiri
System.cpp
1 /*
2  * Copyright (C) 2018 The UBports project
3  * Copyright (C) 2014-2016 Canonical Ltd.
4  *
5  * This program is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 3, as published
7  * by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranties of
11  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12  * PURPOSE. See the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "System.h"
19 
20 #include <QDBusPendingCall>
21 #include <QDBusMessage>
22 #include <QDBusConnection>
23 #include <QDBusMetaType>
24 #include <QDir>
25 #include <QFile>
26 #include <QLocale>
27 #include <QMap>
28 #include <QProcess>
29 #include <QDebug>
30 #include <QSettings>
31 #include <QStringBuilder>
32 
33 System::System()
34  : QObject()
35 {
36  // Register the argument needed for UpdateActivationEnvironment below
37  qDBusRegisterMetaType<QMap<QString,QString>>();
38 
39  if(!wizardEnabled()) {
40  m_fsWatcher.addPath(wizardEnabledPath());
41  }
42  connect(&m_fsWatcher, &QFileSystemWatcher::fileChanged, this, &System::watcherFileChanged);
43 }
44 
45 QString System::wizardEnabledPath()
46 {
47  return QDir::home().filePath(QStringLiteral(".config/lomiri/wizard-has-run"));
48 }
49 
50 QString System::currentFrameworkPath()
51 {
52  QFileInfo f("/usr/share/click/frameworks/current");
53  return f.canonicalFilePath();
54 }
55 
56 /*
57 wizardEnabled and isUpdate logic
58 
59 if wizard-has-run does NOT exist == is new install
60 if wizard-has-run exists but does NOT match current framework == is update
61 if wizard-has-run exists but does match current framework == show no wizard
62 */
63 
64 bool System::wizardPathExists() {
65  return QFile::exists(wizardEnabledPath());
66 }
67 
68 bool System::wizardEnabled() const
69 {
70  if (!wizardPathExists()) {
71  return true;
72  }
73  return isUpdate();
74 }
75 
76 QString System::readCurrentFramework()
77 {
78  QFile f(currentFrameworkPath());
79  if (!f.open(QFile::ReadOnly | QFile::Text)) return "";
80  QTextStream in(&f);
81  return in.readAll();
82 }
83 
84 QString System::readWizardEnabled()
85 {
86  QFile f(wizardEnabledPath());
87  if (!f.open(QFile::ReadOnly | QFile::Text)) return "";
88  QTextStream in(&f);
89  return in.readAll();
90 }
91 
92 QString System::version() const
93 {
94  return readCurrentFramework();
95 }
96 
97 bool System::isUpdate() const
98 {
99  if (!wizardPathExists()) {
100  return false;
101  }
102 
103  return readCurrentFramework() != readWizardEnabled();
104 }
105 
106 void System::setWizardEnabled(bool enabled)
107 {
108  if (wizardEnabled() == enabled && !isUpdate())
109  return;
110 
111  if (enabled) {
112  QFile::remove(wizardEnabledPath());
113  } else {
114  QDir(wizardEnabledPath()).mkpath(QStringLiteral(".."));
115  if (QFile::exists(wizardEnabledPath())) {
116  QFile::remove(wizardEnabledPath());
117  }
118  // For special cases check if wizardEnabledPath is a folder
119  if (QDir(wizardEnabledPath()).exists()) {
120  QDir(wizardEnabledPath()).removeRecursively();
121  }
122  if (!QFile::copy(currentFrameworkPath(), wizardEnabledPath())) {
123  // Make en empty file if framework does not exist
124  QFile f(wizardEnabledPath());
125  f.open(QFile::WriteOnly);
126  }
127  m_fsWatcher.addPath(wizardEnabledPath());
128  Q_EMIT wizardEnabledChanged();
129  Q_EMIT isUpdateChanged();
130  }
131 }
132 
133 void System::watcherFileChanged()
134 {
135  Q_EMIT wizardEnabledChanged();
136  Q_EMIT isUpdateChanged();
137  m_fsWatcher.removePath(wizardEnabledPath());
138 }
139 
140 void System::setSessionVariable(const QString &variable, const QString &value)
141 {
142  // We need to update both systemd's and DBus's environment
143  QStringList vars = { variable % QChar('=') % value };
144  QDBusMessage systemdMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"),
145  QStringLiteral("/org/freedesktop/systemd1"),
146  QStringLiteral("org.freedesktop.systemd1.Manager"),
147  QStringLiteral("SetEnvironment"));
148  systemdMsg << QVariant::fromValue(vars);
149  QDBusConnection::sessionBus().asyncCall(systemdMsg);
150 
151  QMap<QString,QString> valueMap;
152  valueMap.insert(variable, value);
153 
154  QDBusMessage dbusMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"),
155  QStringLiteral("/org/freedesktop/DBus"),
156  QStringLiteral("org.freedesktop.DBus"),
157  QStringLiteral("UpdateActivationEnvironment"));
158 
159  dbusMsg << QVariant::fromValue(valueMap);
160  QDBusConnection::sessionBus().asyncCall(dbusMsg);
161 }
162 
163 void System::updateSessionLocale(const QString &locale)
164 {
165  const QString language = locale.split(QStringLiteral("."))[0];
166 
167  setSessionVariable(QStringLiteral("LANGUAGE"), language);
168  setSessionVariable(QStringLiteral("LANG"), locale);
169  setSessionVariable(QStringLiteral("LC_ALL"), locale);
170 
171  // QLocale caches the default locale on startup, and Qt uses that cached
172  // copy when formatting dates. So manually update it here.
173  QLocale::setDefault(QLocale(locale));
174 
175  // Restart bits of the session to pick up new language.
176  // FIXME not implemented
177 }
178 
179 void System::skipUntilFinishedPage()
180 {
181  QSettings settings;
182  settings.setValue(QStringLiteral("Wizard/SkipUntilFinishedPage"), true);
183  settings.sync();
184 }