159 lines
6.4 KiB
C#
159 lines
6.4 KiB
C#
/// Copyright 2016 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<http://www.gnu.org/licenses/>.
|
|
///
|
|
/// Consult LICENSE file for details
|
|
|
|
using Acacia.Features.GAB;
|
|
using Acacia.Stubs;
|
|
using Acacia.Utils;
|
|
using Acacia.ZPush;
|
|
using Acacia.ZPush.Connect;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Acacia.Features.FreeBusy
|
|
{
|
|
public class FreeBusyServer
|
|
{
|
|
private readonly FeatureFreeBusy _freeBusy;
|
|
private readonly int _port;
|
|
private readonly Regex _httpRequest;
|
|
|
|
public FreeBusyServer(FeatureFreeBusy freeBusy)
|
|
{
|
|
this._freeBusy = freeBusy;
|
|
this._port = freeBusy.Port;
|
|
this._httpRequest = new Regex(@"^GET " + FeatureFreeBusy.URL_IDENTIFIER + @"([^ ]+) HTTP/(\d.\d)$");
|
|
}
|
|
|
|
public void HandleRequest(TcpClient client)
|
|
{
|
|
try
|
|
{
|
|
using (client)
|
|
{
|
|
StreamWriter writer = new StreamWriter(client.GetStream());
|
|
StreamReader reader = new StreamReader(client.GetStream());
|
|
try
|
|
{
|
|
// Read the request
|
|
string s = reader.ReadLine();
|
|
Match m = _httpRequest.Match(s);
|
|
if (!m.Success)
|
|
{
|
|
Logger.Instance.Trace(this, "Invalid request: {0}", s);
|
|
throw new InvalidOperationException();
|
|
}
|
|
string username = m.Groups[1].Value;
|
|
Logger.Instance.Trace(this, "REQUEST: {0} -> {1}, {2}", s, m.Groups[1], m.Groups[2]);
|
|
|
|
// Headers
|
|
for (;;)
|
|
{
|
|
s = reader.ReadLine();
|
|
if (string.IsNullOrEmpty(s))
|
|
break;
|
|
}
|
|
|
|
// Write response
|
|
FetchData(username, writer);
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
writer.Write("HTTP/1.0 404 Not found\r\nConnection: close\r\n\r\n");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.Instance.Error(this, "Error in FreeBusy worker: {0}", e);
|
|
writer.Write("HTTP/1.0 404 Not found\r\nConnection: close\r\n\r\n");
|
|
}
|
|
writer.Flush();
|
|
}
|
|
}
|
|
catch(Exception 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";
|
|
}
|
|
}
|
|
}
|
|
}
|