@@ -207,6 +207,18 @@ public void TerminateProcess(Process proc)
207207 }
208208 }
209209
210+ public OS . ICredentialStorage CreateCredentialStorage ( )
211+ {
212+ var toolPath = FindExecutable ( "secret-tool" ) ;
213+ if ( string . IsNullOrEmpty ( toolPath ) )
214+ return null ;
215+
216+ if ( ! File . Exists ( toolPath ) )
217+ return null ;
218+
219+ return new LinuxCredentialStorage ( toolPath ) ;
220+ }
221+
210222 private string FindExecutable ( string filename )
211223 {
212224 var pathVariable = Environment . GetEnvironmentVariable ( "PATH" ) ?? string . Empty ;
@@ -231,4 +243,99 @@ private string GetXdgDirectory(string envVar, string fallback, string subDirName
231243 return Path . Combine ( fallback , subDirName ) ;
232244 }
233245 }
246+
247+ [ SupportedOSPlatform ( "linux" ) ]
248+ public class LinuxCredentialStorage : OS . ICredentialStorage
249+ {
250+ public LinuxCredentialStorage ( string toolPath )
251+ {
252+ _toolPath = toolPath ;
253+ }
254+
255+ public void Store ( string host , string username , string password )
256+ {
257+ var label = $ "sourcegit:{ host } :{ username } ";
258+
259+ var psi = new ProcessStartInfo
260+ {
261+ FileName = _toolPath ,
262+ Arguments = $ "store --label={ label . Quoted ( ) } service \" sourcegit\" host { host . Quoted ( ) } account { username . Quoted ( ) } ",
263+ RedirectStandardInput = true ,
264+ RedirectStandardOutput = true ,
265+ RedirectStandardError = true ,
266+ UseShellExecute = true ,
267+ CreateNoWindow = true
268+ } ;
269+
270+ using ( var proc = Process . Start ( psi ) )
271+ {
272+ if ( proc == null )
273+ throw new Exception ( $ "Failed to start credential helper: { _toolPath } ") ;
274+
275+ proc . StandardInput . WriteLine ( password ) ;
276+ proc . StandardInput . Close ( ) ;
277+
278+ var err = proc . StandardError . ReadToEnd ( ) ;
279+ proc . WaitForExit ( ) ;
280+
281+ if ( proc . ExitCode != 0 )
282+ throw new Exception ( $ "Credential helper exited with code { proc . ExitCode } : { err } ") ;
283+ }
284+ }
285+
286+ public string Read ( string host , string username )
287+ {
288+ var psi = new ProcessStartInfo
289+ {
290+ FileName = _toolPath ,
291+ Arguments = $ "lookup service \" sourcegit\" host { host . Quoted ( ) } account { username . Quoted ( ) } ",
292+ RedirectStandardOutput = true ,
293+ RedirectStandardError = true ,
294+ UseShellExecute = true ,
295+ CreateNoWindow = true
296+ } ;
297+
298+ using ( var proc = Process . Start ( psi ) )
299+ {
300+ if ( proc == null )
301+ throw new Exception ( $ "Failed to start credential helper: { _toolPath } ") ;
302+
303+ var output = proc . StandardOutput . ReadToEnd ( ) ;
304+ var err = proc . StandardError . ReadToEnd ( ) ;
305+ proc . WaitForExit ( ) ;
306+
307+ if ( proc . ExitCode != 0 )
308+ throw new Exception ( $ "Credential helper exited with code { proc . ExitCode } : { err } ") ;
309+
310+ return output . TrimEnd ( '\r ' , '\n ' ) ;
311+ }
312+ }
313+
314+ public void Erase ( string host , string username )
315+ {
316+ var psi = new ProcessStartInfo
317+ {
318+ FileName = _toolPath ,
319+ Arguments = $ "clear service \" sourcegit\" host { host . Quoted ( ) } account { username . Quoted ( ) } ",
320+ RedirectStandardOutput = true ,
321+ RedirectStandardError = true ,
322+ UseShellExecute = true ,
323+ CreateNoWindow = true
324+ } ;
325+
326+ using ( var proc = Process . Start ( psi ) )
327+ {
328+ if ( proc == null )
329+ throw new Exception ( $ "Failed to start credential helper: { _toolPath } ") ;
330+
331+ var err = proc . StandardError . ReadToEnd ( ) ;
332+ proc . WaitForExit ( ) ;
333+
334+ if ( proc . ExitCode != 0 )
335+ throw new Exception ( $ "Credential helper exited with code { proc . ExitCode } : { err } ") ;
336+ }
337+ }
338+
339+ private string _toolPath ;
340+ }
234341}
0 commit comments