Add .keep guard files in order to restore lost but empty directories we had with...
[reactos.git] / base / applications / rapps / misc.c
1 /*
2 * PROJECT: ReactOS Applications Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/rapps/misc.c
5 * PURPOSE: Misc functions
6 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org)
7 * Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
8 */
9
10 #include "rapps.h"
11 #include <sha1.h>
12
13 /* SESSION Operation */
14 #define EXTRACT_FILLFILELIST 0x00000001
15 #define EXTRACT_EXTRACTFILES 0x00000002
16
17 static HANDLE hLog = NULL;
18 WCHAR szCachedINISectionLocale[MAX_PATH] = L"Section.";
19 WCHAR szCachedINISectionLocaleNeutral[MAX_PATH] = {0};
20 BYTE bCachedSectionStatus = FALSE;
21
22 typedef struct
23 {
24 int erfOper;
25 int erfType;
26 BOOL fError;
27 } ERF, *PERF;
28
29 struct FILELIST
30 {
31 LPSTR FileName;
32 struct FILELIST *next;
33 BOOL DoExtract;
34 };
35
36 typedef struct
37 {
38 INT FileSize;
39 ERF Error;
40 struct FILELIST *FileList;
41 INT FileCount;
42 INT Operation;
43 CHAR Destination[MAX_PATH];
44 CHAR CurrentFile[MAX_PATH];
45 CHAR Reserved[MAX_PATH];
46 struct FILELIST *FilterList;
47 } SESSION;
48
49 HRESULT (WINAPI *pfnExtract)(SESSION *dest, LPCSTR szCabName);
50
51
52 INT
53 GetSystemColorDepth(VOID)
54 {
55 DEVMODE pDevMode;
56 INT ColorDepth;
57
58 pDevMode.dmSize = sizeof(pDevMode);
59 pDevMode.dmDriverExtra = 0;
60
61 if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &pDevMode))
62 {
63 /* TODO: Error message */
64 return ILC_COLOR;
65 }
66
67 switch (pDevMode.dmBitsPerPel)
68 {
69 case 32: ColorDepth = ILC_COLOR32; break;
70 case 24: ColorDepth = ILC_COLOR24; break;
71 case 16: ColorDepth = ILC_COLOR16; break;
72 case 8: ColorDepth = ILC_COLOR8; break;
73 case 4: ColorDepth = ILC_COLOR4; break;
74 default: ColorDepth = ILC_COLOR; break;
75 }
76
77 return ColorDepth;
78 }
79
80 int
81 GetWindowWidth(HWND hwnd)
82 {
83 RECT Rect;
84
85 GetWindowRect(hwnd, &Rect);
86 return (Rect.right - Rect.left);
87 }
88
89 int
90 GetWindowHeight(HWND hwnd)
91 {
92 RECT Rect;
93
94 GetWindowRect(hwnd, &Rect);
95 return (Rect.bottom - Rect.top);
96 }
97
98 int
99 GetClientWindowWidth(HWND hwnd)
100 {
101 RECT Rect;
102
103 GetClientRect(hwnd, &Rect);
104 return (Rect.right - Rect.left);
105 }
106
107 int
108 GetClientWindowHeight(HWND hwnd)
109 {
110 RECT Rect;
111
112 GetClientRect(hwnd, &Rect);
113 return (Rect.bottom - Rect.top);
114 }
115
116 VOID
117 CopyTextToClipboard(LPCWSTR lpszText)
118 {
119 HRESULT hr;
120
121 if (OpenClipboard(NULL))
122 {
123 HGLOBAL ClipBuffer;
124 WCHAR *Buffer;
125 DWORD cchBuffer;
126
127 EmptyClipboard();
128 cchBuffer = wcslen(lpszText) + 1;
129 ClipBuffer = GlobalAlloc(GMEM_DDESHARE, cchBuffer * sizeof(WCHAR));
130 Buffer = GlobalLock(ClipBuffer);
131 hr = StringCchCopyW(Buffer, cchBuffer, lpszText);
132 GlobalUnlock(ClipBuffer);
133
134 if (SUCCEEDED(hr))
135 SetClipboardData(CF_UNICODETEXT, ClipBuffer);
136
137 CloseClipboard();
138 }
139 }
140
141 VOID
142 SetWelcomeText(VOID)
143 {
144 WCHAR szText[MAX_STR_LEN*3];
145
146 LoadStringW(hInst, IDS_WELCOME_TITLE, szText, _countof(szText));
147 NewRichEditText(szText, CFE_BOLD);
148
149 LoadStringW(hInst, IDS_WELCOME_TEXT, szText, _countof(szText));
150 InsertRichEditText(szText, 0);
151
152 LoadStringW(hInst, IDS_WELCOME_URL, szText, _countof(szText));
153 InsertRichEditText(szText, CFM_LINK);
154 }
155
156 VOID
157 ShowPopupMenu(HWND hwnd, UINT MenuID, UINT DefaultItem)
158 {
159 HMENU hMenu = NULL;
160 HMENU hPopupMenu;
161 MENUITEMINFO mii;
162 POINT pt;
163
164 if (MenuID)
165 {
166 hMenu = LoadMenuW(hInst, MAKEINTRESOURCEW(MenuID));
167 hPopupMenu = GetSubMenu(hMenu, 0);
168 }
169 else
170 hPopupMenu = GetMenu(hwnd);
171
172 ZeroMemory(&mii, sizeof(mii));
173 mii.cbSize = sizeof(mii);
174 mii.fMask = MIIM_STATE;
175 GetMenuItemInfo(hPopupMenu, DefaultItem, FALSE, &mii);
176
177 if (!(mii.fState & MFS_GRAYED))
178 SetMenuDefaultItem(hPopupMenu, DefaultItem, FALSE);
179
180 GetCursorPos(&pt);
181
182 SetForegroundWindow(hwnd);
183 TrackPopupMenu(hPopupMenu, 0, pt.x, pt.y, 0, hMainWnd, NULL);
184
185 if (hMenu)
186 DestroyMenu(hMenu);
187 }
188
189 BOOL
190 StartProcess(LPWSTR lpPath, BOOL Wait)
191 {
192 PROCESS_INFORMATION pi;
193 STARTUPINFOW si;
194 DWORD dwRet;
195 MSG msg;
196
197 ZeroMemory(&si, sizeof(si));
198 si.cb = sizeof(si);
199 si.dwFlags = STARTF_USESHOWWINDOW;
200 si.wShowWindow = SW_SHOW;
201
202 if (!CreateProcessW(NULL, lpPath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
203 {
204 return FALSE;
205 }
206
207 CloseHandle(pi.hThread);
208 if (Wait) EnableWindow(hMainWnd, FALSE);
209
210 while (Wait)
211 {
212 dwRet = MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, INFINITE, QS_ALLEVENTS);
213 if (dwRet == WAIT_OBJECT_0 + 1)
214 {
215 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
216 {
217 TranslateMessage(&msg);
218 DispatchMessage(&msg);
219 }
220 }
221 else
222 {
223 if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED)
224 break;
225 }
226 }
227
228 CloseHandle(pi.hProcess);
229
230 if (Wait)
231 {
232 EnableWindow(hMainWnd, TRUE);
233 SetForegroundWindow(hMainWnd);
234 SetFocus(hMainWnd);
235 }
236
237 return TRUE;
238 }
239
240 BOOL
241 GetStorageDirectory(PWCHAR lpDirectory, DWORD cch)
242 {
243 if (cch < MAX_PATH)
244 return FALSE;
245
246 if (!SHGetSpecialFolderPathW(NULL, lpDirectory, CSIDL_LOCAL_APPDATA, TRUE))
247 return FALSE;
248
249 if (FAILED(StringCchCatW(lpDirectory, cch, L"\\rapps")))
250 return FALSE;
251
252 if (!CreateDirectoryW(lpDirectory, NULL) &&
253 GetLastError() != ERROR_ALREADY_EXISTS)
254 {
255 return FALSE;
256 }
257
258 return TRUE;
259 }
260
261 BOOL
262 ExtractFilesFromCab(LPWSTR lpCabName, LPWSTR lpOutputPath)
263 {
264 HINSTANCE hCabinetDll;
265 CHAR szCabName[MAX_PATH];
266 SESSION Dest;
267 HRESULT Result;
268
269 hCabinetDll = LoadLibraryW(L"cabinet.dll");
270 if (hCabinetDll)
271 {
272 pfnExtract = (void *) GetProcAddress(hCabinetDll, "Extract");
273 if (pfnExtract)
274 {
275 ZeroMemory(&Dest, sizeof(Dest));
276
277 WideCharToMultiByte(CP_ACP, 0, lpOutputPath, -1, Dest.Destination, MAX_PATH, NULL, NULL);
278 WideCharToMultiByte(CP_ACP, 0, lpCabName, -1, szCabName, MAX_PATH, NULL, NULL);
279 Dest.Operation = EXTRACT_FILLFILELIST;
280
281 Result = pfnExtract(&Dest, szCabName);
282 if (Result == S_OK)
283 {
284 Dest.Operation = EXTRACT_EXTRACTFILES;
285 Result = pfnExtract(&Dest, szCabName);
286 if (Result == S_OK)
287 {
288 FreeLibrary(hCabinetDll);
289 return TRUE;
290 }
291 }
292 }
293 FreeLibrary(hCabinetDll);
294 }
295
296 return FALSE;
297 }
298
299 VOID
300 InitLogs(VOID)
301 {
302 WCHAR szBuf[MAX_PATH] = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\ReactOS Application Manager";
303 WCHAR szPath[MAX_PATH];
304 DWORD dwCategoryNum = 1;
305 DWORD dwDisp, dwData;
306 HKEY hKey;
307
308 if (!SettingsInfo.bLogEnabled) return;
309
310 if (RegCreateKeyExW(HKEY_LOCAL_MACHINE,
311 szBuf, 0, NULL,
312 REG_OPTION_NON_VOLATILE,
313 KEY_WRITE, NULL, &hKey, &dwDisp) != ERROR_SUCCESS)
314 {
315 return;
316 }
317
318 if (!GetModuleFileNameW(NULL, szPath, _countof(szPath)))
319 return;
320
321 if (RegSetValueExW(hKey,
322 L"EventMessageFile",
323 0,
324 REG_EXPAND_SZ,
325 (LPBYTE)szPath,
326 (DWORD)(wcslen(szPath) + 1) * sizeof(WCHAR)) != ERROR_SUCCESS)
327 {
328 RegCloseKey(hKey);
329 return;
330 }
331
332 dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
333 EVENTLOG_INFORMATION_TYPE;
334
335 if (RegSetValueExW(hKey,
336 L"TypesSupported",
337 0,
338 REG_DWORD,
339 (LPBYTE)&dwData,
340 sizeof(DWORD)) != ERROR_SUCCESS)
341 {
342 RegCloseKey(hKey);
343 return;
344 }
345
346 if (RegSetValueExW(hKey,
347 L"CategoryMessageFile",
348 0,
349 REG_EXPAND_SZ,
350 (LPBYTE)szPath,
351 (DWORD)(wcslen(szPath) + 1) * sizeof(WCHAR)) != ERROR_SUCCESS)
352 {
353 RegCloseKey(hKey);
354 return;
355 }
356
357 if (RegSetValueExW(hKey,
358 L"CategoryCount",
359 0,
360 REG_DWORD,
361 (LPBYTE)&dwCategoryNum,
362 sizeof(DWORD)) != ERROR_SUCCESS)
363 {
364 RegCloseKey(hKey);
365 return;
366 }
367
368 RegCloseKey(hKey);
369
370 hLog = RegisterEventSourceW(NULL, L"ReactOS Application Manager");
371 }
372
373
374 VOID
375 FreeLogs(VOID)
376 {
377 if (hLog) DeregisterEventSource(hLog);
378 }
379
380
381 BOOL
382 WriteLogMessage(WORD wType, DWORD dwEventID, LPWSTR lpMsg)
383 {
384 if (!SettingsInfo.bLogEnabled) return TRUE;
385
386 if (!ReportEventW(hLog,
387 wType,
388 0,
389 dwEventID,
390 NULL,
391 1,
392 0,
393 (LPCWSTR*)&lpMsg,
394 NULL))
395 {
396 return FALSE;
397 }
398
399 return TRUE;
400 }
401
402
403 LPWSTR GetINIFullPath(LPCWSTR lpFileName)
404 {
405 WCHAR szDir[MAX_PATH];
406 static WCHAR szBuffer[MAX_PATH];
407
408 GetStorageDirectory(szDir, _countof(szDir));
409 StringCbPrintfW(szBuffer, sizeof(szBuffer), L"%ls\\rapps\\%ls", szDir, lpFileName);
410
411 return szBuffer;
412 }
413
414
415 UINT ParserGetString(LPCWSTR lpKeyName, LPWSTR lpReturnedString, UINT nSize, LPCWSTR lpFileName)
416 {
417 PWSTR lpFullFileName = GetINIFullPath(lpFileName);
418 DWORD dwResult;
419
420 /* we don't have cached section strings for the current system language, create them */
421 if(bCachedSectionStatus == FALSE)
422 {
423 WCHAR szLocale[4 + 1];
424 DWORD len;
425
426 /* find out what is the current system lang code (e.g. "0a") and append it to SectionLocale */
427 GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE,
428 szLocale, _countof(szLocale));
429
430 StringCbCatW(szCachedINISectionLocale, sizeof(szCachedINISectionLocale), szLocale);
431
432 /* copy the locale-dependent string into the buffer of the future neutral one */
433 StringCbCopyW(szCachedINISectionLocaleNeutral,
434 sizeof(szCachedINISectionLocaleNeutral),
435 szCachedINISectionLocale);
436
437 /* turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part */
438 len = wcslen(szCachedINISectionLocale);
439
440 memmove((szCachedINISectionLocaleNeutral + len) - 4,
441 (szCachedINISectionLocaleNeutral + len) - 2,
442 (2 * sizeof(WCHAR)) + sizeof(UNICODE_NULL));
443
444 /* finally, mark us as cache-friendly for the next time */
445 bCachedSectionStatus = TRUE;
446 }
447
448 /* 1st - find localized strings (e.g. "Section.0c0a") */
449 dwResult = GetPrivateProfileStringW(szCachedINISectionLocale,
450 lpKeyName,
451 NULL,
452 lpReturnedString,
453 nSize,
454 lpFullFileName);
455
456 if (dwResult != 0)
457 return TRUE;
458
459 /* 2nd - if they weren't present check for neutral sub-langs/ generic translations (e.g. "Section.0a") */
460 dwResult = GetPrivateProfileStringW(szCachedINISectionLocaleNeutral,
461 lpKeyName,
462 NULL,
463 lpReturnedString,
464 nSize,
465 lpFullFileName);
466
467 if (dwResult != 0)
468 return TRUE;
469
470 /* 3rd - if they weren't present fallback to standard english strings (just "Section") */
471 dwResult = GetPrivateProfileStringW(L"Section",
472 lpKeyName,
473 NULL,
474 lpReturnedString,
475 nSize,
476 lpFullFileName);
477
478 return (dwResult != 0 ? TRUE : FALSE);
479 }
480
481 UINT ParserGetInt(LPCWSTR lpKeyName, LPCWSTR lpFileName)
482 {
483 WCHAR Buffer[30];
484 UNICODE_STRING BufferW;
485 ULONG Result;
486
487 /* grab the text version of our entry */
488 if (!ParserGetString(lpKeyName, Buffer, _countof(Buffer), lpFileName))
489 return FALSE;
490
491 if (!Buffer[0])
492 return FALSE;
493
494 /* convert it to an actual integer */
495 RtlInitUnicodeString(&BufferW, Buffer);
496 RtlUnicodeStringToInteger(&BufferW, 0, &Result);
497
498 return Result;
499 }
500
501 BOOL VerifyInteg(LPCWSTR lpSHA1Hash, LPCWSTR lpFileName)
502 {
503 BOOL ret = FALSE;
504 const unsigned char *file_map;
505 HANDLE file, map;
506
507 ULONG sha[5];
508 WCHAR buf[40 + 1];
509 SHA_CTX ctx;
510
511 LARGE_INTEGER size;
512 UINT i;
513
514 /* first off, does it exist at all? */
515 file = CreateFileW(lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
516
517 if (file == INVALID_HANDLE_VALUE)
518 return FALSE;
519
520 /* let's grab the actual file size to organize the mmap'ing rounds */
521 GetFileSizeEx(file, &size);
522
523 /* retrieve a handle to map the file contents to memory */
524 map = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL);
525 if (!map)
526 goto cleanup;
527
528 /* initialize the SHA-1 context */
529 A_SHAInit(&ctx);
530
531 /* map that thing in address space */
532 file_map = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
533 if (!file_map)
534 goto cleanup;
535
536 /* feed the data to the cookie monster */
537 A_SHAUpdate(&ctx, file_map, size.LowPart);
538
539 /* cool, we don't need this anymore */
540 UnmapViewOfFile(file_map);
541
542 /* we're done, compute the final hash */
543 A_SHAFinal(&ctx, sha);
544
545 for (i = 0; i < sizeof(sha); i++)
546 swprintf(buf + 2 * i, L"%02x", ((unsigned char *)sha)[i]);
547
548 /* does the resulting SHA1 match with the provided one? */
549 if (!_wcsicmp(buf, lpSHA1Hash))
550 ret = TRUE;
551
552 cleanup:
553 CloseHandle(map);
554 CloseHandle(file);
555
556 return ret;
557 }