Fixed a buffer overflow in RefreshListView.
[reactos.git] / reactos / subsys / system / regedit / listview.c
1 /*
2 * Regedit listviews
3 *
4 * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include <windows.h>
22 #include <windowsx.h>
23 #include <commctrl.h>
24 #include <stdlib.h>
25 #include <tchar.h>
26 #include <process.h>
27 #include <stdio.h>
28
29 #include "main.h"
30 #include "regproc.h"
31
32 #define CX_ICON 16
33 #define CY_ICON 16
34 #define NUM_ICONS 2
35
36 int Image_String = 0;
37 int Image_Bin = 0;
38
39 typedef struct tagLINE_INFO
40 {
41 DWORD dwValType;
42 LPTSTR name;
43 void* val;
44 size_t val_len;
45 } LINE_INFO, *PLINE_INFO;
46
47 /*******************************************************************************
48 * Global and Local Variables:
49 */
50
51 static DWORD g_columnToSort = ~0UL;
52 static BOOL g_invertSort = FALSE;
53 static LPTSTR g_valueName;
54
55 #define MAX_LIST_COLUMNS (IDS_LIST_COLUMN_LAST - IDS_LIST_COLUMN_FIRST + 1)
56 static int default_column_widths[MAX_LIST_COLUMNS] = { 200, 175, 400 };
57 static int column_alignment[MAX_LIST_COLUMNS] = { LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT };
58
59 LPCTSTR GetValueName(HWND hwndLV, int iStartAt)
60 {
61 int item, len, maxLen;
62 LPTSTR newStr;
63 LVITEM LVItem;
64 PLINE_INFO lineinfo;
65
66 if (!g_valueName) g_valueName = HeapAlloc(GetProcessHeap(), 0, 1024);
67 if (!g_valueName) return NULL;
68 *g_valueName = 0;
69 maxLen = HeapSize(GetProcessHeap(), 0, g_valueName);
70 if (maxLen == -1) return NULL;
71
72 item = ListView_GetNextItem(hwndLV, iStartAt, LVNI_SELECTED);
73 if (item == -1) return NULL;
74 LVItem.mask = LVIF_PARAM;
75 LVItem.iItem = item;
76 for(;;)
77 {
78 if(ListView_GetItem(hwndLV, &LVItem))
79 {
80 lineinfo = (PLINE_INFO)LVItem.lParam;
81 if(!lineinfo->name)
82 {
83 *g_valueName = 0;
84 return g_valueName;
85 }
86 len = _tcslen(lineinfo->name);
87 if (len < maxLen - 1) break;
88 newStr = HeapReAlloc(GetProcessHeap(), 0, g_valueName, maxLen * 2);
89 if (!newStr) return NULL;
90 g_valueName = newStr;
91 maxLen *= 2;
92 }
93 else
94 return NULL;
95 }
96 memcpy(g_valueName, lineinfo->name, sizeof(TCHAR) * (len + 1));
97 return g_valueName;
98 }
99
100 BOOL IsDefaultValue(HWND hwndLV, int i)
101 {
102 PLINE_INFO lineinfo;
103 LVITEM Item;
104
105 Item.mask = LVIF_PARAM;
106 Item.iItem = i;
107 if(ListView_GetItem(hwndLV, &Item))
108 {
109 lineinfo = (PLINE_INFO)Item.lParam;
110 return lineinfo && (!lineinfo->name || !_tcscmp(lineinfo->name, _T("")));
111 }
112 return FALSE;
113 }
114
115 /*******************************************************************************
116 * Local module support methods
117 */
118 static void AddEntryToList(HWND hwndLV, LPTSTR Name, DWORD dwValType, void* ValBuf, DWORD dwCount, int Position, BOOL ValExists)
119 {
120 LINE_INFO* linfo;
121 LVITEM item;
122 int index;
123
124 linfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINE_INFO) + dwCount);
125 linfo->dwValType = dwValType;
126 linfo->val_len = dwCount;
127 if(dwCount > 0)
128 {
129 memcpy(&linfo[1], ValBuf, dwCount);
130 }
131 linfo->name = _tcsdup(Name);
132
133 item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
134 item.iItem = (Position == -1 ? 0: Position);
135 item.iSubItem = 0;
136 item.state = 0;
137 item.stateMask = 0;
138 item.pszText = Name;
139 item.cchTextMax = _tcslen(item.pszText);
140 if (item.cchTextMax == 0)
141 item.pszText = LPSTR_TEXTCALLBACK;
142 item.iImage = 0;
143 item.lParam = (LPARAM)linfo;
144 switch(dwValType)
145 {
146 case REG_SZ:
147 case REG_EXPAND_SZ:
148 case REG_MULTI_SZ:
149 item.iImage = Image_String;
150 break;
151 default:
152 item.iImage = Image_Bin;
153 break;
154 }
155
156 /* item.lParam = (LPARAM)ValBuf; */
157 #if (_WIN32_IE >= 0x0300)
158 item.iIndent = 0;
159 #endif
160
161 index = ListView_InsertItem(hwndLV, &item);
162 if (index != -1) {
163 switch (dwValType) {
164 case REG_SZ:
165 case REG_EXPAND_SZ:
166 if(dwCount > 0)
167 {
168 ListView_SetItemText(hwndLV, index, 2, ValBuf);
169 }
170 else if(!ValExists)
171 {
172 TCHAR buffer[255];
173 /* load (value not set) string */
174 LoadString(hInst, IDS_VALUE_NOT_SET, buffer, sizeof(buffer)/sizeof(TCHAR));
175 ListView_SetItemText(hwndLV, index, 2, buffer);
176 }
177 break;
178 case REG_MULTI_SZ:
179 {
180 LPTSTR src, str;
181 if(dwCount >= 2)
182 {
183 src = (LPTSTR)ValBuf;
184 str = HeapAlloc(GetProcessHeap(), 0, dwCount);
185 if(str != NULL)
186 {
187 *str = _T('\0');
188 /* concatenate all srings */
189 while(*src != _T('\0'))
190 {
191 _tcscat(str, src);
192 _tcscat(str, _T(" "));
193 src += _tcslen(src) + 1;
194 }
195 ListView_SetItemText(hwndLV, index, 2, str);
196 HeapFree(GetProcessHeap(), 0, str);
197 }
198 else
199 ListView_SetItemText(hwndLV, index, 2, _T(""));
200 }
201 else
202 ListView_SetItemText(hwndLV, index, 2, _T(""));
203 }
204 break;
205 case REG_DWORD: {
206 TCHAR buf[200];
207 if(dwCount == sizeof(DWORD))
208 {
209 wsprintf(buf, _T("0x%08X (%d)"), *(DWORD*)ValBuf, *(DWORD*)ValBuf);
210 }
211 else
212 {
213 LoadString(hInst, IDS_INVALID_DWORD, buf, sizeof(buf)/sizeof(TCHAR));
214 }
215 ListView_SetItemText(hwndLV, index, 2, buf);
216 }
217 /* lpsRes = convertHexToDWORDStr(lpbData, dwLen); */
218 break;
219 default:
220 {
221 unsigned int i;
222 LPBYTE pData = (LPBYTE)ValBuf;
223 LPTSTR strBinary;
224 if(dwCount > 0)
225 {
226 strBinary = HeapAlloc(GetProcessHeap(), 0, (dwCount * sizeof(TCHAR) * 3) + sizeof(TCHAR));
227 for (i = 0; i < dwCount; i++)
228 {
229 wsprintf( strBinary + i*3, _T("%02X "), pData[i] );
230 }
231 strBinary[dwCount * 3] = 0;
232 ListView_SetItemText(hwndLV, index, 2, strBinary);
233 HeapFree(GetProcessHeap(), 0, strBinary);
234 }
235 else
236 {
237 TCHAR szText[128];
238 LoadString(hInst, IDS_BINARY_EMPTY, szText, sizeof(szText)/sizeof(TCHAR));
239 ListView_SetItemText(hwndLV, index, 2, szText);
240 }
241 }
242 break;
243 }
244 }
245 }
246
247 static BOOL CreateListColumns(HWND hWndListView)
248 {
249 TCHAR szText[50];
250 int index;
251 LV_COLUMN lvC;
252
253 /* Create columns. */
254 lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
255 lvC.pszText = szText;
256
257 /* Load the column labels from the resource file. */
258 for (index = 0; index < MAX_LIST_COLUMNS; index++) {
259 lvC.iSubItem = index;
260 lvC.cx = default_column_widths[index];
261 lvC.fmt = column_alignment[index];
262 LoadString(hInst, IDS_LIST_COLUMN_FIRST + index, szText, sizeof(szText)/sizeof(TCHAR));
263 if (ListView_InsertColumn(hWndListView, index, &lvC) == -1) return FALSE;
264 }
265 return TRUE;
266 }
267
268 static BOOL InitListViewImageLists(HWND hwndLV)
269 {
270 HIMAGELIST himl; /* handle to image list */
271 HICON hico; /* handle to icon */
272
273 /* Create the image list. */
274 if ((himl = ImageList_Create(CX_ICON, CY_ICON,
275 ILC_MASK, 0, NUM_ICONS)) == NULL)
276 return FALSE;
277
278 hico = LoadIcon(hInst, MAKEINTRESOURCE(IDI_BIN));
279 Image_Bin = ImageList_AddIcon(himl, hico);
280
281 hico = LoadIcon(hInst, MAKEINTRESOURCE(IDI_STRING));
282 Image_String = ImageList_AddIcon(himl, hico);
283
284
285 /* Fail if not all of the images were added. */
286 if (ImageList_GetImageCount(himl) < NUM_ICONS)
287 {
288 return FALSE;
289 }
290
291 /* Associate the image list with the tree view control. */
292 ListView_SetImageList(hwndLV, himl, LVSIL_SMALL);
293
294 return TRUE;
295 }
296
297 /* OnGetDispInfo - processes the LVN_GETDISPINFO notification message. */
298
299 static void OnGetDispInfo(NMLVDISPINFO* plvdi)
300 {
301 static TCHAR buffer[200];
302
303 plvdi->item.pszText = NULL;
304 plvdi->item.cchTextMax = 0;
305
306 switch (plvdi->item.iSubItem) {
307 case 0:
308 LoadString(hInst, IDS_DEFAULT_VALUE_NAME, buffer, sizeof(buffer)/sizeof(TCHAR));
309 plvdi->item.pszText = buffer;
310 break;
311 case 1:
312 switch (((LINE_INFO*)plvdi->item.lParam)->dwValType) {
313 case REG_NONE:
314 plvdi->item.pszText = _T("REG_NONE");
315 break;
316 case REG_SZ:
317 plvdi->item.pszText = _T("REG_SZ");
318 break;
319 case REG_EXPAND_SZ:
320 plvdi->item.pszText = _T("REG_EXPAND_SZ");
321 break;
322 case REG_BINARY:
323 plvdi->item.pszText = _T("REG_BINARY");
324 break;
325 case REG_DWORD: /* REG_DWORD_LITTLE_ENDIAN */
326 plvdi->item.pszText = _T("REG_DWORD");
327 break;
328 case REG_DWORD_BIG_ENDIAN:
329 plvdi->item.pszText = _T("REG_DWORD_BIG_ENDIAN");
330 break;
331 case REG_LINK:
332 plvdi->item.pszText = _T("REG_LINK");
333 break;
334 case REG_MULTI_SZ:
335 plvdi->item.pszText = _T("REG_MULTI_SZ");
336 break;
337 case REG_RESOURCE_LIST:
338 plvdi->item.pszText = _T("REG_RESOURCE_LIST");
339 break;
340 case REG_FULL_RESOURCE_DESCRIPTOR:
341 plvdi->item.pszText = _T("REG_FULL_RESOURCE_DESCRIPTOR");
342 break;
343 case REG_RESOURCE_REQUIREMENTS_LIST:
344 plvdi->item.pszText = _T("REG_RESOURCE_REQUIREMENTS_LIST");
345 break;
346 case REG_QWORD: /* REG_QWORD_LITTLE_ENDIAN */
347 plvdi->item.pszText = _T("REG_QWORD");
348 break;
349 default: {
350 TCHAR buf2[200];
351 LoadString(hInst, IDS_UNKNOWN_TYPE, buf2, sizeof(buf2)/sizeof(TCHAR));
352 wsprintf(buffer, buf2, ((LINE_INFO*)plvdi->item.lParam)->dwValType);
353 plvdi->item.pszText = buffer;
354 break;
355 }
356 }
357 break;
358 case 3:
359 plvdi->item.pszText = _T("");
360 break;
361 }
362 }
363
364 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
365 {
366 LINE_INFO*l, *r;
367 l = (LINE_INFO*)lParam1;
368 r = (LINE_INFO*)lParam2;
369
370 if (g_columnToSort == ~0UL)
371 g_columnToSort = 0;
372
373 if (g_columnToSort == 1 && l->dwValType != r->dwValType)
374 return g_invertSort ? (int)r->dwValType - (int)l->dwValType : (int)l->dwValType - (int)r->dwValType;
375 if (g_columnToSort == 2) {
376 /* FIXME: Sort on value */
377 }
378 return g_invertSort ? _tcscmp(r->name, l->name) : _tcscmp(l->name, r->name);
379 }
380
381 BOOL ListWndNotifyProc(HWND hWnd, WPARAM wParam, LPARAM lParam, BOOL *Result)
382 {
383 NMLVDISPINFO* Info;
384 *Result = TRUE;
385 switch (((LPNMHDR)lParam)->code) {
386 case LVN_GETDISPINFO:
387 OnGetDispInfo((NMLVDISPINFO*)lParam);
388 return TRUE;
389 case LVN_COLUMNCLICK:
390 if (g_columnToSort == (DWORD)((LPNMLISTVIEW)lParam)->iSubItem)
391 g_invertSort = !g_invertSort;
392 else {
393 g_columnToSort = ((LPNMLISTVIEW)lParam)->iSubItem;
394 g_invertSort = FALSE;
395 }
396
397 ListView_SortItems(hWnd, CompareFunc, (WPARAM)hWnd);
398 return TRUE;
399 case NM_DBLCLK:
400 case NM_RETURN:
401 {
402 SendMessage(hFrameWnd, WM_COMMAND, MAKEWPARAM(ID_EDIT_MODIFY, 0), 0);
403 }
404 return TRUE;
405 case NM_SETFOCUS:
406 g_pChildWnd->nFocusPanel = 0;
407 break;
408 case LVN_BEGINLABELEDIT:
409 {
410 PLINE_INFO lineinfo;
411 Info = (NMLVDISPINFO*)lParam;
412 if(Info)
413 {
414 lineinfo = (PLINE_INFO)Info->item.lParam;
415 if(!lineinfo->name || !_tcscmp(lineinfo->name, _T("")))
416 {
417 *Result = TRUE;
418 }
419 else
420 {
421 *Result = FALSE;
422 }
423 }
424 else
425 *Result = TRUE;
426 return TRUE;
427 }
428 case LVN_ENDLABELEDIT:
429 {
430 PLINE_INFO lineinfo;
431 Info = (NMLVDISPINFO*)lParam;
432 if(Info && Info->item.pszText)
433 {
434 lineinfo = (PLINE_INFO)Info->item.lParam;
435 if(!lineinfo->name || !_tcscmp(lineinfo->name, _T("")))
436 {
437 *Result = FALSE;
438 }
439 else
440 {
441 //if((ret = RenameValue(lineinfo->name, Info->item.pszText)) != ERROR_SUCCESS)
442 {
443 TCHAR msg[128], caption[128];
444
445 LoadString(hInst, IDS_ERR_RENVAL_CAPTION, caption, sizeof(caption)/sizeof(TCHAR));
446 if(_tcslen(Info->item.pszText) == 0)
447 {
448 LoadString(hInst, IDS_ERR_RENVAL_TOEMPTY, msg, sizeof(msg)/sizeof(TCHAR));
449 MessageBox(0, msg, NULL, 0);
450 *Result = TRUE;
451 }
452 else
453 {
454 HKEY hKeyRoot;
455 LPCTSTR keyPath;
456 LONG lResult;
457 keyPath = GetItemPath(g_pChildWnd->hTreeWnd, 0, &hKeyRoot);
458 lResult = RegRenameValue(hKeyRoot, keyPath, Info->item.pszText, lineinfo->name);
459 if (lineinfo->name)
460 LocalFree(lineinfo->name);
461 lineinfo->name = _tcsdup(Info->item.pszText);
462 *Result = TRUE;
463 return (lResult == ERROR_SUCCESS);
464 }
465 }
466 }
467 }
468 else
469 *Result = TRUE;
470 return TRUE;
471 }
472 }
473 return FALSE;
474 }
475
476
477 HWND CreateListView(HWND hwndParent, HMENU id)
478 {
479 RECT rcClient;
480 HWND hwndLV;
481
482 /* Get the dimensions of the parent window's client area, and create the list view control. */
483 GetClientRect(hwndParent, &rcClient);
484 hwndLV = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, _T("List View"),
485 WS_VISIBLE | WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_EDITLABELS,
486 0, 0, rcClient.right, rcClient.bottom,
487 hwndParent, id, hInst, NULL);
488 if (!hwndLV) return NULL;
489
490 /* Initialize the image list, and add items to the control. */
491 if (!CreateListColumns(hwndLV)) goto fail;
492 if (!InitListViewImageLists(hwndLV)) goto fail;
493
494 return hwndLV;
495 fail:
496 DestroyWindow(hwndLV);
497 return NULL;
498 }
499
500 void DestroyListView(HWND hwndLV) {
501 INT count, i;
502 LVITEM item;
503
504 if (g_valueName)
505 HeapFree(GetProcessHeap(), 0, g_valueName);
506
507 count = ListView_GetItemCount(hwndLV);
508 for (i = 0; i < count; i++) {
509 item.mask = LVIF_PARAM;
510 item.iItem = i;
511 ListView_GetItem(hwndLV, &item);
512 free(((LINE_INFO*)item.lParam)->name);
513 HeapFree(GetProcessHeap(), 0, (void*)item.lParam);
514 }
515
516 }
517
518 BOOL RefreshListView(HWND hwndLV, HKEY hKey, LPCTSTR keyPath)
519 {
520 DWORD max_sub_key_len;
521 DWORD max_val_name_len;
522 DWORD max_val_size;
523 DWORD val_count;
524 HKEY hNewKey;
525 LONG errCode;
526 INT count, i;
527 LVITEM item;
528 BOOL AddedDefault = FALSE;
529
530 if (!hwndLV) return FALSE;
531
532 ListView_EditLabel(hwndLV, -1);
533
534 SendMessage(hwndLV, WM_SETREDRAW, FALSE, 0);
535 count = ListView_GetItemCount(hwndLV);
536 for (i = 0; i < count; i++) {
537 item.mask = LVIF_PARAM;
538 item.iItem = i;
539 ListView_GetItem(hwndLV, &item);
540 free(((LINE_INFO*)item.lParam)->name);
541 HeapFree(GetProcessHeap(), 0, (void*)item.lParam);
542 }
543 g_columnToSort = ~0UL;
544 ListView_DeleteAllItems(hwndLV);
545
546 if(!hKey) return FALSE;
547
548 errCode = RegOpenKeyEx(hKey, keyPath, 0, KEY_READ, &hNewKey);
549 if (errCode != ERROR_SUCCESS) return FALSE;
550
551 /* get size information and resize the buffers if necessary */
552 errCode = RegQueryInfoKey(hNewKey, NULL, NULL, NULL, NULL, &max_sub_key_len, NULL,
553 &val_count, &max_val_name_len, &max_val_size, NULL, NULL);
554
555 if (errCode == ERROR_SUCCESS) {
556 TCHAR* ValName = HeapAlloc(GetProcessHeap(), 0, ++max_val_name_len * sizeof(TCHAR));
557 DWORD dwValNameLen = max_val_name_len;
558 BYTE* ValBuf = HeapAlloc(GetProcessHeap(), 0, max_val_size + sizeof(TCHAR));
559 DWORD dwValSize = max_val_size;
560 DWORD dwIndex = 0L;
561 DWORD dwValType;
562 /* if (RegQueryValueEx(hNewKey, NULL, NULL, &dwValType, ValBuf, &dwValSize) == ERROR_SUCCESS) { */
563 /* AddEntryToList(hwndLV, _T("(Default)"), dwValType, ValBuf, dwValSize); */
564 /* } */
565 /* dwValSize = max_val_size; */
566 while (RegEnumValue(hNewKey, dwIndex, ValName, &dwValNameLen, NULL, &dwValType, ValBuf, &dwValSize) == ERROR_SUCCESS) {
567 /* Add a terminating 0 character. Usually this is only necessary for strings. */
568 ((TCHAR*)ValBuf)[dwValSize/sizeof(TCHAR)] = 0;
569 AddEntryToList(hwndLV, ValName, dwValType, ValBuf, dwValSize, -1, TRUE);
570 dwValNameLen = max_val_name_len;
571 dwValSize = max_val_size;
572 dwValType = 0L;
573 ++dwIndex;
574 if(!_tcscmp(ValName, _T("")))
575 {
576 AddedDefault = TRUE;
577 }
578 }
579 HeapFree(GetProcessHeap(), 0, ValBuf);
580 HeapFree(GetProcessHeap(), 0, ValName);
581 }
582 if(!AddedDefault)
583 {
584 AddEntryToList(hwndLV, _T(""), REG_SZ, NULL, 0, 0, FALSE);
585 }
586 ListView_SortItems(hwndLV, CompareFunc, (WPARAM)hwndLV);
587 RegCloseKey(hNewKey);
588 SendMessage(hwndLV, WM_SETREDRAW, TRUE, 0);
589
590 return TRUE;
591 }