diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
index 57fb0a8..0b8109f 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj
@@ -397,6 +397,7 @@
+
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SyncState/FeatureSyncState.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SyncState/FeatureSyncState.cs
index 5063508..20992eb 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SyncState/FeatureSyncState.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SyncState/FeatureSyncState.cs
@@ -420,34 +420,58 @@ namespace Acacia.Features.SyncState
}
}
+
+ private class UserStoreInfo
+ {
+ public string emailaddress;
+ public string fullname;
+ public long foldercount;
+ public long storesize;
+
+ public override string ToString()
+ {
+ return string.Format("emailaddress={0}\nfullname={1}\nfoldercount={2}\nstoresize={3}",
+ emailaddress, fullname, foldercount, storesize);
+ }
+ }
+ private class GetUserStoreInfoRequest : SoapRequest
+ {
+ }
+
private void CheckSyncState(ZPushAccount account)
{
// TODO: we probably want one invocation for all accounts
using (ZPushConnection connection = account.Connect())
- using (ZPushWebServiceDevice deviceService = connection.DeviceService)
{
- // Fetch
- DeviceDetails details = deviceService.Execute(new GetDeviceDetailsRequest());
- if (details != null)
+ // Check total size
+ CheckTotalSize(connection);
+
+ // Update sync state
+ using (ZPushWebServiceDevice deviceService = connection.DeviceService)
{
- bool wasSyncing = false;
-
- // Create or update session
- SyncSession session = account.GetFeatureData(this, null);
- if (session == null)
- session = new SyncSession(this, account);
- else
- wasSyncing = session.IsSyncing;
-
- session.Add(details);
-
- // Store with the account
- account.SetFeatureData(this, null, session);
-
- if (wasSyncing != session.IsSyncing)
+ // Fetch
+ DeviceDetails details = deviceService.Execute(new GetDeviceDetailsRequest());
+ if (details != null)
{
- // Sync state has changed, update the schedule
- Watcher.Sync.SetTaskSchedule(_task, account, session.IsSyncing ? CheckPeriodSync : (TimeSpan?)null);
+ bool wasSyncing = false;
+
+ // Create or update session
+ SyncSession session = account.GetFeatureData(this, null);
+ if (session == null)
+ session = new SyncSession(this, account);
+ else
+ wasSyncing = session.IsSyncing;
+
+ session.Add(details);
+
+ // Store with the account
+ account.SetFeatureData(this, null, session);
+
+ if (wasSyncing != session.IsSyncing)
+ {
+ // Sync state has changed, update the schedule
+ Watcher.Sync.SetTaskSchedule(_task, account, session.IsSyncing ? CheckPeriodSync : (TimeSpan?)null);
+ }
}
}
}
@@ -739,7 +763,7 @@ namespace Acacia.Features.SyncState
public bool SupportsSyncTimeFrame(ZPushAccount account)
{
- return account?.ZPushVersion.IsAtLeast(2, 4) == true;
+ return account?.ZPushVersion?.IsAtLeast(2, 4) == true;
}
private class SetDeviceOptionsRequest : SoapRequest
@@ -752,7 +776,6 @@ namespace Acacia.Features.SyncState
public void SetDeviceOptions(ZPushAccount account, SyncTimeFrame timeFrame)
{
-
try
{
Logger.Instance.Debug(this, "Setting sync time frame for {0} to {1}", account, timeFrame);
@@ -778,5 +801,126 @@ namespace Acacia.Features.SyncState
}
+
+
+ #region Size checking
+
+ [AcaciaOption("Disable checking of store size to suggest a sync time frame.")]
+ public bool CheckStoreSize
+ {
+ get { return GetOption(OPTION_CHECK_STORE_SIZE); }
+ set { SetOption(OPTION_CHECK_STORE_SIZE, value); }
+ }
+ private static readonly BoolOption OPTION_CHECK_STORE_SIZE = new BoolOption("CheckStoreSize", true);
+
+ private bool _checkedStoreSize = false;
+
+ private const string KEY_CHECKED_SIZE = "KOE Size Checked";
+
+ private void CheckTotalSize(ZPushConnection connection)
+ {
+ if (!CheckStoreSize || _checkedStoreSize)
+ return;
+
+ // Only works on 2.5
+ // If we don't have the version yet, try again later
+ if (connection.Account.ZPushVersion == null)
+ return;
+
+ // Only check once
+ _checkedStoreSize = true;
+
+ // If it's not 2.5, don't check again
+ if (!connection.Account.ZPushVersion.IsAtLeast(2, 5))
+ return;
+
+ try
+ {
+ Logger.Instance.Debug(this, "Fetching size information for account {0}", connection.Account);
+ using (ZPushWebServiceInfo infoService = connection.InfoService)
+ {
+ UserStoreInfo info = infoService.Execute(new GetUserStoreInfoRequest());
+ Logger.Instance.Debug(this, "Size information: {0}", info);
+ SyncTimeFrame suggested = SuggestSyncTimeFrame(info);
+
+ if (suggested.IsShorterThan(connection.Account.SyncTimeFrame))
+ {
+ // Suggest shorter time frame, if not already done
+ string lastCheckedSize = connection.Account.Account[KEY_CHECKED_SIZE];
+ if (!string.IsNullOrWhiteSpace(lastCheckedSize))
+ {
+ try
+ {
+ SyncTimeFrame old = (SyncTimeFrame)int.Parse(lastCheckedSize);
+ if (old >= suggested)
+ {
+ Logger.Instance.Trace(this, "Not suggesting reduced sync time frame again: {0}: {2} -> {1}", info, suggested, old);
+ return;
+ }
+ }
+ catch(Exception e)
+ {
+ Logger.Instance.Warning(this, "Invalid lastCheckedSize: {0}: {1}", lastCheckedSize, e);
+ }
+ }
+
+ Logger.Instance.Debug(this, "Suggesting reduced sync time frame: {0}: {2} -> {1}", info, suggested, connection.Account.SyncTimeFrame);
+
+ // Suggest a shorter timeframe
+ DialogResult result = MessageBox.Show(ThisAddIn.Instance.Window,
+ string.Format(Properties.Resources.SyncState_StoreSize_Body,
+ info.storesize.ToSizeString(SizeUtil.Size.MB),
+ suggested.ToDisplayString()),
+ Properties.Resources.SyncState_StoreSize_Caption,
+ MessageBoxButtons.OKCancel, MessageBoxIcon.Information);
+
+ if (result == DialogResult.OK)
+ {
+ // Set the sync time frame
+ Logger.Instance.Debug(this, "Applying reduced sync time frame: {0}: {2} -> {1}", info, suggested, connection.Account.SyncTimeFrame);
+ connection.Account.SyncTimeFrame = suggested;
+ }
+ }
+
+ connection.Account.Account[KEY_CHECKED_SIZE] = ((int)suggested).ToString();
+ }
+ }
+ catch(Exception e)
+ {
+ Logger.Instance.Warning(this, "Error suggesting size: {0}", e);
+ }
+ }
+
+ private struct SuggestedSyncTimeFrame
+ {
+ public long storeSize;
+ public SyncTimeFrame timeFrame;
+
+ public SuggestedSyncTimeFrame(long storeSize, SyncTimeFrame timeFrame) : this()
+ {
+ this.storeSize = storeSize;
+ this.timeFrame = timeFrame;
+ }
+ }
+
+ private static readonly SuggestedSyncTimeFrame[] SUGGESTED_SYNC_TIME_FRAMES =
+ {
+ new SuggestedSyncTimeFrame(10.WithSize(SizeUtil.Size.GB), SyncTimeFrame.MONTH_1),
+ new SuggestedSyncTimeFrame(5.WithSize(SizeUtil.Size.GB), SyncTimeFrame.MONTH_3),
+ new SuggestedSyncTimeFrame(2.WithSize(SizeUtil.Size.GB), SyncTimeFrame.MONTH_6)
+ };
+
+ private SyncTimeFrame SuggestSyncTimeFrame(UserStoreInfo info)
+ {
+ foreach(SuggestedSyncTimeFrame suggested in SUGGESTED_SYNC_TIME_FRAMES)
+ {
+ if (info.storesize >= suggested.storeSize)
+ return suggested.timeFrame;
+ }
+ return SyncTimeFrame.ALL;
+ }
+
+ #endregion
}
+
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs
index b1ea2b7..6f2014a 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs
@@ -1346,6 +1346,99 @@ namespace Acacia.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to KOE has detected that the current store size exceeds to recommended value for synced data and will therefore reduce the time synced.
+ ///
+ ///Current store size: {0}
+ ///New sync window: {1}.
+ ///
+ internal static string SyncState_StoreSize_Body {
+ get {
+ return ResourceManager.GetString("SyncState_StoreSize_Body", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Store size.
+ ///
+ internal static string SyncState_StoreSize_Caption {
+ get {
+ return ResourceManager.GetString("SyncState_StoreSize_Caption", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to All.
+ ///
+ internal static string SyncTimeFrame_ALL {
+ get {
+ return ResourceManager.GetString("SyncTimeFrame_ALL", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to 1 day.
+ ///
+ internal static string SyncTimeFrame_DAY_1 {
+ get {
+ return ResourceManager.GetString("SyncTimeFrame_DAY_1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to 3 days.
+ ///
+ internal static string SyncTimeFrame_DAY_3 {
+ get {
+ return ResourceManager.GetString("SyncTimeFrame_DAY_3", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to 1 month.
+ ///
+ internal static string SyncTimeFrame_MONTH_1 {
+ get {
+ return ResourceManager.GetString("SyncTimeFrame_MONTH_1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to 3 months.
+ ///
+ internal static string SyncTimeFrame_MONTH_3 {
+ get {
+ return ResourceManager.GetString("SyncTimeFrame_MONTH_3", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to 6 months.
+ ///
+ internal static string SyncTimeFrame_MONTH_6 {
+ get {
+ return ResourceManager.GetString("SyncTimeFrame_MONTH_6", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to 1 week.
+ ///
+ internal static string SyncTimeFrame_WEEK_1 {
+ get {
+ return ResourceManager.GetString("SyncTimeFrame_WEEK_1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to 2 weeks.
+ ///
+ internal static string SyncTimeFrame_WEEK_2 {
+ get {
+ return ResourceManager.GetString("SyncTimeFrame_WEEK_2", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Kopano.
///
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx
index d8ad968..f026e3e 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx
@@ -551,4 +551,37 @@ Please contact your system administrator for any required changes.
Shared Folders
+
+ KOE has detected that the current store size exceeds to recommended value for synced data and will therefore reduce the time synced.
+
+Current store size: {0}
+New sync window: {1}
+
+
+ Store size
+
+
+ All
+
+
+ 1 day
+
+
+ 3 days
+
+
+ 1 month
+
+
+ 3 months
+
+
+ 6 months
+
+
+ 1 week
+
+
+ 2 weeks
+
\ No newline at end of file
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/SizeUtil.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/SizeUtil.cs
new file mode 100644
index 0000000..d2cc63b
--- /dev/null
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/SizeUtil.cs
@@ -0,0 +1,50 @@
+/// Copyright 2018 Kopano b.v.
+///
+/// This program is free software: you can redistribute it and/or modify
+/// it under the terms of the GNU Affero General Public License, version 3,
+/// as published by the Free Software Foundation.
+///
+/// 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 Affero General Public License for more details.
+///
+/// You should have received a copy of the GNU Affero General Public License
+/// along with this program.If not, see.
+///
+/// Consult LICENSE file for details
+///
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Acacia.Utils
+{
+ public static class SizeUtil
+ {
+ public enum Size
+ {
+ KB = 1024,
+ MB = 1024 * 1024,
+ GB = 1024 * 1024 * 1024
+ }
+
+ public static long WithSize(this long size, Size unit)
+ {
+ return size * (int)unit;
+ }
+
+ public static long WithSize(this int size, Size unit)
+ {
+ return WithSize((long)size, unit);
+ }
+
+ public static string ToSizeString(this long size, Size unit)
+ {
+ double value = (double)size / (int)unit;
+ return string.Format("{0:0.00}{1}", value, unit.ToString());
+ }
+ }
+}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/Connect/Soap/SoapSerializer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/Connect/Soap/SoapSerializer.cs
index 9abd813..3687440 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/Connect/Soap/SoapSerializer.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/Connect/Soap/SoapSerializer.cs
@@ -201,6 +201,37 @@ namespace Acacia.ZPush.Connect.Soap
public TypeHandlerLong() : base(SoapConstants.XMLNS_XSD, "int", typeof(long)) { }
}
+
+ abstract private class TypeHandlerReal : TypeHandler
+ {
+ public TypeHandlerReal(string xmlns, string name, Type baseType) : base(xmlns, name, baseType)
+ {
+ }
+
+ public override void Serialize(string name, object value, StringBuilder s)
+ {
+ s.Append(string.Format("<{0} xsi:type=\"xsd:{2}\">{1}{0}>", name, value, HandlesType == typeof(float) ? "float" : "double"));
+ }
+
+ protected override object DeserializeContents(XmlNode node, Type expectedType)
+ {
+ double value = double.Parse(node.InnerText);
+ if (expectedType == typeof(float))
+ return (float)value;
+ return value;
+ }
+ }
+
+ private class TypeHandlerFloat : TypeHandlerReal
+ {
+ public TypeHandlerFloat() : base(SoapConstants.XMLNS_XSD, "float", typeof(float)) { }
+ }
+
+ private class TypeHandlerDouble : TypeHandlerReal
+ {
+ public TypeHandlerDouble() : base(SoapConstants.XMLNS_XSD, "double", typeof(double)) { }
+ }
+
private class TypeHandlerString : TypeHandler
{
public TypeHandlerString() : base(SoapConstants.XMLNS_XSD, "string", typeof(string)) { }
@@ -486,6 +517,8 @@ namespace Acacia.ZPush.Connect.Soap
// What is called an int in soap might actually be a long here.
RegisterTypeHandler(new TypeHandlerLong());
RegisterTypeHandler(new TypeHandlerInt());
+ RegisterTypeHandler(new TypeHandlerFloat());
+ RegisterTypeHandler(new TypeHandlerDouble());
RegisterTypeHandler(new TypeHandlerString());
RegisterTypeHandler(new TypeHandlerList());
RegisterTypeHandler(new TypeHandlerStruct());
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushAccount.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushAccount.cs
index f1972ae..5be9a85 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushAccount.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushAccount.cs
@@ -348,7 +348,7 @@ namespace Acacia.ZPush
SyncTimeFrame frame = (SyncTimeFrame)val;
// If the timeframe exceeds one month, but Outlook is set to one month, only one month will be synced.
- if (!IsSyncOneMonthOrLess(frame) && EASSyncOneMonth)
+ if (!frame.IsOneMonthOrLess() && EASSyncOneMonth)
return SyncTimeFrame.MONTH_1;
return frame;
}
@@ -358,7 +358,7 @@ namespace Acacia.ZPush
if (value != SyncTimeFrame)
{
// Set the outlook property
- EASSyncOneMonth = IsSyncOneMonthOrLess(value);
+ EASSyncOneMonth = value.IsOneMonthOrLess();
// And the registry value
RegistryUtil.SetValueDword(Account.RegistryBaseKey, OutlookConstants.REG_VAL_SYNC_TIMEFRAME, (int)value);
}
@@ -381,11 +381,6 @@ namespace Acacia.ZPush
}
}
- private bool IsSyncOneMonthOrLess(SyncTimeFrame sync)
- {
- return sync <= SyncTimeFrame.MONTH_1 && sync != SyncTimeFrame.ALL;
- }
-
#endregion
#region Send as
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushTypes.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushTypes.cs
index b81e512..9e9de9e 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushTypes.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushTypes.cs
@@ -163,4 +163,30 @@ namespace Acacia.ZPush
MONTH_3,
MONTH_6
}
+
+ public static class SyncTimeFrameMethods
+ {
+ public static bool IsOneMonthOrLess(this SyncTimeFrame sync)
+ {
+ return sync <= SyncTimeFrame.MONTH_1 && sync != SyncTimeFrame.ALL;
+ }
+
+ public static bool IsShorterThan(this SyncTimeFrame _this, SyncTimeFrame other)
+ {
+ if (_this == SyncTimeFrame.ALL)
+ return false; // ALL can not be shorter than anything
+ if (other == SyncTimeFrame.ALL)
+ return true; // Always true, if this was ALL, already returned above, so this must be shorter
+
+ return (int)_this < (int)other;
+ }
+
+ public static string ToDisplayString(this SyncTimeFrame _this)
+ {
+ string s = Properties.Resources.ResourceManager.GetString("SyncTimeFrame_" + _this.ToString());
+ if (s == null)
+ return _this.ToString();
+ return s;
+ }
+ }
}
diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushVersion.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushVersion.cs
index 1d89da0..984264f 100644
--- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushVersion.cs
+++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/ZPush/ZPushVersion.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Acacia.ZPush
@@ -42,17 +43,20 @@ namespace Acacia.ZPush
if (string.IsNullOrWhiteSpace(version))
return null;
- string[] parts = version.Split('.');
try
{
- int major = int.Parse(parts[0]);
- int minor = int.Parse(parts[1]);
- return new ZPushVersion(major, minor, version);
+ Match match = new Regex(@"(\d+)[.](\d+)[.](\d+)[.]").Match(version);
+ if (match.Success)
+ {
+ int major = int.Parse(match.Groups[1].Value);
+ int minor = int.Parse(match.Groups[2].Value);
+ return new ZPushVersion(major, minor, version);
+ }
}
catch (Exception)
{
- return null;
}
+ return null;
}
public bool IsAtLeast(int major, int minor)