/*
* Copyright (c) 2023 Proton AG
*
* This file is part of ProtonVPN.
*
* ProtonVPN 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 3 of the License, or
* (at your option) any later version.
*
* ProtonVPN 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 ProtonVPN. If not, see .
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ProtonVPN.Files.Contracts;
using ProtonVPN.Logging.Contracts;
using ProtonVPN.Logging.Contracts.Events.AppLogs;
using ProtonVPN.Logging.Contracts.Events.SettingsLogs;
using ProtonVPN.Serialization.Contracts;
using ProtonVPN.Serialization.Contracts.Json;
namespace ProtonVPN.Files;
public class FileReaderWriter : IFileReaderWriter
{
private readonly ILogger _logger;
private readonly IProtobufSerializer _protobufSerializer;
private readonly IJsonSerializer _jsonSerializer;
private readonly IPrettyJsonSerializer _prettyJsonSerializer;
public FileReaderWriter(ILogger logger,
IProtobufSerializer protobufSerializer,
IJsonSerializer jsonSerializer,
IPrettyJsonSerializer prettyJsonSerializer)
{
_logger = logger;
_protobufSerializer = protobufSerializer;
_jsonSerializer = jsonSerializer;
_prettyJsonSerializer = prettyJsonSerializer;
}
public T ReadOrNew(string fullFilePath, Serializers serializer)
where T : new()
{
return ReadOrDefault(fullFilePath, serializer) ?? new();
}
public T ReadOrDefault(string fullFilePath, Serializers serializer)
{
try
{
if (File.Exists(fullFilePath))
{
using (MemoryStream memoryStream = new())
{
using (FileStream fileStream = new(fullFilePath, FileMode.Open, FileAccess.Read))
{
fileStream.CopyTo(memoryStream);
}
return GetSerializer(serializer).Deserialize(memoryStream);
}
}
}
catch (Exception ex)
{
_logger.Error($"Failed to read the file {fullFilePath}.", ex);
}
return default(T);
}
private ISerializer GetSerializer(Serializers serializer)
{
return serializer switch
{
Serializers.Protobuf => _protobufSerializer,
Serializers.Json => _jsonSerializer,
Serializers.PrettyJson => _prettyJsonSerializer,
_ => _protobufSerializer,
};
}
public IDictionary ReadAllUsers(string folderPath, string fileNamePrefix, string fileExtension, Serializers serializer)
{
IList fileNames = Directory.EnumerateFiles(folderPath)
.Select(Path.GetFileName)
.Where(f => f.StartsWith(fileNamePrefix) && f.EndsWith(fileExtension))
.ToList();
Dictionary result = new();
foreach (string fileName in fileNames)
{
string userIdHash = fileName.Replace($"{fileNamePrefix}.", string.Empty).Replace(fileExtension, string.Empty);
result.Add(userIdHash, ReadOrDefault(Path.Combine(folderPath, fileName), serializer));
}
return result;
}
public FileOperationResult Write(T value, string fullFilePath, Serializers serializer)
{
return WriteByFileMode(value, fullFilePath, serializer, FileMode.Create);
}
private FileOperationResult WriteByFileMode(T value, string fullFilePath, Serializers serializer, FileMode fileMode)
{
if (fullFilePath is null)
{
_logger.Info("Cannot save the file because the FullFilePath is null.");
return FileOperationResult.Failed;
}
CreateDirectory(fullFilePath);
try
{
using (MemoryStream memoryStream = GetSerializer(serializer).Serialize(value))
using (FileStream fileStream = new(fullFilePath, fileMode, FileAccess.Write, FileShare.None))
{
memoryStream.CopyTo(fileStream);
}
return FileOperationResult.Success;
}
catch (IOException ex) when (ex.HResult == -2147024816) // 0x80070050 - The file already exists
{
_logger.Info($"The file already exists ({fullFilePath}).");
return FileOperationResult.AlreadyExists;
}
catch (Exception ex)
{
_logger.Error($"Failed to write the file {fullFilePath}.", ex);
return FileOperationResult.Failed;
}
}
private void CreateDirectory(string fullFilePath)
{
try
{
string fullDirectoryPath = Path.GetDirectoryName(fullFilePath);
if (!Directory.Exists(fullDirectoryPath))
{
Directory.CreateDirectory(fullDirectoryPath);
_logger.Info($"Created the directory '{fullDirectoryPath}'.");
}
}
catch (Exception ex)
{
_logger.Error($"Failed to find or create the folder of the file {fullFilePath}.", ex);
}
}
public FileOperationResult CreateNew(T value, string fullFilePath, Serializers serializer)
{
return WriteByFileMode(value, fullFilePath, serializer, FileMode.CreateNew);
}
}