mirror of
https://github.com/Kopano-dev/kopano-ol-extension.git
synced 2023-10-10 13:37:40 +02:00
Small clean up to FreeBusy webserver to allow it to be used for different purposes in the future.
This commit is contained in:
parent
0fa8b2b949
commit
35ef86d5f0
@ -241,6 +241,8 @@
|
|||||||
<Compile Include="Controls\KUITask.cs" />
|
<Compile Include="Controls\KUITask.cs" />
|
||||||
<Compile Include="Controls\KUIUtil.cs" />
|
<Compile Include="Controls\KUIUtil.cs" />
|
||||||
<Compile Include="DebugOptions.cs" />
|
<Compile Include="DebugOptions.cs" />
|
||||||
|
<Compile Include="Features\FreeBusy\FreeBusyServlet.cs" />
|
||||||
|
<Compile Include="Features\FreeBusy\Servlet.cs" />
|
||||||
<Compile Include="Features\SecondaryContacts\FeatureSecondaryContacts.cs" />
|
<Compile Include="Features\SecondaryContacts\FeatureSecondaryContacts.cs" />
|
||||||
<Compile Include="Features\DebugSupport\AboutDialog.cs">
|
<Compile Include="Features\DebugSupport\AboutDialog.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
|
@ -111,9 +111,9 @@ namespace Acacia.Features.FreeBusy
|
|||||||
|
|
||||||
private const string REG_KEY = @"Options\Calendar\Internet Free/Busy";
|
private const string REG_KEY = @"Options\Calendar\Internet Free/Busy";
|
||||||
private const string REG_VALUE = @"Read URL";
|
private const string REG_VALUE = @"Read URL";
|
||||||
internal const string URL_IDENTIFIER = "/zpush/";
|
internal const string URL_IDENTIFIER = "zpush";
|
||||||
private const int DEFAULT_PORT = 18632;
|
private const int DEFAULT_PORT = 18632;
|
||||||
private const string URL_PREFIX = @"http://127.0.0.1:{0}" + URL_IDENTIFIER;
|
private const string URL_PREFIX = @"http://127.0.0.1:{0}/" + URL_IDENTIFIER + "/";
|
||||||
private const string URL = URL_PREFIX + "%NAME%@%SERVER%";
|
private const string URL = URL_PREFIX + "%NAME%@%SERVER%";
|
||||||
|
|
||||||
private void Worker()
|
private void Worker()
|
||||||
@ -125,8 +125,9 @@ namespace Acacia.Features.FreeBusy
|
|||||||
{
|
{
|
||||||
if (key != null)
|
if (key != null)
|
||||||
{
|
{
|
||||||
|
// Set only if empty or already our URL
|
||||||
string oldURL = key.GetValueString(REG_VALUE);
|
string oldURL = key.GetValueString(REG_VALUE);
|
||||||
if (string.IsNullOrWhiteSpace(oldURL) || oldURL.Contains(URL_IDENTIFIER))
|
if (string.IsNullOrWhiteSpace(oldURL) || oldURL.Contains("/" + URL_IDENTIFIER + "/"))
|
||||||
key.SetValue(REG_VALUE, string.Format(URL, Port));
|
key.SetValue(REG_VALUE, string.Format(URL, Port));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,15 +32,15 @@ namespace Acacia.Features.FreeBusy
|
|||||||
{
|
{
|
||||||
public class FreeBusyServer
|
public class FreeBusyServer
|
||||||
{
|
{
|
||||||
private readonly FeatureFreeBusy _freeBusy;
|
|
||||||
private readonly int _port;
|
|
||||||
private readonly Regex _httpRequest;
|
private readonly Regex _httpRequest;
|
||||||
|
|
||||||
|
public delegate Servlet ServletFactory();
|
||||||
|
private readonly Dictionary<string, ServletFactory> _servlets = new Dictionary<string, ServletFactory>();
|
||||||
|
|
||||||
public FreeBusyServer(FeatureFreeBusy freeBusy)
|
public FreeBusyServer(FeatureFreeBusy freeBusy)
|
||||||
{
|
{
|
||||||
this._freeBusy = freeBusy;
|
this._httpRequest = new Regex(@"^GET /([^/]+)/([^ ]*) HTTP/(\d.\d)$");
|
||||||
this._port = freeBusy.Port;
|
_servlets.Add(FeatureFreeBusy.URL_IDENTIFIER, () => new FreeBusyServlet(freeBusy));
|
||||||
this._httpRequest = new Regex(@"^GET " + FeatureFreeBusy.URL_IDENTIFIER + @"([^ ]+) HTTP/(\d.\d)$");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleRequest(TcpClient client)
|
public void HandleRequest(TcpClient client)
|
||||||
@ -61,19 +61,17 @@ namespace Acacia.Features.FreeBusy
|
|||||||
Logger.Instance.Trace(this, "Invalid request: {0}", s);
|
Logger.Instance.Trace(this, "Invalid request: {0}", s);
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
string username = m.Groups[1].Value;
|
string app = m.Groups[1].Value;
|
||||||
Logger.Instance.Trace(this, "REQUEST: {0} -> {1}, {2}", s, m.Groups[1], m.Groups[2]);
|
ServletFactory factory;
|
||||||
|
if (!_servlets.TryGetValue(app, out factory))
|
||||||
// Headers
|
|
||||||
for (;;)
|
|
||||||
{
|
{
|
||||||
s = reader.ReadLine();
|
Logger.Instance.Trace(this, "Unknown servlet: {0} -> {1}", s, app);
|
||||||
if (string.IsNullOrEmpty(s))
|
throw new InvalidOperationException();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write response
|
Servlet servlet = factory();
|
||||||
FetchData(username, writer);
|
servlet.Init(s, m.Groups[2].Value, reader, writer);
|
||||||
|
servlet.Process();
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
{
|
{
|
||||||
@ -92,67 +90,5 @@ namespace Acacia.Features.FreeBusy
|
|||||||
Logger.Instance.Error(this, "Error in FreeBusy worker: {0}", e);
|
Logger.Instance.Error(this, "Error in FreeBusy worker: {0}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FetchData(string username, StreamWriter output)
|
|
||||||
{
|
|
||||||
// Request the data from the ZPush server
|
|
||||||
ZPushConnection connection = new ZPushConnection(_freeBusy.FindZPushAccount(username), new System.Threading.CancellationToken(false));
|
|
||||||
|
|
||||||
// Include yesterday in the request, outlook shows it by default
|
|
||||||
var request = new ActiveSync.ResolveRecipientsRequest(username,
|
|
||||||
DateTime.Today.AddDays(-1),
|
|
||||||
DateTime.Today.AddMonths(6));
|
|
||||||
var response = connection.Execute(request);
|
|
||||||
|
|
||||||
// If there is no FreeBusy data, return 404
|
|
||||||
if (response?.FreeBusy == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Instance.Trace(this, "Writing response");
|
|
||||||
// Encode the response in vcard format
|
|
||||||
output.WriteLine("HTTP/1.0 200 OK");
|
|
||||||
output.WriteLine("Content-Type: text/vcard");
|
|
||||||
output.WriteLine("Connection: close");
|
|
||||||
output.WriteLine("");
|
|
||||||
|
|
||||||
|
|
||||||
output.WriteLine("BEGIN:VCALENDAR");
|
|
||||||
output.WriteLine("PRODID:-//ZPush//EN");
|
|
||||||
output.WriteLine("VERSION:2.0");
|
|
||||||
output.WriteLine("BEGIN:VFREEBUSY");
|
|
||||||
output.WriteLine("ORGANIZER:" + username);
|
|
||||||
output.WriteLine(string.Format("URL:http://127.0.0.1:{0}{1}{2}", _port, FeatureFreeBusy.URL_IDENTIFIER, username));
|
|
||||||
output.WriteLine(string.Format("DTSTAMP:{0:" + Constants.DATE_ISO_8601 + "}", DateTime.Now));
|
|
||||||
output.WriteLine(string.Format("DTSTART:{0:" + Constants.DATE_ISO_8601 + "}", response.FreeBusy.StartTime));
|
|
||||||
output.WriteLine(string.Format("DTEND:{0:" + Constants.DATE_ISO_8601 + "}", response.FreeBusy.EndTime));
|
|
||||||
|
|
||||||
foreach(ActiveSync.FreeBusyData data in response.FreeBusy)
|
|
||||||
{
|
|
||||||
if (data.Type != ActiveSync.FreeBusyType.Free)
|
|
||||||
{
|
|
||||||
string freeBusy = string.Format("FREEBUSY;FBTYPE={2}:{0:" + Constants.DATE_ISO_8601 + "}/{1:" + Constants.DATE_ISO_8601 + "}",
|
|
||||||
data.Start, data.End, MapType(data.Type));
|
|
||||||
output.WriteLine(freeBusy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output.WriteLine("END:VFREEBUSY");
|
|
||||||
output.WriteLine("END:VCALENDAR");
|
|
||||||
}
|
|
||||||
|
|
||||||
private object MapType(ActiveSync.FreeBusyType type)
|
|
||||||
{
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case ActiveSync.FreeBusyType.Free: return "FREE";
|
|
||||||
case ActiveSync.FreeBusyType.Busy: return "BUSY";
|
|
||||||
case ActiveSync.FreeBusyType.Tentative: return "BUSY-TENTATIVE";
|
|
||||||
case ActiveSync.FreeBusyType.OutOfOffice: return "BUSY-UNAVAILABLE";
|
|
||||||
default:
|
|
||||||
return "BUSY";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
using Acacia.Utils;
|
||||||
|
using Acacia.ZPush.Connect;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Acacia.Features.FreeBusy
|
||||||
|
{
|
||||||
|
public class FreeBusyServlet : Servlet
|
||||||
|
{
|
||||||
|
private readonly FeatureFreeBusy _freeBusy;
|
||||||
|
|
||||||
|
public FreeBusyServlet(FeatureFreeBusy freeBusy)
|
||||||
|
{
|
||||||
|
this._freeBusy = freeBusy;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ProcessRequest()
|
||||||
|
{
|
||||||
|
string username = Url;
|
||||||
|
// Request the data from the ZPush server
|
||||||
|
ZPushConnection connection = new ZPushConnection(_freeBusy.FindZPushAccount(username), new System.Threading.CancellationToken(false));
|
||||||
|
|
||||||
|
// Include yesterday in the request, outlook shows it by default
|
||||||
|
var request = new ActiveSync.ResolveRecipientsRequest(username,
|
||||||
|
DateTime.Today.AddDays(-1),
|
||||||
|
DateTime.Today.AddMonths(6));
|
||||||
|
var response = connection.Execute(request);
|
||||||
|
|
||||||
|
// If there is no FreeBusy data, return 404
|
||||||
|
if (response?.FreeBusy == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Instance.Trace(this, "Writing response");
|
||||||
|
// Encode the response in vcard format
|
||||||
|
Out.WriteLine("HTTP/1.0 200 OK");
|
||||||
|
Out.WriteLine("Content-Type: text/vcard");
|
||||||
|
Out.WriteLine("Connection: close");
|
||||||
|
Out.WriteLine("");
|
||||||
|
|
||||||
|
|
||||||
|
Out.WriteLine("BEGIN:VCALENDAR");
|
||||||
|
Out.WriteLine("PRODID:-//ZPush//EN");
|
||||||
|
Out.WriteLine("VERSION:2.0");
|
||||||
|
Out.WriteLine("BEGIN:VFREEBUSY");
|
||||||
|
Out.WriteLine("ORGANIZER:" + username);
|
||||||
|
Out.WriteLine(string.Format("URL:http://127.0.0.1:{0}/{1}/{2}", _freeBusy.Port, FeatureFreeBusy.URL_IDENTIFIER, username));
|
||||||
|
Out.WriteLine(string.Format("DTSTAMP:{0:" + Constants.DATE_ISO_8601 + "}", DateTime.Now));
|
||||||
|
Out.WriteLine(string.Format("DTSTART:{0:" + Constants.DATE_ISO_8601 + "}", response.FreeBusy.StartTime));
|
||||||
|
Out.WriteLine(string.Format("DTEND:{0:" + Constants.DATE_ISO_8601 + "}", response.FreeBusy.EndTime));
|
||||||
|
|
||||||
|
foreach (ActiveSync.FreeBusyData data in response.FreeBusy)
|
||||||
|
{
|
||||||
|
if (data.Type != ActiveSync.FreeBusyType.Free)
|
||||||
|
{
|
||||||
|
string freeBusy = string.Format("FREEBUSY;FBTYPE={2}:{0:" + Constants.DATE_ISO_8601 + "}/{1:" + Constants.DATE_ISO_8601 + "}",
|
||||||
|
data.Start, data.End, MapType(data.Type));
|
||||||
|
Out.WriteLine(freeBusy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Out.WriteLine("END:VFREEBUSY");
|
||||||
|
Out.WriteLine("END:VCALENDAR");
|
||||||
|
}
|
||||||
|
|
||||||
|
private object MapType(ActiveSync.FreeBusyType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ActiveSync.FreeBusyType.Free: return "FREE";
|
||||||
|
case ActiveSync.FreeBusyType.Busy: return "BUSY";
|
||||||
|
case ActiveSync.FreeBusyType.Tentative: return "BUSY-TENTATIVE";
|
||||||
|
case ActiveSync.FreeBusyType.OutOfOffice: return "BUSY-UNAVAILABLE";
|
||||||
|
default:
|
||||||
|
return "BUSY";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Acacia.Features.FreeBusy
|
||||||
|
{
|
||||||
|
abstract public class Servlet
|
||||||
|
{
|
||||||
|
private string _request;
|
||||||
|
protected string Url { get; private set; }
|
||||||
|
protected StreamReader In { get; private set; }
|
||||||
|
protected StreamWriter Out { get; private set; }
|
||||||
|
|
||||||
|
public void Init(string request, string url, StreamReader reader, StreamWriter writer)
|
||||||
|
{
|
||||||
|
this._request = request;
|
||||||
|
this.Url = url;
|
||||||
|
this.In = reader;
|
||||||
|
this.Out = writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Process()
|
||||||
|
{
|
||||||
|
ProcessHeaders();
|
||||||
|
ProcessRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual protected void ProcessHeaders()
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
string s = In.ReadLine();
|
||||||
|
if (string.IsNullOrEmpty(s))
|
||||||
|
break;
|
||||||
|
ProcessHeader(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual protected void ProcessHeader(string s)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected void ProcessRequest();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user