Fix typos
[TortoiseGit.git] / src / TortoiseProc / Settings / SettingGitCredential.cpp
blob7ebc19006b399b5ebe87f3272acff51e271644da
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013-2023 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program 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.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // SettingGitCredential.cpp : implementation file
22 #include "stdafx.h"
23 #include "TortoiseProc.h"
24 #include "SettingGitCredential.h"
25 #include "Settings.h"
26 #include "GitAdminDir.h"
27 #include "MessageBox.h"
28 #include "AppUtils.h"
29 #include "Git.h"
30 #include "PathUtils.h"
32 namespace SimpleCredentialType
34 static int Advanced = -1;
35 static int None = -1;
36 static int LocalWincred = -1;
37 static int LocalWinstore = -1;
38 static int LocalGCM = -1;
39 static int LocalGCMCore = -1;
40 static int GlobalWincred = -1;
41 static int GlobalWinstore = -1;
42 static int GlobalGCM = -1;
43 static int GlobalGCMCore = -1;
44 static int SystemWincred = -1;
45 static int SystemGCM = -1;
46 static int SystemGCMCore = -1;
49 namespace ConfigType
51 static int Local = -1;
52 static int Global = -1;
53 static int System = -1;
56 // CSettingGitCredential dialog
58 IMPLEMENT_DYNAMIC(CSettingGitCredential, ISettingsPropPage)
60 CSettingGitCredential::CSettingGitCredential()
61 : ISettingsPropPage(CSettingGitCredential::IDD)
62 , m_bUseHttpPath(FALSE)
63 , m_iSimpleStoredValue(-2)
67 CSettingGitCredential::~CSettingGitCredential()
71 void CSettingGitCredential::DoDataExchange(CDataExchange* pDX)
73 CPropertyPage::DoDataExchange(pDX);
74 DDX_Control(pDX, IDC_COMBO_SIMPLECREDENTIAL, m_ctrlSimpleCredential);
75 DDX_Control(pDX, IDC_LIST_REMOTE, m_ctrlUrlList);
76 DDX_Text(pDX, IDC_EDIT_URL, m_strUrl);
77 DDX_Text(pDX, IDC_COMBO_HELPER, m_strHelper);
78 DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUsername);
79 DDX_Check(pDX, IDC_CHECK_USEHTTPPATH, m_bUseHttpPath);
80 DDX_Control(pDX, IDC_COMBO_CONFIGTYPE, m_ctrlConfigType);
84 BEGIN_MESSAGE_MAP(CSettingGitCredential, CPropertyPage)
85 ON_CBN_SELCHANGE(IDC_COMBO_SIMPLECREDENTIAL, &CSettingGitCredential::OnCbnSelchangeComboSimplecredential)
86 ON_BN_CLICKED(IDC_BUTTON_ADD, &CSettingGitCredential::OnBnClickedButtonAdd)
87 ON_LBN_SELCHANGE(IDC_LIST_REMOTE, &CSettingGitCredential::OnLbnSelchangeListUrl)
88 ON_CBN_SELCHANGE(IDC_COMBO_CONFIGTYPE, &CSettingGitCredential::OnCbnSelchangeComboConfigType)
89 ON_EN_CHANGE(IDC_EDIT_URL, &CSettingGitCredential::OnEnChangeEditUrl)
90 ON_CBN_EDITCHANGE(IDC_COMBO_HELPER, &CSettingGitCredential::OnEnChangeEditHelper)
91 ON_CBN_SELCHANGE(IDC_COMBO_HELPER, &CSettingGitCredential::OnEnChangeEditHelper)
92 ON_EN_CHANGE(IDC_EDIT_USERNAME, &CSettingGitCredential::OnEnChangeEditUsername)
93 ON_BN_CLICKED(IDC_CHECK_USEHTTPPATH, &CSettingGitCredential::OnBnClickedCheckUsehttppath)
94 ON_BN_CLICKED(IDC_BUTTON_REMOVE, &CSettingGitCredential::OnBnClickedButtonRemove)
95 ON_BN_CLICKED(IDC_OPENSETTINGSELEVATED, &CSettingGitCredential::OnBnClickedOpensettingselevated)
96 ON_BN_CLICKED(IDC_WINDOWSCREDMGR, &CSettingGitCredential::OnBnClickedWindowscredmgr)
97 END_MESSAGE_MAP()
99 static CStringA RegexEscape(CStringA str)
101 CStringA result;
102 for (int i = 0; i < str.GetLength(); ++i)
104 char c = str[i];
105 switch (c)
107 case '\\': case '*': case '+': case '?': case '|':
108 case '{': case '[': case '(': case ')': case '^':
109 case '$': case '.': case '#': case ' ':
110 result.AppendChar('\\');
111 result.AppendChar(c);
112 break;
113 case '\t': result.Append("\\t"); break;
114 case '\n': result.Append("\\n"); break;
115 case '\r': result.Append("\\r"); break;
116 case '\f': result.Append("\\f"); break;
117 default: result.AppendChar(c); break;
121 return result;
124 BOOL CSettingGitCredential::OnInitDialog()
126 ISettingsPropPage::OnInitDialog();
128 AdjustControlSize(IDC_CHECK_USEHTTPPATH);
130 bool hasLocal = GitAdminDir::HasAdminDir(g_Git.m_CurrentDir);
132 m_ctrlUrlList.ResetContent();
134 AddConfigType(ConfigType::Local, CString(MAKEINTRESOURCE(IDS_SETTINGS_LOCAL)), hasLocal);
135 AddConfigType(ConfigType::Global, CString(MAKEINTRESOURCE(IDS_SETTINGS_GLOBAL)));
136 AddConfigType(ConfigType::System, CString(MAKEINTRESOURCE(IDS_SETTINGS_SYSTEM)));
137 m_ctrlConfigType.SetCurSel(0);
139 if (WincredExists())
140 static_cast<CComboBox*>(GetDlgItem(IDC_COMBO_HELPER))->AddString(L"wincred");
141 if (WinstoreExists())
142 static_cast<CComboBox*>(GetDlgItem(IDC_COMBO_HELPER))->AddString(GetWinstorePath());
143 if (GCMExists())
144 static_cast<CComboBox*>(GetDlgItem(IDC_COMBO_HELPER))->AddString(L"manager");
145 if (GCMCoreExists())
146 static_cast<CComboBox*>(GetDlgItem(IDC_COMBO_HELPER))->AddString(L"manager-core");
148 LoadList();
149 m_iSimpleStoredValue = m_ctrlSimpleCredential.GetCurSel();
151 if (!CAppUtils::IsAdminLogin())
153 reinterpret_cast<CButton*>(GetDlgItem(IDC_OPENSETTINGSELEVATED))->SetShield(TRUE);
154 GetDlgItem(IDC_OPENSETTINGSELEVATED)->EnableWindow(TRUE);
155 GetDlgItem(IDC_OPENSETTINGSELEVATED)->ShowWindow(SW_SHOW);
156 GetDlgItem(IDC_STATICELEVATIONNEEDED)->ShowWindow(SW_SHOW);
159 EnableAdvancedOptions();
161 UpdateData(FALSE);
162 return TRUE;
164 // CSettingGitCredential message handlers
166 void CSettingGitCredential::OnCbnSelchangeComboSimplecredential()
168 EnableAdvancedOptions();
169 SetModified();
170 m_ChangedMask |= CREDENTIAL_SIMPLE;
173 void CSettingGitCredential::OnBnClickedButtonAdd()
175 UpdateData();
177 if (m_strHelper.Trim().IsEmpty())
179 CMessageBox::Show(GetSafeHwnd(), IDS_GITCREDENTIAL_HELPEREMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
180 return;
183 m_ChangedMask = CREDENTIAL_URL | CREDENTIAL_HELPER | CREDENTIAL_USERNAME | CREDENTIAL_USEHTTPPATH;
184 int sel = m_ctrlConfigType.GetCurSel();
185 CString prefix = sel == ConfigType::System ? L"S" : sel == ConfigType::Global ? L"G" : L"L";
186 CString text;
187 if (!m_strUrl.IsEmpty())
188 text.Format(L"%s:%s", static_cast<LPCWSTR>(prefix), static_cast<LPCWSTR>(m_strUrl));
189 else
190 text = prefix;
191 if (IsUrlExist(text))
193 CString msg;
194 msg.Format(IDS_GITCREDENTIAL_OVERWRITEHELPER, static_cast<LPCWSTR>(m_strUrl));
195 if (CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES)
196 m_ChangedMask &= ~CREDENTIAL_URL;
197 else
198 return;
200 else
202 if (m_strUsername.IsEmpty())
203 m_ChangedMask &= ~CREDENTIAL_USERNAME;
204 if (!m_bUseHttpPath)
205 m_ChangedMask &= ~CREDENTIAL_USEHTTPPATH;
208 SaveSettings();
211 void CSettingGitCredential::EnableAdvancedOptions()
213 BOOL enable = m_ctrlSimpleCredential.GetCurSel() == SimpleCredentialType::Advanced ? TRUE : FALSE;
214 GetDlgItem(IDC_LIST_REMOTE)->EnableWindow(enable);
215 GetDlgItem(IDC_EDIT_URL)->EnableWindow(enable);
216 GetDlgItem(IDC_COMBO_HELPER)->EnableWindow(enable);
217 GetDlgItem(IDC_EDIT_USERNAME)->EnableWindow(enable);
218 GetDlgItem(IDC_COMBO_CONFIGTYPE)->EnableWindow(enable);
219 GetDlgItem(IDC_CHECK_USEHTTPPATH)->EnableWindow(enable);
221 bool canModifySystem = CAppUtils::IsAdminLogin() || (m_ctrlConfigType.GetCurSel() != ConfigType::System);
222 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(enable && canModifySystem);
223 GetDlgItem(IDC_BUTTON_REMOVE)->EnableWindow(enable && canModifySystem);
226 BOOL CSettingGitCredential::IsUrlExist(const CString& text)
228 CString str;
229 for(int i = 0; i < m_ctrlUrlList.GetCount();i++)
231 m_ctrlUrlList.GetText(i, str);
232 if (str == text)
233 return true;
235 return false;
238 void CSettingGitCredential::OnLbnSelchangeListUrl()
240 CWaitCursor wait;
242 if (m_ChangedMask & CREDENTIAL_ADVANCED_MASK)
244 if (CMessageBox::Show(GetSafeHwnd(), IDS_GITCREDENTIAL_SAVEHELPER, IDS_APPNAME, 1, IDI_QUESTION, IDS_SAVEBUTTON, IDS_DISCARDBUTTON) == 1)
245 SaveSettings();
247 m_ChangedMask &= ~CREDENTIAL_ADVANCED_MASK;
248 if (!m_ChangedMask)
249 SetModified(FALSE);
251 CString cmd, output;
252 int index = m_ctrlUrlList.GetCurSel();
253 if (index < 0)
255 m_strHelper.Empty();
256 m_strUrl.Empty();
257 m_strHelper.Empty();
258 m_bUseHttpPath = FALSE;
259 UpdateData(FALSE);
260 return;
263 CString text;
264 m_ctrlUrlList.GetText(index, text);
265 int pos = text.Find(L':');
266 CString prefix = pos >= 0 ? text.Left(pos) : text.Left(1);
267 m_ctrlConfigType.SetCurSel((prefix == L"S" || prefix == L"P") ? ConfigType::System : prefix == L"X" ? ConfigType::Global : prefix == L"G" ? ConfigType::Global : ConfigType::Local);
268 m_strUrl = pos >= 0 ? text.Mid(pos + 1) : CString();
270 m_strHelper = Load(L"helper");
271 m_strUsername = Load(L"username");
272 m_bUseHttpPath = Load(L"useHttpPath") == L"true" ? TRUE : FALSE;
274 bool canModifySystem = CAppUtils::IsAdminLogin() || (m_ctrlConfigType.GetCurSel() != ConfigType::System);
275 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(canModifySystem ? TRUE : FALSE);
276 GetDlgItem(IDC_BUTTON_REMOVE)->EnableWindow(canModifySystem ? TRUE : FALSE);
277 this->UpdateData(FALSE);
280 void CSettingGitCredential::OnCbnSelchangeComboConfigType()
282 if (!CAppUtils::IsAdminLogin())
284 int sel = m_ctrlConfigType.GetCurSel();
285 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(sel != ConfigType::System);
286 GetDlgItem(IDC_BUTTON_REMOVE)->EnableWindow(sel != ConfigType::System);
287 if (sel == ConfigType::System)
288 return;
290 SetModified();
293 void CSettingGitCredential::OnEnChangeEditUrl()
295 m_ChangedMask |= CREDENTIAL_URL;
296 SetModified();
299 void CSettingGitCredential::OnEnChangeEditHelper()
301 m_ChangedMask |= CREDENTIAL_HELPER;
303 UpdateData();
304 if (!m_strHelper.IsEmpty())
305 SetModified();
306 else if (!m_ChangedMask)
307 SetModified(0);
310 void CSettingGitCredential::OnEnChangeEditUsername()
312 m_ChangedMask |= CREDENTIAL_USERNAME;
313 SetModified();
316 void CSettingGitCredential::OnBnClickedCheckUsehttppath()
318 m_ChangedMask |= CREDENTIAL_USEHTTPPATH;
319 SetModified();
322 void CSettingGitCredential::AddConfigType(int &index, CString text, bool add)
324 if (add)
325 index = m_ctrlConfigType.AddString(text);
328 void CSettingGitCredential::AddSimpleCredential(int &index, CString text, bool add)
330 if (add)
331 index = m_ctrlSimpleCredential.AddString(text);
334 void CSettingGitCredential::FillSimpleList(bool anySystem, bool systemWincred, bool systemGCM, bool systemGCMCore)
336 ATLASSERT(SimpleCredentialType::Advanced == -1);
338 bool hasLocal = GitAdminDir::HasAdminDir(g_Git.m_CurrentDir);
339 bool isAdmin = CAppUtils::IsAdminLogin();
341 AddSimpleCredential(SimpleCredentialType::Advanced, CString(MAKEINTRESOURCE(IDS_ADVANCED)));
342 if (isAdmin || !anySystem)
343 AddSimpleCredential(SimpleCredentialType::None, CString(MAKEINTRESOURCE(IDS_NONE)));
344 if (isAdmin || !(systemWincred || systemGCM || systemGCMCore || anySystem))
346 AddSimpleCredential(SimpleCredentialType::LocalWincred, CString(MAKEINTRESOURCE(IDS_LOCAL_WINCRED)), hasLocal && WincredExists());
347 AddSimpleCredential(SimpleCredentialType::LocalWinstore, CString(MAKEINTRESOURCE(IDS_LOCAL_WINSTORE)), hasLocal && WinstoreExists());
348 AddSimpleCredential(SimpleCredentialType::LocalGCM, CString(MAKEINTRESOURCE(IDS_LOCAL_GCM)), hasLocal && GCMExists());
349 AddSimpleCredential(SimpleCredentialType::LocalGCMCore, CString(MAKEINTRESOURCE(IDS_LOCAL_GCMCORE)), hasLocal && GCMCoreExists());
350 AddSimpleCredential(SimpleCredentialType::GlobalWincred, CString(MAKEINTRESOURCE(IDS_GLOBAL_WINCRED)), WincredExists());
351 AddSimpleCredential(SimpleCredentialType::GlobalWinstore, CString(MAKEINTRESOURCE(IDS_GLOBAL_WINSTORE)), WinstoreExists());
352 AddSimpleCredential(SimpleCredentialType::GlobalGCM, CString(MAKEINTRESOURCE(IDS_GLOBAL_GCM)), GCMExists());
353 AddSimpleCredential(SimpleCredentialType::GlobalGCMCore, CString(MAKEINTRESOURCE(IDS_GLOBAL_GCMCORE)), GCMCoreExists());
355 if (isAdmin || systemWincred)
356 AddSimpleCredential(SimpleCredentialType::SystemWincred, CString(MAKEINTRESOURCE(IDS_SYSTEM_WINCRED)), WincredExists());
357 if (isAdmin || systemGCM)
358 AddSimpleCredential(SimpleCredentialType::SystemGCM, CString(MAKEINTRESOURCE(IDS_SYSTEM_GCM)), GCMExists());
359 if (isAdmin || systemGCMCore)
360 AddSimpleCredential(SimpleCredentialType::SystemGCMCore, CString(MAKEINTRESOURCE(IDS_SYSTEM_GCMCORE)), GCMCoreExists());
363 void CSettingGitCredential::LoadList()
365 CAutoConfig config(true);
366 CAutoRepository repo(g_Git.GetGitRepository());
367 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, repo, FALSE);
368 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, repo, FALSE);
369 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, repo, FALSE);
370 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, repo, FALSE);
372 STRING_VECTOR defaultList, urlList;
373 git_config_foreach_match(config, "credential\\.helper", GetCredentialDefaultUrlCallback, &defaultList);
374 git_config_foreach_match(config, "credential\\..*\\.helper", GetCredentialUrlCallback, &urlList);
375 STRING_VECTOR anyList;
376 git_config_foreach_match(config, "credential\\..*", GetCredentialAnyEntryCallback, &anyList);
378 for (size_t i = 0; i < defaultList.size(); ++i)
379 m_ctrlUrlList.AddString(defaultList[i]);
380 for (size_t i = 0; i < urlList.size(); ++i)
381 m_ctrlUrlList.AddString(urlList[i]);
383 if (anyList.empty())
385 FillSimpleList(false, false, false, false);
386 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::None);
387 return;
389 if (anyList.size() > 1)
391 bool hasSystemType = false;
392 for (size_t i = 0; i < anyList.size(); ++i)
394 int pos = 0;
395 CString prefix = anyList[i].Tokenize(L"\n", pos);
396 if (prefix == L"S" || prefix == L"P")
398 hasSystemType = true;
399 break;
402 FillSimpleList(hasSystemType, false, false, false);
403 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
404 return;
407 int pos = 0;
408 CString prefix = anyList[0].Tokenize(L"\n", pos);
409 CString key = anyList[0].Tokenize(L"\n", pos);
410 CString value = anyList[0].Tokenize(L"\n", pos);
411 if (key != L"credential.helper")
413 FillSimpleList(true, false, false, false);
414 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
415 return;
418 CString winstore = GetWinstorePath();
419 if (prefix == L"L")
421 if (value == L"wincred")
423 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalWincred);
424 return;
426 else if (value == winstore)
428 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalWinstore);
429 return;
431 else if (value == L"manager")
433 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalGCM);
434 return;
436 else if (value == L"manager-core")
438 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalGCMCore);
439 return;
443 if (prefix == L"G" || prefix == L"X")
445 if (value == L"wincred")
447 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalWincred);
448 return;
450 else if (value == winstore)
452 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalWinstore);
453 return;
455 else if (value == L"manager")
457 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalGCM);
458 return;
460 else if (value == L"manager-core")
462 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalGCMCore);
463 return;
467 if (prefix == L"S" || prefix == L"P")
469 if (value == L"wincred")
471 FillSimpleList(true, true, false, false);
472 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::SystemWincred);
473 return;
475 else if (value == L"manager")
477 FillSimpleList(true, false, true, false);
478 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::SystemGCM);
479 return;
481 else if (value == L"manager-core")
483 FillSimpleList(true, false, false, true);
484 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::SystemGCMCore);
485 return;
489 FillSimpleList((prefix == L"S" || prefix == L"P"), false, false, false);
490 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
493 CString CSettingGitCredential::Load(CString key)
495 CString cmd;
497 if (m_strUrl.IsEmpty())
498 cmd.Format(L"credential.%s", static_cast<LPCWSTR>(key));
499 else
500 cmd.Format(L"credential.%s.%s", static_cast<LPCWSTR>(m_strUrl), static_cast<LPCWSTR>(key));
502 CAutoConfig config(true);
503 CAutoRepository repo(g_Git.GetGitRepository());
504 int sel = m_ctrlConfigType.GetCurSel();
505 if (sel == ConfigType::Local)
506 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, repo, FALSE);
507 else if (sel == ConfigType::Global)
509 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, repo, FALSE);
510 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, repo, FALSE);
512 else if (sel == ConfigType::System)
513 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, repo, FALSE);
515 CStringA cmdA = CUnicodeUtils::GetUTF8(cmd);
516 CStringA valueA;
517 git_config_entry* entry;
518 if (!git_config_get_entry(&entry, config, cmdA))
519 valueA = CStringA(entry->value);
520 git_config_entry_free(entry);
522 return CUnicodeUtils::GetUnicode(valueA);
525 void CSettingGitCredential::Save(CString key, CString value)
527 CString cmd, out;
529 if (m_strUrl.IsEmpty())
530 cmd.Format(L"credential.%s", static_cast<LPCWSTR>(key));
531 else
532 cmd.Format(L"credential.%s.%s", static_cast<LPCWSTR>(m_strUrl), static_cast<LPCWSTR>(key));
534 int sel = m_ctrlConfigType.GetCurSel();
535 CONFIG_TYPE configLevel = sel == ConfigType::System ? CONFIG_SYSTEM : sel == ConfigType::Global ? CONFIG_GLOBAL : CONFIG_LOCAL;
537 bool old = g_Git.m_IsUseGitDLL;
538 // workaround gitdll bug
539 // TODO: switch to libgit2
540 g_Git.m_IsUseGitDLL = false;
541 if (g_Git.SetConfigValue(cmd, value, configLevel))
543 CString msg;
544 msg.FormatMessage(IDS_PROC_SAVECONFIGFAILED, static_cast<LPCWSTR>(cmd), static_cast<LPCWSTR>(value));
545 CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_OK | MB_ICONERROR);
547 if (value.IsEmpty())
549 if (g_Git.UnsetConfigValue(cmd, configLevel))
551 CString msg;
552 msg.FormatMessage(IDS_PROC_SAVECONFIGFAILED, static_cast<LPCWSTR>(cmd), static_cast<LPCWSTR>(value));
553 CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_OK | MB_ICONERROR);
556 g_Git.m_IsUseGitDLL = old;
559 int CSettingGitCredential::DeleteOtherKeys(int type)
561 CString match;
562 if (type == SimpleCredentialType::LocalWincred)
563 match = L"L\ncredential.helper\nwincred";
564 else if (type == SimpleCredentialType::LocalWinstore)
565 match = L"L\ncredential.helper\n" + GetWinstorePath();
566 else if (type == SimpleCredentialType::LocalGCM)
567 match = L"L\ncredential.helper\nmanager";
568 else if (type == SimpleCredentialType::LocalGCMCore)
569 match = L"L\ncredential.helper\nmanager-core";
570 else if (type == SimpleCredentialType::GlobalWincred)
571 match = L"G\ncredential.helper\nwincred";
572 else if (type == SimpleCredentialType::GlobalWinstore)
573 match = L"G\ncredential.helper\n" + GetWinstorePath();
574 else if (type == SimpleCredentialType::GlobalGCM)
575 match = L"G\ncredential.helper\nmanager";
576 else if (type == SimpleCredentialType::GlobalGCMCore)
577 match = L"G\ncredential.helper\nmanager-core";
578 else if (type == SimpleCredentialType::SystemWincred)
579 match = L"S\ncredential.helper\nwincred";
580 else if (type == SimpleCredentialType::SystemGCM)
581 match = L"S\ncredential.helper\nmanager";
582 else if (type == SimpleCredentialType::SystemGCMCore)
583 match = L"S\ncredential.helper\nmanager-core";
585 CAutoConfig config(true);
586 CAutoRepository repo = g_Git.GetGitRepository();
587 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, repo, FALSE);
588 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, repo, FALSE);
589 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, repo, FALSE);
590 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, repo, FALSE);
592 STRING_VECTOR list;
593 git_config_foreach_match(config, "credential\\..*", GetCredentialAnyEntryCallback, &list);
594 for (size_t i = 0; i < list.size(); ++i)
596 int pos = 0;
597 CString prefix = list[i].Tokenize(L"\n", pos);
598 if ((prefix == L"S" || prefix == L"P") && list[i] != match && !CAppUtils::IsAdminLogin())
600 if (MessageBox(L"Cannot modify a system config without proper rights.\nDiscard changes?", L"TortoiseGit", MB_ICONERROR | MB_YESNO) == IDYES)
601 return 0;
602 return -1;
606 // workaround gitdll bug
607 // TODO: switch to libgit2
608 bool old = g_Git.m_IsUseGitDLL;
609 g_Git.m_IsUseGitDLL = false;
610 SCOPE_EXIT { g_Git.m_IsUseGitDLL = old; };
611 for (size_t i = 0; i < list.size(); ++i)
613 if (list[i] == match)
614 continue;
616 int pos = 0;
617 CString prefix = list[i].Tokenize(L"\n", pos);
618 CString key = list[i].Tokenize(L"\n", pos);
619 CONFIG_TYPE configLevel = (prefix == L"S" || prefix == L"P") ? CONFIG_SYSTEM : prefix == L"G" || prefix == L"X" ? CONFIG_GLOBAL : CONFIG_LOCAL;
620 if (g_Git.UnsetConfigValue(key, configLevel))
622 CString msg;
623 msg.FormatMessage(IDS_PROC_SAVECONFIGFAILED, static_cast<LPCWSTR>(key), L"");
624 CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_OK | MB_ICONERROR);
625 return 1;
629 return 0;
632 bool CSettingGitCredential::SaveSimpleCredential(int type)
634 CONFIG_TYPE configLevel;
635 CString value;
636 if (type == SimpleCredentialType::LocalWincred)
638 configLevel = CONFIG_LOCAL;
639 value = L"wincred";
641 else if (type == SimpleCredentialType::LocalWinstore)
643 configLevel = CONFIG_LOCAL;
644 value = GetWinstorePath();
646 else if (type == SimpleCredentialType::LocalGCM)
648 configLevel = CONFIG_LOCAL;
649 value = L"manager";
651 else if (type == SimpleCredentialType::LocalGCMCore)
653 configLevel = CONFIG_LOCAL;
654 value = L"manager-core";
656 else if (type == SimpleCredentialType::GlobalWincred)
658 configLevel = CONFIG_GLOBAL;
659 value = L"wincred";
661 else if (type == SimpleCredentialType::GlobalWinstore)
663 configLevel = CONFIG_GLOBAL;
664 value = GetWinstorePath();
666 else if (type == SimpleCredentialType::GlobalGCM)
668 configLevel = CONFIG_GLOBAL;
669 value = L"manager";
671 else if (type == SimpleCredentialType::GlobalGCMCore)
673 configLevel = CONFIG_GLOBAL;
674 value = L"manager-core";
676 else if (type == SimpleCredentialType::SystemWincred)
678 configLevel = CONFIG_SYSTEM;
679 value = L"wincred";
681 else if (type == SimpleCredentialType::SystemGCM)
683 configLevel = CONFIG_SYSTEM;
684 value = L"manager";
686 else if (type == SimpleCredentialType::SystemGCMCore)
688 configLevel = CONFIG_SYSTEM;
689 value = L"manager-core";
691 else
692 return true;
694 // workaround gitdll bug
695 // TODO: switch to libgit2
696 bool old = g_Git.m_IsUseGitDLL;
697 g_Git.m_IsUseGitDLL = false;
698 if (g_Git.SetConfigValue(L"credential.helper", value, configLevel))
700 CString msg;
701 msg.FormatMessage(IDS_PROC_SAVECONFIGFAILED, L"credential.helper", static_cast<LPCWSTR>(value));
702 CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_OK | MB_ICONERROR);
703 return false;
705 g_Git.m_IsUseGitDLL = old;
706 return true;
709 BOOL CSettingGitCredential::OnApply()
711 if (!m_ChangedMask)
712 return ISettingsPropPage::OnApply();
714 if (SaveSettings())
715 return ISettingsPropPage::OnApply();
716 else
717 return FALSE;
720 bool CSettingGitCredential::SaveSettings()
722 CWaitCursor wait;
723 UpdateData();
725 int type = m_ctrlSimpleCredential.GetCurSel();
726 if ((type == SimpleCredentialType::SystemWincred || type == SimpleCredentialType::SystemGCM || type == SimpleCredentialType::SystemGCMCore) && !CAppUtils::IsAdminLogin())
728 if (m_iSimpleStoredValue == type)
730 if (DeleteOtherKeys(type) > 0)
731 return false;
732 m_ChangedMask = 0;
733 return true;
736 if (MessageBox(L"Cannot modify a system config without proper rights.\nDiscard changes?", L"TortoiseGit", MB_ICONERROR | MB_YESNO) == IDYES)
737 return true;
738 return false;
740 if (type != SimpleCredentialType::Advanced)
742 if (!SaveSimpleCredential(type))
743 return false;
745 if (DeleteOtherKeys(type) > 0)
746 return false;
747 m_ChangedMask = 0;
748 return true;
751 int sel = m_ctrlConfigType.GetCurSel();
752 if (sel == ConfigType::System && !CAppUtils::IsAdminLogin())
754 if (MessageBox(L"Cannot modify a system config without proper rights.\nDiscard changes?", L"TortoiseGit", MB_ICONERROR | MB_YESNO) == IDYES)
755 return true;
756 return false;
759 if (m_ChangedMask & CREDENTIAL_URL)
761 //Add Helper
762 if (m_strHelper.IsEmpty())
764 CMessageBox::Show(GetSafeHwnd(), IDS_GITCREDENTIAL_HELPEREMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
765 return false;
767 m_strUrl.Replace(L'\\', L'/');
768 m_strHelper.Replace(L'\\', L'/');
770 Save(L"helper", m_strHelper);
771 m_ChangedMask &= ~CREDENTIAL_HELPER;
773 sel = m_ctrlConfigType.GetCurSel();
774 CString prefix = sel == ConfigType::System ? L"S" : sel == ConfigType::Global ? L"G" : L"L";
775 CString text;
776 if (!m_strUrl.IsEmpty())
777 text.Format(L"%s:%s", static_cast<LPCWSTR>(prefix), static_cast<LPCWSTR>(m_strUrl));
778 else
779 text = prefix;
780 int urlIndex = m_ctrlUrlList.AddString(text);
781 m_ctrlUrlList.SetCurSel(urlIndex);
782 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(TRUE);
785 if (m_ChangedMask & CREDENTIAL_HELPER)
787 m_strHelper.Replace(L'\\', L'/');
788 Save(L"helper", m_strHelper);
791 if (m_ChangedMask & CREDENTIAL_USERNAME)
792 Save(L"username", m_strUsername);
794 if (m_ChangedMask & CREDENTIAL_USEHTTPPATH)
795 Save(L"useHttpPath", m_bUseHttpPath ? L"true" : L"");
797 m_ChangedMask &= ~CREDENTIAL_ADVANCED_MASK;
798 if (!m_ChangedMask)
799 SetModified(FALSE);
801 return true;
804 void CSettingGitCredential::OnBnClickedButtonRemove()
806 int index = m_ctrlUrlList.GetCurSel();
807 if (index >= 0)
809 CString str;
810 m_ctrlUrlList.GetText(index, str);
811 CString msg;
812 msg.Format(IDS_WARN_REMOVE, static_cast<LPCWSTR>(str));
813 if (CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_YESNO | MB_ICONQUESTION) == IDYES)
815 CAutoConfig config(true);
816 CAutoRepository repo(g_Git.GetGitRepository());
817 int pos = str.Find(L':');
818 CString prefix = pos >= 0 ? str.Left(pos) : str;
819 CString url = pos >= 0 ? str.Mid(pos + 1) : CString();
820 CONFIG_TYPE configLevel = CONFIG_LOCAL;
821 switch (prefix[0])
823 case L'L':
825 configLevel = CONFIG_LOCAL;
826 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, repo, FALSE);
827 break;
829 case L'G':
830 case L'X':
832 configLevel = CONFIG_GLOBAL;
833 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, repo, FALSE);
834 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, repo, FALSE);
835 break;
837 case L'P':
838 case L'S':
840 if (!CAppUtils::IsAdminLogin())
842 MessageBox(L"Cannot modify a system config without proper rights.", L"TortoiseGit", MB_ICONERROR);
843 return;
845 configLevel = CONFIG_SYSTEM;
846 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, repo, FALSE);
847 break;
851 STRING_VECTOR list;
852 CStringA urlA = CUnicodeUtils::GetUTF8(url);
853 CStringA pattern = urlA.IsEmpty() ? CStringA("^credential\\.[^.]+$") : ("credential\\." + RegexEscape(urlA) + "\\..*");
854 git_config_foreach_match(config, pattern, GetCredentialEntryCallback, &list);
855 for (size_t i = 0; i < list.size(); ++i)
856 g_Git.UnsetConfigValue(list[i], configLevel);
857 m_ctrlUrlList.DeleteString(index);
858 OnLbnSelchangeListUrl();
863 void CSettingGitCredential::OnBnClickedOpensettingselevated()
865 CString sCmd;
866 sCmd.Format(L"/command:settings /page:gitcredential /path:\"%s\"", static_cast<LPCWSTR>(g_Git.m_CurrentDir));
867 CAppUtils::RunTortoiseGitProc(sCmd, true);
870 void CSettingGitCredential::OnBnClickedWindowscredmgr()
872 // https://stackoverflow.com/a/31782500/3906760
873 if (CComHeapPtr<WCHAR> wcharPtr; SUCCEEDED(SHGetKnownFolderPath(FOLDERID_System, 0, nullptr, &wcharPtr)))
875 CString folder{ static_cast<LPCWSTR>(wcharPtr) };
876 CAppUtils::LaunchApplication(folder + L"\\rundll32.exe keymgr.dll,KRShowKeyMgr", CAppUtils::LaunchApplicationFlags());