1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qsettings.h"
41 #ifndef QT_NO_SETTINGS
42 
43 #include "qsettings_p.h"
44 #ifndef QT_NO_QOBJECT
45 #include "qcoreapplication.h"
46 #include <QFile>
47 #endif // QT_NO_QOBJECT
48 #include <QDebug>
49 
50 #include <QFileInfo>
51 #include <QDir>
52 #include <emscripten.h>
53 
54 QT_BEGIN_NAMESPACE
55 
56 static bool isReadReady = false;
57 
58 class QWasmSettingsPrivate : public QConfFileSettingsPrivate
59 {
60 public:
61     QWasmSettingsPrivate(QSettings::Scope scope, const QString &organization,
62                         const QString &application);
63     ~QWasmSettingsPrivate();
64 
65     bool get(const QString &key, QVariant *value) const override;
66     QStringList children(const QString &prefix, ChildSpec spec) const override;
67     void clear() override;
68     void sync() override;
69     void flush() override;
70     bool isWritable() const override;
71 
72     void syncToLocal(const char *data, int size);
73     void loadLocal(const QByteArray &filename);
74     void setReady();
75     void initAccess() override;
76 
77 private:
78     QString databaseName;
79     QString id;
80 };
81 
QWasmSettingsPrivate_onLoad(void * userData,void * dataPtr,int size)82 static void QWasmSettingsPrivate_onLoad(void *userData, void *dataPtr, int size)
83 {
84     QWasmSettingsPrivate *wasm = reinterpret_cast<QWasmSettingsPrivate *>(userData);
85 
86     QFile file(wasm->fileName());
87     QFileInfo fileInfo(wasm->fileName());
88     QDir dir(fileInfo.path());
89     if (!dir.exists())
90         dir.mkpath(fileInfo.path());
91 
92     if (file.open(QFile::WriteOnly)) {
93         file.write(reinterpret_cast<char *>(dataPtr), size);
94         file.close();
95         wasm->setReady();
96     }
97 }
98 
QWasmSettingsPrivate_onError(void * userData)99 static void QWasmSettingsPrivate_onError(void *userData)
100 {
101     QWasmSettingsPrivate *wasm = reinterpret_cast<QWasmSettingsPrivate *>(userData);
102     if (wasm)
103         wasm->setStatus(QSettings::AccessError);
104 }
105 
QWasmSettingsPrivate_onStore(void * userData)106 static void QWasmSettingsPrivate_onStore(void *userData)
107 {
108     QWasmSettingsPrivate *wasm = reinterpret_cast<QWasmSettingsPrivate *>(userData);
109     if (wasm)
110         wasm->setStatus(QSettings::NoError);
111 }
112 
QWasmSettingsPrivate_onCheck(void * userData,int exists)113 static void QWasmSettingsPrivate_onCheck(void *userData, int exists)
114 {
115     QWasmSettingsPrivate *wasm = reinterpret_cast<QWasmSettingsPrivate *>(userData);
116     if (wasm) {
117         if (exists)
118             wasm->loadLocal(wasm->fileName().toLocal8Bit());
119         else
120             wasm->setReady();
121     }
122 }
123 
create(QSettings::Format format,QSettings::Scope scope,const QString & organization,const QString & application)124 QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format,
125                                            QSettings::Scope scope,
126                                            const QString &organization,
127                                            const QString &application)
128 {
129     Q_UNUSED(format)
130     if (organization == QLatin1String("Qt"))
131     {
132         QString organizationDomain = QCoreApplication::organizationDomain();
133         QString applicationName = QCoreApplication::applicationName();
134 
135         QSettingsPrivate *newSettings;
136         newSettings = new QWasmSettingsPrivate(scope, organizationDomain, applicationName);
137 
138         newSettings->beginGroupOrArray(QSettingsGroup(normalizedKey(organization)));
139         if (!application.isEmpty())
140             newSettings->beginGroupOrArray(QSettingsGroup(normalizedKey(application)));
141 
142         return newSettings;
143     }
144     return new QWasmSettingsPrivate(scope, organization, application);
145 }
146 
QWasmSettingsPrivate(QSettings::Scope scope,const QString & organization,const QString & application)147 QWasmSettingsPrivate::QWasmSettingsPrivate(QSettings::Scope scope, const QString &organization,
148                                            const QString &application)
149     : QConfFileSettingsPrivate(QSettings::NativeFormat, scope, organization, application)
150 {
151     setStatus(QSettings::AccessError); // access error until sandbox gets loaded
152     databaseName = organization;
153     id = application;
154 
155     emscripten_idb_async_exists("/home/web_user",
156                                 fileName().toLocal8Bit(),
157                                 reinterpret_cast<void*>(this),
158                                 QWasmSettingsPrivate_onCheck,
159                                 QWasmSettingsPrivate_onError);
160 }
161 
~QWasmSettingsPrivate()162 QWasmSettingsPrivate::~QWasmSettingsPrivate()
163 {
164 }
165 
initAccess()166  void QWasmSettingsPrivate::initAccess()
167 {
168      if (isReadReady)
169          QConfFileSettingsPrivate::initAccess();
170 }
171 
get(const QString & key,QVariant * value) const172 bool QWasmSettingsPrivate::get(const QString &key, QVariant *value) const
173 {
174     if (isReadReady)
175         return QConfFileSettingsPrivate::get(key, value);
176 
177     return false;
178 }
179 
children(const QString & prefix,ChildSpec spec) const180 QStringList QWasmSettingsPrivate::children(const QString &prefix, ChildSpec spec) const
181 {
182     return QConfFileSettingsPrivate::children(prefix, spec);
183 }
184 
clear()185 void QWasmSettingsPrivate::clear()
186 {
187     QConfFileSettingsPrivate::clear();
188     emscripten_idb_async_delete("/home/web_user",
189                                 fileName().toLocal8Bit(),
190                                 reinterpret_cast<void*>(this),
191                                 QWasmSettingsPrivate_onStore,
192                                 QWasmSettingsPrivate_onError);
193 }
194 
sync()195 void QWasmSettingsPrivate::sync()
196 {
197     QConfFileSettingsPrivate::sync();
198 
199     QFile file(fileName());
200     if (file.open(QFile::ReadOnly)) {
201         QByteArray dataPointer = file.readAll();
202 
203         emscripten_idb_async_store("/home/web_user",
204                                   fileName().toLocal8Bit(),
205                                    reinterpret_cast<void *>(dataPointer.data()),
206                                    dataPointer.length(),
207                                    reinterpret_cast<void*>(this),
208                                    QWasmSettingsPrivate_onStore,
209                                    QWasmSettingsPrivate_onError);
210     }
211 }
212 
flush()213 void QWasmSettingsPrivate::flush()
214 {
215     sync();
216 }
217 
isWritable() const218 bool QWasmSettingsPrivate::isWritable() const
219 {
220     return isReadReady && QConfFileSettingsPrivate::isWritable();
221 }
222 
syncToLocal(const char * data,int size)223 void QWasmSettingsPrivate::syncToLocal(const char *data, int size)
224 {
225     QFile file(fileName());
226 
227     if (file.open(QFile::WriteOnly)) {
228         file.write(data, size + 1);
229         QByteArray data = file.readAll();
230 
231         emscripten_idb_async_store("/home/web_user",
232                                    fileName().toLocal8Bit(),
233                                    reinterpret_cast<void *>(data.data()),
234                                    data.length(),
235                                    reinterpret_cast<void*>(this),
236                                    QWasmSettingsPrivate_onStore,
237                                    QWasmSettingsPrivate_onError);
238         setReady();
239     }
240 }
241 
loadLocal(const QByteArray & filename)242 void QWasmSettingsPrivate::loadLocal(const QByteArray &filename)
243 {
244     emscripten_idb_async_load("/home/web_user",
245                               filename.data(),
246                               reinterpret_cast<void*>(this),
247                               QWasmSettingsPrivate_onLoad,
248                               QWasmSettingsPrivate_onError);
249 }
250 
setReady()251 void QWasmSettingsPrivate::setReady()
252 {
253     isReadReady = true;
254     setStatus(QSettings::NoError);
255     QConfFileSettingsPrivate::initAccess();
256 }
257 
258 QT_END_NAMESPACE
259 #endif // QT_NO_SETTINGS
260