1 /*
2 	Actiona
3 	Copyright (C) 2005 Jonathan Mercier-Ganady
4 
5 	Actiona is free software: you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation, either version 3 of the License, or
8 	(at your option) any later version.
9 
10 	Actiona is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18 	Contact : jmgr@jmgr.info
19 */
20 
21 #include "registry.h"
22 
23 #include <QtEndian>
24 #include <QDebug>
25 
26 #ifdef Q_OS_WIN
27 #include <strsafe.h>
28 #endif
29 
30 namespace Code
31 {
constructor(QScriptContext * context,QScriptEngine * engine)32 	QScriptValue Registry::constructor(QScriptContext *context, QScriptEngine *engine)
33 	{
34 		return CodeClass::constructor(new Registry, context, engine);
35 	}
36 
Registry()37 	Registry::Registry()
38 		: CodeClass()
39 	#ifdef Q_OS_WIN
40 		, mHKey(0)
41 		, mRootKey(ClassesRoot)
42 	#endif
43 	{
44 	}
45 
~Registry()46 	Registry::~Registry()
47 	{
48 	#ifdef Q_OS_WIN
49 		RegCloseKey(mHKey);
50 #endif
51     }
52 
openKey(Key key,const QString & subKey)53 	QScriptValue Registry::openKey(Key key, const QString &subKey)
54 	{
55 	#ifdef Q_OS_WIN
56 		HKEY hKey = enumToKey(key);
57 
58 		if(RegOpenKeyEx(hKey, subKey.toStdWString().c_str(), 0, KEY_ALL_ACCESS, &mHKey) != ERROR_SUCCESS)
59             throwError(QStringLiteral("OpenKeyError"), tr("Unable to open the key"));
60 		else
61 		{
62 			mRootKey = key;
63 			mSubKey = subKey;
64 		}
65 	#else
66 		Q_UNUSED(key)
67 		Q_UNUSED(subKey)
68 	#endif
69 		return thisObject();
70 	}
71 
createKey(Key key,const QString & subKey)72 	QScriptValue Registry::createKey(Key key, const QString &subKey)
73 	{
74 	#ifdef Q_OS_WIN
75 		HKEY hKey = enumToKey(key);
76 
77 		if(RegCreateKeyEx(hKey, subKey.toStdWString().c_str(), 0, 0, 0, KEY_ALL_ACCESS, 0, &mHKey, 0) != ERROR_SUCCESS)
78             throwError(QStringLiteral("CreateKeyError"), tr("Unable to create the key"));
79 		else
80 		{
81 			mRootKey = key;
82 			mSubKey = subKey;
83 		}
84 	#else
85 		Q_UNUSED(key)
86 		Q_UNUSED(subKey)
87 	#endif
88 		return thisObject();
89 	}
90 
setValue(const QString & value,const QVariant & data) const91 	QScriptValue Registry::setValue(const QString &value, const QVariant &data) const
92 	{
93 	#ifdef Q_OS_WIN
94 		std::wstring wideValue = value.toStdWString();
95 
96 		switch(data.type())
97 		{
98 		case QVariant::Int:
99 		case QVariant::UInt:
100 			{
101 				int intData = data.toInt();
102 				if(RegSetValueEx(mHKey, wideValue.c_str(), 0, REG_DWORD, reinterpret_cast<LPBYTE>(&intData), sizeof(int)) != ERROR_SUCCESS)
103                     throwError(QStringLiteral("SetValueError"), tr("Cannot set the value data"));
104 			}
105 			break;
106 		case QVariant::LongLong:
107 		case QVariant::ULongLong:
108 			{
109 				long long intData = data.toLongLong();
110 				if(RegSetValueEx(mHKey, wideValue.c_str(), 0, REG_QWORD, reinterpret_cast<LPBYTE>(&intData), sizeof(long long)) != ERROR_SUCCESS)
111                     throwError(QStringLiteral("SetValueError"), tr("Cannot set the value data"));
112 			}
113 			break;
114 		case QVariant::StringList:
115 			{
116 				const QStringList &stringList = data.toStringList();
117 				std::wstring wideData;
118 
119                 for(const QString &string: stringList)
120 				{
121 					wideData += string.toStdWString();
122 					wideData += L'\0';
123 				}
124 
125 				if(RegSetValueEx(mHKey, wideValue.c_str(), 0, REG_MULTI_SZ, reinterpret_cast<LPBYTE>(const_cast<wchar_t*>(wideData.c_str())), static_cast<DWORD>(wideData.size() * sizeof(wchar_t))) != ERROR_SUCCESS)
126                     throwError(QStringLiteral("SetValueError"), tr("Cannot set the value data"));
127 			}
128 			break;
129 		case QVariant::ByteArray:
130 			{
131 				QByteArray byteArray = data.toByteArray();
132 				if(RegSetValueEx(mHKey, wideValue.c_str(), 0, REG_BINARY, reinterpret_cast<LPBYTE>(byteArray.data()), static_cast<DWORD>(byteArray.size())) != ERROR_SUCCESS)
133                     throwError(QStringLiteral("SetValueError"), tr("Cannot set the value data"));
134 			}
135 			break;
136 		default:
137 			{
138 				if(data.type() == QVariant::String || data.canConvert(QVariant::String))
139 				{
140 					std::wstring wideData = data.toString().toStdWString();
141 					if(RegSetValueEx(mHKey, wideValue.c_str(), 0, REG_SZ, reinterpret_cast<LPBYTE>(const_cast<wchar_t*>(wideData.c_str())), static_cast<DWORD>(wideData.size() * sizeof(wchar_t))) != ERROR_SUCCESS)
142                         throwError(QStringLiteral("SetValueError"), tr("Cannot set the value data"));
143 				}
144 				else
145                     throwError(QStringLiteral("SetValueError"), tr("Cannot set the value data"));
146 			}
147 			break;
148 		}
149 	#else
150 		Q_UNUSED(value)
151 		Q_UNUSED(data)
152 	#endif
153 		return thisObject();
154 	}
155 
value(const QString & value) const156 	QVariant Registry::value(const QString &value) const
157 	{
158 	#ifdef Q_OS_WIN
159 		DWORD size;
160 		DWORD type;
161 		std::wstring wideValue = value.toStdWString();
162 		if(RegQueryValueEx(mHKey, wideValue.c_str(), 0, &type, 0, &size) != ERROR_SUCCESS)
163 		{
164             throwError(QStringLiteral("FindValueError"), tr("Cannot find the value to read"));
165             return {};
166 		}
167 
168 		switch(type)
169 		{
170 		case REG_DWORD:
171 		case REG_DWORD_BIG_ENDIAN:
172 			{
173 				qint32 value;
174 				if(RegQueryValueEx(mHKey, wideValue.c_str(), 0, 0, reinterpret_cast<LPBYTE>(&value), &size) != ERROR_SUCCESS)
175 				{
176                     throwError(QStringLiteral("FindValueError"), tr("Cannot find the value to read"));
177                     return {};
178 				}
179 
180 				if(type == REG_DWORD_BIG_ENDIAN)
181 					value = qFromBigEndian(value);
182 
183 				return value;
184 			}
185 			break;
186 		case REG_SZ:
187 		case REG_EXPAND_SZ:
188 		case REG_LINK:
189 		case REG_MULTI_SZ:
190 			{
191                 std::vector<wchar_t> buffer(size);
192                 if(RegQueryValueEx(mHKey, wideValue.c_str(), 0, 0, reinterpret_cast<LPBYTE>(buffer.data()), &size) != ERROR_SUCCESS)
193 				{
194                     throwError(QStringLiteral("FindValueError"), tr("Cannot find the value to read"));
195                     return {};
196 				}
197 
198 				if(type == REG_MULTI_SZ)
199 				{
200                     QStringList stringList = QString::fromWCharArray(buffer.data(), size / 2).split(QChar(L'\0'), QString::SkipEmptyParts);
201 
202 					if(stringList.last().isEmpty())
203 						stringList.removeLast();
204 
205 					return stringList;
206 				}
207 				else
208                     return QString::fromWCharArray(buffer.data(), size / 2);
209 			}
210 			break;
211 		case REG_BINARY:
212 			{
213                 std::vector<char> buffer(size);
214                 if(RegQueryValueEx(mHKey, wideValue.c_str(), 0, 0, reinterpret_cast<LPBYTE>(buffer.data()), &size) != ERROR_SUCCESS)
215 				{
216                     throwError(QStringLiteral("FindValueError"), tr("Cannot find the value to read"));
217                     return {};
218 				}
219 
220                 QByteArray back = QByteArray::fromRawData(buffer.data(), size);
221 
222 				return back;
223 			}
224 			break;
225 		case REG_QWORD:
226 			{
227 				qint64 value;
228 				if(RegQueryValueEx(mHKey, wideValue.c_str(), 0, 0, reinterpret_cast<LPBYTE>(&value), &size) != ERROR_SUCCESS)
229 				{
230                     throwError(QStringLiteral("FindValueError"), tr("Cannot find the value to read"));
231                     return {};
232 				}
233 
234 				return value;
235 			}
236 			break;
237 		case REG_NONE:
238 		default:
239             throwError(QStringLiteral("InvalidValueError"), tr("Invalid value type"));
240             return {};
241 			break;
242 		}
243 	#else
244 		Q_UNUSED(value)
245 
246 		return QVariant();
247 	#endif
248 	}
249 
valueNames() const250 	QStringList Registry::valueNames() const
251 	{
252 	#ifdef Q_OS_WIN
253 		int index = 0;
254 		DWORD valueCount;
255 		DWORD maxValueNameLength;
256 
257 		if(RegQueryInfoKey(mHKey, 0, 0, 0, 0, 0, 0, &valueCount, &maxValueNameLength, 0, 0, 0) != ERROR_SUCCESS)
258 		{
259             throwError(QStringLiteral("InvalidKeyError"), tr("Unable to query informations about this key"));
260             return {};
261 		}
262 
263 		if(valueCount == 0 || maxValueNameLength == 0)
264             return {};
265 
266         std::vector<wchar_t> valueName(maxValueNameLength + 1);
267 		int result;
268 		QStringList back;
269 
270 		for(;;++index)
271 		{
272 			DWORD valueNameSize = maxValueNameLength + 1;
273 
274             result = RegEnumValue(mHKey, index, valueName.data(), &valueNameSize, 0, 0, 0, 0);
275 			if(result == ERROR_NO_MORE_ITEMS)
276 				break;
277 
278 			if(valueNameSize == 0)
279 				continue;//Skip the default value
280 
281             back.append(QString::fromWCharArray(valueName.data(), valueNameSize));
282 		}
283 
284 		return back;
285 	#else
286         return {};
287 	#endif
288 	}
289 
keys() const290 	QStringList Registry::keys() const
291 	{
292 	#ifdef Q_OS_WIN
293 		int index = 0;
294 		DWORD subKeyCount;
295 		DWORD maxSubKeyNameLength;
296 
297 		if(RegQueryInfoKey(mHKey, 0, 0, 0, &subKeyCount, &maxSubKeyNameLength, 0, 0, 0, 0, 0, 0) != ERROR_SUCCESS)
298 		{
299             throwError(QStringLiteral("InvalidKeyError"), tr("Unable to query informations about this key"));
300             return {};
301 		}
302 
303 		if(subKeyCount == 0 || maxSubKeyNameLength == 0)
304             return {};
305 
306         std::vector<wchar_t> subKeyName(maxSubKeyNameLength + 1);
307 		int result;
308 		QStringList back;
309 
310 		for(;;++index)
311 		{
312 			DWORD subKeyNameSize = maxSubKeyNameLength + 1;
313 
314             result = RegEnumKeyEx(mHKey, index, subKeyName.data(), &subKeyNameSize, 0, 0, 0, 0);
315 			if(result == ERROR_NO_MORE_ITEMS)
316 				break;
317 
318             back.append(QString::fromWCharArray(subKeyName.data(), subKeyNameSize));
319 		}
320 
321 		return back;
322 	#else
323         return {};
324 	#endif
325 	}
326 
deleteValue(const QString & value) const327 	QScriptValue Registry::deleteValue(const QString &value) const
328 	{
329 	#ifdef Q_OS_WIN
330 		if(RegDeleteValue(mHKey, value.toStdWString().c_str()) != ERROR_SUCCESS)
331             throwError(QStringLiteral("InvalidKeyError"), tr("Unable to delete the key"));
332 	#else
333 		Q_UNUSED(value)
334 	#endif
335 		return thisObject();
336 	}
337 
338 	#ifdef Q_OS_WIN
RegDelnodeRecurse(HKEY hKeyRoot,LPTSTR lpSubKey)339 	BOOL RegDelnodeRecurse (HKEY hKeyRoot, LPTSTR lpSubKey)
340 	{
341 		LPTSTR lpEnd;
342 		LONG lResult;
343 		DWORD dwSize;
344 		WCHAR szName[MAX_PATH];
345 		HKEY hKey;
346 		FILETIME ftWrite;
347 
348 		// First, see if we can delete the key without having
349 		// to recurse.
350 
351 		lResult = RegDeleteKey(hKeyRoot, lpSubKey);
352 
353 		if (lResult == ERROR_SUCCESS)
354 			return TRUE;
355 
356 		lResult = RegOpenKeyEx (hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
357 
358 		if (lResult != ERROR_SUCCESS)
359 		{
360 			if (lResult == ERROR_FILE_NOT_FOUND) {
361 				printf("Key not found.\n");
362 				return TRUE;
363 			}
364 			else {
365 				printf("Error opening key.\n");
366 				return FALSE;
367 			}
368 		}
369 
370 		// Check for an ending slash and add one if it is missing.
371 
372 		lpEnd = lpSubKey + lstrlen(lpSubKey);
373 
374 		if (*(lpEnd - 1) !=  TEXT('\\'))
375 		{
376 			*lpEnd =  TEXT('\\');
377 			lpEnd++;
378 			*lpEnd =  TEXT('\0');
379 		}
380 
381 		// Enumerate the keys
382 
383 		dwSize = MAX_PATH;
384 		lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
385 							   NULL, NULL, &ftWrite);
386 
387 		if (lResult == ERROR_SUCCESS)
388 		{
389 			do {
390 
391 				StringCchCopy (lpEnd, MAX_PATH*2, szName);
392 
393 				if (!RegDelnodeRecurse(hKeyRoot, lpSubKey)) {
394 					break;
395 				}
396 
397 				dwSize = MAX_PATH;
398 
399 				lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
400 									   NULL, NULL, &ftWrite);
401 
402 			} while (lResult == ERROR_SUCCESS);
403 		}
404 
405 		lpEnd--;
406 		*lpEnd = TEXT('\0');
407 
408 		RegCloseKey (hKey);
409 
410 		// Try again to delete the key.
411 
412 		lResult = RegDeleteKey(hKeyRoot, lpSubKey);
413 
414 		if (lResult == ERROR_SUCCESS)
415 			return TRUE;
416 
417 		return FALSE;
418 	}
419 
RegDelnode(HKEY hKeyRoot,LPCTSTR lpSubKey)420 	BOOL RegDelnode (HKEY hKeyRoot, LPCTSTR lpSubKey)
421 	{
422 		WCHAR szDelKey[MAX_PATH*2];
423 
424 		StringCchCopy (szDelKey, MAX_PATH*2, lpSubKey);
425 		return RegDelnodeRecurse(hKeyRoot, szDelKey);
426 
427 	}
428 	#endif
429 
deleteKey(Key key,const QString & subKey) const430 	QScriptValue Registry::deleteKey(Key key, const QString &subKey) const
431 	{
432 	#ifdef Q_OS_WIN
433 		HKEY hKey = enumToKey(key);
434 
435 		if(!RegDelnode(hKey, subKey.toStdWString().c_str()))
436             throwError(QStringLiteral("InvalidKeyError"), tr("Unable to delete the key"));
437 	#else
438 		Q_UNUSED(key)
439 		Q_UNUSED(subKey)
440 	#endif
441 		return thisObject();
442 	}
443 
deleteKey() const444 	QScriptValue Registry::deleteKey() const
445 	{
446 	#ifdef Q_OS_WIN
447 		RegCloseKey(mHKey);
448 
449 		deleteKey(mRootKey, mSubKey);
450 	#endif
451 		return thisObject();
452 	}
453 
closeKey() const454 	QScriptValue Registry::closeKey() const
455 	{
456 	#ifdef Q_OS_WIN
457 		RegCloseKey(mHKey);
458 	#endif
459 		return thisObject();
460 	}
461 
462 	#ifdef Q_OS_WIN
enumToKey(Key key) const463 	HKEY Registry::enumToKey(Key key) const
464 	{
465 		switch(key)
466 		{
467 		case ClassesRoot:
468 			return HKEY_CLASSES_ROOT;
469 		case CurrentConfig:
470 			return HKEY_CURRENT_CONFIG;
471 		case CurrentUser:
472 			return HKEY_CURRENT_USER;
473 		case Users:
474 			return HKEY_USERS;
475 		case LocalMachine:
476 			return HKEY_LOCAL_MACHINE;
477 		default:
478 			return HKEY_CURRENT_USER;
479 		}
480 	}
481 	#endif
482 }
483