[tomboy/mac-0.15.7] [WebSync] Bundle MonoHttp (for HttpListener) on Windows
- From: Sanford Armstrong <sharm src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [tomboy/mac-0.15.7] [WebSync] Bundle MonoHttp (for HttpListener) on Windows
- Date: Tue, 8 Sep 2009 19:09:12 +0000 (UTC)
commit 921e95411cfe5a890a9a2a81d941b1e0f481ad01
Author: Sandy Armstrong <sanfordarmstrong gmail com>
Date: Tue Sep 8 10:49:48 2009 -0700
[WebSync] Bundle MonoHttp (for HttpListener) on Windows
This works around the problem that .NET's HttpListener
only works if you have admin privileges.
.../MonoHttp/AuthenticationSchemeSelector.cs | 37 +
.../MonoHttp/AuthenticationSchemes.cs | 51 ++
.../Addins/WebSyncService/MonoHttp/ChunkStream.cs | 333 ++++++++++
.../WebSyncService/MonoHttp/ChunkedInputStream.cs | 178 +++++
.../WebSyncService/MonoHttp/EndPointListener.cs | 326 +++++++++
.../WebSyncService/MonoHttp/EndPointManager.cs | 147 ++++
.../WebSyncService/MonoHttp/HttpConnection.cs | 353 ++++++++++
.../Addins/WebSyncService/MonoHttp/HttpListener.cs | 319 +++++++++
.../MonoHttp/HttpListenerBasicIdentity.cs | 47 ++
.../WebSyncService/MonoHttp/HttpListenerContext.cs | 139 ++++
.../MonoHttp/HttpListenerException.cs | 59 ++
.../MonoHttp/HttpListenerPrefixCollection.cs | 130 ++++
.../WebSyncService/MonoHttp/HttpListenerRequest.cs | 450 +++++++++++++
.../MonoHttp/HttpListenerResponse.cs | 512 ++++++++++++++
.../WebSyncService/MonoHttp/HttpRequestHeader.cs | 77 +++
.../WebSyncService/MonoHttp/HttpResponseHeader.cs | 66 ++
.../MonoHttp/HttpStreamAsyncResult.cs | 97 +++
.../Addins/WebSyncService/MonoHttp/HttpUtility.cs | 105 +++
.../WebSyncService/MonoHttp/ListenerAsyncResult.cs | 135 ++++
.../WebSyncService/MonoHttp/ListenerPrefix.cs | 163 +++++
.../WebSyncService/MonoHttp/RequestStream.cs | 226 +++++++
.../WebSyncService/MonoHttp/ResponseStream.cs | 243 +++++++
Tomboy/Addins/WebSyncService/MonoHttp/Utility.cs | 98 +++
.../WebSyncService/MonoHttp/WebHeaderCollection.cs | 699 ++++++++++++++++++++
.../WebSyncService/WebSyncPreferencesWidget.cs | 5 +-
Tomboy/Addins/WebSyncService/WebSyncService.csproj | 24 +
26 files changed, 5016 insertions(+), 3 deletions(-)
---
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/AuthenticationSchemeSelector.cs b/Tomboy/Addins/WebSyncService/MonoHttp/AuthenticationSchemeSelector.cs
new file mode 100644
index 0000000..b16e11a
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/AuthenticationSchemeSelector.cs
@@ -0,0 +1,37 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.AuthenticationSchemeSelector.cs
+//
+// Author:
+// Gonzalo Paniagua Javier <gonzalo novell com>
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if !NET_2_0
+
+using System; using System.Net; namespace MonoHttp {
+ internal delegate AuthenticationSchemes AuthenticationSchemeSelector (HttpListenerRequest httpRequest);
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/AuthenticationSchemes.cs b/Tomboy/Addins/WebSyncService/MonoHttp/AuthenticationSchemes.cs
new file mode 100644
index 0000000..cce5fa9
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/AuthenticationSchemes.cs
@@ -0,0 +1,51 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.AuthenticationSchemes.cs
+//
+// Author:
+// Atsushi Enomoto <atsushi ximian com>
+//
+// (C) 2005 Novell, Inc. (http://www.novell.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if !NET_2_0
+
+using System.Collections;
+using System.Configuration;
+
+using System; using System.Net; namespace MonoHttp
+{
+ [Flags]
+ internal enum AuthenticationSchemes {
+ None,
+ Digest = 1,
+ Negotiate = 2,
+ Ntlm = 4,
+ IntegratedWindowsAuthentication = 6,
+ Basic = 8,
+ Anonymous = 0x8000,
+ }
+}
+
+#endif
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/ChunkStream.cs b/Tomboy/Addins/WebSyncService/MonoHttp/ChunkStream.cs
new file mode 100644
index 0000000..c2314f9
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/ChunkStream.cs
@@ -0,0 +1,333 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.ChunkStream
+//
+// Authors:
+// Gonzalo Paniagua Javier (gonzalo ximian com)
+//
+// (C) 2003 Ximian, Inc (http://www.ximian.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+using System; using System.Net; namespace MonoHttp
+{
+ class ChunkStream
+ {
+ enum State {
+ None,
+ Body,
+ BodyFinished,
+ Trailer
+ }
+
+ class Chunk {
+ public byte [] Bytes;
+ public int Offset;
+
+ public Chunk (byte [] chunk)
+ {
+ this.Bytes = chunk;
+ }
+
+ public int Read (byte [] buffer, int offset, int size)
+ {
+ int nread = (size > Bytes.Length - Offset) ? Bytes.Length - Offset : size;
+ Buffer.BlockCopy (Bytes, Offset, buffer, offset, nread);
+ Offset += nread;
+ return nread;
+ }
+ }
+
+ internal WebHeaderCollection headers;
+ int chunkSize;
+ int chunkRead;
+ State state;
+ //byte [] waitBuffer;
+ StringBuilder saved;
+ bool sawCR;
+ bool gotit;
+ int trailerState;
+ ArrayList chunks;
+
+ public ChunkStream (byte [] buffer, int offset, int size, WebHeaderCollection headers)
+ : this (headers)
+ {
+ Write (buffer, offset, size);
+ }
+
+ public ChunkStream (WebHeaderCollection headers)
+ {
+ this.headers = headers;
+ saved = new StringBuilder ();
+ chunks = new ArrayList ();
+ chunkSize = -1;
+ }
+
+ public void ResetBuffer ()
+ {
+ chunkSize = -1;
+ chunkRead = 0;
+ chunks.Clear ();
+ }
+
+ public void WriteAndReadBack (byte [] buffer, int offset, int size, ref int read)
+ {
+ if (offset + read > 0)
+ Write (buffer, offset, offset+read);
+ read = Read (buffer, offset, size);
+ }
+
+ public int Read (byte [] buffer, int offset, int size)
+ {
+ return ReadFromChunks (buffer, offset, size);
+ }
+
+ int ReadFromChunks (byte [] buffer, int offset, int size)
+ {
+ int count = chunks.Count;
+ int nread = 0;
+ for (int i = 0; i < count; i++) {
+ Chunk chunk = (Chunk) chunks [i];
+ if (chunk == null)
+ continue;
+
+ if (chunk.Offset == chunk.Bytes.Length) {
+ chunks [i] = null;
+ continue;
+ }
+
+ nread += chunk.Read (buffer, offset + nread, size - nread);
+ if (nread == size)
+ break;
+ }
+
+ return nread;
+ }
+
+ public void Write (byte [] buffer, int offset, int size)
+ {
+ InternalWrite (buffer, ref offset, size);
+ }
+
+ void InternalWrite (byte [] buffer, ref int offset, int size)
+ {
+ if (state == State.None) {
+ state = GetChunkSize (buffer, ref offset, size);
+ if (state == State.None)
+ return;
+
+ saved.Length = 0;
+ sawCR = false;
+ gotit = false;
+ }
+
+ if (state == State.Body && offset < size) {
+ state = ReadBody (buffer, ref offset, size);
+ if (state == State.Body)
+ return;
+ }
+
+ if (state == State.BodyFinished && offset < size) {
+ state = ReadCRLF (buffer, ref offset, size);
+ if (state == State.BodyFinished)
+ return;
+
+ sawCR = false;
+ }
+
+ if (state == State.Trailer && offset < size) {
+ state = ReadTrailer (buffer, ref offset, size);
+ if (state == State.Trailer)
+ return;
+
+ saved.Length = 0;
+ sawCR = false;
+ gotit = false;
+ }
+
+ if (offset < size)
+ InternalWrite (buffer, ref offset, size);
+ }
+
+ public bool WantMore {
+ get { return (chunkRead != chunkSize || chunkSize != 0 || state != State.None); }
+ }
+
+ public int ChunkLeft {
+ get { return chunkSize - chunkRead; }
+ }
+
+ State ReadBody (byte [] buffer, ref int offset, int size)
+ {
+ if (chunkSize == 0)
+ return State.BodyFinished;
+
+ int diff = size - offset;
+ if (diff + chunkRead > chunkSize)
+ diff = chunkSize - chunkRead;
+
+ byte [] chunk = new byte [diff];
+ Buffer.BlockCopy (buffer, offset, chunk, 0, diff);
+ chunks.Add (new Chunk (chunk));
+ offset += diff;
+ chunkRead += diff;
+ return (chunkRead == chunkSize) ? State.BodyFinished : State.Body;
+
+ }
+
+ State GetChunkSize (byte [] buffer, ref int offset, int size)
+ {
+ char c = '\0';
+ while (offset < size) {
+ c = (char) buffer [offset++];
+ if (c == '\r') {
+ if (sawCR)
+ ThrowProtocolViolation ("2 CR found");
+
+ sawCR = true;
+ continue;
+ }
+
+ if (sawCR && c == '\n')
+ break;
+
+ if (c == ' ')
+ gotit = true;
+
+ if (!gotit)
+ saved.Append (c);
+
+ if (saved.Length > 20)
+ ThrowProtocolViolation ("chunk size too long.");
+ }
+
+ if (!sawCR || c != '\n') {
+ if (offset < size)
+ ThrowProtocolViolation ("Missing \\n");
+
+ try {
+ if (saved.Length > 0)
+ chunkSize = Int32.Parse (saved.ToString (), NumberStyles.HexNumber);
+ } catch (Exception) {
+ ThrowProtocolViolation ("Cannot parse chunk size.");
+ }
+
+ return State.None;
+ }
+
+ chunkRead = 0;
+ try {
+ chunkSize = Int32.Parse (saved.ToString (), NumberStyles.HexNumber);
+ } catch (Exception) {
+ ThrowProtocolViolation ("Cannot parse chunk size.");
+ }
+
+ if (chunkSize == 0) {
+ trailerState = 2;
+ return State.Trailer;
+ }
+
+ return State.Body;
+ }
+
+ State ReadCRLF (byte [] buffer, ref int offset, int size)
+ {
+ if (!sawCR) {
+ if ((char) buffer [offset++] != '\r')
+ ThrowProtocolViolation ("Expecting \\r");
+
+ sawCR = true;
+ if (offset == size)
+ return State.BodyFinished;
+ }
+
+ if (sawCR && (char) buffer [offset++] != '\n')
+ ThrowProtocolViolation ("Expecting \\n");
+
+ return State.None;
+ }
+
+ State ReadTrailer (byte [] buffer, ref int offset, int size)
+ {
+ char c = '\0';
+
+ // short path
+ if (trailerState == 2 && (char) buffer [offset] == '\r' && saved.Length == 0) {
+ offset++;
+ if (offset < size && (char) buffer [offset] == '\n') {
+ offset++;
+ return State.None;
+ }
+ offset--;
+ }
+
+ int st = trailerState;
+ string stString = "\r\n\r";
+ while (offset < size && st < 4) {
+ c = (char) buffer [offset++];
+ if ((st == 0 || st == 2) && c == '\r') {
+ st++;
+ continue;
+ }
+
+ if ((st == 1 || st == 3) && c == '\n') {
+ st++;
+ continue;
+ }
+
+ if (st > 0) {
+ saved.Append (stString.Substring (0, saved.Length == 0? st-2: st));
+ st = 0;
+ }
+ }
+
+ if (st < 4) {
+ trailerState = st;
+ if (offset < size)
+ ThrowProtocolViolation ("Error reading trailer.");
+
+ return State.Trailer;
+ }
+
+ StringReader reader = new StringReader (saved.ToString ());
+ string line;
+ while ((line = reader.ReadLine ()) != null && line != "")
+ headers.Add (line);
+
+ return State.None;
+ }
+
+ static void ThrowProtocolViolation (string message)
+ {
+ WebException we = new WebException (message, null, WebExceptionStatus.ServerProtocolViolation, null);
+ throw we;
+ }
+ }
+}
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/ChunkedInputStream.cs b/Tomboy/Addins/WebSyncService/MonoHttp/ChunkedInputStream.cs
new file mode 100644
index 0000000..2d004a4
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/ChunkedInputStream.cs
@@ -0,0 +1,178 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.ChunkedInputStream
+//
+// Authors:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#if EMBEDDED_IN_1_0
+
+using System.IO;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System; using System.Net; namespace MonoHttp {
+ class ChunkedInputStream : RequestStream {
+ bool disposed;
+ ChunkStream decoder;
+ HttpListenerContext context;
+ bool no_more_data;
+
+ class ReadBufferState {
+ public byte [] Buffer;
+ public int Offset;
+ public int Count;
+ public int InitialCount;
+ public HttpStreamAsyncResult Ares;
+ public ReadBufferState (byte [] buffer, int offset, int count,
+ HttpStreamAsyncResult ares)
+ {
+ Buffer = buffer;
+ Offset = offset;
+ Count = count;
+ InitialCount = count;
+ Ares = ares;
+ }
+ }
+
+ public ChunkedInputStream (HttpListenerContext context, Stream stream,
+ byte [] buffer, int offset, int length)
+ : base (stream, buffer, offset, length)
+ {
+ this.context = context;
+ WebHeaderCollection coll = (WebHeaderCollection) context.Request.Headers;
+ decoder = new ChunkStream (coll);
+ }
+
+ public ChunkStream Decoder {
+ get { return decoder; }
+ set { decoder = value; }
+ }
+
+ public override int Read ([In,Out] byte [] buffer, int offset, int count)
+ {
+ IAsyncResult ares = BeginRead (buffer, offset, count, null, null);
+ return EndRead (ares);
+ }
+
+ public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
+ AsyncCallback cback, object state)
+ {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ if (buffer == null)
+ throw new ArgumentNullException ("buffer");
+
+ int len = buffer.Length;
+ if (offset < 0 || offset > len)
+ throw new ArgumentOutOfRangeException ("offset exceeds the size of buffer");
+
+ if (count < 0 || offset > len - count)
+ throw new ArgumentOutOfRangeException ("offset+size exceeds the size of buffer");
+
+ HttpStreamAsyncResult ares = new HttpStreamAsyncResult ();
+ ares.Callback = cback;
+ ares.State = state;
+ if (no_more_data) {
+ ares.Complete ();
+ return ares;
+ }
+ int nread = decoder.Read (buffer, offset, count);
+ offset += nread;
+ count -= nread;
+ if (count == 0) {
+ // got all we wanted, no need to bother the decoder yet
+ ares.Count = nread;
+ ares.Complete ();
+ return ares;
+ }
+ if (!decoder.WantMore) {
+ no_more_data = nread == 0;
+ ares.Count = nread;
+ ares.Complete ();
+ return ares;
+ }
+ ares.Buffer = new byte [8192];
+ ares.Offset = 0;
+ ares.Count = 8192;
+ ReadBufferState rb = new ReadBufferState (buffer, offset, count, ares);
+ rb.InitialCount += nread;
+ base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
+ return ares;
+ }
+
+ void OnRead (IAsyncResult base_ares)
+ {
+ ReadBufferState rb = (ReadBufferState) base_ares.AsyncState;
+ HttpStreamAsyncResult ares = rb.Ares;
+ try {
+ int nread = base.EndRead (base_ares);
+ decoder.Write (ares.Buffer, ares.Offset, nread);
+ nread = decoder.Read (rb.Buffer, rb.Offset, rb.Count);
+ rb.Offset += nread;
+ rb.Count -= nread;
+ if (rb.Count == 0 || !decoder.WantMore || nread == 0) {
+ no_more_data = !decoder.WantMore && nread == 0;
+ ares.Count = rb.InitialCount - rb.Count;
+ ares.Complete ();
+ return;
+ }
+ ares.Offset = 0;
+ ares.Count = Math.Min (8192, decoder.ChunkLeft + 6);
+ base.BeginRead (ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
+ } catch (Exception e) {
+ context.Connection.SendError (e.Message, 400);
+ ares.Complete (e);
+ }
+ }
+
+ public override int EndRead (IAsyncResult ares)
+ {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ HttpStreamAsyncResult my_ares = ares as HttpStreamAsyncResult;
+ if (ares == null)
+ throw new ArgumentException ("Invalid IAsyncResult", "ares");
+
+ if (!ares.IsCompleted)
+ ares.AsyncWaitHandle.WaitOne ();
+
+ if (my_ares.Error != null)
+ throw new HttpListenerException (400, "I/O operation aborted.");
+
+ return my_ares.Count;
+ }
+
+ public override void Close ()
+ {
+ if (!disposed) {
+ disposed = true;
+ base.Close ();
+ }
+ }
+ }
+}
+#endif
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/EndPointListener.cs b/Tomboy/Addins/WebSyncService/MonoHttp/EndPointListener.cs
new file mode 100644
index 0000000..1fc1edd
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/EndPointListener.cs
@@ -0,0 +1,326 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.EndPointListener
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if !NET_2_0
+
+using System.IO;
+using System.Net.Sockets;
+using System.Collections;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+//using Mono.Security.Authenticode;
+
+using System; using System.Net; namespace MonoHttp {
+
+ sealed class EndPointListener : IHttpListenerContextBinder
+ {
+ IPEndPoint endpoint;
+ Socket sock;
+ Hashtable prefixes; // Dictionary <ListenerPrefix, HttpListener>
+ ArrayList unhandled; // List<ListenerPrefix> unhandled; host = '*'
+ ArrayList all; // List<ListenerPrefix> all; host = '+'
+
+ AsymmetricAlgorithm key;
+ bool secure;
+
+ public EndPointListener (IPAddress addr, int port, bool secure)
+ {
+#if !EMBEDDED_IN_1_0
+ if (secure) {
+ this.secure = secure;
+ LoadCertificateAndKey (addr, port);
+ }
+#endif
+
+ endpoint = new IPEndPoint (addr, port);
+ sock = new Socket (addr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+ sock.Bind (endpoint);
+ sock.Listen (500);
+ sock.BeginAccept (OnAccept, this);
+ prefixes = new Hashtable ();
+ }
+#if !EMBEDDED_IN_1_0
+ void LoadCertificateAndKey (IPAddress addr, int port)
+ {
+ // Actually load the certificate
+ try {
+ string dirname = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
+ string path = Path.Combine (dirname, ".mono");
+ path = Path.Combine (path, "httplistener");
+ string cert_file = Path.Combine (path, String.Format ("{0}.cer", port));
+ string pvk_file = Path.Combine (path, String.Format ("{0}.pvk", port));
+ cert = new X509Certificate2 (cert_file);
+ key = PrivateKey.CreateFromFile (pvk_file).RSA;
+ } catch {
+ // ignore errors
+ }
+ }
+#endif
+ static void OnAccept (IAsyncResult ares)
+ {
+ EndPointListener epl = (EndPointListener) ares.AsyncState;
+ Socket accepted = null;
+ try {
+ accepted = epl.sock.EndAccept (ares);
+ } catch {
+ // Anything to do here?
+ } finally {
+ try {
+ epl.sock.BeginAccept (OnAccept, epl);
+ } catch {
+ if (accepted != null) {
+ try {
+ accepted.Close ();
+ } catch {}
+ accepted = null;
+ }
+ }
+ }
+
+ if (accepted == null)
+ return;
+
+ if (epl.secure && (epl.key == null)) {
+ accepted.Close ();
+ return;
+ }
+ HttpConnection conn = new HttpConnection (accepted, epl);
+ conn.BeginReadRequest ();
+ }
+
+ public bool BindContext (HttpListenerContext context)
+ {
+ HttpListenerRequest req = context.Request;
+ ListenerPrefix prefix;
+ HttpListener listener = SearchListener (req.UserHostName, req.RawUrl, out prefix);
+ if (listener == null)
+ return false;
+
+ context.Listener = listener;
+ context.Connection.Prefix = prefix;
+ listener.RegisterContext (context);
+ return true;
+ }
+
+ public void UnbindContext (HttpListenerContext context)
+ {
+ if (context == null || context.Request == null)
+ return;
+
+ HttpListenerRequest req = context.Request;
+ ListenerPrefix prefix;
+ HttpListener listener = SearchListener (req.UserHostName, req.RawUrl, out prefix);
+ if (listener != null)
+ listener.UnregisterContext (context);
+ }
+
+ HttpListener SearchListener (string host, string raw_url, out ListenerPrefix prefix)
+ {
+ prefix = null;
+ if (raw_url == null)
+ return null;
+
+ //TODO: We should use a ReaderWriterLock between this and the add/remove operations.
+ if (host != null) {
+ int colon = host.IndexOf (':');
+ if (colon >= 0)
+ host = host.Substring (0, colon);
+ }
+
+ string path;
+ Uri raw_uri;
+ #if NET_2_0
+if (MonoHttp.Utility.MaybeUri (raw_url) && Uri.TryCreate (raw_url, UriKind.Absolute, out raw_uri))
+#else
+try { raw_uri = new Uri (raw_url); } catch { raw_uri = null; } if (raw_uri != null)
+#endif
+
+ path = raw_uri.PathAndQuery;
+ else
+ path = HttpUtility.UrlDecode (raw_url);
+
+ string path_slash = path [path.Length - 1] == '/' ? path : path + "/";
+
+ HttpListener best_match = null;
+ int best_length = -1;
+
+ lock (prefixes) {
+ if (host != null && host != "") {
+ foreach (ListenerPrefix p in prefixes.Keys) {
+ string ppath = p.Path;
+ if (ppath.Length < best_length)
+ continue;
+
+ if (p.Host == host && (path.StartsWith (ppath) || path_slash.StartsWith (ppath))) {
+ best_length = ppath.Length;
+ best_match = (HttpListener) prefixes [p];
+ prefix = p;
+ }
+ }
+ if (best_length != -1)
+ return best_match;
+ }
+
+ best_match = MatchFromList (host, path, unhandled, out prefix);
+ if (best_match != null)
+ return best_match;
+
+ best_match = MatchFromList (host, path, all, out prefix);
+ if (best_match != null)
+ return best_match;
+ }
+ return null;
+ }
+
+ HttpListener MatchFromList (string host, string path, ArrayList list, out ListenerPrefix prefix)
+ {
+ prefix = null;
+ if (list == null)
+ return null;
+
+ HttpListener best_match = null;
+ int best_length = -1;
+
+ foreach (ListenerPrefix p in list) {
+ string ppath = p.Path;
+ if (ppath.Length < best_length)
+ continue;
+
+ if (path.StartsWith (ppath)) {
+ best_length = ppath.Length;
+ best_match = p.Listener;
+ prefix = p;
+ }
+ }
+
+ return best_match;
+ }
+
+ void AddSpecial (ArrayList coll, ListenerPrefix prefix)
+ {
+ if (coll == null)
+ return;
+
+ foreach (ListenerPrefix p in coll) {
+ if (p.Path == prefix.Path) //TODO: code
+ throw new HttpListenerException (400, "Prefix already in use.");
+ }
+
+ coll.Add (prefix);
+ }
+
+ void RemoveSpecial (ArrayList coll, ListenerPrefix prefix)
+ {
+ if (coll == null)
+ return;
+
+ int c = coll.Count;
+ for (int i = 0; i < c; i++) {
+ ListenerPrefix p = (ListenerPrefix) coll [i];
+ if (p.Path == prefix.Path) {
+ coll.RemoveAt (i);
+ CheckIfRemove ();
+ return;
+ }
+ }
+ }
+
+ void CheckIfRemove ()
+ {
+ if (prefixes.Count > 0)
+ return;
+
+ if (unhandled != null && unhandled.Count > 0)
+ return;
+
+ if (all != null && all.Count > 0)
+ return;
+
+ EndPointManager.RemoveEndPoint (this, endpoint);
+ }
+
+ public void Close ()
+ {
+ sock.Close ();
+ }
+
+ public void AddPrefix (ListenerPrefix prefix, HttpListener listener)
+ {
+ lock (prefixes) {
+ if (prefix.Host == "*") {
+ if (unhandled == null)
+ unhandled = new ArrayList ();
+
+ prefix.Listener = listener;
+ AddSpecial (unhandled, prefix);
+ return;
+ }
+
+ if (prefix.Host == "+") {
+ if (all == null)
+ all = new ArrayList ();
+ prefix.Listener = listener;
+ AddSpecial (all, prefix);
+ return;
+ }
+
+ if (prefixes.ContainsKey (prefix)) {
+ HttpListener other = (HttpListener) prefixes [prefix];
+ if (other != listener) // TODO: code.
+ throw new HttpListenerException (400, "There's another listener for " + prefix);
+ return;
+ }
+
+ prefixes [prefix] = listener;
+ }
+ }
+
+ public void RemovePrefix (ListenerPrefix prefix, HttpListener listener)
+ {
+ lock (prefixes) {
+ if (prefix.Host == "*") {
+ RemoveSpecial (unhandled, prefix);
+ return;
+ }
+
+ if (prefix.Host == "+") {
+ RemoveSpecial (all, prefix);
+ return;
+ }
+
+ if (prefixes.ContainsKey (prefix)) {
+ prefixes.Remove (prefix);
+ }
+ }
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/EndPointManager.cs b/Tomboy/Addins/WebSyncService/MonoHttp/EndPointManager.cs
new file mode 100644
index 0000000..7ca0117
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/EndPointManager.cs
@@ -0,0 +1,147 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.EndPointManager
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo ximian com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if !NET_2_0
+
+using System.Collections;
+using System.Collections.Generic;
+using System; using System.Net; namespace MonoHttp {
+ sealed class EndPointManager
+ {
+ // Dictionary<IPAddress, Dictionary<int, EndPointListener>>
+ static Hashtable ip_to_endpoints = new Hashtable ();
+
+ private EndPointManager ()
+ {
+ }
+
+ public static void AddListener (HttpListener listener)
+ {
+ ArrayList added = new ArrayList ();
+ try {
+ lock (ip_to_endpoints) {
+ foreach (string prefix in listener.Prefixes) {
+ AddPrefixInternal (prefix, listener);
+ added.Add (prefix);
+ }
+ }
+ } catch {
+ foreach (string prefix in added) {
+ RemovePrefixInternal (prefix, listener);
+ }
+ throw;
+ }
+ }
+
+ public static void AddPrefix (string prefix, HttpListener listener)
+ {
+ lock (ip_to_endpoints) {
+ AddPrefixInternal (prefix, listener);
+ }
+ }
+
+ static void AddPrefixInternal (string p, HttpListener listener)
+ {
+ ListenerPrefix lp = new ListenerPrefix (p);
+ if (lp.Path.IndexOf ('%') != -1)
+ throw new HttpListenerException (400, "Invalid path.");
+
+ if (lp.Path.IndexOf ("//") != -1) // TODO: Code?
+ throw new HttpListenerException (400, "Invalid path.");
+
+ // Always listens on all the interfaces, no matter the host name/ip used.
+ EndPointListener epl = GetEPListener (IPAddress.Any, lp.Port, listener, lp.Secure);
+ epl.AddPrefix (lp, listener);
+ }
+
+ static EndPointListener GetEPListener (IPAddress addr, int port, HttpListener listener, bool secure)
+ {
+ Hashtable p = null; // Dictionary<int, EndPointListener>
+ if (ip_to_endpoints.ContainsKey (addr)) {
+ p = (Hashtable) ip_to_endpoints [addr];
+ } else {
+ p = new Hashtable ();
+ ip_to_endpoints [addr] = p;
+ }
+
+ EndPointListener epl = null;
+ if (p.ContainsKey (port)) {
+ epl = (EndPointListener) p [port];
+ } else {
+ epl = new EndPointListener (addr, port, secure);
+ p [port] = epl;
+ }
+
+ return epl;
+ }
+
+ public static void RemoveEndPoint (EndPointListener epl, IPEndPoint ep)
+ {
+ lock (ip_to_endpoints) {
+ // Dictionary<int, EndPointListener> p
+ Hashtable p = null;
+ p = (Hashtable) ip_to_endpoints [ep.Address];
+ p.Remove (ep.Port);
+ epl.Close ();
+ }
+ }
+
+ public static void RemoveListener (HttpListener listener)
+ {
+ lock (ip_to_endpoints) {
+ foreach (string prefix in listener.Prefixes) {
+ RemovePrefixInternal (prefix, listener);
+ }
+ }
+ }
+
+ public static void RemovePrefix (string prefix, HttpListener listener)
+ {
+ lock (ip_to_endpoints) {
+ RemovePrefixInternal (prefix, listener);
+ }
+ }
+
+ static void RemovePrefixInternal (string prefix, HttpListener listener)
+ {
+ ListenerPrefix lp = new ListenerPrefix (prefix);
+ if (lp.Path.IndexOf ('%') != -1)
+ return;
+
+ if (lp.Path.IndexOf ("//") != -1)
+ return;
+
+ EndPointListener epl = GetEPListener (IPAddress.Any, lp.Port, listener, lp.Secure);
+ epl.RemovePrefix (lp, listener);
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpConnection.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpConnection.cs
new file mode 100644
index 0000000..cd0121c
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpConnection.cs
@@ -0,0 +1,353 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpConnection
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if EMBEDDED_IN_1_0
+
+using System.IO;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Text;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+#if !EMBEDDED_IN_1_0
+using Mono.Security.Protocol.Tls;
+#endif
+
+using System; using System.Net; namespace MonoHttp {
+
+ interface IHttpListenerContextBinder {
+ bool BindContext (HttpListenerContext context);
+ void UnbindContext (HttpListenerContext context);
+ }
+
+ sealed class HttpConnection
+ {
+ const int BufferSize = 8192;
+ Socket sock;
+ Stream stream;
+ IHttpListenerContextBinder epl;
+ MemoryStream ms;
+ byte [] buffer;
+ HttpListenerContext context;
+ StringBuilder current_line;
+ ListenerPrefix prefix;
+ RequestStream i_stream;
+ ResponseStream o_stream;
+ bool chunked;
+ int chunked_uses;
+ bool context_bound;
+
+#if EMBEDDED_IN_1_0
+ public HttpConnection (Socket sock, IHttpListenerContextBinder epl)
+ {
+ this.sock = sock;
+ this.epl = epl;
+ stream = new NetworkStream (sock, false);
+ Init ();
+ }
+#else
+ public HttpConnection (Socket sock, IHttpListenerContextBinder epl, bool secure, X509Certificate2 cert, AsymmetricAlgorithm key)
+ {
+ this.sock = sock;
+ this.epl = epl;
+ this.secure = secure;
+ this.key = key;
+ if (secure == false) {
+ stream = new NetworkStream (sock, false);
+ } else {
+ SslServerStream ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, false);
+ ssl_stream.PrivateKeyCertSelectionDelegate += OnPVKSelection;
+ stream = ssl_stream;
+
+ }
+ Init ();
+ }
+#endif
+
+ void Init ()
+ {
+ context_bound = false;
+ i_stream = null;
+ o_stream = null;
+ prefix = null;
+ chunked = false;
+ ms = new MemoryStream ();
+ position = 0;
+ input_state = InputState.RequestLine;
+ line_state = LineState.None;
+ context = new HttpListenerContext (this);
+ }
+
+ public int ChunkedUses {
+ get { return chunked_uses; }
+ }
+
+ public IPEndPoint LocalEndPoint {
+ get { return (IPEndPoint) sock.LocalEndPoint; }
+ }
+
+ public IPEndPoint RemoteEndPoint {
+ get { return (IPEndPoint) sock.RemoteEndPoint; }
+ }
+
+ public bool IsSecure {
+ get { return false; }
+ }
+
+ public ListenerPrefix Prefix {
+ get { return prefix; }
+ set { prefix = value; }
+ }
+
+ public void BeginReadRequest ()
+ {
+ if (buffer == null)
+ buffer = new byte [BufferSize];
+ try {
+ stream.BeginRead (buffer, 0, BufferSize, OnRead, this);
+ } catch {
+ sock.Close (); // stream disposed
+ }
+ }
+
+ public RequestStream GetRequestStream (bool chunked, long contentlength)
+ {
+ if (i_stream == null) {
+ byte [] buffer = ms.GetBuffer ();
+ int length = (int) ms.Length;
+ ms = null;
+ if (chunked) {
+ this.chunked = true;
+ context.Response.SendChunked = true;
+ i_stream = new ChunkedInputStream (context, stream, buffer, position, length - position);
+ } else {
+ i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
+ }
+ }
+ return i_stream;
+ }
+
+ public ResponseStream GetResponseStream ()
+ {
+ // TODO: can we get this stream before reading the input?
+ if (o_stream == null) {
+ bool ign = false;// ? true : listener.IgnoreWriteExceptions;
+ o_stream = new ResponseStream (stream, context.Response, ign);
+ }
+ return o_stream;
+ }
+
+ void OnRead (IAsyncResult ares)
+ {
+ // TODO: set a limit on ms length.
+ HttpConnection cnc = (HttpConnection) ares.AsyncState;
+ int nread = -1;
+ try {
+ nread = stream.EndRead (ares);
+ ms.Write (buffer, 0, nread);
+ } catch (Exception) {
+ //Console.WriteLine (e);
+ if (ms.Length > 0)
+ SendError ();
+ sock.Close ();
+ return;
+ }
+
+ if (nread == 0) {
+ //if (ms.Length > 0)
+ // SendError (); // Why bother?
+ sock.Close ();
+ return;
+ }
+
+ if (ProcessInput (ms)) {
+ if (!context.HaveError)
+ context.Request.FinishInitialization ();
+
+ if (context.HaveError) {
+ SendError ();
+ Close ();
+ return;
+ }
+
+ if (!epl.BindContext (context)) {
+ SendError ("Invalid host", 400);
+ Close ();
+ }
+ context_bound = true;
+ return;
+ }
+ stream.BeginRead (buffer, 0, BufferSize, OnRead, cnc);
+ }
+
+ enum InputState {
+ RequestLine,
+ Headers
+ }
+
+ enum LineState {
+ None,
+ CR,
+ LF
+ }
+
+ InputState input_state = InputState.RequestLine;
+ LineState line_state = LineState.None;
+ int position;
+
+ // true -> done processing
+ // false -> need more input
+ bool ProcessInput (MemoryStream ms)
+ {
+ byte [] buffer = ms.GetBuffer ();
+ int len = (int) ms.Length;
+ int used = 0;
+ string line;
+ while ((line = ReadLine (buffer, position, len - position, ref used)) != null) {
+ position += used;
+ if (line == "") {
+ if (input_state == InputState.RequestLine)
+ continue;
+ current_line = null;
+ ms = null;
+ return true;
+ }
+
+ if (input_state == InputState.RequestLine) {
+ context.Request.SetRequestLine (line);
+ input_state = InputState.Headers;
+ } else {
+ context.Request.AddHeader (line);
+ }
+
+ if (context.HaveError)
+ return true;
+
+ if (position >= len)
+ break;
+ }
+
+ if (used == len) {
+ ms.SetLength (0);
+ position = 0;
+ }
+ return false;
+ }
+
+ string ReadLine (byte [] buffer, int offset, int len, ref int used)
+ {
+ if (current_line == null)
+ current_line = new StringBuilder ();
+ int last = offset + len;
+ used = 0;
+ for (int i = offset; i < last && line_state != LineState.LF; i++) {
+ used++;
+ byte b = buffer [i];
+ if (b == 13) {
+ line_state = LineState.CR;
+ } else if (b == 10) {
+ line_state = LineState.LF;
+ } else {
+ current_line.Append ((char) b);
+ }
+ }
+
+ string result = null;
+ if (line_state == LineState.LF) {
+ line_state = LineState.None;
+ result = current_line.ToString ();
+ current_line.Length = 0;
+ }
+
+ return result;
+ }
+
+ public void SendError (string msg, int status)
+ {
+ HttpListenerResponse response = context.Response;
+ response.StatusCode = status;
+ response.ContentType = "text/html";
+ string description = HttpListenerResponse.GetStatusDescription (status);
+ string str;
+ if (msg != null)
+ str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
+ else
+ str = String.Format ("<h1>{0}</h1>", description);
+
+ byte [] error = context.Response.ContentEncoding.GetBytes (str);
+ response.Close (error, false);
+ }
+
+ public void SendError ()
+ {
+ SendError (context.ErrorMessage, context.ErrorStatus);
+ }
+
+ public void Close ()
+ {
+ if (sock != null) {
+ Stream st = GetResponseStream ();
+ st.Close ();
+ o_stream = null;
+ }
+
+ if (sock != null) {
+ if (chunked && context.Response.ForceCloseChunked == false) {
+ // Don't close. Keep working.
+ chunked_uses++;
+ Init ();
+ BeginReadRequest ();
+ return;
+ }
+
+ if (context.Response.Headers ["connection"] == "close") {
+ Socket s = sock;
+ sock = null;
+ try {
+ s.Shutdown (SocketShutdown.Both);
+ } catch {
+ } finally {
+ s.Close ();
+ }
+ } else {
+ Init ();
+ BeginReadRequest ();
+ return;
+ }
+
+ if (context_bound)
+ epl.UnbindContext (context);
+ }
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpListener.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListener.cs
new file mode 100644
index 0000000..f1ff1b3
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListener.cs
@@ -0,0 +1,319 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpListener
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if !NET_2_0
+
+using System.Collections;
+using System.Threading;
+//TODO: logging
+using System; using System.Net; namespace MonoHttp {
+ internal class HttpListener : IDisposable {
+ AuthenticationSchemes auth_schemes;
+ HttpListenerPrefixCollection prefixes;
+ AuthenticationSchemeSelector auth_selector;
+ string realm;
+ bool ignore_write_exceptions;
+ bool unsafe_ntlm_auth;
+ bool listening;
+ bool disposed;
+
+ Hashtable registry; // Dictionary<HttpListenerContext,HttpListenerContext>
+ ArrayList ctx_queue; // List<HttpListenerContext> ctx_queue;
+ ArrayList wait_queue; // List<ListenerAsyncResult> wait_queue;
+
+ public HttpListener ()
+ {
+ prefixes = new HttpListenerPrefixCollection (this);
+ registry = new Hashtable ();
+ ctx_queue = new ArrayList ();
+ wait_queue = new ArrayList ();
+ auth_schemes = AuthenticationSchemes.Anonymous;
+ }
+
+ // TODO: Digest, NTLM and Negotiate require ControlPrincipal
+ public AuthenticationSchemes AuthenticationSchemes {
+ get { return auth_schemes; }
+ set {
+ CheckDisposed ();
+ auth_schemes = value;
+ }
+ }
+
+ //TODO: when is this called?
+ public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate {
+ get { return auth_selector; }
+ set {
+ CheckDisposed ();
+ auth_selector = value;
+ }
+ }
+
+ public bool IgnoreWriteExceptions {
+ get { return ignore_write_exceptions; }
+ set {
+ CheckDisposed ();
+ ignore_write_exceptions = value;
+ }
+ }
+
+ public bool IsListening {
+ get { return listening; }
+ }
+
+ public static bool IsSupported {
+ get { return true; }
+ }
+
+ public HttpListenerPrefixCollection Prefixes {
+ get {
+ CheckDisposed ();
+ return prefixes;
+ }
+ }
+
+ // TODO: use this
+ public string Realm {
+ get { return realm; }
+ set {
+ CheckDisposed ();
+ realm = value;
+ }
+ }
+
+ //[MonoTODO ("Support for NTLM needs some loving.")]
+ public bool UnsafeConnectionNtlmAuthentication {
+ get { return unsafe_ntlm_auth; }
+ set {
+ CheckDisposed ();
+ unsafe_ntlm_auth = value;
+ }
+ }
+
+ public void Abort ()
+ {
+ if (disposed)
+ return;
+
+ if (!listening) {
+ return;
+ }
+
+ Close (true);
+ }
+
+ public void Close ()
+ {
+ if (disposed)
+ return;
+
+ if (!listening) {
+ disposed = true;
+ return;
+ }
+
+ Close (false);
+ disposed = true;
+ }
+
+ void Close (bool force)
+ {
+ CheckDisposed ();
+ EndPointManager.RemoveListener (this);
+ Cleanup (force);
+ }
+
+ void Cleanup (bool close_existing)
+ {
+ lock (registry) {
+ if (close_existing) {
+ foreach (HttpListenerContext context in registry.Keys) {
+ context.Connection.Close ();
+ }
+ registry.Clear (); // Just in case.
+ }
+
+ lock (ctx_queue) {
+ foreach (HttpListenerContext context in ctx_queue)
+ context.Connection.Close ();
+
+ ctx_queue.Clear ();
+ }
+
+ lock (wait_queue) {
+ foreach (ListenerAsyncResult ares in wait_queue) {
+ ares.Complete ("Listener was closed.");
+ }
+ wait_queue.Clear ();
+ }
+ }
+ }
+
+ public IAsyncResult BeginGetContext (AsyncCallback callback, Object state)
+ {
+ CheckDisposed ();
+ if (!listening)
+ throw new InvalidOperationException ("Please, call Start before using this method.");
+
+ ListenerAsyncResult ares = new ListenerAsyncResult (callback, state);
+
+ // lock wait_queue early to avoid race conditions
+ lock (wait_queue) {
+ lock (ctx_queue) {
+ HttpListenerContext ctx = GetContextFromQueue ();
+ if (ctx != null) {
+ ares.Complete (ctx, true);
+ return ares;
+ }
+ }
+
+ wait_queue.Add (ares);
+ }
+
+ return ares;
+ }
+
+ public HttpListenerContext EndGetContext (IAsyncResult asyncResult)
+ {
+ CheckDisposed ();
+ if (asyncResult == null)
+ throw new ArgumentNullException ("asyncResult");
+
+ ListenerAsyncResult ares = asyncResult as ListenerAsyncResult;
+ if (ares == null)
+ throw new ArgumentException ("Wrong IAsyncResult.", "asyncResult");
+
+ if (!ares.IsCompleted)
+ ares.AsyncWaitHandle.WaitOne ();
+
+ lock (wait_queue) {
+ int idx = wait_queue.IndexOf (ares);
+ if (idx >= 0)
+ wait_queue.RemoveAt (idx);
+ }
+
+ HttpListenerContext context = ares.GetContext ();
+ if (auth_schemes != AuthenticationSchemes.Anonymous) {
+ context.ParseAuthentication ();
+ }
+ return context; // This will throw on error.
+ }
+
+ public HttpListenerContext GetContext ()
+ {
+ // The prefixes are not checked when using the async interface!?
+ if (prefixes.Count == 0)
+ throw new InvalidOperationException ("Please, call AddPrefix before using this method.");
+
+ IAsyncResult ares = BeginGetContext (null, null);
+ return EndGetContext (ares);
+ }
+
+ public void Start ()
+ {
+ CheckDisposed ();
+ if (listening)
+ return;
+
+ EndPointManager.AddListener (this);
+ listening = true;
+ }
+
+ public void Stop ()
+ {
+ CheckDisposed ();
+ listening = false;
+ Close (false);
+ }
+
+ void IDisposable.Dispose ()
+ {
+ if (disposed)
+ return;
+
+ Close (true); //TODO: Should we force here or not?
+ disposed = true;
+ }
+
+ internal void CheckDisposed ()
+ {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+ }
+
+ // Must be called with a lock on ctx_queue
+ HttpListenerContext GetContextFromQueue ()
+ {
+ if (ctx_queue.Count == 0)
+ return null;
+
+ HttpListenerContext context = (HttpListenerContext) ctx_queue [0];
+ ctx_queue.RemoveAt (0);
+ return context;
+ }
+
+ internal void RegisterContext (HttpListenerContext context)
+ {
+ try {
+ Monitor.Enter (registry);
+ registry [context] = context;
+ Monitor.Enter (wait_queue);
+ Monitor.Enter (ctx_queue);
+ if (wait_queue.Count == 0) {
+ ctx_queue.Add (context);
+ } else {
+ ListenerAsyncResult ares = (ListenerAsyncResult) wait_queue [0];
+ wait_queue.RemoveAt (0);
+ ares.Complete (context);
+ }
+ } finally {
+ Monitor.Exit (ctx_queue);
+ Monitor.Exit (wait_queue);
+ Monitor.Exit (registry);
+ }
+ }
+
+ internal void UnregisterContext (HttpListenerContext context)
+ {
+ try {
+ Monitor.Enter (registry);
+ Monitor.Enter (ctx_queue);
+ int idx = ctx_queue.IndexOf (context);
+ if (idx >= 0)
+ ctx_queue.RemoveAt (idx);
+ registry.Remove (context);
+ } finally {
+ Monitor.Exit (ctx_queue);
+ Monitor.Exit (registry);
+ }
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerBasicIdentity.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerBasicIdentity.cs
new file mode 100644
index 0000000..e785bac
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerBasicIdentity.cs
@@ -0,0 +1,47 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpListenerBasicIdentity
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if !NET_2_0
+using System.Security.Principal;
+using System; using System.Net; namespace MonoHttp {
+ internal class HttpListenerBasicIdentity : GenericIdentity {
+ string password;
+
+ public HttpListenerBasicIdentity (string username, string password) : base (username, "Basic")
+ {
+ this.password = password;
+ }
+
+ public virtual string Password {
+ get { return password; }
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerContext.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerContext.cs
new file mode 100644
index 0000000..b002a6a
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerContext.cs
@@ -0,0 +1,139 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpListenerContext
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if EMBEDDED_IN_1_0
+
+using System.Collections.Specialized;
+using System.IO;
+using System.Security.Principal;
+using System.Text;
+using System; using System.Net; namespace MonoHttp {
+ internal class HttpListenerContext {
+ HttpListenerRequest request;
+ HttpListenerResponse response;
+ IPrincipal user;
+ HttpConnection cnc;
+ string error;
+ int err_status = 400;
+ internal HttpListener Listener;
+
+ internal HttpListenerContext (HttpConnection cnc)
+ {
+ this.cnc = cnc;
+ request = new HttpListenerRequest (this);
+ response = new HttpListenerResponse (this);
+ }
+
+ internal int ErrorStatus {
+ get { return err_status; }
+ set { err_status = value; }
+ }
+
+ internal string ErrorMessage {
+ get { return error; }
+ set { error = value; }
+ }
+
+ internal bool HaveError {
+ get { return (error != null); }
+ }
+
+ internal HttpConnection Connection {
+ get { return cnc; }
+ }
+
+ public HttpListenerRequest Request {
+ get { return request; }
+ }
+
+ public HttpListenerResponse Response {
+ get { return response; }
+ }
+
+ public IPrincipal User {
+ get { return user; }
+ }
+
+ internal void ParseAuthentication () {
+ // TODO: Handle NTLM/Digest modes
+ string header = request.Headers ["Authorization"];
+
+ if (header == null || header.Length < 2)
+ return;
+
+ string [] authenticationData = header.Substring (header.IndexOf (':') + 1).Split (new char [] {' '});
+
+ if (string.Compare (authenticationData [0], "basic", true) == 0) {
+ user = ParseBasicAuthentication (authenticationData [1]);
+ }
+ }
+
+ internal IPrincipal ParseBasicAuthentication (string authData) {
+ try {
+ // Basic AUTH Data is a formatted Base64 String
+ //string domain = null;
+ string user = null;
+ string password = null;
+ int pos = -1;
+ string authString = System.Text.Encoding.Default.GetString (Convert.FromBase64String (authData));
+
+ // The format is DOMAIN\username:password
+ // Domain is optional
+
+ pos = authString.IndexOf (':');
+
+ // parse the password off the end
+ password = authString.Substring (pos+1);
+
+ // discard the password
+ authString = authString.Substring (0, pos);
+
+ // check if there is a domain
+ pos = authString.IndexOf ('\\');
+
+ if (pos > 0) {
+ //domain = authString.Substring (0, pos);
+ user = authString.Substring (pos);
+ } else {
+ user = authString;
+ }
+
+ HttpListenerBasicIdentity identity = new HttpListenerBasicIdentity (user, password);
+ // TODO: What are the roles MS sets
+ return new GenericPrincipal (identity, new string [0]);
+ } catch (Exception) {
+ // Invalid auth data is swallowed silently
+ return null;
+ }
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerException.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerException.cs
new file mode 100644
index 0000000..036058e
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerException.cs
@@ -0,0 +1,59 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpListenerException
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if !NET_2_0
+using System.ComponentModel;
+using System.Runtime.Serialization;
+using System; using System.Net; namespace MonoHttp {
+ [Serializable]
+ internal class HttpListenerException : Win32Exception
+ {
+ public HttpListenerException ()
+ {
+ }
+
+ public HttpListenerException (int errorCode) : base (errorCode)
+ {
+ }
+
+ public HttpListenerException (int errorCode, string message) : base (errorCode, message)
+ {
+ }
+
+ protected HttpListenerException (SerializationInfo serializationInfo, StreamingContext streamingContext) : base (serializationInfo, streamingContext)
+ {
+ }
+
+ public override int ErrorCode {
+ get { return base.ErrorCode; }
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerPrefixCollection.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerPrefixCollection.cs
new file mode 100644
index 0000000..bbcc25b
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerPrefixCollection.cs
@@ -0,0 +1,130 @@
+//#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpListenerPrefixCollection.cs
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if !NET_2_0
+
+using System.Collections;
+using System.Collections.Generic;
+using System; using System.Net; namespace MonoHttp {
+#if EMBEDDED_IN_1_0
+ internal class HttpListenerPrefixCollection : IEnumerable, ICollection {
+ ArrayList prefixes;
+
+#else
+ internal class HttpListenerPrefixCollection : ICollection<string>, IEnumerable<string>, IEnumerable {
+ List<string> prefixes = new List<string> ();
+#endif
+ HttpListener listener;
+
+ internal HttpListenerPrefixCollection (HttpListener listener)
+ {
+ this.listener = listener;
+ }
+
+ public int Count {
+ get { return prefixes.Count; }
+ }
+
+ public bool IsReadOnly {
+ get { return false; }
+ }
+
+ public bool IsSynchronized {
+ get { return false; }
+ }
+
+ public void Add (string uriPrefix)
+ {
+ listener.CheckDisposed ();
+ ListenerPrefix.CheckUri (uriPrefix);
+ if (prefixes.Contains (uriPrefix))
+ return;
+
+ prefixes.Add (uriPrefix);
+ if (listener.IsListening)
+ EndPointManager.AddPrefix (uriPrefix, listener);
+ }
+
+ public void Clear ()
+ {
+ listener.CheckDisposed ();
+ if (listener.IsListening)
+ EndPointManager.RemoveListener (listener);
+ prefixes.Clear ();
+ }
+
+ public bool Contains (string uriPrefix)
+ {
+ listener.CheckDisposed ();
+ return prefixes.Contains (uriPrefix);
+ }
+
+ public void CopyTo (string [] array, int offset)
+ {
+ listener.CheckDisposed ();
+ prefixes.CopyTo (array, offset);
+ }
+
+ public void CopyTo (Array array, int offset)
+ {
+ listener.CheckDisposed ();
+ ((ICollection) prefixes).CopyTo (array, offset);
+ }
+
+#if !EMBEDDED_IN_1_0
+ public IEnumerator<string> GetEnumerator ()
+ {
+ return prefixes.GetEnumerator ();
+ }
+#else
+ object ICollection.SyncRoot { get { return this; } }
+#endif
+
+
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return prefixes.GetEnumerator ();
+ }
+
+ public bool Remove (string uriPrefix)
+ {
+ listener.CheckDisposed ();
+ if (uriPrefix == null)
+ throw new ArgumentNullException ("uriPrefix");
+
+ bool result = prefixes.Contains (uriPrefix); if (result) prefixes.Remove (uriPrefix);;
+ if (result && listener.IsListening)
+ EndPointManager.RemovePrefix (uriPrefix, listener);
+
+ return result;
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerRequest.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerRequest.cs
new file mode 100644
index 0000000..143c392
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerRequest.cs
@@ -0,0 +1,450 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpListenerRequest
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if EMBEDDED_IN_1_0
+
+using System.Collections;
+using System.Collections.Specialized;
+using System.Globalization;
+using System.IO;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System; using System.Net; namespace MonoHttp {
+ internal class HttpListenerRequest
+ {
+ string [] accept_types;
+// int client_cert_error;
+// bool no_get_certificate;
+ Encoding content_encoding;
+ long content_length;
+ bool cl_set;
+ CookieCollection cookies;
+ WebHeaderCollection headers;
+ string method;
+ Stream input_stream;
+ Version version;
+ NameValueCollection query_string; // check if null is ok, check if read-only, check case-sensitiveness
+ string raw_url;
+ Guid identifier;
+ Uri url;
+ Uri referrer;
+ string [] user_languages;
+ HttpListenerContext context;
+ bool is_chunked;
+ static byte [] _100continue = Encoding.ASCII.GetBytes ("HTTP/1.1 100 Continue\r\n\r\n");
+ static readonly string [] no_body_methods = new string [] {
+ "GET", "HEAD", "DELETE" };
+
+ internal HttpListenerRequest (HttpListenerContext context)
+ {
+ this.context = context;
+ headers = new WebHeaderCollection ();
+ input_stream = Stream.Null;
+ version = HttpVersion.Version10;
+ }
+
+ static char [] separators = new char [] { ' ' };
+
+ internal void SetRequestLine (string req)
+ {
+ string [] parts = req.Split (separators, 3);
+ if (parts.Length != 3) {
+ context.ErrorMessage = "Invalid request line (parts).";
+ return;
+ }
+
+ method = parts [0];
+ foreach (char c in method){
+ int ic = (int) c;
+
+ if ((ic >= 'A' && ic <= 'Z') ||
+ (ic > 32 && c < 127 && c != '(' && c != ')' && c != '<' &&
+ c != '<' && c != '>' && c != '@' && c != ',' && c != ';' &&
+ c != ':' && c != '\\' && c != '"' && c != '/' && c != '[' &&
+ c != ']' && c != '?' && c != '=' && c != '{' && c != '}'))
+ continue;
+
+ context.ErrorMessage = "(Invalid verb)";
+ return;
+ }
+
+ raw_url = parts [1];
+ if (parts [2].Length != 8 || !parts [2].StartsWith ("HTTP/")) {
+ context.ErrorMessage = "Invalid request line (version).";
+ return;
+ }
+
+ try {
+ version = new Version (parts [2].Substring (5));
+ if (version.Major < 1)
+ throw new Exception ();
+ } catch {
+ context.ErrorMessage = "Invalid request line (version).";
+ return;
+ }
+ }
+
+ void CreateQueryString (string query)
+ {
+ query_string = new NameValueCollection ();
+ if (query == null || query.Length == 0)
+ return;
+
+ if (query [0] == '?')
+ query = query.Substring (1);
+ string [] components = query.Split ('&');
+ foreach (string kv in components) {
+ int pos = kv.IndexOf ('=');
+ if (pos == -1) {
+ query_string.Add (null, HttpUtility.UrlDecode (kv));
+ } else {
+ string key = HttpUtility.UrlDecode (kv.Substring (0, pos));
+ string val = HttpUtility.UrlDecode (kv.Substring (pos + 1));
+
+ query_string.Add (key, val);
+ }
+ }
+ }
+
+ internal void FinishInitialization ()
+ {
+ string host = UserHostName;
+ if (version > HttpVersion.Version10 && (host == null || host.Length == 0)) {
+ context.ErrorMessage = "Invalid host name";
+ return;
+ }
+
+ string path;
+ Uri raw_uri;
+ #if NET_2_0
+if (MonoHttp.Utility.MaybeUri (raw_url) && Uri.TryCreate (raw_url, UriKind.Absolute, out raw_uri))
+#else
+try { raw_uri = new Uri (raw_url); } catch { raw_uri = null; } if (url != raw_uri && (raw_uri.Host == null || raw_uri.Host.Length == 0)) raw_uri = null; if (raw_uri != null)
+#endif
+
+ path = raw_uri.PathAndQuery;
+ else
+ path = raw_url;
+
+ if ((host == null || host.Length == 0))
+ host = UserHostAddress;
+
+ if (raw_uri != null)
+ host = raw_uri.Host;
+
+ int colon = host.IndexOf (':');
+ if (colon >= 0)
+ host = host.Substring (0, colon);
+
+ string base_uri = String.Format ("{0}://{1}:{2}",
+ (IsSecureConnection) ? "https" : "http",
+ host,
+ LocalEndPoint.Port);
+
+ #if NET_2_0
+if (!Uri.TryCreate (base_uri + path, UriKind.Absolute, out url)){
+#else
+try { url = new Uri (base_uri + path); } catch {}; if (url != null && (url.Host == null || url.Host.Length == 0)) url = null; if (url == null) {
+#endif
+
+ context.ErrorMessage = "Invalid url: " + base_uri + path;
+ return;
+ }
+
+ CreateQueryString (url.Query);
+
+ string t_encoding = null;
+ if (version >= HttpVersion.Version11) {
+ t_encoding = Headers ["Transfer-Encoding"];
+ // 'identity' is not valid!
+ if (t_encoding != null && t_encoding != "chunked") {
+ context.Connection.SendError (null, 501);
+ return;
+ }
+ }
+
+ is_chunked = (t_encoding == "chunked");
+
+ foreach (string m in no_body_methods)
+ if (string.Compare (method, m, true, System.Globalization.CultureInfo.InvariantCulture) == 0)
+ return;
+
+ if (!is_chunked && !cl_set) {
+ context.Connection.SendError (null, 411);
+ return;
+ }
+
+ if (is_chunked || content_length > 0) {
+ input_stream = context.Connection.GetRequestStream (is_chunked, content_length);
+ }
+
+ if (Headers ["Expect"] == "100-continue") {
+ ResponseStream output = context.Connection.GetResponseStream ();
+ output.InternalWrite (_100continue, 0, _100continue.Length);
+ }
+ }
+
+ internal static string Unquote (String str) {
+ int start = str.IndexOf ('\"');
+ int end = str.LastIndexOf ('\"');
+ if (start >= 0 && end >=0)
+ str = str.Substring (start + 1, end - 1);
+ return str.Trim ();
+ }
+
+ internal void AddHeader (string header)
+ {
+ int colon = header.IndexOf (':');
+ if (colon == -1 || colon == 0) {
+ context.ErrorMessage = "Bad Request";
+ return;
+ }
+
+ string name = header.Substring (0, colon).Trim ();
+ string val = header.Substring (colon + 1).Trim ();
+ string lower = name.ToLower (CultureInfo.InvariantCulture);
+ headers.SetInternal (name, val);
+ switch (lower) {
+ case "accept-language":
+ user_languages = val.Split (','); // yes, only split with a ','
+ break;
+ case "accept":
+ accept_types = val.Split (','); // yes, only split with a ','
+ break;
+ case "content-length":
+ try {
+ //TODO: max. content_length?
+ content_length = Int64.Parse (val.Trim ());
+ if (content_length < 0)
+ context.ErrorMessage = "Invalid Content-Length.";
+ cl_set = true;
+ } catch {
+ context.ErrorMessage = "Invalid Content-Length.";
+ }
+
+ break;
+ case "referer":
+ try {
+ referrer = new Uri (val);
+ } catch {
+ referrer = new Uri ("http://someone.is.screwing.with.the.headers.com/");
+ }
+ break;
+ case "cookie":
+ if (cookies == null)
+ cookies = new CookieCollection();
+
+ string[] cookieStrings = val.Split(new char[] {',', ';'});
+ Cookie current = null;
+ int version = 0;
+ foreach (string cookieString in cookieStrings) {
+ string str = cookieString.Trim ();
+ if (str.Length == 0)
+ continue;
+ if (str.StartsWith ("$Version")) {
+ version = Int32.Parse (Unquote (str.Substring (str.IndexOf ("=") + 1)));
+ } else if (str.StartsWith ("$Path")) {
+ if (current != null)
+ current.Path = str.Substring (str.IndexOf ("=") + 1).Trim ();
+ } else if (str.StartsWith ("$Domain")) {
+ if (current != null)
+ current.Domain = str.Substring (str.IndexOf ("=") + 1).Trim ();
+ } else if (str.StartsWith ("$Port")) {
+ if (current != null)
+ current.Port = str.Substring (str.IndexOf ("=") + 1).Trim ();
+ } else {
+ if (current != null) {
+ cookies.Add (current);
+ }
+ current = new Cookie ();
+ int idx = str.IndexOf ("=");
+ if (idx > 0) {
+ current.Name = str.Substring (0, idx).Trim ();
+ current.Value = str.Substring (idx + 1).Trim ();
+ } else {
+ current.Name = str.Trim ();
+ current.Value = String.Empty;
+ }
+ current.Version = version;
+ }
+ }
+ if (current != null) {
+ cookies.Add (current);
+ }
+ break;
+ }
+ }
+
+ public string [] AcceptTypes {
+ get { return accept_types; }
+ }
+
+ //[MonoTODO ("Always returns 0")]
+ public int ClientCertificateError {
+ get {
+/*
+ if (no_get_certificate)
+ throw new InvalidOperationException (
+ "Call GetClientCertificate() before calling this method.");
+ return client_cert_error;
+*/
+ return 0;
+ }
+ }
+
+ public Encoding ContentEncoding {
+ get {
+ if (content_encoding == null)
+ content_encoding = Encoding.Default;
+ return content_encoding;
+ }
+ }
+
+ public long ContentLength64 {
+ get { return content_length; }
+ }
+
+ public string ContentType {
+ get { return headers ["content-type"]; }
+ }
+
+ public CookieCollection Cookies {
+ get {
+ // TODO: check if the collection is read-only
+ if (cookies == null)
+ cookies = new CookieCollection ();
+ return cookies;
+ }
+ }
+
+ public bool HasEntityBody {
+ get { return (content_length > 0 || is_chunked); }
+ }
+
+ public NameValueCollection Headers {
+ get { return headers; }
+ }
+
+ public string HttpMethod {
+ get { return method; }
+ }
+
+ public Stream InputStream {
+ get { return input_stream; }
+ }
+
+ //[MonoTODO ("Always returns false")]
+ public bool IsAuthenticated {
+ get { return false; }
+ }
+
+ public bool IsLocal {
+ get { return IPAddress.IsLoopback (RemoteEndPoint.Address); }
+ }
+
+ public bool IsSecureConnection {
+ get { return context.Connection.IsSecure; }
+ }
+
+ public bool KeepAlive {
+ get { return false; }
+ }
+
+ public IPEndPoint LocalEndPoint {
+ get { return context.Connection.LocalEndPoint; }
+ }
+
+ public Version ProtocolVersion {
+ get { return version; }
+ }
+
+ public NameValueCollection QueryString {
+ get { return query_string; }
+ }
+
+ public string RawUrl {
+ get { return raw_url; }
+ }
+
+ public IPEndPoint RemoteEndPoint {
+ get { return context.Connection.RemoteEndPoint; }
+ }
+
+ public Guid RequestTraceIdentifier {
+ get { return identifier; }
+ }
+
+ public Uri Url {
+ get { return url; }
+ }
+
+ public Uri UrlReferrer {
+ get { return referrer; }
+ }
+
+ public string UserAgent {
+ get { return headers ["user-agent"]; }
+ }
+
+ public string UserHostAddress {
+ get { return LocalEndPoint.ToString (); }
+ }
+
+ public string UserHostName {
+ get { return headers ["host"]; }
+ }
+
+ public string [] UserLanguages {
+ get { return user_languages; }
+ }
+
+ public IAsyncResult BeginGetClientCertificate (AsyncCallback requestCallback, Object state)
+ {
+ return null;
+ }
+#if SECURITY_DEP
+ public X509Certificate2 EndGetClientCertificate (IAsyncResult asyncResult)
+ {
+ return null;
+ // set no_client_certificate once done.
+ }
+
+ public X509Certificate2 GetClientCertificate ()
+ {
+ // set no_client_certificate once done.
+
+ // InvalidOp if call in progress.
+ return null;
+ }
+#endif
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerResponse.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerResponse.cs
new file mode 100644
index 0000000..dc3bdbc
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpListenerResponse.cs
@@ -0,0 +1,512 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpListenerResponse
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if EMBEDDED_IN_1_0
+
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System; using System.Net; namespace MonoHttp {
+ internal class HttpListenerResponse : IDisposable
+ {
+ bool disposed;
+ Encoding content_encoding;
+ long content_length;
+ bool cl_set;
+ string content_type;
+ CookieCollection cookies;
+ WebHeaderCollection headers = new WebHeaderCollection ();
+ bool keep_alive = true;
+ ResponseStream output_stream;
+ Version version = HttpVersion.Version11;
+ string location;
+ int status_code = 200;
+ string status_description = "OK";
+ bool chunked;
+ HttpListenerContext context;
+ internal bool HeadersSent;
+ bool force_close_chunked;
+
+ internal HttpListenerResponse (HttpListenerContext context)
+ {
+ this.context = context;
+ }
+
+ internal bool ForceCloseChunked {
+ get { return force_close_chunked; }
+ }
+
+ public Encoding ContentEncoding {
+ get {
+ if (content_encoding == null)
+ content_encoding = Encoding.Default;
+ return content_encoding;
+ }
+ set {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ //TODO: is null ok?
+ if (HeadersSent)
+ throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+
+ content_encoding = value;
+ }
+ }
+
+ public long ContentLength64 {
+ get { return content_length; }
+ set {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ if (HeadersSent)
+ throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+
+ if (value < 0)
+ throw new ArgumentOutOfRangeException ("Must be >= 0", "value");
+
+ cl_set = true;
+ content_length = value;
+ }
+ }
+
+ public string ContentType {
+ get { return content_type; }
+ set {
+ // TODO: is null ok?
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ if (HeadersSent)
+ throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+
+ content_type = value;
+ }
+ }
+
+ // RFC 2109, 2965 + the netscape specification at http://wp.netscape.com/newsref/std/cookie_spec.html
+ public CookieCollection Cookies {
+ get {
+ if (cookies == null)
+ cookies = new CookieCollection ();
+ return cookies;
+ }
+ set { cookies = value; } // null allowed?
+ }
+
+ public WebHeaderCollection Headers {
+ get { return headers; }
+ set {
+ /**
+ * "If you attempt to set a Content-Length, Keep-Alive, Transfer-Encoding, or
+ * WWW-Authenticate header using the Headers property, an exception will be
+ * thrown. Use the KeepAlive or ContentLength64 properties to set these headers.
+ * You cannot set the Transfer-Encoding or WWW-Authenticate headers manually."
+ */
+ // TODO: check if this is marked readonly after headers are sent.
+ headers = value;
+ }
+ }
+
+ public bool KeepAlive {
+ get { return keep_alive; }
+ set {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ if (HeadersSent)
+ throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+
+ keep_alive = value;
+ }
+ }
+
+ public Stream OutputStream {
+ get {
+ if (output_stream == null)
+ output_stream = context.Connection.GetResponseStream ();
+ return output_stream;
+ }
+ }
+
+ public Version ProtocolVersion {
+ get { return version; }
+ set {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ if (HeadersSent)
+ throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+
+ if (value == null)
+ throw new ArgumentNullException ("value");
+
+ if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1))
+ throw new ArgumentException ("Must be 1.0 or 1.1", "value");
+
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ version = value;
+ }
+ }
+
+ public string RedirectLocation {
+ get { return location; }
+ set {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ if (HeadersSent)
+ throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+
+ location = value;
+ }
+ }
+
+ public bool SendChunked {
+ get { return chunked; }
+ set {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ if (HeadersSent)
+ throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+
+ chunked = value;
+ }
+ }
+
+ public int StatusCode {
+ get { return status_code; }
+ set {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ if (HeadersSent)
+ throw new InvalidOperationException ("Cannot be changed after headers are sent.");
+
+ if (value < 100 || value > 999)
+ throw new ProtocolViolationException ("StatusCode must be between 100 and 999.");
+ status_code = value;
+ status_description = GetStatusDescription (value);
+ }
+ }
+
+ internal static string GetStatusDescription (int code)
+ {
+ switch (code){
+ case 100: return "Continue";
+ case 101: return "Switching Protocols";
+ case 102: return "Processing";
+ case 200: return "OK";
+ case 201: return "Created";
+ case 202: return "Accepted";
+ case 203: return "Non-Authoritative Information";
+ case 204: return "No Content";
+ case 205: return "Reset Content";
+ case 206: return "Partial Content";
+ case 207: return "Multi-Status";
+ case 300: return "Multiple Choices";
+ case 301: return "Moved Permanently";
+ case 302: return "Found";
+ case 303: return "See Other";
+ case 304: return "Not Modified";
+ case 305: return "Use Proxy";
+ case 307: return "Temporary Redirect";
+ case 400: return "Bad Request";
+ case 401: return "Unauthorized";
+ case 402: return "Payment Required";
+ case 403: return "Forbidden";
+ case 404: return "Not Found";
+ case 405: return "Method Not Allowed";
+ case 406: return "Not Acceptable";
+ case 407: return "Proxy Authentication Required";
+ case 408: return "Request Timeout";
+ case 409: return "Conflict";
+ case 410: return "Gone";
+ case 411: return "Length Required";
+ case 412: return "Precondition Failed";
+ case 413: return "Request Entity Too Large";
+ case 414: return "Request-Uri Too Long";
+ case 415: return "Unsupported Media Type";
+ case 416: return "Requested Range Not Satisfiable";
+ case 417: return "Expectation Failed";
+ case 422: return "Unprocessable Entity";
+ case 423: return "Locked";
+ case 424: return "Failed Dependency";
+ case 500: return "Internal Server Error";
+ case 501: return "Not Implemented";
+ case 502: return "Bad Gateway";
+ case 503: return "Service Unavailable";
+ case 504: return "Gateway Timeout";
+ case 505: return "Http Version Not Supported";
+ case 507: return "Insufficient Storage";
+ }
+ return "";
+ }
+
+ public string StatusDescription {
+ get { return status_description; }
+ set {
+ status_description = value;
+ }
+ }
+
+ void IDisposable.Dispose ()
+ {
+ Close (true); //TODO: Abort or Close?
+ }
+
+ public void Abort ()
+ {
+ if (disposed)
+ return;
+
+ Close (true);
+ }
+
+ public void AddHeader (string name, string value)
+ {
+ if (name == null)
+ throw new ArgumentNullException ("name");
+
+ if (name == "")
+ throw new ArgumentException ("'name' cannot be empty", "name");
+
+ //TODO: check for forbidden headers and invalid characters
+ if (value.Length > 65535)
+ throw new ArgumentOutOfRangeException ("value");
+
+ headers.Set (name, value);
+ }
+
+ public void AppendCookie (Cookie cookie)
+ {
+ if (cookie == null)
+ throw new ArgumentNullException ("cookie");
+
+ Cookies.Add (cookie);
+ }
+
+ public void AppendHeader (string name, string value)
+ {
+ if (name == null)
+ throw new ArgumentNullException ("name");
+
+ if (name == "")
+ throw new ArgumentException ("'name' cannot be empty", "name");
+
+ if (value.Length > 65535)
+ throw new ArgumentOutOfRangeException ("value");
+
+ headers.Add (name, value);
+ }
+
+ void Close (bool force)
+ {
+ // TODO: use the 'force' argument
+ context.Connection.Close ();
+ disposed = true;
+ }
+
+ public void Close ()
+ {
+ if (disposed)
+ return;
+
+ Close (false);
+ }
+
+ public void Close (byte [] responseEntity, bool willBlock)
+ {
+ if (disposed)
+ return;
+
+ if (responseEntity == null)
+ throw new ArgumentNullException ("responseEntity");
+
+ //TODO: if willBlock -> BeginWrite + Close ?
+ ContentLength64 = responseEntity.Length;
+ OutputStream.Write (responseEntity, 0, (int) content_length);
+ Close (false);
+ }
+
+ public void CopyFrom (HttpListenerResponse templateResponse)
+ {
+ headers.Clear ();
+ headers.Add (templateResponse.headers);
+ content_length = templateResponse.content_length;
+ status_code = templateResponse.status_code;
+ status_description = templateResponse.status_description;
+ keep_alive = templateResponse.keep_alive;
+ version = templateResponse.version;
+ }
+
+ public void Redirect (string url)
+ {
+ StatusCode = 302; // Found
+ location = url;
+ }
+
+ bool FindCookie (Cookie cookie)
+ {
+ string name = cookie.Name;
+ string domain = cookie.Domain;
+ string path = cookie.Path;
+ foreach (Cookie c in cookies) {
+ if (name != c.Name)
+ continue;
+ if (domain != c.Domain)
+ continue;
+ if (path == c.Path)
+ return true;
+ }
+
+ return false;
+ }
+
+ internal void SendHeaders (bool closing, MemoryStream ms)
+ {
+ //TODO: When do we send KeepAlive?
+ Encoding encoding = content_encoding;
+ if (encoding == null)
+ encoding = Encoding.Default;
+
+ if (content_type != null) {
+ if (content_encoding != null && content_type.IndexOf ("charset=") == -1) {
+ string enc_name = content_encoding.WebName;
+ headers.SetInternal ("Content-Type", content_type + "; charset=" + enc_name);
+ } else {
+ headers.SetInternal ("Content-Type", content_type);
+ }
+ }
+
+ if (headers ["Server"] == null)
+ headers.SetInternal ("Server", "Mono-HTTPAPI/1.0");
+
+ CultureInfo inv = CultureInfo.InvariantCulture;
+ if (headers ["Date"] == null)
+ headers.SetInternal ("Date", DateTime.UtcNow.ToString ("r", inv));
+
+ if (!chunked) {
+ if (!cl_set && closing) {
+ cl_set = true;
+ content_length = 0;
+ }
+
+ if (cl_set)
+ headers.SetInternal ("Content-Length", content_length.ToString (inv));
+ }
+
+ Version v = context.Request.ProtocolVersion;
+ if (!cl_set && !chunked && v >= HttpVersion.Version11)
+ chunked = true;
+
+ /* Apache forces closing the connection for these status codes:
+ * HttpStatusCode.BadRequest 400
+ * HttpStatusCode.RequestTimeout 408
+ * HttpStatusCode.LengthRequired 411
+ * HttpStatusCode.RequestEntityTooLarge 413
+ * HttpStatusCode.RequestUriTooLong 414
+ * HttpStatusCode.InternalServerError 500
+ * HttpStatusCode.ServiceUnavailable 503
+ */
+ bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 ||
+ status_code == 413 || status_code == 414 || status_code == 500 ||
+ status_code == 503);
+
+ if (conn_close == false) {
+ conn_close = (context.Request.Headers ["connection"] == "close");
+ conn_close |= (v <= HttpVersion.Version10);
+ }
+
+ // They sent both KeepAlive: true and Connection: close!?
+ if (!keep_alive || conn_close)
+ headers.SetInternal ("Connection", "close");
+
+ if (chunked)
+ headers.SetInternal ("Transfer-Encoding", "chunked");
+
+ int chunked_uses = context.Connection.ChunkedUses;
+ if (chunked_uses >= 100) {
+ force_close_chunked = true;
+ if (!conn_close)
+ headers.SetInternal ("Connection", "close");
+ }
+
+ if (location != null)
+ headers.SetInternal ("Location", location);
+
+ if (cookies != null) {
+ bool firstDone = false;
+ StringBuilder cookieSB = new StringBuilder ();
+ foreach (Cookie cookie in cookies) {
+ if (firstDone)
+ cookieSB.Append (",");
+ firstDone = true;
+ cookieSB.Append (MonoHttp.Utility.ToClientString (cookie));
+ }
+ headers.SetInternal("Set-Cookie2", cookieSB.ToString ());
+ }
+
+ StreamWriter writer = new StreamWriter (ms, encoding);
+ writer.Write ("HTTP/{0} {1} {2}\r\n", version, status_code, status_description);
+ string headers_str = headers.ToString ();
+ writer.Write (headers_str);
+ writer.Flush ();
+ int preamble = encoding.GetPreamble ().Length;
+ if (output_stream == null)
+ output_stream = context.Connection.GetResponseStream ();
+
+ /* Assumes that the ms was at position 0 */
+ ms.Position = preamble;
+ HeadersSent = true;
+ }
+
+ public void SetCookie (Cookie cookie)
+ {
+ if (cookie == null)
+ throw new ArgumentNullException ("cookie");
+
+ if (cookies != null) {
+ if (FindCookie (cookie))
+ throw new ArgumentException ("The cookie already exists.");
+ } else {
+ cookies = new CookieCollection ();
+ }
+
+ cookies.Add (cookie);
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpRequestHeader.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpRequestHeader.cs
new file mode 100644
index 0000000..66783bf
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpRequestHeader.cs
@@ -0,0 +1,77 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpRequestHeader
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if !NET_2_0
+using System; using System.Net; namespace MonoHttp {
+ internal enum HttpRequestHeader {
+ CacheControl,
+ Connection,
+ Date,
+ KeepAlive,
+ Pragma,
+ Trailer,
+ TransferEncoding,
+ Upgrade,
+ Via,
+ Warning,
+ Allow,
+ ContentLength,
+ ContentType,
+ ContentEncoding,
+ ContentLanguage,
+ ContentLocation,
+ ContentMd5,
+ ContentRange,
+ Expires,
+ LastModified,
+ Accept,
+ AcceptCharset,
+ AcceptEncoding,
+ AcceptLanguage,
+ Authorization,
+ Cookie,
+ Expect,
+ From,
+ Host,
+ IfMatch,
+ IfModifiedSince,
+ IfNoneMatch,
+ IfRange,
+ IfUnmodifiedSince,
+ MaxForwards,
+ ProxyAuthorization,
+ Referer,
+ Range,
+ Te,
+ Translate,
+ UserAgent
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpResponseHeader.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpResponseHeader.cs
new file mode 100644
index 0000000..6493ecc
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpResponseHeader.cs
@@ -0,0 +1,66 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpResponseHeader
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if !NET_2_0
+using System; using System.Net; namespace MonoHttp {
+ internal enum HttpResponseHeader {
+ CacheControl,
+ Connection,
+ Date,
+ KeepAlive,
+ Pragma,
+ Trailer,
+ TransferEncoding,
+ Upgrade,
+ Via,
+ Warning,
+ Allow,
+ ContentLength,
+ ContentType,
+ ContentEncoding,
+ ContentLanguage,
+ ContentLocation,
+ ContentMd5,
+ ContentRange,
+ Expires,
+ LastModified,
+ AcceptRanges,
+ Age,
+ ETag,
+ Location,
+ ProxyAuthenticate,
+ RetryAfter,
+ Server,
+ SetCookie,
+ Vary,
+ WwwAuthenticate
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpStreamAsyncResult.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpStreamAsyncResult.cs
new file mode 100644
index 0000000..41aa236
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpStreamAsyncResult.cs
@@ -0,0 +1,97 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpStreamAsyncResult
+//
+// Authors:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if EMBEDDED_IN_1_0
+using System.Threading;
+
+using System; using System.Net; namespace MonoHttp {
+ class HttpStreamAsyncResult : IAsyncResult {
+ object locker = new object ();
+ ManualResetEvent handle;
+ bool completed;
+
+ internal byte [] Buffer;
+ internal int Offset;
+ internal int Count;
+ internal AsyncCallback Callback;
+ internal object State;
+ internal int SynchRead;
+ internal Exception Error;
+
+ public void Complete (Exception e)
+ {
+ Error = e;
+ Complete ();
+ }
+
+ public void Complete ()
+ {
+ lock (locker) {
+ if (completed)
+ return;
+
+ completed = true;
+ if (handle != null)
+ handle.Set ();
+
+ if (Callback != null)
+ Callback.BeginInvoke (this, null, null);
+ }
+ }
+
+ public object AsyncState {
+ get { return State; }
+ }
+
+ public WaitHandle AsyncWaitHandle {
+ get {
+ lock (locker) {
+ if (handle == null)
+ handle = new ManualResetEvent (completed);
+ }
+
+ return handle;
+ }
+ }
+
+ public bool CompletedSynchronously {
+ get { return (SynchRead == Count); }
+ }
+
+ public bool IsCompleted {
+ get {
+ lock (locker) {
+ return completed;
+ }
+ }
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/HttpUtility.cs b/Tomboy/Addins/WebSyncService/MonoHttp/HttpUtility.cs
new file mode 100644
index 0000000..3ee4747
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/HttpUtility.cs
@@ -0,0 +1,105 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.HttpUtility
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if EMBEDDED_IN_1_0
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System; using System.Net; namespace MonoHttp {
+ sealed class HttpUtility
+ {
+ private HttpUtility ()
+ {
+ }
+
+ public static string UrlDecode (string s)
+ {
+ return UrlDecode (s, null);
+ }
+
+ static char [] GetChars (MemoryStream b, Encoding e)
+ {
+ return e.GetChars (b.GetBuffer (), 0, (int) b.Length);
+ }
+
+ public static string UrlDecode (string s, Encoding e)
+ {
+ if (null == s)
+ return null;
+
+ if (s.IndexOf ('%') == -1 && s.IndexOf ('+') == -1)
+ return s;
+
+ if (e == null)
+ e = Encoding.GetEncoding (28591);
+
+ StringBuilder output = new StringBuilder ();
+ long len = s.Length;
+ NumberStyles hexa = NumberStyles.HexNumber;
+ MemoryStream bytes = new MemoryStream ();
+
+ for (int i = 0; i < len; i++) {
+ if (s [i] == '%' && i + 2 < len) {
+ if (s [i + 1] == 'u' && i + 5 < len) {
+ if (bytes.Length > 0) {
+ output.Append (GetChars (bytes, e));
+ bytes.SetLength (0);
+ }
+ output.Append ((char) Int32.Parse (s.Substring (i + 2, 4), hexa));
+ i += 5;
+ } else {
+ bytes.WriteByte ((byte) Int32.Parse (s.Substring (i + 1, 2), hexa));
+ i += 2;
+ }
+ continue;
+ }
+
+ if (bytes.Length > 0) {
+ output.Append (GetChars (bytes, e));
+ bytes.SetLength (0);
+ }
+
+ if (s [i] == '+') {
+ output.Append (' ');
+ } else {
+ output.Append (s [i]);
+ }
+ }
+
+ if (bytes.Length > 0) {
+ output.Append (GetChars (bytes, e));
+ }
+
+ bytes = null;
+ return output.ToString ();
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/ListenerAsyncResult.cs b/Tomboy/Addins/WebSyncService/MonoHttp/ListenerAsyncResult.cs
new file mode 100644
index 0000000..f4348d9
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/ListenerAsyncResult.cs
@@ -0,0 +1,135 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.ListenerAsyncResult
+//
+// Authors:
+// Gonzalo Paniagua Javier (gonzalo ximian com)
+//
+// Copyright (c) 2005 Ximian, Inc (http://www.ximian.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if EMBEDDED_IN_1_0
+
+using System.Threading;
+using System; using System.Net; namespace MonoHttp {
+ class ListenerAsyncResult : IAsyncResult {
+ ManualResetEvent handle;
+ bool synch;
+ bool completed;
+ AsyncCallback cb;
+ object state;
+ Exception exception;
+ HttpListenerContext context;
+ object locker = new object ();
+
+ public ListenerAsyncResult (AsyncCallback cb, object state)
+ {
+ this.cb = cb;
+ this.state = state;
+ }
+
+ internal void Complete (string error)
+ {
+ //FIXME: error_code?
+ exception = new HttpListenerException (0, error);
+ lock (locker) {
+ completed = true;
+ if (handle != null)
+ handle.Set ();
+
+ if (cb != null)
+ ThreadPool.QueueUserWorkItem (InvokeCallback, this);
+ }
+ }
+
+ static void InvokeCallback (object o)
+ {
+ ListenerAsyncResult ares = (ListenerAsyncResult) o;
+ ares.cb (ares);
+ }
+
+ internal void Complete (HttpListenerContext context)
+ {
+ Complete (context, false);
+ }
+
+ internal void Complete (HttpListenerContext context, bool synch)
+ {
+ this.synch = synch;
+ this.context = context;
+ lock (locker) {
+ completed = true;
+ if (handle != null)
+ handle.Set ();
+
+ if ((context.Listener.AuthenticationSchemes == AuthenticationSchemes.Basic || context.Listener.AuthenticationSchemes == AuthenticationSchemes.Negotiate) && context.Request.Headers ["Authorization"] == null) {
+ context.Listener.EndGetContext (this);
+ context.Response.StatusCode = 401;
+ context.Response.Headers ["WWW-Authenticate"] = AuthenticationSchemes.Basic + " realm=\"\"";
+ context.Response.OutputStream.Close ();
+ context.Listener.BeginGetContext (cb, state);
+ } else if (cb != null)
+ ThreadPool.QueueUserWorkItem (InvokeCallback, this);
+ }
+ }
+
+ internal HttpListenerContext GetContext ()
+ {
+ if (exception != null)
+ throw exception;
+
+ return context;
+ }
+
+ public object AsyncState {
+ get { return state; }
+ }
+
+ public WaitHandle AsyncWaitHandle {
+ get {
+ lock (locker) {
+ if (handle == null)
+ handle = new ManualResetEvent (completed);
+ }
+
+ return handle;
+ }
+ }
+
+ public bool CompletedSynchronously {
+ get { return synch; }
+ }
+
+ public bool IsCompleted {
+ get {
+ lock (locker) {
+ return completed;
+ }
+ }
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/ListenerPrefix.cs b/Tomboy/Addins/WebSyncService/MonoHttp/ListenerPrefix.cs
new file mode 100644
index 0000000..f857bab
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/ListenerPrefix.cs
@@ -0,0 +1,163 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.ListenerPrefix
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+// Oleg Mihailik (mihailik gmail co_m)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if EMBEDDED_IN_1_0
+
+using System; using System.Net; namespace MonoHttp {
+ sealed class ListenerPrefix
+ {
+ string original;
+ string host;
+ ushort port;
+ string path;
+ bool secure;
+ IPAddress [] addresses;
+ public HttpListener Listener;
+
+ public ListenerPrefix (string prefix)
+ {
+ this.original = prefix;
+ Parse (prefix);
+ }
+
+ public override string ToString ()
+ {
+ return original;
+ }
+
+ public IPAddress [] Addresses {
+ get { return addresses; }
+ set { addresses = value; }
+ }
+ public bool Secure {
+ get { return secure; }
+ }
+
+ public string Host {
+ get { return host; }
+ }
+
+ public int Port {
+ get { return (int) port; }
+ }
+
+ public string Path {
+ get { return path; }
+ }
+
+ // Equals and GetHashCode are required to detect duplicates in HttpListenerPrefixCollection.
+ public override bool Equals (object o)
+ {
+ ListenerPrefix other = o as ListenerPrefix;
+ if (other == null)
+ return false;
+
+ return (original == other.original);
+ }
+
+ public override int GetHashCode ()
+ {
+ return original.GetHashCode ();
+ }
+
+ void Parse (string uri)
+ {
+ int default_port = (uri.StartsWith ("http://")) ? 80 : -1;
+ if (default_port == -1) {
+ default_port = (uri.StartsWith ("https://")) ? 443 : -1;
+ secure = true;
+ }
+
+ int length = uri.Length;
+ int start_host = uri.IndexOf (':') + 3;
+ if (start_host >= length)
+ throw new ArgumentException ("No host specified.");
+
+ int colon = uri.IndexOf (':', start_host, length - start_host);
+ int root;
+ if (colon > 0) {
+ host = uri.Substring (start_host, colon - start_host);
+ root = uri.IndexOf ('/', colon, length - colon);
+ port = (ushort) Int32.Parse (uri.Substring (colon + 1, root - colon - 1));
+ path = uri.Substring (root);
+ } else {
+ root = uri.IndexOf ('/', start_host, length - start_host);
+ host = uri.Substring (start_host, root - start_host);
+ path = uri.Substring (root);
+ }
+ }
+
+ public static void CheckUri (string uri)
+ {
+ if (uri == null)
+ throw new ArgumentNullException ("uriPrefix");
+
+ int default_port = (uri.StartsWith ("http://")) ? 80 : -1;
+ if (default_port == -1)
+ default_port = (uri.StartsWith ("https://")) ? 443 : -1;
+ if (default_port == -1)
+ throw new ArgumentException ("Only 'http' and 'https' schemes are supported.");
+
+ int length = uri.Length;
+ int start_host = uri.IndexOf (':') + 3;
+ if (start_host >= length)
+ throw new ArgumentException ("No host specified.");
+
+ int colon = uri.IndexOf (':', start_host, length - start_host);
+ if (start_host == colon)
+ throw new ArgumentException ("No host specified.");
+
+ int root;
+ if (colon > 0) {
+ root = uri.IndexOf ('/', colon, length - colon);
+ if (root == -1)
+ throw new ArgumentException ("No path specified.");
+
+ try {
+ int p = Int32.Parse (uri.Substring (colon + 1, root - colon - 1));
+ if (p <= 0 || p >= 65536)
+ throw new Exception ();
+ } catch {
+ throw new ArgumentException ("Invalid port.");
+ }
+ } else {
+ root = uri.IndexOf ('/', start_host, length - start_host);
+ if (root == -1)
+ throw new ArgumentException ("No path specified.");
+ }
+
+ if (uri [uri.Length - 1] != '/')
+ throw new ArgumentException ("The prefix must end with '/'");
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/RequestStream.cs b/Tomboy/Addins/WebSyncService/MonoHttp/RequestStream.cs
new file mode 100644
index 0000000..42212e1
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/RequestStream.cs
@@ -0,0 +1,226 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.RequestStream
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if EMBEDDED_IN_1_0
+using System.IO;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System; using System.Net; namespace MonoHttp {
+ class RequestStream : Stream
+ {
+ byte [] buffer;
+ int offset;
+ int length;
+ long remaining_body;
+ bool disposed;
+ Stream stream;
+
+ internal RequestStream (Stream stream, byte [] buffer, int offset, int length)
+ : this (stream, buffer, offset, length, -1)
+ {
+ }
+
+ internal RequestStream (Stream stream, byte [] buffer, int offset, int length, long contentlength)
+ {
+ this.stream = stream;
+ this.buffer = buffer;
+ this.offset = offset;
+ this.length = length;
+ this.remaining_body = contentlength;
+ }
+
+ public override bool CanRead {
+ get { return true; }
+ }
+
+ public override bool CanSeek {
+ get { return false; }
+ }
+
+ public override bool CanWrite {
+ get { return false; }
+ }
+
+ public override long Length {
+ get { throw new NotSupportedException (); }
+ }
+
+ public override long Position {
+ get { throw new NotSupportedException (); }
+ set { throw new NotSupportedException (); }
+ }
+
+
+ public override void Close ()
+ {
+ disposed = true;
+ }
+
+ public override void Flush ()
+ {
+ }
+
+
+ // Returns 0 if we can keep reading from the base stream,
+ // > 0 if we read something from the buffer.
+ // -1 if we had a content length set and we finished reading that many bytes.
+ int FillFromBuffer (byte [] buffer, int off, int count)
+ {
+ if (buffer == null)
+ throw new ArgumentNullException ("buffer");
+ if (off < 0)
+ throw new ArgumentOutOfRangeException ("offset", "< 0");
+ if (count < 0)
+ throw new ArgumentOutOfRangeException ("count", "< 0");
+ int len = buffer.Length;
+ if (off > len)
+ throw new ArgumentException ("destination offset is beyond array size");
+ if (off > len - count)
+ throw new ArgumentException ("Reading would overrun buffer");
+
+ if (this.remaining_body == 0)
+ return -1;
+
+ if (this.length == 0)
+ return 0;
+
+ int size = Math.Min (this.length, count);
+ if (this.remaining_body > 0)
+ size = (int) Math.Min (size, this.remaining_body);
+
+ if (this.offset > this.buffer.Length - size) {
+ size = Math.Min (size, this.buffer.Length - this.offset);
+ }
+ if (size == 0)
+ return 0;
+
+ Buffer.BlockCopy (this.buffer, this.offset, buffer, off, size);
+ this.offset += size;
+ this.length -= size;
+ if (this.remaining_body > 0)
+ remaining_body -= size;
+ return size;
+ }
+
+ public override int Read ([In,Out] byte[] buffer, int offset, int count)
+ {
+ if (disposed)
+ throw new ObjectDisposedException (typeof (RequestStream).ToString ());
+
+ // Call FillFromBuffer to check for buffer boundaries even when remaining_body is 0
+ int nread = FillFromBuffer (buffer, offset, count);
+ if (nread == -1) { // No more bytes available (Content-Length)
+ return 0;
+ } else if (nread > 0) {
+ return nread;
+ }
+
+ nread = stream.Read (buffer, offset, count);
+ if (nread > 0 && remaining_body > 0)
+ remaining_body -= nread;
+ return nread;
+ }
+
+ public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
+ AsyncCallback cback, object state)
+ {
+ if (disposed)
+ throw new ObjectDisposedException (typeof (RequestStream).ToString ());
+
+ int nread = FillFromBuffer (buffer, offset, count);
+ if (nread > 0 || nread == -1) {
+ HttpStreamAsyncResult ares = new HttpStreamAsyncResult ();
+ ares.Buffer = buffer;
+ ares.Offset = offset;
+ ares.Count = count;
+ ares.Callback = cback;
+ ares.State = state;
+ ares.SynchRead = nread;
+ ares.Complete ();
+ return ares;
+ }
+
+ // Avoid reading past the end of the request to allow
+ // for HTTP pipelining
+ if (remaining_body >= 0 && count > remaining_body)
+ count = (int) Math.Min (Int32.MaxValue, remaining_body);
+ return stream.BeginRead (buffer, offset, count, cback, state);
+ }
+
+ public override int EndRead (IAsyncResult ares)
+ {
+ if (disposed)
+ throw new ObjectDisposedException (typeof (RequestStream).ToString ());
+
+ if (ares == null)
+ throw new ArgumentNullException ("async_result");
+
+ if (ares is HttpStreamAsyncResult) {
+ HttpStreamAsyncResult r = (HttpStreamAsyncResult) ares;
+ if (!ares.IsCompleted)
+ ares.AsyncWaitHandle.WaitOne ();
+ return r.SynchRead;
+ }
+
+ // Close on exception?
+ int nread = stream.EndRead (ares);
+ if (remaining_body > 0 && nread > 0)
+ remaining_body -= nread;
+ return nread;
+ }
+
+ public override long Seek (long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override void SetLength (long value)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override void Write (byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
+ AsyncCallback cback, object state)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override void EndWrite (IAsyncResult async_result)
+ {
+ throw new NotSupportedException ();
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/ResponseStream.cs b/Tomboy/Addins/WebSyncService/MonoHttp/ResponseStream.cs
new file mode 100644
index 0000000..ae386bc
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/ResponseStream.cs
@@ -0,0 +1,243 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.ResponseStream
+//
+// Author:
+// Gonzalo Paniagua Javier (gonzalo novell com)
+//
+// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if EMBEDDED_IN_1_0
+
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+using System.Runtime.InteropServices;
+using System; using System.Net; namespace MonoHttp {
+ // FIXME: Does this buffer the response until Close?
+ // Update: we send a single packet for the first non-chunked Write
+ // What happens when we set content-length to X and write X-1 bytes then close?
+ // what if we don't set content-length at all?
+ class ResponseStream : Stream
+ {
+ HttpListenerResponse response;
+ bool ignore_errors;
+ bool disposed;
+ bool trailer_sent;
+ Stream stream;
+
+ internal ResponseStream (Stream stream, HttpListenerResponse response, bool ignore_errors)
+ {
+ this.response = response;
+ this.ignore_errors = ignore_errors;
+ this.stream = stream;
+ }
+
+ public override bool CanRead {
+ get { return false; }
+ }
+
+ public override bool CanSeek {
+ get { return false; }
+ }
+
+ public override bool CanWrite {
+ get { return true; }
+ }
+
+ public override long Length {
+ get { throw new NotSupportedException (); }
+ }
+
+ public override long Position {
+ get { throw new NotSupportedException (); }
+ set { throw new NotSupportedException (); }
+ }
+
+
+ public override void Close ()
+ {
+ if (disposed == false) {
+ disposed = true;
+ byte [] bytes = null;
+ MemoryStream ms = GetHeaders (true);
+ bool chunked = response.SendChunked;
+ if (ms != null) {
+ long start = ms.Position;
+ if (chunked && !trailer_sent) {
+ bytes = GetChunkSizeBytes (0, true);
+ ms.Position = ms.Length;
+ ms.Write (bytes, 0, bytes.Length);
+ }
+ InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start));
+ trailer_sent = true;
+ } else if (chunked && !trailer_sent) {
+ bytes = GetChunkSizeBytes (0, true);
+ InternalWrite (bytes, 0, bytes.Length);
+ trailer_sent = true;
+ }
+ response.Close ();
+ }
+ }
+
+ MemoryStream GetHeaders (bool closing)
+ {
+ if (response.HeadersSent)
+ return null;
+ MemoryStream ms = new MemoryStream ();
+ response.SendHeaders (closing, ms);
+ return ms;
+ }
+
+ public override void Flush ()
+ {
+ }
+
+ static byte [] crlf = new byte [] { 13, 10 };
+ static byte [] GetChunkSizeBytes (int size, bool final)
+ {
+ string str = String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : "");
+ return Encoding.ASCII.GetBytes (str);
+ }
+
+ internal void InternalWrite (byte [] buffer, int offset, int count)
+ {
+ if (ignore_errors) {
+ try {
+ stream.Write (buffer, offset, count);
+ } catch { }
+ } else {
+ stream.Write (buffer, offset, count);
+ }
+ }
+
+ public override void Write (byte [] buffer, int offset, int count)
+ {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ byte [] bytes = null;
+ MemoryStream ms = GetHeaders (false);
+ bool chunked = response.SendChunked;
+ if (ms != null) {
+ long start = ms.Position; // After the possible preamble for the encoding
+ ms.Position = ms.Length;
+ if (chunked) {
+ bytes = GetChunkSizeBytes (count, false);
+ ms.Write (bytes, 0, bytes.Length);
+ }
+
+ int new_count = Math.Min (count, 16384 - (int) ms.Position + (int) start);
+ ms.Write (buffer, offset, new_count);
+ count -= new_count;
+ offset += new_count;
+ InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start));
+ ms.SetLength (0);
+ ms.Capacity = 0; // 'dispose' the buffer in ms.
+ } else if (chunked) {
+ bytes = GetChunkSizeBytes (count, false);
+ InternalWrite (bytes, 0, bytes.Length);
+ }
+
+ if (count > 0)
+ InternalWrite (buffer, offset, count);
+ if (chunked)
+ InternalWrite (crlf, 0, 2);
+ }
+
+ public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
+ AsyncCallback cback, object state)
+ {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ byte [] bytes = null;
+ MemoryStream ms = GetHeaders (false);
+ bool chunked = response.SendChunked;
+ if (ms != null) {
+ long start = ms.Position;
+ ms.Position = ms.Length;
+ if (chunked) {
+ bytes = GetChunkSizeBytes (count, false);
+ ms.Write (bytes, 0, bytes.Length);
+ }
+ ms.Write (buffer, offset, count);
+ buffer = ms.GetBuffer ();
+ offset = (int) start;
+ count = (int) (ms.Position - start);
+ } else if (chunked) {
+ bytes = GetChunkSizeBytes (count, false);
+ InternalWrite (bytes, 0, bytes.Length);
+ }
+
+ return stream.BeginWrite (buffer, offset, count, cback, state);
+ }
+
+ public override void EndWrite (IAsyncResult ares)
+ {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ if (ignore_errors) {
+ try {
+ stream.EndWrite (ares);
+ if (response.SendChunked)
+ stream.Write (crlf, 0, 2);
+ } catch { }
+ } else {
+ stream.EndWrite (ares);
+ if (response.SendChunked)
+ stream.Write (crlf, 0, 2);
+ }
+ }
+
+ public override int Read ([In,Out] byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
+ AsyncCallback cback, object state)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override int EndRead (IAsyncResult ares)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override long Seek (long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override void SetLength (long value)
+ {
+ throw new NotSupportedException ();
+ }
+ }
+}
+#endif
+
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/Utility.cs b/Tomboy/Addins/WebSyncService/MonoHttp/Utility.cs
new file mode 100644
index 0000000..fe6100d
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/Utility.cs
@@ -0,0 +1,98 @@
+
+
+using System;
+using System.Text;
+
+namespace MonoHttp
+{
+ class Utility
+ {
+
+ #region from System.Uri
+
+ internal static bool MaybeUri (string s)
+ {
+ int p = s.IndexOf (':');
+ if (p == -1)
+ return false;
+
+ if (p >= 10)
+ return false;
+
+ return IsPredefinedScheme (s.Substring (0, p));
+ }
+
+ private static bool IsPredefinedScheme (string scheme)
+ {
+ switch (scheme) {
+ case "http":
+ case "https":
+ case "file":
+ case "ftp":
+ case "nntp":
+ case "gopher":
+ case "mailto":
+ case "news":
+#if NET_2_0
+ case "net.pipe":
+ case "net.tcp":
+#endif
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ #endregion
+
+ #region from System.Net.Cookiie
+
+ internal static string ToClientString (System.Net.Cookie cookie)
+ {
+ if (cookie.Name.Length == 0)
+ return String.Empty;
+
+ StringBuilder result = new StringBuilder (64);
+
+ if (cookie.Version > 0)
+ result.Append ("Version=").Append (cookie.Version).Append (";");
+
+ result.Append (cookie.Name).Append ("=").Append (cookie.Value);
+
+ if (cookie.Path != null && cookie.Path.Length != 0)
+ result.Append (";Path=").Append (QuotedString (cookie, cookie.Path));
+
+ if (cookie.Domain != null && cookie.Domain.Length != 0)
+ result.Append (";Domain=").Append (QuotedString (cookie, cookie.Domain));
+
+ if (cookie.Port != null && cookie.Port.Length != 0)
+ result.Append (";Port=").Append (cookie.Port);
+
+ return result.ToString ();
+ }
+
+ // See par 3.6 of RFC 2616
+ static string QuotedString (System.Net.Cookie cookie, string value)
+ {
+ if (cookie.Version == 0 || IsToken (value))
+ return value;
+ else
+ return "\"" + value.Replace("\"", "\\\"") + "\"";
+ }
+
+ static bool IsToken (string value)
+ {
+ int len = value.Length;
+ for (int i = 0; i < len; i++) {
+ char c = value [i];
+ if (c < 0x20 || c >= 0x7f || tspecials.IndexOf (c) != -1)
+ return false;
+ }
+ return true;
+ }
+
+ static string tspecials = "()<>@,;:\\\"/[]?={} \t"; // from RFC 2965, 2068
+
+ #endregion
+ }
+}
diff --git a/Tomboy/Addins/WebSyncService/MonoHttp/WebHeaderCollection.cs b/Tomboy/Addins/WebSyncService/MonoHttp/WebHeaderCollection.cs
new file mode 100644
index 0000000..a55384f
--- /dev/null
+++ b/Tomboy/Addins/WebSyncService/MonoHttp/WebHeaderCollection.cs
@@ -0,0 +1,699 @@
+#define EMBEDDED_IN_1_0
+
+//
+// System.Net.WebHeaderCollection
+//
+// Authors:
+// Lawrence Pit (loz cable a2000 nl)
+// Gonzalo Paniagua Javier (gonzalo ximian com)
+// Miguel de Icaza (miguel novell com)
+//
+// Copyright 2003 Ximian, Inc. (http://www.ximian.com)
+// Copyright 2007 Novell, Inc. (http://www.novell.com)
+//
+//
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Text;
+
+// See RFC 2068 par 4.2 Message Headers
+
+using System; using System.Net; namespace MonoHttp
+{
+ [Serializable]
+ [ComVisible(true)]
+ internal class WebHeaderCollection : NameValueCollection, ISerializable
+ {
+ private static readonly Hashtable restricted;
+ private static readonly Hashtable multiValue;
+ private bool internallyCreated = false;
+
+ // Static Initializer
+
+ static WebHeaderCollection ()
+ {
+ // the list of restricted header names as defined
+ // by the ms.net spec
+ restricted = new Hashtable (CaseInsensitiveHashCodeProvider.DefaultInvariant,
+ CaseInsensitiveComparer.DefaultInvariant);
+
+ restricted.Add ("accept", true);
+ restricted.Add ("connection", true);
+ restricted.Add ("content-length", true);
+ restricted.Add ("content-type", true);
+ restricted.Add ("date", true);
+ restricted.Add ("expect", true);
+ restricted.Add ("host", true);
+ restricted.Add ("if-modified-since", true);
+ restricted.Add ("range", true);
+ restricted.Add ("referer", true);
+ restricted.Add ("transfer-encoding", true);
+ restricted.Add ("user-agent", true);
+
+ // see par 14 of RFC 2068 to see which header names
+ // accept multiple values each separated by a comma
+ multiValue = new Hashtable (CaseInsensitiveHashCodeProvider.DefaultInvariant,
+ CaseInsensitiveComparer.DefaultInvariant);
+
+ multiValue.Add ("accept", true);
+ multiValue.Add ("accept-charset", true);
+ multiValue.Add ("accept-encoding", true);
+ multiValue.Add ("accept-language", true);
+ multiValue.Add ("accept-ranges", true);
+ multiValue.Add ("allow", true);
+ multiValue.Add ("authorization", true);
+ multiValue.Add ("cache-control", true);
+ multiValue.Add ("connection", true);
+ multiValue.Add ("content-encoding", true);
+ multiValue.Add ("content-language", true);
+ multiValue.Add ("expect", true);
+ multiValue.Add ("if-match", true);
+ multiValue.Add ("if-none-match", true);
+ multiValue.Add ("proxy-authenticate", true);
+ multiValue.Add ("public", true);
+ multiValue.Add ("range", true);
+ multiValue.Add ("transfer-encoding", true);
+ multiValue.Add ("upgrade", true);
+ multiValue.Add ("vary", true);
+ multiValue.Add ("via", true);
+ multiValue.Add ("warning", true);
+ multiValue.Add ("www-authenticate", true);
+
+ // Extra
+ multiValue.Add ("set-cookie", true);
+ multiValue.Add ("set-cookie2", true);
+ }
+
+ // Constructors
+
+ public WebHeaderCollection () { }
+
+ protected WebHeaderCollection (SerializationInfo serializationInfo,
+ StreamingContext streamingContext)
+ {
+ int count;
+
+ try {
+ count = serializationInfo.GetInt32("Count");
+ for (int i = 0; i < count; i++)
+ this.Add (serializationInfo.GetString (i.ToString ()),
+ serializationInfo.GetString ((count + i).ToString ()));
+ } catch (SerializationException){
+ count = serializationInfo.GetInt32("count");
+ for (int i = 0; i < count; i++)
+ this.Add (serializationInfo.GetString ("k" + i),
+ serializationInfo.GetString ("v" + i));
+ }
+
+ }
+
+ internal WebHeaderCollection (bool internallyCreated)
+ {
+ this.internallyCreated = internallyCreated;
+ }
+
+ // Methods
+
+ public void Add (string header)
+ {
+ if (header == null)
+ throw new ArgumentNullException ("header");
+ int pos = header.IndexOf (':');
+ if (pos == -1)
+ throw new ArgumentException ("no colon found", "header");
+ this.Add (header.Substring (0, pos),
+ header.Substring (pos + 1));
+ }
+
+ public override void Add (string name, string value)
+ {
+ if (name == null)
+ throw new ArgumentNullException ("name");
+ if (internallyCreated && IsRestricted (name))
+ throw new ArgumentException ("This header must be modified with the appropiate property.");
+ this.AddWithoutValidate (name, value);
+ }
+
+ protected void AddWithoutValidate (string headerName, string headerValue)
+ {
+ if (!IsHeaderName (headerName))
+ throw new ArgumentException ("invalid header name: " + headerName, "headerName");
+ if (headerValue == null)
+ headerValue = String.Empty;
+ else
+ headerValue = headerValue.Trim ();
+ if (!IsHeaderValue (headerValue))
+ throw new ArgumentException ("invalid header value: " + headerValue, "headerValue");
+ base.Add (headerName, headerValue);
+ }
+
+ public override string [] GetValues (string header)
+ {
+ if (header == null)
+ throw new ArgumentNullException ("header");
+
+ string [] values = base.GetValues (header);
+ if (values == null || values.Length == 0)
+ return null;
+
+ /*
+ if (IsMultiValue (header)) {
+ values = GetMultipleValues (values);
+ }
+ */
+
+ return values;
+ }
+
+ public override string[] GetValues (int index)
+ {
+ string[] values = base.GetValues (index);
+ if (values == null || values.Length == 0) {
+ return(null);
+ }
+
+ return(values);
+ }
+
+ /* Now i wonder why this is here...
+ static string [] GetMultipleValues (string [] values)
+ {
+ ArrayList mvalues = new ArrayList (values.Length);
+ StringBuilder sb = null;
+ for (int i = 0; i < values.Length; ++i) {
+ string val = values [i];
+ if (val.IndexOf (',') == -1) {
+ mvalues.Add (val);
+ continue;
+ }
+
+ if (sb == null)
+ sb = new StringBuilder ();
+
+ bool quote = false;
+ for (int k = 0; k < val.Length; k++) {
+ char c = val [k];
+ if (c == '"') {
+ quote = !quote;
+ } else if (!quote && c == ',') {
+ mvalues.Add (sb.ToString ().Trim ());
+ sb.Length = 0;
+ continue;
+ }
+ sb.Append (c);
+ }
+
+ if (sb.Length > 0) {
+ mvalues.Add (sb.ToString ().Trim ());
+ sb.Length = 0;
+ }
+ }
+
+ return (string []) mvalues.ToArray (typeof (string));
+ }
+ */
+
+ public static bool IsRestricted (string headerName)
+ {
+ if (headerName == null)
+ throw new ArgumentNullException ("headerName");
+
+ if (headerName == "") // MS throw nullexception here!
+ throw new ArgumentException ("empty string", "headerName");
+
+ return restricted.ContainsKey (headerName);
+ }
+
+#if EMBEDDED_IN_1_0
+ //[MonoNotSupported("")]
+ public static bool IsRestricted (string headerName, bool response)
+ {
+ throw new NotImplementedException ();
+ }
+#endif
+
+ public override void OnDeserialization (object sender)
+ {
+ }
+
+ public override void Remove (string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException ("name");
+ if (internallyCreated && IsRestricted (name))
+ throw new ArgumentException ("restricted header");
+ base.Remove (name);
+ }
+
+ public override void Set (string name, string value)
+ {
+ if (name == null)
+ throw new ArgumentNullException ("name");
+ if (internallyCreated && IsRestricted (name))
+ throw new ArgumentException ("restricted header");
+ if (!IsHeaderName (name))
+ throw new ArgumentException ("invalid header name");
+ if (value == null)
+ value = String.Empty;
+ else
+ value = value.Trim ();
+ if (!IsHeaderValue (value))
+ throw new ArgumentException ("invalid header value");
+ base.Set (name, value);
+ }
+
+ public byte[] ToByteArray ()
+ {
+ return Encoding.UTF8.GetBytes(ToString ());
+ }
+
+ public override string ToString ()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ int count = base.Count;
+ for (int i = 0; i < count ; i++)
+ sb.Append (GetKey (i))
+ .Append (": ")
+ .Append (Get (i))
+ .Append ("\r\n");
+
+ return sb.Append("\r\n").ToString();
+ }
+#if !TARGET_JVM
+ void ISerializable.GetObjectData (SerializationInfo serializationInfo,
+ StreamingContext streamingContext)
+ {
+ GetObjectData (serializationInfo, streamingContext);
+ }
+#endif
+ public override void GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)
+ {
+ int count = base.Count;
+ serializationInfo.AddValue ("Count", count);
+ for (int i = 0; i < count; i++) {
+ serializationInfo.AddValue (i.ToString (), GetKey (i));
+ serializationInfo.AddValue ((count + i).ToString (), Get (i));
+ }
+ }
+
+ public override string[] AllKeys
+ {
+ get {
+ return(base.AllKeys);
+ }
+ }
+
+ public override int Count
+ {
+ get {
+ return(base.Count);
+ }
+ }
+
+ public override KeysCollection Keys
+ {
+ get {
+ return(base.Keys);
+ }
+ }
+
+ public override string Get (int index)
+ {
+ return(base.Get (index));
+ }
+
+ public override string Get (string name)
+ {
+ return(base.Get (name));
+ }
+
+ public override string GetKey (int index)
+ {
+ return(base.GetKey (index));
+ }
+
+#if EMBEDDED_IN_1_0
+ public void Add (HttpRequestHeader header, string value)
+ {
+ Add (RequestHeaderToString (header), value);
+ }
+
+ public void Remove (HttpRequestHeader header)
+ {
+ Remove (RequestHeaderToString (header));
+ }
+
+ public void Set (HttpRequestHeader header, string value)
+ {
+ Set (RequestHeaderToString (header), value);
+ }
+
+ public void Add (HttpResponseHeader header, string value)
+ {
+ Add (ResponseHeaderToString (header), value);
+ }
+
+ public void Remove (HttpResponseHeader header)
+ {
+ Remove (ResponseHeaderToString (header));
+ }
+
+ public void Set (HttpResponseHeader header, string value)
+ {
+ Set (ResponseHeaderToString (header), value);
+ }
+
+ string RequestHeaderToString (HttpRequestHeader value)
+ {
+ switch (value){
+ case HttpRequestHeader.CacheControl:
+ return "cache-control";
+ case HttpRequestHeader.Connection:
+ return "connection";
+ case HttpRequestHeader.Date:
+ return "date";
+ case HttpRequestHeader.KeepAlive:
+ return "keep-alive";
+ case HttpRequestHeader.Pragma:
+ return "pragma";
+ case HttpRequestHeader.Trailer:
+ return "trailer";
+ case HttpRequestHeader.TransferEncoding:
+ return "transfer-encoding";
+ case HttpRequestHeader.Upgrade:
+ return "upgrade";
+ case HttpRequestHeader.Via:
+ return "via";
+ case HttpRequestHeader.Warning:
+ return "warning";
+ case HttpRequestHeader.Allow:
+ return "allow";
+ case HttpRequestHeader.ContentLength:
+ return "content-length";
+ case HttpRequestHeader.ContentType:
+ return "content-type";
+ case HttpRequestHeader.ContentEncoding:
+ return "content-encoding";
+ case HttpRequestHeader.ContentLanguage:
+ return "content-language";
+ case HttpRequestHeader.ContentLocation:
+ return "content-location";
+ case HttpRequestHeader.ContentMd5:
+ return "content-md5";
+ case HttpRequestHeader.ContentRange:
+ return "content-range";
+ case HttpRequestHeader.Expires:
+ return "expires";
+ case HttpRequestHeader.LastModified:
+ return "last-modified";
+ case HttpRequestHeader.Accept:
+ return "accept";
+ case HttpRequestHeader.AcceptCharset:
+ return "accept-charset";
+ case HttpRequestHeader.AcceptEncoding:
+ return "accept-encoding";
+ case HttpRequestHeader.AcceptLanguage:
+ return "accept-language";
+ case HttpRequestHeader.Authorization:
+ return "authorization";
+ case HttpRequestHeader.Cookie:
+ return "cookie";
+ case HttpRequestHeader.Expect:
+ return "expect";
+ case HttpRequestHeader.From:
+ return "from";
+ case HttpRequestHeader.Host:
+ return "host";
+ case HttpRequestHeader.IfMatch:
+ return "if-match";
+ case HttpRequestHeader.IfModifiedSince:
+ return "if-modified-since";
+ case HttpRequestHeader.IfNoneMatch:
+ return "if-none-match";
+ case HttpRequestHeader.IfRange:
+ return "if-range";
+ case HttpRequestHeader.IfUnmodifiedSince:
+ return "if-unmodified-since";
+ case HttpRequestHeader.MaxForwards:
+ return "max-forwards";
+ case HttpRequestHeader.ProxyAuthorization:
+ return "proxy-authorization";
+ case HttpRequestHeader.Referer:
+ return "referer";
+ case HttpRequestHeader.Range:
+ return "range";
+ case HttpRequestHeader.Te:
+ return "te";
+ case HttpRequestHeader.Translate:
+ return "translate";
+ case HttpRequestHeader.UserAgent:
+ return "user-agent";
+ default:
+ throw new InvalidOperationException ();
+ }
+ }
+
+
+ public string this[HttpRequestHeader hrh]
+ {
+ get {
+ return Get (RequestHeaderToString (hrh));
+ }
+
+ set {
+ Add (RequestHeaderToString (hrh), value);
+ }
+ }
+
+ string ResponseHeaderToString (HttpResponseHeader value)
+ {
+ switch (value){
+ case HttpResponseHeader.CacheControl:
+ return "cache-control";
+ case HttpResponseHeader.Connection:
+ return "connection";
+ case HttpResponseHeader.Date:
+ return "date";
+ case HttpResponseHeader.KeepAlive:
+ return "keep-alive";
+ case HttpResponseHeader.Pragma:
+ return "pragma";
+ case HttpResponseHeader.Trailer:
+ return "trailer";
+ case HttpResponseHeader.TransferEncoding:
+ return "transfer-encoding";
+ case HttpResponseHeader.Upgrade:
+ return "upgrade";
+ case HttpResponseHeader.Via:
+ return "via";
+ case HttpResponseHeader.Warning:
+ return "warning";
+ case HttpResponseHeader.Allow:
+ return "allow";
+ case HttpResponseHeader.ContentLength:
+ return "content-length";
+ case HttpResponseHeader.ContentType:
+ return "content-type";
+ case HttpResponseHeader.ContentEncoding:
+ return "content-encoding";
+ case HttpResponseHeader.ContentLanguage:
+ return "content-language";
+ case HttpResponseHeader.ContentLocation:
+ return "content-location";
+ case HttpResponseHeader.ContentMd5:
+ return "content-md5";
+ case HttpResponseHeader.ContentRange:
+ return "content-range";
+ case HttpResponseHeader.Expires:
+ return "expires";
+ case HttpResponseHeader.LastModified:
+ return "last-modified";
+ case HttpResponseHeader.AcceptRanges:
+ return "accept-ranges";
+ case HttpResponseHeader.Age:
+ return "age";
+ case HttpResponseHeader.ETag:
+ return "etag";
+ case HttpResponseHeader.Location:
+ return "location";
+ case HttpResponseHeader.ProxyAuthenticate:
+ return "proxy-authenticate";
+ case HttpResponseHeader.RetryAfter:
+ return "RetryAfter";
+ case HttpResponseHeader.Server:
+ return "server";
+ case HttpResponseHeader.SetCookie:
+ return "set-cookie";
+ case HttpResponseHeader.Vary:
+ return "vary";
+ case HttpResponseHeader.WwwAuthenticate:
+ return "www-authenticate";
+ default:
+ throw new InvalidOperationException ();
+ }
+ }
+ public string this[HttpResponseHeader hrh]
+ {
+ get
+ {
+ return Get (ResponseHeaderToString (hrh));
+ }
+
+ set
+ {
+ Add (ResponseHeaderToString (hrh), value);
+ }
+ }
+
+
+#endif
+#if EMBEDDED_IN_1_0 && !EMBEDDED_IN_1_0
+ public override void Clear ()
+ {
+ base.Clear ();
+ }
+
+
+ public override IEnumerator GetEnumerator ()
+ {
+ return(base.GetEnumerator ());
+ }
+#endif
+
+ // Internal Methods
+
+ // With this we don't check for invalid characters in header. See bug #55994.
+ internal void SetInternal (string header)
+ {
+ int pos = header.IndexOf (':');
+ if (pos == -1)
+ throw new ArgumentException ("no colon found", "header");
+
+ SetInternal (header.Substring (0, pos), header.Substring (pos + 1));
+ }
+
+ internal void SetInternal (string name, string value)
+ {
+ if (value == null)
+ value = String.Empty;
+ else
+ value = value.Trim ();
+ if (!IsHeaderValue (value))
+ throw new ArgumentException ("invalid header value");
+
+ if (IsMultiValue (name)) {
+ base.Add (name, value);
+ } else {
+ base.Remove (name);
+ base.Set (name, value);
+ }
+ }
+
+ internal void RemoveAndAdd (string name, string value)
+ {
+ if (value == null)
+ value = String.Empty;
+ else
+ value = value.Trim ();
+
+ base.Remove (name);
+ base.Set (name, value);
+ }
+
+ internal void RemoveInternal (string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException ("name");
+ base.Remove (name);
+ }
+
+ // Private Methods
+
+ internal static bool IsMultiValue (string headerName)
+ {
+ if (headerName == null || headerName == "")
+ return false;
+
+ return multiValue.ContainsKey (headerName);
+ }
+
+ internal static bool IsHeaderValue (string value)
+ {
+ // TEXT any 8 bit value except CTL's (0-31 and 127)
+ // but including \r\n space and \t
+ // after a newline at least one space or \t must follow
+ // certain header fields allow comments ()
+
+ int len = value.Length;
+ for (int i = 0; i < len; i++) {
+ char c = value [i];
+ if (c == 127)
+ return false;
+ if (c < 0x20 && (c != '\r' && c != '\n' && c != '\t'))
+ return false;
+ if (c == '\n' && ++i < len) {
+ c = value [i];
+ if (c != ' ' && c != '\t')
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ internal static bool IsHeaderName (string name)
+ {
+ // token = 1*<any CHAR except CTLs or tspecials>
+ // tspecials = "(" | ")" | "<" | ">" | "@"
+ // | "," | ";" | ":" | "\" | <">
+ // | "/" | "[" | "]" | "?" | "="
+ // | "{" | "}" | SP | HT
+
+ if (name == null || name.Length == 0)
+ return false;
+
+ int len = name.Length;
+ for (int i = 0; i < len; i++) {
+ char c = name [i];
+ if (c < 0x20 || c >= 0x7f)
+ return false;
+ }
+
+ return name.IndexOfAny (tspecials) == -1;
+ }
+
+ private static char [] tspecials =
+ new char [] {'(', ')', '<', '>', '@',
+ ',', ';', ':', '\\', '"',
+ '/', '[', ']', '?', '=',
+ '{', '}', ' ', '\t'};
+
+ }
+}
+
+
diff --git a/Tomboy/Addins/WebSyncService/WebSyncPreferencesWidget.cs b/Tomboy/Addins/WebSyncService/WebSyncPreferencesWidget.cs
index a99c2cc..79b2a78 100644
--- a/Tomboy/Addins/WebSyncService/WebSyncPreferencesWidget.cs
+++ b/Tomboy/Addins/WebSyncService/WebSyncPreferencesWidget.cs
@@ -24,7 +24,6 @@
//
using System;
-using System.Net;
using System.Web;
using Mono.Unix;
@@ -37,7 +36,7 @@ namespace Tomboy.WebSync
private Gtk.Entry serverEntry;
private Gtk.Button authButton;
private Api.OAuth oauth;
- private HttpListener listener;
+ private MonoHttp.HttpListener listener;
private const string callbackHtmlTemplate =
@"<html><head><title>{0}</title></head><body><div><h1>{0}</h1>{1}</div></body></html>";
@@ -123,7 +122,7 @@ namespace Tomboy.WebSync
}
if (!Auth.IsAccessToken) {
- listener = new HttpListener ();
+ listener = new MonoHttp.HttpListener ();
int portToTry = 8000;
string callbackUrl = string.Empty;
while (!listener.IsListening && portToTry < 9000) {
diff --git a/Tomboy/Addins/WebSyncService/WebSyncService.csproj b/Tomboy/Addins/WebSyncService/WebSyncService.csproj
index a04aaf8..e629a96 100644
--- a/Tomboy/Addins/WebSyncService/WebSyncService.csproj
+++ b/Tomboy/Addins/WebSyncService/WebSyncService.csproj
@@ -87,6 +87,30 @@
<Compile Include="Hyena.Json\Token.cs" />
<Compile Include="Hyena.Json\Tokenizer.cs" />
<Compile Include="Hyena.Json\TokenType.cs" />
+ <Compile Include="MonoHttp\AuthenticationSchemes.cs" />
+ <Compile Include="MonoHttp\AuthenticationSchemeSelector.cs" />
+ <Compile Include="MonoHttp\ChunkedInputStream.cs" />
+ <Compile Include="MonoHttp\ChunkStream.cs" />
+ <Compile Include="MonoHttp\EndPointListener.cs" />
+ <Compile Include="MonoHttp\EndPointManager.cs" />
+ <Compile Include="MonoHttp\HttpConnection.cs" />
+ <Compile Include="MonoHttp\HttpListener.cs" />
+ <Compile Include="MonoHttp\HttpListenerBasicIdentity.cs" />
+ <Compile Include="MonoHttp\HttpListenerContext.cs" />
+ <Compile Include="MonoHttp\HttpListenerException.cs" />
+ <Compile Include="MonoHttp\HttpListenerPrefixCollection.cs" />
+ <Compile Include="MonoHttp\HttpListenerRequest.cs" />
+ <Compile Include="MonoHttp\HttpListenerResponse.cs" />
+ <Compile Include="MonoHttp\HttpRequestHeader.cs" />
+ <Compile Include="MonoHttp\HttpResponseHeader.cs" />
+ <Compile Include="MonoHttp\HttpStreamAsyncResult.cs" />
+ <Compile Include="MonoHttp\HttpUtility.cs" />
+ <Compile Include="MonoHttp\ListenerAsyncResult.cs" />
+ <Compile Include="MonoHttp\ListenerPrefix.cs" />
+ <Compile Include="MonoHttp\RequestStream.cs" />
+ <Compile Include="MonoHttp\ResponseStream.cs" />
+ <Compile Include="MonoHttp\Utility.cs" />
+ <Compile Include="MonoHttp\WebHeaderCollection.cs" />
<Compile Include="OAuth\Base.cs" />
<Compile Include="OAuth\Enums.cs" />
<Compile Include="OAuth\Extensions.cs" />
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]