public static Stream OpenWrite(IOConnectionInfo ioc) { if (ioc == null) { Debug.Assert(false); return(null); } RaiseIOAccessPreEvent(ioc, IOAccessType.Write); if (ioc.IsLocalFile()) { return(OpenWriteLocal(ioc)); } Uri uri = new Uri(ioc.Path); Stream s; // Mono does not set HttpWebRequest.Method to POST for writes, // so one needs to set the method to PUT explicitly if (NativeLib.IsUnix() && IsHttpWebRequest(uri)) { s = CreateWebClient(ioc).OpenWrite(uri, WebRequestMethods.Http.Put); } else { s = CreateWebClient(ioc).OpenWrite(uri); } return(IocStream.WrapIfRequired(s)); }
public static bool FileExists(IOConnectionInfo ioc, bool bThrowErrors) { if (ioc == null) { Debug.Assert(false); return(false); } RaiseIOAccessPreEvent(ioc, IOAccessType.Exists); #if ModernKeePassLib return(ioc.Bytes != null); #else if (ioc.IsLocalFile()) { return(File.Exists(ioc.Path)); } #if !KeePassLibSD if (ioc.Path.StartsWith("ftp://", StrUtil.CaseIgnoreCmp)) { bool b = SendCommand(ioc, WebRequestMethods.Ftp.GetDateTimestamp); if (!b && bThrowErrors) { throw new InvalidOperationException(); } return(b); } #endif try { Stream s = OpenRead(ioc); if (s == null) { throw new FileNotFoundException(); } try { s.ReadByte(); } catch (Exception) { } // We didn't download the file completely; close may throw // an exception -- that's okay try { s.Close(); } catch (Exception) { } } catch (Exception) { if (bThrowErrors) { throw; } return(false); } return(true); #endif }
public static void DeleteFile(IOConnectionInfo ioc) { RaiseIOAccessPreEvent(ioc, IOAccessType.Delete); #if ModernKeePassLib if (!ioc.IsLocalFile()) { return; } MemUtil.ZeroByteArray(ioc.Bytes); #else if (ioc.IsLocalFile()) { File.Delete(ioc.Path); return; } #if !KeePassLibSD WebRequest req = CreateWebRequest(ioc); if (req != null) { if (IsHttpWebRequest(req)) { req.Method = "DELETE"; } else if (IsFtpWebRequest(req)) { req.Method = WebRequestMethods.Ftp.DeleteFile; } else if (IsFileWebRequest(req)) { File.Delete(UrlUtil.FileUrlToPath(ioc.Path)); return; } else { req.Method = WrmDeleteFile; } DisposeResponse(req.GetResponse(), true); } #endif #endif }
public static Stream OpenRead(IOConnectionInfo ioc) { RaiseIOAccessPreEvent(ioc, IOAccessType.Read); if (StrUtil.IsDataUri(ioc.Path)) { byte[] pbData = StrUtil.DataUriToData(ioc.Path); if (pbData != null) { return(new MemoryStream(pbData, false)); } } if (ioc.IsLocalFile()) { return(OpenReadLocal(ioc)); } return(IocStream.WrapIfRequired(CreateWebClient(ioc).OpenRead( new Uri(ioc.Path)))); }
public FileTransactionEx(IOConnectionInfo iocBaseFile, bool bTransacted) { if (iocBaseFile == null) { throw new ArgumentNullException("iocBaseFile"); } m_bTransacted = bTransacted; m_iocBase = iocBaseFile.CloneDeep(); if (m_iocBase.IsLocalFile()) { m_iocBase.Path = UrlUtil.GetShortestAbsolutePath(m_iocBase.Path); } string strPath = m_iocBase.Path; #if !ModernKeePassLib if (m_iocBase.IsLocalFile()) { try { if (File.Exists(strPath)) { // Symbolic links are realized via reparse points; // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365503.aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365680.aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365006.aspx // Performing a file transaction on a symbolic link // would delete/replace the symbolic link instead of // writing to its target FileAttributes fa = File.GetAttributes(strPath); if ((long)(fa & FileAttributes.ReparsePoint) != 0) { m_bTransacted = false; } } else { // If the base and the temporary file are in different // folders and the base file doesn't exist (i.e. we can't // backup the ACL), a transaction would cause the new file // to have the default ACL of the temporary folder instead // of the one of the base folder; therefore, we don't use // a transaction when the base file doesn't exist (this // also results in other applications monitoring the folder // to see one file creation only) m_bTransacted = false; } } catch (Exception) { Debug.Assert(false); } } #endif #if !ModernKeePassLib // Prevent transactions for FTP URLs under .NET 4.0 in order to // avoid/workaround .NET bug 621450: // https://connect.microsoft.com/VisualStudio/feedback/details/621450/problem-renaming-file-on-ftp-server-using-ftpwebrequest-in-net-framework-4-0-vs2010-only if (strPath.StartsWith("ftp:", StrUtil.CaseIgnoreCmp) && (Environment.Version.Major >= 4) && !NativeLib.IsUnix()) { m_bTransacted = false; } #endif foreach (KeyValuePair <string, bool> kvp in g_dEnabled) { if (strPath.StartsWith(kvp.Key, StrUtil.CaseIgnoreCmp)) { m_bTransacted = kvp.Value; break; } } if (m_bTransacted) { m_iocTemp = m_iocBase.CloneDeep(); m_iocTemp.Path += StrTempSuffix; TxfPrepare(); // Adjusts m_iocTemp } else { m_iocTemp = m_iocBase; } }
private void CommitWriteTransaction() { if (g_bExtraSafe) { if (!IOConnection.FileExists(m_iocTemp)) { throw new FileNotFoundException(m_iocTemp.Path + MessageService.NewLine + KLRes.FileSaveFailed); } } bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path); #if !ModernKeePassLib // 'All' includes 'Audit' (SACL), which requires SeSecurityPrivilege, // which we usually don't have and therefore get an exception; // trying to set 'Owner' or 'Group' can result in an // UnauthorizedAccessException; thus we restore 'Access' (DACL) only const AccessControlSections acs = AccessControlSections.Access; #endif bool bEfsEncrypted = false; byte[] pbSec = null; DateTime?otCreation = null; bool bBaseExists = IOConnection.FileExists(m_iocBase); if (bBaseExists && m_iocBase.IsLocalFile()) { // FileAttributes faBase = FileAttributes.Normal; try { #if !ModernKeePassLib FileAttributes faBase = File.GetAttributes(m_iocBase.Path); bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0); try { if (bEfsEncrypted) { File.Decrypt(m_iocBase.Path); } } // For TxF catch (Exception) { Debug.Assert(false); } #endif #if ModernKeePassLib otCreation = m_iocBase.StorageFile.DateCreated.UtcDateTime; #else otCreation = File.GetCreationTimeUtc(m_iocBase.Path); #endif #if !ModernKeePassLib // May throw with Mono FileSecurity sec = File.GetAccessControl(m_iocBase.Path, acs); if (sec != null) { pbSec = sec.GetSecurityDescriptorBinaryForm(); } #endif } catch (Exception) { Debug.Assert(NativeLib.IsUnix()); } // if((long)(faBase & FileAttributes.ReadOnly) != 0) // throw new UnauthorizedAccessException(); } if (!TxfMove()) { if (bBaseExists) { IOConnection.DeleteFile(m_iocBase); } IOConnection.RenameFile(m_iocTemp, m_iocBase); } else { Debug.Assert(pbSec != null); } // TxF success => NTFS => has ACL try { // If File.GetCreationTimeUtc fails, it may return a // date with year 1601, and Unix times start in 1970, // so testing for 1971 should ensure validity; // https://msdn.microsoft.com/en-us/library/system.io.file.getcreationtimeutc.aspx #if !ModernKeePassLib if (otCreation.HasValue && (otCreation.Value.Year >= 1971)) { File.SetCreationTimeUtc(m_iocBase.Path, otCreation.Value); } #endif #if !ModernKeePassLib if (bEfsEncrypted) { try { File.Encrypt(m_iocBase.Path); } catch (Exception) { Debug.Assert(false); } } // File.SetAccessControl(m_iocBase.Path, secPrev); // Directly calling File.SetAccessControl with the previous // FileSecurity object does not work; the binary form // indirection is required; // https://sourceforge.net/p/keepass/bugs/1738/ // https://msdn.microsoft.com/en-us/library/system.io.file.setaccesscontrol.aspx if ((pbSec != null) && (pbSec.Length != 0)) { FileSecurity sec = new FileSecurity(); sec.SetSecurityDescriptorBinaryForm(pbSec, acs); File.SetAccessControl(m_iocBase.Path, sec); } #endif } catch (Exception) { Debug.Assert(false); } if (bMadeUnhidden) { UrlUtil.HideFile(m_iocBase.Path, true); } }
/// <summary> /// Rename/move a file. For local file system and WebDAV, the /// specified file is moved, i.e. the file destination can be /// in a different directory/path. In contrast, for FTP the /// file is renamed, i.e. its destination must be in the same /// directory/path. /// </summary> /// <param name="iocFrom">Source file path.</param> /// <param name="iocTo">Target file path.</param> public static void RenameFile(IOConnectionInfo iocFrom, IOConnectionInfo iocTo) { RaiseIOAccessPreEvent(iocFrom, iocTo, IOAccessType.Move); #if !ModernKeePassLib if (iocFrom.IsLocalFile()) { File.Move(iocFrom.Path, iocTo.Path); return; } #if !KeePassLibSD WebRequest req = CreateWebRequest(iocFrom); if (req != null) { string strToCnc = UrlUtil.GetCanonicalUri(iocTo.Path); if (IsHttpWebRequest(req)) { #if KeePassUAP throw new NotSupportedException(); #else req.Method = "MOVE"; req.Headers.Set("Destination", strToCnc); // Full URL supported #endif } else if (IsFtpWebRequest(req)) { #if KeePassUAP throw new NotSupportedException(); #else req.Method = WebRequestMethods.Ftp.Rename; string strToName = UrlUtil.GetFileName(strToCnc); // We're affected by .NET bug 621450: // https://connect.microsoft.com/VisualStudio/feedback/details/621450/problem-renaming-file-on-ftp-server-using-ftpwebrequest-in-net-framework-4-0-vs2010-only // Prepending "./", "%2E/" or "Dummy/../" doesn't work. ((FtpWebRequest)req).RenameTo = strToName; #endif } else if (IsFileWebRequest(req)) { File.Move(UrlUtil.FileUrlToPath(iocFrom.Path), UrlUtil.FileUrlToPath(iocTo.Path)); return; } else { #if KeePassUAP throw new NotSupportedException(); #else req.Method = WrmMoveFile; req.Headers.Set(WrhMoveFileTo, strToCnc); #endif } #if !KeePassUAP // Unreachable code DisposeResponse(req.GetResponse(), true); #endif } #endif // using(Stream sIn = IOConnection.OpenRead(iocFrom)) // { // using(Stream sOut = IOConnection.OpenWrite(iocTo)) // { // MemUtil.CopyStream(sIn, sOut); // sOut.Close(); // } // // sIn.Close(); // } // DeleteFile(iocFrom); #endif }