keepass2android/src/KeePassLib2Android/Serialization/FileLock.cs

263 lines
6.9 KiB
C#

/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2012 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using KeePassLib.Cryptography;
using KeePassLib.Resources;
using KeePassLib.Utility;
namespace KeePassLib.Serialization
{
public sealed class FileLockException : Exception
{
private readonly string m_strMsg;
public override string Message
{
get { return m_strMsg; }
}
public FileLockException(string strBaseFile, string strUser)
{
StringBuilder sb = new StringBuilder();
if(!string.IsNullOrEmpty(strBaseFile))
{
sb.Append(strBaseFile);
sb.Append(MessageService.NewParagraph);
}
sb.Append(KLRes.FileLockedWrite);
sb.Append(MessageService.NewLine);
if(!string.IsNullOrEmpty(strUser)) sb.Append(strUser);
else sb.Append("?");
sb.Append(MessageService.NewParagraph);
sb.Append(KLRes.TryAgainSecs);
m_strMsg = sb.ToString();
}
}
public sealed class FileLock : IDisposable
{
private const string LockFileExt = ".lock";
private const string LockFileHeader = "KeePass Lock File";
private IOConnectionInfo m_iocLockFile;
private sealed class LockFileInfo
{
public readonly string ID;
public readonly DateTime Time;
public readonly string UserName;
public readonly string Machine;
public readonly string Domain;
private LockFileInfo(string strID, string strTime, string strUserName,
string strMachine, string strDomain)
{
this.ID = (strID ?? string.Empty).Trim();
DateTime dt;
if(TimeUtil.TryDeserializeUtc(strTime.Trim(), out dt))
this.Time = dt;
else
{
Debug.Assert(false);
this.Time = DateTime.UtcNow;
}
this.UserName = (strUserName ?? string.Empty).Trim();
this.Machine = (strMachine ?? string.Empty).Trim();
this.Domain = (strDomain ?? string.Empty).Trim();
if(this.Domain.Equals(this.Machine, StrUtil.CaseIgnoreCmp))
this.Domain = string.Empty;
}
public string GetOwner()
{
StringBuilder sb = new StringBuilder();
sb.Append((this.UserName.Length > 0) ? this.UserName : "?");
bool bMachine = (this.Machine.Length > 0);
bool bDomain = (this.Domain.Length > 0);
if(bMachine || bDomain)
{
sb.Append(" (");
sb.Append(this.Machine);
if(bMachine && bDomain) sb.Append(" @ ");
sb.Append(this.Domain);
sb.Append(")");
}
return sb.ToString();
}
public static LockFileInfo Load(IOConnectionInfo iocLockFile)
{
Stream s = null;
try
{
s = IOConnection.OpenRead(iocLockFile);
if(s == null) return null;
StreamReader sr = new StreamReader(s, StrUtil.Utf8);
string str = sr.ReadToEnd();
sr.Close();
if(str == null) { Debug.Assert(false); return null; }
str = StrUtil.NormalizeNewLines(str, false);
string[] v = str.Split('\n');
if((v == null) || (v.Length < 6)) { Debug.Assert(false); return null; }
if(!v[0].StartsWith(LockFileHeader)) { Debug.Assert(false); return null; }
return new LockFileInfo(v[1], v[2], v[3], v[4], v[5]);
}
catch(Java.IO.FileNotFoundException) { }
catch(Exception) { Debug.Assert(false); }
finally { if(s != null) s.Close(); }
return null;
}
// Throws on error
public static LockFileInfo Create(IOConnectionInfo iocLockFile)
{
LockFileInfo lfi;
Stream s = null;
try
{
byte[] pbID = CryptoRandom.Instance.GetRandomBytes(16);
string strTime = TimeUtil.SerializeUtc(DateTime.Now);
#if !KeePassLibSD
lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
Environment.UserName, Environment.MachineName,
Environment.UserDomainName);
#else
lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
string.Empty, string.Empty, string.Empty);
#endif
StringBuilder sb = new StringBuilder();
#if !KeePassLibSD
sb.AppendLine(LockFileHeader);
sb.AppendLine(lfi.ID);
sb.AppendLine(strTime);
sb.AppendLine(lfi.UserName);
sb.AppendLine(lfi.Machine);
sb.AppendLine(lfi.Domain);
#else
sb.Append(LockFileHeader + MessageService.NewLine);
sb.Append(lfi.ID + MessageService.NewLine);
sb.Append(strTime + MessageService.NewLine);
sb.Append(lfi.UserName + MessageService.NewLine);
sb.Append(lfi.Machine + MessageService.NewLine);
sb.Append(lfi.Domain + MessageService.NewLine);
#endif
byte[] pbFile = StrUtil.Utf8.GetBytes(sb.ToString());
s = IOConnection.OpenWrite(iocLockFile);
if(s == null) throw new IOException(iocLockFile.GetDisplayName());
s.Write(pbFile, 0, pbFile.Length);
}
finally { if(s != null) s.Close(); }
return lfi;
}
}
public FileLock(IOConnectionInfo iocBaseFile)
{
if(iocBaseFile == null) throw new ArgumentNullException("strBaseFile");
m_iocLockFile = iocBaseFile.CloneDeep();
m_iocLockFile.Path += LockFileExt;
LockFileInfo lfiEx = LockFileInfo.Load(m_iocLockFile);
if(lfiEx != null)
{
m_iocLockFile = null; // Otherwise Dispose deletes the existing one
throw new FileLockException(iocBaseFile.GetDisplayName(),
lfiEx.GetOwner());
}
LockFileInfo.Create(m_iocLockFile);
}
~FileLock()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool bDisposing)
{
if(m_iocLockFile == null) return;
bool bFileDeleted = false;
for(int r = 0; r < 5; ++r)
{
// if(!OwnLockFile()) { bFileDeleted = true; break; }
try
{
IOConnection.DeleteFile(m_iocLockFile);
bFileDeleted = true;
}
catch(Exception) { Debug.Assert(false); }
if(bFileDeleted) break;
if(bDisposing) Thread.Sleep(50);
}
if(bDisposing && !bFileDeleted)
IOConnection.DeleteFile(m_iocLockFile); // Possibly with exception
m_iocLockFile = null;
}
// private bool OwnLockFile()
// {
// if(m_iocLockFile == null) { Debug.Assert(false); return false; }
// if(m_strLockID == null) { Debug.Assert(false); return false; }
// LockFileInfo lfi = LockFileInfo.Load(m_iocLockFile);
// if(lfi == null) return false;
// return m_strLockID.Equals(lfi.ID);
// }
}
}