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
23 #include "TortoiseProc.h"
24 #include "SettingGitCredential.h"
26 #include "GitAdminDir.h"
27 #include "MessageBox.h"
30 #include "PathUtils.h"
46 // CSettingGitCredential dialog
48 IMPLEMENT_DYNAMIC(CSettingGitCredential
, ISettingsPropPage
)
50 CSettingGitCredential::CSettingGitCredential(CString cmdPath
)
51 : ISettingsPropPage(CSettingGitCredential::IDD
)
54 , m_strUsername(_T(""))
55 , m_bUseHttpPath(FALSE
)
62 CSettingGitCredential::~CSettingGitCredential()
66 void CSettingGitCredential::DoDataExchange(CDataExchange
* pDX
)
68 CPropertyPage::DoDataExchange(pDX
);
69 DDX_Control(pDX
, IDC_LIST_REMOTE
, m_ctrlUrlList
);
70 DDX_Text(pDX
, IDC_EDIT_URL
, m_strUrl
);
71 DDX_Text(pDX
, IDC_COMBO_HELPER
, m_strHelper
);
72 DDX_Text(pDX
, IDC_EDIT_USERNAME
, m_strUsername
);
73 DDX_Check(pDX
, IDC_CHECK_USEHTTPPATH
, m_bUseHttpPath
);
74 DDX_Control(pDX
, IDC_COMBO_CONFIGTYPE
, m_ctrlConfigType
);
78 BEGIN_MESSAGE_MAP(CSettingGitCredential
, CPropertyPage
)
79 ON_BN_CLICKED(IDC_BUTTON_ADD
, &CSettingGitCredential::OnBnClickedButtonAdd
)
80 ON_LBN_SELCHANGE(IDC_LIST_REMOTE
, &CSettingGitCredential::OnLbnSelchangeListUrl
)
81 ON_CBN_SELCHANGE(IDC_COMBO_CONFIGTYPE
, &CSettingGitCredential::OnCbnSelchangeComboConfigType
)
82 ON_EN_CHANGE(IDC_EDIT_URL
, &CSettingGitCredential::OnEnChangeEditUrl
)
83 ON_CBN_EDITCHANGE(IDC_COMBO_HELPER
, &CSettingGitCredential::OnEnChangeEditHelper
)
84 ON_CBN_SELCHANGE(IDC_COMBO_HELPER
, &CSettingGitCredential::OnEnChangeEditHelper
)
85 ON_EN_CHANGE(IDC_EDIT_USERNAME
, &CSettingGitCredential::OnEnChangeEditUsername
)
86 ON_BN_CLICKED(IDC_CHECK_USEHTTPPATH
, &CSettingGitCredential::OnBnClickedCheckUsehttppath
)
87 ON_BN_CLICKED(IDC_BUTTON_REMOVE
, &CSettingGitCredential::OnBnClickedButtonRemove
)
93 sCmd
.Format(_T("/command:settings /page:gitcredential /path:\"%s\""), g_Git
.m_CurrentDir
);
94 return CAppUtils::RunTortoiseGitProc(sCmd
, true);
97 static CString
GetWinstorePath()
99 TCHAR winstorebuf
[MAX_PATH
];
100 ExpandEnvironmentStrings(_T("%AppData%\\GitCredStore\\git-credential-winstore.exe"), winstorebuf
, MAX_PATH
);
102 winstore
.Format(_T("!'%s'"), winstorebuf
);
106 static bool WincredExists()
108 CString path
= g_Git
.ms_LastMsysGitDir
;
109 if (g_Git
.ms_LastMsysGitDir
.Right(1) != _T("\\"))
110 path
.AppendChar(_T('\\'));
111 path
.Append(_T("..\\libexec\\git-core\\git-credential-wincred.exe"));
112 return !!PathFileExists(path
);
115 static bool WinstoreExists()
117 return !!PathFileExists(GetWinstorePath());
120 static CStringA
RegexEscape(CStringA str
)
123 for (int i
= 0; i
< str
.GetLength(); ++i
)
128 case '\\': case '*': case '+': case '?': case '|':
129 case '{': case '[': case '(': case ')': case '^':
130 case '$': case '.': case '#': case ' ':
131 result
.AppendChar('\\');
132 result
.AppendChar(c
);
134 case '\t': result
.Append("\\t"); break;
135 case '\n': result
.Append("\\n"); break;
136 case '\r': result
.Append("\\r"); break;
137 case '\f': result
.Append("\\f"); break;
138 default: result
.AppendChar(c
); break;
145 BOOL
CSettingGitCredential::OnInitDialog()
147 ISettingsPropPage::OnInitDialog();
150 bool hasLocal
= g_GitAdminDir
.HasAdminDir(m_cmdPath
, &proj
);
154 this->GetWindowText(title
);
155 this->SetWindowText(title
+ _T(" - ") + proj
);
158 m_ctrlUrlList
.ResetContent();
161 AddConfigType(ConfigType::Local
, CString(MAKEINTRESOURCE(IDS_SETTINGS_LOCAL
)), hasLocal
);
162 AddConfigType(ConfigType::Global
, CString(MAKEINTRESOURCE(IDS_SETTINGS_GLOBAL
)));
163 AddConfigType(ConfigType::System
, CString(MAKEINTRESOURCE(IDS_SETTINGS_SYSTEM
)));
164 m_ctrlConfigType
.SetCurSel(0);
167 ((CComboBox
*) GetDlgItem(IDC_COMBO_HELPER
))->AddString(_T("wincred"));
168 if (WinstoreExists())
169 ((CComboBox
*) GetDlgItem(IDC_COMBO_HELPER
))->AddString(GetWinstorePath());
176 // CSettingGitCredential message handlers
178 void CSettingGitCredential::OnBnClickedButtonAdd()
182 if (m_strHelper
.Trim().IsEmpty())
184 CMessageBox::Show(NULL
, IDS_GITCREDENTIAL_HELPEREMPTY
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
188 m_ChangedMask
= CREDENTIAL_URL
| CREDENTIAL_HELPER
| CREDENTIAL_USERNAME
| CREDENTIAL_USEHTTPPATH
;
189 int sel
= m_ctrlConfigType
.GetCurSel();
190 CString prefix
= sel
== ConfigType::System
? _T("S") : sel
== ConfigType::Global
? _T("G") : _T("L");
192 if (!m_strUrl
.IsEmpty())
193 text
.Format(_T("%s:%s"), prefix
, m_strUrl
);
196 if (IsUrlExist(text
))
199 msg
.Format(IDS_GITCREDENTIAL_OVERWRITEHELPER
, m_strUrl
);
200 if (CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
| MB_DEFBUTTON2
) == IDYES
)
201 m_ChangedMask
&= ~CREDENTIAL_URL
;
207 if (m_strUsername
.IsEmpty())
208 m_ChangedMask
&= ~CREDENTIAL_USERNAME
;
210 m_ChangedMask
&= ~CREDENTIAL_USEHTTPPATH
;
216 BOOL
CSettingGitCredential::IsUrlExist(CString
&text
)
219 for(int i
= 0; i
< m_ctrlUrlList
.GetCount();i
++)
221 m_ctrlUrlList
.GetText(i
, str
);
230 void CSettingGitCredential::OnLbnSelchangeListUrl()
236 if (CMessageBox::Show(NULL
, IDS_GITCREDENTIAL_SAVEHELPER
, IDS_APPNAME
, 1, IDI_QUESTION
, IDS_SAVEBUTTON
, IDS_DISCARDBUTTON
) == 1)
242 int index
= m_ctrlUrlList
.GetCurSel();
248 m_bUseHttpPath
= FALSE
;
254 m_ctrlUrlList
.GetText(index
, text
);
255 int pos
= text
.Find(_T(':'));
256 CString prefix
= pos
>= 0 ? text
.Left(pos
) : text
.Left(1);
257 m_ctrlConfigType
.SetCurSel(prefix
== _T("S") ? ConfigType::System
: prefix
== _T("X") ? ConfigType::Global
: prefix
== _T("G") ? ConfigType::Global
: ConfigType::Local
);
258 m_strUrl
= pos
>= 0 ? text
.Mid(pos
+ 1) : _T("");
260 m_strHelper
= Load(_T("helper"));
261 m_strUsername
= Load(_T("username"));
262 m_bUseHttpPath
= Load(_T("useHttpPath")) == _T("true") ? TRUE
: FALSE
;
266 GetDlgItem(IDC_BUTTON_ADD
)->EnableWindow(TRUE
);
267 GetDlgItem(IDC_BUTTON_REMOVE
)->EnableWindow(TRUE
);
268 this->UpdateData(FALSE
);
271 void CSettingGitCredential::OnCbnSelchangeComboConfigType()
276 void CSettingGitCredential::OnEnChangeEditUrl()
278 m_ChangedMask
|= CREDENTIAL_URL
;
282 void CSettingGitCredential::OnEnChangeEditHelper()
284 m_ChangedMask
|= CREDENTIAL_HELPER
;
287 if (!m_strHelper
.IsEmpty())
293 void CSettingGitCredential::OnEnChangeEditUsername()
295 m_ChangedMask
|= CREDENTIAL_USERNAME
;
299 void CSettingGitCredential::OnBnClickedCheckUsehttppath()
301 m_ChangedMask
|= CREDENTIAL_USEHTTPPATH
;
305 static int GetCredentialDefaultUrlCallback(const git_config_entry
*entry
, void *payload
)
307 CString display
= entry
->level
== 1 ? _T("S") : entry
->level
== 2 ? _T("X") : entry
->level
== 3 ? _T("G") : _T("L");
308 ((STRING_VECTOR
*)payload
)->push_back(display
);
312 static int GetCredentialUrlCallback(const git_config_entry
*entry
, void *payload
)
314 CString name
= CUnicodeUtils::GetUnicode(entry
->name
);
315 int pos1
= name
.Find(_T('.'));
316 int pos2
= name
.ReverseFind(_T('.'));
317 CString url
= name
.Mid(pos1
+ 1, pos2
- pos1
- 1);
319 display
.Format(_T("%s:%s"), entry
->level
== 1 ? _T("S") : entry
->level
== 2 ? _T("X") : entry
->level
== 3 ? _T("G") : _T("L"), url
);
320 ((STRING_VECTOR
*)payload
)->push_back(display
);
324 static int GetCredentialEntryCallback(const git_config_entry
*entry
, void *payload
)
326 CString name
= CUnicodeUtils::GetUnicode(entry
->name
);
327 ((STRING_VECTOR
*)payload
)->push_back(name
);
331 void CSettingGitCredential::AddConfigType(int &index
, CString text
, bool add
)
334 index
= m_ctrlConfigType
.AddString(text
);
337 void CSettingGitCredential::LoadList()
340 git_config_new(&config
);
341 CStringA projectConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitLocalConfig());
342 git_config_add_file_ondisk(config
, projectConfigA
.GetBuffer(), 4, FALSE
);
343 projectConfigA
.ReleaseBuffer();
344 CStringA globalConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitGlobalConfig());
345 git_config_add_file_ondisk(config
, globalConfigA
.GetBuffer(), 3, FALSE
);
346 globalConfigA
.ReleaseBuffer();
347 CStringA globalXDGConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitGlobalXDGConfig());
348 git_config_add_file_ondisk(config
, globalXDGConfigA
.GetBuffer(), 2, FALSE
);
349 globalXDGConfigA
.ReleaseBuffer();
350 CStringA systemConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitSystemConfig());
351 git_config_add_file_ondisk(config
, systemConfigA
.GetBuffer(), 1, FALSE
);
352 systemConfigA
.ReleaseBuffer();
354 STRING_VECTOR defaultList
, urlList
;
355 git_config_foreach_match(config
, "credential\\.helper", GetCredentialDefaultUrlCallback
, &defaultList
);
356 git_config_foreach_match(config
, "credential\\..*\\.helper", GetCredentialUrlCallback
, &urlList
);
357 git_config_free(config
);
359 for (int i
= 0; i
< defaultList
.size(); ++i
)
360 m_ctrlUrlList
.AddString(defaultList
[i
]);
361 for (int i
= 0; i
< urlList
.size(); ++i
)
362 m_ctrlUrlList
.AddString(urlList
[i
]);
365 CString
CSettingGitCredential::Load(CString key
)
369 if (m_strUrl
.IsEmpty())
370 cmd
.Format(_T("credential.%s"), key
);
372 cmd
.Format(_T("credential.%s.%s"), m_strUrl
, key
);
375 git_config_new(&config
);
376 int sel
= m_ctrlConfigType
.GetCurSel();
377 if (sel
== ConfigType::Local
)
379 CStringA projectConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitLocalConfig());
380 git_config_add_file_ondisk(config
, projectConfigA
.GetBuffer(), 1, FALSE
);
381 projectConfigA
.ReleaseBuffer();
383 else if (sel
== ConfigType::Global
)
385 CStringA globalConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitGlobalConfig());
386 git_config_add_file_ondisk(config
, globalConfigA
.GetBuffer(), 2, FALSE
);
387 globalConfigA
.ReleaseBuffer();
388 CStringA globalXDGConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitGlobalXDGConfig());
389 git_config_add_file_ondisk(config
, globalXDGConfigA
.GetBuffer(), 1, FALSE
);
390 globalXDGConfigA
.ReleaseBuffer();
392 else if (sel
== ConfigType::System
)
394 CStringA systemConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitSystemConfig());
395 git_config_add_file_ondisk(config
, systemConfigA
.GetBuffer(), 1, FALSE
);
396 systemConfigA
.ReleaseBuffer();
399 CStringA cmdA
= CUnicodeUtils::GetUTF8(cmd
);
401 const git_config_entry
*entry
;
402 if (!git_config_get_entry(&entry
, config
, cmdA
))
403 valueA
= CStringA(entry
->value
);
404 cmdA
.ReleaseBuffer();
405 git_config_free(config
);
407 return CUnicodeUtils::GetUnicode(valueA
);
410 void CSettingGitCredential::Save(CString key
, CString value
)
414 if (m_strUrl
.IsEmpty())
415 cmd
.Format(_T("credential.%s"), key
);
417 cmd
.Format(_T("credential.%s.%s"), m_strUrl
, key
);
419 int sel
= m_ctrlConfigType
.GetCurSel();
420 CONFIG_TYPE configLevel
= sel
== ConfigType::System
? CONFIG_SYSTEM
: sel
== ConfigType::Global
? CONFIG_GLOBAL
: CONFIG_LOCAL
;
422 bool old
= g_Git
.m_IsUseGitDLL
;
423 // workaround gitdll bug
424 // TODO: switch to libgit2
425 g_Git
.m_IsUseGitDLL
= false;
426 if (g_Git
.SetConfigValue(cmd
, value
, configLevel
, CP_UTF8
, &g_Git
.m_CurrentDir
))
429 msg
.Format(IDS_PROC_SAVECONFIGFAILED
, cmd
, value
);
430 CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
434 if (g_Git
.UnsetConfigValue(cmd
, configLevel
, CP_UTF8
, &g_Git
.m_CurrentDir
))
437 msg
.Format(IDS_PROC_SAVECONFIGFAILED
, cmd
, value
);
438 CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), MB_OK
| MB_ICONERROR
);
441 g_Git
.m_IsUseGitDLL
= old
;
444 BOOL
CSettingGitCredential::OnApply()
449 int sel
= m_ctrlConfigType
.GetCurSel();
450 if (sel
== ConfigType::System
&& !CAppUtils::IsAdminLogin())
457 if (m_ChangedMask
& CREDENTIAL_URL
)
460 if (m_strHelper
.IsEmpty())
462 CMessageBox::Show(NULL
, IDS_GITCREDENTIAL_HELPEREMPTY
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
465 m_strUrl
.Replace(L
'\\', L
'/');
466 m_strHelper
.Replace(L
'\\', L
'/');
468 Save(_T("helper"), m_strHelper
);
469 m_ChangedMask
&= ~CREDENTIAL_HELPER
;
471 int sel
= m_ctrlConfigType
.GetCurSel();
472 CString prefix
= sel
== ConfigType::System
? _T("S") : sel
== ConfigType::Global
? _T("G") : _T("L");
474 if (!m_strUrl
.IsEmpty())
475 text
.Format(_T("%s:%s"), prefix
, m_strUrl
);
478 int urlIndex
= m_ctrlUrlList
.AddString(text
);
479 m_ctrlUrlList
.SetCurSel(urlIndex
);
480 GetDlgItem(IDC_BUTTON_ADD
)->EnableWindow(TRUE
);
483 if (m_ChangedMask
& CREDENTIAL_HELPER
)
485 m_strHelper
.Replace(L
'\\', L
'/');
486 Save(_T("helper"), m_strHelper
);
489 if (m_ChangedMask
& CREDENTIAL_USERNAME
)
490 Save(_T("username"), m_strUsername
);
492 if (m_ChangedMask
& CREDENTIAL_USEHTTPPATH
)
493 Save(_T("useHttpPath"), m_bUseHttpPath
? _T("true") : _T(""));
498 return ISettingsPropPage::OnApply();
501 void CSettingGitCredential::OnBnClickedButtonRemove()
503 int index
= m_ctrlUrlList
.GetCurSel();
507 m_ctrlUrlList
.GetText(index
, str
);
509 msg
.Format(IDS_GITCREDENTIAL_DELETEHELPER
, str
);
510 if (CMessageBox::Show(NULL
, msg
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
) == IDYES
)
513 git_config_new(&config
);
514 int pos
= str
.Find(_T(':'));
515 CString prefix
= pos
>= 0 ? str
.Left(pos
) : str
;
516 CString url
= pos
>= 0 ? str
.Mid(pos
+ 1) : _T("");
517 CONFIG_TYPE configLevel
= CONFIG_LOCAL
;
522 configLevel
= CONFIG_LOCAL
;
523 CStringA projectConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitLocalConfig());
524 git_config_add_file_ondisk(config
, projectConfigA
.GetBuffer(), 1, FALSE
);
525 projectConfigA
.ReleaseBuffer();
531 configLevel
= CONFIG_GLOBAL
;
532 CStringA globalConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitGlobalConfig());
533 git_config_add_file_ondisk(config
, globalConfigA
.GetBuffer(), 2, FALSE
);
534 globalConfigA
.ReleaseBuffer();
535 CStringA globalXDGConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitGlobalXDGConfig());
536 git_config_add_file_ondisk(config
, globalXDGConfigA
.GetBuffer(), 1, FALSE
);
537 globalXDGConfigA
.ReleaseBuffer();
542 if (!CAppUtils::IsAdminLogin())
548 configLevel
= CONFIG_SYSTEM
;
549 CStringA systemConfigA
= CUnicodeUtils::GetUTF8(g_Git
.GetGitSystemConfig());
550 git_config_add_file_ondisk(config
, systemConfigA
.GetBuffer(), 1, FALSE
);
551 systemConfigA
.ReleaseBuffer();
557 CStringA urlA
= CUnicodeUtils::GetUTF8(url
);
558 CStringA pattern
= urlA
.IsEmpty() ? "^credential\\.[^.]+$" : ("credential\\." + RegexEscape(urlA
) + "\\..*");
559 git_config_foreach_match(config
, pattern
, GetCredentialEntryCallback
, &list
);
560 for (size_t i
= 0; i
< list
.size(); ++i
)
561 g_Git
.UnsetConfigValue(list
[i
], configLevel
, CP_UTF8
, &g_Git
.m_CurrentDir
);
563 m_ctrlUrlList
.DeleteString(index
);
564 OnLbnSelchangeListUrl();