mirror of
https://github.com/aluxnimm/outlookcaldavsynchronizer.git
synced 2025-10-05 16:02:49 +02:00
Optimize vCardStandardReader/Writer and use LINQ.
Filter out invalid xml characters in EncodeEscaped.
This commit is contained in:
@@ -47,25 +47,31 @@ namespace CalDavSynchronizer.Implementation.DistributionLists.Sogo
|
||||
|
||||
protected override string Serialize(DistributionList vcard)
|
||||
{
|
||||
char[] escapechars = {',', '\\', ';', '\r', '\n'};
|
||||
|
||||
Dictionary<string, string> escapeTokens = new Dictionary<string, string>
|
||||
{ {@"\", @"\\"},
|
||||
{"\n", @"\n"},
|
||||
{"\r", @"\r"},
|
||||
{",", @"\,"},
|
||||
{";", @"\;"}
|
||||
};
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine("BEGIN:VLIST");
|
||||
builder.AppendLine("VERSION:1.0");
|
||||
builder.Append("UID:");
|
||||
builder.AppendLine(vcard.Uid);
|
||||
builder.Append("FN:");
|
||||
builder.AppendLine(vCardStandardWriter.EncodeEscaped(vcard.Name, escapechars));
|
||||
builder.AppendLine(vCardStandardWriter.EncodeEscaped(vcard.Name, escapeTokens));
|
||||
if (!string.IsNullOrEmpty(vcard.Description))
|
||||
{
|
||||
builder.Append("DESCRIPTION:");
|
||||
builder.AppendLine(vCardStandardWriter.EncodeEscaped(vcard.Description.Replace("\r\n", "\n"), escapechars));
|
||||
builder.AppendLine(vCardStandardWriter.EncodeEscaped(vcard.Description.Replace("\r\n", "\n"), escapeTokens));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(vcard.Nickname))
|
||||
{
|
||||
builder.Append("NICKNAME:");
|
||||
builder.AppendLine(vCardStandardWriter.EncodeEscaped(vcard.Nickname, escapechars));
|
||||
builder.AppendLine(vCardStandardWriter.EncodeEscaped(vcard.Nickname, escapeTokens));
|
||||
}
|
||||
|
||||
foreach (var member in vcard.Members)
|
||||
@@ -73,7 +79,7 @@ namespace CalDavSynchronizer.Implementation.DistributionLists.Sogo
|
||||
builder.Append("CARD;EMAIL=");
|
||||
builder.Append(member.EmailAddress);
|
||||
builder.Append(";FN=");
|
||||
builder.Append(vCardStandardWriter.EncodeEscaped(member.DisplayName, escapechars));
|
||||
builder.Append(vCardStandardWriter.EncodeEscaped(member.DisplayName, escapeTokens));
|
||||
builder.Append(":");
|
||||
builder.AppendLine(member.ServerFileName);
|
||||
}
|
||||
@@ -81,7 +87,7 @@ namespace CalDavSynchronizer.Implementation.DistributionLists.Sogo
|
||||
foreach (var member in vcard.NonAddressBookMembers)
|
||||
{
|
||||
builder.Append(NonAddressBookMemberValueName + ";CN=");
|
||||
builder.Append(vCardStandardWriter.EncodeEscaped(member.DisplayName, escapechars));
|
||||
builder.Append(vCardStandardWriter.EncodeEscaped(member.DisplayName, escapeTokens));
|
||||
builder.Append(":mailto:");
|
||||
builder.AppendLine(member.EmailAddress);
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -254,83 +255,17 @@ namespace Thought.vCards
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return value;
|
||||
|
||||
StringBuilder builder = new StringBuilder(value.Length);
|
||||
|
||||
int startIndex = 0;
|
||||
|
||||
do
|
||||
Dictionary<string, string> standardEspaceTokens = new Dictionary<string, string>
|
||||
{
|
||||
// Get the index of the next backslash character.
|
||||
// This marks the beginning of an escape sequence.
|
||||
|
||||
int nextIndex = value.IndexOf('\\', startIndex);
|
||||
|
||||
if ((nextIndex == -1) || (nextIndex == value.Length - 1))
|
||||
{
|
||||
// There are no more escape codes, or the backslash
|
||||
// is located at the very end of the string. The
|
||||
// characters between the index and the end of the
|
||||
// string need to be copied to the output buffer.
|
||||
|
||||
builder.Append(
|
||||
value,
|
||||
startIndex,
|
||||
value.Length - startIndex);
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// A backslash was located somewhere in the string.
|
||||
// The previous statement ensured the backslash is
|
||||
// not the very last character, and therefore the
|
||||
// following statement is safe.
|
||||
|
||||
char code = value[nextIndex + 1];
|
||||
|
||||
// Any characters between the starting point and
|
||||
// the index must be pushed into the buffer.
|
||||
|
||||
builder.Append(
|
||||
value,
|
||||
startIndex,
|
||||
nextIndex - startIndex);
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case '\\':
|
||||
case ',':
|
||||
case ';':
|
||||
case ':':
|
||||
|
||||
builder.Append(code);
|
||||
nextIndex += 2;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
case 'N':
|
||||
builder.Append('\n');
|
||||
nextIndex += 2;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
case 'R':
|
||||
builder.Append('\r');
|
||||
nextIndex += 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
builder.Append('\\');
|
||||
builder.Append(code);
|
||||
nextIndex += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
startIndex = nextIndex;
|
||||
} while (startIndex < value.Length);
|
||||
|
||||
return builder.ToString();
|
||||
{@"\\", @"\"},
|
||||
{@"\N", "\n"},
|
||||
{@"\R", "\r"},
|
||||
{@"\n", "\n"},
|
||||
{@"\r", "\r"},
|
||||
{@"\,",","},
|
||||
{@"\;", ";"}
|
||||
};
|
||||
return standardEspaceTokens.Aggregate(value, (current, token) => current.Replace(token.Key, token.Value));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@@ -5,9 +5,12 @@
|
||||
* ======================================================================= */
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using System.Xml;
|
||||
|
||||
namespace Thought.vCards
|
||||
{
|
||||
/// <summary>
|
||||
@@ -22,25 +25,33 @@ namespace Thought.vCards
|
||||
private string productId;
|
||||
private const string TYPE = "TYPE";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The characters that are escaped per the original
|
||||
/// vCard specification.
|
||||
/// </summary>
|
||||
private readonly char[] standardEscapedCharacters =
|
||||
new char[] {',', '\\', ';', '\r', '\n'};
|
||||
|
||||
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, string> standardEspaceTokens = new Dictionary<string, string>
|
||||
{
|
||||
{@"\", @"\\"},
|
||||
{"\n", @"\n"},
|
||||
{"\r", @"\r"},
|
||||
{",", @"\,"},
|
||||
{";", @"\;"}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The characters that are escaped by Microsoft Outlook.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Microsoft Outlook does not property decode escaped
|
||||
/// commas in values.
|
||||
/// </remarks>
|
||||
private readonly char[] outlookEscapedCharacters =
|
||||
new char[] {'\\', ';', '\r', '\n'};
|
||||
|
||||
/// </remarks>
|
||||
private readonly Dictionary<string, string> outlookEspaceTokens = new Dictionary<string, string>
|
||||
{
|
||||
{@"\", @"\\"},
|
||||
{"\n", @"\n"},
|
||||
{"\r", @"\r"},
|
||||
{";", @"\;"}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the standard writer.
|
||||
@@ -1507,95 +1518,55 @@ namespace Thought.vCards
|
||||
(this.options & vCardStandardWriterOptions.IgnoreCommas) ==
|
||||
vCardStandardWriterOptions.IgnoreCommas)
|
||||
{
|
||||
return EncodeEscaped(value, outlookEscapedCharacters);
|
||||
return EncodeEscaped(value, outlookEspaceTokens);
|
||||
}
|
||||
else
|
||||
{
|
||||
return EncodeEscaped(value, standardEscapedCharacters);
|
||||
return EncodeEscaped(value, standardEspaceTokens);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region [ EncodeEscaped(string, char[]) ]
|
||||
#region [ EncodeEscaped(string, Dictionary<string,string>) ]
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a character array using simple escape sequences.
|
||||
/// </summary>
|
||||
public static string EncodeEscaped(
|
||||
string value,
|
||||
char[] escaped)
|
||||
public static string EncodeEscaped(string value, Dictionary<string,string> escaped)
|
||||
{
|
||||
if (escaped == null)
|
||||
throw new ArgumentNullException("escaped");
|
||||
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return value;
|
||||
return value;
|
||||
|
||||
string escapedValue = escaped.Aggregate(value, (current, token) => current.Replace(token.Key, token.Value));
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
int startIndex = 0;
|
||||
|
||||
do
|
||||
{
|
||||
// Get the index of the next character
|
||||
// to be escaped (e.g. the next semicolon).
|
||||
|
||||
int nextIndex = value.IndexOfAny(escaped, startIndex);
|
||||
|
||||
if (nextIndex == -1)
|
||||
{
|
||||
// No more characters need to be escaped.
|
||||
// Any characters between the start index
|
||||
// and the end of the string can be copied
|
||||
// to the buffer.
|
||||
|
||||
buffer.Append(
|
||||
value,
|
||||
startIndex,
|
||||
value.Length - startIndex);
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
char replacement;
|
||||
switch (value[nextIndex])
|
||||
{
|
||||
case '\n':
|
||||
replacement = 'n';
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
replacement = 'r';
|
||||
break;
|
||||
|
||||
default:
|
||||
replacement = value[nextIndex];
|
||||
break;
|
||||
}
|
||||
|
||||
buffer.Append(
|
||||
value,
|
||||
startIndex,
|
||||
nextIndex - startIndex);
|
||||
|
||||
buffer.Append('\\');
|
||||
buffer.Append(replacement);
|
||||
|
||||
startIndex = nextIndex + 1;
|
||||
}
|
||||
} while (startIndex < value.Length);
|
||||
|
||||
return buffer.ToString();
|
||||
|
||||
// The following must be encoded:
|
||||
//
|
||||
// Backslash (\\)
|
||||
// Colon (\:)
|
||||
// Semicolon (\;)
|
||||
// filter out invalid xml characters
|
||||
const char _replacementCharacter = '\uFFFD';
|
||||
int length = escapedValue.Length;
|
||||
StringBuilder stringBuilder = new StringBuilder(length);
|
||||
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
if (XmlConvert.IsXmlChar(escapedValue[i]))
|
||||
{
|
||||
stringBuilder.Append(escapedValue[i]);
|
||||
}
|
||||
else if (i + 1 < length && XmlConvert.IsXmlSurrogatePair(escapedValue[i + 1], escapedValue[i]))
|
||||
{
|
||||
stringBuilder.Append(escapedValue[i]);
|
||||
stringBuilder.Append(escapedValue[i + 1]);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append(_replacementCharacter);
|
||||
}
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region [ EncodeQuotedPrintable ]
|
||||
|
Reference in New Issue
Block a user