1
0
mirror of https://github.com/Kopano-dev/kopano-ol-extension.git synced 2023-10-10 13:37:40 +02:00

[KOE-123] Added basic code to register an account on startup. Not working fully yet, just creates a store.

This commit is contained in:
Patrick Simpson 2017-09-06 09:32:09 +03:00
parent ba21bf0474
commit 4ea8432f0f
14 changed files with 259 additions and 117 deletions

View File

@ -355,6 +355,7 @@
<Compile Include="Stubs\IPicture.cs" />
<Compile Include="Stubs\IRecipient.cs" />
<Compile Include="Stubs\IRecipients.cs" />
<Compile Include="Stubs\IRestarter.cs" />
<Compile Include="Stubs\ISignature.cs" />
<Compile Include="Stubs\ISignatures.cs" />
<Compile Include="Stubs\IStores.cs" />
@ -375,6 +376,7 @@
<Compile Include="Stubs\OutlookWrappers\RecipientsWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\RecipientWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\InspectorsWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\Restarter.cs" />
<Compile Include="Stubs\OutlookWrappers\SignaturesWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\SignatureWrapper.cs" />
<Compile Include="Stubs\OutlookWrappers\StoresWrapper.cs" />

View File

@ -162,7 +162,9 @@ namespace Acacia.Features.SecondaryContacts
MessageBoxIcon.Information
) == DialogResult.Yes)
{
ThisAddIn.Instance.Restart(true);
IRestarter restarter = ThisAddIn.Instance.Restarter();
restarter.CloseWindows = true;
restarter.Restart();
return true;
}
}

View File

@ -129,6 +129,7 @@
this.kTreeFolders.NodePadding = new System.Windows.Forms.Padding(2, 4, 2, 4);
this.kTreeFolders.CheckStateChanged += new Acacia.Controls.KTree.CheckStateChangedHandler(this.kTreeFolders_CheckStateChanged);
this.kTreeFolders.SelectionChanged += new Acacia.Controls.KTree.SelectionChangedDelegate(this.kTreeFolders_SelectionChanged);
this.kTreeFolders.DoubleClick += new System.EventHandler(this.kTreeFolders_DoubleClick);
//
// _layoutOptions
//

View File

@ -576,5 +576,23 @@ namespace Acacia.Features.SharedFolders
}
#endregion
private void kTreeFolders_DoubleClick(object sender, EventArgs e)
{
// TODO: This is some testing code for [KOE-123]
/*
if (ModifierKeys.HasFlag(Keys.Shift) && kTreeFolders.SelectedNodes.Count == 1)
{
KTreeNode selected = kTreeFolders.SelectedNodes.First();
if (selected is StoreTreeNode)
{
// Open store for user
Acacia.Stubs.IRestarter restarter = ThisAddIn.Instance.Restarter();
restarter.CloseWindows = true;
restarter.OpenShare(this._account, ((StoreTreeNode)selected).User);
restarter.Restart();
}
}*/
}
}
}

View File

@ -553,7 +553,9 @@ namespace Acacia.Features.SyncState
MessageBoxIcon.Error
) == DialogResult.Yes)
{
ThisAddIn.Instance.RestartResync(account);
IRestarter restarter = ThisAddIn.Instance.Restarter();
restarter.ResyncAccounts(account);
restarter.Restart();
}
});
}
@ -693,7 +695,9 @@ namespace Acacia.Features.SyncState
return true;
case ResyncOption.Full:
ThisAddIn.Instance.RestartResync(_accounts);
IRestarter restarter = ThisAddIn.Instance.Restarter();
restarter.ResyncAccounts(_accounts);
restarter.Restart();
return true;
}
return true;

View File

@ -42,6 +42,7 @@ namespace Acacia.Stubs
string DeviceId { get; }
SecureString Password { get; }
byte[] EncryptedPassword { get; }
bool HasPassword { get; }

View File

@ -66,13 +66,7 @@ namespace Acacia.Stubs
/// <summary>
/// Restarts the application
/// </summary>
void Restart(bool closeWindows);
/// <summary>
/// Restarts the application and removes the specified account files.
/// </summary>
/// <param name="accounts"></param>
void RestartResync(params ZPushAccount[] accounts);
IRestarter Restarter();
void Quit(bool closeWindows);

View File

@ -0,0 +1,35 @@
using Acacia.ZPush;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Acacia.Stubs
{
public interface IRestarter
{
/// <summary>
/// Should any open windows be closed? Default is false.
/// </summary>
bool CloseWindows { get; set; }
/// <summary>
/// Adds any accounts that need to be resynced.
/// </summary>
/// <param name="accounts"></param>
void ResyncAccounts(params ZPushAccount[] accounts);
/// <summary>
/// Adds a share.
/// </summary>
/// <param name="account"></param>
/// <param name="store"></param>
void OpenShare(ZPushAccount account, GABUser store);
/// <summary>
/// Performs the actual restart.
/// </summary>
void Restart();
}
}

View File

@ -160,8 +160,15 @@ namespace Acacia.Stubs.OutlookWrappers
{
get
{
byte[] encrypted = (byte[])Registry.GetValue(_regPath, OutlookConstants.REG_VAL_EAS_PASSWORD, null);
return PasswordEncryption.Decrypt(encrypted);
return PasswordEncryption.Decrypt(EncryptedPassword);
}
}
[Browsable(false)]
public byte[] EncryptedPassword
{
get
{
return (byte[])Registry.GetValue(_regPath, OutlookConstants.REG_VAL_EAS_PASSWORD, null);
}
}

View File

@ -156,56 +156,9 @@ namespace Acacia.Stubs.OutlookWrappers
_stores.Start();
}
public void Restart(bool closeWindows)
public IRestarter Restarter()
{
DoRestart(closeWindows, null);
}
private void DoRestart(bool closeWindows, string additionalCommandLine)
{
// Can not use the assembly location, as that is in the GAC
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
// Create the path to the restarter
path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(path), "OutlookRestarter.exe");
// Use the current command line, with a profile command if not specified
string commandLine = Environment.CommandLine;
// This selects both /profile and /profiles. In that case we don't specify the profile, otherwise
// we specify the current profile
// It seems to be impossible to escape a profile name with a quote, so in that case ignore it
if (!commandLine.ToLower().Contains("/profile") && !ProfileName.Contains("\""))
{
commandLine += " /profile " + Util.QuoteCommandLine(ProfileName);
}
if (!string.IsNullOrEmpty(additionalCommandLine))
{
commandLine = commandLine + " " + additionalCommandLine;
}
// Run that
Process process = new Process();
process.StartInfo = new ProcessStartInfo(path, Process.GetCurrentProcess().Id + " " + commandLine);
process.Start();
// And close us and any other windows
Quit(closeWindows);
}
public void RestartResync(ZPushAccount[] accounts)
{
string commandLine = "";
foreach(ZPushAccount account in accounts)
{
string path = account.Account.BackingFilePath;
if (!string.IsNullOrEmpty(path) && System.IO.Path.GetExtension(path) == ".ost")
{
commandLine += "/cleankoe " + Util.QuoteCommandLine(path);
}
}
DoRestart(true, commandLine);
return new Restarter(this);
}
public void Quit(bool closeWindows)

View File

@ -0,0 +1,104 @@
using Acacia.Utils;
using Acacia.ZPush;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Acacia.Stubs.OutlookWrappers
{
class Restarter : IRestarter
{
private readonly AddInWrapper _addIn;
private readonly List<ZPushAccount> _resyncAccounts = new List<ZPushAccount>();
private readonly List<KeyValuePair<ZPushAccount, GABUser>> _shares = new List<KeyValuePair<ZPushAccount, GABUser>>();
public Restarter(AddInWrapper addIn)
{
this._addIn = addIn;
}
public bool CloseWindows
{
get;
set;
}
private string RestarterPath
{
get
{
// Can not use the assembly location, as that is in the GAC
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
// Create the path to the restarter
return System.IO.Path.Combine(System.IO.Path.GetDirectoryName(path), "OutlookRestarter.exe");
}
}
public void ResyncAccounts(params ZPushAccount[] accounts)
{
_resyncAccounts.AddRange(accounts);
}
public void OpenShare(ZPushAccount account, GABUser store)
{
_shares.Add(new KeyValuePair<ZPushAccount, GABUser>(account, store));
}
public void Restart()
{
// Use the current command line, with a profile command if not specified
string commandLine = Environment.CommandLine;
// This selects both /profile and /profiles. In that case we don't specify the profile, otherwise
// we specify the current profile
// It seems to be impossible to escape a profile name with a quote, so in that case ignore it
if (!commandLine.ToLower().Contains("/profile") && !_addIn.ProfileName.Contains("\""))
{
commandLine += " /profile " + Util.QuoteCommandLine(_addIn.ProfileName);
}
// Custom command line
foreach (ZPushAccount account in _resyncAccounts)
{
string path = account.Account.BackingFilePath;
if (!string.IsNullOrEmpty(path) && System.IO.Path.GetExtension(path) == ".ost")
{
commandLine += " /cleankoe" + Util.QuoteCommandLine(path);
}
}
foreach(KeyValuePair<ZPushAccount, GABUser> share in _shares)
{
using (RegistryKey key = OutlookRegistryUtils.OpenProfileOutlookKey(_addIn.ProfileName, RegistryKeyPermissionCheck.ReadWriteSubTree))
{
int accountId = (int)key.GetValue(OutlookConstants.REG_VAL_NEXT_ACCOUNT_ID);
using (RegistryKey accountKey = key.CreateSubKey(string.Format("{0:X8}", accountId)))
{
accountKey.SetValue(OutlookConstants.REG_VAL_ACCOUNTNAME, share.Value.UserName + " through " + share.Key.DisplayName);
accountKey.SetValue(OutlookConstants.REG_VAL_DISPLAYNAME, "Share for " + share.Value.DisplayName);
accountKey.SetValue(OutlookConstants.REG_VAL_EMAIL, "test" + share.Key.Account.SmtpAddress);
accountKey.SetValue(OutlookConstants.REG_VAL_EAS_SERVER, share.Key.Account.ServerURL);
accountKey.SetValue(OutlookConstants.REG_VAL_EAS_USERNAME, share.Key.Account.UserName + ".share." + share.Value.UserName);
accountKey.SetValue(OutlookConstants.REG_VAL_EAS_PASSWORD, share.Key.Account.EncryptedPassword);
//accountKey.SetValue(OutlookConstants.REG_VAL_EAS_DEVICEID, share.Key.Account.DeviceId);
accountKey.SetValue("clsid", "{ED475415-B0D6-11D2-8C3B-00104B2A6676}");
}
key.SetValue(OutlookConstants.REG_VAL_NEXT_ACCOUNT_ID, accountId + 1);
}
}
// Run that
Process process = new Process();
process.StartInfo = new ProcessStartInfo(RestarterPath, Process.GetCurrentProcess().Id + " " + commandLine);
process.Start();
// And close us and any other windows
_addIn.Quit(CloseWindows);
}
}
}

View File

@ -292,8 +292,7 @@ namespace Acacia.Stubs.OutlookWrappers
NSOutlook.NameSpace session = _item.Session;
try
{
string path = string.Format(OutlookConstants.REG_SUBKEY_ACCOUNTS, session.CurrentProfileName);
return OutlookRegistryUtils.OpenOutlookKey(path);
return OutlookRegistryUtils.OpenProfileOutlookKey(session.CurrentProfileName);
}
finally
{

View File

@ -25,6 +25,11 @@ namespace Acacia.Utils
{
public static class OutlookRegistryUtils
{
public static RegistryKey OpenProfileOutlookKey(string profile, RegistryKeyPermissionCheck permissions = RegistryKeyPermissionCheck.Default)
{
string path = string.Format(OutlookConstants.REG_SUBKEY_ACCOUNTS, profile);
return OpenOutlookKey(path, permissions);
}
public static RegistryKey OpenOutlookKey(string suffix = null, RegistryKeyPermissionCheck permissions = RegistryKeyPermissionCheck.Default)
{

View File

@ -1,5 +1,7 @@

using Acacia;
using Acacia.Utils;
using Microsoft.Win32;
/// Copyright 2017 Kopano b.v.
///
/// This program is free software: you can redistribute it and/or modify
@ -46,70 +48,85 @@ namespace OutlookRestarter
{
Logger.Instance.Debug(typeof(OutlookRestarter), "Restarting: {0}: {1}", BuildVersions.VERSION, string.Join(", ", args));
string procPath = args[1];
List<string> procArgs = args.Skip(2).ToList();
try
{
// Attempt waiting for the process to finish
int procId = int.Parse(args[0]);
Process proc = Process.GetProcessById(procId);
Logger.Instance.Debug(typeof(OutlookRestarter), "Waiting for process to exit: {0}: {1}", procId, proc);
bool finished = proc.WaitForExit(FINISH_WAIT_TIME);
Logger.Instance.Debug(typeof(OutlookRestarter), "Waited for process to exit: {0}: {1}", procId, finished);
}
finally
{
Logger.Instance.Debug(typeof(OutlookRestarter), "Parsing arguments");
List<string> useArgs = new List<string>();
for (int i = 0; i < procArgs.Count; ++i)
string procPath = args[1];
List<string> procArgs = args.Skip(2).ToList();
try
{
if (procArgs[i] == "/cleankoe")
Logger.Instance.Debug(typeof(OutlookRestarter), "Waiting1");
// Attempt waiting for the process to finish
int procId = int.Parse(args[0]);
Logger.Instance.Debug(typeof(OutlookRestarter), "Waiting2");
Process proc = Process.GetProcessById(procId);
Logger.Instance.Debug(typeof(OutlookRestarter), "Waiting3");
Logger.Instance.Debug(typeof(OutlookRestarter), "Waiting for process to exit: {0}: {1}", procId, proc);
bool finished = proc.WaitForExit(FINISH_WAIT_TIME);
Logger.Instance.Debug(typeof(OutlookRestarter), "Waited for process to exit: {0}: {1}", procId, finished);
}
finally
{
Logger.Instance.Debug(typeof(OutlookRestarter), "Parsing arguments");
List<string> useArgs = new List<string>();
for (int i = 0; i < procArgs.Count; ++i)
{
++i;
string path = procArgs[i];
Logger.Instance.Debug(typeof(OutlookRestarter), "Request to remove store: {0}", path);
if (System.IO.Path.GetExtension(path) == ".ost")
if (procArgs[i] == "/cleankoe")
{
Logger.Instance.Info(typeof(OutlookRestarter), "Removing store: {0}", path);
for (int attempt = 0; attempt < DELETE_RETRIES; ++attempt)
{
// Delete it
try
{
System.IO.File.Delete(path);
// Success, done
break;
}
catch (IOException e)
{
Logger.Instance.Error(typeof(OutlookRestarter), "IOException removing store: {0}: on attempt {1}: {2}", path, attempt, e);
// IO Exception. Wait a while and retry
Thread.Sleep(DELETE_WAIT_TIME);
}
catch (Exception e)
{
Logger.Instance.Error(typeof(OutlookRestarter), "Exception removing store: {0}: {1}", path, e);
// This kind of exception will not be resolved by retrying
break;
}
}
++i;
string path = procArgs[i];
HandleCleanKoe(path);
}
else
{
useArgs.Add(procArgs[i]);
}
}
else
string argsString = string.Join(" ", useArgs);
Logger.Instance.Debug(typeof(OutlookRestarter), "Parsed arguments: {0}", argsString);
// Start the process
Process process = new Process();
process.StartInfo = new ProcessStartInfo(procPath, argsString);
Logger.Instance.Debug(typeof(OutlookRestarter), "Starting process: {0}", process);
process.Start();
Logger.Instance.Debug(typeof(OutlookRestarter), "Started process: {0}", process);
}
}
catch(Exception e)
{
Logger.Instance.Fatal(typeof(OutlookRestarter), "Exception: {0}", e);
}
}
private static void HandleCleanKoe(string path)
{
Logger.Instance.Debug(typeof(OutlookRestarter), "Request to remove store: {0}", path);
if (Path.GetExtension(path) == ".ost")
{
Logger.Instance.Info(typeof(OutlookRestarter), "Removing store: {0}", path);
for (int attempt = 0; attempt < DELETE_RETRIES; ++attempt)
{
// Delete it
try
{
useArgs.Add(procArgs[i]);
File.Delete(path);
// Success, done
break;
}
catch (IOException e)
{
Logger.Instance.Error(typeof(OutlookRestarter), "IOException removing store: {0}: on attempt {1}: {2}", path, attempt, e);
// IO Exception. Wait a while and retry
Thread.Sleep(DELETE_WAIT_TIME);
}
catch (Exception e)
{
Logger.Instance.Error(typeof(OutlookRestarter), "Exception removing store: {0}: {1}", path, e);
// This kind of exception will not be resolved by retrying
break;
}
}
string argsString = string.Join(" ", useArgs);
Logger.Instance.Debug(typeof(OutlookRestarter), "Parsed arguments: {0}", argsString);
// Start the process
Process process = new Process();
process.StartInfo = new ProcessStartInfo(procPath, argsString);
Logger.Instance.Debug(typeof(OutlookRestarter), "Starting process: {0}", process);
process.Start();
Logger.Instance.Debug(typeof(OutlookRestarter), "Started process: {0}", process);
}
}
}