Constify
[TortoiseGit.git] / src / TortoiseProc / Settings / SettingGitCredential.cpp
blob1e09e36c5f6d5cfc7217311c2e0d30bd7739019f
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013-2020 - 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)
65 m_ChangedMask = 0;
68 CSettingGitCredential::~CSettingGitCredential()
72 void CSettingGitCredential::DoDataExchange(CDataExchange* pDX)
74 CPropertyPage::DoDataExchange(pDX);
75 DDX_Control(pDX, IDC_COMBO_SIMPLECREDENTIAL, m_ctrlSimpleCredential);
76 DDX_Control(pDX, IDC_LIST_REMOTE, m_ctrlUrlList);
77 DDX_Text(pDX, IDC_EDIT_URL, m_strUrl);
78 DDX_Text(pDX, IDC_COMBO_HELPER, m_strHelper);
79 DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUsername);
80 DDX_Check(pDX, IDC_CHECK_USEHTTPPATH, m_bUseHttpPath);
81 DDX_Control(pDX, IDC_COMBO_CONFIGTYPE, m_ctrlConfigType);
85 BEGIN_MESSAGE_MAP(CSettingGitCredential, CPropertyPage)
86 ON_CBN_SELCHANGE(IDC_COMBO_SIMPLECREDENTIAL, &CSettingGitCredential::OnCbnSelchangeComboSimplecredential)
87 ON_BN_CLICKED(IDC_BUTTON_ADD, &CSettingGitCredential::OnBnClickedButtonAdd)
88 ON_LBN_SELCHANGE(IDC_LIST_REMOTE, &CSettingGitCredential::OnLbnSelchangeListUrl)
89 ON_CBN_SELCHANGE(IDC_COMBO_CONFIGTYPE, &CSettingGitCredential::OnCbnSelchangeComboConfigType)
90 ON_EN_CHANGE(IDC_EDIT_URL, &CSettingGitCredential::OnEnChangeEditUrl)
91 ON_CBN_EDITCHANGE(IDC_COMBO_HELPER, &CSettingGitCredential::OnEnChangeEditHelper)
92 ON_CBN_SELCHANGE(IDC_COMBO_HELPER, &CSettingGitCredential::OnEnChangeEditHelper)
93 ON_EN_CHANGE(IDC_EDIT_USERNAME, &CSettingGitCredential::OnEnChangeEditUsername)
94 ON_BN_CLICKED(IDC_CHECK_USEHTTPPATH, &CSettingGitCredential::OnBnClickedCheckUsehttppath)
95 ON_BN_CLICKED(IDC_BUTTON_REMOVE, &CSettingGitCredential::OnBnClickedButtonRemove)
96 ON_BN_CLICKED(IDC_OPENSETTINGSELEVATED, &CSettingGitCredential::OnBnClickedOpensettingselevated)
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)->ShowWindow(SW_SHOW);
155 GetDlgItem(IDC_STATICELEVATIONNEEDED)->ShowWindow(SW_SHOW);
158 EnableAdvancedOptions();
160 UpdateData(FALSE);
161 return TRUE;
163 // CSettingGitCredential message handlers
165 void CSettingGitCredential::OnCbnSelchangeComboSimplecredential()
167 EnableAdvancedOptions();
168 SetModified();
169 m_ChangedMask |= CREDENTIAL_SIMPLE;
172 void CSettingGitCredential::OnBnClickedButtonAdd()
174 UpdateData();
176 if (m_strHelper.Trim().IsEmpty())
178 CMessageBox::Show(GetSafeHwnd(), IDS_GITCREDENTIAL_HELPEREMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
179 return;
182 m_ChangedMask = CREDENTIAL_URL | CREDENTIAL_HELPER | CREDENTIAL_USERNAME | CREDENTIAL_USEHTTPPATH;
183 int sel = m_ctrlConfigType.GetCurSel();
184 CString prefix = sel == ConfigType::System ? L"S" : sel == ConfigType::Global ? L"G" : L"L";
185 CString text;
186 if (!m_strUrl.IsEmpty())
187 text.Format(L"%s:%s", static_cast<LPCTSTR>(prefix), static_cast<LPCTSTR>(m_strUrl));
188 else
189 text = prefix;
190 if (IsUrlExist(text))
192 CString msg;
193 msg.Format(IDS_GITCREDENTIAL_OVERWRITEHELPER, static_cast<LPCTSTR>(m_strUrl));
194 if (CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES)
195 m_ChangedMask &= ~CREDENTIAL_URL;
196 else
197 return;
199 else
201 if (m_strUsername.IsEmpty())
202 m_ChangedMask &= ~CREDENTIAL_USERNAME;
203 if (!m_bUseHttpPath)
204 m_ChangedMask &= ~CREDENTIAL_USEHTTPPATH;
207 SaveSettings();
210 void CSettingGitCredential::EnableAdvancedOptions()
212 BOOL enable = m_ctrlSimpleCredential.GetCurSel() == SimpleCredentialType::Advanced ? TRUE : FALSE;
213 GetDlgItem(IDC_LIST_REMOTE)->EnableWindow(enable);
214 GetDlgItem(IDC_EDIT_URL)->EnableWindow(enable);
215 GetDlgItem(IDC_COMBO_HELPER)->EnableWindow(enable);
216 GetDlgItem(IDC_EDIT_USERNAME)->EnableWindow(enable);
217 GetDlgItem(IDC_COMBO_CONFIGTYPE)->EnableWindow(enable);
218 GetDlgItem(IDC_CHECK_USEHTTPPATH)->EnableWindow(enable);
220 bool canModifySystem = CAppUtils::IsAdminLogin() || (m_ctrlConfigType.GetCurSel() != ConfigType::System);
221 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(enable && canModifySystem);
222 GetDlgItem(IDC_BUTTON_REMOVE)->EnableWindow(enable && canModifySystem);
225 BOOL CSettingGitCredential::IsUrlExist(const CString& text)
227 CString str;
228 for(int i = 0; i < m_ctrlUrlList.GetCount();i++)
230 m_ctrlUrlList.GetText(i, str);
231 if (str == text)
232 return true;
234 return false;
237 void CSettingGitCredential::OnLbnSelchangeListUrl()
239 CWaitCursor wait;
241 if (m_ChangedMask & CREDENTIAL_ADVANCED_MASK)
243 if (CMessageBox::Show(GetSafeHwnd(), IDS_GITCREDENTIAL_SAVEHELPER, IDS_APPNAME, 1, IDI_QUESTION, IDS_SAVEBUTTON, IDS_DISCARDBUTTON) == 1)
244 SaveSettings();
246 m_ChangedMask &= ~CREDENTIAL_ADVANCED_MASK;
247 if (!m_ChangedMask)
248 SetModified(FALSE);
250 CString cmd, output;
251 int index = m_ctrlUrlList.GetCurSel();
252 if (index < 0)
254 m_strHelper.Empty();
255 m_strUrl.Empty();
256 m_strHelper.Empty();
257 m_bUseHttpPath = FALSE;
258 UpdateData(FALSE);
259 return;
262 CString text;
263 m_ctrlUrlList.GetText(index, text);
264 int pos = text.Find(L':');
265 CString prefix = pos >= 0 ? text.Left(pos) : text.Left(1);
266 m_ctrlConfigType.SetCurSel((prefix == L"S" || prefix == L"P") ? ConfigType::System : prefix == L"X" ? ConfigType::Global : prefix == L"G" ? ConfigType::Global : ConfigType::Local);
267 m_strUrl = pos >= 0 ? text.Mid(pos + 1) : L"";
269 m_strHelper = Load(L"helper");
270 m_strUsername = Load(L"username");
271 m_bUseHttpPath = Load(L"useHttpPath") == L"true" ? TRUE : FALSE;
273 bool canModifySystem = CAppUtils::IsAdminLogin() || (m_ctrlConfigType.GetCurSel() != ConfigType::System);
274 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(canModifySystem ? TRUE : FALSE);
275 GetDlgItem(IDC_BUTTON_REMOVE)->EnableWindow(canModifySystem ? TRUE : FALSE);
276 this->UpdateData(FALSE);
279 void CSettingGitCredential::OnCbnSelchangeComboConfigType()
281 if (!CAppUtils::IsAdminLogin())
283 int sel = m_ctrlConfigType.GetCurSel();
284 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(sel != ConfigType::System);
285 GetDlgItem(IDC_BUTTON_REMOVE)->EnableWindow(sel != ConfigType::System);
286 if (sel == ConfigType::System)
287 return;
289 SetModified();
292 void CSettingGitCredential::OnEnChangeEditUrl()
294 m_ChangedMask |= CREDENTIAL_URL;
295 SetModified();
298 void CSettingGitCredential::OnEnChangeEditHelper()
300 m_ChangedMask |= CREDENTIAL_HELPER;
302 UpdateData();
303 if (!m_strHelper.IsEmpty())
304 SetModified();
305 else if (!m_ChangedMask)
306 SetModified(0);
309 void CSettingGitCredential::OnEnChangeEditUsername()
311 m_ChangedMask |= CREDENTIAL_USERNAME;
312 SetModified();
315 void CSettingGitCredential::OnBnClickedCheckUsehttppath()
317 m_ChangedMask |= CREDENTIAL_USEHTTPPATH;
318 SetModified();
321 void CSettingGitCredential::AddConfigType(int &index, CString text, bool add)
323 if (add)
324 index = m_ctrlConfigType.AddString(text);
327 void CSettingGitCredential::AddSimpleCredential(int &index, CString text, bool add)
329 if (add)
330 index = m_ctrlSimpleCredential.AddString(text);
333 void CSettingGitCredential::FillSimpleList(bool anySystem, bool systemWincred, bool systemGCM, bool systemGCMCore)
335 ATLASSERT(SimpleCredentialType::Advanced == -1);
337 bool hasLocal = GitAdminDir::HasAdminDir(g_Git.m_CurrentDir);
338 bool isAdmin = CAppUtils::IsAdminLogin();
340 AddSimpleCredential(SimpleCredentialType::Advanced, CString(MAKEINTRESOURCE(IDS_ADVANCED)));
341 if (isAdmin || !anySystem)
342 AddSimpleCredential(SimpleCredentialType::None, CString(MAKEINTRESOURCE(IDS_NONE)));
343 if (isAdmin || !(systemWincred || systemGCM || systemGCMCore || anySystem))
345 AddSimpleCredential(SimpleCredentialType::LocalWincred, CString(MAKEINTRESOURCE(IDS_LOCAL_WINCRED)), hasLocal && WincredExists());
346 AddSimpleCredential(SimpleCredentialType::LocalWinstore, CString(MAKEINTRESOURCE(IDS_LOCAL_WINSTORE)), hasLocal && WinstoreExists());
347 AddSimpleCredential(SimpleCredentialType::LocalGCM, CString(MAKEINTRESOURCE(IDS_LOCAL_GCM)), hasLocal && GCMExists());
348 AddSimpleCredential(SimpleCredentialType::LocalGCMCore, CString(MAKEINTRESOURCE(IDS_LOCAL_GCMCORE)), hasLocal && GCMCoreExists());
349 AddSimpleCredential(SimpleCredentialType::GlobalWincred, CString(MAKEINTRESOURCE(IDS_GLOBAL_WINCRED)), WincredExists());
350 AddSimpleCredential(SimpleCredentialType::GlobalWinstore, CString(MAKEINTRESOURCE(IDS_GLOBAL_WINSTORE)), WinstoreExists());
351 AddSimpleCredential(SimpleCredentialType::GlobalGCM, CString(MAKEINTRESOURCE(IDS_GLOBAL_GCM)), GCMExists());
352 AddSimpleCredential(SimpleCredentialType::GlobalGCMCore, CString(MAKEINTRESOURCE(IDS_GLOBAL_GCMCORE)), GCMCoreExists());
354 if (isAdmin || systemWincred)
355 AddSimpleCredential(SimpleCredentialType::SystemWincred, CString(MAKEINTRESOURCE(IDS_SYSTEM_WINCRED)), WincredExists());
356 if (isAdmin || systemGCM)
357 AddSimpleCredential(SimpleCredentialType::SystemGCM, CString(MAKEINTRESOURCE(IDS_SYSTEM_GCM)), GCMExists());
358 if (isAdmin || systemGCMCore)
359 AddSimpleCredential(SimpleCredentialType::SystemGCMCore, CString(MAKEINTRESOURCE(IDS_SYSTEM_GCMCORE)), GCMCoreExists());
362 void CSettingGitCredential::LoadList()
364 CAutoConfig config(true);
365 CAutoRepository repo(g_Git.GetGitRepository());
366 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, repo, FALSE);
367 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, repo, FALSE);
368 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, repo, FALSE);
369 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, repo, FALSE);
370 if (!g_Git.ms_bCygwinGit && !g_Git.ms_bMsys2Git && !g_Git.GetGitProgramDataConfig().IsEmpty())
371 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitProgramDataConfig()), GIT_CONFIG_LEVEL_PROGRAMDATA, repo, FALSE);
373 STRING_VECTOR defaultList, urlList;
374 git_config_foreach_match(config, "credential\\.helper", GetCredentialDefaultUrlCallback, &defaultList);
375 git_config_foreach_match(config, "credential\\..*\\.helper", GetCredentialUrlCallback, &urlList);
376 STRING_VECTOR anyList;
377 git_config_foreach_match(config, "credential\\..*", GetCredentialAnyEntryCallback, &anyList);
379 for (size_t i = 0; i < defaultList.size(); ++i)
380 m_ctrlUrlList.AddString(defaultList[i]);
381 for (size_t i = 0; i < urlList.size(); ++i)
382 m_ctrlUrlList.AddString(urlList[i]);
384 if (anyList.empty())
386 FillSimpleList(false, false, false, false);
387 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::None);
388 return;
390 if (anyList.size() > 1)
392 bool hasSystemType = false;
393 for (size_t i = 0; i < anyList.size(); ++i)
395 int pos = 0;
396 CString prefix = anyList[i].Tokenize(L"\n", pos);
397 if (prefix == L"S" || prefix == L"P")
399 hasSystemType = true;
400 break;
403 FillSimpleList(hasSystemType, false, false, false);
404 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
405 return;
408 int pos = 0;
409 CString prefix = anyList[0].Tokenize(L"\n", pos);
410 CString key = anyList[0].Tokenize(L"\n", pos);
411 CString value = anyList[0].Tokenize(L"\n", pos);
412 if (key != L"credential.helper")
414 FillSimpleList(true, false, false, false);
415 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
416 return;
419 CString winstore = GetWinstorePath();
420 if (prefix == L"L")
422 if (value == L"wincred")
424 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalWincred);
425 return;
427 else if (value == winstore)
429 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalWinstore);
430 return;
432 else if (value == L"manager")
434 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalGCM);
435 return;
437 else if (value == L"manager-core")
439 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalGCMCore);
440 return;
444 if (prefix == L"G" || prefix == L"X")
446 if (value == L"wincred")
448 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalWincred);
449 return;
451 else if (value == winstore)
453 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalWinstore);
454 return;
456 else if (value == L"manager")
458 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalGCM);
459 return;
461 else if (value == L"manager-core")
463 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalGCMCore);
464 return;
468 if (prefix == L"S" || prefix == L"P")
470 if (value == L"wincred")
472 FillSimpleList(true, true, false, false);
473 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::SystemWincred);
474 return;
476 else if (value == L"manager")
478 FillSimpleList(true, false, true, false);
479 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::SystemGCM);
480 return;
482 else if (value == L"manager-core")
484 FillSimpleList(true, false, false, true);
485 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::SystemGCMCore);
486 return;
490 FillSimpleList((prefix == L"S" || prefix == L"P"), false, false, false);
491 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
494 CString CSettingGitCredential::Load(CString key)
496 CString cmd;
498 if (m_strUrl.IsEmpty())
499 cmd.Format(L"credential.%s", static_cast<LPCTSTR>(key));
500 else
501 cmd.Format(L"credential.%s.%s", static_cast<LPCTSTR>(m_strUrl), static_cast<LPCTSTR>(key));
503 CAutoConfig config(true);
504 CAutoRepository repo(g_Git.GetGitRepository());
505 int sel = m_ctrlConfigType.GetCurSel();
506 if (sel == ConfigType::Local)
507 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, repo, FALSE);
508 else if (sel == ConfigType::Global)
510 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, repo, FALSE);
511 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, repo, FALSE);
513 else if (sel == ConfigType::System)
514 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, repo, FALSE);
516 CStringA cmdA = CUnicodeUtils::GetUTF8(cmd);
517 CStringA valueA;
518 git_config_entry* entry;
519 if (!git_config_get_entry(&entry, config, cmdA))
520 valueA = CStringA(entry->value);
521 git_config_entry_free(entry);
523 return CUnicodeUtils::GetUnicode(valueA);
526 void CSettingGitCredential::Save(CString key, CString value)
528 CString cmd, out;
530 if (m_strUrl.IsEmpty())
531 cmd.Format(L"credential.%s", static_cast<LPCTSTR>(key));
532 else
533 cmd.Format(L"credential.%s.%s", static_cast<LPCTSTR>(m_strUrl), static_cast<LPCTSTR>(key));
535 int sel = m_ctrlConfigType.GetCurSel();
536 CONFIG_TYPE configLevel = sel == ConfigType::System ? CONFIG_SYSTEM : sel == ConfigType::Global ? CONFIG_GLOBAL : CONFIG_LOCAL;
538 bool old = g_Git.m_IsUseGitDLL;
539 // workaround gitdll bug
540 // TODO: switch to libgit2
541 g_Git.m_IsUseGitDLL = false;
542 if (g_Git.SetConfigValue(cmd, value, configLevel))
544 CString msg;
545 msg.FormatMessage(IDS_PROC_SAVECONFIGFAILED, static_cast<LPCTSTR>(cmd), static_cast<LPCTSTR>(value));
546 CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_OK | MB_ICONERROR);
548 if (value.IsEmpty())
550 if (g_Git.UnsetConfigValue(cmd, configLevel))
552 CString msg;
553 msg.FormatMessage(IDS_PROC_SAVECONFIGFAILED, static_cast<LPCTSTR>(cmd), static_cast<LPCTSTR>(value));
554 CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_OK | MB_ICONERROR);
557 g_Git.m_IsUseGitDLL = old;
560 int CSettingGitCredential::DeleteOtherKeys(int type)
562 CString match;
563 if (type == SimpleCredentialType::LocalWincred)
564 match = L"L\ncredential.helper\nwincred";
565 else if (type == SimpleCredentialType::LocalWinstore)
566 match = L"L\ncredential.helper\n" + GetWinstorePath();
567 else if (type == SimpleCredentialType::LocalGCM)
568 match = L"L\ncredential.helper\nmanager";
569 else if (type == SimpleCredentialType::LocalGCMCore)
570 match = L"L\ncredential.helper\nmanager-core";
571 else if (type == SimpleCredentialType::GlobalWincred)
572 match = L"G\ncredential.helper\nwincred";
573 else if (type == SimpleCredentialType::GlobalWinstore)
574 match = L"G\ncredential.helper\n" + GetWinstorePath();
575 else if (type == SimpleCredentialType::GlobalGCM)
576 match = L"G\ncredential.helper\nmanager";
577 else if (type == SimpleCredentialType::GlobalGCMCore)
578 match = L"G\ncredential.helper\nmanager-core";
579 else if (type == SimpleCredentialType::SystemWincred)
580 match = L"S\ncredential.helper\nwincred";
581 else if (type == SimpleCredentialType::SystemGCM)
582 match = L"S\ncredential.helper\nmanager";
583 else if (type == SimpleCredentialType::SystemGCMCore)
584 match = L"S\ncredential.helper\nmanager-core";
586 CAutoConfig config(true);
587 CAutoRepository repo = g_Git.GetGitRepository();
588 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, repo, FALSE);
589 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, repo, FALSE);
590 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, repo, FALSE);
591 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, repo, FALSE);
593 STRING_VECTOR list;
594 git_config_foreach_match(config, "credential\\..*", GetCredentialAnyEntryCallback, &list);
595 for (size_t i = 0; i < list.size(); ++i)
597 int pos = 0;
598 CString prefix = list[i].Tokenize(L"\n", pos);
599 if ((prefix == L"S" || prefix == L"P") && list[i] != match && !CAppUtils::IsAdminLogin())
601 if (MessageBox(L"Cannot modify a system config without proper rights.\nDiscard changes?", L"TortoiseGit", MB_ICONERROR | MB_YESNO) == IDYES)
602 return 0;
603 return -1;
607 // workaround gitdll bug
608 // TODO: switch to libgit2
609 bool old = g_Git.m_IsUseGitDLL;
610 g_Git.m_IsUseGitDLL = false;
611 SCOPE_EXIT { g_Git.m_IsUseGitDLL = old; };
612 for (size_t i = 0; i < list.size(); ++i)
614 if (list[i] == match)
615 continue;
617 int pos = 0;
618 CString prefix = list[i].Tokenize(L"\n", pos);
619 CString key = list[i].Tokenize(L"\n", pos);
620 CONFIG_TYPE configLevel = (prefix == L"S" || prefix == L"P") ? CONFIG_SYSTEM : prefix == L"G" || prefix == L"X" ? CONFIG_GLOBAL : CONFIG_LOCAL;
621 if (g_Git.UnsetConfigValue(key, configLevel))
623 CString msg;
624 msg.FormatMessage(IDS_PROC_SAVECONFIGFAILED, static_cast<LPCTSTR>(key), L"");
625 CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_OK | MB_ICONERROR);
626 return 1;
630 return 0;
633 bool CSettingGitCredential::SaveSimpleCredential(int type)
635 CONFIG_TYPE configLevel;
636 CString value;
637 if (type == SimpleCredentialType::LocalWincred)
639 configLevel = CONFIG_LOCAL;
640 value = L"wincred";
642 else if (type == SimpleCredentialType::LocalWinstore)
644 configLevel = CONFIG_LOCAL;
645 value = GetWinstorePath();
647 else if (type == SimpleCredentialType::LocalGCM)
649 configLevel = CONFIG_LOCAL;
650 value = L"manager";
652 else if (type == SimpleCredentialType::LocalGCMCore)
654 configLevel = CONFIG_LOCAL;
655 value = L"manager-core";
657 else if (type == SimpleCredentialType::GlobalWincred)
659 configLevel = CONFIG_GLOBAL;
660 value = L"wincred";
662 else if (type == SimpleCredentialType::GlobalWinstore)
664 configLevel = CONFIG_GLOBAL;
665 value = GetWinstorePath();
667 else if (type == SimpleCredentialType::GlobalGCM)
669 configLevel = CONFIG_GLOBAL;
670 value = L"manager";
672 else if (type == SimpleCredentialType::GlobalGCMCore)
674 configLevel = CONFIG_GLOBAL;
675 value = L"manager-core";
677 else if (type == SimpleCredentialType::SystemWincred)
679 configLevel = CONFIG_SYSTEM;
680 value = L"wincred";
682 else if (type == SimpleCredentialType::SystemGCM)
684 configLevel = CONFIG_SYSTEM;
685 value = L"manager";
687 else if (type == SimpleCredentialType::SystemGCMCore)
689 configLevel = CONFIG_SYSTEM;
690 value = L"manager-core";
692 else
693 return true;
695 // workaround gitdll bug
696 // TODO: switch to libgit2
697 bool old = g_Git.m_IsUseGitDLL;
698 g_Git.m_IsUseGitDLL = false;
699 if (g_Git.SetConfigValue(L"credential.helper", value, configLevel))
701 CString msg;
702 msg.FormatMessage(IDS_PROC_SAVECONFIGFAILED, L"credential.helper", static_cast<LPCTSTR>(value));
703 CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_OK | MB_ICONERROR);
704 return false;
706 g_Git.m_IsUseGitDLL = old;
707 return true;
710 BOOL CSettingGitCredential::OnApply()
712 if (!m_ChangedMask)
713 return ISettingsPropPage::OnApply();
715 if (SaveSettings())
716 return ISettingsPropPage::OnApply();
717 else
718 return FALSE;
721 bool CSettingGitCredential::SaveSettings()
723 CWaitCursor wait;
724 UpdateData();
726 int type = m_ctrlSimpleCredential.GetCurSel();
727 if ((type == SimpleCredentialType::SystemWincred || type == SimpleCredentialType::SystemGCM || type == SimpleCredentialType::SystemGCMCore) && !CAppUtils::IsAdminLogin())
729 if (m_iSimpleStoredValue == type)
731 if (DeleteOtherKeys(type) > 0)
732 return false;
733 m_ChangedMask = 0;
734 return true;
737 if (MessageBox(L"Cannot modify a system config without proper rights.\nDiscard changes?", L"TortoiseGit", MB_ICONERROR | MB_YESNO) == IDYES)
738 return true;
739 return false;
741 if (type != SimpleCredentialType::Advanced)
743 if (!SaveSimpleCredential(type))
744 return false;
746 if (DeleteOtherKeys(type) > 0)
747 return false;
748 m_ChangedMask = 0;
749 return true;
752 int sel = m_ctrlConfigType.GetCurSel();
753 if (sel == ConfigType::System && !CAppUtils::IsAdminLogin())
755 if (MessageBox(L"Cannot modify a system config without proper rights.\nDiscard changes?", L"TortoiseGit", MB_ICONERROR | MB_YESNO) == IDYES)
756 return true;
757 return false;
760 if (m_ChangedMask & CREDENTIAL_URL)
762 //Add Helper
763 if (m_strHelper.IsEmpty())
765 CMessageBox::Show(GetSafeHwnd(), IDS_GITCREDENTIAL_HELPEREMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
766 return false;
768 m_strUrl.Replace(L'\\', L'/');
769 m_strHelper.Replace(L'\\', L'/');
771 Save(L"helper", m_strHelper);
772 m_ChangedMask &= ~CREDENTIAL_HELPER;
774 sel = m_ctrlConfigType.GetCurSel();
775 CString prefix = sel == ConfigType::System ? L"S" : sel == ConfigType::Global ? L"G" : L"L";
776 CString text;
777 if (!m_strUrl.IsEmpty())
778 text.Format(L"%s:%s", static_cast<LPCTSTR>(prefix), static_cast<LPCTSTR>(m_strUrl));
779 else
780 text = prefix;
781 int urlIndex = m_ctrlUrlList.AddString(text);
782 m_ctrlUrlList.SetCurSel(urlIndex);
783 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(TRUE);
786 if (m_ChangedMask & CREDENTIAL_HELPER)
788 m_strHelper.Replace(L'\\', L'/');
789 Save(L"helper", m_strHelper);
792 if (m_ChangedMask & CREDENTIAL_USERNAME)
793 Save(L"username", m_strUsername);
795 if (m_ChangedMask & CREDENTIAL_USEHTTPPATH)
796 Save(L"useHttpPath", m_bUseHttpPath ? L"true" : L"");
798 m_ChangedMask &= ~CREDENTIAL_ADVANCED_MASK;
799 if (!m_ChangedMask)
800 SetModified(FALSE);
802 return true;
805 void CSettingGitCredential::OnBnClickedButtonRemove()
807 int index = m_ctrlUrlList.GetCurSel();
808 if (index >= 0)
810 CString str;
811 m_ctrlUrlList.GetText(index, str);
812 CString msg;
813 msg.Format(IDS_WARN_REMOVE, static_cast<LPCTSTR>(str));
814 if (CMessageBox::Show(GetSafeHwnd(), msg, L"TortoiseGit", MB_YESNO | MB_ICONQUESTION) == IDYES)
816 CAutoConfig config(true);
817 CAutoRepository repo(g_Git.GetGitRepository());
818 int pos = str.Find(L':');
819 CString prefix = pos >= 0 ? str.Left(pos) : str;
820 CString url = pos >= 0 ? str.Mid(pos + 1) : L"";
821 CONFIG_TYPE configLevel = CONFIG_LOCAL;
822 switch (prefix[0])
824 case L'L':
826 configLevel = CONFIG_LOCAL;
827 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, repo, FALSE);
828 break;
830 case L'G':
831 case L'X':
833 configLevel = CONFIG_GLOBAL;
834 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, repo, FALSE);
835 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, repo, FALSE);
836 break;
838 case L'P':
839 case L'S':
841 if (!CAppUtils::IsAdminLogin())
843 MessageBox(L"Cannot modify a system config without proper rights.", L"TortoiseGit", MB_ICONERROR);
844 return;
846 configLevel = CONFIG_SYSTEM;
847 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, repo, FALSE);
848 if (!g_Git.ms_bCygwinGit && !g_Git.ms_bMsys2Git && !g_Git.GetGitProgramDataConfig().IsEmpty())
849 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitProgramDataConfig()), GIT_CONFIG_LEVEL_PROGRAMDATA, repo, FALSE);
850 break;
854 STRING_VECTOR list;
855 CStringA urlA = CUnicodeUtils::GetUTF8(url);
856 CStringA pattern = urlA.IsEmpty() ? "^credential\\.[^.]+$" : ("credential\\." + RegexEscape(urlA) + "\\..*");
857 git_config_foreach_match(config, pattern, GetCredentialEntryCallback, &list);
858 for (size_t i = 0; i < list.size(); ++i)
859 g_Git.UnsetConfigValue(list[i], configLevel);
860 m_ctrlUrlList.DeleteString(index);
861 OnLbnSelchangeListUrl();
866 void CSettingGitCredential::OnBnClickedOpensettingselevated()
868 CString sCmd;
869 sCmd.Format(L"/command:settings /page:gitcredential /path:\"%s\"", static_cast<LPCTSTR>(g_Git.m_CurrentDir));
870 CAppUtils::RunTortoiseGitProc(sCmd, true);