1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the qmake application 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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include <QtCore/qdir.h>
43 #include <QtCore/qxmlstream.h>
44 
45 #include "epocroot_p.h"
46 #include "../windows/registry_p.h"
47 
48 QT_BEGIN_NAMESPACE
49 
50 // Registry key under which the location of the Symbian devices.xml file is
51 // stored.
52 // Note that, on 64-bit machines, this key is located under the 32-bit
53 // compatibility key:
54 //     HKEY_LOCAL_MACHINE\Software\Wow6432Node
55 #define SYMBIAN_SDKS_REG_SUBKEY "Software\\Symbian\\EPOC SDKs\\CommonPath"
56 
57 #ifdef Q_OS_WIN32
58 #   define SYMBIAN_SDKS_REG_HANDLE HKEY_LOCAL_MACHINE
59 #else
60 #   define SYMBIAN_SDKS_REG_HANDLE 0
61 #endif
62 
63 // Value which is populated and returned by the epocRoot() function.
64 // Stored as a static value in order to avoid unnecessary re-evaluation.
65 static QString epocRootValue;
66 
getDevicesXmlPath()67 static QString getDevicesXmlPath()
68     {
69     // Note that the following call will return a null string on platforms other
70     // than Windows.  If support is required on other platforms for devices.xml,
71     // an alternative mechanism for retrieving the location of this file will
72     // be required.
73     return qt_readRegistryKey(SYMBIAN_SDKS_REG_HANDLE, QLatin1String(SYMBIAN_SDKS_REG_SUBKEY));
74     }
75 
76 /**
77  * Checks whether epocRootValue points to an existent directory.
78  * If not, epocRootValue is set to an empty string and an error message is printed.
79  */
checkEpocRootExists(const QString & source)80 static void checkEpocRootExists(const QString &source)
81 {
82     if (!epocRootValue.isEmpty()) {
83         QDir dir(epocRootValue);
84         if (!dir.exists()) {
85             qWarning("Warning: %s is set to an invalid path: '%s'", qPrintable(source),
86                      qPrintable(epocRootValue));
87             epocRootValue = QString();
88         }
89     }
90 }
91 
92 /**
93  * Translate path from Windows to Qt format.
94  */
fixEpocRoot(QString & path)95 static void fixEpocRoot(QString &path)
96 {
97     path.replace(QLatin1Char('\\'), QLatin1Char('/'));
98 
99     if (!path.size() || path[path.size()-1] != QLatin1Char('/')) {
100         path += QLatin1Char('/');
101     }
102 #ifdef Q_OS_WIN32
103     // Make sure we have drive letter in epocroot
104     if (path.startsWith(QLatin1Char('/')))
105         path.prepend(QDir::currentPath().left(2));
106 #endif
107 }
108 
109 /**
110  * Determine the epoc root for the currently active SDK.
111  */
qt_epocRoot()112 QString qt_epocRoot()
113 {
114     if (epocRootValue.isEmpty()) {
115         // 1. If environment variable EPOCROOT is set and points to an existent
116         //    directory, this is returned.
117         epocRootValue = QString::fromLocal8Bit(qgetenv("EPOCROOT").constData());
118         checkEpocRootExists(QLatin1String("EPOCROOT environment variable"));
119 
120         if (epocRootValue.isEmpty()) {
121             // 2. The location of devices.xml is specified by a registry key.  If this
122             //    file exists, it is parsed.
123             QString devicesXmlPath = getDevicesXmlPath();
124             if (!devicesXmlPath.isEmpty()) {
125                 devicesXmlPath += QLatin1String("/devices.xml");
126                 QFile devicesFile(devicesXmlPath);
127                 if (devicesFile.open(QIODevice::ReadOnly)) {
128 
129                     // 3. If the EPOCDEVICE environment variable is set and a corresponding
130                     //    entry is found in devices.xml, and its epocroot value points to an
131                     //    existent directory, it is returned.
132                     // 4. If a device element marked as default is found in devices.xml and its
133                     //    epocroot value points to an existent directory, this is returned.
134 
135                     const QString epocDeviceValue = QString::fromLocal8Bit(qgetenv("EPOCDEVICE").constData());
136                     bool epocDeviceFound = false;
137 
138                     QXmlStreamReader xml(&devicesFile);
139                     while (!xml.atEnd()) {
140                         xml.readNext();
141                         if (xml.isStartElement() && xml.name() == QLatin1String("devices")) {
142                             if (xml.attributes().value(QLatin1String("version")) == QLatin1String("1.0")) {
143                                 while (!(xml.isEndElement() && xml.name() == QLatin1String("devices")) && !xml.atEnd()) {
144                                     xml.readNext();
145                                     if (xml.isStartElement() && xml.name() == QLatin1String("device")) {
146                                         const bool isDefault = xml.attributes().value(QLatin1String("default")) == QLatin1String("yes");
147                                         const QString id = xml.attributes().value(QLatin1String("id")).toString();
148                                         const QString name = xml.attributes().value(QLatin1String("name")).toString();
149                                         const QString alias = xml.attributes().value(QLatin1String("alias")).toString();
150                                         bool epocDeviceMatch = QString(id + QLatin1String(":") + name) == epocDeviceValue;
151                                         if (!alias.isEmpty())
152                                             epocDeviceMatch |= alias == epocDeviceValue;
153                                         epocDeviceFound |= epocDeviceMatch;
154 
155                                         if((epocDeviceValue.isEmpty() && isDefault) || epocDeviceMatch) {
156                                             // Found a matching device
157                                             while (!(xml.isEndElement() && xml.name() == QLatin1String("device")) && !xml.atEnd()) {
158                                                 xml.readNext();
159                                                 if (xml.isStartElement() && xml.name() == QLatin1String("epocroot")) {
160                                                     epocRootValue = xml.readElementText();
161                                                     const QString deviceSource = epocDeviceValue.isEmpty()
162                                                         ? QLatin1String("default device")
163                                                         : QString(QLatin1String("EPOCDEVICE (") + epocDeviceValue + QLatin1String(")"));
164                                                     checkEpocRootExists(deviceSource);
165                                                 }
166                                             }
167 
168                                             if (epocRootValue.isEmpty())
169                                                 xml.raiseError(QLatin1String("No epocroot element found"));
170                                         }
171                                     }
172                                 }
173                             } else {
174                                 xml.raiseError(QLatin1String("Invalid 'devices' element version"));
175                             }
176                         }
177                     }
178                     if (xml.hasError()) {
179                         qWarning("Warning: Error \"%s\" when parsing devices.xml",
180                                  qPrintable(xml.errorString()));
181                     } else {
182                         if (epocRootValue.isEmpty()) {
183                             if (!epocDeviceValue.isEmpty()) {
184                                 if (epocDeviceFound) {
185                                     qWarning("Warning: Missing or invalid epocroot attribute in device '%s' in devices.xml.",
186                                              qPrintable(epocDeviceValue));
187                                 } else {
188                                     qWarning("Warning: No device matching EPOCDEVICE (%s) in devices.xml.",
189                                              qPrintable(epocDeviceValue));
190                                 }
191                             } else {
192                                 if (epocDeviceFound) {
193                                     qWarning("Warning: Missing or invalid epocroot attribute in default device in devices.xml.");
194                                 } else {
195                                     qWarning("Warning: No default device set in devices.xml.");
196                                 }
197                             }
198                         }
199                     }
200                 } else {
201                     qWarning("Warning: Could not open file: '%s'.", qPrintable(devicesXmlPath));
202                 }
203             }
204         }
205 
206         if (epocRootValue.isEmpty()) {
207             // 5. An empty string is returned.
208             qWarning("Warning: failed to resolve epocroot."
209 #ifdef Q_OS_WIN32
210                      "\nEither\n"
211                      "    1. Set EPOCROOT environment variable to a valid value.\n"
212                      " or 2. Ensure that the HKEY_LOCAL_MACHINE\\" SYMBIAN_SDKS_REG_SUBKEY
213                      " registry key is set, and then\n"
214                      "       a. Set EPOCDEVICE environment variable to a valid device\n"
215                      "    or b. Specify a default device in the devices.xml file.");
216 #else
217                      " Set EPOCROOT environment variable to a valid value.");
218 #endif
219         } else {
220             fixEpocRoot(epocRootValue);
221         }
222     }
223 
224     return epocRootValue;
225 }
226 
227 QT_END_NAMESPACE
228