CPatch: New memory management
[TortoiseGit.git] / src / Utils / Hooks.cpp
blob5edfe07690db84f62366c8b44ed34a0ddf906d5f
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2011-2016, 2018 - TortoiseGit
4 // Copyright (C) 2007-2008 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
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.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "Hooks.h"
22 #include "registry.h"
23 #include "StringUtils.h"
24 #include "TempFile.h"
25 #include "SmartHandle.h"
26 #include "Git.h"
27 #include "resource.h"
28 #include <afxtaskdialog.h>
29 #include <WinCrypt.h>
31 CHooks* CHooks::m_pInstance;
32 CTGitPath CHooks::m_RootPath;
34 static CString CalcSHA256(const CString& text)
36 HCRYPTPROV hProv = 0;
37 if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
38 return L"";
39 SCOPE_EXIT{ CryptReleaseContext(hProv, 0); };
41 HCRYPTHASH hHash = 0;
42 if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
43 return L"";
44 SCOPE_EXIT{ CryptDestroyHash(hHash); };
46 CStringA textA = CUnicodeUtils::GetUTF8(text);
47 if (!CryptHashData(hHash, (LPBYTE)(LPCSTR)textA, textA.GetLength(), 0))
48 return L"";
50 CString hash;
51 BYTE rgbHash[32];
52 DWORD cbHash = _countof(rgbHash);
53 if (!CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
54 return L"";
56 for (DWORD i = 0; i < cbHash; i++)
58 BYTE hi = rgbHash[i] >> 4;
59 BYTE lo = rgbHash[i] & 0xf;
60 hash.AppendChar(hi + (hi > 9 ? 87 : 48));
61 hash.AppendChar(lo + (lo > 9 ? 87 : 48));
64 return hash;
67 CHooks::CHooks()
71 CHooks::~CHooks()
75 bool CHooks::Create()
77 if (!m_pInstance)
78 m_pInstance = new CHooks();
79 CRegString reghooks(L"Software\\TortoiseGit\\hooks");
80 CString strhooks = reghooks;
81 ParseHookString(strhooks, false);
82 return true;
85 CHooks& CHooks::Instance()
87 return *m_pInstance;
90 void CHooks::Destroy()
92 delete m_pInstance;
95 bool CHooks::Save()
97 CString strhooks;
98 for (hookiterator it = begin(); it != end(); ++it)
100 if (it->second.bLocal)
101 continue;
102 strhooks += GetHookTypeString(it->first.htype);
103 strhooks += '\n';
104 if (!it->second.bEnabled)
105 strhooks += '!';
106 strhooks += it->first.path.GetWinPathString();
107 strhooks += '\n';
108 strhooks += it->second.commandline;
109 strhooks += '\n';
110 strhooks += (it->second.bWait ? L"true" : L"false");
111 strhooks += '\n';
112 strhooks += (it->second.bShow ? L"show" : L"hide");
113 strhooks += '\n';
115 CRegString reghooks(L"Software\\TortoiseGit\\hooks");
116 reghooks = strhooks;
117 if (reghooks.GetLastError())
118 return false;
120 if (g_Git.m_CurrentDir.IsEmpty() || GitAdminDir::IsBareRepo(g_Git.m_CurrentDir))
121 return true;
123 // load the .tgitconfig file
124 CAutoConfig gitconfig(true);
125 git_config_add_file_ondisk(gitconfig, CGit::GetGitPathStringA(g_Git.CombinePath(L".tgitconfig")), GIT_CONFIG_LEVEL_LOCAL, nullptr, FALSE); // this needs to have the second highest priority
127 // delete all existing local hooks
128 for (const auto& hook : { PROJECTPROPNAME_STARTCOMMITHOOK, PROJECTPROPNAME_PRECOMMITHOOK, PROJECTPROPNAME_POSTCOMMITHOOK, PROJECTPROPNAME_PREPUSHHOOK, PROJECTPROPNAME_POSTPUSHHOOK, PROJECTPROPNAME_PREREBASEHOOK })
130 CStringA hookA = CUnicodeUtils::GetUTF8(hook);
131 for (const auto& val : { "cmdline", "wait", "show" })
133 git_config_delete_entry(gitconfig, hookA + val);
137 // now save the local hooks to .tgitconfig
138 for (const auto& hook : CHooks::Instance())
140 if (!hook.second.bLocal)
141 continue;
143 CStringA sHookPropName;
144 switch (hook.first.htype)
146 case start_commit_hook:
147 sHookPropName = CUnicodeUtils::GetUTF8(PROJECTPROPNAME_STARTCOMMITHOOK);
148 break;
149 case pre_commit_hook:
150 sHookPropName = CUnicodeUtils::GetUTF8(PROJECTPROPNAME_PRECOMMITHOOK);
151 break;
152 case post_commit_hook:
153 sHookPropName = CUnicodeUtils::GetUTF8(PROJECTPROPNAME_POSTCOMMITHOOK);
154 break;
155 case pre_push_hook:
156 sHookPropName = CUnicodeUtils::GetUTF8(PROJECTPROPNAME_PREPUSHHOOK);
157 break;
158 case post_push_hook:
159 sHookPropName = CUnicodeUtils::GetUTF8(PROJECTPROPNAME_POSTPUSHHOOK);
160 break;
161 case pre_rebase_hook:
162 sHookPropName = CUnicodeUtils::GetUTF8(PROJECTPROPNAME_PREREBASEHOOK);
163 break;
165 git_config_set_string(gitconfig, sHookPropName + "cmdline", CUnicodeUtils::GetUTF8(hook.second.commandline));
166 git_config_set_bool(gitconfig, sHookPropName + "wait", hook.second.bWait);
167 git_config_set_bool(gitconfig, sHookPropName + "show", hook.second.bShow);
170 return true;
173 bool CHooks::Remove(const hookkey &key)
175 return (erase(key) > 0);
178 void CHooks::Add(hooktype ht, const CTGitPath& Path, LPCTSTR szCmd, bool bWait, bool bShow, bool bEnabled, bool bLocal)
180 hookkey key;
181 key.htype = ht;
182 key.path = Path;
183 hookiterator it = find(key);
184 if (it!=end())
185 erase(it);
187 hookcmd cmd;
188 cmd.commandline = szCmd;
189 cmd.bWait = bWait;
190 cmd.bShow = bShow;
191 cmd.bEnabled = bEnabled;
192 cmd.bLocal = bLocal;
193 insert(std::pair<hookkey, hookcmd>(key, cmd));
196 bool CHooks::SetEnabled(const hookkey& k, bool bEnabled)
198 auto it = find(k);
199 if (it == end())
200 return false;
201 if (it->second.bEnabled == bEnabled)
202 return false;
203 it->second.bEnabled = bEnabled;
204 return true;
207 CString CHooks::GetHookTypeString(hooktype t)
209 switch (t)
211 case start_commit_hook:
212 return L"start_commit_hook";
213 case pre_commit_hook:
214 return L"pre_commit_hook";
215 case post_commit_hook:
216 return L"post_commit_hook";
217 case pre_push_hook:
218 return L"pre_push_hook";
219 case post_push_hook:
220 return L"post_push_hook";
221 case pre_rebase_hook:
222 return L"pre_rebase_hook";
224 return L"";
227 hooktype CHooks::GetHookType(const CString& s)
229 if (s.Compare(L"start_commit_hook") == 0)
230 return start_commit_hook;
231 if (s.Compare(L"pre_commit_hook") == 0)
232 return pre_commit_hook;
233 if (s.Compare(L"post_commit_hook") == 0)
234 return post_commit_hook;
235 if (s.Compare(L"pre_push_hook") == 0)
236 return pre_push_hook;
237 if (s.Compare(L"post_push_hook") == 0)
238 return post_push_hook;
239 if (s.Compare(L"pre_rebase_hook") == 0)
240 return pre_rebase_hook;
242 return unknown_hook;
245 void CHooks::SetProjectProperties(const CTGitPath& Path, const ProjectProperties& pp)
247 m_RootPath = Path;
249 CString sHookString;
250 if (!pp.sPreCommitHook.IsEmpty())
251 sHookString += GetHookTypeString(pre_commit_hook) + L"\n?\n" + pp.sPreCommitHook + L"\n";
252 if (!pp.sStartCommitHook.IsEmpty())
253 sHookString += GetHookTypeString(start_commit_hook) + L"\n?\n" + pp.sStartCommitHook + L"\n";
254 if (!pp.sPostCommitHook.IsEmpty())
255 sHookString += GetHookTypeString(post_commit_hook) + L"\n?\n" + pp.sPostCommitHook + L"\n";
256 if (!pp.sPrePushHook.IsEmpty())
257 sHookString += GetHookTypeString(pre_push_hook) + L"\n?\n" + pp.sPrePushHook + L"\n";
258 if (!pp.sPostPushHook.IsEmpty())
259 sHookString += GetHookTypeString(post_push_hook) + L"\n?\n" + pp.sPostPushHook + L"\n";
260 if (!pp.sPreRebaseHook.IsEmpty())
261 sHookString += GetHookTypeString(pre_rebase_hook) + L"\n?\n" + pp.sPreRebaseHook + L"\n";
262 ParseHookString(sHookString, true);
265 void CHooks::AddParam(CString& sCmd, const CString& param)
267 sCmd += L" \"";
268 sCmd += param;
269 sCmd += L'"';
272 void CHooks::AddPathParam(CString& sCmd, const CTGitPathList& pathList)
274 CTGitPath temppath = CTempFiles::Instance().GetTempFilePath(true);
275 pathList.WriteToFile(temppath.GetWinPathString(), true);
276 AddParam(sCmd, temppath.GetWinPathString());
279 void CHooks::AddCWDParam(CString& sCmd, const CString& workingTree)
281 AddParam(sCmd, workingTree);
284 void CHooks::AddErrorParam(CString& sCmd, const CString& error)
286 CTGitPath tempPath;
287 tempPath = CTempFiles::Instance().GetTempFilePath(true);
288 CStringUtils::WriteStringToTextFile(tempPath.GetWinPath(), (LPCTSTR)error);
289 AddParam(sCmd, tempPath.GetWinPathString());
292 CTGitPath CHooks::AddMessageFileParam(CString& sCmd, const CString& message)
294 CTGitPath tempPath;
295 tempPath = CTempFiles::Instance().GetTempFilePath(true);
296 CStringUtils::WriteStringToTextFile(tempPath.GetWinPath(), (LPCTSTR)message);
297 AddParam(sCmd, tempPath.GetWinPathString());
298 return tempPath;
301 bool CHooks::StartCommit(HWND hWnd, const CString& workingTree, const CTGitPathList& pathList, CString& message, DWORD& exitcode, CString& error)
303 auto it = FindItem(start_commit_hook, workingTree);
304 if (it == end())
305 return false;
306 if (!ApproveHook(hWnd, it))
308 exitcode = 1;
309 error.LoadString(IDS_ERR_HOOKNOTAPPROVED);
310 return false;
312 CString sCmd = it->second.commandline;
313 sCmd.Replace(L"%root%", m_RootPath.GetWinPathString());
314 AddPathParam(sCmd, pathList);
315 CTGitPath temppath = AddMessageFileParam(sCmd, message);
316 AddCWDParam(sCmd, workingTree);
317 exitcode = RunScript(sCmd, workingTree, error, it->second.bWait, it->second.bShow);
318 if (!exitcode && !temppath.IsEmpty())
320 CStringUtils::ReadStringFromTextFile(temppath.GetWinPathString(), message);
322 return true;
325 bool CHooks::PreCommit(HWND hWnd, const CString& workingTree, const CTGitPathList& pathList, CString& message, DWORD& exitcode, CString& error)
327 auto it = FindItem(pre_commit_hook, workingTree);
328 if (it == end())
329 return false;
330 if (!ApproveHook(hWnd, it))
332 exitcode = 1;
333 error.LoadString(IDS_ERR_HOOKNOTAPPROVED);
334 return false;
336 CString sCmd = it->second.commandline;
337 sCmd.Replace(L"%root%", m_RootPath.GetWinPathString());
338 AddPathParam(sCmd, pathList);
339 CTGitPath temppath = AddMessageFileParam(sCmd, message);
340 AddCWDParam(sCmd, workingTree);
341 exitcode = RunScript(sCmd, workingTree, error, it->second.bWait, it->second.bShow);
342 if (!exitcode && !temppath.IsEmpty())
343 CStringUtils::ReadStringFromTextFile(temppath.GetWinPathString(), message);
344 return true;
347 bool CHooks::PostCommit(HWND hWnd, const CString& workingTree, bool amend, DWORD& exitcode, CString& error)
349 auto it = FindItem(post_commit_hook, workingTree);
350 if (it == end())
351 return false;
352 if (!ApproveHook(hWnd, it))
354 exitcode = 1;
355 error.LoadString(IDS_ERR_HOOKNOTAPPROVED);
356 return false;
358 CString sCmd = it->second.commandline;
359 sCmd.Replace(L"%root%", m_RootPath.GetWinPathString());
360 AddCWDParam(sCmd, workingTree);
361 if (amend)
362 AddParam(sCmd, L"true");
363 else
364 AddParam(sCmd, L"false");
365 exitcode = RunScript(sCmd, workingTree, error, it->second.bWait, it->second.bShow);
366 return true;
369 bool CHooks::PrePush(HWND hWnd, const CString& workingTree, DWORD& exitcode, CString& error)
371 auto it = FindItem(pre_push_hook, workingTree);
372 if (it == end())
373 return false;
374 if (!ApproveHook(hWnd, it))
376 exitcode = 1;
377 error.LoadString(IDS_ERR_HOOKNOTAPPROVED);
378 return false;
380 CString sCmd = it->second.commandline;
381 sCmd.Replace(L"%root%", m_RootPath.GetWinPathString());
382 AddErrorParam(sCmd, error);
383 AddCWDParam(sCmd, workingTree);
384 exitcode = RunScript(sCmd, workingTree, error, it->second.bWait, it->second.bShow);
385 return true;
388 bool CHooks::PostPush(HWND hWnd, const CString& workingTree, DWORD& exitcode, CString& error)
390 auto it = FindItem(post_push_hook, workingTree);
391 if (it == end())
392 return false;
393 if (!ApproveHook(hWnd, it))
395 exitcode = 1;
396 error.LoadString(IDS_ERR_HOOKNOTAPPROVED);
397 return false;
399 CString sCmd = it->second.commandline;
400 sCmd.Replace(L"%root%", m_RootPath.GetWinPathString());
401 AddErrorParam(sCmd, error);
402 AddCWDParam(sCmd, workingTree);
403 exitcode = RunScript(sCmd, workingTree, error, it->second.bWait, it->second.bShow);
404 return true;
407 bool CHooks::PreRebase(HWND hWnd, const CString& workingTree, const CString& upstream, const CString& rebasedBranch, DWORD& exitcode, CString& error)
409 auto it = FindItem(pre_rebase_hook, workingTree);
410 if (it == end())
411 return false;
412 if (!ApproveHook(hWnd, it))
414 exitcode = 1;
415 error.LoadString(IDS_ERR_HOOKNOTAPPROVED);
416 return false;
418 CString sCmd = it->second.commandline;
419 sCmd.Replace(L"%root%", m_RootPath.GetWinPathString());
420 AddParam(sCmd, upstream);
421 AddParam(sCmd, rebasedBranch);
422 AddErrorParam(sCmd, error);
423 AddCWDParam(sCmd, workingTree);
424 exitcode = RunScript(sCmd, workingTree, error, it->second.bWait, it->second.bShow);
425 return true;
428 bool CHooks::IsHookPresent(hooktype t, const CString& workingTree)
430 auto it = FindItem(t, workingTree);
431 return it != end();
434 hookiterator CHooks::FindItem(hooktype t, const CString& workingTree)
436 hookkey key;
437 CTGitPath path = workingTree;
440 key.htype = t;
441 key.path = path;
442 auto it = find(key);
443 if (it != end() && it->second.bEnabled)
444 return it;
445 path = path.GetContainingDirectory();
446 } while(!path.IsEmpty());
447 // look for a script with a path as '*'
448 key.htype = t;
449 key.path = CTGitPath(L"*");
450 auto it = find(key);
451 if (it != end() && it->second.bEnabled)
453 return it;
456 // try the root path
457 key.htype = t;
458 key.path = m_RootPath;
459 it = find(key);
460 if (it != end())
461 return it;
463 // look for a script with a path as '*'
464 key.htype = t;
465 key.path = CTGitPath(L"*");
466 it = find(key);
467 if (it != end())
468 return it;
470 return end();
473 void CHooks::ParseHookString(CString strhooks, bool bLocal)
475 // now fill the map with all the hooks defined in the string
476 // the string consists of multiple lines, where one hook script is defined
477 // as four lines:
478 // line 1: the hook type
479 // line 2: path to working copy where to apply the hook script,
480 // if it starts with "!" this hook is disabled (this should provide backward and forward compatibility)
481 // if it starts with "?" this hook is for the current project folder
482 // line 3: command line to execute
483 // line 4: 'true' or 'false' for waiting for the script to finish
484 // line 5: 'show' or 'hide' on how to start the hook script
485 hookkey key;
486 int pos = 0;
487 hookcmd cmd;
488 cmd.bLocal = bLocal;
489 cmd.bApproved = !bLocal; // user configured scripts are pre-approved
490 cmd.bStored = false;
491 while ((pos = strhooks.Find(L'\n')) >= 0)
493 // line 1
494 key.htype = GetHookType(strhooks.Left(pos));
495 if (pos + 1 < strhooks.GetLength())
496 strhooks = strhooks.Mid(pos + 1);
497 else
498 strhooks.Empty();
499 bool bComplete = false;
500 if ((pos = strhooks.Find(L'\n')) >= 0)
502 // line 2
503 cmd.bEnabled = true;
504 if (strhooks[0] == L'!' && pos > 1)
506 cmd.bEnabled = false;
507 strhooks = strhooks.Mid((int)wcslen(L"!"));
508 --pos;
510 if (strhooks[0] == L'?' && pos > 0)
511 key.path = CTGitPath(m_RootPath);
512 else
513 key.path = CTGitPath(strhooks.Left(pos));
514 if (pos + 1 < strhooks.GetLength())
515 strhooks = strhooks.Mid(pos + 1);
516 else
517 strhooks.Empty();
518 if ((pos = strhooks.Find(L'\n')) >= 0)
520 // line 3
521 cmd.commandline = strhooks.Left(pos);
522 if (pos + 1 < strhooks.GetLength())
523 strhooks = strhooks.Mid(pos + 1);
524 else
525 strhooks.Empty();
526 if ((pos = strhooks.Find(L'\n')) >= 0)
528 // line 4
529 cmd.bWait = (strhooks.Left(pos).CompareNoCase(L"true") == 0);
530 if (pos + 1 < strhooks.GetLength())
531 strhooks = strhooks.Mid(pos + 1);
532 else
533 strhooks.Empty();
534 if ((pos = strhooks.Find(L'\n')) >= 0)
536 // line 5
537 cmd.bShow = (strhooks.Left(pos).CompareNoCase(L"show") == 0);
538 if (pos + 1 < strhooks.GetLength())
539 strhooks = strhooks.Mid(pos + 1);
540 else
541 strhooks.Empty();
542 if (cmd.bLocal)
544 CString temp;
545 temp.Format(L"%s|%d%s", m_RootPath.GetWinPath(), (int)key.htype, (LPCTSTR)cmd.commandline);
547 cmd.sRegKey = L"Software\\TortoiseGit\\approvedhooks\\" + CalcSHA256(temp);
548 CRegDWORD reg(cmd.sRegKey, 0);
549 cmd.bApproved = (DWORD(reg) != 0);
550 cmd.bStored = reg.exists();
553 bComplete = true;
558 if (bComplete)
559 m_pInstance->insert(std::pair<hookkey, hookcmd>(key, cmd));
563 DWORD CHooks::RunScript(CString cmd, LPCTSTR currentDir, CString& error, bool bWait, bool bShow)
565 DWORD exitcode = 0;
566 SECURITY_ATTRIBUTES sa = { 0 };
567 sa.nLength = sizeof(sa);
568 sa.bInheritHandle = TRUE;
570 CAutoFile hOut ;
571 CAutoFile hRedir;
572 CAutoFile hErr;
574 // clear the error string
575 error.Empty();
577 // Create Temp File for redirection
578 TCHAR szTempPath[MAX_PATH] = {0};
579 TCHAR szOutput[MAX_PATH] = {0};
580 TCHAR szErr[MAX_PATH] = {0};
581 GetTortoiseGitTempPath(_countof(szTempPath), szTempPath);
582 GetTempFileName(szTempPath, L"git", 0, szErr);
584 // setup redirection handles
585 // output handle must be WRITE mode, share READ
586 // redirect handle must be READ mode, share WRITE
587 hErr = CreateFile(szErr, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, nullptr);
589 if (!hErr)
591 error = CFormatMessageWrapper();
592 return (DWORD)-1;
595 hRedir = CreateFile(szErr, GENERIC_READ, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
597 if (!hRedir)
599 error = CFormatMessageWrapper();
600 return (DWORD)-1;
603 GetTempFileName(szTempPath, L"git", 0, szOutput);
604 hOut = CreateFile(szOutput, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, nullptr);
606 if (!hOut)
608 error = CFormatMessageWrapper();
609 return (DWORD)-1;
612 // setup startup info, set std out/err handles
613 // hide window
614 STARTUPINFO si = { 0 };
615 si.cb = sizeof(si);
616 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
617 si.hStdOutput = hOut;
618 si.hStdError = hErr;
619 si.wShowWindow = bShow ? SW_SHOW : SW_HIDE;
621 PROCESS_INFORMATION pi = { 0 };
622 if (!CreateProcess(nullptr, cmd.GetBuffer(), nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT, nullptr, currentDir, &si, &pi))
624 const DWORD err = GetLastError(); // preserve the CreateProcess error
625 error = CFormatMessageWrapper(err);
626 SetLastError(err);
627 cmd.ReleaseBuffer();
628 return (DWORD)-1;
630 cmd.ReleaseBuffer();
632 CloseHandle(pi.hThread);
634 // wait for process to finish, capture redirection and
635 // send it to the parent window/console
636 if (bWait)
638 DWORD dw;
639 char buf[256] = { 0 };
642 while (ReadFile(hRedir, &buf, sizeof(buf) - 1, &dw, nullptr))
644 if (dw == 0)
645 break;
646 error += CString(CStringA(buf,dw));
648 Sleep(150);
649 } while (WaitForSingleObject(pi.hProcess, 0) != WAIT_OBJECT_0);
651 // perform any final flushing
652 while (ReadFile(hRedir, &buf, sizeof(buf) - 1, &dw, nullptr))
654 if (dw == 0)
655 break;
657 error += CString(CStringA(buf, dw));
659 WaitForSingleObject(pi.hProcess, INFINITE);
660 GetExitCodeProcess(pi.hProcess, &exitcode);
662 CloseHandle(pi.hProcess);
663 DeleteFile(szOutput);
664 DeleteFile(szErr);
666 return exitcode;
669 bool CHooks::ApproveHook(HWND hWnd, hookiterator it)
671 if (it->second.bApproved || it->second.bStored)
672 return it->second.bApproved;
674 CString sQuestion;
675 sQuestion.Format(IDS_HOOKS_APPROVE_TASK1, (LPCWSTR)it->second.commandline);
676 bool bApproved = false;
677 bool bDoNotAskAgain = false;
678 CTaskDialog taskdlg(sQuestion, CString(MAKEINTRESOURCE(IDS_HOOKS_APPROVE_TASK2)), L"TortoiseGit", 0, TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW | TDF_SIZE_TO_CONTENT);
679 taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_HOOKS_APPROVE_TASK3)));
680 taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_HOOKS_APPROVE_TASK4)));
681 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
682 taskdlg.SetVerificationCheckboxText(CString(MAKEINTRESOURCE(IDS_HOOKS_APPROVE_TASK5)));
683 taskdlg.SetVerificationCheckbox(false);
684 taskdlg.SetDefaultCommandControl(2);
685 taskdlg.SetMainIcon(TD_WARNING_ICON);
686 taskdlg.SetFooterText(CString(MAKEINTRESOURCE(IDS_HOOKS_APPROVE_SECURITYHINT)));
687 bApproved = taskdlg.DoModal(hWnd) == 1;
688 bDoNotAskAgain = !!taskdlg.GetVerificationCheckboxState();
690 if (bDoNotAskAgain)
692 CRegDWORD reg(it->second.sRegKey, 0);
693 reg = bApproved ? 1 : 0;
694 it->second.bStored = true;
696 it->second.bApproved = bApproved;
697 return bApproved;