1 /*
2  * regexpl - Console Registry Explorer
3  *
4  * Copyright (C) 2000-2005 Nedko Arnaudov <nedko@users.sourceforge.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; see the file COPYING.  If not, write to
18  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 // ShellCommandSetValue.cpp: implementation of the CShellCommandSetValue class.
23 //
24 //////////////////////////////////////////////////////////////////////
25 
26 #include "ph.h"
27 #include "ShellCommandSetValue.h"
28 #include "RegistryExplorer.h"
29 #include "RegistryTree.h"
30 #include "RegistryKey.h"
31 
32 #define SET_VALUE_CMD				_T("SV")
33 #define SET_VALUE_CMD_LENGTH		COMMAND_LENGTH(SET_VALUE_CMD)
34 #define SET_VALUE_CMD_SHORT_DESC	SET_VALUE_CMD _T(" command is used to set value.\n")
35 
36 BOOL StringToDWORD(DWORD& rdwOut, const TCHAR *pszIn)
37 {
38 	const TCHAR *pszDigits;
39 	const TCHAR *pszOctalNumbers = _T("01234567");
40 	const TCHAR *pszDecimalNumbers = _T("0123456789");
41 	const TCHAR *pszHexNumbers = _T("0123456789ABCDEF");
42 	const TCHAR *pszNumbers;
43 	unsigned int nBase = 0;
44 	if (*pszIn == _T('0'))
45 	{
46 		if ((*(pszIn+1) == _T('x'))||((*(pszIn+1) == _T('X'))))
47 		{	// hex
48 			nBase = 16;
49 			pszDigits = pszIn+2;
50 			pszNumbers = pszHexNumbers;
51 		}
52 		else
53 		{	// octal
54 			nBase = 8;
55 			pszDigits = pszIn+1;
56 			pszNumbers = pszOctalNumbers;
57 		}
58 	}
59 	else
60 	{	//decimal
61 		nBase = 10;
62 		pszDigits = pszIn;
63 		pszNumbers = pszDecimalNumbers;
64 	}
65 
66 	const TCHAR *pszDigit = pszDigits;
67 	pszDigit += _tcslen(pszDigit);
68 
69 	DWORD nMul = 1;
70 	rdwOut = 0;
71 	DWORD dwAdd;
72 	const TCHAR *pszNumber;
73 	while (pszDigit > pszDigits)
74 	{
75 		pszDigit--;
76 		pszNumber = _tcschr(pszNumbers,*pszDigit);
77 		if (!pszNumber)
78 			return FALSE;	// wrong char in input string
79 
80 		dwAdd = (pszNumber-pszNumbers)*nMul;
81 
82 		if (rdwOut + dwAdd < rdwOut)
83 			return FALSE;	// overflow
84 		rdwOut += dwAdd;
85 
86 		if (nMul * nBase < nMul)
87 			return FALSE;	// overflow
88 		nMul *= nBase;
89 	};
90 
91 	return TRUE;
92 }
93 
94 //////////////////////////////////////////////////////////////////////
95 // Construction/Destruction
96 //////////////////////////////////////////////////////////////////////
97 
98 CShellCommandSetValue::CShellCommandSetValue(CRegistryTree& rTree):m_rTree(rTree)
99 {
100 }
101 
102 CShellCommandSetValue::~CShellCommandSetValue()
103 {
104 }
105 
106 BOOL CShellCommandSetValue::Match(const TCHAR *pszCommand)
107 {
108 	if (_tcsicmp(pszCommand,SET_VALUE_CMD) == 0)
109 		return TRUE;
110 	if (_tcsnicmp(pszCommand,SET_VALUE_CMD _T(".."),SET_VALUE_CMD_LENGTH+2*sizeof(TCHAR)) == 0)
111 		return TRUE;
112 	if (_tcsnicmp(pszCommand,SET_VALUE_CMD _T("/"),SET_VALUE_CMD_LENGTH+1*sizeof(TCHAR)) == 0)
113 		return TRUE;
114 	if (_tcsnicmp(pszCommand,SET_VALUE_CMD _T("\\"),SET_VALUE_CMD_LENGTH+1*sizeof(TCHAR)) == 0)
115 		return TRUE;
116 	return FALSE;
117 }
118 
119 int CShellCommandSetValue::Execute(CConsole &rConsole, CArgumentParser& rArguments)
120 {
121   LONG nError;
122 
123 	rArguments.ResetArgumentIteration();
124 	TCHAR *pszCommandItself = rArguments.GetNextArgument();
125 
126 	TCHAR *pszParameter;
127 	TCHAR *pszValueFull = NULL;
128 	TCHAR *pszValueData = NULL;
129 	BOOL blnBadParameter = FALSE;
130 	BOOL blnHelp = FALSE;
131 	DWORD dwValueSize = 0;
132 	DWORD dwType = REG_NONE;
133 	BYTE *pDataBuffer = NULL;
134 
135 	if ((_tcsnicmp(pszCommandItself,SET_VALUE_CMD _T(".."),SET_VALUE_CMD_LENGTH+2*sizeof(TCHAR)) == 0)||
136 		(_tcsnicmp(pszCommandItself,SET_VALUE_CMD _T("\\"),SET_VALUE_CMD_LENGTH+1*sizeof(TCHAR)) == 0))
137 	{
138 		pszValueFull = pszCommandItself + SET_VALUE_CMD_LENGTH;
139 	}
140 	else if (_tcsnicmp(pszCommandItself,SET_VALUE_CMD _T("/"),SET_VALUE_CMD_LENGTH+1*sizeof(TCHAR)) == 0)
141 	{
142 		pszParameter = pszCommandItself + SET_VALUE_CMD_LENGTH;
143 		goto CheckValueArgument;
144 	}
145 
146 	while((pszParameter = rArguments.GetNextArgument()) != NULL)
147 	{
148 CheckValueArgument:
149 		blnBadParameter = FALSE;
150 		if (((*pszParameter == _T('/'))||(*pszParameter == _T('-')))
151 			&&(*(pszParameter+1) == _T('?')))
152 		{
153 			blnHelp = TRUE;
154 		}
155 		else if (dwType == REG_NONE)
156 		{
157 			if (_tcsicmp(pszParameter,_T("b")) == 0)
158 			{
159 				dwType = REG_BINARY;
160 			}
161 			else if (_tcsicmp(pszParameter,_T("dw")) == 0)
162 			{
163 				dwType = REG_DWORD;
164 			}
165 			else if (_tcsicmp(pszParameter,_T("dwle")) == 0)
166 			{
167 				dwType = REG_DWORD_LITTLE_ENDIAN;
168 			}
169 			else if (_tcsicmp(pszParameter,_T("dwbe")) == 0)
170 			{
171 				dwType = REG_DWORD_BIG_ENDIAN;
172 			}
173 			else if (_tcsicmp(pszParameter,_T("sz")) == 0)
174 			{
175 				dwType = REG_SZ;
176 			}
177 			else if (_tcsicmp(pszParameter,_T("esz")) == 0)
178 			{
179 				dwType = REG_EXPAND_SZ;
180 			}
181 			else
182 			{
183 				blnBadParameter = TRUE;
184 			}
185 		}
186 		else if (pszValueData == NULL)
187 		{
188 			pszValueData = pszParameter;
189 		}
190 		else if (!pszValueFull)
191 		{
192 			pszValueFull = pszParameter;
193 		}
194 		else
195 		{
196 			blnBadParameter = TRUE;
197 		}
198 		if (blnBadParameter)
199 		{
200 			rConsole.Write(_T("Bad parameter: "));
201 			rConsole.Write(pszParameter);
202 			rConsole.Write(_T("\n"));
203 		}
204 	}
205 
206 	if (!pszValueData)
207 		blnHelp = TRUE;
208 
209 	CRegistryKey Key;
210 	TCHAR *pszValueName;
211 	const TCHAR *pszEmpty = _T("");
212 	const TCHAR *pszPath;
213 
214 	if (blnHelp)
215 	{
216 		rConsole.Write(GetHelpString());
217 
218 		if (pDataBuffer)
219 			delete pDataBuffer;
220 
221 		return 0;
222 	}
223 
224 	if (pszValueFull)
225 	{
226 		if (_tcscmp(pszValueFull,_T("\\")) == 0)
227 			goto CommandNAonRoot;
228 
229 		TCHAR *pchSep = _tcsrchr(pszValueFull,_T('\\'));
230 		pszValueName = pchSep?(pchSep+1):(pszValueFull);
231 		pszPath = pchSep?pszValueFull:_T(".");
232 
233 		//if (_tcsrchr(pszValueName,_T('.')))
234 		//{
235 		//	pszValueName = _T("");
236 		//	pszPath = pszValueFull;
237 		//}
238 		//else
239 		if (pchSep)
240 			*pchSep = 0;
241 	}
242 	else
243 	{
244 		pszValueName = (TCHAR*)pszEmpty;
245 		pszPath = _T(".");
246 	}
247 
248   if (!m_rTree.GetKey(pszPath,KEY_SET_VALUE,Key))
249   {
250     rConsole.Write(m_rTree.GetLastErrorDescription());
251     goto SkipCommand;
252   }
253 
254 	if (Key.IsRoot())
255     goto CommandNAonRoot;
256 
257   switch (dwType)
258   {
259   case REG_BINARY:
260     {
261       HANDLE hFile;
262       DWORD dwBytesReaded;
263       hFile = CreateFile(pszValueData,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
264       if (hFile == INVALID_HANDLE_VALUE)
265       {
266         rConsole.Write(_T("Cannot open file "));
267         rConsole.Write(pszValueData);
268         rConsole.Write(_T("\n"));
269         goto SkipCommand;
270       }
271       dwValueSize = GetFileSize(hFile,NULL);
272       if (dwValueSize == (DWORD)-1)	// ok, that's right, we compare signed with unsigned here.
273         // GetFileSize is documented and declared to return DWORD.
274         // Error is indicated by checking if return is -1. Design->documentation bug ???
275       {
276         rConsole.Write(_T("Cannot get size of file "));
277         rConsole.Write(pszValueData);
278         rConsole.Write(_T("\n"));
279         VERIFY(CloseHandle(hFile));
280         goto SkipCommand;
281       }
282       pDataBuffer = new BYTE [dwValueSize];
283       if (!pDataBuffer)
284       {
285         rConsole.Write(_T("Cannot load file into memory. Out of memory.\n"));
286         VERIFY(CloseHandle(hFile));
287         goto SkipCommand;
288       }
289       if (!ReadFile(hFile,pDataBuffer,dwValueSize,&dwBytesReaded,NULL))
290       {
291         rConsole.Write(_T("Cannot load file into memory. Error reading file.\n"));
292         VERIFY(CloseHandle(hFile));
293         goto SkipCommand;
294       }
295 
296       VERIFY(CloseHandle(hFile));
297       ASSERT(dwBytesReaded == dwValueSize);
298     }
299     break;
300   case REG_DWORD_LITTLE_ENDIAN:
301   case REG_DWORD_BIG_ENDIAN:
302     dwValueSize = 4;
303     pDataBuffer = (BYTE *) new BYTE [dwValueSize];
304     if (!StringToDWORD(*(DWORD *)pDataBuffer,pszValueData))
305     {
306       rConsole.Write(_T("Cannot convert "));
307       rConsole.Write(pszValueData);
308       rConsole.Write(_T(" to DWORD \n"));
309       goto SkipCommand;
310     }
311     if (dwType == REG_DWORD_BIG_ENDIAN)
312     {
313       unsigned char nByte;
314       nByte = *pDataBuffer;
315       *pDataBuffer = *(pDataBuffer+3);
316       *(pDataBuffer+3) = nByte;
317       nByte = *(pDataBuffer+1);
318       *(pDataBuffer+1) = *(pDataBuffer+2);
319       *(pDataBuffer+2) = nByte;
320     }
321     break;
322   case REG_SZ:
323   case REG_EXPAND_SZ:
324     dwValueSize = _tcslen(pszValueData)+1;
325     if (*pszValueData == _T('\"'))
326     {
327       dwValueSize -= 2;
328       *(pszValueData+dwValueSize) = 0;
329       pszValueData++;
330     }
331     dwValueSize *= sizeof(TCHAR);
332     pDataBuffer = (BYTE *) new BYTE [dwValueSize];
333 
334     {
335       const TCHAR *pchSrc = pszValueData;
336       TCHAR *pchDest = (TCHAR *)pDataBuffer;
337       while(*pchSrc)
338       {
339         if (pchSrc[0] == _T('^'))
340         {
341           if (pchSrc[1] == _T('a'))
342             *pchDest = _T('\a');
343           else if (pchSrc[1] == _T('b'))
344             *pchDest = _T('\b');
345           else if (pchSrc[1] == _T('f'))
346             *pchDest = _T('\f');
347           else if (pchSrc[1] == _T('n'))
348             *pchDest = _T('\n');
349           else if (pchSrc[1] == _T('r'))
350             *pchDest = _T('\r');
351           else if (pchSrc[1] == _T('t'))
352             *pchDest = _T('\t');
353           else
354             *pchDest = pchSrc[1];
355 
356           pchSrc +=2;
357           pchDest++;
358           dwValueSize--;
359         }
360         else
361         {
362           *pchDest = *pchSrc;
363           pchSrc++;
364           pchDest++;
365         }
366       }
367       *pchDest = _T('\0');
368     }
369     break;
370   default:
371     ASSERT(FALSE);
372   }
373 
374   {
375     size_t s = _tcslen(pszValueName);
376     if (s && (pszValueName[0] == _T('\"'))&&(pszValueName[s-1] == _T('\"')))
377     {
378       pszValueName[s-1] = 0;
379       pszValueName++;
380     }
381   }
382 
383   nError = Key.SetValue(pszValueName,dwType,pDataBuffer,dwValueSize);
384   if (nError != ERROR_SUCCESS)
385   {
386     char Buffer[254];
387     _stprintf(Buffer,_T("Cannot set value. Error is %u\n"),(unsigned int)nError);
388     rConsole.Write(Buffer);
389   }
390   else
391   {
392     InvalidateCompletion();
393   }
394 
395 SkipCommand:
396 	if (pDataBuffer)
397 		delete[] pDataBuffer;
398 	return 0;
399 
400 CommandNAonRoot:
401   rConsole.Write(SET_VALUE_CMD COMMAND_NA_ON_ROOT);
402   return 0;
403 }
404 
405 const TCHAR * CShellCommandSetValue::GetHelpString()
406 {
407 	return SET_VALUE_CMD_SHORT_DESC
408 			_T("Syntax: ") SET_VALUE_CMD _T(" <TYPE> <VALUE> [<PATH>][<VALUE_NAME>] [/?]\n\n")
409 			_T("    <TYPE>       - Type of value to be set. Must be one of following:\n")
410 			_T("                    b    - binary value.\n")
411 			_T("                    dw   - A 32-bit number.\n")
412 			_T("                    dwle - A 32-bit number in little-endian format.\n")
413 			_T("                    dwbe - A 32-bit number in big-endian format.\n")
414 			_T("                    sz   - A null-terminated string.\n")
415 			_T("                    esz  - A null-terminated string that contains unexpanded\n")
416 			_T("                           references to environment variables.\n")
417 //			_T("                    msz  - An array of null-terminated strings,\n")
418 //			_T("                           terminated by two null characters.\n")
419 			_T("    <VALUE>      - The data to be set. According to <TYPE>, <VALUE> means:\n")
420 			_T("                    b    - name of file from which to read binary data.\n")
421 			_T("                    dw   \\\n")
422 			_T("                    dwle - number with syntax: [0 [{ x | X }]] [digits]\n")
423 			_T("                    dwbe /\n")
424 			_T("                    sz   \\\n")
425 			_T("                    esz  - <VALUE> string is interpreted as string\n")
426 			_T("    <PATH>       - Optional relative path of key which value will be processed. ^ is the escape char\n")
427 			_T("    <VALUE_NAME> - Name of key's value. Default is key's default value.\n")
428 			_T("    /?           - This help.\n");
429 }
430 
431 const TCHAR * CShellCommandSetValue::GetHelpShortDescriptionString()
432 {
433 	return SET_VALUE_CMD_SHORT_DESC;
434 }
435