Add Thunderbird email support for Linux

On Windows this should already work through MAPI, but Linux doesn't have a standard email api.
This commit is contained in:
Ben Olden-Cooligan 2023-03-25 16:56:03 +00:00
parent 072f092a98
commit 50e12c4dc4
10 changed files with 262 additions and 320 deletions

View File

@ -22,6 +22,8 @@ public class AutofacEmailProviderFactory : IEmailProviderFactory
return _container.Resolve<GmailEmailProvider>();
case EmailProviderType.OutlookWeb:
return _container.Resolve<OutlookWebEmailProvider>();
case EmailProviderType.Thunderbird:
return _container.Resolve<ThunderbirdEmailProvider>();
default:
return _container.Resolve<MapiEmailProvider>();
}

View File

@ -13,17 +13,20 @@ public class EmailProviderForm : EtoDialogBase
private readonly SystemEmailClients _systemEmailClients;
private readonly GmailOauthProvider _gmailOauthProvider;
private readonly OutlookWebOauthProvider _outlookWebOauthProvider;
private readonly ThunderbirdEmailProvider _thunderbirdProvider;
private readonly List<EmailProviderWidget> _providerWidgets;
private readonly string[] _systemClientNames;
private readonly string? _defaultSystemClientName;
public EmailProviderForm(Naps2Config config, SystemEmailClients systemEmailClients,
GmailOauthProvider gmailOauthProvider, OutlookWebOauthProvider outlookWebOauthProvider) : base(config)
GmailOauthProvider gmailOauthProvider, OutlookWebOauthProvider outlookWebOauthProvider,
ThunderbirdEmailProvider thunderbirdProvider) : base(config)
{
_systemEmailClients = systemEmailClients;
_gmailOauthProvider = gmailOauthProvider;
_outlookWebOauthProvider = outlookWebOauthProvider;
_thunderbirdProvider = thunderbirdProvider;
_providerWidgets = new List<EmailProviderWidget>();
#if NET6_0_OR_GREATER
@ -52,6 +55,21 @@ public class EmailProviderForm : EtoDialogBase
}
#if NET6_0_OR_GREATER
}
// For Windows we expect Thunderbird to be used through MAPI. For Linux we need to handle it specially.
if (!OperatingSystem.IsWindows())
{
_providerWidgets.Add(new EmailProviderWidget
{
ProviderType = EmailProviderType.Thunderbird,
ProviderIcon = Icons.thunderbird.ToEtoImage(),
ProviderName = EmailProviderType.Thunderbird.Description(),
ClickAction = ChooseThunderbird,
// When Thunderbird isn't available, we disable it rather than hide it.
// The point is to give a hint to the user that Thunderbird support is present.
Enabled = _thunderbirdProvider.IsAvailable
});
}
#endif
if (_gmailOauthProvider.HasClientCreds)
@ -105,7 +123,8 @@ public class EmailProviderForm : EtoDialogBase
_providerWidgets.Select(x => C.Button(new ActionCommand(x.ClickAction)
{
Text = x.ProviderName,
Image = x.ProviderIcon
Image = x.ProviderIcon,
Enabled = x.Enabled
}, ButtonImagePosition.Left, big: true).NaturalWidth(500).Height(50)).Expand()
);
}
@ -123,6 +142,16 @@ public class EmailProviderForm : EtoDialogBase
Close();
}
private void ChooseThunderbird()
{
var transact = Config.User.BeginTransaction();
transact.Remove(c => c.EmailSetup);
transact.Set(c => c.EmailSetup.ProviderType, EmailProviderType.Thunderbird);
transact.Commit();
Result = true;
Close();
}
private void ChooseOauth(OauthProvider provider)
{
var authForm = FormFactory.Create<AuthorizeForm>();
@ -167,5 +196,6 @@ public class EmailProviderForm : EtoDialogBase
public required Bitmap ProviderIcon { get; init; }
public required string ProviderName { get; init; }
public required Action ClickAction { get; init; }
public bool Enabled { get; set; } = true;
}
}

View File

@ -609,6 +609,16 @@ namespace NAPS2 {
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] thunderbird {
get {
object obj = ResourceManager.GetObject("thunderbird", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>

View File

@ -346,6 +346,9 @@
<data name="outlookweb" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Icons\outlookweb.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="thunderbird" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Icons\thunderbird.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="email_setting" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Icons\email_setting.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>

BIN
NAPS2.Lib/Icons/thunderbird.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -10,5 +10,7 @@ public enum EmailProviderType
[LocalizedDescription(typeof(SettingsResources), "EmailProviderType_Gmail")]
Gmail,
[LocalizedDescription(typeof(SettingsResources), "EmailProviderType_OutlookWeb")]
OutlookWeb
OutlookWeb,
[LocalizedDescription(typeof(SettingsResources), "EmailProviderType_Thunderbird")]
Thunderbird
}

View File

@ -0,0 +1,92 @@
using System.Text;
using System.Text.RegularExpressions;
namespace NAPS2.ImportExport.Email;
public class ThunderbirdEmailProvider : IEmailProvider
{
private readonly ErrorOutput _errorOutput;
public ThunderbirdEmailProvider(ErrorOutput errorOutput)
{
_errorOutput = errorOutput;
}
// Note we can't really support the Flatpak version of Thunderbird as it won't have access to attachment files from
// the sandbox.
public bool IsAvailable => ProcessHelper.IsSuccessful("thunderbird", "-v", 1000);
public Task<bool> SendEmail(EmailMessage message, ProgressHandler progress = default)
{
return Task.Run(async () =>
{
var arguments = new List<string>();
string? bodyFile = null;
try
{
if (message.Attachments.Any())
{
// TODO: Need to use name if different than path (i.e. copy to temp)
arguments.Add($"attachment='{string.Join(",", message.Attachments.Select(x => x.FilePath))}'");
}
if (!string.IsNullOrEmpty(message.BodyText))
{
bodyFile = Path.Combine(Paths.Temp, Path.GetRandomFileName() + ".txt");
await File.WriteAllTextAsync(bodyFile, message.BodyText, Encoding.UTF8);
arguments.Add($"message='{bodyFile}'");
}
if (!string.IsNullOrEmpty(message.Subject))
{
// There doesn't seem to be a way to escape "'," but it shouldn't be common
arguments.Add($"subject='{message.Subject.Replace("',", "' ,")}'");
}
if (message.Recipients.Any(x => x.Type == EmailRecipientType.To))
{
arguments.Add(
$"to='{string.Join(",", message.Recipients.Where(x => x.Type == EmailRecipientType.To).Select(x => x.Address))}'");
}
if (message.Recipients.Any(x => x.Type == EmailRecipientType.Cc))
{
arguments.Add(
$"cc='{string.Join(",", message.Recipients.Where(x => x.Type == EmailRecipientType.Cc).Select(x => x.Address))}'");
}
if (message.Recipients.Any(x => x.Type == EmailRecipientType.Bcc))
{
arguments.Add(
$"bcc='{string.Join(",", message.Recipients.Where(x => x.Type == EmailRecipientType.Bcc).Select(x => x.Address))}'");
}
// This escaping isn't perfect but it should be good enough. I can't identify clear rules used by the
// thunderbird parser anyway.
var composeArgs = string.Join(",", arguments.Select(x => x.Replace("\"", "\\\"")));
Console.WriteLine(composeArgs);
var process =
Process.Start(new ProcessStartInfo("thunderbird", $"-compose \"{composeArgs}\"")
{
RedirectStandardOutput = true,
RedirectStandardError = true
});
await process.WaitForExitAsync();
}
catch (Exception ex)
{
_errorOutput.DisplayError(MiscResources.EmailError, ex);
return false;
}
finally
{
if (bodyFile != null)
{
try
{
File.Delete(bodyFile);
}
catch (Exception)
{
// Ignore
}
}
}
return true;
});
}
}

View File

@ -11,46 +11,32 @@ namespace NAPS2.Lang.Resources {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class SettingsResources {
private static global::System.Resources.ResourceManager resourceMan;
private static System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
private static System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal SettingsResources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
internal static System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NAPS2.Lang.Resources.SettingsResources", typeof(SettingsResources).Assembly);
if (object.Equals(null, resourceMan)) {
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("NAPS2.Lang.Resources.SettingsResources", typeof(SettingsResources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
internal static System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
@ -59,517 +45,304 @@ namespace NAPS2.Lang.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Black and White.
/// </summary>
internal static string BitDepth_1BlackAndWhite {
get {
return ResourceManager.GetString("BitDepth_1BlackAndWhite", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 24-bit Color.
/// </summary>
internal static string BitDepth_24Color {
get {
return ResourceManager.GetString("BitDepth_24Color", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Grayscale.
/// </summary>
internal static string BitDepth_8Grayscale {
get {
return ResourceManager.GetString("BitDepth_8Grayscale", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 100 dpi.
/// </summary>
internal static string Dpi_100 {
get {
return ResourceManager.GetString("Dpi_100", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 1200 dpi.
/// </summary>
internal static string Dpi_1200 {
get {
return ResourceManager.GetString("Dpi_1200", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 150 dpi.
/// </summary>
internal static string Dpi_150 {
get {
return ResourceManager.GetString("Dpi_150", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 200 dpi.
/// </summary>
internal static string Dpi_200 {
get {
return ResourceManager.GetString("Dpi_200", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 300 dpi.
/// </summary>
internal static string Dpi_300 {
get {
return ResourceManager.GetString("Dpi_300", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 400 dpi.
/// </summary>
internal static string Dpi_400 {
get {
return ResourceManager.GetString("Dpi_400", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 600 dpi.
/// </summary>
internal static string Dpi_600 {
get {
return ResourceManager.GetString("Dpi_600", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 800 dpi.
/// </summary>
internal static string Dpi_800 {
get {
return ResourceManager.GetString("Dpi_800", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} dpi.
/// </summary>
internal static string DpiFormat {
get {
return ResourceManager.GetString("DpiFormat", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No provider selected..
/// </summary>
internal static string EmailProvider_NotSelected {
get {
return ResourceManager.GetString("EmailProvider_NotSelected", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Custom SMTP.
/// </summary>
internal static string EmailProviderType_CustomSmtp {
get {
return ResourceManager.GetString("EmailProviderType_CustomSmtp", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Gmail.
/// </summary>
internal static string EmailProviderType_Gmail {
get {
return ResourceManager.GetString("EmailProviderType_Gmail", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Outlook Web Access.
/// </summary>
internal static string EmailProviderType_OutlookWeb {
get {
return ResourceManager.GetString("EmailProviderType_OutlookWeb", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Center.
/// </summary>
internal static string EmailProviderType_Thunderbird {
get {
return ResourceManager.GetString("EmailProviderType_Thunderbird", resourceCulture);
}
}
internal static string EmailProvider_NotSelected {
get {
return ResourceManager.GetString("EmailProvider_NotSelected", resourceCulture);
}
}
internal static string HorizontalAlign_Center {
get {
return ResourceManager.GetString("HorizontalAlign_Center", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Left.
/// </summary>
internal static string HorizontalAlign_Left {
get {
return ResourceManager.GetString("HorizontalAlign_Left", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Right.
/// </summary>
internal static string HorizontalAlign_Right {
get {
return ResourceManager.GetString("HorizontalAlign_Right", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Best.
/// </summary>
internal static string OcrMode_Best {
get {
return ResourceManager.GetString("OcrMode_Best", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Fast.
/// </summary>
internal static string OcrMode_Fast {
get {
return ResourceManager.GetString("OcrMode_Fast", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to A3 (297x420 mm).
/// </summary>
internal static string PageSize_A3 {
get {
return ResourceManager.GetString("PageSize_A3", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to A4 (210x297 mm).
/// </summary>
internal static string PageSize_A4 {
get {
return ResourceManager.GetString("PageSize_A4", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to A5 (148x210 mm).
/// </summary>
internal static string PageSize_A5 {
get {
return ResourceManager.GetString("PageSize_A5", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to B4 (250x353 mm).
/// </summary>
internal static string PageSize_B4 {
get {
return ResourceManager.GetString("PageSize_B4", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to B5 (176x250 mm).
/// </summary>
internal static string PageSize_B5 {
get {
return ResourceManager.GetString("PageSize_B5", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Custom....
/// </summary>
internal static string PageSize_Custom {
get {
return ResourceManager.GetString("PageSize_Custom", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to US Legal (8.5x14 in).
/// </summary>
internal static string PageSize_Legal {
get {
return ResourceManager.GetString("PageSize_Legal", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to US Letter (8.5x11 in).
/// </summary>
internal static string PageSize_Letter {
get {
return ResourceManager.GetString("PageSize_Letter", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to cm.
/// </summary>
internal static string PageSizeUnit_Centimetre {
get {
return ResourceManager.GetString("PageSizeUnit_Centimetre", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to in.
/// </summary>
internal static string PageSizeUnit_Inch {
get {
return ResourceManager.GetString("PageSizeUnit_Inch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to mm.
/// </summary>
internal static string PageSizeUnit_Millimetre {
get {
return ResourceManager.GetString("PageSizeUnit_Millimetre", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Default.
/// </summary>
internal static string PageSize_A3 {
get {
return ResourceManager.GetString("PageSize_A3", resourceCulture);
}
}
internal static string PageSize_A4 {
get {
return ResourceManager.GetString("PageSize_A4", resourceCulture);
}
}
internal static string PageSize_A5 {
get {
return ResourceManager.GetString("PageSize_A5", resourceCulture);
}
}
internal static string PageSize_B4 {
get {
return ResourceManager.GetString("PageSize_B4", resourceCulture);
}
}
internal static string PageSize_B5 {
get {
return ResourceManager.GetString("PageSize_B5", resourceCulture);
}
}
internal static string PageSize_Custom {
get {
return ResourceManager.GetString("PageSize_Custom", resourceCulture);
}
}
internal static string PageSize_Legal {
get {
return ResourceManager.GetString("PageSize_Legal", resourceCulture);
}
}
internal static string PageSize_Letter {
get {
return ResourceManager.GetString("PageSize_Letter", resourceCulture);
}
}
internal static string PdfCompat_Default {
get {
return ResourceManager.GetString("PdfCompat_Default", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to PDF/A-1b.
/// </summary>
internal static string PdfCompat_PdfA1B {
get {
return ResourceManager.GetString("PdfCompat_PdfA1B", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to PDF/A-2b.
/// </summary>
internal static string PdfCompat_PdfA2B {
get {
return ResourceManager.GetString("PdfCompat_PdfA2B", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to PDF/A-3b.
/// </summary>
internal static string PdfCompat_PdfA3B {
get {
return ResourceManager.GetString("PdfCompat_PdfA3B", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to PDF/A-3u.
/// </summary>
internal static string PdfCompat_PdfA3U {
get {
return ResourceManager.GetString("PdfCompat_PdfA3U", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 1:1.
/// </summary>
internal static string Scale_1_1 {
get {
return ResourceManager.GetString("Scale_1_1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 1:2.
/// </summary>
internal static string Scale_1_2 {
get {
return ResourceManager.GetString("Scale_1_2", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 1:4.
/// </summary>
internal static string Scale_1_4 {
get {
return ResourceManager.GetString("Scale_1_4", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 1:8.
/// </summary>
internal static string Scale_1_8 {
get {
return ResourceManager.GetString("Scale_1_8", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Duplex.
/// </summary>
internal static string Source_Duplex {
get {
return ResourceManager.GetString("Source_Duplex", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Feeder.
/// </summary>
internal static string Source_Feeder {
get {
return ResourceManager.GetString("Source_Feeder", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Glass.
/// </summary>
internal static string Source_Glass {
get {
return ResourceManager.GetString("Source_Glass", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Auto.
/// </summary>
internal static string TiffComp_Auto {
get {
return ResourceManager.GetString("TiffComp_Auto", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to CCITT4.
/// </summary>
internal static string TiffComp_Ccitt4 {
get {
return ResourceManager.GetString("TiffComp_Ccitt4", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to LZW.
/// </summary>
internal static string TiffComp_Lzw {
get {
return ResourceManager.GetString("TiffComp_Lzw", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to None.
/// </summary>
internal static string TiffComp_None {
get {
return ResourceManager.GetString("TiffComp_None", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Default.
/// </summary>
internal static string TwainImpl_Default {
get {
return ResourceManager.GetString("TwainImpl_Default", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Legacy (native UI only).
/// </summary>
internal static string TwainImpl_Legacy {
get {
return ResourceManager.GetString("TwainImpl_Legacy", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Memory Transfer.
/// </summary>
internal static string TwainImpl_MemXfer {
get {
return ResourceManager.GetString("TwainImpl_MemXfer", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Native Transfer.
/// </summary>
internal static string TwainImpl_NativeXfer {
get {
return ResourceManager.GetString("TwainImpl_NativeXfer", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Old DSM.
/// </summary>
internal static string TwainImpl_MemXfer {
get {
return ResourceManager.GetString("TwainImpl_MemXfer", resourceCulture);
}
}
internal static string TwainImpl_OldDsm {
get {
return ResourceManager.GetString("TwainImpl_OldDsm", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to x64.
/// </summary>
internal static string TwainImpl_X64 {
get {
return ResourceManager.GetString("TwainImpl_X64", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Default.
/// </summary>
internal static string WiaVersion_Default {
get {
return ResourceManager.GetString("WiaVersion_Default", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 1.0.
/// </summary>
internal static string WiaVersion_Wia10 {
get {
return ResourceManager.GetString("WiaVersion_Wia10", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to 2.0.
/// </summary>
internal static string WiaVersion_Wia20 {
get {
return ResourceManager.GetString("WiaVersion_Wia20", resourceCulture);
}
}
internal static string OcrMode_Fast {
get {
return ResourceManager.GetString("OcrMode_Fast", resourceCulture);
}
}
internal static string OcrMode_Best {
get {
return ResourceManager.GetString("OcrMode_Best", resourceCulture);
}
}
}
}

View File

@ -138,6 +138,9 @@
<data name="EmailProviderType_OutlookWeb" xml:space="preserve">
<value>Outlook Web Access</value>
</data>
<data name="EmailProviderType_Thunderbird" xml:space="preserve">
<value>Thunderbird</value>
</data>
<data name="EmailProvider_NotSelected" xml:space="preserve">
<value>No provider selected.</value>
</data>

View File

@ -3,4 +3,31 @@ namespace NAPS2.Util;
public static class ProcessHelper
{
public static void OpenUrl(string url) => Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
public static bool IsSuccessful(string command, string args, int timeoutMs)
{
try
{
var process = Process.Start(new ProcessStartInfo(command, args)
{
RedirectStandardOutput = true,
RedirectStandardError = true
});
if (process != null)
{
process.WaitForExit(timeoutMs);
bool result = process.HasExited && process.ExitCode == 0;
if (!process.HasExited)
{
process.Kill();
}
return result;
}
return false;
}
catch (Exception)
{
return false;
}
}
}