Allow to choose Git Credential Manager for Windows (GCM)
[TortoiseGit.git] / src / TortoiseProc / Settings / SettingGitCredential.cpp
blob0fdc4d3758adc2eedc3aada459999ee085445119
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013-2015 - 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;
35 static int None;
36 static int LocalWincred;
37 static int LocalWinstore;
38 static int LocalGCM;
39 static int GlobalWincred;
40 static int GlobalWinstore;
41 static int GlobalGCM;
42 static int SystemWincred;
43 static int SystemGCM;
45 static void Init()
47 Advanced = -1;
48 None = -1;
49 LocalWincred = -1;
50 LocalWinstore = -1;
51 LocalGCM = -1;
52 GlobalWincred = -1;
53 GlobalWinstore = -1;
54 GlobalGCM = -1;
55 SystemWincred = -1;
56 SystemGCM = -1;
60 namespace ConfigType
62 static int Local;
63 static int Global;
64 static int System;
66 static void Init()
68 Local = -1;
69 Global = -1;
70 System = -1;
74 // CSettingGitCredential dialog
76 IMPLEMENT_DYNAMIC(CSettingGitCredential, ISettingsPropPage)
78 CSettingGitCredential::CSettingGitCredential()
79 : ISettingsPropPage(CSettingGitCredential::IDD)
80 , m_bUseHttpPath(FALSE)
83 m_ChangedMask = 0;
86 CSettingGitCredential::~CSettingGitCredential()
90 void CSettingGitCredential::DoDataExchange(CDataExchange* pDX)
92 CPropertyPage::DoDataExchange(pDX);
93 DDX_Control(pDX, IDC_COMBO_SIMPLECREDENTIAL, m_ctrlSimpleCredential);
94 DDX_Control(pDX, IDC_LIST_REMOTE, m_ctrlUrlList);
95 DDX_Text(pDX, IDC_EDIT_URL, m_strUrl);
96 DDX_Text(pDX, IDC_COMBO_HELPER, m_strHelper);
97 DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUsername);
98 DDX_Check(pDX, IDC_CHECK_USEHTTPPATH, m_bUseHttpPath);
99 DDX_Control(pDX, IDC_COMBO_CONFIGTYPE, m_ctrlConfigType);
103 BEGIN_MESSAGE_MAP(CSettingGitCredential, CPropertyPage)
104 ON_CBN_SELCHANGE(IDC_COMBO_SIMPLECREDENTIAL, &CSettingGitCredential::OnCbnSelchangeComboSimplecredential)
105 ON_BN_CLICKED(IDC_BUTTON_ADD, &CSettingGitCredential::OnBnClickedButtonAdd)
106 ON_LBN_SELCHANGE(IDC_LIST_REMOTE, &CSettingGitCredential::OnLbnSelchangeListUrl)
107 ON_CBN_SELCHANGE(IDC_COMBO_CONFIGTYPE, &CSettingGitCredential::OnCbnSelchangeComboConfigType)
108 ON_EN_CHANGE(IDC_EDIT_URL, &CSettingGitCredential::OnEnChangeEditUrl)
109 ON_CBN_EDITCHANGE(IDC_COMBO_HELPER, &CSettingGitCredential::OnEnChangeEditHelper)
110 ON_CBN_SELCHANGE(IDC_COMBO_HELPER, &CSettingGitCredential::OnEnChangeEditHelper)
111 ON_EN_CHANGE(IDC_EDIT_USERNAME, &CSettingGitCredential::OnEnChangeEditUsername)
112 ON_BN_CLICKED(IDC_CHECK_USEHTTPPATH, &CSettingGitCredential::OnBnClickedCheckUsehttppath)
113 ON_BN_CLICKED(IDC_BUTTON_REMOVE, &CSettingGitCredential::OnBnClickedButtonRemove)
114 END_MESSAGE_MAP()
116 static bool RunUAC()
118 CString sCmd;
119 sCmd.Format(_T("/command:settings /page:gitcredential /path:\"%s\""), (LPCTSTR)g_Git.m_CurrentDir);
120 return CAppUtils::RunTortoiseGitProc(sCmd, true);
123 static CString GetWinstorePath()
125 TCHAR winstorebuf[MAX_PATH] = {0};
126 ExpandEnvironmentStrings(_T("%AppData%\\GitCredStore\\git-credential-winstore.exe"), winstorebuf, MAX_PATH);
127 CString winstore;
128 winstore.Format(_T("!'%s'"), winstorebuf);
129 return winstore;
132 static bool WincredExists()
134 CString path = CGit::ms_MsysGitRootDir;
135 path.Append(_T("libexec\\git-core\\git-credential-wincred.exe"));
136 return !!PathFileExists(path);
139 static bool WinstoreExists()
141 return !!PathFileExists(GetWinstorePath());
144 static bool GCMExists()
146 CString path = CGit::ms_MsysGitRootDir;
147 path.Append(_T("libexec\\git-core\\git-credential-manager.exe"));
148 return !!PathFileExists(path);
151 static CStringA RegexEscape(CStringA str)
153 CStringA result;
154 for (int i = 0; i < str.GetLength(); ++i)
156 char c = str[i];
157 switch (c)
159 case '\\': case '*': case '+': case '?': case '|':
160 case '{': case '[': case '(': case ')': case '^':
161 case '$': case '.': case '#': case ' ':
162 result.AppendChar('\\');
163 result.AppendChar(c);
164 break;
165 case '\t': result.Append("\\t"); break;
166 case '\n': result.Append("\\n"); break;
167 case '\r': result.Append("\\r"); break;
168 case '\f': result.Append("\\f"); break;
169 default: result.AppendChar(c); break;
173 return result;
176 BOOL CSettingGitCredential::OnInitDialog()
178 ISettingsPropPage::OnInitDialog();
180 AdjustControlSize(IDC_CHECK_USEHTTPPATH);
182 bool hasLocal = GitAdminDir::HasAdminDir(g_Git.m_CurrentDir);
184 m_ctrlUrlList.ResetContent();
186 ConfigType::Init();
187 AddConfigType(ConfigType::Local, CString(MAKEINTRESOURCE(IDS_SETTINGS_LOCAL)), hasLocal);
188 AddConfigType(ConfigType::Global, CString(MAKEINTRESOURCE(IDS_SETTINGS_GLOBAL)));
189 AddConfigType(ConfigType::System, CString(MAKEINTRESOURCE(IDS_SETTINGS_SYSTEM)));
190 m_ctrlConfigType.SetCurSel(0);
192 if (WincredExists())
193 ((CComboBox*) GetDlgItem(IDC_COMBO_HELPER))->AddString(_T("wincred"));
194 if (WinstoreExists())
195 ((CComboBox*) GetDlgItem(IDC_COMBO_HELPER))->AddString(GetWinstorePath());
196 if (GCMExists())
197 ((CComboBox*)GetDlgItem(IDC_COMBO_HELPER))->AddString(_T("manager"));
199 SimpleCredentialType::Init();
200 AddSimpleCredential(SimpleCredentialType::Advanced, CString(MAKEINTRESOURCE(IDS_ADVANCED)));
201 AddSimpleCredential(SimpleCredentialType::None, CString(MAKEINTRESOURCE(IDS_NONE)));
202 AddSimpleCredential(SimpleCredentialType::LocalWincred, CString(MAKEINTRESOURCE(IDS_LOCAL_WINCRED)), hasLocal && WincredExists());
203 AddSimpleCredential(SimpleCredentialType::LocalWinstore, CString(MAKEINTRESOURCE(IDS_LOCAL_WINSTORE)), hasLocal && WinstoreExists());
204 AddSimpleCredential(SimpleCredentialType::LocalGCM, CString(MAKEINTRESOURCE(IDS_LOCAL_GCM)), hasLocal && GCMExists());
205 AddSimpleCredential(SimpleCredentialType::GlobalWincred, CString(MAKEINTRESOURCE(IDS_GLOBAL_WINCRED)), WincredExists());
206 AddSimpleCredential(SimpleCredentialType::GlobalWinstore, CString(MAKEINTRESOURCE(IDS_GLOBAL_WINSTORE)), WinstoreExists());
207 AddSimpleCredential(SimpleCredentialType::GlobalGCM, CString(MAKEINTRESOURCE(IDS_GLOBAL_GCM)), GCMExists());
208 AddSimpleCredential(SimpleCredentialType::SystemWincred, CString(MAKEINTRESOURCE(IDS_SYSTEM_WINCRED)), WincredExists());
209 AddSimpleCredential(SimpleCredentialType::SystemGCM, CString(MAKEINTRESOURCE(IDS_SYSTEM_GCM)), GCMExists());
211 LoadList();
213 EnableAdvancedOptions();
215 UpdateData(FALSE);
216 return TRUE;
218 // CSettingGitCredential message handlers
220 void CSettingGitCredential::OnCbnSelchangeComboSimplecredential()
222 EnableAdvancedOptions();
223 SetModified();
226 void CSettingGitCredential::OnBnClickedButtonAdd()
228 UpdateData();
230 if (m_strHelper.Trim().IsEmpty())
232 CMessageBox::Show(NULL, IDS_GITCREDENTIAL_HELPEREMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
233 return;
236 m_ChangedMask = CREDENTIAL_URL | CREDENTIAL_HELPER | CREDENTIAL_USERNAME | CREDENTIAL_USEHTTPPATH;
237 int sel = m_ctrlConfigType.GetCurSel();
238 CString prefix = sel == ConfigType::System ? _T("S") : sel == ConfigType::Global ? _T("G") : _T("L");
239 CString text;
240 if (!m_strUrl.IsEmpty())
241 text.Format(_T("%s:%s"), (LPCTSTR)prefix, (LPCTSTR)m_strUrl);
242 else
243 text = prefix;
244 if (IsUrlExist(text))
246 CString msg;
247 msg.Format(IDS_GITCREDENTIAL_OVERWRITEHELPER, (LPCTSTR)m_strUrl);
248 if (CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES)
249 m_ChangedMask &= ~CREDENTIAL_URL;
250 else
251 return;
253 else
255 if (m_strUsername.IsEmpty())
256 m_ChangedMask &= ~CREDENTIAL_USERNAME;
257 if (!m_bUseHttpPath)
258 m_ChangedMask &= ~CREDENTIAL_USEHTTPPATH;
261 OnApply();
264 void CSettingGitCredential::EnableAdvancedOptions()
266 BOOL enable = m_ctrlSimpleCredential.GetCurSel() == SimpleCredentialType::Advanced ? TRUE : FALSE;
267 GetDlgItem(IDC_LIST_REMOTE)->EnableWindow(enable);
268 GetDlgItem(IDC_EDIT_URL)->EnableWindow(enable);
269 GetDlgItem(IDC_COMBO_HELPER)->EnableWindow(enable);
270 GetDlgItem(IDC_EDIT_USERNAME)->EnableWindow(enable);
271 GetDlgItem(IDC_COMBO_CONFIGTYPE)->EnableWindow(enable);
272 GetDlgItem(IDC_CHECK_USEHTTPPATH)->EnableWindow(enable);
273 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(enable);
274 GetDlgItem(IDC_BUTTON_REMOVE)->EnableWindow(enable);
277 BOOL CSettingGitCredential::IsUrlExist(CString &text)
279 CString str;
280 for(int i = 0; i < m_ctrlUrlList.GetCount();i++)
282 m_ctrlUrlList.GetText(i, str);
283 if (str == text)
285 return true;
288 return false;
291 void CSettingGitCredential::OnLbnSelchangeListUrl()
293 CWaitCursor wait;
295 if (m_ChangedMask)
297 if (CMessageBox::Show(NULL, IDS_GITCREDENTIAL_SAVEHELPER, IDS_APPNAME, 1, IDI_QUESTION, IDS_SAVEBUTTON, IDS_DISCARDBUTTON) == 1)
298 OnApply();
300 SetModified(FALSE);
302 CString cmd, output;
303 int index = m_ctrlUrlList.GetCurSel();
304 if (index < 0)
306 m_strHelper.Empty();
307 m_strUrl.Empty();
308 m_strHelper.Empty();
309 m_bUseHttpPath = FALSE;
310 UpdateData(FALSE);
311 return;
314 CString text;
315 m_ctrlUrlList.GetText(index, text);
316 int pos = text.Find(_T(':'));
317 CString prefix = pos >= 0 ? text.Left(pos) : text.Left(1);
318 m_ctrlConfigType.SetCurSel(prefix == _T("S") ? ConfigType::System : prefix == _T("X") ? ConfigType::Global : prefix == _T("G") ? ConfigType::Global : ConfigType::Local);
319 m_strUrl = pos >= 0 ? text.Mid(pos + 1) : _T("");
321 m_strHelper = Load(_T("helper"));
322 m_strUsername = Load(_T("username"));
323 m_bUseHttpPath = Load(_T("useHttpPath")) == _T("true") ? TRUE : FALSE;
325 m_ChangedMask = 0;
327 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(TRUE);
328 GetDlgItem(IDC_BUTTON_REMOVE)->EnableWindow(TRUE);
329 this->UpdateData(FALSE);
332 void CSettingGitCredential::OnCbnSelchangeComboConfigType()
334 SetModified();
337 void CSettingGitCredential::OnEnChangeEditUrl()
339 m_ChangedMask |= CREDENTIAL_URL;
340 SetModified();
343 void CSettingGitCredential::OnEnChangeEditHelper()
345 m_ChangedMask |= CREDENTIAL_HELPER;
347 UpdateData();
348 if (!m_strHelper.IsEmpty())
349 SetModified();
350 else
351 SetModified(0);
354 void CSettingGitCredential::OnEnChangeEditUsername()
356 m_ChangedMask |= CREDENTIAL_USERNAME;
357 SetModified();
360 void CSettingGitCredential::OnBnClickedCheckUsehttppath()
362 m_ChangedMask |= CREDENTIAL_USEHTTPPATH;
363 SetModified();
366 static int GetCredentialDefaultUrlCallback(const git_config_entry *entry, void *payload)
368 CString display = entry->level == 1 ? _T("S") : entry->level == 2 ? _T("X") : entry->level == 3 ? _T("G") : _T("L");
369 ((STRING_VECTOR *)payload)->push_back(display);
370 return 0;
373 static int GetCredentialUrlCallback(const git_config_entry *entry, void *payload)
375 CString name = CUnicodeUtils::GetUnicode(entry->name);
376 int pos1 = name.Find(_T('.'));
377 int pos2 = name.ReverseFind(_T('.'));
378 CString url = name.Mid(pos1 + 1, pos2 - pos1 - 1);
379 CString display;
380 display.Format(_T("%s:%s"), entry->level == 1 ? _T("S") : entry->level == 2 ? _T("X") : entry->level == 3 ? _T("G") : _T("L"), url);
381 ((STRING_VECTOR *)payload)->push_back(display);
382 return 0;
385 static int GetCredentialEntryCallback(const git_config_entry *entry, void *payload)
387 CString name = CUnicodeUtils::GetUnicode(entry->name);
388 ((STRING_VECTOR *)payload)->push_back(name);
389 return 0;
392 static int GetCredentialAnyEntryCallback(const git_config_entry *entry, void *payload)
394 CString name = CUnicodeUtils::GetUnicode(entry->name);
395 CString value = CUnicodeUtils::GetUnicode(entry->value);
396 CString display = entry->level == 1 ? _T("S") : entry->level == 2 ? _T("X") : entry->level == 3 ? _T("G") : _T("L");
397 CString text;
398 text.Format(_T("%s\n%s\n%s"), (LPCTSTR)display, (LPCTSTR)name, (LPCTSTR)value);
399 ((STRING_VECTOR *)payload)->push_back(text);
400 return 0;
403 void CSettingGitCredential::AddConfigType(int &index, CString text, bool add)
405 if (add)
406 index = m_ctrlConfigType.AddString(text);
409 void CSettingGitCredential::AddSimpleCredential(int &index, CString text, bool add)
411 if (add)
412 index = m_ctrlSimpleCredential.AddString(text);
415 void CSettingGitCredential::LoadList()
417 CAutoConfig config(true);
418 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, FALSE);
419 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, FALSE);
420 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, FALSE);
421 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, FALSE);
423 STRING_VECTOR defaultList, urlList;
424 git_config_foreach_match(config, "credential\\.helper", GetCredentialDefaultUrlCallback, &defaultList);
425 git_config_foreach_match(config, "credential\\..*\\.helper", GetCredentialUrlCallback, &urlList);
426 STRING_VECTOR anyList;
427 git_config_foreach_match(config, "credential\\..*", GetCredentialAnyEntryCallback, &anyList);
429 for (size_t i = 0; i < defaultList.size(); ++i)
430 m_ctrlUrlList.AddString(defaultList[i]);
431 for (size_t i = 0; i < urlList.size(); ++i)
432 m_ctrlUrlList.AddString(urlList[i]);
434 if (anyList.empty())
436 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::None);
437 return;
439 if (anyList.size() > 1)
441 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
442 return;
445 int pos = 0;
446 CString prefix = anyList[0].Tokenize(_T("\n"), pos);
447 CString key = anyList[0].Tokenize(_T("\n"), pos);
448 CString value = anyList[0].Tokenize(_T("\n"), pos);
449 if (key != _T("credential.helper"))
451 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
452 return;
455 CString winstore = GetWinstorePath();
456 if (prefix == _T("L"))
458 if (value == _T("wincred"))
460 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalWincred);
461 return;
463 else if (value == winstore)
465 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalWinstore);
466 return;
468 else if (value == _T("manager"))
470 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalGCM);
471 return;
475 if (prefix == _T("G") || prefix == _T("X"))
477 if (value == _T("wincred"))
479 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalWincred);
480 return;
482 else if (value == winstore)
484 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalWinstore);
485 return;
487 else if (value == _T("manager"))
489 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalGCM);
490 return;
494 if (prefix == _T("S"))
496 if (value == _T("wincred"))
498 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::SystemWincred);
499 return;
501 else if (value == _T("manager"))
503 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::SystemGCM);
504 return;
508 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
511 CString CSettingGitCredential::Load(CString key)
513 CString cmd;
515 if (m_strUrl.IsEmpty())
516 cmd.Format(_T("credential.%s"), (LPCTSTR)key);
517 else
518 cmd.Format(_T("credential.%s.%s"), (LPCTSTR)m_strUrl, (LPCTSTR)key);
520 CAutoConfig config(true);
521 int sel = m_ctrlConfigType.GetCurSel();
522 if (sel == ConfigType::Local)
524 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, FALSE);
526 else if (sel == ConfigType::Global)
528 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, FALSE);
529 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, FALSE);
531 else if (sel == ConfigType::System)
533 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, FALSE);
536 CStringA cmdA = CUnicodeUtils::GetUTF8(cmd);
537 CStringA valueA;
538 git_config_entry* entry;
539 if (!git_config_get_entry(&entry, config, cmdA))
540 valueA = CStringA(entry->value);
541 git_config_entry_free(entry);
543 return CUnicodeUtils::GetUnicode(valueA);
546 void CSettingGitCredential::Save(CString key, CString value)
548 CString cmd, out;
550 if (m_strUrl.IsEmpty())
551 cmd.Format(_T("credential.%s"), (LPCTSTR)key);
552 else
553 cmd.Format(_T("credential.%s.%s"), (LPCTSTR)m_strUrl, (LPCTSTR)key);
555 int sel = m_ctrlConfigType.GetCurSel();
556 CONFIG_TYPE configLevel = sel == ConfigType::System ? CONFIG_SYSTEM : sel == ConfigType::Global ? CONFIG_GLOBAL : CONFIG_LOCAL;
558 bool old = g_Git.m_IsUseGitDLL;
559 // workaround gitdll bug
560 // TODO: switch to libgit2
561 g_Git.m_IsUseGitDLL = false;
562 if (g_Git.SetConfigValue(cmd, value, configLevel))
564 CString msg;
565 msg.Format(IDS_PROC_SAVECONFIGFAILED, (LPCTSTR)cmd, (LPCTSTR)value);
566 CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
568 if (value.IsEmpty())
570 if (g_Git.UnsetConfigValue(cmd, configLevel))
572 CString msg;
573 msg.Format(IDS_PROC_SAVECONFIGFAILED, (LPCTSTR)cmd, (LPCTSTR)value);
574 CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
577 g_Git.m_IsUseGitDLL = old;
580 static int DeleteOtherKeys(int type)
582 CString match;
583 if (type == SimpleCredentialType::LocalWincred)
584 match = _T("L\ncredential.helper\nwincred");
585 else if (type == SimpleCredentialType::LocalWinstore)
586 match = _T("L\ncredential.helper\n") + GetWinstorePath();
587 else if (type == SimpleCredentialType::LocalGCM)
588 match = _T("L\ncredential.helper\nmanager");
589 else if (type == SimpleCredentialType::GlobalWincred)
590 match = _T("G\ncredential.helper\nwincred");
591 else if (type == SimpleCredentialType::GlobalWinstore)
592 match = _T("G\ncredential.helper\n") + GetWinstorePath();
593 else if (type == SimpleCredentialType::GlobalGCM)
594 match = _T("G\ncredential.helper\nmanager");
595 else if (type == SimpleCredentialType::SystemWincred)
596 match = _T("S\ncredential.helper\nwincred");
597 else if (type == SimpleCredentialType::SystemGCM)
598 match = _T("S\ncredential.helper\nmanager");
600 CAutoConfig config(true);
601 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, FALSE);
602 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, FALSE);
603 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, FALSE);
604 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, FALSE);
606 STRING_VECTOR list;
607 git_config_foreach_match(config, "credential\\..*", GetCredentialAnyEntryCallback, &list);
608 for (size_t i = 0; i < list.size(); ++i)
610 int pos = 0;
611 CString prefix = list[i].Tokenize(_T("\n"), pos);
612 if (prefix == _T("S") && !CAppUtils::IsAdminLogin())
614 RunUAC();
615 return -1;
619 int result = 0;
620 // workaround gitdll bug
621 // TODO: switch to libgit2
622 bool old = g_Git.m_IsUseGitDLL;
623 g_Git.m_IsUseGitDLL = false;
624 for (size_t i = 0; i < list.size(); ++i)
626 if (list[i] != match)
628 int pos = 0;
629 CString prefix = list[i].Tokenize(_T("\n"), pos);
630 CString key = list[i].Tokenize(_T("\n"), pos);
631 CONFIG_TYPE configLevel = prefix == _T("S") ? CONFIG_SYSTEM : prefix == _T("G") || prefix == _T("X") ? CONFIG_GLOBAL : CONFIG_LOCAL;
632 if (g_Git.UnsetConfigValue(key, configLevel))
634 CString msg;
635 msg.Format(IDS_PROC_SAVECONFIGFAILED, (LPCTSTR)key, _T(""));
636 CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
637 result = 1;
638 break;
642 g_Git.m_IsUseGitDLL = old;
644 return result;
647 bool SaveSimpleCredential(int type)
649 CONFIG_TYPE configLevel;
650 CString value;
651 if (type == SimpleCredentialType::LocalWincred)
653 configLevel = CONFIG_LOCAL;
654 value = _T("wincred");
656 else if (type == SimpleCredentialType::LocalWinstore)
658 configLevel = CONFIG_LOCAL;
659 value = GetWinstorePath();
661 else if (type == SimpleCredentialType::LocalGCM)
663 configLevel = CONFIG_LOCAL;
664 value = _T("manager");
666 else if (type == SimpleCredentialType::GlobalWincred)
668 configLevel = CONFIG_GLOBAL;
669 value = _T("wincred");
671 else if (type == SimpleCredentialType::GlobalWinstore)
673 configLevel = CONFIG_GLOBAL;
674 value = GetWinstorePath();
676 else if (type == SimpleCredentialType::GlobalGCM)
678 configLevel = CONFIG_GLOBAL;
679 value = _T("manager");
681 else if (type == SimpleCredentialType::SystemWincred)
683 configLevel = CONFIG_SYSTEM;
684 value = _T("wincred");
686 else if (type == SimpleCredentialType::SystemGCM)
688 configLevel = CONFIG_SYSTEM;
689 value = _T("manager");
691 else
693 return true;
696 // workaround gitdll bug
697 // TODO: switch to libgit2
698 bool old = g_Git.m_IsUseGitDLL;
699 g_Git.m_IsUseGitDLL = false;
700 if (g_Git.SetConfigValue(_T("credential.helper"), value, configLevel))
702 CString msg;
703 msg.Format(IDS_PROC_SAVECONFIGFAILED, _T("credential.helper"), (LPCTSTR)value);
704 CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
705 return false;
707 g_Git.m_IsUseGitDLL = old;
708 return true;
711 BOOL CSettingGitCredential::OnApply()
713 CWaitCursor wait;
714 UpdateData();
716 int type = m_ctrlSimpleCredential.GetCurSel();
717 if (type == SimpleCredentialType::SystemWincred && !CAppUtils::IsAdminLogin())
719 RunUAC();
720 EndDialog(0);
721 return FALSE;
723 if (type == SimpleCredentialType::SystemGCM && !CAppUtils::IsAdminLogin())
725 RunUAC();
726 EndDialog(0);
727 return FALSE;
729 if (type != SimpleCredentialType::Advanced)
731 if (!SaveSimpleCredential(type))
732 return FALSE;
734 int ret = DeleteOtherKeys(type);
735 if (ret < 0)
736 EndDialog(0);
737 if (ret)
738 return FALSE;
739 SetModified(FALSE);
740 return ISettingsPropPage::OnApply();
743 int sel = m_ctrlConfigType.GetCurSel();
744 if (sel == ConfigType::System && !CAppUtils::IsAdminLogin())
746 RunUAC();
747 EndDialog(0);
748 return FALSE;
751 if (m_ChangedMask & CREDENTIAL_URL)
753 //Add Helper
754 if (m_strHelper.IsEmpty())
756 CMessageBox::Show(NULL, IDS_GITCREDENTIAL_HELPEREMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
757 return FALSE;
759 m_strUrl.Replace(L'\\', L'/');
760 m_strHelper.Replace(L'\\', L'/');
762 Save(_T("helper"), m_strHelper);
763 m_ChangedMask &= ~CREDENTIAL_HELPER;
765 sel = m_ctrlConfigType.GetCurSel();
766 CString prefix = sel == ConfigType::System ? _T("S") : sel == ConfigType::Global ? _T("G") : _T("L");
767 CString text;
768 if (!m_strUrl.IsEmpty())
769 text.Format(_T("%s:%s"), (LPCTSTR)prefix, (LPCTSTR)m_strUrl);
770 else
771 text = prefix;
772 int urlIndex = m_ctrlUrlList.AddString(text);
773 m_ctrlUrlList.SetCurSel(urlIndex);
774 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(TRUE);
777 if (m_ChangedMask & CREDENTIAL_HELPER)
779 m_strHelper.Replace(L'\\', L'/');
780 Save(_T("helper"), m_strHelper);
783 if (m_ChangedMask & CREDENTIAL_USERNAME)
784 Save(_T("username"), m_strUsername);
786 if (m_ChangedMask & CREDENTIAL_USEHTTPPATH)
787 Save(_T("useHttpPath"), m_bUseHttpPath ? _T("true") : _T(""));
789 SetModified(FALSE);
791 m_ChangedMask = 0;
792 return ISettingsPropPage::OnApply();
795 void CSettingGitCredential::OnBnClickedButtonRemove()
797 int index = m_ctrlUrlList.GetCurSel();
798 if (index >= 0)
800 CString str;
801 m_ctrlUrlList.GetText(index, str);
802 CString msg;
803 msg.Format(IDS_WARN_REMOVE, (LPCTSTR)str);
804 if (CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
806 CAutoConfig config(true);
807 int pos = str.Find(_T(':'));
808 CString prefix = pos >= 0 ? str.Left(pos) : str;
809 CString url = pos >= 0 ? str.Mid(pos + 1) : _T("");
810 CONFIG_TYPE configLevel = CONFIG_LOCAL;
811 switch (prefix[0])
813 case _T('L'):
815 configLevel = CONFIG_LOCAL;
816 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitLocalConfig()), GIT_CONFIG_LEVEL_LOCAL, FALSE);
817 break;
819 case _T('G'):
820 case _T('X'):
822 configLevel = CONFIG_GLOBAL;
823 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalConfig()), GIT_CONFIG_LEVEL_GLOBAL, FALSE);
824 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitGlobalXDGConfig()), GIT_CONFIG_LEVEL_XDG, FALSE);
825 break;
827 case _T('S'):
829 if (!CAppUtils::IsAdminLogin())
831 RunUAC();
832 EndDialog(0);
833 return;
835 configLevel = CONFIG_SYSTEM;
836 git_config_add_file_ondisk(config, CGit::GetGitPathStringA(g_Git.GetGitSystemConfig()), GIT_CONFIG_LEVEL_SYSTEM, FALSE);
837 break;
841 STRING_VECTOR list;
842 CStringA urlA = CUnicodeUtils::GetUTF8(url);
843 CStringA pattern = urlA.IsEmpty() ? "^credential\\.[^.]+$" : ("credential\\." + RegexEscape(urlA) + "\\..*");
844 git_config_foreach_match(config, pattern, GetCredentialEntryCallback, &list);
845 for (size_t i = 0; i < list.size(); ++i)
846 g_Git.UnsetConfigValue(list[i], configLevel);
847 m_ctrlUrlList.DeleteString(index);
848 OnLbnSelchangeListUrl();