Add simplified credential helper selection
[TortoiseGit.git] / src / TortoiseProc / Settings / SettingGitCredential.cpp
blob69e3e5d9dca4be0090b36e92dfb217e38a30f924
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013 - 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 GlobalWincred;
39 static int GlobalWinstore;
40 static int SystemWincred;
42 static void Init()
44 Advanced = -1;
45 None = -1;
46 LocalWincred = -1;
47 LocalWinstore = -1;
48 GlobalWincred = -1;
49 GlobalWinstore = -1;
50 SystemWincred = -1;
54 namespace ConfigType
56 static int Local;
57 static int Global;
58 static int System;
60 static void Init()
62 Local = -1;
63 Global = -1;
64 System = -1;
68 // CSettingGitCredential dialog
70 IMPLEMENT_DYNAMIC(CSettingGitCredential, ISettingsPropPage)
72 CSettingGitCredential::CSettingGitCredential(CString cmdPath)
73 : ISettingsPropPage(CSettingGitCredential::IDD)
74 , m_strUrl(_T(""))
75 , m_strHelper(_T(""))
76 , m_strUsername(_T(""))
77 , m_bUseHttpPath(FALSE)
78 , m_cmdPath(cmdPath)
81 m_ChangedMask = 0;
84 CSettingGitCredential::~CSettingGitCredential()
88 void CSettingGitCredential::DoDataExchange(CDataExchange* pDX)
90 CPropertyPage::DoDataExchange(pDX);
91 DDX_Control(pDX, IDC_COMBO_SIMPLECREDENTIAL, m_ctrlSimpleCredential);
92 DDX_Control(pDX, IDC_LIST_REMOTE, m_ctrlUrlList);
93 DDX_Text(pDX, IDC_EDIT_URL, m_strUrl);
94 DDX_Text(pDX, IDC_COMBO_HELPER, m_strHelper);
95 DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUsername);
96 DDX_Check(pDX, IDC_CHECK_USEHTTPPATH, m_bUseHttpPath);
97 DDX_Control(pDX, IDC_COMBO_CONFIGTYPE, m_ctrlConfigType);
101 BEGIN_MESSAGE_MAP(CSettingGitCredential, CPropertyPage)
102 ON_CBN_SELCHANGE(IDC_COMBO_SIMPLECREDENTIAL, &CSettingGitCredential::OnCbnSelchangeComboSimplecredential)
103 ON_BN_CLICKED(IDC_BUTTON_ADD, &CSettingGitCredential::OnBnClickedButtonAdd)
104 ON_LBN_SELCHANGE(IDC_LIST_REMOTE, &CSettingGitCredential::OnLbnSelchangeListUrl)
105 ON_CBN_SELCHANGE(IDC_COMBO_CONFIGTYPE, &CSettingGitCredential::OnCbnSelchangeComboConfigType)
106 ON_EN_CHANGE(IDC_EDIT_URL, &CSettingGitCredential::OnEnChangeEditUrl)
107 ON_CBN_EDITCHANGE(IDC_COMBO_HELPER, &CSettingGitCredential::OnEnChangeEditHelper)
108 ON_CBN_SELCHANGE(IDC_COMBO_HELPER, &CSettingGitCredential::OnEnChangeEditHelper)
109 ON_EN_CHANGE(IDC_EDIT_USERNAME, &CSettingGitCredential::OnEnChangeEditUsername)
110 ON_BN_CLICKED(IDC_CHECK_USEHTTPPATH, &CSettingGitCredential::OnBnClickedCheckUsehttppath)
111 ON_BN_CLICKED(IDC_BUTTON_REMOVE, &CSettingGitCredential::OnBnClickedButtonRemove)
112 END_MESSAGE_MAP()
114 static bool RunUAC()
116 CString sCmd;
117 sCmd.Format(_T("/command:settings /page:gitcredential /path:\"%s\""), g_Git.m_CurrentDir);
118 return CAppUtils::RunTortoiseGitProc(sCmd, true);
121 static CString GetWinstorePath()
123 TCHAR winstorebuf[MAX_PATH];
124 ExpandEnvironmentStrings(_T("%AppData%\\GitCredStore\\git-credential-winstore.exe"), winstorebuf, MAX_PATH);
125 CString winstore;
126 winstore.Format(_T("!'%s'"), winstorebuf);
127 return winstore;
130 static bool WincredExists()
132 CString path = g_Git.ms_LastMsysGitDir;
133 if (g_Git.ms_LastMsysGitDir.Right(1) != _T("\\"))
134 path.AppendChar(_T('\\'));
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 CStringA RegexEscape(CStringA str)
146 CStringA result;
147 for (int i = 0; i < str.GetLength(); ++i)
149 char c = str[i];
150 switch (c)
152 case '\\': case '*': case '+': case '?': case '|':
153 case '{': case '[': case '(': case ')': case '^':
154 case '$': case '.': case '#': case ' ':
155 result.AppendChar('\\');
156 result.AppendChar(c);
157 break;
158 case '\t': result.Append("\\t"); break;
159 case '\n': result.Append("\\n"); break;
160 case '\r': result.Append("\\r"); break;
161 case '\f': result.Append("\\f"); break;
162 default: result.AppendChar(c); break;
166 return result;
169 BOOL CSettingGitCredential::OnInitDialog()
171 ISettingsPropPage::OnInitDialog();
173 CString proj;
174 bool hasLocal = g_GitAdminDir.HasAdminDir(m_cmdPath, &proj);
175 if (hasLocal)
177 CString title;
178 this->GetWindowText(title);
179 this->SetWindowText(title + _T(" - ") + proj);
182 m_ctrlUrlList.ResetContent();
184 ConfigType::Init();
185 AddConfigType(ConfigType::Local, CString(MAKEINTRESOURCE(IDS_SETTINGS_LOCAL)), hasLocal);
186 AddConfigType(ConfigType::Global, CString(MAKEINTRESOURCE(IDS_SETTINGS_GLOBAL)));
187 AddConfigType(ConfigType::System, CString(MAKEINTRESOURCE(IDS_SETTINGS_SYSTEM)));
188 m_ctrlConfigType.SetCurSel(0);
190 if (WincredExists())
191 ((CComboBox*) GetDlgItem(IDC_COMBO_HELPER))->AddString(_T("wincred"));
192 if (WinstoreExists())
193 ((CComboBox*) GetDlgItem(IDC_COMBO_HELPER))->AddString(GetWinstorePath());
195 SimpleCredentialType::Init();
196 AddSimpleCredential(SimpleCredentialType::Advanced, CString(MAKEINTRESOURCE(IDS_ADVANCED)));
197 AddSimpleCredential(SimpleCredentialType::None, CString(MAKEINTRESOURCE(IDS_NONE)));
198 AddSimpleCredential(SimpleCredentialType::LocalWincred, CString(MAKEINTRESOURCE(IDS_LOCAL_WINCRED)), hasLocal && WincredExists());
199 AddSimpleCredential(SimpleCredentialType::LocalWinstore, CString(MAKEINTRESOURCE(IDS_LOCAL_WINSTORE)), hasLocal && WinstoreExists());
200 AddSimpleCredential(SimpleCredentialType::GlobalWincred, CString(MAKEINTRESOURCE(IDS_GLOBAL_WINCRED)), WincredExists());
201 AddSimpleCredential(SimpleCredentialType::GlobalWinstore, CString(MAKEINTRESOURCE(IDS_GLOBAL_WINSTORE)), WinstoreExists());
202 AddSimpleCredential(SimpleCredentialType::SystemWincred, CString(MAKEINTRESOURCE(IDS_SYSTEM_WINCRED)), WincredExists());
204 LoadList();
206 EnableAdvancedOptions();
208 UpdateData(FALSE);
209 return TRUE;
211 // CSettingGitCredential message handlers
213 void CSettingGitCredential::OnCbnSelchangeComboSimplecredential()
215 EnableAdvancedOptions();
216 SetModified();
219 void CSettingGitCredential::OnBnClickedButtonAdd()
221 UpdateData();
223 if (m_strHelper.Trim().IsEmpty())
225 CMessageBox::Show(NULL, IDS_GITCREDENTIAL_HELPEREMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
226 return;
229 m_ChangedMask = CREDENTIAL_URL | CREDENTIAL_HELPER | CREDENTIAL_USERNAME | CREDENTIAL_USEHTTPPATH;
230 int sel = m_ctrlConfigType.GetCurSel();
231 CString prefix = sel == ConfigType::System ? _T("S") : sel == ConfigType::Global ? _T("G") : _T("L");
232 CString text;
233 if (!m_strUrl.IsEmpty())
234 text.Format(_T("%s:%s"), prefix, m_strUrl);
235 else
236 text = prefix;
237 if (IsUrlExist(text))
239 CString msg;
240 msg.Format(IDS_GITCREDENTIAL_OVERWRITEHELPER, m_strUrl);
241 if (CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES)
242 m_ChangedMask &= ~CREDENTIAL_URL;
243 else
244 return;
246 else
248 if (m_strUsername.IsEmpty())
249 m_ChangedMask &= ~CREDENTIAL_USERNAME;
250 if (!m_bUseHttpPath)
251 m_ChangedMask &= ~CREDENTIAL_USEHTTPPATH;
254 OnApply();
257 void CSettingGitCredential::EnableAdvancedOptions()
259 BOOL enable = m_ctrlSimpleCredential.GetCurSel() == SimpleCredentialType::Advanced ? TRUE : FALSE;
260 GetDlgItem(IDC_LIST_REMOTE)->EnableWindow(enable);
261 GetDlgItem(IDC_EDIT_URL)->EnableWindow(enable);
262 GetDlgItem(IDC_COMBO_HELPER)->EnableWindow(enable);
263 GetDlgItem(IDC_EDIT_USERNAME)->EnableWindow(enable);
264 GetDlgItem(IDC_COMBO_CONFIGTYPE)->EnableWindow(enable);
265 GetDlgItem(IDC_CHECK_USEHTTPPATH)->EnableWindow(enable);
266 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(enable);
267 GetDlgItem(IDC_BUTTON_REMOVE)->EnableWindow(enable);
270 BOOL CSettingGitCredential::IsUrlExist(CString &text)
272 CString str;
273 for(int i = 0; i < m_ctrlUrlList.GetCount();i++)
275 m_ctrlUrlList.GetText(i, str);
276 if (str == text)
278 return true;
281 return false;
284 void CSettingGitCredential::OnLbnSelchangeListUrl()
286 CWaitCursor wait;
288 if (m_ChangedMask)
290 if (CMessageBox::Show(NULL, IDS_GITCREDENTIAL_SAVEHELPER, IDS_APPNAME, 1, IDI_QUESTION, IDS_SAVEBUTTON, IDS_DISCARDBUTTON) == 1)
291 OnApply();
293 SetModified(FALSE);
295 CString cmd, output;
296 int index = m_ctrlUrlList.GetCurSel();
297 if (index < 0)
299 m_strHelper.Empty();
300 m_strUrl.Empty();
301 m_strHelper.Empty();
302 m_bUseHttpPath = FALSE;
303 UpdateData(FALSE);
304 return;
307 CString text;
308 m_ctrlUrlList.GetText(index, text);
309 int pos = text.Find(_T(':'));
310 CString prefix = pos >= 0 ? text.Left(pos) : text.Left(1);
311 m_ctrlConfigType.SetCurSel(prefix == _T("S") ? ConfigType::System : prefix == _T("X") ? ConfigType::Global : prefix == _T("G") ? ConfigType::Global : ConfigType::Local);
312 m_strUrl = pos >= 0 ? text.Mid(pos + 1) : _T("");
314 m_strHelper = Load(_T("helper"));
315 m_strUsername = Load(_T("username"));
316 m_bUseHttpPath = Load(_T("useHttpPath")) == _T("true") ? TRUE : FALSE;
318 m_ChangedMask = 0;
320 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(TRUE);
321 GetDlgItem(IDC_BUTTON_REMOVE)->EnableWindow(TRUE);
322 this->UpdateData(FALSE);
325 void CSettingGitCredential::OnCbnSelchangeComboConfigType()
327 SetModified();
330 void CSettingGitCredential::OnEnChangeEditUrl()
332 m_ChangedMask |= CREDENTIAL_URL;
333 SetModified();
336 void CSettingGitCredential::OnEnChangeEditHelper()
338 m_ChangedMask |= CREDENTIAL_HELPER;
340 UpdateData();
341 if (!m_strHelper.IsEmpty())
342 SetModified();
343 else
344 SetModified(0);
347 void CSettingGitCredential::OnEnChangeEditUsername()
349 m_ChangedMask |= CREDENTIAL_USERNAME;
350 SetModified();
353 void CSettingGitCredential::OnBnClickedCheckUsehttppath()
355 m_ChangedMask |= CREDENTIAL_USEHTTPPATH;
356 SetModified();
359 static int GetCredentialDefaultUrlCallback(const git_config_entry *entry, void *payload)
361 CString display = entry->level == 1 ? _T("S") : entry->level == 2 ? _T("X") : entry->level == 3 ? _T("G") : _T("L");
362 ((STRING_VECTOR *)payload)->push_back(display);
363 return 0;
366 static int GetCredentialUrlCallback(const git_config_entry *entry, void *payload)
368 CString name = CUnicodeUtils::GetUnicode(entry->name);
369 int pos1 = name.Find(_T('.'));
370 int pos2 = name.ReverseFind(_T('.'));
371 CString url = name.Mid(pos1 + 1, pos2 - pos1 - 1);
372 CString display;
373 display.Format(_T("%s:%s"), entry->level == 1 ? _T("S") : entry->level == 2 ? _T("X") : entry->level == 3 ? _T("G") : _T("L"), url);
374 ((STRING_VECTOR *)payload)->push_back(display);
375 return 0;
378 static int GetCredentialEntryCallback(const git_config_entry *entry, void *payload)
380 CString name = CUnicodeUtils::GetUnicode(entry->name);
381 ((STRING_VECTOR *)payload)->push_back(name);
382 return 0;
385 static int GetCredentialAnyEntryCallback(const git_config_entry *entry, void *payload)
387 CString name = CUnicodeUtils::GetUnicode(entry->name);
388 CString value = CUnicodeUtils::GetUnicode(entry->value);
389 CString display = entry->level == 1 ? _T("S") : entry->level == 2 ? _T("X") : entry->level == 3 ? _T("G") : _T("L");
390 CString text;
391 text.Format(_T("%s\n%s\n%s"), display, name, value);
392 ((STRING_VECTOR *)payload)->push_back(text);
393 return 0;
396 void CSettingGitCredential::AddConfigType(int &index, CString text, bool add)
398 if (add)
399 index = m_ctrlConfigType.AddString(text);
402 void CSettingGitCredential::AddSimpleCredential(int &index, CString text, bool add)
404 if (add)
405 index = m_ctrlSimpleCredential.AddString(text);
408 void CSettingGitCredential::LoadList()
410 git_config * config;
411 git_config_new(&config);
412 CStringA projectConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitLocalConfig());
413 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 4, FALSE);
414 projectConfigA.ReleaseBuffer();
415 CStringA globalConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitGlobalConfig());
416 git_config_add_file_ondisk(config, globalConfigA.GetBuffer(), 3, FALSE);
417 globalConfigA.ReleaseBuffer();
418 CStringA globalXDGConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitGlobalXDGConfig());
419 git_config_add_file_ondisk(config, globalXDGConfigA.GetBuffer(), 2, FALSE);
420 globalXDGConfigA.ReleaseBuffer();
421 CStringA systemConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitSystemConfig());
422 git_config_add_file_ondisk(config, systemConfigA.GetBuffer(), 1, FALSE);
423 systemConfigA.ReleaseBuffer();
425 STRING_VECTOR defaultList, urlList;
426 git_config_foreach_match(config, "credential\\.helper", GetCredentialDefaultUrlCallback, &defaultList);
427 git_config_foreach_match(config, "credential\\..*\\.helper", GetCredentialUrlCallback, &urlList);
428 STRING_VECTOR anyList;
429 git_config_foreach_match(config, "credential\\..*", GetCredentialAnyEntryCallback, &anyList);
430 git_config_free(config);
432 for (int i = 0; i < defaultList.size(); ++i)
433 m_ctrlUrlList.AddString(defaultList[i]);
434 for (int i = 0; i < urlList.size(); ++i)
435 m_ctrlUrlList.AddString(urlList[i]);
437 if (anyList.empty())
439 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::None);
440 return;
442 if (anyList.size() > 1)
444 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
445 return;
448 int pos = 0;
449 CString prefix = anyList[0].Tokenize(_T("\n"), pos);
450 CString key = anyList[0].Tokenize(_T("\n"), pos);
451 CString value = anyList[0].Tokenize(_T("\n"), pos);
452 if (key != _T("credential.helper"))
454 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
455 return;
458 CString winstore = GetWinstorePath();
459 if (prefix == _T("L"))
461 if (value == _T("wincred"))
463 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalWincred);
464 return;
466 else if (value == winstore)
468 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::LocalWinstore);
469 return;
473 if (prefix == _T("G") || prefix == _T("X"))
475 if (value == _T("wincred"))
477 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalWincred);
478 return;
480 else if (value == winstore)
482 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::GlobalWinstore);
483 return;
487 if (prefix == _T("S"))
489 if (value == _T("wincred"))
491 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::SystemWincred);
492 return;
496 m_ctrlSimpleCredential.SetCurSel(SimpleCredentialType::Advanced);
499 CString CSettingGitCredential::Load(CString key)
501 CString cmd;
503 if (m_strUrl.IsEmpty())
504 cmd.Format(_T("credential.%s"), key);
505 else
506 cmd.Format(_T("credential.%s.%s"), m_strUrl, key);
508 git_config * config;
509 git_config_new(&config);
510 int sel = m_ctrlConfigType.GetCurSel();
511 if (sel == ConfigType::Local)
513 CStringA projectConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitLocalConfig());
514 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 1, FALSE);
515 projectConfigA.ReleaseBuffer();
517 else if (sel == ConfigType::Global)
519 CStringA globalConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitGlobalConfig());
520 git_config_add_file_ondisk(config, globalConfigA.GetBuffer(), 2, FALSE);
521 globalConfigA.ReleaseBuffer();
522 CStringA globalXDGConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitGlobalXDGConfig());
523 git_config_add_file_ondisk(config, globalXDGConfigA.GetBuffer(), 1, FALSE);
524 globalXDGConfigA.ReleaseBuffer();
526 else if (sel == ConfigType::System)
528 CStringA systemConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitSystemConfig());
529 git_config_add_file_ondisk(config, systemConfigA.GetBuffer(), 1, FALSE);
530 systemConfigA.ReleaseBuffer();
533 CStringA cmdA = CUnicodeUtils::GetUTF8(cmd);
534 CStringA valueA;
535 const git_config_entry *entry;
536 if (!git_config_get_entry(&entry, config, cmdA))
537 valueA = CStringA(entry->value);
538 cmdA.ReleaseBuffer();
539 git_config_free(config);
541 return CUnicodeUtils::GetUnicode(valueA);
544 void CSettingGitCredential::Save(CString key, CString value)
546 CString cmd, out;
548 if (m_strUrl.IsEmpty())
549 cmd.Format(_T("credential.%s"), key);
550 else
551 cmd.Format(_T("credential.%s.%s"), m_strUrl, key);
553 int sel = m_ctrlConfigType.GetCurSel();
554 CONFIG_TYPE configLevel = sel == ConfigType::System ? CONFIG_SYSTEM : sel == ConfigType::Global ? CONFIG_GLOBAL : CONFIG_LOCAL;
556 bool old = g_Git.m_IsUseGitDLL;
557 // workaround gitdll bug
558 // TODO: switch to libgit2
559 g_Git.m_IsUseGitDLL = false;
560 if (g_Git.SetConfigValue(cmd, value, configLevel, CP_UTF8, &g_Git.m_CurrentDir))
562 CString msg;
563 msg.Format(IDS_PROC_SAVECONFIGFAILED, cmd, value);
564 CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
566 if (value.IsEmpty())
568 if (g_Git.UnsetConfigValue(cmd, configLevel, CP_UTF8, &g_Git.m_CurrentDir))
570 CString msg;
571 msg.Format(IDS_PROC_SAVECONFIGFAILED, cmd, value);
572 CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
575 g_Git.m_IsUseGitDLL = old;
578 static int DeleteOtherKeys(int type)
580 CString match;
581 if (type == SimpleCredentialType::LocalWincred)
582 match = _T("L\ncredential.helper\nwincred");
583 else if (type == SimpleCredentialType::LocalWinstore)
584 match = _T("L\ncredential.helper\n") + GetWinstorePath();
585 else if (type == SimpleCredentialType::GlobalWincred)
586 match = _T("G\ncredential.helper\nwincred");
587 else if (type == SimpleCredentialType::GlobalWinstore)
588 match = _T("G\ncredential.helper\n") + GetWinstorePath();
589 else if (type == SimpleCredentialType::SystemWincred)
590 match = _T("S\ncredential.helper\nwincred");
592 git_config * config;
593 git_config_new(&config);
594 CStringA projectConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitLocalConfig());
595 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 4, FALSE);
596 projectConfigA.ReleaseBuffer();
597 CStringA globalConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitGlobalConfig());
598 git_config_add_file_ondisk(config, globalConfigA.GetBuffer(), 3, FALSE);
599 globalConfigA.ReleaseBuffer();
600 CStringA globalXDGConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitGlobalXDGConfig());
601 git_config_add_file_ondisk(config, globalXDGConfigA.GetBuffer(), 2, FALSE);
602 globalXDGConfigA.ReleaseBuffer();
603 CStringA systemConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitSystemConfig());
604 git_config_add_file_ondisk(config, systemConfigA.GetBuffer(), 1, FALSE);
605 systemConfigA.ReleaseBuffer();
607 STRING_VECTOR list;
608 git_config_foreach_match(config, "credential\\..*", GetCredentialAnyEntryCallback, &list);
609 for (size_t i = 0; i < list.size(); ++i)
611 int pos = 0;
612 CString prefix = list[i].Tokenize(_T("\n"), pos);
613 if (prefix == _T("S") && !CAppUtils::IsAdminLogin())
615 RunUAC();
616 return -1;
620 int result = 0;
621 // workaround gitdll bug
622 // TODO: switch to libgit2
623 bool old = g_Git.m_IsUseGitDLL;
624 g_Git.m_IsUseGitDLL = false;
625 for (size_t i = 0; i < list.size(); ++i)
627 if (list[i] != match)
629 int pos = 0;
630 CString prefix = list[i].Tokenize(_T("\n"), pos);
631 CString key = list[i].Tokenize(_T("\n"), pos);
632 CONFIG_TYPE configLevel = prefix == _T("S") ? CONFIG_SYSTEM : prefix == _T("G") || prefix == _T("X") ? CONFIG_GLOBAL : CONFIG_LOCAL;
633 if (g_Git.UnsetConfigValue(key, configLevel, CP_UTF8, &g_Git.m_CurrentDir))
635 CString msg;
636 msg.Format(IDS_PROC_SAVECONFIGFAILED, key, _T(""));
637 CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
638 result = 1;
639 break;
643 g_Git.m_IsUseGitDLL = old;
645 return result;
648 bool SaveSimpleCredential(int type)
650 CONFIG_TYPE configLevel;
651 CString value;
652 if (type == SimpleCredentialType::LocalWincred)
654 configLevel = CONFIG_LOCAL;
655 value = _T("wincred");
657 else if (type == SimpleCredentialType::LocalWinstore)
659 configLevel = CONFIG_LOCAL;
660 value = GetWinstorePath();
662 else if (type == SimpleCredentialType::GlobalWincred)
664 configLevel = CONFIG_GLOBAL;
665 value = _T("wincred");
667 else if (type == SimpleCredentialType::GlobalWinstore)
669 configLevel = CONFIG_GLOBAL;
670 value = GetWinstorePath();
672 else if (type == SimpleCredentialType::SystemWincred)
674 configLevel = CONFIG_SYSTEM;
675 value = _T("wincred");
677 else
679 return true;
682 // workaround gitdll bug
683 // TODO: switch to libgit2
684 bool old = g_Git.m_IsUseGitDLL;
685 g_Git.m_IsUseGitDLL = false;
686 if (g_Git.SetConfigValue(_T("credential.helper"), value, configLevel, CP_UTF8, &g_Git.m_CurrentDir))
688 CString msg;
689 msg.Format(IDS_PROC_SAVECONFIGFAILED, _T("credential.helper"), value);
690 CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
691 return false;
693 g_Git.m_IsUseGitDLL = old;
694 return true;
697 BOOL CSettingGitCredential::OnApply()
699 CWaitCursor wait;
700 UpdateData();
702 int type = m_ctrlSimpleCredential.GetCurSel();
703 if (type == SimpleCredentialType::SystemWincred && !CAppUtils::IsAdminLogin())
705 RunUAC();
706 EndDialog(0);
707 return FALSE;
709 if (type != SimpleCredentialType::Advanced)
711 if (!SaveSimpleCredential(type))
712 return FALSE;
714 if (!DeleteOtherKeys(type))
716 EndDialog(0);
717 return FALSE;
719 return ISettingsPropPage::OnApply();
722 int sel = m_ctrlConfigType.GetCurSel();
723 if (sel == ConfigType::System && !CAppUtils::IsAdminLogin())
725 RunUAC();
726 EndDialog(0);
727 return FALSE;
730 if (m_ChangedMask & CREDENTIAL_URL)
732 //Add Helper
733 if (m_strHelper.IsEmpty())
735 CMessageBox::Show(NULL, IDS_GITCREDENTIAL_HELPEREMPTY, IDS_APPNAME, MB_OK | MB_ICONERROR);
736 return FALSE;
738 m_strUrl.Replace(L'\\', L'/');
739 m_strHelper.Replace(L'\\', L'/');
741 Save(_T("helper"), m_strHelper);
742 m_ChangedMask &= ~CREDENTIAL_HELPER;
744 int sel = m_ctrlConfigType.GetCurSel();
745 CString prefix = sel == ConfigType::System ? _T("S") : sel == ConfigType::Global ? _T("G") : _T("L");
746 CString text;
747 if (!m_strUrl.IsEmpty())
748 text.Format(_T("%s:%s"), prefix, m_strUrl);
749 else
750 text = prefix;
751 int urlIndex = m_ctrlUrlList.AddString(text);
752 m_ctrlUrlList.SetCurSel(urlIndex);
753 GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(TRUE);
756 if (m_ChangedMask & CREDENTIAL_HELPER)
758 m_strHelper.Replace(L'\\', L'/');
759 Save(_T("helper"), m_strHelper);
762 if (m_ChangedMask & CREDENTIAL_USERNAME)
763 Save(_T("username"), m_strUsername);
765 if (m_ChangedMask & CREDENTIAL_USEHTTPPATH)
766 Save(_T("useHttpPath"), m_bUseHttpPath ? _T("true") : _T(""));
768 SetModified(FALSE);
770 m_ChangedMask = 0;
771 return ISettingsPropPage::OnApply();
774 void CSettingGitCredential::OnBnClickedButtonRemove()
776 int index = m_ctrlUrlList.GetCurSel();
777 if (index >= 0)
779 CString str;
780 m_ctrlUrlList.GetText(index, str);
781 CString msg;
782 msg.Format(IDS_GITCREDENTIAL_DELETEHELPER, str);
783 if (CMessageBox::Show(NULL, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
785 git_config * config;
786 git_config_new(&config);
787 int pos = str.Find(_T(':'));
788 CString prefix = pos >= 0 ? str.Left(pos) : str;
789 CString url = pos >= 0 ? str.Mid(pos + 1) : _T("");
790 CONFIG_TYPE configLevel = CONFIG_LOCAL;
791 switch (prefix[0])
793 case _T('L'):
795 configLevel = CONFIG_LOCAL;
796 CStringA projectConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitLocalConfig());
797 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 1, FALSE);
798 projectConfigA.ReleaseBuffer();
799 break;
801 case _T('G'):
802 case _T('X'):
804 configLevel = CONFIG_GLOBAL;
805 CStringA globalConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitGlobalConfig());
806 git_config_add_file_ondisk(config, globalConfigA.GetBuffer(), 2, FALSE);
807 globalConfigA.ReleaseBuffer();
808 CStringA globalXDGConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitGlobalXDGConfig());
809 git_config_add_file_ondisk(config, globalXDGConfigA.GetBuffer(), 1, FALSE);
810 globalXDGConfigA.ReleaseBuffer();
811 break;
813 case _T('S'):
815 if (!CAppUtils::IsAdminLogin())
817 RunUAC();
818 EndDialog(0);
819 return;
821 configLevel = CONFIG_SYSTEM;
822 CStringA systemConfigA = CUnicodeUtils::GetUTF8(g_Git.GetGitSystemConfig());
823 git_config_add_file_ondisk(config, systemConfigA.GetBuffer(), 1, FALSE);
824 systemConfigA.ReleaseBuffer();
825 break;
829 STRING_VECTOR list;
830 CStringA urlA = CUnicodeUtils::GetUTF8(url);
831 CStringA pattern = urlA.IsEmpty() ? "^credential\\.[^.]+$" : ("credential\\." + RegexEscape(urlA) + "\\..*");
832 git_config_foreach_match(config, pattern, GetCredentialEntryCallback, &list);
833 for (size_t i = 0; i < list.size(); ++i)
834 g_Git.UnsetConfigValue(list[i], configLevel, CP_UTF8, &g_Git.m_CurrentDir);
836 m_ctrlUrlList.DeleteString(index);
837 OnLbnSelchangeListUrl();