diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..7481ade57b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,201 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = tab
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = true
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = true
+dotnet_sort_system_directives_first = true
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false:silent
+dotnet_style_qualification_for_field = false:silent
+dotnet_style_qualification_for_method = false:silent
+dotnet_style_qualification_for_property = false:silent
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true:silent
+dotnet_style_predefined_type_for_member_access = true:silent
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true:silent
+dotnet_style_prefer_compound_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:silent
+dotnet_style_prefer_conditional_expression_over_return = true:silent
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+
+# Field preferences
+dotnet_style_readonly_field = true:suggestion
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:suggestion
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false:silent
+csharp_style_var_for_built_in_types = false:silent
+csharp_style_var_when_type_is_apparent = false:silent
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_prefer_switch_expression = true:suggestion
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Modifier preferences
+csharp_prefer_static_local_function = true:suggestion
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_pattern_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+csharp_style_unused_value_expression_statement_preference = discard_variable:silent
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = false
+csharp_new_line_before_else = false
+csharp_new_line_before_finally = false
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = methods,types
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = false
+
+# Space preferences
+csharp_space_after_cast = true
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = true
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = true
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000..72ffe3f6df
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+custom: ['https://paypal.me/pools/c/857bnxBTXg']
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000000..d94a74e5f4
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,30 @@
+---
+name: Bug report
+about: Create a report to help us improve
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**Platform (please complete the following information):**
+ - OS: [e.g. Windows, Linux, MacOS, iOS, Android, Windows Phone, etc.]
+ - .NET Runtime: [e.g. CoreCLR, Mono]
+ - .NET Framework: [e.g. .Net Core, .NET 4.5, UWP, etc.]
+ - MimeKit Version:
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000000..066b2d920a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,17 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.gitignore b/.gitignore
index e45da4800f..89cd9f284c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
+MimeKit/Resources/Resource.designer.cs
Mono.Data.Sqlite/Documentation
+project.nuget.cache
*project.lock.json
*.csproj.nuget.cache
+*.csproj.nuget.dgspec.json
*.userprefs
*.user
*.suo
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 8ac64ba8e6..0000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,7 +0,0 @@
-[submodule "submodules/Portable.Text.Encoding"]
- path = submodules/Portable.Text.Encoding
- url = https://github.com/jstedfast/Portable.Text.Encoding.git
-[submodule "submodules/bc-csharp"]
- path = submodules/bc-csharp
- url = https://github.com/jstedfast/bc-csharp.git
- branch = build-fix
diff --git a/.nuget/packages.config b/.nuget/packages.config
deleted file mode 100644
index eb80f3b1ce..0000000000
--- a/.nuget/packages.config
+++ /dev/null
@@ -1,6 +0,0 @@
-
- Hey Alice, Hey Alice, What are you up to this weekend? Monica is throwing one of her parties on
Saturday and I was hoping you could make it. Will you be my +1? -- Joey
foreach (var mailbox in message.To.Mailboxes)
diff --git a/Documentation/Content/GettingStarted.aml b/Documentation/Content/GettingStarted.aml
index 85e326b79f..0b1e0491a4 100644
--- a/Documentation/Content/GettingStarted.aml
+++ b/Documentation/Content/GettingStarted.aml
@@ -1,4 +1,4 @@
-
+
+ // In order to reference selfie.jpg from the html text, we'll need to add it
+ // to builder.LinkedResources and then use its Content-Id value in the img src.
+ var image = builder.LinkedResources.Add (@"C:\Users\Joey\Documents\Selfies\selfie.jpg");
+ image.ContentId = MimeUtils.GenerateMessageId ();
+
+ // Set the html version of the message text
+ builder.HtmlBody = string.Format (@"
-
+ htmlWriter.WriteEndTag (HtmlTagId.BlockQuote);
+
+ // pass the
+ htmlWriter.WriteStartTag (HtmlTagId.BlockQuote); + htmlWriter.WriteAttribute (HtmlAttributeId.Style, "border-left: 1px #ccc solid; margin: 0 0 0 .8ex; padding-left: 1ex;"); + + ctx.InvokeCallbackForEndTag = true; + } + } else { + // pass the tag through to the output + ctx.WriteTag (htmlWriter, true); + } + } + + string QuoteText (string text) + { + using (var quoted = new StringWriter ()) { + quoted.WriteLine (GetOnDateSenderWrote (message)); + + using (var reader = new StringReader (text)) { + string line; + + while ((line = reader.ReadLine ()) != null) { + quoted.Write ("> "); + quoted.WriteLine (line); + } + } + + return quoted.ToString (); + } + } + + protected override void VisitTextPart (TextPart entity) + { + string text; + + if (entity.IsHtml) { + var converter = new HtmlToHtml { + HtmlTagCallback = HtmlTagCallback + }; + + text = converter.Convert (entity.Text); + } else if (entity.IsFlowed) { + var converter = new FlowedToText (); + + text = converter.Convert (entity.Text); + text = QuoteText (text); + } else { + // quote the original message text + text = QuoteText (entity.Text); + } + + var part = new TextPart (entity.ContentType.MediaSubtype.ToLowerInvariant ()) { + Text = text + }; + + Push (part); + } + + protected override void VisitMessagePart (MessagePart entity) + { + // don't descend into message/rfc822 parts + } + } + #endregion + + public class Program + { + #region RenderMessage + void Render (MimeMessage message) + { + var tmpDir = Path.Combine (Path.GetTempPath (), message.MessageId); + var visitor = new HtmlPreviewVisitor (tmpDir); + + Directory.CreateDirectory (tmpDir); + + message.Accept (visitor); + + DisplayHtml (visitor.HtmlBody); + DisplayAttachments (visitor.Attachments); + } + #endregion + + #region ReplySimple + public static MimeMessage Reply (MimeMessage message, MailboxAddress from, bool replyToAll) + { + var reply = new MimeMessage (); + + reply.From.Add (from); + + // reply to the sender of the message + if (message.ReplyTo.Count > 0) { + reply.To.AddRange (message.ReplyTo); + } else if (message.From.Count > 0) { + reply.To.AddRange (message.From); + } else if (message.Sender != null) { + reply.To.Add (message.Sender); + } + + if (replyToAll) { + // include all of the other original recipients (removing ourselves from the list) + reply.To.AddRange (message.To.Mailboxes.Where (x => x.Address != from.Address)); + reply.Cc.AddRange (message.Cc.Mailboxes.Where (x => x.Address != from.Address)); + } + + // set the reply subject + if (!message.Subject.StartsWith ("Re:", StringComparison.OrdinalIgnoreCase)) + reply.Subject = "Re: " + message.Subject; + else + reply.Subject = message.Subject; + + // construct the In-Reply-To and References headers + if (!string.IsNullOrEmpty (message.MessageId)) { + reply.InReplyTo = message.MessageId; + foreach (var id in message.References) + reply.References.Add (id); + reply.References.Add (message.MessageId); + } + + // quote the original message text + using (var quoted = new StringWriter ()) { + var sender = message.Sender ?? message.From.Mailboxes.FirstOrDefault (); + var name = sender != null ? (!string.IsNullOrEmpty (sender.Name) ? sender.Name : sender.Address) : "someone"; + + quoted.WriteLine ("On {0}, {1} wrote:", message.Date.ToString ("f"), name); + using (var reader = new StringReader (message.TextBody)) { + string line; + + while ((line = reader.ReadLine ()) != null) { + quoted.Write ("> "); + quoted.WriteLine (line); + } + } + + reply.Body = new TextPart ("plain") { + Text = quoted.ToString () + }; + } + + return reply; + } + #endregion + + #region Reply + public static MimeMessage Reply (MimeMessage message, MailboxAddress from, bool replyToAll) + { + var visitor = new ReplyVisitor (); + var reply = new MimeMessage (); + + reply.From.Add (from); + + // reply to the sender of the message + if (message.ReplyTo.Count > 0) { + reply.To.AddRange (message.ReplyTo); + } else if (message.From.Count > 0) { + reply.To.AddRange (message.From); + } else if (message.Sender != null) { + reply.To.Add (message.Sender); + } + + if (replyToAll) { + // include all of the other original recipients (removing ourselves from the list) + reply.To.AddRange (message.To.Mailboxes.Where (x => x.Address != from.Address)); + reply.Cc.AddRange (message.Cc.Mailboxes.Where (x => x.Address != from.Address)); + } + + // set the reply subject + if (!message.Subject.StartsWith ("Re:", StringComparison.OrdinalIgnoreCase)) + reply.Subject = "Re: " + message.Subject; + else + reply.Subject = message.Subject; + + // construct the In-Reply-To and References headers + if (!string.IsNullOrEmpty (message.MessageId)) { + reply.InReplyTo = message.MessageId; + foreach (var id in message.References) + reply.References.Add (id); + reply.References.Add (message.MessageId); + } + + visitor.Visit (message); + + reply.Body = visitor.Body ?? new TextPart ("plain") { Text = ReplyVisitor.GetOnDateSenderWrote (message) + Environment.NewLine }; + + return reply; + } + #endregion + } +} diff --git a/Documentation/Examples/MultipartFormDataExample.cs b/Documentation/Examples/MultipartFormDataExample.cs index 722f490e73..157af96124 100644 --- a/Documentation/Examples/MultipartFormDataExample.cs +++ b/Documentation/Examples/MultipartFormDataExample.cs @@ -11,7 +11,7 @@ MimeEntity ParseMultipartFormData (HttpWebResponse response) { var contentType = ContentType.Parse (response.ContentType); - return MimeEntity.Parse (contentType, response.GetResponseStream ()); + return MimeEntity.Load (contentType, response.GetResponseStream ()); } #endregion diff --git a/Documentation/Examples/OpenPGPExamples.cs b/Documentation/Examples/OpenPGPExamples.cs index de4b40589b..6007cce257 100644 --- a/Documentation/Examples/OpenPGPExamples.cs +++ b/Documentation/Examples/OpenPGPExamples.cs @@ -30,7 +30,7 @@ public void RegisterMyGnuPGeContext () #region RegisterCustomContext // Note: by registering our custom context it becomes the default OpenPGP context // instantiated by MimeKit when methods such as Encrypt(), Decrypt(), Sign(), and - // Verify() are used without an expliit context. + // Verify() are used without an explicit context. CryptographyContext.Register (typeof (MyGnuPGContext)); #endregion } diff --git a/Documentation/Examples/SMimeExamples.cs b/Documentation/Examples/SMimeExamples.cs index ac6431b310..fa5de663e7 100644 --- a/Documentation/Examples/SMimeExamples.cs +++ b/Documentation/Examples/SMimeExamples.cs @@ -39,7 +39,7 @@ public void RegisterMySecureMimeContext () #region RegisterCustomContext // Note: by registering our custom context it becomes the default S/MIME context // instantiated by MimeKit when methods such as Encrypt(), Decrypt(), Sign(), and - // Verify() are used without an expliit context. + // Verify() are used without an explicit context. CryptographyContext.Register (typeof (MySecureMimeContext)); #endregion } diff --git a/FAQ.md b/FAQ.md index 613774e514..8585343797 100644 --- a/FAQ.md +++ b/FAQ.md @@ -42,7 +42,7 @@ container which you'll then want to add the message body to first. Once you've a then add MIME parts to it that contain the content of the files you'd like to attach, being sure to set the `Content-Disposition` header value to attachment. You'll probably also want to set the `filename` parameter on the `Content-Disposition` header as well as the `name` parameter on the `Content-Type` -header. The most convenient way to do this is to simply use the +header. The most convenient way to do this is to use the [MimePart.FileName](http://www.mimekit.net/docs/html/P_MimeKit_MimePart_FileName.htm) property which will set both parameters for you as well as setting the `Content-Disposition` header value to `attachment` if it has not already been set to something else. @@ -113,7 +113,7 @@ builder.Attachments.Add (@"C:\Users\Joey\Documents\party.ics"); message.Body = builder.ToMessageBody (); ``` -For more information, see [Creating Messages](http://www.mimekit.net/docs/html/CreatingMessages.htm). +For more information, see [Creating Messages](http://www.mimekit.net/docs/html/Creating-Messages.htm). ### Q: How do I get the main body of a message? @@ -190,7 +190,7 @@ for this: [TextBody](http://www.mimekit.net/docs/html/P_MimeKit_MimeMessage_Text appropriate body part with a `Content-Type` of `text/html` that can be interpreted as the message body. Likewise, the `TextBody` property can be used to get the `text/plain` version of the message body. -For more information, see [Working with Messages](http://www.mimekit.net/docs/html/WorkingWithMessages.htm). +For more information, see [Working with Messages](http://www.mimekit.net/docs/html/Working-With-Messages.htm). ### Q: How do I tell if a message has attachments? @@ -302,11 +302,17 @@ class HtmlPreviewVisitor : MimeVisitor return false; } - // Save the image to our temp directory and return a "file://" url suitable for - // the browser control to load. - // Note: if you'd rather embed the image data into the HTML, you can construct a - // "data:" url instead. - string SaveImage (MimePart image, string url) + ///+ /// Get a file:// URI for the image attachment. + /// + ///+ /// Saves the image attachment to a temp file and returns a file:// URI for the + /// temp file. + /// + ///The file:// URI. + /// The image attachment. + /// The original HTML image URL. + string GetFileUri (MimePart image, string url) { string fileName = url.Replace (':', '_').Replace ('\\', '_').Replace ('/', '_'); @@ -320,6 +326,28 @@ class HtmlPreviewVisitor : MimeVisitor return "file://" + path.Replace ('\\', '/'); } + ///+ /// Get a file:// URI for the image attachment. + /// + ///+ /// Saves the image attachment to a temp file and returns a file:// URI for the + /// temp file. + /// + ///The file:// URI. + /// The image attachment. + /// The original HTML image URL. + string GetDataUri (MimePart image) + { + using (var memory = new MemoryStream ()) { + image.Content.DecodeTo (memory); + var buffer = memory.GetBuffer (); + var length = (int) memory.Length; + var base64 = Convert.ToBase64String (buffer, 0, length); + + return string.Format ("data:{0};base64,{1}", image.ContentType.MimeType, base64); + } + } + // Replaces urls that refer to images embedded within the message with // "file://" urls that the browser control will actually be able to load. void HtmlTagCallback (HtmlTagContext ctx, HtmlWriter htmlWriter) @@ -338,7 +366,10 @@ class HtmlPreviewVisitor : MimeVisitor continue; } - url = SaveImage (image, attribute.Value); + // Note: you can either use a "file://" URI or you can use a + // "data:" URI, the choice is yours. + url = GetFileUri (image, attribute.Value); + //uri = GetDataUri (image); htmlWriter.WriteAttributeName (attribute.Name); htmlWriter.WriteAttributeValue (url); @@ -614,7 +645,7 @@ To: John Smith``` If you only care about getting a flattened list of the mailbox addresses in a `From`, `To`, or `Cc` -header, you can simply do something like this: +header, you can do something like this: ```csharp foreach (var mailbox in message.To.Mailboxes) @@ -649,13 +680,12 @@ if (attachment.ContentDisposition.Parameters.TryGetValue ("filename", out param) param.EncodingMethod = ParameterEncodingMethod.Rfc2047; ``` -The other way is to use a [FormatOptions](http://www.mimekit.net/docs/html/T_MimeKit_FormatOptions.htm): +Or ```csharp -var options = FormatOptions.Default.Clone (); -options.ParameterEncodingMethod = ParameterEncodingMethod.Rfc2047; - -message.WriteTo (options, stream); +foreach (var param in attachment.ContentDisposition.Parameters) { + param.EncodingMethod = ParameterEncodingMethod.Rfc2047; +} ``` ### Q: How do I decrypt PGP messages that are embedded in the main message text? @@ -1102,7 +1132,7 @@ public static MimeMessage Forward (MimeMessage original, MailboxAddress from, IE } ``` -To forward a message by simply inlining the original message's text content, you can do something like this: +To forward a message by inlining the original message's text content, you can do something like this: ```csharp public static MimeMessage Forward (MimeMessage original, MailboxAddress from, IEnumerable to) @@ -1162,6 +1192,6 @@ MimeEntity ParseMultipartFormData (HttpWebResponse response) { var contentType = ContentType.Parse (response.ContentType); - return MimeEntity.Parse (contentType, response.GetResponseStream ()); + return MimeEntity.Load (contentType, response.GetResponseStream ()); } ``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..7614a032f3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2012-2020 .NET Foundation and Contributors + +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. \ No newline at end of file diff --git a/License.md b/License.md deleted file mode 100644 index 5810935a00..0000000000 --- a/License.md +++ /dev/null @@ -1,21 +0,0 @@ -## License Information - -MimeKit is Copyright (C) 2012-2016 Xamarin Inc. and is licensed under the MIT license: - - 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. diff --git a/Makefile b/Makefile deleted file mode 100644 index 28679160d1..0000000000 --- a/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -OUTDIR=MimeKit/bin/Release/lib/net40 -ASSEMBLY=$(OUTDIR)/MimeKit.dll -XMLDOCS=$(OUTDIR)/MimeKit.xml - -all: - xbuild /target:Build /p:Configuration=Release MimeKit.Net40.sln - -debug: - xbuild /target:Build /p:Configuration=Debug MimeKit.Net40.sln - -clean: - xbuild /target:Clean /p:Configuration=Debug MimeKit.Net40.sln - xbuild /target:Clean /p:Configuration=Release MimeKit.Net40.sln - -check-docs: - @find docs/en -name "*.xml" -exec grep -l "To be added." {} \; - -update-docs: $(ASSEMBLY) - mdoc update --delete -o docs/en $(ASSEMBLY) - -merge-docs: $(ASSEMBLY) $(XMLDOCS) - mdoc update -i $(XMLDOCS) -o docs/en $(ASSEMBLY) - -html-docs: - mdoc export-html --force-update --template=docs/github-pages.xslt -o ../MimeKit-docs/docs docs/en diff --git a/MimeKit.Coverity.sln b/MimeKit.Coverity.sln index 226b4ce9db..891c66a136 100644 --- a/MimeKit.Coverity.sln +++ b/MimeKit.Coverity.sln @@ -1,9 +1,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.12 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30711.63 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit.Net45", "MimeKit\MimeKit.Net45.csproj", "{D5F54A4F-D84B-430F-9271-F7861E285B3E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MimeKit", "MimeKit\MimeKit.csproj", "{29F68E0E-0119-45CC-B6B4-A0C70FADA4AD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,14 +11,17 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Release|Any CPU.Build.0 = Release|Any CPU + {29F68E0E-0119-45CC-B6B4-A0C70FADA4AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29F68E0E-0119-45CC-B6B4-A0C70FADA4AD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29F68E0E-0119-45CC-B6B4-A0C70FADA4AD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29F68E0E-0119-45CC-B6B4-A0C70FADA4AD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FE861017-7B1A-4D73-AAEC-0DEBA21F6C52} + EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 $0.StandardHeader = $1 diff --git a/MimeKit.Documentation.sln b/MimeKit.Documentation.sln index 3d20677c84..fc9df2585c 100644 --- a/MimeKit.Documentation.sln +++ b/MimeKit.Documentation.sln @@ -1,31 +1,31 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30204.135 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit.Net45", "MimeKit\MimeKit.Net45.csproj", "{D5F54A4F-D84B-430F-9271-F7861E285B3E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BouncyCastle", "submodules\bc-csharp\crypto\BouncyCastle.csproj", "{4C235092-820C-4DEB-9074-D356FB797D8B}" -EndProject Project("{7CF6DF6D-3B04-46F8-A40B-537D21BCA0B4}") = "Documentation", "Documentation\Documentation.shfbproj", "{59115814-A1E3-46AE-AE30-4065AE8F4CAF}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MimeKit", "MimeKit\MimeKit.csproj", "{76894ADA-0818-4556-83BD-6510D8EA2809}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4C235092-820C-4DEB-9074-D356FB797D8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C235092-820C-4DEB-9074-D356FB797D8B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C235092-820C-4DEB-9074-D356FB797D8B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C235092-820C-4DEB-9074-D356FB797D8B}.Release|Any CPU.Build.0 = Release|Any CPU {59115814-A1E3-46AE-AE30-4065AE8F4CAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {59115814-A1E3-46AE-AE30-4065AE8F4CAF}.Release|Any CPU.ActiveCfg = Release|Any CPU {59115814-A1E3-46AE-AE30-4065AE8F4CAF}.Release|Any CPU.Build.0 = Release|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Release|Any CPU.Build.0 = Release|Any CPU + {76894ADA-0818-4556-83BD-6510D8EA2809}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76894ADA-0818-4556-83BD-6510D8EA2809}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76894ADA-0818-4556-83BD-6510D8EA2809}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76894ADA-0818-4556-83BD-6510D8EA2809}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {59220391-9856-4F95-AC74-AD4BC4FF2011} EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 @@ -38,7 +38,4 @@ Global $2.inheritsSet = VisualStudio $2.inheritsScope = text/plain EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection EndGlobal diff --git a/MimeKit.Mobile.sln b/MimeKit.Mobile.sln deleted file mode 100644 index 77d7620476..0000000000 --- a/MimeKit.Mobile.sln +++ /dev/null @@ -1,68 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit.Android", "MimeKit\MimeKit.Android.csproj", "{004B4019-62B7-4A15-AF2C-C20968845C46}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit.iOS", "MimeKit\MimeKit.iOS.csproj", "{4C1288AD-12C8-4BF7-AED7-6C4DC539C856}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BouncyCastle.Android", "submodules\bc-csharp\crypto\BouncyCastle.Android.csproj", "{A0D302CB-8866-4AB1-98B9-F0772EABF5DF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BouncyCastle.iOS", "submodules\bc-csharp\crypto\BouncyCastle.iOS.csproj", "{0249241C-205E-4AC0-828B-90F822359B9E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {004B4019-62B7-4A15-AF2C-C20968845C46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {004B4019-62B7-4A15-AF2C-C20968845C46}.Debug|Any CPU.Build.0 = Debug|Any CPU - {004B4019-62B7-4A15-AF2C-C20968845C46}.Release|Any CPU.ActiveCfg = Release|Any CPU - {004B4019-62B7-4A15-AF2C-C20968845C46}.Release|Any CPU.Build.0 = Release|Any CPU - {0249241C-205E-4AC0-828B-90F822359B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0249241C-205E-4AC0-828B-90F822359B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0249241C-205E-4AC0-828B-90F822359B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0249241C-205E-4AC0-828B-90F822359B9E}.Release|Any CPU.Build.0 = Release|Any CPU - {4C1288AD-12C8-4BF7-AED7-6C4DC539C856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C1288AD-12C8-4BF7-AED7-6C4DC539C856}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C1288AD-12C8-4BF7-AED7-6C4DC539C856}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C1288AD-12C8-4BF7-AED7-6C4DC539C856}.Release|Any CPU.Build.0 = Release|Any CPU - {A0D302CB-8866-4AB1-98B9-F0772EABF5DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A0D302CB-8866-4AB1-98B9-F0772EABF5DF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A0D302CB-8866-4AB1-98B9-F0772EABF5DF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A0D302CB-8866-4AB1-98B9-F0772EABF5DF}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = MimeKit\MimeKit.iOS.csproj - Policies = $0 - $0.StandardHeader = $1 - $1.Text = @\n${FileName}\n \nAuthor: ${AuthorName} <${AuthorEmail}>\n\nCopyright (c) ${Year} ${CopyrightHolder}\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n - $1.IncludeInNewFiles = True - $0.TextStylePolicy = $2 - $2.inheritsSet = null - $2.scope = text/x-csharp - $0.CSharpFormattingPolicy = $3 - $3.NamespaceBraceStyle = EndOfLine - $3.StructBraceStyle = EndOfLine - $3.EnumBraceStyle = EndOfLine - $3.AfterDelegateDeclarationParameterComma = True - $3.BeforeSizeOfParentheses = True - $3.BeforeTypeOfParentheses = True - $3.SpacesBeforeBrackets = False - $3.SpacesAfterTypecast = True - $3.AlignToFirstIndexerArgument = True - $3.inheritsSet = Mono - $3.inheritsScope = text/x-csharp - $3.scope = text/x-csharp - $0.TextStylePolicy = $4 - $4.FileWidth = 120 - $4.TabsToSpaces = False - $4.EolMarker = Unix - $4.inheritsSet = VisualStudio - $4.inheritsScope = text/plain - $4.scope = text/plain - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/MimeKit.Net45.sln b/MimeKit.Net45.sln deleted file mode 100644 index de0208c582..0000000000 --- a/MimeKit.Net45.sln +++ /dev/null @@ -1,78 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit.Net45", "MimeKit\MimeKit.Net45.csproj", "{D5F54A4F-D84B-430F-9271-F7861E285B3E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Data.Sqlite", "Mono.Data.Sqlite\Mono.Data.Sqlite.csproj", "{F26434C1-BA3D-41FB-B560-C009CB72B1B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{0225FDB7-CF63-4402-BB30-9B149AC06C2E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{C2CA4C1F-78BB-41D1-8E31-F723FC88CF38}" - ProjectSection(SolutionItems) = preProject - .nuget\packages.config = .nuget\packages.config - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0225FDB7-CF63-4402-BB30-9B149AC06C2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0225FDB7-CF63-4402-BB30-9B149AC06C2E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0225FDB7-CF63-4402-BB30-9B149AC06C2E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0225FDB7-CF63-4402-BB30-9B149AC06C2E}.Release|Any CPU.Build.0 = Release|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Release|Any CPU.Build.0 = Release|Any CPU - {F26434C1-BA3D-41FB-B560-C009CB72B1B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F26434C1-BA3D-41FB-B560-C009CB72B1B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F26434C1-BA3D-41FB-B560-C009CB72B1B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F26434C1-BA3D-41FB-B560-C009CB72B1B6}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - Policies = $0 - $0.StandardHeader = $1 - $1.Text = @\n${FileName}\n \nAuthor: ${AuthorName} <${AuthorEmail}>\n\nCopyright (c) ${Year} ${CopyrightHolder}\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n - $0.TextStylePolicy = $2 - $2.scope = text/plain - $2.EolMarker = Unix - $0.CSharpFormattingPolicy = $3 - $3.NamespaceBraceStyle = EndOfLine - $3.StructBraceStyle = EndOfLine - $3.EnumBraceStyle = EndOfLine - $3.AfterDelegateDeclarationParameterComma = True - $3.BeforeSizeOfParentheses = True - $3.BeforeTypeOfParentheses = True - $3.SpacesBeforeBrackets = False - $3.SpacesAfterTypecast = True - $3.AlignToFirstIndexerArgument = True - $3.scope = text/x-csharp - $3.SpaceAfterCast = True - $3.IndentSwitchSection = False - $3.NewLinesForBracesInProperties = False - $3.NewLinesForBracesInAccessors = False - $3.NewLinesForBracesInAnonymousMethods = False - $3.NewLinesForBracesInControlBlocks = False - $3.NewLinesForBracesInAnonymousTypes = False - $3.NewLinesForBracesInObjectCollectionArrayInitializers = False - $3.NewLinesForBracesInLambdaExpressionBody = False - $3.NewLineForElse = False - $3.NewLineForCatch = False - $3.NewLineForFinally = False - $3.NewLineForClausesInQuery = False - $3.SpacingAfterMethodDeclarationName = True - $3.SpaceAfterMethodCallName = True - $0.TextStylePolicy = $4 - $4.FileWidth = 120 - $4.TabsToSpaces = False - $4.EolMarker = Unix - $4.inheritsSet = VisualStudio - $4.inheritsScope = text/plain - $4.scope = text/plain - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/MimeKit.sln b/MimeKit.sln index ea073db237..ea49bc916b 100644 --- a/MimeKit.sln +++ b/MimeKit.sln @@ -1,31 +1,24 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit.Net45", "MimeKit\MimeKit.Net45.csproj", "{D5F54A4F-D84B-430F-9271-F7861E285B3E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{0225FDB7-CF63-4402-BB30-9B149AC06C2E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit.Android", "MimeKit\MimeKit.Android.csproj", "{004B4019-62B7-4A15-AF2C-C20968845C46}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit.iOS", "MimeKit\MimeKit.iOS.csproj", "{4C1288AD-12C8-4BF7-AED7-6C4DC539C856}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit.Portable", "MimeKit\MimeKit.Portable.csproj", "{BE542CE1-F773-467E-8DED-D02B89C5040A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BouncyCastle.Android", "submodules\bc-csharp\crypto\BouncyCastle.Android.csproj", "{A0D302CB-8866-4AB1-98B9-F0772EABF5DF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BouncyCastle.iOS", "submodules\bc-csharp\crypto\BouncyCastle.iOS.csproj", "{0249241C-205E-4AC0-828B-90F822359B9E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4B4EB921-F77E-4A51-897F-BBA7FA3E3468}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Data.Sqlite", "Mono.Data.Sqlite\Mono.Data.Sqlite.csproj", "{F26434C1-BA3D-41FB-B560-C009CB72B1B6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Portable.Text.Encoding", "submodules\Portable.Text.Encoding\Portable.Text.Encoding\Portable.Text.Encoding.csproj", "{EEE48C75-11BE-4B50-B759-F85B5052D473}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Portable.Text.Encoding.WindowsUniversal81", "submodules\Portable.Text.Encoding\Portable.Text.Encoding\Portable.Text.Encoding.WindowsUniversal81.csproj", "{B76A64F9-B00E-4243-AE89-5D024CA3B436}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "UnitTests\UnitTests.csproj", "{88EC8D73-8099-4DD6-B78B-C21FCED97EA1}" + ProjectSection(ProjectDependencies) = postProject + {F26434C1-BA3D-41FB-B560-C009CB72B1B6} = {F26434C1-BA3D-41FB-B560-C009CB72B1B6} + {559F9C27-70F6-44C5-8F55-7292DBBC8F87} = {559F9C27-70F6-44C5-8F55-7292DBBC8F87} + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit.WindowsUniversal81", "MimeKit\MimeKit.WindowsUniversal81.csproj", "{D9906B8C-7BBD-4CCE-AC7C-E9BCA020D20C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKit", "MimeKit\MimeKit.csproj", "{559F9C27-70F6-44C5-8F55-7292DBBC8F87}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MimeKit.NetStandard", "MimeKit\MimeKit.NetStandard.csproj", "{E8667DCE-A5BB-4D30-9815-FC8959E447F5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{1D6B883A-ABC9-4AC2-9E05-963D1D3F40EB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,54 +26,22 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D5F54A4F-D84B-430F-9271-F7861E285B3E}.Release|Any CPU.Build.0 = Release|Any CPU - {0225FDB7-CF63-4402-BB30-9B149AC06C2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0225FDB7-CF63-4402-BB30-9B149AC06C2E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0225FDB7-CF63-4402-BB30-9B149AC06C2E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0225FDB7-CF63-4402-BB30-9B149AC06C2E}.Release|Any CPU.Build.0 = Release|Any CPU - {004B4019-62B7-4A15-AF2C-C20968845C46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {004B4019-62B7-4A15-AF2C-C20968845C46}.Debug|Any CPU.Build.0 = Debug|Any CPU - {004B4019-62B7-4A15-AF2C-C20968845C46}.Release|Any CPU.ActiveCfg = Release|Any CPU - {004B4019-62B7-4A15-AF2C-C20968845C46}.Release|Any CPU.Build.0 = Release|Any CPU - {4C1288AD-12C8-4BF7-AED7-6C4DC539C856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C1288AD-12C8-4BF7-AED7-6C4DC539C856}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C1288AD-12C8-4BF7-AED7-6C4DC539C856}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C1288AD-12C8-4BF7-AED7-6C4DC539C856}.Release|Any CPU.Build.0 = Release|Any CPU - {BE542CE1-F773-467E-8DED-D02B89C5040A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BE542CE1-F773-467E-8DED-D02B89C5040A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BE542CE1-F773-467E-8DED-D02B89C5040A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BE542CE1-F773-467E-8DED-D02B89C5040A}.Release|Any CPU.Build.0 = Release|Any CPU - {A0D302CB-8866-4AB1-98B9-F0772EABF5DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A0D302CB-8866-4AB1-98B9-F0772EABF5DF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A0D302CB-8866-4AB1-98B9-F0772EABF5DF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A0D302CB-8866-4AB1-98B9-F0772EABF5DF}.Release|Any CPU.Build.0 = Release|Any CPU - {0249241C-205E-4AC0-828B-90F822359B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0249241C-205E-4AC0-828B-90F822359B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0249241C-205E-4AC0-828B-90F822359B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0249241C-205E-4AC0-828B-90F822359B9E}.Release|Any CPU.Build.0 = Release|Any CPU {F26434C1-BA3D-41FB-B560-C009CB72B1B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F26434C1-BA3D-41FB-B560-C009CB72B1B6}.Debug|Any CPU.Build.0 = Debug|Any CPU {F26434C1-BA3D-41FB-B560-C009CB72B1B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {F26434C1-BA3D-41FB-B560-C009CB72B1B6}.Release|Any CPU.Build.0 = Release|Any CPU - {EEE48C75-11BE-4B50-B759-F85B5052D473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EEE48C75-11BE-4B50-B759-F85B5052D473}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EEE48C75-11BE-4B50-B759-F85B5052D473}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EEE48C75-11BE-4B50-B759-F85B5052D473}.Release|Any CPU.Build.0 = Release|Any CPU - {B76A64F9-B00E-4243-AE89-5D024CA3B436}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B76A64F9-B00E-4243-AE89-5D024CA3B436}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B76A64F9-B00E-4243-AE89-5D024CA3B436}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B76A64F9-B00E-4243-AE89-5D024CA3B436}.Release|Any CPU.Build.0 = Release|Any CPU - {D9906B8C-7BBD-4CCE-AC7C-E9BCA020D20C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D9906B8C-7BBD-4CCE-AC7C-E9BCA020D20C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D9906B8C-7BBD-4CCE-AC7C-E9BCA020D20C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D9906B8C-7BBD-4CCE-AC7C-E9BCA020D20C}.Release|Any CPU.Build.0 = Release|Any CPU - {E8667DCE-A5BB-4D30-9815-FC8959E447F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8667DCE-A5BB-4D30-9815-FC8959E447F5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8667DCE-A5BB-4D30-9815-FC8959E447F5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8667DCE-A5BB-4D30-9815-FC8959E447F5}.Release|Any CPU.Build.0 = Release|Any CPU + {88EC8D73-8099-4DD6-B78B-C21FCED97EA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88EC8D73-8099-4DD6-B78B-C21FCED97EA1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88EC8D73-8099-4DD6-B78B-C21FCED97EA1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88EC8D73-8099-4DD6-B78B-C21FCED97EA1}.Release|Any CPU.Build.0 = Release|Any CPU + {559F9C27-70F6-44C5-8F55-7292DBBC8F87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {559F9C27-70F6-44C5-8F55-7292DBBC8F87}.Debug|Any CPU.Build.0 = Debug|Any CPU + {559F9C27-70F6-44C5-8F55-7292DBBC8F87}.Release|Any CPU.ActiveCfg = Release|Any CPU + {559F9C27-70F6-44C5-8F55-7292DBBC8F87}.Release|Any CPU.Build.0 = Release|Any CPU + {1D6B883A-ABC9-4AC2-9E05-963D1D3F40EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D6B883A-ABC9-4AC2-9E05-963D1D3F40EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D6B883A-ABC9-4AC2-9E05-963D1D3F40EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D6B883A-ABC9-4AC2-9E05-963D1D3F40EB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MimeKit/AsyncMimeParser.cs b/MimeKit/AsyncMimeParser.cs index 70ee29bfde..763aadffb6 100644 --- a/MimeKit/AsyncMimeParser.cs +++ b/MimeKit/AsyncMimeParser.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -30,8 +30,8 @@ using System.Diagnostics; using System.Threading.Tasks; -using MimeKit.Utils; using MimeKit.IO; +using MimeKit.Utils; namespace MimeKit { public partial class MimeParser @@ -47,7 +47,7 @@ async Task ReadAheadAsync (int atleast, int save, CancellationToken cancell if (nread > 0) { inputEnd += nread; - offset += nread; + position += nread; } else { eos = true; } @@ -80,8 +80,7 @@ async Task StepByteOrderMarkAsync (CancellationToken cancellationToken) async Task StepMboxMarkerAsync (CancellationToken cancellationToken) { - bool complete = false; - bool needInput; + bool complete; int left = 0; mboxMarkerLength = 0; @@ -96,11 +95,9 @@ async Task StepMboxMarkerAsync (CancellationToken cancellationToken) return; } - needInput = false; - unsafe { fixed (byte* inbuf = input) { - StepMboxMarker (inbuf, ref needInput, ref complete, ref left); + complete = StepMboxMarker (inbuf, ref left); } } } while (!complete); @@ -117,6 +114,8 @@ async Task StepHeadersAsync (CancellationToken cancellationToken) bool valid = true; int left = 0; + headerBlockBegin = GetOffset (inputIndex); + boundary = BoundaryType.None; ResetRawHeaderData (); headers.Clear (); @@ -126,29 +125,43 @@ async Task StepHeadersAsync (CancellationToken cancellationToken) unsafe { fixed (byte *inbuf = input) { if (!StepHeaders (inbuf, ref scanningFieldName, ref checkFolded, ref midline, ref blank, ref valid, ref left)) - return; + break; } } var available = await ReadAheadAsync (left + 1, 0, cancellationToken).ConfigureAwait (false); - if (available == 0) { + if (available == left) { // EOF reached before we reached the end of the headers... - if (left > 0) { - AppendRawHeaderData (inputIndex, left); - inputIndex = inputEnd; - } + if (scanningFieldName && left > 0) { + // EOF reached right in the middle of a header field name. Throw an error. + // + // See private email from Feb 8, 2018 which contained a sample message w/o + // any breaks between the header and message body. The file also did not + // end with a newline sequence. + state = MimeParserState.Error; + } else { + // EOF reached somewhere in the middle of the value. + // + // Append whatever data we've got left and pretend we found the end + // of the header value (and the header block). + // + // For more details, see https://github.com/jstedfast/MimeKit/pull/51 + // and https://github.com/jstedfast/MimeKit/issues/348 + if (left > 0) { + AppendRawHeaderData (inputIndex, left); + inputIndex = inputEnd; + } - ParseAndAppendHeader (); + ParseAndAppendHeader (); - // fail gracefully by pretending we found the end of the headers... - // - // For more details, see https://github.com/jstedfast/MimeKit/pull/51 - // and https://github.com/jstedfast/MimeKit/issues/348 - state = MimeParserState.Content; - return; + state = MimeParserState.Content; + } + break; } } while (true); + + headerBlockEnd = GetOffset (inputIndex); } async Task SkipLineAsync (bool consumeNewLine, CancellationToken cancellationToken) @@ -183,29 +196,18 @@ async Task StepAsync (CancellationToken cancellationToken) case MimeParserState.MessageHeaders: case MimeParserState.Headers: await StepHeadersAsync (cancellationToken).ConfigureAwait (false); + toplevel = false; break; } return state; } - struct ScanContentResults - { - public readonly BoundaryType Boundary; - public readonly bool IsEmpty; - - public ScanContentResults (BoundaryType boundary, bool empty) - { - Boundary = boundary; - IsEmpty = empty; - } - } - - async Task ScanContentAsync (Stream content, bool trimNewLine, CancellationToken cancellationToken) + async Task ScanContentAsync (Stream content, bool trimNewLine, CancellationToken cancellationToken) { int atleast = Math.Max (ReadAheadSize, GetMaxBoundaryLength ()); - BoundaryType found = BoundaryType.None; int contentIndex = inputIndex; + var formats = new bool[2]; bool midline = false; int nleft; @@ -215,24 +217,24 @@ async Task ScanContentAsync (Stream content, bool trimNewLin nleft = inputEnd - inputIndex; if (await ReadAheadAsync (atleast, 2, cancellationToken).ConfigureAwait (false) <= 0) { + boundary = BoundaryType.Eos; contentIndex = inputIndex; - found = BoundaryType.Eos; break; } unsafe { fixed (byte* inbuf = input) { - ScanContent (inbuf, ref contentIndex, ref nleft, ref midline, ref found); + ScanContent (inbuf, ref contentIndex, ref nleft, ref midline, ref formats); } } - } while (found == BoundaryType.None); + } while (boundary == BoundaryType.None); if (contentIndex < inputIndex) content.Write (input, contentIndex, inputIndex - contentIndex); - var empty = content.Length == 0; + var isEmpty = content.Length == 0; - if (found != BoundaryType.Eos && trimNewLine) { + if (boundary != BoundaryType.Eos && trimNewLine) { // the last \r\n belongs to the boundary if (content.Length > 0) { if (input[inputIndex - 2] == (byte) '\r') @@ -242,45 +244,57 @@ async Task ScanContentAsync (Stream content, bool trimNewLin } } - return new ScanContentResults (found, empty); + return new ScanContentResult (formats, isEmpty); } - async Task ConstructMimePartAsync (MimePart part, CancellationToken cancellationToken) + async Task ConstructMimePartAsync (MimePart part, MimeEntityEndEventArgs args, CancellationToken cancellationToken) { - ScanContentResults results; + long endOffset, beginOffset = GetOffset (inputIndex); + var beginLineNumber = lineNumber; + ScanContentResult result; Stream content; if (persistent) { - long begin = GetOffset (inputIndex); - long end; - using (var measured = new MeasuringStream ()) { - results = await ScanContentAsync (measured, true, cancellationToken).ConfigureAwait (false); - end = begin + measured.Length; + result = await ScanContentAsync (measured, true, cancellationToken).ConfigureAwait (false); + endOffset = beginOffset + measured.Length; } - content = new BoundStream (stream, begin, end, true); + content = new BoundStream (stream, beginOffset, endOffset, true); } else { content = new MemoryBlockStream (); - results = await ScanContentAsync (content, true, cancellationToken).ConfigureAwait (false); - content.Seek (0, SeekOrigin.Begin); + + try { + result = await ScanContentAsync (content, true, cancellationToken).ConfigureAwait (false); + content.Seek (0, SeekOrigin.Begin); + } catch { + content.Dispose (); + throw; + } + + endOffset = beginOffset + content.Length; } - if (!results.IsEmpty) - part.Content = new MimeContent (content, part.ContentTransferEncoding); + args.Lines = GetLineCount (beginLineNumber, beginOffset, endOffset); - return results.Boundary; + if (!result.IsEmpty) + part.Content = new MimeContent (content, part.ContentTransferEncoding) { NewLineFormat = result.Format }; + else + content.Dispose (); } - async Task ConstructMessagePartAsync (MessagePart part, CancellationToken cancellationToken) + async Task ConstructMessagePartAsync (MessagePart rfc822, MimeEntityEndEventArgs args, int depth, CancellationToken cancellationToken) { - BoundaryType found; + var beginOffset = GetOffset (inputIndex); + var beginLineNumber = lineNumber; if (bounds.Count > 0) { int atleast = Math.Max (ReadAheadSize, GetMaxBoundaryLength ()); - if (await ReadAheadAsync (atleast, 0, cancellationToken).ConfigureAwait (false) <= 0) - return BoundaryType.Eos; + if (await ReadAheadAsync (atleast, 0, cancellationToken).ConfigureAwait (false) <= 0) { + boundary = BoundaryType.Eos; + return; + } unsafe { fixed (byte* inbuf = input) { @@ -293,17 +307,18 @@ async Task ConstructMessagePartAsync (MessagePart part, Cancellati while (*inptr != (byte) '\n') inptr++; - found = CheckBoundary (inputIndex, start, (int) (inptr - start)); + boundary = CheckBoundary (inputIndex, start, (int) (inptr - start)); - switch (found) { + switch (boundary) { case BoundaryType.ImmediateEndBoundary: case BoundaryType.ImmediateBoundary: case BoundaryType.ParentBoundary: - return found; + return; case BoundaryType.ParentEndBoundary: // ignore "From " boundaries, broken mailers tend to include these... - if (!IsMboxMarker (start)) - return found; + if (!IsMboxMarker (start)) { + return; + } break; } } @@ -311,116 +326,199 @@ async Task ConstructMessagePartAsync (MessagePart part, Cancellati } // parse the headers... - state = MimeParserState.Headers; + state = MimeParserState.MessageHeaders; if (await StepAsync (cancellationToken).ConfigureAwait (false) == MimeParserState.Error) { // Note: this either means that StepHeaders() found the end of the stream // or an invalid header field name at the start of the message headers, // which likely means that this is not a valid MIME stream? - return BoundaryType.Eos; + boundary = BoundaryType.Eos; + return; } var message = new MimeMessage (options, headers, RfcComplianceMode.Loose); - var type = GetContentType (null); + var messageArgs = new MimeMessageEndEventArgs (message, rfc822) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeMessageBegin (messageArgs); if (preHeaderBuffer.Length > 0) { message.MboxMarker = new byte[preHeaderLength]; Buffer.BlockCopy (preHeaderBuffer, 0, message.MboxMarker, 0, preHeaderLength); } - var entity = options.CreateEntity (type, headers, true); + var type = GetContentType (null); + var entity = options.CreateEntity (type, headers, true, depth); + var entityArgs = new MimeEntityEndEventArgs (entity) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeEntityBegin (entityArgs); + message.Body = entity; if (entity is Multipart) - found = await ConstructMultipartAsync ((Multipart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMultipartAsync ((Multipart) entity, entityArgs, depth + 1, cancellationToken).ConfigureAwait (false); else if (entity is MessagePart) - found = await ConstructMessagePartAsync ((MessagePart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMessagePartAsync ((MessagePart) entity, entityArgs, depth + 1, cancellationToken).ConfigureAwait (false); else - found = await ConstructMimePartAsync ((MimePart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMimePartAsync ((MimePart) entity, entityArgs, cancellationToken).ConfigureAwait (false); + + rfc822.Message = message; - part.Message = message; + var endOffset = GetEndOffset (inputIndex); + messageArgs.HeadersEndOffset = entityArgs.HeadersEndOffset = Math.Min (entityArgs.HeadersEndOffset, endOffset); + messageArgs.EndOffset = entityArgs.EndOffset = endOffset; - return found; + OnMimeEntityEnd (entityArgs); + OnMimeMessageEnd (messageArgs); + + args.Lines = GetLineCount (beginLineNumber, beginOffset, endOffset); } - async Task MultipartScanPreambleAsync (Multipart multipart, CancellationToken cancellationToken) + async Task MultipartScanPreambleAsync (Multipart multipart, CancellationToken cancellationToken) { using (var memory = new MemoryStream ()) { - var found = await ScanContentAsync (memory, false, cancellationToken).ConfigureAwait (false); + long offset = GetOffset (inputIndex); + + //OnMultipartPreambleBegin (multipart, offset); + await ScanContentAsync (memory, false, cancellationToken).ConfigureAwait (false); multipart.RawPreamble = memory.ToArray (); - return found.Boundary; + //OnMultipartPreambleEnd (multipart, offset + memory.Length); } } - async Task MultipartScanEpilogueAsync (Multipart multipart, CancellationToken cancellationToken) + async Task MultipartScanEpilogueAsync (Multipart multipart, CancellationToken cancellationToken) { using (var memory = new MemoryStream ()) { - var found = await ScanContentAsync (memory, true, cancellationToken).ConfigureAwait (false); - multipart.RawEpilogue = found.IsEmpty ? null : memory.ToArray (); - return found.Boundary; + long offset = GetOffset (inputIndex); + + //OnMultipartEpilogueBegin (multipart, offset); + var result = await ScanContentAsync (memory, true, cancellationToken).ConfigureAwait (false); + multipart.RawEpilogue = result.IsEmpty ? null : memory.ToArray (); + //OnMultipartEpilogueEnd (multipart, offset + memory.Length); } } - async Task MultipartScanSubpartsAsync (Multipart multipart, CancellationToken cancellationToken) + async Task MultipartScanSubpartsAsync (Multipart multipart, int depth, CancellationToken cancellationToken) { - BoundaryType found; + //var beginOffset = GetOffset (inputIndex); do { + //OnMultipartBoundaryBegin (multipart, beginOffset); + // skip over the boundary marker - if (!await SkipLineAsync (true, cancellationToken).ConfigureAwait (false)) - return BoundaryType.Eos; + if (!await SkipLineAsync (true, cancellationToken).ConfigureAwait (false)) { + //OnMultipartBoundaryEnd (multipart, GetOffset (inputIndex)); + boundary = BoundaryType.Eos; + return; + } + + //OnMultipartBoundaryEnd (multipart, GetOffset (inputIndex)); + + var beginLineNumber = lineNumber; // parse the headers state = MimeParserState.Headers; - if (await StepAsync (cancellationToken).ConfigureAwait (false) == MimeParserState.Error) - return BoundaryType.Eos; + if (await StepAsync (cancellationToken).ConfigureAwait (false) == MimeParserState.Error) { + boundary = BoundaryType.Eos; + return; + } + + if (state == MimeParserState.Boundary) { + if (headers.Count == 0) { + if (boundary == BoundaryType.ImmediateBoundary) { + //beginOffset = GetOffset (inputIndex); + continue; + } + return; + } + + // This part has no content, but that will be handled in ConstructMultipartAsync() + // or ConstructMimePartAsync(). + } //if (state == ParserState.Complete && headers.Count == 0) // return BoundaryType.EndBoundary; var type = GetContentType (multipart.ContentType); - var entity = options.CreateEntity (type, headers, false); + var entity = options.CreateEntity (type, headers, false, depth); + var entityArgs = new MimeEntityEndEventArgs (entity, multipart) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeEntityBegin (entityArgs); if (entity is Multipart) - found = await ConstructMultipartAsync ((Multipart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMultipartAsync ((Multipart) entity, entityArgs, depth + 1, cancellationToken).ConfigureAwait (false); else if (entity is MessagePart) - found = await ConstructMessagePartAsync ((MessagePart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMessagePartAsync ((MessagePart) entity, entityArgs, depth + 1, cancellationToken).ConfigureAwait (false); else - found = await ConstructMimePartAsync ((MimePart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMimePartAsync ((MimePart) entity, entityArgs, cancellationToken).ConfigureAwait (false); - multipart.Add (entity); - } while (found == BoundaryType.ImmediateBoundary); + var endOffset = GetEndOffset (inputIndex); + entityArgs.HeadersEndOffset = Math.Min (entityArgs.HeadersEndOffset, endOffset); + entityArgs.EndOffset = endOffset; + + OnMimeEntityEnd (entityArgs); - return found; + //beginOffset = endOffset; + multipart.Add (entity); + } while (boundary == BoundaryType.ImmediateBoundary); } - async Task ConstructMultipartAsync (Multipart multipart, CancellationToken cancellationToken) + async Task ConstructMultipartAsync (Multipart multipart, MimeEntityEndEventArgs args, int depth, CancellationToken cancellationToken) { - var boundary = multipart.Boundary; + var beginOffset = GetOffset (inputIndex); + var beginLineNumber = lineNumber; + var marker = multipart.Boundary; + long endOffset; - if (boundary == null) { + if (marker == null) { #if DEBUG Debug.WriteLine ("Multipart without a boundary encountered!"); #endif // Note: this will scan all content into the preamble... - return await MultipartScanPreambleAsync (multipart, cancellationToken).ConfigureAwait (false); + await MultipartScanPreambleAsync (multipart, cancellationToken).ConfigureAwait (false); + + endOffset = GetEndOffset (inputIndex); + args.Lines = GetLineCount (beginLineNumber, beginOffset, endOffset); + return; } - PushBoundary (boundary); + PushBoundary (marker); - var found = await MultipartScanPreambleAsync (multipart, cancellationToken).ConfigureAwait (false); - if (found == BoundaryType.ImmediateBoundary) - found = await MultipartScanSubpartsAsync (multipart, cancellationToken).ConfigureAwait (false); + await MultipartScanPreambleAsync (multipart, cancellationToken).ConfigureAwait (false); + if (boundary == BoundaryType.ImmediateBoundary) + await MultipartScanSubpartsAsync (multipart, depth, cancellationToken).ConfigureAwait (false); + + if (boundary == BoundaryType.ImmediateEndBoundary) { + //OnMultipartEndBoundaryBegin (multipart, GetEndOffset (inputIndex)); - if (found == BoundaryType.ImmediateEndBoundary) { // consume the end boundary and read the epilogue (if there is one) multipart.WriteEndBoundary = true; await SkipLineAsync (false, cancellationToken).ConfigureAwait (false); PopBoundary (); - return await MultipartScanEpilogueAsync (multipart, cancellationToken).ConfigureAwait (false); + //OnMultipartEndBoundaryEnd (multipart, GetOffset (inputIndex)); + + await MultipartScanEpilogueAsync (multipart, cancellationToken).ConfigureAwait (false); + + endOffset = GetEndOffset (inputIndex); + args.Lines = GetLineCount (beginLineNumber, beginOffset, endOffset); + return; } + endOffset = GetEndOffset (inputIndex); + args.Lines = GetLineCount (beginLineNumber, beginOffset, endOffset); + multipart.WriteEndBoundary = false; // We either found the end of the stream or we found a parent's boundary @@ -428,15 +526,12 @@ async Task ConstructMultipartAsync (Multipart multipart, Cancellat unsafe { fixed (byte* inbuf = input) { - if (found == BoundaryType.ParentEndBoundary && FoundImmediateBoundary (inbuf, true)) - return BoundaryType.ImmediateEndBoundary; - - if (found == BoundaryType.ParentBoundary && FoundImmediateBoundary (inbuf, false)) - return BoundaryType.ImmediateBoundary; + if (boundary == BoundaryType.ParentEndBoundary && FoundImmediateBoundary (inbuf, true)) + boundary = BoundaryType.ImmediateEndBoundary; + else if (boundary == BoundaryType.ParentBoundary && FoundImmediateBoundary (inbuf, false)) + boundary = BoundaryType.ImmediateBoundary; } } - - return found; } /// @@ -493,31 +588,48 @@ async Task ///ConstructMultipartAsync (Multipart multipart, Cancellat // Note: if a previously parsed MimePart's content has been read, // then the stream position will have moved and will need to be // reset. - if (persistent && stream.Position != offset) - stream.Seek (offset, SeekOrigin.Begin); + if (persistent && stream.Position != position) + stream.Seek (position, SeekOrigin.Begin); + + var beginLineNumber = lineNumber; state = MimeParserState.Headers; + toplevel = true; + if (await StepAsync (cancellationToken).ConfigureAwait (false) == MimeParserState.Error) throw new FormatException ("Failed to parse entity headers."); var type = GetContentType (null); - BoundaryType found; // Note: we pass 'false' as the 'toplevel' argument here because // we want the entity to consume all of the headers. - var entity = options.CreateEntity (type, headers, false); + var entity = options.CreateEntity (type, headers, false, 0); + var entityArgs = new MimeEntityEndEventArgs (entity) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeEntityBegin (entityArgs); + if (entity is Multipart) - found = await ConstructMultipartAsync ((Multipart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMultipartAsync ((Multipart) entity, entityArgs, 0, cancellationToken).ConfigureAwait (false); else if (entity is MessagePart) - found = await ConstructMessagePartAsync ((MessagePart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMessagePartAsync ((MessagePart) entity, entityArgs, 0, cancellationToken).ConfigureAwait (false); else - found = await ConstructMimePartAsync ((MimePart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMimePartAsync ((MimePart) entity, entityArgs, cancellationToken).ConfigureAwait (false); + + var endOffset = GetEndOffset (inputIndex); + entityArgs.HeadersEndOffset = Math.Min (entityArgs.HeadersEndOffset, endOffset); + entityArgs.EndOffset = endOffset; - if (found != BoundaryType.Eos) + if (boundary != BoundaryType.Eos) state = MimeParserState.Complete; else state = MimeParserState.Eos; + OnMimeEntityEnd (entityArgs); + return entity; } @@ -540,13 +652,11 @@ async Task ConstructMultipartAsync (Multipart multipart, Cancellat /// public async Task ParseMessageAsync (CancellationToken cancellationToken = default (CancellationToken)) { - BoundaryType found; - // Note: if a previously parsed MimePart's content has been read, // then the stream position will have moved and will need to be // reset. - if (persistent && stream.Position != offset) - stream.Seek (offset, SeekOrigin.Begin); + if (persistent && stream.Position != position) + stream.Seek (position, SeekOrigin.Begin); // scan the from-line if we are parsing an mbox while (state != MimeParserState.MessageHeaders) { @@ -558,44 +668,67 @@ async Task ConstructMultipartAsync (Multipart multipart, Cancellat } } + toplevel = true; + // parse the headers + var beginLineNumber = lineNumber; if (state < MimeParserState.Content && await StepAsync (cancellationToken).ConfigureAwait (false) == MimeParserState.Error) throw new FormatException ("Failed to parse message headers."); var message = new MimeMessage (options, headers, RfcComplianceMode.Loose); + var messageArgs = new MimeMessageEndEventArgs (message) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeMessageBegin (messageArgs); if (format == MimeFormat.Mbox && options.RespectContentLength) { - bounds[0].ContentEnd = -1; + contentEnd = 0; for (int i = 0; i < headers.Count; i++) { - if (!headers[i].Field.Equals ("Content-Length", StringComparison.OrdinalIgnoreCase)) + if (headers[i].Id != HeaderId.ContentLength) continue; var value = headers[i].RawValue; - int length, index = 0; + int index = 0; - if (!ParseUtils.TryParseInt32 (value, ref index, value.Length, out length)) + if (!ParseUtils.SkipWhiteSpace (value, ref index, value.Length)) continue; - long endOffset = GetOffset (inputIndex) + length; + if (!ParseUtils.TryParseInt32 (value, ref index, value.Length, out int length)) + continue; - bounds[0].ContentEnd = endOffset; + contentEnd = GetOffset (inputIndex) + length; break; } } var type = GetContentType (null); - var entity = options.CreateEntity (type, headers, true); + var entity = options.CreateEntity (type, headers, true, 0); + var entityArgs = new MimeEntityEndEventArgs (entity) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeEntityBegin (entityArgs); + message.Body = entity; if (entity is Multipart) - found = await ConstructMultipartAsync ((Multipart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMultipartAsync ((Multipart) entity, entityArgs, 0, cancellationToken).ConfigureAwait (false); else if (entity is MessagePart) - found = await ConstructMessagePartAsync ((MessagePart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMessagePartAsync ((MessagePart) entity, entityArgs, 0, cancellationToken).ConfigureAwait (false); else - found = await ConstructMimePartAsync ((MimePart) entity, cancellationToken).ConfigureAwait (false); + await ConstructMimePartAsync ((MimePart) entity, entityArgs, cancellationToken).ConfigureAwait (false); - if (found != BoundaryType.Eos) { + var endOffset = GetEndOffset (inputIndex); + messageArgs.HeadersEndOffset = entityArgs.HeadersEndOffset = Math.Min (entityArgs.HeadersEndOffset, endOffset); + messageArgs.EndOffset = entityArgs.EndOffset = endOffset; + + if (boundary != BoundaryType.Eos) { if (format == MimeFormat.Mbox) state = MimeParserState.MboxMarker; else @@ -604,6 +737,9 @@ async Task ConstructMultipartAsync (Multipart multipart, Cancellat state = MimeParserState.Eos; } + OnMimeEntityEnd (entityArgs); + OnMimeMessageEnd (messageArgs); + return message; } } diff --git a/MimeKit/AttachmentCollection.cs b/MimeKit/AttachmentCollection.cs index b4291440bd..70a960b461 100644 --- a/MimeKit/AttachmentCollection.cs +++ b/MimeKit/AttachmentCollection.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,6 +26,7 @@ using System; using System.IO; +using System.Buffers; using System.Collections; using System.Collections.Generic; @@ -39,13 +40,18 @@ namespace MimeKit { /// /// The + ///is only used when building a message body with a . /// + /// public class AttachmentCollection : IList+ ///
{ + const int BufferLength = 4096; + readonly List attachments; readonly bool linked; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new @@ -60,7 +66,7 @@ public AttachmentCollection (bool linkedResources) } ///. - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -94,10 +100,10 @@ public bool IsReadOnly { } /// - /// Gets or sets the ///at the specified index. + /// Gets or sets the at the specified index. /// - /// Gets or sets the ///at the specified index. + /// Gets or sets the at the specified index. /// The attachment at the specified index. /// The index. @@ -128,20 +134,32 @@ public MimeEntity this [int index] { static void LoadContent (MimePart attachment, Stream stream) { var content = new MemoryBlockStream (); - var filter = new BestEncodingFilter (); - var buf = new byte[4096]; - int index, length; - int nread; - - while ((nread = stream.Read (buf, 0, buf.Length)) > 0) { - filter.Filter (buf, 0, nread, out index, out length); - content.Write (buf, 0, nread); + + if (attachment.ContentType.IsMimeType ("text", "*")) { + var buf = ArrayPool.Shared.Rent (BufferLength); + var filter = new BestEncodingFilter (); + int index, length; + int nread; + + try { + while ((nread = stream.Read (buf, 0, BufferLength)) > 0) { + filter.Filter (buf, 0, nread, out index, out length); + content.Write (buf, 0, nread); + } + + filter.Flush (buf, 0, 0, out index, out length); + } finally { + ArrayPool .Shared.Return (buf); + } + + attachment.ContentTransferEncoding = filter.GetBestEncoding (EncodingConstraint.SevenBit); + } else { + attachment.ContentTransferEncoding = ContentEncoding.Base64; + stream.CopyTo (content, 4096); } - filter.Flush (buf, 0, 0, out index, out length); content.Position = 0; - attachment.ContentTransferEncoding = filter.GetBestEncoding (EncodingConstraint.SevenBit); attachment.Content = new MimeContent (content); } @@ -152,27 +170,42 @@ static ContentType GetMimeType (string fileName) return ContentType.Parse (mimeType); } - MimePart CreateAttachment (ContentType contentType, string fileName, Stream stream) + static string GetFileName (string path) { - MimePart attachment; + int index = path.LastIndexOf (Path.DirectorySeparatorChar); - if (contentType.IsMimeType ("text", "*")) { - attachment = new TextPart (contentType.MediaSubtype); - foreach (var param in contentType.Parameters) - attachment.ContentType.Parameters.Add (param); + return index > 0 ? path.Substring (index + 1) : path; + } - // TODO: should we try to auto-detect charsets if no charset parameter is specified? + MimeEntity CreateAttachment (ContentType contentType, string path, Stream stream) + { + var fileName = GetFileName (path); + MimeEntity attachment; + + if (contentType.IsMimeType ("message", "rfc822")) { + var message = MimeMessage.Load (stream); + + attachment = new MessagePart { Message = message }; } else { - attachment = new MimePart (contentType); + MimePart part; + + if (contentType.IsMimeType ("text", "*")) { + // TODO: should we try to auto-detect charsets if no charset parameter is specified? + part = new TextPart (contentType); + } else { + part = new MimePart (contentType); + } + + LoadContent (part, stream); + attachment = part; } - attachment.FileName = Path.GetFileName (fileName); - attachment.IsAttachment = !linked; + attachment.ContentDisposition = new ContentDisposition (linked ? ContentDisposition.Inline : ContentDisposition.Attachment); + attachment.ContentDisposition.FileName = fileName; + attachment.ContentType.Name = fileName; if (linked) - attachment.ContentLocation = new Uri (Path.GetFileName (fileName), UriKind.Relative); - - LoadContent (attachment, stream); + attachment.ContentLocation = new Uri (fileName, UriKind.Relative); return attachment; } @@ -351,7 +384,6 @@ public MimeEntity Add (string fileName, Stream stream) return attachment; } -#if !PORTABLE /// /// Add the specified attachment. /// @@ -406,6 +438,9 @@ public MimeEntity Add (string fileName, ContentType contentType) ////// + ///Adds the specified file as an attachment. ///+ /// ///+ ///
The newly added attachment /// The name of the file. ///. @@ -413,8 +448,7 @@ public MimeEntity Add (string fileName, ContentType contentType) /// ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// is an invalid file path. @@ -444,7 +478,6 @@ public MimeEntity Add (string fileName) return attachment; } } -#endif /// /// Add the specified attachment. diff --git a/MimeKit/BodyBuilder.cs b/MimeKit/BodyBuilder.cs index 6b81e45052..bcf6730fdf 100644 --- a/MimeKit/BodyBuilder.cs +++ b/MimeKit/BodyBuilder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast UUEncode, } } diff --git a/MimeKit/ContentType.cs b/MimeKit/ContentType.cs index 382c01a79f..0c0ab73ed9 100644 --- a/MimeKit/ContentType.cs +++ b/MimeKit/ContentType.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -33,14 +33,20 @@ namespace MimeKit { /// /// + ///is a helper class for building common MIME body structures. /// + /// public class BodyBuilder { ///+ ///
- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new + ///. /// + /// public BodyBuilder () { LinkedResources = new AttachmentCollection (true); @@ -48,68 +54,83 @@ public BodyBuilder () } ///+ ///
- /// Gets the attachments. + /// Get the attachments. /// ////// Represents a collection of file attachments that will be included in the message. /// + ///+ /// ///+ ///
The attachments. public AttachmentCollection Attachments { get; private set; } ///- /// Gets the linked resources. + /// Get the linked resources. /// ////// Linked resources are a special type of attachment which are linked to from the + ///. /// + /// ///+ ///
The linked resources. public AttachmentCollection LinkedResources { get; private set; } ///- /// Gets or sets the text body. + /// Get or set the text body. /// ////// Represents the plain-text formatted version of the message body. /// + ///+ /// ///+ ///
The text body. public string TextBody { get; set; } ///- /// Gets or sets the html body. + /// Get or set the html body. /// ////// Represents the html formatted version of the message body and may link to any of the + ///. /// + /// ///+ ///
The html body. public string HtmlBody { get; set; } ///- /// Constructs the message body based on the text-based bodies, the linked resources, and the attachments. + /// Construct the message body based on the text-based bodies, the linked resources, and the attachments. /// ////// Combines the + ///, , , /// and into the proper MIME structure suitable for display in many common /// mail clients. /// + /// ///+ ///
The message body. public MimeEntity ToMessageBody () { MultipartAlternative alternative = null; MimeEntity body = null; - if (!string.IsNullOrEmpty (TextBody)) { + if (TextBody != null) { var text = new TextPart ("plain"); text.Text = TextBody; - if (!string.IsNullOrEmpty (HtmlBody)) { + if (HtmlBody != null) { alternative = new MultipartAlternative (); alternative.Add (text); body = alternative; @@ -118,11 +139,10 @@ public MimeEntity ToMessageBody () } } - if (!string.IsNullOrEmpty (HtmlBody)) { + if (HtmlBody != null) { var text = new TextPart ("html"); MimeEntity html; - text.ContentId = MimeUtils.GenerateMessageId (); text.Text = HtmlBody; if (LinkedResources.Count > 0) { @@ -145,6 +165,9 @@ public MimeEntity ToMessageBody () } if (Attachments.Count > 0) { + if (body == null && Attachments.Count == 1) + return Attachments[0]; + var mixed = new Multipart ("mixed"); if (body != null) diff --git a/MimeKit/ContentDisposition.cs b/MimeKit/ContentDisposition.cs index dbcdf45a2f..cafcfe88d2 100644 --- a/MimeKit/ContentDisposition.cs +++ b/MimeKit/ContentDisposition.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,10 +26,7 @@ using System; using System.Text; - -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif +using System.Globalization; using MimeKit.Utils; @@ -72,7 +69,7 @@ public class ContentDisposition string disposition; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// The disposition should either be @@ -92,7 +89,7 @@ public ContentDisposition (string disposition) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// This is identical to with a disposition @@ -108,7 +105,7 @@ static bool IsAsciiAtom (byte c) } /// - /// Gets or sets the disposition. + /// Get or set the disposition. /// ////// The disposition is typically either "attachment" or"inline" . @@ -143,7 +140,7 @@ public string Disposition { } ///- /// Gets or sets a value indicating whether the ///is an attachment. + /// Get or set a value indicating whether the is an attachment. /// /// A convenience property to determine if the entity should be considered an attachment or not. @@ -155,7 +152,7 @@ public bool IsAttachment { } /// - /// Gets the parameters. + /// Get the list of parameters on the ///. /// /// In addition to specifying whether the entity should be treated as an @@ -163,6 +160,9 @@ public bool IsAttachment { /// contain parameters to provide further information to the receiving client /// such as the file attributes. /// + ///+ /// ///+ ///
The parameters. public ParameterList Parameters { get { return parameters; } @@ -176,7 +176,7 @@ private set { } ///- /// Gets or sets the name of the file. + /// Get or set the name of the file. /// ////// When set, this can provide a useful hint for a default file name for the @@ -210,7 +210,7 @@ static bool IsNullOrWhiteSpace (string value) } /// - /// Gets or sets the creation-date parameter. + /// Get or set the creation-date parameter. /// ////// Refers to the date and time that the content file was created on the @@ -241,7 +241,7 @@ public DateTimeOffset? CreationDate { } /// - /// Gets or sets the modification-date parameter. + /// Get or set the modification-date parameter. /// ////// Refers to the date and time that the content file was last modified on @@ -272,7 +272,7 @@ public DateTimeOffset? ModificationDate { } /// - /// Gets or sets the read-date parameter. + /// Get or set the read-date parameter. /// ////// Refers to the date and time that the content file was last read on the @@ -303,7 +303,7 @@ public DateTimeOffset? ReadDate { } /// - /// Gets or sets the size parameter. + /// Get or set the size parameter. /// ////// When set, the size parameter typically refers to the original size of the @@ -346,7 +346,7 @@ internal string Encode (FormatOptions options, Encoding charset) } /// - /// Serializes the ///to a string, + /// Serialize the to a string, /// optionally encoding the parameters. /// @@ -385,7 +385,7 @@ public string ToString (FormatOptions options, Encoding charset, bool encode) } /// - /// Serializes the ///to a string, + /// Serialize the to a string, /// optionally encoding the parameters. /// @@ -404,14 +404,13 @@ public string ToString (Encoding charset, bool encode) } /// - /// Returns a ///that represents the current - /// . + /// Serialize the to a string. /// /// Creates a string-representation of the ///. /// A + ///that represents the current - /// . . public override string ToString () { return ToString (FormatOptions.Default, Encoding.UTF8, false); @@ -442,7 +441,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Expected atom token at position {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Expected atom token at position {0}", index), index, index); return false; } @@ -450,7 +449,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index atom = index; if (text[index] == '"') { if (throwOnError) - throw new ParseException (string.Format ("Unxpected qstring token at position {0}", atom), atom, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unxpected qstring token at position {0}", atom), atom, index); // Note: This is a work-around for broken mailers that quote the disposition value... // @@ -466,7 +465,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index } else { if (!ParseUtils.SkipAtom (text, ref index, endIndex)) { if (throwOnError) - throw new ParseException (string.Format ("Invalid atom token at position {0}", atom), atom, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid atom token at position {0}", atom), atom, index); // Note: this is a work-around for broken mailers that do not specify a disposition value... // @@ -491,7 +490,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if (text[index] != (byte) ';') { if (throwOnError) - throw new ParseException (string.Format ("Expected ';' at position {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Expected ';' at position {0}", index), index, index); return false; } @@ -514,13 +513,13 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Disposition value from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// - ///+ /// true , if the disposition was successfully parsed,false otherwise./// The parser options. /// The input buffer. /// The starting index of the input buffer. @@ -545,13 +544,13 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// true if the disposition was successfully parsed; otherwise,false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Disposition value from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// - ///+ /// true , if the disposition was successfully parsed,false otherwise./// The input buffer. /// The starting index of the input buffer. /// The number of bytes in the input buffer to parse. @@ -569,12 +568,12 @@ public static bool TryParse (byte[] buffer, int startIndex, int length, out Cont } /// true if the disposition was successfully parsed; otherwise,false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Disposition value from the supplied buffer starting at the specified index. /// - ///+ /// true , if the disposition was successfully parsed,false otherwise./// The parser options. /// The input buffer. /// The starting index of the input buffer. @@ -597,12 +596,12 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// true if the disposition was successfully parsed; otherwise,false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Disposition value from the supplied buffer starting at the specified index. /// - ///+ /// true , if the disposition was successfully parsed,false otherwise./// The input buffer. /// The starting index of the input buffer. /// The parsed disposition. @@ -618,12 +617,12 @@ public static bool TryParse (byte[] buffer, int startIndex, out ContentDispositi } /// true if the disposition was successfully parsed; otherwise,false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Disposition value from the specified buffer. /// - ///+ /// true , if the disposition was successfully parsed,false otherwise./// The parser options. /// The input buffer. /// The parsed disposition. @@ -642,12 +641,12 @@ public static bool TryParse (ParserOptions options, byte[] buffer, out ContentDi } /// true if the disposition was successfully parsed; otherwise,false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Disposition value from the specified buffer. /// - ///+ /// true , if the disposition was successfully parsed,false otherwise./// The input buffer. /// The parsed disposition. /// true if the disposition was successfully parsed; otherwise,false .@@ -659,12 +658,12 @@ public static bool TryParse (byte[] buffer, out ContentDisposition disposition) } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a Content-Disposition value from the supplied text. /// - ///+ /// true , if the disposition was successfully parsed,false otherwise./// The parser options. /// The text to parse. /// The parsed disposition. @@ -684,12 +683,12 @@ public static bool TryParse (ParserOptions options, string text, out ContentDisp } /// true if the disposition was successfully parsed; otherwise,false .- /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a Content-Disposition value from the supplied text. /// - ///+ /// true , if the disposition was successfully parsed,false otherwise./// The text to parse. /// The parsed disposition. /// true if the disposition was successfully parsed; otherwise,false .@@ -701,13 +700,13 @@ public static bool TryParse (string text, out ContentDisposition disposition) } /// - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Disposition value from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// - ///The parsed + ///. The parsed /// The parser options. /// The input buffer. /// The start index of the buffer. @@ -737,13 +736,13 @@ public static ContentDisposition Parse (ParserOptions options, byte[] buffer, in } ///. - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Disposition value from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// - ///The parsed + ///. The parsed /// The input buffer. /// The start index of the buffer. /// The length of the buffer. @@ -763,12 +762,12 @@ public static ContentDisposition Parse (byte[] buffer, int startIndex, int lengt } ///. - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Disposition value from the supplied buffer starting at the specified index. /// - ///The parsed + ///. The parsed /// The parser options. /// The input buffer. /// The start index of the buffer. @@ -796,12 +795,12 @@ public static ContentDisposition Parse (ParserOptions options, byte[] buffer, in } ///. - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Disposition value from the supplied buffer starting at the specified index. /// - ///The parsed + ///. The parsed /// The input buffer. /// The start index of the buffer. ///. @@ -819,12 +818,12 @@ public static ContentDisposition Parse (byte[] buffer, int startIndex) } /// - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Disposition value from the supplied buffer. /// - ///The parsed + ///. The parsed /// The parser options. /// The input buffer. ///. @@ -848,12 +847,12 @@ public static ContentDisposition Parse (ParserOptions options, byte[] buffer) } /// - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Disposition value from the supplied buffer. /// - ///The parsed + ///. The parsed /// The input buffer. ///. /// is null . @@ -867,12 +866,12 @@ public static ContentDisposition Parse (byte[] buffer) } ///- /// Parse the specified text into a new instance of the ///class. + /// Parse the specified text into a new instance of the class. /// /// Parses a Content-Disposition value from the specified text. /// - ///The parsed + ///. The parsed /// The parser options. /// The input text. ///. @@ -897,12 +896,12 @@ public static ContentDisposition Parse (ParserOptions options, string text) } /// - /// Parse the specified text into a new instance of the ///class. + /// Parse the specified text into a new instance of the class. /// /// Parses a Content-Disposition value from the specified text. /// - ///The parsed + ///. The parsed /// The input text. ///. /// is null . diff --git a/MimeKit/ContentEncoding.cs b/MimeKit/ContentEncoding.cs index b2d30218d4..53c6cbd650 100644 --- a/MimeKit/ContentEncoding.cs +++ b/MimeKit/ContentEncoding.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ namespace MimeKit { /// /// An enumeration of all supported content transfer encodings. - /// ///. /// /// Some older mail software is unable to properly deal with /// data outside of the ASCII range, so it is sometimes /// necessary to encode the content of MIME entities. /// + ///public enum ContentEncoding { /// /// The default encoding (aka no encoding at all). @@ -41,65 +41,43 @@ public enum ContentEncoding { Default, /// SevenBit, ///- /// The 7bit content transfer encoding. - /// - ///- /// This encoding should be restricted to textual content + /// The 7bit content transfer encoding. This encoding should be restricted to textual content /// in the US-ASCII range. - /// + ///- /// The 8bit content transfer encoding. + /// The 8bit content transfer encoding. This encoding should be restricted to textual content + /// outside of the US-ASCII range but may not be supported by all transport services such as + /// older SMTP servers that do not support the 8BITMIME extension. /// - ///- /// This encoding should be restricted to textual content - /// outside of the US-ASCII range but may not be supported - /// by all transport services such as older SMTP servers - /// that do not support the 8BITMIME extension. - /// EightBit, ///- /// The binary content transfer encoding. + /// The binary content transfer encoding. This encoding is simply unencoded binary data. Typically + /// not supported by standard message transport services such as SMTP. /// - ///- /// This encoding is simply unencoded binary data. Typically not - /// supported by standard message transport services such as SMTP. - /// Binary, ///- /// The base64 content transfer encoding. - /// - ///. + /// The base64 content transfer encoding. This encoding is typically used for encoding binary data + /// or textual content in a largely 8bit charset encoding and is supported by all message transport + /// services. /// - /// This encoding is typically used for encoding binary data - /// or textual content in a largely 8bit charset encoding and - /// is supported by all message transport services. - /// Base64, ///- /// The quoted printable content transfer encoding. - /// - ///. + /// The quoted-printable content transfer encoding. This encoding is used for textual content that + /// is in a charset that has a minority of characters outside of the US-ASCII range (such as + /// ISO-8859-1 and other single-byte charset encodings) and is supported by all message transport + /// services. /// - /// This encoding is used for textual content that is in a charset - /// that has a minority of characters outside of the US-ASCII range - /// (such as ISO-8859-1 and other single-byte charset encodings) and - /// is supported by all message transport services. - /// QuotedPrintable, ///- /// The uuencode content transfer encoding. - /// - ///. - /// - /// This is an obsolete encoding meant for encoding binary + /// The uuencode content transfer encoding. This is an obsolete encoding meant for encoding binary /// data and has largely been superceeded by + ///. - /// // -// Copyright (c) 2013-2018 Xamarin Inc. +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,10 +26,7 @@ using System; using System.Text; - -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif +using System.Globalization; using MimeKit.Utils; @@ -48,7 +45,7 @@ public class ContentType string type, subtype; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new based on the media type and subtype provided. @@ -73,18 +70,8 @@ public ContentType (string mediaType, string mediaSubtype) type = mediaType; } - static bool IsToken (byte c) - { - return c.IsToken (); - } - - static bool IsAsciiAtom (byte c) - { - return c.IsAsciiAtom (); - } - /// - /// Gets or sets the type of the media. + /// Get or set the type of the media. /// ////// Represents the media type of the . Examples include @@ -111,7 +98,7 @@ public string MediaType { } /// - /// Gets or sets the media subtype. + /// Get or set the media subtype. /// ////// Represents the media subtype of the . Examples include @@ -138,7 +125,7 @@ public string MediaSubtype { } /// - /// Gets the parameters. + /// Get the list of parameters on the ///. /// /// In addition to the media type and subtype, the Content-Type header may also @@ -158,7 +145,7 @@ internal set { } /// - /// Gets or sets the boundary parameter. + /// Get or set the boundary parameter. /// ////// This is a special parameter on entities, designating to the @@ -176,7 +163,7 @@ public string Boundary { } /// - /// Gets or sets the charset parameter. + /// Get or set the charset parameter. /// ////// Text-based entities will often include a charset parameter @@ -194,7 +181,33 @@ public string Charset { } /// - /// Gets or sets the format parameter. + /// Get or set the charset parameter as an + ///. + /// + /// Text-based + ///entities will often include a charset parameter + /// so that the receiving client can properly render the text. + /// The charset encoding. + public Encoding CharsetEncoding { + get { + var charset = Charset; + + if (charset == null) + return null; + + try { + return CharsetUtils.GetEncoding (charset); + } catch { + return null; + } + } + set { + Charset = value != null ? CharsetUtils.GetMimeCharset (value) : null; + } + } + + ///+ /// Get or set the format parameter. /// ////// The format parameter is typically use with text/plain @@ -223,7 +236,7 @@ public string MimeType { } /// - /// Gets or sets the name parameter. + /// Get or set the name parameter. /// ////// The name parameter is a way for the originiating client to suggest @@ -243,7 +256,7 @@ public string Name { } /// - /// Checks if the this instance of ///matches + /// Check if the this instance of matches /// the specified MIME media type and subtype. /// @@ -291,7 +304,7 @@ internal string Encode (FormatOptions options, Encoding charset) } /// - /// Serializes the ///to a string, + /// Serialize the to a string, /// optionally encoding the parameters. /// @@ -332,7 +345,7 @@ public string ToString (FormatOptions options, Encoding charset, bool encode) } /// - /// Serializes the ///to a string, + /// Serialize the to a string, /// optionally encoding the parameters. /// @@ -351,14 +364,13 @@ public string ToString (Encoding charset, bool encode) } /// - /// Returns a ///that represents the current - /// . + /// Serialize the to a string. /// /// Creates a string-representation of the ///. /// A + ///that represents the current - /// . . public override string ToString () { return ToString (FormatOptions.Default, Encoding.UTF8, false); @@ -400,7 +412,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index start = index; if (!SkipType (text, ref index, endIndex)) { if (throwOnError) - throw new ParseException (string.Format ("Invalid type token at position {0}", start), start, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid type token at position {0}", start), start, index); return false; } @@ -412,7 +424,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if (index >= endIndex || text[index] != (byte) '/') { if (throwOnError) - throw new ParseException (string.Format ("Expected '/' at position {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Expected '/' at position {0}", index), index, index); return false; } @@ -426,7 +438,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index start = index; if (!ParseUtils.SkipToken (text, ref index, endIndex)) { if (throwOnError) - throw new ParseException (string.Format ("Invalid atom token at position {0}", start), start, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid atom token at position {0}", start), start, index); return false; } @@ -443,7 +455,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if (text[index] != (byte) ';') { if (throwOnError) - throw new ParseException (string.Format ("Expected ';' at position {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Expected ';' at position {0}", index), index, index); return false; } @@ -466,13 +478,13 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Type value from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// - ///+ /// true , if the content type was successfully parsed,false otherwise./// The parser options. /// The input buffer. /// The starting index of the input buffer. @@ -497,13 +509,13 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// true if the content type was successfully parsed; otherwise,false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Type value from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// - ///+ /// true , if the content type was successfully parsed,false otherwise./// The input buffer. /// The starting index of the input buffer. /// The number of bytes in the input buffer to parse. @@ -521,12 +533,12 @@ public static bool TryParse (byte[] buffer, int startIndex, int length, out Cont } /// true if the content type was successfully parsed; otherwise,false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Type value from the supplied buffer starting at the specified index. /// - ///+ /// true , if the content type was successfully parsed,false otherwise./// The parser options. /// The input buffer. /// The starting index of the input buffer. @@ -549,12 +561,12 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// true if the content type was successfully parsed; otherwise,false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Type value from the supplied buffer starting at the specified index. /// - ///+ /// true , if the content type was successfully parsed,false otherwise./// The input buffer. /// The starting index of the input buffer. /// The parsed content type. @@ -570,12 +582,12 @@ public static bool TryParse (byte[] buffer, int startIndex, out ContentType type } /// true if the content type was successfully parsed; otherwise,false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Type value from the specified buffer. /// - ///+ /// true , if the content type was successfully parsed,false otherwise./// The parser options. /// The input buffer. /// The parsed content type. @@ -594,12 +606,12 @@ public static bool TryParse (ParserOptions options, byte[] buffer, out ContentTy } /// true if the content type was successfully parsed; otherwise,false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a Content-Type value from the specified buffer. /// - ///+ /// true , if the content type was successfully parsed,false otherwise./// The input buffer. /// The parsed content type. /// true if the content type was successfully parsed; otherwise,false .@@ -611,12 +623,12 @@ public static bool TryParse (byte[] buffer, out ContentType type) } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a Content-Type value from the specified text. /// - ///+ /// true , if the content type was successfully parsed,false otherwise./// THe parser options. /// The text to parse. /// The parsed content type. @@ -636,12 +648,12 @@ public static bool TryParse (ParserOptions options, string text, out ContentType } /// true if the content type was successfully parsed; otherwise,false .- /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a Content-Type value from the specified text. /// - ///+ /// true , if the content type was successfully parsed,false otherwise./// The text to parse. /// The parsed content type. /// true if the content type was successfully parsed; otherwise,false .@@ -653,13 +665,13 @@ public static bool TryParse (string text, out ContentType type) } /// - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Type value from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// - ///The parsed + ///. The parsed /// The parser options. /// The input buffer. /// The start index of the buffer. @@ -689,13 +701,13 @@ public static ContentType Parse (ParserOptions options, byte[] buffer, int start } ///. - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Type value from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// - ///The parsed + ///. The parsed /// The input buffer. /// The start index of the buffer. /// The length of the buffer. @@ -715,12 +727,12 @@ public static ContentType Parse (byte[] buffer, int startIndex, int length) } ///. - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Type value from the supplied buffer starting at the specified index. /// - ///The parsed + ///. The parsed /// The parser options. /// The input buffer. /// The start index of the buffer. @@ -748,12 +760,12 @@ public static ContentType Parse (ParserOptions options, byte[] buffer, int start } ///. - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Type value from the supplied buffer starting at the specified index. /// - ///The parsed + ///. The parsed /// The input buffer. /// The start index of the buffer. ///. @@ -771,12 +783,12 @@ public static ContentType Parse (byte[] buffer, int startIndex) } /// - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Type value from the specified buffer. /// - ///The parsed + ///. The parsed /// The parser options. /// The input buffer. ///. @@ -800,12 +812,12 @@ public static ContentType Parse (ParserOptions options, byte[] buffer) } /// - /// Parse the specified input buffer into a new instance of the ///class. + /// Parse the specified input buffer into a new instance of the class. /// /// Parses a Content-Type value from the specified buffer. /// - ///The parsed + ///. The parsed /// The input buffer. ///. /// is null . @@ -819,12 +831,12 @@ public static ContentType Parse (byte[] buffer) } ///- /// Parse the specified text into a new instance of the ///class. + /// Parse the specified text into a new instance of the class. /// /// Parses a Content-Type value from the specified text. /// - ///The parsed + ///. The parsed /// The parser options. /// The text. ///. @@ -849,12 +861,12 @@ public static ContentType Parse (ParserOptions options, string text) } /// - /// Parse the specified text into a new instance of the ///class. + /// Parse the specified text into a new instance of the class. /// /// Parses a Content-Type value from the specified text. /// - ///The parsed + ///. The parsed /// The text. ///. /// is null . diff --git a/MimeKit/Cryptography/ApplicationPgpEncrypted.cs b/MimeKit/Cryptography/ApplicationPgpEncrypted.cs index 70d6fd6198..05859d6fc7 100644 --- a/MimeKit/Cryptography/ApplicationPgpEncrypted.cs +++ b/MimeKit/Cryptography/ApplicationPgpEncrypted.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -40,11 +40,11 @@ namespace MimeKit.Cryptography { public class ApplicationPgpEncrypted : MimePart { /// - /// Initializes a new instance of the ///- /// class based on the . + /// Initialize a new instance of the + /// class based on the . /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -55,7 +55,7 @@ public ApplicationPgpEncrypted (MimeEntityConstructorArgs args) : base (args) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new MIME part with a Content-Type of application/pgp-encrypted @@ -75,12 +75,12 @@ public ApplicationPgpEncrypted () : base ("application", "pgp-encrypted") /// Dispatches to the specific visit method for this MIME entity. /// - /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// diff --git a/MimeKit/Cryptography/ApplicationPgpSignature.cs b/MimeKit/Cryptography/ApplicationPgpSignature.cs index 95dec8d7a0..d7590e31c3 100644 --- a/MimeKit/Cryptography/ApplicationPgpSignature.cs +++ b/MimeKit/Cryptography/ApplicationPgpSignature.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast - ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -41,11 +41,11 @@ namespace MimeKit.Cryptography { public class ApplicationPgpSignature : MimePart { /// - /// Initializes a new instance of the ///- /// class based on the . + /// Initialize a new instance of the + /// class based on the . /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -56,7 +56,7 @@ public ApplicationPgpSignature (MimeEntityConstructorArgs args) : base (args) } /// - ///- /// Initializes a new instance of the ///+ /// Initialize a new instance of the /// class with a Content-Type of application/pgp-signature. /// @@ -84,12 +84,12 @@ public ApplicationPgpSignature (Stream stream) : base ("application", "pgp-signa /// Dispatches to the specific visit method for this MIME entity. /// /// public abstract class BouncyCastleSecureMimeContext : SecureMimeContext { + static readonly string RsassaPssOid = PkcsObjectIdentifiers.IdRsassaPss.Id; + HttpClient client; ///- /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// diff --git a/MimeKit/Cryptography/ApplicationPkcs7Mime.cs b/MimeKit/Cryptography/ApplicationPkcs7Mime.cs index 482329bc3e..12ebd8045a 100644 --- a/MimeKit/Cryptography/ApplicationPkcs7Mime.cs +++ b/MimeKit/Cryptography/ApplicationPkcs7Mime.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -42,10 +42,10 @@ namespace MimeKit.Cryptography { public class ApplicationPkcs7Mime : MimePart { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -56,7 +56,7 @@ public ApplicationPkcs7Mime (MimeEntityConstructorArgs args) : base (args) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// public static class BouncyCastleCertificateExtensions { -#if !PORTABLE ///Creates a new MIME part with a Content-Type of application/pkcs7-mime @@ -127,6 +127,7 @@ public SecureMimeType SecureMimeType { return SecureMimeType.Unknown; switch (type.ToLowerInvariant ()) { + case "authenveloped-data": return SecureMimeType.AuthEnvelopedData; case "compressed-data": return SecureMimeType.CompressedData; case "enveloped-data": return SecureMimeType.EnvelopedData; case "signed-data": return SecureMimeType.SignedData; @@ -140,12 +141,12 @@ public SecureMimeType SecureMimeType { /// Dispatches to the specific visit method for this MIME entity. /// /// - /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -165,7 +166,7 @@ public override void Accept (MimeVisitor visitor) /// /// Decompresses the compressed-data using the specified - ///. /// The decompressed + ///. The decompressed /// The S/MIME context to use for decompressing. ///. /// public MimeEntity Decompress () { - if (SecureMimeType != SecureMimeType.CompressedData) + if (SecureMimeType != SecureMimeType.CompressedData && SecureMimeType != SecureMimeType.Unknown) throw new InvalidOperationException (); using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) @@ -220,7 +221,7 @@ public MimeEntity Decompress () ///is null . @@ -181,7 +182,7 @@ public MimeEntity Decompress (SecureMimeContext ctx) if (ctx == null) throw new ArgumentNullException (nameof (ctx)); - if (SecureMimeType != SecureMimeType.CompressedData) + if (SecureMimeType != SecureMimeType.CompressedData && SecureMimeType != SecureMimeType.Unknown) throw new InvalidOperationException (); using (var memory = new MemoryBlockStream ()) { @@ -198,7 +199,7 @@ public MimeEntity Decompress (SecureMimeContext ctx) ////// Decompresses the compressed-data using the default - ///. /// The decompressed + ///. The decompressed ///. /// The "smime-type" parameter on the Content-Type header is not "compressed-data". /// @@ -207,7 +208,7 @@ public MimeEntity Decompress (SecureMimeContext ctx) ////// Decrypts the enveloped-data using the specified - ///. /// The decrypted + ///. The decrypted /// The S/MIME context to use for decrypting. /// The cancellation token. ///. @@ -240,7 +241,7 @@ public MimeEntity Decompress () if (ctx == null) throw new ArgumentNullException (nameof (ctx)); - if (SecureMimeType != SecureMimeType.EnvelopedData) + if (SecureMimeType != SecureMimeType.EnvelopedData && SecureMimeType != SecureMimeType.Unknown) throw new InvalidOperationException (); using (var memory = new MemoryBlockStream ()) { @@ -257,7 +258,7 @@ public MimeEntity Decompress () /// /// Decrypts the enveloped-data using the default - ///. /// The decrypted + ///. The decrypted /// The cancellation token. ///. /// The "smime-type" parameter on the Content-Type header is not "certs-only". @@ -295,7 +296,7 @@ public void Import (SecureMimeContext ctx) if (ctx == null) throw new ArgumentNullException (nameof (ctx)); - if (SecureMimeType != SecureMimeType.CertsOnly) + if (SecureMimeType != SecureMimeType.CertsOnly && SecureMimeType != SecureMimeType.Unknown) throw new InvalidOperationException (); using (var memory = new MemoryBlockStream ()) { @@ -307,10 +308,10 @@ public void Import (SecureMimeContext ctx) } /// - /// Verify the signed-data and return the unencapsulated ///. + /// Verify the signed-data and return the unencapsulated . /// - /// Verifies the signed-data and returns the unencapsulated ///. + /// Verifies the signed-data and returns the unencapsulated . /// The list of digital signatures. /// The S/MIME context to use for verifying the signature. @@ -336,7 +337,7 @@ public void Import (SecureMimeContext ctx) if (ctx == null) throw new ArgumentNullException (nameof (ctx)); - if (SecureMimeType != SecureMimeType.SignedData) + if (SecureMimeType != SecureMimeType.SignedData && SecureMimeType != SecureMimeType.Unknown) throw new InvalidOperationException (); using (var memory = new MemoryBlockStream ()) { @@ -348,11 +349,11 @@ public void Import (SecureMimeContext ctx) } ///- /// Verifies the signed-data and returns the unencapsulated ///. + /// Verifies the signed-data and returns the unencapsulated . /// /// Verifies the signed-data using the default ///and returns the - /// unencapsulated . + /// unencapsulated . /// The list of digital signatures. /// The unencapsulated entity. @@ -377,8 +378,7 @@ public void Import (SecureMimeContext ctx) /// ////// ///Compresses the specified entity using the specified - ///. + /// Most mail clients, even among those that support S/MIME, - /// do not support compression. Most mail clients, even among those that support S/MIME, do not support compression. ///The compressed entity. /// The S/MIME context to use for compressing. @@ -415,8 +415,7 @@ public static ApplicationPkcs7Mime Compress (SecureMimeContext ctx, MimeEntity e /// ////// ///Compresses the specified entity using the default - ///. + /// Most mail clients, even among those that support S/MIME, - /// do not support compression. Most mail clients, even among those that support S/MIME, do not support compression. ///The compressed entity. /// The entity. diff --git a/MimeKit/Cryptography/ApplicationPkcs7Signature.cs b/MimeKit/Cryptography/ApplicationPkcs7Signature.cs index 612fa09c27..200d503211 100644 --- a/MimeKit/Cryptography/ApplicationPkcs7Signature.cs +++ b/MimeKit/Cryptography/ApplicationPkcs7Signature.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -41,10 +41,10 @@ namespace MimeKit.Cryptography { public class ApplicationPkcs7Signature : MimePart { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -55,7 +55,7 @@ public ApplicationPkcs7Signature (MimeEntityConstructorArgs args) : base (args) } /// - /// Initializes a new instance of the ///+ /// Initialize a new instance of the /// class with a Content-Type of application/pkcs7-signature. /// @@ -83,12 +83,12 @@ public ApplicationPkcs7Signature (Stream stream) : base ("application", "pkcs7-s /// Dispatches to the specific visit method for this MIME entity. /// /// public static class AsymmetricAlgorithmExtensions { - static void GetAsymmetricKeyParameters (DSACryptoServiceProvider dsa, bool publicOnly, out AsymmetricKeyParameter pub, out AsymmetricKeyParameter key) + static void GetAsymmetricKeyParameters (DSA dsa, bool publicOnly, out AsymmetricKeyParameter pub, out AsymmetricKeyParameter key) { var dp = dsa.ExportParameters (!publicOnly); var validationParameters = dp.Seed != null ? new DsaValidationParameters (dp.Seed, dp.Counter) : null; @@ -57,26 +57,36 @@ static void GetAsymmetricKeyParameters (DSACryptoServiceProvider dsa, bool publi static AsymmetricKeyParameter GetAsymmetricKeyParameter (DSACryptoServiceProvider dsa) { - AsymmetricKeyParameter pub, key; - - GetAsymmetricKeyParameters (dsa, dsa.PublicOnly, out pub, out key); + GetAsymmetricKeyParameters (dsa, dsa.PublicOnly, out var pub, out var key); return dsa.PublicOnly ? pub : key; } static AsymmetricCipherKeyPair GetAsymmetricCipherKeyPair (DSACryptoServiceProvider dsa) { - AsymmetricKeyParameter pub, key; - if (dsa.PublicOnly) throw new ArgumentException ("DSA key is not a private key.", "key"); - GetAsymmetricKeyParameters (dsa, dsa.PublicOnly, out pub, out key); + GetAsymmetricKeyParameters (dsa, dsa.PublicOnly, out var pub, out var key); return new AsymmetricCipherKeyPair (pub, key); } - static void GetAsymmetricKeyParameters (RSACryptoServiceProvider rsa, bool publicOnly, out AsymmetricKeyParameter pub, out AsymmetricKeyParameter key) + static AsymmetricKeyParameter GetAsymmetricKeyParameter (DSA dsa) + { + GetAsymmetricKeyParameters (dsa, false, out _, out var key); + + return key; + } + + static AsymmetricCipherKeyPair GetAsymmetricCipherKeyPair (DSA dsa) + { + GetAsymmetricKeyParameters (dsa, false, out var pub, out var key); + + return new AsymmetricCipherKeyPair (pub, key); + } + + static void GetAsymmetricKeyParameters (RSA rsa, bool publicOnly, out AsymmetricKeyParameter pub, out AsymmetricKeyParameter key) { var rp = rsa.ExportParameters (!publicOnly); var modulus = new BigInteger (1, rp.Modulus); @@ -97,21 +107,31 @@ static void GetAsymmetricKeyParameters (RSACryptoServiceProvider rsa, bool publi static AsymmetricKeyParameter GetAsymmetricKeyParameter (RSACryptoServiceProvider rsa) { - AsymmetricKeyParameter pub, key; - - GetAsymmetricKeyParameters (rsa, rsa.PublicOnly, out pub, out key); + GetAsymmetricKeyParameters (rsa, rsa.PublicOnly, out var pub, out var key); return rsa.PublicOnly ? pub : key; } static AsymmetricCipherKeyPair GetAsymmetricCipherKeyPair (RSACryptoServiceProvider rsa) { - AsymmetricKeyParameter pub, key; - if (rsa.PublicOnly) throw new ArgumentException ("RSA key is not a private key.", "key"); - GetAsymmetricKeyParameters (rsa, rsa.PublicOnly, out pub, out key); + GetAsymmetricKeyParameters (rsa, rsa.PublicOnly, out var pub, out var key); + + return new AsymmetricCipherKeyPair (pub, key); + } + + static AsymmetricKeyParameter GetAsymmetricKeyParameter (RSA rsa) + { + GetAsymmetricKeyParameters (rsa, false, out _, out var key); + + return key; + } + + static AsymmetricCipherKeyPair GetAsymmetricCipherKeyPair (RSA rsa) + { + GetAsymmetricKeyParameters (rsa, false, out var pub, out var key); return new AsymmetricCipherKeyPair (pub, key); } @@ -136,11 +156,17 @@ public static AsymmetricKeyParameter AsAsymmetricKeyParameter (this AsymmetricAl if (key == null) throw new ArgumentNullException (nameof (key)); - if (key is DSACryptoServiceProvider) - return GetAsymmetricKeyParameter ((DSACryptoServiceProvider) key); + if (key is RSACryptoServiceProvider rsaKey) + return GetAsymmetricKeyParameter (rsaKey); + + if (key is RSA rsa) + return GetAsymmetricKeyParameter (rsa); - if (key is RSACryptoServiceProvider) - return GetAsymmetricKeyParameter ((RSACryptoServiceProvider) key); + if (key is DSACryptoServiceProvider dsaKey) + return GetAsymmetricKeyParameter (dsaKey); + + if (key is DSA dsa) + return GetAsymmetricKeyParameter (dsa); // TODO: support ECDiffieHellman and ECDsa? @@ -170,11 +196,17 @@ public static AsymmetricCipherKeyPair AsAsymmetricCipherKeyPair (this Asymmetric if (key == null) throw new ArgumentNullException (nameof (key)); - if (key is DSACryptoServiceProvider) - return GetAsymmetricCipherKeyPair ((DSACryptoServiceProvider) key); + if (key is RSACryptoServiceProvider rsaKey) + return GetAsymmetricCipherKeyPair (rsaKey); + + if (key is RSA rsa) + return GetAsymmetricCipherKeyPair (rsa); - if (key is RSACryptoServiceProvider) - return GetAsymmetricCipherKeyPair ((RSACryptoServiceProvider) key); + if (key is DSACryptoServiceProvider dsaKey) + return GetAsymmetricCipherKeyPair (dsaKey); + + if (key is DSA dsa) + return GetAsymmetricCipherKeyPair (dsa); // TODO: support ECDiffieHellman and ECDsa? @@ -220,6 +252,7 @@ static AsymmetricAlgorithm GetAsymmetricAlgorithm (DsaPrivateKeyParameters key, parameters.Y = pub.Y.ToByteArrayUnsigned (); var dsa = new DSACryptoServiceProvider (); + dsa.ImportParameters (parameters); return dsa; @@ -231,6 +264,7 @@ static AsymmetricAlgorithm GetAsymmetricAlgorithm (DsaPublicKeyParameters key) parameters.Y = key.Y.ToByteArrayUnsigned (); var dsa = new DSACryptoServiceProvider (); + dsa.ImportParameters (parameters); return dsa; @@ -251,6 +285,7 @@ static AsymmetricAlgorithm GetAsymmetricAlgorithm (RsaPrivateCrtKeyParameters ke parameters.DQ = GetPaddedByteArray (key.DQ, parameters.Q.Length); var rsa = new RSACryptoServiceProvider (); + rsa.ImportParameters (parameters); return rsa; @@ -263,6 +298,7 @@ static AsymmetricAlgorithm GetAsymmetricAlgorithm (RsaKeyParameters key) parameters.Modulus = key.Modulus.ToByteArrayUnsigned (); var rsa = new RSACryptoServiceProvider (); + rsa.ImportParameters (parameters); return rsa; @@ -289,17 +325,17 @@ public static AsymmetricAlgorithm AsAsymmetricAlgorithm (this AsymmetricKeyParam throw new ArgumentNullException (nameof (key)); if (key.IsPrivate) { - if (key is DsaPrivateKeyParameters) - return GetAsymmetricAlgorithm ((DsaPrivateKeyParameters) key, null); + if (key is RsaPrivateCrtKeyParameters rsaPrivateKey) + return GetAsymmetricAlgorithm (rsaPrivateKey); - if (key is RsaPrivateCrtKeyParameters) - return GetAsymmetricAlgorithm ((RsaPrivateCrtKeyParameters) key); + if (key is DsaPrivateKeyParameters dsaPrivateKey) + return GetAsymmetricAlgorithm (dsaPrivateKey, null); } else { - if (key is DsaPublicKeyParameters) - return GetAsymmetricAlgorithm ((DsaPublicKeyParameters) key); + if (key is RsaKeyParameters rsaPublicKey) + return GetAsymmetricAlgorithm (rsaPublicKey); - if (key is RsaKeyParameters) - return GetAsymmetricAlgorithm ((RsaKeyParameters) key); + if (key is DsaPublicKeyParameters dsaPublicKey) + return GetAsymmetricAlgorithm (dsaPublicKey); } throw new NotSupportedException (string.Format ("{0} is currently not supported.", key.GetType ().Name)); @@ -325,11 +361,11 @@ public static AsymmetricAlgorithm AsAsymmetricAlgorithm (this AsymmetricCipherKe if (key == null) throw new ArgumentNullException (nameof (key)); - if (key.Private is DsaPrivateKeyParameters) - return GetAsymmetricAlgorithm ((DsaPrivateKeyParameters) key.Private, (DsaPublicKeyParameters) key.Public); + if (key.Private is RsaPrivateCrtKeyParameters rsaPrivateKey) + return GetAsymmetricAlgorithm (rsaPrivateKey); - if (key.Private is RsaPrivateCrtKeyParameters) - return GetAsymmetricAlgorithm ((RsaPrivateCrtKeyParameters) key.Private); + if (key.Private is DsaPrivateKeyParameters dsaPrivateKey) + return GetAsymmetricAlgorithm (dsaPrivateKey, (DsaPublicKeyParameters) key.Public); throw new NotSupportedException (string.Format ("{0} is currently not supported.", key.GetType ().Name)); } diff --git a/MimeKit/Cryptography/AuthenticationResults.cs b/MimeKit/Cryptography/AuthenticationResults.cs new file mode 100644 index 0000000000..b8e710ebd3 --- /dev/null +++ b/MimeKit/Cryptography/AuthenticationResults.cs @@ -0,0 +1,1332 @@ +// +// AuthenticationResults.cs +// +// Author: Jeffrey Stedfast- /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// diff --git a/MimeKit/Cryptography/ArcSigner.cs b/MimeKit/Cryptography/ArcSigner.cs new file mode 100644 index 0000000000..d7c3485dea --- /dev/null +++ b/MimeKit/Cryptography/ArcSigner.cs @@ -0,0 +1,725 @@ +// +// ArcSigner.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Globalization; +using System.Threading.Tasks; +using System.Collections.Generic; + +using Org.BouncyCastle.Crypto; + +using MimeKit.IO; +using MimeKit.Utils; + +namespace MimeKit.Cryptography { + /// + /// An ARC signer. + /// + ///+ /// An ARC signer. + /// + ///+ /// + public abstract class ArcSigner : DkimSignerBase + { + static readonly string[] ArcShouldNotInclude = { "return-path", "received", "comments", "keywords", "bcc", "resent-bcc", "arc-seal" }; + + ///+ ///
+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The domain that the signer represents. + /// The selector subdividing the domain. + /// The signature algorithm. + ///. + /// + /// + protected ArcSigner (string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : base (domain, selector, algorithm) + { + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The signer's private key. + /// The domain that the signer represents. + /// The selector subdividing the domain. + /// The signature algorithm. + ///. + /// + /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + protected ArcSigner (AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) + { + if (key == null) + throw new ArgumentNullException (nameof (key)); + + if (!key.IsPrivate) + throw new ArgumentException ("The key must be a private key.", nameof (key)); + + PrivateKey = key; + } + + ///is not a private key. + /// + /// Initialize a new instance of the + ///class. + /// + /// Creates a new + ///. + /// + /// + /// The file containing the private key. + /// The domain that the signer represents. + /// The selector subdividing the domain. + /// The signature algorithm. + ///+ ///
+ /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///is a zero-length string, contains only white space, or + /// contains one or more invalid characters. + /// + /// The file did not contain a private key. + /// + ///+ /// + ///is an invalid file path. + /// + /// The specified file path could not be found. + /// + ///+ /// The user does not have access to read the specified file. + /// + ///+ /// An I/O error occurred. + /// + protected ArcSigner (string fileName, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) + { + if (fileName == null) + throw new ArgumentNullException (nameof (fileName)); + + if (fileName.Length == 0) + throw new ArgumentException ("The file name cannot be empty.", nameof (fileName)); + + using (var stream = File.OpenRead (fileName)) + PrivateKey = LoadPrivateKey (stream); + } + + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The stream containing the private key. + /// The domain that the signer represents. + /// The selector subdividing the domain. + /// The signature algorithm. + ///. + /// + /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// The file did not contain a private key. + /// + ///+ /// An I/O error occurred. + /// + protected ArcSigner (Stream stream, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) + { + if (stream == null) + throw new ArgumentNullException (nameof (stream)); + + PrivateKey = LoadPrivateKey (stream); + } + + ///+ /// Generate an ARC-Authentication-Results header. + /// + ///+ /// + ///Generates an ARC-Authentication-Results header. + ///If the returned + ///contains a + /// with a equal to "arc" , then the + ///will be used as the cv= tag value + /// in theARC-Seal header generated by the. + /// + /// The format options. + /// The message to create the ARC-Authentication-Results header for. + /// The cancellation token. + ///+ ///
The ARC-Authentication-Results header or + protected abstract AuthenticationResults GenerateArcAuthenticationResults (FormatOptions options, MimeMessage message, CancellationToken cancellationToken); + + ///null if theshould not sign the message. + /// Asynchronously generate an ARC-Authentication-Results header. + /// + ///+ /// + ///Asynchronously generates an ARC-Authentication-Results header. + ///If the returned + ///contains a + /// with a equal to "arc" , then the + ///will be used as the cv= tag value + /// in theARC-Seal header generated by the. + /// + /// The format options. + /// The message to create the ARC-Authentication-Results header for. + /// The cancellation token. + ///+ ///
The ARC-Authentication-Results header or + protected abstract Tasknull if theshould not sign the message. GenerateArcAuthenticationResultsAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken); + + /// + /// Get the timestamp value. + /// + ///+ /// Gets the timestamp to use as the + ///t= value in the ARC-Message-Signature and ARC-Seal headers. + ///A value representing the timestamp value. + protected virtual long GetTimestamp () + { + return (long) (DateTime.UtcNow - DateUtils.UnixEpoch).TotalSeconds; + } + + StringBuilder CreateArcHeaderBuilder (int instance) + { + var value = new StringBuilder (); + + value.AppendFormat ("i={0}", instance.ToString (CultureInfo.InvariantCulture)); + + switch (SignatureAlgorithm) { + case DkimSignatureAlgorithm.Ed25519Sha256: + value.Append ("; a=ed25519-sha256"); + break; + case DkimSignatureAlgorithm.RsaSha256: + value.Append ("; a=rsa-sha256"); + break; + default: + value.Append ("; a=rsa-sha1"); + break; + } + + return value; + } + + Header GenerateArcMessageSignature (FormatOptions options, MimeMessage message, int instance, long t, IListheaders) + { + var value = CreateArcHeaderBuilder (instance); + byte[] signature, hash; + Header ams; + + value.AppendFormat ("; d={0}; s={1}", Domain, Selector); + value.AppendFormat ("; c={0}/{1}", + HeaderCanonicalizationAlgorithm.ToString ().ToLowerInvariant (), + BodyCanonicalizationAlgorithm.ToString ().ToLowerInvariant ()); + value.AppendFormat ("; t={0}", t); + + using (var stream = new DkimSignatureStream (CreateSigningContext ())) { + using (var filtered = new FilteredStream (stream)) { + filtered.Add (options.CreateNewLineFilter ()); + + // write the specified message headers + DkimVerifierBase.WriteHeaders (options, message, headers, HeaderCanonicalizationAlgorithm, filtered); + + value.AppendFormat ("; h={0}", string.Join (":", headers.ToArray ())); + + hash = message.HashBody (options, SignatureAlgorithm, BodyCanonicalizationAlgorithm, -1); + value.AppendFormat ("; bh={0}", Convert.ToBase64String (hash)); + value.Append ("; b="); + + ams = new Header (HeaderId.ArcMessageSignature, value.ToString ()); + + switch (HeaderCanonicalizationAlgorithm) { + case DkimCanonicalizationAlgorithm.Relaxed: + DkimVerifierBase.WriteHeaderRelaxed (options, filtered, ams, true); + break; + default: + DkimVerifierBase.WriteHeaderSimple (options, filtered, ams, true); + break; + } + + filtered.Flush (); + } + + signature = stream.GenerateSignature (); + + ams.Value += Convert.ToBase64String (signature); + + return ams; + } + } + + Header GenerateArcSeal (FormatOptions options, int instance, string cv, long t, ArcHeaderSet[] sets, int count, Header aar, Header ams) + { + var value = CreateArcHeaderBuilder (instance); + byte[] signature; + Header seal; + + value.AppendFormat ("; cv={0}", cv); + + value.AppendFormat ("; d={0}; s={1}", Domain, Selector); + value.AppendFormat ("; t={0}", t); + + using (var stream = new DkimSignatureStream (CreateSigningContext ())) { + using (var filtered = new FilteredStream (stream)) { + filtered.Add (options.CreateNewLineFilter ()); + + for (int i = 0; i < count; i++) { + DkimVerifierBase.WriteHeaderRelaxed (options, filtered, sets[i].ArcAuthenticationResult, false); + DkimVerifierBase.WriteHeaderRelaxed (options, filtered, sets[i].ArcMessageSignature, false); + DkimVerifierBase.WriteHeaderRelaxed (options, filtered, sets[i].ArcSeal, false); + } + + DkimVerifierBase.WriteHeaderRelaxed (options, filtered, aar, false); + DkimVerifierBase.WriteHeaderRelaxed (options, filtered, ams, false); + + value.Append ("; b="); + + seal = new Header (HeaderId.ArcSeal, value.ToString ()); + DkimVerifierBase.WriteHeaderRelaxed (options, filtered, seal, true); + + filtered.Flush (); + } + + signature = stream.GenerateSignature (); + + seal.Value += Convert.ToBase64String (signature); + + return seal; + } + } + + async Task ArcSignAsync (FormatOptions options, MimeMessage message, IList headers, bool doAsync, CancellationToken cancellationToken) + { + ArcVerifier.GetArcHeaderSets (message, true, out ArcHeaderSet[] sets, out int count, out var errors); + AuthenticationResults authres; + int instance = count + 1; + string cv; + + // do not sign if there is already a failed/invalid ARC-Seal. + if (count > 0 && (errors & ArcValidationErrors.InvalidArcSealChainValidationValue) != 0) + return; + + options = options.Clone (); + options.NewLineFormat = NewLineFormat.Dos; + options.EnsureNewLine = true; + + if (doAsync) + authres = await GenerateArcAuthenticationResultsAsync (options, message, cancellationToken).ConfigureAwait (false); + else + authres = GenerateArcAuthenticationResults (options, message, cancellationToken); + + if (authres == null) + return; + + authres.Instance = instance; + + var aar = new Header (HeaderId.ArcAuthenticationResults, authres.ToString ()); + cv = "none"; + + if (count > 0) { + cv = "pass"; + + foreach (var method in authres.Results) { + if (method.Method.Equals ("arc", StringComparison.OrdinalIgnoreCase)) { + cv = method.Result; + break; + } + } + } + + var t = GetTimestamp (); + var ams = GenerateArcMessageSignature (options, message, instance, t, headers); + var seal = GenerateArcSeal (options, instance, cv, t, sets, count, aar, ams); + + message.Headers.Insert (0, aar); + message.Headers.Insert (0, ams); + message.Headers.Insert (0, seal); + } + + Task SignAsync (FormatOptions options, MimeMessage message, IList headers, bool doAsync, CancellationToken cancellationToken) + { + if (options == null) + throw new ArgumentNullException (nameof (options)); + + if (message == null) + throw new ArgumentNullException (nameof (message)); + + if (headers == null) + throw new ArgumentNullException (nameof (headers)); + + var fields = new string[headers.Count]; + var containsFrom = false; + + for (int i = 0; i < headers.Count; i++) { + if (headers[i] == null) + throw new ArgumentException ("The list of headers cannot contain null.", nameof (headers)); + + if (headers[i].Length == 0) + throw new ArgumentException ("The list of headers cannot contain empty string.", nameof (headers)); + + fields[i] = headers[i].ToLowerInvariant (); + + if (ArcShouldNotInclude.Contains (fields[i])) + throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i]), nameof (headers)); + + if (fields[i] == "from") + containsFrom = true; + } + + if (!containsFrom) + throw new ArgumentException ("The list of headers to sign MUST include the 'From' header.", nameof (headers)); + + return ArcSignAsync (options, message, fields, doAsync, cancellationToken); + } + + Task SignAsync (FormatOptions options, MimeMessage message, IList headers, bool doAsync, CancellationToken cancellationToken) + { + if (options == null) + throw new ArgumentNullException (nameof (options)); + + if (message == null) + throw new ArgumentNullException (nameof (message)); + + if (headers == null) + throw new ArgumentNullException (nameof (headers)); + + var fields = new string[headers.Count]; + var containsFrom = false; + + for (int i = 0; i < headers.Count; i++) { + if (headers[i] == HeaderId.Unknown) + throw new ArgumentException ("The list of headers to sign cannot include the 'Unknown' header.", nameof (headers)); + + fields[i] = headers[i].ToHeaderName ().ToLowerInvariant (); + + if (ArcShouldNotInclude.Contains (fields[i])) + throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i].ToHeaderName ()), nameof (headers)); + + if (headers[i] == HeaderId.From) + containsFrom = true; + } + + if (!containsFrom) + throw new ArgumentException ("The list of headers to sign MUST include the 'From' header.", nameof (headers)); + + return ArcSignAsync (options, message, fields, doAsync, cancellationToken); + } + + /// + /// Digitally sign and seal a message using ARC. + /// + ///+ /// Digitally signs and seals a message using ARC. + /// + ///+ /// + /// The formatting options. + /// The message to sign. + /// The list of header fields to sign. + /// The cancellation token. + ///+ ///
+ /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. + /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. + /// + public void Sign (FormatOptions options, MimeMessage message, IListheaders, CancellationToken cancellationToken = default (CancellationToken)) + { + SignAsync (options, message, headers, false, cancellationToken).GetAwaiter ().GetResult (); + } + + /// + /// Asynchronously digitally sign and seal a message using ARC. + /// + ///+ /// Asynchronously digitally signs and seals a message using ARC. + /// + ///+ /// + ///+ ///
An awaitable task. + /// The formatting options. + /// The message to sign. + /// The list of header fields to sign. + /// The cancellation token. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. + /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. + /// + public Task SignAsync (FormatOptions options, MimeMessage message, IListheaders, CancellationToken cancellationToken = default (CancellationToken)) + { + return SignAsync (options, message, headers, true, cancellationToken); + } + + /// + /// Digitally sign and seal a message using ARC. + /// + ///+ /// Digitally signs and seals a message using ARC. + /// + ///+ /// + /// The message to sign. + /// The list of header fields to sign. + /// The cancellation token. + ///+ ///
+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. + /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. + /// + public void Sign (MimeMessage message, IListheaders, CancellationToken cancellationToken = default (CancellationToken)) + { + Sign (FormatOptions.Default, message, headers, cancellationToken); + } + + /// + /// Asynchronously digitally sign and seal a message using ARC. + /// + ///+ /// Asynchronously digitally signs and seals a message using ARC. + /// + ///+ /// + ///+ ///
An awaitable task. + /// The message to sign. + /// The list of header fields to sign. + /// The cancellation token. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. + /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. + /// + public Task SignAsync (MimeMessage message, IListheaders, CancellationToken cancellationToken = default (CancellationToken)) + { + return SignAsync (FormatOptions.Default, message, headers, cancellationToken); + } + + /// + /// Digitally sign and seal a message using ARC. + /// + ///+ /// Digitally signs and seals a message using ARC. + /// + ///+ /// + /// The formatting options. + /// The message to sign. + /// The list of header fields to sign. + /// The cancellation token. + ///+ ///
+ /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. + /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. + /// + public void Sign (FormatOptions options, MimeMessage message, IListheaders, CancellationToken cancellationToken = default (CancellationToken)) + { + SignAsync (options, message, headers, false, cancellationToken).GetAwaiter ().GetResult (); + } + + /// + /// Asynchronously digitally sign and seal a message using ARC. + /// + ///+ /// Asynchronously digitally signs and seals a message using ARC. + /// + ///+ /// + ///+ ///
An awaitable task. + /// The formatting options. + /// The message to sign. + /// The list of header fields to sign. + /// The cancellation token. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. + /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. + /// + public Task SignAsync (FormatOptions options, MimeMessage message, IListheaders, CancellationToken cancellationToken = default (CancellationToken)) + { + return SignAsync (options, message, headers, true, cancellationToken); + } + + /// + /// Digitally sign and seal a message using ARC. + /// + ///+ /// Digitally signs and seals a message using ARC. + /// + ///+ /// + /// The message to sign. + /// The list of header fields to sign. + /// The cancellation token. + ///+ ///
+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. + /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. + /// + public void Sign (MimeMessage message, IListheaders, CancellationToken cancellationToken = default (CancellationToken)) + { + Sign (FormatOptions.Default, message, headers, cancellationToken); + } + + /// + /// Asynchronously digitally sign and seal a message using ARC. + /// + ///+ /// Asynchronously digitally signs and seals a message using ARC. + /// + ///+ /// + ///+ ///
An awaitable task. + /// The message to sign. + /// The list of header fields to sign. + /// The cancellation token. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. + /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. + /// + public Task SignAsync (MimeMessage message, IListheaders, CancellationToken cancellationToken = default (CancellationToken)) + { + return SignAsync (FormatOptions.Default, message, headers, cancellationToken); + } + } +} diff --git a/MimeKit/Cryptography/ArcVerifier.cs b/MimeKit/Cryptography/ArcVerifier.cs new file mode 100644 index 0000000000..726814916a --- /dev/null +++ b/MimeKit/Cryptography/ArcVerifier.cs @@ -0,0 +1,817 @@ +// +// ArcVerifier.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.Threading; +using System.Globalization; +using System.Threading.Tasks; +using System.Collections.Generic; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +using MimeKit.IO; + +namespace MimeKit.Cryptography { + /// + /// An ARC signature validation result. + /// + ///+ /// An ARC signature validation result. + /// + ///+ /// + public enum ArcSignatureValidationResult + { + ///+ ///
+ /// No signatures to validate. + /// + None, + + ///+ /// The validation passed. + /// + Pass, + + ///+ /// The validation failed. + /// + Fail + } + + ///+ /// An enumeration of possible ARC validation errors. + /// + ///+ /// An enumeration of possible ARC validation errors. + /// + [Flags] + public enum ArcValidationErrors + { + ///+ /// No errors. + /// + None = 0, + + ///+ /// One or more duplicate ARC-Authentication-Results headers exist. + /// + DuplicateArcAuthenticationResults = 1 << 0, + + ///+ /// One or more duplicate ARC-Message-Signature headers exist. + /// + DuplicateArcMessageSignature = 1 << 1, + + ///+ /// One or more duplicate ARC-Seal headers exist. + /// + DuplicateArcSeal = 1 << 2, + + ///+ /// One or more ARC-Authentication-Results headers are missing. + /// + MissingArcAuthenticationResults = 1 << 3, + + ///+ /// One or more ARC-Message-Signature headers are missing. + /// + MissingArcMessageSignature = 1 << 4, + + ///+ /// One or more ARC-Seal headers are missing. + /// + MissingArcSeal = 1 << 5, + + ///+ /// One or more ARC-Authentication-Results headers could not be parsed. + /// + InvalidArcAuthenticationResults = 1 << 6, + + ///+ /// One or more ARC-Message-Signature headers could not be parsed. + /// + InvalidArcMessageSignature = 1 << 7, + + ///+ /// One or more ARC-Seal headers could not be parsed. + /// + InvalidArcSeal = 1 << 8, + + ///+ /// One or more ARC-Seal headers have an invalid + InvalidArcSealChainValidationValue = 1 << 9, + + ///cv value. + ///+ /// One or more ARC-Seal headers are missing a + MissingArcSealChainValidationValue = 1 << 10, + + ///cv value. + ///+ /// Validation failed for the most recent ARC-Message-Signature header. + /// + MessageSignatureValidationFailed = 1 << 11, + + ///+ /// Validation failed for one or more of the ARC-Seal headers. + /// + SealValidationFailed = 1 << 12 + } + + ///+ /// An ARC header validation result. + /// + ///+ /// Represents an ARC header and its signature validation result. + /// + ///+ /// + public class ArcHeaderValidationResult + { + ///+ ///
+ /// Initialize a new instance of the + /// The ARC header. + ///class. + /// + /// + internal ArcHeaderValidationResult (Header header) + { + if (header == null) + throw new ArgumentNullException (nameof (header)); + + Header = header; + } + + ///is null . + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The ARC header. + /// The signature validation result. + ///. + /// + /// + public ArcHeaderValidationResult (Header header, ArcSignatureValidationResult signature) : this (header) + { + Signature = signature; + } + + ///is null . + ///+ /// Get the signature validation result. + /// + ///+ /// Gets the signature validation result. + /// + ///The signature validation result. + public ArcSignatureValidationResult Signature { + get; internal set; + } + + ///+ /// Get the ARC header. + /// + ///+ /// Gets the ARC header. + /// + ///The ARC header. + public Header Header { + get; private set; + } + } + + ///+ /// An ARC validation result. + /// + ///+ /// + ///Represents the results of ArcVerifier.Verify + /// or ArcVerifier.VerifyAsync. + ///If no ARC headers are found on the + ///, then the result will be + /// and both and + /// will be null .If ARC headers are found on the + ///but could not be parsed, then the + /// result will be and both + /// and will be null .+ /// + public class ArcValidationResult + { + internal ArcValidationResult () + { + Chain = ArcSignatureValidationResult.None; + } + + ///+ ///
+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The signature validation results of the entire chain. + /// The validation results for the ARC-Message-Signature header. + /// The validation results for the ARC-Seal headers. + public ArcValidationResult (ArcSignatureValidationResult chain, ArcHeaderValidationResult messageSignature, ArcHeaderValidationResult[] seals) + { + MessageSignature = messageSignature; + Seals = seals; + Chain = chain; + } + + ///. + /// + /// Get the validation results for the ARC-Message-Signature header. + /// + ///+ /// Gets the validation results for the ARC-Message-Signature header. + /// + ///The validation results for the ARC-Message-Signature header or + public ArcHeaderValidationResult MessageSignature { + get; internal set; + } + + ///null + /// if the ARC-Message-Signature header was not found.+ /// Get the validation results for each of the ARC-Seal headers. + /// + ///+ /// Gets the validation results for each of the ARC-Seal headers in + /// their instance order. + /// + ///The array of validation results for the ARC-Seal headers or + public ArcHeaderValidationResult[] Seals { + get; internal set; + } + + ///null + /// if no ARC-Seal headers were found.+ /// Get the signature validation results of the entire chain. + /// + ///+ /// Gets the signature validation results of the entire chain. + /// + ///+ /// + ///+ ///
The signature validation results of the entire chain. + public ArcSignatureValidationResult Chain { + get; internal set; + } + + ///+ /// Get the chain validation errors. + /// + ///+ /// Gets the chain validation errors. + /// + ///The chain validation errors. + public ArcValidationErrors ChainErrors { + get; internal set; + } + } + + class ArcHeaderSet + { + public Header ArcAuthenticationResult { get; private set; } + + public DictionaryArcMessageSignatureParameters { get; private set; } + public Header ArcMessageSignature { get; private set; } + + public Dictionary ArcSealParameters { get; private set; } + public Header ArcSeal { get; private set; } + + public bool Add (Header header, Dictionary parameters) + { + switch (header.Id) { + case HeaderId.ArcAuthenticationResults: + if (ArcAuthenticationResult != null) + return false; + + ArcAuthenticationResult = header; + break; + case HeaderId.ArcMessageSignature: + if (ArcMessageSignature != null) + return false; + + ArcMessageSignatureParameters = parameters; + ArcMessageSignature = header; + break; + case HeaderId.ArcSeal: + if (ArcSeal != null) + return false; + + ArcSealParameters = parameters; + ArcSeal = header; + break; + default: + return false; + } + + return true; + } + } + + /// + /// An ARC verifier. + /// + ///+ /// Validates Authenticated Received Chains. + /// + ///+ /// + public class ArcVerifier : DkimVerifierBase + { + ///+ ///
+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + ///. + /// + /// + /// The public key locator. + ///+ ///
+ /// + public ArcVerifier (IDkimPublicKeyLocator publicKeyLocator) : base (publicKeyLocator) + { + } + + static void ValidateArcMessageSignatureParameters (IDictionaryis null . + ///parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, + out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) + { + ValidateCommonSignatureParameters ("ARC-Message-Signature", parameters, out algorithm, out headerAlgorithm, out bodyAlgorithm, out d, out s, out q, out headers, out bh, out b, out maxLength); + } + + static void ValidateArcSealParameters (IDictionary parameters, out DkimSignatureAlgorithm algorithm, out string d, out string s, out string q, out string b) + { + ValidateCommonParameters ("ARC-Seal", parameters, out algorithm, out d, out s, out q, out b); + + if (parameters.TryGetValue ("h", out string h)) + throw new FormatException ("Malformed ARC-Seal header: the 'h' parameter tag is not allowed."); + } + + async Task VerifyArcMessageSignatureAsync (FormatOptions options, MimeMessage message, Header arcSignature, Dictionary parameters, bool doAsync, CancellationToken cancellationToken) + { + DkimCanonicalizationAlgorithm headerAlgorithm, bodyAlgorithm; + DkimSignatureAlgorithm signatureAlgorithm; + AsymmetricKeyParameter key; + string d, s, q, bh, b; + string[] headers; + int maxLength; + + ValidateArcMessageSignatureParameters (parameters, out signatureAlgorithm, out headerAlgorithm, out bodyAlgorithm, + out d, out s, out q, out headers, out bh, out b, out maxLength); + + if (!IsEnabled (signatureAlgorithm)) + return false; + + options = options.Clone (); + options.NewLineFormat = NewLineFormat.Dos; + + // first check the body hash (if that's invalid, then the entire signature is invalid) + if (!VerifyBodyHash (options, message, signatureAlgorithm, bodyAlgorithm, maxLength, bh)) + return false; + + if (doAsync) + key = await PublicKeyLocator.LocatePublicKeyAsync (q, d, s, cancellationToken).ConfigureAwait (false); + else + key = PublicKeyLocator.LocatePublicKey (q, d, s, cancellationToken); + + if ((key is RsaKeyParameters rsa) && rsa.Modulus.BitLength < MinimumRsaKeyLength) + return false; + + return VerifySignature (options, message, arcSignature, signatureAlgorithm, key, headers, headerAlgorithm, b); + } + + async Task VerifyArcSealAsync (FormatOptions options, ArcHeaderSet[] sets, int i, bool doAsync, CancellationToken cancellationToken) + { + DkimSignatureAlgorithm algorithm; + AsymmetricKeyParameter key; + string d, s, q, b; + + ValidateArcSealParameters (sets[i].ArcSealParameters, out algorithm, out d, out s, out q, out b); + + if (!IsEnabled (algorithm)) + return false; + + if (doAsync) + key = await PublicKeyLocator.LocatePublicKeyAsync (q, d, s, cancellationToken).ConfigureAwait (false); + else + key = PublicKeyLocator.LocatePublicKey (q, d, s, cancellationToken); + + if ((key is RsaKeyParameters rsa) && rsa.Modulus.BitLength < MinimumRsaKeyLength) + return false; + + options = options.Clone (); + options.NewLineFormat = NewLineFormat.Dos; + + using (var stream = new DkimSignatureStream (CreateVerifyContext (algorithm, key))) { + using (var filtered = new FilteredStream (stream)) { + filtered.Add (options.CreateNewLineFilter ()); + + for (int j = 0; j < i; j++) { + WriteHeaderRelaxed (options, filtered, sets[j].ArcAuthenticationResult, false); + WriteHeaderRelaxed (options, filtered, sets[j].ArcMessageSignature, false); + WriteHeaderRelaxed (options, filtered, sets[j].ArcSeal, false); + } + + WriteHeaderRelaxed (options, filtered, sets[i].ArcAuthenticationResult, false); + WriteHeaderRelaxed (options, filtered, sets[i].ArcMessageSignature, false); + + // now include the ARC-Seal header that we are verifying, + // but only after removing the "b=" signature value. + var seal = GetSignedSignatureHeader (sets[i].ArcSeal); + + WriteHeaderRelaxed (options, filtered, seal, true); + + filtered.Flush (); + } + + return stream.VerifySignature (b); + } + } + + internal static ArcSignatureValidationResult GetArcHeaderSets (MimeMessage message, bool throwOnError, out ArcHeaderSet[] sets, out int count, out ArcValidationErrors errors) + { + ArcHeaderSet set; + + errors = ArcValidationErrors.None; + sets = new ArcHeaderSet[50]; + count = 0; + + for (int i = 0; i < message.Headers.Count; i++) { + Dictionary parameters = null; + var header = message.Headers[i]; + int instance = 0; + string value; + + switch (header.Id) { + case HeaderId.ArcAuthenticationResults: + if (!AuthenticationResults.TryParse (header.RawValue, out AuthenticationResults authres)) { + if (throwOnError) + throw new FormatException ("Invalid ARC-Authentication-Results header."); + + errors |= ArcValidationErrors.InvalidArcAuthenticationResults; + break; + } + + if (!authres.Instance.HasValue) { + if (throwOnError) + throw new FormatException ("Missing instance tag in ARC-Authentication-Results header."); + + errors |= ArcValidationErrors.InvalidArcAuthenticationResults; + break; + } + + instance = authres.Instance.Value; + + if (instance < 1 || instance > 50) { + if (throwOnError) + throw new FormatException (string.Format (CultureInfo.InvariantCulture, "Invalid instance tag in ARC-Authentication-Results header: i={0}", instance)); + + errors |= ArcValidationErrors.InvalidArcAuthenticationResults; + instance = 0; + break; + } + break; + case HeaderId.ArcMessageSignature: + case HeaderId.ArcSeal: + try { + parameters = ParseParameterTags (header.Id, header.Value); + } catch { + if (throwOnError) + throw; + + if (header.Id == HeaderId.ArcMessageSignature) + errors |= ArcValidationErrors.InvalidArcMessageSignature; + else + errors |= ArcValidationErrors.InvalidArcSeal; + + break; + } + + if (!parameters.TryGetValue ("i", out value)) { + if (throwOnError) + throw new FormatException (string.Format (CultureInfo.InvariantCulture, "Missing instance tag in {0} header.", header.Id.ToHeaderName ())); + + if (header.Id == HeaderId.ArcMessageSignature) + errors |= ArcValidationErrors.InvalidArcMessageSignature; + else + errors |= ArcValidationErrors.InvalidArcSeal; + + break; + } + + if (!int.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out instance) || instance < 1 || instance > 50) { + if (throwOnError) + throw new FormatException (string.Format (CultureInfo.InvariantCulture, "Invalid instance tag in {0} header: i={1}", header.Id.ToHeaderName (), value)); + + if (header.Id == HeaderId.ArcMessageSignature) + errors |= ArcValidationErrors.InvalidArcMessageSignature; + else + errors |= ArcValidationErrors.InvalidArcSeal; + + instance = 0; + break; + } + break; + } + + if (instance == 0) + continue; + + set = sets[instance - 1]; + if (set == null) + sets[instance - 1] = set = new ArcHeaderSet (); + + if (!set.Add (header, parameters)) { + if (throwOnError) + throw new FormatException (string.Format (CultureInfo.InvariantCulture, "Duplicate {0} header for i={1}", header.Id.ToHeaderName (), instance)); + + switch (header.Id) { + case HeaderId.ArcAuthenticationResults: + errors |= ArcValidationErrors.DuplicateArcAuthenticationResults; + break; + case HeaderId.ArcMessageSignature: + errors |= ArcValidationErrors.DuplicateArcMessageSignature; + break; + case HeaderId.ArcSeal: + errors |= ArcValidationErrors.DuplicateArcSeal; + break; + } + } + + if (instance > count) + count = instance; + } + + if (count == 0) { + // there are no ARC sets + return ArcSignatureValidationResult.None; + } + + // verify that all ARC sets are complete + for (int i = 0; i < count; i++) { + set = sets[i]; + + if (set == null) { + if (throwOnError) + throw new FormatException (string.Format (CultureInfo.InvariantCulture, "Missing ARC headers for i={0}", i + 1)); + + if ((errors & ArcValidationErrors.InvalidArcAuthenticationResults) == 0) + errors |= ArcValidationErrors.MissingArcAuthenticationResults; + if ((errors & ArcValidationErrors.InvalidArcMessageSignature) == 0) + errors |= ArcValidationErrors.MissingArcMessageSignature; + if ((errors & ArcValidationErrors.InvalidArcSeal) == 0) + errors |= ArcValidationErrors.MissingArcSeal; + continue; + } + + if (set.ArcAuthenticationResult == null) { + if (throwOnError) + throw new FormatException (string.Format (CultureInfo.InvariantCulture, "Missing ARC-Authentication-Results header for i={0}", i + 1)); + + if ((errors & ArcValidationErrors.InvalidArcAuthenticationResults) == 0) + errors |= ArcValidationErrors.MissingArcAuthenticationResults; + } + + if (set.ArcMessageSignature == null) { + if (throwOnError) + throw new FormatException (string.Format (CultureInfo.InvariantCulture, "Missing ARC-Message-Signature header for i={0}", i + 1)); + + if ((errors & ArcValidationErrors.InvalidArcMessageSignature) == 0) + errors |= ArcValidationErrors.MissingArcMessageSignature; + } + + if (set.ArcSeal == null) { + if (throwOnError) + throw new FormatException (string.Format (CultureInfo.InvariantCulture, "Missing ARC-Seal header for i={0}", i + 1)); + + if ((errors & ArcValidationErrors.InvalidArcSeal) == 0) + errors |= ArcValidationErrors.MissingArcSeal; + continue; + } + + if (!set.ArcSealParameters.TryGetValue ("cv", out string cv)) { + if (throwOnError) + throw new FormatException (string.Format (CultureInfo.InvariantCulture, "Missing chain validation tag in ARC-Seal header for i={0}.", i + 1)); + + errors |= ArcValidationErrors.MissingArcSealChainValidationValue; + continue; + } + + // The "cv" value for all ARC-Seal header fields MUST NOT be + // "fail". For ARC Sets with instance values > 1, the values + // MUST be "pass". For the ARC Set with instance value = 1, the + // value MUST be "none". + if (!cv.Equals (i == 0 ? "none" : "pass", StringComparison.Ordinal)) + errors |= ArcValidationErrors.InvalidArcSealChainValidationValue; + } + + return errors == ArcValidationErrors.None ? ArcSignatureValidationResult.Pass : ArcSignatureValidationResult.Fail; + } + + async Task VerifyAsync (FormatOptions options, MimeMessage message, bool doAsync, CancellationToken cancellationToken) + { + const ArcValidationErrors ArcSealCvParamErrors = ArcValidationErrors.InvalidArcSealChainValidationValue | ArcValidationErrors.MissingArcSealChainValidationValue; + + if (options == null) + throw new ArgumentNullException (nameof (options)); + + if (message == null) + throw new ArgumentNullException (nameof (message)); + + var result = new ArcValidationResult (); + + switch (GetArcHeaderSets (message, false, out ArcHeaderSet[] sets, out int count, out var errors)) { + case ArcSignatureValidationResult.None: return result; + case ArcSignatureValidationResult.Fail: + result.Chain = ArcSignatureValidationResult.Fail; + result.ChainErrors = errors; + + // If the only error(s) are invalid or missing 'cv' values, ignore the errors for now. + if ((errors & ~ArcSealCvParamErrors) == 0) + break; + + return result; + default: + result.Chain = ArcSignatureValidationResult.Pass; + break; + } + + int newest = count - 1; + + result.Seals = new ArcHeaderValidationResult[count]; + + // validate the most recent Arc-Message-Signature + try { + var parameters = sets[newest].ArcMessageSignatureParameters; + var header = sets[newest].ArcMessageSignature; + + result.MessageSignature = new ArcHeaderValidationResult (header); + + if (await VerifyArcMessageSignatureAsync (options, message, header, parameters, doAsync, cancellationToken).ConfigureAwait (false)) { + result.MessageSignature.Signature = ArcSignatureValidationResult.Pass; + } else { + result.MessageSignature.Signature = ArcSignatureValidationResult.Fail; + result.ChainErrors |= ArcValidationErrors.MessageSignatureValidationFailed; + result.Chain = ArcSignatureValidationResult.Fail; + } + } catch { + result.MessageSignature.Signature = ArcSignatureValidationResult.Fail; + result.ChainErrors |= ArcValidationErrors.MessageSignatureValidationFailed; + result.Chain = ArcSignatureValidationResult.Fail; + } + + // validate all Arc-Seals starting with the most recent and proceeding to the oldest + for (int i = newest; i >= 0; i--) { + result.Seals[i] = new ArcHeaderValidationResult (sets[i].ArcSeal); + + try { + if (await VerifyArcSealAsync (options, sets, i, doAsync, cancellationToken).ConfigureAwait (false)) { + result.Seals[i].Signature = ArcSignatureValidationResult.Pass; + } else { + result.Seals[i].Signature = ArcSignatureValidationResult.Fail; + result.ChainErrors |= ArcValidationErrors.SealValidationFailed; + result.Chain = ArcSignatureValidationResult.Fail; + } + } catch { + result.Seals[i].Signature = ArcSignatureValidationResult.Fail; + result.ChainErrors |= ArcValidationErrors.SealValidationFailed; + result.Chain = ArcSignatureValidationResult.Fail; + } + } + + return result; + } + + /// + /// Verify the ARC signature chain. + /// + ///+ /// Verifies the ARC signature chain. + /// + ///+ /// + ///+ ///
The ARC validation result. + /// The formatting options. + /// The message to verify. + /// The cancellation token. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// The operation was canceled via the cancellation token. + /// + public ArcValidationResult Verify (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default (CancellationToken)) + { + return VerifyAsync (options, message, false, cancellationToken).GetAwaiter ().GetResult (); + } + + ///+ /// Asynchronously verify the ARC signature chain. + /// + ///+ /// Asynchronously verifies the ARC signature chain. + /// + ///+ /// + ///+ ///
The ARC validation result. + /// The formatting options. + /// The message to verify. + /// The cancellation token. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// The operation was canceled via the cancellation token. + /// + public TaskVerifyAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default (CancellationToken)) + { + return VerifyAsync (options, message, true, cancellationToken); + } + + /// + /// Verify the ARC signature chain. + /// + ///+ /// Verifies the ARC signature chain. + /// + ///+ /// + ///+ ///
The ARC validation result. + /// The message to verify. + /// The cancellation token. + ///+ /// + ///is null . + ///+ /// The operation was canceled via the cancellation token. + /// + public ArcValidationResult Verify (MimeMessage message, CancellationToken cancellationToken = default (CancellationToken)) + { + return Verify (FormatOptions.Default, message, cancellationToken); + } + + ///+ /// Asynchronously verify the ARC signature chain. + /// + ///+ /// Asynchronously verifies the ARC signature chain. + /// + ///+ /// + ///+ ///
The ARC validation result. + /// The message to verify. + /// The cancellation token. + ///+ /// + ///is null . + ///+ /// The operation was canceled via the cancellation token. + /// + public TaskVerifyAsync (MimeMessage message, CancellationToken cancellationToken = default (CancellationToken)) + { + return VerifyAsync (FormatOptions.Default, message, cancellationToken); + } + } +} diff --git a/MimeKit/Cryptography/AsymmetricAlgorithmExtensions.cs b/MimeKit/Cryptography/AsymmetricAlgorithmExtensions.cs index bf59ff25b3..a422250732 100644 --- a/MimeKit/Cryptography/AsymmetricAlgorithmExtensions.cs +++ b/MimeKit/Cryptography/AsymmetricAlgorithmExtensions.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 Xamarin Inc. (www.xamarin.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 @@ -41,7 +41,7 @@ namespace MimeKit.Cryptography /// +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.Text; +using System.Globalization; +using System.Collections.Generic; + +using MimeKit.Utils; + +namespace MimeKit.Cryptography { + /// + /// A parsed representation of the Authentication-Results header. + /// + ///+ /// The Authentication-Results header is used with electronic mail messages to + /// indicate the results of message authentication efforts. Any receiver-side + /// software, such as mail filters or Mail User Agents (MUAs), can use this header + /// field to relay that information in a convenient and meaningful way to users or + /// to make sorting and filtering decisions. + /// + public class AuthenticationResults + { + AuthenticationResults () + { + Results = new List(); + } + + /// + /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The authentication service identifier. + ///. + /// + /// + public AuthenticationResults (string authservid) : this () + { + if (authservid == null) + throw new ArgumentNullException (nameof (authservid)); + + AuthenticationServiceIdentifier = authservid; + } + + ///is null . + ///+ /// Get the authentication service identifier. + /// + ///+ /// + ///Gets the authentication service identifier. + ///The authentication service identifier is the + ///authserv-id token + /// as defined in rfc7601.The authserv-id token. + public string AuthenticationServiceIdentifier { + get; private set; + } + + ///+ /// Get or set the instance value. + /// + ///+ /// + ///Gets or sets the instance value. + ///This value will only be set if the + ///+ /// represents an ARC-Authentication-Results header value. The instance. + public int? Instance { + get; set; + } + + ///+ /// Get or set the Authentication-Results version. + /// + ///+ /// + ///Gets or sets the Authentication-Results version. + ///The version value is the + ///authres-version token as defined in + /// rfc7601.The authres-version token. + public int? Version { + get; set; + } + + ///+ /// Get the list of authentication results. + /// + ///+ /// Gets the list of authentication results. + /// + ///The list of authentication results. + public ListResults { + get; private set; + } + + internal void Encode (FormatOptions options, StringBuilder builder, int lineLength) + { + int space = 1; + + if (Instance.HasValue) { + var i = Instance.Value.ToString (CultureInfo.InvariantCulture); + + builder.AppendFormat (" i={0};", i); + lineLength += 4 + i.Length; + } + + if (AuthenticationServiceIdentifier != null) { + if (lineLength + space + AuthenticationServiceIdentifier.Length > options.MaxLineLength) { + builder.Append (options.NewLine); + builder.Append ('\t'); + lineLength = 1; + space = 0; + } + + if (space > 0) { + builder.Append (' '); + lineLength++; + } + + builder.Append (AuthenticationServiceIdentifier); + lineLength += AuthenticationServiceIdentifier.Length; + + if (Version.HasValue) { + var version = Version.Value.ToString (CultureInfo.InvariantCulture); + + if (lineLength + 1 + version.Length > options.MaxLineLength) { + builder.Append (options.NewLine); + builder.Append ('\t'); + lineLength = 1; + } else { + builder.Append (' '); + lineLength++; + } + + lineLength += version.Length; + builder.Append (version); + } + + builder.Append (';'); + lineLength++; + } + + if (Results.Count > 0) { + for (int i = 0; i < Results.Count; i++) { + if (i > 0) { + builder.Append (';'); + lineLength++; + } + + Results[i].Encode (options, builder, ref lineLength); + } + } else { + builder.Append (" none"); + } + + builder.Append (options.NewLine); + } + + /// + /// Serializes the + ///to a string. + /// + /// Creates a string-representation of the + ///. + /// The serialized string. + public override string ToString () + { + var builder = new StringBuilder (); + + if (Instance.HasValue) + builder.AppendFormat ("i={0}; ", Instance.Value.ToString (CultureInfo.InvariantCulture)); + + if (AuthenticationServiceIdentifier != null) { + builder.Append (AuthenticationServiceIdentifier); + + if (Version.HasValue) { + builder.Append (' '); + builder.Append (Version.Value.ToString (CultureInfo.InvariantCulture)); + } + + builder.Append ("; "); + } + + if (Results.Count > 0) { + for (int i = 0; i < Results.Count; i++) { + if (i > 0) + builder.Append ("; "); + builder.Append (Results[i]); + } + } else { + builder.Append ("none"); + } + + return builder.ToString (); + } + + static bool IsKeyword (byte c) + { + return (c >= (byte) 'A' && c <= (byte) 'Z') || + (c >= (byte) 'a' && c <= (byte) 'z') || + (c >= (byte) '0' && c <= (byte) '9') || + c == (byte) '-' || c == (byte) '_'; + } + + static bool SkipKeyword (byte[] text, ref int index, int endIndex) + { + int startIndex = index; + + while (index < endIndex && IsKeyword (text[index])) + index++; + + return index > startIndex; + } + + static bool SkipValue (byte[] text, ref int index, int endIndex, out bool quoted) + { + if (text[index] == (byte) '"') { + quoted = true; + + if (!ParseUtils.SkipQuoted (text, ref index, endIndex, false)) + return false; + } else { + quoted = false; + + if (!ParseUtils.SkipToken (text, ref index, endIndex)) + return false; + } + + return true; + } + + static bool SkipDomain (byte[] text, ref int index, int endIndex) + { + int startIndex = index; + + while (ParseUtils.SkipAtom (text, ref index, endIndex) && index < endIndex && text[index] == (byte) '.') + index++; + + if (index > startIndex && text[index - 1] != (byte) '.') + return true; + + return false; + } + + static bool SkipPropertyValue (byte[] text, ref int index, int endIndex, out bool quoted) + { + // pvalue := [CFWS] ( value / [ [ local-part ] "@" ] domain-name ) [CFWS] + // value := token / quoted-string + // token := 1*+ // tspecials := "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "/" / "[" / "]" / "?" / "=" + if (text[index] == (byte) '"') { + // quoted-string + quoted = true; + + if (!ParseUtils.SkipQuoted (text, ref index, endIndex, false)) + return false; + + return true; + } + + quoted = false; + + // Note: we're forced to accept even tspecials in the property value because they are used in the real-world. + // See https://github.com/jstedfast/MimeKit/issues/518 ('/') and https://github.com/jstedfast/MimeKit/issues/590 ('=') + // for details. + while (index < endIndex && !text[index].IsWhitespace() && text[index] != (byte) ';' && text[index] != (byte) '(') + index++; + + return true; + } + + static bool TryParseMethods (byte[] text, ref int index, int endIndex, bool throwOnError, AuthenticationResults authres) + { + string value; + bool quoted; + + while (index < endIndex) { + string srvid = null; + + method_token: + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) + break; + + int methodIndex = index; + + // skip the method name + if (!SkipKeyword (text, ref index, endIndex)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid method token at offset {0}", methodIndex), methodIndex, index); + + return false; + } + + // Note: Office365 seems to (sometimes) place a method-specific authserv-id token before each + // method. This block of code is here to handle that case. + // + // See https://github.com/jstedfast/MimeKit/issues/527 for details. + if (srvid == null && index < endIndex && text[index] == '.') { + index = methodIndex; + + if (!SkipDomain (text, ref index, endIndex)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid Office365 authserv-id token at offset {0}", methodIndex), methodIndex, index); + + return false; + } + + srvid = Encoding.UTF8.GetString (text, methodIndex, index - methodIndex); + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Missing semi-colon after Office365 authserv-id token at offset {0}", methodIndex), methodIndex, index); + + return false; + } + + if (text[index] != (byte) ';') { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token after Office365 authserv-id token at offset {0}", index), index, index); + + return false; + } + + // skip over ';' + index++; + + goto method_token; + } + + var method = Encoding.ASCII.GetString (text, methodIndex, index - methodIndex); + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (method != "none") { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index); + + return false; + } + + if (authres.Results.Count > 0) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid no-result token at offset {0}", methodIndex), methodIndex, index); + + return false; + } + + break; + } + + var resinfo = new AuthenticationMethodResult (method); + resinfo.Office365AuthenticationServiceIdentifier = srvid; + authres.Results.Add (resinfo); + + int tokenIndex; + + if (text[index] == (byte) '/') { + // optional method-version token + index++; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + tokenIndex = index; + + if (!ParseUtils.TryParseInt32 (text, ref index, endIndex, out int version)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid method-version token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + resinfo.Version = version; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index); + + return false; + } + } + + if (text[index] != (byte) '=') { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid methodspec token at offset {0}", methodIndex), methodIndex, index); + + return false; + } + + // skip over '=' + index++; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index); + + return false; + } + + tokenIndex = index; + + if (!SkipKeyword (text, ref index, endIndex)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid result token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + resinfo.Result = Encoding.ASCII.GetString (text, tokenIndex, index - tokenIndex); + + ParseUtils.SkipWhiteSpace (text, ref index, endIndex); + + if (index < endIndex && text[index] == (byte) '(') { + int commentIndex = index; + + if (!ParseUtils.SkipComment (text, ref index, endIndex)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete comment token at offset {0}", commentIndex), commentIndex, index); + + return false; + } + + commentIndex++; + + resinfo.ResultComment = Header.Unfold (Encoding.UTF8.GetString (text, commentIndex, (index - 1) - commentIndex)); + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + } + + if (index >= endIndex) + break; + + if (text[index] == (byte) ';') { + index++; + continue; + } + + // optional reasonspec or propspec + tokenIndex = index; + + if (!SkipKeyword (text, ref index, endIndex)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid reasonspec or propspec token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + value = Encoding.ASCII.GetString (text, tokenIndex, index - tokenIndex); + + if (value == "reason" || value == "action") { + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete {0}spec token at offset {1}", value, tokenIndex), tokenIndex, index); + + return false; + } + + if (text[index] != (byte) '=') { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid {0}spec token at offset {1}", value, tokenIndex), tokenIndex, index); + + return false; + } + + index++; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + int reasonIndex = index; + + if (index >= endIndex || !SkipValue (text, ref index, endIndex, out quoted)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid {0}spec value token at offset {1}", value, reasonIndex), reasonIndex, index); + + return false; + } + + var reason = Encoding.UTF8.GetString (text, reasonIndex, index - reasonIndex); + + if (quoted) + reason = MimeUtils.Unquote (reason); + + if (value == "action") + resinfo.Action = reason; + else + resinfo.Reason = reason; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) + break; + + if (text[index] == (byte) ';') { + index++; + continue; + } + + // optional propspec + tokenIndex = index; + + if (!SkipKeyword (text, ref index, endIndex)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + value = Encoding.ASCII.GetString (text, tokenIndex, index - tokenIndex); + } + + do { + // value is a propspec ptype token + var ptype = value; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + if (text[index] != (byte) '.') { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + index++; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + int propertyIndex = index; + + if (!SkipKeyword (text, ref index, endIndex)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid property token at offset {0}", propertyIndex), propertyIndex, index); + + return false; + } + + var property = Encoding.ASCII.GetString (text, propertyIndex, index - propertyIndex); + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + if (text[index] != (byte) '=') { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + index++; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + int valueIndex = index; + + if (index >= text.Length || !SkipPropertyValue (text, ref index, endIndex, out quoted)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + value = Encoding.UTF8.GetString (text, valueIndex, index - valueIndex); + + if (quoted) + value = MimeUtils.Unquote (value); + + var propspec = new AuthenticationMethodProperty (ptype, property, value, quoted); + resinfo.Properties.Add (propspec); + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex || text[index] == (byte) ';') + break; + + tokenIndex = index; + + if (!SkipKeyword (text, ref index, endIndex)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + value = Encoding.ASCII.GetString (text, tokenIndex, index - tokenIndex); + } while (true); + + // skip over ';' + index++; + } + + return true; + } + + static bool TryParse (byte[] text, ref int index, int endIndex, bool throwOnError, out AuthenticationResults authres) + { + int? instance = null; + string srvid = null; + string value; + bool quoted; + + authres = null; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + do { + int start = index; + + if (index >= endIndex || !SkipValue (text, ref index, endIndex, out quoted)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete authserv-id token at offset {0}", start), start, index); + + return false; + } + + value = Encoding.UTF8.GetString (text, start, index - start); + + if (quoted) { + // this can only be the authserv-id token + srvid = MimeUtils.Unquote (value); + } else { + // this could either be the authserv-id or it could be "i=#" (ARC instance) + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index < endIndex && text[index] == (byte) '=') { + // probably i=# + if (instance.HasValue) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid token at offset {0}", start), start, index); + + return false; + } + + if (value != "i") { + // Office 365 Authentication-Results do not include an authserv-id token, so this is probably a method. + // Rewind the parser and start over again with the assumption that the Authentication-Results only + // contains methods. + // + // See https://github.com/jstedfast/MimeKit/issues/490 for details. + + authres = new AuthenticationResults (); + index = 0; + + return TryParseMethods (text, ref index, endIndex, throwOnError, authres); + } + + // skip over '=' + index++; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + start = index; + + if (!ParseUtils.TryParseInt32 (text, ref index, endIndex, out int i)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid instance value at offset {0}", start), start, index); + + return false; + } + + instance = i; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Missing semi-colon after instance value at offset {0}", start), start, index); + + return false; + } + + if (text[index] != (byte) ';') { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token after instance value at offset {0}", index), index, index); + + return false; + } + + // skip over ';' + index++; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + } else { + srvid = value; + } + } + } while (srvid == null); + + authres = new AuthenticationResults (srvid) { Instance = instance }; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) + return true; + + if (text[index] != (byte) ';') { + // might be the authres-version token + int start = index; + + if (!ParseUtils.TryParseInt32 (text, ref index, endIndex, out int version)) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid authres-version at offset {0}", start), start, index); + + return false; + } + + authres.Version = version; + + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) + return true; + + if (text[index] != (byte) ';') { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unknown token at offset {0}", index), index, index); + + return false; + } + } + + // skip the ';' + index++; + + return TryParseMethods (text, ref index, endIndex, throwOnError, authres); + } + + /// + /// Try to parse the given input buffer into a new + ///instance. + /// + /// Parses an Authentication-Results header value from the supplied buffer starting at the given index + /// and spanning across the specified number of bytes. + /// + ///+ /// The input buffer. + /// The starting index of the input buffer. + /// The number of bytes in the input buffer to parse. + /// The parsed authentication results. + /// true if the authentication results were successfully parsed; otherwise,false .+ /// + ///is null . + ///+ /// + public static bool TryParse (byte[] buffer, int startIndex, int length, out AuthenticationResults authres) + { + ParseUtils.ValidateArguments (buffer, startIndex, length); + + int index = startIndex; + + return TryParse (buffer, ref index, startIndex + length, false, out authres); + } + + ///and do not specify + /// a valid range in the byte array. + /// + /// Try to parse the given input buffer into a new + ///instance. + /// + /// Parses an Authentication-Results header value from the supplied buffer. + /// + ///+ /// The input buffer. + /// The parsed authentication results. + /// true if the authentication results were successfully parsed; otherwise,false .+ /// + public static bool TryParse (byte[] buffer, out AuthenticationResults authres) + { + if (buffer == null) + throw new ArgumentNullException (nameof (buffer)); + + int index = 0; + + return TryParse (buffer, ref index, buffer.Length, false, out authres); + } + + ///is null . + ///+ /// Parse the specified input buffer into a new instance of the + ///class. + /// + /// Parses an Authentication-Results header value from the supplied buffer starting at the given index + /// and spanning across the specified number of bytes. + /// + ///The parsed + /// The input buffer. + /// The start index of the buffer. + /// The length of the buffer. + ///. + /// + ///is null . + ///+ /// + ///and do not specify + /// a valid range in the byte array. + /// + /// The + public static AuthenticationResults Parse (byte[] buffer, int startIndex, int length) + { + ParseUtils.ValidateArguments (buffer, startIndex, length); + + AuthenticationResults authres; + int index = startIndex; + + TryParse (buffer, ref index, startIndex + length, true, out authres); + + return authres; + } + + ///could not be parsed. + /// + /// Parse the specified input buffer into a new instance of the + ///class. + /// + /// Parses an Authentication-Results header value from the supplied buffer. + /// + ///The parsed + /// The input buffer. + ///. + /// + ///is null . + ///+ /// The + public static AuthenticationResults Parse (byte[] buffer) + { + if (buffer == null) + throw new ArgumentNullException (nameof (buffer)); + + AuthenticationResults authres; + int index = 0; + + TryParse (buffer, ref index, buffer.Length, true, out authres); + + return authres; + } + } + + ///could not be parsed. + /// + /// An authentication method results. + /// + ///+ /// An authentication method results. + /// + public class AuthenticationMethodResult + { + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The method used for authentication. + ///. + /// + /// + internal AuthenticationMethodResult (string method) + { + if (method == null) + throw new ArgumentNullException (nameof (method)); + + Properties = new Listis null . + ///(); + Method = method; + } + + /// + /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The method used for authentication. + /// The result of the authentication method. + ///. + /// + /// + public AuthenticationMethodResult (string method, string result) : this (method) + { + if (result == null) + throw new ArgumentNullException (nameof (result)); + + Result = result; + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Get the Office365 method-specific authserv-id. + /// + ///+ /// + ///Gets the Office365 method-specific authserv-id. + ///An authentication service identifier is the + ///authserv-id token + /// as defined in rfc7601.Instead of specifying a single authentication service identifier at the + /// beginning of the header value, Office365 seems to provide a different + /// authentication service identifier for each method. + ///The authserv-id token. + public string Office365AuthenticationServiceIdentifier { + get; internal set; + } + + ///+ /// Get the authentication method. + /// + ///+ /// Gets the authentication method. + /// + ///The authentication method. + public string Method { + get; private set; + } + + ///+ /// Get the authentication method version. + /// + ///+ /// Gets the authentication method version. + /// + ///The authentication method version. + public int? Version { + get; set; + } + + ///+ /// Get the authentication method results. + /// + ///+ /// Gets the authentication method results. + /// + ///The authentication method results. + public string Result { + get; internal set; + } + + ///+ /// Get the comment regarding the authentication method result. + /// + ///+ /// Gets the comment regarding the authentication method result. + /// + ///The comment regarding the authentication method result. + public string ResultComment { + get; set; + } + + ///+ /// Get the action taken for the authentication method result. + /// + ///+ /// Gets the action taken for the authentication method result. + /// + ///The action taken for the authentication method result. + public string Action { + get; internal set; + } + + ///+ /// Get the reason for the authentication method result. + /// + ///+ /// Gets the reason for the authentication method result. + /// + ///The reason for the authentication method result. + public string Reason { + get; set; + } + + ///+ /// Get the properties used by the authentication method. + /// + ///+ /// Gets the properties used by the authentication method. + /// + ///The properties used by the authentication method. + public ListProperties { + get; private set; + } + + internal void Encode (FormatOptions options, StringBuilder builder, ref int lineLength) + { + // try to put the entire result on 1 line + var complete = ToString (); + + if (complete.Length + 1 < options.MaxLineLength) { + // if it fits, it sits... + if (lineLength + complete.Length + 1 > options.MaxLineLength) { + builder.Append (options.NewLine); + builder.Append ('\t'); + lineLength = 1; + } else { + builder.Append (' '); + lineLength++; + } + + lineLength += complete.Length; + builder.Append (complete); + return; + } + + // Note: if we've made it this far, then we can't put everything on one line... + + var tokens = new List (); + tokens.Add (" "); + + if (Office365AuthenticationServiceIdentifier != null) { + tokens.Add (Office365AuthenticationServiceIdentifier); + tokens.Add (";"); + tokens.Add (" "); + } + + if (Version.HasValue) { + var version = Version.Value.ToString (CultureInfo.InvariantCulture); + + if (Method.Length + 1 + version.Length + 1 + Result.Length < options.MaxLineLength) { + tokens.Add ($"{Method}/{version}={Result}"); + } else if (Method.Length + 1 + version.Length < options.MaxLineLength) { + tokens.Add ($"{Method}/{version}"); + tokens.Add ("="); + tokens.Add (Result); + } else { + tokens.Add (Method); + tokens.Add ("/"); + tokens.Add (version); + tokens.Add ("="); + tokens.Add (Result); + } + } else { + if (Method.Length + 1 + Result.Length < options.MaxLineLength) { + tokens.Add ($"{Method}={Result}"); + } else { + // we will have to break this up into individual tokens + tokens.Add (Method); + tokens.Add ("="); + tokens.Add (Result); + } + } + + if (!string.IsNullOrEmpty (ResultComment)) { + tokens.Add (" "); + tokens.Add ($"({ResultComment})"); + } + + if (!string.IsNullOrEmpty (Reason)) { + var reason = MimeUtils.Quote (Reason); + + tokens.Add (" "); + + if ("reason=".Length + reason.Length < options.MaxLineLength) { + tokens.Add ($"reason={reason}"); + } else { + tokens.Add ("reason="); + tokens.Add (reason); + } + } else if (!string.IsNullOrEmpty (Action)) { + var action = MimeUtils.Quote (Action); + + tokens.Add (" "); + + if ("action=".Length + action.Length < options.MaxLineLength) { + tokens.Add ($"action={action}"); + } else { + tokens.Add ("action="); + tokens.Add (action); + } + } + + for (int i = 0; i < Properties.Count; i++) + Properties[i].AppendTokens (options, tokens); + + builder.AppendTokens (options, ref lineLength, tokens); + } + + /// + /// Serializes the + ///to a string. + /// + /// Creates a string-representation of the + ///. + /// The serialized string. + public override string ToString () + { + var builder = new StringBuilder (); + + if (Office365AuthenticationServiceIdentifier != null) { + builder.Append (Office365AuthenticationServiceIdentifier); + builder.Append ("; "); + } + + builder.Append (Method); + + if (Version.HasValue) { + builder.Append ('/'); + builder.Append (Version.Value.ToString (CultureInfo.InvariantCulture)); + } + + builder.Append ('='); + builder.Append (Result); + + if (!string.IsNullOrEmpty (ResultComment)) { + builder.Append (" ("); + builder.Append (ResultComment); + builder.Append (')'); + } + + if (!string.IsNullOrEmpty (Reason)) { + builder.Append (" reason="); + builder.Append (MimeUtils.Quote (Reason)); + } else if (!string.IsNullOrEmpty (Action)) { + builder.Append (" action="); + builder.Append (MimeUtils.Quote (Action)); + } + + for (int i = 0; i < Properties.Count; i++) { + builder.Append (' '); + builder.Append (Properties[i]); + } + + return builder.ToString (); + } + } + + ///+ /// An authentication method property. + /// + ///+ /// An authentication method property. + /// + public class AuthenticationMethodProperty + { + static readonly char[] TokenSpecials = ByteExtensions.TokenSpecials.ToCharArray (); + bool? quoted; + + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The property type. + /// The name of the property. + /// The value of the property. + ///. + /// true if the property value was originally quoted; otherwise,false . + ///+ /// + internal AuthenticationMethodProperty (string ptype, string property, string value, bool? quoted) + { + if (ptype == null) + throw new ArgumentNullException (nameof (ptype)); + + if (property == null) + throw new ArgumentNullException (nameof (property)); + + if (value == null) + throw new ArgumentNullException (nameof (value)); + + this.quoted = quoted; + PropertyType = ptype; + Property = property; + Value = value; + } + + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The property type. + /// The name of the property. + /// The value of the property. + ///. + /// + /// + public AuthenticationMethodProperty (string ptype, string property, string value) : this (ptype, property, value, null) + { + } + + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// Get the type of the property. + /// + ///+ /// Gets the type of the property. + /// + ///The type of the property. + public string PropertyType { + get; private set; + } + + ///+ /// Get the property name. + /// + ///+ /// Gets the property name. + /// + ///The name of the property. + public string Property { + get; private set; + } + + ///+ /// Get the property value. + /// + ///+ /// Gets the property value. + /// + ///The value of the property. + public string Value { + get; private set; + } + + internal void AppendTokens (FormatOptions options, Listtokens) + { + var quote = quoted.HasValue ? quoted.Value : Value.IndexOfAny (TokenSpecials) != -1; + var value = quote ? MimeUtils.Quote (Value) : Value; + + tokens.Add (" "); + + if (PropertyType.Length + 1 + Property.Length + 1 + value.Length < options.MaxLineLength) { + tokens.Add ($"{PropertyType}.{Property}={value}"); + } else if (PropertyType.Length + 1 + Property.Length + 1 < options.MaxLineLength) { + tokens.Add ($"{PropertyType}.{Property}="); + tokens.Add (value); + } else { + tokens.Add (PropertyType); + tokens.Add ("."); + tokens.Add (Property); + tokens.Add ("="); + tokens.Add (value); + } + } + + /// + /// Serializes the + ///to a string. + /// + /// Creates a string-representation of the + ///. + /// The serialized string. + public override string ToString () + { + var quote = quoted.HasValue ? quoted.Value : Value.IndexOfAny (TokenSpecials) != -1; + var value = quote ? MimeUtils.Quote (Value) : Value; + + return $"{PropertyType}.{Property}={value}"; + } + } +} diff --git a/MimeKit/Cryptography/BouncyCastleCertificateExtensions.cs b/MimeKit/Cryptography/BouncyCastleCertificateExtensions.cs index 4bb201f330..10d2e31394 100644 --- a/MimeKit/Cryptography/BouncyCastleCertificateExtensions.cs +++ b/MimeKit/Cryptography/BouncyCastleCertificateExtensions.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -34,10 +34,9 @@ using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.Smime; using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; -#if !PORTABLE using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; -#endif namespace MimeKit.Cryptography { /// @@ -48,7 +47,6 @@ namespace MimeKit.Cryptography { /// /// Convert a BouncyCastle certificate into an X509Certificate2. /// @@ -67,7 +65,11 @@ public static X509Certificate2 AsX509Certificate2 (this X509Certificate certific return new X509Certificate2 (certificate.GetEncoded ()); } -#endif + + internal static bool IsSelfSigned (this X509Certificate certificate) + { + return certificate.SubjectDN.Equivalent (certificate.IssuerDN); + } ////// Gets the issuer name info. @@ -89,7 +91,7 @@ public static string GetIssuerNameInfo (this X509Certificate certificate, DerObj // FIXME: GetValueList() should be fixed to return IList var list = certificate.IssuerDN.GetValueList (identifier); if (list.Count == 0) - return null; + return string.Empty; return (string) list[0]; } @@ -114,7 +116,7 @@ public static string GetSubjectNameInfo (this X509Certificate certificate, DerOb // FIXME: GetValueList() should be fixed to return IList var list = certificate.SubjectDN.GetValueList (identifier); if (list.Count == 0) - return null; + return string.Empty; return (string) list[0]; } @@ -168,13 +170,13 @@ public static string GetSubjectEmailAddress (this X509Certificate certificate) { var address = certificate.GetSubjectNameInfo (X509Name.EmailAddress); - if (address != null) + if (!string.IsNullOrEmpty (address)) return address; var alt = certificate.GetExtensionValue (X509Extensions.SubjectAlternativeName); if (alt == null) - return null; + return string.Empty; var seq = Asn1Sequence.GetInstance (Asn1Object.FromByteArray (alt.GetOctets ())); @@ -188,6 +190,16 @@ public static string GetSubjectEmailAddress (this X509Certificate certificate) return null; } + internal static string AsHex (this byte[] blob) + { + var hex = new StringBuilder (blob.Length * 2); + + for (int i = 0; i < blob.Length; i++) + hex.Append (blob[i].ToString ("x2")); + + return hex.ToString (); + } + /// /// Gets the fingerprint of the certificate. /// @@ -206,17 +218,46 @@ public static string GetFingerprint (this X509Certificate certificate) throw new ArgumentNullException (nameof (certificate)); var encoded = certificate.GetEncoded (); - var fingerprint = new StringBuilder (); var sha1 = new Sha1Digest (); - var data = new byte[20]; sha1.BlockUpdate (encoded, 0, encoded.Length); - sha1.DoFinal (data, 0); - for (int i = 0; i < data.Length; i++) - fingerprint.Append (data[i].ToString ("x2")); + var fingerprint = new byte[20]; + sha1.DoFinal (fingerprint, 0); + + return fingerprint.AsHex (); + } + + ///+ /// Gets the public key algorithm for the certificate. + /// + ///+ /// Gets the public key algorithm for the ceretificate. + /// + ///The public key algorithm. + /// The certificate. + ///+ /// + public static PublicKeyAlgorithm GetPublicKeyAlgorithm (this X509Certificate certificate) + { + if (certificate == null) + throw new ArgumentNullException (nameof (certificate)); + + var pubkey = certificate.GetPublicKey (); + + if (pubkey is DsaKeyParameters) + return PublicKeyAlgorithm.Dsa; + if (pubkey is RsaKeyParameters) + return PublicKeyAlgorithm.RsaGeneral; + if (pubkey is ElGamalKeyParameters) + return PublicKeyAlgorithm.ElGamalGeneral; + if (pubkey is ECKeyParameters) + return PublicKeyAlgorithm.EllipticCurve; + if (pubkey is DHKeyParameters) + return PublicKeyAlgorithm.DiffieHellman; - return fingerprint.ToString (); + return PublicKeyAlgorithm.None; } internal static X509KeyUsageFlags GetKeyUsageFlags (bool[] usage) @@ -319,7 +360,7 @@ internal static bool IsDelta (this X509Crl crl) { var critical = crl.GetCriticalExtensionOids (); - return critical.Contains (X509Extensions.DeltaCrlIndicator.Id); + return critical != null ? critical.Contains (X509Extensions.DeltaCrlIndicator.Id) : false; } } } diff --git a/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs b/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs index 2d2aa6e867..616060f332 100644 --- a/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs +++ b/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastis null . + ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -42,17 +42,20 @@ using Org.BouncyCastle.Pkix; using Org.BouncyCastle.X509; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.Smime; using Org.BouncyCastle.X509.Store; using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities.Collections; -using Org.BouncyCastle.Asn1.Smime; -using Org.BouncyCastle.Asn1.X509; using AttributeTable = Org.BouncyCastle.Asn1.Cms.AttributeTable; +using IssuerAndSerialNumber = Org.BouncyCastle.Asn1.Cms.IssuerAndSerialNumber; using MimeKit.IO; -using MimeKit.Utils; namespace MimeKit.Cryptography { @@ -64,30 +67,36 @@ namespace MimeKit.Cryptography /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new protected BouncyCastleSecureMimeContext () { - CheckCertificateRevocation = true; client = new HttpClient (); } ////// - /// Get or set whether or not certificate revocation should be checked when verifying signatures. + /// Get or set whether or not certificate revocation lists should be downloaded when verifying signatures. /// ///- /// - ///Gets or sets whether or not certificate revocation should be checked when verifying signatures. + ///Gets or sets whether or not certificate revocation lists should be downloaded when verifying + /// signatures. ///If enabled, the + ///will attempt to automatically download /// Certificate Revocation Lists (CRLs) from the internet based on the CRL Distribution Point extension on /// each certificate. Enabling this feature opens the client up to potential privacy risks. An attacker + /// can generate a custom X.509 certificate containing a CRL Distribution Point or OCSP URL pointing to an + /// attacker-controlled server, thereby getting a notification when the user decrypts the message or verifies + /// its digital signature. ///+ /// true if certificate revocation should be checked; otherwise,false .public bool CheckCertificateRevocation { get; set; } @@ -237,20 +246,40 @@ protected CmsRecipientCollection GetCmsRecipients (IEnumerable true if CRLs should be downloaded automatically; otherwise,false .m /// The timestamp. protected abstract void UpdateSecureMimeCapabilities (X509Certificate certificate, EncryptionAlgorithm[] algorithms, DateTime timestamp); - AttributeTable AddSecureMimeCapabilities (AttributeTable signedAttributes) + CmsAttributeTableGenerator AddSecureMimeCapabilities (AttributeTable signedAttributes) { - var attr = GetSecureMimeCapabilitiesAttribute (); + var attr = GetSecureMimeCapabilitiesAttribute (true); // populate our signed attributes with some S/MIME capabilities - return signedAttributes.Add (attr.AttrType, attr.AttrValues[0]); + return new DefaultSignedAttributeTableGenerator (signedAttributes.Add (attr.AttrType, attr.AttrValues[0])); } Stream Sign (CmsSigner signer, Stream content, bool encapsulate) { + var unsignedAttributes = new SimpleAttributeTableGenerator (signer.UnsignedAttributes); + var signedAttributes = AddSecureMimeCapabilities (signer.SignedAttributes); var signedData = new CmsSignedDataStreamGenerator (); + var digestOid = GetDigestOid (signer.DigestAlgorithm); + byte[] subjectKeyId = null; + + if (signer.SignerIdentifierType == SubjectIdentifierType.SubjectKeyIdentifier) { + var subjectKeyIdentifier = signer.Certificate.GetExtensionValue (X509Extensions.SubjectKeyIdentifier); + if (subjectKeyIdentifier != null) { + var id = (Asn1OctetString) Asn1Object.FromByteArray (subjectKeyIdentifier.GetOctets ()); + subjectKeyId = id.GetOctets (); + } + } - signedData.AddSigner (signer.PrivateKey, signer.Certificate, GetDigestOid (signer.DigestAlgorithm), - AddSecureMimeCapabilities (signer.SignedAttributes), signer.UnsignedAttributes); + if (signer.PrivateKey is RsaKeyParameters && signer.RsaSignaturePadding == RsaSignaturePadding.Pss) { + if (subjectKeyId == null) + signedData.AddSigner (signer.PrivateKey, signer.Certificate, RsassaPssOid, digestOid, signedAttributes, unsignedAttributes); + else + signedData.AddSigner (signer.PrivateKey, subjectKeyId, RsassaPssOid, digestOid, signedAttributes, unsignedAttributes); + } else if (subjectKeyId == null) { + signedData.AddSigner (signer.PrivateKey, signer.Certificate, digestOid, signedAttributes, unsignedAttributes); + } else { + signedData.AddSigner (signer.PrivateKey, subjectKeyId, digestOid, signedAttributes, unsignedAttributes); + } signedData.AddCertificates (signer.CertificateChain); @@ -270,7 +299,7 @@ Stream Sign (CmsSigner signer, Stream content, bool encapsulate) /// /// Cryptographically signs and encapsulates the content using the specified signer. /// - ///A new instance + /// A new /// The signer. /// The content. @@ -299,7 +328,7 @@ public override ApplicationPkcs7Mime EncapsulatedSign (CmsSigner signer, Stream ///instance /// containing the detached signature data. /// Cryptographically signs and encapsulates the content using the specified signer and digest algorithm. /// - ///A new instance + /// A new /// The signer. /// The digest algorithm to use for signing. @@ -340,7 +369,7 @@ public override ApplicationPkcs7Mime EncapsulatedSign (MailboxAddress signer, Di ///instance /// containing the detached signature data. /// Cryptographically signs the content using the specified signer. /// - ///A new instance + /// A new /// The signer. /// The content. @@ -369,7 +398,7 @@ public override ApplicationPkcs7Signature Sign (CmsSigner signer, Stream content ///instance /// containing the detached signature data. /// Cryptographically signs the content using the specified signer and digest algorithm. /// - ///A new instance + /// A new /// The signer. /// The digest algorithm to use for signing. @@ -414,30 +443,69 @@ X509Certificate GetCertificate (IX509Store store, SignerID signer) return GetCertificate (signer); } - PkixCertPath BuildCertPath (HashSet anchors, IX509Store certificates, IX509Store crls, X509Certificate certificate, DateTime? signingTime) + ///instance /// containing the detached signature data. + /// Build a certificate chain. + /// + ///+ /// + /// The certificate to build the chain for. + ///Builds a certificate chain for the provided certificate to include when signing. + ///This method is ideal for use with custom + ///+ /// implementations when it is desirable to include the certificate chain + /// in the signature. The certificate chain, including the specified certificate. + protected IListBuildCertificateChain (X509Certificate certificate) { - var intermediate = new X509CertificateStore (); - foreach (X509Certificate cert in certificates.GetMatches (null)) - intermediate.Add (cert); - var selector = new X509CertStoreSelector (); selector.Certificate = certificate; - var parameters = new PkixBuilderParameters (anchors, selector); + var intermediates = new X509CertificateStore (); + intermediates.Add (certificate); + + var parameters = new PkixBuilderParameters (GetTrustedAnchors (), selector); + parameters.ValidityModel = PkixParameters.PkixValidityModel; + parameters.AddStore (intermediates); parameters.AddStore (GetIntermediateCertificates ()); - parameters.AddStore (intermediate); + parameters.IsRevocationEnabled = false; + parameters.Date = new DateTimeObject (DateTime.UtcNow); + + var builder = new PkixCertPathBuilder (); + var result = builder.Build (parameters); + + var chain = new X509Certificate[result.CertPath.Certificates.Count]; - var localCrls = GetCertificateRevocationLists (); - parameters.AddStore (localCrls); + for (int i = 0; i < chain.Length; i++) + chain[i] = (X509Certificate) result.CertPath.Certificates[i]; + + return chain; + } + + PkixCertPath BuildCertPath (HashSet anchors, IX509Store certificates, IX509Store crls, X509Certificate certificate, DateTime signingTime) + { + var selector = new X509CertStoreSelector (); + selector.Certificate = certificate; + + var intermediates = new X509CertificateStore (); + intermediates.Add (certificate); + + foreach (X509Certificate cert in certificates.GetMatches (null)) + intermediates.Add (cert); + + var parameters = new PkixBuilderParameters (anchors, selector); + parameters.AddStore (intermediates); parameters.AddStore (crls); + parameters.AddStore (GetIntermediateCertificates ()); + parameters.AddStore (GetCertificateRevocationLists ()); + parameters.ValidityModel = PkixParameters.PkixValidityModel; parameters.IsRevocationEnabled = false; - if (signingTime.HasValue) - parameters.Date = new DateTimeObject (signingTime.Value); + if (signingTime != default (DateTime)) + parameters.Date = new DateTimeObject (signingTime); - var result = new PkixCertPathBuilder ().Build (parameters); + var builder = new PkixCertPathBuilder (); + var result = builder.Build (parameters); return result.CertPath; } @@ -456,7 +524,7 @@ PkixCertPath BuildCertPath (HashSet anchors, IX509Store certificates, IX509Store /// /// - protected static bool TryGetDigestAlgorithm (AlgorithmIdentifier identifier, out DigestAlgorithm algorithm) + internal protected static bool TryGetDigestAlgorithm (AlgorithmIdentifier identifier, out DigestAlgorithm algorithm) { if (identifier == null) throw new ArgumentNullException (nameof (identifier)); @@ -576,23 +644,14 @@ internal protected static bool TryGetEncryptionAlgorithm (AlgorithmIdentifier id return false; } - static DateTime ToAdjustedDateTime (DerUtcTime time) - { - //try { - // return time.ToAdjustedDateTime (); - //} catch { - return DateUtils.Parse (time.AdjustedTimeString, "yyyyMMddHHmmsszzz"); - //} - } - async Taskis null . ///DownloadCrlsOverHttpAsync (string location, Stream stream, bool doAsync, CancellationToken cancellationToken) { try { if (doAsync) { - using (var response = await client.GetAsync (location, cancellationToken)) - await response.Content.CopyToAsync (stream); + using (var response = await client.GetAsync (location, cancellationToken).ConfigureAwait (false)) + await response.Content.CopyToAsync (stream).ConfigureAwait (false); } else { -#if !NETSTANDARD && !PORTABLE +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 cancellationToken.ThrowIfCancellationRequested (); var request = (HttpWebRequest) WebRequest.Create (location); @@ -750,58 +809,22 @@ async Task GetDigitalSignaturesAsync (CmsSignedDataP foreach (SignerInformation signerInfo in store.GetSigners ()) { var certificate = GetCertificate (certificates, signerInfo.SignerID); - var signature = new SecureMimeDigitalSignature (signerInfo); - var algorithms = new List (); - DateTime? signedDate = null; - DigestAlgorithm digestAlgo; + var signature = new SecureMimeDigitalSignature (signerInfo, certificate); if (CheckCertificateRevocation && certificate != null) await DownloadCrlsAsync (certificate, doAsync, cancellationToken).ConfigureAwait (false); - if (signerInfo.SignedAttributes != null) { - Asn1EncodableVector vector = signerInfo.SignedAttributes.GetAll (CmsAttributes.SigningTime); - foreach (Org.BouncyCastle.Asn1.Cms.Attribute attr in vector) { - var signingTime = (DerUtcTime) ((DerSet) attr.AttrValues)[0]; - signature.CreationDate = ToAdjustedDateTime (signingTime); - signedDate = signature.CreationDate; - break; - } - - vector = signerInfo.SignedAttributes.GetAll (SmimeAttributes.SmimeCapabilities); - foreach (Org.BouncyCastle.Asn1.Cms.Attribute attr in vector) { - foreach (Asn1Sequence sequence in attr.AttrValues) { - for (int i = 0; i < sequence.Count; i++) { - var identifier = AlgorithmIdentifier.GetInstance (sequence[i]); - EncryptionAlgorithm algorithm; - - if (TryGetEncryptionAlgorithm (identifier, out algorithm)) - algorithms.Add (algorithm); - } - } - } - - signature.EncryptionAlgorithms = algorithms.ToArray (); - } - - if (TryGetDigestAlgorithm (signerInfo.DigestAlgorithmID, out digestAlgo)) - signature.DigestAlgorithm = digestAlgo; - if (certificate != null) { - signature.SignerCertificate = new SecureMimeDigitalCertificate (certificate); - if (algorithms.Count > 0 && signedDate != null) { - UpdateSecureMimeCapabilities (certificate, signature.EncryptionAlgorithms, signedDate.Value); - } else { - try { - Import (certificate); - } catch { - } - } + Import (certificate); + + if (signature.EncryptionAlgorithms.Length > 0 && signature.CreationDate != default (DateTime)) + UpdateSecureMimeCapabilities (certificate, signature.EncryptionAlgorithms, signature.CreationDate); } var anchors = GetTrustedAnchors (); try { - signature.Chain = BuildCertPath (anchors, certificates, crls, certificate, signedDate); + signature.Chain = BuildCertPath (anchors, certificates, crls, certificate, signature.CreationDate); } catch (Exception ex) { signature.ChainException = ex; } @@ -959,6 +982,73 @@ async Task GetDigitalSignaturesAsync (CmsSignedDataP return content; } + class CmsRecipientInfoGenerator : RecipientInfoGenerator + { + readonly CmsRecipient recipient; + + public CmsRecipientInfoGenerator (CmsRecipient recipient) + { + this.recipient = recipient; + } + + IWrapper CreateWrapper (AlgorithmIdentifier keyExchangeAlgorithm) + { + string name; + + if (PkcsObjectIdentifiers.IdRsaesOaep.Id.Equals (keyExchangeAlgorithm.Algorithm.Id, StringComparison.Ordinal)) { + var oaepParameters = RsaesOaepParameters.GetInstance (keyExchangeAlgorithm.Parameters); + name = "RSA//OAEPWITH" + DigestUtilities.GetAlgorithmName (oaepParameters.HashAlgorithm.Algorithm) + "ANDMGF1Padding"; + } else if (PkcsObjectIdentifiers.RsaEncryption.Id.Equals (keyExchangeAlgorithm.Algorithm.Id, StringComparison.Ordinal)) { + name = "RSA//PKCS1Padding"; + } else { + name = keyExchangeAlgorithm.Algorithm.Id; + } + + return WrapperUtilities.GetWrapper (name); + } + + byte[] GenerateWrappedKey (KeyParameter contentEncryptionKey, AlgorithmIdentifier keyEncryptionAlgorithm, AsymmetricKeyParameter publicKey, SecureRandom random) + { + var keyWrapper = CreateWrapper (keyEncryptionAlgorithm); + var keyBytes = contentEncryptionKey.GetKey (); + + keyWrapper.Init (true, new ParametersWithRandom (publicKey, random)); + + return keyWrapper.Wrap (keyBytes, 0, keyBytes.Length); + } + + public RecipientInfo Generate (KeyParameter contentEncryptionKey, SecureRandom random) + { + var tbs = Asn1Object.FromByteArray (recipient.Certificate.GetTbsCertificate ()); + var certificate = TbsCertificateStructure.GetInstance (tbs); + var publicKey = recipient.Certificate.GetPublicKey (); + var publicKeyInfo = certificate.SubjectPublicKeyInfo; + AlgorithmIdentifier keyEncryptionAlgorithm; + + if (publicKey is RsaKeyParameters && recipient.RsaEncryptionPadding?.Scheme == RsaEncryptionPaddingScheme.Oaep) { + keyEncryptionAlgorithm = recipient.RsaEncryptionPadding.GetAlgorithmIdentifier (); + } else { + keyEncryptionAlgorithm = publicKeyInfo.AlgorithmID; + } + + var encryptedKeyBytes = GenerateWrappedKey (contentEncryptionKey, keyEncryptionAlgorithm, publicKey, random); + RecipientIdentifier recipientIdentifier = null; + + if (recipient.RecipientIdentifierType == SubjectIdentifierType.SubjectKeyIdentifier) { + var subjectKeyIdentifier = recipient.Certificate.GetExtensionValue (X509Extensions.SubjectKeyIdentifier); + recipientIdentifier = new RecipientIdentifier (subjectKeyIdentifier); + } + + if (recipientIdentifier == null) { + var issuerAndSerial = new IssuerAndSerialNumber (certificate.Issuer, certificate.SerialNumber.Value); + recipientIdentifier = new RecipientIdentifier (issuerAndSerial); + } + + return new RecipientInfo (new KeyTransRecipientInfo (recipientIdentifier, keyEncryptionAlgorithm, + new DerOctetString (encryptedKeyBytes))); + } + } + Stream Envelope (CmsRecipientCollection recipients, Stream content) { var unique = new HashSet (); @@ -967,7 +1057,7 @@ Stream Envelope (CmsRecipientCollection recipients, Stream content) foreach (var recipient in recipients) { if (unique.Add (recipient.Certificate)) { - cms.AddKeyTransRecipient (recipient.Certificate); + cms.AddRecipientInfoGenerator (new CmsRecipientInfoGenerator (recipient)); count++; } } @@ -1041,7 +1131,7 @@ Stream Envelope (CmsRecipientCollection recipients, Stream content) /// /// Encrypts the specified content for the specified recipients. /// - ///A new instance + /// A new /// The recipients. /// The content. @@ -1070,7 +1160,7 @@ public override ApplicationPkcs7Mime Encrypt (CmsRecipientCollection recipients, ///instance /// containing the encrypted content. /// Encrypts the specified content for the specified recipients. /// - ///A new instance + /// A new /// The recipients. /// The content. @@ -1105,7 +1195,7 @@ public override MimePart Encrypt (IEnumerableinstance /// containing the encrypted data. recipients, Stream /// /// Decrypts the specified encryptedData. /// - ///The decrypted + ///. The decrypted /// The encrypted data. /// The cancellation token. ///. @@ -1147,22 +1237,22 @@ public override MimePart Encrypt (IEnumerable ///recipients, Stream /// Decrypts the specified encryptedData to an output stream. /// /// The encrypted data. - /// The output stream. + /// The output stream. /// /// ////// is null .-or- - ///+ /// is null ./// is null ./// An error occurred in the cryptographic message syntax subsystem. /// - public override void DecryptTo (Stream encryptedData, Stream output) + public override void DecryptTo (Stream encryptedData, Stream decryptedData) { if (encryptedData == null) throw new ArgumentNullException (nameof (encryptedData)); - if (output == null) - throw new ArgumentNullException (nameof (output)); + if (decryptedData == null) + throw new ArgumentNullException (nameof (decryptedData)); var parser = new CmsEnvelopedDataParser (encryptedData); var recipients = parser.GetRecipientInfos (); @@ -1174,8 +1264,7 @@ public override void DecryptTo (Stream encryptedData, Stream output) continue; var content = recipient.GetContentStream (key); - - content.ContentStream.CopyTo (output, 4096); + content.ContentStream.CopyTo (decryptedData, 4096); return; } @@ -1188,7 +1277,7 @@ public override void DecryptTo (Stream encryptedData, Stream output) ////// Exports the certificates for the specified mailboxes. /// - ///A new instance containing + /// A new /// The mailboxes. ///instance containing /// the exported keys. @@ -1221,9 +1310,9 @@ public override MimePart Export (IEnumerable ///mailboxes) throw new ArgumentException ("No mailboxes specified.", nameof (mailboxes)); var cms = new CmsSignedDataStreamGenerator (); - var memory = new MemoryBlockStream (); - cms.AddCertificates (certificates); + + var memory = new MemoryBlockStream (); cms.Open (memory).Dispose (); memory.Position = 0; diff --git a/MimeKit/Cryptography/CertificateNotFoundException.cs b/MimeKit/Cryptography/CertificateNotFoundException.cs index 87cba4d7d6..1d538e75da 100644 --- a/MimeKit/Cryptography/CertificateNotFoundException.cs +++ b/MimeKit/Cryptography/CertificateNotFoundException.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -45,7 +45,7 @@ public class CertificateNotFoundException : Exception { #if SERIALIZABLE /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -66,7 +66,7 @@ protected CertificateNotFoundException (SerializationInfo info, StreamingContext #endif /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -95,12 +95,9 @@ public CertificateNotFoundException (MailboxAddress mailbox, string message) : b [SecurityCritical] public override void GetObjectData (SerializationInfo info, StreamingContext context) { - if (info == null) - throw new ArgumentNullException (nameof (info)); + base.GetObjectData (info, context); info.AddValue ("Mailbox", Mailbox.ToString (true)); - - base.GetObjectData (info, context); } #endif diff --git a/MimeKit/Cryptography/CmsRecipient.cs b/MimeKit/Cryptography/CmsRecipient.cs index f05e8bcb76..aae14c1aeb 100644 --- a/MimeKit/Cryptography/CmsRecipient.cs +++ b/MimeKit/Cryptography/CmsRecipient.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -29,9 +29,7 @@ using Org.BouncyCastle.X509; -#if !PORTABLE && !NETSTANDARD using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; -#endif namespace MimeKit.Cryptography { /// @@ -46,7 +44,7 @@ namespace MimeKit.Cryptography { public class CmsRecipient { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new @@ -66,7 +64,7 @@ public CmsRecipient (X509Certificate certificate, SubjectIdentifierType recipien if (certificate == null) throw new ArgumentNullException (nameof (certificate)); - if (recipientIdentifierType == SubjectIdentifierType.IssuerAndSerialNumber) + if (recipientIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) RecipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; else RecipientIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; @@ -76,7 +74,7 @@ public CmsRecipient (X509Certificate certificate, SubjectIdentifierType recipien } ///based on the provided certificate. - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new @@ -102,14 +100,13 @@ public CmsRecipient (Stream stream, SubjectIdentifierType recipientIdentifierTyp if (stream == null) throw new ArgumentNullException (nameof (stream)); - if (recipientIdentifierType == SubjectIdentifierType.IssuerAndSerialNumber) + if (recipientIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) RecipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; else RecipientIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; var parser = new X509CertificateParser (); - RecipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; Certificate = parser.ReadCertificate (stream); if (Certificate == null) @@ -118,9 +115,8 @@ public CmsRecipient (Stream stream, SubjectIdentifierType recipientIdentifierTyp EncryptionAlgorithms = Certificate.GetEncryptionAlgorithms (); } -#if !PORTABLE ///, loading the certificate from the specified stream. - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new @@ -137,8 +133,7 @@ public CmsRecipient (Stream stream, SubjectIdentifierType recipientIdentifierTyp ///, loading the certificate from the specified file. /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// ///is an invalid file path. @@ -160,26 +155,26 @@ public CmsRecipient (string fileName, SubjectIdentifierType recipientIdentifierT if (fileName == null) throw new ArgumentNullException (nameof (fileName)); - var parser = new X509CertificateParser (); - - if (recipientIdentifierType == SubjectIdentifierType.IssuerAndSerialNumber) + if (recipientIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) RecipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; else RecipientIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; - using (var stream = File.OpenRead (fileName)) + using (var stream = File.OpenRead (fileName)) { + var parser = new X509CertificateParser (); + Certificate = parser.ReadCertificate (stream); + } if (Certificate == null) throw new FormatException (); EncryptionAlgorithms = Certificate.GetEncryptionAlgorithms (); } -#endif -#if !PORTABLE && !NETSTANDARD +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new @@ -199,7 +194,7 @@ public CmsRecipient (X509Certificate2 certificate, SubjectIdentifierType recipie if (certificate == null) throw new ArgumentNullException (nameof (certificate)); - if (recipientIdentifierType == SubjectIdentifierType.IssuerAndSerialNumber) + if (recipientIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) RecipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; else RecipientIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; @@ -245,5 +240,17 @@ public SubjectIdentifierType RecipientIdentifierType { public EncryptionAlgorithm[] EncryptionAlgorithms { get; set; } + + ///based on the provided certificate. + /// Get or set the RSA key encryption padding. + /// + ///+ /// + ///Gets or sets the padding to use for key encryption when + /// the + ///'s public key is an RSA key. The encryption padding scheme. + public RsaEncryptionPadding RsaEncryptionPadding { + get; set; + } } } diff --git a/MimeKit/Cryptography/CmsRecipientCollection.cs b/MimeKit/Cryptography/CmsRecipientCollection.cs index 6b704940ec..e50234cd4c 100644 --- a/MimeKit/Cryptography/CmsRecipientCollection.cs +++ b/MimeKit/Cryptography/CmsRecipientCollection.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -43,7 +43,7 @@ public class CmsRecipientCollection : ICollection readonly IList recipients; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -67,14 +67,14 @@ public int Count { } /// - /// Gets a value indicating whether this instance is read only. + /// Get a value indicating whether the ///is read only. /// /// A ///is never read-only. /// public bool IsReadOnly { - get; private set; + get { return false; } } /// true if this instance is read only; otherwise,false .diff --git a/MimeKit/Cryptography/CmsSigner.cs b/MimeKit/Cryptography/CmsSigner.cs index 9a3a8a51d2..47c8ee8d3c 100644 --- a/MimeKit/Cryptography/CmsSigner.cs +++ b/MimeKit/Cryptography/CmsSigner.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,16 +28,14 @@ using System.IO; using System.Collections.Generic; -#if !PORTABLE -using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; -#endif - using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Asn1.Cms; +using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; + namespace MimeKit.Cryptography { /// /// An S/MIME signer. @@ -50,11 +48,11 @@ namespace MimeKit.Cryptography { public class CmsSigner { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// @@ -62,28 +60,37 @@ public class CmsSigner { UnsignedAttributes = new AttributeTable (new DictionaryThe initial value of the ///will be set to - /// and both the + /// and both the /// and properties /// will be initialized to empty tables. ()); SignedAttributes = new AttributeTable (new Dictionary ()); - DigestAlgorithm = DigestAlgorithm.Sha1; + DigestAlgorithm = DigestAlgorithm.Sha256; } - static void CheckCertificateCanBeUsedForSigning (X509Certificate certificate) + static bool CanSign (X509Certificate certificate) { var flags = certificate.GetKeyUsageFlags (); if (flags != X509KeyUsageFlags.None && (flags & SecureMimeContext.DigitalSignatureKeyUsageFlags) == 0) - throw new ArgumentException ("The certificate cannot be used for signing."); + return false; + + return true; + } + + static void CheckCertificateCanBeUsedForSigning (X509Certificate certificate) + { + if (!CanSign (certificate)) + throw new ArgumentException ("The certificate cannot be used for signing.", nameof (certificate)); } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// /// The chain of certificates starting with the signer's certificate back to the root. /// The signer's private key. + /// The scheme used for identifying the signer certificate. ///The initial value of the ///will be set to - /// and both the + /// and both the /// and properties /// will be initialized to empty tables. /// - public CmsSigner (IEnumerable/// is null .-or- @@ -96,7 +103,7 @@ static void CheckCertificateCanBeUsedForSigning (X509Certificate certificate) ///-or- ////// is not a private key. chain, AsymmetricKeyParameter key) : this () + public CmsSigner (IEnumerable chain, AsymmetricKeyParameter key, SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) : this () { if (chain == null) throw new ArgumentNullException (nameof (chain)); @@ -114,21 +121,27 @@ public CmsSigner (IEnumerable chain, AsymmetricKeyParameter key if (!key.IsPrivate) throw new ArgumentException ("The key must be a private key.", nameof (key)); + if (signerIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) + SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; + else + SignerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; + Certificate = CertificateChain[0]; PrivateKey = key; } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// /// The signer's certificate. /// The signer's private key. + /// The scheme used for identifying the signer certificate. ///The initial value of the will - /// be set to and both the + /// The initial value of the ///will + /// be set to and both the /// and properties will be /// initialized to empty tables. /// - public CmsSigner (X509Certificate certificate, AsymmetricKeyParameter key) : this () + public CmsSigner (X509Certificate certificate, AsymmetricKeyParameter key, SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) : this () { if (certificate == null) throw new ArgumentNullException (nameof (certificate)); @@ -152,15 +165,21 @@ public CmsSigner (X509Certificate certificate, AsymmetricKeyParameter key) : thi if (!key.IsPrivate) throw new ArgumentException ("The key must be a private key.", nameof (key)); + if (signerIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) + SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; + else + SignerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; + CertificateChain = new X509CertificateChain (); CertificateChain.Add (certificate); Certificate = certificate; PrivateKey = key; } - void LoadPkcs12 (Stream stream, string password) + void LoadPkcs12 (Stream stream, string password, SubjectIdentifierType signerIdentifierType) { var pkcs12 = new Pkcs12Store (stream, password.ToCharArray ()); + bool hasPrivateKey = false; foreach (string alias in pkcs12.Aliases) { if (!pkcs12.IsKeyEntry (alias)) @@ -169,15 +188,18 @@ void LoadPkcs12 (Stream stream, string password) var chain = pkcs12.GetCertificateChain (alias); var key = pkcs12.GetKey (alias); - if (!key.Key.IsPrivate || chain.Length == 0) + if (!key.Key.IsPrivate) continue; - var flags = chain[0].Certificate.GetKeyUsageFlags (); + hasPrivateKey = true; - if (flags != X509KeyUsageFlags.None && (flags & SecureMimeContext.DigitalSignatureKeyUsageFlags) == 0) + if (chain.Length == 0 || !CanSign (chain[0].Certificate)) continue; - CheckCertificateCanBeUsedForSigning (chain[0].Certificate); + if (signerIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) + SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; + else + SignerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; CertificateChain = new X509CertificateChain (); Certificate = chain[0].Certificate; @@ -186,38 +208,43 @@ void LoadPkcs12 (Stream stream, string password) foreach (var entry in chain) CertificateChain.Add (entry.Certificate); - break; + return; } - if (PrivateKey == null) + if (!hasPrivateKey) throw new ArgumentException ("The stream did not contain a private key.", nameof (stream)); + + throw new ArgumentException ("The stream did not contain a certificate that could be used to create digital signatures.", nameof (stream)); } ////// is null .-or- @@ -139,7 +152,7 @@ public CmsSigner (IEnumerablechain, AsymmetricKeyParameter key /// -or- ////// is not a private key. - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// /// The raw certificate and key data in pkcs12 format. /// The password to unlock the stream. + /// The scheme used for identifying the signer certificate. ///Creates a new - ///, loading the X.509 certificate and private key /// from the specified stream. The initial value of the will - /// be set to and both the + /// The initial value of the ///will + /// be set to and both the /// and properties will be /// initialized to empty tables. /// ////// is null .-or- ////// is null .- /// ///does not contain a private key. + /// + /// does not contain a private key. -or- + ////// does not contain a certificate that could be used for signing. /// An I/O error occurred. /// - public CmsSigner (Stream stream, string password) : this () + public CmsSigner (Stream stream, string password, SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) : this () { if (stream == null) throw new ArgumentNullException (nameof (stream)); @@ -225,32 +252,35 @@ public CmsSigner (Stream stream, string password) : this () if (password == null) throw new ArgumentNullException (nameof (password)); - LoadPkcs12 (stream, password); + LoadPkcs12 (stream, password, signerIdentifierType); } -#if !PORTABLE ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// /// The raw certificate and key data in pkcs12 format. /// The password to unlock the stream. + /// The scheme used for identifying the signer certificate. ///Creates a new - ///, loading the X.509 certificate and private key /// from the specified file. The initial value of the will - /// be set to and both the + /// The initial value of the ///will + /// be set to and both the /// and properties will be /// initialized to empty tables. /// ////// is null .-or- ////// is null .- /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// + /// is a zero-length string, contains only white space, or + /// contains one or more invalid characters. -or- + ///+ /// does not contain a private key. -or- + ////// does not contain a certificate that could be used for signing. /// ///is an invalid file path. @@ -264,7 +294,7 @@ public CmsSigner (Stream stream, string password) : this () /// /// An I/O error occurred. /// - public CmsSigner (string fileName, string password) : this () + public CmsSigner (string fileName, string password, SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) : this () { if (fileName == null) throw new ArgumentNullException (nameof (fileName)); @@ -273,28 +303,28 @@ public CmsSigner (string fileName, string password) : this () throw new ArgumentNullException (nameof (password)); using (var stream = File.OpenRead (fileName)) - LoadPkcs12 (stream, password); + LoadPkcs12 (stream, password, signerIdentifierType); } -#endif -#if !PORTABLE && !NETSTANDARD +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// /// The signer's certificate. + /// The scheme used for identifying the signer certificate. ///The initial value of the will - /// be set to and both the + /// The initial value of the ///will + /// be set to and both the /// and properties will be /// initialized to empty tables. /// ///is null . ////// - public CmsSigner (X509Certificate2 certificate) : this () + public CmsSigner (X509Certificate2 certificate, SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) : this () { if (certificate == null) throw new ArgumentNullException (nameof (certificate)); @@ -307,6 +337,11 @@ public CmsSigner (X509Certificate2 certificate) : this () CheckCertificateCanBeUsedForSigning (cert); + if (signerIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) + SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; + else + SignerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; + CertificateChain = new X509CertificateChain (); CertificateChain.Add (cert); Certificate = cert; @@ -315,7 +350,7 @@ public CmsSigner (X509Certificate2 certificate) : this () #endif ///cannot be used for signing. /// - /// Gets the signer's certificate. + /// Get the signer's certificate. /// ////// The signer's certificate that contains a public key that can be used for @@ -327,7 +362,7 @@ public X509Certificate Certificate { } /// - /// Gets the certificate chain. + /// Get the certificate chain. /// ////// Gets the certificate chain. @@ -338,7 +373,7 @@ public X509CertificateChain CertificateChain { } /// - /// Gets or sets the digest algorithm. + /// Get or set the digest algorithm. /// ////// Specifies which digest algorithm to use to generate the @@ -350,7 +385,7 @@ public DigestAlgorithm DigestAlgorithm { } /// - /// Gets the private key. + /// Get the private key. /// ////// The private key used for signing. @@ -361,7 +396,50 @@ public AsymmetricKeyParameter PrivateKey { } /// - /// Gets or sets the signed attributes. + /// Get or set the RSA signature padding scheme. + /// + ///+ /// + ///Gets or sets the signature padding scheme to use for signing when + /// the + ///is an RSA key. The signature padding scheme. + [Obsolete ("Use RsaSignaturePadding instead.")] + public RsaSignaturePaddingScheme RsaSignaturePaddingScheme { + get { return RsaSignaturePadding?.Scheme ?? RsaSignaturePaddingScheme.Pkcs1; } + set { + switch (value) { + case RsaSignaturePaddingScheme.Pkcs1: RsaSignaturePadding = RsaSignaturePadding.Pkcs1; break; + case RsaSignaturePaddingScheme.Pss: RsaSignaturePadding = RsaSignaturePadding.Pss; break; + default: throw new ArgumentOutOfRangeException (nameof (value)); + } + } + } + + ///+ /// Get or set the RSA signature padding. + /// + ///+ /// + ///Gets or sets the signature padding to use for signing when + /// the + ///is an RSA key. The signature padding scheme. + public RsaSignaturePadding RsaSignaturePadding { + get; set; + } + + ///+ /// Gets the signer identifier type. + /// + ///+ /// Specifies how the certificate should be looked up on the recipient's end. + /// + ///The signer identifier type. + public SubjectIdentifierType SignerIdentifierType { + get; private set; + } + + ///+ /// Get or set the signed attributes. /// ////// A table of attributes that should be included in the signature. @@ -372,7 +450,7 @@ public AttributeTable SignedAttributes { } /// - /// Gets or sets the unsigned attributes. + /// Get or set the unsigned attributes. /// ////// A table of attributes that should not be signed in the signature, diff --git a/MimeKit/Cryptography/CryptographyContext.cs b/MimeKit/Cryptography/CryptographyContext.cs index 37188bb21e..60bed14d8d 100644 --- a/MimeKit/Cryptography/CryptographyContext.cs +++ b/MimeKit/Cryptography/CryptographyContext.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -44,7 +44,7 @@ public abstract class CryptographyContext : IDisposable { const string SubclassAndRegisterFormat = "You need to subclass {0} and then register it with MimeKit.Cryptography.CryptographyContext.Register()."; static Func SecureMimeContextFactory; - static Func OpenPgpContextFactory; + static Func PgpContextFactory; static readonly object mutex = new object (); EncryptionAlgorithm[] encryptionAlgorithmRank; @@ -54,7 +54,7 @@ public abstract class CryptographyContext : IDisposable int enabledDigestAlgorithms; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// + ///Creates a new @@ -108,7 +108,7 @@ protected CryptographyContext () #if NOT_YET ///. - /// Gets or sets a value indicating whether this ///allows online + /// Gets or sets a value indicating whether this allows online /// certificate retrieval. /// @@ -349,7 +349,7 @@ public bool IsEnabled (DigestAlgorithm algorithm) /// true if online certificate retrieval should be allowed; otherwise,false ./// Cryptographically signs the content using the specified signer and digest algorithm. /// - ///A new instance + /// A new /// The signer. /// The digest algorithm to use for signing. @@ -416,7 +416,7 @@ public bool IsEnabled (DigestAlgorithm algorithm) ///instance /// containing the detached signature data. /// Encrypts the specified content for the specified recipients. /// - ///A new + ///instance containing the encrypted data. A new /// The recipients. /// The content. ///instance containing the encrypted data. @@ -435,7 +435,7 @@ public bool IsEnabled (DigestAlgorithm algorithm) /// ////// Decrypts the specified encryptedData. /// - ///The decrypted + ///. The decrypted /// The encrypted data. /// The cancellation token. ///. @@ -467,7 +467,7 @@ public bool IsEnabled (DigestAlgorithm algorithm) /// - protected DkimSigner (string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) + protected DkimSigner (string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : base (domain, selector, algorithm) { - if (domain == null) - throw new ArgumentNullException (nameof (domain)); - - if (selector == null) - throw new ArgumentNullException (nameof (selector)); - - SignatureAlgorithm = algorithm; - Selector = selector; - Domain = domain; } ////// Exports the keys for the specified mailboxes. /// - ///A new + ///instance containing the exported keys. A new /// The mailboxes. ///instance containing the exported keys. /// protected override CmsSigner GetCmsSigner (MailboxAddress mailbox, DigestAlgorithm digestAlgo) { + AsymmetricKeyParameter privateKey = null; + X509Certificate certificate = null; + foreach (var record in dbase.Find (mailbox, DateTime.UtcNow, true, CmsSignerFields)) { - if (record.KeyUsage != X509KeyUsageFlags.None && (record.KeyUsage & SecureMimeContext.DigitalSignatureKeyUsageFlags) == 0) + if (record.KeyUsage != X509KeyUsageFlags.None && (record.KeyUsage & DigitalSignatureKeyUsageFlags) == 0) continue; - var signer = new CmsSigner (record.Certificate, record.PrivateKey); + certificate = record.Certificate; + privateKey = record.PrivateKey; + break; + } + + if (certificate != null && privateKey != null) { + var signer = new CmsSigner (BuildCertificateChain (certificate), privateKey); signer.DigestAlgorithm = digestAlgo; return signer; @@ -547,29 +579,61 @@ public override void Import (Stream stream, string password) startIndex = 1; } - for (int i = startIndex; i < chain.Length; i++) { - if ((record = dbase.Find (chain[i].Certificate, X509CertificateRecordFields.Id)) == null) - dbase.Add (new X509CertificateRecord (chain[i].Certificate) { IsTrusted = true }); - } + for (int i = startIndex; i < chain.Length; i++) + Import (chain[i].Certificate, true); } else if (pkcs12.IsCertificateEntry (alias)) { var entry = pkcs12.GetCertificate (alias); - if ((record = dbase.Find (entry.Certificate, X509CertificateRecordFields.Id)) == null) - dbase.Add (new X509CertificateRecord (entry.Certificate) { IsTrusted = true }); + Import (entry.Certificate, true); } } } -#endregion + #endregion + + ///is null . @@ -495,12 +495,12 @@ protected virtual void Dispose (bool disposing) } ///- /// Releases all resources used by the - ///object. + /// Releases all resources used by the object. /// Call + ///when you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the so - /// the garbage collector can reclaim the memory that the was occupying. Call public void Dispose () { Dispose (true); @@ -542,28 +542,15 @@ public static CryptographyContext Create (string protocol) if (SecureMimeContextFactory != null) return SecureMimeContextFactory (); -#if !PORTABLE - if (!SqliteCertificateDatabase.IsAvailable) { - const string format = "SQLite is not available. Either install the {0} nuget or subclass MimeKit.Cryptography.SecureMimeContext and register it with MimeKit.Cryptography.CryptographyContext.Register()."; -#if NETSTANDARD - throw new NotSupportedException (string.Format (format, "Microsoft.Data.Sqlite")); -#else - throw new NotSupportedException (string.Format (format, "System.Data.SQLite")); -#endif - } - return new DefaultSecureMimeContext (); -#else - throw new NotSupportedException (string.Format (SubclassAndRegisterFormat, "MimeKit.Cryptography.SecureMimeContext")); -#endif case "application/x-pgp-signature": case "application/pgp-signature": case "application/x-pgp-encrypted": case "application/pgp-encrypted": case "application/x-pgp-keys": case "application/pgp-keys": - if (OpenPgpContextFactory != null) - return OpenPgpContextFactory (); + if (PgpContextFactory != null) + return PgpContextFactory (); throw new NotSupportedException (string.Format (SubclassAndRegisterFormat, "MimeKit.Cryptography.OpenPgpContext or MimeKit.Cryptography.GnuPGContext")); default: @@ -572,22 +559,6 @@ public static CryptographyContext Create (string protocol) } } -#if PORTABLE - static ConstructorInfo GetConstructor (TypeInfo type) - { - foreach (var ctor in type.DeclaredConstructors) { - var args = ctor.GetParameters (); - - if (args.Length != 0) - continue; - - return ctor; - } - - return null; - } -#endif - ///when you are finished using the . The + /// method leaves the in an unusable state. After + /// calling , you must release all references to the so + /// the garbage collector can reclaim the memory that the was occupying. /// Registers a default @@ -611,17 +582,13 @@ public static void Register (Type type) if (type == null) throw new ArgumentNullException (nameof (type)); -#if PORTABLE || NETSTANDARD +#if NETSTANDARD1_3 || NETSTANDARD1_6 var info = type.GetTypeInfo (); #else var info = type; #endif - -#if PORTABLE - var ctor = GetConstructor (info); -#else var ctor = type.GetConstructor (new Type[0]); -#endif + if (ctor == null) throw new ArgumentException ("The specified type must have a parameterless constructor.", nameof (type)); @@ -629,9 +596,9 @@ public static void Register (Type type) lock (mutex) { SecureMimeContextFactory = () => (SecureMimeContext) ctor.Invoke (new object[0]); } - } else if (info.IsSubclassOf (typeof (OpenPgpContext))) { + } else if (info.IsSubclassOf (typeof (OpenPgpContextBase))) { lock (mutex) { - OpenPgpContextFactory = () => (OpenPgpContext) ctor.Invoke (new object[0]); + PgpContextFactory = () => (OpenPgpContextBase) ctor.Invoke (new object[0]); } } else { throw new ArgumentException ("The specified type must be a subclass of SecureMimeContext or OpenPgpContext.", nameof (type)); @@ -668,13 +635,13 @@ public static void Register (Funcor . /// factory) /// /// - public static void Register (Funcis null . ///factory) + public static void Register (Func factory) { if (factory == null) throw new ArgumentNullException(nameof (factory)); lock (mutex) { - OpenPgpContextFactory = factory; + PgpContextFactory = factory; } } } diff --git a/MimeKit/Cryptography/DbExtensions.cs b/MimeKit/Cryptography/DbExtensions.cs index 7f12c89085..0d66fa1015 100644 --- a/MimeKit/Cryptography/DbExtensions.cs +++ b/MimeKit/Cryptography/DbExtensions.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Cryptography/DefaultSecureMimeContext.cs b/MimeKit/Cryptography/DefaultSecureMimeContext.cs index bdec9eaf6f..ceee70cf56 100644 --- a/MimeKit/Cryptography/DefaultSecureMimeContext.cs +++ b/MimeKit/Cryptography/DefaultSecureMimeContext.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -68,7 +68,7 @@ static DefaultSecureMimeContext () { string path; -#if !NETSTANDARD +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 if (Path.DirectorySeparatorChar == '\\') { var appData = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); path = Path.Combine (appData, "Roaming\\mimekit"); @@ -83,8 +83,20 @@ static DefaultSecureMimeContext () DefaultDatabasePath = Path.Combine (path, "smime.db"); } + static void CheckIsAvailable () + { + if (!SqliteCertificateDatabase.IsAvailable) { + const string format = "SQLite is not available. Install the {0} nuget."; +#if NETSTANDARD1_3 || NETSTANDARD1_6 + throw new NotSupportedException (string.Format (format, "Microsoft.Data.Sqlite")); +#else + throw new NotSupportedException (string.Format (format, "System.Data.SQLite")); +#endif + } + } + /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Allows the program to specify its own location for the SQLite database. If the file does not exist, @@ -118,16 +130,15 @@ public DefaultSecureMimeContext (string fileName, string password) if (password == null) throw new ArgumentNullException (nameof (password)); + CheckIsAvailable (); + var dir = Path.GetDirectoryName (fileName); var exists = File.Exists (fileName); if (!string.IsNullOrEmpty (dir) && !Directory.Exists (dir)) Directory.CreateDirectory (dir); - if (SqliteCertificateDatabase.IsAvailable) - dbase = new SqliteCertificateDatabase (fileName, password); - else - throw new NotSupportedException ("Mono.Data.Sqlite is not available."); + dbase = new SqliteCertificateDatabase (fileName, password); if (!exists) { // TODO: initialize our dbase with some root CA certificates. @@ -135,7 +146,7 @@ public DefaultSecureMimeContext (string fileName, string password) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Allows the program to specify its own password for the default database. @@ -156,7 +167,7 @@ public DefaultSecureMimeContext (string password) : this (DefaultDatabasePath, p } ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Not recommended for production use as the password to unlock the private keys is hard-coded. @@ -176,7 +187,7 @@ public DefaultSecureMimeContext () : this (DefaultDatabasePath, "no.secret") } ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// This constructor is useful for supplying a custom . @@ -290,9 +301,8 @@ protected override Org.BouncyCastle.Utilities.Collections.HashSet GetTrustedAnch keyUsage[(int) X509KeyUsageBits.KeyCertSign] = true; selector.KeyUsage = keyUsage; - foreach (var record in dbase.Find (selector, true, X509CertificateRecordFields.Certificate)) { + foreach (var record in dbase.Find (selector, true, X509CertificateRecordFields.Certificate)) anchors.Add (new TrustAnchor (record.Certificate, null)); - } return anchors; } @@ -308,6 +318,19 @@ protected override Org.BouncyCastle.Utilities.Collections.HashSet GetTrustedAnch /// The intermediate certificates. protected override IX509Store GetIntermediateCertificates () { + //var intermediates = new X509CertificateStore (); + //var selector = new X509CertStoreSelector (); + //var keyUsage = new bool[9]; + + //keyUsage[(int) X509KeyUsageBits.KeyCertSign] = true; + //selector.KeyUsage = keyUsage; + + //foreach (var record in dbase.Find (selector, false, X509CertificateRecordFields.Certificate)) { + // if (!record.Certificate.IsSelfSigned ()) + // intermediates.Add (record.Certificate); + //} + + //return intermediates; return dbase; } @@ -393,11 +416,20 @@ protected override CmsRecipient GetCmsRecipient (MailboxAddress mailbox) ///+ /// Imports a certificate. + /// + ///+ /// + /// The certificate. + ///Imports the certificate. + ///If the certificate already exists in the database and + ///is true , + /// then the IsTrusted state is updated otherwise the certificate is added to the database with the + /// specified trust.true if the certificate is trusted; otherwise,false . + ///+ /// + public void Import (X509Certificate certificate, bool trusted) + { + if (certificate == null) + throw new ArgumentNullException (nameof (certificate)); + + X509CertificateRecord record; + + if ((record = dbase.Find (certificate, X509CertificateRecordFields.Id | X509CertificateRecordFields.Trusted)) != null) { + if (trusted && !record.IsTrusted) { + record.IsTrusted = trusted; + dbase.Update (record, X509CertificateRecordFields.Trusted); + } + + return; + } + + record = new X509CertificateRecord (certificate); + record.IsTrusted = trusted; + dbase.Add (record); + } ///is null . + ////// Imports a DER-encoded certificate stream. /// ///- /// Imports all of the certificates in the DER-encoded stream. + /// Imports the certificate(s). /// /// The raw certificate(s). - ///true if the certificates are trusted. + ///true if the certificates are trusted; othewrwise,false . ////// @@ -580,21 +644,8 @@ public void Import (Stream stream, bool trusted) var parser = new X509CertificateParser (); - foreach (X509Certificate certificate in parser.ReadCertificates (stream)) { - X509CertificateRecord record; - - if ((record = dbase.Find (certificate, X509CertificateRecordFields.Id | X509CertificateRecordFields.Trusted)) != null) { - if (trusted && !record.IsTrusted) { - record.IsTrusted = trusted; - dbase.Update (record, X509CertificateRecordFields.Trusted); - } - continue; - } - - record = new X509CertificateRecord (certificate); - record.IsTrusted = trusted; - dbase.Add (record); - } + foreach (X509Certificate certificate in parser.ReadCertificates (stream)) + Import (certificate, trusted); } ///is null . ///diff --git a/MimeKit/Cryptography/DigestAlgorithm.cs b/MimeKit/Cryptography/DigestAlgorithm.cs index 4a0a16815b..154f73a237 100644 --- a/MimeKit/Cryptography/DigestAlgorithm.cs +++ b/MimeKit/Cryptography/DigestAlgorithm.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Cryptography/DigitalSignatureCollection.cs b/MimeKit/Cryptography/DigitalSignatureCollection.cs index 6aff3ad713..211dfe46f3 100644 --- a/MimeKit/Cryptography/DigitalSignatureCollection.cs +++ b/MimeKit/Cryptography/DigitalSignatureCollection.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -41,7 +41,7 @@ namespace MimeKit.Cryptography { public class DigitalSignatureCollection : ReadOnlyCollection { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/Cryptography/DigitalSignatureVerifyException.cs b/MimeKit/Cryptography/DigitalSignatureVerifyException.cs index 1bb0cc6ea7..cd491ebcd4 100644 --- a/MimeKit/Cryptography/DigitalSignatureVerifyException.cs +++ b/MimeKit/Cryptography/DigitalSignatureVerifyException.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,7 +32,7 @@ namespace MimeKit.Cryptography { /// - /// An exception that is thrown when an error occurrs in ///. + /// An exception that is thrown when an error occurrs in . /// /// For more information about the error condition, check the property. @@ -44,7 +44,7 @@ public class DigitalSignatureVerifyException : Exception { #if SERIALIZABLE /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -61,7 +61,7 @@ protected DigitalSignatureVerifyException (SerializationInfo info, StreamingCont #endif /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -75,7 +75,7 @@ public DigitalSignatureVerifyException (long keyId, string message, Exception in } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -88,7 +88,7 @@ public DigitalSignatureVerifyException (long keyId, string message) : base (mess } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -100,7 +100,7 @@ public DigitalSignatureVerifyException (string message, Exception innerException } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -127,12 +127,9 @@ public DigitalSignatureVerifyException (string message) : base (message) [SecurityCritical] public override void GetObjectData (SerializationInfo info, StreamingContext context) { - if (info == null) - throw new ArgumentNullException (nameof (info)); + base.GetObjectData (info, context); info.AddValue ("KeyId", KeyId, typeof (long?)); - - base.GetObjectData (info, context); } #endif diff --git a/MimeKit/Cryptography/DkimBodyFilter.cs b/MimeKit/Cryptography/DkimBodyFilter.cs index 7cce65ccb0..b675cc3cbf 100644 --- a/MimeKit/Cryptography/DkimBodyFilter.cs +++ b/MimeKit/Cryptography/DkimBodyFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -60,7 +60,7 @@ abstract class DkimBodyFilter : MimeFilterBase protected int EmptyLines; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new + ///. diff --git a/MimeKit/Cryptography/DkimCanonicalizationAlgorithm.cs b/MimeKit/Cryptography/DkimCanonicalizationAlgorithm.cs index 090f9997e6..67f0f75019 100644 --- a/MimeKit/Cryptography/DkimCanonicalizationAlgorithm.cs +++ b/MimeKit/Cryptography/DkimCanonicalizationAlgorithm.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -39,6 +39,9 @@ namespace MimeKit.Cryptography { /// result in a signature verification failure. These signers prefer a canonicalization /// algorithm that does not tolerate in-transit modification of the signed email. /// + /// public enum DkimCanonicalizationAlgorithm { ///+ ///
/// The simple canonicalization algorithm tolerates almost no modification diff --git a/MimeKit/Cryptography/DkimHashStream.cs b/MimeKit/Cryptography/DkimHashStream.cs index 9caab74aba..b639b74466 100644 --- a/MimeKit/Cryptography/DkimHashStream.cs +++ b/MimeKit/Cryptography/DkimHashStream.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -53,7 +53,7 @@ class DkimHashStream : Stream int max; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -64,6 +64,7 @@ public DkimHashStream (DkimSignatureAlgorithm algorithm, int maxLength = -1) { #if ENABLE_NATIVE_DKIM switch (algorithm) { + case DkimSignatureAlgorithm.Ed25519Sha256: case DkimSignatureAlgorithm.RsaSha256: digest = SHA256.Create (); break; @@ -73,6 +74,7 @@ public DkimHashStream (DkimSignatureAlgorithm algorithm, int maxLength = -1) } #else switch (algorithm) { + case DkimSignatureAlgorithm.Ed25519Sha256: case DkimSignatureAlgorithm.RsaSha256: digest = new Sha256Digest (); break; diff --git a/MimeKit/Cryptography/DkimPublicKeyLocatorBase.cs b/MimeKit/Cryptography/DkimPublicKeyLocatorBase.cs new file mode 100644 index 0000000000..d86c8b83ae --- /dev/null +++ b/MimeKit/Cryptography/DkimPublicKeyLocatorBase.cs @@ -0,0 +1,189 @@ +// +// DkimPublicKeyLocatorBase.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Crypto.Parameters; + +namespace MimeKit.Cryptography { + /// + /// A base class for implemnentations of + ///. + /// + /// The + ///class provides a helpful + /// method for parsing DNS TXT records in order to extract the public key. + /// + /// + ///+ ///
+ /// + public abstract class DkimPublicKeyLocatorBase : IDkimPublicKeyLocator + { + ///+ ///
+ /// Get the public key from a DNS TXT record. + /// + ///+ /// Gets the public key from a DNS TXT record. + /// + /// The DNS TXT record. + ///The public key. + ///+ /// The + ///is null . + ///+ /// There was an error parsing the DNS TXT record. + /// + protected AsymmetricKeyParameter GetPublicKey (string txt) + { + AsymmetricKeyParameter pubkey; + string k = "rsa", p = null; + int index = 0; + + if (txt == null) + throw new ArgumentNullException (nameof (txt)); + + // parse the response (will look something like: "k=rsa; p=") + while (index < txt.Length) { + while (index < txt.Length && char.IsWhiteSpace (txt[index])) + index++; + + if (index == txt.Length) + break; + + // find the end of the key + int startIndex = index; + while (index < txt.Length && txt[index] != '=') + index++; + + if (index == txt.Length) + break; + + var key = txt.Substring (startIndex, index - startIndex); + + // skip over the '=' + index++; + + // find the end of the value + startIndex = index; + while (index < txt.Length && txt[index] != ';') + index++; + + var value = txt.Substring (startIndex, index - startIndex); + + switch (key) { + case "k": + switch (value) { + case "rsa": case "ed25519": k = value; break; + default: throw new ParseException ($"Unknown public key algorithm: {value}", startIndex, index); + } + break; + case "p": + p = value.Replace (" ", ""); + break; + } + + // skip over the ';' + index++; + } + + if (p != null) { + if (k == "ed25519") { + var decoded = Convert.FromBase64String (p); + + return new Ed25519PublicKeyParameters (decoded, 0); + } + + var data = "-----BEGIN PUBLIC KEY-----\r\n" + p + "\r\n-----END PUBLIC KEY-----\r\n"; + var rawData = Encoding.ASCII.GetBytes (data); + + using (var stream = new MemoryStream (rawData, false)) { + using (var reader = new StreamReader (stream)) { + var pem = new PemReader (reader); + + pubkey = pem.ReadObject () as AsymmetricKeyParameter; + + if (pubkey != null) + return pubkey; + } + } + } + + throw new ParseException ("Public key parameters not found in DNS TXT record.", 0, txt.Length); + } + + /// + /// Locate and retrieve the public key for the given domain and selector. + /// + ///+ /// + ///Locates and retrieves the public key for the given domain and selector. + ///+ /// + ///+ ///
+ /// + ///+ ///
+ /// + /// + /// The public key. + /// A colon-separated list of query methods used to retrieve the public key. The default is"dns/txt" . + /// The domain. + /// The selector. + /// The cancellation token. + public abstract AsymmetricKeyParameter LocatePublicKey (string methods, string domain, string selector, CancellationToken cancellationToken = default (CancellationToken)); + + ///+ /// Asynchronously locate and retrieve the public key for the given domain and selector. + /// + ///+ /// + ///Locates and retrieves the public key for the given domain and selector. + ///+ /// + ///+ ///
+ /// + ///+ ///
+ /// + /// + /// The public key. + /// A colon-separated list of query methods used to retrieve the public key. The default is"dns/txt" . + /// The domain. + /// The selector. + /// The cancellation token. + public abstract TaskLocatePublicKeyAsync (string methods, string domain, string selector, CancellationToken cancellationToken = default (CancellationToken)); + } +} diff --git a/MimeKit/Cryptography/DkimRelaxedBodyFilter.cs b/MimeKit/Cryptography/DkimRelaxedBodyFilter.cs index 9d06048581..91905ac019 100644 --- a/MimeKit/Cryptography/DkimRelaxedBodyFilter.cs +++ b/MimeKit/Cryptography/DkimRelaxedBodyFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -38,7 +38,7 @@ class DkimRelaxedBodyFilter : DkimBodyFilter bool lwsp, cr; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/Cryptography/DkimSignatureAlgorithm.cs b/MimeKit/Cryptography/DkimSignatureAlgorithm.cs index 43c6c9fbf0..5b38ddc736 100644 --- a/MimeKit/Cryptography/DkimSignatureAlgorithm.cs +++ b/MimeKit/Cryptography/DkimSignatureAlgorithm.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -31,6 +31,9 @@ namespace MimeKit.Cryptography { /// /// A DKIM signature algorithm. /// + ///+ /// public enum DkimSignatureAlgorithm { ///+ ///
/// The RSA-SHA1 signature algorithm. @@ -40,6 +43,11 @@ public enum DkimSignatureAlgorithm { /// /// The RSA-SHA256 signature algorithm. /// - RsaSha256 + RsaSha256, + + ///+ /// The Ed25519-SHA256 signature algorithm. + /// + Ed25519Sha256 } } diff --git a/MimeKit/Cryptography/DkimSignatureStream.cs b/MimeKit/Cryptography/DkimSignatureStream.cs index 28812a7642..83badb54dd 100644 --- a/MimeKit/Cryptography/DkimSignatureStream.cs +++ b/MimeKit/Cryptography/DkimSignatureStream.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -42,7 +42,7 @@ class DkimSignatureStream : Stream long length; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/Cryptography/DkimSigner.cs b/MimeKit/Cryptography/DkimSigner.cs index 30c5187bdf..8b86833d92 100644 --- a/MimeKit/Cryptography/DkimSigner.cs +++ b/MimeKit/Cryptography/DkimSigner.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,15 +26,14 @@ using System; using System.IO; -#if ENABLE_NATIVE_DKIM -using System.Security.Cryptography; -#endif +using System.Linq; +using System.Text; +using System.Collections.Generic; -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.OpenSsl; -using Org.BouncyCastle.Security; + +using MimeKit.IO; +using MimeKit.Utils; namespace MimeKit.Cryptography { /// @@ -43,13 +42,22 @@ namespace MimeKit.Cryptography { /// /// A DKIM signer. /// - public class DkimSigner + ///+ /// + public class DkimSigner : DkimSignerBase { + static readonly string[] DkimShouldNotInclude = { "return-path", "received", "comments", "keywords", "bcc", "resent-bcc", "dkim-signature" }; + ///+ ///
- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// Creates a new /// The domain that the signer represents. /// The selector subdividing the domain. @@ -59,24 +67,19 @@ public class DkimSigner ///. + /// Creates a new + ///. Due to the recognized weakness of the SHA-1 hash algorithm + /// and the wide availability of the SHA-256 hash algorithm (it has been a required + /// part of DKIM since it was originally standardized in 2007), it is recommended + /// that ///NOT be used. -or- ////// is null .- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// Creates a new /// The signer's private key. /// The domain that the signer represents. @@ -92,60 +95,30 @@ protected DkimSigner (string domain, string selector, DkimSignatureAlgorithm alg ///. + /// Creates a new + ///. Due to the recognized weakness of the SHA-1 hash algorithm + /// and the wide availability of the SHA-256 hash algorithm (it has been a required + /// part of DKIM since it was originally standardized in 2007), it is recommended + /// that ///NOT be used. /// - public DkimSigner (AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) + public DkimSigner (AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) { if (key == null) throw new ArgumentNullException (nameof (key)); - if (domain == null) - throw new ArgumentNullException (nameof (domain)); - - if (selector == null) - throw new ArgumentNullException (nameof (selector)); - if (!key.IsPrivate) throw new ArgumentException ("The key must be a private key.", nameof (key)); - SignatureAlgorithm = algorithm; - Selector = selector; PrivateKey = key; - Domain = domain; } - static AsymmetricKeyParameter LoadPrivateKey (Stream stream) - { - AsymmetricKeyParameter key = null; - - using (var reader = new StreamReader (stream)) { - var pem = new PemReader (reader); - - var keyObject = pem.ReadObject (); - - if (keyObject != null) { - key = keyObject as AsymmetricKeyParameter; - - if (key == null) { - var pair = keyObject as AsymmetricCipherKeyPair; - - if (pair != null) - key = pair.Private; - } - } - } - - if (key == null || !key.IsPrivate) - throw new FormatException ("Private key not found."); - - return key; - } - -#if !PORTABLE ///is not a private key. /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// Creates a new + ///. + /// Creates a new + ///. Due to the recognized weakness of the SHA-1 hash algorithm + /// and the wide availability of the SHA-256 hash algorithm (it has been a required + /// part of DKIM since it was originally standardized in 2007), it is recommended + /// that ///NOT be used. + /// /// The file containing the private key. /// The domain that the signer represents. /// The selector subdividing the domain. @@ -159,8 +132,7 @@ static AsymmetricKeyParameter LoadPrivateKey (Stream stream) ///+ ///
/// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The file did not contain a private key. @@ -177,7 +149,7 @@ static AsymmetricKeyParameter LoadPrivateKey (Stream stream) /// /// An I/O error occurred. /// - public DkimSigner (string fileName, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) + public DkimSigner (string fileName, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) { if (fileName == null) throw new ArgumentNullException (nameof (fileName)); @@ -185,26 +157,19 @@ public DkimSigner (string fileName, string domain, string selector, DkimSignatur if (fileName.Length == 0) throw new ArgumentException ("The file name cannot be empty.", nameof (fileName)); - if (domain == null) - throw new ArgumentNullException (nameof (domain)); - - if (selector == null) - throw new ArgumentNullException (nameof (selector)); - using (var stream = File.OpenRead (fileName)) PrivateKey = LoadPrivateKey (stream); - - SignatureAlgorithm = algorithm; - Selector = selector; - Domain = domain; } -#endif ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// Creates a new /// The stream containing the private key. /// The domain that the signer represents. @@ -223,54 +188,12 @@ public DkimSigner (string fileName, string domain, string selector, DkimSignatur ///. + /// Creates a new + ///. Due to the recognized weakness of the SHA-1 hash algorithm + /// and the wide availability of the SHA-256 hash algorithm (it has been a required + /// part of DKIM since it was originally standardized in 2007), it is recommended + /// that ///NOT be used. /// An I/O error occurred. /// - public DkimSigner (Stream stream, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) + public DkimSigner (Stream stream, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) { if (stream == null) throw new ArgumentNullException (nameof (stream)); - if (domain == null) - throw new ArgumentNullException (nameof (domain)); - - if (selector == null) - throw new ArgumentNullException (nameof (selector)); - PrivateKey = LoadPrivateKey (stream); - SignatureAlgorithm = algorithm; - Selector = selector; - Domain = domain; - } - - ///- /// Gets the private key. - /// - ///- /// The private key used for signing. - /// - ///The private key. - protected AsymmetricKeyParameter PrivateKey { - get; set; - } - - ///- /// Get the domain that the signer represents. - /// - ///- /// Gets the domain that the signer represents. - /// - ///The domain. - public string Domain { - get; private set; - } - - ///- /// Get the selector subdividing the domain. - /// - ///- /// Gets the selector subdividing the domain. - /// - ///The selector. - public string Selector { - get; private set; } ///@@ -279,22 +202,14 @@ public string Selector { /// /// Gets or sets the agent or user identifier. /// + ///+ /// ///+ ///
The agent or user identifier. public string AgentOrUserIdentifier { get; set; } - ///- /// Get or set the algorithm to use for signing. - /// - ///- /// Gets or sets the algorithm to use for signing. - /// - ///The signature algorithm. - public DkimSignatureAlgorithm SignatureAlgorithm { - get; set; - } - ////// Get or set the public key query method. /// @@ -305,107 +220,264 @@ public DkimSignatureAlgorithm SignatureAlgorithm { /// query method is of the form "type[/options]", where the syntax and /// semantics of the options depend on the type and specified options. ///+ /// ///+ ///
The public key query method. public string QueryMethod { get; set; } ///- /// Get the digest signing context for a specified signature algorithm. + /// Get the timestamp value. /// ///- /// Gets the digest signing context for the specified signature algorithm. + /// Gets the timestamp to use as the - ///t= value in the DKIM-Signature header. ///The digest signer. - public virtual ISigner DigestSigner { - get { -#if ENABLE_NATIVE_DKIM - return new SystemSecuritySigner (SignatureAlgorithm, PrivateKey.AsAsymmetricAlgorithm ()); -#else - DerObjectIdentifier id; - - if (SignatureAlgorithm == DkimSignatureAlgorithm.RsaSha256) - id = PkcsObjectIdentifiers.Sha256WithRsaEncryption; - else - id = PkcsObjectIdentifiers.Sha1WithRsaEncryption; - - var signer = SignerUtilities.GetSigner (id); - - signer.Init (true, PrivateKey); - - return signer; -#endif - } + ///A value representing the timestamp value. + protected virtual long GetTimestamp () + { + return (long) (DateTime.UtcNow - DateUtils.UnixEpoch).TotalSeconds; } - } - -#if ENABLE_NATIVE_DKIM - class SystemSecuritySigner : ISigner - { - readonly RSACryptoServiceProvider rsa; - readonly HashAlgorithm hash; - readonly string oid; - public SystemSecuritySigner (DkimSignatureAlgorithm algorithm, AsymmetricAlgorithm key) + void DkimSign (FormatOptions options, MimeMessage message, IListheaders) { - rsa = key as RSACryptoServiceProvider; - - switch (algorithm) { + var value = new StringBuilder ("v=1"); + var t = GetTimestamp (); + byte[] signature, hash; + Header dkim; + + options = options.Clone (); + options.NewLineFormat = NewLineFormat.Dos; + options.EnsureNewLine = true; + + switch (SignatureAlgorithm) { + case DkimSignatureAlgorithm.Ed25519Sha256: + value.Append ("; a=ed25519-sha256"); + break; case DkimSignatureAlgorithm.RsaSha256: - oid = SecureMimeContext.GetDigestOid (DigestAlgorithm.Sha256); - AlgorithmName = "RSASHA256"; - hash = SHA256.Create (); + value.Append ("; a=rsa-sha256"); break; default: - oid = SecureMimeContext.GetDigestOid (DigestAlgorithm.Sha1); - AlgorithmName = "RSASHA1"; - hash = SHA1.Create (); + value.Append ("; a=rsa-sha1"); break; } - } - public string AlgorithmName { - get; private set; - } + value.AppendFormat ("; d={0}; s={1}", Domain, Selector); + value.AppendFormat ("; c={0}/{1}", + HeaderCanonicalizationAlgorithm.ToString ().ToLowerInvariant (), + BodyCanonicalizationAlgorithm.ToString ().ToLowerInvariant ()); + if (!string.IsNullOrEmpty (QueryMethod)) + value.AppendFormat ("; q={0}", QueryMethod); + if (!string.IsNullOrEmpty (AgentOrUserIdentifier)) + value.AppendFormat ("; i={0}", AgentOrUserIdentifier); + value.AppendFormat ("; t={0}", t); + + using (var stream = new DkimSignatureStream (CreateSigningContext ())) { + using (var filtered = new FilteredStream (stream)) { + filtered.Add (options.CreateNewLineFilter ()); + + // write the specified message headers + DkimVerifierBase.WriteHeaders (options, message, headers, HeaderCanonicalizationAlgorithm, filtered); + + value.AppendFormat ("; h={0}", string.Join (":", headers.ToArray ())); + + hash = message.HashBody (options, SignatureAlgorithm, BodyCanonicalizationAlgorithm, -1); + value.AppendFormat ("; bh={0}", Convert.ToBase64String (hash)); + value.Append ("; b="); + + dkim = new Header (HeaderId.DkimSignature, value.ToString ()); + message.Headers.Insert (0, dkim); + + switch (HeaderCanonicalizationAlgorithm) { + case DkimCanonicalizationAlgorithm.Relaxed: + DkimVerifierBase.WriteHeaderRelaxed (options, filtered, dkim, true); + break; + default: + DkimVerifierBase.WriteHeaderSimple (options, filtered, dkim, true); + break; + } - public void BlockUpdate (byte[] input, int inOff, int length) - { - hash.TransformBlock (input, inOff, length, null, 0); - } + filtered.Flush (); + } - public byte[] GenerateSignature () - { - hash.TransformFinalBlock (new byte[0], 0, 0); + signature = stream.GenerateSignature (); - return rsa.SignHash (hash.Hash, oid); + dkim.Value += Convert.ToBase64String (signature); + } } - public void Init (bool forSigning, ICipherParameters parameters) + /// + /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. + /// + ///+ /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. + /// + ///+ /// + /// The formatting options. + /// The message to sign. + /// The list of header fields to sign. + ///+ ///
+ /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + public void Sign (FormatOptions options, MimeMessage message, IList+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. headers) { - throw new NotImplementedException (); - } + if (options == null) + throw new ArgumentNullException (nameof (options)); - public void Reset () - { - hash.Initialize (); + if (message == null) + throw new ArgumentNullException (nameof (message)); + + if (headers == null) + throw new ArgumentNullException (nameof (headers)); + + var fields = new string[headers.Count]; + var containsFrom = false; + + for (int i = 0; i < headers.Count; i++) { + if (headers[i] == null) + throw new ArgumentException ("The list of headers cannot contain null.", nameof (headers)); + + if (headers[i].Length == 0) + throw new ArgumentException ("The list of headers cannot contain empty string.", nameof (headers)); + + fields[i] = headers[i].ToLowerInvariant (); + + if (DkimShouldNotInclude.Contains (fields[i])) + throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i]), nameof (headers)); + + if (fields[i] == "from") + containsFrom = true; + } + + if (!containsFrom) + throw new ArgumentException ("The list of headers to sign MUST include the 'From' header.", nameof (headers)); + + DkimSign (options, message, fields); } - public void Update (byte input) + /// + /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. + /// + ///+ /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. + /// + ///+ /// + /// The message to sign. + /// The headers to sign. + ///+ ///
+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + public void Sign (MimeMessage message, IList+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. headers) { - hash.TransformBlock (new byte[] { input }, 0, 1, null, 0); + Sign (FormatOptions.Default, message, headers); } - public bool VerifySignature (byte[] signature) + /// + /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. + /// + ///+ /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. + /// + ///+ /// + /// The formatting options. + /// The message to sign. + /// The list of header fields to sign. + ///+ ///
+ /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + public void Sign (FormatOptions options, MimeMessage message, IList+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. headers) { - hash.TransformFinalBlock (new byte[0], 0, 0); + if (options == null) + throw new ArgumentNullException (nameof (options)); + + if (message == null) + throw new ArgumentNullException (nameof (message)); + + if (headers == null) + throw new ArgumentNullException (nameof (headers)); + + var fields = new string[headers.Count]; + var containsFrom = false; - return rsa.VerifyHash (hash.Hash, oid, signature); + for (int i = 0; i < headers.Count; i++) { + if (headers[i] == HeaderId.Unknown) + throw new ArgumentException ("The list of headers to sign cannot include the 'Unknown' header.", nameof (headers)); + + fields[i] = headers[i].ToHeaderName ().ToLowerInvariant (); + + if (DkimShouldNotInclude.Contains (fields[i])) + throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i].ToHeaderName ()), nameof (headers)); + + if (headers[i] == HeaderId.From) + containsFrom = true; + } + + if (!containsFrom) + throw new ArgumentException ("The list of headers to sign MUST include the 'From' header.", nameof (headers)); + + DkimSign (options, message, fields); } - public void Dispose () + /// + /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. + /// + ///+ /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. + /// + ///+ /// + /// The message to sign. + /// The headers to sign. + ///+ ///
+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + public void Sign (MimeMessage message, IList+ /// does not contain the 'From' header. -or- + ///+ /// contains one or more of the following headers: Return-Path, + /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. headers) { - rsa.Dispose (); + Sign (FormatOptions.Default, message, headers); } } -#endif } diff --git a/MimeKit/Cryptography/DkimSignerBase.cs b/MimeKit/Cryptography/DkimSignerBase.cs new file mode 100644 index 0000000000..a7f525785e --- /dev/null +++ b/MimeKit/Cryptography/DkimSignerBase.cs @@ -0,0 +1,293 @@ +// +// DkimSignerBase.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.IO; +#if ENABLE_NATIVE_DKIM +using System.Security.Cryptography; +#endif + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Signers; + +namespace MimeKit.Cryptography { + /// + /// A base class for DKIM and ARC signers. + /// + ///+ /// The base class for + public abstract class DkimSignerBase + { + ///and . + /// + /// Initialize a new instance of the + ///class. + /// + /// + /// The domain that the signer represents. + /// The selector subdividing the domain. + /// The signature algorithm. + ///Creates a new + ///. Due to the recognized weakness of the SHA-1 hash algorithm + /// and the wide availability of the SHA-256 hash algorithm (it has been a required + /// part of DKIM since it was originally standardized in 2007), it is recommended + /// that + ///NOT be used. + /// + protected DkimSignerBase (string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) + { + if (domain == null) + throw new ArgumentNullException (nameof (domain)); + + if (selector == null) + throw new ArgumentNullException (nameof (selector)); + + SignatureAlgorithm = algorithm; + Selector = selector; + Domain = domain; + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Get the domain that the signer represents. + /// + ///+ /// Gets the domain that the signer represents. + /// + ///+ /// + ///+ ///
The domain. + public string Domain { + get; private set; + } + + ///+ /// Get the selector subdividing the domain. + /// + ///+ /// Gets the selector subdividing the domain. + /// + ///+ /// + ///+ ///
The selector. + public string Selector { + get; private set; + } + + ///+ /// Get or set the algorithm to use for signing. + /// + ///+ /// + ///Gets or sets the algorithm to use for signing. + ///Creates a new + ///. Due to the recognized weakness of the SHA-1 hash algorithm + /// and the wide availability of the SHA-256 hash algorithm (it has been a required + /// part of DKIM since it was originally standardized in 2007), it is recommended + /// that + ///NOT be used. + /// + ///+ ///
The signature algorithm. + public DkimSignatureAlgorithm SignatureAlgorithm { + get; set; + } + + ///+ /// Get or set the canonicalization algorithm to use for the message body. + /// + ///+ /// Gets or sets the canonicalization algorithm to use for the message body. + /// + ///+ /// + ///+ ///
The canonicalization algorithm. + public DkimCanonicalizationAlgorithm BodyCanonicalizationAlgorithm { + get; set; + } + + ///+ /// Get or set the canonicalization algorithm to use for the message headers. + /// + ///+ /// Gets or sets the canonicalization algorithm to use for the message headers. + /// + ///+ /// + ///+ ///
The canonicalization algorithm. + public DkimCanonicalizationAlgorithm HeaderCanonicalizationAlgorithm { + get; set; + } + + ///+ /// Gets the private key. + /// + ///+ /// The private key used for signing. + /// + ///The private key. + protected AsymmetricKeyParameter PrivateKey { + get; set; + } + + internal static AsymmetricKeyParameter LoadPrivateKey (Stream stream) + { + AsymmetricKeyParameter key = null; + + using (var reader = new StreamReader (stream)) { + var pem = new PemReader (reader); + + var keyObject = pem.ReadObject (); + + if (keyObject is AsymmetricCipherKeyPair pair) { + key = pair.Private; + } else if (keyObject is AsymmetricKeyParameter) { + key = (AsymmetricKeyParameter) keyObject; + } + } + + if (key == null || !key.IsPrivate) + throw new FormatException ("Private key not found."); + + return key; + } + + ///+ /// Create the digest signing context. + /// + ///+ /// Creates a new digest signing context. + /// + ///The digest signer. + ///+ /// The + internal protected virtual ISigner CreateSigningContext () + { +#if ENABLE_NATIVE_DKIM + return new SystemSecuritySigner (SignatureAlgorithm, PrivateKey.AsAsymmetricAlgorithm ()); +#else + ISigner signer; + + switch (SignatureAlgorithm) { + case DkimSignatureAlgorithm.RsaSha1: + signer = new RsaDigestSigner (new Sha1Digest ()); + break; + case DkimSignatureAlgorithm.RsaSha256: + signer = new RsaDigestSigner (new Sha256Digest ()); + break; + case DkimSignatureAlgorithm.Ed25519Sha256: + signer = new Ed25519DigestSigner (new Sha256Digest ()); + break; + default: + throw new NotSupportedException (string.Format ("{0} is not supported.", SignatureAlgorithm)); + } + + signer.Init (true, PrivateKey); + + return signer; +#endif + } + } + +#if ENABLE_NATIVE_DKIM + class SystemSecuritySigner : ISigner + { + readonly RSACryptoServiceProvider rsa; + readonly HashAlgorithm hash; + readonly string oid; + + public SystemSecuritySigner (DkimSignatureAlgorithm algorithm, AsymmetricAlgorithm key) + { + rsa = key as RSACryptoServiceProvider; + + switch (algorithm) { + case DkimSignatureAlgorithm.RsaSha256: + oid = SecureMimeContext.GetDigestOid (DigestAlgorithm.Sha256); + AlgorithmName = "RSASHA256"; + hash = SHA256.Create (); + break; + default: + oid = SecureMimeContext.GetDigestOid (DigestAlgorithm.Sha1); + AlgorithmName = "RSASHA1"; + hash = SHA1.Create (); + break; + } + } + + public string AlgorithmName { + get; private set; + } + + public void BlockUpdate (byte[] input, int inOff, int length) + { + hash.TransformBlock (input, inOff, length, null, 0); + } + + public byte[] GenerateSignature () + { + hash.TransformFinalBlock (new byte[0], 0, 0); + + return rsa.SignHash (hash.Hash, oid); + } + + public void Init (bool forSigning, ICipherParameters parameters) + { + throw new NotImplementedException (); + } + + public void Reset () + { + hash.Initialize (); + } + + public void Update (byte input) + { + hash.TransformBlock (new byte[] { input }, 0, 1, null, 0); + } + + public bool VerifySignature (byte[] signature) + { + hash.TransformFinalBlock (new byte[0], 0, 0); + + return rsa.VerifyHash (hash.Hash, oid, signature); + } + + public void Dispose () + { + rsa.Dispose (); + } + } +#endif +} diff --git a/MimeKit/Cryptography/DkimSimpleBodyFilter.cs b/MimeKit/Cryptography/DkimSimpleBodyFilter.cs index d1afe759dc..1c1e440f57 100644 --- a/MimeKit/Cryptography/DkimSimpleBodyFilter.cs +++ b/MimeKit/Cryptography/DkimSimpleBodyFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastis not supported. + /// // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -34,7 +34,7 @@ namespace MimeKit.Cryptography { class DkimSimpleBodyFilter : DkimBodyFilter { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///. diff --git a/MimeKit/Cryptography/DkimVerifier.cs b/MimeKit/Cryptography/DkimVerifier.cs new file mode 100644 index 0000000000..e836101efc --- /dev/null +++ b/MimeKit/Cryptography/DkimVerifier.cs @@ -0,0 +1,281 @@ +// +// DkimVerifier.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace MimeKit.Cryptography { + /// + /// A DKIM-Signature verifier. + /// + ///+ /// Verifies DomainKeys Identified Mail (DKIM) signatures. + /// + ///+ /// + public class DkimVerifier : DkimVerifierBase + { + ///+ ///
+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + ///. + /// + /// + /// The public key locator. + ///+ ///
+ /// + public DkimVerifier (IDkimPublicKeyLocator publicKeyLocator) : base (publicKeyLocator) + { + } + + static void ValidateDkimSignatureParameters (IDictionaryis null . + ///parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, + out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) + { + bool containsFrom = false; + + if (!parameters.TryGetValue ("v", out string v)) + throw new FormatException ("Malformed DKIM-Signature header: no version parameter detected."); + + if (v != "1") + throw new FormatException (string.Format ("Unrecognized DKIM-Signature version: v={0}", v)); + + ValidateCommonSignatureParameters ("DKIM-Signature", parameters, out algorithm, out headerAlgorithm, out bodyAlgorithm, out d, out s, out q, out headers, out bh, out b, out maxLength); + + for (int i = 0; i < headers.Length; i++) { + if (headers[i].Equals ("from", StringComparison.OrdinalIgnoreCase)) { + containsFrom = true; + break; + } + } + + if (!containsFrom) + throw new FormatException ("Malformed DKIM-Signature header: From header not signed."); + + if (parameters.TryGetValue ("i", out string id)) { + string ident; + int at; + + if ((at = id.LastIndexOf ('@')) == -1) + throw new FormatException ("Malformed DKIM-Signature header: no @ in the AUID value."); + + ident = id.Substring (at + 1); + + if (!ident.Equals (d, StringComparison.OrdinalIgnoreCase) && !ident.EndsWith ("." + d, StringComparison.OrdinalIgnoreCase)) + throw new FormatException ("Invalid DKIM-Signature header: the domain in the AUID does not match the domain parameter."); + } + } + + async Task VerifyAsync (FormatOptions options, MimeMessage message, Header dkimSignature, bool doAsync, CancellationToken cancellationToken) + { + if (options == null) + throw new ArgumentNullException (nameof (options)); + + if (message == null) + throw new ArgumentNullException (nameof (message)); + + if (dkimSignature == null) + throw new ArgumentNullException (nameof (dkimSignature)); + + if (dkimSignature.Id != HeaderId.DkimSignature) + throw new ArgumentException ("The signature parameter MUST be a DKIM-Signature header.", nameof (dkimSignature)); + + var parameters = ParseParameterTags (dkimSignature.Id, dkimSignature.Value); + DkimCanonicalizationAlgorithm headerAlgorithm, bodyAlgorithm; + DkimSignatureAlgorithm signatureAlgorithm; + AsymmetricKeyParameter key; + string d, s, q, bh, b; + string[] headers; + int maxLength; + + ValidateDkimSignatureParameters (parameters, out signatureAlgorithm, out headerAlgorithm, out bodyAlgorithm, + out d, out s, out q, out headers, out bh, out b, out maxLength); + + if (!IsEnabled (signatureAlgorithm)) + return false; + + options = options.Clone (); + options.NewLineFormat = NewLineFormat.Dos; + + // first check the body hash (if that's invalid, then the entire signature is invalid) + if (!VerifyBodyHash (options, message, signatureAlgorithm, bodyAlgorithm, maxLength, bh)) + return false; + + if (doAsync) + key = await PublicKeyLocator.LocatePublicKeyAsync (q, d, s, cancellationToken).ConfigureAwait (false); + else + key = PublicKeyLocator.LocatePublicKey (q, d, s, cancellationToken); + + if ((key is RsaKeyParameters rsa) && rsa.Modulus.BitLength < MinimumRsaKeyLength) + return false; + + return VerifySignature (options, message, dkimSignature, signatureAlgorithm, key, headers, headerAlgorithm, b); + } + + /// + /// Verify the specified DKIM-Signature header. + /// + ///+ /// Verifies the specified DKIM-Signature header. + /// + ///+ /// + ///+ ///
+ /// The formatting options. + /// The message to verify. + /// The DKIM-Signature header. + /// The cancellation token. + /// true if the DKIM-Signature is valid; otherwise,false .+ /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///is not a DKIM-Signature header. + /// + /// The DKIM-Signature header value is malformed. + /// + ///+ /// The operation was canceled via the cancellation token. + /// + public bool Verify (FormatOptions options, MimeMessage message, Header dkimSignature, CancellationToken cancellationToken = default (CancellationToken)) + { + return VerifyAsync (options, message, dkimSignature, false, cancellationToken).GetAwaiter ().GetResult (); + } + + ///+ /// Asynchronously verify the specified DKIM-Signature header. + /// + ///+ /// Verifies the specified DKIM-Signature header. + /// + ///+ /// + ///+ ///
+ /// The formatting options. + /// The message to verify. + /// The DKIM-Signature header. + /// The cancellation token. + /// true if the DKIM-Signature is valid; otherwise,false .+ /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///is not a DKIM-Signature header. + /// + /// The DKIM-Signature header value is malformed. + /// + ///+ /// The operation was canceled via the cancellation token. + /// + public TaskVerifyAsync (FormatOptions options, MimeMessage message, Header dkimSignature, CancellationToken cancellationToken = default (CancellationToken)) + { + return VerifyAsync (options, message, dkimSignature, true, cancellationToken); + } + + /// + /// Verify the specified DKIM-Signature header. + /// + ///+ /// Verifies the specified DKIM-Signature header. + /// + ///+ /// + ///+ ///
+ /// The message to verify. + /// The DKIM-Signature header. + /// The cancellation token. + /// true if the DKIM-Signature is valid; otherwise,false .+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///is not a DKIM-Signature header. + /// + /// The DKIM-Signature header value is malformed. + /// + ///+ /// The operation was canceled via the cancellation token. + /// + public bool Verify (MimeMessage message, Header dkimSignature, CancellationToken cancellationToken = default (CancellationToken)) + { + return Verify (FormatOptions.Default, message, dkimSignature, cancellationToken); + } + + ///+ /// Asynchronously verify the specified DKIM-Signature header. + /// + ///+ /// Verifies the specified DKIM-Signature header. + /// + ///+ /// + ///+ ///
+ /// The message to verify. + /// The DKIM-Signature header. + /// The cancellation token. + /// true if the DKIM-Signature is valid; otherwise,false .+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///is not a DKIM-Signature header. + /// + /// The DKIM-Signature header value is malformed. + /// + ///+ /// The operation was canceled via the cancellation token. + /// + public TaskVerifyAsync (MimeMessage message, Header dkimSignature, CancellationToken cancellationToken = default (CancellationToken)) + { + return VerifyAsync (FormatOptions.Default, message, dkimSignature, cancellationToken); + } + } +} diff --git a/MimeKit/Cryptography/DkimVerifierBase.cs b/MimeKit/Cryptography/DkimVerifierBase.cs new file mode 100644 index 0000000000..fdaca3fa70 --- /dev/null +++ b/MimeKit/Cryptography/DkimVerifierBase.cs @@ -0,0 +1,559 @@ +// +// DkimVerifierBase.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.IO; +using System.Text; +using System.Globalization; +using System.Collections.Generic; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Signers; + +using MimeKit.IO; +using MimeKit.Utils; + +namespace MimeKit.Cryptography { + /// + /// A base class for DKIM and ARC verifiers. + /// + ///+ /// The base class for + public abstract class DkimVerifierBase + { + int enabledSignatureAlgorithms; + + ///and . + /// + /// Initialize a new instance of the + ///class. + /// + /// Initializes the + /// The public key locator. + ///. + /// + /// + protected DkimVerifierBase (IDkimPublicKeyLocator publicKeyLocator) + { + if (publicKeyLocator == null) + throw new ArgumentNullException (nameof (publicKeyLocator)); + + PublicKeyLocator = publicKeyLocator; + + Enable (DkimSignatureAlgorithm.Ed25519Sha256); + Enable (DkimSignatureAlgorithm.RsaSha256); + //Enable (DkimSignatureAlgorithm.RsaSha1); + MinimumRsaKeyLength = 1024; + } + + ///is null . + ///+ /// Get the public key locator. + /// + ///+ /// Gets the public key locator. + /// + ///The public key locator. + protected IDkimPublicKeyLocator PublicKeyLocator { + get; private set; + } + + ///+ /// Get or set the minimum allowed RSA key length. + /// + ///+ /// + public int MinimumRsaKeyLength { + get; set; + } + + ///Gets the minimum allowed RSA key length. + ///The DKIM specifications specify a single signing algorithm, RSA, + /// and recommend key sizes of 1024 to 2048 bits (but require verification of 512-bit keys). + /// As discussed in US-CERT Vulnerability Note VU#268267, the operational community has + /// recognized that shorter keys compromise the effectiveness of DKIM. While 1024-bit + /// signatures are common, stronger signatures are not. Widely used DNS configuration + /// software places a practical limit on key sizes, because the software only handles a + /// single 256-octet string in a TXT record, and RSA keys significantly longer than 1024 + /// bits don't fit in 256 octets. + ///+ /// Enable a DKIM signature algorithm. + /// + ///+ /// + /// The DKIM signature algorithm. + public void Enable (DkimSignatureAlgorithm algorithm) + { + enabledSignatureAlgorithms |= 1 << (int) algorithm; + } + + ///Enables the specified DKIM signature algorithm. + ///Due to the recognized weakness of the SHA-1 hash algorithm + /// and the wide availability of the SHA-256 hash algorithm (it has been a required + /// part of DKIM since it was originally standardized in 2007), it is recommended + /// that + ///NOT be enabled. + /// Disable a DKIM signature algorithm. + /// + ///+ /// + /// The DKIM signature algorithm. + public void Disable (DkimSignatureAlgorithm algorithm) + { + enabledSignatureAlgorithms &= ~(1 << (int) algorithm); + } + + ///Disables the specified DKIM signature algorithm. + ///Due to the recognized weakness of the SHA-1 hash algorithm + /// and the wide availability of the SHA-256 hash algorithm (it has been a required + /// part of DKIM since it was originally standardized in 2007), it is recommended + /// that + ///NOT be enabled. + /// Check whether a DKIM signature algorithm is enabled. + /// + ///+ /// + ///Determines whether the specified DKIM signature algorithm is enabled. + ///Due to the recognized weakness of the SHA-1 hash algorithm + /// and the wide availability of the SHA-256 hash algorithm (it has been a required + /// part of DKIM since it was originally standardized in 2007), it is recommended + /// that + ///NOT be enabled. + /// The DKIM signature algorithm. + public bool IsEnabled (DkimSignatureAlgorithm algorithm) + { + return (enabledSignatureAlgorithms & (1 << (int) algorithm)) != 0; + } + + static bool IsWhiteSpace (char c) + { + return c == ' ' || c == '\t'; + } + + static bool IsAlpha (char c) + { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + } + + internal static Dictionary true if the specified DKIM signature algorithm is enabled; otherwise,false .ParseParameterTags (HeaderId header, string signature) + { + var parameters = new Dictionary (); + var value = new StringBuilder (); + int index = 0; + + while (index < signature.Length) { + while (index < signature.Length && IsWhiteSpace (signature[index])) + index++; + + if (index >= signature.Length) + break; + + if (signature[index] == ';' || !IsAlpha (signature[index])) + throw new FormatException (string.Format ("Malformed {0} value.", header.ToHeaderName ())); + + int startIndex = index++; + + while (index < signature.Length && signature[index] != '=') + index++; + + if (index >= signature.Length) + continue; + + var name = signature.Substring (startIndex, index - startIndex).TrimEnd (); + + // skip over '=' and clear value buffer + value.Length = 0; + index++; + + while (index < signature.Length && signature[index] != ';') { + if (!IsWhiteSpace (signature[index])) + value.Append (signature[index]); + index++; + } + + if (parameters.ContainsKey (name)) + throw new FormatException (string.Format ("Malformed {0} value: duplicate parameter '{1}'.", header.ToHeaderName (), name)); + + parameters.Add (name, value.ToString ()); + + // skip over ';' + index++; + } + + return parameters; + } + + internal static void ValidateCommonParameters (string header, IDictionary parameters, out DkimSignatureAlgorithm algorithm, + out string d, out string s, out string q, out string b) + { + if (!parameters.TryGetValue ("a", out string a)) + throw new FormatException (string.Format ("Malformed {0} header: no signature algorithm parameter detected.", header)); + + switch (a.ToLowerInvariant ()) { + case "ed25519-sha256": algorithm = DkimSignatureAlgorithm.Ed25519Sha256; break; + case "rsa-sha256": algorithm = DkimSignatureAlgorithm.RsaSha256; break; + case "rsa-sha1": algorithm = DkimSignatureAlgorithm.RsaSha1; break; + default: throw new FormatException (string.Format ("Unrecognized {0} algorithm parameter: a={1}", header, a)); + } + + if (!parameters.TryGetValue ("d", out d)) + throw new FormatException (string.Format ("Malformed {0} header: no domain parameter detected.", header)); + + if (d.Length == 0) + throw new FormatException (string.Format ("Malformed {0} header: empty domain parameter detected.", header)); + + if (!parameters.TryGetValue ("s", out s)) + throw new FormatException (string.Format ("Malformed {0} header: no selector parameter detected.", header)); + + if (s.Length == 0) + throw new FormatException (string.Format ("Malformed {0} header: empty selector parameter detected.", header)); + + if (!parameters.TryGetValue ("q", out q)) + q = "dns/txt"; + + if (!parameters.TryGetValue ("b", out b)) + throw new FormatException (string.Format ("Malformed {0} header: no signature parameter detected.", header)); + + if (b.Length == 0) + throw new FormatException (string.Format ("Malformed {0} header: empty signature parameter detected.", header)); + + if (parameters.TryGetValue ("t", out string t)) { + if (!int.TryParse (t, NumberStyles.Integer, CultureInfo.InvariantCulture, out int timestamp) || timestamp < 0) + throw new FormatException (string.Format ("Malformed {0} header: invalid timestamp parameter: t={1}.", header, t)); + } + } + + internal static void ValidateCommonSignatureParameters (string header, IDictionary parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, + out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) + { + ValidateCommonParameters (header, parameters, out algorithm, out d, out s, out q, out b); + + if (parameters.TryGetValue ("l", out string l)) { + if (!int.TryParse (l, NumberStyles.Integer, CultureInfo.InvariantCulture, out maxLength) || maxLength < 0) + throw new FormatException (string.Format ("Malformed {0} header: invalid length parameter: l={1}", header, l)); + } else { + maxLength = -1; + } + + if (parameters.TryGetValue ("c", out string c)) { + var tokens = c.ToLowerInvariant ().Split ('/'); + + if (tokens.Length == 0 || tokens.Length > 2) + throw new FormatException (string.Format ("Malformed {0} header: invalid canonicalization parameter: c={1}", header, c)); + + switch (tokens[0]) { + case "relaxed": headerAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; + case "simple": headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; + default: throw new FormatException (string.Format ("Malformed {0} header: invalid canonicalization parameter: c={1}", header, c)); + } + + if (tokens.Length == 2) { + switch (tokens[1]) { + case "relaxed": bodyAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; + case "simple": bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; + default: throw new FormatException (string.Format ("Malformed {0} header: invalid canonicalization parameter: c={1}", header, c)); + } + } else { + bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; + } + } else { + headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; + bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; + } + + if (!parameters.TryGetValue ("h", out string h)) + throw new FormatException (string.Format ("Malformed {0} header: no signed header parameter detected.", header)); + + headers = h.Split (':'); + + if (!parameters.TryGetValue ("bh", out bh)) + throw new FormatException (string.Format ("Malformed {0} header: no body hash parameter detected.", header)); + } + + internal static void WriteHeaderRelaxed (FormatOptions options, Stream stream, Header header, bool isDkimSignature) + { + // o Convert all header field names (not the header field values) to + // lowercase. For example, convert "SUBJect: AbC" to "subject: AbC". + var name = Encoding.ASCII.GetBytes (header.Field.ToLowerInvariant ()); + var rawValue = header.GetRawValue (options); + int index = 0; + + // o Delete any WSP characters remaining before and after the colon + // separating the header field name from the header field value. The + // colon separator MUST be retained. + stream.Write (name, 0, name.Length); + stream.WriteByte ((byte) ':'); + + // trim leading whitespace... + while (index < rawValue.Length && rawValue[index].IsWhitespace ()) + index++; + + while (index < rawValue.Length) { + int startIndex = index; + + // look for the first non-whitespace character + while (index < rawValue.Length && rawValue[index].IsWhitespace ()) + index++; + + // o Delete all WSP characters at the end of each unfolded header field + // value. + if (index >= rawValue.Length) + break; + + // o Convert all sequences of one or more WSP characters to a single SP + // character. WSP characters here include those before and after a + // line folding boundary. + if (index > startIndex) + stream.WriteByte ((byte) ' '); + + startIndex = index; + + while (index < rawValue.Length && !rawValue[index].IsWhitespace ()) + index++; + + if (index > startIndex) + stream.Write (rawValue, startIndex, index - startIndex); + } + + if (!isDkimSignature) + stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); + } + + internal static void WriteHeaderSimple (FormatOptions options, Stream stream, Header header, bool isDkimSignature) + { + var rawValue = header.GetRawValue (options); + int rawLength = rawValue.Length; + + if (isDkimSignature && rawLength > 0) { + if (rawValue[rawLength - 1] == (byte) '\n') { + rawLength--; + + if (rawLength > 0 && rawValue[rawLength - 1] == (byte) '\r') + rawLength--; + } + } + + stream.Write (header.RawField, 0, header.RawField.Length); + stream.Write (Header.Colon, 0, Header.Colon.Length); + stream.Write (rawValue, 0, rawLength); + } + + /// + /// Create the digest signing context. + /// + ///+ /// Creates a new digest signing context that uses the specified algorithm. + /// + /// The DKIM signature algorithm. + /// The public key. + ///The digest signer. + internal virtual ISigner CreateVerifyContext (DkimSignatureAlgorithm algorithm, AsymmetricKeyParameter key) + { +#if ENABLE_NATIVE_DKIM + return new SystemSecuritySigner (algorithm, key.AsAsymmetricAlgorithm ()); +#else + ISigner signer; + + switch (algorithm) { + case DkimSignatureAlgorithm.RsaSha1: + signer = new RsaDigestSigner (new Sha1Digest ()); + break; + case DkimSignatureAlgorithm.RsaSha256: + signer = new RsaDigestSigner (new Sha256Digest ()); + break; + case DkimSignatureAlgorithm.Ed25519Sha256: + signer = new Ed25519DigestSigner (new Sha256Digest ()); + break; + default: + throw new NotSupportedException (string.Format ("{0} is not supported.", algorithm)); + } + + signer.Init (key.IsPrivate, key); + + return signer; +#endif + } + + internal static void WriteHeaders (FormatOptions options, MimeMessage message, IListfields, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm, Stream stream) + { + var counts = new Dictionary (StringComparer.Ordinal); + + for (int i = 0; i < fields.Count; i++) { + var headers = fields[i].StartsWith ("Content-", StringComparison.OrdinalIgnoreCase) ? message.Body.Headers : message.Headers; + var name = fields[i].ToLowerInvariant (); + int index, count, n = 0; + + if (!counts.TryGetValue (name, out count)) + count = 0; + + // Note: signers choosing to sign an existing header field that occurs more + // than once in the message (such as Received) MUST sign the physically last + // instance of that header field in the header block. Signers wishing to sign + // multiple instances of such a header field MUST include the header field + // name multiple times in the list of header fields and MUST sign such header + // fields in order from the bottom of the header field block to the top. + index = headers.LastIndexOf (name); + + // find the n'th header with this name + while (n < count && --index >= 0) { + if (headers[index].Field.Equals (name, StringComparison.OrdinalIgnoreCase)) + n++; + } + + if (index < 0) + continue; + + var header = headers[index]; + + switch (headerCanonicalizationAlgorithm) { + case DkimCanonicalizationAlgorithm.Relaxed: + WriteHeaderRelaxed (options, stream, header, false); + break; + default: + WriteHeaderSimple (options, stream, header, false); + break; + } + + counts[name] = ++count; + } + } + + internal static Header GetSignedSignatureHeader (Header header) + { + // modify the raw DKIM-Signature header value by chopping off the signature value after the "b=" + var rawValue = (byte[]) header.RawValue.Clone (); + int length = 0, index = 0; + + do { + while (index < rawValue.Length && rawValue[index].IsWhitespace ()) + index++; + + if (index + 2 < rawValue.Length) { + var param = (char) rawValue[index++]; + + while (index < rawValue.Length && rawValue[index].IsWhitespace ()) + index++; + + if (index < rawValue.Length && rawValue[index] == (byte) '=' && param == 'b') { + length = ++index; + + while (index < rawValue.Length && rawValue[index] != (byte) ';') + index++; + + if (index == rawValue.Length && rawValue[index - 1] == (byte) '\n') { + index--; + + if (rawValue[index - 1] == (byte) '\r') + index--; + } + + break; + } + } + + while (index < rawValue.Length && rawValue[index] != (byte) ';') + index++; + + if (index < rawValue.Length) + index++; + } while (index < rawValue.Length); + + if (index == rawValue.Length) + throw new FormatException (string.Format ("Malformed {0} header: missing signature parameter.", header.Id.ToHeaderName ())); + + while (index < rawValue.Length) + rawValue[length++] = rawValue[index++]; + + Array.Resize (ref rawValue, length); + + return new Header (header.Options, header.RawField, rawValue, false); + } + + /// + /// Verify the hash of the message body. + /// + ///+ /// Verifies the hash of the message body. + /// + /// The formatting options. + /// The signed MIME message. + /// The algorithm used to sign the message. + /// The algorithm used to canonicalize the message body. + /// The max length of the message body to hash or-1 to hash the entire message body. + /// The expected message body hash encoded in base64. + ///+ protected bool VerifyBodyHash (FormatOptions options, MimeMessage message, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm canonicalizationAlgorithm, int maxLength, string bodyHash) + { + var hash = Convert.ToBase64String (message.HashBody (options, signatureAlgorithm, canonicalizationAlgorithm, maxLength)); + + return hash == bodyHash; + } + + /// true if the calculated body hash matches; otherwise, false .+ /// Verify the signature of the message headers. + /// + ///+ /// Verifies the signature of the message headers. + /// + /// The formatting options. + /// The signed MIME message. + /// The DKIM-Signature or ARC-Message-Signature header. + /// The algorithm used to sign the message headers. + /// The public key used to verify the signature. + /// The list of headers that were signed. + /// The algorithm used to canonicalize the headers. + /// The expected signature of the headers encoded in base64. + ///+ protected bool VerifySignature (FormatOptions options, MimeMessage message, Header dkimSignature, DkimSignatureAlgorithm signatureAlgorithm, AsymmetricKeyParameter key, string[] headers, DkimCanonicalizationAlgorithm canonicalizationAlgorithm, string signature) + { + using (var stream = new DkimSignatureStream (CreateVerifyContext (signatureAlgorithm, key))) { + using (var filtered = new FilteredStream (stream)) { + filtered.Add (options.CreateNewLineFilter ()); + + WriteHeaders (options, message, headers, canonicalizationAlgorithm, filtered); + + // now include the DKIM-Signature header that we are verifying, + // but only after removing the "b=" signature value. + var header = GetSignedSignatureHeader (dkimSignature); + + switch (canonicalizationAlgorithm) { + case DkimCanonicalizationAlgorithm.Relaxed: + WriteHeaderRelaxed (options, filtered, header, true); + break; + default: + WriteHeaderSimple (options, filtered, header, true); + break; + } + + filtered.Flush (); + } + + return stream.VerifySignature (signature); + } + } + } +} diff --git a/MimeKit/Cryptography/Ed25519DigestSigner.cs b/MimeKit/Cryptography/Ed25519DigestSigner.cs new file mode 100644 index 0000000000..ec100de3da --- /dev/null +++ b/MimeKit/Cryptography/Ed25519DigestSigner.cs @@ -0,0 +1,114 @@ +// +// Ed25519DigestSigner.cs +// +// Author: Jeffrey Stedfast true if the calculated signature matches; otherwise, false .+// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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 Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Crypto.Parameters; + +namespace MimeKit.Cryptography { + class Ed25519DigestSigner : ISigner + { + Ed25519PrivateKeyParameters privateKey; + Ed25519PublicKeyParameters publicKey; + readonly IDigest digest; + + public Ed25519DigestSigner (IDigest digest) + { + this.digest = digest; + } + + public string AlgorithmName { + get { return digest.AlgorithmName + "withEd25519"; } + } + + public void Init (bool forSigning, ICipherParameters parameters) + { + if (forSigning) { + privateKey = (Ed25519PrivateKeyParameters) parameters; + publicKey = privateKey.GeneratePublicKey (); + } else { + publicKey = (Ed25519PublicKeyParameters) parameters; + privateKey = null; + } + + Reset (); + } + + public void Update (byte input) + { + digest.Update (input); + } + + public void BlockUpdate (byte[] input, int inOff, int length) + { + digest.BlockUpdate (input, inOff, length); + } + + public byte[] GenerateSignature () + { + if (privateKey == null) + throw new InvalidOperationException ("Ed25519DigestSigner not initialised for signature generation."); + + var hash = new byte[digest.GetDigestSize ()]; + + digest.DoFinal (hash, 0); + + var signature = new byte[Ed25519PrivateKeyParameters.SignatureSize]; + privateKey.Sign (Ed25519.Algorithm.Ed25519, null, hash, 0, hash.Length, signature, 0); + + Reset (); + + return signature; + } + + public bool VerifySignature (byte[] signature) + { + if (privateKey != null) + throw new InvalidOperationException ("Ed25519DigestSigner not initialised for verification"); + + if (Ed25519.SignatureSize != signature.Length) + return false; + + var hash = new byte[digest.GetDigestSize ()]; + + digest.DoFinal (hash, 0); + + var pk = publicKey.GetEncoded (); + var result = Ed25519.Verify (signature, 0, pk, 0, hash, 0, hash.Length); + + Reset (); + + return result; + } + + public void Reset () + { + digest.Reset (); + } + } +} diff --git a/MimeKit/Cryptography/EncryptionAlgorithm.cs b/MimeKit/Cryptography/EncryptionAlgorithm.cs index 8aa15f1671..cc092f2d6f 100644 --- a/MimeKit/Cryptography/EncryptionAlgorithm.cs +++ b/MimeKit/Cryptography/EncryptionAlgorithm.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Cryptography/GnuPGContext.cs b/MimeKit/Cryptography/GnuPGContext.cs index 1af582f2e0..efbe928782 100644 --- a/MimeKit/Cryptography/GnuPGContext.cs +++ b/MimeKit/Cryptography/GnuPGContext.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -40,6 +40,7 @@ public abstract class GnuPGContext : OpenPgpContext static readonly Dictionary EncryptionAlgorithms; //static readonly Dictionary PublicKeyAlgorithms; static readonly Dictionary DigestAlgorithms; + static readonly char[] Whitespace = { ' ', '\t' }; static readonly string PublicKeyRing; static readonly string SecretKeyRing; static readonly string Configuration; @@ -49,7 +50,7 @@ static GnuPGContext () var gnupg = Environment.GetEnvironmentVariable ("GNUPGHOME"); if (gnupg == null) { -#if !NETSTANDARD +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 if (Path.DirectorySeparatorChar == '\\') { var appData = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); gnupg = Path.Combine (appData, "gnupg"); @@ -66,7 +67,7 @@ static GnuPGContext () SecretKeyRing = Path.Combine (gnupg, "secring.gpg"); Configuration = Path.Combine (gnupg, "gpg.conf"); - EncryptionAlgorithms = new Dictionary { + EncryptionAlgorithms = new Dictionary (StringComparer.Ordinal) { { "AES", EncryptionAlgorithm.Aes128 }, { "AES128", EncryptionAlgorithm.Aes128 }, { "AES192", EncryptionAlgorithm.Aes192 }, @@ -90,7 +91,7 @@ static GnuPGContext () // { "RSA", PublicKeyAlgorithm.RsaGeneral } //}; - DigestAlgorithms = new Dictionary { + DigestAlgorithms = new Dictionary (StringComparer.Ordinal) { { "RIPEMD160", DigestAlgorithm.RipeMD160 }, { "SHA1", DigestAlgorithm.Sha1 }, { "SHA224", DigestAlgorithm.Sha224 }, @@ -101,7 +102,7 @@ static GnuPGContext () } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///. @@ -135,7 +136,7 @@ void UpdateKeyServerOptions (string value) if (string.IsNullOrEmpty (value)) return; - var options = value.Split (new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + var options = value.Split (Whitespace, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < options.Length; i++) { switch (options[i]) { case "auto-key-retrieve": @@ -147,7 +148,7 @@ void UpdateKeyServerOptions (string value) static EncryptionAlgorithm[] ParseEncryptionAlgorithms (string value) { - var names = value.Split (new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + var names = value.Split (Whitespace, StringSplitOptions.RemoveEmptyEntries); var algorithms = new List (); var seen = new HashSet (); @@ -187,7 +188,7 @@ static EncryptionAlgorithm[] ParseEncryptionAlgorithms (string value) static DigestAlgorithm[] ParseDigestAlgorithms (string value) { - var names = value.Split (new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + var names = value.Split (Whitespace, StringSplitOptions.RemoveEmptyEntries); var algorithms = new List (); var seen = new HashSet (); diff --git a/MimeKit/Cryptography/IDigitalCertificate.cs b/MimeKit/Cryptography/IDigitalCertificate.cs index 05206c9b11..45e6fac287 100644 --- a/MimeKit/Cryptography/IDigitalCertificate.cs +++ b/MimeKit/Cryptography/IDigitalCertificate.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Cryptography/IDigitalSignature.cs b/MimeKit/Cryptography/IDigitalSignature.cs index e39dda5a11..aa26eb09df 100644 --- a/MimeKit/Cryptography/IDigitalSignature.cs +++ b/MimeKit/Cryptography/IDigitalSignature.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -77,10 +77,23 @@ public interface IDigitalSignature /// /// Verifies the digital signature. /// - ///+ /// true if the signature is valid; otherwisefalse ./// true if the signature is valid; otherwise,false ./// An error verifying the signature has occurred. /// bool Verify (); + + ///+ /// Verifies the digital signature. + /// + ///+ /// Verifies the digital signature. + /// + ///true if only the signature itself should be verified; otherwise, both the signature and the certificate chain are validated. + ///+ /// true if the signature is valid; otherwise,false .+ /// An error verifying the signature has occurred. + /// + bool Verify (bool verifySignatureOnly); } } diff --git a/MimeKit/Cryptography/IDkimPublicKeyLocator.cs b/MimeKit/Cryptography/IDkimPublicKeyLocator.cs index 151ded7ad9..c729dbf2d4 100644 --- a/MimeKit/Cryptography/IDkimPublicKeyLocator.cs +++ b/MimeKit/Cryptography/IDkimPublicKeyLocator.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -37,23 +37,33 @@ namespace MimeKit.Cryptography { /// An interface for a service which locates and retrieves DKIM public keys (probably via DNS). ///Since MimeKit itself does not implement DNS, it is up to the client to implement public key lookups /// via DNS. - ////// /// + //////
+ /// + ///+ ///
+ /// + /// public interface IDkimPublicKeyLocator { /// - /// Locate and retrieves the public key for the given domain and selector. + /// Locate and retrieve the public key for the given domain and selector. /// ////// ///Locates and retrieves the public key for the given domain and selector. - ////// /// + //////
+ /// + ///+ ///
+ /// + /// /// The public key. /// A colon-separated list of query methods used to retrieve the public key. The default is"dns/txt" . /// The domain. @@ -62,15 +72,20 @@ public interface IDkimPublicKeyLocator AsymmetricKeyParameter LocatePublicKey (string methods, string domain, string selector, CancellationToken cancellationToken = default (CancellationToken)); ///- /// Asynchronously locate and retrieves the public key for the given domain and selector. + /// Asynchronously locate and retrieve the public key for the given domain and selector. /// ////// ///Locates and retrieves the public key for the given domain and selector. - ////// /// + //////
+ /// + ///+ ///
+ /// + /// /// The public key. /// A colon-separated list of query methods used to retrieve the public key. The default is"dns/txt" . /// The domain. diff --git a/MimeKit/Cryptography/IX509CertificateDatabase.cs b/MimeKit/Cryptography/IX509CertificateDatabase.cs index a8ee7e7819..9be106ad4f 100644 --- a/MimeKit/Cryptography/IX509CertificateDatabase.cs +++ b/MimeKit/Cryptography/IX509CertificateDatabase.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -101,9 +101,9 @@ public interface IX509CertificateDatabase : IX509Store, IDisposable /// The matching certificate records populated with the desired fields. /// The match selector ornull to match all certificates. - ///true if only trusted certificates should be returned. + ///true if only trusted anchor certificates should be returned. /// The desired fields. - IEnumerableFind (IX509Selector selector, bool trustedOnly, X509CertificateRecordFields fields); + IEnumerable Find (IX509Selector selector, bool trustedAnchorsOnly, X509CertificateRecordFields fields); /// /// Add the specified certificate record. diff --git a/MimeKit/Cryptography/LdapUri.cs b/MimeKit/Cryptography/LdapUri.cs index 9a20d12d2d..0533814a89 100644 --- a/MimeKit/Cryptography/LdapUri.cs +++ b/MimeKit/Cryptography/LdapUri.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2019 Xamarin Inc. (www.xamarin.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 diff --git a/MimeKit/Cryptography/MD5.cs b/MimeKit/Cryptography/MD5.cs index dcb45deddd..10719be8dc 100644 --- a/MimeKit/Cryptography/MD5.cs +++ b/MimeKit/Cryptography/MD5.cs @@ -70,7 +70,7 @@ public sealed class MD5 : IDisposable ulong count; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new instance of an MD5 hash algorithm context. @@ -85,7 +85,7 @@ public sealed class MD5 : IDisposable } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new instance of an MD5 hash algorithm context. @@ -97,11 +97,11 @@ public static MD5 Create () /// /// Releases unmanaged resources and performs other cleanup operations before the - /// ///is reclaimed by garbage collection. + /// is reclaimed by garbage collection. /// /// Releases unmanaged resources and performs other cleanup operations before the - /// ~MD5 () { @@ -733,12 +733,12 @@ void Dispose (bool disposing) } ///is reclaimed by garbage collection. + /// is reclaimed by garbage collection. /// - /// Releases all resource used by the - ///object. + /// Releases all resource used by the object. /// Call + ///when you are finished using the . The - /// method leaves the in an unusable state. After calling - /// , you must release all references to the so the - /// garbage collector can reclaim the memory that the was occupying. Call public void Dispose () { Dispose (true); diff --git a/MimeKit/Cryptography/MultipartEncrypted.cs b/MimeKit/Cryptography/MultipartEncrypted.cs index b15ce2eb44..d6a69fc9a4 100644 --- a/MimeKit/Cryptography/MultipartEncrypted.cs +++ b/MimeKit/Cryptography/MultipartEncrypted.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastwhen you are finished using the . The + /// method leaves the in an unusable state. After calling + /// , you must release all references to the so the + /// garbage collector can reclaim the memory that the was occupying. // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -44,10 +44,10 @@ namespace MimeKit.Cryptography { public class MultipartEncrypted : Multipart { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -58,7 +58,7 @@ public MultipartEncrypted (MimeEntityConstructorArgs args) : base (args) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -71,12 +71,12 @@ public MultipartEncrypted () : base ("encrypted") /// Dispatches to the specific visit method for this MIME entity. /// - /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -1029,11 +1029,10 @@ public static MultipartEncrypted Encrypt (IEnumerable public MimeEntity Decrypt (OpenPgpContext ctx, CancellationToken cancellationToken = default (CancellationToken)) { - DigitalSignatureCollection signatures; - - return Decrypt (ctx, out signatures, cancellationToken); + return Decrypt (ctx, out _, cancellationToken); } ///recipients, if (ctx == null) throw new ArgumentNullException (nameof (ctx)); - var protocol = ContentType.Parameters["protocol"]; + var protocol = ContentType.Parameters["protocol"]?.Trim (); if (string.IsNullOrEmpty (protocol)) throw new FormatException (); - protocol = protocol.Trim ().ToLowerInvariant (); if (!ctx.Supports (protocol)) throw new NotSupportedException (); @@ -1046,7 +1045,7 @@ public static MultipartEncrypted Encrypt (IEnumerable recipients, var ctype = version.ContentType; var value = string.Format ("{0}/{1}", ctype.MediaType, ctype.MediaSubtype); - if (value.ToLowerInvariant () != protocol) + if (!value.Equals (protocol, StringComparison.OrdinalIgnoreCase)) throw new FormatException (); var encrypted = this[1] as MimePart; @@ -1097,9 +1096,7 @@ public static MultipartEncrypted Encrypt (IEnumerable recipients, /// @@ -1118,7 +1115,7 @@ public static MultipartEncrypted Encrypt (IEnumerable recipients, /// The multipart is malformed in some way. ///- /// A suitable ///for + /// A suitable for /// decrypting could not be found. /// @@ -1134,12 +1131,10 @@ public static MultipartEncrypted Encrypt (IEnumerable public MimeEntity Decrypt (out DigitalSignatureCollection signatures, CancellationToken cancellationToken = default (CancellationToken)) { - var protocol = ContentType.Parameters["protocol"]; + var protocol = ContentType.Parameters["protocol"]?.Trim (); if (string.IsNullOrEmpty (protocol)) throw new FormatException (); - protocol = protocol.Trim ().ToLowerInvariant (); - if (Count < 2) throw new FormatException (); @@ -1149,7 +1144,7 @@ public static MultipartEncrypted Encrypt (IEnumerablerecipients, /// recipients, var ctype = version.ContentType; var value = string.Format ("{0}/{1}", ctype.MediaType, ctype.MediaSubtype); - if (value.ToLowerInvariant () != protocol) + if (!value.Equals (protocol, StringComparison.OrdinalIgnoreCase)) throw new FormatException (); var encrypted = this[1] as MimePart; @@ -1190,7 +1185,7 @@ public static MultipartEncrypted Encrypt (IEnumerable recipients, /// The multipart is malformed in some way. ///- /// A suitable ///for + /// A suitable for /// decrypting could not be found. /// @@ -1206,9 +1201,7 @@ public static MultipartEncrypted Encrypt (IEnumerable public MimeEntity Decrypt (CancellationToken cancellationToken = default (CancellationToken)) { - DigitalSignatureCollection signatures; - - return Decrypt (out signatures, cancellationToken); + return Decrypt (out _, cancellationToken); } } } diff --git a/MimeKit/Cryptography/MultipartSigned.cs b/MimeKit/Cryptography/MultipartSigned.cs index cf3b06b122..04b30c01ad 100644 --- a/MimeKit/Cryptography/MultipartSigned.cs +++ b/MimeKit/Cryptography/MultipartSigned.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastrecipients, /// // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -46,9 +46,9 @@ namespace MimeKit.Cryptography { public class MultipartSigned : Multipart { /// - /// Initializes a new instance of the - ///class. + /// Initialize a new instance of the class. /// This constructor is used by + ///. This constructor is used by /// Information used by the constructor. ///. /// public Taskis null . @@ -58,7 +58,7 @@ public MultipartSigned (MimeEntityConstructorArgs args) : base (args) } ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -71,12 +71,12 @@ public MultipartSigned () : base ("signed") /// Dispatches to the specific visit method for this MIME entity. /// /// - /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -409,11 +409,11 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = if (ctx == null) throw new ArgumentNullException (nameof (ctx)); - var protocol = ContentType.Parameters["protocol"]; + var protocol = ContentType.Parameters["protocol"]?.Trim (); if (string.IsNullOrEmpty (protocol)) throw new FormatException ("The multipart/signed part did not specify a protocol."); - if (!ctx.Supports (protocol.Trim ())) + if (!ctx.Supports (protocol)) throw new NotSupportedException ("The specified cryptography context does not support the signature protocol."); if (Count < 2) @@ -436,6 +436,7 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = // Note: see rfc2015 or rfc3156, section 5.1 var options = FormatOptions.CloneDefault (); options.NewLineFormat = NewLineFormat.Dos; + options.VerifyingSignature = true; this[0].WriteTo (options, cleartext); cleartext.Position = 0; @@ -474,11 +475,11 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = if (ctx == null) throw new ArgumentNullException (nameof (ctx)); - var protocol = ContentType.Parameters["protocol"]; + var protocol = ContentType.Parameters["protocol"]?.Trim (); if (string.IsNullOrEmpty (protocol)) throw new FormatException ("The multipart/signed part did not specify a protocol."); - if (!ctx.Supports (protocol.Trim ())) + if (!ctx.Supports (protocol)) throw new NotSupportedException ("The specified cryptography context does not support the signature protocol."); if (Count < 2) @@ -501,6 +502,7 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = // Note: see rfc2015 or rfc3156, section 5.1 var options = FormatOptions.CloneDefault (); options.NewLineFormat = NewLineFormat.Dos; + options.VerifyingSignature = true; await this[0].WriteToAsync (options, cleartext, cancellationToken); cleartext.Position = 0; @@ -534,12 +536,11 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = /// public DigitalSignatureCollection Verify (CancellationToken cancellationToken = default (CancellationToken)) { - var protocol = ContentType.Parameters["protocol"]; + var protocol = ContentType.Parameters["protocol"]?.Trim (); + if (string.IsNullOrEmpty (protocol)) throw new FormatException ("The multipart/signed part did not specify a protocol."); - protocol = protocol.Trim ().ToLowerInvariant (); - using (var ctx = CryptographyContext.Create (protocol)) return Verify (ctx, cancellationToken); } @@ -568,12 +569,11 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = ///VerifyAsync (CancellationToken cancellationToken = default (CancellationToken)) { - var protocol = ContentType.Parameters["protocol"]; + var protocol = ContentType.Parameters["protocol"]?.Trim (); + if (string.IsNullOrEmpty (protocol)) throw new FormatException ("The multipart/signed part did not specify a protocol."); - protocol = protocol.Trim ().ToLowerInvariant (); - using (var ctx = CryptographyContext.Create (protocol)) return VerifyAsync (ctx, cancellationToken); } diff --git a/MimeKit/Cryptography/NpgsqlCertificateDatabase.cs b/MimeKit/Cryptography/NpgsqlCertificateDatabase.cs index c2d7fb98f0..1bee953df6 100644 --- a/MimeKit/Cryptography/NpgsqlCertificateDatabase.cs +++ b/MimeKit/Cryptography/NpgsqlCertificateDatabase.cs @@ -3,7 +3,7 @@ // // Author: Federico Di Gregorio // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2019 Xamarin Inc. (www.xamarin.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 @@ -30,6 +30,7 @@ using System.Text; using System.Reflection; using System.Data.Common; +using System.Collections.Generic; namespace MimeKit.Cryptography { /// @@ -115,89 +116,149 @@ public NpgsqlCertificateDatabase (DbConnection connection, string password) : ba } /// - /// Gets the command to create the certificates table. + /// Gets the columns for the specified table. /// ///- /// Constructs the command to create a certificates table suitable for storing - /// - ///objects. + /// Gets the list of columns for the specified table. /// The /// The. . - protected override DbCommand GetCreateCertificatesTableCommand (DbConnection connection) + /// The name of the table. + /// The list of columns. + protected override IListGetTableColumns (DbConnection connection, string tableName) { - var statement = new StringBuilder ("CREATE TABLE IF NOT EXISTS CERTIFICATES("); - var columns = X509CertificateRecord.ColumnNames; - - for (int i = 0; i < columns.Length; i++) { - if (i > 0) - statement.Append (", "); - - statement.Append (columns[i]).Append (' '); - switch (columns[i]) { - case "ID": statement.Append ("serial PRIMARY KEY"); break; - case "BASICCONSTRAINTS": statement.Append ("integer NOT NULL"); break; - case "TRUSTED": statement.Append ("boolean NOT NULL"); break; - case "KEYUSAGE": statement.Append ("integer NOT NULL"); break; - case "NOTBEFORE": statement.Append ("timestamp NOT NULL"); break; - case "NOTAFTER": statement.Append ("timestamp NOT NULL"); break; - case "ISSUERNAME": statement.Append ("text NOT NULL"); break; - case "SERIALNUMBER": statement.Append ("text NOT NULL"); break; - case "SUBJECTEMAIL": statement.Append ("text "); break; - case "FINGERPRINT": statement.Append ("text NOT NULL"); break; - case "ALGORITHMS": statement.Append ("text"); break; - case "ALGORITHMSUPDATED": statement.Append ("timestamp NOT NULL"); break; - case "CERTIFICATE": statement.Append ("bytea UNIQUE NOT NULL"); break; - case "PRIVATEKEY": statement.Append ("bytea"); break; + using (var command = connection.CreateCommand ()) { + command.CommandText = $"PRAGMA table_info({tableName})"; + using (var reader = command.ExecuteReader ()) { + var columns = new List (); + + while (reader.Read ()) { + var column = new DataColumn (); + + for (int i = 0; i < reader.FieldCount; i++) { + var field = reader.GetName (i).ToUpperInvariant (); + + switch (field) { + case "NAME": + column.ColumnName = reader.GetString (i); + break; + case "TYPE": + var type = reader.GetString (i); + switch (type) { + case "boolean": column.DataType = typeof (bool); break; + case "integer": column.DataType = typeof (long); break; + case "bytea": column.DataType = typeof (byte[]); break; + case "text": column.DataType = typeof (string); break; + } + break; + case "NOTNULL": + column.AllowDBNull = !reader.GetBoolean (i); + break; + } + } + + columns.Add (column); + } + + return columns; } } + } - statement.Append (')'); + static void Build (StringBuilder statement, DataTable table, DataColumn column, ref int primaryKeys) + { + statement.Append (column.ColumnName); + statement.Append (' '); + + if (column.DataType == typeof (long) || column.DataType == typeof (int)) { + if (column.AutoIncrement) + statement.Append ("serial"); + else + statement.Append ("integer"); + } else if (column.DataType == typeof (bool)) { + statement.Append ("boolean"); + } else if (column.DataType == typeof (byte[])) { + statement.Append ("bytea"); + } else if (column.DataType == typeof (string)) { + statement.Append ("text"); + } else { + throw new NotImplementedException (); + } - var command = connection.CreateCommand (); + bool isPrimaryKey = false; + if (table != null && table.PrimaryKey != null && primaryKeys < table.PrimaryKey.Length) { + for (int i = 0; i < table.PrimaryKey.Length; i++) { + if (column == table.PrimaryKey[i]) { + statement.Append (" PRIMARY KEY"); + isPrimaryKey = true; + primaryKeys++; + break; + } + } + } - command.CommandText = statement.ToString (); - command.CommandType = CommandType.Text; + if (column.Unique && !isPrimaryKey) + statement.Append (" UNIQUE"); - return command; + if (!column.AllowDBNull) + statement.Append (" NOT NULL"); } /// - /// Gets the command to create the CRLs table. + /// Create a table. /// ///- /// Constructs the command to create a CRLs table suitable for storing - /// - ///objects. + /// Creates the specified table. /// The /// The. . - protected override DbCommand GetCreateCrlsTableCommand (DbConnection connection) + /// The table. + protected override void CreateTable (DbConnection connection, DataTable table) { - var statement = new StringBuilder ("CREATE TABLE IF NOT EXISTS CRLS("); - var columns = X509CrlRecord.ColumnNames; - - for (int i = 0; i < columns.Length; i++) { - if (i > 0) - statement.Append (", "); - - statement.Append (columns[i]).Append (' '); - switch (columns[i]) { - case "ID": statement.Append ("serial PRIMARY KEY"); break; - case "DELTA" : statement.Append ("integer NOT NULL"); break; - case "ISSUERNAME": statement.Append ("text NOT NULL"); break; - case "THISUPDATE": statement.Append ("integer NOT NULL"); break; - case "NEXTUPDATE": statement.Append ("integer NOT NULL"); break; - case "CRL": statement.Append ("bytea NOT NULL"); break; - } + var statement = new StringBuilder ("CREATE TABLE IF NOT EXISTS "); + int primaryKeys = 0; + + statement.Append (table.TableName); + statement.Append ('('); + + foreach (DataColumn column in table.Columns) { + Build (statement, table, column, ref primaryKeys); + statement.Append (", "); } + if (table.Columns.Count > 0) + statement.Length -= 2; + statement.Append (')'); - var command = connection.CreateCommand (); + using (var command = connection.CreateCommand ()) { + command.CommandText = statement.ToString (); + command.CommandType = CommandType.Text; + command.ExecuteNonQuery (); + } + } - command.CommandText = statement.ToString (); - command.CommandType = CommandType.Text; + /// + /// Adds a column to a table. + /// + ///+ /// Adds a column to a table. + /// + /// The. + /// The table. + /// The column to add. + protected override void AddTableColumn (DbConnection connection, DataTable table, DataColumn column) + { + var statement = new StringBuilder ("ALTER TABLE "); + int primaryKeys = table.PrimaryKey?.Length ?? 0; + + statement.Append (table.TableName); + statement.Append (" ADD COLUMN "); + Build (statement, table, column, ref primaryKeys); - return command; + using (var command = connection.CreateCommand ()) { + command.CommandText = statement.ToString (); + command.CommandType = CommandType.Text; + command.ExecuteNonQuery (); + } } } } diff --git a/MimeKit/Cryptography/OpenPgpBlockFilter.cs b/MimeKit/Cryptography/OpenPgpBlockFilter.cs index 33c21a7d6b..d5c109b42d 100644 --- a/MimeKit/Cryptography/OpenPgpBlockFilter.cs +++ b/MimeKit/Cryptography/OpenPgpBlockFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -43,7 +43,7 @@ class OpenPgpBlockFilter : MimeFilterBase bool midline; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///. diff --git a/MimeKit/Cryptography/OpenPgpContext.cs b/MimeKit/Cryptography/OpenPgpContext.cs index cdc3348fe3..37c504d5ef 100644 --- a/MimeKit/Cryptography/OpenPgpContext.cs +++ b/MimeKit/Cryptography/OpenPgpContext.cs @@ -1,9 +1,9 @@ -// +// // OpenPgpContext.cs // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,10 +26,7 @@ using System; using System.IO; -using System.Net; using System.Linq; -using System.Text; -using System.Net.Http; using System.Threading; using System.Diagnostics; using System.Threading.Tasks; @@ -39,6 +36,7 @@ using Org.BouncyCastle.Math; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Prng; using Org.BouncyCastle.Bcpg.OpenPgp; using Org.BouncyCastle.Crypto.Parameters; @@ -47,112 +45,32 @@ namespace MimeKit.Cryptography { /// - /// An abstract OpenPGP cryptography context which can be used for PGP/MIME. + /// An abstract OpenPGP cryptography context which can be used for OpenPGP and PGP/MIME that + /// manages keyrings stored on the local file system as keyring bundles. /// ///- /// Generally speaking, applications should not use a - public abstract class OpenPgpContext : CryptographyContext + public abstract class OpenPgpContext : OpenPgpContextBase { - const string BeginPublicKeyBlock = "-----BEGIN PGP PUBLIC KEY BLOCK-----"; - const string EndPublicKeyBlock = "-----END PGP PUBLIC KEY BLOCK-----"; - - internal static readonly EncryptionAlgorithm[] DefaultEncryptionAlgorithmRank = { - EncryptionAlgorithm.Idea, - EncryptionAlgorithm.TripleDes, - EncryptionAlgorithm.Cast5, - EncryptionAlgorithm.Blowfish, - EncryptionAlgorithm.Aes128, - EncryptionAlgorithm.Aes192, - EncryptionAlgorithm.Aes256, - EncryptionAlgorithm.Twofish, - EncryptionAlgorithm.Camellia128, - EncryptionAlgorithm.Camellia192, - EncryptionAlgorithm.Camellia256 - }; - - internal static readonly DigestAlgorithm[] DefaultDigestAlgorithmRank = { - DigestAlgorithm.Sha1, - DigestAlgorithm.RipeMD160, - DigestAlgorithm.Sha256, - DigestAlgorithm.Sha384, - DigestAlgorithm.Sha512, - DigestAlgorithm.Sha224 - }; - - EncryptionAlgorithm defaultAlgorithm; - HttpClient client; - Uri keyServer; - ///+ /// PGP software such as older versions of GnuPG (pre 2.1.0) typically store the user's + /// keyrings on the file system using the OpenPGP Keyring Bundle format. + ///Generally speaking, applications should not use a ////// directly, but rather via higher level APIs such as - /// and . + /// and . - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Subclasses choosing to use this constructor MUST set the - protected OpenPgpContext () + protected OpenPgpContext () : base () { - EncryptionAlgorithmRank = DefaultEncryptionAlgorithmRank; - DigestAlgorithmRank = DefaultDigestAlgorithmRank; - - foreach (var algorithm in EncryptionAlgorithmRank) - Enable (algorithm); - - foreach (var algorithm in DigestAlgorithmRank) - Enable (algorithm); - - defaultAlgorithm = EncryptionAlgorithm.Cast5; - - client = new HttpClient (); } -#if PORTABLE - ///, /// , , and the /// properties themselves. /// - /// Initializes a new instance of the - ///class. - /// - /// Creates a new - /// The public keyring. - /// The secret keyring. - ///using the specified public and private keyrings. - /// - /// - ///- /// is null .-or- - ///- /// is null .- /// An error occurred while reading one of the keyring files. - /// - ///- /// An error occurred while parsing one of the keyring files. - /// - protected OpenPgpContext (Stream pubring, Stream secring) : this () - { - if (pubring == null) - throw new ArgumentNullException ("pubring"); - - if (secring == null) - throw new ArgumentNullException ("secring"); - - PublicKeyRing = pubring; - SecretKeyRing = secring; - - PublicKeyRingBundle = new PgpPublicKeyRingBundle (pubring); - SecretKeyRingBundle = new PgpSecretKeyRingBundle (secring); - - if (pubring.CanSeek) - pubring.Seek (0, SeekOrigin.Begin); - - if (secring.CanSeek) - secring.Seek (0, SeekOrigin.Begin); - } -#else ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///using the specified public and private keyring paths. @@ -182,7 +100,7 @@ protected OpenPgpContext (string pubring, string secring) : this () SecretKeyRingPath = secring; if (File.Exists (pubring)) { - using (var file = File.Open (pubring, FileMode.Open, FileAccess.Read)) { + using (var file = File.OpenRead (pubring)) { PublicKeyRingBundle = new PgpPublicKeyRingBundle (file); } } else { @@ -190,103 +108,14 @@ protected OpenPgpContext (string pubring, string secring) : this () } if (File.Exists (secring)) { - using (var file = File.Open (secring, FileMode.Open, FileAccess.Read)) { + using (var file = File.OpenRead (secring)) { SecretKeyRingBundle = new PgpSecretKeyRingBundle (file); } } else { SecretKeyRingBundle = new PgpSecretKeyRingBundle (new byte[0]); } } -#endif - - /// - /// Get or set the default encryption algorithm. - /// - ///- /// Gets or sets the default encryption algorithm. - /// - ///The encryption algorithm. - ///- /// The specified encryption algorithm is not supported. - /// - public EncryptionAlgorithm DefaultEncryptionAlgorithm { - get { return defaultAlgorithm; } - set { - GetSymmetricKeyAlgorithm (value); - defaultAlgorithm = value; - } - } - - bool IsValidKeyServer { - get { - if (keyServer == null) - return false; - - switch (keyServer.Scheme.ToLowerInvariant ()) { - case "https": case "http": case "hkp": return true; - default: return false; - } - } - } - - ///- /// Get or set the key server to use when automatically retrieving keys. - /// - ///- /// - ///Gets or sets the key server to use when verifying keys that are - /// not already in the public keychain. - ///Only HTTP and HKP protocols are supported. - ///The key server. - ///- /// - public Uri KeyServer { - get { return keyServer; } - set { - if (value != null && !value.IsAbsoluteUri) - throw new ArgumentException ("The key server URI must be absolute.", nameof (value)); - - keyServer = value; - } - } - - ///is not an absolute URI. - /// - /// Get or set whether unknown PGP keys should automtically be retrieved. - /// - ///- /// - ///Gets or sets whether or not the - ///should automatically - /// fetch keys as needed from the keyserver when verifying signatures. Requires a valid - ///to be set. - public bool AutoKeyRetrieve { - get; set; - } - -#if PORTABLE - /// true if unknown PGP keys should automatically be retrieved; otherwise,false .- /// Get the public keyring. - /// - ///- /// Gets the public keyring. - /// - ///The public key ring. - protected Stream PublicKeyRing { - get; set; - } - ///- /// Get the secret keyring. - /// - ///- /// Gets the secret keyring. - /// - ///The secret key ring. - protected Stream SecretKeyRing { - get; set; - } -#else ////// Get the public keyring path. /// @@ -308,7 +137,6 @@ protected string PublicKeyRingPath { protected string SecretKeyRingPath { get; set; } -#endif ////// Get the public keyring bundle. @@ -332,271 +160,74 @@ public PgpSecretKeyRingBundle SecretKeyRingBundle { get; protected set; } - /// - /// Get the signature protocol. - /// - ///- /// - ///The signature protocol is used by - ///- /// in order to determine what the protocol parameter of the Content-Type - /// header should be. The signature protocol. - public override string SignatureProtocol { - get { return "application/pgp-signature"; } - } + bool TryGetPublicKeyRing (long keyId, out PgpPublicKeyRing keyring) + { + foreach (PgpPublicKeyRing ring in PublicKeyRingBundle.GetKeyRings ()) { + foreach (PgpPublicKey key in ring.GetPublicKeys ()) { + if (key.KeyId == keyId) { + keyring = ring; + return true; + } + } + } - ///- /// Get the encryption protocol. - /// - ///- /// - ///The encryption protocol is used by - ///- /// in order to determine what the protocol parameter of the Content-Type - /// header should be. The encryption protocol. - public override string EncryptionProtocol { - get { return "application/pgp-encrypted"; } - } + keyring = null; - ///- /// Get the key exchange protocol. - /// - ///- /// Gets the key exchange protocol. - /// - ///The key exchange protocol. - public override string KeyExchangeProtocol { - get { return "application/pgp-keys"; } + return false; } ///- /// Check whether or not the specified protocol is supported. + /// Get the public keyring that contains the specified key. /// ///- /// Used in order to make sure that the protocol parameter value specified in either a multipart/signed - /// or multipart/encrypted part is supported by the supplied cryptography context. + /// - ///Gets the public keyring that contains the specified key. + ///Implementations should first try to obtain the keyring stored (or cached) locally. + /// Failing that, if ///is enabled, they should use + /// to attempt to + /// retrieve the keyring from the configured . - /// The protocol. - /// true if the protocol is supported; otherwisefalse - /// is null . + /// The public key identifier. + /// The cancellation token. + ///The public keyring that contains the specified key or + ///null if the keyring could not be found.+ /// The operation was cancelled. /// - public override bool Supports (string protocol) + protected override PgpPublicKeyRing GetPublicKeyRing (long keyId, CancellationToken cancellationToken) { - if (protocol == null) - throw new ArgumentNullException (nameof (protocol)); - - var type = protocol.ToLowerInvariant ().Split ('/'); - if (type.Length != 2 || type[0] != "application") - return false; + if (TryGetPublicKeyRing (keyId, out var keyring)) + return keyring; - if (type[1].StartsWith ("x-", StringComparison.Ordinal)) - type[1] = type[1].Substring (2); - - return type[1] == "pgp-signature" || type[1] == "pgp-encrypted" || type[1] == "pgp-keys"; - } + if (AutoKeyRetrieve) + return RetrievePublicKeyRing (keyId, cancellationToken); - ///- /// Get the string name of the digest algorithm for use with the micalg parameter of a multipart/signed part. - /// - ///- /// - ///Maps the - ///to the appropriate string identifier - /// as used by the micalg parameter value of a multipart/signed Content-Type - /// header. For example: - ///
- ///- /// Algorithm Name - - ///
pgp-md5 - - ///
pgp-sha1 - - ///
pgp-ripemd160 - - ///
pgp-md2 - - ///
pgp-tiger192 - - ///
pgp-haval-5-160 - - ///
pgp-sha256 - - ///
pgp-sha384 - - ///
pgp-sha512 - - ///
pgp-sha224 The micalg value. - /// The digest algorithm. - ///- /// - public override string GetDigestAlgorithmName (DigestAlgorithm micalg) - { - switch (micalg) { - case DigestAlgorithm.MD5: return "pgp-md5"; - case DigestAlgorithm.Sha1: return "pgp-sha1"; - case DigestAlgorithm.RipeMD160: return "pgp-ripemd160"; - case DigestAlgorithm.MD2: return "pgp-md2"; - case DigestAlgorithm.Tiger192: return "pgp-tiger192"; - case DigestAlgorithm.Haval5160: return "pgp-haval-5-160"; - case DigestAlgorithm.Sha256: return "pgp-sha256"; - case DigestAlgorithm.Sha384: return "pgp-sha384"; - case DigestAlgorithm.Sha512: return "pgp-sha512"; - case DigestAlgorithm.Sha224: return "pgp-sha224"; - case DigestAlgorithm.MD4: return "pgp-md4"; - default: throw new ArgumentOutOfRangeException (nameof (micalg)); - } + return null; } ///is out of range. - /// - /// Get the digest algorithm from the micalg parameter value in a multipart/signed part. + /// Asynchronously get the public keyring that contains the specified key. /// ///- /// Maps the micalg parameter value string back to the appropriate - ///. + /// Gets the public keyring that contains the specified key. + ///Implementations should first try to obtain the keyring stored (or cached) locally. + /// Failing that, if ///is enabled, they should use + /// to attempt to + /// retrieve the keyring from the configured . The digest algorithm. - /// The micalg parameter value. - ///- /// is null . + /// The public key identifier. + /// The cancellation token. + ///The public keyring that contains the specified key or + ///null if the keyring could not be found.+ /// The operation was cancelled. /// - public override DigestAlgorithm GetDigestAlgorithm (string micalg) - { - if (micalg == null) - throw new ArgumentNullException (nameof (micalg)); - - switch (micalg.ToLowerInvariant ()) { - case "pgp-md5": return DigestAlgorithm.MD5; - case "pgp-sha1": return DigestAlgorithm.Sha1; - case "pgp-ripemd160": return DigestAlgorithm.RipeMD160; - case "pgp-md2": return DigestAlgorithm.MD2; - case "pgp-tiger192": return DigestAlgorithm.Tiger192; - case "pgp-haval-5-160": return DigestAlgorithm.Haval5160; - case "pgp-sha256": return DigestAlgorithm.Sha256; - case "pgp-sha384": return DigestAlgorithm.Sha384; - case "pgp-sha512": return DigestAlgorithm.Sha512; - case "pgp-sha224": return DigestAlgorithm.Sha224; - case "pgp-md4": return DigestAlgorithm.MD4; - default: return DigestAlgorithm.None; - } - } - - static string HexEncode (byte[] data) - { - var fingerprint = new StringBuilder (); - - for (int i = 0; i < data.Length; i++) - fingerprint.Append (data[i].ToString ("x2")); - - return fingerprint.ToString (); - } - - static bool PgpPublicKeyMatches (PgpPublicKey key, MailboxAddress mailbox) - { - var secure = mailbox as SecureMailboxAddress; - - if (secure != null && !string.IsNullOrEmpty (secure.Fingerprint)) { - if (secure.Fingerprint.Length > 16) { - var fingerprint = HexEncode (key.GetFingerprint ()); - - return secure.Fingerprint.Equals (fingerprint, StringComparison.OrdinalIgnoreCase); - } - - var id = ((int) key.KeyId).ToString ("X2"); - - return secure.Fingerprint.EndsWith (id, StringComparison.OrdinalIgnoreCase); - } - - foreach (string userId in key.GetUserIds ()) { - MailboxAddress email; - - if (!MailboxAddress.TryParse (userId, out email)) - continue; - - if (mailbox.Address.Equals (email.Address, StringComparison.OrdinalIgnoreCase)) - return true; - } - - return false; - } - - async TaskRetrievePublicKeyRingAsync (long keyId, bool doAsync, CancellationToken cancellationToken) - { - var scheme = keyServer.Scheme.ToLowerInvariant (); - var uri = new UriBuilder (); - - uri.Scheme = scheme == "hkp" ? "http" : scheme; - uri.Host = keyServer.Host; - - if (keyServer.IsDefaultPort) { - if (scheme == "hkp") - uri.Port = 11371; - } else { - uri.Port = keyServer.Port; - } - - uri.Path = "/pks/lookup"; - uri.Query = string.Format ("op=get&search=0x{0:X}", keyId); - - using (var stream = new MemoryBlockStream ()) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (new OpenPgpBlockFilter (BeginPublicKeyBlock, EndPublicKeyBlock)); - - if (doAsync) { - using (var response = await client.GetAsync (uri.ToString (), cancellationToken)) - await response.Content.CopyToAsync (filtered); - } else { -#if !NETSTANDARD && !PORTABLE - var request = (HttpWebRequest) WebRequest.Create (uri.ToString ()); - using (var response = request.GetResponse ()) { - var content = response.GetResponseStream (); - content.CopyTo (filtered, 4096); - } -#else - using (var response = client.GetAsync (uri.ToString (), cancellationToken).GetAwaiter ().GetResult ()) - response.Content.CopyToAsync (filtered).GetAwaiter ().GetResult (); -#endif - } - - filtered.Flush (); - } - - stream.Position = 0; - - using (var armored = new ArmoredInputStream (stream, true)) { - var bundle = new PgpPublicKeyRingBundle (armored); - - Import (bundle); - - return bundle.GetPublicKeyRing (keyId); - } - } - } - - class KeyRetrievalResults - { - public readonly PgpPublicKeyRing KeyRing; - public readonly PgpPublicKey Key; - - public KeyRetrievalResults (PgpPublicKeyRing keyring, PgpPublicKey pubkey) - { - KeyRing = keyring; - Key = pubkey; - } - } - - async Task GetPublicKeyRingAsync (long keyId, bool doAsync, CancellationToken cancellationToken) + protected override async Task GetPublicKeyRingAsync (long keyId, CancellationToken cancellationToken) { - foreach (PgpPublicKeyRing ring in PublicKeyRingBundle.GetKeyRings ()) { - foreach (PgpPublicKey key in ring.GetPublicKeys ()) { - if (key.KeyId == keyId) - return new KeyRetrievalResults (ring, key); - } - } - - if (AutoKeyRetrieve && IsValidKeyServer) { - try { - var keyring = await RetrievePublicKeyRingAsync (keyId, doAsync, cancellationToken).ConfigureAwait (false); + if (TryGetPublicKeyRing (keyId, out var keyring)) + return keyring; - return new KeyRetrievalResults (keyring, keyring.GetPublicKey (keyId)); - } catch (OperationCanceledException) { - throw; - } catch { - } - } + if (AutoKeyRetrieve) + return await RetrievePublicKeyRingAsync (keyId, cancellationToken).ConfigureAwait (false); - return new KeyRetrievalResults (null, null); + return null; } /// @@ -606,7 +237,7 @@ async Task GetPublicKeyRingAsync (long keyId, bool doAsync, /// Enumerates all public keyrings. /// The list of available public keyrings. - public IEnumerableEnumeratePublicKeyRings () + public virtual IEnumerable EnumeratePublicKeyRings () { foreach (PgpPublicKeyRing keyring in PublicKeyRingBundle.GetKeyRings ()) yield return keyring; @@ -621,7 +252,7 @@ public IEnumerable EnumeratePublicKeyRings () /// Enumerates all public keys. /// The list of available public keys. - public IEnumerableEnumeratePublicKeys () + public virtual IEnumerable EnumeratePublicKeys () { foreach (var keyring in EnumeratePublicKeyRings ()) { foreach (PgpPublicKey key in keyring.GetPublicKeys ()) @@ -642,13 +273,13 @@ public IEnumerable EnumeratePublicKeys () /// /// - public IEnumerableis null . ///EnumeratePublicKeyRings (MailboxAddress mailbox) + public virtual IEnumerable EnumeratePublicKeyRings (MailboxAddress mailbox) { if (mailbox == null) throw new ArgumentNullException (nameof (mailbox)); foreach (var keyring in EnumeratePublicKeyRings ()) { - if (PgpPublicKeyMatches (keyring.GetPublicKey (), mailbox)) + if (IsMatch (keyring.GetPublicKey (), mailbox)) yield return keyring; } @@ -666,7 +297,7 @@ public IEnumerable EnumeratePublicKeyRings (MailboxAddress mai /// /// - public IEnumerableis null . ///EnumeratePublicKeys (MailboxAddress mailbox) + public virtual IEnumerable EnumeratePublicKeys (MailboxAddress mailbox) { foreach (var keyring in EnumeratePublicKeyRings (mailbox)) { foreach (PgpPublicKey key in keyring.GetPublicKeys ()) @@ -683,7 +314,7 @@ public IEnumerable EnumeratePublicKeys (MailboxAddress mailbox) /// Enumerates all secret keyrings. /// /// The list of available secret keyrings. - public IEnumerableEnumerateSecretKeyRings () + public virtual IEnumerable EnumerateSecretKeyRings () { foreach (PgpSecretKeyRing keyring in SecretKeyRingBundle.GetKeyRings ()) yield return keyring; @@ -698,7 +329,7 @@ public IEnumerable EnumerateSecretKeyRings () /// Enumerates all secret keys. /// /// The list of available secret keys. - public IEnumerableEnumerateSecretKeys () + public virtual IEnumerable EnumerateSecretKeys () { foreach (var keyring in EnumerateSecretKeyRings ()) { foreach (PgpSecretKey key in keyring.GetSecretKeys ()) @@ -719,13 +350,13 @@ public IEnumerable EnumerateSecretKeys () /// /// - public IEnumerableis null . ///EnumerateSecretKeyRings (MailboxAddress mailbox) + public virtual IEnumerable EnumerateSecretKeyRings (MailboxAddress mailbox) { if (mailbox == null) throw new ArgumentNullException (nameof (mailbox)); foreach (var keyring in EnumerateSecretKeyRings ()) { - if (PgpSecretKeyMatches (keyring.GetSecretKey (), mailbox)) + if (IsMatch (keyring.GetSecretKey (), mailbox)) yield return keyring; } @@ -743,7 +374,7 @@ public IEnumerable EnumerateSecretKeyRings (MailboxAddress mai /// /// - public IEnumerableis null . ///EnumerateSecretKeys (MailboxAddress mailbox) + public virtual IEnumerable EnumerateSecretKeys (MailboxAddress mailbox) { foreach (var keyring in EnumerateSecretKeyRings (mailbox)) { foreach (PgpSecretKey key in keyring.GetSecretKeys ()) @@ -770,16 +401,9 @@ public IEnumerable EnumerateSecretKeys (MailboxAddress mailbox) protected virtual PgpPublicKey GetPublicKey (MailboxAddress mailbox) { foreach (var key in EnumeratePublicKeys (mailbox)) { - if (!key.IsEncryptionKey || key.IsRevoked ()) + if (!key.IsEncryptionKey || key.IsRevoked () || IsExpired (key)) continue; - long seconds = key.GetValidSeconds (); - if (seconds != 0) { - var expires = key.CreationTime.AddSeconds ((double) seconds); - if (expires <= DateTime.Now) - continue; - } - return key; } @@ -800,46 +424,38 @@ protected virtual PgpPublicKey GetPublicKey (MailboxAddress mailbox) /// /// A public key for one or more of the - internal protected virtual IListcould not be found. /// GetPublicKeys (IEnumerable mailboxes) + public override IList GetPublicKeys (IEnumerable mailboxes) { if (mailboxes == null) throw new ArgumentNullException (nameof (mailboxes)); - var recipients = new List (); + var keys = new List (); foreach (var mailbox in mailboxes) - recipients.Add (GetPublicKey (mailbox)); + keys.Add (GetPublicKey (mailbox)); - return recipients; + return keys; } - static bool PgpSecretKeyMatches (PgpSecretKey key, MailboxAddress mailbox) + /// + /// Get the secret key for a specified key identifier. + /// + ///+ /// Gets the secret key for a specified key identifier. + /// + /// The key identifier for the desired secret key. + ///The secret key. + ///+ /// The secret key specified by the + protected override PgpSecretKey GetSecretKey (long keyId) { - var secure = mailbox as SecureMailboxAddress; - - if (secure != null && !string.IsNullOrEmpty (secure.Fingerprint)) { - if (secure.Fingerprint.Length > 16) { - var fingerprint = HexEncode (key.PublicKey.GetFingerprint ()); - - return secure.Fingerprint.Equals (fingerprint, StringComparison.OrdinalIgnoreCase); - } - - var id = ((int) key.KeyId).ToString ("X2"); - - return secure.Fingerprint.EndsWith (id, StringComparison.OrdinalIgnoreCase); - } - - foreach (string userId in key.UserIds) { - MailboxAddress email; - - if (!MailboxAddress.TryParse (userId, out email)) - continue; - - if (mailbox.Address.Equals (email.Address, StringComparison.OrdinalIgnoreCase)) - return true; + foreach (var key in EnumerateSecretKeys ()) { + if (key.KeyId == keyId) + return key; } - return false; + throw new PrivateKeyNotFoundException (keyId, "The secret key could not be found."); } ///could not be found. + /// @@ -854,32 +470,22 @@ static bool PgpSecretKeyMatches (PgpSecretKey key, MailboxAddress mailbox) /// is null . ///- /// A private key for the specified - internal protected virtual PgpSecretKey GetSigningKey (MailboxAddress mailbox) + public override PgpSecretKey GetSigningKey (MailboxAddress mailbox) { if (mailbox == null) throw new ArgumentNullException (nameof (mailbox)); - foreach (PgpSecretKeyRing keyring in SecretKeyRingBundle.GetKeyRings ()) { + foreach (var keyring in EnumerateSecretKeyRings (mailbox)) { foreach (PgpSecretKey key in keyring.GetSecretKeys ()) { - if (!PgpSecretKeyMatches (keyring.GetSecretKey (), mailbox)) - continue; - if (!key.IsSigningKey) continue; var pubkey = key.PublicKey; - if (pubkey.IsRevoked ()) + if (pubkey.IsRevoked () || IsExpired (pubkey)) continue; - long seconds = pubkey.GetValidSeconds (); - if (seconds != 0) { - var expires = pubkey.CreationTime.AddSeconds ((double) seconds); - if (expires <= DateTime.Now) - continue; - } - return key; } } @@ -888,77 +494,59 @@ internal protected virtual PgpSecretKey GetSigningKey (MailboxAddress mailbox) } ///could not be found. + /// A secret key for the specified could not be found. /// - /// Gets the password for key. + /// Check whether or not a particular mailbox address can be used for signing. /// ///- /// Gets the password for key. + /// Checks whether or not as particular mailbocx address can be used for signing. /// - ///The password for key. - /// The key. - ///- /// The user chose to cancel the password request. + /// protected void SaveSecretKeyRingBundle () { -#if !PORTABLE var filename = Path.GetFileName (SecretKeyRingPath) + "~"; var dirname = Path.GetDirectoryName (SecretKeyRingPath); var tmp = Path.Combine (dirname, "." + filename); @@ -2456,7 +836,7 @@ protected void SaveSecretKeyRingBundle () } if (File.Exists (SecretKeyRingPath)) { -#if !NETSTANDARD +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 File.Replace (tmp, SecretKeyRingPath, bak); #else if (File.Exists (bak)) @@ -2467,10 +847,6 @@ protected void SaveSecretKeyRingBundle () } else { File.Move (tmp, SecretKeyRingPath); } -#else - SecretKeyRingBundle.Encode (SecretKeyRing); - SecretKeyRing.Seek (0, SeekOrigin.Begin); -#endif } ///+ /// The signer. + /// true if the mailbox address can be used for signing; otherwise,false .+ /// - protected abstract string GetPasswordForKey (PgpSecretKey key); + public override bool CanSign (MailboxAddress signer) + { + if (signer == null) + throw new ArgumentNullException (nameof (signer)); + + foreach (var key in EnumerateSecretKeys (signer)) { + if (!key.IsSigningKey) + continue; + + var pubkey = key.PublicKey; + if (pubkey.IsRevoked () || IsExpired (pubkey)) + continue; + + return true; + } + + return false; + } ///is null . ///- /// Gets the private key from the specified secret key. + /// Check whether or not the cryptography context can encrypt to a particular recipient. /// ///- /// Gets the private key from the specified secret key. + /// Checks whether or not the cryptography context can be used to encrypt to a particular recipient. /// - ///The private key. - /// The secret key. + ///+ /// The recipient's mailbox address. /// true if the cryptography context can be used to encrypt to the designated recipient; otherwise,false .- /// - ///is null . - ///- /// The user chose to cancel the password prompt. - /// - ///- /// 3 bad attempts were made to unlock the secret key. + /// - protected PgpPrivateKey GetPrivateKey (PgpSecretKey key) + public override bool CanEncrypt (MailboxAddress mailbox) { - int attempts = 0; - string password; - - if (key == null) - throw new ArgumentNullException (nameof (key)); - - do { - if ((password = GetPasswordForKey (key)) == null) - throw new OperationCanceledException (); - - try { - var privateKey = key.ExtractPrivateKey (password.ToCharArray ()); - - // Note: the private key will be null if the private key is empty. - if (privateKey == null) - break; - - return privateKey; - } catch (Exception ex) { -#if DEBUG - Debug.WriteLine (string.Format ("Failed to extract secret key: {0}", ex)); -#endif - } - - attempts++; - } while (attempts < 3); + if (mailbox == null) + throw new ArgumentNullException (nameof (mailbox)); - throw new UnauthorizedAccessException (); - } + foreach (var key in EnumeratePublicKeys (mailbox)) { + if (!key.IsEncryptionKey || key.IsRevoked () || IsExpired (key)) + continue; - PgpSecretKey GetSecretKey (long keyId) - { - foreach (PgpSecretKeyRing keyring in SecretKeyRingBundle.GetKeyRings ()) { - foreach (PgpSecretKey key in keyring.GetSecretKeys ()) { - if (key.KeyId == keyId) - return key; - } + return true; } - throw new PrivateKeyNotFoundException (keyId, "The private key could not be found."); + return false; } #if false @@ -1025,7 +613,7 @@ void AddEncryptionKeyPair (PgpKeyRingGenerator keyRingGenerator, KeyGenerationPa keyRingGenerator.AddSubKey (keyPair, subpacketGenerator.Generate (), null); } - PgpKeyRingGenerator CreateKeyRingGenerator (MailboxAddress mailbox, EncryptionAlgorithm algorithm, long expirationTime, string password, DateTime now) + PgpKeyRingGenerator CreateKeyRingGenerator (MailboxAddress mailbox, EncryptionAlgorithm algorithm, long expirationTime, string password, DateTime now, SecureRandom random) { var enabledEncryptionAlgorithms = EnabledEncryptionAlgorithms; var enabledDigestAlgorithms = EnabledDigestAlgorithms; @@ -1037,7 +625,7 @@ PgpKeyRingGenerator CreateKeyRingGenerator (MailboxAddress mailbox, EncryptionAl for (int i = 0; i < enabledDigestAlgorithms.Length; i++) digestAlgorithms[i] = (int) enabledDigestAlgorithms[i]; - var parameters = new RsaKeyGenerationParameters (BigInteger.ValueOf (0x10001), new SecureRandom (), 2048, 12); + var parameters = new RsaKeyGenerationParameters (BigInteger.ValueOf (0x10001), random, 2048, 12); var signingAlgorithm = PublicKeyAlgorithmTag.RsaSign; var keyPairGenerator = GeneratorUtilities.GetKeyPairGenerator ("RSA"); @@ -1067,7 +655,7 @@ PgpKeyRingGenerator CreateKeyRingGenerator (MailboxAddress mailbox, EncryptionAl true, subpacketGenerator.Generate (), null, - new SecureRandom ()); + random); // Add the (optional) encryption subkey. AddEncryptionKeyPair (keyRingGenerator, parameters, PublicKeyAlgorithmTag.RsaGeneral, now, expirationTime, encryptionAlgorithms, digestAlgorithms); @@ -1085,6 +673,7 @@ PgpKeyRingGenerator CreateKeyRingGenerator (MailboxAddress mailbox, EncryptionAl /// The password to be set on the secret key. /// The expiration date for the generated key pair. /// The symmetric key algorithm to use. + /// The source of randomness to use when generating the key pair. ///is null . ////// protected void SavePublicKeyRingBundle () { -#if !PORTABLE var filename = Path.GetFileName (PublicKeyRingPath) + "~"; var dirname = Path.GetDirectoryName (PublicKeyRingPath); var tmp = Path.Combine (dirname, "." + filename); @@ -2413,7 +798,7 @@ protected void SavePublicKeyRingBundle () } if (File.Exists (PublicKeyRingPath)) { -#if !NETSTANDARD +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 File.Replace (tmp, PublicKeyRingPath, bak); #else if (File.Exists (bak)) @@ -2424,10 +809,6 @@ protected void SavePublicKeyRingBundle () } else { File.Move (tmp, PublicKeyRingPath); } -#else - PublicKeyRingBundle.Encode (PublicKeyRing); - PublicKeyRing.Seek (0, SeekOrigin.Begin); -#endif } ////// is null .-or- @@ -1093,7 +682,7 @@ PgpKeyRingGenerator CreateKeyRingGenerator (MailboxAddress mailbox, EncryptionAl ////// - public void GenerateKeyPair (MailboxAddress mailbox, string password, DateTime? expirationDate = null, EncryptionAlgorithm algorithm = EncryptionAlgorithm.Aes256) + public void GenerateKeyPair (MailboxAddress mailbox, string password, DateTime? expirationDate = null, EncryptionAlgorithm algorithm = EncryptionAlgorithm.Aes256, SecureRandom random = null) { var now = DateTime.UtcNow; long expirationTime = 0; @@ -1108,13 +697,21 @@ public void GenerateKeyPair (MailboxAddress mailbox, string password, DateTime? var utc = expirationDate.Value.ToUniversalTime (); if (utc <= now) - throw new ArgumentException ("expirationDate needs to be greater than DateTime.Now"); + throw new ArgumentException ("expirationDate needs to be greater than DateTime.Now", nameof (expirationDate)); if ((expirationTime = Convert.ToInt64 (utc.Subtract (now).TotalSeconds)) <= 0) - throw new ArgumentException ("expirationDate needs to be greater than DateTime.Now"); + throw new ArgumentException ("expirationDate needs to be greater than DateTime.Now", nameof (expirationDate)); + } + + if (random == null) { +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 + random = new SecureRandom (new CryptoApiRandomGenerator ()); +#else + random = new SecureRandom (); +#endif } - var generator = CreateKeyRingGenerator (mailbox, algorithm, expirationTime, password, now); + var generator = CreateKeyRingGenerator (mailbox, algorithm, expirationTime, password, now, random); Import (generator.GenerateSecretKeyRing ()); Import (generator.GeneratePublicKeyRing ()); @@ -1173,1222 +770,11 @@ public void SignKey (PgpSecretKey secretKey, PgpPublicKey publicKey, DigestAlgor if (keyring == null) keyring = new PgpPublicKeyRing (signedKey.GetEncoded ()); - PublicKeyRingBundle = PgpPublicKeyRingBundle.AddPublicKeyRing (PublicKeyRingBundle, keyring); - SavePublicKeyRingBundle (); + Import (keyring); } ///is not a date in the future. /// - /// Gets the equivalent - ///for the - /// specified . - /// - /// Maps a - ///to the equivalent . - /// The hash algorithm. - /// The digest algorithm. - ///- /// - ///is out of range. - /// - /// - public static HashAlgorithmTag GetHashAlgorithm (DigestAlgorithm digestAlgo) - { - switch (digestAlgo) { - case DigestAlgorithm.MD5: return HashAlgorithmTag.MD5; - case DigestAlgorithm.Sha1: return HashAlgorithmTag.Sha1; - case DigestAlgorithm.RipeMD160: return HashAlgorithmTag.RipeMD160; - case DigestAlgorithm.DoubleSha: throw new NotSupportedException ("The Double SHA digest algorithm is not supported."); - case DigestAlgorithm.MD2: return HashAlgorithmTag.MD2; - case DigestAlgorithm.Tiger192: throw new NotSupportedException ("The Tiger-192 digest algorithm is not supported."); - case DigestAlgorithm.Haval5160: throw new NotSupportedException ("The HAVAL 5 160 digest algorithm is not supported."); - case DigestAlgorithm.Sha256: return HashAlgorithmTag.Sha256; - case DigestAlgorithm.Sha384: return HashAlgorithmTag.Sha384; - case DigestAlgorithm.Sha512: return HashAlgorithmTag.Sha512; - case DigestAlgorithm.Sha224: return HashAlgorithmTag.Sha224; - case DigestAlgorithm.MD4: throw new NotSupportedException ("The MD4 digest algorithm is not supported."); - default: throw new ArgumentOutOfRangeException (nameof (digestAlgo)); - } - } - - ///is not a supported digest algorithm. - /// - /// Check whether or not a particular mailbox address can be used for signing. - /// - ///- /// Checks whether or not as particular mailbocx address can be used for signing. - /// - ///- /// The signer. - /// true if the mailbox address can be used for signing; otherwise,false .- /// - public override bool CanSign (MailboxAddress signer) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - foreach (PgpSecretKeyRing keyring in SecretKeyRingBundle.GetKeyRings ()) { - foreach (PgpSecretKey key in keyring.GetSecretKeys ()) { - if (!PgpSecretKeyMatches (keyring.GetSecretKey (), signer)) - continue; - - if (!key.IsSigningKey) - continue; - - var pubkey = key.PublicKey; - if (pubkey.IsRevoked ()) - continue; - - long seconds = pubkey.GetValidSeconds (); - if (seconds != 0) { - var expires = pubkey.CreationTime.AddSeconds ((double) seconds); - if (expires <= DateTime.Now) - continue; - } - - return true; - } - } - - return false; - } - - ///is null . - ///- /// Check whether or not the cryptography context can encrypt to a particular recipient. - /// - ///- /// Checks whether or not the cryptography context can be used to encrypt to a particular recipient. - /// - ///- /// The recipient's mailbox address. - /// true if the cryptography context can be used to encrypt to the designated recipient; otherwise,false .- /// - public override bool CanEncrypt (MailboxAddress mailbox) - { - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - foreach (var key in EnumeratePublicKeys (mailbox)) { - if (!key.IsEncryptionKey || key.IsRevoked ()) - continue; - - long seconds = key.GetValidSeconds (); - if (seconds != 0) { - var expires = key.CreationTime.AddSeconds ((double) seconds); - if (expires <= DateTime.Now) - continue; - } - - return true; - } - - return false; - } - - ///is null . - ///- /// Cryptographically signs the content. - /// - ///- /// Cryptographically signs the content using the specified signer and digest algorithm. - /// - ///A new - /// The signer. - /// The digest algorithm to use for signing. - /// The content. - ///instance - /// containing the detached signature data. - /// - ///- /// is null .-or- - ///- /// is null .- /// - ///is out of range. - /// - /// The specified - ///is not supported by this context. - /// - /// A signing key could not be found for - ///. - /// - /// The user chose to cancel the password prompt. - /// - ///- /// 3 bad attempts were made to unlock the secret key. - /// - public override MimePart Sign (MailboxAddress signer, DigestAlgorithm digestAlgo, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var key = GetSigningKey (signer); - - return Sign (key, digestAlgo, content); - } - - ///- /// Cryptographically signs the content. - /// - ///- /// Cryptographically signs the content using the specified signer and digest algorithm. - /// - ///A new - /// The signer. - /// The digest algorithm to use for signing. - /// The content. - ///instance - /// containing the detached signature data. - /// - ///- /// is null .-or- - ///- /// is null .- /// - ///cannot be used for signing. - /// - /// The - ///was out of range. - /// - /// The - ///is not supported. - /// - /// The user chose to cancel the password prompt. - /// - ///- /// 3 bad attempts were made to unlock the secret key. - /// - public ApplicationPgpSignature Sign (PgpSecretKey signer, DigestAlgorithm digestAlgo, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (!signer.IsSigningKey) - throw new ArgumentException ("The specified secret key cannot be used for signing.", nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var hashAlgorithm = GetHashAlgorithm (digestAlgo); - var memory = new MemoryBlockStream (); - - using (var armored = new ArmoredOutputStream (memory)) { - armored.SetHeader ("Version", null); - - var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib); - using (var compressed = compresser.Open (armored)) { - var signatureGenerator = new PgpSignatureGenerator (signer.PublicKey.Algorithm, hashAlgorithm); - var buf = new byte[4096]; - int nread; - - signatureGenerator.InitSign (PgpSignature.CanonicalTextDocument, GetPrivateKey (signer)); - - while ((nread = content.Read (buf, 0, buf.Length)) > 0) - signatureGenerator.Update (buf, 0, nread); - - var signature = signatureGenerator.Generate (); - - signature.Encode (compressed); - compressed.Flush (); - } - - armored.Flush (); - } - - memory.Position = 0; - - return new ApplicationPgpSignature (memory); - } - - ///- /// Gets the equivalent - ///for the specified - /// . - /// - /// Gets the equivalent - ///for the specified - /// . - /// The digest algorithm. - /// The hash algorithm. - ///- /// - ///is out of range. - /// - /// - public static DigestAlgorithm GetDigestAlgorithm (HashAlgorithmTag hashAlgorithm) - { - switch (hashAlgorithm) { - case HashAlgorithmTag.MD5: return DigestAlgorithm.MD5; - case HashAlgorithmTag.Sha1: return DigestAlgorithm.Sha1; - case HashAlgorithmTag.RipeMD160: return DigestAlgorithm.RipeMD160; - case HashAlgorithmTag.DoubleSha: return DigestAlgorithm.DoubleSha; - case HashAlgorithmTag.MD2: return DigestAlgorithm.MD2; - case HashAlgorithmTag.Tiger192: return DigestAlgorithm.Tiger192; - case HashAlgorithmTag.Haval5pass160: return DigestAlgorithm.Haval5160; - case HashAlgorithmTag.Sha256: return DigestAlgorithm.Sha256; - case HashAlgorithmTag.Sha384: return DigestAlgorithm.Sha384; - case HashAlgorithmTag.Sha512: return DigestAlgorithm.Sha512; - case HashAlgorithmTag.Sha224: return DigestAlgorithm.Sha224; - default: throw new ArgumentOutOfRangeException (nameof (hashAlgorithm)); - } - } - - ///does not have an equivalent value. - /// - /// Gets the equivalent - ///for the specified - /// . - /// - /// Gets the equivalent - ///for the specified - /// . - /// The public-key algorithm. - /// The public-key algorithm. - ///- /// - ///is out of range. - /// - /// - public static PublicKeyAlgorithm GetPublicKeyAlgorithm (PublicKeyAlgorithmTag algorithm) - { - switch (algorithm) { - case PublicKeyAlgorithmTag.RsaGeneral: return PublicKeyAlgorithm.RsaGeneral; - case PublicKeyAlgorithmTag.RsaEncrypt: return PublicKeyAlgorithm.RsaEncrypt; - case PublicKeyAlgorithmTag.RsaSign: return PublicKeyAlgorithm.RsaSign; - case PublicKeyAlgorithmTag.ElGamalEncrypt: return PublicKeyAlgorithm.ElGamalEncrypt; - case PublicKeyAlgorithmTag.Dsa: return PublicKeyAlgorithm.Dsa; - case PublicKeyAlgorithmTag.ECDH: return PublicKeyAlgorithm.EllipticCurve; - case PublicKeyAlgorithmTag.ECDsa: return PublicKeyAlgorithm.EllipticCurveDsa; - case PublicKeyAlgorithmTag.ElGamalGeneral: return PublicKeyAlgorithm.ElGamalGeneral; - case PublicKeyAlgorithmTag.DiffieHellman: return PublicKeyAlgorithm.DiffieHellman; - default: throw new ArgumentOutOfRangeException (nameof (algorithm)); - } - } - - async Taskdoes not have an equivalent value. - /// GetDigitalSignaturesAsync (PgpSignatureList signatureList, Stream content, bool doAsync, CancellationToken cancellationToken) - { - var signatures = new List (); - var buf = new byte[4096]; - int nread; - - for (int i = 0; i < signatureList.Count; i++) { - long keyId = signatureList[i].KeyId; - KeyRetrievalResults results; - - if (doAsync) - results = await GetPublicKeyRingAsync (keyId, doAsync, cancellationToken).ConfigureAwait (false); - else - results = GetPublicKeyRingAsync (keyId, doAsync, cancellationToken).GetAwaiter ().GetResult (); - - var signature = new OpenPgpDigitalSignature (results.KeyRing, results.Key, signatureList[i]) { - PublicKeyAlgorithm = GetPublicKeyAlgorithm (signatureList[i].KeyAlgorithm), - DigestAlgorithm = GetDigestAlgorithm (signatureList[i].HashAlgorithm), - CreationDate = signatureList[i].CreationTime, - }; - - if (results.Key != null) - signatureList[i].InitVerify (results.Key); - - signatures.Add (signature); - } - - while ((nread = content.Read (buf, 0, buf.Length)) > 0) { - for (int i = 0; i < signatures.Count; i++) { - if (signatures[i].SignerCertificate != null) { - var pgp = (OpenPgpDigitalSignature) signatures[i]; - pgp.Signature.Update (buf, 0, nread); - } - } - } - - return new DigitalSignatureCollection (signatures); - } - - Task VerifyAsync (Stream content, Stream signatureData, bool doAsync, CancellationToken cancellationToken) - { - if (content == null) - throw new ArgumentNullException (nameof (content)); - - if (signatureData == null) - throw new ArgumentNullException (nameof (signatureData)); - - using (var armored = new ArmoredInputStream (signatureData)) { - var factory = new PgpObjectFactory (armored); - var data = factory.NextPgpObject (); - PgpSignatureList signatureList; - - var compressed = data as PgpCompressedData; - if (compressed != null) { - factory = new PgpObjectFactory (compressed.GetDataStream ()); - data = factory.NextPgpObject (); - } - - if (data == null) - throw new FormatException ("Invalid PGP format."); - - signatureList = (PgpSignatureList) data; - - return GetDigitalSignaturesAsync (signatureList, content, doAsync, cancellationToken); - } - } - - /// - /// Verify the specified content using the detached signatureData. - /// - ///- /// - ///Verifies the specified content using the detached signatureData. - ///If any of the signatures were made with an unrecognized key and - ///is enabled, - /// an attempt will be made to retrieve said key(s). The can be used to cancel - /// key retrieval. A list of digital signatures. - /// The content. - /// The signature data. - /// The cancellation token. - ///- /// - ///- /// is null .-or- - ///- /// is null .- /// - public override DigitalSignatureCollection Verify (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (content, signatureData, false, cancellationToken).GetAwaiter ().GetResult (); - } - - ///does not contain valid PGP signature data. - /// - /// Asynchronously verify the specified content using the detached signatureData. - /// - ///- /// - ///Verifies the specified content using the detached signatureData. - ///If any of the signatures were made with an unrecognized key and - ///is enabled, - /// an attempt will be made to retrieve said key(s). The can be used to cancel - /// key retrieval. A list of digital signatures. - /// The content. - /// The signature data. - /// The cancellation token. - ///- /// - ///- /// is null .-or- - ///- /// is null .- /// - public override Taskdoes not contain valid PGP signature data. - /// VerifyAsync (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (content, signatureData, true, cancellationToken); - } - - static Stream Compress (Stream content, byte[] buf) - { - var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib); - var memory = new MemoryBlockStream (); - - using (var compressed = compresser.Open (memory)) { - var literalGenerator = new PgpLiteralDataGenerator (); - - using (var literal = literalGenerator.Open (compressed, 't', "mime.txt", content.Length, DateTime.Now)) { - int nread; - - while ((nread = content.Read (buf, 0, buf.Length)) > 0) - literal.Write (buf, 0, nread); - - literal.Flush (); - } - - compressed.Flush (); - } - - memory.Position = 0; - - return memory; - } - - static Stream Encrypt (PgpEncryptedDataGenerator encrypter, Stream content) - { - var memory = new MemoryBlockStream (); - - using (var armored = new ArmoredOutputStream (memory)) { - var buf = new byte[4096]; - - armored.SetHeader ("Version", null); - - using (var compressed = Compress (content, buf)) { - using (var encrypted = encrypter.Open (armored, compressed.Length)) { - int nread; - - while ((nread = compressed.Read (buf, 0, buf.Length)) > 0) - encrypted.Write (buf, 0, nread); - - encrypted.Flush (); - } - } - - armored.Flush (); - } - - memory.Position = 0; - - return memory; - } - - static SymmetricKeyAlgorithmTag GetSymmetricKeyAlgorithm (EncryptionAlgorithm algorithm) - { - switch (algorithm) { - case EncryptionAlgorithm.Aes128: return SymmetricKeyAlgorithmTag.Aes128; - case EncryptionAlgorithm.Aes192: return SymmetricKeyAlgorithmTag.Aes192; - case EncryptionAlgorithm.Aes256: return SymmetricKeyAlgorithmTag.Aes256; - case EncryptionAlgorithm.Camellia128: return SymmetricKeyAlgorithmTag.Camellia128; - case EncryptionAlgorithm.Camellia192: return SymmetricKeyAlgorithmTag.Camellia192; - case EncryptionAlgorithm.Camellia256: return SymmetricKeyAlgorithmTag.Camellia256; - case EncryptionAlgorithm.Cast5: return SymmetricKeyAlgorithmTag.Cast5; - case EncryptionAlgorithm.Des: return SymmetricKeyAlgorithmTag.Des; - case EncryptionAlgorithm.TripleDes: return SymmetricKeyAlgorithmTag.TripleDes; - case EncryptionAlgorithm.Idea: return SymmetricKeyAlgorithmTag.Idea; - case EncryptionAlgorithm.Blowfish: return SymmetricKeyAlgorithmTag.Blowfish; - case EncryptionAlgorithm.Twofish: return SymmetricKeyAlgorithmTag.Twofish; - default: throw new NotSupportedException (string.Format ("{0} is not supported.", algorithm)); - } - } - - /// - /// Encrypt the specified content for the specified recipients. - /// - ///- /// Encrypts the specified content for the specified recipients. - /// - ///A new - /// The recipients. - /// The content. - ///instance - /// containing the encrypted data. - /// - ///- /// is null .-or- - ///- /// is null .- /// - ///One or more of the recipient keys cannot be used for encrypting. - ///-or- - ///No recipients were specified. - ///- /// A public key could not be found for one or more of the - public override MimePart Encrypt (IEnumerable. - /// recipients, Stream content) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - // TODO: document the exceptions that can be thrown by BouncyCastle - return Encrypt (GetPublicKeys (recipients), content); - } - - /// - /// Encrypt the specified content for the specified recipients. - /// - ///- /// Encrypts the specified content for the specified recipients. - /// - ///A new - /// The encryption algorithm. - /// The recipients. - /// The content. - ///instance - /// containing the encrypted data. - /// - ///- /// is null .-or- - ///- /// is null .- /// - ///One or more of the recipient keys cannot be used for encrypting. - ///-or- - ///No recipients were specified. - ///- /// A public key could not be found for one or more of the - ///. - /// - /// The specified encryption algorithm is not supported. - /// - public MimePart Encrypt (EncryptionAlgorithm algorithm, IEnumerablerecipients, Stream content) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - // TODO: document the exceptions that can be thrown by BouncyCastle - return Encrypt (algorithm, GetPublicKeys (recipients), content); - } - - /// - /// Encrypt the specified content for the specified recipients. - /// - ///- /// Encrypts the specified content for the specified recipients. - /// - ///A new - /// The encryption algorithm. - /// The recipients. - /// The content. - ///instance - /// containing the encrypted data. - /// - ///- /// is null .-or- - ///- /// is null .- /// - ///One or more of the recipient keys cannot be used for encrypting. - ///-or- - ///No recipients were specified. - ///- /// The specified encryption algorithm is not supported. - /// - public MimePart Encrypt (EncryptionAlgorithm algorithm, IEnumerablerecipients, Stream content) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var encrypter = new PgpEncryptedDataGenerator (GetSymmetricKeyAlgorithm (algorithm), true); - var unique = new HashSet (); - int count = 0; - - foreach (var recipient in recipients) { - if (!recipient.IsEncryptionKey) - throw new ArgumentException ("One or more of the recipient keys cannot be used for encrypting.", nameof (recipients)); - - if (unique.Add (recipient.KeyId)) { - encrypter.AddMethod (recipient); - count++; - } - } - - if (count == 0) - throw new ArgumentException ("No recipients specified.", nameof (recipients)); - - var encrypted = Encrypt (encrypter, content); - - return new MimePart ("application", "octet-stream") { - ContentDisposition = new ContentDisposition (ContentDisposition.Attachment), - Content = new MimeContent (encrypted), - }; - } - - /// - /// Encrypt the specified content for the specified recipients. - /// - ///- /// Encrypts the specified content for the specified recipients. - /// - ///A new - /// The recipients. - /// The content. - ///instance - /// containing the encrypted data. - /// - ///- /// is null .-or- - ///- /// is null .- /// - public MimePart Encrypt (IEnumerableOne or more of the recipient keys cannot be used for encrypting. - ///-or- - ///No recipients were specified. - ///recipients, Stream content) - { - return Encrypt (defaultAlgorithm, recipients, content); - } - - /// - /// Cryptographically sign and encrypt the specified content for the specified recipients. - /// - ///- /// Cryptographically signs and encrypts the specified content for the specified recipients. - /// - ///A new - /// The signer. - /// The digest algorithm to use for signing. - /// The recipients. - /// The content. - ///instance - /// containing the encrypted data. - /// - ///- /// is null .-or- - ///- /// is null .-or- - ///- /// is null .- /// - ///is out of range. - /// - /// - ///One or more of the recipient keys cannot be used for encrypting. - ///-or- - ///No recipients were specified. - ///- /// The specified - ///is not supported by this context. - /// - /// The private key could not be found for - ///. - /// - /// A public key could not be found for one or more of the - ///. - /// - /// The user chose to cancel the password prompt. - /// - ///- /// 3 bad attempts were made to unlock the secret key. - /// - public MimePart SignAndEncrypt (MailboxAddress signer, DigestAlgorithm digestAlgo, IEnumerablerecipients, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var key = GetSigningKey (signer); - - return SignAndEncrypt (key, digestAlgo, GetPublicKeys (recipients), content); - } - - /// - /// Cryptographically sign and encrypt the specified content for the specified recipients. - /// - ///- /// Cryptographically signs and encrypts the specified content for the specified recipients. - /// - ///A new - /// The signer. - /// The digest algorithm to use for signing. - /// The encryption algorithm. - /// The recipients. - /// The content. - ///instance - /// containing the encrypted data. - /// - ///- /// is null .-or- - ///- /// is null .-or- - ///- /// is null .- /// - ///- /// cannot be used for signing. -or- - ///One or more of the recipient keys cannot be used for encrypting. - ///-or- - ///No recipients were specified. - ///- /// The specified encryption algorithm is not supported. - /// - ///- /// The user chose to cancel the password prompt. - /// - ///- /// 3 bad attempts were made to unlock the secret key. - /// - public MimePart SignAndEncrypt (MailboxAddress signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerablerecipients, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var key = GetSigningKey (signer); - - return SignAndEncrypt (key, digestAlgo, cipherAlgo, GetPublicKeys (recipients), content); - } - - /// - /// Cryptographically sign and encrypt the specified content for the specified recipients. - /// - ///- /// Cryptographically signs and encrypts the specified content for the specified recipients. - /// - ///A new - /// The signer. - /// The digest algorithm to use for signing. - /// The encryption algorithm. - /// The recipients. - /// The content. - ///instance - /// containing the encrypted data. - /// - ///- /// is null .-or- - ///- /// is null .-or- - ///- /// is null .- /// - ///- /// cannot be used for signing. -or- - ///One or more of the recipient keys cannot be used for encrypting. - ///-or- - ///No recipients were specified. - ///- /// The specified encryption algorithm is not supported. - /// - ///- /// The user chose to cancel the password prompt. - /// - ///- /// 3 bad attempts were made to unlock the secret key. - /// - public MimePart SignAndEncrypt (PgpSecretKey signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerablerecipients, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (!signer.IsSigningKey) - throw new ArgumentException ("The specified secret key cannot be used for signing.", nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var encrypter = new PgpEncryptedDataGenerator (GetSymmetricKeyAlgorithm (cipherAlgo), true); - var hashAlgorithm = GetHashAlgorithm (digestAlgo); - var unique = new HashSet (); - var buf = new byte[4096]; - int nread, count = 0; - - foreach (var recipient in recipients) { - if (!recipient.IsEncryptionKey) - throw new ArgumentException ("One or more of the recipient keys cannot be used for encrypting.", nameof (recipients)); - - if (unique.Add (recipient.KeyId)) { - encrypter.AddMethod (recipient); - count++; - } - } - - if (count == 0) - throw new ArgumentException ("No recipients specified.", nameof (recipients)); - - var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib); - - using (var compressed = new MemoryBlockStream ()) { - using (var signed = compresser.Open (compressed)) { - var signatureGenerator = new PgpSignatureGenerator (signer.PublicKey.Algorithm, hashAlgorithm); - signatureGenerator.InitSign (PgpSignature.CanonicalTextDocument, GetPrivateKey (signer)); - var subpacket = new PgpSignatureSubpacketGenerator (); - - foreach (string userId in signer.PublicKey.GetUserIds ()) { - subpacket.SetSignerUserId (false, userId); - break; - } - - signatureGenerator.SetHashedSubpackets (subpacket.Generate ()); - - var onepass = signatureGenerator.GenerateOnePassVersion (false); - onepass.Encode (signed); - - var literalGenerator = new PgpLiteralDataGenerator (); - using (var literal = literalGenerator.Open (signed, 't', "mime.txt", content.Length, DateTime.Now)) { - while ((nread = content.Read (buf, 0, buf.Length)) > 0) { - signatureGenerator.Update (buf, 0, nread); - literal.Write (buf, 0, nread); - } - - literal.Flush (); - } - - var signature = signatureGenerator.Generate (); - signature.Encode (signed); - - signed.Flush (); - } - - compressed.Position = 0; - - var memory = new MemoryBlockStream (); - - using (var armored = new ArmoredOutputStream (memory)) { - armored.SetHeader ("Version", null); - - using (var encrypted = encrypter.Open (armored, compressed.Length)) { - while ((nread = compressed.Read (buf, 0, buf.Length)) > 0) - encrypted.Write (buf, 0, nread); - - encrypted.Flush (); - } - - armored.Flush (); - } - - memory.Position = 0; - - return new MimePart ("application", "octet-stream") { - ContentDisposition = new ContentDisposition (ContentDisposition.Attachment), - Content = new MimeContent (memory) - }; - } - } - - /// - /// Cryptographically sign and encrypt the specified content for the specified recipients. - /// - ///- /// Cryptographically signs and encrypts the specified content for the specified recipients. - /// - ///A new - /// The signer. - /// The digest algorithm to use for signing. - /// The recipients. - /// The content. - ///instance - /// containing the encrypted data. - /// - ///- /// is null .-or- - ///- /// is null .-or- - ///- /// is null .- /// - ///- /// cannot be used for signing. -or- - ///One or more of the recipient keys cannot be used for encrypting. - ///-or- - ///No recipients were specified. - ///- /// The user chose to cancel the password prompt. - /// - ///- /// 3 bad attempts were made to unlock the secret key. - /// - public MimePart SignAndEncrypt (PgpSecretKey signer, DigestAlgorithm digestAlgo, IEnumerablerecipients, Stream content) - { - return SignAndEncrypt (signer, digestAlgo, defaultAlgorithm, recipients, content); - } - - async Task DecryptToAsync (Stream encryptedData, Stream decryptedData, bool doAsync, CancellationToken cancellationToken) - { - if (encryptedData == null) - throw new ArgumentNullException (nameof (encryptedData)); - - if (decryptedData == null) - throw new ArgumentNullException (nameof (decryptedData)); - - using (var armored = new ArmoredInputStream (encryptedData)) { - var factory = new PgpObjectFactory (armored); - var obj = factory.NextPgpObject (); - var list = obj as PgpEncryptedDataList; - - if (list == null) { - // probably a PgpMarker... - obj = factory.NextPgpObject (); - - list = obj as PgpEncryptedDataList; - - if (list == null) - throw new PgpException ("Unexpected OpenPGP packet."); - } - - PgpPublicKeyEncryptedData encrypted = null; - PrivateKeyNotFoundException pkex = null; - bool hasEncryptedPackets = false; - PgpSecretKey secret = null; - - foreach (PgpEncryptedData data in list.GetEncryptedDataObjects ()) { - if ((encrypted = data as PgpPublicKeyEncryptedData) == null) - continue; - - hasEncryptedPackets = true; - - try { - secret = GetSecretKey (encrypted.KeyId); - break; - } catch (PrivateKeyNotFoundException ex) { - pkex = ex; - } - } - - if (!hasEncryptedPackets) - throw new PgpException ("No encrypted packets found."); - - if (secret == null) - throw pkex; - - factory = new PgpObjectFactory (encrypted.GetDataStream (GetPrivateKey (secret))); - List onepassList = null; - DigitalSignatureCollection signatures; - PgpSignatureList signatureList = null; - PgpCompressedData compressed = null; - var position = decryptedData.Position; - long nwritten = 0; - - obj = factory.NextPgpObject (); - while (obj != null) { - if (obj is PgpCompressedData) { - if (compressed != null) - throw new PgpException ("Recursive compression packets are not supported."); - - compressed = (PgpCompressedData) obj; - factory = new PgpObjectFactory (compressed.GetDataStream ()); - } else if (obj is PgpOnePassSignatureList) { - if (nwritten == 0) { - var onepasses = (PgpOnePassSignatureList) obj; - - onepassList = new List (); - - for (int i = 0; i < onepasses.Count; i++) { - var onepass = onepasses[i]; - - var results = await GetPublicKeyRingAsync (onepass.KeyId, doAsync, cancellationToken).ConfigureAwait (false); - - if (results.KeyRing == null) { - // too messy, pretend we never found a one-pass signature list - onepassList = null; - break; - } - - onepass.InitVerify (results.Key); - - var signature = new OpenPgpDigitalSignature (results.KeyRing, results.Key, onepass) { - PublicKeyAlgorithm = GetPublicKeyAlgorithm (onepass.KeyAlgorithm), - DigestAlgorithm = GetDigestAlgorithm (onepass.HashAlgorithm), - }; - - onepassList.Add (signature); - } - } - } else if (obj is PgpSignatureList) { - signatureList = (PgpSignatureList) obj; - } else if (obj is PgpLiteralData) { - var literal = (PgpLiteralData) obj; - - using (var stream = literal.GetDataStream ()) { - var buffer = new byte[4096]; - int nread; - - while ((nread = stream.Read (buffer, 0, buffer.Length)) > 0) { - if (onepassList != null) { - // update our one-pass signatures... - for (int index = 0; index < nread; index++) { - byte c = buffer[index]; - - for (int i = 0; i < onepassList.Count; i++) { - var pgp = (OpenPgpDigitalSignature) onepassList[i]; - pgp.OnePassSignature.Update (c); - } - } - } - - if (doAsync) - await decryptedData.WriteAsync (buffer, 0, nread, cancellationToken).ConfigureAwait (false); - else - decryptedData.Write (buffer, 0, nread); - - nwritten += nread; - } - } - } - - obj = factory.NextPgpObject (); - } - - if (signatureList != null) { - if (onepassList != null && signatureList.Count == onepassList.Count) { - for (int i = 0; i < onepassList.Count; i++) { - var pgp = (OpenPgpDigitalSignature) onepassList[i]; - pgp.CreationDate = signatureList[i].CreationTime; - pgp.Signature = signatureList[i]; - } - - signatures = new DigitalSignatureCollection (onepassList); - } else { - decryptedData.Position = position; - signatures = await GetDigitalSignaturesAsync (signatureList, decryptedData, doAsync, cancellationToken).ConfigureAwait (false); - decryptedData.Position = decryptedData.Length; - } - } else { - signatures = null; - } - - return signatures; - } - } - - /// - /// Decrypt an encrypted stream and extract the digital signers if the content was also signed. - /// - ///- /// - ///Decrypts an encrypted stream and extracts the digital signers if the content was also signed. - ///If any of the signatures were made with an unrecognized key and - ///is enabled, - /// an attempt will be made to retrieve said key(s). The can be used to cancel - /// key retrieval. The list of digital signatures if the data was both signed and encrypted; otherwise, - /// The encrypted data. - /// The stream to write the decrypted data to. - /// The cancellation token. - ///null .- /// - ///- /// is null .-or- - ///- /// is null .- /// The private key could not be found to decrypt the stream. - /// - ///- /// - ///The user chose to cancel the password prompt. - ///-or- - ///The operation was cancelled via the cancellation token. - ///- /// 3 bad attempts were made to unlock the secret key. - /// - ///- /// An OpenPGP error occurred. - /// - public DigitalSignatureCollection DecryptTo (Stream encryptedData, Stream decryptedData, CancellationToken cancellationToken = default (CancellationToken)) - { - return DecryptToAsync (encryptedData, decryptedData, false, cancellationToken).GetAwaiter ().GetResult (); - } - - ///- /// Asynchronously decrypt an encrypted stream and extract the digital signers if the content was also signed. - /// - ///- /// - ///Decrypts an encrypted stream and extracts the digital signers if the content was also signed. - ///If any of the signatures were made with an unrecognized key and - ///is enabled, - /// an attempt will be made to retrieve said key(s). The can be used to cancel - /// key retrieval. The list of digital signatures if the data was both signed and encrypted; otherwise, - /// The encrypted data. - /// The stream to write the decrypted data to. - /// The cancellation token. - ///null .- /// - ///- /// is null .-or- - ///- /// is null .- /// The private key could not be found to decrypt the stream. - /// - ///- /// - ///The user chose to cancel the password prompt. - ///-or- - ///The operation was cancelled via the cancellation token. - ///- /// 3 bad attempts were made to unlock the secret key. - /// - ///- /// An OpenPGP error occurred. - /// - public TaskDecryptToAsync (Stream encryptedData, Stream decryptedData, CancellationToken cancellationToken = default (CancellationToken)) - { - return DecryptToAsync (encryptedData, decryptedData, true, cancellationToken); - } - - /// - /// Decrypts the specified encryptedData and extracts the digital signers if the content was also signed. - /// - ///- /// Decrypts the specified encryptedData and extracts the digital signers if the content was also signed. - /// - ///The decrypted - /// The encrypted data. - /// A list of digital signatures if the data was both signed and encrypted. - /// The cancellation token. - ///. - /// - ///is null . - ///- /// The private key could not be found to decrypt the stream. - /// - ///- /// - ///The user chose to cancel the password prompt. - ///-or- - ///The operation was cancelled via the cancellation token. - ///- /// 3 bad attempts were made to unlock the secret key. - /// - ///- /// An OpenPGP error occurred. - /// - public MimeEntity Decrypt (Stream encryptedData, out DigitalSignatureCollection signatures, CancellationToken cancellationToken = default (CancellationToken)) - { - var decryptedData = new MemoryBlockStream (); - - signatures = DecryptTo (encryptedData, decryptedData, cancellationToken); - decryptedData.Position = 0; - - return MimeEntity.Load (decryptedData, true, cancellationToken); - } - - ///- /// Decrypts the specified encryptedData. - /// - ///- /// Decrypts the specified encryptedData. - /// - ///The decrypted - /// The encrypted data. - /// The cancellation token. - ///. - /// - ///is null . - ///- /// The private key could not be found to decrypt the stream. - /// - ///- /// - ///The user chose to cancel the password prompt. - ///-or- - ///The operation was cancelled via the cancellation token. - ///- /// 3 bad attempts were made to unlock the secret key. - /// - ///- /// An OpenPGP error occurred. - /// - public override MimeEntity Decrypt (Stream encryptedData, CancellationToken cancellationToken = default (CancellationToken)) - { - var decryptedData = new MemoryBlockStream (); - - DecryptTo (encryptedData, decryptedData, cancellationToken); - decryptedData.Position = 0; - - return MimeEntity.Load (decryptedData, true, cancellationToken); - } - - ///- /// Saves the public key-ring bundle. + /// Saves the public key-ring bundle. /// ////// Atomically saves the public key-ring bundle to the path specified by @@ -2399,7 +785,6 @@ async Task. DecryptToAsync (Stream encryptedData, Str /// @@ -2442,7 +823,6 @@ protected void SavePublicKeyRingBundle () /// @@ -2483,7 +859,7 @@ protected void SaveSecretKeyRingBundle () /// /// - public void Import (PgpPublicKeyRing keyring) + public virtual void Import (PgpPublicKeyRing keyring) { if (keyring == null) throw new ArgumentNullException (nameof (keyring)); @@ -2502,7 +878,7 @@ public void Import (PgpPublicKeyRing keyring) ///is null . ////// - public void Import (PgpPublicKeyRingBundle bundle) + public override void Import (PgpPublicKeyRingBundle bundle) { if (bundle == null) throw new ArgumentNullException (nameof (bundle)); @@ -2552,7 +928,7 @@ public override void Import (Stream stream) ///is null . ////// - public void Import (PgpSecretKeyRing keyring) + public virtual void Import (PgpSecretKeyRing keyring) { if (keyring == null) throw new ArgumentNullException (nameof (keyring)); @@ -2571,7 +947,7 @@ public void Import (PgpSecretKeyRing keyring) ///is null . ////// - public void Import (PgpSecretKeyRingBundle bundle) + public virtual void Import (PgpSecretKeyRingBundle bundle) { if (bundle == null) throw new ArgumentNullException (nameof (bundle)); @@ -2593,7 +969,7 @@ public void Import (PgpSecretKeyRingBundle bundle) ///is null . ////// Exports the public keys for the specified mailboxes. /// - ///A new + ///instance containing the exported public keys. A new /// The mailboxes associated with the public keys to export. ///instance containing the exported public keys. /// public SecureMimeDigitalCertificate (X509Certificate certificate) { - Certificate = certificate; - - var pubkey = certificate.GetPublicKey (); - if (pubkey is DsaKeyParameters) - PublicKeyAlgorithm = PublicKeyAlgorithm.Dsa; - else if (pubkey is RsaKeyParameters) - PublicKeyAlgorithm = PublicKeyAlgorithm.RsaGeneral; - else if (pubkey is ElGamalKeyParameters) - PublicKeyAlgorithm = PublicKeyAlgorithm.ElGamalGeneral; - else if (pubkey is ECKeyParameters) - PublicKeyAlgorithm = PublicKeyAlgorithm.EllipticCurve; - else if (pubkey is DHKeyParameters) - PublicKeyAlgorithm = PublicKeyAlgorithm.DiffieHellman; + if (certificate == null) + throw new ArgumentNullException (nameof (certificate)); + Certificate = certificate; Fingerprint = certificate.GetFingerprint (); + PublicKeyAlgorithm = certificate.GetPublicKeyAlgorithm (); } ///is null . @@ -2621,7 +997,7 @@ public override MimePart Export (IEnumerablemailboxes) /// /// Exports the specified public keys. /// - ///A new + ///instance containing the exported public keys. A new /// The public keys to export. ///instance containing the exported public keys. /// + [Obsolete ("Use new SecureMailboxAddress (string.Empty, address, fingerprint) instead.")] public SecureMailboxAddress (string address, string fingerprint) : base (address) { ValidateFingerprint (fingerprint); @@ -206,13 +204,13 @@ static void ValidateFingerprint (string fingerprint) ///is null . @@ -2643,7 +1019,7 @@ public MimePart Export (IEnumerablekeys) /// /// Exports the specified public keys. /// - ///A new + ///instance containing the exported public keys. A new /// The public keys to export. ///instance containing the exported public keys. /// + [Obsolete ("Use new SecureMailboxAddress (string.Empty, route, address, fingerprint) instead.")] public SecureMailboxAddress (IEnumerableis null . @@ -2705,7 +1081,7 @@ public void Export (IEnumerablemailboxes, Stream stream, bool a /// /// Exports the specified public keys. /// - ///A new + ///instance containing the exported public keys. A new /// The public keys to export. /// The output stream. ///instance containing the exported public keys. true if the output should be armored; otherwise,false . @@ -2778,7 +1154,7 @@ public void Export (PgpPublicKeyRingBundle keys, Stream stream, bool armor) ////// - public void Delete (PgpPublicKeyRing keyring) + public virtual void Delete (PgpPublicKeyRing keyring) { if (keyring == null) throw new ArgumentNullException (nameof (keyring)); @@ -2797,7 +1173,7 @@ public void Delete (PgpPublicKeyRing keyring) ///is null . ////// - public void Delete (PgpSecretKeyRing keyring) + public virtual void Delete (PgpSecretKeyRing keyring) { if (keyring == null) throw new ArgumentNullException (nameof (keyring)); @@ -2805,22 +1181,5 @@ public void Delete (PgpSecretKeyRing keyring) SecretKeyRingBundle = PgpSecretKeyRingBundle.RemoveSecretKeyRing (SecretKeyRingBundle, keyring); SaveSecretKeyRingBundle (); } - - ///is null . ///- /// Releases all resources used by the - ///object. - /// Call - protected override void Dispose (bool disposing) - { - if (disposing && client != null) { - client.Dispose (); - client = null; - } - - base.Dispose (disposing); - } } } diff --git a/MimeKit/Cryptography/OpenPgpContextBase.cs b/MimeKit/Cryptography/OpenPgpContextBase.cs new file mode 100644 index 0000000000..32bd421b74 --- /dev/null +++ b/MimeKit/Cryptography/OpenPgpContextBase.cs @@ -0,0 +1,1924 @@ +// +// OpenPgpContext.cs +// +// Authors: Jeffrey Stedfastwhen you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the so - /// the garbage collector can reclaim the memory that the was occupying. +// Thomas Hansen +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.IO; +using System.Net; +using System.Text; +using System.Buffers; +using System.Net.Http; +using System.Threading; +using System.Diagnostics; +using System.Globalization; +using System.Threading.Tasks; +using System.Collections.Generic; + +using Org.BouncyCastle.Bcpg; +using Org.BouncyCastle.Bcpg.OpenPgp; + +using MimeKit.IO; + +namespace MimeKit.Cryptography { + /// + /// An abstract OpenPGP cryptography context which can be used for PGP/MIME. + /// + ///+ /// Generally speaking, applications should not use a + public abstract class OpenPgpContextBase : CryptographyContext + { + static readonly string[] ProtocolSubtypes = { "pgp-signature", "pgp-encrypted", "pgp-keys", "x-pgp-signature", "x-pgp-encrypted", "x-pgp-keys" }; + const string BeginPublicKeyBlock = "-----BEGIN PGP PUBLIC KEY BLOCK-----"; + const string EndPublicKeyBlock = "-----END PGP PUBLIC KEY BLOCK-----"; + const int BufferLength = 4096; + + static readonly EncryptionAlgorithm[] DefaultEncryptionAlgorithmRank = { + EncryptionAlgorithm.Idea, + EncryptionAlgorithm.TripleDes, + EncryptionAlgorithm.Cast5, + EncryptionAlgorithm.Blowfish, + EncryptionAlgorithm.Aes128, + EncryptionAlgorithm.Aes192, + EncryptionAlgorithm.Aes256, + EncryptionAlgorithm.Twofish, + EncryptionAlgorithm.Camellia128, + EncryptionAlgorithm.Camellia192, + EncryptionAlgorithm.Camellia256 + }; + + static readonly DigestAlgorithm[] DefaultDigestAlgorithmRank = { + DigestAlgorithm.Sha1, + DigestAlgorithm.RipeMD160, + DigestAlgorithm.Sha256, + DigestAlgorithm.Sha384, + DigestAlgorithm.Sha512, + DigestAlgorithm.Sha224 + }; + + EncryptionAlgorithm defaultAlgorithm; + HttpClient client; + Uri keyServer; + + ///+ /// directly, but rather via higher level APIs such as + /// and . + /// + /// Initialize a new instance of the + ///class. + /// + /// Creates a new + protected OpenPgpContextBase () + { + EncryptionAlgorithmRank = DefaultEncryptionAlgorithmRank; + DigestAlgorithmRank = DefaultDigestAlgorithmRank; + + foreach (var algorithm in EncryptionAlgorithmRank) + Enable (algorithm); + + foreach (var algorithm in DigestAlgorithmRank) + Enable (algorithm); + + defaultAlgorithm = EncryptionAlgorithm.Cast5; + + client = new HttpClient (); + } + + ///. + /// + /// Get the password for a secret key. + /// + ///+ /// Gets the password for a secret key. + /// + ///The password for the secret key. + /// The secret key. + ///+ /// The user chose to cancel the password request. + /// + protected abstract string GetPasswordForKey (PgpSecretKey key); // FIXME: rename this to GetPassword() in the future + + ///+ /// Get the public keyring that contains the specified key. + /// + ///+ /// + /// The public key identifier. + /// The cancellation token. + ///Gets the public keyring that contains the specified key. + ///Implementations should first try to obtain the keyring stored (or cached) locally. + /// Failing that, if + ///is enabled, they should use + /// to attempt to + /// retrieve the keyring from the configured . The public keyring that contains the specified key or + ///null if the keyring could not be found.+ /// The operation was cancelled. + /// + protected abstract PgpPublicKeyRing GetPublicKeyRing (long keyId, CancellationToken cancellationToken); + + ///+ /// Get the public keyring that contains the specified key asynchronously. + /// + ///+ /// + /// The public key identifier. + /// The cancellation token. + ///Gets the public keyring that contains the specified key. + ///Implementations should first try to obtain the keyring stored (or cached) locally. + /// Failing that, if + ///is enabled, they should use + /// to attempt to + /// retrieve the keyring from the configured . The public keyring that contains the specified key or + ///null if the keyring could not be found.+ /// The operation was cancelled. + /// + protected abstract TaskGetPublicKeyRingAsync (long keyId, CancellationToken cancellationToken); + + /// + /// Get the secret key for a specified key identifier. + /// + ///+ /// Gets the secret key for a specified key identifier. + /// + /// The key identifier for the desired secret key. + ///The secret key. + ///+ /// The secret key specified by the + protected abstract PgpSecretKey GetSecretKey (long keyId); + + ///could not be found. + /// + /// Get the public keys for the specified mailbox addresses. + /// + ///+ /// Gets a list of valid public keys for the specified mailbox addresses that can be used for encryption. + /// + ///The encryption keys. + /// The mailboxes. + ///+ /// + ///is null . + ///+ /// A public key for one or more of the + public abstract IListcould not be found. + /// GetPublicKeys (IEnumerable mailboxes); + + /// + /// Get the signing key associated with the mailbox address. + /// + ///+ /// Gets the signing key associated with the mailbox address. + /// + ///The signing key. + /// The mailbox. + ///+ /// + ///is null . + ///+ /// A secret key for the specified + public abstract PgpSecretKey GetSigningKey (MailboxAddress mailbox); + + ///could not be found. + /// + /// Get or set the default encryption algorithm. + /// + ///+ /// Gets or sets the default encryption algorithm. + /// + ///The encryption algorithm. + ///+ /// The specified encryption algorithm is not supported. + /// + public EncryptionAlgorithm DefaultEncryptionAlgorithm { + get { return defaultAlgorithm; } + set { + GetSymmetricKeyAlgorithm (value); + defaultAlgorithm = value; + } + } + + bool IsValidKeyServer { + get { + if (keyServer == null) + return false; + + switch (keyServer.Scheme.ToLowerInvariant ()) { + case "https": case "http": case "hkp": return true; + default: return false; + } + } + } + + ///+ /// Get or set the key server to use when automatically retrieving keys. + /// + ///+ /// + ///Gets or sets the key server to use when verifying keys that are + /// not already in the public keychain. + ///Only HTTP and HKP protocols are supported. + ///The key server. + ///+ /// + public Uri KeyServer { + get { return keyServer; } + set { + if (value != null && !value.IsAbsoluteUri) + throw new ArgumentException ("The key server URI must be absolute.", nameof (value)); + + keyServer = value; + } + } + + ///is not an absolute URI. + /// + /// Get or set whether unknown PGP keys should automtically be retrieved. + /// + ///+ /// + ///Gets or sets whether or not the + ///should automatically + /// fetch keys as needed from the keyserver when verifying signatures. Requires a valid + ///to be set. + public bool AutoKeyRetrieve { + get; set; + } + + /// true if unknown PGP keys should automatically be retrieved; otherwise,false .+ /// Get the signature protocol. + /// + ///+ /// + ///The signature protocol is used by + ///+ /// in order to determine what the protocol parameter of the Content-Type + /// header should be. The signature protocol. + public override string SignatureProtocol { + get { return "application/pgp-signature"; } + } + + ///+ /// Get the encryption protocol. + /// + ///+ /// + ///The encryption protocol is used by + ///+ /// in order to determine what the protocol parameter of the Content-Type + /// header should be. The encryption protocol. + public override string EncryptionProtocol { + get { return "application/pgp-encrypted"; } + } + + ///+ /// Get the key exchange protocol. + /// + ///+ /// Gets the key exchange protocol. + /// + ///The key exchange protocol. + public override string KeyExchangeProtocol { + get { return "application/pgp-keys"; } + } + + ///+ /// Check whether or not the specified protocol is supported. + /// + ///+ /// Used in order to make sure that the protocol parameter value specified in either a multipart/signed + /// or multipart/encrypted part is supported by the supplied cryptography context. + /// + ///+ /// The protocol. + /// true if the protocol is supported; otherwisefalse + /// + public override bool Supports (string protocol) + { + if (protocol == null) + throw new ArgumentNullException (nameof (protocol)); + + if (!protocol.StartsWith ("application/", StringComparison.OrdinalIgnoreCase)) + return false; + + int startIndex = "application/".Length; + int subtypeLength = protocol.Length - startIndex; + + for (int i = 0; i < ProtocolSubtypes.Length; i++) { + if (subtypeLength != ProtocolSubtypes[i].Length) + continue; + + if (string.Compare (protocol, startIndex, ProtocolSubtypes[i], 0, subtypeLength, StringComparison.OrdinalIgnoreCase) == 0) + return true; + } + + return false; + } + + ///is null . + ///+ /// Get the string name of the digest algorithm for use with the micalg parameter of a multipart/signed part. + /// + ///+ /// + ///Maps the + ///to the appropriate string identifier + /// as used by the micalg parameter value of a multipart/signed Content-Type + /// header. For example: + ///
+ ///+ /// Algorithm Name - + ///
pgp-md5 - + ///
pgp-sha1 - + ///
pgp-ripemd160 - + ///
pgp-md2 - + ///
pgp-tiger192 - + ///
pgp-haval-5-160 - + ///
pgp-sha256 - + ///
pgp-sha384 - + ///
pgp-sha512 - + ///
pgp-sha224 The micalg value. + /// The digest algorithm. + ///+ /// + public override string GetDigestAlgorithmName (DigestAlgorithm micalg) + { + switch (micalg) { + case DigestAlgorithm.MD5: return "pgp-md5"; + case DigestAlgorithm.Sha1: return "pgp-sha1"; + case DigestAlgorithm.RipeMD160: return "pgp-ripemd160"; + case DigestAlgorithm.MD2: return "pgp-md2"; + case DigestAlgorithm.Tiger192: return "pgp-tiger192"; + case DigestAlgorithm.Haval5160: return "pgp-haval-5-160"; + case DigestAlgorithm.Sha256: return "pgp-sha256"; + case DigestAlgorithm.Sha384: return "pgp-sha384"; + case DigestAlgorithm.Sha512: return "pgp-sha512"; + case DigestAlgorithm.Sha224: return "pgp-sha224"; + case DigestAlgorithm.MD4: return "pgp-md4"; + default: throw new ArgumentOutOfRangeException (nameof (micalg)); + } + } + + ///is out of range. + /// + /// Get the digest algorithm from the micalg parameter value in a multipart/signed part. + /// + ///+ /// Maps the micalg parameter value string back to the appropriate + ///. + /// The digest algorithm. + /// The micalg parameter value. + ///+ /// + public override DigestAlgorithm GetDigestAlgorithm (string micalg) + { + if (micalg == null) + throw new ArgumentNullException (nameof (micalg)); + + switch (micalg.ToLowerInvariant ()) { + case "pgp-md5": return DigestAlgorithm.MD5; + case "pgp-sha1": return DigestAlgorithm.Sha1; + case "pgp-ripemd160": return DigestAlgorithm.RipeMD160; + case "pgp-md2": return DigestAlgorithm.MD2; + case "pgp-tiger192": return DigestAlgorithm.Tiger192; + case "pgp-haval-5-160": return DigestAlgorithm.Haval5160; + case "pgp-sha256": return DigestAlgorithm.Sha256; + case "pgp-sha384": return DigestAlgorithm.Sha384; + case "pgp-sha512": return DigestAlgorithm.Sha512; + case "pgp-sha224": return DigestAlgorithm.Sha224; + case "pgp-md4": return DigestAlgorithm.MD4; + default: return DigestAlgorithm.None; + } + } + + ///is null . + ///+ /// Hex encode an array of bytes. + /// + ///+ /// This method is used to hex-encode the PGP key fingerprints. + /// + /// The data to encode. + ///A string representing the hex-encoded data. + static string HexEncode (byte[] data) + { + var fingerprint = new StringBuilder (); + + for (int i = 0; i < data.Length; i++) + fingerprint.Append (data[i].ToString ("x2")); + + return fingerprint.ToString (); + } + + ///+ /// Check that a public key is a match for the specified mailbox. + /// + ///+ /// + /// The public key. + /// The mailbox address. + ///Checks that the public key is a match for the specified mailbox. + ///If the + ///is a with a non-empty + /// , then the fingerprint is used to match the key's + /// fingerprint. Otherwise, the email address(es) contained within the key's user identifier strings + /// are compared to the mailbox address. + /// true if the key is a match for the specified mailbox; otherwise,false .+ /// + protected static bool IsMatch (PgpPublicKey key, MailboxAddress mailbox) + { + if (key == null) + throw new ArgumentNullException (nameof (key)); + + if (mailbox == null) + throw new ArgumentNullException (nameof (mailbox)); + + if (mailbox is SecureMailboxAddress secure && !string.IsNullOrEmpty (secure.Fingerprint)) { + if (secure.Fingerprint.Length > 16) { + var fingerprint = HexEncode (key.GetFingerprint ()); + + return secure.Fingerprint.Equals (fingerprint, StringComparison.OrdinalIgnoreCase); + } + + var id = ((int) key.KeyId).ToString ("X2"); + + return secure.Fingerprint.EndsWith (id, StringComparison.OrdinalIgnoreCase); + } + + foreach (string userId in key.GetUserIds ()) { + if (!MailboxAddress.TryParse (userId, out var email)) + continue; + + if (mailbox.Address.Equals (email.Address, StringComparison.OrdinalIgnoreCase)) + return true; + } + + return false; + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Check that a secret key is a match for the specified mailbox. + /// + ///+ /// + /// The secret key. + /// The mailbox address. + ///Checks that the secret key is a match for the specified mailbox. + ///If the + ///is a with a non-empty + /// , then the fingerprint is used to match the key's + /// fingerprint. Otherwise, the email address(es) contained within the key's user identifier strings + /// are compared to the mailbox address. + /// true if the key is a match for the specified mailbox; otherwise,false .+ /// + protected static bool IsMatch (PgpSecretKey key, MailboxAddress mailbox) + { + if (key == null) + throw new ArgumentNullException (nameof (key)); + + if (mailbox == null) + throw new ArgumentNullException (nameof (mailbox)); + + if (mailbox is SecureMailboxAddress secure && !string.IsNullOrEmpty (secure.Fingerprint)) { + if (secure.Fingerprint.Length > 16) { + var fingerprint = HexEncode (key.PublicKey.GetFingerprint ()); + + return secure.Fingerprint.Equals (fingerprint, StringComparison.OrdinalIgnoreCase); + } + + var id = ((int) key.KeyId).ToString ("X2"); + + return secure.Fingerprint.EndsWith (id, StringComparison.OrdinalIgnoreCase); + } + + foreach (string userId in key.UserIds) { + if (!MailboxAddress.TryParse (userId, out var email)) + continue; + + if (mailbox.Address.Equals (email.Address, StringComparison.OrdinalIgnoreCase)) + return true; + } + + return false; + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Check if a public key is expired. + /// + ///+ /// Checks if a public key is expired. + /// + /// The public key. + ///+ /// true if the public key is expired; otherwise,false .+ /// + protected static bool IsExpired (PgpPublicKey key) + { + if (key == null) + throw new ArgumentNullException (nameof (key)); + + long seconds = key.GetValidSeconds (); + + if (seconds != 0) { + var expires = key.CreationTime.AddSeconds ((double) seconds); + if (expires <= DateTime.Now) + return true; + } + + return false; + } + + ///is null . + ///+ /// Retrieves the public keyring, using the preferred key server, automatically importing it afterwards. + /// + /// The identifier of the key to be retrieved. + ///true if this operation should be done asynchronously; otherweise,false . + /// The cancellation token. + ///The public key ring. + async TaskRetrievePublicKeyRingAsync (long keyId, bool doAsync, CancellationToken cancellationToken) + { + if (!IsValidKeyServer) + return null; + + var scheme = keyServer.Scheme.ToLowerInvariant (); + var uri = new UriBuilder (); + + uri.Scheme = scheme == "hkp" ? "http" : scheme; + uri.Host = keyServer.Host; + + if (keyServer.IsDefaultPort) { + if (scheme == "hkp") + uri.Port = 11371; + } else { + uri.Port = keyServer.Port; + } + + uri.Path = "/pks/lookup"; + uri.Query = string.Format (CultureInfo.InvariantCulture, "op=get&search=0x{0:X}", keyId); + + using (var stream = new MemoryBlockStream ()) { + using (var filtered = new FilteredStream (stream)) { + filtered.Add (new OpenPgpBlockFilter (BeginPublicKeyBlock, EndPublicKeyBlock)); + + if (doAsync) { + using (var response = await client.GetAsync (uri.ToString (), cancellationToken).ConfigureAwait (false)) + await response.Content.CopyToAsync (filtered).ConfigureAwait (false); + } else { +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 + var request = (HttpWebRequest) WebRequest.Create (uri.ToString ()); + using (var response = request.GetResponse ()) { + var content = response.GetResponseStream (); + content.CopyTo (filtered, 4096); + } +#else + using (var response = client.GetAsync (uri.ToString (), cancellationToken).GetAwaiter ().GetResult ()) + response.Content.CopyToAsync (filtered).GetAwaiter ().GetResult (); +#endif + } + + filtered.Flush (); + } + + stream.Position = 0; + + using (var armored = new ArmoredInputStream (stream, true)) { + var bundle = new PgpPublicKeyRingBundle (armored); + + Import (bundle); + + return bundle.GetPublicKeyRing (keyId); + } + } + } + + /// + /// Retrieve the public keyring using the configured key server. + /// + ///+ /// + /// The identifier of the public key to be retrieved. + /// The cancellation token. + ///Retrieves the public keyring specified by the + ///from the key server + /// set on the property. If the keyring is successfully retrieved, it will + /// be imported via . This method should be called by + ///+ /// when the keyring is not available locally. The public key ring. + protected PgpPublicKeyRing RetrievePublicKeyRing (long keyId, CancellationToken cancellationToken) + { + return RetrievePublicKeyRingAsync (keyId, false, cancellationToken).GetAwaiter ().GetResult (); + } + + ///+ /// Asynchronously retrieve the public keyring using the configured key server. + /// + ///+ /// + /// The identifier of the public key to be retrieved. + /// The cancellation token. + ///Retrieves the public keyring specified by the + ///from the key server + /// set on the property. If the keyring is successfully retrieved, it will + /// be imported via . This method should be called by + ///+ /// when the keyring is not available locally. The public key ring. + protected TaskRetrievePublicKeyRingAsync (long keyId, CancellationToken cancellationToken) + { + return RetrievePublicKeyRingAsync (keyId, true, cancellationToken); + } + + /// + /// Gets the private key from the specified secret key. + /// + ///+ /// Gets the private key from the specified secret key. + /// + ///The private key. + /// The secret key. + ///+ /// + ///is null . + ///+ /// The user chose to cancel the password prompt. + /// + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + protected PgpPrivateKey GetPrivateKey (PgpSecretKey key) + { + int attempts = 0; + string password; + + if (key == null) + throw new ArgumentNullException (nameof (key)); + + do { + if ((password = GetPasswordForKey (key)) == null) + throw new OperationCanceledException (); + + try { + var privateKey = key.ExtractPrivateKey (password.ToCharArray ()); + + // Note: the private key will be null if the private key is empty. + if (privateKey == null) + break; + + return privateKey; + } catch (Exception ex) { +#if DEBUG + Debug.WriteLine (string.Format ("Failed to extract secret key: {0}", ex)); +#endif + } + + attempts++; + } while (attempts < 3); + + throw new UnauthorizedAccessException (); + } + + ///+ /// Gets the equivalent + ///for the + /// specified . + /// + /// Maps a + ///to the equivalent . + /// The hash algorithm. + /// The digest algorithm. + ///+ /// + ///is out of range. + /// + /// + public static HashAlgorithmTag GetHashAlgorithm (DigestAlgorithm digestAlgo) + { + switch (digestAlgo) { + case DigestAlgorithm.MD5: return HashAlgorithmTag.MD5; + case DigestAlgorithm.Sha1: return HashAlgorithmTag.Sha1; + case DigestAlgorithm.RipeMD160: return HashAlgorithmTag.RipeMD160; + case DigestAlgorithm.DoubleSha: throw new NotSupportedException ("The Double SHA digest algorithm is not supported."); + case DigestAlgorithm.MD2: return HashAlgorithmTag.MD2; + case DigestAlgorithm.Tiger192: throw new NotSupportedException ("The Tiger-192 digest algorithm is not supported."); + case DigestAlgorithm.Haval5160: throw new NotSupportedException ("The HAVAL 5 160 digest algorithm is not supported."); + case DigestAlgorithm.Sha256: return HashAlgorithmTag.Sha256; + case DigestAlgorithm.Sha384: return HashAlgorithmTag.Sha384; + case DigestAlgorithm.Sha512: return HashAlgorithmTag.Sha512; + case DigestAlgorithm.Sha224: return HashAlgorithmTag.Sha224; + case DigestAlgorithm.MD4: throw new NotSupportedException ("The MD4 digest algorithm is not supported."); + default: throw new ArgumentOutOfRangeException (nameof (digestAlgo)); + } + } + + ///is not a supported digest algorithm. + /// + /// Cryptographically signs the content. + /// + ///+ /// Cryptographically signs the content using the specified signer and digest algorithm. + /// + ///A new + /// The signer. + /// The digest algorithm to use for signing. + /// The content. + ///instance + /// containing the detached signature data. + /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///is out of range. + /// + /// The specified + ///is not supported by this context. + /// + /// A signing key could not be found for + ///. + /// + /// The user chose to cancel the password prompt. + /// + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + public override MimePart Sign (MailboxAddress signer, DigestAlgorithm digestAlgo, Stream content) + { + if (signer == null) + throw new ArgumentNullException (nameof (signer)); + + if (content == null) + throw new ArgumentNullException (nameof (content)); + + var key = GetSigningKey (signer); + + return Sign (key, digestAlgo, content); + } + + ///+ /// Cryptographically signs the content. + /// + ///+ /// Cryptographically signs the content using the specified signer and digest algorithm. + /// + ///A new + /// The signer. + /// The digest algorithm to use for signing. + /// The content. + ///instance + /// containing the detached signature data. + /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///cannot be used for signing. + /// + /// The + ///was out of range. + /// + /// The + ///is not supported. + /// + /// The user chose to cancel the password prompt. + /// + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + public ApplicationPgpSignature Sign (PgpSecretKey signer, DigestAlgorithm digestAlgo, Stream content) + { + if (signer == null) + throw new ArgumentNullException (nameof (signer)); + + if (!signer.IsSigningKey) + throw new ArgumentException ("The specified secret key cannot be used for signing.", nameof (signer)); + + if (content == null) + throw new ArgumentNullException (nameof (content)); + + var hashAlgorithm = GetHashAlgorithm (digestAlgo); + var memory = new MemoryBlockStream (); + + using (var armored = new ArmoredOutputStream (memory)) { + armored.SetHeader ("Version", null); + + var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib); + using (var compressed = compresser.Open (armored)) { + var signatureGenerator = new PgpSignatureGenerator (signer.PublicKey.Algorithm, hashAlgorithm); + var buf = ArrayPool.Shared.Rent (BufferLength); + int nread; + + try { + signatureGenerator.InitSign (PgpSignature.CanonicalTextDocument, GetPrivateKey (signer)); + + while ((nread = content.Read (buf, 0, BufferLength)) > 0) + signatureGenerator.Update (buf, 0, nread); + } finally { + ArrayPool .Shared.Return (buf); + } + + var signature = signatureGenerator.Generate (); + + signature.Encode (compressed); + compressed.Flush (); + } + + armored.Flush (); + } + + memory.Position = 0; + + return new ApplicationPgpSignature (memory); + } + + /// + /// Gets the equivalent + ///for the specified + /// . + /// + /// Gets the equivalent + ///for the specified + /// . + /// The digest algorithm. + /// The hash algorithm. + ///+ /// + ///is out of range. + /// + /// + public static DigestAlgorithm GetDigestAlgorithm (HashAlgorithmTag hashAlgorithm) + { + switch (hashAlgorithm) { + case HashAlgorithmTag.MD5: return DigestAlgorithm.MD5; + case HashAlgorithmTag.Sha1: return DigestAlgorithm.Sha1; + case HashAlgorithmTag.RipeMD160: return DigestAlgorithm.RipeMD160; + case HashAlgorithmTag.DoubleSha: return DigestAlgorithm.DoubleSha; + case HashAlgorithmTag.MD2: return DigestAlgorithm.MD2; + case HashAlgorithmTag.Tiger192: return DigestAlgorithm.Tiger192; + case HashAlgorithmTag.Haval5pass160: return DigestAlgorithm.Haval5160; + case HashAlgorithmTag.Sha256: return DigestAlgorithm.Sha256; + case HashAlgorithmTag.Sha384: return DigestAlgorithm.Sha384; + case HashAlgorithmTag.Sha512: return DigestAlgorithm.Sha512; + case HashAlgorithmTag.Sha224: return DigestAlgorithm.Sha224; + default: throw new ArgumentOutOfRangeException (nameof (hashAlgorithm)); + } + } + + ///does not have an equivalent value. + /// + /// Gets the equivalent + ///for the specified + /// . + /// + /// Gets the equivalent + ///for the specified + /// . + /// The public-key algorithm. + /// The public-key algorithm. + ///+ /// + ///is out of range. + /// + /// + public static PublicKeyAlgorithm GetPublicKeyAlgorithm (PublicKeyAlgorithmTag algorithm) + { + switch (algorithm) { + case PublicKeyAlgorithmTag.RsaGeneral: return PublicKeyAlgorithm.RsaGeneral; + case PublicKeyAlgorithmTag.RsaEncrypt: return PublicKeyAlgorithm.RsaEncrypt; + case PublicKeyAlgorithmTag.RsaSign: return PublicKeyAlgorithm.RsaSign; + case PublicKeyAlgorithmTag.ElGamalGeneral: return PublicKeyAlgorithm.ElGamalGeneral; + case PublicKeyAlgorithmTag.ElGamalEncrypt: return PublicKeyAlgorithm.ElGamalEncrypt; + case PublicKeyAlgorithmTag.Dsa: return PublicKeyAlgorithm.Dsa; + case PublicKeyAlgorithmTag.ECDH: return PublicKeyAlgorithm.EllipticCurve; + case PublicKeyAlgorithmTag.ECDsa: return PublicKeyAlgorithm.EllipticCurveDsa; + case PublicKeyAlgorithmTag.DiffieHellman: return PublicKeyAlgorithm.DiffieHellman; + default: throw new ArgumentOutOfRangeException (nameof (algorithm)); + } + } + + bool TryGetPublicKey (PgpPublicKeyRing keyring, long keyId, out PgpPublicKey pubkey) + { + if (keyring != null) { + foreach (PgpPublicKey key in keyring.GetPublicKeys ()) { + if (key.KeyId == keyId) { + pubkey = key; + return true; + } + } + } + + pubkey = null; + + return false; + } + + async Taskdoes not have an equivalent value. + /// GetDigitalSignaturesAsync (PgpSignatureList signatureList, Stream content, bool doAsync, CancellationToken cancellationToken) + { + var signatures = new List (); + + for (int i = 0; i < signatureList.Count; i++) { + long keyId = signatureList[i].KeyId; + PgpPublicKeyRing keyring; + + if (doAsync) + keyring = await GetPublicKeyRingAsync (keyId, cancellationToken).ConfigureAwait (false); + else + keyring = GetPublicKeyRing (keyId, cancellationToken); + + TryGetPublicKey (keyring, keyId, out var key); + + var signature = new OpenPgpDigitalSignature (keyring, key, signatureList[i]) { + PublicKeyAlgorithm = GetPublicKeyAlgorithm (signatureList[i].KeyAlgorithm), + DigestAlgorithm = GetDigestAlgorithm (signatureList[i].HashAlgorithm), + CreationDate = signatureList[i].CreationTime, + }; + + if (key != null) + signatureList[i].InitVerify (key); + + signatures.Add (signature); + } + + var buf = ArrayPool .Shared.Rent (BufferLength); + int nread; + + try { + while ((nread = content.Read (buf, 0, BufferLength)) > 0) { + for (int i = 0; i < signatures.Count; i++) { + if (signatures[i].SignerCertificate != null) { + var pgp = (OpenPgpDigitalSignature) signatures[i]; + pgp.Signature.Update (buf, 0, nread); + } + } + } + } finally { + ArrayPool .Shared.Return (buf); + } + + return new DigitalSignatureCollection (signatures); + } + + Task VerifyAsync (Stream content, Stream signatureData, bool doAsync, CancellationToken cancellationToken) + { + if (content == null) + throw new ArgumentNullException (nameof (content)); + + if (signatureData == null) + throw new ArgumentNullException (nameof (signatureData)); + + using (var armored = new ArmoredInputStream (signatureData)) { + var factory = new PgpObjectFactory (armored); + var data = factory.NextPgpObject (); + PgpSignatureList signatureList; + + var compressed = data as PgpCompressedData; + if (compressed != null) { + factory = new PgpObjectFactory (compressed.GetDataStream ()); + data = factory.NextPgpObject (); + } + + if (data == null) + throw new FormatException ("Invalid PGP format."); + + signatureList = (PgpSignatureList) data; + + return GetDigitalSignaturesAsync (signatureList, content, doAsync, cancellationToken); + } + } + + /// + /// Verify the specified content using the detached signatureData. + /// + ///+ /// + ///Verifies the specified content using the detached signatureData. + ///If any of the signatures were made with an unrecognized key and + ///is enabled, + /// an attempt will be made to retrieve said key(s). The can be used to cancel + /// key retrieval. A list of digital signatures. + /// The content. + /// The signature data. + /// The cancellation token. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + public override DigitalSignatureCollection Verify (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) + { + return VerifyAsync (content, signatureData, false, cancellationToken).GetAwaiter ().GetResult (); + } + + ///does not contain valid PGP signature data. + /// + /// Asynchronously verify the specified content using the detached signatureData. + /// + ///+ /// + ///Verifies the specified content using the detached signatureData. + ///If any of the signatures were made with an unrecognized key and + ///is enabled, + /// an attempt will be made to retrieve said key(s). The can be used to cancel + /// key retrieval. A list of digital signatures. + /// The content. + /// The signature data. + /// The cancellation token. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + public override Taskdoes not contain valid PGP signature data. + /// VerifyAsync (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) + { + return VerifyAsync (content, signatureData, true, cancellationToken); + } + + static Stream Compress (Stream content, byte[] buf, int bufferLength) + { + var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib); + var memory = new MemoryBlockStream (); + + using (var compressed = compresser.Open (memory)) { + var literalGenerator = new PgpLiteralDataGenerator (); + + using (var literal = literalGenerator.Open (compressed, 't', "mime.txt", content.Length, DateTime.Now)) { + int nread; + + while ((nread = content.Read (buf, 0, bufferLength)) > 0) + literal.Write (buf, 0, nread); + + literal.Flush (); + } + + compressed.Flush (); + } + + memory.Position = 0; + + return memory; + } + + static Stream Encrypt (PgpEncryptedDataGenerator encrypter, Stream content) + { + var memory = new MemoryBlockStream (); + + using (var armored = new ArmoredOutputStream (memory)) { + var buf = ArrayPool .Shared.Rent (BufferLength); + + try { + armored.SetHeader ("Version", null); + + using (var compressed = Compress (content, buf, BufferLength)) { + using (var encrypted = encrypter.Open (armored, compressed.Length)) { + int nread; + + try { + while ((nread = compressed.Read (buf, 0, BufferLength)) > 0) + encrypted.Write (buf, 0, nread); + } finally { + ArrayPool .Shared.Return (buf); + } + + encrypted.Flush (); + } + } + + armored.Flush (); + } finally { + ArrayPool .Shared.Return (buf); + } + } + + memory.Position = 0; + + return memory; + } + + internal static SymmetricKeyAlgorithmTag GetSymmetricKeyAlgorithm (EncryptionAlgorithm algorithm) + { + switch (algorithm) { + case EncryptionAlgorithm.Aes128: return SymmetricKeyAlgorithmTag.Aes128; + case EncryptionAlgorithm.Aes192: return SymmetricKeyAlgorithmTag.Aes192; + case EncryptionAlgorithm.Aes256: return SymmetricKeyAlgorithmTag.Aes256; + case EncryptionAlgorithm.Camellia128: return SymmetricKeyAlgorithmTag.Camellia128; + case EncryptionAlgorithm.Camellia192: return SymmetricKeyAlgorithmTag.Camellia192; + case EncryptionAlgorithm.Camellia256: return SymmetricKeyAlgorithmTag.Camellia256; + case EncryptionAlgorithm.Cast5: return SymmetricKeyAlgorithmTag.Cast5; + case EncryptionAlgorithm.Des: return SymmetricKeyAlgorithmTag.Des; + case EncryptionAlgorithm.TripleDes: return SymmetricKeyAlgorithmTag.TripleDes; + case EncryptionAlgorithm.Idea: return SymmetricKeyAlgorithmTag.Idea; + case EncryptionAlgorithm.Blowfish: return SymmetricKeyAlgorithmTag.Blowfish; + case EncryptionAlgorithm.Twofish: return SymmetricKeyAlgorithmTag.Twofish; + default: throw new NotSupportedException (string.Format ("{0} is not supported.", algorithm)); + } + } + + /// + /// Encrypt the specified content for the specified recipients. + /// + ///+ /// Encrypts the specified content for the specified recipients. + /// + ///A new + /// The recipients. + /// The content. + ///instance + /// containing the encrypted data. + /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///One or more of the recipient keys cannot be used for encrypting. + ///-or- + ///No recipients were specified. + ///+ /// A public key could not be found for one or more of the + public override MimePart Encrypt (IEnumerable. + /// recipients, Stream content) + { + if (recipients == null) + throw new ArgumentNullException (nameof (recipients)); + + if (content == null) + throw new ArgumentNullException (nameof (content)); + + // TODO: document the exceptions that can be thrown by BouncyCastle + return Encrypt (GetPublicKeys (recipients), content); + } + + /// + /// Encrypt the specified content for the specified recipients. + /// + ///+ /// Encrypts the specified content for the specified recipients. + /// + ///A new + /// The encryption algorithm. + /// The recipients. + /// The content. + ///instance + /// containing the encrypted data. + /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///One or more of the recipient keys cannot be used for encrypting. + ///-or- + ///No recipients were specified. + ///+ /// A public key could not be found for one or more of the + ///. + /// + /// The specified encryption algorithm is not supported. + /// + public MimePart Encrypt (EncryptionAlgorithm algorithm, IEnumerablerecipients, Stream content) + { + if (recipients == null) + throw new ArgumentNullException (nameof (recipients)); + + if (content == null) + throw new ArgumentNullException (nameof (content)); + + // TODO: document the exceptions that can be thrown by BouncyCastle + return Encrypt (algorithm, GetPublicKeys (recipients), content); + } + + /// + /// Encrypt the specified content for the specified recipients. + /// + ///+ /// Encrypts the specified content for the specified recipients. + /// + ///A new + /// The encryption algorithm. + /// The recipients. + /// The content. + ///instance + /// containing the encrypted data. + /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///One or more of the recipient keys cannot be used for encrypting. + ///-or- + ///No recipients were specified. + ///+ /// The specified encryption algorithm is not supported. + /// + public MimePart Encrypt (EncryptionAlgorithm algorithm, IEnumerablerecipients, Stream content) + { + if (recipients == null) + throw new ArgumentNullException (nameof (recipients)); + + if (content == null) + throw new ArgumentNullException (nameof (content)); + + var encrypter = new PgpEncryptedDataGenerator (GetSymmetricKeyAlgorithm (algorithm), true); + var unique = new HashSet (); + int count = 0; + + foreach (var recipient in recipients) { + if (!recipient.IsEncryptionKey) + throw new ArgumentException ("One or more of the recipient keys cannot be used for encrypting.", nameof (recipients)); + + if (unique.Add (recipient.KeyId)) { + encrypter.AddMethod (recipient); + count++; + } + } + + if (count == 0) + throw new ArgumentException ("No recipients specified.", nameof (recipients)); + + var encrypted = Encrypt (encrypter, content); + + return new MimePart ("application", "octet-stream") { + ContentDisposition = new ContentDisposition (ContentDisposition.Attachment), + Content = new MimeContent (encrypted), + }; + } + + /// + /// Encrypt the specified content for the specified recipients. + /// + ///+ /// Encrypts the specified content for the specified recipients. + /// + ///A new + /// The recipients. + /// The content. + ///instance + /// containing the encrypted data. + /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + public MimePart Encrypt (IEnumerableOne or more of the recipient keys cannot be used for encrypting. + ///-or- + ///No recipients were specified. + ///recipients, Stream content) + { + return Encrypt (defaultAlgorithm, recipients, content); + } + + /// + /// Cryptographically sign and encrypt the specified content for the specified recipients. + /// + ///+ /// Cryptographically signs and encrypts the specified content for the specified recipients. + /// + ///A new + /// The signer. + /// The digest algorithm to use for signing. + /// The recipients. + /// The content. + ///instance + /// containing the encrypted data. + /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///is out of range. + /// + /// + ///One or more of the recipient keys cannot be used for encrypting. + ///-or- + ///No recipients were specified. + ///+ /// The specified + ///is not supported by this context. + /// + /// The private key could not be found for + ///. + /// + /// A public key could not be found for one or more of the + ///. + /// + /// The user chose to cancel the password prompt. + /// + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + public MimePart SignAndEncrypt (MailboxAddress signer, DigestAlgorithm digestAlgo, IEnumerablerecipients, Stream content) + { + if (signer == null) + throw new ArgumentNullException (nameof (signer)); + + if (recipients == null) + throw new ArgumentNullException (nameof (recipients)); + + if (content == null) + throw new ArgumentNullException (nameof (content)); + + var key = GetSigningKey (signer); + + return SignAndEncrypt (key, digestAlgo, GetPublicKeys (recipients), content); + } + + /// + /// Cryptographically sign and encrypt the specified content for the specified recipients. + /// + ///+ /// Cryptographically signs and encrypts the specified content for the specified recipients. + /// + ///A new + /// The signer. + /// The digest algorithm to use for signing. + /// The encryption algorithm. + /// The recipients. + /// The content. + ///instance + /// containing the encrypted data. + /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// cannot be used for signing. -or- + ///One or more of the recipient keys cannot be used for encrypting. + ///-or- + ///No recipients were specified. + ///+ /// The specified encryption algorithm is not supported. + /// + ///+ /// The user chose to cancel the password prompt. + /// + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + public MimePart SignAndEncrypt (MailboxAddress signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerablerecipients, Stream content) + { + if (signer == null) + throw new ArgumentNullException (nameof (signer)); + + if (recipients == null) + throw new ArgumentNullException (nameof (recipients)); + + if (content == null) + throw new ArgumentNullException (nameof (content)); + + var key = GetSigningKey (signer); + + return SignAndEncrypt (key, digestAlgo, cipherAlgo, GetPublicKeys (recipients), content); + } + + /// + /// Cryptographically sign and encrypt the specified content for the specified recipients. + /// + ///+ /// Cryptographically signs and encrypts the specified content for the specified recipients. + /// + ///A new + /// The signer. + /// The digest algorithm to use for signing. + /// The encryption algorithm. + /// The recipients. + /// The content. + ///instance + /// containing the encrypted data. + /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// cannot be used for signing. -or- + ///One or more of the recipient keys cannot be used for encrypting. + ///-or- + ///No recipients were specified. + ///+ /// The specified encryption algorithm is not supported. + /// + ///+ /// The user chose to cancel the password prompt. + /// + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + public MimePart SignAndEncrypt (PgpSecretKey signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerablerecipients, Stream content) + { + if (signer == null) + throw new ArgumentNullException (nameof (signer)); + + if (!signer.IsSigningKey) + throw new ArgumentException ("The specified secret key cannot be used for signing.", nameof (signer)); + + if (recipients == null) + throw new ArgumentNullException (nameof (recipients)); + + if (content == null) + throw new ArgumentNullException (nameof (content)); + + var encrypter = new PgpEncryptedDataGenerator (GetSymmetricKeyAlgorithm (cipherAlgo), true); + var hashAlgorithm = GetHashAlgorithm (digestAlgo); + var unique = new HashSet (); + int count = 0; + + foreach (var recipient in recipients) { + if (!recipient.IsEncryptionKey) + throw new ArgumentException ("One or more of the recipient keys cannot be used for encrypting.", nameof (recipients)); + + if (unique.Add (recipient.KeyId)) { + encrypter.AddMethod (recipient); + count++; + } + } + + if (count == 0) + throw new ArgumentException ("No recipients specified.", nameof (recipients)); + + var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib); + + using (var compressed = new MemoryBlockStream ()) { + using (var signed = compresser.Open (compressed)) { + var signatureGenerator = new PgpSignatureGenerator (signer.PublicKey.Algorithm, hashAlgorithm); + signatureGenerator.InitSign (PgpSignature.CanonicalTextDocument, GetPrivateKey (signer)); + var subpacket = new PgpSignatureSubpacketGenerator (); + + foreach (string userId in signer.PublicKey.GetUserIds ()) { + subpacket.SetSignerUserId (false, userId); + break; + } + + signatureGenerator.SetHashedSubpackets (subpacket.Generate ()); + + var onepass = signatureGenerator.GenerateOnePassVersion (false); + onepass.Encode (signed); + + var literalGenerator = new PgpLiteralDataGenerator (); + using (var literal = literalGenerator.Open (signed, 't', "mime.txt", content.Length, DateTime.Now)) { + var buf = ArrayPool .Shared.Rent (BufferLength); + int nread; + + try { + while ((nread = content.Read (buf, 0, BufferLength)) > 0) { + signatureGenerator.Update (buf, 0, nread); + literal.Write (buf, 0, nread); + } + } finally { + ArrayPool .Shared.Return (buf); + } + + literal.Flush (); + } + + var signature = signatureGenerator.Generate (); + signature.Encode (signed); + + signed.Flush (); + } + + compressed.Position = 0; + + var memory = new MemoryBlockStream (); + + using (var armored = new ArmoredOutputStream (memory)) { + armored.SetHeader ("Version", null); + + using (var encrypted = encrypter.Open (armored, compressed.Length)) { + var buf = ArrayPool .Shared.Rent (BufferLength); + int nread; + + try { + while ((nread = compressed.Read (buf, 0, BufferLength)) > 0) + encrypted.Write (buf, 0, nread); + } finally { + ArrayPool .Shared.Return (buf); + } + + encrypted.Flush (); + } + + armored.Flush (); + } + + memory.Position = 0; + + return new MimePart ("application", "octet-stream") { + ContentDisposition = new ContentDisposition (ContentDisposition.Attachment), + Content = new MimeContent (memory) + }; + } + } + + /// + /// Cryptographically sign and encrypt the specified content for the specified recipients. + /// + ///+ /// Cryptographically signs and encrypts the specified content for the specified recipients. + /// + ///A new + /// The signer. + /// The digest algorithm to use for signing. + /// The recipients. + /// The content. + ///instance + /// containing the encrypted data. + /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// cannot be used for signing. -or- + ///One or more of the recipient keys cannot be used for encrypting. + ///-or- + ///No recipients were specified. + ///+ /// The user chose to cancel the password prompt. + /// + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + public MimePart SignAndEncrypt (PgpSecretKey signer, DigestAlgorithm digestAlgo, IEnumerablerecipients, Stream content) + { + return SignAndEncrypt (signer, digestAlgo, defaultAlgorithm, recipients, content); + } + + async Task DecryptToAsync (Stream encryptedData, Stream decryptedData, bool doAsync, CancellationToken cancellationToken) + { + if (encryptedData == null) + throw new ArgumentNullException (nameof (encryptedData)); + + if (decryptedData == null) + throw new ArgumentNullException (nameof (decryptedData)); + + using (var armored = new ArmoredInputStream (encryptedData)) { + var factory = new PgpObjectFactory (armored); + var obj = factory.NextPgpObject (); + var list = obj as PgpEncryptedDataList; + + if (list == null) { + // probably a PgpMarker... + obj = factory.NextPgpObject (); + + list = obj as PgpEncryptedDataList; + + if (list == null) + throw new PgpException ("Unexpected OpenPGP packet."); + } + + PgpPublicKeyEncryptedData encrypted = null; + PrivateKeyNotFoundException pkex = null; + bool hasEncryptedPackets = false; + PgpSecretKey secret = null; + + foreach (PgpEncryptedData data in list.GetEncryptedDataObjects ()) { + if ((encrypted = data as PgpPublicKeyEncryptedData) == null) + continue; + + hasEncryptedPackets = true; + + try { + secret = GetSecretKey (encrypted.KeyId); + break; + } catch (PrivateKeyNotFoundException ex) { + pkex = ex; + } + } + + if (!hasEncryptedPackets) + throw new PgpException ("No encrypted packets found."); + + if (secret == null) + throw pkex; + + factory = new PgpObjectFactory (encrypted.GetDataStream (GetPrivateKey (secret))); + List onepassList = null; + DigitalSignatureCollection signatures; + PgpSignatureList signatureList = null; + PgpCompressedData compressed = null; + var position = decryptedData.Position; + long nwritten = 0; + + obj = factory.NextPgpObject (); + while (obj != null) { + if (obj is PgpCompressedData) { + if (compressed != null) + throw new PgpException ("Recursive compression packets are not supported."); + + compressed = (PgpCompressedData) obj; + factory = new PgpObjectFactory (compressed.GetDataStream ()); + } else if (obj is PgpOnePassSignatureList) { + if (nwritten == 0) { + var onepasses = (PgpOnePassSignatureList) obj; + + onepassList = new List (); + + for (int i = 0; i < onepasses.Count; i++) { + var onepass = onepasses[i]; + PgpPublicKeyRing keyring; + + if (doAsync) + keyring = await GetPublicKeyRingAsync (onepass.KeyId, cancellationToken).ConfigureAwait (false); + else + keyring = GetPublicKeyRing (onepass.KeyId, cancellationToken); + + if (!TryGetPublicKey (keyring, onepass.KeyId, out var key)) { + // too messy, pretend we never found a one-pass signature list + onepassList = null; + break; + } + + onepass.InitVerify (key); + + var signature = new OpenPgpDigitalSignature (keyring, key, onepass) { + PublicKeyAlgorithm = GetPublicKeyAlgorithm (onepass.KeyAlgorithm), + DigestAlgorithm = GetDigestAlgorithm (onepass.HashAlgorithm), + }; + + onepassList.Add (signature); + } + } + } else if (obj is PgpSignatureList) { + signatureList = (PgpSignatureList) obj; + } else if (obj is PgpLiteralData) { + var literal = (PgpLiteralData) obj; + + using (var stream = literal.GetDataStream ()) { + var buf = ArrayPool .Shared.Rent (BufferLength); + int nread; + + try { + while ((nread = stream.Read (buf, 0, BufferLength)) > 0) { + if (onepassList != null) { + // update our one-pass signatures... + for (int index = 0; index < nread; index++) { + byte c = buf[index]; + + for (int i = 0; i < onepassList.Count; i++) { + var pgp = (OpenPgpDigitalSignature) onepassList[i]; + pgp.OnePassSignature.Update (c); + } + } + } + + if (doAsync) + await decryptedData.WriteAsync (buf, 0, nread, cancellationToken).ConfigureAwait (false); + else + decryptedData.Write (buf, 0, nread); + + nwritten += nread; + } + } finally { + ArrayPool .Shared.Return (buf); + } + } + } + + obj = factory.NextPgpObject (); + } + + if (signatureList != null) { + if (onepassList != null && signatureList.Count == onepassList.Count) { + for (int i = 0; i < onepassList.Count; i++) { + var pgp = (OpenPgpDigitalSignature) onepassList[i]; + pgp.CreationDate = signatureList[i].CreationTime; + pgp.Signature = signatureList[i]; + } + + signatures = new DigitalSignatureCollection (onepassList); + } else { + decryptedData.Position = position; + signatures = await GetDigitalSignaturesAsync (signatureList, decryptedData, doAsync, cancellationToken).ConfigureAwait (false); + decryptedData.Position = decryptedData.Length; + } + } else { + signatures = null; + } + + return signatures; + } + } + + /// + /// Decrypt an encrypted stream and extract the digital signers if the content was also signed. + /// + ///+ /// + ///Decrypts an encrypted stream and extracts the digital signers if the content was also signed. + ///If any of the signatures were made with an unrecognized key and + ///is enabled, + /// an attempt will be made to retrieve said key(s). The can be used to cancel + /// key retrieval. The list of digital signatures if the data was both signed and encrypted; otherwise, + /// The encrypted data. + /// The stream to write the decrypted data to. + /// The cancellation token. + ///null .+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// The private key could not be found to decrypt the stream. + /// + ///+ /// + ///The user chose to cancel the password prompt. + ///-or- + ///The operation was cancelled via the cancellation token. + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + ///+ /// An OpenPGP error occurred. + /// + public DigitalSignatureCollection DecryptTo (Stream encryptedData, Stream decryptedData, CancellationToken cancellationToken = default (CancellationToken)) + { + return DecryptToAsync (encryptedData, decryptedData, false, cancellationToken).GetAwaiter ().GetResult (); + } + + ///+ /// Asynchronously decrypt an encrypted stream and extract the digital signers if the content was also signed. + /// + ///+ /// + ///Decrypts an encrypted stream and extracts the digital signers if the content was also signed. + ///If any of the signatures were made with an unrecognized key and + ///is enabled, + /// an attempt will be made to retrieve said key(s). The can be used to cancel + /// key retrieval. The list of digital signatures if the data was both signed and encrypted; otherwise, + /// The encrypted data. + /// The stream to write the decrypted data to. + /// The cancellation token. + ///null .+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// The private key could not be found to decrypt the stream. + /// + ///+ /// + ///The user chose to cancel the password prompt. + ///-or- + ///The operation was cancelled via the cancellation token. + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + ///+ /// An OpenPGP error occurred. + /// + public TaskDecryptToAsync (Stream encryptedData, Stream decryptedData, CancellationToken cancellationToken = default (CancellationToken)) + { + return DecryptToAsync (encryptedData, decryptedData, true, cancellationToken); + } + + /// + /// Decrypts the specified encryptedData and extracts the digital signers if the content was also signed. + /// + ///+ /// Decrypts the specified encryptedData and extracts the digital signers if the content was also signed. + /// + ///The decrypted + /// The encrypted data. + /// A list of digital signatures if the data was both signed and encrypted. + /// The cancellation token. + ///. + /// + ///is null . + ///+ /// The private key could not be found to decrypt the stream. + /// + ///+ /// + ///The user chose to cancel the password prompt. + ///-or- + ///The operation was cancelled via the cancellation token. + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + ///+ /// An OpenPGP error occurred. + /// + public MimeEntity Decrypt (Stream encryptedData, out DigitalSignatureCollection signatures, CancellationToken cancellationToken = default (CancellationToken)) + { + using (var decryptedData = new MemoryBlockStream ()) { + signatures = DecryptTo (encryptedData, decryptedData, cancellationToken); + decryptedData.Position = 0; + + return MimeEntity.Load (decryptedData, cancellationToken); + } + } + + ///+ /// Decrypts the specified encryptedData. + /// + ///+ /// Decrypts the specified encryptedData. + /// + ///The decrypted + /// The encrypted data. + /// The cancellation token. + ///. + /// + ///is null . + ///+ /// The private key could not be found to decrypt the stream. + /// + ///+ /// + ///The user chose to cancel the password prompt. + ///-or- + ///The operation was cancelled via the cancellation token. + ///+ /// 3 bad attempts were made to unlock the secret key. + /// + ///+ /// An OpenPGP error occurred. + /// + public override MimeEntity Decrypt (Stream encryptedData, CancellationToken cancellationToken = default (CancellationToken)) + { + using (var decryptedData = new MemoryBlockStream ()) { + DecryptTo (encryptedData, decryptedData, cancellationToken); + decryptedData.Position = 0; + + return MimeEntity.Load (decryptedData, cancellationToken); + } + } + + ///+ /// Import the specified public keyring bundle. + /// + ///+ /// Imports the specified public keyring bundle. + /// + /// THe bundle of public keyrings to import. + public abstract void Import (PgpPublicKeyRingBundle bundle); + + ///+ /// Releases all resources used by the + ///object. + /// Call + protected override void Dispose (bool disposing) + { + if (disposing && client != null) { + client.Dispose (); + client = null; + } + + base.Dispose (disposing); + } + } +} diff --git a/MimeKit/Cryptography/OpenPgpDataType.cs b/MimeKit/Cryptography/OpenPgpDataType.cs index d0b0b7908c..55cdc30718 100644 --- a/MimeKit/Cryptography/OpenPgpDataType.cs +++ b/MimeKit/Cryptography/OpenPgpDataType.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastwhen you are finished using the . The + /// method leaves the in an unusable state. After + /// calling , you must release all references to the so + /// the garbage collector can reclaim the memory that the was occupying. // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Cryptography/OpenPgpDetectionFilter.cs b/MimeKit/Cryptography/OpenPgpDetectionFilter.cs index 0ea3f0dd9c..ea9c7574c5 100644 --- a/MimeKit/Cryptography/OpenPgpDetectionFilter.cs +++ b/MimeKit/Cryptography/OpenPgpDetectionFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -84,7 +84,7 @@ public OpenPgpMarker (string marker, OpenPgpState initial, OpenPgpState detected bool midline; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///. @@ -300,10 +300,7 @@ protected override byte[] Filter (byte[] input, int startIndex, int length, out return input; } - outputLength = index - outputIndex; - position += index - startIndex; - - return input; + break; } index++; diff --git a/MimeKit/Cryptography/OpenPgpDigitalCertificate.cs b/MimeKit/Cryptography/OpenPgpDigitalCertificate.cs index 60102de14e..bba0c865a5 100644 --- a/MimeKit/Cryptography/OpenPgpDigitalCertificate.cs +++ b/MimeKit/Cryptography/OpenPgpDigitalCertificate.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -139,7 +139,11 @@ public DateTime CreationDate { /// The expiration date. public DateTime ExpirationDate { - get { return CreationDate.AddSeconds ((double) PublicKey.GetValidSeconds ()); } + get { + long seconds = PublicKey.GetValidSeconds (); + + return seconds > 0 ? CreationDate.AddSeconds ((double) seconds) : DateTime.MaxValue; + } } ///diff --git a/MimeKit/Cryptography/OpenPgpDigitalSignature.cs b/MimeKit/Cryptography/OpenPgpDigitalSignature.cs index 74efaa9d2e..f74e079af3 100644 --- a/MimeKit/Cryptography/OpenPgpDigitalSignature.cs +++ b/MimeKit/Cryptography/OpenPgpDigitalSignature.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,6 +25,7 @@ // using System; +using System.Globalization; using Org.BouncyCastle.Bcpg.OpenPgp; @@ -112,7 +113,7 @@ public DateTime CreationDate { /// /// Verifies the digital signature. /// - ///+ /// true if the signature is valid; otherwisefalse ./// true if the signature is valid; otherwise,false ./// An error verifying the signature has occurred. /// @@ -125,7 +126,7 @@ public bool Verify () throw vex; if (SignerCertificate == null) { - var message = string.Format ("Failed to verify digital signature: no public key found for {0:X8}", (int) Signature.KeyId); + var message = string.Format (CultureInfo.InvariantCulture, "Failed to verify digital signature: no public key found for {0:X8}", (int) Signature.KeyId); vex = new DigitalSignatureVerifyException (Signature.KeyId, message); throw vex; } @@ -143,6 +144,22 @@ public bool Verify () } } + ///+ /// Verifies the digital signature. + /// + ///+ /// Verifies the digital signature. + /// + /// This option is ignored for OpenPGP digital signatures. + ///+ /// true if the signature is valid; otherwise,false .+ /// An error verifying the signature has occurred. + /// + public bool Verify (bool verifySignatureOnly) + { + return Verify (); + } + #endregion } } diff --git a/MimeKit/Cryptography/OpenPgpKeyCertification.cs b/MimeKit/Cryptography/OpenPgpKeyCertification.cs index b0b84a2ca6..656315b5c7 100644 --- a/MimeKit/Cryptography/OpenPgpKeyCertification.cs +++ b/MimeKit/Cryptography/OpenPgpKeyCertification.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Cryptography/PrivateKeyNotFoundException.cs b/MimeKit/Cryptography/PrivateKeyNotFoundException.cs index a297b4b554..b4bf7bb259 100644 --- a/MimeKit/Cryptography/PrivateKeyNotFoundException.cs +++ b/MimeKit/Cryptography/PrivateKeyNotFoundException.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -45,7 +45,7 @@ public class PrivateKeyNotFoundException : Exception { #if SERIALIZABLE /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -62,7 +62,7 @@ protected PrivateKeyNotFoundException (SerializationInfo info, StreamingContext #endif /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -81,7 +81,7 @@ public PrivateKeyNotFoundException (MailboxAddress mailbox, string message) : ba } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -100,7 +100,7 @@ public PrivateKeyNotFoundException (string keyid, string message) : base (messag } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -132,12 +132,9 @@ public PrivateKeyNotFoundException (long keyid, string message) : base (message) [SecurityCritical] public override void GetObjectData (SerializationInfo info, StreamingContext context) { - if (info == null) - throw new ArgumentNullException (nameof (info)); + base.GetObjectData (info, context); info.AddValue ("KeyId", KeyId); - - base.GetObjectData (info, context); } #endif diff --git a/MimeKit/Cryptography/PublicKeyAlgorithm.cs b/MimeKit/Cryptography/PublicKeyAlgorithm.cs index 7d174ab6ec..8ece4b6096 100644 --- a/MimeKit/Cryptography/PublicKeyAlgorithm.cs +++ b/MimeKit/Cryptography/PublicKeyAlgorithm.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Cryptography/PublicKeyNotFoundException.cs b/MimeKit/Cryptography/PublicKeyNotFoundException.cs index e1c106cc8b..2221f8f000 100644 --- a/MimeKit/Cryptography/PublicKeyNotFoundException.cs +++ b/MimeKit/Cryptography/PublicKeyNotFoundException.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -45,7 +45,7 @@ public class PublicKeyNotFoundException : Exception { #if SERIALIZABLE /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -66,7 +66,7 @@ protected PublicKeyNotFoundException (SerializationInfo info, StreamingContext c #endif /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -95,12 +95,9 @@ public PublicKeyNotFoundException (MailboxAddress mailbox, string message) : bas [SecurityCritical] public override void GetObjectData (SerializationInfo info, StreamingContext context) { - if (info == null) - throw new ArgumentNullException (nameof (info)); + base.GetObjectData (info, context); info.AddValue ("Mailbox", Mailbox.ToString (true)); - - base.GetObjectData (info, context); } #endif diff --git a/MimeKit/Cryptography/RsaEncryptionPadding.cs b/MimeKit/Cryptography/RsaEncryptionPadding.cs new file mode 100644 index 0000000000..e009605a8e --- /dev/null +++ b/MimeKit/Cryptography/RsaEncryptionPadding.cs @@ -0,0 +1,251 @@ +// +// RsaEncryptionPadding.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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; + +#if NETCOREAPP3_0 +using System.Security.Cryptography; +#endif + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; + +namespace MimeKit.Cryptography { + /// + /// The RSA encryption padding schemes and parameters used by S/MIME. + /// + ///+ /// The RSA encryption padding schemes and parameters used by S/MIME as described in + /// rfc8017. + /// + public sealed class RsaEncryptionPadding : IEquatable+ { + /// + /// The PKCS #1 v1.5 encryption padding. + /// + public static readonly RsaEncryptionPadding Pkcs1 = new RsaEncryptionPadding (RsaEncryptionPaddingScheme.Pkcs1, DigestAlgorithm.None); + + ///+ /// The Optimal Asymmetric Encryption Padding (OAEP) scheme using the default (SHA-1) hash algorithm. + /// + public static readonly RsaEncryptionPadding OaepSha1 = new RsaEncryptionPadding (RsaEncryptionPaddingScheme.Oaep, DigestAlgorithm.Sha1); + + ///+ /// The Optimal Asymmetric Encryption Padding (OAEP) scheme using the SHA-256 hash algorithm. + /// + public static readonly RsaEncryptionPadding OaepSha256 = new RsaEncryptionPadding (RsaEncryptionPaddingScheme.Oaep, DigestAlgorithm.Sha256); + + ///+ /// The Optimal Asymmetric Encryption Padding (OAEP) scheme using the SHA-384 hash algorithm. + /// + public static readonly RsaEncryptionPadding OaepSha384 = new RsaEncryptionPadding (RsaEncryptionPaddingScheme.Oaep, DigestAlgorithm.Sha384); + + ///+ /// The Optimal Asymmetric Encryption Padding (OAEP) scheme using the SHA-512 hash algorithm. + /// + public static readonly RsaEncryptionPadding OaepSha512 = new RsaEncryptionPadding (RsaEncryptionPaddingScheme.Oaep, DigestAlgorithm.Sha512); + + RsaEncryptionPadding (RsaEncryptionPaddingScheme scheme, DigestAlgorithm oaepHashAlgorithm) + { + OaepHashAlgorithm = oaepHashAlgorithm; + Scheme = scheme; + } + + ///+ /// Get the RSA encryption padding scheme. + /// + ///+ /// Gets the RSA encryption padding scheme. + /// + ///The RSA encryption padding scheme. + public RsaEncryptionPaddingScheme Scheme { + get; private set; + } + + ///+ /// Get the hash algorithm used for RSAES-OAEP padding. + /// + ///+ /// Gets the hash algorithm used for RSAES-OAEP padding. + /// + public DigestAlgorithm OaepHashAlgorithm { + get; private set; + } + + ///+ /// Determines whether the specified + ///is equal to the current . + /// + /// Compares two RSA encryption paddings to determine if they are identical or not. + /// + /// Theto compare with the current . + /// + public bool Equals (RsaEncryptionPadding other) + { + if (other == null) + return false; + + return other.Scheme == Scheme && other.OaepHashAlgorithm == OaepHashAlgorithm; + } + + /// true if the specifiedis equal to the current + /// ; otherwise, false .+ /// Determines whether the specified object is equal to the current object. + /// + ///+ /// The type of comparison between the current instance and the + /// The object to compare with the current object. + ///parameter depends on whether + /// the current instance is a reference type or a value type. + /// + public override bool Equals (object obj) + { + return Equals (obj as RsaEncryptionPadding); + } + + /// true if the specified object is equal to the current object; otherwise,false .+ /// Returns the hash code for this instance. + /// + ///+ /// Returns the hash code for this instance. + /// + ///A hash code for the current object. + public override int GetHashCode () + { + int hash = Scheme.GetHashCode (); + + return ((hash << 5) + hash) ^ OaepHashAlgorithm.GetHashCode (); + } + + ///+ /// Returns a + ///that represents the current + /// . + /// + /// Creates a string-representation of the + ///. + /// A + public override string ToString () + { + return Scheme == RsaEncryptionPaddingScheme.Pkcs1 ? "Pkcs1" : "Oaep" + OaepHashAlgorithm.ToString (); + } + + ///that represents the current + /// . + /// Compare two + ///objects for equality. + /// + /// Compares two + /// The first object to compare. + /// The second object to compare. + ///objects for equality. + /// + public static bool operator == (RsaEncryptionPadding left, RsaEncryptionPadding right) + { + if (ReferenceEquals (left, null)) + return ReferenceEquals (right, null); + + return left.Equals (right); + } + + /// true ifand are equal; otherwise, false .+ /// Compare two + ///objects for inequality. + /// + /// Compares two + /// The first object to compare. + /// The second object to compare. + ///objects for inequality. + /// + public static bool operator != (RsaEncryptionPadding left, RsaEncryptionPadding right) + { + return !(left == right); + } + + /// true ifand are unequal; otherwise, false .+ /// Create a new + ///using and the specified hash algorithm. + /// + /// Creates a new + /// The hash algorithm. + ///using and the specified hash algorithm. + /// An + ///using and the specified hash algorithm. + /// The + public static RsaEncryptionPadding CreateOaep (DigestAlgorithm hashAlgorithm) + { + switch (hashAlgorithm) { + case DigestAlgorithm.Sha1: return OaepSha1; + case DigestAlgorithm.Sha256: return OaepSha256; + case DigestAlgorithm.Sha384: return OaepSha384; + case DigestAlgorithm.Sha512: return OaepSha512; + default: throw new NotSupportedException ($"The {hashAlgorithm} hash algorithm is not supported."); + } + } + + internal RsaesOaepParameters GetRsaesOaepParameters () + { + if (OaepHashAlgorithm == DigestAlgorithm.Sha1) + return new RsaesOaepParameters (); + + var oid = SecureMimeContext.GetDigestOid (OaepHashAlgorithm); + var hashAlgorithm = new AlgorithmIdentifier (new DerObjectIdentifier (oid), DerNull.Instance); + var maskGenFunction = new AlgorithmIdentifier (PkcsObjectIdentifiers.IdMgf1, hashAlgorithm); + + return new RsaesOaepParameters (hashAlgorithm, maskGenFunction, RsaesOaepParameters.DefaultPSourceAlgorithm); + } + + internal AlgorithmIdentifier GetAlgorithmIdentifier () + { + if (Scheme != RsaEncryptionPaddingScheme.Oaep) + return null; + + return new AlgorithmIdentifier (PkcsObjectIdentifiers.IdRsaesOaep, GetRsaesOaepParameters ()); + } + +#if NETCOREAPP3_0 + internal RSAEncryptionPadding AsRSAEncryptionPadding () + { + switch (Scheme) { + case RsaEncryptionPaddingScheme.Oaep: + switch (OaepHashAlgorithm) { + case DigestAlgorithm.Sha1: return RSAEncryptionPadding.OaepSHA1; + case DigestAlgorithm.Sha256: return RSAEncryptionPadding.OaepSHA256; + case DigestAlgorithm.Sha384: return RSAEncryptionPadding.OaepSHA384; + case DigestAlgorithm.Sha512: return RSAEncryptionPadding.OaepSHA512; + default: return null; + } + case RsaEncryptionPaddingScheme.Pkcs1: + return RSAEncryptionPadding.Pkcs1; + default: + return null; + } + } +#endif + } +} diff --git a/MimeKit/Cryptography/RsaEncryptionPaddingScheme.cs b/MimeKit/Cryptography/RsaEncryptionPaddingScheme.cs new file mode 100644 index 0000000000..c867bc3778 --- /dev/null +++ b/MimeKit/Cryptography/RsaEncryptionPaddingScheme.cs @@ -0,0 +1,47 @@ +// +// RsaEncryptionPaddingScheme.cs +// +// Author: Jeffrey Stedfastis not supported. + /// +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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. +// + +namespace MimeKit.Cryptography { + /// + /// The RSA encryption padding schemes used by S/MIME. + /// + ///+ /// The RSA encryption padding schemes used by S/MIME as described in + /// rfc8017. + /// + public enum RsaEncryptionPaddingScheme + { + ///+ /// The PKCS #1 v1.5 encryption padding scheme. + /// + Pkcs1, + + ///+ /// The Optimal Asymmetric Encryption Padding (OAEP) scheme. + /// + Oaep + } +} diff --git a/MimeKit/Cryptography/RsaSignaturePadding.cs b/MimeKit/Cryptography/RsaSignaturePadding.cs new file mode 100644 index 0000000000..dce313236e --- /dev/null +++ b/MimeKit/Cryptography/RsaSignaturePadding.cs @@ -0,0 +1,153 @@ +// +// RsaSignaturePadding.cs +// +// Author: Jeffrey Stedfast+// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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; + +namespace MimeKit.Cryptography { + /// + /// The RSA signature padding schemes and parameters used by S/MIME. + /// + ///+ /// The RSA signature padding schemes and parameters used by S/MIME as described in + /// rfc8017. + /// + public sealed class RsaSignaturePadding : IEquatable+ { + /// + /// The PKCS #1 v1.5 signature padding. + /// + public static readonly RsaSignaturePadding Pkcs1 = new RsaSignaturePadding (RsaSignaturePaddingScheme.Pkcs1); + + ///+ /// The Probibilistic Signature Scheme (PSS) padding. + /// + public static readonly RsaSignaturePadding Pss = new RsaSignaturePadding (RsaSignaturePaddingScheme.Pss); + + RsaSignaturePadding (RsaSignaturePaddingScheme scheme) + { + Scheme = scheme; + } + + ///+ /// Get the RSA signature padding scheme. + /// + ///+ /// Gets the RSA signature padding scheme. + /// + ///The RSA signature padding scheme. + public RsaSignaturePaddingScheme Scheme { + get; private set; + } + + ///+ /// Determines whether the specified + ///is equal to the current . + /// + /// Compares two RSA Signature paddings to determine if they are identical or not. + /// + /// Theto compare with the current . + /// + public bool Equals (RsaSignaturePadding other) + { + if (other == null) + return false; + + return other.Scheme == Scheme; + } + + /// true if the specifiedis equal to the current + /// ; otherwise, false .+ /// Determines whether the specified object is equal to the current object. + /// + ///+ /// The type of comparison between the current instance and the + /// The object to compare with the current object. + ///parameter depends on whether + /// the current instance is a reference type or a value type. + /// + public override bool Equals (object obj) + { + return Equals (obj as RsaSignaturePadding); + } + + /// true if the specified object is equal to the current object; otherwise,false .+ /// Returns the hash code for this instance. + /// + ///+ /// Returns the hash code for this instance. + /// + ///A hash code for the current object. + public override int GetHashCode () + { + return Scheme.GetHashCode (); + } + + ///+ /// Returns a + ///that represents the current + /// . + /// + /// Creates a string-representation of the + ///. + /// A + public override string ToString () + { + return Scheme == RsaSignaturePaddingScheme.Pkcs1 ? "Pkcs1" : "Pss"; + } + + ///that represents the current + /// . + /// Compare two + ///objects for equality. + /// + /// Compares two + /// The first object to compare. + /// The second object to compare. + ///objects for equality. + /// + public static bool operator == (RsaSignaturePadding left, RsaSignaturePadding right) + { + if (ReferenceEquals (left, null)) + return ReferenceEquals (right, null); + + return left.Equals (right); + } + + /// true ifand are equal; otherwise, false .+ /// Compare two + ///objects for inequality. + /// + /// Compares two + /// The first object to compare. + /// The second object to compare. + ///objects for inequality. + /// + public static bool operator != (RsaSignaturePadding left, RsaSignaturePadding right) + { + return !(left == right); + } + } +} diff --git a/MimeKit/Cryptography/RsaSignaturePaddingScheme.cs b/MimeKit/Cryptography/RsaSignaturePaddingScheme.cs new file mode 100644 index 0000000000..c408c47877 --- /dev/null +++ b/MimeKit/Cryptography/RsaSignaturePaddingScheme.cs @@ -0,0 +1,47 @@ +// +// RsaSignaturePaddingScheme.cs +// +// Author: Jeffrey Stedfast true ifand are unequal; otherwise, false .+// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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. +// + +namespace MimeKit.Cryptography { + /// + /// The RSA signature padding schemes used by S/MIME. + /// + ///+ /// The RSA signature padding schemes used by S/MIME as described in + /// rfc8017. + /// + public enum RsaSignaturePaddingScheme + { + ///+ /// The PKCS #1 v1.5 signature padding scheme. + /// + Pkcs1, + + ///+ /// The Probibilistic Signature Scheme (PSS). + /// + Pss + } +} diff --git a/MimeKit/Cryptography/SQLServerCertificateDatabase.cs b/MimeKit/Cryptography/SQLServerCertificateDatabase.cs new file mode 100644 index 0000000000..806c94285d --- /dev/null +++ b/MimeKit/Cryptography/SQLServerCertificateDatabase.cs @@ -0,0 +1,297 @@ +// +// SQLServerCertificateDatabase.cs +// +// Author: Rob Blackin+// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.Text; +using System.Data; +using System.Data.Common; +using System.Collections.Generic; + +using MimeKit.Utils; + +using Org.BouncyCastle.X509; + +namespace MimeKit.Cryptography { + /// + /// An X.509 certificate database built on SQL Server. + /// + ///+ /// + public class SQLServerCertificateDatabase : SqlCertificateDatabase + { + ///An X.509 certificate database is used for storing certificates, metdata related to the certificates + /// (such as encryption algorithms supported by the associated client), certificate revocation lists (CRLs), + /// and private keys. + ///This particular database uses SQL Server to store the data. + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The SQL Server connection. + /// The password used for encrypting and decrypting the private keys. + ///using the provided SQL Server database connection. + /// + /// + public SQLServerCertificateDatabase (DbConnection connection, string password) : base (connection, password) + { + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Adds a column to a table. + /// + ///+ /// Adds a column to a table. + /// + /// The. + /// The table. + /// The column to add. + protected override void AddTableColumn (DbConnection connection, DataTable table, DataColumn column) + { + var statement = new StringBuilder ("ALTER TABLE "); + int primaryKeys = table.PrimaryKey?.Length ?? 0; + + statement.Append (table.TableName); + statement.Append (" ADD COLUMN "); + Build (statement, table, column, ref primaryKeys); + + using (var command = connection.CreateCommand ()) { + command.CommandText = statement.ToString (); + command.CommandType = CommandType.Text; + command.ExecuteNonQuery (); + } + } + + /// + /// Create a table. + /// + ///+ /// Creates the specified table. + /// + /// The. + /// The table. + protected override void CreateTable (DbConnection connection, DataTable table) + { + var statement = new StringBuilder ($"if not exists (select * from sysobjects where name='{table.TableName}' and xtype='U') "); + int primaryKeys = 0; + + statement.Append ($"Create table {table.TableName} ("); + + foreach (DataColumn column in table.Columns) { + Build (statement, table, column, ref primaryKeys); + statement.Append (", "); + } + + if (table.Columns.Count > 0) + statement.Length -= 2; + + statement.Append (')'); + + using (var command = connection.CreateCommand ()) { + command.CommandText = statement.ToString (); + command.CommandType = CommandType.Text; + command.ExecuteNonQuery (); + } + } + + static void Build (StringBuilder statement, DataTable table, DataColumn column, ref int primaryKeys) + { + statement.Append (column.ColumnName); + statement.Append (' '); + + if (column.DataType == typeof (long) || column.DataType == typeof (int)) { + if (column.AutoIncrement) + statement.Append ("int identity(1,1)"); + else if (column.DataType == typeof (long)) + statement.Append ("DateTime"); + else + statement.Append ("int"); + } else if (column.DataType == typeof (bool)) { + statement.Append ("bit"); + } else if (column.DataType == typeof (byte[])) { + statement.Append ($"varbinary(4096)"); + } else if (column.DataType == typeof (string)) { + statement.Append ("varchar(256)"); + } else { + throw new NotImplementedException (); + } + + if (table != null && table.PrimaryKey != null && primaryKeys < table.PrimaryKey.Length) { + for (int i = 0; i < table.PrimaryKey.Length; i++) { + if (column == table.PrimaryKey[i]) { + statement.Append (" PRIMARY KEY Clustered"); + primaryKeys++; + break; + } + } + } + + if (!column.AllowDBNull) + statement.Append (" NOT NULL"); + } + + /// + /// Gets the columns for the specified table. + /// + ///+ /// Gets the list of columns for the specified table. + /// + /// The. + /// The name of the table. + /// The list of columns. + protected override IListGetTableColumns (DbConnection connection, string tableName) + { + using (var command = connection.CreateCommand ()) { + command.CommandText = $"select top 1 * from {tableName}"; + using (var reader = command.ExecuteReader ()) { + var columns = new List (); + var table = reader.GetSchemaTable (); + + foreach (DataRow row in table.Rows) + columns.Add (new DataColumn { ColumnName = row.Field ("ColumnName") }); + + return columns; + } + } + } + + /// + /// Creates an index for faster table lookups. + /// + ///+ /// Creates an index for faster table lookups. + /// + /// The. + /// The name of the table. + /// The names of the columns to index. + protected override void CreateIndex (DbConnection connection, string tableName, string[] columnNames) + { + var indexName = GetIndexName (tableName, columnNames); + var query = string.Format ("IF NOT EXISTS (Select 8 from sys.indexes where name='{0}' and object_id=OBJECT_ID('{1}')) CREATE INDEX {0} ON {1}({2})", indexName, tableName, string.Join (", ", columnNames)); + + using (var command = connection.CreateCommand ()) { + command.CommandText = query; + command.ExecuteNonQuery (); + } + } + + /// + /// Removes an index that is no longer needed. + /// + ///+ /// Removes an index that is no longer needed. + /// + /// The. + /// The name of the table. + /// The names of the columns that were indexed. + protected override void RemoveIndex (DbConnection connection, string tableName, string[] columnNames) + { + var indexName = GetIndexName (tableName, columnNames); + var query = string.Format ("IF EXISTS (Select 8 from sys.indexes where name='{0}' and object_id=OBJECT_ID('{1}')) DROP INDEX {0} ON {1}", indexName, tableName); + + using (var command = connection.CreateCommand ()) { + command.CommandText = query; + command.ExecuteNonQuery (); + } + } + + /// + /// Gets the database command to select the record matching the specified certificate. + /// + ///+ /// Gets the database command to select the record matching the specified certificate. + /// + ///The database command. + /// The certificate. + /// The fields to return. + protected override DbCommand GetSelectCommand (X509Certificate certificate, X509CertificateRecordFields fields) + { + var fingerprint = certificate.GetFingerprint ().ToLowerInvariant (); + var serialNumber = certificate.SerialNumber.ToString (); + var issuerName = certificate.IssuerDN.ToString (); + var command = Connection.CreateCommand (); + var query = CreateSelectQuery (fields).Replace ("SELECT", "SELECT top 1"); + + // FIXME: Is this really the best way to query for an exact match of a certificate? + query = query.Append (" WHERE ISSUERNAME = @ISSUERNAME AND SERIALNUMBER = @SERIALNUMBER AND FINGERPRINT = @FINGERPRINT"); + command.AddParameterWithValue ("@ISSUERNAME", issuerName); + command.AddParameterWithValue ("@SERIALNUMBER", serialNumber); + command.AddParameterWithValue ("@FINGERPRINT", fingerprint); + + command.CommandText = query.ToString (); + command.CommandType = CommandType.Text; + + return command; + } + + ///+ /// Gets the database command to insert the specified certificate record. + /// + ///+ /// Gets the database command to insert the specified certificate record. + /// + ///The database command. + /// The certificate record. + protected override DbCommand GetInsertCommand (X509CertificateRecord record) + { + var statement = new StringBuilder ("INSERT INTO CERTIFICATES("); + var variables = new StringBuilder ("VALUES("); + var command = Connection.CreateCommand (); + var columns = CertificatesTable.Columns; + + for (int i = 1; i < columns.Count; i++) { + if (i > 1) { + statement.Append (", "); + variables.Append (", "); + } + + var value = GetValue (record, columns[i].ColumnName); + if (value is DateTime dateTime && dateTime < DateUtils.UnixEpoch) + value = DateUtils.UnixEpoch; + + if (columns[i].ColumnName == "PRIVATEKEY" && value is DBNull) + value = new byte[0]; + + var variable = "@" + columns[i]; + + command.AddParameterWithValue (variable, value); + statement.Append (columns[i]); + variables.Append (variable); + } + + statement.Append (')'); + variables.Append (')'); + + command.CommandText = statement + " " + variables; + command.CommandType = CommandType.Text; + + return command; + } + } +} diff --git a/MimeKit/Cryptography/SecureMailboxAddress.cs b/MimeKit/Cryptography/SecureMailboxAddress.cs index 0dec732e15..c93bd8dc90 100644 --- a/MimeKit/Cryptography/SecureMailboxAddress.cs +++ b/MimeKit/Cryptography/SecureMailboxAddress.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,6 @@ using System.Text; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - using MimeKit.Utils; namespace MimeKit.Cryptography { @@ -48,7 +44,7 @@ namespace MimeKit.Cryptography { public class SecureMailboxAddress : MailboxAddress { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified fingerprint. @@ -75,7 +71,7 @@ public SecureMailboxAddress (Encoding encoding, string name, IEnumerable } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified fingerprint. @@ -99,7 +95,7 @@ public SecureMailboxAddress (string name, IEnumerable route, string addr } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified fingerprint. @@ -114,6 +110,7 @@ public SecureMailboxAddress (string name, IEnumerable route, string addr /// -or- ////// is null .route, string address, string fingerprint) : base (route, address) { ValidateFingerprint (fingerprint); @@ -122,7 +119,7 @@ public SecureMailboxAddress (IEnumerable route, string address, string f } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified fingerprint. @@ -146,7 +143,7 @@ public SecureMailboxAddress (Encoding encoding, string name, string address, str } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified fingerprint. @@ -167,7 +164,7 @@ public SecureMailboxAddress (string name, string address, string fingerprint) : } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new @@ -186,6 +183,7 @@ public SecureMailboxAddress (string name, string address, string fingerprint) : ///with the specified address. -or- ////// is null ./// Gets the fingerprint of the certificate and/or key to use for signing or encrypting. - /// ///- /// /// /// A fingerprint is a SHA-1 hash of the raw certificate data and is often used /// as a unique identifier for a particular certificate in a certificate store. /// + ///+ /// /// The fingerprint of the certificate. public string Fingerprint { get; private set; diff --git a/MimeKit/Cryptography/SecureMimeContext.cs b/MimeKit/Cryptography/SecureMimeContext.cs index 171d0e36cf..2aa2978f6b 100644 --- a/MimeKit/Cryptography/SecureMimeContext.cs +++ b/MimeKit/Cryptography/SecureMimeContext.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -31,10 +31,11 @@ using Org.BouncyCastle.Cms; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.X509; -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Asn1.Smime; using Org.BouncyCastle.Asn1.Ntt; using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Smime; namespace MimeKit.Cryptography { /// @@ -47,13 +48,14 @@ namespace MimeKit.Cryptography { /// public abstract class SecureMimeContext : CryptographyContext { + static readonly string[] ProtocolSubtypes = { "pkcs7-signature", "pkcs7-mime", "pkcs7-keys", "x-pkcs7-signature", "x-pkcs7-mime", "x-pkcs7-keys" }; internal const X509KeyUsageFlags DigitalSignatureKeyUsageFlags = X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation; internal static readonly int EncryptionAlgorithmCount = Enum.GetValues (typeof (EncryptionAlgorithm)).Length; internal static readonly DerObjectIdentifier Blowfish = new DerObjectIdentifier ("1.3.6.1.4.1.3029.1.2"); internal static readonly DerObjectIdentifier Twofish = new DerObjectIdentifier ("1.3.6.1.4.1.25258.3.3"); /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Enables the following encryption algorithms by default: @@ -159,14 +161,21 @@ public override bool Supports (string protocol) if (protocol == null) throw new ArgumentNullException (nameof (protocol)); - var type = protocol.ToLowerInvariant ().Split (new [] { '/' }); - if (type.Length != 2 || type[0] != "application") + if (!protocol.StartsWith ("application/", StringComparison.OrdinalIgnoreCase)) return false; - if (type[1].StartsWith ("x-", StringComparison.Ordinal)) - type[1] = type[1].Substring (2); + int startIndex = "application/".Length; + int subtypeLength = protocol.Length - startIndex; - return type[1] == "pkcs7-signature" || type[1] == "pkcs7-mime" || type[1] == "pkcs7-keys"; + for (int i = 0; i < ProtocolSubtypes.Length; i++) { + if (subtypeLength != ProtocolSubtypes[i].Length) + continue; + + if (string.Compare (protocol, startIndex, ProtocolSubtypes[i], 0, subtypeLength, StringComparison.OrdinalIgnoreCase) == 0) + return true; + } + + return false; } ///@@ -423,7 +432,7 @@ protected virtual EncryptionAlgorithm GetPreferredEncryptionAlgorithm (CmsRecipi /// /// Compresses the specified stream. /// - ///A new instance + /// A new /// The stream to compress. ///instance /// containing the compressed content. @@ -500,7 +509,7 @@ public virtual void DecompressTo (Stream stream, Stream output) content.ContentStream.CopyTo (output, 4096); } - internal SmimeCapabilitiesAttribute GetSecureMimeCapabilitiesAttribute () + internal SmimeCapabilitiesAttribute GetSecureMimeCapabilitiesAttribute (bool includeRsaesOaep) { var capabilities = new SmimeCapabilityVector (); @@ -560,6 +569,13 @@ internal SmimeCapabilitiesAttribute GetSecureMimeCapabilitiesAttribute () } } + if (includeRsaesOaep) { + capabilities.AddCapability (PkcsObjectIdentifiers.IdRsaesOaep, RsaEncryptionPadding.OaepSha1.GetRsaesOaepParameters ()); + capabilities.AddCapability (PkcsObjectIdentifiers.IdRsaesOaep, RsaEncryptionPadding.OaepSha256.GetRsaesOaepParameters ()); + capabilities.AddCapability (PkcsObjectIdentifiers.IdRsaesOaep, RsaEncryptionPadding.OaepSha384.GetRsaesOaepParameters ()); + capabilities.AddCapability (PkcsObjectIdentifiers.IdRsaesOaep, RsaEncryptionPadding.OaepSha512.GetRsaesOaepParameters ()); + } + return new SmimeCapabilitiesAttribute (capabilities); } @@ -569,7 +585,7 @@ internal SmimeCapabilitiesAttribute GetSecureMimeCapabilitiesAttribute () /// public abstract void Import (Stream stream, string password); + ////// Cryptographically signs and encapsulates the content using the specified signer. /// - ///A new instance + /// A new /// The signer. /// The content. @@ -586,7 +602,7 @@ internal SmimeCapabilitiesAttribute GetSecureMimeCapabilitiesAttribute () ///instance /// containing the detached signature data. /// Cryptographically signs and encapsulates the content using the specified signer and digest algorithm. /// - ///A new instance + /// A new /// The signer. /// The digest algorithm to use for signing. @@ -616,7 +632,7 @@ internal SmimeCapabilitiesAttribute GetSecureMimeCapabilitiesAttribute () ///instance /// containing the detached signature data. /// Cryptographically signs the content using the specified signer. /// - ///A new instance + /// A new /// The signer. /// The content. @@ -675,7 +691,7 @@ internal SmimeCapabilitiesAttribute GetSecureMimeCapabilitiesAttribute () ///instance /// containing the detached signature data. /// Encrypts the specified content for the specified recipients. /// - ///A new instance + /// A new /// The recipients. /// The content. @@ -719,6 +735,54 @@ internal SmimeCapabilitiesAttribute GetSecureMimeCapabilitiesAttribute () ///instance /// containing the encrypted content. + /// Imports certificates and keys from a pkcs12 file. + /// + ///+ /// Imports certificates and keys from a pkcs12 file. + /// + /// The raw certificate and key data in pkcs12 format. + /// The password to unlock the stream. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + ///+ /// is a zero-length string, contains only white space, or + /// contains one or more invalid characters. -or- + ///+ /// does not contain a private key. -or- + ///+ /// does not contain a certificate that could be used for signing. + /// + ///is an invalid file path. + /// + /// The specified file path could not be found. + /// + ///+ /// The user does not have access to read the specified file. + /// + ///+ /// An I/O error occurred. + /// + ///+ /// Importing keys is not supported by this cryptography context. + /// + public virtual void Import (string fileName, string password) + { + if (fileName == null) + throw new ArgumentNullException (nameof (fileName)); + + if (password == null) + throw new ArgumentNullException (nameof (password)); + + using (var stream = File.OpenRead (fileName)) + Import (stream, password); + } + ////// Imports the specified certificate. /// diff --git a/MimeKit/Cryptography/SecureMimeDigitalCertificate.cs b/MimeKit/Cryptography/SecureMimeDigitalCertificate.cs index 928a258695..073460a142 100644 --- a/MimeKit/Cryptography/SecureMimeDigitalCertificate.cs +++ b/MimeKit/Cryptography/SecureMimeDigitalCertificate.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,7 +27,6 @@ using System; using Org.BouncyCastle.X509; -using Org.BouncyCastle.Crypto.Parameters; namespace MimeKit.Cryptography { /// @@ -39,7 +38,7 @@ namespace MimeKit.Cryptography { public class SecureMimeDigitalCertificate : IDigitalCertificate { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -50,21 +49,12 @@ public class SecureMimeDigitalCertificate : IDigitalCertificate /// diff --git a/MimeKit/Cryptography/SecureMimeDigitalSignature.cs b/MimeKit/Cryptography/SecureMimeDigitalSignature.cs index 667be711b5..9739810f8c 100644 --- a/MimeKit/Cryptography/SecureMimeDigitalSignature.cs +++ b/MimeKit/Cryptography/SecureMimeDigitalSignature.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,10 +25,17 @@ // using System; +using System.Collections.Generic; using Org.BouncyCastle.Cms; using Org.BouncyCastle.X509; using Org.BouncyCastle.Pkix; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Smime; +using Org.BouncyCastle.Asn1.X509; + +using MimeKit.Utils; namespace MimeKit.Cryptography { /// @@ -42,9 +49,65 @@ public class SecureMimeDigitalSignature : IDigitalSignature DigitalSignatureVerifyException vex; bool? valid; - internal SecureMimeDigitalSignature (SignerInformation signerInfo) + static DateTime ToAdjustedDateTime (DerUtcTime time) { + //try { + // return time.ToAdjustedDateTime (); + //} catch { + return DateUtils.Parse (time.AdjustedTimeString, "yyyyMMddHHmmsszzz"); + //} + } + + /// + /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The information about the signer. + /// The signer's certificate. + ///. + /// + /// + public SecureMimeDigitalSignature (SignerInformation signerInfo, X509Certificate certificate) + { + if (signerInfo == null) + throw new ArgumentNullException (nameof (signerInfo)); + SignerInfo = signerInfo; + + var algorithms = new Listis null . + ///(); + DigestAlgorithm digestAlgo; + + if (signerInfo.SignedAttributes != null) { + Asn1EncodableVector vector = signerInfo.SignedAttributes.GetAll (CmsAttributes.SigningTime); + foreach (Org.BouncyCastle.Asn1.Cms.Attribute attr in vector) { + var signingTime = (DerUtcTime) ((DerSet) attr.AttrValues)[0]; + CreationDate = ToAdjustedDateTime (signingTime); + break; + } + + vector = signerInfo.SignedAttributes.GetAll (SmimeAttributes.SmimeCapabilities); + foreach (Org.BouncyCastle.Asn1.Cms.Attribute attr in vector) { + foreach (Asn1Sequence sequence in attr.AttrValues) { + for (int i = 0; i < sequence.Count; i++) { + var identifier = AlgorithmIdentifier.GetInstance (sequence[i]); + EncryptionAlgorithm algorithm; + + if (BouncyCastleSecureMimeContext.TryGetEncryptionAlgorithm (identifier, out algorithm)) + algorithms.Add (algorithm); + } + } + } + } + + EncryptionAlgorithms = algorithms.ToArray (); + + if (BouncyCastleSecureMimeContext.TryGetDigestAlgorithm (signerInfo.DigestAlgorithmID, out digestAlgo)) + DigestAlgorithm = digestAlgo; + + if (certificate != null) + SignerCertificate = new SecureMimeDigitalCertificate (certificate); } /// @@ -68,7 +131,7 @@ public SignerInformation SignerInfo { /// /// The S/MIME encryption algorithms. public EncryptionAlgorithm[] EncryptionAlgorithms { - get; internal set; + get; private set; } ///@@ -104,7 +167,7 @@ public Exception ChainException { /// /// The signer's certificate. public IDigitalCertificate SignerCertificate { - get; internal set; + get; private set; } ///@@ -115,7 +178,7 @@ public IDigitalCertificate SignerCertificate { /// /// The public key algorithm. public PublicKeyAlgorithm PublicKeyAlgorithm { - get { return PublicKeyAlgorithm.None; } + get { return SignerCertificate != null ? SignerCertificate.PublicKeyAlgorithm : PublicKeyAlgorithm.None; } } ///@@ -126,7 +189,7 @@ public PublicKeyAlgorithm PublicKeyAlgorithm { /// /// The digest algorithm. public DigestAlgorithm DigestAlgorithm { - get; internal set; + get; private set; } ///@@ -137,7 +200,7 @@ public DigestAlgorithm DigestAlgorithm { /// /// The creation date in coordinated universal time (UTC). public DateTime CreationDate { - get; internal set; + get; private set; } ///@@ -146,39 +209,55 @@ public DateTime CreationDate { /// /// Verifies the digital signature. /// - ///+ /// true if the signature is valid; otherwisefalse ./// true if the signature is valid; otherwise,false ./// An error verifying the signature has occurred. /// public bool Verify () { - if (valid.HasValue) - return valid.Value; + return Verify (false); + } + ///+ /// Verifies the digital signature. + /// + ///+ /// Verifies the digital signature. + /// + ///true if only the signature itself should be verified; otherwise, both the signature and the certificate chain are validated. + ///+ /// true if the signature is valid; otherwise,false .+ /// An error verifying the signature has occurred. + /// + public bool Verify (bool verifySignatureOnly) + { if (vex != null) throw vex; if (SignerCertificate == null) { - var message = string.Format ("Failed to verify digital signature: missing certificate."); + var message = "Failed to verify digital signature: missing certificate."; vex = new DigitalSignatureVerifyException (message); throw vex; } - if (ChainException != null) { - var message = string.Format ("Failed to verify digital signature: {0}", ChainException.Message); - vex = new DigitalSignatureVerifyException (message, ChainException); - throw vex; + if (!valid.HasValue) { + try { + var certificate = ((SecureMimeDigitalCertificate) SignerCertificate).Certificate; + valid = SignerInfo.Verify (certificate); + } catch (Exception ex) { + var message = string.Format ("Failed to verify digital signature: {0}", ex.Message); + vex = new DigitalSignatureVerifyException (message, ex); + throw vex; + } } - try { - var certificate = ((SecureMimeDigitalCertificate) SignerCertificate).Certificate; - valid = SignerInfo.Verify (certificate); - return valid.Value; - } catch (Exception ex) { - var message = string.Format ("Failed to verify digital signature: {0}", ex.Message); - vex = new DigitalSignatureVerifyException (message, ex); - throw vex; + if (!verifySignatureOnly && ChainException != null) { + var message = string.Format ("Failed to verify digital signature chain: {0}", ChainException.Message); + + throw new DigitalSignatureVerifyException (message, ChainException); } + + return valid.Value; } #endregion diff --git a/MimeKit/Cryptography/SecureMimeType.cs b/MimeKit/Cryptography/SecureMimeType.cs index 00f0cf0438..150e0022f2 100644 --- a/MimeKit/Cryptography/SecureMimeType.cs +++ b/MimeKit/Cryptography/SecureMimeType.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -33,28 +33,33 @@ namespace MimeKit.Cryptography { /// public enum SecureMimeType { /// - /// S/MIME compressed data. + /// The S/MIME data type is unknown. + /// + Unknown = -1, + + ///+ /// The S/MIME content is compressed. /// CompressedData, ///- /// S/MIME enveloped data. + /// The S/MIME content is encrypted. /// EnvelopedData, ///- /// S/MIME signed data. + /// The S/MIME content is signed. /// SignedData, ///- /// S/MIME certificate data. + /// The S/MIME content contains only certificates. /// CertsOnly, ///- /// The S/MIME data type is unknown. + /// The S/MIME content is both signed and encrypted. /// - Unknown + AuthEnvelopedData, } } diff --git a/MimeKit/Cryptography/SqlCertificateDatabase.cs b/MimeKit/Cryptography/SqlCertificateDatabase.cs index 603b0af1f2..bffa3863ec 100644 --- a/MimeKit/Cryptography/SqlCertificateDatabase.cs +++ b/MimeKit/Cryptography/SqlCertificateDatabase.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,6 +28,7 @@ using System.Data; using System.Text; using System.Data.Common; +using System.Collections.Generic; #if __MOBILE__ using Mono.Data.Sqlite; @@ -35,6 +36,7 @@ using System.Reflection; #endif +using Org.BouncyCastle.Asn1; using Org.BouncyCastle.X509; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.X509.Store; @@ -51,11 +53,10 @@ namespace MimeKit.Cryptography { /// public abstract class SqlCertificateDatabase : X509CertificateDatabase { - readonly DbConnection connection; bool disposed; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new using the provided database connection. @@ -72,40 +73,219 @@ protected SqlCertificateDatabase (DbConnection connection, string password) : ba if (connection == null) throw new ArgumentNullException (nameof (connection)); - this.connection = connection; + Connection = connection; if (connection.State != ConnectionState.Open) connection.Open (); - CreateCertificatesTable (); - CreateCrlsTable (); + CertificatesTable = CreateCertificatesDataTable ("CERTIFICATES"); + CrlsTable = CreateCrlsDataTable ("CRLS"); + + CreateCertificatesTable (CertificatesTable); + CreateCrlsTable (CrlsTable); + } + + /// + /// Gets the database connection. + /// + ///+ /// Gets the database connection. + /// + ///The database connection. + protected DbConnection Connection { + get; private set; // TODO: push this down into X509CertificateDatabase and fix GetSelectCommand()/GetInsertCommand()/etc to take a DbConnection arg, then this could be private. + } + + ///+ /// Gets the X.509 certificate table definition. + /// + ///+ /// Gets the X.509 certificate table definition. + /// + ///The X.509 certificates table definition. + protected DataTable CertificatesTable { + get; private set; + } + + ///+ /// Gets the X.509 certificate revocation lists (CRLs) table definition. + /// + ///+ /// Gets the X.509 certificate revocation lists (CRLs) table definition. + /// + ///The X.509 certificate revocation lists table definition. + protected DataTable CrlsTable { + get; private set; + } + +#if NETSTANDARD1_3 || NETSTANDARD1_6 +#pragma warning disable 1591 + protected class DataColumn + { + public DataColumn (string columnName, Type dataType) + { + ColumnName = columnName; + DataType = dataType; + } + + public DataColumn () + { + } + + public bool AllowDBNull { + get; set; + } + + public bool AutoIncrement { + get; set; + } + + public string ColumnName { + get; set; + } + + public Type DataType { + get; set; + } + + public bool Unique { + get; set; + } + } + + protected class DataColumnCollection : List+ { + public int IndexOf (string columnName) + { + for (int i = 0; i < Count; i++) { + if (this[i].ColumnName.Equals (columnName, StringComparison.Ordinal)) + return i; + } + + return -1; + } + } + + protected class DataTable + { + public DataTable (string tableName) + { + Columns = new DataColumnCollection (); + TableName = tableName; + } + + public string TableName { + get; set; + } + + public DataColumnCollection Columns { + get; private set; + } + + public DataColumn[] PrimaryKey { + get; set; + } + } +#pragma warning restore 1591 +#endif + + static DataTable CreateCertificatesDataTable (string tableName) + { + var table = new DataTable (tableName); + table.Columns.Add (new DataColumn ("ID", typeof (int)) { AutoIncrement = true }); + table.Columns.Add (new DataColumn ("TRUSTED", typeof (bool)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("ANCHOR", typeof (bool)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("BASICCONSTRAINTS", typeof (int)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("KEYUSAGE", typeof (int)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("NOTBEFORE", typeof (long)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("NOTAFTER", typeof (long)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("ISSUERNAME", typeof (string)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("SERIALNUMBER", typeof (string)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("SUBJECTNAME", typeof (string)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("SUBJECTKEYIDENTIFIER", typeof (string)) { AllowDBNull = true }); + table.Columns.Add (new DataColumn ("SUBJECTEMAIL", typeof (string)) { AllowDBNull = true }); + table.Columns.Add (new DataColumn ("FINGERPRINT", typeof (string)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("ALGORITHMS", typeof (string)) { AllowDBNull = true }); + table.Columns.Add (new DataColumn ("ALGORITHMSUPDATED", typeof (long)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("CERTIFICATE", typeof (byte[])) { AllowDBNull = false, Unique = true }); + table.Columns.Add (new DataColumn ("PRIVATEKEY", typeof (byte[])) { AllowDBNull = true }); + table.PrimaryKey = new DataColumn[] { table.Columns[0] }; + + return table; } + static DataTable CreateCrlsDataTable (string tableName) + { + var table = new DataTable (tableName); + table.Columns.Add (new DataColumn ("ID", typeof (int)) { AutoIncrement = true }); + table.Columns.Add (new DataColumn ("DELTA", typeof (bool)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("ISSUERNAME", typeof (string)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("THISUPDATE", typeof (long)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("NEXTUPDATE", typeof (long)) { AllowDBNull = false }); + table.Columns.Add (new DataColumn ("CRL", typeof (byte[])) { AllowDBNull = false }); + table.PrimaryKey = new DataColumn[] { table.Columns[0] }; + + return table; + } + + /// + /// Gets the columns for the specified table. + /// + ///+ /// Gets the list of columns for the specified table. + /// + /// The. + /// The name of the table. + /// The list of columns. + protected abstract IListGetTableColumns (DbConnection connection, string tableName); + /// - /// Gets the command to create the certificates table. + /// Gets the command to create a table. /// ///- /// Constructs the command to create a certificates table suitable for storing - /// - ///objects. + /// Constructs the command to create a table. /// The /// The. . - protected abstract DbCommand GetCreateCertificatesTableCommand (DbConnection connection); + /// The table. + protected abstract void CreateTable (DbConnection connection, DataTable table); /// - /// Gets the command to create the CRLs table. + /// Adds a column to a table. /// ///- /// Constructs the command to create a CRLs table suitable for storing - /// - ///objects. + /// Adds a column to a table. /// The /// The. . - protected abstract DbCommand GetCreateCrlsTableCommand (DbConnection connection); + /// The table. + /// The column to add. + protected abstract void AddTableColumn (DbConnection connection, DataTable table, DataColumn column); - static void CreateIndex (DbConnection connection, string tableName, string[] columnNames) + /// + /// Gets the name of an index based on the table and columns that it is built against. + /// + ///+ /// Gets the name of an index based on the table and columns that it is built against. + /// + /// The name of the table. + /// The names of the columns that are indexed. + ///+ protected string GetIndexName (string tableName, string[] columnNames) { - var indexName = string.Format ("{0}_{1}_INDEX", tableName, string.Join ("_", columnNames)); + return string.Format ("{0}_{1}_INDEX", tableName, string.Join ("_", columnNames)); + } + + /// + /// Creates an index for faster table lookups. + /// + ///+ /// Creates an index for faster table lookups. + /// + /// The. + /// The name of the table. + /// The names of the columns to index. + protected virtual void CreateIndex (DbConnection connection, string tableName, string[] columnNames) + { + var indexName = GetIndexName (tableName, columnNames); var query = string.Format ("CREATE INDEX IF NOT EXISTS {0} ON {1}({2})", indexName, tableName, string.Join (", ", columnNames)); using (var command = connection.CreateCommand ()) { @@ -114,26 +294,128 @@ static void CreateIndex (DbConnection connection, string tableName, string[] col } } - void CreateCertificatesTable () + /// + /// Removes an index that is no longer needed. + /// + ///+ /// Removes an index that is no longer needed. + /// + /// The. + /// The name of the table. + /// The names of the columns that were indexed. + protected virtual void RemoveIndex (DbConnection connection, string tableName, string[] columnNames) { - using (var command = GetCreateCertificatesTableCommand (connection)) + var indexName = GetIndexName (tableName, columnNames); + var query = string.Format ("DROP INDEX IF EXISTS {0}", indexName); + + using (var command = connection.CreateCommand ()) { + command.CommandText = query; command.ExecuteNonQuery (); + } + } + + void CreateCertificatesTable (DataTable table) + { + CreateTable (Connection, table); + + var currentColumns = GetTableColumns (Connection, table.TableName); + bool hasAnchorColumn = false; + + for (int i = 0; i < currentColumns.Count; i++) { + if (currentColumns[i].ColumnName.Equals ("ANCHOR", StringComparison.Ordinal)) { + hasAnchorColumn = true; + break; + } + } + + // Note: The ANCHOR, SUBJECTNAME and SUBJECTKEYIDENTIFIER columns were all added in the same version, + // so if the ANCHOR column is missing, they all are. + if (!hasAnchorColumn) { + using (var transaction = Connection.BeginTransaction ()) { + try { + var column = table.Columns[table.Columns.IndexOf ("ANCHOR")]; + AddTableColumn (Connection, table, column); + + column = table.Columns[table.Columns.IndexOf ("SUBJECTNAME")]; + AddTableColumn (Connection, table, column); + + column = table.Columns[table.Columns.IndexOf ("SUBJECTKEYIDENTIFIER")]; + AddTableColumn (Connection, table, column); + + foreach (var record in Find (null, false, X509CertificateRecordFields.Id | X509CertificateRecordFields.Certificate)) { + var statement = "UPDATE CERTIFICATES SET ANCHOR = @ANCHOR, SUBJECTNAME = @SUBJECTNAME, SUBJECTKEYIDENTIFIER = @SUBJECTKEYIDENTIFIER WHERE ID = @ID"; + + using (var command = Connection.CreateCommand ()) { + command.AddParameterWithValue ("@ID", record.Id); + command.AddParameterWithValue ("@ANCHOR", record.IsAnchor); + command.AddParameterWithValue ("@SUBJECTNAME", record.SubjectName); + command.AddParameterWithValue ("@SUBJECTKEYIDENTIFIER", record.SubjectKeyIdentifier?.AsHex ()); + command.CommandType = CommandType.Text; + command.CommandText = statement; + + command.ExecuteNonQuery (); + } + } + + transaction.Commit (); + } catch { + transaction.Rollback (); + throw; + } + } + + // Remove some old indexes + RemoveIndex (Connection, table.TableName, new[] { "TRUSTED" }); + RemoveIndex (Connection, table.TableName, new[] { "TRUSTED", "BASICCONSTRAINTS", "ISSUERNAME", "SERIALNUMBER" }); + RemoveIndex (Connection, table.TableName, new[] { "BASICCONSTRAINTS", "ISSUERNAME", "SERIALNUMBER" }); + RemoveIndex (Connection, table.TableName, new[] { "BASICCONSTRAINTS", "FINGERPRINT" }); + RemoveIndex (Connection, table.TableName, new[] { "BASICCONSTRAINTS", "SUBJECTEMAIL" }); + } + + // Note: Use "EXPLAIN QUERY PLAN SELECT ... FROM CERTIFICATES WHERE ..." to verify that any indexes we create get used as expected. + + // Index for matching against a specific certificate + CreateIndex (Connection, table.TableName, new [] { "ISSUERNAME", "SERIALNUMBER", "FINGERPRINT" }); - CreateIndex (connection, "CERTIFICATES", new [] { "ISSUERNAME", "SERIALNUMBER", "FINGERPRINT" }); - CreateIndex (connection, "CERTIFICATES", new [] { "BASICCONSTRAINTS", "FINGERPRINT" }); - CreateIndex (connection, "CERTIFICATES", new [] { "BASICCONSTRAINTS", "SUBJECTEMAIL" }); - CreateIndex (connection, "CERTIFICATES", new [] { "TRUSTED" }); - CreateIndex (connection, "CERTIFICATES", new [] { "TRUSTED", "BASICCONSTRAINTS", "ISSUERNAME", "SERIALNUMBER" }); - CreateIndex (connection, "CERTIFICATES", new [] { "BASICCONSTRAINTS", "ISSUERNAME", "SERIALNUMBER" }); + // Index for searching for a certificate based on a SecureMailboxAddress + CreateIndex (Connection, table.TableName, new [] { "BASICCONSTRAINTS", "FINGERPRINT", "NOTBEFORE", "NOTAFTER" }); + + // Index for searching for a certificate based on a MailboxAddress + CreateIndex (Connection, table.TableName, new [] { "BASICCONSTRAINTS", "SUBJECTEMAIL", "NOTBEFORE", "NOTAFTER" }); + + // Index for gathering a list of Trusted Anchors + CreateIndex (Connection, table.TableName, new [] { "TRUSTED", "ANCHOR", "KEYUSAGE" }); } - void CreateCrlsTable () + void CreateCrlsTable (DataTable table) { - using (var command = GetCreateCrlsTableCommand (connection)) - command.ExecuteNonQuery (); + CreateTable (Connection, table); - CreateIndex (connection, "CRLS", new [] { "ISSUERNAME" }); - CreateIndex (connection, "CRLS", new [] { "DELTA", "ISSUERNAME", "THISUPDATE" }); + CreateIndex (Connection, table.TableName, new [] { "ISSUERNAME" }); + CreateIndex (Connection, table.TableName, new [] { "DELTA", "ISSUERNAME", "THISUPDATE" }); + } + + /// + /// Creates a SELECT query string builder for the specified fields of an X.509 certificate record. + /// + ///+ /// Creates a SELECT query string builder for the specified fields of an X.509 certificate record. + /// + /// THe X.509 certificate fields. + ///A + protected StringBuilder CreateSelectQuery (X509CertificateRecordFields fields) + { + var query = new StringBuilder ("SELECT "); + var columns = GetColumnNames (fields); + + for (int i = 0; i < columns.Length; i++) { + if (i > 0) + query = query.Append (", "); + + query = query.Append (columns[i]); + } + + return query.Append (" FROM CERTIFICATES"); } ///containing a basic SELECT query string. @@ -147,23 +429,26 @@ void CreateCrlsTable () /// The fields to return. protected override DbCommand GetSelectCommand (X509Certificate certificate, X509CertificateRecordFields fields) { - var query = "SELECT " + string.Join (", ", GetColumnNames (fields)) + " FROM CERTIFICATES "; var fingerprint = certificate.GetFingerprint ().ToLowerInvariant (); var serialNumber = certificate.SerialNumber.ToString (); var issuerName = certificate.IssuerDN.ToString (); - var command = connection.CreateCommand (); + var command = Connection.CreateCommand (); + var query = CreateSelectQuery (fields); - command.CommandText = query + "WHERE ISSUERNAME = @ISSUERNAME AND SERIALNUMBER = @SERIALNUMBER AND FINGERPRINT = @FINGERPRINT LIMIT 1"; + // FIXME: Is this really the best way to query for an exact match of a certificate? + query = query.Append (" WHERE ISSUERNAME = @ISSUERNAME AND SERIALNUMBER = @SERIALNUMBER AND FINGERPRINT = @FINGERPRINT LIMIT 1"); command.AddParameterWithValue ("@ISSUERNAME", issuerName); command.AddParameterWithValue ("@SERIALNUMBER", serialNumber); command.AddParameterWithValue ("@FINGERPRINT", fingerprint); + + command.CommandText = query.ToString (); command.CommandType = CommandType.Text; return command; } /// - /// Gets the database command to select the certificate records for the specified mailbox. + /// Get the database command to select the certificate records for the specified mailbox. /// ////// Gets the database command to select the certificate records for the specified mailbox. @@ -175,85 +460,140 @@ protected override DbCommand GetSelectCommand (X509Certificate certificate, X509 /// The fields to return. protected override DbCommand GetSelectCommand (MailboxAddress mailbox, DateTime now, bool requirePrivateKey, X509CertificateRecordFields fields) { - var query = "SELECT " + string.Join (", ", GetColumnNames (fields)) + " FROM CERTIFICATES"; var secure = mailbox as SecureMailboxAddress; - var command = connection.CreateCommand (); - var constraints = " WHERE "; + var command = Connection.CreateCommand (); + var query = CreateSelectQuery (fields); - constraints += "BASICCONSTRAINTS = @BASICCONSTRAINTS "; + query = query.Append (" WHERE BASICCONSTRAINTS = @BASICCONSTRAINTS "); command.AddParameterWithValue ("@BASICCONSTRAINTS", -1); if (secure != null && !string.IsNullOrEmpty (secure.Fingerprint)) { if (secure.Fingerprint.Length < 40) { - constraints += "AND FINGERPRINT LIKE @FINGERPRINT "; command.AddParameterWithValue ("@FINGERPRINT", secure.Fingerprint.ToLowerInvariant () + "%"); + query = query.Append ("AND FINGERPRINT LIKE @FINGERPRINT "); } else { - constraints += "AND FINGERPRINT = @FINGERPRINT "; command.AddParameterWithValue ("@FINGERPRINT", secure.Fingerprint.ToLowerInvariant ()); + query = query.Append ("AND FINGERPRINT = @FINGERPRINT "); } } else { - constraints += "AND SUBJECTEMAIL = @SUBJECTEMAIL "; command.AddParameterWithValue ("@SUBJECTEMAIL", mailbox.Address.ToLowerInvariant ()); + query = query.Append ("AND SUBJECTEMAIL = @SUBJECTEMAIL "); } - constraints += "AND NOTBEFORE < @NOW AND NOTAFTER > @NOW "; + query = query.Append ("AND NOTBEFORE < @NOW AND NOTAFTER > @NOW"); command.AddParameterWithValue ("@NOW", now.ToUniversalTime ()); if (requirePrivateKey) - constraints += "AND PRIVATEKEY IS NOT NULL"; + query = query.Append (" AND PRIVATEKEY IS NOT NULL"); - command.CommandText = query + constraints; + command.CommandText = query.ToString (); command.CommandType = CommandType.Text; return command; } /// - /// Gets the database command to select the certificate records for the specified mailbox. + /// Get the database command to select the requested certificate records. /// ///- /// Gets the database command to select the certificate records for the specified mailbox. + /// Gets the database command to select the requested certificate records. /// ///The database command. - /// Selector. - /// If set totrue trusted only. - /// true + /// The certificate selector. + ///true if only trusted anchor certificates should be matched; otherwise,false . + ///true if the certificate must have a private key; otherwise,false . /// The fields to return. - protected override DbCommand GetSelectCommand (IX509Selector selector, bool trustedOnly, bool requirePrivateKey, X509CertificateRecordFields fields) + protected override DbCommand GetSelectCommand (IX509Selector selector, bool trustedAnchorsOnly, bool requirePrivateKey, X509CertificateRecordFields fields) { - var query = "SELECT " + string.Join (", ", GetColumnNames (fields)) + " FROM CERTIFICATES"; var match = selector as X509CertStoreSelector; - var command = connection.CreateCommand (); - var constraints = " WHERE "; + var command = Connection.CreateCommand (); + var query = CreateSelectQuery (fields); + int baseQueryLength = query.Length; - if (trustedOnly) { + query = query.Append (" WHERE "); + + // FIXME: We could create an X509CertificateDatabaseSelector subclass of X509CertStoreSelector that + // adds properties like bool Trusted, bool Anchor, and bool HasPrivateKey ? Then we could drop the + // bool method arguments... + if (trustedAnchorsOnly) { + query = query.Append ("TRUSTED = @TRUSTED AND ANCHOR = @ANCHOR"); command.AddParameterWithValue ("@TRUSTED", true); - constraints += "TRUSTED = @TRUSTED"; + command.AddParameterWithValue ("@ANCHOR", true); } if (match != null) { - if (match.BasicConstraints != -1) { + if (match.BasicConstraints >= 0 || match.BasicConstraints == -2) { if (command.Parameters.Count > 0) - constraints += " AND "; + query = query.Append (" AND "); + + if (match.BasicConstraints == -2) { + command.AddParameterWithValue ("@BASICCONSTRAINTS", -1); + query = query.Append ("BASICCONSTRAINTS = @BASICCONSTRAINTS"); + } else { + command.AddParameterWithValue ("@BASICCONSTRAINTS", match.BasicConstraints); + query = query.Append ("BASICCONSTRAINTS >= @BASICCONSTRAINTS"); + } + } + + if (match.CertificateValid != null) { + if (command.Parameters.Count > 0) + query = query.Append (" AND "); - command.AddParameterWithValue ("@BASICCONSTRAINTS", match.BasicConstraints); - constraints += "BASICCONSTRAINTS = @BASICCONSTRAINTS"; + command.AddParameterWithValue ("@DATETIME", match.CertificateValid.Value.ToUniversalTime ()); + query = query.Append ("NOTBEFORE < @DATETIME AND NOTAFTER > @DATETIME"); } - if (match.Issuer != null) { + if (match.Issuer != null || match.Certificate != null) { + // Note: GetSelectCommand (X509Certificate certificate, X509CertificateRecordFields fields) + // queries for ISSUERNAME, SERIALNUMBER, and FINGERPRINT so we'll do the same. + var issuer = match.Issuer ?? match.Certificate.IssuerDN; + if (command.Parameters.Count > 0) - constraints += " AND "; + query = query.Append (" AND "); - command.AddParameterWithValue ("@ISSUERNAME", match.Issuer.ToString ()); - constraints += "ISSUERNAME = @ISSUERNAME"; + command.AddParameterWithValue ("@ISSUERNAME", issuer.ToString ()); + query = query.Append ("ISSUERNAME = @ISSUERNAME"); } - if (match.SerialNumber != null) { + if (match.SerialNumber != null || match.Certificate != null) { + // Note: GetSelectCommand (X509Certificate certificate, X509CertificateRecordFields fields) + // queries for ISSUERNAME, SERIALNUMBER, and FINGERPRINT so we'll do the same. + var serialNumber = match.SerialNumber ?? match.Certificate.SerialNumber; + + if (command.Parameters.Count > 0) + query = query.Append (" AND "); + + command.AddParameterWithValue ("@SERIALNUMBER", serialNumber.ToString ()); + query = query.Append ("SERIALNUMBER = @SERIALNUMBER"); + } + + if (match.Certificate != null) { + // Note: GetSelectCommand (X509Certificate certificate, X509CertificateRecordFields fields) + // queries for ISSUERNAME, SERIALNUMBER, and FINGERPRINT so we'll do the same. + if (command.Parameters.Count > 0) + query = query.Append (" AND "); + + command.AddParameterWithValue ("@FINGERPRINT", match.Certificate.GetFingerprint ()); + query = query.Append ("FINGERPRINT = @FINGERPRINT"); + } + + if (match.Subject != null) { if (command.Parameters.Count > 0) - constraints += " AND "; + query = query.Append (" AND "); - command.AddParameterWithValue ("@SERIALNUMBER", match.SerialNumber.ToString ()); - constraints += "SERIALNUMBER = @SERIALNUMBER"; + command.AddParameterWithValue ("@SUBJECTNAME", match.Subject.ToString ()); + query = query.Append ("SUBJECTNAME = @SUBJECTNAME"); + } + + if (match.SubjectKeyIdentifier != null) { + if (command.Parameters.Count > 0) + query = query.Append (" AND "); + + var id = (Asn1OctetString) Asn1Object.FromByteArray (match.SubjectKeyIdentifier); + var subjectKeyIdentifier = id.GetOctets ().AsHex (); + + command.AddParameterWithValue ("@SUBJECTKEYIDENTIFIER", subjectKeyIdentifier); + query = query.Append ("SUBJECTKEYIDENTIFIER = @SUBJECTKEYIDENTIFIER"); } if (match.KeyUsage != null) { @@ -261,24 +601,24 @@ protected override DbCommand GetSelectCommand (IX509Selector selector, bool trus if (flags != X509KeyUsageFlags.None) { if (command.Parameters.Count > 0) - constraints += " AND "; + query = query.Append (" AND "); command.AddParameterWithValue ("@FLAGS", (int) flags); - constraints += "(KEYUSAGE == 0 OR (KEYUSAGE & @FLAGS) == @FLAGS)"; + query = query.Append ("(KEYUSAGE = 0 OR (KEYUSAGE & @FLAGS) = @FLAGS)"); } } } if (requirePrivateKey) { if (command.Parameters.Count > 0) - constraints += " AND "; + query = query.Append (" AND "); - constraints += "PRIVATEKEY IS NOT NULL"; + query = query.Append ("PRIVATEKEY IS NOT NULL"); } else if (command.Parameters.Count == 0) { - constraints = string.Empty; + query.Length = baseQueryLength; } - command.CommandText = query + constraints; + command.CommandText = query.ToString (); command.CommandType = CommandType.Text; return command; @@ -296,7 +636,7 @@ protected override DbCommand GetSelectCommand (IX509Selector selector, bool trus protected override DbCommand GetSelectCommand (X509Name issuer, X509CrlRecordFields fields) { var query = "SELECT " + string.Join (", ", GetColumnNames (fields)) + " FROM CRLS "; - var command = connection.CreateCommand (); + var command = Connection.CreateCommand (); command.CommandText = query + "WHERE ISSUERNAME = @ISSUERNAME"; command.AddParameterWithValue ("@ISSUERNAME", issuer.ToString ()); @@ -318,7 +658,7 @@ protected override DbCommand GetSelectCommand (X509Crl crl, X509CrlRecordFields { var query = "SELECT " + string.Join (", ", GetColumnNames (fields)) + " FROM CRLS "; var issuerName = crl.IssuerDN.ToString (); - var command = connection.CreateCommand (); + var command = Connection.CreateCommand (); command.CommandText = query + "WHERE DELTA = @DELTA AND ISSUERNAME = @ISSUERNAME AND THISUPDATE = @THISUPDATE LIMIT 1"; command.AddParameterWithValue ("@DELTA", crl.IsDelta ()); @@ -338,7 +678,7 @@ protected override DbCommand GetSelectCommand (X509Crl crl, X509CrlRecordFields ///The database command. protected override DbCommand GetSelectAllCrlsCommand () { - var command = connection.CreateCommand (); + var command = Connection.CreateCommand (); command.CommandText = "SELECT ID, CRL FROM CRLS"; command.CommandType = CommandType.Text; @@ -356,7 +696,7 @@ protected override DbCommand GetSelectAllCrlsCommand () /// The certificate record. protected override DbCommand GetDeleteCommand (X509CertificateRecord record) { - var command = connection.CreateCommand (); + var command = Connection.CreateCommand (); command.CommandText = "DELETE FROM CERTIFICATES WHERE ID = @ID"; command.AddParameterWithValue ("@ID", record.Id); @@ -375,7 +715,7 @@ protected override DbCommand GetDeleteCommand (X509CertificateRecord record) /// The record. protected override DbCommand GetDeleteCommand (X509CrlRecord record) { - var command = connection.CreateCommand (); + var command = Connection.CreateCommand (); command.CommandText = "DELETE FROM CRLS WHERE ID = @ID"; command.AddParameterWithValue ("@ID", record.Id); @@ -395,17 +735,17 @@ protected override DbCommand GetDeleteCommand (X509CrlRecord record) protected override DbCommand GetInsertCommand (X509CertificateRecord record) { var statement = new StringBuilder ("INSERT INTO CERTIFICATES("); - var columns = X509CertificateRecord.ColumnNames; var variables = new StringBuilder ("VALUES("); - var command = connection.CreateCommand (); + var command = Connection.CreateCommand (); + var columns = CertificatesTable.Columns; - for (int i = 1; i < columns.Length; i++) { + for (int i = 1; i < columns.Count; i++) { if (i > 1) { statement.Append (", "); variables.Append (", "); } - var value = GetValue (record, columns[i]); + var value = GetValue (record, columns[i].ColumnName); var variable = "@" + columns[i]; command.AddParameterWithValue (variable, value); @@ -434,16 +774,16 @@ protected override DbCommand GetInsertCommand (X509CrlRecord record) { var statement = new StringBuilder ("INSERT INTO CRLS("); var variables = new StringBuilder ("VALUES("); - var columns = X509CrlRecord.ColumnNames; - var command = connection.CreateCommand (); + var command = Connection.CreateCommand (); + var columns = CrlsTable.Columns; - for (int i = 1; i < columns.Length; i++) { + for (int i = 1; i < columns.Count; i++) { if (i > 1) { statement.Append (", "); variables.Append (", "); } - var value = GetValue (record, columns[i]); + var value = GetValue (record, columns[i].ColumnName); var variable = "@" + columns[i]; command.AddParameterWithValue (variable, value); @@ -473,7 +813,7 @@ protected override DbCommand GetUpdateCommand (X509CertificateRecord record, X50 { var statement = new StringBuilder ("UPDATE CERTIFICATES SET "); var columns = GetColumnNames (fields & ~X509CertificateRecordFields.Id); - var command = connection.CreateCommand (); + var command = Connection.CreateCommand (); for (int i = 0; i < columns.Length; i++) { var value = GetValue (record, columns[i]); @@ -509,11 +849,11 @@ protected override DbCommand GetUpdateCommand (X509CertificateRecord record, X50 protected override DbCommand GetUpdateCommand (X509CrlRecord record) { var statement = new StringBuilder ("UPDATE CRLS SET "); - var columns = X509CrlRecord.ColumnNames; - var command = connection.CreateCommand (); + var command = Connection.CreateCommand (); + var columns = CrlsTable.Columns; - for (int i = 1; i < columns.Length; i++) { - var value = GetValue (record, columns[i]); + for (int i = 1; i < columns.Count; i++) { + var value = GetValue (record, columns[i].ColumnName); var variable = "@" + columns[i]; if (i > 1) @@ -548,8 +888,8 @@ protected override DbCommand GetUpdateCommand (X509CrlRecord record) protected override void Dispose (bool disposing) { if (disposing && !disposed) { - if (connection != null) - connection.Dispose (); + if (Connection != null) + Connection.Dispose (); disposed = true; } diff --git a/MimeKit/Cryptography/SqliteCertificateDatabase.cs b/MimeKit/Cryptography/SqliteCertificateDatabase.cs index 678509d51d..50f430da43 100644 --- a/MimeKit/Cryptography/SqliteCertificateDatabase.cs +++ b/MimeKit/Cryptography/SqliteCertificateDatabase.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -29,6 +29,7 @@ using System.Data; using System.Text; using System.Data.Common; +using System.Collections.Generic; #if __MOBILE__ using Mono.Data.Sqlite; @@ -49,9 +50,44 @@ namespace MimeKit.Cryptography { public class SqliteCertificateDatabase : SqlCertificateDatabase { #if !__MOBILE__ - static readonly Type sqliteConnectionStringBuilderClass; - static readonly Type sqliteConnectionClass; - static readonly Assembly sqliteAssembly; + class SQLiteAssembly + { + public Type ConnectionStringBuilderType { get; private set; } + public Type ConnectionType { get; private set; } + public Assembly Assembly { get; private set; } + + public PropertyInfo ConnectionStringProperty { get; private set; } + public PropertyInfo DateTimeFormatProperty { get; private set; } + public PropertyInfo DataSourceProperty { get; private set; } + + public static SQLiteAssembly Load (string assemblyName) + { + try { + int dot = assemblyName.LastIndexOf ('.'); + var prefix = assemblyName.Substring (dot + 1); + + var assembly = Assembly.Load (new AssemblyName (assemblyName)); + var builderType = assembly.GetType (assemblyName + "." + prefix + "ConnectionStringBuilder"); + var connectionType = assembly.GetType (assemblyName + "." + prefix + "Connection"); + var connectionString = builderType.GetProperty ("ConnectionString"); + var dateTimeFormat = builderType.GetProperty ("DateTimeFormat"); + var dataSource = builderType.GetProperty ("DataSource"); + + return new SQLiteAssembly { + Assembly = assembly, + ConnectionType = connectionType, + ConnectionStringBuilderType = builderType, + ConnectionStringProperty = connectionString, + DateTimeFormatProperty = dateTimeFormat, + DataSourceProperty = dataSource + }; + } catch { + return null; + } + } + } + + static readonly SQLiteAssembly sqliteAssembly; #endif // At class initialization we try to use reflection to load the @@ -60,61 +96,66 @@ public class SqliteCertificateDatabase : SqlCertificateDatabase // assembly. static SqliteCertificateDatabase () { -#if NETSTANDARD - try { - if ((sqliteAssembly = Assembly.Load (new AssemblyName ("Microsoft.Data.Sqlite"))) != null) { - sqliteConnectionClass = sqliteAssembly.GetType ("Microsoft.Data.Sqlite.SqliteConnection"); - sqliteConnectionStringBuilderClass = sqliteAssembly.GetType ("Microsoft.Data.Sqlite.SqliteConnectionStringBuilder"); - - // Make sure that the runtime can load the native sqlite library - var builder = Activator.CreateInstance (sqliteConnectionStringBuilderClass); +#if __MOBILE__ + IsAvailable = true; +#else // !__MOBILE__ +#if NETFRAMEWORK || NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_0 + var platform = Environment.OSVersion.Platform; +#endif +#if NETSTANDARD1_3 || NETSTANDARD1_6 || NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_0 + if ((sqliteAssembly = SQLiteAssembly.Load ("Microsoft.Data.Sqlite")) != null) { + // Make sure that the runtime can load the native sqlite library + if (VerifySQLiteAssemblyIsUsable ()) { IsAvailable = true; + return; } - } catch (FileNotFoundException) { - } catch (FileLoadException) { - } catch (BadImageFormatException) { } -#elif !__MOBILE__ - var platform = Environment.OSVersion.Platform; - - try { - // Mono.Data.Sqlite will only work on Unix-based platforms and 32-bit Windows platforms. - if (platform == PlatformID.Unix || platform == PlatformID.MacOSX || IntPtr.Size == 4) { - if ((sqliteAssembly = Assembly.Load ("Mono.Data.Sqlite")) != null) { - sqliteConnectionClass = sqliteAssembly.GetType ("Mono.Data.Sqlite.SqliteConnection"); - sqliteConnectionStringBuilderClass = sqliteAssembly.GetType ("Mono.Data.Sqlite.SqliteConnectionStringBuilder"); - - // Make sure that the runtime can load the native sqlite3 library - var builder = Activator.CreateInstance (sqliteConnectionStringBuilderClass); - sqliteConnectionStringBuilderClass.GetProperty ("DateTimeFormat").SetValue (builder, 0, null); +#endif +#if NETFRAMEWORK || NETCOREAPP3_0 + // Mono.Data.Sqlite will only work on Unix-based platforms. + if (platform == PlatformID.Unix || platform == PlatformID.MacOSX) { + if ((sqliteAssembly = SQLiteAssembly.Load ("Mono.Data.Sqlite")) != null) { + // Make sure that the runtime can load the native sqlite3 library + if (VerifySQLiteAssemblyIsUsable ()) { IsAvailable = true; + return; } } + } +#endif - // System.Data.Sqlite is only available for Windows-based platforms. - if (!IsAvailable && platform != PlatformID.Unix && platform != PlatformID.MacOSX) { - if ((sqliteAssembly = Assembly.Load ("System.Data.SQLite")) != null) { - sqliteConnectionClass = sqliteAssembly.GetType ("System.Data.SQLite.SQLiteConnection"); - sqliteConnectionStringBuilderClass = sqliteAssembly.GetType ("System.Data.SQLite.SQLiteConnectionStringBuilder"); - - // Make sure that the runtime can load the native sqlite3 library - var builder = Activator.CreateInstance (sqliteConnectionStringBuilderClass); - sqliteConnectionStringBuilderClass.GetProperty ("DateTimeFormat").SetValue (builder, 0, null); - - IsAvailable = true; - } +#if NETFRAMEWORK || NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_0 + if ((sqliteAssembly = SQLiteAssembly.Load ("System.Data.SQLite")) != null) { + // Make sure that the runtime can load the native sqlite3 library + if (VerifySQLiteAssemblyIsUsable ()) { + IsAvailable = true; + return; } - } catch (FileNotFoundException) { - } catch (FileLoadException) { - } catch (BadImageFormatException) { } -#else - IsAvailable = true; #endif +#endif // __MOBILE__ } +#if !__MOBILE__ + static bool VerifySQLiteAssemblyIsUsable () + { + // Make sure that the runtime can load the native sqlite3 library. + var fileName = Path.GetTempFileName (); + + try { + var connection = CreateConnection (fileName); + connection.Dispose (); + return true; + } catch { + return false; + } finally { + File.Delete (fileName); + } + } +#endif + internal static bool IsAvailable { get; private set; } @@ -127,47 +168,39 @@ static DbConnection CreateConnection (string fileName) if (fileName.Length == 0) throw new ArgumentException ("The file name cannot be empty.", nameof (fileName)); -#if !__MOBILE__ - var dateTimeFormat = sqliteConnectionStringBuilderClass.GetProperty ("DateTimeFormat"); - var builder = Activator.CreateInstance (sqliteConnectionStringBuilderClass); - - sqliteConnectionStringBuilderClass.GetProperty ("DataSource").SetValue (builder, fileName, null); - - if (dateTimeFormat != null) - dateTimeFormat.SetValue (builder, 0, null); - if (!File.Exists (fileName)) { var dir = Path.GetDirectoryName (fileName); if (!string.IsNullOrEmpty (dir) && !Directory.Exists (dir)) Directory.CreateDirectory (dir); +#if __MOBILE__ + SqliteConnection.CreateFile (fileName); +#else File.Create (fileName).Dispose (); +#endif } - var connectionString = (string) sqliteConnectionStringBuilderClass.GetProperty ("ConnectionString").GetValue (builder, null); +#if !__MOBILE__ + var builder = Activator.CreateInstance (sqliteAssembly.ConnectionStringBuilderType); + + sqliteAssembly.DataSourceProperty.SetValue (builder, fileName, null); + sqliteAssembly.DateTimeFormatProperty?.SetValue (builder, 0, null); + + var connectionString = (string) sqliteAssembly.ConnectionStringProperty.GetValue (builder, null); - return (DbConnection) Activator.CreateInstance (sqliteConnectionClass, new [] { connectionString }); + return (DbConnection) Activator.CreateInstance (sqliteAssembly.ConnectionType, new [] { connectionString }); #else var builder = new SqliteConnectionStringBuilder (); builder.DateTimeFormat = SQLiteDateFormats.Ticks; builder.DataSource = fileName; - if (!File.Exists (fileName)) { - var dir = Path.GetDirectoryName (fileName); - - if (!string.IsNullOrEmpty (dir) && !Directory.Exists (dir)) - Directory.CreateDirectory (dir); - - SqliteConnection.CreateFile (fileName); - } - return new SqliteConnection (builder.ConnectionString); #endif } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new and opens a connection to the @@ -193,12 +226,12 @@ static DbConnection CreateConnection (string fileName) /// /// An error occurred reading the file. /// - public SqliteCertificateDatabase (string fileName, string password) : base (CreateConnection (fileName), password) + public SqliteCertificateDatabase (string fileName, string password) : this (CreateConnection (fileName), password) { } ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new public class TemporarySecureMimeContext : BouncyCastleSecureMimeContext { - readonly Dictionaryusing the provided SQLite database connection. @@ -215,89 +248,147 @@ public SqliteCertificateDatabase (DbConnection connection, string password) : ba } /// - /// Gets the command to create the certificates table. + /// Gets the columns for the specified table. /// ///- /// Constructs the command to create a certificates table suitable for storing - /// - ///objects. + /// Gets the list of columns for the specified table. /// The /// The. . - protected override DbCommand GetCreateCertificatesTableCommand (DbConnection connection) + /// The name of the table. + /// The list of columns. + protected override IListGetTableColumns (DbConnection connection, string tableName) { - var statement = new StringBuilder ("CREATE TABLE IF NOT EXISTS CERTIFICATES("); - var columns = X509CertificateRecord.ColumnNames; - - for (int i = 0; i < columns.Length; i++) { - if (i > 0) - statement.Append (", "); - - statement.Append (columns[i] + " "); - switch (columns[i]) { - case "ID": statement.Append ("INTEGER PRIMARY KEY AUTOINCREMENT"); break; - case "BASICCONSTRAINTS": statement.Append ("INTEGER NOT NULL"); break; - case "TRUSTED": statement.Append ("INTEGER NOT NULL"); break; - case "KEYUSAGE": statement.Append ("INTEGER NOT NULL"); break; - case "NOTBEFORE": statement.Append ("INTEGER NOT NULL"); break; - case "NOTAFTER": statement.Append ("INTEGER NOT NULL"); break; - case "ISSUERNAME": statement.Append ("TEXT NOT NULL"); break; - case "SERIALNUMBER": statement.Append ("TEXT NOT NULL"); break; - case "SUBJECTEMAIL": statement.Append ("TEXT"); break; - case "FINGERPRINT": statement.Append ("TEXT NOT NULL"); break; - case "ALGORITHMS": statement.Append ("TEXT"); break; - case "ALGORITHMSUPDATED": statement.Append ("INTEGER NOT NULL"); break; - case "CERTIFICATE": statement.Append ("BLOB UNIQUE NOT NULL"); break; - case "PRIVATEKEY": statement.Append ("BLOB"); break; + using (var command = connection.CreateCommand ()) { + command.CommandText = $"PRAGMA table_info({tableName})"; + using (var reader = command.ExecuteReader ()) { + var columns = new List (); + + while (reader.Read ()) { + var column = new DataColumn (); + + for (int i = 0; i < reader.FieldCount; i++) { + var field = reader.GetName (i).ToUpperInvariant (); + + switch (field) { + case "NAME": + column.ColumnName = reader.GetString (i); + break; + case "TYPE": + var type = reader.GetString (i); + switch (type) { + case "INTEGER": column.DataType = typeof (long); break; + case "BLOB": column.DataType = typeof (byte[]); break; + case "TEXT": column.DataType = typeof (string); break; + } + break; + case "NOTNULL": + column.AllowDBNull = !reader.GetBoolean (i); + break; + } + } + + columns.Add (column); + } + + return columns; } } + } - statement.Append (')'); + static void Build (StringBuilder statement, DataTable table, DataColumn column, ref int primaryKeys, bool create) + { + statement.Append (column.ColumnName); + statement.Append (' '); + + if (column.DataType == typeof (long) || column.DataType == typeof (int) || column.DataType == typeof (bool)) { + statement.Append ("INTEGER"); + } else if (column.DataType == typeof (byte[])) { + statement.Append ("BLOB"); + } else if (column.DataType == typeof (string)) { + statement.Append ("TEXT"); + } else { + throw new NotImplementedException (); + } + + bool isPrimaryKey = false; + if (table != null && table.PrimaryKey != null && primaryKeys < table.PrimaryKey.Length) { + for (int i = 0; i < table.PrimaryKey.Length; i++) { + if (column == table.PrimaryKey[i]) { + statement.Append (" PRIMARY KEY"); + isPrimaryKey = true; + primaryKeys++; + break; + } + } + } - var command = connection.CreateCommand (); + if (column.AutoIncrement) + statement.Append (" AUTOINCREMENT"); - command.CommandText = statement.ToString (); - command.CommandType = CommandType.Text; + if (column.Unique && !isPrimaryKey) + statement.Append (" UNIQUE"); - return command; + // Note: Normally we'd want to include NOT NULL, but we can't *add* new columns with the NOT NULL restriction + if (create && !column.AllowDBNull) + statement.Append (" NOT NULL"); } /// - /// Gets the command to create the CRLs table. + /// Create a table. /// ///- /// Constructs the command to create a CRLs table suitable for storing - /// - ///objects. + /// Creates the specified table. /// The /// The. . - protected override DbCommand GetCreateCrlsTableCommand (DbConnection connection) + /// The table. + protected override void CreateTable (DbConnection connection, DataTable table) { - var statement = new StringBuilder ("CREATE TABLE IF NOT EXISTS CRLS("); - var columns = X509CrlRecord.ColumnNames; - - for (int i = 0; i < columns.Length; i++) { - if (i > 0) - statement.Append (", "); - - statement.Append (columns[i] + " "); - switch (columns[i]) { - case "ID": statement.Append ("INTEGER PRIMARY KEY AUTOINCREMENT"); break; - case "DELTA" : statement.Append ("INTEGER NOT NULL"); break; - case "ISSUERNAME": statement.Append ("TEXT NOT NULL"); break; - case "THISUPDATE": statement.Append ("INTEGER NOT NULL"); break; - case "NEXTUPDATE": statement.Append ("INTEGER NOT NULL"); break; - case "CRL": statement.Append ("BLOB NOT NULL"); break; - } + var statement = new StringBuilder ("CREATE TABLE IF NOT EXISTS "); + int primaryKeys = 0; + + statement.Append (table.TableName); + statement.Append ('('); + + foreach (DataColumn column in table.Columns) { + Build (statement, table, column, ref primaryKeys, true); + statement.Append (", "); } + if (table.Columns.Count > 0) + statement.Length -= 2; + statement.Append (')'); - var command = connection.CreateCommand (); + using (var command = connection.CreateCommand ()) { + command.CommandText = statement.ToString (); + command.CommandType = CommandType.Text; + command.ExecuteNonQuery (); + } + } - command.CommandText = statement.ToString (); - command.CommandType = CommandType.Text; + /// + /// Adds a column to a table. + /// + ///+ /// Adds a column to a table. + /// + /// The. + /// The table. + /// The column to add. + protected override void AddTableColumn (DbConnection connection, DataTable table, DataColumn column) + { + var statement = new StringBuilder ("ALTER TABLE "); + int primaryKeys = table.PrimaryKey?.Length ?? 0; - return command; + statement.Append (table.TableName); + statement.Append (" ADD COLUMN "); + Build (statement, table, column, ref primaryKeys, false); + + using (var command = connection.CreateCommand ()) { + command.CommandText = statement.ToString (); + command.CommandType = CommandType.Text; + command.ExecuteNonQuery (); + } } } } diff --git a/MimeKit/Cryptography/SubjectIdentifierType.cs b/MimeKit/Cryptography/SubjectIdentifierType.cs index 2eb5e0e2bc..6206f45f71 100644 --- a/MimeKit/Cryptography/SubjectIdentifierType.cs +++ b/MimeKit/Cryptography/SubjectIdentifierType.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Cryptography/TemporarySecureMimeContext.cs b/MimeKit/Cryptography/TemporarySecureMimeContext.cs index 6ea222a295..7400847475 100644 --- a/MimeKit/Cryptography/TemporarySecureMimeContext.cs +++ b/MimeKit/Cryptography/TemporarySecureMimeContext.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -46,22 +46,24 @@ namespace MimeKit.Cryptography { /// capabilities; - readonly Dictionary keys; - readonly List certificates; + readonly Dictionary capabilities; + internal readonly Dictionary keys; + internal readonly List certificates; + readonly HashSet fingerprints; readonly List crls; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new public TemporarySecureMimeContext () { - capabilities = new Dictionary. /// (); - keys = new Dictionary (); + capabilities = new Dictionary (StringComparer.Ordinal); + keys = new Dictionary (StringComparer.Ordinal); certificates = new List (); + fingerprints = new HashSet (); crls = new List (); } @@ -139,9 +141,10 @@ protected override X509Certificate GetCertificate (IX509Selector selector) protected override AsymmetricKeyParameter GetPrivateKey (IX509Selector selector) { foreach (var certificate in certificates) { + var fingerprint = certificate.GetFingerprint (); AsymmetricKeyParameter key; - if (!keys.TryGetValue (certificate, out key)) + if (!keys.TryGetValue (fingerprint, out key)) continue; if (selector != null && !selector.Match (certificate)) @@ -166,7 +169,10 @@ protected override Org.BouncyCastle.Utilities.Collections.HashSet GetTrustedAnch var anchors = new Org.BouncyCastle.Utilities.Collections.HashSet (); foreach (var certificate in certificates) { - anchors.Add (new TrustAnchor (certificate, null)); + var keyUsage = certificate.GetKeyUsage (); + + if (keyUsage != null && keyUsage[(int) X509KeyUsageBits.KeyCertSign] && certificate.IsSelfSigned ()) + anchors.Add (new TrustAnchor (certificate, null)); } return anchors; @@ -183,13 +189,16 @@ protected override Org.BouncyCastle.Utilities.Collections.HashSet GetTrustedAnch /// The intermediate certificates. protected override IX509Store GetIntermediateCertificates () { - var store = new X509CertificateStore (); + var intermediates = new X509CertificateStore (); foreach (var certificate in certificates) { - store.Add (certificate); + var keyUsage = certificate.GetKeyUsage (); + + if (keyUsage != null && keyUsage[(int) X509KeyUsageBits.KeyCertSign] && !certificate.IsSelfSigned ()) + intermediates.Add (certificate); } - return store; + return intermediates; } ///@@ -249,7 +258,7 @@ X509Certificate GetCmsRecipientCertificate (MailboxAddress mailbox) } else { var address = certificate.GetSubjectEmailAddress (); - if (address == null || !address.Equals (mailbox.Address, StringComparison.OrdinalIgnoreCase)) + if (!address.Equals (mailbox.Address, StringComparison.OrdinalIgnoreCase)) continue; } @@ -284,7 +293,7 @@ protected override CmsRecipient GetCmsRecipient (MailboxAddress mailbox) var recipient = new CmsRecipient (certificate); EncryptionAlgorithm[] algorithms; - if (capabilities.TryGetValue (certificate, out algorithms)) + if (capabilities.TryGetValue (certificate.GetFingerprint (), out algorithms)) recipient.EncryptionAlgorithms = algorithms; return recipient; @@ -303,18 +312,18 @@ X509Certificate GetCmsSignerCertificate (MailboxAddress mailbox, out AsymmetricK if (keyUsage != 0 && (keyUsage & DigitalSignatureKeyUsageFlags) == 0) continue; - if (!keys.TryGetValue (certificate, out key)) + var fingerprint = certificate.GetFingerprint (); + + if (!keys.TryGetValue (fingerprint, out key)) continue; if (secure != null) { - var fingerprint = certificate.GetFingerprint (); - if (!fingerprint.Equals (secure.Fingerprint, StringComparison.OrdinalIgnoreCase)) continue; } else { var address = certificate.GetSubjectEmailAddress (); - if (address == null || !address.Equals (mailbox.Address, StringComparison.OrdinalIgnoreCase)) + if (!address.Equals (mailbox.Address, StringComparison.OrdinalIgnoreCase)) continue; } @@ -350,7 +359,7 @@ protected override CmsSigner GetCmsSigner (MailboxAddress mailbox, DigestAlgorit if ((certificate = GetCmsSignerCertificate (mailbox, out key)) == null) throw new CertificateNotFoundException (mailbox, "A valid signing certificate could not be found."); - return new CmsSigner (certificate, key) { + return new CmsSigner (BuildCertificateChain (certificate), key) { DigestAlgorithm = digestAlgo }; } @@ -366,7 +375,7 @@ protected override CmsSigner GetCmsSigner (MailboxAddress mailbox, DigestAlgorit /// The timestamp. protected override void UpdateSecureMimeCapabilities (X509Certificate certificate, EncryptionAlgorithm[] algorithms, DateTime timestamp) { - capabilities[certificate] = algorithms; + capabilities[certificate.GetFingerprint ()] = algorithms; } /// @@ -398,12 +407,15 @@ public override void Import (Stream stream, string password) var entry = pkcs12.GetKey (alias); for (int i = 0; i < chain.Length; i++) - certificates.Add (chain[i].Certificate); + Import (chain[i].Certificate); - keys.Add (chain[0].Certificate, entry.Key); + var fingerprint = chain[0].Certificate.GetFingerprint (); + if (!keys.ContainsKey (fingerprint)) + keys.Add (fingerprint, entry.Key); } else if (pkcs12.IsCertificateEntry (alias)) { var entry = pkcs12.GetCertificate (alias); - certificates.Add (entry.Certificate); + + Import (entry.Certificate); } } } @@ -423,7 +435,8 @@ public override void Import (X509Certificate certificate) if (certificate == null) throw new ArgumentNullException (nameof (certificate)); - certificates.Add (certificate); + if (fingerprints.Add (certificate.GetFingerprint ())) + certificates.Add (certificate); } /// diff --git a/MimeKit/Cryptography/WindowsSecureMimeContext.cs b/MimeKit/Cryptography/WindowsSecureMimeContext.cs index 2fa96705d8..ac3efaced6 100644 --- a/MimeKit/Cryptography/WindowsSecureMimeContext.cs +++ b/MimeKit/Cryptography/WindowsSecureMimeContext.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -60,7 +60,7 @@ public class WindowsSecureMimeContext : SecureMimeContext const X509KeyStorageFlags DefaultKeyStorageFlags = X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -90,7 +90,7 @@ public WindowsSecureMimeContext (StoreLocation location) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Constructs an S/MIME context using the current user's X.509 store location. @@ -223,7 +223,7 @@ protected virtual RealCmsRecipient GetCmsRecipient (MailboxAddress mailbox) if ((certificate = GetRecipientCertificate (mailbox)) == null) throw new CertificateNotFoundException (mailbox, "A valid certificate could not be found."); - return new RealCmsRecipient (RealSubjectIdentifierType.SubjectKeyIdentifier, certificate); + return new RealCmsRecipient (certificate); } /// @@ -260,13 +260,28 @@ RealCmsRecipientCollection GetCmsRecipients (CmsRecipientCollection recipients) foreach (var recipient in recipients) { var certificate = new X509Certificate2 (recipient.Certificate.GetEncoded ()); RealSubjectIdentifierType type; + RealCmsRecipient real; - if (recipient.RecipientIdentifierType == SubjectIdentifierType.IssuerAndSerialNumber) + if (recipient.RecipientIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) type = RealSubjectIdentifierType.IssuerAndSerialNumber; else type = RealSubjectIdentifierType.SubjectKeyIdentifier; - collection.Add (new RealCmsRecipient (type, certificate)); +#if NETCOREAPP3_0 + var padding = recipient.RsaEncryptionPadding?.AsRSAEncryptionPadding (); + + if (padding != null) + real = new RealCmsRecipient (type, certificate, padding); + else + real = new RealCmsRecipient (type, certificate); +#else + if (recipient.RsaEncryptionPadding?.Scheme == RsaEncryptionPaddingScheme.Oaep) + throw new NotSupportedException ("The RSAES-OAEP encryption padding scheme is not supported by the WindowsSecureMimeContext. You must use a subclass of BouncyCastleSecureMimeContext to get this feature."); + + real = new RealCmsRecipient (type, certificate); +#endif + + collection.Add (real); } return collection; @@ -324,14 +339,14 @@ protected virtual X509Certificate2 GetSignerCertificate (MailboxAddress mailbox) AsnEncodedData GetSecureMimeCapabilities () { - var attr = GetSecureMimeCapabilitiesAttribute (); + var attr = GetSecureMimeCapabilitiesAttribute (false); return new AsnEncodedData (attr.AttrType.Id, attr.AttrValues[0].GetEncoded ()); } - RealCmsSigner GetRealCmsSigner (X509Certificate2 certificate, DigestAlgorithm digestAlgo) + RealCmsSigner GetRealCmsSigner (RealSubjectIdentifierType type, X509Certificate2 certificate, DigestAlgorithm digestAlgo) { - var signer = new RealCmsSigner (certificate); + var signer = new RealCmsSigner (type, certificate); signer.DigestAlgorithm = new Oid (GetDigestOid (digestAlgo)); signer.SignedAttributes.Add (GetSecureMimeCapabilities ()); signer.SignedAttributes.Add (new Pkcs9SigningTime ()); @@ -341,11 +356,20 @@ RealCmsSigner GetRealCmsSigner (X509Certificate2 certificate, DigestAlgorithm di RealCmsSigner GetRealCmsSigner (CmsSigner signer) { + if (signer.RsaSignaturePadding == RsaSignaturePadding.Pss) + throw new NotSupportedException ("The RSASSA-PSS signature padding scheme is not supported by the WindowsSecureMimeContext. You must use a subclass of BouncyCastleSecureMimeContext to get this feature."); + var certificate = signer.Certificate.AsX509Certificate2 (); + RealSubjectIdentifierType type; + + if (signer.SignerIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) + type = RealSubjectIdentifierType.IssuerAndSerialNumber; + else + type = RealSubjectIdentifierType.SubjectKeyIdentifier; certificate.PrivateKey = signer.PrivateKey.AsAsymmetricAlgorithm (); - return GetRealCmsSigner (certificate, signer.DigestAlgorithm); + return GetRealCmsSigner (type, certificate, signer.DigestAlgorithm); } /// @@ -371,7 +395,7 @@ protected virtual RealCmsSigner GetCmsSigner (MailboxAddress mailbox, DigestAlgo if ((certificate = GetSignerCertificate (mailbox)) == null) throw new CertificateNotFoundException (mailbox, "A valid signing certificate could not be found."); - return GetRealCmsSigner (certificate, digestAlgo); + return GetRealCmsSigner (RealSubjectIdentifierType.IssuerAndSerialNumber, certificate, digestAlgo); } /// @@ -379,8 +403,9 @@ protected virtual RealCmsSigner GetCmsSigner (MailboxAddress mailbox, DigestAlgo /// ////// /// The certificate. /// The encryption algorithm capabilities of the client (in preferred order). @@ -424,10 +449,10 @@ Stream Sign (RealCmsSigner signer, Stream content, bool detach) var signed = new SignedCms (contentInfo, detach); try { - signed.ComputeSignature (signer); + signed.ComputeSignature (signer, false); } catch (CryptographicException) { signer.IncludeOption = X509IncludeOption.EndCertOnly; - signed.ComputeSignature (signer); + signed.ComputeSignature (signer, false); } var signedData = signed.Encode (); @@ -441,7 +466,7 @@ Stream Sign (RealCmsSigner signer, Stream content, bool detach) ///Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. - ///This method is called from + ///, allowing custom implementations - /// to update the X.509 certificate records with the list of preferred encryption algorithms specified by the sending client. This method is called when decoding digital signatures that include S/MIME capabilities in the metadata, allowing custom + /// implementations to update the X.509 certificate records with the list of preferred encryption algorithms specified by the + /// sending client. ////// Cryptographically signs and encapsulates the content using the specified signer. /// - ///A new instance + /// A new /// The signer. /// The content. @@ -463,7 +488,7 @@ public override ApplicationPkcs7Mime EncapsulatedSign (CmsSigner signer, Stream var real = GetRealCmsSigner (signer); - return new ApplicationPkcs7Mime (SecureMimeType.SignedData, Sign (real, content, true)); + return new ApplicationPkcs7Mime (SecureMimeType.SignedData, Sign (real, content, false)); } ///instance /// containing the detached signature data. @@ -472,7 +497,7 @@ public override ApplicationPkcs7Mime EncapsulatedSign (CmsSigner signer, Stream /// /// Sign and encapsulate the content using the specified signer. /// - ///A new instance + /// A new /// The signer. /// The digest algorithm to use for signing. @@ -513,7 +538,7 @@ public override ApplicationPkcs7Mime EncapsulatedSign (MailboxAddress signer, Di ///instance /// containing the detached signature data. /// Cryptographically signs the content using the specified signer. /// - ///A new instance + /// A new /// The signer. /// The content. @@ -535,7 +560,7 @@ public override ApplicationPkcs7Signature Sign (CmsSigner signer, Stream content var real = GetRealCmsSigner (signer); - return new ApplicationPkcs7Signature (Sign (real, content, false)); + return new ApplicationPkcs7Signature (Sign (real, content, true)); } ///instance /// containing the detached signature data. @@ -544,7 +569,7 @@ public override ApplicationPkcs7Signature Sign (CmsSigner signer, Stream content /// /// Sign the content using the specified signer. /// - ///A new instance + /// A new /// The signer. /// The digest algorithm to use for signing. @@ -638,12 +663,12 @@ DigitalSignatureCollection GetDigitalSignatures (SignedCms signed) ///instance /// containing the detached signature data. -or- ////// is null .- /// An error occurred in the cryptographic message syntax subsystem. - /// ////// The operation was cancelled via the cancellation token. /// + ///+ /// An error occurred in the cryptographic message syntax subsystem. + /// public override DigitalSignatureCollection Verify (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) { if (content == null) @@ -675,12 +700,12 @@ DigitalSignatureCollection GetDigitalSignatures (SignedCms signed) ///-or- ////// is null .- /// An error occurred in the cryptographic message syntax subsystem. - /// ////// The operation was cancelled via the cancellation token. /// + ///+ /// An error occurred in the cryptographic message syntax subsystem. + /// public override async TaskVerifyAsync (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) { if (content == null) @@ -713,21 +738,21 @@ DigitalSignatureCollection GetDigitalSignatures (SignedCms signed) /// /// The extracted content could not be parsed as a MIME entity. /// - ///- /// An error occurred in the cryptographic message syntax subsystem. - /// ////// The operation was cancelled via the cancellation token. /// + ///+ /// An error occurred in the cryptographic message syntax subsystem. + /// public override DigitalSignatureCollection Verify (Stream signedData, out MimeEntity entity, CancellationToken cancellationToken = default (CancellationToken)) { if (signedData == null) throw new ArgumentNullException (nameof (signedData)); - var contentInfo = new ContentInfo (ReadAllBytes (signedData)); + var content = ReadAllBytes (signedData); var signed = new SignedCms (); - signed.Decode (ReadAllBytes (signedData)); + signed.Decode (content); var memory = new MemoryStream (signed.ContentInfo.Content, false); @@ -754,21 +779,21 @@ DigitalSignatureCollection GetDigitalSignatures (SignedCms signed) ////// - ///is null . ///- /// An error occurred in the cryptographic message syntax subsystem. - /// ////// The operation was cancelled via the cancellation token. /// + ///+ /// An error occurred in the cryptographic message syntax subsystem. + /// public override Stream Verify (Stream signedData, out DigitalSignatureCollection signatures, CancellationToken cancellationToken = default (CancellationToken)) { if (signedData == null) throw new ArgumentNullException (nameof (signedData)); - var contentInfo = new ContentInfo (ReadAllBytes (signedData)); + var content = ReadAllBytes (signedData); var signed = new SignedCms (); - signed.Decode (ReadAllBytes (signedData)); + signed.Decode (content); signatures = GetDigitalSignatures (signed); @@ -885,7 +910,7 @@ Stream Envelope (CmsRecipientCollection recipients, Stream content) ////// Encrypts the specified content for the specified recipients. /// - ///A new public override bool Equals (object obj) { if (!(obj is TnefPropertyTag)) @@ -5649,12 +5713,12 @@ public override bool Equals (object obj) } ///instance + /// A new /// The recipients. /// The content. @@ -914,7 +939,7 @@ public override ApplicationPkcs7Mime Encrypt (CmsRecipientCollection recipients, ///instance /// containing the encrypted content. /// Encrypts the specified content for the specified recipients. /// - ///A new public override bool Equals (object obj) { var prop = obj as TnefPropertyReader; diff --git a/MimeKit/Tnef/TnefPropertyTag.cs b/MimeKit/Tnef/TnefPropertyTag.cs index 577ac1c684..a72d50cbd2 100644 --- a/MimeKit/Tnef/TnefPropertyTag.cs +++ b/MimeKit/Tnef/TnefPropertyTag.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastinstance + /// A new /// The recipients. /// The content. @@ -951,7 +976,7 @@ public override MimePart Encrypt (IEnumerableinstance /// containing the encrypted data. recipients, Stream /// /// Decrypt the encrypted data. /// - ///The decrypted + ///. The decrypted /// The encrypted data. /// The cancellation token. ///. @@ -969,9 +994,22 @@ public override MimePart Encrypt (IEnumerable public bool ReadNextRow () { - if (rowIndex >= rowCount) - return false; - while (ReadNextProperty ()) { - // skip over the property... + // skip over the remaining property/properties in the current row... } + if (rowIndex >= rowCount) + return false; + try { LoadPropertyCount (); rowIndex++; @@ -797,7 +789,7 @@ public int ReadTextValue (char[] buffer, int offset, int count) var bytes = new byte[n]; - n = reader.ReadAttributeRawValue (bytes, 0, bytes.Length); + n = reader.ReadAttributeRawValue (bytes, 0, n); var flush = reader.StreamOffset >= valueEndOffset; @@ -1518,10 +1510,36 @@ public string ReadValueAsString () } ///recipients, Stream throw new ArgumentNullException (nameof (encryptedData)); var enveloped = new EnvelopedCms (); + CryptographicException ce = null; enveloped.Decode (ReadAllBytes (encryptedData)); - enveloped.Decrypt (); + + foreach (var recipient in enveloped.RecipientInfos) { + try { + enveloped.Decrypt (recipient); + ce = null; + break; + } catch (CryptographicException ex) { + ce = ex; + } + } + + if (ce != null) + throw ce; var decryptedData = enveloped.Encode (); @@ -1101,7 +1139,12 @@ public override void Import (X509Crl crl) if (crl == null) throw new ArgumentNullException (nameof (crl)); - foreach (Org.BouncyCastle.X509.X509Certificate certificate in crl.GetRevokedCertificates ()) + var revoked = crl.GetRevokedCertificates (); + + if (revoked == null) + return; + + foreach (Org.BouncyCastle.X509.X509Certificate certificate in revoked) Import (StoreName.Disallowed, certificate); } @@ -1161,7 +1204,7 @@ public override void Import (Stream stream, string password) /// /// Exports the certificates for the specified mailboxes. /// - ///A new public override bool Equals (object obj) { if (!(obj is TnefNameId)) diff --git a/MimeKit/Tnef/TnefNameIdKind.cs b/MimeKit/Tnef/TnefNameIdKind.cs index bb7d4af9eb..ff1c580e10 100644 --- a/MimeKit/Tnef/TnefNameIdKind.cs +++ b/MimeKit/Tnef/TnefNameIdKind.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastinstance containing + /// A new /// The mailboxes. ///instance containing /// the exported keys. @@ -1173,7 +1216,7 @@ public override void Import (Stream stream, string password) /// ////// A certificate for one or more of the - ///could not be found. /// + /// public bool Verify () + { + return Verify (false); + } + + ////// An error occurred in the cryptographic message syntax subsystem. /// public override MimePart Export (IEnumerablemailboxes) @@ -1197,9 +1240,9 @@ public override MimePart Export (IEnumerable mailboxes) throw new ArgumentException ("No mailboxes specified.", nameof (mailboxes)); var cms = new CmsSignedDataStreamGenerator (); - var memory = new MemoryBlockStream (); - cms.AddCertificates (certificates); + + var memory = new MemoryBlockStream (); cms.Open (memory).Dispose (); memory.Position = 0; diff --git a/MimeKit/Cryptography/WindowsSecureMimeDigitalCertificate.cs b/MimeKit/Cryptography/WindowsSecureMimeDigitalCertificate.cs index 218b0713c3..c95b8233e1 100644 --- a/MimeKit/Cryptography/WindowsSecureMimeDigitalCertificate.cs +++ b/MimeKit/Cryptography/WindowsSecureMimeDigitalCertificate.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 Xamarin Inc. (www.xamarin.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 @@ -38,7 +38,7 @@ namespace MimeKit.Cryptography { public class WindowsSecureMimeDigitalCertificate : IDigitalCertificate { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -53,17 +53,7 @@ public WindowsSecureMimeDigitalCertificate (X509Certificate2 certificate) throw new ArgumentNullException (nameof (certificate)); Certificate = certificate; - - var algorithm = certificate.PublicKey.Key; - - if (algorithm is DSA) - PublicKeyAlgorithm = PublicKeyAlgorithm.Dsa; - else if (algorithm is RSA) - PublicKeyAlgorithm = PublicKeyAlgorithm.RsaGeneral; - else if (algorithm is ECDiffieHellman) - PublicKeyAlgorithm = PublicKeyAlgorithm.DiffieHellman; - else if (algorithm is ECDsa) - PublicKeyAlgorithm = PublicKeyAlgorithm.EdwardsCurveDsa; + PublicKeyAlgorithm = certificate.GetPublicKeyAlgorithm (); } /// diff --git a/MimeKit/Cryptography/WindowsSecureMimeDigitalSignature.cs b/MimeKit/Cryptography/WindowsSecureMimeDigitalSignature.cs index 5bb7fdadcf..7754ab3489 100644 --- a/MimeKit/Cryptography/WindowsSecureMimeDigitalSignature.cs +++ b/MimeKit/Cryptography/WindowsSecureMimeDigitalSignature.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 Xamarin Inc. (www.xamarin.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 @@ -46,7 +46,7 @@ public class WindowsSecureMimeDigitalSignature : IDigitalSignature DigitalSignatureVerifyException vex; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///. @@ -141,7 +141,7 @@ public IDigitalCertificate SignerCertificate { /// The public key algorithm. public PublicKeyAlgorithm PublicKeyAlgorithm { - get { return PublicKeyAlgorithm.None; } + get { return SignerCertificate != null ? SignerCertificate.PublicKeyAlgorithm : PublicKeyAlgorithm.None; } } ///@@ -177,12 +177,28 @@ public DateTime CreationDate { /// An error verifying the signature has occurred. /// + /// Verifies the digital signature. + /// + ///+ /// Verifies the digital signature. + /// + ///true if only the signature itself should be verified; otherwise, both the signature and the certificate chain are validated. + ///+ /// true if the signature is valid; otherwise,false .+ /// An error verifying the signature has occurred. + /// + public bool Verify (bool verifySignatureOnly) { if (vex != null) throw vex; try { - SignerInfo.CheckSignature (false); + SignerInfo.CheckSignature (verifySignatureOnly); return true; } catch (Exception ex) { var message = string.Format ("Failed to verify digital signature: {0}", ex.Message); @@ -193,4 +209,4 @@ public bool Verify () #endregion } -} \ No newline at end of file +} diff --git a/MimeKit/Cryptography/X509Certificate2Extensions.cs b/MimeKit/Cryptography/X509Certificate2Extensions.cs index 4107178568..9184315ab4 100644 --- a/MimeKit/Cryptography/X509Certificate2Extensions.cs +++ b/MimeKit/Cryptography/X509Certificate2Extensions.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 Xamarin Inc. (www.xamarin.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 @@ -27,6 +27,7 @@ using System; using System.IO; using System.Collections.Generic; +using System.Security.Cryptography; using Org.BouncyCastle.X509; using Org.BouncyCastle.Asn1; @@ -61,8 +62,38 @@ public static X509Certificate AsBouncyCastleCertificate (this X509Certificate2 c throw new ArgumentNullException (nameof (certificate)); var rawData = certificate.GetRawCertData (); + var parser = new X509CertificateParser (); + var cert = parser.ReadCertificate (rawData); - return new X509CertificateParser ().ReadCertificate (rawData); + return cert; + } + + /// + /// Gets the public key algorithm for the certificate. + /// + ///+ /// Gets the public key algorithm for the ceretificate. + /// + ///The public key algorithm. + /// The certificate. + ///+ /// + public static PublicKeyAlgorithm GetPublicKeyAlgorithm (this X509Certificate2 certificate) + { + if (certificate == null) + throw new ArgumentNullException (nameof (certificate)); + + var identifier = certificate.GetKeyAlgorithm (); + var oid = new Oid (identifier); + + switch (oid.FriendlyName) { + case "DSA": return PublicKeyAlgorithm.Dsa; + case "RSA": return PublicKeyAlgorithm.RsaGeneral; + case "ECC": return PublicKeyAlgorithm.EllipticCurve; + case "DH": return PublicKeyAlgorithm.DiffieHellman; + default: return PublicKeyAlgorithm.None; + } } static EncryptionAlgorithm[] DecodeEncryptionAlgorithms (byte[] rawData) diff --git a/MimeKit/Cryptography/X509CertificateChain.cs b/MimeKit/Cryptography/X509CertificateChain.cs index 09d81e7035..9193fe323a 100644 --- a/MimeKit/Cryptography/X509CertificateChain.cs +++ b/MimeKit/Cryptography/X509CertificateChain.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastis null . + ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -43,7 +43,7 @@ public class X509CertificateChain : IList , IX509Store readonly List certificates; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new X.509 certificate chain. @@ -54,7 +54,7 @@ public X509CertificateChain () } /// public class X509CertificateRecord { - internal static readonly string[] ColumnNames = { - "ID", - "BASICCONSTRAINTS", - "TRUSTED", - "KEYUSAGE", - "NOTBEFORE", - "NOTAFTER", - "ISSUERNAME", - "SERIALNUMBER", - "SUBJECTEMAIL", - "FINGERPRINT", - "ALGORITHMS", - "ALGORITHMSUPDATED", - "CERTIFICATE", - "PRIVATEKEY" - }; - ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new X.509 certificate chain based on the specified collection of certificates. @@ -168,7 +168,7 @@ public int Count { } /// public void Dispose () { Dispose (true); diff --git a/MimeKit/Cryptography/X509CertificateRecord.cs b/MimeKit/Cryptography/X509CertificateRecord.cs index 33dcead980..fa971db127 100644 --- a/MimeKit/Cryptography/X509CertificateRecord.cs +++ b/MimeKit/Cryptography/X509CertificateRecord.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast- /// Gets a value indicating whether this instance is read only. + /// Get a value indicating whether the ///is read only. /// /// A ///is never read-only. diff --git a/MimeKit/Cryptography/X509CertificateDatabase.cs b/MimeKit/Cryptography/X509CertificateDatabase.cs index c8fb2f98c9..2a64837a3d 100644 --- a/MimeKit/Cryptography/X509CertificateDatabase.cs +++ b/MimeKit/Cryptography/X509CertificateDatabase.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -60,7 +60,7 @@ public abstract class X509CertificateDatabase : IX509CertificateDatabase readonly char[] passwd; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// The password is used to encrypt and decrypt private keys in the database and cannot be null. @@ -83,11 +83,11 @@ protected X509CertificateDatabase (string password) /// ////// Releases unmanaged resources and performs other cleanup operations before the - /// ///is reclaimed by garbage collection. + /// is reclaimed by garbage collection. /// /// Releases unmanaged resources and performs other cleanup operations before the - /// ~X509CertificateDatabase () { @@ -131,7 +131,7 @@ protected int SaltSize { static int ReadBinaryBlob (DbDataReader reader, int column, ref byte[] buffer) { -#if NETSTANDARD +#if NETSTANDARD1_3 || NETSTANDARD1_6 buffer = reader.GetFieldValueis reclaimed by garbage collection. + /// is reclaimed by garbage collection. /// (column); return (int) buffer.Length; #else @@ -373,7 +373,7 @@ protected static string[] GetColumnNames (X509CertificateRecordFields fields) /// The database command. /// The mailbox. /// The date and time for which the certificate should be valid. - ///true if the certificate must have a private key. + ///true if the certificate must have a private key; otherwise,false . /// The fields to return. protected abstract DbCommand GetSelectCommand (MailboxAddress mailbox, DateTime now, bool requirePrivateKey, X509CertificateRecordFields fields); @@ -384,11 +384,11 @@ protected static string[] GetColumnNames (X509CertificateRecordFields fields) /// Gets the database command to select certificate records matching the specified selector. ///The database command. - /// Selector. - ///true if only trusted certificates should be matched. - ///true if the certificate must have a private key. + /// The certificate selector. + ///true if only trusted anchor certificates should be matched; otherwise,false . + ///true if the certificate must have a private key; otherwise,false . /// The fields to return. - protected abstract DbCommand GetSelectCommand (IX509Selector selector, bool trustedOnly, bool requirePrivateKey, X509CertificateRecordFields fields); + protected abstract DbCommand GetSelectCommand (IX509Selector selector, bool trustedAnchorsOnly, bool requirePrivateKey, X509CertificateRecordFields fields); ////// Gets the column names for the specified fields. @@ -400,13 +400,6 @@ protected static string[] GetColumnNames (X509CertificateRecordFields fields) /// The fields. protected static string[] GetColumnNames (X509CrlRecordFields fields) { - const X509CrlRecordFields all = X509CrlRecordFields.Id | X509CrlRecordFields.IsDelta | - X509CrlRecordFields.IssuerName | X509CrlRecordFields.ThisUpdate | - X509CrlRecordFields.NextUpdate | X509CrlRecordFields.Crl; - - if (fields == all) - return new [] { "*" }; - var columns = new List (); if ((fields & X509CrlRecordFields.Id) != 0) @@ -491,14 +484,17 @@ protected static string[] GetColumnNames (X509CrlRecordFields fields) protected object GetValue (X509CertificateRecord record, string columnName) { switch (columnName) { - case "ID": return record.Id; + //case "ID": return record.Id; case "BASICCONSTRAINTS": return record.BasicConstraints; case "TRUSTED": return record.IsTrusted; + case "ANCHOR": return record.IsAnchor; case "KEYUSAGE": return (int) record.KeyUsage; case "NOTBEFORE": return record.NotBefore.ToUniversalTime (); case "NOTAFTER": return record.NotAfter.ToUniversalTime (); case "ISSUERNAME": return record.IssuerName; case "SERIALNUMBER": return record.SerialNumber; + case "SUBJECTNAME": return record.SubjectName; + case "SUBJECTKEYIDENTIFIER": return record.SubjectKeyIdentifier?.AsHex (); case "SUBJECTEMAIL": return record.SubjectEmail != null ? record.SubjectEmail.ToLowerInvariant () : string.Empty; case "FINGERPRINT": return record.Fingerprint.ToLowerInvariant (); case "ALGORITHMS": return EncodeEncryptionAlgorithms (record.Algorithms); @@ -524,7 +520,7 @@ protected object GetValue (X509CertificateRecord record, string columnName) protected static object GetValue (X509CrlRecord record, string columnName) { switch (columnName) { - case "ID": return record.Id; + //case "ID": return record.Id; case "DELTA": return record.IsDelta; case "ISSUERNAME": return record.IssuerName; case "THISUPDATE": return record.ThisUpdate; @@ -594,21 +590,13 @@ public X509CertificateRecord Find (X509Certificate certificate, X509CertificateR throw new ArgumentNullException (nameof (certificate)); using (var command = GetSelectCommand (certificate, fields)) { - var reader = command.ExecuteReader (); - - try { + using (var reader = command.ExecuteReader ()) { if (reader.Read ()) { var parser = new X509CertificateParser (); var buffer = new byte[4096]; return LoadCertificateRecord (reader, parser, ref buffer); } - } finally { -#if NETSTANDARD - reader.Dispose (); -#else - reader.Close (); -#endif } } @@ -627,9 +615,7 @@ public X509CertificateRecord Find (X509Certificate certificate, X509CertificateR public IEnumerable FindCertificates (IX509Selector selector) { using (var command = GetSelectCommand (selector, false, false, X509CertificateRecordFields.Certificate)) { - var reader = command.ExecuteReader (); - - try { + using (var reader = command.ExecuteReader ()) { var parser = new X509CertificateParser (); var buffer = new byte[4096]; @@ -638,12 +624,6 @@ public IEnumerable FindCertificates (IX509Selector selector) if (selector == null || selector.Match (record.Certificate)) yield return record.Certificate; } - } finally { -#if NETSTANDARD - reader.Dispose (); -#else - reader.Close (); -#endif } } @@ -662,9 +642,7 @@ public IEnumerable FindCertificates (IX509Selector selector) public IEnumerable FindPrivateKeys (IX509Selector selector) { using (var command = GetSelectCommand (selector, false, true, PrivateKeyFields)) { - var reader = command.ExecuteReader (); - - try { + using (var reader = command.ExecuteReader ()) { var parser = new X509CertificateParser (); var buffer = new byte[4096]; @@ -674,12 +652,6 @@ public IEnumerable FindPrivateKeys (IX509Selector select if (selector == null || selector.Match (record.Certificate)) yield return record.PrivateKey; } - } finally { -#if NETSTANDARD - reader.Dispose (); -#else - reader.Close (); -#endif } } @@ -708,21 +680,13 @@ public IEnumerable Find (MailboxAddress mailbox, DateTime throw new ArgumentNullException (nameof (mailbox)); using (var command = GetSelectCommand (mailbox, now, requirePrivateKey, fields)) { - var reader = command.ExecuteReader (); - - try { + using (var reader = command.ExecuteReader ()) { var parser = new X509CertificateParser (); var buffer = new byte[4096]; while (reader.Read ()) { yield return LoadCertificateRecord (reader, parser, ref buffer); } - } finally { -#if NETSTANDARD - reader.Dispose (); -#else - reader.Close (); -#endif } } @@ -738,14 +702,12 @@ public IEnumerable Find (MailboxAddress mailbox, DateTime /// The matching certificate records populated with the desired fields. /// The match selector ornull to match all certificates. - ///true if only trusted certificates should be returned. + ///true if only trusted anchor certificates should be returned. /// The desired fields. - public IEnumerableFind (IX509Selector selector, bool trustedOnly, X509CertificateRecordFields fields) + public IEnumerable Find (IX509Selector selector, bool trustedAnchorsOnly, X509CertificateRecordFields fields) { - using (var command = GetSelectCommand (selector, trustedOnly, false, fields | X509CertificateRecordFields.Certificate)) { - var reader = command.ExecuteReader (); - - try { + using (var command = GetSelectCommand (selector, trustedAnchorsOnly, false, fields | X509CertificateRecordFields.Certificate)) { + using (var reader = command.ExecuteReader ()) { var parser = new X509CertificateParser (); var buffer = new byte[4096]; @@ -755,12 +717,6 @@ public IEnumerable Find (IX509Selector selector, bool tru if (selector == null || selector.Match (record.Certificate)) yield return record; } - } finally { -#if NETSTANDARD - reader.Dispose (); -#else - reader.Close (); -#endif } } @@ -782,9 +738,8 @@ public void Add (X509CertificateRecord record) if (record == null) throw new ArgumentNullException (nameof (record)); - using (var command = GetInsertCommand (record)) { + using (var command = GetInsertCommand (record)) command.ExecuteNonQuery (); - } } /// @@ -802,9 +757,8 @@ public void Remove (X509CertificateRecord record) if (record == null) throw new ArgumentNullException (nameof (record)); - using (var command = GetDeleteCommand (record)) { + using (var command = GetDeleteCommand (record)) command.ExecuteNonQuery (); - } } /// @@ -823,9 +777,8 @@ public void Update (X509CertificateRecord record, X509CertificateRecordFields fi if (record == null) throw new ArgumentNullException (nameof (record)); - using (var command = GetUpdateCommand (record, fields)) { + using (var command = GetUpdateCommand (record, fields)) command.ExecuteNonQuery (); - } } /// @@ -847,21 +800,13 @@ public IEnumerable Find (X509Name issuer, X509CrlRecordFields fie throw new ArgumentNullException (nameof (issuer)); using (var command = GetSelectCommand (issuer, fields)) { - var reader = command.ExecuteReader (); - - try { + using (var reader = command.ExecuteReader ()) { var parser = new X509CrlParser (); var buffer = new byte[4096]; while (reader.Read ()) { yield return LoadCrlRecord (reader, parser, ref buffer); } - } finally { -#if NETSTANDARD - reader.Dispose (); -#else - reader.Close (); -#endif } } @@ -887,21 +832,13 @@ public X509CrlRecord Find (X509Crl crl, X509CrlRecordFields fields) throw new ArgumentNullException (nameof (crl)); using (var command = GetSelectCommand (crl, fields)) { - var reader = command.ExecuteReader (); - - try { + using (var reader = command.ExecuteReader ()) { if (reader.Read ()) { var parser = new X509CrlParser (); var buffer = new byte[4096]; return LoadCrlRecord (reader, parser, ref buffer); } - } finally { -#if NETSTANDARD - reader.Dispose (); -#else - reader.Close (); -#endif } } @@ -923,9 +860,8 @@ public void Add (X509CrlRecord record) if (record == null) throw new ArgumentNullException (nameof (record)); - using (var command = GetInsertCommand (record)) { + using (var command = GetInsertCommand (record)) command.ExecuteNonQuery (); - } } /// @@ -943,9 +879,8 @@ public void Remove (X509CrlRecord record) if (record == null) throw new ArgumentNullException (nameof (record)); - using (var command = GetDeleteCommand (record)) { + using (var command = GetDeleteCommand (record)) command.ExecuteNonQuery (); - } } /// @@ -963,9 +898,8 @@ public void Update (X509CrlRecord record) if (record == null) throw new ArgumentNullException (nameof (record)); - using (var command = GetUpdateCommand (record)) { + using (var command = GetUpdateCommand (record)) command.ExecuteNonQuery (); - } } /// @@ -974,15 +908,13 @@ public void Update (X509CrlRecord record) /// /// Gets a certificate revocation list store. /// - ///A certificate recovation list store. + ///A certificate revocation list store. public IX509Store GetCrlStore () { var crls = new List(); using (var command = GetSelectAllCrlsCommand ()) { - var reader = command.ExecuteReader (); - - try { + using (var reader = command.ExecuteReader ()) { var parser = new X509CrlParser (); var buffer = new byte[4096]; @@ -990,12 +922,6 @@ public IX509Store GetCrlStore () var record = LoadCrlRecord (reader, parser, ref buffer); crls.Add (record.Crl); } - } finally { -#if NETSTANDARD - reader.Dispose (); -#else - reader.Close (); -#endif } } @@ -1033,19 +959,21 @@ ICollection IX509Store.GetMatches (IX509Selector selector) /// false to release only the unmanaged resources. protected virtual void Dispose (bool disposing) { - for (int i = 0; i < passwd.Length; i++) - passwd[i] = '\0'; + if (passwd != null) { + for (int i = 0; i < passwd.Length; i++) + passwd[i] = '\0'; + } } ///- /// Releases all resource used by the ///object. + /// Releases all resource used by the object. /// Call + ///when you are finished using the - /// . The method leaves the - /// in an unusable state. After calling + /// . The method leaves the + /// in an unusable state. After calling /// , you must release all references to the - /// so the garbage collector can reclaim the memory that - /// the was occupying. so the garbage collector can reclaim the memory that + /// the was occupying. // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,8 +26,10 @@ using System; +using Org.BouncyCastle.Asn1; using Org.BouncyCastle.X509; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Asn1.X509; namespace MimeKit.Cryptography { /// @@ -84,23 +86,6 @@ public enum X509CertificateRecordFields { /// /// Gets the identifier. /// @@ -129,6 +114,15 @@ public class X509CertificateRecord ///public bool IsTrusted { get; set; } + /// true if the certificate is trusted; otherwise,false .+ /// Gets whether or not the certificate is an anchor. + /// + ///+ /// Gets whether or not the certificate is an anchor. + /// + ///+ public bool IsAnchor { get { return Certificate.IsSelfSigned (); } } + /// true if the certificate is an anchor; otherwise,false ./// Gets the key usage flags for the certificate. /// @@ -157,12 +151,12 @@ public class X509CertificateRecord public DateTime NotAfter { get { return Certificate.NotAfter.ToUniversalTime (); } } ///- /// Gets the certificate issuer's name. + /// Gets the certificate's issuer name. /// ///- /// Gets the certificate issuer's name. + /// Gets the certificate's issuer name. /// - ///The issuer's name. + ///The certificate's issuer name. public string IssuerName { get { return Certificate.IssuerDN.ToString (); } } ///@@ -174,6 +168,33 @@ public class X509CertificateRecord /// The serial number. public string SerialNumber { get { return Certificate.SerialNumber.ToString (); } } + ///+ /// Gets the certificate's subject name. + /// + ///+ /// Gets the certificate's subject name. + /// + ///The certificate's subject name. + public string SubjectName { get { return Certificate.SubjectDN.ToString (); } } + + ///+ /// Gets the certificate's subject key identifier. + /// + ///+ /// Gets the certificate's subject key identifier. + /// + ///The certificate's subject key identifier. + public byte[] SubjectKeyIdentifier { + get { + var subjectKeyIdentifier = Certificate.GetExtensionValue (X509Extensions.SubjectKeyIdentifier); + + if (subjectKeyIdentifier != null) + subjectKeyIdentifier = (Asn1OctetString) Asn1Object.FromByteArray (subjectKeyIdentifier.GetOctets ()); + + return subjectKeyIdentifier?.GetOctets (); + } + } + ////// Gets the subject email address. /// @@ -229,7 +250,7 @@ public class X509CertificateRecord public AsymmetricKeyParameter PrivateKey { get; set; } ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new certificate record with a private key for storing in a @@ -262,7 +283,7 @@ public X509CertificateRecord (X509Certificate certificate, AsymmetricKeyParamete } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new certificate record for storing in a . @@ -281,7 +302,7 @@ public X509CertificateRecord (X509Certificate certificate) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// This constructor is only meant to be used by implementors of diff --git a/MimeKit/Cryptography/X509CertificateStore.cs b/MimeKit/Cryptography/X509CertificateStore.cs index c807392a79..6bdd6e0c22 100644 --- a/MimeKit/Cryptography/X509CertificateStore.cs +++ b/MimeKit/Cryptography/X509CertificateStore.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -49,7 +49,7 @@ public class X509CertificateStore : IX509Store readonly List certs; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -196,7 +196,6 @@ public void Import (Stream stream) } } -#if !PORTABLE /// /// Imports the certificate(s) from the specified file. /// @@ -221,7 +220,6 @@ public void Import (string fileName) using (var stream = File.OpenRead (fileName)) Import (stream); } -#endif ////// Imports the certificate(s) from the specified byte array. @@ -289,7 +287,6 @@ public void Import (Stream stream, string password) } } -#if !PORTABLE /// /// Imports certificates and private keys from the specified file. /// @@ -305,8 +302,7 @@ public void Import (Stream stream, string password) ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The specified file could not be found. @@ -325,7 +321,6 @@ public void Import (string fileName, string password) using (var stream = File.OpenRead (fileName)) Import (stream, password); } -#endif /// ////// Imports certificates and private keys from the specified byte array. @@ -373,7 +368,6 @@ public void Export (Stream stream) } } -#if !PORTABLE /// /// Exports the certificates to an unencrypted file. /// @@ -386,8 +380,7 @@ public void Export (Stream stream) ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The specified path exceeds the maximum allowed path length of the system. @@ -409,7 +402,6 @@ public void Export (string fileName) using (var file = File.Create (fileName)) Export (file); } -#endif /// ////// Exports the specified stream and password to a pkcs12 encrypted file. @@ -468,7 +460,6 @@ public void Export (Stream stream, string password) store.Save (stream, password.ToCharArray (), new SecureRandom ()); } -#if !PORTABLE /// /// Exports the specified stream and password to a pkcs12 encrypted file. /// @@ -484,8 +475,7 @@ public void Export (Stream stream, string password) ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The specified path exceeds the maximum allowed path length of the system. @@ -510,7 +500,6 @@ public void Export (string fileName, string password) using (var file = File.Create (fileName)) Export (file, password); } -#endif /// public override async Task WriteToAsync (FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) { - if (Boundary == null) - Boundary = GenerateBoundary (); - await base.WriteToAsync (options, stream, contentOnly, cancellationToken).ConfigureAwait (false); if (ContentType.IsMimeType ("multipart", "signed")) { @@ -554,7 +545,7 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = } if (RawPreamble != null && RawPreamble.Length > 0) - await WriteBytesAsync (options, stream, RawPreamble, cancellationToken).ConfigureAwait (false); + await WriteBytesAsync (options, stream, RawPreamble, children.Count > 0 || EnsureNewLine, cancellationToken).ConfigureAwait (false); var boundary = Encoding.ASCII.GetBytes ("--" + Boundary + "--"); @@ -588,13 +579,13 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); if (RawEpilogue != null && RawEpilogue.Length > 0) - await WriteBytesAsync (options, stream, RawEpilogue, cancellationToken).ConfigureAwait (false); + await WriteBytesAsync (options, stream, RawEpilogue, EnsureNewLine, cancellationToken).ConfigureAwait (false); } #region ICollection implementation ////// Gets an enumerator of matching X.509 certificates based on the specified selector. diff --git a/MimeKit/Cryptography/X509CrlRecord.cs b/MimeKit/Cryptography/X509CrlRecord.cs index 4812c2fcaf..1bb17b6eec 100644 --- a/MimeKit/Cryptography/X509CrlRecord.cs +++ b/MimeKit/Cryptography/X509CrlRecord.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast Dos, + + ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -78,15 +78,6 @@ public enum X509CrlRecordFields { /// public class X509CrlRecord { - internal static readonly string[] ColumnNames = { - "ID", - "DELTA", - "ISSUERNAME", - "THISUPDATE", - "NEXTUPDATE", - "CRL" - }; - /// /// Gets the identifier. /// @@ -144,7 +135,7 @@ public class X509CrlRecord public X509Crl Crl { get; set; } ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new CRL record for storing in a . @@ -168,7 +159,7 @@ public X509CrlRecord (X509Crl crl) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// This constructor is only meant to be used by implementors of diff --git a/MimeKit/Cryptography/X509KeyUsageFlags.cs b/MimeKit/Cryptography/X509KeyUsageFlags.cs index c0156f8e73..689924acd6 100644 --- a/MimeKit/Cryptography/X509KeyUsageFlags.cs +++ b/MimeKit/Cryptography/X509KeyUsageFlags.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -33,9 +33,9 @@ namespace MimeKit.Cryptography { /// /// [Flags] public enum X509KeyUsageFlags { diff --git a/MimeKit/DomainList.cs b/MimeKit/DomainList.cs index 0654a003bd..14d4e33f65 100644 --- a/MimeKit/DomainList.cs +++ b/MimeKit/DomainList.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey StedfastThe X.509 Key Usage Flags can be used to determine what operations /// a certificate can be used for. - ///+ /// A value of indicates that + /// A value of indicates that /// there are no restrictions on the use of the - /// . . /// // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,6 +27,7 @@ using System; using System.Text; using System.Collections; +using System.Globalization; using System.Collections.Generic; using MimeKit.Utils; @@ -44,7 +45,7 @@ public class DomainList : IList readonly List domains; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new based on the domains provided. @@ -62,7 +63,7 @@ public DomainList (IEnumerable domains) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -216,7 +217,7 @@ public bool Contains (string domain) } /// - /// Copies all of the domains in the ///to the specified array. + /// Copies all of the domains in the to the specified array. /// /// Copies all of the domains within the into the array, @@ -260,7 +261,7 @@ public bool Remove (string domain) } /// - /// Gets the number of domains in the ///. + /// Gets the number of domains in the . /// /// Indicates the number of domains in the list. @@ -271,7 +272,7 @@ public int Count { } /// - /// Gets a value indicating whether this instance is read only. + /// Get a value indicating whether the ///is read only. /// /// A is never read-only. @@ -333,15 +334,12 @@ internal string Encode (FormatOptions options) var builder = new StringBuilder (); for (int i = 0; i < domains.Count; i++) { - if (IsNullOrWhiteSpace (domains[i]) && builder.Length == 0) + if (IsNullOrWhiteSpace (domains[i])) continue; if (builder.Length > 0) builder.Append (','); - if (IsNullOrWhiteSpace (domains[i])) - continue; - builder.Append ('@'); if (!options.International && ParseUtils.IsInternational (domains[i])) { @@ -369,14 +367,13 @@ public override string ToString () var builder = new StringBuilder (); for (int i = 0; i < domains.Count; i++) { - if (IsNullOrWhiteSpace (domains[i]) && builder.Length == 0) + if (IsNullOrWhiteSpace (domains[i])) continue; if (builder.Length > 0) builder.Append (','); - if (!IsNullOrWhiteSpace (domains[i])) - builder.Append ('@'); + builder.Append ('@'); builder.Append (domains[i]); } @@ -393,7 +390,7 @@ void OnChanged () } /// - /// Tries to parse a list of domains. + /// Try to parse a list of domains. /// ////// Attempts to parse a from the text buffer starting at the @@ -422,7 +419,7 @@ internal static bool TryParse (byte[] buffer, ref int index, int endIndex, bool if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete domain-list at offset: {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete domain-list at offset: {0}", startIndex), startIndex, index); return false; } @@ -453,7 +450,7 @@ internal static bool TryParse (byte[] buffer, ref int index, int endIndex, bool } /// - /// Tries to parse a list of domains. + /// Try to parse a list of domains. /// ////// Attempts to parse a from the supplied text. The index diff --git a/MimeKit/EncodingConstraint.cs b/MimeKit/EncodingConstraint.cs index b86f4bbdfa..d3ce03b205 100644 --- a/MimeKit/EncodingConstraint.cs +++ b/MimeKit/EncodingConstraint.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Encodings/Base64Decoder.cs b/MimeKit/Encodings/Base64Decoder.cs index 35e43c9aea..ba2e5b2aca 100644 --- a/MimeKit/Encodings/Base64Decoder.cs +++ b/MimeKit/Encodings/Base64Decoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -61,7 +61,7 @@ public class Base64Decoder : IMimeDecoder byte npad; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new base64 decoder. diff --git a/MimeKit/Encodings/Base64Encoder.cs b/MimeKit/Encodings/Base64Encoder.cs index 70a60d86f4..5dcf0f45ec 100644 --- a/MimeKit/Encodings/Base64Encoder.cs +++ b/MimeKit/Encodings/Base64Encoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -52,7 +52,7 @@ public class Base64Encoder : IMimeEncoder byte saved; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new base64 encoder. @@ -72,7 +72,7 @@ internal Base64Encoder (bool rfc2047, int maxLineLength = 72) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new base64 encoder. @@ -277,7 +277,7 @@ unsafe int Flush (byte* input, int length, byte* output) *outptr++ = (byte) '\n'; Reset (); - + return (int) (outptr - output); } diff --git a/MimeKit/Encodings/HexDecoder.cs b/MimeKit/Encodings/HexDecoder.cs index 8f2606642a..d7fb2e5155 100644 --- a/MimeKit/Encodings/HexDecoder.cs +++ b/MimeKit/Encodings/HexDecoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -48,7 +48,7 @@ enum HexDecoderState : byte { byte saved; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new hex decoder. diff --git a/MimeKit/Encodings/HexEncoder.cs b/MimeKit/Encodings/HexEncoder.cs index 19cf2d4ebd..5a7beaea5d 100644 --- a/MimeKit/Encodings/HexEncoder.cs +++ b/MimeKit/Encodings/HexEncoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -44,7 +44,7 @@ public class HexEncoder : IMimeEncoder }; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new hex encoder. diff --git a/MimeKit/Encodings/IMimeDecoder.cs b/MimeKit/Encodings/IMimeDecoder.cs index d940ad5995..dec3d763ef 100644 --- a/MimeKit/Encodings/IMimeDecoder.cs +++ b/MimeKit/Encodings/IMimeDecoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Encodings/IMimeEncoder.cs b/MimeKit/Encodings/IMimeEncoder.cs index 8733114d2f..741bb1255e 100644 --- a/MimeKit/Encodings/IMimeEncoder.cs +++ b/MimeKit/Encodings/IMimeEncoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Encodings/PassThroughDecoder.cs b/MimeKit/Encodings/PassThroughDecoder.cs index 63b01d889f..87aaf73f6c 100644 --- a/MimeKit/Encodings/PassThroughDecoder.cs +++ b/MimeKit/Encodings/PassThroughDecoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -36,7 +36,7 @@ namespace MimeKit.Encodings { public class PassThroughDecoder : IMimeDecoder { /// - /// Initializes a new instance of the /// The encoding to return in theclass. + /// Initialize a new instance of the class. /// property. /// diff --git a/MimeKit/Encodings/PassThroughEncoder.cs b/MimeKit/Encodings/PassThroughEncoder.cs index 3931ba5c2e..3dc50db2ad 100644 --- a/MimeKit/Encodings/PassThroughEncoder.cs +++ b/MimeKit/Encodings/PassThroughEncoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -36,7 +36,7 @@ namespace MimeKit.Encodings { public class PassThroughEncoder : IMimeEncoder { /// - /// Initializes a new instance of the /// The encoding to return in theclass. + /// Initialize a new instance of the class. /// property. /// diff --git a/MimeKit/Encodings/QEncoder.cs b/MimeKit/Encodings/QEncoder.cs index 53acc7382a..e8a5ba3d8c 100644 --- a/MimeKit/Encodings/QEncoder.cs +++ b/MimeKit/Encodings/QEncoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -66,7 +66,7 @@ public class QEncoder : IMimeEncoder readonly CharType mask; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new rfc2047 quoted-printable encoder. diff --git a/MimeKit/Encodings/QuotedPrintableDecoder.cs b/MimeKit/Encodings/QuotedPrintableDecoder.cs index 1178248351..eee3f167ac 100644 --- a/MimeKit/Encodings/QuotedPrintableDecoder.cs +++ b/MimeKit/Encodings/QuotedPrintableDecoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -51,7 +51,7 @@ enum QpDecoderState : byte { byte saved; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new quoted-printable decoder. @@ -65,7 +65,7 @@ public QuotedPrintableDecoder (bool rfc2047) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new quoted-printable decoder. diff --git a/MimeKit/Encodings/QuotedPrintableEncoder.cs b/MimeKit/Encodings/QuotedPrintableEncoder.cs index 1bf7cbf4b7..ff7d4fc892 100644 --- a/MimeKit/Encodings/QuotedPrintableEncoder.cs +++ b/MimeKit/Encodings/QuotedPrintableEncoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -52,7 +52,7 @@ public class QuotedPrintableEncoder : IMimeEncoder short saved; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new quoted-printable encoder. diff --git a/MimeKit/Encodings/UUDecoder.cs b/MimeKit/Encodings/UUDecoder.cs index f40ec6b22d..59a86c8ed5 100644 --- a/MimeKit/Encodings/UUDecoder.cs +++ b/MimeKit/Encodings/UUDecoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -77,7 +77,7 @@ enum UUDecoderState : byte { uint saved; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new Unix-to-Unix decoder. @@ -92,7 +92,7 @@ public UUDecoder (bool payloadOnly) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new Unix-to-Unix decoder. diff --git a/MimeKit/Encodings/UUEncoder.cs b/MimeKit/Encodings/UUEncoder.cs index 93dcc10064..eef18cdc1f 100644 --- a/MimeKit/Encodings/UUEncoder.cs +++ b/MimeKit/Encodings/UUEncoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -48,7 +48,7 @@ public class UUEncoder : IMimeEncoder byte uulen; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new Unix-to-Unix encoder. @@ -134,17 +134,17 @@ unsafe int Encode (byte* input, int length, byte[] outbuf, byte* output, byte *u byte* bufptr; byte b0, b1, b2; - if ((length + uulen) < 45) { + if ((length + nsaved + uulen) < 45) { // not enough input to write a full uuencoded line bufptr = uuptr + ((uulen / 3) * 4); } else { bufptr = outptr + 1; - + if (uulen > 0) { // copy the previous call's uubuf to output int n = (uulen / 3) * 4; - Buffer.BlockCopy (uubuf, 0, outbuf, (int) (bufptr - output), n); + Buffer.BlockCopy (uubuf, 0, outbuf, 1, n); bufptr += n; } } @@ -186,8 +186,8 @@ unsafe int Encode (byte* input, int length, byte[] outbuf, byte* output, byte *u } } - while (inptr < inend) { - while (uulen < 45 && (inptr + 3) <= inend) { + do { + while (uulen < 45 && (inptr + 2) < inend) { b0 = *inptr++; b1 = *inptr++; b2 = *inptr++; @@ -216,10 +216,12 @@ unsafe int Encode (byte* input, int length, byte[] outbuf, byte* output, byte *u } } else { // not enough input to continue... - for (nsaved = 0, saved = 0; inptr < inend; nsaved++) + while (inptr < inend) { saved = (saved << 8) | *inptr++; + nsaved++; + } } - } + } while (inptr < inend); return (int) (outptr - output); } diff --git a/MimeKit/Encodings/YDecoder.cs b/MimeKit/Encodings/YDecoder.cs index caaeac9267..61d8cbbf5c 100644 --- a/MimeKit/Encodings/YDecoder.cs +++ b/MimeKit/Encodings/YDecoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -72,7 +72,7 @@ enum YDecoderState : byte { Crc32 crc; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new yEnc decoder. @@ -88,7 +88,7 @@ public YDecoder (bool payloadOnly) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new yEnc decoder. diff --git a/MimeKit/Encodings/YEncoder.cs b/MimeKit/Encodings/YEncoder.cs index 49f25dbd39..0873eec5fe 100644 --- a/MimeKit/Encodings/YEncoder.cs +++ b/MimeKit/Encodings/YEncoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -44,7 +44,7 @@ public class YEncoder : IMimeEncoder Crc32 crc; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new yEnc encoder. diff --git a/MimeKit/FormatOptions.cs b/MimeKit/FormatOptions.cs index 56d99dd74b..8866754dc8 100644 --- a/MimeKit/FormatOptions.cs +++ b/MimeKit/FormatOptions.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -50,6 +50,12 @@ public enum NewLineFormat : byte { /// The DOS New-Line format ( "\r\n" ). ///+ /// A mixed New-Line format where some lines use Unix-based line endings and + /// other lines use DOS-based line endings. + /// + Mixed, } ///@@ -73,7 +79,10 @@ public class FormatOptions ParameterEncodingMethod parameterEncodingMethod; bool allowMixedHeaderCharsets; NewLineFormat newLineFormat; + bool verifyingSignature; + bool ensureNewLine; bool international; + int maxLineLength; /// ////// The default formatting options. @@ -86,25 +95,43 @@ public class FormatOptions public static readonly FormatOptions Default; /// ///- /// Gets the maximum line length used by the encoders. The encoders + /// Gets or sets the maximum line length used by the encoders. The encoders /// use this value to determine where to place line breaks. /// ////// Specifies the maximum line length to use when line-wrapping headers. /// ///The maximum line length. + ///+ /// + ///is out of range. It must be between 60 and 998. + /// + /// public int MaxLineLength { - get { return DefaultMaxLineLength; } + get { return maxLineLength; } + set { + if (this == Default) + throw new InvalidOperationException ("The default formatting options cannot be changed."); + + if (value < MinimumLineLength || value > MaximumLineLength) + throw new ArgumentOutOfRangeException (nameof (value)); + + maxLineLength = value; + } } ///cannot be changed. + /// - /// Gets or sets the new-line format. + /// Get or set the new-line format. /// ////// Specifies the new-line encoding to use when writing the message /// or entity to a stream. /// ///The new-line format. + ///+ /// ///is not a valid + ///. /// @@ -114,7 +141,39 @@ public NewLineFormat NewLineFormat { if (this == Default) throw new InvalidOperationException ("The default formatting options cannot be changed."); - newLineFormat = value; + switch (newLineFormat) { + case NewLineFormat.Unix: + case NewLineFormat.Dos: + newLineFormat = value; + break; + default: + throw new ArgumentOutOfRangeException (nameof (value)); + } + } + } + + ///cannot be changed. /// + /// Get or set whether the formatter should ensure that messages end with a new-line sequence. + /// + ///+ /// + ///By default, when writing a + ///to a stream, the serializer attempts to + /// maintain byte-for-byte compatibility with the original stream that the message was parsed from. + /// This means that if the ogirinal message stream did not end with a new-line sequence, then the + /// output of writing the message back to a stream will also not end with a new-line sequence. To override this behavior, you can set this property to + ///true in order to ensure + /// that writing the message back to a stream will always end with a new-line sequence.+ /// true in order to ensure that the message will end with a new-line sequence; otherwise,false .+ /// + public bool EnsureNewLine { + get { return ensureNewLine; } + set { + if (this == Default) + throw new InvalidOperationException ("The default formatting options cannot be changed."); + + ensureNewLine = value; } } @@ -136,8 +195,13 @@ internal byte[] NewLineBytes { get { return NewLineFormats[(int) NewLineFormat]; } } + internal bool VerifyingSignature { + get { return verifyingSignature; } + set { verifyingSignature = value; } + } + ///cannot be changed. + /// - /// Gets the message headers that should be hidden. + /// Get the message headers that should be hidden. /// ////// Specifies the set of headers that should be removed when @@ -152,7 +216,7 @@ public HashSet HiddenHeaders { } /// - /// Gets or sets whether the new "Internationalized Email" formatting standards should be used. + /// Get or set whether the new "Internationalized Email" formatting standards should be used. /// ////// The new "Internationalized Email" format is defined by @@ -178,11 +242,11 @@ public bool International { } /// ///- /// Gets or sets whether the formatter should allow mixed charsets in the headers. + /// Get or set whether the formatter should allow mixed charsets in the headers. /// ///- /// - ///When this option is enabled, the MIME formatter will try to use US-ASCII and/or - /// ISO-8859-1 to encode headers when appropriate rather than being forced to use the + /// When this option is enabled, the MIME formatter will try to use us-ascii and/or + /// iso-8859-1 to encode headers when appropriate rather than being forced to use the /// specified charset for all encoded-word tokens in order to maximize readability. ///Unfortunately, mail clients like Outlook and Thunderbird do not treat /// encoded-word tokens individually and assume that all tokens are encoded using the @@ -192,7 +256,7 @@ public bool International { /// /// https://bugzilla.mozilla.org/show_bug.cgi?id=317263. ///+ /// true if the formatter should be allowed to use ISO-8859-1 when encoding headers; otherwise,false .public bool AllowMixedHeaderCharsets { get { return allowMixedHeaderCharsets; } set { @@ -204,12 +268,12 @@ public bool AllowMixedHeaderCharsets { } /// true if the formatter should be allowed to use us-ascii and/or iso-8859-1 when encoding headers; otherwise,false .- /// The method to use for encoding Content-Type and Content-Disposition parameter values. + /// Get or set the method to use for encoding Content-Type and Content-Disposition parameter values. /// ////// The method to use for encoding Content-Type and Content-Disposition parameter /// values when the + ///is set to - /// . . The MIME specifications specify that the proper method for encoding Content-Type /// and Content-Disposition parameter values is the method described in /// rfc2231. However, it is common for @@ -243,7 +307,7 @@ static FormatOptions () } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new set of formatting options for use with methods such as @@ -253,8 +317,9 @@ public FormatOptions () { HiddenHeaders = new HashSet (); parameterEncodingMethod = ParameterEncodingMethod.Rfc2231; - //maxLineLength = DefaultMaxLineLength; + maxLineLength = DefaultMaxLineLength; allowMixedHeaderCharsets = false; + ensureNewLine = false; international = false; if (Environment.NewLine.Length == 1) @@ -264,7 +329,7 @@ public FormatOptions () } /// - /// Clones an instance of ///. + /// Clones an instance of . /// /// Clones the formatting options. @@ -273,11 +338,13 @@ public FormatOptions () public FormatOptions Clone () { var options = new FormatOptions (); - //options.maxLineLength = maxLineLength; + options.maxLineLength = maxLineLength; options.newLineFormat = newLineFormat; + options.ensureNewLine = ensureNewLine; options.HiddenHeaders = new HashSet (HiddenHeaders); options.allowMixedHeaderCharsets = allowMixedHeaderCharsets; options.parameterEncodingMethod = parameterEncodingMethod; + options.verifyingSignature = verifyingSignature; options.international = international; return options; } diff --git a/MimeKit/GroupAddress.cs b/MimeKit/GroupAddress.cs index 0e0a248904..deedc742f0 100644 --- a/MimeKit/GroupAddress.cs +++ b/MimeKit/GroupAddress.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,12 +27,9 @@ using System; using System.Linq; using System.Text; +using System.Globalization; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - using MimeKit.Utils; namespace MimeKit { @@ -46,7 +43,7 @@ namespace MimeKit { public class GroupAddress : InternetAddress { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified name and list of addresses. The @@ -65,7 +62,7 @@ public GroupAddress (Encoding encoding, string name, IEnumerable - /// Initializes a new instance of the class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified name and list of addresses. @@ -77,7 +74,7 @@ public GroupAddress (string name, IEnumerable addresses) : this } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified name. The specified @@ -95,7 +92,7 @@ public GroupAddress (Encoding encoding, string name) : base (encoding, name) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified name. @@ -121,23 +118,21 @@ public override InternetAddress Clone () /// Gets the members of the group. /// - /// Represents the member addresses of the group. Typically the member addresses - /// will be of the ///variety, but it is possible - /// for groups to contain other groups. + /// Represents the member addresses of the group. If the group address properly conforms + /// to the internet standards, every group member should be of the + ///+ /// variety. When handling group addresses constructed by third-party software, it is possible + /// for groups to contain members of the variety. When constructing new messages, it is recommended that address groups not contain + /// anything other than ///members in order to comply with internet + /// standards. The list of members. public InternetAddressList Members { get; private set; } - internal override void Encode (FormatOptions options, StringBuilder builder, ref int lineLength) + internal override void Encode (FormatOptions options, StringBuilder builder, bool firstToken, ref int lineLength) { - if (builder == null) - throw new ArgumentNullException (nameof (builder)); - - if (lineLength < 0) - throw new ArgumentOutOfRangeException (nameof (lineLength)); - if (!string.IsNullOrEmpty (Name)) { string name; @@ -151,11 +146,11 @@ internal override void Encode (FormatOptions options, StringBuilder builder, ref if (lineLength + name.Length > options.MaxLineLength) { if (name.Length > options.MaxLineLength) { // we need to break up the name... - builder.AppendFolded (options, name, ref lineLength); + builder.AppendFolded (options, firstToken, name, ref lineLength); } else { // the name itself is short enough to fit on a single line, // but only if we write it on a line by itself - if (lineLength > 1) { + if (!firstToken && lineLength > 1) { builder.LineWrap (options); lineLength = 1; } @@ -173,7 +168,7 @@ internal override void Encode (FormatOptions options, StringBuilder builder, ref builder.Append (": "); lineLength += 2; - Members.Encode (options, builder, ref lineLength); + Members.Encode (options, builder, false, ref lineLength); builder.Append (';'); lineLength++; @@ -205,7 +200,7 @@ public override string ToString (FormatOptions options, bool encode) if (encode) { int lineLength = 0; - Encode (options, builder, ref lineLength); + Encode (options, builder, true, ref lineLength); } else { builder.Append (Name); builder.Append (':'); @@ -227,14 +222,14 @@ public override string ToString (FormatOptions options, bool encode) #region IEquatable implementation ///- /// Determines whether the specified ///is equal to the current . + /// Determines whether the specified is equal to the current . /// /// Compares two group addresses to determine if they are identical or not. /// - /// Theto compare with the current . - /// + /// The true if the specifiedis equal to the current - /// ; otherwise, false .to compare with the current . + /// public override bool Equals (InternetAddress other) { var group = other as GroupAddress; @@ -271,7 +266,7 @@ static bool TryParse (ParserOptions options, byte[] text, ref int index, int end } /// true if the specifiedis equal to the current + /// ; otherwise, false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a group address or @@ -311,7 +306,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a group address or @@ -335,7 +330,7 @@ public static bool TryParse (byte[] buffer, int startIndex, int length, out Grou } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a group address or @@ -373,7 +368,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a group address or @@ -395,7 +390,7 @@ public static bool TryParse (byte[] buffer, int startIndex, out GroupAddress gro } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a group address or @@ -429,7 +424,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, out GroupAddr } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a group address or @@ -447,7 +442,7 @@ public static bool TryParse (byte[] buffer, out GroupAddress group) } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a single . If the address is not a group address or @@ -484,7 +479,7 @@ public static bool TryParse (ParserOptions options, string text, out GroupAddres } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a single ///. If the address is not a group address or @@ -502,13 +497,13 @@ public static bool TryParse (string text, out GroupAddress group) } /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a group address or /// there is more than a single group address, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The input buffer. /// The starting index of the input buffer. @@ -539,19 +534,19 @@ public static bool TryParse (string text, out GroupAddress group) ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return group; } ///. - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a group address or /// there is more than a single group address, then parsing will fail. /// The parsed + ///. The parsed /// The input buffer. /// The starting index of the input buffer. /// The number of bytes in the input buffer to parse. @@ -571,13 +566,13 @@ public static bool TryParse (string text, out GroupAddress group) } ///. - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a group address or /// there is more than a single group address, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The input buffer. /// The starting index of the input buffer. @@ -606,19 +601,19 @@ public static bool TryParse (string text, out GroupAddress group) ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return group; } ///. - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a group address or /// there is more than a single group address, then parsing will fail. /// The parsed + ///. The parsed /// The input buffer. /// The starting index of the input buffer. ///. @@ -636,13 +631,13 @@ public static bool TryParse (string text, out GroupAddress group) } /// ///- /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a group address or /// there is more than a single group address, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The input buffer. ///. @@ -667,19 +662,19 @@ public static bool TryParse (string text, out GroupAddress group) ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return group; } /// ///- /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a group address or /// there is more than a single group address, then parsing will fail. /// The parsed + ///. The parsed /// The input buffer. ///. /// public Header this [int index] { get { - if (index < 0 || index > Count) + if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); return headers[index]; } set { - if (index < 0 || index > Count) + if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); if (value == null) @@ -1182,7 +1184,7 @@ public Header this [int index] { #region IEnumerable implementation ///is null . @@ -693,13 +688,13 @@ public static bool TryParse (string text, out GroupAddress group) } ///- /// Parses the given text into a new ///instance. + /// Parse the given text into a new instance. /// /// Parses a single - ///. If the address is not a group address or /// there is more than a single group address, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The text. ///. @@ -729,19 +724,19 @@ public static bool TryParse (string text, out GroupAddress group) ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return group; } /// public void RemoveAt (int index) { - if (index < 0 || index > Count) + if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); var header = headers[index]; @@ -1102,10 +1104,10 @@ public void RemoveAt (int index) } ///- /// Parses the given text into a new ///instance. + /// Parse the given text into a new instance. /// /// Parses a single - ///. If the address is not a group address or /// there is more than a single group address, then parsing will fail. /// The parsed + ///. The parsed /// The text. ///. /// public static bool TryParse (ParserOptions options, byte[] buffer, out Header header) { - ParseUtils.ValidateArguments (options, buffer); - - unsafe { - fixed (byte* inptr = buffer) { - return TryParse (options.Clone (), inptr, buffer.Length, true, out header); - } - } + return TryParse (options, buffer, 0, out header); } ///is null . diff --git a/MimeKit/Header.cs b/MimeKit/Header.cs index d31a5e7a04..24b60853e1 100644 --- a/MimeKit/Header.cs +++ b/MimeKit/Header.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,11 +28,8 @@ using System.Text; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - using MimeKit.Utils; +using MimeKit.Cryptography; namespace MimeKit { /// @@ -53,11 +50,12 @@ public class Header //Encoding charset = CharsetUtils.UTF8; readonly byte[] rawField; + bool explicitRawValue; string textValue; byte[] rawValue; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new message or entity header for the specified field and @@ -96,7 +94,7 @@ public Header (Encoding encoding, HeaderId id, string value) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new message or entity header for the specified field and @@ -139,7 +137,7 @@ public Header (string charset, HeaderId id, string value) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new message or entity header for the specified field and @@ -158,7 +156,7 @@ public Header (HeaderId id, string value) : this (Encoding.UTF8, id, value) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new message or entity header for the specified field and @@ -207,7 +205,7 @@ public Header (Encoding encoding, string field, string value) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new message or entity header for the specified field and @@ -260,7 +258,7 @@ public Header (string charset, string field, string value) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new message or entity header for the specified field and @@ -280,8 +278,19 @@ public Header (string field, string value) : this (Encoding.UTF8, field, value) { } - // Note: this .ctor is only used by Clone() - internal Header (ParserOptions options, HeaderId id, string name, byte[] field, byte[] value) + /// + /// Initialize a new instance of the + ///class. + /// + /// + /// The parser options used. + /// The id of the header. + /// The name of the header field. + /// The raw header field. + /// The raw value of the header. + protected Header (ParserOptions options, HeaderId id, string name, byte[] field, byte[] value) { Options = options; rawField = field; @@ -290,12 +299,24 @@ internal Header (ParserOptions options, HeaderId id, string name, byte[] field, Id = id; } - internal Header (ParserOptions options, byte[] field, byte[] value) + ///Creates a new message or entity header with the specified values. + ///This constructor is used by + ///. + /// Initialize a new instance of the + ///class. + /// + /// + /// The parser options used. + /// The raw header field. + /// The raw value of the header. + ///Creates a new message or entity header with the specified raw values. + ///This constructor is used by the + /// TryParse methods. + ///true if the header field is invalid; othereise,false . + internal protected Header (ParserOptions options, byte[] field, byte[] value, bool invalid) { var chars = new char[field.Length]; int count = 0; - while (count < field.Length && !field[count].IsBlank ()) { + while (count < field.Length && (invalid || !field[count].IsBlank ())) { chars[count] = (char) field[count]; count++; } @@ -306,9 +327,22 @@ internal Header (ParserOptions options, byte[] field, byte[] value) Field = new string (chars, 0, count); Id = Field.ToHeaderId (); + IsInvalid = invalid; } - internal Header (ParserOptions options, HeaderId id, string field, byte[] value) + ///+ /// Initialize a new instance of the + ///class. + /// + /// + /// The parser options used. + /// The id of the header. + /// The raw header field. + /// The raw value of the header. + internal protected Header (ParserOptions options, HeaderId id, string field, byte[] value) { Options = options; rawField = Encoding.ASCII.GetBytes (field); @@ -326,7 +360,10 @@ internal Header (ParserOptions options, HeaderId id, string field, byte[] value) ///Creates a new message or entity header with the specified raw values. + ///This constructor is used by + ///and + /// when serializing new values for headers. A copy of the header with its current state. public Header Clone () { - var header = new Header (Options, Id, Field, RawField, RawValue); + var header = new Header (Options, Id, Field, rawField, rawValue) { + explicitRawValue = explicitRawValue, + IsInvalid = IsInvalid + }; // if the textValue has already been calculated, set it on the cloned header as well. header.textValue = textValue; @@ -368,6 +405,10 @@ public HeaderId Id { get; private set; } + internal bool IsInvalid { + get; private set; + } + ////// Gets the raw field name of the header. /// @@ -403,7 +444,7 @@ public byte[] RawValue { public string Value { get { if (textValue == null) - textValue = Unfold (Rfc2047.DecodeText (Options, RawValue)); + textValue = Unfold (Rfc2047.DecodeText (Options, rawValue)); return textValue; } @@ -433,7 +474,7 @@ public string GetValue (Encoding encoding) var options = Options.Clone (); options.CharsetEncoding = encoding; - return Unfold (Rfc2047.DecodeText (options, RawValue)); + return Unfold (Rfc2047.DecodeText (options, rawValue)); } ///@@ -459,7 +500,7 @@ public string GetValue (string charset) return GetValue (encoding); } - static byte[] EncodeAddressHeader (ParserOptions options, FormatOptions format, Encoding charset, string field, string value) + static byte[] EncodeAddressHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) { var encoded = new StringBuilder (" "); int lineLength = field.Length + 2; @@ -468,7 +509,7 @@ static byte[] EncodeAddressHeader (ParserOptions options, FormatOptions format, if (!InternetAddressList.TryParse (options, value, out list)) return (byte[]) format.NewLineBytes.Clone (); - list.Encode (format, encoded, ref lineLength); + list.Encode (format, encoded, true, ref lineLength); encoded.Append (format.NewLine); if (format.International) @@ -477,9 +518,9 @@ static byte[] EncodeAddressHeader (ParserOptions options, FormatOptions format, return Encoding.ASCII.GetBytes (encoded.ToString ()); } - static byte[] EncodeMessageIdHeader (ParserOptions options, FormatOptions format, Encoding charset, string field, string value) + static byte[] EncodeMessageIdHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) { - return charset.GetBytes (" " + value + format.NewLine); + return encoding.GetBytes (" " + value + format.NewLine); } delegate void ReceivedTokenSkipValueFunc (byte[] text, ref int index); @@ -583,10 +624,10 @@ public ReceivedTokenValue (int startIndex, int length) } } - static byte[] EncodeReceivedHeader (ParserOptions options, FormatOptions format, Encoding charset, string field, string value) + static byte[] EncodeReceivedHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) { var tokens = new List (); - var rawValue = charset.GetBytes (value); + var rawValue = encoding.GetBytes (value); var encoded = new StringBuilder (); int lineLength = field.Length + 1; bool date = false; @@ -605,7 +646,7 @@ static byte[] EncodeReceivedHeader (ParserOptions options, FormatOptions format, while (index < rawValue.Length && !rawValue[index].IsWhitespace ()) index++; - var atom = charset.GetString (rawValue, startIndex, index - startIndex); + var atom = encoding.GetString (rawValue, startIndex, index - startIndex); for (int i = 0; i < ReceivedTokens.Length; i++) { if (atom == ReceivedTokens[i].Atom) { @@ -644,7 +685,7 @@ static byte[] EncodeReceivedHeader (ParserOptions options, FormatOptions format, } foreach (var token in tokens) { - var text = charset.GetString (rawValue, token.StartIndex, token.Length).TrimEnd (); + var text = encoding.GetString (rawValue, token.StartIndex, token.Length).TrimEnd (); if (count > 0 && lineLength + text.Length + 1 > format.MaxLineLength) { encoded.Append (format.NewLine); @@ -663,7 +704,22 @@ static byte[] EncodeReceivedHeader (ParserOptions options, FormatOptions format, encoded.Append (format.NewLine); - return charset.GetBytes (encoded.ToString ()); + return encoding.GetBytes (encoded.ToString ()); + } + + static byte[] EncodeAuthenticationResultsHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) + { + var buffer = Encoding.UTF8.GetBytes (value); + + if (!AuthenticationResults.TryParse (buffer, out AuthenticationResults authres)) + return EncodeUnstructuredHeader (options, format, encoding, field, value); + + var encoded = new StringBuilder (); + int lineLength = field.Length + 1; + + authres.Encode (format, encoded, lineLength); + + return encoding.GetBytes (encoded.ToString ()); } static void EncodeDkimLongValue (FormatOptions format, StringBuilder encoded, ref int lineLength, string value) @@ -716,7 +772,7 @@ static void EncodeDkimHeaderList (FormatOptions format, StringBuilder encoded, r } } - static byte[] EncodeDkimSignatureHeader (ParserOptions options, FormatOptions format, Encoding charset, string field, string value) + static byte[] EncodeDkimOrArcSignatureHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) { var encoded = new StringBuilder (); int lineLength = field.Length + 1; @@ -780,10 +836,10 @@ static byte[] EncodeDkimSignatureHeader (ParserOptions options, FormatOptions fo encoded.Append (format.NewLine); - return charset.GetBytes (encoded.ToString ()); + return encoding.GetBytes (encoded.ToString ()); } - static byte[] EncodeReferencesHeader (ParserOptions options, FormatOptions format, Encoding charset, string field, string value) + static byte[] EncodeReferencesHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) { var encoded = new StringBuilder (); int lineLength = field.Length + 1; @@ -807,7 +863,7 @@ static byte[] EncodeReferencesHeader (ParserOptions options, FormatOptions forma encoded.Append (format.NewLine); - return charset.GetBytes (encoded.ToString ()); + return encoding.GetBytes (encoded.ToString ()); } static bool IsWhiteSpace (char c) @@ -936,23 +992,23 @@ internal static string Fold (FormatOptions format, string field, string value) return folded.ToString (); } - static byte[] EncodeContentDisposition (ParserOptions options, FormatOptions format, Encoding charset, string field, string value) + static byte[] EncodeContentDisposition (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) { var disposition = ContentDisposition.Parse (options, value); - var encoded = disposition.Encode (format, charset); + var encoded = disposition.Encode (format, encoding); return Encoding.UTF8.GetBytes (encoded); } - static byte[] EncodeContentType (ParserOptions options, FormatOptions format, Encoding charset, string field, string value) + static byte[] EncodeContentType (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) { var contentType = ContentType.Parse (options, value); - var encoded = contentType.Encode (format, charset); + var encoded = contentType.Encode (format, encoding); return Encoding.UTF8.GetBytes (encoded); } - static byte[] EncodeUnstructuredHeader (ParserOptions options, FormatOptions format, Encoding charset, string field, string value) + static byte[] EncodeUnstructuredHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) { if (format.International) { var folded = Fold (format, field, value); @@ -960,12 +1016,24 @@ static byte[] EncodeUnstructuredHeader (ParserOptions options, FormatOptions for return Encoding.UTF8.GetBytes (folded); } - var encoded = Rfc2047.EncodeText (format, charset, value); + var encoded = Rfc2047.EncodeText (format, encoding, value); return Rfc2047.FoldUnstructuredHeader (format, field, encoded); } - byte[] FormatRawValue (FormatOptions format, Encoding encoding) + /// + /// Format the raw value of the header to conform with the specified formatting options. + /// + ///+ /// This method will called by the SetValue + /// methods and may also be conditionally called when the header is being written to a + /// + /// The formatting options. + /// The character encoding to be used. + /// The decoded (and unfolded) header value. + ///. + /// A byte array containing the raw header value that should be written. + protected virtual byte[] FormatRawValue (FormatOptions format, Encoding encoding, string value) { switch (Id) { case HeaderId.DispositionNotificationTo: @@ -981,34 +1049,40 @@ byte[] FormatRawValue (FormatOptions format, Encoding encoding) case HeaderId.Bcc: case HeaderId.Cc: case HeaderId.To: - return EncodeAddressHeader (Options, format, encoding, Field, textValue); + return EncodeAddressHeader (Options, format, encoding, Field, value); case HeaderId.Received: - return EncodeReceivedHeader (Options, format, encoding, Field, textValue); + return EncodeReceivedHeader (Options, format, encoding, Field, value); case HeaderId.ResentMessageId: + case HeaderId.InReplyTo: case HeaderId.MessageId: case HeaderId.ContentId: - return EncodeMessageIdHeader (Options, format, encoding, Field, textValue); + return EncodeMessageIdHeader (Options, format, encoding, Field, value); case HeaderId.References: - return EncodeReferencesHeader (Options, format, encoding, Field, textValue); + return EncodeReferencesHeader (Options, format, encoding, Field, value); case HeaderId.ContentDisposition: - return EncodeContentDisposition (Options, format, encoding, Field, textValue); + return EncodeContentDisposition (Options, format, encoding, Field, value); case HeaderId.ContentType: - return EncodeContentType (Options, format, encoding, Field, textValue); + return EncodeContentType (Options, format, encoding, Field, value); + case HeaderId.ArcAuthenticationResults: + case HeaderId.AuthenticationResults: + return EncodeAuthenticationResultsHeader (Options, format, encoding, Field, value); + case HeaderId.ArcMessageSignature: + case HeaderId.ArcSeal: case HeaderId.DkimSignature: - return EncodeDkimSignatureHeader (Options, format, encoding, Field, textValue); + return EncodeDkimOrArcSignatureHeader (Options, format, encoding, Field, value); default: - return EncodeUnstructuredHeader (Options, format, encoding, Field, textValue); + return EncodeUnstructuredHeader (Options, format, encoding, Field, value); } } internal byte[] GetRawValue (FormatOptions format) { - if (format.International) { + if (format.International && !explicitRawValue) { if (textValue == null) - textValue = Unfold (Rfc2047.DecodeText (Options, RawValue)); + textValue = Unfold (Rfc2047.DecodeText (Options, rawValue)); // Note: if we're reformatting to be International, then charset doesn't matter. - return FormatRawValue (format, CharsetUtils.UTF8); + return FormatRawValue (format, CharsetUtils.UTF8, textValue); } return rawValue; @@ -1045,7 +1119,7 @@ public void SetValue (FormatOptions format, Encoding encoding, string value) textValue = Unfold (value.Trim ()); - rawValue = FormatRawValue (format, encoding); + rawValue = FormatRawValue (format, encoding, textValue); // cache the formatting options that change the way the header is formatted //allowMixedHeaderCharsets = format.AllowMixedHeaderCharsets; @@ -1138,6 +1212,36 @@ public void SetValue (string charset, string value) SetValue (FormatOptions.Default, encoding, value); } + ///+ /// Set the raw header value. + /// + ///+ /// + /// The raw header value. + ///Sets the raw header value. + ///This method can be used to override default encoding and folding behavior + /// for a particular header. + ///+ /// + ///is null . + ///+ /// + public void SetRawValue (byte[] value) + { + if (value == null) + throw new ArgumentNullException (nameof (value)); + + if (value.Length == 0 || value[value.Length - 1] != (byte) '\n') + throw new ArgumentException ("The raw value MUST end with a new-line character.", nameof (value)); + + explicitRawValue = true; + rawValue = value; + textValue = null; + + OnChanged (); + } + internal event EventHandler Changed; void OnChanged () @@ -1155,7 +1259,7 @@ void OnChanged () ///does not end with a new-line character. + /// A string representing the public override string ToString () { - return Field + ": " + Value; + return IsInvalid ? Field : Field + ": " + Value; } ///. @@ -1228,6 +1332,7 @@ internal static unsafe bool TryParse (ParserOptions options, byte* input, int le byte* inend = input + length; byte* start = input; byte* inptr = input; + var invalid = false; // find the end of the field name if (strict) { @@ -1242,8 +1347,13 @@ internal static unsafe bool TryParse (ParserOptions options, byte* input, int le inptr++; if (inptr == inend || *inptr != ':') { - header = null; - return false; + if (strict) { + header = null; + return false; + } + + invalid = true; + inptr = inend; } var field = new byte[(int) (inptr - start)]; @@ -1254,25 +1364,31 @@ internal static unsafe bool TryParse (ParserOptions options, byte* input, int le *outptr++ = *start++; } - inptr++; + byte[] value; - int count = (int) (inend - inptr); - var value = new byte[count]; + if (inptr < inend) { + inptr++; - fixed (byte *outbuf = value) { - byte* outptr = outbuf; + int count = (int) (inend - inptr); + value = new byte[count]; - while (inptr < inend) - *outptr++ = *inptr++; + fixed (byte* outbuf = value) { + byte* outptr = outbuf; + + while (inptr < inend) + *outptr++ = *inptr++; + } + } else { + value = new byte[0]; } - header = new Header (options, field, value); + header = new Header (options, field, value, invalid); return true; } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a header from the supplied buffer starting at the given index @@ -1305,7 +1421,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a header from the supplied buffer starting at the given index @@ -1329,7 +1445,7 @@ public static bool TryParse (byte[] buffer, int startIndex, int length, out Head } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a header from the supplied buffer starting at the specified index. @@ -1361,7 +1477,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a header from the supplied buffer starting at the specified index. @@ -1382,7 +1498,7 @@ public static bool TryParse (byte[] buffer, int startIndex, out Header header) } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a header from the specified buffer. @@ -1398,17 +1514,11 @@ public static bool TryParse (byte[] buffer, int startIndex, out Header header) /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a header from the specified buffer. @@ -1425,7 +1535,7 @@ public static bool TryParse (byte[] buffer, out Header header) } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a header from the specified text. @@ -1453,7 +1563,7 @@ public static bool TryParse (ParserOptions options, string text, out Header head } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a header from the specified text. diff --git a/MimeKit/HeaderId.cs b/MimeKit/HeaderId.cs index 3177ca1888..a3ea7f8ca7 100644 --- a/MimeKit/HeaderId.cs +++ b/MimeKit/HeaderId.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast public enum HeaderId { + ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -41,11 +41,21 @@ namespace MimeKit { /// comparing strings. /// + /// The Accept-Language header field. + /// + AcceptLanguage, + ////// The Ad-Hoc header field. /// AdHoc, + ///+ /// The Alternate-Recipient header field. + /// + AlternateRecipient, + ////// The Apparently-To header field. /// @@ -56,16 +66,89 @@ public enum HeaderId { /// Approved, + ///+ /// The ARC-Authentication-Results header field. + /// + [HeaderName ("ARC-Authentication-Results")] + ArcAuthenticationResults, + + ///+ /// The ARC-Message-Signature header field. + /// + [HeaderName ("ARC-Message-Signature")] + ArcMessageSignature, + + ///+ /// The ARC-Seal header field. + /// + [HeaderName ("ARC-Seal")] + ArcSeal, + + ///+ /// The Archive header field. + /// + Archive, + + ///+ /// The Archived-At header field. + /// + ArchivedAt, + ////// The Article header field. /// Article, + ///+ /// The Authentication-Results header field. + /// + AuthenticationResults, + + ///+ /// The Autocrypt header field. + /// + Autocrypt, + + ///+ /// The Autocrypt-Gossip header field. + /// + AutocryptGossip, + + ///+ /// The Autocrypt-Setup-Message header field. + /// + AutocryptSetupMessage, + + ///+ /// The Autoforwarded header field. + /// + Autoforwarded, + + ///+ /// The Auto-Submitted header field. + /// + AutoSubmitted, + + ///+ /// The Autosubmitted header field. + /// + Autosubmitted, + + ///+ /// The Base header field. + /// + Base, + ////// The Bcc header field. /// Bcc, + ///+ /// The Body header field. + /// + Body, + ////// The Bytes header field. /// @@ -81,6 +164,11 @@ public enum HeaderId { /// Comments, + ///+ /// The Content-Alternative header field. + /// + ContentAlternative, + ////// The Content-Base header field. /// @@ -106,11 +194,21 @@ public enum HeaderId { /// ContentDuration, + ///+ /// The Content-Features header field. + /// + ContentFeatures, + ////// The Content-Id header field. /// ContentId, + ///+ /// The Content-Identifier header field. + /// + ContentIdentifier, + ////// The Content-Language header field. /// @@ -131,11 +229,21 @@ public enum HeaderId { /// ContentMd5, + ///+ /// The Content-Return header field. + /// + ContentReturn, + ////// The Content-Transfer-Encoding header field. /// ContentTransferEncoding, + ///+ /// The Content-Translation-Type header field. + /// + ContentTranslationType, + ////// The Content-Type header field. /// @@ -146,16 +254,41 @@ public enum HeaderId { /// Control, + ///+ /// The Conversion header field. + /// + Conversion, + + ///+ /// The Conversion-With-Loss header field. + /// + ConversionWithLoss, + ////// The Date header field. /// Date, + ///+ /// The Date-Received header field. + /// + DateReceived, + ////// The Deferred-Delivery header field. /// DeferredDelivery, + ///+ /// The Delivery-Date header field. + /// + DeliveryDate, + + ///+ /// The Disclose-Recipients header field. + /// + DiscloseRecipients, + ////// The Disposition-Notification-Options header field. /// @@ -213,11 +346,26 @@ public enum HeaderId { /// From, + ///+ /// The Generate-Delivery-Report header field. + /// + GenerateDeliveryReport, + ////// The Importance header field. /// Importance, + ///+ /// The Injection-Date header field. + /// + InjectionDate, + + ///+ /// The Injection-Info header field. + /// + InjectionInfo, + ////// The In-Reply-To header field. /// @@ -228,16 +376,46 @@ public enum HeaderId { /// Keywords, + ///+ /// The Language header. + /// + Language, + + ///+ /// The Latest-Delivery-Time header. + /// + LatestDeliveryTime, + ////// The Lines header field. /// Lines, + ///+ /// THe List-Archive header field. + /// + ListArchive, + ////// The List-Help header field. /// ListHelp, + ///+ /// The List-Id header field. + /// + ListId, + + ///+ /// The List-Owner header field. + /// + ListOwner, + + ///+ /// The List-Post header field. + /// + ListPost, + ////// The List-Subscribe header field. /// @@ -248,6 +426,11 @@ public enum HeaderId { /// ListUnsubscribe, + ///+ /// The List-Unsubscribe-Post header field. + /// + ListUnsubscribePost, + ////// The Message-Id header field. /// @@ -274,11 +457,31 @@ public enum HeaderId { /// Organization, + ///+ /// The Original-From header field. + /// + OriginalFrom, + + ///+ /// The Original-Message-Id header field. + /// + OriginalMessageId, + ////// The Original-Recipient header field. /// OriginalRecipient, + ///+ /// The Original-Return-Address header field. + /// + OriginalReturnAddress, + + ///+ /// The Original-Subject header field. + /// + OriginalSubject, + ////// The Path header field. /// @@ -289,6 +492,12 @@ public enum HeaderId { /// Precedence, + ///+ /// The Prevent-NonDelivery-Report header field. + /// + [HeaderName ("Prevent-NonDelivery-Report")] + PreventNonDeliveryReport, + ////// The Priority header field. /// @@ -299,11 +508,22 @@ public enum HeaderId { /// Received, + ///+ /// The Received-SPF header field. + /// + [HeaderName ("Received-SPF")] + ReceivedSPF, + ////// The References header field. /// References, + ///+ /// The Relay-Version header field. + /// + RelayVersion, + ////// The Reply-By header field. /// @@ -314,6 +534,11 @@ public enum HeaderId { /// ReplyTo, + ///+ /// The Require-Recipient-Valid-Since header field. + /// + RequireRecipientValidSince, + ////// The Resent-Bcc header field. /// @@ -364,6 +589,11 @@ public enum HeaderId { /// ReturnReceiptTo, + ///+ /// The See-Also header field. + /// + SeeAlso, + ////// The Sender header field. /// @@ -374,6 +604,11 @@ public enum HeaderId { /// Sensitivity, + ///+ /// The Solicitation header field. + /// + Solicitation, + ////// The Status header field. /// @@ -394,6 +629,12 @@ public enum HeaderId { /// Supersedes, + ///+ /// The TLS-Required header field. + /// + [HeaderName ("TLS-Required")] + TLSRequired, + ////// The To header field. /// @@ -404,6 +645,54 @@ public enum HeaderId { /// UserAgent, + ///+ /// The X400-Content-Identifier header field. + /// + [HeaderName ("X400-Content-Identifier")] + X400ContentIdentifier, + + ///+ /// The X400-Content-Return header field. + /// + [HeaderName ("X400-Content-Return")] + X400ContentReturn, + + ///+ /// The X400-Content-Type header field. + /// + [HeaderName ("X400-Content-Type")] + X400ContentType, + + ///+ /// The X400-MTS-Identifier header field. + /// + [HeaderName ("X400-MTS-Identifier")] + X400MTSIdentifier, + + ///+ /// The X400-Originator header field. + /// + [HeaderName ("X400-Originator")] + X400Originator, + + ///+ /// The X400-Received header field. + /// + [HeaderName ("X400-Received")] + X400Received, + + ///+ /// The X400-Recipients header field. + /// + [HeaderName ("X400-Recipients")] + X400Recipients, + + ///+ /// The X400-Trace header field. + /// + [HeaderName ("X400-Trace")] + X400Trace, + ////// The X-Mailer header field. /// @@ -475,7 +764,7 @@ public static string ToHeaderName (this HeaderId value) { var name = value.ToString (); -#if PORTABLE || NETSTANDARD +#if NETSTANDARD1_3 || NETSTANDARD1_6 var field = typeof (HeaderId).GetTypeInfo ().GetDeclaredField (name); var attrs = field.GetCustomAttributes (typeof (HeaderNameAttribute), false).ToArray (); #else diff --git a/MimeKit/HeaderList.cs b/MimeKit/HeaderList.cs index d8f0a396a9..0317d412fa 100644 --- a/MimeKit/HeaderList.cs +++ b/MimeKit/HeaderList.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,16 +32,12 @@ using System.Threading.Tasks; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - using MimeKit.IO; using MimeKit.Utils; namespace MimeKit { /// - /// A list of ///s. + /// A list of s. /// /// Represents a list of headers as found in a @@ -63,7 +59,7 @@ internal HeaderList (ParserOptions options) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new empty header list. @@ -73,7 +69,7 @@ public HeaderList () : this (ParserOptions.Default.Clone ()) } /// - /// Adds a header with the specified field and value. + /// Add a header with the specified field and value. /// ////// Adds a new header for the specified field and value pair. @@ -92,7 +88,7 @@ public void Add (HeaderId id, string value) } /// - /// Adds a header with the specified field and value. + /// Add a header with the specified field and value. /// ////// Adds a new header for the specified field and value pair. @@ -113,7 +109,7 @@ public void Add (string field, string value) } /// - /// Adds a header with the specified field and value. + /// Add a header with the specified field and value. /// ////// Adds a new header for the specified field and value pair. @@ -135,7 +131,7 @@ public void Add (HeaderId id, Encoding encoding, string value) } /// - /// Adds a header with the specified field and value. + /// Add a header with the specified field and value. /// ////// Adds a new header for the specified field and value pair. @@ -159,7 +155,7 @@ public void Add (string field, Encoding encoding, string value) } /// - /// Checks if the ///contains a header with the specified field name. + /// Check if the contains a header with the specified field name. /// /// Determines whether or not the header list contains the specified header. @@ -179,7 +175,7 @@ public bool Contains (HeaderId id) } /// - /// Checks if the ///contains a header with the specified field name. + /// Check if the contains a header with the specified field name. /// /// Determines whether or not the header list contains the specified header. @@ -199,7 +195,7 @@ public bool Contains (string field) } /// - /// Gets the index of the requested header, if it exists. + /// Get the index of the requested header, if it exists. /// ////// Finds the first index of the specified header, if it exists. @@ -223,7 +219,7 @@ public int IndexOf (HeaderId id) } /// - /// Gets the index of the requested header, if it exists. + /// Get the index of the requested header, if it exists. /// ////// Finds the first index of the specified header, if it exists. @@ -247,7 +243,7 @@ public int IndexOf (string field) } /// - /// Inserts a header with the specified field and value at the given index. + /// Insert a header with the specified field and value at the given index. /// ////// Inserts the header at the specified index in the list. @@ -269,7 +265,7 @@ public void Insert (int index, HeaderId id, string value) } /// - /// Inserts a header with the specified field and value at the given index. + /// Insert a header with the specified field and value at the given index. /// ////// Inserts the header at the specified index in the list. @@ -294,7 +290,7 @@ public void Insert (int index, string field, string value) } /// - /// Inserts a header with the specified field and value at the given index. + /// Insert a header with the specified field and value at the given index. /// ////// Inserts the header at the specified index in the list. @@ -319,7 +315,7 @@ public void Insert (int index, HeaderId id, Encoding encoding, string value) } /// - /// Inserts a header with the specified field and value at the given index. + /// Insert a header with the specified field and value at the given index. /// ////// Inserts the header at the specified index in the list. @@ -347,7 +343,7 @@ public void Insert (int index, string field, Encoding encoding, string value) } /// - /// Gets the last index of the requested header, if it exists. + /// Get the last index of the requested header, if it exists. /// ////// Finds the last index of the specified header, if it exists. @@ -371,7 +367,7 @@ public int LastIndexOf (HeaderId id) } /// - /// Gets the last index of the requested header, if it exists. + /// Get the last index of the requested header, if it exists. /// ////// Finds the last index of the specified header, if it exists. @@ -395,7 +391,7 @@ public int LastIndexOf (string field) } /// - /// Removes the first occurance of the specified header field. + /// Remove the first occurance of the specified header field. /// ////// Removes the first occurance of the specified header field, if any exist. @@ -419,7 +415,7 @@ public bool Remove (HeaderId id) } /// - /// Removes the first occurance of the specified header field. + /// Remove the first occurance of the specified header field. /// ////// Removes the first occurance of the specified header field, if any exist. @@ -443,7 +439,7 @@ public bool Remove (string field) } /// - /// Removes all of the headers matching the specified field name. + /// Remove all of the headers matching the specified field name. /// ////// Removes all of the headers matching the specified field name. @@ -471,7 +467,7 @@ public void RemoveAll (HeaderId id) } /// - /// Removes all of the headers matching the specified field name. + /// Remove all of the headers matching the specified field name. /// ////// Removes all of the headers matching the specified field name. @@ -499,7 +495,7 @@ public void RemoveAll (string field) } /// - /// Replaces all headers with identical field names with the single specified header. + /// Replace all headers with identical field names with the single specified header. /// ////// Replaces all headers with identical field names with the single specified header. @@ -522,7 +518,7 @@ public void Replace (HeaderId id, Encoding encoding, string value) } ///- /// Replaces all headers with identical field names with the single specified header. + /// Replace all headers with identical field names with the single specified header. /// ////// Replaces all headers with identical field names with the single specified header. @@ -542,7 +538,7 @@ public void Replace (HeaderId id, string value) } ///- /// Replaces all headers with identical field names with the single specified header. + /// Replace all headers with identical field names with the single specified header. /// ////// Replaces all headers with identical field names with the single specified header. @@ -564,7 +560,7 @@ public void Replace (string field, Encoding encoding, string value) } ///- /// Replaces all headers with identical field names with the single specified header. + /// Replace all headers with identical field names with the single specified header. /// ////// Replaces all headers with identical field names with the single specified header. @@ -586,7 +582,7 @@ public void Replace (string field, string value) } ///- /// Gets or sets the value of the first occurance of a header + /// Get or set the value of the first occurance of a header /// with the specified field name. /// ///@@ -626,7 +622,7 @@ public string this [HeaderId id] { } /// - /// Gets or sets the value of the first occurance of a header + /// Get or set the value of the first occurance of a header /// with the specified field name. /// ///@@ -668,7 +664,7 @@ public string this [string field] { } /// - /// Write the ///to the specified output stream. + /// Write the to the specified output stream. /// /// Writes all of the headers to the output stream. @@ -699,11 +695,14 @@ public string this [string field] { filtered.Add (options.CreateNewLineFilter ()); foreach (var header in headers) { - var rawValue = header.GetRawValue (options); - filtered.Write (header.RawField, 0, header.RawField.Length, cancellationToken); - filtered.Write (Header.Colon, 0, Header.Colon.Length, cancellationToken); - filtered.Write (rawValue, 0, rawValue.Length, cancellationToken); + + if (!header.IsInvalid) { + var rawValue = header.GetRawValue (options); + + filtered.Write (Header.Colon, 0, Header.Colon.Length, cancellationToken); + filtered.Write (rawValue, 0, rawValue.Length, cancellationToken); + } } filtered.Flush (cancellationToken); @@ -720,7 +719,7 @@ public string this [string field] { } /// - /// Asynchronously write the ///to the specified output stream. + /// Asynchronously write the to the specified output stream. /// /// Writes all of the headers to the output stream. @@ -752,11 +751,14 @@ public string this [string field] { filtered.Add (options.CreateNewLineFilter ()); foreach (var header in headers) { - var rawValue = header.GetRawValue (options); - await filtered.WriteAsync (header.RawField, 0, header.RawField.Length, cancellationToken).ConfigureAwait (false); - await filtered.WriteAsync (Header.Colon, 0, Header.Colon.Length, cancellationToken).ConfigureAwait (false); - await filtered.WriteAsync (rawValue, 0, rawValue.Length, cancellationToken).ConfigureAwait (false); + + if (!header.IsInvalid) { + var rawValue = header.GetRawValue (options); + + await filtered.WriteAsync (Header.Colon, 0, Header.Colon.Length, cancellationToken).ConfigureAwait (false); + await filtered.WriteAsync (rawValue, 0, rawValue.Length, cancellationToken).ConfigureAwait (false); + } } await filtered.FlushAsync (cancellationToken).ConfigureAwait (false); @@ -766,7 +768,7 @@ public string this [string field] { } /// - /// Write the ///to the specified output stream. + /// Write the to the specified output stream. /// /// Writes all of the headers to the output stream. @@ -788,7 +790,7 @@ public string this [string field] { } /// - /// Asynchronously write the ///to the specified output stream. + /// Asynchronously write the to the specified output stream. /// /// Writes all of the headers to the output stream. @@ -813,7 +815,7 @@ public string this [string field] { #region ICollection implementation /// - /// Gets the number of headers in the list. + /// Get the number of headers in the list. /// ////// Gets the number of headers in the list. @@ -824,7 +826,7 @@ public int Count { } /// - /// Gets whether or not the header list is read only. + /// Get whether or not the header list is read only. /// ////// A is never read-only. @@ -835,7 +837,7 @@ public bool IsReadOnly { } /// - /// Adds the specified header. + /// Add the specified header. /// ////// Adds the specified header to the end of the header list. @@ -859,7 +861,7 @@ public void Add (Header header) } /// - /// Clears the header list. + /// Clear the header list. /// ////// Removes all of the headers from the list. @@ -876,7 +878,7 @@ public void Clear () } /// - /// Checks if the ///contains the specified header. + /// Check if the contains the specified header. /// /// Determines whether or not the header list contains the specified header. @@ -896,7 +898,7 @@ public bool Contains (Header header) } /// - /// Copies all of the headers in the ///to the specified array. + /// Copy all of the headers in the to the specified array. /// /// Copies all of the headers within the into the array, @@ -916,7 +918,7 @@ public void CopyTo (Header[] array, int arrayIndex) } /// - /// Removes the specified header. + /// Remove the specified header. /// ////// Removes the specified header from the list if it exists. @@ -959,7 +961,7 @@ public bool Remove (Header header) } /// - /// Replaces all headers with identical field names with the single specified header. + /// Replace all headers with identical field names with the single specified header. /// ////// Replaces all headers with identical field names with the single specified header. @@ -1008,7 +1010,7 @@ public void Replace (Header header) #region IList implementation ///- /// Gets the index of the requested header, if it exists. + /// Get the index of the requested header, if it exists. /// ////// Finds the index of the specified header, if it exists. @@ -1027,7 +1029,7 @@ public int IndexOf (Header header) } /// - /// Inserts the specified header at the given index. + /// Insert the specified header at the given index. /// ////// Inserts the header at the specified index in the list. @@ -1066,7 +1068,7 @@ public void Insert (int index, Header header) } /// - /// Removes the header at the specified index. + /// Remove the header at the specified index. /// ////// Removes the header at the specified index. @@ -1077,7 +1079,7 @@ public void Insert (int index, Header header) /// - /// Gets or sets the ///at the specified index. + /// Get or set the at the specified index. /// - /// Gets or sets the ///at the specified index. + /// Gets or sets the at the specified index. /// The header at the specified index. /// The index. @@ -1117,13 +1119,13 @@ public void RemoveAt (int index) ///- /// Gets an enumerator for the list of headers. + /// Get an enumerator for the list of headers. /// ////// Gets an enumerator for the list of headers. @@ -1198,7 +1200,7 @@ public IEnumerator GetEnumerator () #region IEnumerable implementation /// - /// Gets an enumerator for the list of headers. + /// Get an enumerator for the list of headers. /// ////// Gets an enumerator for the list of headers. @@ -1362,7 +1364,6 @@ internal bool TryGetHeader (string field, out Header header) return LoadAsync (ParserOptions.Default, stream, cancellationToken); } -#if !PORTABLE /// /// Load a @@ -1381,8 +1382,7 @@ internal bool TryGetHeader (string field, out Header header) ///from the specified file. /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// ///is an invalid file path. @@ -1432,8 +1432,7 @@ internal bool TryGetHeader (string field, out Header header) /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// ///is an invalid file path. @@ -1480,8 +1479,7 @@ internal bool TryGetHeader (string field, out Header header) /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// ///is an invalid file path. @@ -1521,8 +1519,7 @@ internal bool TryGetHeader (string field, out Header header) /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// public HeaderList this [int index] { get { - if (index < 0 || index > Count) + if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); return groups[index]; } set { - if (index < 0 || index > Count) + if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); if (value == null) @@ -167,7 +167,7 @@ public bool Contains (HeaderList group) } ///is an invalid file path. @@ -1546,6 +1543,5 @@ internal bool TryGetHeader (string field, out Header header) { return LoadAsync (ParserOptions.Default, fileName, cancellationToken); } -#endif // !PORTABLE } } diff --git a/MimeKit/HeaderListChangedEventArgs.cs b/MimeKit/HeaderListChangedEventArgs.cs index 8673b917c2..f0da1ea1f8 100644 --- a/MimeKit/HeaderListChangedEventArgs.cs +++ b/MimeKit/HeaderListChangedEventArgs.cs @@ -1,9 +1,9 @@ // -// HeaderChangedEventArgs.cs +// HeaderListChangedEventArgs.cs // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/HeaderListCollection.cs b/MimeKit/HeaderListCollection.cs index 7e66aa7edb..211e113b6f 100644 --- a/MimeKit/HeaderListCollection.cs +++ b/MimeKit/HeaderListCollection.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -42,7 +42,7 @@ public class HeaderListCollection : ICollection readonly List groups; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -75,10 +75,10 @@ public bool IsReadOnly { } /// - /// Gets or sets the ///at the specified index. + /// Gets or sets the at the specified index. /// - /// Gets or sets the ///at the specified index. + /// Gets or sets the at the specified index. /// The group of headers at the specified index. /// The index. @@ -90,13 +90,13 @@ public bool IsReadOnly { ///- /// Copies all of the header groups in the ///to the specified array. + /// Copies all of the header groups in the to the specified array. /// /// Copies all of the header groups within the into the array, diff --git a/MimeKit/IMimeContent.cs b/MimeKit/IMimeContent.cs index 45cb3f22ff..d14331c8b0 100644 --- a/MimeKit/IMimeContent.cs +++ b/MimeKit/IMimeContent.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,7 @@ namespace MimeKit { /// - /// An interface for content stream encapsulation as used by ///. + /// An interface for content stream encapsulation as used by . /// /// Implemented by @@ -3247,7 +2800,6 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) return LoadAsync (ParserOptions.Default, stream, false, cancellationToken); } -#if !PORTABLE ///. @@ -41,7 +41,7 @@ namespace MimeKit { public interface IMimeContent { /// - /// Gets the content encoding. + /// Get the content encoding. /// ////// If the @@ -3168,7 +2721,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) /// defaultis not encoded, this value will be @@ -52,7 +52,18 @@ public interface IMimeContent ContentEncoding Encoding { get; } /// - /// Gets the content stream. + /// Get the new-line format, if known. + /// + ///+ /// + ///This property is typically only set by the + ///as it parses + /// the content of a and is only used as a hint when verifying + /// digital signatures. The new-line format, if known. + NewLineFormat? NewLineFormat { get; } + + ///+ /// Get the content stream. /// ////// Gets the content stream. @@ -61,7 +72,7 @@ public interface IMimeContent Stream Stream { get; } /// @@ -3135,7 +2688,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) /// default- /// Opens the decoded content stream. + /// Open the decoded content stream. /// ////// Provides a means of reading the decoded content without having to first write it to another @@ -71,13 +82,16 @@ public interface IMimeContent Stream Open (); /// @@ -3031,7 +2584,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) /// specified- /// Decodes the content stream into another stream. + /// Decode the content stream into another stream. /// ////// If the content stream is encoded, this method will decode it into the output stream /// using a suitable decoder based on the + ///property, otherwise the /// stream will be copied into the output stream as-is. /// + /// /// The output stream. /// The cancellation token. ///+ ///
@@ -89,19 +103,20 @@ public interface IMimeContent /// /// An I/O error occurred. /// - ///- /// void DecodeTo (Stream stream, CancellationToken cancellationToken = default (CancellationToken)); ///- ///
- /// Asynchronously decodes the content stream into another stream. + /// Asynchronously decode the content stream into another stream. /// ////// If the content stream is encoded, this method will decode it into the output stream /// using a suitable decoder based on the + ///property, otherwise the /// stream will be copied into the output stream as-is. /// + /// + ///+ ///
An awaitable task. /// The output stream. /// The cancellation token. ///@@ -113,13 +128,10 @@ public interface IMimeContent /// + [Obsolete ("Use DkimSigner.Sign() instead.")] public void Sign (DkimSigner signer, IList/// An I/O error occurred. /// - ///- /// Task DecodeToAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)); ///- ///
- /// Copies the content stream to the specified output stream. + /// Copy the content stream to the specified output stream. /// ////// public class MessageDispositionNotification : MimePart { HeaderList fields; ///This is equivalent to simply using ///@@ -141,7 +153,7 @@ public interface IMimeContent void WriteTo (Stream stream, CancellationToken cancellationToken = default (CancellationToken)); /// - /// Asynchronously copies the content stream to the specified output stream. + /// Asynchronously copy the content stream to the specified output stream. /// ////// + ///This is equivalent to simply using @@ -149,6 +161,7 @@ public interface IMimeContent /// If you want the decoded content, use /// ///instead. An awaitable task. /// The output stream. /// The cancellation token. ///diff --git a/MimeKit/IO/BoundStream.cs b/MimeKit/IO/BoundStream.cs index 2adcafbe4a..0bc2c79cd0 100644 --- a/MimeKit/IO/BoundStream.cs +++ b/MimeKit/IO/BoundStream.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -46,7 +46,7 @@ public class BoundStream : Stream bool eos; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// If the is less than 0 , then the end of the stream @@ -123,11 +123,11 @@ public long EndBoundary { ////// Checks whether or not the underlying stream will remain open after - /// the ///is disposed. + /// the is disposed. /// /// Checks whether or not the underlying stream will remain open after - /// the ///is disposed. + /// the is disposed. /// diff --git a/MimeKit/IO/ChainedStream.cs b/MimeKit/IO/ChainedStream.cs index 164a25ef23..7561e4c981 100644 --- a/MimeKit/IO/ChainedStream.cs +++ b/MimeKit/IO/ChainedStream.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast true if the underlying stream should remain open after the ///is disposed; otherwise, false .// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -49,7 +49,7 @@ public class ChainedStream : Stream bool eos; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/IO/FilteredStream.cs b/MimeKit/IO/FilteredStream.cs index 9576532d61..604bc8fe44 100644 --- a/MimeKit/IO/FilteredStream.cs +++ b/MimeKit/IO/FilteredStream.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -58,7 +58,7 @@ enum IOOperation : byte { bool flushed; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a filtered stream using the specified source stream. @@ -522,6 +522,9 @@ public void Write (byte[] buffer, int offset, int count, CancellationToken cance foreach (var filter in filters) filtered = filter.Filter (filtered, filteredIndex, filteredLength, out filteredIndex, out filteredLength); + if (filteredLength == 0) + return; + var cancellable = Source as ICancellableStream; if (cancellable != null) { @@ -690,12 +693,6 @@ public void Flush (CancellationToken cancellationToken) filteredIndex = 0; filteredLength = 0; } - - if (cancellable != null) { - cancellable.Flush (cancellationToken); - } else { - Source.Flush (); - } } /// @@ -767,8 +764,6 @@ public override async Task FlushAsync (CancellationToken cancellationToken) filteredIndex = 0; filteredLength = 0; } - - await Source.FlushAsync (cancellationToken).ConfigureAwait (false); } /// diff --git a/MimeKit/IO/Filters/ArmoredFromFilter.cs b/MimeKit/IO/Filters/ArmoredFromFilter.cs index 86f5731ae0..48237a21d0 100644 --- a/MimeKit/IO/Filters/ArmoredFromFilter.cs +++ b/MimeKit/IO/Filters/ArmoredFromFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -45,7 +45,7 @@ public class ArmoredFromFilter : MimeFilterBase bool midline; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/IO/Filters/BestEncodingFilter.cs b/MimeKit/IO/Filters/BestEncodingFilter.cs index 9e0e143510..557d851f43 100644 --- a/MimeKit/IO/Filters/BestEncodingFilter.cs +++ b/MimeKit/IO/Filters/BestEncodingFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -42,9 +42,10 @@ public class BestEncodingFilter : IMimeFilter int markerLength; bool hasMarker; int total; + byte pc; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -153,9 +154,13 @@ unsafe void Scan (byte* inptr, byte* inend) marker[markerLength++] = c; linelen++; + pc = c; } if (c == (byte) '\n') { + if (pc == (byte) '\r') + linelen--; + maxline = Math.Max (maxline, linelen); linelen = 0; @@ -259,6 +264,7 @@ public void Reset () count0 = 0; count8 = 0; total = 0; + pc = 0; } #endregion diff --git a/MimeKit/IO/Filters/CharsetFilter.cs b/MimeKit/IO/Filters/CharsetFilter.cs index 8892fe0be4..d673fe366d 100644 --- a/MimeKit/IO/Filters/CharsetFilter.cs +++ b/MimeKit/IO/Filters/CharsetFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,12 +27,6 @@ using System; using System.Text; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -using Encoder = Portable.Text.Encoder; -using Decoder = Portable.Text.Decoder; -#endif - using MimeKit.Utils; namespace MimeKit.IO.Filters { @@ -58,7 +52,7 @@ static Encoding GetEncoding (string paramName, string encodingName) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new to convert text from the specified @@ -83,7 +77,7 @@ public CharsetFilter (string sourceEncodingName, string targetEncodingName) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new to convert text from the specified @@ -107,7 +101,7 @@ public CharsetFilter (int sourceCodePage, int targetCodePage) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new to convert text from the specified diff --git a/MimeKit/IO/Filters/DecoderFilter.cs b/MimeKit/IO/Filters/DecoderFilter.cs index 21821d356e..c85cc491ba 100644 --- a/MimeKit/IO/Filters/DecoderFilter.cs +++ b/MimeKit/IO/Filters/DecoderFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -61,7 +61,7 @@ public ContentEncoding Encoding { } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new using the specified decoder. diff --git a/MimeKit/IO/Filters/Dos2UnixFilter.cs b/MimeKit/IO/Filters/Dos2UnixFilter.cs index 48cb550f5f..14978344f7 100644 --- a/MimeKit/IO/Filters/Dos2UnixFilter.cs +++ b/MimeKit/IO/Filters/Dos2UnixFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -37,7 +37,7 @@ public class Dos2UnixFilter : MimeFilterBase byte pc; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/IO/Filters/EncoderFilter.cs b/MimeKit/IO/Filters/EncoderFilter.cs index 8345ea6abc..8f261bc31a 100644 --- a/MimeKit/IO/Filters/EncoderFilter.cs +++ b/MimeKit/IO/Filters/EncoderFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -61,7 +61,7 @@ public ContentEncoding Encoding { } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new using the specified encoder. diff --git a/MimeKit/IO/Filters/IMimeFilter.cs b/MimeKit/IO/Filters/IMimeFilter.cs index 113976a985..01547422a3 100644 --- a/MimeKit/IO/Filters/IMimeFilter.cs +++ b/MimeKit/IO/Filters/IMimeFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/IO/Filters/MimeFilterBase.cs b/MimeKit/IO/Filters/MimeFilterBase.cs index 887f9a6d85..4f229b92b0 100644 --- a/MimeKit/IO/Filters/MimeFilterBase.cs +++ b/MimeKit/IO/Filters/MimeFilterBase.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -41,7 +41,7 @@ public abstract class MimeFilterBase : IMimeFilter byte[] inbuf; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/IO/Filters/PassThroughFilter.cs b/MimeKit/IO/Filters/PassThroughFilter.cs index 51474769b4..61bc497fe0 100644 --- a/MimeKit/IO/Filters/PassThroughFilter.cs +++ b/MimeKit/IO/Filters/PassThroughFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -34,7 +34,7 @@ namespace MimeKit.IO.Filters { public class PassThroughFilter : IMimeFilter { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/IO/Filters/TrailingWhitespaceFilter.cs b/MimeKit/IO/Filters/TrailingWhitespaceFilter.cs index f165651389..08916e5936 100644 --- a/MimeKit/IO/Filters/TrailingWhitespaceFilter.cs +++ b/MimeKit/IO/Filters/TrailingWhitespaceFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -38,7 +38,7 @@ public class TrailingWhitespaceFilter : MimeFilterBase readonly PackedByteArray lwsp = new PackedByteArray (); /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/IO/Filters/Unix2DosFilter.cs b/MimeKit/IO/Filters/Unix2DosFilter.cs index 921bc661ca..d70f31e315 100644 --- a/MimeKit/IO/Filters/Unix2DosFilter.cs +++ b/MimeKit/IO/Filters/Unix2DosFilter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -37,7 +37,7 @@ public class Unix2DosFilter : MimeFilterBase byte pc; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/IO/ICancellableStream.cs b/MimeKit/IO/ICancellableStream.cs index 323c6b7112..2fa2c6dd52 100644 --- a/MimeKit/IO/ICancellableStream.cs +++ b/MimeKit/IO/ICancellableStream.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,9 +32,9 @@ namespace MimeKit.IO { /// /// This interface is meant to extend the functionality of a + /// allowing the, - /// allowing the to have much finer-grained canellability. to have much finer-grained canellability. When a custom stream implementation also implements this interface, - /// the ///will opt to use this interface + /// the will opt to use this interface /// instead of the normal /// API to read data from the stream. This is really useful when parsing a message or other MIME entity @@ -48,7 +48,7 @@ public interface ICancellableStream /// /// /// + ///When a custom stream implementation also implements this interface, - /// the ///will opt to use this interface + /// the will opt to use this interface /// instead of the normal /// API to read data from the stream. This is really useful when parsing a message or other MIME entity @@ -68,7 +68,7 @@ public interface ICancellableStream /// /// ////// When a custom stream implementation also implements this interface, - /// writing a diff --git a/MimeKit/IO/MeasuringStream.cs b/MimeKit/IO/MeasuringStream.cs index c6ad01a852..6ce1c70911 100644 --- a/MimeKit/IO/MeasuringStream.cs +++ b/MimeKit/IO/MeasuringStream.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastor + /// writing a or /// to the custom stream will opt to use this interface /// instead of the normal /// API to write data to the stream. // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -46,7 +46,7 @@ public class MeasuringStream : Stream long length; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new + ///. diff --git a/MimeKit/IO/MemoryBlockStream.cs b/MimeKit/IO/MemoryBlockStream.cs index f0d81e398e..441f5123b8 100644 --- a/MimeKit/IO/MemoryBlockStream.cs +++ b/MimeKit/IO/MemoryBlockStream.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,9 +27,13 @@ using System; using System.IO; using System.Threading; +using System.Diagnostics; +using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; +using MimeKit.Utils; + namespace MimeKit.IO { /// /// An efficient memory stream implementation that sacrifices the ability to @@ -47,12 +51,15 @@ public class MemoryBlockStream : Stream const long MaxCapacity = int.MaxValue * BlockSize; const long BlockSize = 2048; + static readonly BufferPool DefaultPool = new BufferPool ((int) BlockSize, 200); + readonly List blocks = new List (); + readonly BufferPool pool; long position, length; bool disposed; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new public MemoryBlockStream () { - blocks.Add (new byte[BlockSize]); + pool = DefaultPool; + blocks.Add (pool.Rent (Debugger.IsAttached)); } ///with an initial memory block @@ -60,7 +67,8 @@ public class MemoryBlockStream : Stream /// @@ -319,7 +327,7 @@ public override void Write (byte[] buffer, int offset, int count) ValidateArguments (buffer, offset, count); if (position + count >= MaxCapacity) - throw new IOException (string.Format ("Cannot exceed {0} bytes", MaxCapacity)); + throw new IOException (string.Format (CultureInfo.InvariantCulture, "Cannot exceed {0} bytes", MaxCapacity)); int startIndex = (int) (position % BlockSize); long capacity = blocks.Count * BlockSize; @@ -327,7 +335,7 @@ public override void Write (byte[] buffer, int offset, int count) int nwritten = 0; while (capacity < position + count) { - blocks.Add (new byte[BlockSize]); + blocks.Add (pool.Rent (Debugger.IsAttached)); capacity += BlockSize; } @@ -426,7 +434,7 @@ public override long Seek (long offset, SeekOrigin origin) throw new IOException ("Cannot seek to a position before the beginning of the stream"); if (real > MaxCapacity) - throw new IOException (string.Format ("Cannot exceed {0} bytes", MaxCapacity)); + throw new IOException (string.Format (CultureInfo.InvariantCulture, "Cannot exceed {0} bytes", MaxCapacity)); // short-cut if we are seeking to our current position if (real == position) @@ -501,12 +509,13 @@ public override void SetLength (long value) if (value > capacity) { do { - blocks.Add (new byte[BlockSize]); + blocks.Add (pool.Rent (Debugger.IsAttached)); capacity += BlockSize; } while (capacity < value); } else if (value < length) { // shed any blocks that are no longer needed while (capacity - value > BlockSize) { + pool.Return (blocks[blocks.Count - 1]); blocks.RemoveAt (blocks.Count - 1); capacity -= BlockSize; } @@ -533,8 +542,17 @@ public override void SetLength (long value) /// ///false to release only the unmanaged resources. protected override void Dispose (bool disposing) { + if (disposing && !disposed) { + for (int i = 0; i < blocks.Count; i++) { + pool.Return (blocks[i]); + blocks[i] = null; + } + + blocks.Clear (); + disposed = true; + } + base.Dispose (disposing); - disposed = true; } } } diff --git a/MimeKit/InternetAddress.cs b/MimeKit/InternetAddress.cs index b239eb5b22..4836013f26 100644 --- a/MimeKit/InternetAddress.cs +++ b/MimeKit/InternetAddress.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,27 +26,14 @@ using System; using System.Text; +using System.Globalization; using System.Collections.Generic; -#if PORTABLE -using EncoderReplacementFallback = Portable.Text.EncoderReplacementFallback; -using DecoderReplacementFallback = Portable.Text.DecoderReplacementFallback; -using EncoderExceptionFallback = Portable.Text.EncoderExceptionFallback; -using DecoderExceptionFallback = Portable.Text.DecoderExceptionFallback; -using EncoderFallbackException = Portable.Text.EncoderFallbackException; -using DecoderFallbackException = Portable.Text.DecoderFallbackException; -using DecoderFallbackBuffer = Portable.Text.DecoderFallbackBuffer; -using DecoderFallback = Portable.Text.DecoderFallback; -using Encoding = Portable.Text.Encoding; -using Encoder = Portable.Text.Encoder; -using Decoder = Portable.Text.Decoder; -#endif - using MimeKit.Utils; namespace MimeKit { /// - /// An internet address, as specified by rfc0822. + /// An abstract internet address, as specified by rfc0822. /// ////// A can be any type of address defined by the @@ -66,7 +53,7 @@ public abstract class InternetAddress : IComparable , IEquatable string name; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Initializes the and properties of the internet address. @@ -86,7 +73,7 @@ protected InternetAddress (Encoding encoding, string name) } /// - /// Gets or sets the character encoding to use when encoding the name of the address. + /// Get or set the character encoding to use when encoding the name of the address. /// ////// The character encoding is used to convert the property, if it is set, @@ -111,7 +98,7 @@ public Encoding Encoding { } /// - /// Gets or sets the display name of the address. + /// Get or set the display name of the address. /// ////// A name is optional and is typically set to the name of the person @@ -201,33 +188,58 @@ public int CompareTo (InternetAddress other) #region IEquatable implementation /// - /// Determines whether the specified ///is equal to the current . + /// Determines whether the specified is equal to the current . /// /// Compares two internet addresses to determine if they are identical or not. /// - /// Theto compare with the current . - /// + /// The true if the specifiedis equal to the current - /// ; otherwise, false .to compare with the current . + /// public abstract bool Equals (InternetAddress other); #endregion + /// true if the specifiedis equal to the current + /// ; otherwise, false .+ /// Determine whether the specified object is equal to the current object. + /// + ///+ /// The type of comparison between the current instance and the + /// The object to compare with the current object. + ///parameter depends on whether + /// the current instance is a reference type or a value type. + /// + public override bool Equals (object obj) + { + return Equals (obj as InternetAddress); + } + + /// true if the specified object is equal to the current object; otherwise,false .+ /// Return the hash code for this instance. + /// + ///+ /// Returns the hash code for this instance. + /// + ///A hash code for the current object. + public override int GetHashCode () + { + return ToString ().GetHashCode (); + } + internal static string EncodeInternationalizedPhrase (string phrase) { for (int i = 0; i < phrase.Length; i++) { - if (char.IsControl (phrase[i]) || AtomSpecials.IndexOf (phrase[i]) != -1) + if (AtomSpecials.IndexOf (phrase[i]) != -1) return MimeUtils.Quote (phrase); } return phrase; } - internal abstract void Encode (FormatOptions options, StringBuilder builder, ref int lineLength); + internal abstract void Encode (FormatOptions options, StringBuilder builder, bool firstToken, ref int lineLength); ///- /// Returns a string representation of the ///, - /// optionally encoding it for transport. + /// Serialize an to a string, optionally encoding it for transport. /// /// If the parameter is true , then this method will return @@ -244,8 +256,7 @@ internal static string EncodeInternationalizedPhrase (string phrase) public abstract string ToString (FormatOptions options, bool encode); ///- /// Returns a string representation of the ///, - /// optionally encoding it for transport. + /// Serialize an to a string, optionally encoding it for transport. /// /// If the parameter is true , then this method will return @@ -261,7 +272,7 @@ public string ToString (bool encode) } ///- /// Returns a string representation of a ///suitable for display. + /// Serialize an to a string suitable for display. /// /// The string returned by this method is suitable only for display purposes. @@ -275,7 +286,7 @@ public override string ToString () internal event EventHandler Changed; /// - /// Raises the internal changed event used by ///to keep headers in sync. + /// Raise the internal changed event used by to keep headers in sync. /// /// This method is called whenever a property of the internet address is changed. @@ -286,7 +297,7 @@ protected virtual void OnChanged () Changed (this, EventArgs.Empty); } - internal static bool TryParseLocalPart (byte[] text, ref int index, int endIndex, bool throwOnError, out string localpart) + internal static bool TryParseLocalPart (byte[] text, ref int index, int endIndex, bool skipTrailingCfws, bool throwOnError, out string localpart) { var token = new StringBuilder (); int startIndex = index; @@ -296,7 +307,7 @@ internal static bool TryParseLocalPart (byte[] text, ref int index, int endIndex do { if (!text[index].IsAtom () && text[index] != '"' && text[index] != '.') { if (throwOnError) - throw new ParseException (string.Format ("Invalid local-part at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid local-part at offset {0}", startIndex), startIndex, index); return false; } @@ -307,18 +318,26 @@ internal static bool TryParseLocalPart (byte[] text, ref int index, int endIndex try { token.Append (CharsetUtils.UTF8.GetString (text, start, index - start)); - } catch (DecoderFallbackException ex) { - if (throwOnError) - throw new ParseException ("Internationalized local-part tokens may only contain UTF-8 characters.", start, start, ex); + } catch (DecoderFallbackException) { + try { + token.Append (CharsetUtils.Latin1.GetString (text, start, index - start)); + } catch (DecoderFallbackException ex) { + if (throwOnError) + throw new ParseException ("Internationalized local-part tokens may only contain UTF-8 characters.", start, start, ex); - return false; + return false; + } } + int cfws = index; if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) return false; - if (index >= endIndex || text[index] != (byte) '.') + if (index >= endIndex || text[index] != (byte) '.') { + if (!skipTrailingCfws) + index = cfws; break; + } token.Append ('.'); index++; @@ -328,7 +347,7 @@ internal static bool TryParseLocalPart (byte[] text, ref int index, int endIndex if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete local-part at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete local-part at offset {0}", startIndex), startIndex, index); return false; } @@ -340,6 +359,9 @@ internal static bool TryParseLocalPart (byte[] text, ref int index, int endIndex localpart = token.ToString (); + if (ParseUtils.IsIdnEncoded (localpart)) + localpart = ParseUtils.IdnDecode (localpart); + return true; } @@ -348,12 +370,12 @@ internal static bool TryParseLocalPart (byte[] text, ref int index, int endIndex internal static bool TryParseAddrspec (byte[] text, ref int index, int endIndex, byte[] sentinels, bool throwOnError, out string addrspec, out int at) { int startIndex = index; + string localpart; addrspec = null; at = -1; - string localpart; - if (!TryParseLocalPart (text, ref index, endIndex, throwOnError, out localpart)) + if (!TryParseLocalPart (text, ref index, endIndex, true, throwOnError, out localpart)) return false; if (index >= endIndex || ParseUtils.IsSentinel (text[index], sentinels)) { @@ -363,7 +385,7 @@ internal static bool TryParseAddrspec (byte[] text, ref int index, int endIndex, if (text[index] != (byte) '@') { if (throwOnError) - throw new ParseException (string.Format ("Invalid addr-spec token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid addr-spec token at offset {0}", startIndex), startIndex, index); return false; } @@ -371,7 +393,7 @@ internal static bool TryParseAddrspec (byte[] text, ref int index, int endIndex, index++; if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete addr-spec token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete addr-spec token at offset {0}", startIndex), startIndex, index); return false; } @@ -381,7 +403,7 @@ internal static bool TryParseAddrspec (byte[] text, ref int index, int endIndex, if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete addr-spec token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete addr-spec token at offset {0}", startIndex), startIndex, index); return false; } @@ -419,7 +441,7 @@ internal static bool TryParseMailbox (ParserOptions options, byte[] text, int st if (index < endIndex && text[index] == (byte) '<') { if (options.AddressParserComplianceMode == RfcComplianceMode.Strict) { if (throwOnError) - throw new ParseException (string.Format ("Excessive angle brackets at offset {0}", index), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Excessive angle brackets at offset {0}", index), startIndex, index); return false; } @@ -429,36 +451,44 @@ internal static bool TryParseMailbox (ParserOptions options, byte[] text, int st } while (index < endIndex && text[index] == '<'); } + if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete mailbox at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete mailbox at offset {0}", startIndex), startIndex, index); return false; } - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - if (text[index] == (byte) '@') { // Note: we always pass 'false' as the throwOnError argument here so that we can throw a more informative exception on error if (!DomainList.TryParse (text, ref index, endIndex, false, out route)) { if (throwOnError) - throw new ParseException (string.Format ("Invalid route in mailbox at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid route in mailbox at offset {0}", startIndex), startIndex, index); return false; } - if (index + 1 >= endIndex || text[index] != (byte) ':') { + if (index >= endIndex || text[index] != (byte) ':') { if (throwOnError) - throw new ParseException (string.Format ("Incomplete route in mailbox at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete route in mailbox at offset {0}", startIndex), startIndex, index); return false; } + // skip over ':' index++; if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) return false; + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete mailbox at offset {0}", startIndex), startIndex, index); + + return false; + } } // Note: The only syntactically correct sentinel token here is the '>', but alas... to deal with the first example @@ -478,7 +508,7 @@ internal static bool TryParseMailbox (ParserOptions options, byte[] text, int st if (index >= endIndex || text[index] != (byte) '>') { if (options.AddressParserComplianceMode == RfcComplianceMode.Strict) { if (throwOnError) - throw new ParseException (string.Format ("Unexpected end of mailbox at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected end of mailbox at offset {0}", startIndex), startIndex, index); return false; } @@ -490,7 +520,7 @@ internal static bool TryParseMailbox (ParserOptions options, byte[] text, int st if (index < endIndex && text[index] == (byte) '>') { if (options.AddressParserComplianceMode == RfcComplianceMode.Strict) { if (throwOnError) - throw new ParseException (string.Format ("Excessive angle brackets at offset {0}", index), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Excessive angle brackets at offset {0}", index), startIndex, index); return false; } @@ -535,7 +565,7 @@ static bool TryParseGroup (ParserOptions options, byte[] text, int startIndex, r if (index >= endIndex || text[index] != (byte) ';') { if (throwOnError && options.AddressParserComplianceMode == RfcComplianceMode.Strict) - throw new ParseException (string.Format ("Expected to find ';' at offset {0}", index), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Expected to find ';' at offset {0}", index), startIndex, index); while (index < endIndex && text[index] != (byte) ';') index++; @@ -560,7 +590,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index { bool strict = options.AddressParserComplianceMode == RfcComplianceMode.Strict; bool throwOnError = (flags & AddressParserFlags.ThrowOnError) != 0; - int minWordCount = options.AllowAddressesWithoutDomain ? 1 : 0; + int minWordCount = options.AllowUnquotedCommasInAddresses ? 0 : 1; address = null; @@ -592,9 +622,9 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index ParseUtils.SkipWhiteSpace (text, ref index, endIndex); - if (!ParseUtils.SkipAtom (text, ref index, endIndex)) { + if (!ParseUtils.SkipPhraseAtom (text, ref index, endIndex)) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete quoted-string token at offset {0}", qstringIndex), qstringIndex, endIndex); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete quoted-string token at offset {0}", qstringIndex), qstringIndex, endIndex); break; } @@ -603,7 +633,6 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index trimLeadingQuote = true; } } else { - if (!ParseUtils.SkipWordAndPeriod (text, ref index, endIndex, throwOnError)) break; } @@ -646,13 +675,18 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if (index >= endIndex || text[index] == (byte) ',' || text[index] == (byte) '>' || text[index] == ';') { // we've completely gobbled up an addr-spec w/o a domain byte sentinel = index < endIndex ? text[index] : (byte) ','; - var sentinels = new byte [] { sentinel }; string name, addrspec; - int at; if ((flags & AddressParserFlags.AllowMailboxAddress) == 0) { if (throwOnError) - throw new ParseException (string.Format ("Addr-spec token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Addr-spec token at offset {0}", startIndex), startIndex, index); + + return false; + } + + if (!options.AllowAddressesWithoutDomain) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete addr-spec token at offset {0}", startIndex), startIndex, index); return false; } @@ -660,24 +694,20 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index // rewind back to the beginning of the local-part index = startIndex; - if (!TryParseAddrspec (text, ref index, endIndex, sentinels, throwOnError, out addrspec, out at)) + if (!TryParseLocalPart (text, ref index, endIndex, false, throwOnError, out addrspec)) return false; ParseUtils.SkipWhiteSpace (text, ref index, endIndex); if (index < endIndex && text[index] == '(') { - int comment = index; - - if (!ParseUtils.SkipComment (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete comment token at offset {0}", comment), comment, index); - - return false; - } + int comment = index + 1; - comment++; + // Note: this can't fail because it has already been skipped in TryParseLocalPart() above. + ParseUtils.SkipComment (text, ref index, endIndex); name = Rfc2047.DecodePhrase (options, text, comment, (index - 1) - comment).Trim (); + + ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError); } else { name = string.Empty; } @@ -685,7 +715,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if (index < endIndex && text[index] == (byte) '>') { if (strict) { if (throwOnError) - throw new ParseException (string.Format ("Unexpected '>' token at offset {0}", index), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected '>' token at offset {0}", index), startIndex, index); return false; } @@ -693,7 +723,14 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index index++; } - address = new MailboxAddress (Encoding.UTF8, name, addrspec, at); + if (index < endIndex && text[index] != sentinel) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), startIndex, index); + + return false; + } + + address = new MailboxAddress (Encoding.UTF8, name, addrspec, -1); return true; } @@ -706,14 +743,14 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if ((flags & AddressParserFlags.AllowGroupAddress) == 0) { if (throwOnError) - throw new ParseException (string.Format ("Group address token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Group address token at offset {0}", startIndex), startIndex, index); return false; } if (groupDepth >= options.MaxAddressGroupDepth) { if (throwOnError) - throw new ParseException (string.Format ("Exceeded maximum rfc822 group depth at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Exceeded maximum rfc822 group depth at offset {0}", startIndex), startIndex, index); return false; } @@ -737,7 +774,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if ((flags & AddressParserFlags.AllowMailboxAddress) == 0) { if (throwOnError) - throw new ParseException (string.Format ("Mailbox address token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Mailbox address token at offset {0}", startIndex), startIndex, index); return false; } @@ -760,7 +797,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if (!ParseUtils.SkipComment (text, ref index, endIndex)) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete comment token at offset {0}", comment), comment, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete comment token at offset {0}", comment), comment, index); return false; } @@ -784,7 +821,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index // We have an address like "user@example.com "; i.e. the name is an unquoted string with an '@'. if (strict) { if (throwOnError) - throw new ParseException (string.Format ("Unexpected '<' token at offset {0}", index), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected '<' token at offset {0}", index), startIndex, index); return false; } @@ -802,7 +839,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if (text[index] == (byte) '>') { if (strict) { if (throwOnError) - throw new ParseException (string.Format ("Unexpected '>' token at offset {0}", index), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected '>' token at offset {0}", index), startIndex, index); return false; } @@ -840,13 +877,13 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index } if (throwOnError) - throw new ParseException (string.Format ("Invalid address token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid address token at offset {0}", startIndex), startIndex, index); return false; } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single or . If the buffer contains @@ -891,7 +928,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single or . If the buffer contains @@ -915,7 +952,7 @@ public static bool TryParse (byte[] buffer, int startIndex, int length, out Inte } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single or . If the buffer contains @@ -953,7 +990,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single or . If the buffer contains @@ -975,7 +1012,7 @@ public static bool TryParse (byte[] buffer, int startIndex, out InternetAddress } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single or . If the buffer contains @@ -1009,7 +1046,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, out InternetA } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single or . If the buffer contains @@ -1027,7 +1064,7 @@ public static bool TryParse (byte[] buffer, out InternetAddress address) } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a single or . If the text contains @@ -1060,7 +1097,7 @@ public static bool TryParse (ParserOptions options, string text, out InternetAdd } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a single or . If the text contains @@ -1078,13 +1115,13 @@ public static bool TryParse (string text, out InternetAddress address) } /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///or . If the buffer contains /// more data, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The input buffer. /// The starting index of the input buffer. @@ -1115,19 +1152,19 @@ public static InternetAddress Parse (ParserOptions options, byte[] buffer, int s ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return address; } ///. - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///or . If the buffer contains /// more data, then parsing will fail. /// The parsed + ///. The parsed /// The input buffer. /// The starting index of the input buffer. /// The number of bytes in the input buffer to parse. @@ -1147,13 +1184,13 @@ public static InternetAddress Parse (byte[] buffer, int startIndex, int length) } ///. - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///or . If the buffer contains /// more data, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The input buffer. /// The starting index of the input buffer. @@ -1182,19 +1219,19 @@ public static InternetAddress Parse (ParserOptions options, byte[] buffer, int s ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return address; } ///. - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///or . If the buffer contains /// more data, then parsing will fail. /// The parsed + ///. The parsed /// The input buffer. /// The starting index of the input buffer. ///. @@ -1212,13 +1249,13 @@ public static InternetAddress Parse (byte[] buffer, int startIndex) } /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///or . If the buffer contains /// more data, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The input buffer. ///. @@ -1243,19 +1280,19 @@ public static InternetAddress Parse (ParserOptions options, byte[] buffer) ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return address; } /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///or . If the buffer contains /// more data, then parsing will fail. /// The parsed + ///. The parsed /// The input buffer. ///. /// is null . @@ -1269,13 +1306,13 @@ public static InternetAddress Parse (byte[] buffer) } ///- /// Parses the given text into a new ///instance. + /// Parse the given text into a new instance. /// /// Parses a single - ///or . If the text contains /// more data, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The text. ///. @@ -1301,19 +1338,19 @@ public static InternetAddress Parse (ParserOptions options, string text) ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return address; } /// - /// Parses the given text into a new ///instance. + /// Parse the given text into a new instance. /// /// Parses a single - ///or . If the text contains /// more data, then parsing will fail. /// The parsed + ///. The parsed /// The text. ///. /// public void Insert (int index, InternetAddress address) { + if (index < 0 || index > list.Count) + throw new ArgumentOutOfRangeException (nameof (index)); + if (address == null) throw new ArgumentNullException (nameof (address)); @@ -156,7 +159,7 @@ public void Insert (int index, InternetAddress address) } ///is null . diff --git a/MimeKit/InternetAddressList.cs b/MimeKit/InternetAddressList.cs index 1c128ab1af..e989bfc786 100644 --- a/MimeKit/InternetAddressList.cs +++ b/MimeKit/InternetAddressList.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -55,7 +55,7 @@ public class InternetAddressList : IList , IEquatable list = new List (); /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new containing the supplied addresses. @@ -76,7 +76,7 @@ public InternetAddressList (IEnumerable addresses) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new, empty, . @@ -86,10 +86,10 @@ public InternetAddressList () } /// - /// Recursively gets all of the mailboxes contained within the ///. + /// Recursively get all of the mailboxes contained within the . /// - /// This API is useful for collecting a flattened list of ///+ /// This API is useful for collecting a flattened list of /// recipients for use with sending via SMTP or for encrypting via S/MIME or PGP/MIME. /// The mailboxes. @@ -113,7 +113,7 @@ public IEnumerableMailboxes { #region IList implementation /// - /// Gets the index of the specified address. + /// Get the index of the specified address. /// ////// Finds the index of the specified address, if it exists. @@ -132,7 +132,7 @@ public int IndexOf (InternetAddress address) } /// - /// Inserts the address at the specified index. + /// Insert an address at the specified index. /// ////// Inserts the address at the specified index in the list. @@ -147,6 +147,9 @@ public int IndexOf (InternetAddress address) /// - /// Removes the address at the specified index. + /// Remove the address at the specified index. /// ////// Removes the address at the specified index. @@ -176,10 +179,10 @@ public void RemoveAt (int index) } /// - /// Gets or sets the ///at the specified index. + /// Get or set the at the specified index. /// - /// Gets or sets the ///at the specified index. + /// Gets or sets the at the specified index. /// The internet address at the specified index. /// The index of the address to get or set. @@ -192,12 +195,17 @@ public void RemoveAt (int index) public InternetAddress this [int index] { get { return list[index]; } set { + if (index < 0 || index >= list.Count) + throw new ArgumentOutOfRangeException (nameof (index)); + if (value == null) throw new ArgumentNullException (nameof (value)); if (list[index] == value) return; + list[index].Changed -= AddressChanged; + value.Changed += AddressChanged; list[index] = value; OnChanged (); } @@ -208,7 +216,7 @@ public InternetAddress this [int index] { #region ICollection implementation ///- /// Gets the number of addresses in the ///. + /// Get the number of addresses in the . /// /// Indicates the number of addresses in the list. @@ -219,7 +227,7 @@ public int Count { } /// - /// Gets a value indicating whether this instance is read only. + /// Get a value indicating whether the ///is read only. /// /// A is never read-only. @@ -230,7 +238,7 @@ public bool IsReadOnly { } /// - /// Adds the specified address. + /// Add an address to the ///. /// /// Adds the specified address to the end of the address list. @@ -250,7 +258,7 @@ public void Add (InternetAddress address) } /// - /// Adds a collection of addresses. + /// Add a collection of addresses to the ///. /// /// Adds a range of addresses to the end of the address list. @@ -277,7 +285,7 @@ public void AddRange (IEnumerable addresses) } /// - /// Clears the address list. + /// Clear the address list. /// ////// Removes all of the addresses from the list. @@ -295,7 +303,7 @@ public void Clear () } /// - /// Checks if the ///contains the specified address. + /// Check if the contains the specified address. /// /// Determines whether or not the address list contains the specified address. @@ -315,7 +323,7 @@ public bool Contains (InternetAddress address) } /// - /// Copies all of the addresses in the ///to the specified array. + /// Copy all of the addresses in the to the specified array. /// /// Copies all of the addresses within the into the array, @@ -335,7 +343,7 @@ public void CopyTo (InternetAddress[] array, int arrayIndex) } /// - /// Removes the specified address. + /// Remove the specified address from the ///. /// /// Removes the specified address. @@ -364,7 +372,7 @@ public bool Remove (InternetAddress address) #region IEnumerable implementation /// - /// Gets an enumerator for the list of addresses. + /// Get an enumerator for the list of addresses. /// ////// Gets an enumerator for the list of addresses. @@ -380,7 +388,7 @@ public IEnumerator GetEnumerator () #region IEnumerable implementation /// - /// Gets an enumerator for the list of addresses. + /// Get an enumerator for the list of addresses. /// ////// Gets an enumerator for the list of addresses. @@ -396,14 +404,14 @@ IEnumerator IEnumerable.GetEnumerator () #region IEquatable implementation /// - /// Determines whether the specified ///is equal to the current . + /// Determine whether the specified is equal to the current . /// - /// Determines whether the specified - /// Theis equal to the current . + /// Determines whether the specified is equal to the current . /// to compare with the current . - /// + /// The true if the specifiedis equal to the current - /// ; otherwise, false .to compare with the current . + /// public bool Equals (InternetAddressList other) { if (other == null) @@ -412,14 +420,8 @@ public bool Equals (InternetAddressList other) if (other.Count != Count) return false; - var otherSorted = new List true if the specifiedis equal to the current + /// ; otherwise, false .(other); - otherSorted.Sort (); - - var sorted = new List (this); - sorted.Sort (); - - for (int i = 0; i < sorted.Count; i++) { - if (!sorted[i].Equals (otherSorted[i])) + for (int i = 0; i < Count; i++) { + if (!this[i].Equals (other[i])) return false; } @@ -431,7 +433,7 @@ public bool Equals (InternetAddressList other) #region IComparable implementation /// - /// Compares two internet address lists. + /// Compare two internet address lists. /// ////// Compares two internet address lists for the purpose of sorting. @@ -458,19 +460,46 @@ public int CompareTo (InternetAddressList other) #endregion - internal void Encode (FormatOptions options, StringBuilder builder, ref int lineLength) + /// + /// Determine whether the specified object is equal to the current object. + /// + ///+ /// The type of comparison between the current instance and the + /// The object to compare with the current object. + ///parameter depends on whether + /// the current instance is a reference type or a value type. + /// + public override bool Equals (object obj) + { + return Equals (obj as InternetAddressList); + } + + /// true if the specified object is equal to the current object; otherwise,false .+ /// Return the hash code for this instance. + /// + ///+ /// Returns the hash code for this instance. + /// + ///A hash code for the current object. + public override int GetHashCode () + { + return ToString ().GetHashCode (); + } + + internal void Encode (FormatOptions options, StringBuilder builder, bool firstToken, ref int lineLength) { for (int i = 0; i < list.Count; i++) { - if (i > 0) + if (i > 0) { builder.Append (", "); + lineLength += 2; + } - list[i].Encode (options, builder, ref lineLength); + list[i].Encode (options, builder, firstToken && i == 0, ref lineLength); } } ///- /// Returns a string representation of the email addresses in the ///, - /// optionally encoding them for transport. + /// Serialize an to a string, optionally encoding the list of addresses for transport. /// /// If is true , each address in the list will be encoded @@ -487,7 +516,7 @@ public string ToString (FormatOptions options, bool encode) if (encode) { int lineLength = 0; - Encode (options, builder, ref lineLength); + Encode (options, builder, true, ref lineLength); return builder.ToString (); } @@ -503,8 +532,7 @@ public string ToString (FormatOptions options, bool encode) } ///- /// Returns a string representation of the email addresses in the ///, - /// optionally encoding them for transport. + /// Serialize an to a string, optionally encoding the list of addresses for transport. /// /// If is true , each address in the list will be encoded @@ -519,7 +547,7 @@ public string ToString (bool encode) } ///- /// Returns a string representation of the email addresses in the ///. + /// Serialize an to a string suitable for display. /// /// If there are multiple addresses in the list, they will be separated by a comma. @@ -591,7 +619,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a list of addresses from the supplied buffer starting at the given index @@ -630,7 +658,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a list of addresses from the supplied buffer starting at the given index @@ -654,7 +682,7 @@ public static bool TryParse (byte[] buffer, int startIndex, int length, out Inte } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a list of addresses from the supplied buffer starting at the specified index. @@ -690,7 +718,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a list of addresses from the supplied buffer starting at the specified index. @@ -711,7 +739,7 @@ public static bool TryParse (byte[] buffer, int startIndex, out InternetAddressL } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a list of addresses from the specified buffer. @@ -743,7 +771,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, out InternetA } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a list of addresses from the specified buffer. @@ -760,7 +788,7 @@ public static bool TryParse (byte[] buffer, out InternetAddressList addresses) } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a list of addresses from the specified text. @@ -793,7 +821,7 @@ public static bool TryParse (ParserOptions options, string text, out InternetAdd } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a list of addresses from the specified text. @@ -810,13 +838,13 @@ public static bool TryParse (string text, out InternetAddressList addresses) } /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a list of addresses from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// - ///The parsed + ///. The parsed /// The parser options to use. /// The input buffer. /// The starting index of the input buffer. @@ -846,13 +874,13 @@ public static InternetAddressList Parse (ParserOptions options, byte[] buffer, i } ///. - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a list of addresses from the supplied buffer starting at the given index /// and spanning across the specified number of bytes. /// - ///The parsed + ///. The parsed /// The input buffer. /// The starting index of the input buffer. /// The number of bytes in the input buffer to parse. @@ -872,12 +900,12 @@ public static InternetAddressList Parse (byte[] buffer, int startIndex, int leng } ///. - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a list of addresses from the supplied buffer starting at the specified index. /// - ///The parsed + ///. The parsed /// The parser options to use. /// The input buffer. /// The starting index of the input buffer. @@ -905,12 +933,12 @@ public static InternetAddressList Parse (ParserOptions options, byte[] buffer, i } ///. - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a list of addresses from the supplied buffer starting at the specified index. /// - ///The parsed + ///. The parsed /// The input buffer. /// The starting index of the input buffer. ///. @@ -928,12 +956,12 @@ public static InternetAddressList Parse (byte[] buffer, int startIndex) } /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a list of addresses from the specified buffer. /// - ///The parsed + ///. The parsed /// The parser options to use. /// The input buffer. ///. @@ -957,12 +985,12 @@ public static InternetAddressList Parse (ParserOptions options, byte[] buffer) } /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a list of addresses from the specified buffer. /// - ///The parsed + ///. The parsed /// The input buffer. ///. /// is null . @@ -976,12 +1004,12 @@ public static InternetAddressList Parse (byte[] buffer) } ///- /// Parses the given text into a new ///instance. + /// Parse the given text into a new instance. /// /// Parses a list of addresses from the specified text. /// - ///The parsed + ///. The parsed /// The parser options to use. /// The text. ///. @@ -1006,12 +1034,12 @@ public static InternetAddressList Parse (ParserOptions options, string text) } /// - /// Parses the given text into a new ///instance. + /// Parse the given text into a new instance. /// /// Parses a list of addresses from the specified text. /// - ///The parsed + ///. The parsed /// The text. ///. /// is null . diff --git a/MimeKit/MailboxAddress.cs b/MimeKit/MailboxAddress.cs index b75a42ece1..652f0acdda 100644 --- a/MimeKit/MailboxAddress.cs +++ b/MimeKit/MailboxAddress.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,12 +26,9 @@ using System; using System.Text; +using System.Globalization; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - #if ENABLE_SNM using System.Net.Mail; #endif @@ -70,7 +67,7 @@ internal MailboxAddress (Encoding encoding, string name, string address, int at) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified name, address and route. The @@ -101,7 +98,7 @@ public MailboxAddress (Encoding encoding, string name, IEnumerable route } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified name, address and route. @@ -122,7 +119,7 @@ public MailboxAddress (string name, IEnumerable route, string address) : } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified address and route. @@ -137,12 +134,13 @@ public MailboxAddress (string name, IEnumerable route, string address) : /// /// + [Obsolete ("This constructor will be going away. Use new MailboxAddress(string name, IEnumerableis malformed. /// route, string address) instead.")] public MailboxAddress (IEnumerable route, string address) : this (Encoding.UTF8, null, route, address) { } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified name and address. The @@ -170,7 +168,7 @@ public MailboxAddress (Encoding encoding, string name, string address) : base (e } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified name and address. @@ -188,7 +186,7 @@ public MailboxAddress (string name, string address) : this (Encoding.UTF8, name, } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new @@ -206,6 +204,7 @@ public MailboxAddress (string name, string address) : this (Encoding.UTF8, name, ///with the specified address. /// + [Obsolete("This constructor will be going away due to it causing too much confusion. Use new MailboxAddress(string name, string address) or MailboxAddress.Parse(string) instead.")] public MailboxAddress (string address) : this (Encoding.UTF8, null, address) { } @@ -238,7 +237,7 @@ public DomainList Route { /// Gets or sets the mailbox address. ///is malformed. /// - /// Represents the actual email address and is in the form of ///user@example.com . + /// Represents the actual email address and is in the form ofuser@domain.com . ///The mailbox address. ///@@ -258,14 +257,12 @@ public string Address { if (value.Length > 0) { var buffer = CharsetUtils.UTF8.GetBytes (value); - string addrspec; int index = 0; - int atIndex; - TryParseAddrspec (buffer, ref index, buffer.Length, new byte[0], true, out addrspec, out atIndex); + TryParseAddrspec (buffer, ref index, buffer.Length, new byte[0], true, out string addrspec, out int atIndex); if (index != buffer.Length) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); address = addrspec; at = atIndex; @@ -307,17 +304,19 @@ public bool IsInternational { static string EncodeAddrspec (string addrspec, int at) { -#if !PORTABLE if (at != -1) { var domain = addrspec.Substring (at + 1); var local = addrspec.Substring (0, at); + if (ParseUtils.IsInternational (local)) + local = ParseUtils.IdnEncode (local); + if (ParseUtils.IsInternational (domain)) domain = ParseUtils.IdnEncode (domain); return local + "@" + domain; } -#endif + return addrspec; } @@ -337,36 +336,33 @@ public static string EncodeAddrspec (string addrspec) if (addrspec == null) throw new ArgumentNullException (nameof (addrspec)); -#if !PORTABLE if (addrspec.Length == 0) return addrspec; var buffer = CharsetUtils.UTF8.GetBytes (addrspec); - int at, index = 0; - string address; + int index = 0; - if (!TryParseAddrspec (buffer, ref index, buffer.Length, new byte[0], false, out address, out at)) + if (!TryParseAddrspec (buffer, ref index, buffer.Length, new byte[0], false, out string address, out int at)) return addrspec; return EncodeAddrspec (address, at); -#else - return addrspec; -#endif } static string DecodeAddrspec (string addrspec, int at) { -#if !PORTABLE if (at != -1) { var domain = addrspec.Substring (at + 1); var local = addrspec.Substring (0, at); + if (ParseUtils.IsIdnEncoded (local)) + local = ParseUtils.IdnDecode (local); + if (ParseUtils.IsIdnEncoded (domain)) domain = ParseUtils.IdnDecode (domain); return local + "@" + domain; } -#endif + return addrspec; } @@ -386,34 +382,41 @@ public static string DecodeAddrspec (string addrspec) if (addrspec == null) throw new ArgumentNullException (nameof (addrspec)); -#if !PORTABLE if (addrspec.Length == 0) return addrspec; var buffer = CharsetUtils.UTF8.GetBytes (addrspec); - int at, index = 0; - string address; + int index = 0; - if (!TryParseAddrspec (buffer, ref index, buffer.Length, new byte[0], false, out address, out at)) + if (!TryParseAddrspec (buffer, ref index, buffer.Length, new byte[0], false, out string address, out int at)) return addrspec; return DecodeAddrspec (address, at); -#else - return addrspec; -#endif } - internal override void Encode (FormatOptions options, StringBuilder builder, ref int lineLength) + /// - ///+ /// Get the mailbox address, optionally encoded according to IDN encoding rules. + /// + ///+ /// If + ///is true , then the returned mailbox address will be encoded according to the IDN encoding rules. + ///true if the address should be encoded according to IDN encoding rules; otherwise,false . + ///The mailbox address. + public string GetAddress (bool idnEncode) + { + if (idnEncode) + return EncodeAddrspec (address, at); + + return DecodeAddrspec (address, at); + } + + internal override void Encode (FormatOptions options, StringBuilder builder, bool firstToken, ref int lineLength) { var route = Route.Encode (options); if (!string.IsNullOrEmpty (route)) route += ":"; - string addrspec; - if (options.International) - addrspec = DecodeAddrspec (address, at); - else - addrspec = EncodeAddrspec (address, at); + var addrspec = GetAddress (!options.International); if (!string.IsNullOrEmpty (Name)) { string name; @@ -428,11 +431,11 @@ internal override void Encode (FormatOptions options, StringBuilder builder, ref if (lineLength + name.Length > options.MaxLineLength) { if (name.Length > options.MaxLineLength) { // we need to break up the name... - builder.AppendFolded (options, name, ref lineLength); + builder.AppendFolded (options, firstToken, name, ref lineLength); } else { // the name itself is short enough to fit on a single line, // but only if we write it on a line by itself - if (lineLength > 1) { + if (!firstToken && lineLength > 1) { builder.LineWrap (options); lineLength = 1; } @@ -462,7 +465,7 @@ internal override void Encode (FormatOptions options, StringBuilder builder, ref builder.Append (addrspec); builder.Append ('>'); } else if (!string.IsNullOrEmpty (route)) { - if ((lineLength + route.Length + addrspec.Length + 2) > options.MaxLineLength) { + if (!firstToken && (lineLength + route.Length + addrspec.Length + 2) > options.MaxLineLength) { builder.Append (options.NewLine); builder.Append ("\t<"); lineLength = 2; @@ -478,7 +481,7 @@ internal override void Encode (FormatOptions options, StringBuilder builder, ref builder.Append (addrspec); builder.Append ('>'); } else { - if ((lineLength + addrspec.Length) > options.MaxLineLength) { + if (!firstToken && (lineLength + addrspec.Length) > options.MaxLineLength) { builder.LineWrap (options); lineLength = 1; } @@ -513,7 +516,7 @@ public override string ToString (FormatOptions options, bool encode) var builder = new StringBuilder (); int lineLength = 0; - Encode (options, builder, ref lineLength); + Encode (options, builder, true, ref lineLength); return builder.ToString (); } @@ -534,14 +537,14 @@ public override string ToString (FormatOptions options, bool encode) #region IEquatable implementation ///- /// Determines whether the specified ///is equal to the current . + /// Determines whether the specified is equal to the current . /// /// Compares two mailbox addresses to determine if they are identical or not. /// - /// Theto compare with the current . - /// + /// The true if the specifiedis equal to the current - /// ; otherwise, false .to compare with the current . + /// public override bool Equals (InternetAddress other) { var mailbox = other as MailboxAddress; @@ -567,7 +570,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index if (throwOnError) flags |= AddressParserFlags.ThrowOnError; - if (!InternetAddress.TryParse (options, text, ref index, endIndex, 0, flags, out address)) { + if (!TryParse (options, text, ref index, endIndex, 0, flags, out address)) { mailbox = null; return false; } @@ -578,7 +581,7 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index } /// true if the specifiedis equal to the current + /// ; otherwise, false .- /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a mailbox address or @@ -618,7 +621,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a mailbox address or @@ -642,7 +645,7 @@ public static bool TryParse (byte[] buffer, int startIndex, int length, out Mail } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a mailbox address or @@ -680,7 +683,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, int startInde } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a mailbox address or @@ -702,7 +705,7 @@ public static bool TryParse (byte[] buffer, int startIndex, out MailboxAddress m } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a mailbox address or @@ -736,7 +739,7 @@ public static bool TryParse (ParserOptions options, byte[] buffer, out MailboxAd } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses a single . If the address is not a mailbox address or @@ -754,7 +757,7 @@ public static bool TryParse (byte[] buffer, out MailboxAddress mailbox) } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a single . If the address is not a mailbox address or @@ -791,7 +794,7 @@ public static bool TryParse (ParserOptions options, string text, out MailboxAddr } /// - /// Tries to parse the given text into a new ///instance. + /// Try to parse the given text into a new instance. /// /// Parses a single . If the address is not a mailbox address or @@ -809,13 +812,13 @@ public static bool TryParse (string text, out MailboxAddress mailbox) } /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a mailbox address or /// there is more than a single mailbox address, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The input buffer. /// The starting index of the input buffer. @@ -846,19 +849,19 @@ public static bool TryParse (string text, out MailboxAddress mailbox) ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return mailbox; } ///. - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a mailbox address or /// there is more than a single mailbox address, then parsing will fail. /// The parsed + ///. The parsed /// The input buffer. /// The starting index of the input buffer. /// The number of bytes in the input buffer to parse. @@ -869,7 +872,7 @@ public static bool TryParse (string text, out MailboxAddress mailbox) ///. and do not specify /// a valid range in the byte array. /// + /// /// public static new MailboxAddress Parse (byte[] buffer, int startIndex, int length) @@ -878,13 +881,13 @@ public static bool TryParse (string text, out MailboxAddress mailbox) } ///could not be parsed. /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a mailbox address or /// there is more than a single mailbox address, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The input buffer. /// The starting index of the input buffer. @@ -896,7 +899,7 @@ public static bool TryParse (string text, out MailboxAddress mailbox) ///. /// - ///is out of range. /// + /// /// public static new MailboxAddress Parse (ParserOptions options, byte[] buffer, int startIndex) @@ -913,19 +916,19 @@ public static bool TryParse (string text, out MailboxAddress mailbox) ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return mailbox; } ///could not be parsed. /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a mailbox address or /// there is more than a single mailbox address, then parsing will fail. /// The parsed + ///. The parsed /// The input buffer. /// The starting index of the input buffer. ///. @@ -943,13 +946,13 @@ public static bool TryParse (string text, out MailboxAddress mailbox) } /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a mailbox address or /// there is more than a single mailbox address, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The input buffer. ///. @@ -974,19 +977,19 @@ public static bool TryParse (string text, out MailboxAddress mailbox) ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return mailbox; } /// - /// Parses the given input buffer into a new ///instance. + /// Parse the given input buffer into a new instance. /// /// Parses a single - ///. If the address is not a mailbox address or /// there is more than a single mailbox address, then parsing will fail. /// The parsed + ///. The parsed /// The input buffer. ///. /// is null . @@ -1000,13 +1003,13 @@ public static bool TryParse (string text, out MailboxAddress mailbox) } ///- /// Parses the given text into a new ///instance. + /// Parse the given text into a new instance. /// /// Parses a single - ///. If the address is not a mailbox address or /// there is more than a single mailbox address, then parsing will fail. /// The parsed + ///. The parsed /// The parser options to use. /// The text. ///. @@ -1036,19 +1039,19 @@ public static bool TryParse (string text, out MailboxAddress mailbox) ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Unexpected token at offset {0}", index), index, index); return mailbox; } /// - /// Parses the given text into a new ///instance. + /// Parse the given text into a new instance. /// /// Parses a single - ///. If the address is not a mailbox address or /// there is more than a single mailbox address, then parsing will fail. /// The parsed + ///. The parsed /// The text. ///. /// is null . diff --git a/MimeKit/MessageDeliveryStatus.cs b/MimeKit/MessageDeliveryStatus.cs index 89764b5aed..3495bdcdc3 100644 --- a/MimeKit/MessageDeliveryStatus.cs +++ b/MimeKit/MessageDeliveryStatus.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -36,16 +36,20 @@ namespace MimeKit { /// A message delivery status MIME part is a machine readable notification denoting the /// delivery status of a message and has a MIME-type of message/delivery-status. ///For more information, see rfc3464. + ////// + /// public class MessageDeliveryStatus : MimePart { HeaderListCollection groups; ///+ ///
- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -56,7 +60,7 @@ public MessageDeliveryStatus (MimeEntityConstructorArgs args) : base (args) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -79,6 +83,9 @@ public MessageDeliveryStatus () : base ("message", "delivery-status") /// Section 2.3 defines /// the per-recipient fields. + /// ///+ ///
The fields. public HeaderListCollection StatusGroups { get { @@ -123,12 +130,12 @@ void OnGroupsChanged (object sender, EventArgs e) /// Dispatches to the specific visit method for this MIME entity. /// ///- /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// diff --git a/MimeKit/MessageDispositionNotification.cs b/MimeKit/MessageDispositionNotification.cs index 7c71482d0d..975fc2404c 100644 --- a/MimeKit/MessageDispositionNotification.cs +++ b/MimeKit/MessageDispositionNotification.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -36,16 +36,17 @@ namespace MimeKit { /// A message disposition notification MIME part is a machine readable notification /// denoting the disposition of a message once it has been successfully delivered /// and has a MIME-type of message/disposition-notification. + /// /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -56,7 +57,7 @@ public MessageDispositionNotification (MimeEntityConstructorArgs args) : base (a } /// + [Obsolete ("Use DkimSigner.Sign() instead.")] public void Sign (FormatOptions options, DkimSigner signer, IList- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -106,12 +107,12 @@ void OnFieldsChanged (object sender, HeaderListChangedEventArgs e) /// Dispatches to the specific visit method for this MIME entity. /// /// - /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// diff --git a/MimeKit/MessageIdList.cs b/MimeKit/MessageIdList.cs index 31bbeadd29..52c435c5cb 100644 --- a/MimeKit/MessageIdList.cs +++ b/MimeKit/MessageIdList.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -29,10 +29,6 @@ using System.Collections; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - using MimeKit.Utils; namespace MimeKit { @@ -47,7 +43,7 @@ public class MessageIdList : IList readonly List references; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new, empty, . @@ -77,7 +73,7 @@ public MessageIdList Clone () #region IList implementation /// - /// Gets the index of the requested Message-Id, if it exists. + /// Get the index of the requested Message-Id, if it exists. /// ////// Finds the index of the specified Message-Id, if it exists. @@ -127,7 +123,7 @@ public void Insert (int index, string messageId) } /// - /// Removes the Message-Id at the specified index. + /// Remove the Message-Id at the specified index. /// ////// Removes the Message-Id at the specified index. @@ -143,7 +139,7 @@ public void RemoveAt (int index) } /// - /// Gets or sets the ///at the specified index. + /// Get or set the Message-Id at the specified index. /// /// Gets or sets the Message-Id at the specified index. @@ -215,7 +211,7 @@ public void AddRange (IEnumerable items) } /// - /// Clears the Message-Id list. + /// Clear the Message-Id list. /// ////// Removes all of the Message-Ids in the list. @@ -227,7 +223,7 @@ public void Clear () } /// - /// Checks if the ///contains the specified Message-Id. + /// Check if the contains the specified Message-Id. /// /// Determines whether or not the list contains the specified Message-Id. @@ -247,7 +243,7 @@ public bool Contains (string messageId) } /// - /// Copies all of the Message-Ids in the ///to the specified array. + /// Copy all of the Message-Ids in the to the specified array. /// /// Copies all of the Message-Ids within the into the array, @@ -267,7 +263,7 @@ public void CopyTo (string[] array, int arrayIndex) } /// - /// Removes the specified Message-Id. + /// Remove a Message-Id from the ///. /// /// Removes the first instance of the specified Message-Id from the list if it exists. @@ -292,7 +288,7 @@ public bool Remove (string messageId) } /// - /// Gets the number of Message-Ids in the ///. + /// Get the number of Message-Ids in the . /// /// Indicates the number of Message-Ids in the list. @@ -303,7 +299,7 @@ public int Count { } /// - /// Gets a value indicating whether this instance is read only. + /// Get a value indicating whether the ///is read only. /// /// A is never read-only. @@ -318,7 +314,7 @@ public bool IsReadOnly { #region IEnumerable implementation /// - /// Gets an enumerator for the list of Message-Ids. + /// Get an enumerator for the list of Message-Ids. /// ////// Gets an enumerator for the list of Message-Ids. @@ -334,7 +330,7 @@ public IEnumerator GetEnumerator () #region IEnumerable implementation /// - /// Gets an enumerator for the list of Message-Ids. + /// Get an enumerator for the list of Message-Ids. /// ////// Gets an enumerator for the list of Message-Ids. @@ -348,7 +344,7 @@ IEnumerator IEnumerable.GetEnumerator () #endregion /// - /// Returns a string representation of the list of Message-Ids. + /// Serialize a ///to a string. /// /// public class MimeMessage { - static readonly string[] StandardAddressHeaders = { - "Resent-From", "Resent-Reply-To", "Resent-To", "Resent-Cc", "Resent-Bcc", - "From", "Reply-To", "To", "Cc", "Bcc" + static readonly HeaderId[] StandardAddressHeaders = { + HeaderId.ResentFrom, HeaderId.ResentReplyTo, HeaderId.ResentTo, HeaderId.ResentCc, HeaderId.ResentBcc, + HeaderId.From, HeaderId.ReplyTo, HeaderId.To, HeaderId.Cc, HeaderId.Bcc }; - readonly DictionaryEach Message-Id will be surrounded by angle brackets. diff --git a/MimeKit/MessageImportance.cs b/MimeKit/MessageImportance.cs index c20404b2b4..24867ca31e 100644 --- a/MimeKit/MessageImportance.cs +++ b/MimeKit/MessageImportance.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/MessagePart.cs b/MimeKit/MessagePart.cs index ae0924aff8..763ad3fadf 100644 --- a/MimeKit/MessagePart.cs +++ b/MimeKit/MessagePart.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -33,7 +33,7 @@ namespace MimeKit { /// - /// A MIME part containing a ///as its content. + /// A MIME part containing a as its content. /// /// Represents MIME entities such as those with a Content-Type of message/rfc822 or message/news. @@ -41,10 +41,10 @@ namespace MimeKit { public class MessagePart : MimeEntity { /// + ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -55,7 +55,7 @@ public MessagePart (MimeEntityConstructorArgs args) : base (args) } /// ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -68,7 +68,7 @@ public MessagePart (MimeEntityConstructorArgs args) : base (args) /// /// is null .- /// @@ -83,8 +83,7 @@ public MessagePart (string subtype, params object[] args) : this (subtype) if (obj == null || TryInit (obj)) continue; - var mesg = obj as MimeMessage; - if (mesg != null) { + if (obj is MimeMessage mesg) { if (message != null) throw new ArgumentException ("MimeMessage should not be specified more than once."); @@ -100,7 +99,24 @@ public MessagePart (string subtype, params object[] args) : this (subtype) } ///+ /// contains more than one . /// contains more than one . -or- ////// contains one or more arguments of an unknown type. - /// Initializes a new instance of the + ///class. + /// Initialize a new instance of the class. + /// + /// Initializes the + /// The media type. + /// The media subtype. + ///based on the provided media type and subtype. + /// + /// + protected MessagePart (string mediaType, string mediaSubtype) : base (mediaType, mediaSubtype) + { + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Initialize a new instance of the ///class. /// /// Creates a new MIME message entity with the specified subtype. @@ -109,12 +125,12 @@ public MessagePart (string subtype, params object[] args) : this (subtype) /// + ////// - public MessagePart (string subtype) : base ("message", subtype) + public MessagePart (string subtype) : this ("message", subtype) { } ///is null . ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new message/rfc822 MIME entity. @@ -138,12 +154,12 @@ public MimeMessage Message { /// Dispatches to the specific visit method for this MIME entity. /// /// + ///- /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -180,7 +196,7 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = } /// - /// Writes the ///to the output stream. + /// Write the to the output stream. /// /// Writes the MIME entity and its message to the output stream. @@ -219,15 +235,21 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = } } + if (options.EnsureNewLine) { + options = options.Clone (); + options.EnsureNewLine = false; + } + Message.WriteTo (options, stream, cancellationToken); } /// + ///- /// Asynchronously writes the ///to the output stream. + /// Asynchronously write the to the output stream. /// - /// Writes the MIME entity and its message to the output stream. + /// Asynchronously writes the MIME entity and its message to the output stream. /// + ///An awaitable task. /// The formatting options. /// The output stream. ///true if only the content should be written; otherwise,false . @@ -255,6 +277,11 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); } + if (options.EnsureNewLine) { + options = options.Clone (); + options.EnsureNewLine = false; + } + await Message.WriteToAsync (options, stream, cancellationToken).ConfigureAwait (false); } } diff --git a/MimeKit/MessagePartial.cs b/MimeKit/MessagePartial.cs index 967eda2ba4..75984864d3 100644 --- a/MimeKit/MessagePartial.cs +++ b/MimeKit/MessagePartial.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -45,10 +45,10 @@ namespace MimeKit { public class MessagePartial : MimePart { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -59,7 +59,7 @@ public MessagePartial (MimeEntityConstructorArgs args) : base (args) } /// ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new message/partial entity. @@ -149,12 +149,12 @@ public int? Total { /// Dispatches to the specific visit method for this MIME entity. /// ///- /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -203,9 +203,40 @@ public static IEnumerable + [Obsolete ("Use MessagePartial.Join (MimeMessage, IEnumerableSplit (MimeMessage message, int maxSize) if (maxSize < 1) throw new ArgumentOutOfRangeException (nameof (maxSize)); + var options = FormatOptions.CloneDefault (); + foreach (HeaderId id in Enum.GetValues (typeof (HeaderId))) { + switch (id) { + case HeaderId.Subject: + case HeaderId.MessageId: + case HeaderId.Encrypted: + case HeaderId.MimeVersion: + case HeaderId.ContentAlternative: + case HeaderId.ContentBase: + case HeaderId.ContentClass: + case HeaderId.ContentDescription: + case HeaderId.ContentDisposition: + case HeaderId.ContentDuration: + case HeaderId.ContentFeatures: + case HeaderId.ContentId: + case HeaderId.ContentIdentifier: + case HeaderId.ContentLanguage: + case HeaderId.ContentLength: + case HeaderId.ContentLocation: + case HeaderId.ContentMd5: + case HeaderId.ContentReturn: + case HeaderId.ContentTransferEncoding: + case HeaderId.ContentTranslationType: + case HeaderId.ContentType: + break; + default: + options.HiddenHeaders.Add (id); + break; + } + } + var memory = new MemoryStream (); - message.WriteTo (memory); + message.WriteTo (options, memory); memory.Seek (0, SeekOrigin.Begin); if (memory.Length <= maxSize) { @@ -216,7 +247,7 @@ public static IEnumerable Split (MimeMessage message, int maxSize) } var streams = new List (); -#if !PORTABLE && !NETSTANDARD +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 var buf = memory.GetBuffer (); #else var buf = memory.ToArray (); @@ -242,12 +273,13 @@ public static IEnumerable Split (MimeMessage message, int maxSize) startIndex = endIndex; } - var id = message.MessageId ?? MimeUtils.GenerateMessageId (); + var msgid = message.MessageId ?? MimeUtils.GenerateMessageId (); int number = 1; foreach (var stream in streams) { - var part = new MessagePartial (id, number++, streams.Count); - part.Content = new MimeContent (stream); + var part = new MessagePartial (msgid, number++, streams.Count) { + Content = new MimeContent (stream) + }; var submessage = CloneMessage (message); submessage.MessageId = MimeUtils.GenerateMessageId (); @@ -262,38 +294,82 @@ public static IEnumerable Split (MimeMessage message, int maxSize) static int PartialCompare (MessagePartial partial1, MessagePartial partial2) { if (!partial1.Number.HasValue || !partial2.Number.HasValue || partial1.Id != partial2.Id) - throw new ArgumentException ("partial"); + throw new ArgumentException ("Partial messages have mismatching identifiers.", "partials"); return partial1.Number.Value - partial2.Number.Value; } - /// - /// Joins the specified message/partial parts into the complete message. - /// - ///- /// Combines all of the message/partial fragments into its original, - /// complete, message. - /// - ///The re-combined message. - /// The parser options to use. - /// The list of partial message parts. - ///- /// - ///- /// is null .-or- - ///- /// is null .- /// - public static MimeMessage Join (ParserOptions options, IEnumerableThe last partial does not have a Total. - ///-or- - ///The number of partials provided does not match the expected count. - ///-or- - ///One or more partials is missing. - ///partials) + static void CombineHeaders (MimeMessage message, MimeMessage joined) + { + var headers = new List (); + int i = 0; + + // RFC2046: Any header fields in the enclosed message which do not start with "Content-" + // (except for the "Subject", "Message-ID", "Encrypted", and "MIME-Version" fields) will + // be ignored and dropped. + while (i < joined.Headers.Count) { + var header = joined.Headers[i]; + + switch (header.Id) { + case HeaderId.Subject: + case HeaderId.MessageId: + case HeaderId.Encrypted: + case HeaderId.MimeVersion: + headers.Add (header); + header.Offset = null; + i++; + break; + default: + joined.Headers.RemoveAt (i); + break; + } + } + + // RFC2046: All of the header fields from the initial enclosing message, except + // those that start with "Content-" and the specific header fields "Subject", + // "Message-ID", "Encrypted", and "MIME-Version", must be copied, in order, + // to the new message. + i = 0; + foreach (var header in message.Headers) { + switch (header.Id) { + case HeaderId.Subject: + case HeaderId.MessageId: + case HeaderId.Encrypted: + case HeaderId.MimeVersion: + for (int j = 0; j < headers.Count; j++) { + if (headers[j].Id == header.Id) { + var original = headers[j]; + + joined.Headers.Remove (original); + joined.Headers.Insert (i++, original); + headers.RemoveAt (j); + break; + } + } + break; + default: + var clone = header.Clone (); + clone.Offset = null; + + joined.Headers.Insert (i++, clone); + break; + } + } + + if (joined.Body != null) { + foreach (var header in joined.Body.Headers) + header.Offset = null; + } + } + + static MimeMessage Join (ParserOptions options, MimeMessage message, IEnumerable partials, bool allowNullMessage) { if (options == null) throw new ArgumentNullException (nameof (options)); + if (!allowNullMessage && message == null) + throw new ArgumentNullException (nameof (message)); + if (partials == null) throw new ArgumentNullException (nameof (partials)); @@ -327,11 +403,93 @@ public static MimeMessage Join (ParserOptions options, IEnumerable + /// Joins the specified message/partial parts into the complete message. + /// + /// + /// Combines all of the message/partial fragments into its original, + /// complete, message. + /// + ///The re-combined message. + /// The parser options to use. + /// The message that contains the first `message/partial` part. + /// The list of partial message parts. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .-or- + ///+ /// is null .+ /// + public static MimeMessage Join (ParserOptions options, MimeMessage message, IEnumerableThe last partial does not have a Total. + ///-or- + ///The number of partials provided does not match the expected count. + ///-or- + ///One or more partials is missing. + ///partials) + { + return Join (options, message, partials, false); + } + + /// + /// Joins the specified message/partial parts into the complete message. + /// + ///+ /// Combines all of the message/partial fragments into its original, + /// complete, message. + /// + ///The re-combined message. + /// The message that contains the first `message/partial` part. + /// The list of partial message parts. + ///+ /// + public static MimeMessage Join (MimeMessage message, IEnumerable. + /// is null -or- + ///+ /// is null .partials) + { + return Join (ParserOptions.Default, message, partials, false); + } + + /// + /// Joins the specified message/partial parts into the complete message. + /// + ///+ /// Combines all of the message/partial fragments into its original, + /// complete, message. + /// + ///The re-combined message. + /// The parser options to use. + /// The list of partial message parts. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + [Obsolete ("Use MessagePartial.Join (ParserOptions, MimeMessage, IEnumerableThe last partial does not have a Total. + ///-or- + ///The number of partials provided does not match the expected count. + ///-or- + ///One or more partials is missing. + ///) instead.")] + public static MimeMessage Join (ParserOptions options, IEnumerable partials) + { + return Join (options, null, partials, true); + } + /// /// Joins the specified message/partial parts into the complete message. /// @@ -344,9 +502,10 @@ public static MimeMessage Join (ParserOptions options, IEnumerable/// is null . ///) instead.")] public static MimeMessage Join (IEnumerable partials) { - return Join (ParserOptions.Default, partials); + return Join (ParserOptions.Default, null, partials, true); } } } diff --git a/MimeKit/MessagePriority.cs b/MimeKit/MessagePriority.cs index 461e4d22c6..e97261fc4d 100644 --- a/MimeKit/MessagePriority.cs +++ b/MimeKit/MessagePriority.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/MimeContent.cs b/MimeKit/MimeContent.cs index b28ce0fa85..fffb6f8f5c 100644 --- a/MimeKit/MimeContent.cs +++ b/MimeKit/MimeContent.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,6 +26,7 @@ using System; using System.IO; +using System.Buffers; using System.Threading; using System.Threading.Tasks; @@ -34,7 +35,7 @@ namespace MimeKit { /// - /// Encapsulates a content stream used by ///. + /// Encapsulates a content stream used by . /// /// A represents the content of a . @@ -44,11 +45,11 @@ namespace MimeKit { public class ContentObject : MimeContent { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// When creating new /// The content stream. @@ -66,7 +67,7 @@ public ContentObject (Stream stream, ContentEncoding encoding = ContentEncoding. } ///s, the - /// should typically be unless the + /// When creating new s, the + /// should typically be unless the /// has already been encoded. /// - /// Encapsulates a content stream used by ///. + /// Encapsulates a content stream used by . /// /// A represents the content of a . @@ -77,12 +78,14 @@ public ContentObject (Stream stream, ContentEncoding encoding = ContentEncoding. /// public class MimeContent : IMimeContent { + const int BufferLength = 4096; + /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// When creating new /// The content stream. @@ -113,7 +116,7 @@ public MimeContent (Stream stream, ContentEncoding encoding = ContentEncoding.De #region IContentObject implementation ///s, the - /// should typically be unless the + /// When creating new s, the + /// should typically be unless the /// has already been encoded. /// - /// Gets or sets the content encoding. + /// Get or set the content encoding. /// ////// If the @@ -1392,7 +1387,6 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) return LoadAsync (ParserOptions.Default, stream, false, cancellationToken); } -#if !PORTABLE ///was parsed from an existing stream, the @@ -126,7 +129,18 @@ public ContentEncoding Encoding { } /// - /// Gets the content stream. + /// Get the new-line format, if known. + /// + ///+ /// + ///This property is typically only set by the + ///as it parses + /// the content of a and is only used as a hint when verifying + /// digital signatures. The new-line format, if known. + public NewLineFormat? NewLineFormat { get; set; } + + ///+ /// Get the content stream. /// ////// Gets the content stream. @@ -137,7 +151,7 @@ public Stream Stream { } /// @@ -1313,7 +1308,7 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) /// default- /// Opens the decoded content stream. + /// Open the decoded content stream. /// ////// Provides a means of reading the decoded content without having to first write it to another @@ -155,7 +169,7 @@ public Stream Open () } /// @@ -1280,7 +1275,7 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) /// default- /// Copies the content stream to the specified output stream. + /// Copy the content stream to the specified output stream. /// ////// @@ -1176,7 +1171,7 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) /// specifiedThis is equivalent to simply using ///@@ -179,21 +193,21 @@ public Stream Open () if (stream == null) throw new ArgumentNullException (nameof (stream)); + Stream.Seek (0, SeekOrigin.Begin); + + var buf = ArrayPool .Shared.Rent (BufferLength); var readable = Stream as ICancellableStream; var writable = stream as ICancellableStream; - var buf = new byte[4096]; int nread; - Stream.Seek (0, SeekOrigin.Begin); - try { do { if (readable != null) { - if ((nread = readable.Read (buf, 0, buf.Length, cancellationToken)) <= 0) + if ((nread = readable.Read (buf, 0, BufferLength, cancellationToken)) <= 0) break; } else { cancellationToken.ThrowIfCancellationRequested (); - if ((nread = Stream.Read (buf, 0, buf.Length)) <= 0) + if ((nread = Stream.Read (buf, 0, BufferLength)) <= 0) break; } @@ -214,11 +228,13 @@ public Stream Open () } throw; + } finally { + ArrayPool .Shared.Return (buf); } } /// - /// Asynchronously copies the content stream to the specified output stream. + /// Asynchronously copy the content stream to the specified output stream. /// ////// + ///This is equivalent to simply using @@ -226,6 +242,7 @@ public Stream Open () /// If you want the decoded content, use /// ///instead. An awaitable task. /// The output stream. /// The cancellation token. ///@@ -242,11 +259,11 @@ public Stream Open () if (stream == null) throw new ArgumentNullException (nameof (stream)); - var buf = new byte[4096]; - int nread; - Stream.Seek (0, SeekOrigin.Begin); + var buf = ArrayPool .Shared.Rent (BufferLength); + int nread; + try { do { if ((nread = await Stream.ReadAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false)) <= 0) @@ -264,17 +281,22 @@ public Stream Open () } throw; + } finally { + ArrayPool .Shared.Return (buf); } } /// - /// Decodes the content stream into another stream. + /// Decode the content stream into another stream. /// ////// If the content stream is encoded, this method will decode it into the output stream /// using a suitable decoder based on the + ///property, otherwise the /// stream will be copied into the output stream as-is. /// + /// /// The output stream. /// The cancellation token. ///+ ///
@@ -286,9 +308,6 @@ public Stream Open () /// /// An I/O error occurred. /// - ///- /// public void DecodeTo (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) { if (stream == null) @@ -302,13 +321,17 @@ public Stream Open () } ///- ///
- /// Asynchronously decodes the content stream into another stream. + /// Asynchronously decode the content stream into another stream. /// ////// If the content stream is encoded, this method will decode it into the output stream /// using a suitable decoder based on the + ///property, otherwise the /// stream will be copied into the output stream as-is. /// + /// + ///+ ///
An awaitable task. /// The output stream. /// The cancellation token. ///@@ -320,9 +343,6 @@ public Stream Open () /// /// An I/O error occurred. /// - ///- /// public async Task DecodeToAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) { if (stream == null) diff --git a/MimeKit/MimeEntity.cs b/MimeKit/MimeEntity.cs index 78881ea05b..de1aef614b 100644 --- a/MimeKit/MimeEntity.cs +++ b/MimeKit/MimeEntity.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast- ///
// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,12 +32,8 @@ using System.Threading.Tasks; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - -using MimeKit.Utils; using MimeKit.IO; +using MimeKit.Utils; namespace MimeKit { /// @@ -59,7 +55,7 @@ public abstract class MimeEntity Uri baseUri; /// ///- /// Initializes a new instance of the ///class + /// Initialize a new instance of the class /// based on the . /// @@ -90,7 +86,7 @@ protected MimeEntity (MimeEntityConstructorArgs args) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Initializes the based on the provided media type and subtype. @@ -107,7 +103,7 @@ protected MimeEntity (string mediaType, string mediaSubtype) : this (new Content } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Initializes the to the one provided. @@ -141,14 +137,12 @@ protected MimeEntity (ContentType contentType) protected bool TryInit (object obj) { // The base MimeEntity class only knows about Headers. - var header = obj as Header; - if (header != null) { + if (obj is Header header) { Headers.Add (header); return true; } - var headers = obj as IEnumerable ; - if (headers != null) { + if (obj is IEnumerable headers) { foreach (Header h in headers) Headers.Add (h); return true; @@ -294,13 +288,12 @@ public string ContentId { } var buffer = Encoding.UTF8.GetBytes (value); - MailboxAddress mailbox; int index = 0; - if (!MailboxAddress.TryParse (Headers.Options, buffer, ref index, buffer.Length, false, out mailbox)) - throw new ArgumentException ("Invalid Content-Id format."); + if (!ParseUtils.TryParseMsgId (buffer, ref index, buffer.Length, false, false, out string id)) + throw new ArgumentException ("Invalid Content-Id format.", nameof (value)); - contentId = mailbox.Address; + contentId = id; SetHeader ("Content-Id", "<" + contentId + ">"); } @@ -329,19 +322,28 @@ public bool IsAttachment { } } + static readonly byte[] ToStringWarning = Encoding.UTF8.GetBytes ("X-MimeKit-Warning: Do NOT use ToString() to serialize entities! Use one of the WriteTo() methods instead!"); + /// - /// Returns a ///that represents the current . + /// Returns a that represents the for debugging purposes. /// - /// Returns a - ///that represents the current . + /// Returns a + ///that represents the for debugging purposes. /// In general, the string returned from this method SHOULD NOT be used for serializing + /// the entity to disk. It is recommended that you use + ///instead. If this method is used for serializing the entity to disk, the iso-8859-1 text encoding should be used for + /// conversion. A + ///that represents the current . A public override string ToString () { using (var memory = new MemoryStream ()) { + memory.Write (ToStringWarning, 0, ToStringWarning.Length); + memory.Write (FormatOptions.Default.NewLineBytes, 0, FormatOptions.Default.NewLineBytes.Length); + WriteTo (memory); -#if !PORTABLE && !NETSTANDARD +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 var buffer = memory.GetBuffer (); #else var buffer = memory.ToArray (); @@ -356,12 +358,12 @@ public override string ToString () /// Dispatches to the specific visit method for this MIME entity. ///that represents the for debugging purposes. - /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -391,7 +393,7 @@ public virtual void Accept (MimeVisitor visitor) public abstract void Prepare (EncodingConstraint constraint, int maxLineLength = 78); /// - /// Write the ///to the specified output stream. + /// Write the to the specified output stream. /// /// Writes the headers to the output stream, followed by a blank line. @@ -425,12 +427,13 @@ public virtual void Accept (MimeVisitor visitor) } ///- /// Asynchronously write the ///to the specified output stream. + /// Asynchronously write the to the specified output stream. /// - /// + ///Writes the headers to the output stream, followed by a blank line. + ///Asynchronously writes the headers to the output stream, followed by a blank line. ///Subclasses should override this method to write the content of the entity. ///An awaitable task. /// The formatting options. /// The output stream. ///true if only the content should be written; otherwise,false . @@ -461,7 +464,7 @@ public virtual void Accept (MimeVisitor visitor) } ///- /// Write the ///to the specified output stream. + /// Write the to the specified output stream. /// /// Writes the headers to the output stream, followed by a blank line. @@ -487,12 +490,13 @@ public virtual void Accept (MimeVisitor visitor) } ///- /// Asynchronously write the ///to the specified output stream. + /// Asynchronously write the to the specified output stream. /// - /// + ///Writes the headers to the output stream, followed by a blank line. + ///Asynchronously writes the headers to the output stream, followed by a blank line. ///Subclasses should override this method to write the content of the entity. ///An awaitable task. /// The formatting options. /// The output stream. /// The cancellation token. @@ -513,7 +517,7 @@ public virtual void Accept (MimeVisitor visitor) } ///- /// Write the ///to the specified output stream. + /// Write the to the specified output stream. /// /// Writes the entity to the output stream. @@ -536,11 +540,12 @@ public virtual void Accept (MimeVisitor visitor) } /// - /// Adsynchronously write the ///to the specified output stream. + /// Asynchronously write the to the specified output stream. /// - /// Writes the entity to the output stream. + /// Asynchronously writes the entity to the output stream. /// + ///An awaitable task. /// The output stream. ///true if only the content should be written; otherwise,false . /// The cancellation token. @@ -559,7 +564,7 @@ public virtual void Accept (MimeVisitor visitor) } ///- /// Write the ///to the specified output stream. + /// Write the to the specified output stream. /// /// Writes the entity to the output stream. @@ -581,11 +586,12 @@ public virtual void Accept (MimeVisitor visitor) } /// - /// Asynchronously write the ///to the specified output stream. + /// Asynchronously write the to the specified output stream. /// - /// Writes the entity to the output stream. + /// Asynchronously writes the entity to the output stream. /// + ///An awaitable task. /// The output stream. /// The cancellation token. ///@@ -602,12 +608,11 @@ public virtual void Accept (MimeVisitor visitor) return WriteToAsync (FormatOptions.Default, stream, false, cancellationToken); } -#if !PORTABLE /// ///- /// Write the ///to the specified file. + /// Write the to the specified file. /// - /// Writes the /// The formatting options. /// The file. @@ -620,8 +625,7 @@ public virtual void Accept (MimeVisitor visitor) ///to the specified file using the provided formatting options. + /// Writes the entity to the specified file using the provided formatting options. /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -651,11 +655,12 @@ public virtual void Accept (MimeVisitor visitor) } /// ///- /// Asynchronously write the ///to the specified file. + /// Asynchronously write the to the specified file. /// - /// Writes the + ///to the specified file using the provided formatting options. + /// Asynchronously writes the entity to the specified file using the provided formatting options. /// An awaitable task. /// The formatting options. /// The file. ///true if only the content should be written; otherwise,false . @@ -667,8 +672,7 @@ public virtual void Accept (MimeVisitor visitor) ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -698,10 +702,10 @@ public virtual void Accept (MimeVisitor visitor) } /// ///- /// Write the ///to the specified file. + /// Write the to the specified file. /// - /// Writes the /// The formatting options. /// The file. @@ -713,8 +717,7 @@ public virtual void Accept (MimeVisitor visitor) ///to the specified file using the provided formatting options. + /// Writes the entity to the specified file using the provided formatting options. /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -739,16 +742,19 @@ public virtual void Accept (MimeVisitor visitor) if (fileName == null) throw new ArgumentNullException (nameof (fileName)); - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) + using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) { WriteTo (options, stream, false, cancellationToken); + stream.Flush (); + } } /// ///- /// Asynchronously write the ///to the specified file. + /// Asynchronously write the to the specified file. /// - /// Writes the + ///to the specified file using the provided formatting options. + /// Asynchronously writes the entity to the specified file using the provided formatting options. /// An awaitable task. /// The formatting options. /// The file. /// The cancellation token. @@ -759,8 +765,7 @@ public virtual void Accept (MimeVisitor visitor) ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -785,15 +790,17 @@ public virtual void Accept (MimeVisitor visitor) if (fileName == null) throw new ArgumentNullException (nameof (fileName)); - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) + using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) { await WriteToAsync (options, stream, false, cancellationToken).ConfigureAwait (false); + await stream.FlushAsync (cancellationToken).ConfigureAwait (false); + } } /// ///- /// Write the ///to the specified file. + /// Write the to the specified file. /// - /// Writes the /// The file. ///to the specified file using the default formatting options. + /// Writes the entity to the specified file using the default formatting options. /// true if only the content should be written; otherwise,false . @@ -803,8 +810,7 @@ public virtual void Accept (MimeVisitor visitor) ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -827,11 +833,12 @@ public virtual void Accept (MimeVisitor visitor) } /// ///- /// Asynchronously write the ///to the specified file. + /// Asynchronously write the to the specified file. /// - /// Writes the + ///to the specified file using the default formatting options. + /// Asynchronously writes the entity to the specified file using the default formatting options. /// An awaitable task. /// The file. ///true if only the content should be written; otherwise,false . /// The cancellation token. @@ -840,8 +847,7 @@ public virtual void Accept (MimeVisitor visitor) ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -864,10 +870,10 @@ public virtual void Accept (MimeVisitor visitor) } /// ///- /// Writes the ///to the specified file. + /// Write the to the specified file. /// - /// Writes the /// The file. /// The cancellation token. @@ -876,8 +882,7 @@ public virtual void Accept (MimeVisitor visitor) ///to the specified file using the default formatting options. + /// Writes the entity to the specified file using the default formatting options. /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -896,19 +901,16 @@ public virtual void Accept (MimeVisitor visitor) /// public void WriteTo (string fileName, CancellationToken cancellationToken = default (CancellationToken)) { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) - WriteTo (FormatOptions.Default, stream, false, cancellationToken); + WriteTo (FormatOptions.Default, fileName, cancellationToken); } ///- /// Asynchronously writes the ///to the specified file. + /// Asynchronously write the to the specified file. /// - /// Writes the + ///to the specified file using the default formatting options. + /// Asynchronously writes the entity to the specified file using the default formatting options. /// An awaitable task. /// The file. /// The cancellation token. ///@@ -916,8 +918,7 @@ public virtual void Accept (MimeVisitor visitor) /// ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -934,18 +935,13 @@ public virtual void Accept (MimeVisitor visitor) /// /// An I/O error occurred. /// - public async Task WriteToAsync (string fileName, CancellationToken cancellationToken = default (CancellationToken)) + public Task WriteToAsync (string fileName, CancellationToken cancellationToken = default (CancellationToken)) { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) - await WriteToAsync (FormatOptions.Default, stream, false, cancellationToken).ConfigureAwait (false); + return WriteToAsync (FormatOptions.Default, fileName, cancellationToken); } -#endif // !PORTABLE ///- /// Removes the header. + /// Remove a header by name. /// ////// Removes all headers matching the specified name without @@ -964,7 +960,7 @@ protected void RemoveHeader (string name) } /// - /// Sets the header. + /// Set the value of a header. /// ////// Sets the header to the specified value without @@ -984,7 +980,7 @@ protected void SetHeader (string name, string value) } /// - /// Sets the header using the raw value. + /// Set the value of a header using the raw value. /// ////// Sets the header to the specified value without @@ -1045,7 +1041,6 @@ void ContentTypeChanged (object sender, EventArgs e) /// The header being added, changed or removed. protected virtual void OnHeadersChanged (HeaderListChangedAction action, Header header) { - MailboxAddress mailbox; int index = 0; string text; @@ -1079,8 +1074,8 @@ protected virtual void OnHeadersChanged (HeaderListChangedAction action, Header baseUri = null; break; case HeaderId.ContentId: - if (MailboxAddress.TryParse (Headers.Options, header.RawValue, ref index, header.RawValue.Length, false, out mailbox)) - contentId = mailbox.Address; + if (ParseUtils.TryParseMsgId (header.RawValue, ref index, header.RawValue.Length, false, false, out string msgid)) + contentId = msgid; else contentId = null; break; @@ -1132,7 +1127,7 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) /// specified . If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save mmeory usage, but also improve /// performance. . /// If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save mmeory usage, but also improve /// performance. . /// If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save mmeory usage, but also improve /// performance. . /// If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save mmeory usage, but also improve /// performance. /// Load a @@ -1411,8 +1405,7 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) ///from the specified file. /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// ///is an invalid file path. @@ -1440,7 +1433,7 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) if (fileName == null) throw new ArgumentNullException (nameof (fileName)); - using (var stream = File.Open (fileName, FileMode.Open, FileAccess.Read)) + using (var stream = File.OpenRead (fileName)) return Load (options, stream, cancellationToken); } @@ -1462,8 +1455,7 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// ///is an invalid file path. @@ -1491,7 +1483,7 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) if (fileName == null) throw new ArgumentNullException (nameof (fileName)); - using (var stream = File.Open (fileName, FileMode.Open, FileAccess.Read)) + using (var stream = File.OpenRead (fileName)) return await LoadAsync (options, stream, cancellationToken).ConfigureAwait (false); } @@ -1510,8 +1502,7 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// ///is an invalid file path. @@ -1551,8 +1542,7 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// is an invalid file path. @@ -1576,7 +1566,6 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) { return LoadAsync (ParserOptions.Default, fileName, cancellationToken); } -#endif // !PORTABLE /// /// Load a from the specified content stream. @@ -1689,6 +1678,9 @@ void HeadersChanged (object sender, HeaderListChangedEventArgs e) /// This method is mostly meant for use with APIs such as /// where the headers are parsed separately from the content. /// + /// ///+ ///
The parsed MIME entity. /// The Content-Type of the stream. /// The content stream. diff --git a/MimeKit/MimeEntityBeginEventArgs.cs b/MimeKit/MimeEntityBeginEventArgs.cs new file mode 100644 index 0000000000..f2ef4ee5b6 --- /dev/null +++ b/MimeKit/MimeEntityBeginEventArgs.cs @@ -0,0 +1,117 @@ +// +// MimeEntityBeginEventArgs.cs +// +// Author: Jeffrey Stedfast+// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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; + +namespace MimeKit { + /// + /// Event args emitted by the + ///when it begins parsing a . + /// + /// Event args emitted by the + public class MimeEntityBeginEventArgs : EventArgs + { + ///when it begins parsing a . + /// + /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The entity that is being parsed. + ///. + /// + /// + public MimeEntityBeginEventArgs (MimeEntity entity) + { + if (entity == null) + throw new ArgumentNullException (nameof (entity)); + + Entity = entity; + } + + ///is null . + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The entity that is being parsed. + /// The parent multipart. + ///. + /// + /// + public MimeEntityBeginEventArgs (MimeEntity entity, Multipart parent) + { + if (entity == null) + throw new ArgumentNullException (nameof (entity)); + + if (parent == null) + throw new ArgumentNullException (nameof (parent)); + + Entity = entity; + Parent = parent; + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Get the MIME entity. + /// + ///+ /// Gets the MIME entity. + /// + ///The MIME entity. + public MimeEntity Entity { get; } + + ///+ /// Get the parent + ///if this entity is a child. + /// + /// Gets the parent + ///if this entity is a child. + /// The parent + public Multipart Parent { get; } + + ///. + /// Get or set the stream offset that marks the beginning of the entity. + /// + ///+ /// Gets or sets the stream offset that marks the beginning of the entity. + /// + ///The stream offset. + public long BeginOffset { get; set; } + + ///+ /// Get or set the line number of the beginning of the entity. + /// + ///+ /// Gets or sets the line number of the beginning of the entity. + /// + ///The line number. + internal int LineNumber { get; set; } + } +} diff --git a/MimeKit/MimeEntityConstructorArgs.cs b/MimeKit/MimeEntityConstructorArgs.cs index bcce3d6818..e2896bca85 100644 --- a/MimeKit/MimeEntityConstructorArgs.cs +++ b/MimeKit/MimeEntityConstructorArgs.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/MimeEntityEndEventArgs.cs b/MimeKit/MimeEntityEndEventArgs.cs new file mode 100644 index 0000000000..b0d9810d6b --- /dev/null +++ b/MimeKit/MimeEntityEndEventArgs.cs @@ -0,0 +1,98 @@ +// +// MimeEntityEndEventArgs.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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; + +namespace MimeKit { + /// + /// Event args emitted by the + ///when a is parsed. + /// + /// Event args emitted by the + public class MimeEntityEndEventArgs : MimeEntityBeginEventArgs + { + ///when a is parsed. + /// + /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The entity that was parsed. + ///. + /// + /// + public MimeEntityEndEventArgs (MimeEntity entity) : base (entity) + { + } + + ///is null . + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The entity that was parsed. + /// The parent multipart. + ///. + /// + /// + public MimeEntityEndEventArgs (MimeEntity entity, Multipart parent) : base (entity, parent) + { + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Get or set the stream offset that marks the end of the entity's headers. + /// + ///+ /// Gets or sets the stream offset that marks the end of the entity's headers. + /// + ///The stream offset. + public long HeadersEndOffset { get; set; } + + ///+ /// Get or set the stream offset that marks the end of the entity. + /// + ///+ /// Gets or sets the stream offset that marks the end of the entity. + /// + ///The stream offset. + public long EndOffset { get; set; } + + ///+ /// Get or set the content length of the entity as measured in lines. + /// + ///+ /// + ///Get or set the content length of the entity as measured in lines. + ///The line count reported by this property is the number of lines in its + /// content transfer encoding and not the resulting line count after any decoding. + ///The length of the content in lines. + public int Lines { get; set; } + } +} diff --git a/MimeKit/MimeFormat.cs b/MimeKit/MimeFormat.cs index 1e9454dbb1..30a169226a 100644 --- a/MimeKit/MimeFormat.cs +++ b/MimeKit/MimeFormat.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/MimeIterator.cs b/MimeKit/MimeIterator.cs index b4ce649cfe..1503d43c99 100644 --- a/MimeKit/MimeIterator.cs +++ b/MimeKit/MimeIterator.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,9 @@ namespace MimeKit { /// /// Walks the MIME tree structure of a + ///in depth-first order. /// + /// public class MimeIterator : IEnumerator+ ///
{ class MimeNode @@ -57,11 +60,14 @@ public MimeNode (MimeEntity entity, bool indexed) int index = -1; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new + ///for the specified message. /// + /// /// The message. ///+ ///
/// is null . @@ -76,11 +82,11 @@ public MimeIterator (MimeMessage message) ////// Releases unmanaged resources and performs other cleanup operations before - /// the ///is reclaimed by garbage collection. + /// the is reclaimed by garbage collection. /// /// Releases unmanaged resources and performs other cleanup operations before - /// the ~MimeIterator () { @@ -112,6 +118,9 @@ public MimeMessage Message { /// will beis reclaimed by garbage collection. + /// the is reclaimed by garbage collection. /// null ; otherwise the parent will be either be a ///or a . /// + /// ///+ ///
The parent entity. ////// Either has not been called or @@ -137,6 +146,9 @@ public MimeEntity Parent { /// also throws a if the last call to /// returned false, which indicates the end of the message. /// + /// ///+ ///
The current entity. ////// Either has not been called or @@ -265,6 +277,9 @@ bool Pop () /// When the iterator is at this position, subsequent calls to MoveNext also return /// false until is called. /// + /// ///+ ///
public bool MoveNext () { @@ -374,7 +389,7 @@ public bool MoveTo (string pathSpecifier) } } - if (i == path.Count && indexes[i] < index) + if (!moveFirst && indexes.Length < path.Count) Reset (); if (moveFirst && !MoveNext ()) @@ -411,11 +426,11 @@ public void Reset () } /// true if the iterator was successfully advanced to the next entity; otherwise,false .- /// Releases the unmanaged resources used by the ///and + /// Releases the unmanaged resources used by the and /// optionally releases the managed resources. /// - /// Releases the unmanaged resources used by the ///and + /// Releases the unmanaged resources used by the and /// optionally releases the managed resources. /// true to release both managed and unmanaged resources; @@ -425,12 +440,12 @@ protected virtual void Dispose (bool disposing) } ///- /// Releases all resources used by the - ///object. + /// Releases all resources used by the object. /// Call + ///when you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the so - /// the garbage collector can reclaim the memory that the was occupying. Call public void Dispose () { Dispose (true); diff --git a/MimeKit/MimeKit.Android.csproj b/MimeKit/MimeKit.Android.csproj deleted file mode 100644 index f9f43a3956..0000000000 --- a/MimeKit/MimeKit.Android.csproj +++ /dev/null @@ -1,275 +0,0 @@ - -when you are finished using the . The + /// method leaves the in an unusable state. After + /// calling , you must release all references to the so + /// the garbage collector can reclaim the memory that the was occupying. - diff --git a/MimeKit/MimeKit.Android.project.json b/MimeKit/MimeKit.Android.project.json deleted file mode 100644 index 22430247f9..0000000000 --- a/MimeKit/MimeKit.Android.project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "frameworks": { - "MonoAndroid,Version=v4.0.3": {} - }, - "runtimes": { - "win-anycpu": {} - } -} diff --git a/MimeKit/MimeKit.Mac.csproj b/MimeKit/MimeKit.Mac.csproj deleted file mode 100644 index 527863b331..0000000000 --- a/MimeKit/MimeKit.Mac.csproj +++ /dev/null @@ -1,253 +0,0 @@ - -- -Debug -AnyCPU -8.0.30703 -2.0 -{004B4019-62B7-4A15-AF2C-C20968845C46} -{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -Library -MimeKit -Resources\Resource.designer.cs -Resource -Resources -Assets -false -MimeKit -v4.0.3 -- -- -true -full -false -bin\Debug\MonoAndroid -obj\Debug\MonoAndroid -DEBUG;SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM;__MOBILE__;__ANDROID__ -true -prompt -4 -- -true -bin\Release\MonoAndroid -obj\Release\MonoAndroid -bin\Release\MonoAndroid\MimeKit.xml -SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM;__MOBILE__;__ANDROID__ -true -prompt -4 -- -true -- -mimekit.snk -- -- - - - - - - - -- -{A0D302CB-8866-4AB1-98B9-F0772EABF5DF} -BouncyCastle.Android -- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- -- -- -- - - diff --git a/MimeKit/MimeKit.Mac.project.json b/MimeKit/MimeKit.Mac.project.json deleted file mode 100644 index 49847c4d05..0000000000 --- a/MimeKit/MimeKit.Mac.project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "frameworks": { - "Xamarin.Mac,Version=v2.0": {} - }, - "dependencies": { - "Portable.BouncyCastle": "1.8.1.2" - } -} \ No newline at end of file diff --git a/MimeKit/MimeKit.Net45.csproj b/MimeKit/MimeKit.Net45.csproj deleted file mode 100644 index dcdb130cce..0000000000 --- a/MimeKit/MimeKit.Net45.csproj +++ /dev/null @@ -1,278 +0,0 @@ - -- -Debug -AnyCPU -{2CE98315-A444-44D3-8BCE-32389EDDA149} -{A3F8F2AB-B479-4A4A-A458-A89E7DC349F1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -Library -MimeKit -MimeKit -v2.0 -Xamarin.Mac -Resources -- -true -full -false -bin\Debug\Xamarin.Mac -obj\Debug\Xamarin.Mac -DEBUG;SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM;__UNIFIED__;__MOBILE__;__MAC__ -true -prompt -4 -- -pdbonly -true -bin\Release\Xamarin.Mac -obj\Release\Xamarin.Mac -bin\Release\Xamarin.Mac\MimeKit.xml -SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM;__UNIFIED__;__MOBILE__;__MAC__ -true -true -prompt -4 -- -true -- -mimekit.snk -- -- - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MimeKit/MimeKit.Net45.project.json b/MimeKit/MimeKit.Net45.project.json deleted file mode 100644 index 8f6b4a8248..0000000000 --- a/MimeKit/MimeKit.Net45.project.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "frameworks": { - ".NETFramework,Version=v4.5": {} - }, - "runtimes": { - "win-anycpu": {}, - "win": {} - }, - "dependencies": { - "BouncyCastle": "1.8.1" - } -} diff --git a/MimeKit/MimeKit.Portable.csproj b/MimeKit/MimeKit.Portable.csproj deleted file mode 100644 index 544f7ee94a..0000000000 --- a/MimeKit/MimeKit.Portable.csproj +++ /dev/null @@ -1,263 +0,0 @@ - -- -Debug -AnyCPU -AnyCPU -{D5F54A4F-D84B-430F-9271-F7861E285B3E} -.NETFramework -v4.5 -Library -Properties -MimeKit -MimeKit -512 -8.0.30703 -2.0 -- -True -full -False -bin\Debug\net45 -obj\Debug\net45 -DEBUG;TRACE;SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM; -prompt -4 -True -- -pdbonly -True -bin\Release\net45 -obj\Release\net45 -TRACE;SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM; -prompt -4 -true -bin\Release\net45\MimeKit.xml -- -true -- -mimekit.snk -- -- - - - - - - - - -..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll -- - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - -- -- -- -- - diff --git a/MimeKit/MimeKit.Portable.project.json b/MimeKit/MimeKit.Portable.project.json deleted file mode 100644 index b5d4cdf842..0000000000 --- a/MimeKit/MimeKit.Portable.project.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "frameworks": { - ".NETPortable,Version=v4.5,Profile=Profile7": {} - }, - "runtimes": { - "win-anycpu": {} - }, - "dependencies": { - "Portable.BouncyCastle": "1.8.1.3" - } -} \ No newline at end of file diff --git a/MimeKit/MimeKit.TvOS.csproj b/MimeKit/MimeKit.TvOS.csproj deleted file mode 100644 index 382e52aa9f..0000000000 --- a/MimeKit/MimeKit.TvOS.csproj +++ /dev/null @@ -1,249 +0,0 @@ - -- -Debug -AnyCPU -8.0.30703 -2.0 -{BE542CE1-F773-467E-8DED-D02B89C5040A} -{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -Library -MimeKit -MimeKit -Profile7 -v4.5 -10.0 -- -true -full -false -bin\Debug\portable -obj\Debug\portable -DEBUG;TRACE;PORTABLE;ENABLE_CRYPTO;USE_HTTP_CLIENT -prompt -4 -false -true -- -true -bin\Release\portable -obj\Release\portable -bin\Release\portable\MimeKit.xml -TRACE;PORTABLE;ENABLE_CRYPTO;USE_HTTP_CLIENT -prompt -4 -false -true -- -true -- -mimekit.snk -- -- -{EEE48C75-11BE-4B50-B759-F85B5052D473} -Portable.Text.Encoding -- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - -- -- -- -- - diff --git a/MimeKit/MimeKit.TvOS.project.json b/MimeKit/MimeKit.TvOS.project.json deleted file mode 100644 index 4a948eec57..0000000000 --- a/MimeKit/MimeKit.TvOS.project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "frameworks": { - "Xamarin.TVOS,Version=v1.0": {} - }, - "dependencies": { - "Portable.BouncyCastle": "1.8.1.2" - } -} diff --git a/MimeKit/MimeKit.WatchOS.csproj b/MimeKit/MimeKit.WatchOS.csproj deleted file mode 100644 index 80ee16910d..0000000000 --- a/MimeKit/MimeKit.WatchOS.csproj +++ /dev/null @@ -1,252 +0,0 @@ - -- -Debug -AnyCPU -{ABEEAB21-341F-43E0-8851-0EDB3CE10AD4} -{06FA79CB-D6CD-4721-BB4B-1BD202089C55};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -Library -MimeKit -MimeKit -Resources -- -true -full -false -bin\Debug\Xamarin.TvOS -obj\Debug\Xamarin.TvOS -DEBUG;SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM;__UNIFIED__;__MOBILE__;__TVOS__ -true -prompt -4 -- -pdbonly -true -bin\Release\Xamarin.TvOS -obj\Release\Xamarin.TvOS -bin\Release\Xamarin.TvOS\MimeKit.xml -SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM;__UNIFIED__;__MOBILE__;__TVOS__ -true -true -prompt -4 -- -- - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - diff --git a/MimeKit/MimeKit.WatchOS.project.json b/MimeKit/MimeKit.WatchOS.project.json deleted file mode 100644 index 82a2acdacf..0000000000 --- a/MimeKit/MimeKit.WatchOS.project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "frameworks": { - "Xamarin.WatchOS,Version=v1.0": {} - }, - "dependencies": { - "Portable.BouncyCastle": "1.8.1.2" - } -} diff --git a/MimeKit/MimeKit.WindowsUniversal81.csproj b/MimeKit/MimeKit.WindowsUniversal81.csproj deleted file mode 100644 index e140d40f74..0000000000 --- a/MimeKit/MimeKit.WindowsUniversal81.csproj +++ /dev/null @@ -1,271 +0,0 @@ - -- -Debug -AnyCPU -{9AD6AE8B-D83D-4511-AA08-2BA8904D2E7C} -{FC940695-DFE0-4552-9F25-99AF4A5619A1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -Library -MimeKit -MimeKit -Resources -- -true -full -false -bin\Debug\Xamarin.WatchOS -DEBUG;SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM;__UNIFIED__;__MOBILE__;__WATCHOS__ -prompt -4 -NSUrlSessionHandler -- -pdbonly -true -bin\Release\Xamarin.WatchOS -obj\Release\Xamarin.WatchOS -bin\Release\Xamarin.WatchOS\MimeKit.xml -SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM;__UNIFIED__;__MOBILE__;__WATCHOS_ -true -true -prompt -4 -NSUrlSessionHandler -- -true -- -mimekit.snk -- -- - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MimeKit/MimeKit.WindowsUniversal81.project.json b/MimeKit/MimeKit.WindowsUniversal81.project.json deleted file mode 100644 index 68db0c76dc..0000000000 --- a/MimeKit/MimeKit.WindowsUniversal81.project.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "frameworks": { - ".NETPortable,Version=v4.5,Profile=Profile111": {} - }, - "runtimes": { - "win-anycpu": {} - }, - "dependencies": { - "Portable.BouncyCastle": "1.8.1.3" - } -} \ No newline at end of file diff --git a/MimeKit/MimeKit.NetStandard.csproj b/MimeKit/MimeKit.csproj similarity index 74% rename from MimeKit/MimeKit.NetStandard.csproj rename to MimeKit/MimeKit.csproj index 36abcb17d6..0c34d35390 100644 --- a/MimeKit/MimeKit.NetStandard.csproj +++ b/MimeKit/MimeKit.csproj @@ -3,14 +3,14 @@- - -11.0 -Debug -AnyCPU -8.0.30703 -2.0 -{D9906B8C-7BBD-4CCE-AC7C-E9BCA020D20C} -Library -Properties -MimeKit -MimeKit -v4.5 -Profile111 -en-US -512 -{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -- -true -full -false -bin\Debug\wpa81 -obj\Debug\wpa81 -DEBUG;TRACE;PORTABLE;ENABLE_CRYPTO;USE_HTTP_CLIENT -prompt -4 -false -true -- -- true -bin\Release\wpa81 -obj\Release\wpa81 -bin\Release\wpa81\MimeKit.xml -TRACE;PORTABLE;ENABLE_CRYPTO;USE_HTTP_CLIENT -prompt -4 -false -true -- -true -- -mimekit.snk -- - -- -{B76A64F9-B00E-4243-AE89-5D024CA3B436} -Portable.Text.Encoding.WindowsUniversal81 -- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - -- -- -- -- - An Open Source library for creating and parsing MIME, S/MIME, PGP messages on desktop and mobile platforms. MimeKit -2.0.1.1 +2.10.1.1 Jeffrey Stedfast -netstandard1.3;netstandard1.6;netstandard2.0 +netstandard2.0;netstandard2.1;net45;net46;net47;net48 true false MimeKit MimeKit -mime;encryption;dkim;security;smime;s/mime;openpgp;pgp;mbox;email;parser;tnef;net45;netstandard;netstandard1.3;netstandard1.6;netstandard2.0;xamarin;android;ios;monodroid;monoandroid;win8;wp81 +mime;encryption;dkim;security;smime;s/mime;openpgp;pgp;mbox;email;parser;tnef;net45;net46;net47;net48;netstandard;netstandard1.3;netstandard1.6;netstandard2.0;netstandard2.1 https://github.com/jstedfast/MimeKit https://github.com/jstedfast/MimeKit/blob/master/License.md false @@ -23,23 +23,55 @@false false MimeKit -$(DefineConstants);ENABLE_CRYPTO;USE_HTTP_CLIENT;NETSTANDARD +$(DefineConstants);ENABLE_CRYPTO true mimekit.snk true -true +true - - + + + +full ++ + +portable ++ +$(DefineConstants);ENABLE_SNM ++ + +$(DefineConstants);ENABLE_SNM;SERIALIZABLE ++ + +- - - + - - + + + ++ + + + + ++ + + ++ + + + + + @@ -47,6 +79,10 @@ diff --git a/MimeKit/MimeKit.iOS.csproj b/MimeKit/MimeKit.iOS.csproj deleted file mode 100644 index 754bab7a33..0000000000 --- a/MimeKit/MimeKit.iOS.csproj +++ /dev/null @@ -1,271 +0,0 @@ - -+ + + + @@ -62,22 +98,28 @@ + + + + + - + + @@ -86,6 +128,10 @@ + + + + @@ -93,8 +139,13 @@ + + + + + @@ -134,6 +185,7 @@ + @@ -148,6 +200,7 @@ + @@ -157,8 +210,10 @@ + + @@ -182,6 +237,7 @@ + @@ -220,10 +276,14 @@ + + + + @@ -239,6 +299,7 @@ + - diff --git a/MimeKit/MimeKit.iOS.project.json b/MimeKit/MimeKit.iOS.project.json deleted file mode 100644 index 4a7c8a5100..0000000000 --- a/MimeKit/MimeKit.iOS.project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "frameworks": { - "Xamarin.iOS,Version=v1.0": {} - }, - "runtimes": { - "win-anycpu": {} - } -} \ No newline at end of file diff --git a/MimeKit/MimeKitLite.Android.csproj b/MimeKit/MimeKitLite.Android.csproj deleted file mode 100644 index 1529b3ed5e..0000000000 --- a/MimeKit/MimeKitLite.Android.csproj +++ /dev/null @@ -1,210 +0,0 @@ - -- -Debug -AnyCPU -8.0.30703 -2.0 -{4C1288AD-12C8-4BF7-AED7-6C4DC539C856} -{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -Library -MimeKit -MimeKit -Resources -Xamarin.iOS -- -true -full -false -bin\Debug\Xamarin.iOS -obj\Debug\Xamarin.iOS -DEBUG;SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM;__UNIFIED__;__MOBILE__;__IOS__ -true -prompt -4 -- -pdbonly -true -bin\Release\Xamarin.iOS -obj\Release\Xamarin.iOS -bin\Release\Xamarin.iOS\MimeKit.xml -SERIALIZABLE;ENABLE_CRYPTO;ENABLE_SNM;__UNIFIED__;__MOBILE__;__IOS__ -true -true -prompt -4 -- -true -- -mimekit.snk -- -- - - - - - - - -- -{0249241C-205E-4AC0-828B-90F822359B9E} -BouncyCastle.iOS -- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- -- -- -- - - diff --git a/MimeKit/MimeKitLite.Android.project.json b/MimeKit/MimeKitLite.Android.project.json deleted file mode 100644 index 22430247f9..0000000000 --- a/MimeKit/MimeKitLite.Android.project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "frameworks": { - "MonoAndroid,Version=v4.0.3": {} - }, - "runtimes": { - "win-anycpu": {} - } -} diff --git a/MimeKit/MimeKitLite.Net45.csproj b/MimeKit/MimeKitLite.Net45.csproj deleted file mode 100644 index 0726764d97..0000000000 --- a/MimeKit/MimeKitLite.Net45.csproj +++ /dev/null @@ -1,216 +0,0 @@ - -- -Debug -AnyCPU -10.0.0 -2.0 -{D95A2734-2A81-4C75-985E-A33B15CC62BC} -{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -Library -MimeKit -Resources\Resource.designer.cs -Resource -Resources -Assets -False -MimeKitLite -v4.0.3 -- -true -full -false -bin\Debug\MonoAndroid -obj\Debug\MonoAndroid -DEBUG;SERIALIZABLE;ENABLE_SNM;__MOBILE__;__ANDROID__ -prompt -4 -None -false -true -- -true -bin\Release\MonoAndroid -obj\Release\MonoAndroid -prompt -4 -false -false -true -SERIALIZABLE;ENABLE_SNM;__MOBILE__;__ANDROID__ -bin\Release\MonoAndroid\MimeKitLite.xml -- -true -- -mimekit.snk -- -- - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- -- -- -- - diff --git a/MimeKit/MimeKitLite.Net45.project.json b/MimeKit/MimeKitLite.Net45.project.json deleted file mode 100644 index 7b2e820fd1..0000000000 --- a/MimeKit/MimeKitLite.Net45.project.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "frameworks": { - "net45": {} - }, - "runtimes": { - "win-anycpu": {}, - "win": {} - } -} diff --git a/MimeKit/MimeKitLite.Portable.csproj b/MimeKit/MimeKitLite.Portable.csproj deleted file mode 100644 index 4d213bc78f..0000000000 --- a/MimeKit/MimeKitLite.Portable.csproj +++ /dev/null @@ -1,210 +0,0 @@ - -- -Debug -AnyCPU -10.0.0 -2.0 -{B4DA3323-F83C-4731-BE30-B1DA19B8C3E7} -v4.5 -Library -Properties -MimeKit -MimeKitLite -512 -- -True -full -False -bin\Debug\net45 -obj\Debug\net45 -DEBUG;TRACE;SERIALIZABLE;ENABLE_SNM -prompt -4 -True -- -True -bin\Release\net45 -obj\Release\net45 -TRACE;SERIALIZABLE;ENABLE_SNM -prompt -4 -true -bin\Release\net45\MimeKitLite.xml -- -true -- -mimekit.snk -- -- - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - -- -- -- -- - diff --git a/MimeKit/MimeKitLite.Portable.project.json b/MimeKit/MimeKitLite.Portable.project.json deleted file mode 100644 index dbfd9834e6..0000000000 --- a/MimeKit/MimeKitLite.Portable.project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "frameworks": { - ".NETPortable,Version=v4.5,Profile=Profile7": {} - }, - "runtimes": { - "win-anycpu": {} - }, -} diff --git a/MimeKit/MimeKitLite.WindowsUniversal81.csproj b/MimeKit/MimeKitLite.WindowsUniversal81.csproj deleted file mode 100644 index 88c0fab0d1..0000000000 --- a/MimeKit/MimeKitLite.WindowsUniversal81.csproj +++ /dev/null @@ -1,217 +0,0 @@ - -- -Debug -AnyCPU -10.0.0 -2.0 -{25C2DD3E-ED14-494F-9BD4-75536911C1BD} -{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -Library -MimeKit -MimeKitLite -Profile7 -v4.5 -10.0 -- -true -full -false -bin\Debug\portable -obj\Debug\portable -DEBUG;TRACE;PORTABLE -prompt -4 -false -true -- -pdbonly -true -bin\Release\portable -obj\Release\portable -bin\Release\portable\MimeKitLite.xml -TRACE;PORTABLE -prompt -4 -false -true -- -true -- -mimekit.snk -- -- -{EEE48C75-11BE-4B50-B759-F85B5052D473} -Portable.Text.Encoding -- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- -- -- -- - diff --git a/MimeKit/MimeKitLite.WindowsUniversal81.project.json b/MimeKit/MimeKitLite.WindowsUniversal81.project.json deleted file mode 100644 index e01dcfbe78..0000000000 --- a/MimeKit/MimeKitLite.WindowsUniversal81.project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "frameworks": { - ".NETPortable,Version=v4.5,Profile=Profile111": {} - }, - "runtimes": { - "win-anycpu": {} - }, -} diff --git a/MimeKit/MimeKitLite.NetStandard.csproj b/MimeKit/MimeKitLite.csproj similarity index 84% rename from MimeKit/MimeKitLite.NetStandard.csproj rename to MimeKit/MimeKitLite.csproj index 903b3362ed..a812b126c2 100644 --- a/MimeKit/MimeKitLite.NetStandard.csproj +++ b/MimeKit/MimeKitLite.csproj @@ -1,16 +1,16 @@ -- - -11.0 -Debug -AnyCPU -10.0.0 -2.0 -{5EB9F403-45B4-4F3A-B64E-0ECC94D14167} -Library -Properties -MimeKit -MimeKitLite -v4.5 -Profile111 -en-US -512 -{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -- -true -full -false -bin\Debug\wpa81 -obj\Debug\wpa81 -DEBUG;TRACE;PORTABLE -prompt -4 -false -true -- -pdbonly -true -bin\Release\wpa81 -obj\Release\wpa81 -bin\Release\wpa81\MimeKitLite.xml -TRACE;PORTABLE -prompt -4 -false -true -- -true -- -mimekit.snk -- - -- -{B76A64F9-B00E-4243-AE89-5D024CA3B436} -Portable.Text.Encoding.WindowsUniversal81 -- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- -- -- -- + - An Open Source library for creating and parsing MIME, S/MIME, PGP messages on desktop and mobile platforms. MimeKit Lite -2.0.1.1 +2.10.1.1 Jeffrey Stedfast -netstandard1.3;netstandard1.6;netstandard2.0 +netstandard2.0;net45;net46;net47;net48 true false MimeKitLite MimeKitLite -mime;mbox;mail;email;parser;tnef;net45;netstandard;netstandard1.3;netstandard1.6;netstandard2.0;xamarin;android;ios;monodroid;monoandroid;win8;wp81 +mime;mbox;mail;email;parser;tnef;net45;netstandard;netstandard2.0 https://github.com/jstedfast/MimeKit https://github.com/jstedfast/MimeKit/blob/master/License.md false @@ -23,24 +23,38 @@false false MimeKit -$(DefineConstants);NETSTANDARD true mimekit.snk true -true +true - - - - - - - + + + +full ++ + +portable ++ +$(DefineConstants);ENABLE_SNM ++ + +$(DefineConstants);ENABLE_SNM;SERIALIZABLE ++ + ++ + + + diff --git a/MimeKit/MimeKitLite.iOS.csproj b/MimeKit/MimeKitLite.iOS.csproj deleted file mode 100644 index b3e3386ae0..0000000000 --- a/MimeKit/MimeKitLite.iOS.csproj +++ /dev/null @@ -1,211 +0,0 @@ - -@@ -74,6 +88,7 @@ + @@ -88,6 +103,7 @@ + @@ -97,8 +113,10 @@ + + @@ -122,6 +140,7 @@ + @@ -160,10 +179,14 @@ + + + + @@ -179,6 +202,7 @@ + - diff --git a/MimeKit/MimeKitLite.iOS.project.json b/MimeKit/MimeKitLite.iOS.project.json deleted file mode 100644 index 4a7c8a5100..0000000000 --- a/MimeKit/MimeKitLite.iOS.project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "frameworks": { - "Xamarin.iOS,Version=v1.0": {} - }, - "runtimes": { - "win-anycpu": {} - } -} \ No newline at end of file diff --git a/MimeKit/MimeMessage.cs b/MimeKit/MimeMessage.cs index 7922418d33..419f8b561c 100644 --- a/MimeKit/MimeMessage.cs +++ b/MimeKit/MimeMessage.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast- -Debug -AnyCPU -10.0.0 -2.0 -{5F211544-940D-46C9-98EB-4FD8F62506AD} -{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} -Library -MimeKit -Resources -MimeKitLite -Xamarin.iOS -- -true -full -false -bin\Debug\Xamarin.iOS -obj\Debug\Xamarin.iOS -DEBUG;SERIALIZABLE;ENABLE_SNM;__MOBILE__;__IOS__ -prompt -4 -false -true -- -true -bin\Release\Xamarin.iOS -obj\Release\Xamarin.iOS -prompt -4 -false -true -SERIALIZABLE;ENABLE_SNM;__MOBILE__;__IOS__ -True -bin\Release\Xamarin.iOS\MimeKitLite.xml -- -true -- -mimekit.snk -- -- - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HtmlEntityDecoder.cs -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - -- -- -- -- - // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -29,13 +29,10 @@ using System.Text; using System.Linq; using System.Threading; +using System.Globalization; using System.Threading.Tasks; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - #if ENABLE_SNM using System.Net.Mail; #endif @@ -45,6 +42,7 @@ using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Parameters; using MimeKit.Cryptography; #endif @@ -65,12 +63,12 @@ namespace MimeKit { /// addresses; + readonly Dictionary addresses; MessageImportance importance = MessageImportance.Normal; XMessagePriority xpriority = XMessagePriority.Normal; MessagePriority priority = MessagePriority.Normal; @@ -88,16 +86,16 @@ public class MimeMessage // Note: this .ctor is used only by the MimeParser and MimeMessage.CreateFromMailMessage() internal MimeMessage (ParserOptions options, IEnumerable headers, RfcComplianceMode mode) { - addresses = new Dictionary (MimeUtils.OrdinalIgnoreCase); + addresses = new Dictionary (); Headers = new HeaderList (options); compliance = mode; // initialize our address lists - foreach (var name in StandardAddressHeaders) { + foreach (var id in StandardAddressHeaders) { var list = new InternetAddressList (); list.Changed += InternetAddressListChanged; - addresses.Add (name, list); + addresses.Add (id, list); } references = new MessageIdList (); @@ -117,16 +115,16 @@ internal MimeMessage (ParserOptions options, IEnumerable headers, RfcCom internal MimeMessage (ParserOptions options) { - addresses = new Dictionary (MimeUtils.OrdinalIgnoreCase); + addresses = new Dictionary (); Headers = new HeaderList (options); compliance = RfcComplianceMode.Strict; // initialize our address lists - foreach (var name in StandardAddressHeaders) { + foreach (var id in StandardAddressHeaders) { var list = new InternetAddressList (); list.Changed += InternetAddressListChanged; - addresses.Add (name, list); + addresses.Add (id, list); } references = new MessageIdList (); @@ -137,7 +135,7 @@ internal MimeMessage (ParserOptions options) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -147,7 +145,7 @@ internal MimeMessage (ParserOptions options) /// is null . ///- /// @@ -180,7 +178,7 @@ public MimeMessage (params object[] args) : this (ParserOptions.Default.Clone () Headers.Add (h); } - continue; + continue; } var entity = obj as MimeEntity; @@ -212,7 +210,7 @@ public MimeMessage (params object[] args) : this (ParserOptions.Default.Clone () } ///+ /// contains more than one . /// contains more than one . -or- ////// contains one or more arguments of an unknown type. - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new MIME message, specifying details at creation time. @@ -230,7 +228,7 @@ public MimeMessage (IEnumerable from, IEnumerable - /// Initializes a new instance of the class. + /// Initialize a new instance of the class. /// /// /// Creates a new MIME message. @@ -244,7 +242,7 @@ public MimeMessage () : this (ParserOptions.Default.Clone ()) } /// - /// Gets or sets the mbox marker. + /// Get or set the mbox marker. /// ////// Set by the when parsing attached message/rfc822 parts @@ -256,17 +254,17 @@ internal byte[] MboxMarker { } /// - /// Gets the list of headers. + /// Get the list of headers. /// ////// ///Represents the list of headers for a message. Typically, the headers of /// a message will contain transmission headers such as From and To along /// with metadata headers such as Subject and Date, but may include just /// about anything. - ///+ /// /// To access any MIME headers other than + /// To access any MIME headers other than /// , you will need to access the /// property of the . - /// The list of headers. public HeaderList Headers { @@ -385,7 +383,7 @@ public XMessagePriority XPriority { } ///- /// Gets or sets the address in the Sender header. + /// Get or set the address in the Sender header. /// ////// The sender may differ from the addresses in if @@ -408,7 +406,7 @@ public MailboxAddress Sender { var builder = new StringBuilder (" "); int len = "Sender: ".Length; - value.Encode (options, builder, ref len); + value.Encode (options, builder, true, ref len); builder.Append (options.NewLine); var raw = Encoding.UTF8.GetBytes (builder.ToString ()); @@ -420,7 +418,7 @@ public MailboxAddress Sender { } /// - /// Gets or sets the address in the Resent-Sender header. + /// Get or set the address in the Resent-Sender header. /// ////// The resent sender may differ from the addresses in if @@ -443,7 +441,7 @@ public MailboxAddress ResentSender { var builder = new StringBuilder (" "); int len = "Resent-Sender: ".Length; - value.Encode (options, builder, ref len); + value.Encode (options, builder, true, ref len); builder.Append (options.NewLine); var raw = Encoding.UTF8.GetBytes (builder.ToString ()); @@ -455,7 +453,7 @@ public MailboxAddress ResentSender { } /// - /// Gets the list of addresses in the From header. + /// Get the list of addresses in the From header. /// ////// ///The "From" header specifies the author(s) of the message. @@ -466,11 +464,11 @@ public MailboxAddress ResentSender { ///The list of addresses in the From header. public InternetAddressList From { - get { return addresses["From"]; } + get { return addresses[HeaderId.From]; } } ///- /// Gets the list of addresses in the Resent-From header. + /// Get the list of addresses in the Resent-From header. /// ////// ///The "Resent-From" header specifies the author(s) of the messagebeing @@ -482,11 +480,11 @@ public InternetAddressList From { /// The list of addresses in the Resent-From header. public InternetAddressList ResentFrom { - get { return addresses["Resent-From"]; } + get { return addresses[HeaderId.ResentFrom]; } } ///- /// Gets the list of addresses in the Reply-To header. + /// Get the list of addresses in the Reply-To header. /// ////// ///When the list of addresses in the Reply-To header is not empty, @@ -498,11 +496,11 @@ public InternetAddressList ResentFrom { /// The list of addresses in the Reply-To header. public InternetAddressList ReplyTo { - get { return addresses["Reply-To"]; } + get { return addresses[HeaderId.ReplyTo]; } } ///- /// Gets the list of addresses in the Resent-Reply-To header. + /// Get the list of addresses in the Resent-Reply-To header. /// ////// ///When the list of addresses in the Resent-Reply-To header is not empty, @@ -514,11 +512,11 @@ public InternetAddressList ReplyTo { /// The list of addresses in the Resent-Reply-To header. public InternetAddressList ResentReplyTo { - get { return addresses["Resent-Reply-To"]; } + get { return addresses[HeaderId.ResentReplyTo]; } } ///- /// Gets the list of addresses in the To header. + /// Get the list of addresses in the To header. /// ////// The addresses in the To header are the primary recipients of @@ -526,11 +524,11 @@ public InternetAddressList ResentReplyTo { /// ///The list of addresses in the To header. public InternetAddressList To { - get { return addresses["To"]; } + get { return addresses[HeaderId.To]; } } ///- /// Gets the list of addresses in the Resent-To header. + /// Get the list of addresses in the Resent-To header. /// ////// The addresses in the Resent-To header are the primary recipients of @@ -538,11 +536,11 @@ public InternetAddressList To { /// ///The list of addresses in the Resent-To header. public InternetAddressList ResentTo { - get { return addresses["Resent-To"]; } + get { return addresses[HeaderId.ResentTo]; } } ///- /// Gets the list of addresses in the Cc header. + /// Get the list of addresses in the Cc header. /// ////// The addresses in the Cc header are secondary recipients of the message @@ -551,11 +549,11 @@ public InternetAddressList ResentTo { /// ///The list of addresses in the Cc header. public InternetAddressList Cc { - get { return addresses["Cc"]; } + get { return addresses[HeaderId.Cc]; } } ///- /// Gets the list of addresses in the Resent-Cc header. + /// Get the list of addresses in the Resent-Cc header. /// ////// The addresses in the Resent-Cc header are secondary recipients of the message @@ -564,11 +562,11 @@ public InternetAddressList Cc { /// ///The list of addresses in the Resent-Cc header. public InternetAddressList ResentCc { - get { return addresses["Resent-Cc"]; } + get { return addresses[HeaderId.ResentCc]; } } ///- /// Gets the list of addresses in the Bcc header. + /// Get the list of addresses in the Bcc header. /// ////// Recipients in the Blind-Carpbon-Copy list will not be visible to @@ -576,11 +574,11 @@ public InternetAddressList ResentCc { /// ///The list of addresses in the Bcc header. public InternetAddressList Bcc { - get { return addresses["Bcc"]; } + get { return addresses[HeaderId.Bcc]; } } ///- /// Gets the list of addresses in the Resent-Bcc header. + /// Get the list of addresses in the Resent-Bcc header. /// ////// Recipients in the Resent-Bcc list will not be visible to @@ -588,11 +586,11 @@ public InternetAddressList Bcc { /// ///The list of addresses in the Resent-Bcc header. public InternetAddressList ResentBcc { - get { return addresses["Resent-Bcc"]; } + get { return addresses[HeaderId.ResentBcc]; } } ///- /// Gets or sets the subject of the message. + /// Get or set the subject of the message. /// ////// The Subject is typically a short string denoting the topic of the message. @@ -613,7 +611,7 @@ public string Subject { } ///- /// Gets or sets the date of the message. + /// Get or set the date of the message. /// ////// If the date is not explicitly set before the message is written to a stream, @@ -632,7 +630,7 @@ public DateTimeOffset Date { } /// - /// Gets or sets the Resent-Date of the message. + /// Get or set the Resent-Date of the message. /// ////// Gets or sets the Resent-Date of the message. @@ -650,7 +648,7 @@ public DateTimeOffset ResentDate { } /// - /// Gets or sets the list of references to other messages. + /// Get the list of references to other messages. /// ////// The References header contains a chain of Message-Ids back to the @@ -662,7 +660,7 @@ public MessageIdList References { } /// - /// Gets or sets the Message-Id that this message is in reply to. + /// Get or set the Message-Id that this message is replying to. /// ////// If the message is a reply to another message, it will typically @@ -699,12 +697,12 @@ public string InReplyTo { } /// - /// Gets or sets the message identifier. + /// Get or set the message identifier. /// ////// ///The Message-Id is meant to be a globally unique identifier for /// a message. - ///can be used + /// /// can be used /// to generate this value. The message identifier. @@ -737,12 +735,12 @@ public string MessageId { } ///- /// Gets or sets the Resent-Message-Id header. + /// Get or set the Resent-Message-Id header. /// ////// ///The Resent-Message-Id is meant to be a globally unique identifier for /// a message. - ///can be used + /// /// can be used /// to generate this value. The Resent-Message-Id. @@ -775,7 +773,7 @@ public string ResentMessageId { } ///- /// Gets or sets the MIME-Version. + /// Get or set the MIME-Version. /// ////// The MIME-Version header specifies the version of the MIME specification @@ -800,7 +798,7 @@ public Version MimeVersion { } /// - /// Gets or sets the body of the message. + /// Get or set the body of the message. /// ////// The body of the message can either be plain text or it can be a @@ -880,7 +878,7 @@ static bool TryGetMultipartBody (Multipart multipart, TextFormat format, out str } /// - /// Gets the text body of the message if it exists. + /// Get the text body of the message if it exists. /// ////// Gets the text content of the first text/plain body part that is found (in depth-first @@ -892,7 +890,7 @@ public string TextBody { } /// - /// Gets the html body of the message if it exists. + /// Get the html body of the message if it exists. /// ////// Gets the HTML-formatted body of the message if it exists. @@ -903,7 +901,7 @@ public string HtmlBody { } ///- /// Gets the text body in the specified format. + /// Get the text body in the specified format. /// ////// Gets the text body in the specified format, if it exists. @@ -949,7 +947,7 @@ static IEnumerable EnumerateMimeParts (MimeEntity entity) } /// - /// Gets the body parts of the message. + /// Get the body parts of the message. /// ////// Traverses over the MIME tree, enumerating all of the objects, @@ -964,7 +962,7 @@ public IEnumerable BodyParts { } /// - /// Gets the attachments. + /// Get the attachments. /// ////// Traverses over the MIME tree, enumerating all of the objects that @@ -978,21 +976,28 @@ public IEnumerable Attachments { get { return EnumerateMimeParts (Body).Where (x => x.IsAttachment); } } + static readonly byte[] ToStringWarning = Encoding.UTF8.GetBytes ("X-MimeKit-Warning: Do NOT use ToString() to serialize messages! Use one of the WriteTo() methods instead!"); + /// - /// Returns a ///that represents the current . + /// Returns a that represents the for debugging purposes. /// - /// - ///Returns a - ///that represents the current . + /// Note: In general, the string returned from this method SHOULD NOT be used for serializing - /// the message to disk. It is recommended that you use instead. Returns a + ///that represents the for debugging purposes. /// In general, the string returned from this method SHOULD NOT be used for serializing + /// the message to disk. It is recommended that you use + ///instead. If this method is used for serializing the message to disk, the iso-8859-1 text encoding should be used for + /// conversion. A + ///that represents the current . A public override string ToString () { using (var memory = new MemoryStream ()) { + memory.Write (ToStringWarning, 0, ToStringWarning.Length); + memory.Write (FormatOptions.Default.NewLineBytes, 0, FormatOptions.Default.NewLineBytes.Length); + WriteTo (FormatOptions.Default, memory); -#if !PORTABLE && !NETSTANDARD +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 var buffer = memory.GetBuffer (); #else var buffer = memory.ToArray (); @@ -1007,12 +1012,12 @@ public override string ToString () /// Dispatches to the specific visit method for this MIME message. /// ///that represents the for debugging purposes. - /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -1044,8 +1049,12 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = if (maxLineLength < FormatOptions.MinimumLineLength || maxLineLength > FormatOptions.MaximumLineLength) throw new ArgumentOutOfRangeException (nameof (maxLineLength)); - if (Body != null) + if (Body != null) { + if (MimeVersion == null && Body.Headers.Count > 0) + MimeVersion = new Version (1, 0); + Body.Prepare (constraint, maxLineLength); + } } /// + [Obsolete ("Use DkimSigner.Sign() instead.")] public void Sign (DkimSigner signer, IList@@ -1088,11 +1097,14 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = if (options.HiddenHeaders.Contains (header.Id)) continue; - var rawValue = header.GetRawValue (options); - filtered.Write (header.RawField, 0, header.RawField.Length, cancellationToken); - filtered.Write (Header.Colon, 0, Header.Colon.Length, cancellationToken); - filtered.Write (rawValue, 0, rawValue.Length, cancellationToken); + + if (!header.IsInvalid) { + var rawValue = header.GetRawValue (options); + + filtered.Write (Header.Colon, 0, Header.Colon.Length, cancellationToken); + filtered.Write (rawValue, 0, rawValue.Length, cancellationToken); + } } filtered.Flush (cancellationToken); @@ -1109,7 +1121,7 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = if (!headersOnly) { try { - Body.EnsureNewLine = compliance == RfcComplianceMode.Strict; + Body.EnsureNewLine = compliance == RfcComplianceMode.Strict || options.EnsureNewLine; Body.WriteTo (options, stream, true, cancellationToken); } finally { Body.EnsureNewLine = false; @@ -1124,8 +1136,9 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = /// Asynchronously write the message to the specified output stream. /// ///- /// Writes the message to the output stream using the provided formatting options. + /// Asynchronously writes the message to the output stream using the provided formatting options. /// + ///An awaitable task. /// The formatting options. /// The output stream. ///true if only the headers should be written; otherwise,false . @@ -1160,11 +1173,14 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = if (options.HiddenHeaders.Contains (header.Id)) continue; - var rawValue = header.GetRawValue (options); - await filtered.WriteAsync (header.RawField, 0, header.RawField.Length, cancellationToken).ConfigureAwait (false); - await filtered.WriteAsync (Header.Colon, 0, Header.Colon.Length, cancellationToken).ConfigureAwait (false); - await filtered.WriteAsync (rawValue, 0, rawValue.Length, cancellationToken).ConfigureAwait (false); + + if (!header.IsInvalid) { + var rawValue = header.GetRawValue (options); + + await filtered.WriteAsync (Header.Colon, 0, Header.Colon.Length, cancellationToken).ConfigureAwait (false); + await filtered.WriteAsync (rawValue, 0, rawValue.Length, cancellationToken).ConfigureAwait (false); + } } await filtered.FlushAsync (cancellationToken).ConfigureAwait (false); @@ -1174,7 +1190,7 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = if (!headersOnly) { try { - Body.EnsureNewLine = compliance == RfcComplianceMode.Strict; + Body.EnsureNewLine = compliance == RfcComplianceMode.Strict || options.EnsureNewLine; await Body.WriteToAsync (options, stream, true, cancellationToken).ConfigureAwait (false); } finally { Body.EnsureNewLine = false; @@ -1214,8 +1230,9 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = /// Asynchronously write the message to the specified output stream. /// ///- /// Writes the message to the output stream using the provided formatting options. + /// Asynchronously writes the message to the output stream using the provided formatting options. /// + ///An awaitable task. /// The formatting options. /// The output stream. /// The cancellation token. @@ -1262,8 +1279,9 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = /// Asynchronously write the message to the specified output stream. /// ///- /// Writes the message to the output stream using the default formatting options. + /// Asynchronously writes the message to the output stream using the default formatting options. /// + ///An awaitable task. /// The output stream. ///true if only the headers should be written; otherwise,false . /// The cancellation token. @@ -1307,8 +1325,9 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = /// Asynchronously write the message to the specified output stream. /// ///- /// Writes the message to the output stream using the default formatting options. + /// Asynchronously writes the message to the output stream using the default formatting options. /// + ///An awaitable task. /// The output stream. /// The cancellation token. ///@@ -1325,7 +1344,6 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = return WriteToAsync (FormatOptions.Default, stream, false, cancellationToken); } -#if !PORTABLE /// ////// Write the message to the specified file. /// @@ -1342,8 +1360,7 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -1368,16 +1385,19 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = if (fileName == null) throw new ArgumentNullException (nameof (fileName)); - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) + using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) { WriteTo (options, stream, cancellationToken); + stream.Flush (); + } } /// ////// Asynchronously write the message to the specified file. /// ///- /// Writes the message to the specified file using the provided formatting options. + /// Asynchronously writes the message to the specified file using the provided formatting options. /// + ///An awaitable task. /// The formatting options. /// The file. /// The cancellation token. @@ -1388,8 +1408,7 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -1414,8 +1433,10 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = if (fileName == null) throw new ArgumentNullException (nameof (fileName)); - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) + using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) { await WriteToAsync (options, stream, cancellationToken).ConfigureAwait (false); + await stream.FlushAsync (cancellationToken).ConfigureAwait (false); + } } /// ///@@ -1431,8 +1452,7 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -1451,19 +1471,16 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = /// public void WriteTo (string fileName, CancellationToken cancellationToken = default (CancellationToken)) { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) - WriteTo (FormatOptions.Default, stream, cancellationToken); + WriteTo (FormatOptions.Default, fileName, cancellationToken); } ////// Asynchronously write the message to the specified file. /// ///- /// Writes the message to the specified file using the default formatting options. + /// Asynchronously writes the message to the specified file using the default formatting options. /// + ///An awaitable task. /// The file. /// The cancellation token. ///@@ -1471,8 +1488,7 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = /// ////// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// The operation was canceled via the cancellation token. @@ -1489,15 +1505,10 @@ public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = /// + [Obsolete ("Use DkimSigner.Sign() instead.")] public void Sign (FormatOptions options, DkimSigner signer, IList/// An I/O error occurred. /// - public async Task WriteToAsync (string fileName, CancellationToken cancellationToken = default (CancellationToken)) + public Task WriteToAsync (string fileName, CancellationToken cancellationToken = default (CancellationToken)) { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) - await WriteToAsync (FormatOptions.Default, stream, cancellationToken).ConfigureAwait (false); + return WriteToAsync (FormatOptions.Default, fileName, cancellationToken); } -#endif MailboxAddress GetMessageSigner () { @@ -1560,95 +1571,7 @@ IListGetMessageRecipients (bool includeSenders) } #if ENABLE_CRYPTO - static void DkimWriteHeaderRelaxed (FormatOptions options, Stream stream, Header header, bool isDkimSignature) - { - // o Convert all header field names (not the header field values) to - // lowercase. For example, convert "SUBJect: AbC" to "subject: AbC". - var name = Encoding.ASCII.GetBytes (header.Field.ToLowerInvariant ()); - var rawValue = header.GetRawValue (options); - int index = 0; - - // o Delete any WSP characters remaining before and after the colon - // separating the header field name from the header field value. The - // colon separator MUST be retained. - stream.Write (name, 0, name.Length); - stream.WriteByte ((byte) ':'); - - // trim leading whitespace... - while (index < rawValue.Length && rawValue[index].IsWhitespace ()) - index++; - - while (index < rawValue.Length) { - int startIndex = index; - - // look for the first non-whitespace character - while (index < rawValue.Length && rawValue[index].IsWhitespace ()) - index++; - - // o Delete all WSP characters at the end of each unfolded header field - // value. - if (index >= rawValue.Length) - break; - - // o Convert all sequences of one or more WSP characters to a single SP - // character. WSP characters here include those before and after a - // line folding boundary. - if (index > startIndex) - stream.WriteByte ((byte) ' '); - - startIndex = index; - - while (index < rawValue.Length && !rawValue[index].IsWhitespace ()) - index++; - - if (index > startIndex) - stream.Write (rawValue, startIndex, index - startIndex); - } - - if (!isDkimSignature) - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - - static void DkimWriteHeaderSimple (FormatOptions options, Stream stream, Header header, bool isDkimSignature) - { - var rawValue = header.GetRawValue (options); - int rawLength = rawValue.Length; - - if (isDkimSignature && rawLength > 0) { - if (rawValue[rawLength - 1] == (byte) '\n') { - rawLength--; - - if (rawLength > 0 && rawValue[rawLength - 1] == (byte) '\r') - rawLength--; - } - } - - stream.Write (header.RawField, 0, header.RawField.Length); - stream.Write (Header.Colon, 0, Header.Colon.Length); - stream.Write (rawValue, 0, rawLength); - } - - static ISigner DkimGetDigestSigner (DkimSignatureAlgorithm algorithm, AsymmetricKeyParameter key) - { -#if ENABLE_NATIVE_DKIM - return new SystemSecuritySigner (algorithm, key.AsAsymmetricAlgorithm ()); -#else - DerObjectIdentifier id; - - if (algorithm == DkimSignatureAlgorithm.RsaSha256) - id = PkcsObjectIdentifiers.Sha256WithRsaEncryption; - else - id = PkcsObjectIdentifiers.Sha1WithRsaEncryption; - - var signer = SignerUtilities.GetSigner (id); - - signer.Init (key.IsPrivate, key); - - return signer; -#endif - } - - byte[] DkimHashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength) + internal byte[] HashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength) { using (var stream = new DkimHashStream (signatureAlgorithm, maxLength)) { using (var filtered = new FilteredStream (stream)) { @@ -1664,7 +1587,7 @@ byte[] DkimHashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgo if (Body != null) { try { - Body.EnsureNewLine = compliance == RfcComplianceMode.Strict; + Body.EnsureNewLine = compliance == RfcComplianceMode.Strict || options.EnsureNewLine; Body.WriteTo (options, filtered, true, CancellationToken.None); } finally { Body.EnsureNewLine = false; @@ -1681,118 +1604,6 @@ byte[] DkimHashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgo } } - void DkimWriteHeaders (FormatOptions options, IList fields, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm, Stream stream) - { - var counts = new Dictionary (); - - for (int i = 0; i < fields.Count; i++) { - var headers = fields[i].StartsWith ("Content-", StringComparison.OrdinalIgnoreCase) ? Body.Headers : Headers; - var name = fields[i].ToLowerInvariant (); - int index, count, n = 0; - - if (!counts.TryGetValue (name, out count)) - count = 0; - - // Note: signers choosing to sign an existing header field that occurs more - // than once in the message (such as Received) MUST sign the physically last - // instance of that header field in the header block. Signers wishing to sign - // multiple instances of such a header field MUST include the header field - // name multiple times in the list of header fields and MUST sign such header - // fields in order from the bottom of the header field block to the top. - index = headers.LastIndexOf (name); - - // find the n'th header with this name - while (n < count && --index >= 0) { - if (headers[index].Field.Equals (name, StringComparison.OrdinalIgnoreCase)) - n++; - } - - if (index < 0) - continue; - - var header = headers[index]; - - switch (headerCanonicalizationAlgorithm) { - case DkimCanonicalizationAlgorithm.Relaxed: - DkimWriteHeaderRelaxed (options, stream, header, false); - break; - default: - DkimWriteHeaderSimple (options, stream, header, false); - break; - } - - counts[name] = ++count; - } - } - - static readonly string[] DkimShouldNotInclude = { "return-path", "received", "comments", "keywords", "bcc", "resent-bcc", "dkim-signature" }; - - void DkimSign (FormatOptions options, DkimSigner signer, IList fields, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm) - { - if (version == null && Body != null && Body.Headers.Count > 0) - MimeVersion = new Version (1, 0); - - var t = DateTime.UtcNow - DateUtils.UnixEpoch; - var value = new StringBuilder ("v=1"); - byte[] signature, hash; - Header dkim; - - options = options.Clone (); - options.NewLineFormat = NewLineFormat.Dos; - - switch (signer.SignatureAlgorithm) { - case DkimSignatureAlgorithm.RsaSha256: - value.Append ("; a=rsa-sha256"); - break; - default: - value.Append ("; a=rsa-sha1"); - break; - } - - value.AppendFormat ("; d={0}; s={1}", signer.Domain, signer.Selector); - value.AppendFormat ("; c={0}/{1}", - headerCanonicalizationAlgorithm.ToString ().ToLowerInvariant (), - bodyCanonicalizationAlgorithm.ToString ().ToLowerInvariant ()); - if (!string.IsNullOrEmpty (signer.QueryMethod)) - value.AppendFormat ("; q={0}", signer.QueryMethod); - if (!string.IsNullOrEmpty (signer.AgentOrUserIdentifier)) - value.AppendFormat ("; i={0}", signer.AgentOrUserIdentifier); - value.AppendFormat ("; t={0}", (long) t.TotalSeconds); - - using (var stream = new DkimSignatureStream (signer.DigestSigner)) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - // write the specified message headers - DkimWriteHeaders (options, fields, headerCanonicalizationAlgorithm, filtered); - - value.AppendFormat ("; h={0}", string.Join (":", fields.ToArray ())); - - hash = DkimHashBody (options, signer.SignatureAlgorithm, bodyCanonicalizationAlgorithm, -1); - value.AppendFormat ("; bh={0}", Convert.ToBase64String (hash)); - value.Append ("; b="); - - dkim = new Header (HeaderId.DkimSignature, value.ToString ()); - Headers.Insert (0, dkim); - - switch (headerCanonicalizationAlgorithm) { - case DkimCanonicalizationAlgorithm.Relaxed: - DkimWriteHeaderRelaxed (options, filtered, dkim, true); - break; - default: - DkimWriteHeaderSimple (options, filtered, dkim, true); - break; - } - - filtered.Flush (); - } - - signature = stream.GenerateSignature (); - - dkim.Value += Convert.ToBase64String (signature); - } - } - /// /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. /// @@ -1820,6 +1631,7 @@ void DkimSign (FormatOptions options, DkimSigner signer, IListfields, D /// /// contains one or more of the following headers: Return-Path, /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) { if (options == null) @@ -1828,32 +1640,10 @@ public void Sign (FormatOptions options, DkimSigner signer, IList header if (signer == null) throw new ArgumentNullException (nameof (signer)); - if (headers == null) - throw new ArgumentNullException (nameof (headers)); - - var fields = new string[headers.Count]; - var containsFrom = false; - - for (int i = 0; i < headers.Count; i++) { - if (headers[i] == null) - throw new ArgumentException ("The list of headers cannot contain null.", nameof (headers)); + signer.HeaderCanonicalizationAlgorithm = headerCanonicalizationAlgorithm; + signer.BodyCanonicalizationAlgorithm = bodyCanonicalizationAlgorithm; - if (headers[i].Length == 0) - throw new ArgumentException ("The list of headers cannot contain empty string.", nameof (headers)); - - fields[i] = headers[i].ToLowerInvariant (); - - if (DkimShouldNotInclude.Contains (fields[i])) - throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i])); - - if (fields[i] == "from") - containsFrom = true; - } - - if (!containsFrom) - throw new ArgumentException ("The list of headers to sign MUST include the 'From' header."); - - DkimSign (options, signer, fields, headerCanonicalizationAlgorithm, bodyCanonicalizationAlgorithm); + signer.Sign (options, this, headers); } /// @@ -1880,6 +1670,7 @@ public void Sign (FormatOptions options, DkimSigner signer, IList header /// /// contains one or more of the following headers: Return-Path, /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) { Sign (FormatOptions.Default, signer, headers, headerCanonicalizationAlgorithm, bodyCanonicalizationAlgorithm); @@ -1912,6 +1703,7 @@ public void Sign (DkimSigner signer, IList headers, DkimCanonicalization /// /// contains one or more of the following headers: Return-Path, /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) { if (options == null) @@ -1920,29 +1712,10 @@ public void Sign (FormatOptions options, DkimSigner signer, IList head if (signer == null) throw new ArgumentNullException (nameof (signer)); - if (headers == null) - throw new ArgumentNullException (nameof (headers)); - - var fields = new string[headers.Count]; - var containsFrom = false; - - for (int i = 0; i < headers.Count; i++) { - if (headers[i] == HeaderId.Unknown) - throw new ArgumentException ("The list of headers to sign cannot include the 'Unknown' header."); + signer.HeaderCanonicalizationAlgorithm = headerCanonicalizationAlgorithm; + signer.BodyCanonicalizationAlgorithm = bodyCanonicalizationAlgorithm; - fields[i] = headers[i].ToHeaderName ().ToLowerInvariant (); - - if (DkimShouldNotInclude.Contains (fields[i])) - throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i].ToHeaderName ())); - - if (headers[i] == HeaderId.From) - containsFrom = true; - } - - if (!containsFrom) - throw new ArgumentException ("The list of headers to sign MUST include the 'From' header."); - - DkimSign (options, signer, fields, headerCanonicalizationAlgorithm, bodyCanonicalizationAlgorithm); + signer.Sign (options, this, headers); } /// @@ -1969,203 +1742,13 @@ public void Sign (FormatOptions options, DkimSigner signer, IList head /// /// contains one or more of the following headers: Return-Path, /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) { Sign (FormatOptions.Default, signer, headers, headerCanonicalizationAlgorithm, bodyCanonicalizationAlgorithm); } - static bool IsWhiteSpace (char c) - { - return c == ' ' || c == '\t'; - } - - static IDictionary ParseDkimSignature (string signature) - { - var parameters = new Dictionary (); - - foreach (var token in signature.Split (';')) { - var value = new StringBuilder (); - int startIndex, index = 0; - string name; - - while (index < token.Length && IsWhiteSpace (token[index])) - index++; - - startIndex = index; - - while (index < token.Length && token[index] != '=') - index++; - - if (index + 1 >= token.Length) - continue; - - name = token.Substring (startIndex, index - startIndex).Trim (); - index++; - - while (index < token.Length) { - if (!IsWhiteSpace (token[index])) - value.Append (token[index]); - index++; - } - - if (parameters.ContainsKey (name)) - throw new FormatException (string.Format ("Malformed DKIM-Signature value: duplicate parameter '{0}'.", name)); - - parameters.Add (name, value.ToString ()); - } - - return parameters; - } - - static void ValidateDkimSignatureParameters (IDictionary parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, - out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) - { - bool containsFrom = false; - string v, a, c, h, l, id; - - if (!parameters.TryGetValue ("v", out v)) - throw new FormatException ("Malformed DKIM-Signature header: no version parameter detected."); - - if (v != "1") - throw new FormatException (string.Format ("Unrecognized DKIM-Signature version: v={0}", v)); - - if (!parameters.TryGetValue ("a", out a)) - throw new FormatException ("Malformed DKIM-Signature header: no signature algorithm parameter detected."); - - switch (a.ToLowerInvariant ()) { - case "rsa-sha256": algorithm = DkimSignatureAlgorithm.RsaSha256; break; - case "rsa-sha1": algorithm = DkimSignatureAlgorithm.RsaSha1; break; - default: throw new FormatException (string.Format ("Unrecognized DKIM-Signature algorithm parameter: a={0}", a)); - } - - if (!parameters.TryGetValue ("d", out d)) - throw new FormatException ("Malformed DKIM-Signature header: no domain parameter detected."); - - if (parameters.TryGetValue ("i", out id)) { - string ident; - int at; - - if ((at = id.LastIndexOf ('@')) == -1) - throw new FormatException ("Malformed DKIM-Signature header: no @ in the AUID value."); - - ident = id.Substring (at + 1); - - if (!ident.Equals (d, StringComparison.OrdinalIgnoreCase) && !ident.EndsWith ("." + d, StringComparison.OrdinalIgnoreCase)) - throw new FormatException ("Invalid DKIM-Signature header: the domain in the AUID does not match the domain parameter."); - } - - if (!parameters.TryGetValue ("s", out s)) - throw new FormatException ("Malformed DKIM-Signature header: no selector parameter detected."); - - if (!parameters.TryGetValue ("q", out q)) - q = "dns/txt"; - - if (parameters.TryGetValue ("l", out l)) { - if (!int.TryParse (l, out maxLength)) - throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid length parameter: l={0}", l)); - } else { - maxLength = -1; - } - - if (parameters.TryGetValue ("c", out c)) { - var tokens = c.ToLowerInvariant ().Split ('/'); - - if (tokens.Length == 0 || tokens.Length > 2) - throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid canonicalization parameter: c={0}", c)); - - switch (tokens[0]) { - case "relaxed": headerAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; - case "simple": headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; - default: throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid canonicalization parameter: c={0}", c)); - } - - if (tokens.Length == 2) { - switch (tokens[1]) { - case "relaxed": bodyAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; - case "simple": bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; - default: throw new FormatException (string.Format ("Malformed DKIM-Signature header: invalid canonicalization parameter: c={0}", c)); - } - } else { - bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; - } - } else { - headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; - bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; - } - - if (!parameters.TryGetValue ("h", out h)) - throw new FormatException ("Malformed DKIM-Signature header: no signed header parameter detected."); - - headers = h.Split (':'); - for (int i = 0; i < headers.Length; i++) { - if (headers[i].Equals ("from", StringComparison.OrdinalIgnoreCase)) { - containsFrom = true; - break; - } - } - - if (!containsFrom) - throw new FormatException (string.Format ("Malformed DKIM-Signature header: From header not signed.")); - - if (!parameters.TryGetValue ("bh", out bh)) - throw new FormatException ("Malformed DKIM-Signature header: no body hash parameter detected."); - - if (!parameters.TryGetValue ("b", out b)) - throw new FormatException ("Malformed DKIM-Signature header: no signature parameter detected."); - } - - static Header GetSignedDkimSignatureHeader (Header dkimSignature) - { - // modify the raw DKIM-Signature header value by chopping off the signature value after the "b=" - var rawValue = (byte[]) dkimSignature.RawValue.Clone (); - int length = 0, index = 0; - - do { - while (index < rawValue.Length && rawValue[index].IsWhitespace ()) - index++; - - if (index + 2 < rawValue.Length) { - var param = (char) rawValue[index++]; - - while (index < rawValue.Length && rawValue[index].IsWhitespace ()) - index++; - - if (index < rawValue.Length && rawValue[index] == (byte) '=' && param == 'b') { - length = ++index; - - while (index < rawValue.Length && rawValue[index] != (byte) ';') - index++; - - if (index == rawValue.Length && rawValue[index - 1] == (byte) '\n') { - index--; - - if (rawValue[index - 1] == (byte) '\r') - index--; - } - - break; - } - } - - while (index < rawValue.Length && rawValue[index] != (byte) ';') - index++; - - if (index < rawValue.Length) - index++; - } while (index < rawValue.Length); - - if (index == rawValue.Length) - throw new FormatException ("Malformed DKIM-Signature header: missing signature parameter."); - - while (index < rawValue.Length) - rawValue[length++] = rawValue[index++]; - - Array.Resize (ref rawValue, length); - - return new Header (dkimSignature.Options, dkimSignature.RawField, rawValue); - } - - async Task VerifyAsync (FormatOptions options, Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, bool doAsync, CancellationToken cancellationToken) + Task DkimVerifyAsync (FormatOptions options, Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, bool doAsync, CancellationToken cancellationToken) { if (options == null) throw new ArgumentNullException (nameof (options)); @@ -2174,60 +1757,14 @@ async Task VerifyAsync (FormatOptions options, Header dkimSignature, IDkim throw new ArgumentNullException (nameof (dkimSignature)); if (dkimSignature.Id != HeaderId.DkimSignature) - throw new ArgumentException ("The dkimSignature parameter MUST be a DKIM-Signature header.", nameof (dkimSignature)); - - if (publicKeyLocator == null) - throw new ArgumentNullException (nameof (publicKeyLocator)); + throw new ArgumentException ("The signature parameter MUST be a DKIM-Signature header.", nameof (dkimSignature)); - var parameters = ParseDkimSignature (dkimSignature.Value); - DkimCanonicalizationAlgorithm headerAlgorithm, bodyAlgorithm; - DkimSignatureAlgorithm signatureAlgorithm; - AsymmetricKeyParameter key; - string d, s, q, bh, b; - string[] headers; - int maxLength; - - ValidateDkimSignatureParameters (parameters, out signatureAlgorithm, out headerAlgorithm, out bodyAlgorithm, - out d, out s, out q, out headers, out bh, out b, out maxLength); + var verifier = new DkimVerifier (publicKeyLocator); if (doAsync) - key = await publicKeyLocator.LocatePublicKeyAsync (q, d, s, cancellationToken).ConfigureAwait (false); - else - key = publicKeyLocator.LocatePublicKey (q, d, s, cancellationToken); - - options = options.Clone (); - options.NewLineFormat = NewLineFormat.Dos; - - // first check the body hash (if that's invalid, then the entire signature is invalid) - var hash = Convert.ToBase64String (DkimHashBody (options, signatureAlgorithm, bodyAlgorithm, maxLength)); - - if (hash != bh) - return false; - - using (var stream = new DkimSignatureStream (DkimGetDigestSigner (signatureAlgorithm, key))) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - DkimWriteHeaders (options, headers, headerAlgorithm, filtered); - - // now include the DKIM-Signature header that we are verifying, - // but only after removing the "b=" signature value. - var header = GetSignedDkimSignatureHeader (dkimSignature); + return verifier.VerifyAsync (options, this, dkimSignature, cancellationToken); - switch (headerAlgorithm) { - case DkimCanonicalizationAlgorithm.Relaxed: - DkimWriteHeaderRelaxed (options, filtered, header, true); - break; - default: - DkimWriteHeaderSimple (options, filtered, header, true); - break; - } - - filtered.Flush (); - } - - return stream.VerifySignature (b); - } + return Task.FromResult (verifier.Verify (options, this, dkimSignature, cancellationToken)); } /// @@ -2260,9 +1797,10 @@ async Task VerifyAsync (FormatOptions options, Header dkimSignature, IDkim /// /// The operation was canceled via the cancellation token. /// + [Obsolete ("Use the DkimVerifier class instead.")] public bool Verify (FormatOptions options, Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) { - return VerifyAsync (options, dkimSignature, publicKeyLocator, false, cancellationToken).GetAwaiter ().GetResult (); + return DkimVerifyAsync (options, dkimSignature, publicKeyLocator, false, cancellationToken).GetAwaiter ().GetResult (); } ///@@ -2295,9 +1833,10 @@ async Task VerifyAsync (FormatOptions options, Header dkimSignature, IDkim /// /// The operation was canceled via the cancellation token. /// + [Obsolete ("Use the DkimVerifier class instead.")] public TaskVerifyAsync (FormatOptions options, Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) { - return VerifyAsync (options, dkimSignature, publicKeyLocator, true, cancellationToken); + return DkimVerifyAsync (options, dkimSignature, publicKeyLocator, true, cancellationToken); } /// @@ -2327,6 +1866,7 @@ async Task VerifyAsync (FormatOptions options, Header dkimSignature, IDkim /// /// The operation was canceled via the cancellation token. /// + [Obsolete ("Use the DkimVerifier class instead.")] public bool Verify (Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) { return Verify (FormatOptions.Default, dkimSignature, publicKeyLocator, cancellationToken); @@ -2359,6 +1899,7 @@ async TaskVerifyAsync (FormatOptions options, Header dkimSignature, IDkim /// /// The operation was canceled via the cancellation token. /// + [Obsolete ("Use the DkimVerifier class instead.")] public TaskVerifyAsync (Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) { return VerifyAsync (FormatOptions.Default, dkimSignature, publicKeyLocator, cancellationToken); @@ -2605,6 +2146,17 @@ IEnumerable MergeHeaders () { int mesgIndex = 0, bodyIndex = 0; + // write all of the prepended message headers first + while (mesgIndex < Headers.Count) { + var mesgHeader = Headers[mesgIndex]; + if (mesgHeader.Offset.HasValue) + break; + + yield return mesgHeader; + mesgIndex++; + } + + // now merge the message and body headers as they appeared in the raw message while (mesgIndex < Headers.Count && bodyIndex < Body.Headers.Count) { var bodyHeader = Body.Headers[bodyIndex]; if (!bodyHeader.Offset.HasValue) @@ -2663,32 +2215,33 @@ void SetHeader (string name, string value) } } - void SerializeAddressList (string field, InternetAddressList list) + void SerializeAddressList (HeaderId id, InternetAddressList list) { if (list.Count == 0) { - RemoveHeader (field.ToHeaderId ()); + RemoveHeader (id); return; } var builder = new StringBuilder (" "); var options = FormatOptions.Default; + var field = id.ToHeaderName (); int lineLength = field.Length + 2; - list.Encode (options, builder, ref lineLength); + list.Encode (options, builder, true, ref lineLength); builder.Append (options.NewLine); var raw = Encoding.UTF8.GetBytes (builder.ToString ()); - ReplaceHeader (field.ToHeaderId (), field, raw); + ReplaceHeader (id, field, raw); } void InternetAddressListChanged (object addrlist, EventArgs e) { var list = (InternetAddressList) addrlist; - foreach (var name in StandardAddressHeaders) { - if (addresses[name] == list) { - SerializeAddressList (name, list); + foreach (var id in StandardAddressHeaders) { + if (addresses[id] == list) { + SerializeAddressList (id, list); break; } } @@ -2880,7 +2433,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) switch (e.Action) { case HeaderListChangedAction.Added: - if (addresses.TryGetValue (e.Header.Field, out list)) { + if (addresses.TryGetValue (e.Header.Id, out list)) { AddAddresses (e.Header, list); break; } @@ -2945,7 +2498,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) break; case HeaderListChangedAction.Changed: case HeaderListChangedAction.Removed: - if (addresses.TryGetValue (e.Header.Field, out list)) { + if (addresses.TryGetValue (e.Header.Id, out list)) { ReloadAddressList (e.Header.Id, list); break; } @@ -2987,7 +2540,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) /// specified . /// If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save mmeory usage, but also improve /// performance. . /// If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save mmeory usage, but also improve /// performance. . /// If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save mmeory usage, but also improve /// performance. . /// If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save mmeory usage, but also improve /// performance. /// Load a @@ -3266,8 +2818,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) ///from the specified file. /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// ///is an invalid file path. @@ -3295,7 +2846,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) if (fileName == null) throw new ArgumentNullException (nameof (fileName)); - using (var stream = File.Open (fileName, FileMode.Open, FileAccess.Read)) + using (var stream = File.OpenRead (fileName)) return Load (options, stream, cancellationToken); } @@ -3317,8 +2868,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// ///is an invalid file path. @@ -3346,7 +2896,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) if (fileName == null) throw new ArgumentNullException (nameof (fileName)); - using (var stream = File.Open (fileName, FileMode.Open, FileAccess.Read)) + using (var stream = File.OpenRead (fileName)) return await LoadAsync (options, stream, cancellationToken).ConfigureAwait (false); } @@ -3365,8 +2915,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// ///is an invalid file path. @@ -3406,8 +2955,7 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) /// /// ///is a zero-length string, contains only white space, or - /// contains one or more invalid characters as defined by - /// . + /// contains one or more invalid characters. /// /// public MimeParser (ParserOptions options, Stream stream, MimeFormat format, bool persistent = false) { - bounds = new Listis an invalid file path. @@ -3431,7 +2979,6 @@ void HeadersChanged (object o, HeaderListChangedEventArgs e) { return LoadAsync (ParserOptions.Default, fileName, cancellationToken); } -#endif // !PORTABLE #if ENABLE_SNM static MimePart GetMimePart (AttachmentBase item) @@ -3470,6 +3017,8 @@ static MimePart GetMimePart (AttachmentBase item) part.ContentId = item.ContentId; var stream = new MemoryBlockStream (); + if (item.ContentStream.CanSeek) + item.ContentStream.Position = 0; item.ContentStream.CopyTo (stream); stream.Position = 0; @@ -3539,6 +3088,9 @@ public static MimeMessage CreateFromMailMessage (MailMessage message) else msg.Subject = message.Subject ?? string.Empty; + if (!msg.Headers.Contains (HeaderId.Date)) + msg.Date = DateTimeOffset.Now; + switch (message.Priority) { case MailPriority.Normal: msg.Headers.RemoveAll (HeaderId.XMSMailPriority); diff --git a/MimeKit/MimeMessageBeginEventArgs.cs b/MimeKit/MimeMessageBeginEventArgs.cs new file mode 100644 index 0000000000..ce3c283b7f --- /dev/null +++ b/MimeKit/MimeMessageBeginEventArgs.cs @@ -0,0 +1,117 @@ +// +// MimeMessageBeginEventArgs.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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; + +namespace MimeKit { + /// + /// Event args emitted by the + ///when it begins parsing a . + /// + /// Event args emitted by the + public class MimeMessageBeginEventArgs : EventArgs + { + ///when it begins parsing a . + /// + /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The message that was parsed. + ///. + /// + /// + public MimeMessageBeginEventArgs (MimeMessage message) + { + if (message == null) + throw new ArgumentNullException (nameof (message)); + + Message = message; + } + + ///is null . + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The message that was parsed. + /// The parent message part. + ///. + /// + /// + public MimeMessageBeginEventArgs (MimeMessage message, MessagePart parent) + { + if (message == null) + throw new ArgumentNullException (nameof (message)); + + if (parent == null) + throw new ArgumentNullException (nameof (parent)); + + Message = message; + Parent = parent; + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Get the message that was parsed. + /// + ///+ /// Gets the message that was parsed. + /// + ///The message. + public MimeMessage Message { get; } + + ///+ /// Get the parent + ///if this message is an attachment. + /// + /// Gets the parent + ///if this message is an attachment. + /// The parent + public MessagePart Parent { get; } + + ///. + /// Get or set the stream offset that marks the beginning of the message. + /// + ///+ /// Gets or sets the stream offset that marks the beginning of the message. + /// + ///The stream offset. + public long BeginOffset { get; set; } + + ///+ /// Get or set the line number of the beginning of the message. + /// + ///+ /// Gets or sets the line number of the beginning of the message. + /// + ///The line number. + internal int LineNumber { get; set; } + } +} diff --git a/MimeKit/MimeMessageEndEventArgs.cs b/MimeKit/MimeMessageEndEventArgs.cs new file mode 100644 index 0000000000..dfceaaa7b2 --- /dev/null +++ b/MimeKit/MimeMessageEndEventArgs.cs @@ -0,0 +1,87 @@ +// +// MimeMessageEndEventArgs.cs +// +// Author: Jeffrey Stedfast+// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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; + +namespace MimeKit { + /// + /// Event args emitted by the + ///when a is parsed. + /// + /// Event args emitted by the + public class MimeMessageEndEventArgs : MimeMessageBeginEventArgs + { + ///when a is parsed. + /// + /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The message that was parsed. + ///. + /// + /// + public MimeMessageEndEventArgs (MimeMessage message) : base (message) + { + } + + ///is null . + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// The message that was parsed. + /// The parent message part. + ///. + /// + /// + public MimeMessageEndEventArgs (MimeMessage message, MessagePart parent) : base (message, parent) + { + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Get or set the stream offset that marks the end of the message headers. + /// + ///+ /// Gets or sets the stream offset that marks the end of the message headers. + /// + ///The stream offset. + public long HeadersEndOffset { get; set; } + + ///+ /// Get or set the stream offset that marks the end of the message. + /// + ///+ /// Gets or sets the stream offset that marks the end of the message. + /// + ///The stream offset. + public long EndOffset { get; set; } + } +} diff --git a/MimeKit/MimeParser.cs b/MimeKit/MimeParser.cs index 8dae1319fd..53374aa77b 100644 --- a/MimeKit/MimeParser.cs +++ b/MimeKit/MimeParser.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,12 +32,8 @@ using System.Collections; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - -using MimeKit.Utils; using MimeKit.IO; +using MimeKit.Utils; namespace MimeKit { enum BoundaryType @@ -58,7 +54,6 @@ class Boundary public int FinalLength { get { return Marker.Length; } } public int Length { get; private set; } public int MaxLength { get; private set; } - public long ContentEnd { get; set; } public Boundary (string boundary, int currentMaxLength) { @@ -66,7 +61,6 @@ public Boundary (string boundary, int currentMaxLength) Length = Marker.Length - 2; MaxLength = Math.Max (currentMaxLength, Marker.Length); - ContentEnd = -1; } Boundary () @@ -77,16 +71,17 @@ public static Boundary CreateMboxBoundary () { var boundary = new Boundary (); boundary.Marker = MboxFrom; - boundary.ContentEnd = -1; boundary.MaxLength = 5; boundary.Length = 5; return boundary; } +#if DEBUG_PARSER public override string ToString () { return Encoding.UTF8.GetString (Marker, 0, Marker.Length); } +#endif } enum MimeParserState : sbyte @@ -97,6 +92,7 @@ enum MimeParserState : sbyte MessageHeaders, Headers, Content, + Boundary, Complete, Eos } @@ -105,8 +101,8 @@ enum MimeParserState : sbyte /// A MIME message and entity parser. /// /// - /// A MIME parser is used to parse public partial class MimeParser : IEnumerableand - /// objects from arbitrary streams. + /// A MIME parser is used to parse and + /// objects from arbitrary streams. /// { @@ -135,26 +131,36 @@ public partial class MimeParser : IEnumerable long headerOffset; int headerIndex; - readonly List bounds; - readonly List headers; + readonly List bounds = new List (); + readonly List headers = new List (); MimeParserState state; + BoundaryType boundary; MimeFormat format; bool persistent; + bool toplevel; bool eos; - ParserOptions options; + ParserOptions options; // FIXME: might be better if devs passed ParserOptions into the Parse*() methods rather than .ctor and/or SetStream() + long headerBlockBegin; + long headerBlockEnd; + long contentEnd; + + long prevLineBeginOffset; + long lineBeginOffset; + int lineNumber; + Stream stream; - long offset; + long position; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///that will parse the specified stream. If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save memory usage, but also improve /// performance. It should be noted, however, that disposing will make it impossible @@ -171,13 +177,13 @@ public MimeParser (Stream stream, MimeFormat format, bool persistent = false) : } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///that will parse the specified stream. If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save memory usage, but also improve /// performance. It should be noted, however, that disposing will make it impossible @@ -193,13 +199,13 @@ public MimeParser (Stream stream, bool persistent = false) : this (ParserOptions } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///that will parse the specified stream. If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save memory usage, but also improve /// performance. It should be noted, however, that disposing will make it impossible @@ -218,13 +224,13 @@ public MimeParser (ParserOptions options, Stream stream, bool persistent = false } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///that will parse the specified stream. If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save memory usage, but also improve /// performance. It should be noted, however, that disposing will make it impossible @@ -241,9 +247,6 @@ public MimeParser (ParserOptions options, Stream stream, bool persistent = false /// (); - headers = new List (); - SetStream (options, stream, format, persistent); } @@ -267,7 +270,7 @@ public bool IsEndOfStream { /// The stream offset. public long Position { - get { return GetOffset (-1); } + get { return GetOffset (inputIndex); } } ///@@ -299,7 +302,7 @@ public string MboxMarker { /// Sets the stream to parse. ///If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save memory usage, but also improve /// performance. It should be noted, however, that disposing will make it impossible @@ -332,11 +335,19 @@ public void SetStream (ParserOptions options, Stream stream, MimeFormat format, mboxMarkerOffset = 0; mboxMarkerLength = 0; - - offset = stream.CanSeek ? stream.Position : 0; + headerBlockBegin = 0; + headerBlockEnd = 0; + lineNumber = 1; + contentEnd = 0; + + position = stream.CanSeek ? stream.Position : 0; + prevLineBeginOffset = position; + lineBeginOffset = position; + preHeaderLength = 0; headers.Clear (); headerOffset = 0; headerIndex = 0; + toplevel = false; eos = false; bounds.Clear (); @@ -348,6 +359,7 @@ public void SetStream (ParserOptions options, Stream stream, MimeFormat format, } state = MimeParserState.Initialized; + boundary = BoundaryType.None; } /// @@ -357,7 +369,7 @@ public void SetStream (ParserOptions options, Stream stream, MimeFormat format, /// Sets the stream to parse. ///If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save memory usage, but also improve /// performance. It should be noted, however, that disposing will make it impossible @@ -383,7 +395,7 @@ public void SetStream (ParserOptions options, Stream stream, bool persistent = f /// Sets the stream to parse. ///If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save memory usage, but also improve /// performance. It should be noted, however, that disposing will make it impossible @@ -407,7 +419,7 @@ public void SetStream (Stream stream, MimeFormat format, bool persistent = false /// Sets the stream to parse. ///If ///is true andis seekable, then /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . + /// it will use a to reference a substream of . /// This has the potential to not only save memory usage, but also improve /// performance. It should be noted, however, that disposing will make it impossible @@ -423,7 +435,87 @@ public void SetStream (Stream stream, bool persistent = false) SetStream (ParserOptions.Default, stream, MimeFormat.Default, persistent); } -#if DEBUG + /// + /// An event signifying the beginning of a new + ///has been encountered. + /// + /// An event signifying the beginning of a new + public event EventHandlerhas been encountered. + /// MimeMessageBegin; + + /// + /// Invoked when the parser begins parsing a + ///. + /// + /// Invoked when the parser begins parsing a + /// The parsed state. + protected virtual void OnMimeMessageBegin (MimeMessageBeginEventArgs args) + { + MimeMessageBegin?.Invoke (this, args); + } + + ///. + /// + /// An event signifying the end of a + ///has been encountered. + /// + /// An event signifying the end of a + public event EventHandlerhas been encountered. + /// MimeMessageEnd; + + /// + /// Invoked when the parser has completed parsing a + ///. + /// + /// Invoked when the parser has completed parsing a + /// The parsed state. + protected virtual void OnMimeMessageEnd (MimeMessageEndEventArgs args) + { + MimeMessageEnd?.Invoke (this, args); + } + + ///. + /// + /// An event signifying the beginning of a new + ///has been encountered. + /// + /// An event signifying the beginning of a new + public event EventHandlerhas been encountered. + /// MimeEntityBegin; + + /// + /// Invoked when the parser begins parsing a + ///. + /// + /// Invoked when the parser begins parsing a + /// The parsed state. + protected virtual void OnMimeEntityBegin (MimeEntityBeginEventArgs args) + { + MimeEntityBegin?.Invoke (this, args); + } + + ///. + /// + /// An event signifying the end of a + ///has been encountered. + /// + /// An event signifying the end of a + public event EventHandlerhas been encountered. + /// MimeEntityEnd; + + /// + /// Invoked when the parser has completed parsing a + ///. + /// + /// Invoked when the parser has completed parsing a + /// The parsed state. + protected virtual void OnMimeEntityEnd (MimeEntityEndEventArgs args) + { + MimeEntityEnd?.Invoke (this, args); + } + +#if DEBUG_PARSER static string ConvertToCString (byte[] buffer, int startIndex, int length) { var cstr = new StringBuilder (); @@ -497,7 +589,7 @@ int ReadAhead (int atleast, int save, CancellationToken cancellationToken) if (nread > 0) { inputEnd += nread; - offset += nread; + position += nread; } else { eos = true; } @@ -507,13 +599,35 @@ int ReadAhead (int atleast, int save, CancellationToken cancellationToken) long GetOffset (int index) { - if (offset == -1) + if (position == -1) return -1; - if (index == -1) - index = inputIndex; + return position - (inputEnd - index); + } + + long GetEndOffset (int index) + { + if (boundary != BoundaryType.Eos && index > 1 && input[index - 1] == (byte) '\n') { + index--; + + if (index > 1 && input[index - 1] == (byte) '\r') + index--; + } + + return GetOffset (index); + } + + int GetLineCount (int beginLineNumber, long beginOffset, long endOffset) + { + var lines = lineNumber - beginLineNumber; - return offset - (inputEnd - index); + if (lineBeginOffset >= beginOffset && endOffset > lineBeginOffset) + lines++; + + if (boundary != BoundaryType.Eos && endOffset == prevLineBeginOffset) + lines--; + + return lines; } static unsafe bool CStringsEqual (byte* str1, byte* str2, int length) @@ -580,7 +694,7 @@ static unsafe bool IsMboxMarker (byte* text, bool allowMunged = false) #endif } - unsafe void StepMboxMarker (byte *inbuf, ref bool needInput, ref bool complete, ref int left) + unsafe bool StepMboxMarker (byte *inbuf, ref int left) { byte* inptr = inbuf + inputIndex; byte* inend = inbuf + inputEnd; @@ -588,53 +702,55 @@ unsafe void StepMboxMarker (byte *inbuf, ref bool needInput, ref bool complete, *inend = (byte) '\n'; while (inptr < inend) { + int startIndex = inputIndex; byte* start = inptr; // scan for the end of the line while (*inptr != (byte) '\n') inptr++; - long length = inptr - start; + var markerLength = (int) (inptr - start); if (inptr > start && *(inptr - 1) == (byte) '\r') - length--; + markerLength--; // consume the '\n' inptr++; + var lineLength = (int) (inptr - start); + if (inptr >= inend) { // we don't have enough input data - inputIndex = (int) (start - inbuf); - left = (int) (inptr - start); - needInput = true; - break; + left = lineLength; + return false; } - if (length >= 5 && IsMboxMarker (start)) { - int startIndex = (int) (start - inbuf); + inputIndex += lineLength; + prevLineBeginOffset = lineBeginOffset; + lineBeginOffset = GetOffset (inputIndex); + lineNumber++; + if (markerLength >= 5 && IsMboxMarker (start)) { mboxMarkerOffset = GetOffset (startIndex); - mboxMarkerLength = (int) length; + mboxMarkerLength = markerLength; if (mboxMarkerBuffer.Length < mboxMarkerLength) Array.Resize (ref mboxMarkerBuffer, mboxMarkerLength); - Buffer.BlockCopy (input, startIndex, mboxMarkerBuffer, 0, (int) length); - complete = true; - break; + Buffer.BlockCopy (input, startIndex, mboxMarkerBuffer, 0, markerLength); + + return true; } } - if (!needInput) { - inputIndex = (int) (inptr - inbuf); - left = 0; - } + left = 0; + + return false; } unsafe void StepMboxMarker (byte* inbuf, CancellationToken cancellationToken) { - bool complete = false; - bool needInput; + bool complete; int left = 0; mboxMarkerLength = 0; @@ -649,9 +765,7 @@ unsafe void StepMboxMarker (byte* inbuf, CancellationToken cancellationToken) return; } - needInput = false; - - StepMboxMarker (inbuf, ref needInput, ref complete, ref left); + complete = StepMboxMarker (inbuf, ref left); } while (!complete); state = MimeParserState.MessageHeaders; @@ -680,19 +794,11 @@ unsafe void ParseAndAppendHeader () return; fixed (byte* buf = headerBuffer) { - Header header; - - if (!Header.TryParse (options, buf, headerIndex, false, out header)) { -#if DEBUG - Debug.WriteLine (string.Format ("Invalid header at offset {0}: {1}", headerOffset, ConvertToCString (headerBuffer, 0, headerIndex))); -#endif + if (Header.TryParse (options, buf, headerIndex, false, out var header)) { + header.Offset = headerOffset; + headers.Add (header); headerIndex = 0; - return; } - - header.Offset = headerOffset; - headers.Add (header); - headerIndex = 0; } } @@ -760,7 +866,7 @@ unsafe bool StepHeaders (byte* inbuf, ref bool scanningFieldName, ref bool check } if (inptr == inend) { - // we don't have enough input data + // we don't have enough input data; restore state back to the beginning of the line left = (int) (inend - start); inputIndex = (int) (start - inbuf); needInput = true; @@ -775,7 +881,7 @@ unsafe bool StepHeaders (byte* inbuf, ref bool scanningFieldName, ref bool check if (!valid) { length = inptr - start; - if (format == MimeFormat.Mbox && length >= 5 && IsMboxMarker (start)) { + if (format == MimeFormat.Mbox && inputIndex >= contentEnd && length >= 5 && IsMboxMarker (start)) { // we've found the start of the next message... inputIndex = (int) (start - inbuf); state = MimeParserState.Complete; @@ -783,10 +889,17 @@ unsafe bool StepHeaders (byte* inbuf, ref bool scanningFieldName, ref bool check return false; } - if (state == MimeParserState.MessageHeaders && headers.Count == 0) { - // ignore From-lines that might appear at the start of a message - if (length < 5 || !IsMboxMarker (start, true)) { - // not a From-line... + if (headers.Count == 0) { + if (state == MimeParserState.MessageHeaders) { + // ignore From-lines that might appear at the start of a message + if (toplevel && (length < 5 || !IsMboxMarker (start, true))) { + // not a From-line... + inputIndex = (int) (start - inbuf); + state = MimeParserState.Error; + headerIndex = 0; + return false; + } + } else if (toplevel && state == MimeParserState.Headers) { inputIndex = (int) (start - inbuf); state = MimeParserState.Error; headerIndex = 0; @@ -825,6 +938,10 @@ unsafe bool StepHeaders (byte* inbuf, ref bool scanningFieldName, ref bool check break; } + prevLineBeginOffset = lineBeginOffset; + lineBeginOffset = GetOffset ((int) (inptr - inbuf) + 1); + lineNumber++; + // check to see if we've reached the end of the headers if (!midline && IsEoln (start)) { inputIndex = (int) (inptr - inbuf) + 1; @@ -836,18 +953,30 @@ unsafe bool StepHeaders (byte* inbuf, ref bool scanningFieldName, ref bool check length = (inptr + 1) - start; - if (!valid && headers.Count == 0 && length > 5 && IsMboxMarker (start, true)) { - if (inptr[-1] == (byte) '\r') + if ((boundary = CheckBoundary ((int) (start - inbuf), start, (int) length)) != BoundaryType.None) { + inputIndex = (int) (start - inbuf); + state = MimeParserState.Boundary; + headerIndex = 0; + return false; + } + + if (!valid && headers.Count == 0) { + if (length > 0 && preHeaderLength == 0) { + if (inptr[-1] == (byte) '\r') + length--; length--; - length--; - preHeaderLength = (int) length; + preHeaderLength = (int) length; - if (preHeaderLength > preHeaderBuffer.Length) - Array.Resize (ref preHeaderBuffer, NextAllocSize (preHeaderLength)); + if (preHeaderLength > preHeaderBuffer.Length) + Array.Resize (ref preHeaderBuffer, NextAllocSize (preHeaderLength)); - Buffer.BlockCopy (input, (int) (start - inbuf), preHeaderBuffer, 0, preHeaderLength); + Buffer.BlockCopy (input, (int) (start - inbuf), preHeaderBuffer, 0, preHeaderLength); + } + scanningFieldName = true; checkFolded = false; + blank = false; + valid = true; } else { AppendRawHeaderData ((int) (start - inbuf), (int) length); checkFolded = true; @@ -874,34 +1003,50 @@ unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken) bool valid = true; int left = 0; + headerBlockBegin = GetOffset (inputIndex); + boundary = BoundaryType.None; ResetRawHeaderData (); headers.Clear (); - ReadAhead (Math.Max (ReadAheadSize, left), 0, cancellationToken); + ReadAhead (ReadAheadSize, 0, cancellationToken); do { if (!StepHeaders (inbuf, ref scanningFieldName, ref checkFolded, ref midline, ref blank, ref valid, ref left)) - return; + break; var available = ReadAhead (left + 1, 0, cancellationToken); - if (available == 0) { + if (available == left) { // EOF reached before we reached the end of the headers... - if (left > 0) { - AppendRawHeaderData (inputIndex, left); - inputIndex = inputEnd; - } + if (scanningFieldName && left > 0) { + // EOF reached right in the middle of a header field name. Throw an error. + // + // See private email from Feb 8, 2018 which contained a sample message w/o + // any breaks between the header and message body. The file also did not + // end with a newline sequence. + state = MimeParserState.Error; + } else { + // EOF reached somewhere in the middle of the value. + // + // Append whatever data we've got left and pretend we found the end + // of the header value (and the header block). + // + // For more details, see https://github.com/jstedfast/MimeKit/pull/51 + // and https://github.com/jstedfast/MimeKit/issues/348 + if (left > 0) { + AppendRawHeaderData (inputIndex, left); + inputIndex = inputEnd; + } - ParseAndAppendHeader (); + ParseAndAppendHeader (); - // fail gracefully by pretending we found the end of the headers... - // - // For more details, see https://github.com/jstedfast/MimeKit/pull/51 - // and https://github.com/jstedfast/MimeKit/issues/348 - state = MimeParserState.Content; - return; + state = MimeParserState.Content; + } + break; } } while (true); + + headerBlockEnd = GetOffset (inputIndex); } unsafe bool SkipLine (byte* inbuf, bool consumeNewLine) @@ -919,6 +1064,9 @@ unsafe bool SkipLine (byte* inbuf, bool consumeNewLine) if (consumeNewLine) { inputIndex++; + lineNumber++; + prevLineBeginOffset = lineBeginOffset; + lineBeginOffset = GetOffset (inputIndex); } else if (*(inptr - 1) == (byte) '\r') { inputIndex--; } @@ -959,6 +1107,7 @@ unsafe MimeParserState Step (byte* inbuf, CancellationToken cancellationToken) case MimeParserState.MessageHeaders: case MimeParserState.Headers: StepHeaders (inbuf, cancellationToken); + toplevel = false; break; } @@ -967,8 +1116,6 @@ unsafe MimeParserState Step (byte* inbuf, CancellationToken cancellationToken) ContentType GetContentType (ContentType parent) { - ContentType type; - for (int i = 0; i < headers.Count; i++) { if (!headers[i].Field.Equals ("Content-Type", StringComparison.OrdinalIgnoreCase)) continue; @@ -976,7 +1123,7 @@ ContentType GetContentType (ContentType parent) var rawValue = headers[i].RawValue; int index = 0; - if (!ContentType.TryParse (options, rawValue, ref index, rawValue.Length, false, out type) && type == null) { + if (!ContentType.TryParse (options, rawValue, ref index, rawValue.Length, false, out var type) && type == null) { // if 'type' is null, then it means that even the mime-type was unintelligible type = new ContentType ("application", "octet-stream"); @@ -985,9 +1132,7 @@ ContentType GetContentType (ContentType parent) index++; if (++index < rawValue.Length) { - ParameterList parameters; - - if (ParameterList.TryParse (options, rawValue, ref index, rawValue.Length, false, out parameters)) + if (ParameterList.TryParse (options, rawValue, ref index, rawValue.Length, false, out var parameters)) type.Parameters = parameters; } } @@ -1046,20 +1191,35 @@ static unsafe bool IsBoundary (byte* text, int length, byte[] boundary, int boun unsafe BoundaryType CheckBoundary (int startIndex, byte* start, int length) { + int count = bounds.Count; + if (!IsPossibleBoundary (start, length)) return BoundaryType.None; - long curOffset = GetOffset (startIndex); - for (int i = 0; i < bounds.Count; i++) { + if (contentEnd > 0) { + // We'll need to special-case checking for the mbox From-marker when respecting Content-Length + count--; + } + + for (int i = 0; i < count; i++) { var boundary = bounds[i]; - if (curOffset >= boundary.ContentEnd && IsBoundary (start, length, boundary.Marker, boundary.FinalLength)) + if (IsBoundary (start, length, boundary.Marker, boundary.FinalLength)) return i == 0 ? BoundaryType.ImmediateEndBoundary : BoundaryType.ParentEndBoundary; if (IsBoundary (start, length, boundary.Marker, boundary.Length)) return i == 0 ? BoundaryType.ImmediateBoundary : BoundaryType.ParentBoundary; } + if (contentEnd > 0) { + // now it is time to check the mbox From-marker for the Content-Length case + long curOffset = GetOffset (startIndex); + var boundary = bounds[count]; + + if (curOffset >= contentEnd && IsBoundary (start, length, boundary.Marker, boundary.Length)) + return BoundaryType.ImmediateEndBoundary; + } + return BoundaryType.None; } @@ -1083,7 +1243,7 @@ int GetMaxBoundaryLength () return bounds.Count > 0 ? bounds[0].MaxLength + 2 : 0; } - unsafe void ScanContent (byte* inbuf, ref int contentIndex, ref int nleft, ref bool midline, ref BoundaryType found) + unsafe void ScanContent (byte* inbuf, ref int contentIndex, ref int nleft, ref bool midline, ref bool[] formats) { int length = inputEnd - inputIndex; byte* inptr = inbuf + inputIndex; @@ -1093,7 +1253,7 @@ unsafe void ScanContent (byte* inbuf, ref int contentIndex, ref int nleft, ref b contentIndex = inputIndex; if (midline && length == nleft) - found = BoundaryType.Eos; + boundary = BoundaryType.Eos; *inend = (byte) '\n'; @@ -1111,7 +1271,7 @@ unsafe void ScanContent (byte* inbuf, ref int contentIndex, ref int nleft, ref b *aligned = c; if (inptr == aligned && c != (byte) '\n') { - // -funroll-loops, bitches. + // -funroll-loops, yippee ki-yay. uint* dword = (uint*) inptr; do { @@ -1127,23 +1287,30 @@ unsafe void ScanContent (byte* inbuf, ref int contentIndex, ref int nleft, ref b length = (int) (inptr - start); if (inptr < inend) { - found = CheckBoundary (startIndex, start, length); - if (found != BoundaryType.None) + if ((boundary = CheckBoundary (startIndex, start, length)) != BoundaryType.None) break; + if (length > 0 && *(inptr - 1) == (byte) '\r') + formats[(int) NewLineFormat.Dos] = true; + else + formats[(int) NewLineFormat.Unix] = true; + + lineNumber++; length++; inptr++; + + prevLineBeginOffset = lineBeginOffset; + lineBeginOffset = GetOffset ((int) (inptr - inbuf)); } else { // didn't find the end of the line... midline = true; - if (found == BoundaryType.None) { + if (boundary == BoundaryType.None) { // not enough to tell if we found a boundary break; } - found = CheckBoundary (startIndex, start, length); - if (found != BoundaryType.None) + if ((boundary = CheckBoundary (startIndex, start, length)) != BoundaryType.None) break; } @@ -1153,11 +1320,30 @@ unsafe void ScanContent (byte* inbuf, ref int contentIndex, ref int nleft, ref b inputIndex = startIndex; } - unsafe BoundaryType ScanContent (byte* inbuf, Stream content, bool trimNewLine, out bool empty, CancellationToken cancellationToken) + class ScanContentResult + { + public readonly NewLineFormat? Format; + public readonly bool IsEmpty; + + public ScanContentResult (bool[] formats, bool isEmpty) + { + if (formats[(int) NewLineFormat.Unix] && formats[(int) NewLineFormat.Dos]) + Format = NewLineFormat.Mixed; + else if (formats[(int) NewLineFormat.Unix]) + Format = NewLineFormat.Unix; + else if (formats[(int) NewLineFormat.Dos]) + Format = NewLineFormat.Dos; + else + Format = null; + IsEmpty = isEmpty; + } + } + + unsafe ScanContentResult ScanContent (byte* inbuf, Stream content, bool trimNewLine, CancellationToken cancellationToken) { int atleast = Math.Max (ReadAheadSize, GetMaxBoundaryLength ()); - BoundaryType found = BoundaryType.None; int contentIndex = inputIndex; + var formats = new bool[2]; bool midline = false; int nleft; @@ -1167,20 +1353,20 @@ unsafe BoundaryType ScanContent (byte* inbuf, Stream content, bool trimNewLine, nleft = inputEnd - inputIndex; if (ReadAhead (atleast, 2, cancellationToken) <= 0) { + boundary = BoundaryType.Eos; contentIndex = inputIndex; - found = BoundaryType.Eos; break; } - ScanContent (inbuf, ref contentIndex, ref nleft, ref midline, ref found); - } while (found == BoundaryType.None); + ScanContent (inbuf, ref contentIndex, ref nleft, ref midline, ref formats); + } while (boundary == BoundaryType.None); if (contentIndex < inputIndex) content.Write (input, contentIndex, inputIndex - contentIndex); - empty = content.Length == 0; + var isEmpty = content.Length == 0; - if (found != BoundaryType.Eos && trimNewLine) { + if (boundary != BoundaryType.Eos && trimNewLine) { // the last \r\n belongs to the boundary if (content.Length > 0) { if (input[inputIndex - 2] == (byte) '\r') @@ -1190,46 +1376,57 @@ unsafe BoundaryType ScanContent (byte* inbuf, Stream content, bool trimNewLine, } } - return found; + return new ScanContentResult (formats, isEmpty); } - unsafe BoundaryType ConstructMimePart (MimePart part, byte* inbuf, CancellationToken cancellationToken) + unsafe void ConstructMimePart (MimePart part, MimeEntityEndEventArgs args, byte* inbuf, CancellationToken cancellationToken) { - BoundaryType found; + long endOffset, beginOffset = GetOffset (inputIndex); + var beginLineNumber = lineNumber; + ScanContentResult result; Stream content; - bool empty; if (persistent) { - long begin = GetOffset (inputIndex); - long end; - using (var measured = new MeasuringStream ()) { - found = ScanContent (inbuf, measured, true, out empty, cancellationToken); - end = begin + measured.Length; + result = ScanContent (inbuf, measured, true, cancellationToken); + endOffset = beginOffset + measured.Length; } - content = new BoundStream (stream, begin, end, true); + content = new BoundStream (stream, beginOffset, endOffset, true); } else { content = new MemoryBlockStream (); - found = ScanContent (inbuf, content, true, out empty, cancellationToken); - content.Seek (0, SeekOrigin.Begin); + + try { + result = ScanContent (inbuf, content, true, cancellationToken); + content.Seek (0, SeekOrigin.Begin); + } catch { + content.Dispose (); + throw; + } + + endOffset = beginOffset + content.Length; } - if (!empty) - part.Content = new MimeContent (content, part.ContentTransferEncoding); + args.Lines = GetLineCount (beginLineNumber, beginOffset, endOffset); - return found; + if (!result.IsEmpty) + part.Content = new MimeContent (content, part.ContentTransferEncoding) { NewLineFormat = result.Format }; + else + content.Dispose (); } - unsafe BoundaryType ConstructMessagePart (MessagePart part, byte* inbuf, CancellationToken cancellationToken) + unsafe void ConstructMessagePart (MessagePart rfc822, MimeEntityEndEventArgs args, byte* inbuf, int depth, CancellationToken cancellationToken) { - BoundaryType found; + var beginOffset = GetOffset (inputIndex); + var beginLineNumber = lineNumber; if (bounds.Count > 0) { int atleast = Math.Max (ReadAheadSize, GetMaxBoundaryLength ()); - if (ReadAhead (atleast, 0, cancellationToken) <= 0) - return BoundaryType.Eos; + if (ReadAhead (atleast, 0, cancellationToken) <= 0) { + boundary = BoundaryType.Eos; + return; + } byte* start = inbuf + inputIndex; byte* inend = inbuf + inputEnd; @@ -1240,106 +1437,168 @@ unsafe BoundaryType ConstructMessagePart (MessagePart part, byte* inbuf, Cancell while (*inptr != (byte) '\n') inptr++; - found = CheckBoundary (inputIndex, start, (int) (inptr - start)); + boundary = CheckBoundary (inputIndex, start, (int) (inptr - start)); - switch (found) { + switch (boundary) { case BoundaryType.ImmediateEndBoundary: case BoundaryType.ImmediateBoundary: case BoundaryType.ParentBoundary: - return found; + return; case BoundaryType.ParentEndBoundary: // ignore "From " boundaries, broken mailers tend to include these... - if (!IsMboxMarker (start)) - return found; + if (!IsMboxMarker (start)) { + return; + } break; } } // parse the headers... - state = MimeParserState.Headers; + state = MimeParserState.MessageHeaders; if (Step (inbuf, cancellationToken) == MimeParserState.Error) { // Note: this either means that StepHeaders() found the end of the stream // or an invalid header field name at the start of the message headers, // which likely means that this is not a valid MIME stream? - return BoundaryType.Eos; + boundary = BoundaryType.Eos; + return; } var message = new MimeMessage (options, headers, RfcComplianceMode.Loose); - var type = GetContentType (null); + var messageArgs = new MimeMessageEndEventArgs (message, rfc822) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeMessageBegin (messageArgs); if (preHeaderBuffer.Length > 0) { message.MboxMarker = new byte[preHeaderLength]; Buffer.BlockCopy (preHeaderBuffer, 0, message.MboxMarker, 0, preHeaderLength); } - var entity = options.CreateEntity (type, headers, true); + var type = GetContentType (null); + var entity = options.CreateEntity (type, headers, true, depth); + var entityArgs = new MimeEntityEndEventArgs (entity) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeEntityBegin (entityArgs); + message.Body = entity; if (entity is Multipart) - found = ConstructMultipart ((Multipart) entity, inbuf, cancellationToken); + ConstructMultipart ((Multipart) entity, entityArgs, inbuf, depth + 1, cancellationToken); else if (entity is MessagePart) - found = ConstructMessagePart ((MessagePart) entity, inbuf, cancellationToken); + ConstructMessagePart ((MessagePart) entity, entityArgs, inbuf, depth + 1, cancellationToken); else - found = ConstructMimePart ((MimePart) entity, inbuf, cancellationToken); + ConstructMimePart ((MimePart) entity, entityArgs, inbuf, cancellationToken); - part.Message = message; + rfc822.Message = message; - return found; + var endOffset = GetEndOffset (inputIndex); + messageArgs.HeadersEndOffset = entityArgs.HeadersEndOffset = Math.Min(entityArgs.HeadersEndOffset, endOffset); + messageArgs.EndOffset = entityArgs.EndOffset = endOffset; + + OnMimeEntityEnd (entityArgs); + OnMimeMessageEnd (messageArgs); + + args.Lines = GetLineCount (beginLineNumber, beginOffset, endOffset); } - unsafe BoundaryType MultipartScanPreamble (Multipart multipart, byte* inbuf, CancellationToken cancellationToken) + unsafe void MultipartScanPreamble (Multipart multipart, byte* inbuf, CancellationToken cancellationToken) { using (var memory = new MemoryStream ()) { - bool empty; + long offset = GetOffset (inputIndex); - var found = ScanContent (inbuf, memory, false, out empty, cancellationToken); + //OnMultipartPreambleBegin (multipart, offset); + ScanContent (inbuf, memory, false, cancellationToken); multipart.RawPreamble = memory.ToArray (); - return found; + //OnMultipartPreambleEnd (multipart, offset + memory.Length); } } - unsafe BoundaryType MultipartScanEpilogue (Multipart multipart, byte* inbuf, CancellationToken cancellationToken) + unsafe void MultipartScanEpilogue (Multipart multipart, byte* inbuf, CancellationToken cancellationToken) { using (var memory = new MemoryStream ()) { - bool empty; + long offset = GetOffset (inputIndex); - var found = ScanContent (inbuf, memory, true, out empty, cancellationToken); - multipart.RawEpilogue = empty ? null : memory.ToArray (); - return found; + //OnMultipartEpilogueBegin (multipart, offset); + var result = ScanContent (inbuf, memory, true, cancellationToken); + multipart.RawEpilogue = result.IsEmpty ? null : memory.ToArray (); + //OnMultipartEpilogueEnd (multipart, offset + memory.Length); } } - unsafe BoundaryType MultipartScanSubparts (Multipart multipart, byte* inbuf, CancellationToken cancellationToken) + unsafe void MultipartScanSubparts (Multipart multipart, byte* inbuf, int depth, CancellationToken cancellationToken) { - BoundaryType found; + //var beginOffset = GetOffset (inputIndex); do { + //OnMultipartBoundaryBegin (multipart, beginOffset); + // skip over the boundary marker - if (!SkipLine (inbuf, true, cancellationToken)) - return BoundaryType.Eos; + if (!SkipLine (inbuf, true, cancellationToken)) { + //OnMultipartBoundaryEnd (multipart, GetOffset (inputIndex)); + boundary = BoundaryType.Eos; + return; + } + + //OnMultipartBoundaryEnd (multipart, GetOffset (inputIndex)); + + var beginLineNumber = lineNumber; // parse the headers state = MimeParserState.Headers; - if (Step (inbuf, cancellationToken) == MimeParserState.Error) - return BoundaryType.Eos; + if (Step (inbuf, cancellationToken) == MimeParserState.Error) { + boundary = BoundaryType.Eos; + return; + } + + if (state == MimeParserState.Boundary) { + if (headers.Count == 0) { + if (boundary == BoundaryType.ImmediateBoundary) { + //beginOffset = GetOffset (inputIndex); + continue; + } + return; + } + + // This part has no content, but that will be handled in ConstructMultipart() + // or ConstructMimePart(). + } //if (state == ParserState.Complete && headers.Count == 0) // return BoundaryType.EndBoundary; var type = GetContentType (multipart.ContentType); - var entity = options.CreateEntity (type, headers, false); + var entity = options.CreateEntity (type, headers, false, depth); + var entityArgs = new MimeEntityEndEventArgs (entity, multipart) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeEntityBegin (entityArgs); if (entity is Multipart) - found = ConstructMultipart ((Multipart) entity, inbuf, cancellationToken); + ConstructMultipart ((Multipart) entity, entityArgs, inbuf, depth + 1, cancellationToken); else if (entity is MessagePart) - found = ConstructMessagePart ((MessagePart) entity, inbuf, cancellationToken); + ConstructMessagePart ((MessagePart) entity, entityArgs, inbuf, depth + 1, cancellationToken); else - found = ConstructMimePart ((MimePart) entity, inbuf, cancellationToken); + ConstructMimePart ((MimePart) entity, entityArgs, inbuf, cancellationToken); - multipart.Add (entity); - } while (found == BoundaryType.ImmediateBoundary); + var endOffset = GetEndOffset (inputIndex); + entityArgs.HeadersEndOffset = Math.Min (entityArgs.HeadersEndOffset, endOffset); + entityArgs.EndOffset = endOffset; + + OnMimeEntityEnd (entityArgs); - return found; + //beginOffset = endOffset; + multipart.Add (entity); + } while (boundary == BoundaryType.ImmediateBoundary); } void PushBoundary (string boundary) @@ -1355,46 +1614,61 @@ void PopBoundary () bounds.RemoveAt (0); } - unsafe BoundaryType ConstructMultipart (Multipart multipart, byte* inbuf, CancellationToken cancellationToken) + unsafe void ConstructMultipart (Multipart multipart, MimeEntityEndEventArgs args, byte* inbuf, int depth, CancellationToken cancellationToken) { - var boundary = multipart.Boundary; + var beginOffset = GetOffset (inputIndex); + var beginLineNumber = lineNumber; + var marker = multipart.Boundary; + long endOffset; - if (boundary == null) { + if (marker == null) { #if DEBUG Debug.WriteLine ("Multipart without a boundary encountered!"); #endif // Note: this will scan all content into the preamble... - return MultipartScanPreamble (multipart, inbuf, cancellationToken); + MultipartScanPreamble (multipart, inbuf, cancellationToken); + + endOffset = GetEndOffset (inputIndex); + args.Lines = GetLineCount (beginLineNumber, beginOffset, endOffset); + return; } - PushBoundary (boundary); + PushBoundary (marker); + + MultipartScanPreamble (multipart, inbuf, cancellationToken); + if (boundary == BoundaryType.ImmediateBoundary) + MultipartScanSubparts (multipart, inbuf, depth, cancellationToken); - var found = MultipartScanPreamble (multipart, inbuf, cancellationToken); - if (found == BoundaryType.ImmediateBoundary) - found = MultipartScanSubparts (multipart, inbuf, cancellationToken); + if (boundary == BoundaryType.ImmediateEndBoundary) { + //OnMultipartEndBoundaryBegin (multipart, GetEndOffset (inputIndex)); - if (found == BoundaryType.ImmediateEndBoundary) { // consume the end boundary and read the epilogue (if there is one) multipart.WriteEndBoundary = true; SkipLine (inbuf, false, cancellationToken); PopBoundary (); - return MultipartScanEpilogue (multipart, inbuf, cancellationToken); + //OnMultipartEndBoundaryEnd (multipart, GetOffset (inputIndex)); + + MultipartScanEpilogue (multipart, inbuf, cancellationToken); + + endOffset = GetEndOffset (inputIndex); + args.Lines = GetLineCount (beginLineNumber, beginOffset, endOffset); + return; } + endOffset = GetEndOffset (inputIndex); + args.Lines = GetLineCount (beginLineNumber, beginOffset, endOffset); + multipart.WriteEndBoundary = false; // We either found the end of the stream or we found a parent's boundary PopBoundary (); - if (found == BoundaryType.ParentEndBoundary && FoundImmediateBoundary (inbuf, true)) - return BoundaryType.ImmediateEndBoundary; - - if (found == BoundaryType.ParentBoundary && FoundImmediateBoundary (inbuf, false)) - return BoundaryType.ImmediateBoundary; - - return found; + if (boundary == BoundaryType.ParentEndBoundary && FoundImmediateBoundary (inbuf, true)) + boundary = BoundaryType.ImmediateEndBoundary; + else if (boundary == BoundaryType.ParentBoundary && FoundImmediateBoundary (inbuf, false)) + boundary = BoundaryType.ImmediateBoundary; } unsafe HeaderList ParseHeaders (byte* inbuf, CancellationToken cancellationToken) @@ -1443,31 +1717,48 @@ unsafe MimeEntity ParseEntity (byte* inbuf, CancellationToken cancellationToken) // Note: if a previously parsed MimePart's content has been read, // then the stream position will have moved and will need to be // reset. - if (persistent && stream.Position != offset) - stream.Seek (offset, SeekOrigin.Begin); + if (persistent && stream.Position != position) + stream.Seek (position, SeekOrigin.Begin); + + var beginLineNumber = lineNumber; state = MimeParserState.Headers; + toplevel = true; + if (Step (inbuf, cancellationToken) == MimeParserState.Error) throw new FormatException ("Failed to parse entity headers."); var type = GetContentType (null); - BoundaryType found; // Note: we pass 'false' as the 'toplevel' argument here because // we want the entity to consume all of the headers. - var entity = options.CreateEntity (type, headers, false); + var entity = options.CreateEntity (type, headers, false, 0); + var entityArgs = new MimeEntityEndEventArgs (entity) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeEntityBegin (entityArgs); + if (entity is Multipart) - found = ConstructMultipart ((Multipart) entity, inbuf, cancellationToken); + ConstructMultipart ((Multipart) entity, entityArgs, inbuf, 0, cancellationToken); else if (entity is MessagePart) - found = ConstructMessagePart ((MessagePart) entity, inbuf, cancellationToken); + ConstructMessagePart ((MessagePart) entity, entityArgs, inbuf, 0, cancellationToken); else - found = ConstructMimePart ((MimePart) entity, inbuf, cancellationToken); + ConstructMimePart ((MimePart) entity, entityArgs, inbuf, cancellationToken); + + var endOffset = GetEndOffset (inputIndex); + entityArgs.HeadersEndOffset = Math.Min (entityArgs.HeadersEndOffset, endOffset); + entityArgs.EndOffset = endOffset; - if (found != BoundaryType.Eos) + if (boundary != BoundaryType.Eos) state = MimeParserState.Complete; else state = MimeParserState.Eos; + OnMimeEntityEnd (entityArgs); + return entity; } @@ -1499,13 +1790,11 @@ unsafe MimeEntity ParseEntity (byte* inbuf, CancellationToken cancellationToken) unsafe MimeMessage ParseMessage (byte* inbuf, CancellationToken cancellationToken) { - BoundaryType found; - // Note: if a previously parsed MimePart's content has been read, // then the stream position will have moved and will need to be // reset. - if (persistent && stream.Position != offset) - stream.Seek (offset, SeekOrigin.Begin); + if (persistent && stream.Position != position) + stream.Seek (position, SeekOrigin.Begin); // scan the from-line if we are parsing an mbox while (state != MimeParserState.MessageHeaders) { @@ -1517,44 +1806,66 @@ unsafe MimeMessage ParseMessage (byte* inbuf, CancellationToken cancellationToke } } + toplevel = true; + // parse the headers + var beginLineNumber = lineNumber; if (state < MimeParserState.Content && Step (inbuf, cancellationToken) == MimeParserState.Error) throw new FormatException ("Failed to parse message headers."); var message = new MimeMessage (options, headers, RfcComplianceMode.Loose); + var messageArgs = new MimeMessageEndEventArgs (message) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; - if (format == MimeFormat.Mbox && options.RespectContentLength) { - bounds[0].ContentEnd = -1; + OnMimeMessageBegin (messageArgs); + contentEnd = 0; + if (format == MimeFormat.Mbox && options.RespectContentLength) { for (int i = 0; i < headers.Count; i++) { - if (!headers[i].Field.Equals ("Content-Length", StringComparison.OrdinalIgnoreCase)) + if (headers[i].Id != HeaderId.ContentLength) continue; var value = headers[i].RawValue; - int length, index = 0; + int index = 0; - if (!ParseUtils.TryParseInt32 (value, ref index, value.Length, out length)) + if (!ParseUtils.SkipWhiteSpace (value, ref index, value.Length)) continue; - long endOffset = GetOffset (inputIndex) + length; + if (!ParseUtils.TryParseInt32 (value, ref index, value.Length, out int length)) + continue; - bounds[0].ContentEnd = endOffset; + contentEnd = GetOffset (inputIndex) + length; break; } } var type = GetContentType (null); - var entity = options.CreateEntity (type, headers, true); + var entity = options.CreateEntity (type, headers, true, 0); + var entityArgs = new MimeEntityEndEventArgs (entity) { + HeadersEndOffset = headerBlockEnd, + BeginOffset = headerBlockBegin, + LineNumber = beginLineNumber + }; + + OnMimeEntityBegin (entityArgs); + message.Body = entity; - if (entity is Multipart) - found = ConstructMultipart ((Multipart) entity, inbuf, cancellationToken); - else if (entity is MessagePart) - found = ConstructMessagePart ((MessagePart) entity, inbuf, cancellationToken); + if (entity is Multipart multipart) + ConstructMultipart (multipart, entityArgs, inbuf, 0, cancellationToken); + else if (entity is MessagePart rfc822) + ConstructMessagePart (rfc822, entityArgs, inbuf, 0, cancellationToken); else - found = ConstructMimePart ((MimePart) entity, inbuf, cancellationToken); + ConstructMimePart ((MimePart) entity, entityArgs, inbuf, cancellationToken); - if (found != BoundaryType.Eos) { + var endOffset = GetEndOffset (inputIndex); + messageArgs.HeadersEndOffset = entityArgs.HeadersEndOffset = Math.Min (entityArgs.HeadersEndOffset, endOffset); + messageArgs.EndOffset = entityArgs.EndOffset = endOffset; + + if (boundary != BoundaryType.Eos) { if (format == MimeFormat.Mbox) state = MimeParserState.MboxMarker; else @@ -1563,6 +1874,9 @@ unsafe MimeMessage ParseMessage (byte* inbuf, CancellationToken cancellationToke state = MimeParserState.Eos; } + OnMimeEntityEnd (entityArgs); + OnMimeMessageEnd (messageArgs); + return message; } diff --git a/MimeKit/MimePart.cs b/MimeKit/MimePart.cs index 7a7617f231..a8a9450bda 100644 --- a/MimeKit/MimePart.cs +++ b/MimeKit/MimePart.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast. + /// // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -30,17 +30,12 @@ using System.Threading; using System.Threading.Tasks; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -using MD5 = MimeKit.Cryptography.MD5; -#else using MD5 = System.Security.Cryptography.MD5; -#endif -using MimeKit.IO.Filters; -using MimeKit.Encodings; -using MimeKit.Utils; using MimeKit.IO; +using MimeKit.Utils; +using MimeKit.Encodings; +using MimeKit.IO.Filters; namespace MimeKit { /// @@ -62,11 +57,11 @@ public class MimePart : MimeEntity int? duration; /// ///- /// Initializes a new instance of the ///class + /// Initialize a new instance of the class /// based on the . /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -77,7 +72,7 @@ public MimePart (MimeEntityConstructorArgs args) : base (args) } /// ///- /// Initializes a new instance of the ///class + /// Initialize a new instance of the class /// with the specified media type and subtype. /// @@ -94,7 +89,7 @@ public MimePart (MimeEntityConstructorArgs args) : base (args) /// /// is null .- /// contains more than one or + /// /// contains more than one or /// . -or- ///@@ -110,17 +105,15 @@ public MimePart (string mediaType, string mediaSubtype, params object[] args) : if (obj == null || TryInit (obj)) continue; - var co = obj as IMimeContent; - if (co != null) { + if (obj is IMimeContent co) { if (content != null) - throw new ArgumentException ("ContentObject should not be specified more than once."); + throw new ArgumentException ("IMimeContent should not be specified more than once."); content = co; continue; } - var stream = obj as Stream; - if (stream != null) { + if (obj is Stream stream) { if (content != null) throw new ArgumentException ("Stream (used as content) should not be specified more than once."); @@ -137,7 +130,7 @@ public MimePart (string mediaType, string mediaSubtype, params object[] args) : } /// contains one or more arguments of an unknown type. - /// Initializes a new instance of the ///class + /// Initialize a new instance of the class /// with the specified media type and subtype. /// @@ -155,7 +148,7 @@ public MimePart (string mediaType, string mediaSubtype) : base (mediaType, media } /// - /// Initializes a new instance of the ///class + /// Initialize a new instance of the class /// with the specified content type. /// @@ -170,7 +163,7 @@ public MimePart (ContentType contentType) : base (contentType) } /// - /// Initializes a new instance of the ///class + /// Initialize a new instance of the class /// with the specified content type. /// @@ -188,7 +181,7 @@ public MimePart (string contentType) : base (ContentType.Parse (contentType)) } /// - /// Initializes a new instance of the ///class + /// Initialize a new instance of the class /// with the default Content-Type of application/octet-stream. /// @@ -358,12 +351,12 @@ public IMimeContent ContentObject { /// Dispatches to the specific visit method for this MIME entity. /// - /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -423,20 +416,24 @@ public override void Accept (MimeVisitor visitor) /// public ContentEncoding GetBestEncoding (EncodingConstraint constraint, int maxLineLength, CancellationToken cancellationToken = default (CancellationToken)) { - if (Content == null) - return ContentEncoding.SevenBit; + if (ContentType.IsMimeType ("text", "*") || ContentType.IsMimeType ("message", "*")) { + if (Content == null) + return ContentEncoding.SevenBit; - using (var measure = new MeasuringStream ()) { - using (var filtered = new FilteredStream (measure)) { - var filter = new BestEncodingFilter (); + using (var measure = new MeasuringStream ()) { + using (var filtered = new FilteredStream (measure)) { + var filter = new BestEncodingFilter (); - filtered.Add (filter); - Content.DecodeTo (filtered, cancellationToken); - filtered.Flush (); + filtered.Add (filter); + Content.DecodeTo (filtered, cancellationToken); + filtered.Flush (); - return filter.GetBestEncoding (constraint, maxLineLength); + return filter.GetBestEncoding (constraint, maxLineLength); + } } } + + return constraint == EncodingConstraint.None ? ContentEncoding.Binary : ContentEncoding.Base64; } ///@@ -504,18 +501,6 @@ public bool VerifyContentMd5 () return md5sum == ComputeContentMd5 (); } - static bool NeedsEncoding (ContentEncoding encoding) - { - switch (encoding) { - case ContentEncoding.EightBit: - case ContentEncoding.Binary: - case ContentEncoding.Default: - return true; - default: - return false; - } - } - /// /// Prepare the MIME entity for transport using the specified encoding constraints. /// @@ -557,7 +542,7 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = } ///- /// Writes the ///to the specified output stream. + /// Write the to the specified output stream. /// /// Writes the MIME part to the output stream. @@ -624,7 +609,16 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } } - } else if (encoding != ContentEncoding.Binary) { + } else if (encoding == ContentEncoding.Binary) { + // Do not alter binary content. + Content.WriteTo (stream, cancellationToken); + } else if (options.VerifyingSignature && Content.NewLineFormat.HasValue && Content.NewLineFormat.Value == NewLineFormat.Mixed) { + // Allow pass-through of the original parsed content without canonicalization when verifying signatures + // if the content contains a mix of line-endings. + // + // See https://github.com/jstedfast/MimeKit/issues/569 for details. + Content.WriteTo (stream, cancellationToken); + } else { using (var filtered = new FilteredStream (stream)) { // Note: if we are writing the top-level MimePart, make sure it ends with a new-line so that // MimeMessage.WriteTo() *always* ends with a new-line. @@ -632,17 +626,16 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = Content.WriteTo (filtered, cancellationToken); filtered.Flush (cancellationToken); } - } else { - Content.WriteTo (stream, cancellationToken); } } /// public static class MimeTypes { + static readonly Dictionary- /// Asynchronously writes the ///to the specified output stream. + /// Asynchronously write the to the specified output stream. /// - /// Writes the MIME part to the output stream. + /// Asynchronously writes the MIME part to the output stream. /// + ///An awaitable task. /// The formatting options. /// The output stream. ///true if only the content should be written; otherwise,false . @@ -665,6 +658,8 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = if (Content == null) return; + var isText = ContentType.IsMimeType ("text", "*") || ContentType.IsMimeType ("message", "*"); + if (Content.Encoding != encoding) { if (encoding == ContentEncoding.UUEncode) { var begin = string.Format ("begin 0644 {0}", FileName ?? "unknown"); @@ -691,7 +686,16 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = await stream.WriteAsync (buffer, 0, buffer.Length, cancellationToken).ConfigureAwait (false); await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); } - } else if (encoding != ContentEncoding.Binary) { + } else if (encoding == ContentEncoding.Binary) { + // Do not alter binary content. + await Content.WriteToAsync (stream, cancellationToken).ConfigureAwait (false); + } else if (options.VerifyingSignature && Content.NewLineFormat.HasValue && Content.NewLineFormat.Value == NewLineFormat.Mixed) { + // Allow pass-through of the original parsed content without canonicalization when verifying signatures + // if the content contains a mix of line-endings. + // + // See https://github.com/jstedfast/MimeKit/issues/569 for details. + await Content.WriteToAsync (stream, cancellationToken).ConfigureAwait (false); + } else { using (var filtered = new FilteredStream (stream)) { // Note: if we are writing the top-level MimePart, make sure it ends with a new-line so that // MimeMessage.WriteTo() *always* ends with a new-line. @@ -699,8 +703,6 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = await Content.WriteToAsync (filtered, cancellationToken).ConfigureAwait (false); await filtered.FlushAsync (cancellationToken).ConfigureAwait (false); } - } else { - await Content.WriteToAsync (stream, cancellationToken).ConfigureAwait (false); } } diff --git a/MimeKit/MimeTypes.cs b/MimeKit/MimeTypes.cs index 6fbcb584e8..957248b831 100644 --- a/MimeKit/MimeTypes.cs +++ b/MimeKit/MimeTypes.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,6 +25,7 @@ // using System; +using System.IO; using System.Collections.Generic; using MimeKit.Utils; @@ -38,570 +39,959 @@ namespace MimeKit { /// extensions; static readonly Dictionary mimeTypes; static MimeTypes () { - mimeTypes = new Dictionary (MimeUtils.OrdinalIgnoreCase); - mimeTypes.Add ("323", "text/h323"); - mimeTypes.Add ("3dmf", "x-world/x-3dmf"); - mimeTypes.Add ("3dm", "x-world/x-3dmf"); - mimeTypes.Add ("3g2", "video/3gpp2"); - mimeTypes.Add ("3gp", "video/3gpp"); - mimeTypes.Add ("7z", "application/x-7z-compressed"); - mimeTypes.Add ("aab", "application/x-authorware-bin"); - mimeTypes.Add ("aac", "audio/aac"); - mimeTypes.Add ("aam", "application/x-authorware-map"); - mimeTypes.Add ("aas", "application/x-authorware-seg"); - mimeTypes.Add ("abc", "text/vnd.abc"); - mimeTypes.Add ("acgi", "text/html"); - mimeTypes.Add ("acx", "application/internet-property-stream"); - mimeTypes.Add ("afl", "video/animaflex"); - mimeTypes.Add ("ai", "application/postscript"); - mimeTypes.Add ("aif", "audio/aiff"); - mimeTypes.Add ("aifc", "audio/aiff"); - mimeTypes.Add ("aiff", "audio/aiff"); - mimeTypes.Add ("aim", "application/x-aim"); - mimeTypes.Add ("aip", "text/x-audiosoft-intra"); - mimeTypes.Add ("ani", "application/x-navi-animation"); - mimeTypes.Add ("aos", "application/x-nokia-9000-communicator-add-on-software"); - mimeTypes.Add ("appcache", "text/cache-manifest"); - mimeTypes.Add ("application", "application/x-ms-application"); - mimeTypes.Add ("aps", "application/mime"); - mimeTypes.Add ("art", "image/x-jg"); - mimeTypes.Add ("asf", "video/x-ms-asf"); - mimeTypes.Add ("asm", "text/x-asm"); - mimeTypes.Add ("asp", "text/asp"); - mimeTypes.Add ("asr", "video/x-ms-asf"); - mimeTypes.Add ("asx", "application/x-mplayer2"); - mimeTypes.Add ("atom", "application/atom+xml"); - mimeTypes.Add ("au", "audio/x-au"); - mimeTypes.Add ("avi", "video/avi"); - mimeTypes.Add ("avs", "video/avs-video"); - mimeTypes.Add ("axs", "application/olescript"); - mimeTypes.Add ("bas", "text/plain"); - mimeTypes.Add ("bcpio", "application/x-bcpio"); - mimeTypes.Add ("bin", "application/octet-stream"); - mimeTypes.Add ("bm", "image/bmp"); - mimeTypes.Add ("bmp", "image/bmp"); - mimeTypes.Add ("boo", "application/book"); - mimeTypes.Add ("book", "application/book"); - mimeTypes.Add ("boz", "application/x-bzip2"); - mimeTypes.Add ("bsh", "application/x-bsh"); - mimeTypes.Add ("bz2", "application/x-bzip2"); - mimeTypes.Add ("bz", "application/x-bzip"); - mimeTypes.Add ("cat", "application/vnd.ms-pki.seccat"); - mimeTypes.Add ("ccad", "application/clariscad"); - mimeTypes.Add ("cco", "application/x-cocoa"); - mimeTypes.Add ("cc", "text/plain"); - mimeTypes.Add ("cdf", "application/cdf"); - mimeTypes.Add ("cer", "application/pkix-cert"); - mimeTypes.Add ("cha", "application/x-chat"); - mimeTypes.Add ("chat", "application/x-chat"); - mimeTypes.Add ("class", "application/x-java-applet"); - mimeTypes.Add ("clp", "application/x-msclip"); - mimeTypes.Add ("cmx", "image/x-cmx"); - mimeTypes.Add ("cod", "image/cis-cod"); - mimeTypes.Add ("coffee", "text/x-coffeescript"); - mimeTypes.Add ("conf", "text/plain"); - mimeTypes.Add ("cpio", "application/x-cpio"); - mimeTypes.Add ("cpp", "text/plain"); - mimeTypes.Add ("cpt", "application/x-cpt"); - mimeTypes.Add ("crd", "application/x-mscardfile"); - mimeTypes.Add ("crl", "application/pkix-crl"); - mimeTypes.Add ("crt", "application/pkix-cert"); - mimeTypes.Add ("csh", "application/x-csh"); - mimeTypes.Add ("css", "text/css"); - mimeTypes.Add ("c", "text/plain"); - mimeTypes.Add ("c++", "text/plain"); - mimeTypes.Add ("cxx", "text/plain"); - mimeTypes.Add ("dart", "application/dart"); - mimeTypes.Add ("dcr", "application/x-director"); - mimeTypes.Add ("deb", "application/x-deb"); - mimeTypes.Add ("deepv", "application/x-deepv"); - mimeTypes.Add ("def", "text/plain"); - mimeTypes.Add ("deploy", "application/octet-stream"); - mimeTypes.Add ("der", "application/x-x509-ca-cert"); - mimeTypes.Add ("dib", "image/bmp"); - mimeTypes.Add ("dif", "video/x-dv"); - mimeTypes.Add ("dir", "application/x-director"); - mimeTypes.Add ("disco", "text/xml"); - mimeTypes.Add ("dll", "application/x-msdownload"); - mimeTypes.Add ("dl", "video/dl"); - mimeTypes.Add ("doc", "application/msword"); - mimeTypes.Add ("docm", "application/vnd.ms-word.document.macroEnabled.12"); - mimeTypes.Add ("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); - mimeTypes.Add ("dot", "application/msword"); - mimeTypes.Add ("dotm", "application/vnd.ms-word.template.macroEnabled.12"); - mimeTypes.Add ("dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"); - mimeTypes.Add ("dp", "application/commonground"); - mimeTypes.Add ("drw", "application/drafting"); - mimeTypes.Add ("dtd", "application/xml-dtd"); - mimeTypes.Add ("dvi", "application/x-dvi"); - mimeTypes.Add ("dv", "video/x-dv"); - mimeTypes.Add ("dwf", "drawing/x-dwf (old)"); - mimeTypes.Add ("dwg", "application/acad"); - mimeTypes.Add ("dxf", "application/dxf"); - mimeTypes.Add ("dxr", "application/x-director"); - mimeTypes.Add ("elc", "application/x-elc"); - mimeTypes.Add ("el", "text/x-script.elisp"); - mimeTypes.Add ("eml", "message/rfc822"); - mimeTypes.Add ("eot", "application/vnd.bw-fontobject"); - mimeTypes.Add ("eps", "application/postscript"); - mimeTypes.Add ("es", "application/x-esrehber"); - mimeTypes.Add ("etx", "text/x-setext"); - mimeTypes.Add ("evy", "application/envoy"); - mimeTypes.Add ("exe", "application/octet-stream"); - mimeTypes.Add ("f77", "text/plain"); - mimeTypes.Add ("f90", "text/plain"); - mimeTypes.Add ("fdf", "application/vnd.fdf"); - mimeTypes.Add ("fif", "image/fif"); - mimeTypes.Add ("flac", "audio/x-flac"); - mimeTypes.Add ("fli", "video/fli"); - mimeTypes.Add ("flo", "image/florian"); - mimeTypes.Add ("flr", "x-world/x-vrml"); - mimeTypes.Add ("flx", "text/vnd.fmi.flexstor"); - mimeTypes.Add ("fmf", "video/x-atomic3d-feature"); - mimeTypes.Add ("for", "text/plain"); - mimeTypes.Add ("fpx", "image/vnd.fpx"); - mimeTypes.Add ("frl", "application/freeloader"); - mimeTypes.Add ("f", "text/plain"); - mimeTypes.Add ("funk", "audio/make"); - mimeTypes.Add ("g3", "image/g3fax"); - mimeTypes.Add ("gif", "image/gif"); - mimeTypes.Add ("gl", "video/gl"); - mimeTypes.Add ("gsd", "audio/x-gsm"); - mimeTypes.Add ("gsm", "audio/x-gsm"); - mimeTypes.Add ("gsp", "application/x-gsp"); - mimeTypes.Add ("gss", "application/x-gss"); - mimeTypes.Add ("gtar", "application/x-gtar"); - mimeTypes.Add ("g", "text/plain"); - mimeTypes.Add ("gz", "application/x-gzip"); - mimeTypes.Add ("gzip", "application/x-gzip"); - mimeTypes.Add ("hdf", "application/x-hdf"); - mimeTypes.Add ("help", "application/x-helpfile"); - mimeTypes.Add ("hgl", "application/vnd.hp-HPGL"); - mimeTypes.Add ("hh", "text/plain"); - mimeTypes.Add ("hlb", "text/x-script"); - mimeTypes.Add ("hlp", "application/x-helpfile"); - mimeTypes.Add ("hpg", "application/vnd.hp-HPGL"); - mimeTypes.Add ("hpgl", "application/vnd.hp-HPGL"); - mimeTypes.Add ("hqx", "application/binhex"); - mimeTypes.Add ("hta", "application/hta"); - mimeTypes.Add ("htc", "text/x-component"); - mimeTypes.Add ("h", "text/plain"); - mimeTypes.Add ("htmls", "text/html"); - mimeTypes.Add ("html", "text/html"); - mimeTypes.Add ("htm", "text/html"); - mimeTypes.Add ("htt", "text/webviewhtml"); - mimeTypes.Add ("htx", "text/html"); - mimeTypes.Add ("ice", "x-conference/x-cooltalk"); - mimeTypes.Add ("ico", "image/x-icon"); - mimeTypes.Add ("ics", "text/calendar"); - mimeTypes.Add ("idc", "text/plain"); - mimeTypes.Add ("ief", "image/ief"); - mimeTypes.Add ("iefs", "image/ief"); - mimeTypes.Add ("iges", "application/iges"); - mimeTypes.Add ("igs", "application/iges"); - mimeTypes.Add ("iii", "application/x-iphone"); - mimeTypes.Add ("ima", "application/x-ima"); - mimeTypes.Add ("imap", "application/x-httpd-imap"); - mimeTypes.Add ("inf", "application/inf"); - mimeTypes.Add ("ins", "application/x-internett-signup"); - mimeTypes.Add ("ip", "application/x-ip2"); - mimeTypes.Add ("isp", "application/x-internet-signup"); - mimeTypes.Add ("isu", "video/x-isvideo"); - mimeTypes.Add ("it", "audio/it"); - mimeTypes.Add ("iv", "application/x-inventor"); - mimeTypes.Add ("ivf", "video/x-ivf"); - mimeTypes.Add ("ivr", "i-world/i-vrml"); - mimeTypes.Add ("ivy", "application/x-livescreen"); - mimeTypes.Add ("jam", "audio/x-jam"); - mimeTypes.Add ("jar", "application/java-archive"); - mimeTypes.Add ("java", "text/plain"); - mimeTypes.Add ("jav", "text/plain"); - mimeTypes.Add ("jcm", "application/x-java-commerce"); - mimeTypes.Add ("jfif", "image/jpeg"); - mimeTypes.Add ("jfif-tbnl", "image/jpeg"); - mimeTypes.Add ("jpeg", "image/jpeg"); - mimeTypes.Add ("jpe", "image/jpeg"); - mimeTypes.Add ("jpg", "image/jpeg"); - mimeTypes.Add ("jps", "image/x-jps"); - mimeTypes.Add ("js", "application/javascript"); - mimeTypes.Add ("json", "application/json"); - mimeTypes.Add ("jut", "image/jutvision"); - mimeTypes.Add ("kar", "audio/midi"); - mimeTypes.Add ("ksh", "text/x-script.ksh"); - mimeTypes.Add ("la", "audio/nspaudio"); - mimeTypes.Add ("lam", "audio/x-liveaudio"); - mimeTypes.Add ("latex", "application/x-latex"); - mimeTypes.Add ("list", "text/plain"); - mimeTypes.Add ("lma", "audio/nspaudio"); - mimeTypes.Add ("log", "text/plain"); - mimeTypes.Add ("lsp", "application/x-lisp"); - mimeTypes.Add ("lst", "text/plain"); - mimeTypes.Add ("lsx", "text/x-la-asf"); - mimeTypes.Add ("ltx", "application/x-latex"); - mimeTypes.Add ("m13", "application/x-msmediaview"); - mimeTypes.Add ("m14", "application/x-msmediaview"); - mimeTypes.Add ("m1v", "video/mpeg"); - mimeTypes.Add ("m2a", "audio/mpeg"); - mimeTypes.Add ("m2v", "video/mpeg"); - mimeTypes.Add ("m3u", "audio/x-mpequrl"); - mimeTypes.Add ("m4a", "audio/mp4"); - mimeTypes.Add ("m4v", "video/mp4"); - mimeTypes.Add ("man", "application/x-troff-man"); - mimeTypes.Add ("manifest", "application/x-ms-manifest"); - mimeTypes.Add ("map", "application/x-navimap"); - mimeTypes.Add ("mar", "text/plain"); - mimeTypes.Add ("mbd", "application/mbedlet"); - mimeTypes.Add ("mc$", "application/x-magic-cap-package-1.0"); - mimeTypes.Add ("mcd", "application/mcad"); - mimeTypes.Add ("mcf", "image/vasa"); - mimeTypes.Add ("mcp", "application/netmc"); - mimeTypes.Add ("mdb", "application/x-msaccess"); - mimeTypes.Add ("mesh", "model/mesh"); - mimeTypes.Add ("me", "application/x-troff-me"); - mimeTypes.Add ("mid", "audio/midi"); - mimeTypes.Add ("midi", "audio/midi"); - mimeTypes.Add ("mif", "application/x-mif"); - mimeTypes.Add ("mjf", "audio/x-vnd.AudioExplosion.MjuiceMediaFile"); - mimeTypes.Add ("mjpg", "video/x-motion-jpeg"); - mimeTypes.Add ("mm", "application/base64"); - mimeTypes.Add ("mme", "application/base64"); - mimeTypes.Add ("mny", "application/x-msmoney"); - mimeTypes.Add ("mod", "audio/mod"); - mimeTypes.Add ("mov", "video/quicktime"); - mimeTypes.Add ("movie", "video/x-sgi-movie"); - mimeTypes.Add ("mp2", "video/mpeg"); - mimeTypes.Add ("mp3", "audio/mpeg"); - mimeTypes.Add ("mp4", "video/mp4"); - mimeTypes.Add ("mp4a", "audio/mp4"); - mimeTypes.Add ("mp4v", "video/mp4"); - mimeTypes.Add ("mpa", "audio/mpeg"); - mimeTypes.Add ("mpc", "application/x-project"); - mimeTypes.Add ("mpeg", "video/mpeg"); - mimeTypes.Add ("mpe", "video/mpeg"); - mimeTypes.Add ("mpga", "audio/mpeg"); - mimeTypes.Add ("mpg", "video/mpeg"); - mimeTypes.Add ("mpp", "application/vnd.ms-project"); - mimeTypes.Add ("mpt", "application/x-project"); - mimeTypes.Add ("mpv2", "video/mpeg"); - mimeTypes.Add ("mpv", "application/x-project"); - mimeTypes.Add ("mpx", "application/x-project"); - mimeTypes.Add ("mrc", "application/marc"); - mimeTypes.Add ("ms", "application/x-troff-ms"); - mimeTypes.Add ("msh", "model/mesh"); - mimeTypes.Add ("m", "text/plain"); - mimeTypes.Add ("mvb", "application/x-msmediaview"); - mimeTypes.Add ("mv", "video/x-sgi-movie"); - mimeTypes.Add ("my", "audio/make"); - mimeTypes.Add ("mzz", "application/x-vnd.AudioExplosion.mzz"); - mimeTypes.Add ("nap", "image/naplps"); - mimeTypes.Add ("naplps", "image/naplps"); - mimeTypes.Add ("nc", "application/x-netcdf"); - mimeTypes.Add ("ncm", "application/vnd.nokia.configuration-message"); - mimeTypes.Add ("niff", "image/x-niff"); - mimeTypes.Add ("nif", "image/x-niff"); - mimeTypes.Add ("nix", "application/x-mix-transfer"); - mimeTypes.Add ("nsc", "application/x-conference"); - mimeTypes.Add ("nvd", "application/x-navidoc"); - mimeTypes.Add ("nws", "message/rfc822"); - mimeTypes.Add ("oda", "application/oda"); - mimeTypes.Add ("ods", "application/oleobject"); - mimeTypes.Add ("oga", "audio/ogg"); - mimeTypes.Add ("ogg", "audio/ogg"); - mimeTypes.Add ("ogv", "video/ogg"); - mimeTypes.Add ("ogx", "application/ogg"); - mimeTypes.Add ("omc", "application/x-omc"); - mimeTypes.Add ("omcd", "application/x-omcdatamaker"); - mimeTypes.Add ("omcr", "application/x-omcregerator"); - mimeTypes.Add ("opus", "audio/ogg"); - mimeTypes.Add ("oxps", "application/oxps"); - mimeTypes.Add ("p10", "application/pkcs10"); - mimeTypes.Add ("p12", "application/pkcs-12"); - mimeTypes.Add ("p7a", "application/x-pkcs7-signature"); - mimeTypes.Add ("p7b", "application/x-pkcs7-certificates"); - mimeTypes.Add ("p7c", "application/pkcs7-mime"); - mimeTypes.Add ("p7m", "application/pkcs7-mime"); - mimeTypes.Add ("p7r", "application/x-pkcs7-certreqresp"); - mimeTypes.Add ("p7s", "application/pkcs7-signature"); - mimeTypes.Add ("part", "application/pro_eng"); - mimeTypes.Add ("pas", "text/pascal"); - mimeTypes.Add ("pbm", "image/x-portable-bitmap"); - mimeTypes.Add ("pcl", "application/x-pcl"); - mimeTypes.Add ("pct", "image/x-pict"); - mimeTypes.Add ("pcx", "image/x-pcx"); - mimeTypes.Add ("pdb", "chemical/x-pdb"); - mimeTypes.Add ("pdf", "application/pdf"); - mimeTypes.Add ("pfunk", "audio/make"); - mimeTypes.Add ("pfx", "application/x-pkcs12"); - mimeTypes.Add ("pgm", "image/x-portable-graymap"); - mimeTypes.Add ("pic", "image/pict"); - mimeTypes.Add ("pict", "image/pict"); - mimeTypes.Add ("pkg", "application/x-newton-compatible-pkg"); - mimeTypes.Add ("pko", "application/vnd.ms-pki.pko"); - mimeTypes.Add ("pl", "text/plain"); - mimeTypes.Add ("plx", "application/x-PiXCLscript"); - mimeTypes.Add ("pm4", "application/x-pagemaker"); - mimeTypes.Add ("pm5", "application/x-pagemaker"); - mimeTypes.Add ("pma", "application/x-perfmon"); - mimeTypes.Add ("pmc", "application/x-perfmon"); - mimeTypes.Add ("pm", "image/x-xpixmap"); - mimeTypes.Add ("pml", "application/x-perfmon"); - mimeTypes.Add ("pmr", "application/x-perfmon"); - mimeTypes.Add ("pmw", "application/x-perfmon"); - mimeTypes.Add ("png", "image/png"); - mimeTypes.Add ("pnm", "application/x-portable-anymap"); - mimeTypes.Add ("pot", "application/vnd.ms-powerpoint"); - mimeTypes.Add ("potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"); - mimeTypes.Add ("potx", "application/vnd.openxmlformats-officedocument.presentationml.template"); - mimeTypes.Add ("pov", "model/x-pov"); - mimeTypes.Add ("ppa", "application/vnd.ms-powerpoint"); - mimeTypes.Add ("ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"); - mimeTypes.Add ("ppm", "image/x-portable-pixmap"); - mimeTypes.Add ("pps", "application/vnd.ms-powerpoint"); - mimeTypes.Add ("ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"); - mimeTypes.Add ("ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"); - mimeTypes.Add ("ppt", "application/vnd.ms-powerpoint"); - mimeTypes.Add ("pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"); - mimeTypes.Add ("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"); - mimeTypes.Add ("ppz", "application/mspowerpoint"); - mimeTypes.Add ("pre", "application/x-freelance"); - mimeTypes.Add ("prf", "application/pics-rules"); - mimeTypes.Add ("prt", "application/pro_eng"); - mimeTypes.Add ("ps", "application/postscript"); - mimeTypes.Add ("p", "text/x-pascal"); - mimeTypes.Add ("pub", "application/x-mspublisher"); - mimeTypes.Add ("pvu", "paleovu/x-pv"); - mimeTypes.Add ("pwz", "application/vnd.ms-powerpoint"); - mimeTypes.Add ("pyc", "applicaiton/x-bytecode.python"); - mimeTypes.Add ("py", "text/x-script.phyton"); - mimeTypes.Add ("qcp", "audio/vnd.qcelp"); - mimeTypes.Add ("qd3d", "x-world/x-3dmf"); - mimeTypes.Add ("qd3", "x-world/x-3dmf"); - mimeTypes.Add ("qif", "image/x-quicktime"); - mimeTypes.Add ("qtc", "video/x-qtc"); - mimeTypes.Add ("qtif", "image/x-quicktime"); - mimeTypes.Add ("qti", "image/x-quicktime"); - mimeTypes.Add ("qt", "video/quicktime"); - mimeTypes.Add ("ra", "audio/x-pn-realaudio"); - mimeTypes.Add ("ram", "audio/x-pn-realaudio"); - mimeTypes.Add ("ras", "application/x-cmu-raster"); - mimeTypes.Add ("rast", "image/cmu-raster"); - mimeTypes.Add ("rexx", "text/x-script.rexx"); - mimeTypes.Add ("rf", "image/vnd.rn-realflash"); - mimeTypes.Add ("rgb", "image/x-rgb"); - mimeTypes.Add ("rm", "application/vnd.rn-realmedia"); - mimeTypes.Add ("rmi", "audio/mid"); - mimeTypes.Add ("rmm", "audio/x-pn-realaudio"); - mimeTypes.Add ("rmp", "audio/x-pn-realaudio"); - mimeTypes.Add ("rng", "application/ringing-tones"); - mimeTypes.Add ("rnx", "application/vnd.rn-realplayer"); - mimeTypes.Add ("roff", "application/x-troff"); - mimeTypes.Add ("rp", "image/vnd.rn-realpix"); - mimeTypes.Add ("rpm", "audio/x-pn-realaudio-plugin"); - mimeTypes.Add ("rss", "application/rss+xml"); - mimeTypes.Add ("rtf", "text/richtext"); - mimeTypes.Add ("rt", "text/richtext"); - mimeTypes.Add ("rtx", "text/richtext"); - mimeTypes.Add ("rv", "video/vnd.rn-realvideo"); - mimeTypes.Add ("s3m", "audio/s3m"); - mimeTypes.Add ("sbk", "application/x-tbook"); - mimeTypes.Add ("scd", "application/x-msschedule"); - mimeTypes.Add ("scm", "application/x-lotusscreencam"); - mimeTypes.Add ("sct", "text/scriptlet"); - mimeTypes.Add ("sdml", "text/plain"); - mimeTypes.Add ("sdp", "application/sdp"); - mimeTypes.Add ("sdr", "application/sounder"); - mimeTypes.Add ("sea", "application/sea"); - mimeTypes.Add ("set", "application/set"); - mimeTypes.Add ("setpay", "application/set-payment-initiation"); - mimeTypes.Add ("setreg", "application/set-registration-initiation"); - mimeTypes.Add ("sgml", "text/sgml"); - mimeTypes.Add ("sgm", "text/sgml"); - mimeTypes.Add ("shar", "application/x-bsh"); - mimeTypes.Add ("sh", "text/x-script.sh"); - mimeTypes.Add ("shtml", "text/html"); - mimeTypes.Add ("sid", "audio/x-psid"); - mimeTypes.Add ("silo", "model/mesh"); - mimeTypes.Add ("sit", "application/x-sit"); - mimeTypes.Add ("skd", "application/x-koan"); - mimeTypes.Add ("skm", "application/x-koan"); - mimeTypes.Add ("skp", "application/x-koan"); - mimeTypes.Add ("skt", "application/x-koan"); - mimeTypes.Add ("sl", "application/x-seelogo"); - mimeTypes.Add ("smi", "application/smil"); - mimeTypes.Add ("smil", "application/smil"); - mimeTypes.Add ("snd", "audio/basic"); - mimeTypes.Add ("sol", "application/solids"); - mimeTypes.Add ("spc", "application/x-pkcs7-certificates"); - mimeTypes.Add ("spl", "application/futuresplash"); - mimeTypes.Add ("spr", "application/x-sprite"); - mimeTypes.Add ("sprite", "application/x-sprite"); - mimeTypes.Add ("spx", "audio/ogg"); - mimeTypes.Add ("src", "application/x-wais-source"); - mimeTypes.Add ("ssi", "text/x-server-parsed-html"); - mimeTypes.Add ("ssm", "application/streamingmedia"); - mimeTypes.Add ("sst", "application/vnd.ms-pki.certstore"); - mimeTypes.Add ("step", "application/step"); - mimeTypes.Add ("s", "text/x-asm"); - mimeTypes.Add ("stl", "application/sla"); - mimeTypes.Add ("stm", "text/html"); - mimeTypes.Add ("stp", "application/step"); - mimeTypes.Add ("sv4cpio", "application/x-sv4cpio"); - mimeTypes.Add ("sv4crc", "application/x-sv4crc"); - mimeTypes.Add ("svf", "image/x-dwg"); - mimeTypes.Add ("svg", "image/svg+xml"); - mimeTypes.Add ("svr", "application/x-world"); - mimeTypes.Add ("swf", "application/x-shockwave-flash"); - mimeTypes.Add ("talk", "text/x-speech"); - mimeTypes.Add ("t", "application/x-troff"); - mimeTypes.Add ("tar", "application/x-tar"); - mimeTypes.Add ("tbk", "application/toolbook"); - mimeTypes.Add ("tcl", "text/x-script.tcl"); - mimeTypes.Add ("tcsh", "text/x-script.tcsh"); - mimeTypes.Add ("tex", "application/x-tex"); - mimeTypes.Add ("texi", "application/x-texinfo"); - mimeTypes.Add ("texinfo", "application/x-texinfo"); - mimeTypes.Add ("text", "text/plain"); - mimeTypes.Add ("tgz", "application/x-compressed"); - mimeTypes.Add ("tiff", "image/tiff"); - mimeTypes.Add ("tif", "image/tiff"); - mimeTypes.Add ("tr", "application/x-troff"); - mimeTypes.Add ("trm", "application/x-msterminal"); - mimeTypes.Add ("ts", "text/x-typescript"); - mimeTypes.Add ("tsi", "audio/tsp-audio"); - mimeTypes.Add ("tsp", "audio/tsplayer"); - mimeTypes.Add ("tsv", "text/tab-separated-values"); - mimeTypes.Add ("ttf", "application/x-font-ttf"); - mimeTypes.Add ("turbot", "image/florian"); - mimeTypes.Add ("txt", "text/plain"); - mimeTypes.Add ("uil", "text/x-uil"); - mimeTypes.Add ("uls", "text/iuls"); - mimeTypes.Add ("unis", "text/uri-list"); - mimeTypes.Add ("uni", "text/uri-list"); - mimeTypes.Add ("unv", "application/i-deas"); - mimeTypes.Add ("uris", "text/uri-list"); - mimeTypes.Add ("uri", "text/uri-list"); - mimeTypes.Add ("ustar", "multipart/x-ustar"); - mimeTypes.Add ("uue", "text/x-uuencode"); - mimeTypes.Add ("uu", "text/x-uuencode"); - mimeTypes.Add ("vcd", "application/x-cdlink"); - mimeTypes.Add ("vcf", "text/vcard"); - mimeTypes.Add ("vcard", "text/vcard"); - mimeTypes.Add ("vcs", "text/x-vCalendar"); - mimeTypes.Add ("vda", "application/vda"); - mimeTypes.Add ("vdo", "video/vdo"); - mimeTypes.Add ("vew", "application/groupwise"); - mimeTypes.Add ("vivo", "video/vivo"); - mimeTypes.Add ("viv", "video/vivo"); - mimeTypes.Add ("vmd", "application/vocaltec-media-desc"); - mimeTypes.Add ("vmf", "application/vocaltec-media-file"); - mimeTypes.Add ("voc", "audio/voc"); - mimeTypes.Add ("vos", "video/vosaic"); - mimeTypes.Add ("vox", "audio/voxware"); - mimeTypes.Add ("vqe", "audio/x-twinvq-plugin"); - mimeTypes.Add ("vqf", "audio/x-twinvq"); - mimeTypes.Add ("vql", "audio/x-twinvq-plugin"); - mimeTypes.Add ("vrml", "application/x-vrml"); - mimeTypes.Add ("vrt", "x-world/x-vrt"); - mimeTypes.Add ("vsd", "application/x-visio"); - mimeTypes.Add ("vst", "application/x-visio"); - mimeTypes.Add ("vsw", "application/x-visio"); - mimeTypes.Add ("w60", "application/wordperfect6.0"); - mimeTypes.Add ("w61", "application/wordperfect6.1"); - mimeTypes.Add ("w6w", "application/msword"); - mimeTypes.Add ("wav", "audio/wav"); - mimeTypes.Add ("wb1", "application/x-qpro"); - mimeTypes.Add ("wbmp", "image/vnd.wap.wbmp"); - mimeTypes.Add ("wcm", "application/vnd.ms-works"); - mimeTypes.Add ("wdb", "application/vnd.ms-works"); - mimeTypes.Add ("web", "application/vnd.xara"); - mimeTypes.Add ("webm", "video/webm"); - mimeTypes.Add ("wiz", "application/msword"); - mimeTypes.Add ("wk1", "application/x-123"); - mimeTypes.Add ("wks", "application/vnd.ms-works"); - mimeTypes.Add ("wmf", "windows/metafile"); - mimeTypes.Add ("wmlc", "application/vnd.wap.wmlc"); - mimeTypes.Add ("wmlsc", "application/vnd.wap.wmlscriptc"); - mimeTypes.Add ("wmls", "text/vnd.wap.wmlscript"); - mimeTypes.Add ("wml", "text/vnd.wap.wml"); - mimeTypes.Add ("wmp", "video/x-ms-wmp"); - mimeTypes.Add ("wmv", "video/x-ms-wmv"); - mimeTypes.Add ("wmx", "video/x-ms-wmx"); - mimeTypes.Add ("woff", "application/x-woff"); - mimeTypes.Add ("word", "application/msword"); - mimeTypes.Add ("wp5", "application/wordperfect"); - mimeTypes.Add ("wp6", "application/wordperfect"); - mimeTypes.Add ("wp", "application/wordperfect"); - mimeTypes.Add ("wpd", "application/wordperfect"); - mimeTypes.Add ("wps", "application/vnd.ms-works"); - mimeTypes.Add ("wq1", "application/x-lotus"); - mimeTypes.Add ("wri", "application/mswrite"); - mimeTypes.Add ("wrl", "application/x-world"); - mimeTypes.Add ("wrz", "model/vrml"); - mimeTypes.Add ("wsc", "text/scriplet"); - mimeTypes.Add ("wsdl", "text/xml"); - mimeTypes.Add ("wsrc", "application/x-wais-source"); - mimeTypes.Add ("wtk", "application/x-wintalk"); - mimeTypes.Add ("wvx", "video/x-ms-wvx"); - mimeTypes.Add ("x3d", "model/x3d+xml"); - mimeTypes.Add ("x3db", "model/x3d+fastinfoset"); - mimeTypes.Add ("x3dv", "model/x3d-vrml"); - mimeTypes.Add ("xaf", "x-world/x-vrml"); - mimeTypes.Add ("xaml", "application/xaml+xml"); - mimeTypes.Add ("xap", "application/x-silverlight-app"); - mimeTypes.Add ("xbap", "application/x-ms-xbap"); - mimeTypes.Add ("xbm", "image/x-xbitmap"); - mimeTypes.Add ("xdr", "video/x-amt-demorun"); - mimeTypes.Add ("xgz", "xgl/drawing"); - mimeTypes.Add ("xht", "application/xhtml+xml"); - mimeTypes.Add ("xhtml", "application/xhtml+xml"); - mimeTypes.Add ("xif", "image/vnd.xiff"); - mimeTypes.Add ("xla", "application/vnd.ms-excel"); - mimeTypes.Add ("xlam", "application/vnd.ms-excel.addin.macroEnabled.12"); - mimeTypes.Add ("xl", "application/excel"); - mimeTypes.Add ("xlb", "application/excel"); - mimeTypes.Add ("xlc", "application/excel"); - mimeTypes.Add ("xld", "application/excel"); - mimeTypes.Add ("xlk", "application/excel"); - mimeTypes.Add ("xll", "application/excel"); - mimeTypes.Add ("xlm", "application/excel"); - mimeTypes.Add ("xls", "application/vnd.ms-excel"); - mimeTypes.Add ("xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"); - mimeTypes.Add ("xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"); - mimeTypes.Add ("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - mimeTypes.Add ("xlt", "application/vnd.ms-excel"); - mimeTypes.Add ("xltm", "application/vnd.ms-excel.template.macroEnabled.12"); - mimeTypes.Add ("xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"); - mimeTypes.Add ("xlv", "application/excel"); - mimeTypes.Add ("xlw", "application/excel"); - mimeTypes.Add ("xm", "audio/xm"); - mimeTypes.Add ("xml", "text/xml"); - mimeTypes.Add ("xmz", "xgl/movie"); - mimeTypes.Add ("xof", "x-world/x-vrml"); - mimeTypes.Add ("xpi", "application/x-xpinstall"); - mimeTypes.Add ("xpix", "application/x-vnd.ls-xpix"); - mimeTypes.Add ("xpm", "image/xpm"); - mimeTypes.Add ("xps", "application/vnd.ms-xpsdocument"); - mimeTypes.Add ("x-png", "image/png"); - mimeTypes.Add ("xsd", "text/xml"); - mimeTypes.Add ("xsl", "text/xml"); - mimeTypes.Add ("xslt", "text/xml"); - mimeTypes.Add ("xsr", "video/x-amt-showrun"); - mimeTypes.Add ("xwd", "image/x-xwd"); - mimeTypes.Add ("xyz", "chemical/x-pdb"); - mimeTypes.Add ("z", "application/x-compressed"); - mimeTypes.Add ("zip", "application/zip"); - mimeTypes.Add ("zsh", "text/x-script.zsh"); + extensions = LoadExtensions (); + mimeTypes = LoadMimeTypes (); + } + + static Dictionary LoadMimeTypes () + { + return new Dictionary (MimeUtils.OrdinalIgnoreCase) { + { ".323", "text/h323" }, + { ".3g2", "video/3gpp2" }, + { ".3gp", "video/3gpp" }, + { ".7z", "application/x-7z-compressed" }, + { ".aab", "application/x-authorware-bin" }, + { ".aac", "audio/aac" }, + { ".aam", "application/x-authorware-map" }, + { ".aas", "application/x-authorware-seg" }, + { ".abc", "text/vnd.abc" }, + { ".acgi", "text/html" }, + { ".acx", "application/internet-property-stream" }, + { ".afl", "video/animaflex" }, + { ".ai", "application/postscript" }, + { ".aif", "audio/aiff" }, + { ".aifc", "audio/aiff" }, + { ".aiff", "audio/aiff" }, + { ".aim", "application/x-aim" }, + { ".aip", "text/x-audiosoft-intra" }, + { ".ani", "application/x-navi-animation" }, + { ".aos", "application/x-nokia-9000-communicator-add-on-software" }, + { ".appcache", "text/cache-manifest" }, + { ".application", "application/x-ms-application" }, + { ".aps", "application/mime" }, + { ".art", "image/x-jg" }, + { ".asf", "video/x-ms-asf" }, + { ".asm", "text/x-asm" }, + { ".asp", "text/asp" }, + { ".asr", "video/x-ms-asf" }, + { ".asx", "application/x-mplayer2" }, + { ".atom", "application/atom+xml" }, + { ".au", "audio/x-au" }, + { ".avi", "video/avi" }, + { ".avs", "video/avs-video" }, + { ".axs", "application/olescript" }, + { ".bas", "text/plain" }, + { ".bcpio", "application/x-bcpio" }, + { ".bin", "application/octet-stream" }, + { ".bm", "image/bmp" }, + { ".bmp", "image/bmp" }, + { ".boo", "application/book" }, + { ".book", "application/book" }, + { ".boz", "application/x-bzip2" }, + { ".bsh", "application/x-bsh" }, + { ".bz2", "application/x-bzip2" }, + { ".bz", "application/x-bzip" }, + { ".cat", "application/vnd.ms-pki.seccat" }, + { ".ccad", "application/clariscad" }, + { ".cco", "application/x-cocoa" }, + { ".cc", "text/plain" }, + { ".cdf", "application/cdf" }, + { ".cer", "application/pkix-cert" }, + { ".cha", "application/x-chat" }, + { ".chat", "application/x-chat" }, + { ".chm", "application/vnd.ms-htmlhelp" }, + { ".class", "application/x-java-applet" }, + { ".clp", "application/x-msclip" }, + { ".cmx", "image/x-cmx" }, + { ".cod", "image/cis-cod" }, + { ".coffee", "text/x-coffeescript" }, + { ".conf", "text/plain" }, + { ".cpio", "application/x-cpio" }, + { ".cpp", "text/plain" }, + { ".cpt", "application/x-cpt" }, + { ".crd", "application/x-mscardfile" }, + { ".crl", "application/pkix-crl" }, + { ".crt", "application/pkix-cert" }, + { ".csh", "application/x-csh" }, + { ".css", "text/css" }, + { ".csv", "text/csv" }, + { ".cs", "text/plain" }, + { ".c", "text/plain" }, + { ".c++", "text/plain" }, + { ".cxx", "text/plain" }, + { ".dart", "application/dart" }, + { ".dcr", "application/x-director" }, + { ".deb", "application/x-deb" }, + { ".deepv", "application/x-deepv" }, + { ".def", "text/plain" }, + { ".deploy", "application/octet-stream" }, + { ".der", "application/x-x509-ca-cert" }, + { ".dib", "image/bmp" }, + { ".dif", "video/x-dv" }, + { ".dir", "application/x-director" }, + { ".disco", "text/xml" }, + { ".dll", "application/x-msdownload" }, + { ".dl", "video/dl" }, + { ".doc", "application/msword" }, + { ".docm", "application/vnd.ms-word.document.macroEnabled.12" }, + { ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }, + { ".dot", "application/msword" }, + { ".dotm", "application/vnd.ms-word.template.macroEnabled.12" }, + { ".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template" }, + { ".dp", "application/commonground" }, + { ".drw", "application/drafting" }, + { ".dtd", "application/xml-dtd" }, + { ".dvi", "application/x-dvi" }, + { ".dv", "video/x-dv" }, + { ".dwg", "application/acad" }, + { ".dxf", "application/dxf" }, + { ".dxr", "application/x-director" }, + { ".el", "text/x-script.elisp" }, + { ".elc", "application/x-elc" }, + { ".eml", "message/rfc822" }, + { ".eot", "application/vnd.bw-fontobject" }, + { ".eps", "application/postscript" }, + { ".epub", "application/epub+zip" }, + { ".es", "application/x-esrehber" }, + { ".etx", "text/x-setext" }, + { ".evy", "application/envoy" }, + { ".exe", "application/octet-stream" }, + { ".f77", "text/plain" }, + { ".f90", "text/plain" }, + { ".fdf", "application/vnd.fdf" }, + { ".fif", "image/fif" }, + { ".flac", "audio/x-flac" }, + { ".fli", "video/fli" }, + { ".flx", "text/vnd.fmi.flexstor" }, + { ".fmf", "video/x-atomic3d-feature" }, + { ".for", "text/plain" }, + { ".fpx", "image/vnd.fpx" }, + { ".frl", "application/freeloader" }, + { ".fsx", "application/fsharp-script" }, + { ".g3", "image/g3fax" }, + { ".gif", "image/gif" }, + { ".gl", "video/gl" }, + { ".gsd", "audio/x-gsm" }, + { ".gsm", "audio/x-gsm" }, + { ".gsp", "application/x-gsp" }, + { ".gss", "application/x-gss" }, + { ".gtar", "application/x-gtar" }, + { ".g", "text/plain" }, + { ".gz", "application/x-gzip" }, + { ".gzip", "application/x-gzip" }, + { ".hdf", "application/x-hdf" }, + { ".help", "application/x-helpfile" }, + { ".hgl", "application/vnd.hp-HPGL" }, + { ".hh", "text/plain" }, + { ".hlb", "text/x-script" }, + { ".hlp", "application/x-helpfile" }, + { ".hpg", "application/vnd.hp-HPGL" }, + { ".hpgl", "application/vnd.hp-HPGL" }, + { ".hqx", "application/binhex" }, + { ".hta", "application/hta" }, + { ".htc", "text/x-component" }, + { ".h", "text/plain" }, + { ".htmls", "text/html" }, + { ".html", "text/html" }, + { ".htm", "text/html" }, + { ".htt", "text/webviewhtml" }, + { ".htx", "text/html" }, + { ".ico", "image/x-icon" }, + { ".ics", "text/calendar" }, + { ".idc", "text/plain" }, + { ".ief", "image/ief" }, + { ".iefs", "image/ief" }, + { ".iges", "model/iges" }, + { ".igs", "model/iges" }, + { ".iii", "application/x-iphone" }, + { ".ima", "application/x-ima" }, + { ".imap", "application/x-httpd-imap" }, + { ".inf", "application/inf" }, + { ".ins", "application/x-internett-signup" }, + { ".ip", "application/x-ip2" }, + { ".isp", "application/x-internet-signup" }, + { ".isu", "video/x-isvideo" }, + { ".it", "audio/it" }, + { ".iv", "application/x-inventor" }, + { ".ivf", "video/x-ivf" }, + { ".ivy", "application/x-livescreen" }, + { ".jam", "audio/x-jam" }, + { ".jar", "application/java-archive" }, + { ".java", "text/plain" }, + { ".jav", "text/plain" }, + { ".jcm", "application/x-java-commerce" }, + { ".jfif", "image/jpeg" }, + { ".jfif-tbnl", "image/jpeg" }, + { ".jpeg", "image/jpeg" }, + { ".jpe", "image/jpeg" }, + { ".jpg", "image/jpeg" }, + { ".jps", "image/x-jps" }, + { ".js", "application/javascript" }, + { ".json", "application/json" }, + { ".jut", "image/jutvision" }, + { ".kar", "audio/midi" }, + { ".ksh", "text/x-script.ksh" }, + { ".la", "audio/nspaudio" }, + { ".lam", "audio/x-liveaudio" }, + { ".latex", "application/x-latex" }, + { ".list", "text/plain" }, + { ".lma", "audio/nspaudio" }, + { ".log", "text/plain" }, + { ".lsp", "application/x-lisp" }, + { ".lst", "text/plain" }, + { ".lsx", "text/x-la-asf" }, + { ".ltx", "application/x-latex" }, + { ".m13", "application/x-msmediaview" }, + { ".m14", "application/x-msmediaview" }, + { ".m1v", "video/mpeg" }, + { ".m2a", "audio/mpeg" }, + { ".m2v", "video/mpeg" }, + { ".m3u", "audio/x-mpequrl" }, + { ".m4a", "audio/mp4" }, + { ".m4v", "video/mp4" }, + { ".man", "application/x-troff-man" }, + { ".manifest", "application/x-ms-manifest" }, + { ".map", "application/x-navimap" }, + { ".mar", "text/plain" }, + { ".markdown", "text/markdown" }, + { ".mbd", "application/mbedlet" }, + { ".mc$", "application/x-magic-cap-package-1.0" }, + { ".mcd", "application/mcad" }, + { ".mcf", "image/vasa" }, + { ".mcp", "application/netmc" }, + { ".md", "text/markdown" }, + { ".mdb", "application/x-msaccess" }, + { ".mesh", "model/mesh" }, + { ".me", "application/x-troff-me" }, + { ".mid", "audio/midi" }, + { ".midi", "audio/midi" }, + { ".mif", "application/x-mif" }, + { ".mjf", "audio/x-vnd.AudioExplosion.MjuiceMediaFile" }, + { ".mjpg", "video/x-motion-jpeg" }, + { ".mm", "application/base64" }, + { ".mme", "application/base64" }, + { ".mny", "application/x-msmoney" }, + { ".mod", "audio/mod" }, + { ".mov", "video/quicktime" }, + { ".movie", "video/x-sgi-movie" }, + { ".mp2", "video/mpeg" }, + { ".mp3", "audio/mpeg" }, + { ".mp4", "video/mp4" }, + { ".mp4a", "audio/mp4" }, + { ".mp4v", "video/mp4" }, + { ".mpa", "audio/mpeg" }, + { ".mpc", "application/x-project" }, + { ".mpeg", "video/mpeg" }, + { ".mpe", "video/mpeg" }, + { ".mpga", "audio/mpeg" }, + { ".mpg", "video/mpeg" }, + { ".mpp", "application/vnd.ms-project" }, + { ".mpt", "application/x-project" }, + { ".mpv2", "video/mpeg" }, + { ".mpv", "application/x-project" }, + { ".mpx", "application/x-project" }, + { ".mrc", "application/marc" }, + { ".ms", "application/x-troff-ms" }, + { ".msh", "model/mesh" }, + { ".m", "text/plain" }, + { ".mvb", "application/x-msmediaview" }, + { ".mv", "video/x-sgi-movie" }, + { ".mzz", "application/x-vnd.AudioExplosion.mzz" }, + { ".nap", "image/naplps" }, + { ".naplps", "image/naplps" }, + { ".nc", "application/x-netcdf" }, + { ".ncm", "application/vnd.nokia.configuration-message" }, + { ".niff", "image/x-niff" }, + { ".nif", "image/x-niff" }, + { ".nix", "application/x-mix-transfer" }, + { ".nsc", "application/x-conference" }, + { ".nvd", "application/x-navidoc" }, + { ".nws", "message/rfc822" }, + { ".oda", "application/oda" }, + { ".ods", "application/oleobject" }, + { ".oga", "audio/ogg" }, + { ".ogg", "audio/ogg" }, + { ".ogv", "video/ogg" }, + { ".ogx", "application/ogg" }, + { ".omc", "application/x-omc" }, + { ".omcd", "application/x-omcdatamaker" }, + { ".omcr", "application/x-omcregerator" }, + { ".opus", "audio/ogg" }, + { ".otf", "font/otf" }, + { ".oxps", "application/oxps" }, + { ".p10", "application/pkcs10" }, + { ".p12", "application/pkcs-12" }, + { ".p7a", "application/x-pkcs7-signature" }, + { ".p7b", "application/x-pkcs7-certificates" }, + { ".p7c", "application/pkcs7-mime" }, + { ".p7m", "application/pkcs7-mime" }, + { ".p7r", "application/x-pkcs7-certreqresp" }, + { ".p7s", "application/pkcs7-signature" }, + { ".part", "application/pro_eng" }, + { ".pas", "text/pascal" }, + { ".pbm", "image/x-portable-bitmap" }, + { ".pcl", "application/x-pcl" }, + { ".pct", "image/x-pict" }, + { ".pcx", "image/x-pcx" }, + { ".pdf", "application/pdf" }, + { ".pfx", "application/x-pkcs12" }, + { ".pgm", "image/x-portable-graymap" }, + { ".pic", "image/pict" }, + { ".pict", "image/pict" }, + { ".pkg", "application/x-newton-compatible-pkg" }, + { ".pko", "application/vnd.ms-pki.pko" }, + { ".pl", "text/plain" }, + { ".plx", "application/x-PiXCLscript" }, + { ".pm4", "application/x-pagemaker" }, + { ".pm5", "application/x-pagemaker" }, + { ".pma", "application/x-perfmon" }, + { ".pmc", "application/x-perfmon" }, + { ".pm", "image/x-xpixmap" }, + { ".pml", "application/x-perfmon" }, + { ".pmr", "application/x-perfmon" }, + { ".pmw", "application/x-perfmon" }, + { ".png", "image/png" }, + { ".pnm", "application/x-portable-anymap" }, + { ".pot", "application/vnd.ms-powerpoint" }, + { ".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12" }, + { ".potx", "application/vnd.openxmlformats-officedocument.presentationml.template" }, + { ".pov", "model/x-pov" }, + { ".ppa", "application/vnd.ms-powerpoint" }, + { ".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12" }, + { ".ppm", "image/x-portable-pixmap" }, + { ".pps", "application/vnd.ms-powerpoint" }, + { ".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12" }, + { ".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow" }, + { ".ppt", "application/vnd.ms-powerpoint" }, + { ".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12" }, + { ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" }, + { ".ppz", "application/mspowerpoint" }, + { ".pre", "application/x-freelance" }, + { ".prf", "application/pics-rules" }, + { ".prt", "application/pro_eng" }, + { ".ps", "application/postscript" }, + { ".p", "text/x-pascal" }, + { ".pub", "application/x-mspublisher" }, + { ".pwz", "application/vnd.ms-powerpoint" }, + { ".pyc", "application/x-bytecode.python" }, + { ".py", "text/x-script.phyton" }, + { ".qcp", "audio/vnd.qcelp" }, + { ".qif", "image/x-quicktime" }, + { ".qtc", "video/x-qtc" }, + { ".qtif", "image/x-quicktime" }, + { ".qti", "image/x-quicktime" }, + { ".qt", "video/quicktime" }, + { ".ra", "audio/x-pn-realaudio" }, + { ".ram", "audio/x-pn-realaudio" }, + { ".ras", "application/x-cmu-raster" }, + { ".rast", "image/cmu-raster" }, + { ".rexx", "text/x-script.rexx" }, + { ".rf", "image/vnd.rn-realflash" }, + { ".rgb", "image/x-rgb" }, + { ".rm", "application/vnd.rn-realmedia" }, + { ".rmi", "audio/mid" }, + { ".rmm", "audio/x-pn-realaudio" }, + { ".rmp", "audio/x-pn-realaudio" }, + { ".rng", "application/ringing-tones" }, + { ".rnx", "application/vnd.rn-realplayer" }, + { ".roff", "application/x-troff" }, + { ".rp", "image/vnd.rn-realpix" }, + { ".rpm", "audio/x-pn-realaudio-plugin" }, + { ".rss", "application/rss+xml" }, + { ".rtf", "text/rtf" }, + { ".rt", "text/richtext" }, + { ".rtx", "text/richtext" }, + { ".rv", "video/vnd.rn-realvideo" }, + { ".s3m", "audio/s3m" }, + { ".sbk", "application/x-tbook" }, + { ".scd", "application/x-msschedule" }, + { ".scm", "application/x-lotusscreencam" }, + { ".sct", "text/scriptlet" }, + { ".sdml", "text/plain" }, + { ".sdp", "application/sdp" }, + { ".sdr", "application/sounder" }, + { ".sea", "application/sea" }, + { ".set", "application/set" }, + { ".setpay", "application/set-payment-initiation" }, + { ".setreg", "application/set-registration-initiation" }, + { ".sgml", "text/sgml" }, + { ".sgm", "text/sgml" }, + { ".shar", "application/x-bsh" }, + { ".sh", "text/x-script.sh" }, + { ".shtml", "text/html" }, + { ".sid", "audio/x-psid" }, + { ".silo", "model/mesh" }, + { ".sit", "application/x-sit" }, + { ".skd", "application/x-koan" }, + { ".skm", "application/x-koan" }, + { ".skp", "application/x-koan" }, + { ".skt", "application/x-koan" }, + { ".sl", "application/x-seelogo" }, + { ".smi", "application/smil" }, + { ".smil", "application/smil" }, + { ".snd", "audio/basic" }, + { ".sol", "application/solids" }, + { ".spc", "application/x-pkcs7-certificates" }, + { ".spl", "application/futuresplash" }, + { ".spr", "application/x-sprite" }, + { ".sprite", "application/x-sprite" }, + { ".spx", "audio/ogg" }, + { ".src", "application/x-wais-source" }, + { ".ssi", "text/x-server-parsed-html" }, + { ".ssm", "application/streamingmedia" }, + { ".sst", "application/vnd.ms-pki.certstore" }, + { ".step", "application/step" }, + { ".s", "text/x-asm" }, + { ".stl", "application/sla" }, + { ".stm", "text/html" }, + { ".stp", "application/step" }, + { ".sv4cpio", "application/x-sv4cpio" }, + { ".sv4crc", "application/x-sv4crc" }, + { ".svf", "image/x-dwg" }, + { ".svg", "image/svg+xml" }, + { ".svr", "application/x-world" }, + { ".swf", "application/x-shockwave-flash" }, + { ".talk", "text/x-speech" }, + { ".t", "application/x-troff" }, + { ".tar", "application/x-tar" }, + { ".tbk", "application/toolbook" }, + { ".tcl", "text/x-script.tcl" }, + { ".tcsh", "text/x-script.tcsh" }, + { ".tex", "application/x-tex" }, + { ".texi", "application/x-texinfo" }, + { ".texinfo", "application/x-texinfo" }, + { ".text", "text/plain" }, + { ".tgz", "application/x-compressed" }, + { ".tiff", "image/tiff" }, + { ".tif", "image/tiff" }, + { ".tr", "application/x-troff" }, + { ".trm", "application/x-msterminal" }, + { ".ts", "application/typescript" }, + { ".tsi", "audio/tsp-audio" }, + { ".tsp", "audio/tsplayer" }, + { ".tsv", "text/tab-separated-values" }, + { ".ttc", "font/collection" }, + { ".ttf", "font/ttf" }, + { ".txt", "text/plain" }, + { ".uil", "text/x-uil" }, + { ".uls", "text/iuls" }, + { ".unis", "text/uri-list" }, + { ".uni", "text/uri-list" }, + { ".unv", "application/i-deas" }, + { ".uris", "text/uri-list" }, + { ".uri", "text/uri-list" }, + { ".ustar", "multipart/x-ustar" }, + { ".uue", "text/x-uuencode" }, + { ".uu", "text/x-uuencode" }, + { ".vcd", "application/x-cdlink" }, + { ".vcf", "text/vcard" }, + { ".vcard", "text/vcard" }, + { ".vcs", "text/x-vcalendar" }, + { ".vda", "application/vda" }, + { ".vdo", "video/vdo" }, + { ".vew", "application/groupwise" }, + { ".vivo", "video/vnd.vivo" }, + { ".viv", "video/vnd.vivo" }, + { ".vmd", "application/vocaltec-media-desc" }, + { ".vmf", "application/vocaltec-media-file" }, + { ".voc", "audio/voc" }, + { ".vos", "video/vosaic" }, + { ".vox", "audio/voxware" }, + { ".vqe", "audio/x-twinvq-plugin" }, + { ".vqf", "audio/x-twinvq" }, + { ".vql", "audio/x-twinvq-plugin" }, + { ".vrml", "application/x-vrml" }, + { ".vsd", "application/x-visio" }, + { ".vst", "application/x-visio" }, + { ".vsw", "application/x-visio" }, + { ".w60", "application/wordperfect6.0" }, + { ".w61", "application/wordperfect6.1" }, + { ".w6w", "application/msword" }, + { ".wav", "audio/wav" }, + { ".wb1", "application/x-qpro" }, + { ".wbmp", "image/vnd.wap.wbmp" }, + { ".wcm", "application/vnd.ms-works" }, + { ".wdb", "application/vnd.ms-works" }, + { ".web", "application/vnd.xara" }, + { ".weba", "audio/webm" }, + { ".webm", "video/webm" }, + { ".webp", "image/webp" }, + { ".wiz", "application/msword" }, + { ".wk1", "application/x-123" }, + { ".wks", "application/vnd.ms-works" }, + { ".wmf", "image/wmf" }, + { ".wmlc", "application/vnd.wap.wmlc" }, + { ".wmlsc", "application/vnd.wap.wmlscriptc" }, + { ".wmls", "text/vnd.wap.wmlscript" }, + { ".wml", "text/vnd.wap.wml" }, + { ".wmp", "video/x-ms-wmp" }, + { ".wmv", "video/x-ms-wmv" }, + { ".wmx", "video/x-ms-wmx" }, + { ".woff", "font/woff" }, + { ".woff2", "font/woff2" }, + { ".word", "application/msword" }, + { ".wp5", "application/wordperfect" }, + { ".wp6", "application/wordperfect" }, + { ".wp", "application/wordperfect" }, + { ".wpd", "application/wordperfect" }, + { ".wps", "application/vnd.ms-works" }, + { ".wq1", "application/x-lotus" }, + { ".wri", "application/mswrite" }, + { ".wrl", "application/x-world" }, + { ".wrz", "model/vrml" }, + { ".wsc", "text/scriplet" }, + { ".wsdl", "text/xml" }, + { ".wsrc", "application/x-wais-source" }, + { ".wtk", "application/x-wintalk" }, + { ".wvx", "video/x-ms-wvx" }, + { ".x3d", "model/x3d+xml" }, + { ".x3db", "model/x3d+fastinfoset" }, + { ".x3dv", "model/x3d-vrml" }, + { ".xaml", "application/xaml+xml" }, + { ".xap", "application/x-silverlight-app" }, + { ".xbap", "application/x-ms-xbap" }, + { ".xbm", "image/x-xbitmap" }, + { ".xdr", "video/x-amt-demorun" }, + { ".xht", "application/xhtml+xml" }, + { ".xhtml", "application/xhtml+xml" }, + { ".xif", "image/vnd.xiff" }, + { ".xla", "application/vnd.ms-excel" }, + { ".xlam", "application/vnd.ms-excel.addin.macroEnabled.12" }, + { ".xl", "application/excel" }, + { ".xlb", "application/excel" }, + { ".xlc", "application/excel" }, + { ".xld", "application/excel" }, + { ".xlk", "application/excel" }, + { ".xll", "application/excel" }, + { ".xlm", "application/excel" }, + { ".xls", "application/vnd.ms-excel" }, + { ".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12" }, + { ".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12" }, + { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }, + { ".xlt", "application/vnd.ms-excel" }, + { ".xltm", "application/vnd.ms-excel.template.macroEnabled.12" }, + { ".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template" }, + { ".xlv", "application/excel" }, + { ".xlw", "application/excel" }, + { ".xm", "audio/xm" }, + { ".xml", "text/xml" }, + { ".xpi", "application/x-xpinstall" }, + { ".xpix", "application/x-vnd.ls-xpix" }, + { ".xpm", "image/xpm" }, + { ".xps", "application/vnd.ms-xpsdocument" }, + { ".x-png", "image/png" }, + { ".xsd", "text/xml" }, + { ".xsl", "text/xml" }, + { ".xslt", "text/xml" }, + { ".xsr", "video/x-amt-showrun" }, + { ".xwd", "image/x-xwd" }, + { ".z", "application/x-compressed" }, + { ".zip", "application/zip" }, + { ".zsh", "text/x-script.zsh" } + }; + } + + static Dictionary LoadExtensions () + { + return new Dictionary (MimeUtils.OrdinalIgnoreCase) { + { "application/acad", ".dwg" }, + { "application/atom+xml", ".atom" }, + { "application/base64", ".mm" }, + { "application/binhex", ".hqx" }, + { "application/book", ".boo" }, + { "application/cdf", ".cdf" }, + { "application/clariscad", ".ccad" }, + { "application/commonground", ".dp" }, + { "application/dart", ".dart" }, + { "application/drafting", ".drw" }, + { "application/dxf", ".dxf" }, + { "application/envoy", ".evy" }, + { "application/epub+zip", ".epub" }, + { "application/excel", ".xls" }, + { "application/freeloader", ".frl" }, + { "application/fsharp-script", ".fsx" }, + { "application/futuresplash", ".spl" }, + { "application/groupwise", ".vew" }, + { "application/hta", ".hta" }, + { "application/i-deas", ".unv" }, + { "application/inf", ".inf" }, + { "application/internet-property-stream", ".acx" }, + { "application/java-archive", ".jar" }, + { "application/javascript", ".js" }, + { "application/json", ".json" }, + { "application/marc", ".mrc" }, + { "application/mbedlet", ".mbd" }, + { "application/mcad", ".mcd" }, + { "application/mime", ".aps" }, + { "application/mspowerpoint", ".ppz" }, + { "application/msword", ".doc" }, + { "application/mswrite", ".wri" }, + { "application/netmc", ".mcp" }, + { "application/octet-stream", ".bin" }, + { "application/oda", ".oda" }, + { "application/ogg", ".ogx" }, + { "application/oleobject", ".ods" }, + { "application/olescript", ".axs" }, + { "application/oxps", ".oxps" }, + { "application/pdf", ".pdf" }, + { "application/pics-rules", ".prf" }, + { "application/pkcs-12", ".p12" }, + { "application/pkcs10", ".p10" }, + { "application/pkcs7-mime", ".p7m" }, + { "application/pkcs7-signature", ".p7s" }, + { "application/pkix-cert", ".cer" }, + { "application/pkix-crl", ".crl" }, + { "application/postscript", ".ps" }, + { "application/pro_eng", ".part" }, + { "application/ringing-tones", ".rng" }, + { "application/rss+xml", ".rss" }, + { "application/sdp", ".sdp" }, + { "application/sea", ".sea" }, + { "application/set", ".set" }, + { "application/set-payment-initiation", ".setpay" }, + { "application/set-registration-initiation", ".setreg" }, + { "application/sla", ".stl" }, + { "application/smil", ".smi" }, + { "application/solids", ".sol" }, + { "application/sounder", ".sdr" }, + { "application/step", ".step" }, + { "application/streamingmedia", ".ssm" }, + { "application/toolbook", ".tbk" }, + { "application/typescript", ".ts" }, + { "application/vda", ".vda" }, + { "application/vnd.bw-fontobject", ".eot" }, + { "application/vnd.fdf", ".fdf" }, + { "application/vnd.hp-HPGL", ".hgl" }, + { "application/vnd.ms-excel", ".xls" }, + { "application/vnd.ms-excel.addin.macroEnabled.12", ".xlam" }, + { "application/vnd.ms-excel.sheet.binary.macroEnabled.12", ".xlsb" }, + { "application/vnd.ms-excel.sheet.macroEnabled.12", ".xlsm" }, + { "application/vnd.ms-excel.template.macroEnabled.12", ".xltm" }, + { "application/vnd.ms-htmlhelp", ".chm" }, + { "application/vnd.ms-pki.certstore", ".sst" }, + { "application/vnd.ms-pki.pko", ".pko" }, + { "application/vnd.ms-pki.seccat", ".cat" }, + { "application/vnd.ms-powerpoint", ".ppt" }, + { "application/vnd.ms-powerpoint.addin.macroEnabled.12", ".ppam" }, + { "application/vnd.ms-powerpoint.presentation.macroEnabled.12", ".pptm" }, + { "application/vnd.ms-powerpoint.slideshow.macroEnabled.12", ".ppsm" }, + { "application/vnd.ms-powerpoint.template.macroEnabled.12", ".potm" }, + { "application/vnd.ms-project", ".mpp" }, + { "application/vnd.ms-word.document.macroEnabled.12", ".docm" }, + { "application/vnd.ms-word.template.macroEnabled.12", ".dotm" }, + { "application/vnd.ms-works", ".wcm" }, + { "application/vnd.ms-xpsdocument", ".xps" }, + { "application/vnd.nokia.configuration-message", ".ncm" }, + { "application/vnd.openxmlformats-officedocument.presentationml.presentation", ".pptx" }, + { "application/vnd.openxmlformats-officedocument.presentationml.slideshow", ".ppsx" }, + { "application/vnd.openxmlformats-officedocument.presentationml.template", ".potx" }, + { "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".xlsx" }, + { "application/vnd.openxmlformats-officedocument.spreadsheetml.template", ".xltx" }, + { "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".docx" }, + { "application/vnd.openxmlformats-officedocument.wordprocessingml.template", ".dotx" }, + { "application/vnd.rn-realmedia", ".rm" }, + { "application/vnd.rn-realplayer", ".rnx" }, + { "application/vnd.wap.wmlc", ".wmlc" }, + { "application/vnd.wap.wmlscriptc", ".wmlsc" }, + { "application/vnd.xara", ".web" }, + { "application/vocaltec-media-desc", ".vmd" }, + { "application/vocaltec-media-file", ".vmf" }, + { "application/wordperfect", ".wp5" }, + { "application/wordperfect6.0", ".w60" }, + { "application/wordperfect6.1", ".w61" }, + { "application/x-123", ".wk1" }, + { "application/x-7z-compressed", ".7z" }, + { "application/x-aim", ".aim" }, + { "application/x-authorware-bin", ".aab" }, + { "application/x-authorware-map", ".aam" }, + { "application/x-authorware-seg", ".aas" }, + { "application/x-bcpio", ".bcpio" }, + { "application/x-bsh", ".bsh" }, + { "application/x-bytecode.python", ".pyc" }, + { "application/x-bzip", ".bz" }, + { "application/x-bzip2", ".bz2" }, + { "application/x-cdlink", ".vcd" }, + { "application/x-chat", ".cha" }, + { "application/x-cmu-raster", ".ras" }, + { "application/x-cocoa", ".cco" }, + { "application/x-compressed", ".z" }, + { "application/x-conference", ".nsc" }, + { "application/x-cpio", ".cpio" }, + { "application/x-cpt", ".cpt" }, + { "application/x-csh", ".csh" }, + { "application/x-deb", ".deb" }, + { "application/x-deepv", ".deepv" }, + { "application/x-director", ".dcr" }, + { "application/x-dvi", ".dvi" }, + { "application/x-elc", ".elc" }, + { "application/x-esrehber", ".es" }, + { "application/x-font-ttf", ".ttf" }, + { "application/x-freelance", ".pre" }, + { "application/x-gsp", ".gsp" }, + { "application/x-gss", ".gss" }, + { "application/x-gtar", ".gtar" }, + { "application/x-gzip", ".gz" }, + { "application/x-hdf", ".hdf" }, + { "application/x-helpfile", ".help" }, + { "application/x-httpd-imap", ".imap" }, + { "application/x-ima", ".ima" }, + { "application/x-internet-signup", ".isp" }, + { "application/x-internett-signup", ".ins" }, + { "application/x-inventor", ".iv" }, + { "application/x-ip2", ".ip" }, + { "application/x-iphone", ".iii" }, + { "application/x-java-applet", ".class" }, + { "application/x-java-commerce", ".jcm" }, + { "application/x-koan", ".skd" }, + { "application/x-latex", ".latex" }, + { "application/x-lisp", ".lsp" }, + { "application/x-livescreen", ".ivy" }, + { "application/x-lotus", ".wq1" }, + { "application/x-lotusscreencam", ".scm" }, + { "application/x-magic-cap-package-1.0", ".mc$" }, + { "application/x-mif", ".mif" }, + { "application/x-mix-transfer", ".nix" }, + { "application/x-mplayer2", ".asx" }, + { "application/x-ms-application", ".application" }, + { "application/x-ms-manifest", ".manifest" }, + { "application/x-ms-xbap", ".xbap" }, + { "application/x-msaccess", ".mdb" }, + { "application/x-mscardfile", ".crd" }, + { "application/x-msclip", ".clp" }, + { "application/x-msdownload", ".dll" }, + { "application/x-msmediaview", ".m13" }, + { "application/x-msmoney", ".mny" }, + { "application/x-mspublisher", ".pub" }, + { "application/x-msschedule", ".scd" }, + { "application/x-msterminal", ".trm" }, + { "application/x-navi-animation", ".ani" }, + { "application/x-navidoc", ".nvd" }, + { "application/x-navimap", ".map" }, + { "application/x-netcdf", ".nc" }, + { "application/x-newton-compatible-pkg", ".pkg" }, + { "application/x-nokia-9000-communicator-add-on-software", ".aos" }, + { "application/x-omc", ".omc" }, + { "application/x-omcdatamaker", ".omcd" }, + { "application/x-omcregerator", ".omcr" }, + { "application/x-pagemaker", ".pm4" }, + { "application/x-pcl", ".pcl" }, + { "application/x-perfmon", ".pma" }, + { "application/x-PiXCLscript", ".plx" }, + { "application/x-pkcs12", ".pfx" }, + { "application/x-pkcs7-certificates", ".p7b" }, + { "application/x-pkcs7-certreqresp", ".p7r" }, + { "application/x-pkcs7-signature", ".p7a" }, + { "application/x-portable-anymap", ".pnm" }, + { "application/x-project", ".mpc" }, + { "application/x-qpro", ".wb1" }, + { "application/x-seelogo", ".sl" }, + { "application/x-shockwave-flash", ".swf" }, + { "application/x-silverlight-app", ".xap" }, + { "application/x-sit", ".sit" }, + { "application/x-sprite", ".spr" }, + { "application/x-sv4cpio", ".sv4cpio" }, + { "application/x-sv4crc", ".sv4crc" }, + { "application/x-tar", ".tar" }, + { "application/x-tbook", ".sbk" }, + { "application/x-tex", ".tex" }, + { "application/x-texinfo", ".texi" }, + { "application/x-troff", ".roff" }, + { "application/x-troff-man", ".man" }, + { "application/x-troff-me", ".me" }, + { "application/x-troff-ms", ".ms" }, + { "application/x-visio", ".vsd" }, + { "application/x-vnd.AudioExplosion.mzz", ".mzz" }, + { "application/x-vnd.ls-xpix", ".xpix" }, + { "application/x-vrml", ".vrml" }, + { "application/x-wais-source", ".src" }, + { "application/x-wintalk", ".wtk" }, + { "application/x-woff", ".woff" }, + { "application/x-world", ".svr" }, + { "application/x-x509-ca-cert", ".der" }, + { "application/x-xpinstall", ".xpi" }, + { "application/xaml+xml", ".xaml" }, + { "application/xhtml+xml", ".xhtml" }, + { "application/xml", ".xml" }, + { "application/xml-dtd", ".dtd" }, + { "application/zip", ".zip" }, + { "audio/aac", ".aac" }, + { "audio/aiff", ".aiff" }, + { "audio/basic", ".snd" }, + { "audio/it", ".it" }, + { "audio/mid", ".rmi" }, + { "audio/midi", ".mid" }, + { "audio/mod", ".mod" }, + { "audio/mp4", ".mp4" }, + { "audio/mpeg", ".mp3" }, + { "audio/nspaudio", ".la" }, + { "audio/ogg", ".ogg" }, + { "audio/s3m", ".s3m" }, + { "audio/tsp-audio", ".tsi" }, + { "audio/tsplayer", ".tsp" }, + { "audio/vnd.qcelp", ".qcp" }, + { "audio/voc", ".voc" }, + { "audio/vorbis", ".ogg" }, + { "audio/voxware", ".vox" }, + { "audio/wav", ".wav" }, + { "audio/webm", ".weba" }, + { "audio/x-au", ".au" }, + { "audio/x-flac", ".flac" }, + { "audio/x-gsm", ".gsd" }, + { "audio/x-jam", ".jam" }, + { "audio/x-liveaudio", ".lam" }, + { "audio/x-mpequrl", ".m3u" }, + { "audio/x-pn-realaudio", ".ra" }, + { "audio/x-pn-realaudio-plugin", ".rpm" }, + { "audio/x-psid", ".sid" }, + { "audio/x-twinvq", ".vqf" }, + { "audio/x-twinvq-plugin", ".vqe" }, + { "audio/x-vnd.AudioExplosion.MjuiceMediaFile", ".mjf" }, + { "audio/xm", ".xm" }, + { "font/collection", ".ttc" }, + { "font/otf", ".otf" }, + { "font/sfnt", ".ttf" }, + { "font/ttf", ".ttf" }, + { "font/woff", ".woff" }, + { "font/woff2", ".woff2" }, + { "image/bmp", ".bmp" }, + { "image/cis-cod", ".cod" }, + { "image/cmu-raster", ".rast" }, + { "image/fif", ".fif" }, + { "image/g3fax", ".g3" }, + { "image/gif", ".gif" }, + { "image/ief", ".ief" }, + { "image/jpeg", ".jpg" }, + { "image/jutvision", ".jut" }, + { "image/naplps", ".nap" }, + { "image/pict", ".pic" }, + { "image/png", ".png" }, + { "image/svg+xml", ".svg" }, + { "image/tiff", ".tif" }, + { "image/vasa", ".mcf" }, + { "image/vnd.fpx", ".fpx" }, + { "image/vnd.rn-realflash", ".rf" }, + { "image/vnd.rn-realpix", ".rp" }, + { "image/vnd.wap.wbmp", ".wbmp" }, + { "image/vnd.xiff", ".xif" }, + { "image/webp", ".webp" }, + { "image/wmf", ".wmf" }, + { "image/x-cmx", ".cmx" }, + { "image/x-dwg", ".svf" }, + { "image/x-icon", ".ico" }, + { "image/x-jg", ".art" }, + { "image/x-jps", ".jps" }, + { "image/x-niff", ".niff" }, + { "image/x-pcx", ".pcx" }, + { "image/x-pict", ".pct" }, + { "image/x-png", ".png" }, + { "image/x-portable-bitmap", ".pbm" }, + { "image/x-portable-graymap", ".pgm" }, + { "image/x-portable-pixmap", ".ppm" }, + { "image/x-quicktime", ".qif" }, + { "image/x-rgb", ".rgb" }, + { "image/x-xbitmap", ".xbm" }, + { "image/x-xpixmap", ".pm" }, + { "image/x-xwd", ".xwd" }, + { "image/xpm", ".xpm" }, + { "message/rfc822", ".eml" }, + { "model/iges", ".iges" }, + { "model/mesh", ".mesh" }, + { "model/vrml", ".wrz" }, + { "model/x-pov", ".pov" }, + { "model/x3d+fastinfoset", ".x3db" }, + { "model/x3d+xml", ".x3d" }, + { "model/x3d-vrml", ".x3dv" }, + { "multipart/x-ustar", ".ustar" }, + { "text/asp", ".asp" }, + { "text/cache-manifest", ".appcache" }, + { "text/calendar", ".ics" }, + { "text/css", ".css" }, + { "text/csv", ".csv" }, + { "text/h323", ".323" }, + { "text/html", ".html" }, + { "text/iuls", ".uls" }, + { "text/markdown", ".md" }, + { "text/pascal", ".pas" }, + { "text/plain", ".txt" }, + { "text/richtext", ".rtx" }, + { "text/rtf", ".rtf" }, + { "text/scriplet", ".wsc" }, + { "text/scriptlet", ".sct" }, + { "text/sgml", ".sgml" }, + { "text/tab-separated-values", ".tsv" }, + { "text/uri-list", ".uri" }, + { "text/vcard", ".vcf" }, + { "text/vnd.abc", ".abc" }, + { "text/vnd.fmi.flexstor", ".flx" }, + { "text/vnd.wap.wml", ".wml" }, + { "text/vnd.wap.wmlscript", ".wmls" }, + { "text/webviewhtml", ".htt" }, + { "text/x-asm", ".asm" }, + { "text/x-audiosoft-intra", ".aip" }, + { "text/x-coffeescript", ".coffee" }, + { "text/x-component", ".htc" }, + { "text/x-la-asf", ".lsx" }, + { "text/x-pascal", ".p" }, + { "text/x-script", ".hlb" }, + { "text/x-script.elisp", ".el" }, + { "text/x-script.ksh", ".ksh" }, + { "text/x-script.phyton", ".py" }, + { "text/x-script.rexx", ".rexx" }, + { "text/x-script.sh", ".sh" }, + { "text/x-script.tcl", ".tcl" }, + { "text/x-script.tcsh", ".tcsh" }, + { "text/x-script.zsh", ".zsh" }, + { "text/x-server-parsed-html", ".ssi" }, + { "text/x-setext", ".etx" }, + { "text/x-speech", ".talk" }, + { "text/x-uil", ".uil" }, + { "text/x-uuencode", ".uu" }, + { "text/x-vcalendar", ".vcs" }, + { "text/xml", ".xml" }, + { "video/3gpp", ".3gp" }, + { "video/3gpp2", ".3g2" }, + { "video/animaflex", ".afl" }, + { "video/avi", ".avi" }, + { "video/avs-video", ".avs" }, + { "video/dl", ".dl" }, + { "video/fli", ".fli" }, + { "video/gl", ".gl" }, + { "video/mp4", ".mp4" }, + { "video/mpeg", ".mpg" }, + { "video/ogg", ".ogv" }, + { "video/quicktime", ".mov" }, + { "video/vdo", ".vdo" }, + { "video/vnd.rn-realvideo", ".rv" }, + { "video/vnd.vivo", ".vivo" }, + { "video/vosaic", ".vos" }, + { "video/webm", ".webm" }, + { "video/x-amt-demorun", ".xdr" }, + { "video/x-amt-showrun", ".xsr" }, + { "video/x-atomic3d-feature", ".fmf" }, + { "video/x-dv", ".dif" }, + { "video/x-isvideo", ".isu" }, + { "video/x-ivf", ".ivf" }, + { "video/x-motion-jpeg", ".mjpg" }, + { "video/x-ms-asf", ".asf" }, + { "video/x-ms-wmp", ".wmp" }, + { "video/x-ms-wmv", ".wmv" }, + { "video/x-ms-wmx", ".wmx" }, + { "video/x-ms-wvx", ".wvx" }, + { "video/x-qtc", ".qtc" }, + { "video/x-sgi-movie", ".movie" } + }; } /// - /// Gets the MIME-type of the file. + /// Get the MIME-type of a file. /// ///- /// Gets the MIME-type of the file based on the file extension. + /// Gets the MIME-type of a file based on the file extension. /// ///The MIME-type. /// The file name. @@ -613,16 +1003,72 @@ public static string GetMimeType (string fileName) if (fileName == null) throw new ArgumentNullException (nameof (fileName)); - int dot = fileName.LastIndexOf ('.'); - string mimeType = null; + var extension = Path.GetExtension (fileName); - if (dot != -1 && fileName.Length > dot + 1) - mimeTypes.TryGetValue (fileName.Substring (dot + 1), out mimeType); + mimeTypes.TryGetValue (extension, out var mimeType); + + return mimeType ?? "application/octet-stream"; + } + ///+ /// Get the standard file extension for a MIME-type. + /// + ///+ /// Gets the standard file extension for a MIME-type. + /// + ///+ /// The MIME-type. + /// The file name extension for the specified MIME-type. + /// true if the extension is known for the specified MIME-type; otherwise,false .+ /// + public static bool TryGetExtension (string mimeType, out string extension) + { if (mimeType == null) - mimeType = "application/octet-stream"; + throw new ArgumentNullException (nameof (mimeType)); + + return extensions.TryGetValue (mimeType, out extension); + } + + ///is null . + ///+ /// Register a MIME-type to file extension mapping. + /// + ///+ /// + /// The MIME-type to register. + /// The file extension to register. + ///Registers a MIME-type to file extension mapping. + ///If the mapping for the MIME-type and/or file extension already exists, + /// then it is overridden by the new mapping. + ///+ /// + ///+ /// is null .-or- + ///+ /// is null .+ /// + public static void Register (string mimeType, string extension) + { + if (mimeType == null) + throw new ArgumentNullException (nameof (mimeType)); + + if (mimeType.Length == 0) + throw new ArgumentException ("Cannot register an empty MIME-type.", nameof (mimeType)); + + if (extension == null) + throw new ArgumentNullException (nameof (extension)); + + if (extension.Length == 0) + throw new ArgumentException ("Cannot register an empty file extension.", nameof (extension)); + + if (extension[0] != '.') + extension = "." + extension; - return mimeType; + mimeTypes[extension] = mimeType; + extensions[mimeType] = extension; } } } diff --git a/MimeKit/MimeVisitor.cs b/MimeKit/MimeVisitor.cs index aa21c044ac..39b2cad4be 100644 --- a/MimeKit/MimeVisitor.cs +++ b/MimeKit/MimeVisitor.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast+ /// is empty. -or- + ///+ /// is empty. // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -239,10 +239,10 @@ protected internal virtual void VisitMimePart (MimePart entity) } /// - /// Visit the children of a ///. + /// Visit the children of a . /// - /// Visits the children of a /// Multipart. protected virtual void VisitChildren (Multipart multipart) @@ -352,6 +352,21 @@ protected internal virtual void VisitTextPart (TextPart entity) VisitMimePart (entity); } + ///. + /// Visits the children of a . /// + /// Visit the text/rfc822-headers MIME entity. + /// + ///+ /// Visits the text/rfc822-headers MIME entity. + /// + ///+ /// + /// The text/rfc822-headers MIME entity. + protected internal virtual void VisitTextRfc822Headers (TextRfc822Headers entity) + { + VisitMessagePart (entity); + } + ///+ ///
/// Visit the Microsoft TNEF MIME part entity. /// diff --git a/MimeKit/Multipart.cs b/MimeKit/Multipart.cs index e827ab8a5e..3a8f7db2fa 100644 --- a/MimeKit/Multipart.cs +++ b/MimeKit/Multipart.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,13 +32,9 @@ using System.Threading.Tasks; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - -using MimeKit.Encodings; -using MimeKit.Utils; using MimeKit.IO; +using MimeKit.Utils; +using MimeKit.Encodings; namespace MimeKit { /// @@ -71,10 +67,10 @@ public class Multipart : MimeEntity, ICollection ///, IList string preamble, epilogue; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -86,7 +82,7 @@ public Multipart (MimeEntityConstructorArgs args) : base (args) } /// public Multipart (string subtype) : base ("multipart", subtype) { - ContentType.Parameters["boundary"] = GenerateBoundary (); + ContentType.Boundary = GenerateBoundary (); children = new List- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified subtype. @@ -110,8 +106,7 @@ public Multipart (string subtype, params object[] args) : this (subtype) if (obj == null || TryInit (obj)) continue; - var entity = obj as MimeEntity; - if (entity != null) { + if (obj is MimeEntity entity) { Add (entity); continue; } @@ -121,7 +116,7 @@ public Multipart (string subtype, params object[] args) : this (subtype) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with the specified subtype. @@ -132,13 +127,13 @@ public Multipart (string subtype, params object[] args) : this (subtype) /// (); WriteEndBoundary = true; } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new with a ContentType of multipart/mixed. @@ -162,7 +157,7 @@ static string GenerateBoundary () } /// - /// Gets or sets the boundary. + /// Get or set the boundary. /// ////// Gets or sets the boundary parameter on the Content-Type header. @@ -189,7 +184,7 @@ internal byte[] RawPreamble { } /// - /// Gets or sets the preamble. + /// Get or set the preamble. /// ////// A multipart preamble appears before the first child entity of the @@ -228,7 +223,7 @@ internal byte[] RawEpilogue { } /// - /// Gets or sets the epilogue. + /// Get or set the epilogue. /// ////// A multipart epiloque is the text that appears after the closing boundary @@ -271,7 +266,7 @@ public string Epilogue { } /// - /// Gets or sets whether the end boundary should be written. + /// Get or set whether the end boundary should be written. /// ////// Gets or sets whether the end boundary should be written. @@ -285,12 +280,12 @@ internal bool WriteEndBoundary { /// Dispatches to the specific visit method for this MIME entity. /// - /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -356,10 +351,10 @@ internal static string FoldPreambleOrEpilogue (FormatOptions options, string tex return builder.ToString (); } - static void WriteBytes (FormatOptions options, Stream stream, byte[] bytes, CancellationToken cancellationToken) + static void WriteBytes (FormatOptions options, Stream stream, byte[] bytes, bool ensureNewLine, CancellationToken cancellationToken) { var cancellable = stream as ICancellableStream; - var filter = options.CreateNewLineFilter (); + var filter = options.CreateNewLineFilter (ensureNewLine); int index, length; var output = filter.Flush (bytes, 0, bytes.Length, out index, out length); @@ -372,9 +367,9 @@ static void WriteBytes (FormatOptions options, Stream stream, byte[] bytes, Canc } } - static Task WriteBytesAsync (FormatOptions options, Stream stream, byte[] bytes, CancellationToken cancellationToken) + static Task WriteBytesAsync (FormatOptions options, Stream stream, byte[] bytes, bool ensureNewLine, CancellationToken cancellationToken) { - var filter = options.CreateNewLineFilter (); + var filter = options.CreateNewLineFilter (ensureNewLine); int index, length; var output = filter.Flush (bytes, 0, bytes.Length, out index, out length); @@ -405,7 +400,7 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = } /// public override void WriteTo (FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) { - if (Boundary == null) - Boundary = GenerateBoundary (); - base.WriteTo (options, stream, contentOnly, cancellationToken); if (ContentType.IsMimeType ("multipart", "signed")) { @@ -444,7 +436,7 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = var cancellable = stream as ICancellableStream; if (RawPreamble != null && RawPreamble.Length > 0) - WriteBytes (options, stream, RawPreamble, cancellationToken); + WriteBytes (options, stream, RawPreamble, children.Count > 0 || EnsureNewLine, cancellationToken); var boundary = Encoding.ASCII.GetBytes ("--" + Boundary + "--"); @@ -479,7 +471,7 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); } else { for (int i = 0; i < children.Count; i++) { - var msg = children[i] as MessagePart; + var rfc822 = children[i] as MessagePart; var multi = children[i] as Multipart; var part = children[i] as MimePart; @@ -488,15 +480,16 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); children[i].WriteTo (options, stream, false, cancellationToken); - if (msg != null && msg.Message != null && msg.Message.Body != null) { - multi = msg.Message.Body as Multipart; - part = msg.Message.Body as MimePart; + if (rfc822 != null && rfc822.Message != null && rfc822.Message.Body != null) { + multi = rfc822.Message.Body as Multipart; + part = rfc822.Message.Body as MimePart; } if ((part != null && part.Content == null) || (multi != null && !multi.WriteEndBoundary)) continue; + cancellationToken.ThrowIfCancellationRequested (); stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } @@ -513,15 +506,16 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = } if (RawEpilogue != null && RawEpilogue.Length > 0) - WriteBytes (options, stream, RawEpilogue, cancellationToken); + WriteBytes (options, stream, RawEpilogue, EnsureNewLine, cancellationToken); } ///- /// Writes the ///to the specified output stream. + /// Write the to the specified output stream. /// /// Writes the multipart MIME entity and its subparts to the output stream. @@ -427,9 +422,6 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = /// - /// Asynchronously writes the ///to the specified output stream. + /// Asynchronously write the to the specified output stream. /// - /// Writes the multipart MIME entity and its subparts to the output stream. + /// Asynchronously writes the multipart MIME entity and its subparts to the output stream. /// + ///An awaitable task. /// The formatting options. /// The output stream. ///true if only the content should be written; otherwise,false . @@ -539,9 +533,6 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength = ///- /// Gets the number of parts in the multipart. + /// Get the number of parts in the multipart. /// ////// Indicates the number of parts in the multipart. @@ -605,7 +596,7 @@ public int Count { } /// - /// Gets a value indicating whether this instance is read only. + /// Get a value indicating whether this instance is read only. /// ////// A is never read-only. @@ -616,7 +607,7 @@ public bool IsReadOnly { } /// - /// Adds the specified part. + /// Add an entity to the multipart. /// ////// Adds the specified part to the multipart. @@ -635,7 +626,7 @@ public void Add (MimeEntity part) } /// - /// Clears the multipart. + /// Clear a multipart. /// ////// Removes all of the parts within the multipart. @@ -647,7 +638,7 @@ public void Clear () } /// - /// Checks if the ///contains the specified part. + /// Check if the contains the specified part. /// /// Determines whether or not the multipart contains the specified part. @@ -667,7 +658,7 @@ public bool Contains (MimeEntity part) } /// - /// Copies all of the entities in the ///to the specified array. + /// Copy all of the entities in the to the specified array. /// /// Copies all of the entities within the into the array, @@ -687,7 +678,7 @@ public void CopyTo (MimeEntity[] array, int arrayIndex) } /// - /// Removes the specified part. + /// Remove an entity from the multipart. /// ////// Removes the specified part, if it exists within the multipart. @@ -715,7 +706,7 @@ public bool Remove (MimeEntity part) #region IList implementation /// - /// Gets the index of the specified part. + /// Get the index of an entity. /// ////// Finds the index of the specified part, if it exists. @@ -734,7 +725,7 @@ public int IndexOf (MimeEntity part) } /// - /// Inserts the part at the specified index. + /// Insert an entity into the ///at the specified index. /// /// Inserts the part into the multipart at the specified index. @@ -760,10 +751,10 @@ public void Insert (int index, MimeEntity part) } /// - /// Removes the part at the specified index. + /// Remove an entity from the ///at the specified index. /// - /// Removes the part at the specified index. + /// Removes the entity at the specified index. /// /// The index. ///@@ -776,7 +767,7 @@ public void RemoveAt (int index) } /// - /// Gets or sets the ///at the specified index. + /// Get or set the at the specified index. /// /// Gets or sets the at the specified index. @@ -805,7 +796,7 @@ public MimeEntity this[int index] { #region IEnumerable implementation /// - /// Gets the enumerator for the children of the ///. + /// Get the enumerator for the children of the . /// /// Gets the enumerator for the children of the . @@ -821,7 +812,7 @@ public IEnumerator GetEnumerator () #region IEnumerable implementation /// - /// Gets the enumerator for the children of the ///. + /// Get the enumerator for the children of the . /// /// Gets the enumerator for the children of the . diff --git a/MimeKit/MultipartAlternative.cs b/MimeKit/MultipartAlternative.cs index f42e950c4a..1fd29ff23c 100644 --- a/MimeKit/MultipartAlternative.cs +++ b/MimeKit/MultipartAlternative.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -42,10 +42,10 @@ namespace MimeKit { public class MultipartAlternative : Multipart { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -56,7 +56,7 @@ public MultipartAlternative (MimeEntityConstructorArgs args) : base (args) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new part. @@ -73,7 +73,7 @@ public MultipartAlternative (params object[] args) : base ("alternative", args) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new + ///part. @@ -108,12 +108,12 @@ public string HtmlBody { /// Dispatches to the specific visit method for this MIME entity. /// /// - /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// diff --git a/MimeKit/MultipartRelated.cs b/MimeKit/MultipartRelated.cs index de002e59ba..cefed06494 100644 --- a/MimeKit/MultipartRelated.cs +++ b/MimeKit/MultipartRelated.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -38,13 +38,16 @@ namespace MimeKit { /// A multipart/related MIME entity contains, as one might expect, inter-related MIME parts which /// typically reference each other via URIs based on the Content-Id and/or Content-Location headers. /// + /// public class MultipartRelated : Multipart { ///+ ///
- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -55,7 +58,7 @@ public MultipartRelated (MimeEntityConstructorArgs args) : base (args) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new + ///part. @@ -72,7 +75,7 @@ public MultipartRelated (params object[] args) : base ("related", args) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new + ///part. @@ -83,19 +86,32 @@ public MultipartRelated () : base ("related") int GetRootIndex () { - string start = ContentType.Parameters["start"]; + var start = ContentType.Parameters["start"]; - if (start == null) - return -1; + if (start != null) { + string contentId; + + if ((contentId = MimeUtils.EnumerateReferences (start).FirstOrDefault ()) == null) + contentId = start; + + var cid = new Uri (string.Format ("cid:{0}", contentId)); + + return IndexOf (cid); + } - string contentId; + var type = ContentType.Parameters["type"]; - if ((contentId = MimeUtils.EnumerateReferences (start).FirstOrDefault ()) == null) - contentId = start; + if (type == null) + return -1; - var cid = new Uri (string.Format ("cid:{0}", contentId)); + for (int index = 0; index < Count; index++) { + var mimeType = this[index].ContentType.MimeType; - return IndexOf (cid); + if (mimeType.Equals (type, StringComparison.OrdinalIgnoreCase)) + return index; + } + + return -1; } /// @@ -109,6 +125,9 @@ int GetRootIndex () /// When setting the root document MIME part, the Content-Type header of the multipart/related part is also /// updated with a appropriate ///"start" and"type" parameters.+ /// ///+ ///
The root MIME part. ////// is null . @@ -140,17 +159,18 @@ public MimeEntity Root { index = 0; } - if (string.IsNullOrEmpty (value.ContentId)) - value.ContentId = MimeUtils.GenerateMessageId (); - ContentType.Parameters["type"] = value.ContentType.MediaType + "/" + value.ContentType.MediaSubtype; // Note: we only use a "start" parameter if the index of the root entity is not at index 0 in order // to work around the following Thunderbird bug: https://bugzilla.mozilla.org/show_bug.cgi?id=471402 - if (index > 0) + if (index > 0) { + if (string.IsNullOrEmpty (value.ContentId)) + value.ContentId = MimeUtils.GenerateMessageId (); + ContentType.Parameters["start"] = "<" + value.ContentId + ">"; - else + } else { ContentType.Parameters.Remove ("start"); + } } } @@ -158,12 +178,12 @@ public MimeEntity Root { /// Dispatches to the specific visit method for this MIME entity. /// ///- /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -205,6 +225,9 @@ public bool Contains (Uri uri) /// multipart/related part's Content-Base header in order to produce an absolute URI that can be /// compared with the provided absolute URI. /// + /// ///+ ///
The index of the part matching the specified URI if found; otherwise /// The URI of the MIME part. ///-1 .diff --git a/MimeKit/MultipartReport.cs b/MimeKit/MultipartReport.cs index 142b7a344d..5754f098c7 100644 --- a/MimeKit/MultipartReport.cs +++ b/MimeKit/MultipartReport.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast public int Search (char[] text, out string pattern) { + if (text == null) + throw new ArgumentNullException (nameof (text)); + return Search (text, 0, text.Length, out pattern); } } diff --git a/MimeKit/Text/UrlScanner.cs b/MimeKit/Text/UrlScanner.cs index a3ef00c51c..3b0dda748a 100644 --- a/MimeKit/Text/UrlScanner.cs +++ b/MimeKit/Text/UrlScanner.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -33,14 +33,19 @@ namespace MimeKit { /// /// A multipart/related MIME entity is a general container part for electronic mail /// reports of any kind. + /// + ///+ /// /// + /// public class MultipartReport : Multipart { ///+ ///
- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -51,7 +56,7 @@ public MultipartReport (MimeEntityConstructorArgs args) : base (args) } /// public int Search (char[] text, int startIndex, out string pattern) { + if (text == null) + throw new ArgumentNullException (nameof (text)); + return Search (text, startIndex, text.Length - startIndex, out pattern); } @@ -341,6 +344,9 @@ public int Search (char[] text, int startIndex, out string pattern) ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new public void Reset () { + semicolon = false; numeric = false; digits = 0; xbase = 0; diff --git a/MimeKit/Text/HtmlEntityDecoder.g.cs b/MimeKit/Text/HtmlEntityDecoder.g.cs index 14fa119eaa..f9cc9c908c 100644 --- a/MimeKit/Text/HtmlEntityDecoder.g.cs +++ b/MimeKit/Text/HtmlEntityDecoder.g.cs @@ -1,9 +1,9 @@ -// +// // HtmlEntityDecoder.g.cs // // Author: Jeffrey Stedfastpart. @@ -75,7 +80,7 @@ public MultipartReport (string reportType, params object[] args) : base ("report } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new + ///part. @@ -100,6 +105,9 @@ public MultipartReport (string reportType) : base ("report") /// The report type should be the subtype of the second ////// of the multipart/report. + /// ///+ ///
The type of the report. ////// is null . @@ -121,12 +129,12 @@ public string ReportType { /// Dispatches to the specific visit method for this MIME entity. /// ///- /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// diff --git a/MimeKit/Parameter.cs b/MimeKit/Parameter.cs index d27220fd2f..d773e86326 100644 --- a/MimeKit/Parameter.cs +++ b/MimeKit/Parameter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast public bool Push (char c) { + if (semicolon) + return false; + if (index == 0) { if (c != '&') throw new ArgumentOutOfRangeException (nameof (c), "The first character that is pushed MUST be the '&' character."); @@ -135,9 +139,6 @@ public bool Push (char c) if (index + 1 > MaxEntityLength) return false; - if (c == ';') - return false; - if (index == 1 && c == '#') { pushed[index] = '#'; states[index] = 0; @@ -146,12 +147,25 @@ public bool Push (char c) return true; } - return numeric ? PushNumericEntity (c) : PushNamedEntity (c); + semicolon = c == ';'; + + if (numeric) { + if (c == ';') { + states[index] = states[index - 1]; + pushed[index] = ';'; + index++; + return true; + } + + return PushNumericEntity (c); + } + + return PushNamedEntity (c); } string GetNumericEntityValue () { - if (digits == 0) + if (digits == 0 || !semicolon) return new string (pushed, 0, index); int state = states[index - 1]; @@ -234,6 +248,7 @@ internal string GetPushedInput () ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,11 +27,6 @@ using System; using System.Text; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -using Encoder = Portable.Text.Encoder; -#endif - using MimeKit.Encodings; using MimeKit.Utils; @@ -50,7 +45,7 @@ public class Parameter string text; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new parameter with the specified name and value. @@ -86,7 +81,7 @@ public Parameter (string name, string value) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new parameter with the specified name and value. @@ -129,7 +124,7 @@ public Parameter (Encoding encoding, string name, string value) } /// + ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new parameter with the specified name and value. @@ -216,6 +211,9 @@ public Encoding Encoding { /// + ///If set to ///, the encoding /// method used will default to the value set on the . + /// ///+ ///
The encoding method. ////// is not a valid value. diff --git a/MimeKit/ParameterEncodingMethod.cs b/MimeKit/ParameterEncodingMethod.cs index 7a35db6d75..3670f6e8cc 100644 --- a/MimeKit/ParameterEncodingMethod.cs +++ b/MimeKit/ParameterEncodingMethod.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -35,6 +35,9 @@ namespace MimeKit { /// some older email clients to improperly encode using the method described in /// rfc2047 instead. /// + /// public enum ParameterEncodingMethod { ///+ ///
/// Use the default encoding method set on the ///. diff --git a/MimeKit/ParameterList.cs b/MimeKit/ParameterList.cs index 9adb4a5fc5..3178b458bb 100644 --- a/MimeKit/ParameterList.cs +++ b/MimeKit/ParameterList.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,14 +28,9 @@ using System.IO; using System.Text; using System.Collections; +using System.Globalization; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -using Encoder = Portable.Text.Encoder; -using Decoder = Portable.Text.Decoder; -#endif - using MimeKit.Encodings; using MimeKit.Utils; @@ -52,7 +47,7 @@ public class ParameterList : IList readonly List parameters; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new parameter list. @@ -64,7 +59,7 @@ public ParameterList () } /// - /// Adds a parameter with the specified name and value. + /// Add a parameter with the specified name and value. /// ////// Adds a new parameter to the list with the specified name and value. @@ -85,7 +80,7 @@ public void Add (string name, string value) } /// - /// Adds a parameter with the specified name and value. + /// Add a parameter with the specified name and value. /// ////// Adds a new parameter to the list with the specified name and value. @@ -109,7 +104,7 @@ public void Add (Encoding encoding, string name, string value) } /// - /// Adds a parameter with the specified name and value. + /// Add a parameter with the specified name and value. /// ////// Adds a new parameter to the list with the specified name and value. @@ -138,7 +133,7 @@ public void Add (string charset, string name, string value) } /// - /// Checks if the ///contains a parameter with the specified name. + /// Check if the contains a parameter with the specified name. /// /// Determines whether or not the parameter list contains a parameter with the specified name. @@ -158,7 +153,7 @@ public bool Contains (string name) } /// - /// Gets the index of the requested parameter, if it exists. + /// Get the index of the requested parameter, if it exists. /// ////// Finds the index of the parameter with the specified name, if it exists. @@ -182,7 +177,7 @@ public int IndexOf (string name) } /// - /// Inserts a parameter with the specified name and value at the given index. + /// Insert a parameter with the specified name and value at the given index. /// ////// Inserts a new parameter with the given name and value at the specified index. @@ -210,7 +205,7 @@ public void Insert (int index, string name, string value) } /// - /// Removes the specified parameter. + /// Remove the specified parameter. /// ////// Removes the parameter with the specified name from the list, if it exists. @@ -234,7 +229,7 @@ public bool Remove (string name) } /// - /// Gets or sets the value of a parameter with the specified name. + /// Get or set the value of a parameter with the specified name. /// ////// Gets or sets the value of a parameter with the specified name. @@ -277,11 +272,14 @@ public string this [string name] { } /// - /// Gets the parameter with the specified name. + /// Get the parameter with the specified name. /// ////// Gets the parameter with the specified name. /// + ///+ /// ///+ ///
/// The parameter name. /// The parameter. @@ -297,7 +295,7 @@ public bool TryGetValue (string name, out Parameter param) } /// true if the parameter exists; otherwise,false .- /// Gets the value of the parameter with the specified name. + /// Get the value of the parameter with the specified name. /// ////// Gets the value of the parameter with the specified name. @@ -328,7 +326,7 @@ public bool TryGetValue (string name, out string value) #region ICollection implementation /// - /// Gets the number of parameters in the ///. + /// Get the number of parameters in the . /// /// Indicates the number of parameters in the list. @@ -339,7 +337,7 @@ public int Count { } /// - /// Gets a value indicating whether this instance is read only. + /// Get a value indicating whether this instance is read only. /// ////// A is never read-only. @@ -350,7 +348,7 @@ public bool IsReadOnly { } /// - /// Adds the specified parameter. + /// Add a ///to a . /// /// Adds the specified parameter to the end of the list. @@ -369,7 +367,7 @@ public void Add (Parameter param) throw new ArgumentNullException (nameof (param)); if (table.ContainsKey (param.Name)) - throw new ArgumentException ("A parameter of that name already exists."); + throw new ArgumentException ("A parameter of that name already exists.", nameof (param)); param.Changed += OnParamChanged; table.Add (param.Name, param); @@ -379,7 +377,7 @@ public void Add (Parameter param) } /// - /// Clears the parameter list. + /// Clear the parameter list. /// ////// Removes all of the parameters from the list. @@ -396,7 +394,7 @@ public void Clear () } /// - /// Checks if the ///contains the specified parameter. + /// Check if the contains the specified parameter. /// /// Determines whether or not the parameter list contains the specified parameter. @@ -416,7 +414,7 @@ public bool Contains (Parameter param) } /// - /// Copies all of the contained parameters to the specified array. + /// Copy all of the parameters in the list to the specified array. /// ////// Copies all of the parameters within the into the array, @@ -430,7 +428,7 @@ public void CopyTo (Parameter[] array, int arrayIndex) } /// - /// Removes the specified parameter. + /// Remove a ///from a . /// /// Removes the specified parameter from the list. @@ -462,7 +460,7 @@ public bool Remove (Parameter param) #region IList implementation /// - /// Gets the index of the requested parameter, if it exists. + /// Ges the index of the requested parameter, if it exists. /// ////// Finds the index of the specified parameter, if it exists. @@ -481,7 +479,7 @@ public int IndexOf (Parameter param) } /// - /// Inserts the specified parameter at the given index. + /// Insert a ///at the specified index. /// /// Inserts the parameter at the specified index in the list. @@ -507,7 +505,7 @@ public void Insert (int index, Parameter param) throw new ArgumentNullException (nameof (param)); if (table.ContainsKey (param.Name)) - throw new ArgumentException ("A parameter of that name already exists."); + throw new ArgumentException ("A parameter of that name already exists.", nameof (param)); parameters.Insert (index, param); table.Add (param.Name, param); @@ -517,7 +515,7 @@ public void Insert (int index, Parameter param) } /// - /// Removes the parameter at the specified index. + /// Remove the parameter at the specified index. /// ////// Removes the parameter at the specified index. @@ -541,10 +539,10 @@ public void RemoveAt (int index) } /// - /// Gets or sets the ///at the specified index. + /// Get or set the at the specified index. /// - /// Gets or sets the ///at the specified index. + /// Gets or sets the at the specified index. /// The parameter at the specified index. /// The index. @@ -563,7 +561,7 @@ public Parameter this [int index] { return parameters[index]; } set { - if (index < 0 || index > Count) + if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException (nameof (index)); if (value == null) @@ -579,7 +577,7 @@ public Parameter this [int index] { if (table[param.Name] == param) table[param.Name] = value; } else if (table.ContainsKey (value.Name)) { - throw new ArgumentException ("A parameter of that name already exists."); + throw new ArgumentException ("A parameter of that name already exists.", nameof (value)); } else { table.Add (value.Name, value); table.Remove (param.Name); @@ -598,7 +596,7 @@ public Parameter this [int index] { #region IEnumerable implementation ///- /// Gets an enumerator for the list of parameters. + /// Get an enumerator for the list of parameters. /// ////// Gets an enumerator for the list of parameters. @@ -614,7 +612,7 @@ public IEnumerator GetEnumerator () #region IEnumerable implementation /// - /// Gets an enumerator for the list of parameters. + /// Get an enumerator for the list of parameters. /// ////// Gets an enumerator for the list of parameters. @@ -634,7 +632,7 @@ internal void Encode (FormatOptions options, StringBuilder builder, ref int line } /// - /// Returns a string representation of the parameters in the ///. + /// Serialize a to a string. /// /// If there are multiple parameters in the list, they will be separated by a semicolon. @@ -714,7 +712,7 @@ static bool TryParseNameValuePair (ParserOptions options, byte[] text, ref int i startIndex = index; if (!SkipParamName (text, ref index, endIndex)) { if (throwOnError) - throw new ParseException (string.Format ("Invalid parameter name token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid parameter name token at offset {0}", startIndex), startIndex, index); return false; } @@ -726,7 +724,7 @@ static bool TryParseNameValuePair (ParserOptions options, byte[] text, ref int i if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete parameter at offset {0}", startIndex), startIndex, index); return false; } @@ -740,7 +738,7 @@ static bool TryParseNameValuePair (ParserOptions options, byte[] text, ref int i if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete parameter at offset {0}", startIndex), startIndex, index); return false; } @@ -752,7 +750,7 @@ static bool TryParseNameValuePair (ParserOptions options, byte[] text, ref int i if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete parameter at offset {0}", startIndex), startIndex, index); return false; } @@ -766,7 +764,7 @@ static bool TryParseNameValuePair (ParserOptions options, byte[] text, ref int i if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete parameter at offset {0}", startIndex), startIndex, index); return false; } @@ -779,12 +777,10 @@ static bool TryParseNameValuePair (ParserOptions options, byte[] text, ref int i } if (text[index] != (byte) '=') { - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete parameter at offset {0}", startIndex), startIndex, index); - return false; - } + return false; } index++; @@ -794,7 +790,7 @@ static bool TryParseNameValuePair (ParserOptions options, byte[] text, ref int i if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete parameter at offset {0}", startIndex), startIndex, index); return false; } @@ -977,13 +973,16 @@ internal static bool TryParse (ParserOptions options, byte[] text, ref int index break; if (text[index] != (byte) ';') { - if (throwOnError) - throw new ParseException (string.Format ("Invalid parameter list token at offset {0}", index), index, index); + if (options.ParameterComplianceMode == RfcComplianceMode.Strict) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid parameter list token at offset {0}", index), index, index); - return false; + return false; + } + } else { + // Skip over ';' + index++; } - - index++; } while (true); paramList = new ParameterList (); diff --git a/MimeKit/ParseException.cs b/MimeKit/ParseException.cs index a461816c8d..1e87e66534 100644 --- a/MimeKit/ParseException.cs +++ b/MimeKit/ParseException.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -49,7 +49,7 @@ public class ParseException : FormatException { #if SERIALIZABLE /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///. @@ -67,7 +67,7 @@ protected ParseException (SerializationInfo info, StreamingContext context) : ba #endif /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///. @@ -83,7 +83,7 @@ public ParseException (string message, int tokenIndex, int errorIndex, Exception } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new public class ParserOptions { - readonly Dictionary. @@ -114,13 +114,10 @@ public ParseException (string message, int tokenIndex, int errorIndex) : base (m [SecurityCritical] public override void GetObjectData (SerializationInfo info, StreamingContext context) { - if (info == null) - throw new ArgumentNullException (nameof (info)); + base.GetObjectData (info, context); info.AddValue ("TokenIndex", TokenIndex); info.AddValue ("ErrorIndex", ErrorIndex); - - base.GetObjectData (info, context); } #endif diff --git a/MimeKit/ParserOptions.cs b/MimeKit/ParserOptions.cs index 45032de409..e851f11f92 100644 --- a/MimeKit/ParserOptions.cs +++ b/MimeKit/ParserOptions.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -29,10 +29,6 @@ using System.Reflection; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - #if ENABLE_CRYPTO using MimeKit.Cryptography; #endif @@ -50,7 +46,7 @@ namespace MimeKit { /// mimeTypes = new Dictionary (); + readonly Dictionary mimeTypes = new Dictionary (StringComparer.Ordinal); static readonly Type[] ConstructorArgTypes = { typeof (MimeEntityConstructorArgs) }; /// @@ -69,21 +65,31 @@ public class ParserOptions /// In general, you'll probably want this value to be - ////// (the default) as it allows maximum interoperability with existing (broken) mail clients /// and other mail software such as sloppily written perl scripts (aka spambots). + /// Even in mode, the address - /// parser is fairly liberal in what it accepts. Setting it to - /// just makes it try harder to deal with garbage input. Even in ///mode, the address parser + /// is fairly liberal in what it accepts. Setting it to + /// just makes it try harder to deal with garbage input. The RFC compliance mode. public RfcComplianceMode AddressParserComplianceMode { get; set; } ///- /// Gets or sets whether the rfc822 address parser should allow addresses without a domain. + /// Gets or sets whether the rfc822 address parser should ignore unquoted commas in address names. /// ///- /// + ///In general, you'll probably want this value to be false (the default) as it allows + ///In general, you'll probably want this value to be + ///true (the default) as it allows /// maximum interoperability with existing (broken) mail clients and other mail software such as /// sloppily written perl scripts (aka spambots) that do not properly quote the name when it /// contains a comma.+ public bool AllowUnquotedCommasInAddresses { get; set; } + + /// true if the address parser should ignore unquoted commas in address names; otherwise,false .+ /// Gets or sets whether the rfc822 address parser should allow addresses without a domain. + /// + ///+ /// - ///In general, you'll probably want this value to be ///true (the default) as it allows + /// maximum interoperability with older email messages that may contain local UNIX addresses.This option exists in order to allow parsing of mailbox addresses that do not have an /// @domain component. These types of addresses are rare and were typically only used when sending /// mail to other users on the same UNIX system. @@ -100,9 +106,21 @@ public class ParserOptions /// formed. If the value is set too large, then it is possible that a maliciously formed set of /// recursive group addresses could cause a stack overflow. ///The max address group depth. + ///The maximum address group depth. public int MaxAddressGroupDepth { get; set; } + ///+ /// Gets or sets the maximum MIME nesting depth the parser should accept. + /// + ///+ /// + ///This option exists in order to define the maximum recursive depth of MIME parts that the parser + /// should accept before treating further nesting as a leaf-node MIME part and not recursing any further. + /// If the value is set too large, then it is possible that a maliciously formed set of rdeeply nested + /// multipart MIME parts could cause a stack overflow. + ///The maximum MIME nesting depth. + public int MaxMimeDepth { get; set; } + ////// Gets or sets the compliance mode that should be used when parsing Content-Type and Content-Disposition parameters. /// @@ -110,9 +128,9 @@ public class ParserOptions ///In general, you'll probably want this value to be - ////// (the default) as it allows maximum interoperability with existing (broken) mail clients /// and other mail software such as sloppily written perl scripts (aka spambots). + /// Even in mode, the parameter - /// parser is fairly liberal in what it accepts. Setting it to - /// just makes it try harder to deal with garbage input. Even in ///mode, the parameter parser + /// is fairly liberal in what it accepts. Setting it to + /// just makes it try harder to deal with garbage input. The RFC compliance mode. public RfcComplianceMode ParameterComplianceMode { get; set; } @@ -145,8 +163,8 @@ public class ParserOptions /// Gets or sets the charset encoding to use as a fallback for 8bit headers. ///- /// and - /// + /// and + /// /// use this charset encoding as a fallback when decoding 8bit text into unicode. The first /// charset encoding attempted is UTF-8, followed by this charset encoding, before finally /// falling back to iso-8859-1. @@ -155,7 +173,7 @@ public class ParserOptions public Encoding CharsetEncoding { get; set; } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// By default, new instances of enable rfc2047 work-arounds @@ -168,13 +186,15 @@ public ParserOptions () ParameterComplianceMode = RfcComplianceMode.Loose; Rfc2047ComplianceMode = RfcComplianceMode.Loose; CharsetEncoding = CharsetUtils.UTF8; - AllowAddressesWithoutDomain = false; + AllowUnquotedCommasInAddresses = true; + AllowAddressesWithoutDomain = true; RespectContentLength = false; MaxAddressGroupDepth = 3; + MaxMimeDepth = 1024; } /// - /// Clones an instance of ///. + /// Clones an instance of . /// /// Clones a set of options, allowing you to change a specific option @@ -185,12 +205,14 @@ public ParserOptions Clone () { var options = new ParserOptions (); options.AddressParserComplianceMode = AddressParserComplianceMode; + options.AllowUnquotedCommasInAddresses = AllowUnquotedCommasInAddresses; options.AllowAddressesWithoutDomain = AllowAddressesWithoutDomain; options.ParameterComplianceMode = ParameterComplianceMode; options.Rfc2047ComplianceMode = Rfc2047ComplianceMode; options.MaxAddressGroupDepth = MaxAddressGroupDepth; options.RespectContentLength = RespectContentLength; options.CharsetEncoding = CharsetEncoding; + options.MaxMimeDepth = MaxMimeDepth; foreach (var mimeType in mimeTypes) options.mimeTypes.Add (mimeType.Key, mimeType.Value); @@ -198,27 +220,6 @@ public ParserOptions Clone () return options; } -#if PORTABLE - static ConstructorInfo GetConstructor (TypeInfo type, Type[] argTypes) - { - foreach (var ctor in type.DeclaredConstructors) { - var args = ctor.GetParameters (); - - if (args.Length != ConstructorArgTypes.Length) - continue; - - bool matched = true; - for (int i = 0; i < argTypes.Length && matched; i++) - matched = matched && args[i].ParameterType == argTypes[i]; - - if (matched) - return ctor; - } - - return null; - } -#endif - /// /// Registers the @@ -252,7 +253,7 @@ public void RegisterMimeType (string mimeType, Type type) mimeType = mimeType.ToLowerInvariant (); -#if PORTABLE || NETSTANDARD +#if NETSTANDARD1_3 || NETSTANDARD1_6 var info = type.GetTypeInfo (); #else var info = type; @@ -263,11 +264,7 @@ public void RegisterMimeType (string mimeType, Type type) !info.IsSubclassOf (typeof (MimePart))) throw new ArgumentException ("The specified type must be a subclass of MessagePart, Multipart, or MimePart.", nameof (type)); -#if PORTABLE - var ctor = GetConstructor (info, ConstructorArgTypes); -#else var ctor = type.GetConstructor (ConstructorArgTypes); -#endif if (ctor == null) throw new ArgumentException ("The specified type must have a constructor that takes a MimeEntityConstructorArgs argument.", nameof (type)); @@ -298,9 +295,13 @@ static bool IsEncoded (IListsubclass for the specified mime-type. /// headers) return false; } - internal MimeEntity CreateEntity (ContentType contentType, IList headers, bool toplevel) + internal MimeEntity CreateEntity (ContentType contentType, IList headers, bool toplevel, int depth) { var args = new MimeEntityConstructorArgs (this, contentType, headers, toplevel); + + if (depth >= MaxMimeDepth) + return new MimePart (args); + var subtype = contentType.MediaSubtype.ToLowerInvariant (); var type = contentType.MediaType.ToLowerInvariant (); @@ -323,17 +324,24 @@ internal MimeEntity CreateEntity (ContentType contentType, IList headers // actually handle that w/o any problems. if (type == "message") { switch (subtype) { + case "global-disposition-notification": case "disposition-notification": return new MessageDispositionNotification (args); + case "global-delivery-status": case "delivery-status": return new MessageDeliveryStatus (args); case "partial": if (!IsEncoded (headers)) return new MessagePartial (args); break; + case "global-headers": + if (!IsEncoded (headers)) + return new TextRfc822Headers (args); + break; case "external-body": case "rfc2822": case "rfc822": + case "global": case "news": if (!IsEncoded (headers)) return new MessagePart (args); @@ -384,8 +392,12 @@ internal MimeEntity CreateEntity (ContentType contentType, IList headers } } - if (type == "text") + if (type == "text") { + if (subtype == "rfc822-headers" && !IsEncoded (headers)) + return new TextRfc822Headers (args); + return new TextPart (args); + } return new MimePart (args); } diff --git a/MimeKit/Properties/AssemblyInfo.cs b/MimeKit/Properties/AssemblyInfo.cs index 058e1482b9..873dfdc746 100644 --- a/MimeKit/Properties/AssemblyInfo.cs +++ b/MimeKit/Properties/AssemblyInfo.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -33,10 +33,10 @@ [assembly: AssemblyTitle ("MimeKit")] [assembly: AssemblyDescription ("A complete MIME library with support for S/MIME, PGP, DKIM and Unix mbox spools.")] [assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("Xamarin Inc.")] +[assembly: AssemblyCompany (".NET Foundation")] [assembly: AssemblyProduct ("MimeKit")] -[assembly: AssemblyCopyright ("Copyright © 2013-2018 Xamarin Inc. (www.xamarin.com)")] -[assembly: AssemblyTrademark ("Xamarin Inc.")] +[assembly: AssemblyCopyright ("Copyright © 2013-2020 .NET Foundation and Contributors")] +[assembly: AssemblyTrademark (".NET Foundation")] [assembly: AssemblyCulture ("")] // Setting ComVisible to false makes the types in this assembly not visible @@ -44,7 +44,6 @@ // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible (true)] -#if !PORTABLE // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid ("2fe79b66-d107-45da-9493-175f59c4a53c")] [assembly: InternalsVisibleTo ("UnitTests, PublicKey=002400000480000094000000060200" + @@ -52,7 +51,6 @@ "4656c5bfe4c47803453a91ae525f723f4316fd90a3f87366f4d948593277e950f6d2df6ee26068" + "1877a6d9e71c3ea77e87e61f3878af1d69bf10dce8debe92c54ca8a10afc44dc08674f3db6594e" + "f545d67d31cc3e18b8f90d8f220c4b67d7e87f5b7e8df410ac8faeb3")] -#endif // Version information for an assembly consists of the following four values: // @@ -80,6 +78,6 @@ // // If there have only been bug fixes, bump the Micro Version and/or the Build Number // in the AssemblyFileVersion attribute. -[assembly: AssemblyInformationalVersion ("2.0.1.1.0")] -[assembly: AssemblyFileVersion ("2.0.1.1.0")] -[assembly: AssemblyVersion ("2.0.0.0")] +[assembly: AssemblyInformationalVersion ("2.10.1.1.0")] +[assembly: AssemblyFileVersion ("2.10.1.1.0")] +[assembly: AssemblyVersion ("2.10.0.0")] diff --git a/MimeKit/RfcComplianceMode.cs b/MimeKit/RfcComplianceMode.cs index 8009ac8540..2522f2fb25 100644 --- a/MimeKit/RfcComplianceMode.cs +++ b/MimeKit/RfcComplianceMode.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Text/CharBuffer.cs b/MimeKit/Text/CharBuffer.cs new file mode 100644 index 0000000000..6afdac1e7e --- /dev/null +++ b/MimeKit/Text/CharBuffer.cs @@ -0,0 +1,93 @@ +// +// CharBuffer.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.Runtime.CompilerServices; + +namespace MimeKit.Text { + class CharBuffer + { + char[] buffer; + + public CharBuffer (int capacity) + { + buffer = new char[capacity]; + } + + public int Length { + [MethodImpl (MethodImplOptions.AggressiveInlining)] + get; + [MethodImpl (MethodImplOptions.AggressiveInlining)] + set; + } + + public char this[int index] { + [MethodImpl (MethodImplOptions.AggressiveInlining)] + get { return buffer[index]; } + [MethodImpl (MethodImplOptions.AggressiveInlining)] + set { buffer[index] = value; } + } + + [MethodImpl (MethodImplOptions.AggressiveInlining)] + void EnsureCapacity (int length) + { + if (length < buffer.Length) + return; + + int capacity = buffer.Length << 1; + while (capacity <= length) + capacity <<= 1; + + Array.Resize (ref buffer, capacity); + } + + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public void Append (char c) + { + EnsureCapacity (Length + 1); + buffer[Length++] = c; + } + + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public void Append (string str) + { + EnsureCapacity (Length + str.Length); + str.CopyTo (0, buffer, Length, str.Length); + Length += str.Length; + } + + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public override string ToString () + { + return new string (buffer, 0, Length); + } + + public static implicit operator string (CharBuffer buffer) + { + return buffer.ToString (); + } + } +} diff --git a/MimeKit/Text/FlowedToHtml.cs b/MimeKit/Text/FlowedToHtml.cs index 79cc06e71f..22f77dccec 100644 --- a/MimeKit/Text/FlowedToHtml.cs +++ b/MimeKit/Text/FlowedToHtml.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -44,7 +44,7 @@ public class FlowedToHtml : TextConverter readonly UrlScanner scanner; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new flowed text to HTML converter. @@ -87,7 +87,7 @@ public override TextFormat OutputFormat { /// ///The flowed text format defines a Content-Type parameter called "delsp" which can /// have a value of "yes" or "no". If the parameter exists and the value is "yes", then /// + /// should be set toshould be set to true , otherwise- /// should be set to false. false . ////// @@ -97,18 +97,6 @@ public bool DeleteSpace { get; set; } - ///
- /// Get or set the text that will be appended to the end of the output. - /// - ///- /// - ///Gets or sets the text that will be appended to the end of the output. - ///The footer must be set before conversion begins. - ///The footer. - public string Footer { - get; set; - } - ////// Get or set the footer format. /// @@ -120,18 +108,6 @@ public HeaderFooterFormat FooterFormat { get; set; } - ///- /// Get or set text that will be prepended to the beginning of the output. - /// - ///- /// - ///Gets or sets the text that will be prepended to the beginning of the output. - ///The header must be set before conversion begins. - ///The header. - public string Header { - get; set; - } - ////// Get or set the header format. /// diff --git a/MimeKit/Text/FlowedToText.cs b/MimeKit/Text/FlowedToText.cs index 234b7b26fb..3b0092fc64 100644 --- a/MimeKit/Text/FlowedToText.cs +++ b/MimeKit/Text/FlowedToText.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -38,7 +38,7 @@ namespace MimeKit.Text { public class FlowedToText : TextConverter { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new flowed text to text converter. @@ -77,37 +77,13 @@ public override TextFormat OutputFormat { /// ///The flowed text format defines a Content-Type parameter called "delsp" which can /// have a value of "yes" or "no". If the parameter exists and the value is "yes", then /// + /// should be set toshould be set to true , otherwise- /// should be set to false. false . ///public bool DeleteSpace { get; set; } - /// true if the trailing space on a wrapped line should be deleted; otherwise,false .- /// Get or set the text that will be appended to the end of the output. - /// - ///- /// - ///Gets or sets the text that will be appended to the end of the output. - ///The footer must be set before conversion begins. - ///The footer. - public string Footer { - get; set; - } - - ///- /// Get or set text that will be prepended to the beginning of the output. - /// - ///- /// - ///Gets or sets the text that will be prepended to the beginning of the output. - ///The header must be set before conversion begins. - ///The header. - public string Header { - get; set; - } - static string Unquote (string line, out int quoteDepth) { int index = 0; diff --git a/MimeKit/Text/HeaderFooterFormat.cs b/MimeKit/Text/HeaderFooterFormat.cs index 3225c107f8..a5bd0ebdbd 100644 --- a/MimeKit/Text/HeaderFooterFormat.cs +++ b/MimeKit/Text/HeaderFooterFormat.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Text/HtmlAttribute.cs b/MimeKit/Text/HtmlAttribute.cs index 8618c3dad9..3586c5942f 100644 --- a/MimeKit/Text/HtmlAttribute.cs +++ b/MimeKit/Text/HtmlAttribute.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -39,7 +39,7 @@ namespace MimeKit.Text { public class HtmlAttribute { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new HTML attribute with the given id and value. @@ -60,7 +60,7 @@ public HtmlAttribute (HtmlAttributeId id, string value) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new HTML attribute with the given name and value. diff --git a/MimeKit/Text/HtmlAttributeCollection.cs b/MimeKit/Text/HtmlAttributeCollection.cs index 736a774150..e805c2fbec 100644 --- a/MimeKit/Text/HtmlAttributeCollection.cs +++ b/MimeKit/Text/HtmlAttributeCollection.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -48,7 +48,7 @@ public class HtmlAttributeCollection : IEnumerable readonly List attributes = new List (); /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . diff --git a/MimeKit/Text/HtmlAttributeId.cs b/MimeKit/Text/HtmlAttributeId.cs index 9f08a0afa4..7c61ead19a 100644 --- a/MimeKit/Text/HtmlAttributeId.cs +++ b/MimeKit/Text/HtmlAttributeId.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -621,7 +621,7 @@ public static string ToAttributeName (this HtmlAttributeId value) { var name = value.ToString (); -#if PORTABLE || NETSTANDARD +#if NETSTANDARD1_3 || NETSTANDARD1_6 var field = typeof (HtmlAttributeId).GetTypeInfo ().GetDeclaredField (name); var attrs = field.GetCustomAttributes (typeof (HtmlAttributeNameAttribute), false).ToArray (); #else diff --git a/MimeKit/Text/HtmlEntityDecoder.cs b/MimeKit/Text/HtmlEntityDecoder.cs index 44bd1ba4fb..3838bdcd19 100644 --- a/MimeKit/Text/HtmlEntityDecoder.cs +++ b/MimeKit/Text/HtmlEntityDecoder.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -37,13 +37,14 @@ public partial class HtmlEntityDecoder { readonly char[] pushed; readonly int[] states; + bool semicolon; bool numeric; byte digits; byte xbase; int index; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -122,6 +123,9 @@ bool PushNumericEntity (char c) /// // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,10192 +26,12408 @@ // WARNING: This file is auto-generated. DO NOT EDIT! +using System.Collections.Generic; + namespace MimeKit.Text { - public partial class HtmlEntityDecoder { - const int MaxEntityLength = 32; + public partial class HtmlEntityDecoder + { + const int MaxEntityLength = 33; + + static readonly Dictionary NamedEntities; + + struct Transition + { + public readonly int From; + public readonly int To; + + public Transition (int from, int to) + { + From = from; + To = to; + } + } + + static readonly Transition[] TransitionTable_1; + static readonly Transition[] TransitionTable_2; + static readonly Transition[] TransitionTable_3; + static readonly Transition[] TransitionTable_4; + static readonly Transition[] TransitionTable_5; + static readonly Transition[] TransitionTable_6; + static readonly Transition[] TransitionTable_7; + static readonly Transition[] TransitionTable_8; + static readonly Transition[] TransitionTable_semicolon; + static readonly Transition[] TransitionTable_A; + static readonly Transition[] TransitionTable_B; + static readonly Transition[] TransitionTable_C; + static readonly Transition[] TransitionTable_D; + static readonly Transition[] TransitionTable_E; + static readonly Transition[] TransitionTable_F; + static readonly Transition[] TransitionTable_G; + static readonly Transition[] TransitionTable_H; + static readonly Transition[] TransitionTable_I; + static readonly Transition[] TransitionTable_J; + static readonly Transition[] TransitionTable_K; + static readonly Transition[] TransitionTable_L; + static readonly Transition[] TransitionTable_M; + static readonly Transition[] TransitionTable_N; + static readonly Transition[] TransitionTable_O; + static readonly Transition[] TransitionTable_P; + static readonly Transition[] TransitionTable_Q; + static readonly Transition[] TransitionTable_R; + static readonly Transition[] TransitionTable_S; + static readonly Transition[] TransitionTable_T; + static readonly Transition[] TransitionTable_U; + static readonly Transition[] TransitionTable_V; + static readonly Transition[] TransitionTable_W; + static readonly Transition[] TransitionTable_X; + static readonly Transition[] TransitionTable_Y; + static readonly Transition[] TransitionTable_Z; + static readonly Transition[] TransitionTable_a; + static readonly Transition[] TransitionTable_b; + static readonly Transition[] TransitionTable_c; + static readonly Transition[] TransitionTable_d; + static readonly Transition[] TransitionTable_e; + static readonly Transition[] TransitionTable_f; + static readonly Transition[] TransitionTable_g; + static readonly Transition[] TransitionTable_h; + static readonly Transition[] TransitionTable_i; + static readonly Transition[] TransitionTable_j; + static readonly Transition[] TransitionTable_k; + static readonly Transition[] TransitionTable_l; + static readonly Transition[] TransitionTable_m; + static readonly Transition[] TransitionTable_n; + static readonly Transition[] TransitionTable_o; + static readonly Transition[] TransitionTable_p; + static readonly Transition[] TransitionTable_q; + static readonly Transition[] TransitionTable_r; + static readonly Transition[] TransitionTable_s; + static readonly Transition[] TransitionTable_t; + static readonly Transition[] TransitionTable_u; + static readonly Transition[] TransitionTable_v; + static readonly Transition[] TransitionTable_w; + static readonly Transition[] TransitionTable_x; + static readonly Transition[] TransitionTable_y; + static readonly Transition[] TransitionTable_z; + + static HtmlEntityDecoder () + { + TransitionTable_1 = new Transition[4] { + new Transition (566, 567), // &blk -> &blk1 + new Transition (2280, 2282), // &emsp -> &emsp1 + new Transition (2649, 2650), // &frac -> &frac1 + new Transition (8284, 8286) // &sup -> ¹ + }; + TransitionTable_2 = new Transition[4] { + new Transition (567, 568), // &blk1 -> &blk12 + new Transition (2649, 2663), // &frac -> &frac2 + new Transition (2650, 2651), // &frac1 -> ½ + new Transition (8284, 8288) // &sup -> ² + }; + TransitionTable_3 = new Transition[6] { + new Transition (566, 572), // &blk -> &blk3 + new Transition (2282, 2283), // &emsp1 -> &emsp13 + new Transition (2649, 2668), // &frac -> &frac3 + new Transition (2650, 2653), // &frac1 -> &frac13 + new Transition (2663, 2664), // &frac2 -> &frac23 + new Transition (8284, 8290) // &sup -> ³ + }; + TransitionTable_4 = new Transition[7] { + new Transition (567, 570), // &blk1 -> &blk14 + new Transition (572, 573), // &blk3 -> &blk34 + new Transition (2282, 2285), // &emsp1 -> &emsp14 + new Transition (2649, 2675), // &frac -> &frac4 + new Transition (2650, 2655), // &frac1 -> ¼ + new Transition (2668, 2669), // &frac3 -> ¾ + new Transition (8464, 8465) // &there -> &there4 + }; + TransitionTable_5 = new Transition[5] { + new Transition (2649, 2678), // &frac -> &frac5 + new Transition (2650, 2657), // &frac1 -> &frac15 + new Transition (2663, 2666), // &frac2 -> &frac25 + new Transition (2668, 2671), // &frac3 -> &frac35 + new Transition (2675, 2676) // &frac4 -> &frac45 + }; + TransitionTable_6 = new Transition[2] { + new Transition (2650, 2659), // &frac1 -> &frac16 + new Transition (2678, 2679) // &frac5 -> &frac56 + }; + TransitionTable_7 = new Transition[1] { + new Transition (2649, 2683) // &frac -> &frac7 + }; + TransitionTable_8 = new Transition[4] { + new Transition (2650, 2661), // &frac1 -> &frac18 + new Transition (2668, 2673), // &frac3 -> &frac38 + new Transition (2678, 2681), // &frac5 -> &frac58 + new Transition (2683, 2684) // &frac7 -> &frac78 + }; + TransitionTable_semicolon = new Transition[2125] { + new Transition (6, 7), // Á -> Á + new Transition (13, 14), // á -> á + new Transition (19, 20), // &Abreve -> Ă + new Transition (25, 26), // &abreve -> ă + new Transition (27, 28), // &ac -> ∾ + new Transition (29, 30), // &acd -> ∿ + new Transition (31, 32), // &acE -> ∾̳ + new Transition (36, 37), // Â -> Â + new Transition (40, 41), // â -> â + new Transition (44, 45), // ´ -> ´ + new Transition (46, 47), // &Acy -> А + new Transition (48, 49), // &acy -> а + new Transition (53, 54), // Æ -> Æ + new Transition (58, 59), // æ -> æ + new Transition (60, 61), // &af -> ⁡ + new Transition (63, 64), // &Afr -> 𝔄 + new Transition (65, 66), // &afr -> 𝔞 + new Transition (71, 72), // À -> À + new Transition (77, 78), // à -> à + new Transition (84, 85), // &alefsym -> ℵ + new Transition (87, 88), // &aleph -> ℵ + new Transition (92, 93), // &Alpha -> Α + new Transition (96, 97), // &alpha -> α + new Transition (101, 102), // &Amacr -> Ā + new Transition (106, 107), // &amacr -> ā + new Transition (109, 110), // &amalg -> ⨿ + new Transition (112, 113), // & -> & + new Transition (114, 115), // & -> & + new Transition (117, 118), // &And -> ⩓ + new Transition (120, 121), // &and -> ∧ + new Transition (124, 125), // &andand -> ⩕ + new Transition (126, 127), // &andd -> ⩜ + new Transition (132, 133), // &andslope -> ⩘ + new Transition (134, 135), // &andv -> ⩚ + new Transition (136, 137), // &ang -> ∠ + new Transition (138, 139), // &ange -> ⦤ + new Transition (141, 142), // &angle -> ∠ + new Transition (145, 146), // &angmsd -> ∡ + new Transition (148, 149), // &angmsdaa -> ⦨ + new Transition (150, 151), // &angmsdab -> ⦩ + new Transition (152, 153), // &angmsdac -> ⦪ + new Transition (154, 155), // &angmsdad -> ⦫ + new Transition (156, 157), // &angmsdae -> ⦬ + new Transition (158, 159), // &angmsdaf -> ⦭ + new Transition (160, 161), // &angmsdag -> ⦮ + new Transition (162, 163), // &angmsdah -> ⦯ + new Transition (165, 166), // &angrt -> ∟ + new Transition (168, 169), // &angrtvb -> ⊾ + new Transition (170, 171), // &angrtvbd -> ⦝ + new Transition (174, 175), // &angsph -> ∢ + new Transition (176, 177), // &angst -> Å + new Transition (181, 182), // &angzarr -> ⍼ + new Transition (186, 187), // &Aogon -> Ą + new Transition (191, 192), // &aogon -> ą + new Transition (194, 195), // &Aopf -> 𝔸 + new Transition (197, 198), // &aopf -> 𝕒 + new Transition (199, 200), // &ap -> ≈ + new Transition (204, 205), // &apacir -> ⩯ + new Transition (206, 207), // &apE -> ⩰ + new Transition (208, 209), // &ape -> ≊ + new Transition (211, 212), // &apid -> ≋ + new Transition (214, 215), // &apos -> ' + new Transition (227, 228), // &ApplyFunction -> ⁡ + new Transition (232, 233), // &approx -> ≈ + new Transition (235, 236), // &approxeq -> ≊ + new Transition (240, 241), // Å -> Å + new Transition (245, 246), // å -> å + new Transition (249, 250), // &Ascr -> 𝒜 + new Transition (253, 254), // &ascr -> 𝒶 + new Transition (258, 259), // &Assign -> ≔ + new Transition (260, 261), // &ast -> * + new Transition (264, 265), // &asymp -> ≈ + new Transition (267, 268), // &asympeq -> ≍ + new Transition (273, 274), // Ã -> Ã + new Transition (279, 280), // ã -> ã + new Transition (283, 284), // Ä -> Ä + new Transition (287, 288), // ä -> ä + new Transition (295, 296), // &awconint -> ∳ + new Transition (299, 300), // &awint -> ⨑ + new Transition (308, 309), // &backcong -> ≌ + new Transition (316, 317), // &backepsilon -> ϶ + new Transition (322, 323), // &backprime -> ‵ + new Transition (326, 327), // &backsim -> ∽ + new Transition (329, 330), // &backsimeq -> ⋍ + new Transition (339, 340), // &Backslash -> ∖ + new Transition (342, 343), // &Barv -> ⫧ + new Transition (347, 348), // &barvee -> ⊽ + new Transition (351, 352), // &Barwed -> ⌆ + new Transition (355, 356), // &barwed -> ⌅ + new Transition (358, 359), // &barwedge -> ⌅ + new Transition (362, 363), // &bbrk -> ⎵ + new Transition (367, 368), // &bbrktbrk -> ⎶ + new Transition (372, 373), // &bcong -> ≌ + new Transition (375, 376), // &Bcy -> Б + new Transition (377, 378), // &bcy -> б + new Transition (382, 383), // &bdquo -> „ + new Transition (388, 389), // &becaus -> ∵ + new Transition (395, 396), // &Because -> ∵ + new Transition (397, 398), // &because -> ∵ + new Transition (403, 404), // &bemptyv -> ⦰ + new Transition (407, 408), // &bepsi -> ϶ + new Transition (412, 413), // &bernou -> ℬ + new Transition (421, 422), // &Bernoullis -> ℬ + new Transition (424, 425), // &Beta -> Β + new Transition (427, 428), // &beta -> β + new Transition (429, 430), // &beth -> ℶ + new Transition (434, 435), // &between -> ≬ + new Transition (437, 438), // &Bfr -> 𝔅 + new Transition (440, 441), // &bfr -> 𝔟 + new Transition (446, 447), // &bigcap -> ⋂ + new Transition (450, 451), // &bigcirc -> ◯ + new Transition (453, 454), // &bigcup -> ⋃ + new Transition (458, 459), // &bigodot -> ⨀ + new Transition (463, 464), // &bigoplus -> ⨁ + new Transition (469, 470), // &bigotimes -> ⨂ + new Transition (475, 476), // &bigsqcup -> ⨆ + new Transition (479, 480), // &bigstar -> ★ + new Transition (492, 493), // &bigtriangledown -> ▽ + new Transition (495, 496), // &bigtriangleup -> △ + new Transition (501, 502), // &biguplus -> ⨄ + new Transition (505, 506), // &bigvee -> ⋁ + new Transition (511, 512), // &bigwedge -> ⋀ + new Transition (517, 518), // &bkarow -> ⤍ + new Transition (529, 530), // &blacklozenge -> ⧫ + new Transition (536, 537), // &blacksquare -> ▪ + new Transition (545, 546), // &blacktriangle -> ▴ + new Transition (550, 551), // &blacktriangledown -> ▾ + new Transition (555, 556), // &blacktriangleleft -> ◂ + new Transition (561, 562), // &blacktriangleright -> ▸ + new Transition (564, 565), // &blank -> ␣ + new Transition (568, 569), // &blk12 -> ▒ + new Transition (570, 571), // &blk14 -> ░ + new Transition (573, 574), // &blk34 -> ▓ + new Transition (577, 578), // &block -> █ + new Transition (580, 581), // &bne -> =⃥ + new Transition (585, 586), // &bnequiv -> ≡⃥ + new Transition (589, 590), // &bNot -> ⫭ + new Transition (592, 593), // &bnot -> ⌐ + new Transition (596, 597), // &Bopf -> 𝔹 + new Transition (600, 601), // &bopf -> 𝕓 + new Transition (602, 603), // &bot -> ⊥ + new Transition (606, 607), // &bottom -> ⊥ + new Transition (611, 612), // &bowtie -> ⋈ + new Transition (616, 617), // &boxbox -> ⧉ + new Transition (619, 620), // &boxDL -> ╗ + new Transition (621, 622), // &boxDl -> ╖ + new Transition (624, 625), // &boxdL -> ╕ + new Transition (626, 627), // &boxdl -> ┐ + new Transition (628, 629), // &boxDR -> ╔ + new Transition (630, 631), // &boxDr -> ╓ + new Transition (632, 633), // &boxdR -> ╒ + new Transition (634, 635), // &boxdr -> ┌ + new Transition (636, 637), // &boxH -> ═ + new Transition (638, 639), // &boxh -> ─ + new Transition (640, 641), // &boxHD -> ╦ + new Transition (642, 643), // &boxHd -> ╤ + new Transition (644, 645), // &boxhD -> ╥ + new Transition (646, 647), // &boxhd -> ┬ + new Transition (648, 649), // &boxHU -> ╩ + new Transition (650, 651), // &boxHu -> ╧ + new Transition (652, 653), // &boxhU -> ╨ + new Transition (654, 655), // &boxhu -> ┴ + new Transition (660, 661), // &boxminus -> ⊟ + new Transition (665, 666), // &boxplus -> ⊞ + new Transition (671, 672), // &boxtimes -> ⊠ + new Transition (674, 675), // &boxUL -> ╝ + new Transition (676, 677), // &boxUl -> ╜ + new Transition (679, 680), // &boxuL -> ╛ + new Transition (681, 682), // &boxul -> ┘ + new Transition (683, 684), // &boxUR -> ╚ + new Transition (685, 686), // &boxUr -> ╙ + new Transition (687, 688), // &boxuR -> ╘ + new Transition (689, 690), // &boxur -> └ + new Transition (691, 692), // &boxV -> ║ + new Transition (693, 694), // &boxv -> │ + new Transition (695, 696), // &boxVH -> ╬ + new Transition (697, 698), // &boxVh -> ╫ + new Transition (699, 700), // &boxvH -> ╪ + new Transition (701, 702), // &boxvh -> ┼ + new Transition (703, 704), // &boxVL -> ╣ + new Transition (705, 706), // &boxVl -> ╢ + new Transition (707, 708), // &boxvL -> ╡ + new Transition (709, 710), // &boxvl -> ┤ + new Transition (711, 712), // &boxVR -> ╠ + new Transition (713, 714), // &boxVr -> ╟ + new Transition (715, 716), // &boxvR -> ╞ + new Transition (717, 718), // &boxvr -> ├ + new Transition (723, 724), // &bprime -> ‵ + new Transition (728, 729), // &Breve -> ˘ + new Transition (733, 734), // &breve -> ˘ + new Transition (738, 739), // ¦ -> ¦ + new Transition (742, 743), // &Bscr -> ℬ + new Transition (746, 747), // &bscr -> 𝒷 + new Transition (750, 751), // &bsemi -> ⁏ + new Transition (753, 754), // &bsim -> ∽ + new Transition (755, 756), // &bsime -> ⋍ + new Transition (758, 759), // &bsol -> \ + new Transition (760, 761), // &bsolb -> ⧅ + new Transition (765, 766), // &bsolhsub -> ⟈ + new Transition (769, 770), // &bull -> • + new Transition (772, 773), // &bullet -> • + new Transition (775, 776), // &bump -> ≎ + new Transition (777, 778), // &bumpE -> ⪮ + new Transition (779, 780), // &bumpe -> ≏ + new Transition (785, 786), // &Bumpeq -> ≎ + new Transition (787, 788), // &bumpeq -> ≏ + new Transition (794, 795), // &Cacute -> Ć + new Transition (801, 802), // &cacute -> ć + new Transition (803, 804), // &Cap -> ⋒ + new Transition (805, 806), // &cap -> ∩ + new Transition (809, 810), // &capand -> ⩄ + new Transition (815, 816), // &capbrcup -> ⩉ + new Transition (819, 820), // &capcap -> ⩋ + new Transition (822, 823), // &capcup -> ⩇ + new Transition (826, 827), // &capdot -> ⩀ + new Transition (844, 845), // &CapitalDifferentialD -> ⅅ + new Transition (846, 847), // &caps -> ∩︀ + new Transition (850, 851), // &caret -> ⁁ + new Transition (853, 854), // &caron -> ˇ + new Transition (859, 860), // &Cayleys -> ℭ + new Transition (864, 865), // &ccaps -> ⩍ + new Transition (870, 871), // &Ccaron -> Č + new Transition (874, 875), // &ccaron -> č + new Transition (879, 880), // Ç -> Ç + new Transition (884, 885), // ç -> ç + new Transition (888, 889), // &Ccirc -> Ĉ + new Transition (892, 893), // &ccirc -> ĉ + new Transition (898, 899), // &Cconint -> ∰ + new Transition (902, 903), // &ccups -> ⩌ + new Transition (905, 906), // &ccupssm -> ⩐ + new Transition (909, 910), // &Cdot -> Ċ + new Transition (913, 914), // &cdot -> ċ + new Transition (918, 919), // ¸ -> ¸ + new Transition (925, 926), // &Cedilla -> ¸ + new Transition (931, 932), // &cemptyv -> ⦲ + new Transition (934, 935), // ¢ -> ¢ + new Transition (942, 943), // &CenterDot -> · + new Transition (948, 949), // ¢erdot -> · + new Transition (951, 952), // &Cfr -> ℭ + new Transition (954, 955), // &cfr -> 𝔠 + new Transition (958, 959), // &CHcy -> Ч + new Transition (962, 963), // &chcy -> ч + new Transition (966, 967), // &check -> ✓ + new Transition (971, 972), // &checkmark -> ✓ + new Transition (974, 975), // &Chi -> Χ + new Transition (976, 977), // &chi -> χ + new Transition (979, 980), // &cir -> ○ + new Transition (981, 982), // &circ -> ˆ + new Transition (984, 985), // &circeq -> ≗ + new Transition (996, 997), // &circlearrowleft -> ↺ + new Transition (1002, 1003), // &circlearrowright -> ↻ + new Transition (1007, 1008), // &circledast -> ⊛ + new Transition (1012, 1013), // &circledcirc -> ⊚ + new Transition (1017, 1018), // &circleddash -> ⊝ + new Transition (1026, 1027), // &CircleDot -> ⊙ + new Transition (1028, 1029), // &circledR -> ® + new Transition (1030, 1031), // &circledS -> Ⓢ + new Transition (1036, 1037), // &CircleMinus -> ⊖ + new Transition (1041, 1042), // &CirclePlus -> ⊕ + new Transition (1047, 1048), // &CircleTimes -> ⊗ + new Transition (1049, 1050), // &cirE -> ⧃ + new Transition (1051, 1052), // &cire -> ≗ + new Transition (1057, 1058), // &cirfnint -> ⨐ + new Transition (1061, 1062), // &cirmid -> ⫯ + new Transition (1066, 1067), // &cirscir -> ⧂ + new Transition (1090, 1091), // &ClockwiseContourIntegral -> ∲ + new Transition (1109, 1110), // &CloseCurlyDoubleQuote -> ” + new Transition (1115, 1116), // &CloseCurlyQuote -> ’ + new Transition (1120, 1121), // &clubs -> ♣ + new Transition (1124, 1125), // &clubsuit -> ♣ + new Transition (1129, 1130), // &Colon -> ∷ + new Transition (1134, 1135), // &colon -> : + new Transition (1136, 1137), // &Colone -> ⩴ + new Transition (1138, 1139), // &colone -> ≔ + new Transition (1140, 1141), // &coloneq -> ≔ + new Transition (1144, 1145), // &comma -> , + new Transition (1146, 1147), // &commat -> @ + new Transition (1148, 1149), // &comp -> ∁ + new Transition (1151, 1152), // &compfn -> ∘ + new Transition (1158, 1159), // &complement -> ∁ + new Transition (1162, 1163), // &complexes -> ℂ + new Transition (1165, 1166), // &cong -> ≅ + new Transition (1169, 1170), // &congdot -> ⩭ + new Transition (1177, 1178), // &Congruent -> ≡ + new Transition (1181, 1182), // &Conint -> ∯ + new Transition (1185, 1186), // &conint -> ∮ + new Transition (1198, 1199), // &ContourIntegral -> ∮ + new Transition (1201, 1202), // &Copf -> ℂ + new Transition (1204, 1205), // &copf -> 𝕔 + new Transition (1208, 1209), // &coprod -> ∐ + new Transition (1215, 1216), // &Coproduct -> ∐ + new Transition (1219, 1220), // © -> © + new Transition (1221, 1222), // © -> © + new Transition (1224, 1225), // ©sr -> ℗ + new Transition (1254, 1255), // &CounterClockwiseContourIntegral -> ∳ + new Transition (1259, 1260), // &crarr -> ↵ + new Transition (1264, 1265), // &Cross -> ⨯ + new Transition (1268, 1269), // &cross -> ✗ + new Transition (1272, 1273), // &Cscr -> 𝒞 + new Transition (1276, 1277), // &cscr -> 𝒸 + new Transition (1279, 1280), // &csub -> ⫏ + new Transition (1281, 1282), // &csube -> ⫑ + new Transition (1283, 1284), // &csup -> ⫐ + new Transition (1285, 1286), // &csupe -> ⫒ + new Transition (1290, 1291), // &ctdot -> ⋯ + new Transition (1297, 1298), // &cudarrl -> ⤸ + new Transition (1299, 1300), // &cudarrr -> ⤵ + new Transition (1303, 1304), // &cuepr -> ⋞ + new Transition (1306, 1307), // &cuesc -> ⋟ + new Transition (1311, 1312), // &cularr -> ↶ + new Transition (1313, 1314), // &cularrp -> ⤽ + new Transition (1316, 1317), // &Cup -> ⋓ + new Transition (1318, 1319), // &cup -> ∪ + new Transition (1324, 1325), // &cupbrcap -> ⩈ + new Transition (1328, 1329), // &CupCap -> ≍ + new Transition (1332, 1333), // &cupcap -> ⩆ + new Transition (1335, 1336), // &cupcup -> ⩊ + new Transition (1339, 1340), // &cupdot -> ⊍ + new Transition (1342, 1343), // &cupor -> ⩅ + new Transition (1344, 1345), // &cups -> ∪︀ + new Transition (1349, 1350), // &curarr -> ↷ + new Transition (1351, 1352), // &curarrm -> ⤼ + new Transition (1360, 1361), // &curlyeqprec -> ⋞ + new Transition (1365, 1366), // &curlyeqsucc -> ⋟ + new Transition (1369, 1370), // &curlyvee -> ⋎ + new Transition (1375, 1376), // &curlywedge -> ⋏ + new Transition (1379, 1380), // ¤ -> ¤ + new Transition (1391, 1392), // &curvearrowleft -> ↶ + new Transition (1397, 1398), // &curvearrowright -> ↷ + new Transition (1401, 1402), // &cuvee -> ⋎ + new Transition (1405, 1406), // &cuwed -> ⋏ + new Transition (1413, 1414), // &cwconint -> ∲ + new Transition (1417, 1418), // &cwint -> ∱ + new Transition (1423, 1424), // &cylcty -> ⌭ + new Transition (1430, 1431), // &Dagger -> ‡ + new Transition (1437, 1438), // &dagger -> † + new Transition (1442, 1443), // &daleth -> ℸ + new Transition (1445, 1446), // &Darr -> ↡ + new Transition (1449, 1450), // &dArr -> ⇓ + new Transition (1452, 1453), // &darr -> ↓ + new Transition (1455, 1456), // &dash -> ‐ + new Transition (1459, 1460), // &Dashv -> ⫤ + new Transition (1461, 1462), // &dashv -> ⊣ + new Transition (1468, 1469), // &dbkarow -> ⤏ + new Transition (1472, 1473), // &dblac -> ˝ + new Transition (1478, 1479), // &Dcaron -> Ď + new Transition (1484, 1485), // &dcaron -> ď + new Transition (1486, 1487), // &Dcy -> Д + new Transition (1488, 1489), // &dcy -> д + new Transition (1490, 1491), // &DD -> ⅅ + new Transition (1492, 1493), // &dd -> ⅆ + new Transition (1498, 1499), // &ddagger -> ‡ + new Transition (1501, 1502), // &ddarr -> ⇊ + new Transition (1508, 1509), // &DDotrahd -> ⤑ + new Transition (1514, 1515), // &ddotseq -> ⩷ + new Transition (1517, 1518), // ° -> ° + new Transition (1520, 1521), // &Del -> ∇ + new Transition (1523, 1524), // &Delta -> Δ + new Transition (1527, 1528), // &delta -> δ + new Transition (1533, 1534), // &demptyv -> ⦱ + new Transition (1539, 1540), // &dfisht -> ⥿ + new Transition (1542, 1543), // &Dfr -> 𝔇 + new Transition (1544, 1545), // &dfr -> 𝔡 + new Transition (1548, 1549), // &dHar -> ⥥ + new Transition (1553, 1554), // &dharl -> ⇃ + new Transition (1555, 1556), // &dharr -> ⇂ + new Transition (1571, 1572), // &DiacriticalAcute -> ´ + new Transition (1575, 1576), // &DiacriticalDot -> ˙ + new Transition (1585, 1586), // &DiacriticalDoubleAcute -> ˝ + new Transition (1591, 1592), // &DiacriticalGrave -> ` + new Transition (1597, 1598), // &DiacriticalTilde -> ˜ + new Transition (1601, 1602), // &diam -> ⋄ + new Transition (1606, 1607), // &Diamond -> ⋄ + new Transition (1610, 1611), // &diamond -> ⋄ + new Transition (1615, 1616), // &diamondsuit -> ♦ + new Transition (1617, 1618), // &diams -> ♦ + new Transition (1619, 1620), // &die -> ¨ + new Transition (1631, 1632), // &DifferentialD -> ⅆ + new Transition (1637, 1638), // &digamma -> ϝ + new Transition (1641, 1642), // &disin -> ⋲ + new Transition (1643, 1644), // &div -> ÷ + new Transition (1647, 1648), // ÷ -> ÷ + new Transition (1655, 1656), // ÷ontimes -> ⋇ + new Transition (1659, 1660), // &divonx -> ⋇ + new Transition (1663, 1664), // &DJcy -> Ђ + new Transition (1667, 1668), // &djcy -> ђ + new Transition (1673, 1674), // &dlcorn -> ⌞ + new Transition (1677, 1678), // &dlcrop -> ⌍ + new Transition (1683, 1684), // &dollar -> $ + new Transition (1687, 1688), // &Dopf -> 𝔻 + new Transition (1690, 1691), // &dopf -> 𝕕 + new Transition (1692, 1693), // &Dot -> ¨ + new Transition (1694, 1695), // &dot -> ˙ + new Transition (1698, 1699), // &DotDot -> ⃜ + new Transition (1701, 1702), // &doteq -> ≐ + new Transition (1705, 1706), // &doteqdot -> ≑ + new Transition (1711, 1712), // &DotEqual -> ≐ + new Transition (1717, 1718), // &dotminus -> ∸ + new Transition (1722, 1723), // &dotplus -> ∔ + new Transition (1729, 1730), // &dotsquare -> ⊡ + new Transition (1742, 1743), // &doublebarwedge -> ⌆ + new Transition (1762, 1763), // &DoubleContourIntegral -> ∯ + new Transition (1766, 1767), // &DoubleDot -> ¨ + new Transition (1774, 1775), // &DoubleDownArrow -> ⇓ + new Transition (1784, 1785), // &DoubleLeftArrow -> ⇐ + new Transition (1795, 1796), // &DoubleLeftRightArrow -> ⇔ + new Transition (1799, 1800), // &DoubleLeftTee -> ⫤ + new Transition (1812, 1813), // &DoubleLongLeftArrow -> ⟸ + new Transition (1823, 1824), // &DoubleLongLeftRightArrow -> ⟺ + new Transition (1834, 1835), // &DoubleLongRightArrow -> ⟹ + new Transition (1845, 1846), // &DoubleRightArrow -> ⇒ + new Transition (1849, 1850), // &DoubleRightTee -> ⊨ + new Transition (1857, 1858), // &DoubleUpArrow -> ⇑ + new Transition (1867, 1868), // &DoubleUpDownArrow -> ⇕ + new Transition (1879, 1880), // &DoubleVerticalBar -> ∥ + new Transition (1887, 1888), // &DownArrow -> ↓ + new Transition (1893, 1894), // &Downarrow -> ⇓ + new Transition (1901, 1902), // &downarrow -> ↓ + new Transition (1905, 1906), // &DownArrowBar -> ⤓ + new Transition (1913, 1914), // &DownArrowUpArrow -> ⇵ + new Transition (1919, 1920), // &DownBreve -> ̑ + new Transition (1930, 1931), // &downdownarrows -> ⇊ + new Transition (1942, 1943), // &downharpoonleft -> ⇃ + new Transition (1948, 1949), // &downharpoonright -> ⇂ + new Transition (1964, 1965), // &DownLeftRightVector -> ⥐ + new Transition (1974, 1975), // &DownLeftTeeVector -> ⥞ + new Transition (1981, 1982), // &DownLeftVector -> ↽ + new Transition (1985, 1986), // &DownLeftVectorBar -> ⥖ + new Transition (2000, 2001), // &DownRightTeeVector -> ⥟ + new Transition (2007, 2008), // &DownRightVector -> ⇁ + new Transition (2011, 2012), // &DownRightVectorBar -> ⥗ + new Transition (2015, 2016), // &DownTee -> ⊤ + new Transition (2021, 2022), // &DownTeeArrow -> ↧ + new Transition (2029, 2030), // &drbkarow -> ⤐ + new Transition (2034, 2035), // &drcorn -> ⌟ + new Transition (2038, 2039), // &drcrop -> ⌌ + new Transition (2042, 2043), // &Dscr -> 𝒟 + new Transition (2046, 2047), // &dscr -> 𝒹 + new Transition (2050, 2051), // &DScy -> Ѕ + new Transition (2052, 2053), // &dscy -> ѕ + new Transition (2055, 2056), // &dsol -> ⧶ + new Transition (2060, 2061), // &Dstrok -> Đ + new Transition (2065, 2066), // &dstrok -> đ + new Transition (2070, 2071), // &dtdot -> ⋱ + new Transition (2073, 2074), // &dtri -> ▿ + new Transition (2075, 2076), // &dtrif -> ▾ + new Transition (2080, 2081), // &duarr -> ⇵ + new Transition (2084, 2085), // &duhar -> ⥯ + new Transition (2091, 2092), // &dwangle -> ⦦ + new Transition (2095, 2096), // &DZcy -> Џ + new Transition (2099, 2100), // &dzcy -> џ + new Transition (2106, 2107), // &dzigrarr -> ⟿ + new Transition (2113, 2114), // É -> É + new Transition (2120, 2121), // é -> é + new Transition (2125, 2126), // &easter -> ⩮ + new Transition (2131, 2132), // &Ecaron -> Ě + new Transition (2137, 2138), // &ecaron -> ě + new Transition (2140, 2141), // &ecir -> ≖ + new Transition (2144, 2145), // Ê -> Ê + new Transition (2146, 2147), // ê -> ê + new Transition (2151, 2152), // &ecolon -> ≕ + new Transition (2153, 2154), // &Ecy -> Э + new Transition (2155, 2156), // &ecy -> э + new Transition (2160, 2161), // &eDDot -> ⩷ + new Transition (2164, 2165), // &Edot -> Ė + new Transition (2167, 2168), // &eDot -> ≑ + new Transition (2171, 2172), // &edot -> ė + new Transition (2173, 2174), // &ee -> ⅇ + new Transition (2178, 2179), // &efDot -> ≒ + new Transition (2181, 2182), // &Efr -> 𝔈 + new Transition (2183, 2184), // &efr -> 𝔢 + new Transition (2185, 2186), // &eg -> ⪚ + new Transition (2191, 2192), // È -> È + new Transition (2196, 2197), // è -> è + new Transition (2198, 2199), // &egs -> ⪖ + new Transition (2202, 2203), // &egsdot -> ⪘ + new Transition (2204, 2205), // &el -> ⪙ + new Transition (2211, 2212), // &Element -> ∈ + new Transition (2218, 2219), // &elinters -> ⏧ + new Transition (2220, 2221), // &ell -> ℓ + new Transition (2222, 2223), // &els -> ⪕ + new Transition (2226, 2227), // &elsdot -> ⪗ + new Transition (2231, 2232), // &Emacr -> Ē + new Transition (2236, 2237), // &emacr -> ē + new Transition (2240, 2241), // &empty -> ∅ + new Transition (2244, 2245), // &emptyset -> ∅ + new Transition (2259, 2260), // &EmptySmallSquare -> ◻ + new Transition (2261, 2262), // &emptyv -> ∅ + new Transition (2277, 2278), // &EmptyVerySmallSquare -> ▫ + new Transition (2280, 2281), // &emsp -> + new Transition (2283, 2284), // &emsp13 ->   + new Transition (2285, 2286), // &emsp14 ->   + new Transition (2288, 2289), // &ENG -> Ŋ + new Transition (2291, 2292), // &eng -> ŋ + new Transition (2294, 2295), // &ensp -> + new Transition (2299, 2300), // &Eogon -> Ę + new Transition (2304, 2305), // &eogon -> ę + new Transition (2307, 2308), // &Eopf -> 𝔼 + new Transition (2310, 2311), // &eopf -> 𝕖 + new Transition (2314, 2315), // &epar -> ⋕ + new Transition (2317, 2318), // &eparsl -> ⧣ + new Transition (2321, 2322), // &eplus -> ⩱ + new Transition (2324, 2325), // &epsi -> ε + new Transition (2331, 2332), // &Epsilon -> Ε + new Transition (2335, 2336), // &epsilon -> ε + new Transition (2337, 2338), // &epsiv -> ϵ + new Transition (2343, 2344), // &eqcirc -> ≖ + new Transition (2348, 2349), // &eqcolon -> ≕ + new Transition (2352, 2353), // &eqsim -> ≂ + new Transition (2360, 2361), // &eqslantgtr -> ⪖ + new Transition (2365, 2366), // &eqslantless -> ⪕ + new Transition (2370, 2371), // &Equal -> ⩵ + new Transition (2375, 2376), // &equals -> = + new Transition (2381, 2382), // &EqualTilde -> ≂ + new Transition (2385, 2386), // &equest -> ≟ + new Transition (2394, 2395), // &Equilibrium -> ⇌ + new Transition (2397, 2398), // &equiv -> ≡ + new Transition (2400, 2401), // &equivDD -> ⩸ + new Transition (2407, 2408), // &eqvparsl -> ⧥ + new Transition (2412, 2413), // &erarr -> ⥱ + new Transition (2416, 2417), // &erDot -> ≓ + new Transition (2420, 2421), // &Escr -> ℰ + new Transition (2424, 2425), // &escr -> ℯ + new Transition (2428, 2429), // &esdot -> ≐ + new Transition (2431, 2432), // &Esim -> ⩳ + new Transition (2434, 2435), // &esim -> ≂ + new Transition (2437, 2438), // &Eta -> Η + new Transition (2440, 2441), // &eta -> η + new Transition (2443, 2444), // Ð -> Ð + new Transition (2445, 2446), // ð -> ð + new Transition (2449, 2450), // Ë -> Ë + new Transition (2453, 2454), // ë -> ë + new Transition (2456, 2457), // &euro -> € + new Transition (2460, 2461), // &excl -> ! + new Transition (2464, 2465), // &exist -> ∃ + new Transition (2470, 2471), // &Exists -> ∃ + new Transition (2480, 2481), // &expectation -> ℰ + new Transition (2491, 2492), // &ExponentialE -> ⅇ + new Transition (2501, 2502), // &exponentiale -> ⅇ + new Transition (2515, 2516), // &fallingdotseq -> ≒ + new Transition (2519, 2520), // &Fcy -> Ф + new Transition (2522, 2523), // &fcy -> ф + new Transition (2528, 2529), // &female -> ♀ + new Transition (2534, 2535), // &ffilig -> ffi + new Transition (2538, 2539), // &fflig -> ff + new Transition (2542, 2543), // &ffllig -> ffl + new Transition (2545, 2546), // &Ffr -> 𝔉 + new Transition (2547, 2548), // &ffr -> 𝔣 + new Transition (2552, 2553), // &filig -> fi + new Transition (2569, 2570), // &FilledSmallSquare -> ◼ + new Transition (2585, 2586), // &FilledVerySmallSquare -> ▪ + new Transition (2590, 2591), // &fjlig -> fj + new Transition (2594, 2595), // &flat -> ♭ + new Transition (2598, 2599), // &fllig -> fl + new Transition (2602, 2603), // &fltns -> ▱ + new Transition (2606, 2607), // &fnof -> ƒ + new Transition (2610, 2611), // &Fopf -> 𝔽 + new Transition (2614, 2615), // &fopf -> 𝕗 + new Transition (2619, 2620), // &ForAll -> ∀ + new Transition (2624, 2625), // &forall -> ∀ + new Transition (2626, 2627), // &fork -> ⋔ + new Transition (2628, 2629), // &forkv -> ⫙ + new Transition (2637, 2638), // &Fouriertrf -> ℱ + new Transition (2645, 2646), // &fpartint -> ⨍ + new Transition (2651, 2652), // ½ -> ½ + new Transition (2653, 2654), // &frac13 -> ⅓ + new Transition (2655, 2656), // ¼ -> ¼ + new Transition (2657, 2658), // &frac15 -> ⅕ + new Transition (2659, 2660), // &frac16 -> ⅙ + new Transition (2661, 2662), // &frac18 -> ⅛ + new Transition (2664, 2665), // &frac23 -> ⅔ + new Transition (2666, 2667), // &frac25 -> ⅖ + new Transition (2669, 2670), // ¾ -> ¾ + new Transition (2671, 2672), // &frac35 -> ⅗ + new Transition (2673, 2674), // &frac38 -> ⅜ + new Transition (2676, 2677), // &frac45 -> ⅘ + new Transition (2679, 2680), // &frac56 -> ⅚ + new Transition (2681, 2682), // &frac58 -> ⅝ + new Transition (2684, 2685), // &frac78 -> ⅞ + new Transition (2687, 2688), // &frasl -> ⁄ + new Transition (2691, 2692), // &frown -> ⌢ + new Transition (2695, 2696), // &Fscr -> ℱ + new Transition (2699, 2700), // &fscr -> 𝒻 + new Transition (2706, 2707), // &gacute -> ǵ + new Transition (2712, 2713), // &Gamma -> Γ + new Transition (2716, 2717), // &gamma -> γ + new Transition (2718, 2719), // &Gammad -> Ϝ + new Transition (2720, 2721), // &gammad -> ϝ + new Transition (2722, 2723), // &gap -> ⪆ + new Transition (2728, 2729), // &Gbreve -> Ğ + new Transition (2734, 2735), // &gbreve -> ğ + new Transition (2740, 2741), // &Gcedil -> Ģ + new Transition (2744, 2745), // &Gcirc -> Ĝ + new Transition (2749, 2750), // &gcirc -> ĝ + new Transition (2751, 2752), // &Gcy -> Г + new Transition (2753, 2754), // &gcy -> г + new Transition (2757, 2758), // &Gdot -> Ġ + new Transition (2761, 2762), // &gdot -> ġ + new Transition (2763, 2764), // &gE -> ≧ + new Transition (2765, 2766), // &ge -> ≥ + new Transition (2767, 2768), // &gEl -> ⪌ + new Transition (2769, 2770), // &gel -> ⋛ + new Transition (2771, 2772), // &geq -> ≥ + new Transition (2773, 2774), // &geqq -> ≧ + new Transition (2779, 2780), // &geqslant -> ⩾ + new Transition (2781, 2782), // &ges -> ⩾ + new Transition (2784, 2785), // &gescc -> ⪩ + new Transition (2788, 2789), // &gesdot -> ⪀ + new Transition (2790, 2791), // &gesdoto -> ⪂ + new Transition (2792, 2793), // &gesdotol -> ⪄ + new Transition (2794, 2795), // &gesl -> ⋛︀ + new Transition (2797, 2798), // &gesles -> ⪔ + new Transition (2800, 2801), // &Gfr -> 𝔊 + new Transition (2803, 2804), // &gfr -> 𝔤 + new Transition (2805, 2806), // &Gg -> ⋙ + new Transition (2807, 2808), // &gg -> ≫ + new Transition (2809, 2810), // &ggg -> ⋙ + new Transition (2814, 2815), // &gimel -> ℷ + new Transition (2818, 2819), // &GJcy -> Ѓ + new Transition (2822, 2823), // &gjcy -> ѓ + new Transition (2824, 2825), // &gl -> ≷ + new Transition (2826, 2827), // &gla -> ⪥ + new Transition (2828, 2829), // &glE -> ⪒ + new Transition (2830, 2831), // &glj -> ⪤ + new Transition (2834, 2835), // &gnap -> ⪊ + new Transition (2839, 2840), // &gnapprox -> ⪊ + new Transition (2841, 2842), // &gnE -> ≩ + new Transition (2843, 2844), // &gne -> ⪈ + new Transition (2845, 2846), // &gneq -> ⪈ + new Transition (2847, 2848), // &gneqq -> ≩ + new Transition (2851, 2852), // &gnsim -> ⋧ + new Transition (2855, 2856), // &Gopf -> 𝔾 + new Transition (2859, 2860), // &gopf -> 𝕘 + new Transition (2864, 2865), // &grave -> ` + new Transition (2876, 2877), // &GreaterEqual -> ≥ + new Transition (2881, 2882), // &GreaterEqualLess -> ⋛ + new Transition (2891, 2892), // &GreaterFullEqual -> ≧ + new Transition (2899, 2900), // &GreaterGreater -> ⪢ + new Transition (2904, 2905), // &GreaterLess -> ≷ + new Transition (2915, 2916), // &GreaterSlantEqual -> ⩾ + new Transition (2921, 2922), // &GreaterTilde -> ≳ + new Transition (2925, 2926), // &Gscr -> 𝒢 + new Transition (2929, 2930), // &gscr -> ℊ + new Transition (2932, 2933), // &gsim -> ≳ + new Transition (2934, 2935), // &gsime -> ⪎ + new Transition (2936, 2937), // &gsiml -> ⪐ + new Transition (2938, 2939), // > -> > + new Transition (2940, 2941), // &Gt -> ≫ + new Transition (2942, 2943), // > -> > + new Transition (2945, 2946), // >cc -> ⪧ + new Transition (2948, 2949), // >cir -> ⩺ + new Transition (2952, 2953), // >dot -> ⋗ + new Transition (2957, 2958), // >lPar -> ⦕ + new Transition (2963, 2964), // >quest -> ⩼ + new Transition (2971, 2972), // >rapprox -> ⪆ + new Transition (2974, 2975), // >rarr -> ⥸ + new Transition (2978, 2979), // >rdot -> ⋗ + new Transition (2985, 2986), // >reqless -> ⋛ + new Transition (2991, 2992), // >reqqless -> ⪌ + new Transition (2996, 2997), // >rless -> ≷ + new Transition (3000, 3001), // >rsim -> ≳ + new Transition (3009, 3010), // &gvertneqq -> ≩︀ + new Transition (3012, 3013), // &gvnE -> ≩︀ + new Transition (3018, 3019), // &Hacek -> ˇ + new Transition (3025, 3026), // &hairsp ->   + new Transition (3028, 3029), // &half -> ½ + new Transition (3033, 3034), // &hamilt -> ℋ + new Transition (3039, 3040), // &HARDcy -> Ъ + new Transition (3044, 3045), // &hardcy -> ъ + new Transition (3048, 3049), // &hArr -> ⇔ + new Transition (3050, 3051), // &harr -> ↔ + new Transition (3054, 3055), // &harrcir -> ⥈ + new Transition (3056, 3057), // &harrw -> ↭ + new Transition (3058, 3059), // &Hat -> ^ + new Transition (3062, 3063), // &hbar -> ℏ + new Transition (3067, 3068), // &Hcirc -> Ĥ + new Transition (3072, 3073), // &hcirc -> ĥ + new Transition (3078, 3079), // &hearts -> ♥ + new Transition (3082, 3083), // &heartsuit -> ♥ + new Transition (3087, 3088), // &hellip -> … + new Transition (3092, 3093), // &hercon -> ⊹ + new Transition (3095, 3096), // &Hfr -> ℌ + new Transition (3098, 3099), // &hfr -> 𝔥 + new Transition (3110, 3111), // &HilbertSpace -> ℋ + new Transition (3118, 3119), // &hksearow -> ⤥ + new Transition (3124, 3125), // &hkswarow -> ⤦ + new Transition (3129, 3130), // &hoarr -> ⇿ + new Transition (3134, 3135), // &homtht -> ∻ + new Transition (3146, 3147), // &hookleftarrow -> ↩ + new Transition (3157, 3158), // &hookrightarrow -> ↪ + new Transition (3161, 3162), // &Hopf -> ℍ + new Transition (3164, 3165), // &hopf -> 𝕙 + new Transition (3169, 3170), // &horbar -> ― + new Transition (3182, 3183), // &HorizontalLine -> ─ + new Transition (3186, 3187), // &Hscr -> ℋ + new Transition (3190, 3191), // &hscr -> 𝒽 + new Transition (3195, 3196), // &hslash -> ℏ + new Transition (3200, 3201), // &Hstrok -> Ħ + new Transition (3205, 3206), // &hstrok -> ħ + new Transition (3217, 3218), // &HumpDownHump -> ≎ + new Transition (3223, 3224), // &HumpEqual -> ≏ + new Transition (3229, 3230), // &hybull -> ⁃ + new Transition (3234, 3235), // &hyphen -> ‐ + new Transition (3241, 3242), // Í -> Í + new Transition (3248, 3249), // í -> í + new Transition (3250, 3251), // &ic -> ⁣ + new Transition (3255, 3256), // Î -> Î + new Transition (3259, 3260), // î -> î + new Transition (3261, 3262), // &Icy -> И + new Transition (3263, 3264), // &icy -> и + new Transition (3267, 3268), // &Idot -> İ + new Transition (3271, 3272), // &IEcy -> Е + new Transition (3275, 3276), // &iecy -> е + new Transition (3279, 3280), // ¡ -> ¡ + new Transition (3282, 3283), // &iff -> ⇔ + new Transition (3285, 3286), // &Ifr -> ℑ + new Transition (3287, 3288), // &ifr -> 𝔦 + new Transition (3293, 3294), // Ì -> Ì + new Transition (3299, 3300), // ì -> ì + new Transition (3301, 3302), // &ii -> ⅈ + new Transition (3306, 3307), // &iiiint -> ⨌ + new Transition (3309, 3310), // &iiint -> ∭ + new Transition (3314, 3315), // &iinfin -> ⧜ + new Transition (3318, 3319), // &iiota -> ℩ + new Transition (3323, 3324), // &IJlig -> IJ + new Transition (3328, 3329), // &ijlig -> ij + new Transition (3330, 3331), // &Im -> ℑ + new Transition (3334, 3335), // &Imacr -> Ī + new Transition (3339, 3340), // &imacr -> ī + new Transition (3342, 3343), // &image -> ℑ + new Transition (3350, 3351), // &ImaginaryI -> ⅈ + new Transition (3355, 3356), // &imagline -> ℐ + new Transition (3360, 3361), // &imagpart -> ℑ + new Transition (3363, 3364), // &imath -> ı + new Transition (3366, 3367), // &imof -> ⊷ + new Transition (3370, 3371), // &imped -> Ƶ + new Transition (3376, 3377), // &Implies -> ⇒ + new Transition (3378, 3379), // &in -> ∈ + new Transition (3383, 3384), // &incare -> ℅ + new Transition (3387, 3388), // &infin -> ∞ + new Transition (3391, 3392), // &infintie -> ⧝ + new Transition (3396, 3397), // &inodot -> ı + new Transition (3399, 3400), // &Int -> ∬ + new Transition (3401, 3402), // &int -> ∫ + new Transition (3405, 3406), // &intcal -> ⊺ + new Transition (3411, 3412), // &integers -> ℤ + new Transition (3417, 3418), // &Integral -> ∫ + new Transition (3422, 3423), // &intercal -> ⊺ + new Transition (3431, 3432), // &Intersection -> ⋂ + new Transition (3437, 3438), // &intlarhk -> ⨗ + new Transition (3442, 3443), // &intprod -> ⨼ + new Transition (3455, 3456), // &InvisibleComma -> ⁣ + new Transition (3461, 3462), // &InvisibleTimes -> ⁢ + new Transition (3465, 3466), // &IOcy -> Ё + new Transition (3469, 3470), // &iocy -> ё + new Transition (3474, 3475), // &Iogon -> Į + new Transition (3478, 3479), // &iogon -> į + new Transition (3481, 3482), // &Iopf -> 𝕀 + new Transition (3484, 3485), // &iopf -> 𝕚 + new Transition (3487, 3488), // &Iota -> Ι + new Transition (3490, 3491), // &iota -> ι + new Transition (3495, 3496), // &iprod -> ⨼ + new Transition (3501, 3502), // ¿ -> ¿ + new Transition (3505, 3506), // &Iscr -> ℐ + new Transition (3509, 3510), // &iscr -> 𝒾 + new Transition (3512, 3513), // &isin -> ∈ + new Transition (3516, 3517), // &isindot -> ⋵ + new Transition (3518, 3519), // &isinE -> ⋹ + new Transition (3520, 3521), // &isins -> ⋴ + new Transition (3522, 3523), // &isinsv -> ⋳ + new Transition (3524, 3525), // &isinv -> ∈ + new Transition (3526, 3527), // &it -> ⁢ + new Transition (3532, 3533), // &Itilde -> Ĩ + new Transition (3537, 3538), // &itilde -> ĩ + new Transition (3542, 3543), // &Iukcy -> І + new Transition (3547, 3548), // &iukcy -> і + new Transition (3550, 3551), // Ï -> Ï + new Transition (3553, 3554), // ï -> ï + new Transition (3559, 3560), // &Jcirc -> Ĵ + new Transition (3565, 3566), // &jcirc -> ĵ + new Transition (3567, 3568), // &Jcy -> Й + new Transition (3569, 3570), // &jcy -> й + new Transition (3572, 3573), // &Jfr -> 𝔍 + new Transition (3575, 3576), // &jfr -> 𝔧 + new Transition (3580, 3581), // &jmath -> ȷ + new Transition (3584, 3585), // &Jopf -> 𝕁 + new Transition (3588, 3589), // &jopf -> 𝕛 + new Transition (3592, 3593), // &Jscr -> 𝒥 + new Transition (3596, 3597), // &jscr -> 𝒿 + new Transition (3601, 3602), // &Jsercy -> Ј + new Transition (3606, 3607), // &jsercy -> ј + new Transition (3611, 3612), // &Jukcy -> Є + new Transition (3616, 3617), // &jukcy -> є + new Transition (3622, 3623), // &Kappa -> Κ + new Transition (3628, 3629), // &kappa -> κ + new Transition (3630, 3631), // &kappav -> ϰ + new Transition (3636, 3637), // &Kcedil -> Ķ + new Transition (3642, 3643), // &kcedil -> ķ + new Transition (3644, 3645), // &Kcy -> К + new Transition (3646, 3647), // &kcy -> к + new Transition (3649, 3650), // &Kfr -> 𝔎 + new Transition (3652, 3653), // &kfr -> 𝔨 + new Transition (3658, 3659), // &kgreen -> ĸ + new Transition (3662, 3663), // &KHcy -> Х + new Transition (3666, 3667), // &khcy -> х + new Transition (3670, 3671), // &KJcy -> Ќ + new Transition (3674, 3675), // &kjcy -> ќ + new Transition (3678, 3679), // &Kopf -> 𝕂 + new Transition (3682, 3683), // &kopf -> 𝕜 + new Transition (3686, 3687), // &Kscr -> 𝒦 + new Transition (3690, 3691), // &kscr -> 𝓀 + new Transition (3696, 3697), // &lAarr -> ⇚ + new Transition (3703, 3704), // &Lacute -> Ĺ + new Transition (3709, 3710), // &lacute -> ĺ + new Transition (3716, 3717), // &laemptyv -> ⦴ + new Transition (3721, 3722), // &lagran -> ℒ + new Transition (3726, 3727), // &Lambda -> Λ + new Transition (3731, 3732), // &lambda -> λ + new Transition (3734, 3735), // &Lang -> ⟪ + new Transition (3737, 3738), // &lang -> 〈 + new Transition (3739, 3740), // &langd -> ⦑ + new Transition (3742, 3743), // &langle -> ⟨ + new Transition (3744, 3745), // &lap -> ⪅ + new Transition (3753, 3754), // &Laplacetrf -> ℒ + new Transition (3757, 3758), // « -> « + new Transition (3760, 3761), // &Larr -> ↞ + new Transition (3763, 3764), // &lArr -> ⇐ + new Transition (3766, 3767), // &larr -> ← + new Transition (3768, 3769), // &larrb -> ⇤ + new Transition (3771, 3772), // &larrbfs -> ⤟ + new Transition (3774, 3775), // &larrfs -> ⤝ + new Transition (3777, 3778), // &larrhk -> ↩ + new Transition (3780, 3781), // &larrlp -> ↫ + new Transition (3783, 3784), // &larrpl -> ⤹ + new Transition (3787, 3788), // &larrsim -> ⥳ + new Transition (3790, 3791), // &larrtl -> ↢ + new Transition (3792, 3793), // &lat -> ⪫ + new Transition (3797, 3798), // &lAtail -> ⤛ + new Transition (3801, 3802), // &latail -> ⤙ + new Transition (3803, 3804), // &late -> ⪭ + new Transition (3805, 3806), // &lates -> ⪭︀ + new Transition (3810, 3811), // &lBarr -> ⤎ + new Transition (3815, 3816), // &lbarr -> ⤌ + new Transition (3819, 3820), // &lbbrk -> ❲ + new Transition (3824, 3825), // &lbrace -> { + new Transition (3826, 3827), // &lbrack -> [ + new Transition (3829, 3830), // &lbrke -> ⦋ + new Transition (3833, 3834), // &lbrksld -> ⦏ + new Transition (3835, 3836), // &lbrkslu -> ⦍ + new Transition (3841, 3842), // &Lcaron -> Ľ + new Transition (3847, 3848), // &lcaron -> ľ + new Transition (3852, 3853), // &Lcedil -> Ļ + new Transition (3857, 3858), // &lcedil -> ļ + new Transition (3860, 3861), // &lceil -> ⌈ + new Transition (3863, 3864), // &lcub -> { + new Transition (3865, 3866), // &Lcy -> Л + new Transition (3867, 3868), // &lcy -> л + new Transition (3871, 3872), // &ldca -> ⤶ + new Transition (3875, 3876), // &ldquo -> “ + new Transition (3877, 3878), // &ldquor -> „ + new Transition (3883, 3884), // &ldrdhar -> ⥧ + new Transition (3889, 3890), // &ldrushar -> ⥋ + new Transition (3892, 3893), // &ldsh -> ↲ + new Transition (3894, 3895), // &lE -> ≦ + new Transition (3896, 3897), // &le -> ≤ + new Transition (3912, 3913), // &LeftAngleBracket -> ⟨ + new Transition (3917, 3918), // &LeftArrow -> ← + new Transition (3923, 3924), // &Leftarrow -> ⇐ + new Transition (3931, 3932), // &leftarrow -> ← + new Transition (3935, 3936), // &LeftArrowBar -> ⇤ + new Transition (3946, 3947), // &LeftArrowRightArrow -> ⇆ + new Transition (3951, 3952), // &leftarrowtail -> ↢ + new Transition (3959, 3960), // &LeftCeiling -> ⌈ + new Transition (3973, 3974), // &LeftDoubleBracket -> ⟦ + new Transition (3985, 3986), // &LeftDownTeeVector -> ⥡ + new Transition (3992, 3993), // &LeftDownVector -> ⇃ + new Transition (3996, 3997), // &LeftDownVectorBar -> ⥙ + new Transition (4002, 4003), // &LeftFloor -> ⌊ + new Transition (4014, 4015), // &leftharpoondown -> ↽ + new Transition (4017, 4018), // &leftharpoonup -> ↼ + new Transition (4028, 4029), // &leftleftarrows -> ⇇ + new Transition (4039, 4040), // &LeftRightArrow -> ↔ + new Transition (4050, 4051), // &Leftrightarrow -> ⇔ + new Transition (4061, 4062), // &leftrightarrow -> ↔ + new Transition (4063, 4064), // &leftrightarrows -> ⇆ + new Transition (4072, 4073), // &leftrightharpoons -> ⇋ + new Transition (4083, 4084), // &leftrightsquigarrow -> ↭ + new Transition (4090, 4091), // &LeftRightVector -> ⥎ + new Transition (4094, 4095), // &LeftTee -> ⊣ + new Transition (4100, 4101), // &LeftTeeArrow -> ↤ + new Transition (4107, 4108), // &LeftTeeVector -> ⥚ + new Transition (4118, 4119), // &leftthreetimes -> ⋋ + new Transition (4126, 4127), // &LeftTriangle -> ⊲ + new Transition (4130, 4131), // &LeftTriangleBar -> ⧏ + new Transition (4136, 4137), // &LeftTriangleEqual -> ⊴ + new Transition (4149, 4150), // &LeftUpDownVector -> ⥑ + new Transition (4159, 4160), // &LeftUpTeeVector -> ⥠ + new Transition (4166, 4167), // &LeftUpVector -> ↿ + new Transition (4170, 4171), // &LeftUpVectorBar -> ⥘ + new Transition (4177, 4178), // &LeftVector -> ↼ + new Transition (4181, 4182), // &LeftVectorBar -> ⥒ + new Transition (4183, 4184), // &lEg -> ⪋ + new Transition (4185, 4186), // &leg -> ⋚ + new Transition (4187, 4188), // &leq -> ≤ + new Transition (4189, 4190), // &leqq -> ≦ + new Transition (4195, 4196), // &leqslant -> ⩽ + new Transition (4197, 4198), // &les -> ⩽ + new Transition (4200, 4201), // &lescc -> ⪨ + new Transition (4204, 4205), // &lesdot -> ⩿ + new Transition (4206, 4207), // &lesdoto -> ⪁ + new Transition (4208, 4209), // &lesdotor -> ⪃ + new Transition (4210, 4211), // &lesg -> ⋚︀ + new Transition (4213, 4214), // &lesges -> ⪓ + new Transition (4221, 4222), // &lessapprox -> ⪅ + new Transition (4225, 4226), // &lessdot -> ⋖ + new Transition (4231, 4232), // &lesseqgtr -> ⋚ + new Transition (4236, 4237), // &lesseqqgtr -> ⪋ + new Transition (4251, 4252), // &LessEqualGreater -> ⋚ + new Transition (4261, 4262), // &LessFullEqual -> ≦ + new Transition (4269, 4270), // &LessGreater -> ≶ + new Transition (4273, 4274), // &lessgtr -> ≶ + new Transition (4278, 4279), // &LessLess -> ⪡ + new Transition (4282, 4283), // &lesssim -> ≲ + new Transition (4293, 4294), // &LessSlantEqual -> ⩽ + new Transition (4299, 4300), // &LessTilde -> ≲ + new Transition (4305, 4306), // &lfisht -> ⥼ + new Transition (4310, 4311), // &lfloor -> ⌊ + new Transition (4313, 4314), // &Lfr -> 𝔏 + new Transition (4315, 4316), // &lfr -> 𝔩 + new Transition (4317, 4318), // &lg -> ≶ + new Transition (4319, 4320), // &lgE -> ⪑ + new Transition (4323, 4324), // &lHar -> ⥢ + new Transition (4328, 4329), // &lhard -> ↽ + new Transition (4330, 4331), // &lharu -> ↼ + new Transition (4332, 4333), // &lharul -> ⥪ + new Transition (4336, 4337), // &lhblk -> ▄ + new Transition (4340, 4341), // &LJcy -> Љ + new Transition (4344, 4345), // &ljcy -> љ + new Transition (4346, 4347), // &Ll -> ⋘ + new Transition (4348, 4349), // &ll -> ≪ + new Transition (4352, 4353), // &llarr -> ⇇ + new Transition (4359, 4360), // &llcorner -> ⌞ + new Transition (4368, 4369), // &Lleftarrow -> ⇚ + new Transition (4373, 4374), // &llhard -> ⥫ + new Transition (4377, 4378), // &lltri -> ◺ + new Transition (4383, 4384), // &Lmidot -> Ŀ + new Transition (4389, 4390), // &lmidot -> ŀ + new Transition (4394, 4395), // &lmoust -> ⎰ + new Transition (4399, 4400), // &lmoustache -> ⎰ + new Transition (4403, 4404), // &lnap -> ⪉ + new Transition (4408, 4409), // &lnapprox -> ⪉ + new Transition (4410, 4411), // &lnE -> ≨ + new Transition (4412, 4413), // &lne -> ⪇ + new Transition (4414, 4415), // &lneq -> ⪇ + new Transition (4416, 4417), // &lneqq -> ≨ + new Transition (4420, 4421), // &lnsim -> ⋦ + new Transition (4425, 4426), // &loang -> ⟬ + new Transition (4428, 4429), // &loarr -> ⇽ + new Transition (4432, 4433), // &lobrk -> ⟦ + new Transition (4445, 4446), // &LongLeftArrow -> ⟵ + new Transition (4455, 4456), // &Longleftarrow -> ⟸ + new Transition (4467, 4468), // &longleftarrow -> ⟵ + new Transition (4478, 4479), // &LongLeftRightArrow -> ⟷ + new Transition (4489, 4490), // &Longleftrightarrow -> ⟺ + new Transition (4500, 4501), // &longleftrightarrow -> ⟷ + new Transition (4507, 4508), // &longmapsto -> ⟼ + new Transition (4518, 4519), // &LongRightArrow -> ⟶ + new Transition (4529, 4530), // &Longrightarrow -> ⟹ + new Transition (4540, 4541), // &longrightarrow -> ⟶ + new Transition (4552, 4553), // &looparrowleft -> ↫ + new Transition (4558, 4559), // &looparrowright -> ↬ + new Transition (4562, 4563), // &lopar -> ⦅ + new Transition (4565, 4566), // &Lopf -> 𝕃 + new Transition (4567, 4568), // &lopf -> 𝕝 + new Transition (4571, 4572), // &loplus -> ⨭ + new Transition (4577, 4578), // &lotimes -> ⨴ + new Transition (4582, 4583), // &lowast -> ∗ + new Transition (4586, 4587), // &lowbar -> _ + new Transition (4599, 4600), // &LowerLeftArrow -> ↙ + new Transition (4610, 4611), // &LowerRightArrow -> ↘ + new Transition (4612, 4613), // &loz -> ◊ + new Transition (4617, 4618), // &lozenge -> ◊ + new Transition (4619, 4620), // &lozf -> ⧫ + new Transition (4623, 4624), // &lpar -> ( + new Transition (4626, 4627), // &lparlt -> ⦓ + new Transition (4631, 4632), // &lrarr -> ⇆ + new Transition (4638, 4639), // &lrcorner -> ⌟ + new Transition (4642, 4643), // &lrhar -> ⇋ + new Transition (4644, 4645), // &lrhard -> ⥭ + new Transition (4646, 4647), // &lrm -> + new Transition (4650, 4651), // &lrtri -> ⊿ + new Transition (4656, 4657), // &lsaquo -> ‹ + new Transition (4660, 4661), // &Lscr -> ℒ + new Transition (4663, 4664), // &lscr -> 𝓁 + new Transition (4665, 4666), // &Lsh -> ↰ + new Transition (4667, 4668), // &lsh -> ↰ + new Transition (4670, 4671), // &lsim -> ≲ + new Transition (4672, 4673), // &lsime -> ⪍ + new Transition (4674, 4675), // &lsimg -> ⪏ + new Transition (4677, 4678), // &lsqb -> [ + new Transition (4680, 4681), // &lsquo -> ‘ + new Transition (4682, 4683), // &lsquor -> ‚ + new Transition (4687, 4688), // &Lstrok -> Ł + new Transition (4692, 4693), // &lstrok -> ł + new Transition (4694, 4695), // < -> < + new Transition (4696, 4697), // &Lt -> ≪ + new Transition (4698, 4699), // < -> < + new Transition (4701, 4702), // <cc -> ⪦ + new Transition (4704, 4705), // <cir -> ⩹ + new Transition (4708, 4709), // <dot -> ⋖ + new Transition (4713, 4714), // <hree -> ⋋ + new Transition (4718, 4719), // <imes -> ⋉ + new Transition (4723, 4724), // <larr -> ⥶ + new Transition (4729, 4730), // <quest -> ⩻ + new Transition (4732, 4733), // <ri -> ◃ + new Transition (4734, 4735), // <rie -> ⊴ + new Transition (4736, 4737), // <rif -> ◂ + new Transition (4740, 4741), // <rPar -> ⦖ + new Transition (4748, 4749), // &lurdshar -> ⥊ + new Transition (4753, 4754), // &luruhar -> ⥦ + new Transition (4762, 4763), // &lvertneqq -> ≨︀ + new Transition (4765, 4766), // &lvnE -> ≨︀ + new Transition (4770, 4771), // ¯ -> ¯ + new Transition (4773, 4774), // &male -> ♂ + new Transition (4775, 4776), // &malt -> ✠ + new Transition (4779, 4780), // &maltese -> ✠ + new Transition (4783, 4784), // &Map -> ⤅ + new Transition (4785, 4786), // &map -> ↦ + new Transition (4789, 4790), // &mapsto -> ↦ + new Transition (4794, 4795), // &mapstodown -> ↧ + new Transition (4799, 4800), // &mapstoleft -> ↤ + new Transition (4802, 4803), // &mapstoup -> ↥ + new Transition (4807, 4808), // &marker -> ▮ + new Transition (4813, 4814), // &mcomma -> ⨩ + new Transition (4816, 4817), // &Mcy -> М + new Transition (4818, 4819), // &mcy -> м + new Transition (4823, 4824), // &mdash -> — + new Transition (4828, 4829), // &mDDot -> ∺ + new Transition (4841, 4842), // &measuredangle -> ∡ + new Transition (4852, 4853), // &MediumSpace ->   + new Transition (4860, 4861), // &Mellintrf -> ℳ + new Transition (4863, 4864), // &Mfr -> 𝔐 + new Transition (4866, 4867), // &mfr -> 𝔪 + new Transition (4869, 4870), // &mho -> ℧ + new Transition (4874, 4875), // µ -> µ + new Transition (4876, 4877), // &mid -> ∣ + new Transition (4880, 4881), // &midast -> * + new Transition (4884, 4885), // &midcir -> ⫰ + new Transition (4888, 4889), // · -> · + new Transition (4892, 4893), // &minus -> − + new Transition (4894, 4895), // &minusb -> ⊟ + new Transition (4896, 4897), // &minusd -> ∸ + new Transition (4898, 4899), // &minusdu -> ⨪ + new Transition (4907, 4908), // &MinusPlus -> ∓ + new Transition (4911, 4912), // &mlcp -> ⫛ + new Transition (4914, 4915), // &mldr -> … + new Transition (4920, 4921), // &mnplus -> ∓ + new Transition (4926, 4927), // &models -> ⊧ + new Transition (4930, 4931), // &Mopf -> 𝕄 + new Transition (4933, 4934), // &mopf -> 𝕞 + new Transition (4935, 4936), // &mp -> ∓ + new Transition (4939, 4940), // &Mscr -> ℳ + new Transition (4943, 4944), // &mscr -> 𝓂 + new Transition (4948, 4949), // &mstpos -> ∾ + new Transition (4950, 4951), // &Mu -> Μ + new Transition (4952, 4953), // &mu -> μ + new Transition (4959, 4960), // &multimap -> ⊸ + new Transition (4963, 4964), // &mumap -> ⊸ + new Transition (4969, 4970), // &nabla -> ∇ + new Transition (4976, 4977), // &Nacute -> Ń + new Transition (4981, 4982), // &nacute -> ń + new Transition (4984, 4985), // &nang -> ∠⃒ + new Transition (4986, 4987), // &nap -> ≉ + new Transition (4988, 4989), // &napE -> ⩰̸ + new Transition (4991, 4992), // &napid -> ≋̸ + new Transition (4994, 4995), // &napos -> ʼn + new Transition (4999, 5000), // &napprox -> ≉ + new Transition (5003, 5004), // &natur -> ♮ + new Transition (5006, 5007), // &natural -> ♮ + new Transition (5008, 5009), // &naturals -> ℕ + new Transition (5012, 5013), //   -> + new Transition (5016, 5017), // &nbump -> ≎̸ + new Transition (5018, 5019), // &nbumpe -> ≏̸ + new Transition (5022, 5023), // &ncap -> ⩃ + new Transition (5028, 5029), // &Ncaron -> Ň + new Transition (5032, 5033), // &ncaron -> ň + new Transition (5037, 5038), // &Ncedil -> Ņ + new Transition (5042, 5043), // &ncedil -> ņ + new Transition (5046, 5047), // &ncong -> ≇ + new Transition (5050, 5051), // &ncongdot -> ⩭̸ + new Transition (5053, 5054), // &ncup -> ⩂ + new Transition (5055, 5056), // &Ncy -> Н + new Transition (5057, 5058), // &ncy -> н + new Transition (5062, 5063), // &ndash -> – + new Transition (5064, 5065), // &ne -> ≠ + new Transition (5069, 5070), // &nearhk -> ⤤ + new Transition (5073, 5074), // &neArr -> ⇗ + new Transition (5075, 5076), // &nearr -> ↗ + new Transition (5078, 5079), // &nearrow -> ↗ + new Transition (5082, 5083), // &nedot -> ≐̸ + new Transition (5101, 5102), // &NegativeMediumSpace -> ​ + new Transition (5112, 5113), // &NegativeThickSpace -> ​ + new Transition (5119, 5120), // &NegativeThinSpace -> ​ + new Transition (5133, 5134), // &NegativeVeryThinSpace -> ​ + new Transition (5138, 5139), // &nequiv -> ≢ + new Transition (5143, 5144), // &nesear -> ⤨ + new Transition (5146, 5147), // &nesim -> ≂̸ + new Transition (5165, 5166), // &NestedGreaterGreater -> ≫ + new Transition (5174, 5175), // &NestedLessLess -> ≪ + new Transition (5180, 5181), // &NewLine -> 
 + new Transition (5185, 5186), // &nexist -> ∄ + new Transition (5187, 5188), // &nexists -> ∄ + new Transition (5190, 5191), // &Nfr -> 𝔑 + new Transition (5193, 5194), // &nfr -> 𝔫 + new Transition (5196, 5197), // &ngE -> ≧̸ + new Transition (5198, 5199), // &nge -> ≱ + new Transition (5200, 5201), // &ngeq -> ≱ + new Transition (5202, 5203), // &ngeqq -> ≧̸ + new Transition (5208, 5209), // &ngeqslant -> ⩾̸ + new Transition (5210, 5211), // &nges -> ⩾̸ + new Transition (5213, 5214), // &nGg -> ⋙̸ + new Transition (5217, 5218), // &ngsim -> ≵ + new Transition (5219, 5220), // &nGt -> ≫⃒ + new Transition (5221, 5222), // &ngt -> ≯ + new Transition (5223, 5224), // &ngtr -> ≯ + new Transition (5225, 5226), // &nGtv -> ≫̸ + new Transition (5230, 5231), // &nhArr -> ⇎ + new Transition (5234, 5235), // &nharr -> ↮ + new Transition (5238, 5239), // &nhpar -> ⫲ + new Transition (5240, 5241), // &ni -> ∋ + new Transition (5242, 5243), // &nis -> ⋼ + new Transition (5244, 5245), // &nisd -> ⋺ + new Transition (5246, 5247), // &niv -> ∋ + new Transition (5250, 5251), // &NJcy -> Њ + new Transition (5254, 5255), // &njcy -> њ + new Transition (5259, 5260), // &nlArr -> ⇍ + new Transition (5263, 5264), // &nlarr -> ↚ + new Transition (5266, 5267), // &nldr -> ‥ + new Transition (5268, 5269), // &nlE -> ≦̸ + new Transition (5270, 5271), // &nle -> ≰ + new Transition (5280, 5281), // &nLeftarrow -> ⇍ + new Transition (5288, 5289), // &nleftarrow -> ↚ + new Transition (5299, 5300), // &nLeftrightarrow -> ⇎ + new Transition (5310, 5311), // &nleftrightarrow -> ↮ + new Transition (5312, 5313), // &nleq -> ≰ + new Transition (5314, 5315), // &nleqq -> ≦̸ + new Transition (5320, 5321), // &nleqslant -> ⩽̸ + new Transition (5322, 5323), // &nles -> ⩽̸ + new Transition (5324, 5325), // &nless -> ≮ + new Transition (5326, 5327), // &nLl -> ⋘̸ + new Transition (5330, 5331), // &nlsim -> ≴ + new Transition (5332, 5333), // &nLt -> ≪⃒ + new Transition (5334, 5335), // &nlt -> ≮ + new Transition (5337, 5338), // &nltri -> ⋪ + new Transition (5339, 5340), // &nltrie -> ⋬ + new Transition (5341, 5342), // &nLtv -> ≪̸ + new Transition (5345, 5346), // &nmid -> ∤ + new Transition (5352, 5353), // &NoBreak -> ⁠ + new Transition (5367, 5368), // &NonBreakingSpace ->   + new Transition (5370, 5371), // &Nopf -> ℕ + new Transition (5374, 5375), // &nopf -> 𝕟 + new Transition (5376, 5377), // &Not -> ⫬ + new Transition (5378, 5379), // ¬ -> ¬ + new Transition (5388, 5389), // &NotCongruent -> ≢ + new Transition (5394, 5395), // &NotCupCap -> ≭ + new Transition (5412, 5413), // &NotDoubleVerticalBar -> ∦ + new Transition (5420, 5421), // &NotElement -> ∉ + new Transition (5425, 5426), // &NotEqual -> ≠ + new Transition (5431, 5432), // &NotEqualTilde -> ≂̸ + new Transition (5437, 5438), // &NotExists -> ∄ + new Transition (5445, 5446), // &NotGreater -> ≯ + new Transition (5451, 5452), // &NotGreaterEqual -> ≱ + new Transition (5461, 5462), // &NotGreaterFullEqual -> ≧̸ + new Transition (5469, 5470), // &NotGreaterGreater -> ≫̸ + new Transition (5474, 5475), // &NotGreaterLess -> ≹ + new Transition (5485, 5486), // &NotGreaterSlantEqual -> ⩾̸ + new Transition (5491, 5492), // &NotGreaterTilde -> ≵ + new Transition (5504, 5505), // &NotHumpDownHump -> ≎̸ + new Transition (5510, 5511), // &NotHumpEqual -> ≏̸ + new Transition (5513, 5514), // ¬in -> ∉ + new Transition (5517, 5518), // ¬indot -> ⋵̸ + new Transition (5519, 5520), // ¬inE -> ⋹̸ + new Transition (5522, 5523), // ¬inva -> ∉ + new Transition (5524, 5525), // ¬invb -> ⋷ + new Transition (5526, 5527), // ¬invc -> ⋶ + new Transition (5539, 5540), // &NotLeftTriangle -> ⋪ + new Transition (5543, 5544), // &NotLeftTriangleBar -> ⧏̸ + new Transition (5549, 5550), // &NotLeftTriangleEqual -> ⋬ + new Transition (5552, 5553), // &NotLess -> ≮ + new Transition (5558, 5559), // &NotLessEqual -> ≰ + new Transition (5566, 5567), // &NotLessGreater -> ≸ + new Transition (5571, 5572), // &NotLessLess -> ≪̸ + new Transition (5582, 5583), // &NotLessSlantEqual -> ⩽̸ + new Transition (5588, 5589), // &NotLessTilde -> ≴ + new Transition (5609, 5610), // &NotNestedGreaterGreater -> ⪢̸ + new Transition (5618, 5619), // &NotNestedLessLess -> ⪡̸ + new Transition (5621, 5622), // ¬ni -> ∌ + new Transition (5624, 5625), // ¬niva -> ∌ + new Transition (5626, 5627), // ¬nivb -> ⋾ + new Transition (5628, 5629), // ¬nivc -> ⋽ + new Transition (5637, 5638), // &NotPrecedes -> ⊀ + new Transition (5643, 5644), // &NotPrecedesEqual -> ⪯̸ + new Transition (5654, 5655), // &NotPrecedesSlantEqual -> ⋠ + new Transition (5669, 5670), // &NotReverseElement -> ∌ + new Transition (5682, 5683), // &NotRightTriangle -> ⋫ + new Transition (5686, 5687), // &NotRightTriangleBar -> ⧐̸ + new Transition (5692, 5693), // &NotRightTriangleEqual -> ⋭ + new Transition (5705, 5706), // &NotSquareSubset -> ⊏̸ + new Transition (5711, 5712), // &NotSquareSubsetEqual -> ⋢ + new Transition (5718, 5719), // &NotSquareSuperset -> ⊐̸ + new Transition (5724, 5725), // &NotSquareSupersetEqual -> ⋣ + new Transition (5730, 5731), // &NotSubset -> ⊂⃒ + new Transition (5736, 5737), // &NotSubsetEqual -> ⊈ + new Transition (5743, 5744), // &NotSucceeds -> ⊁ + new Transition (5749, 5750), // &NotSucceedsEqual -> ⪰̸ + new Transition (5760, 5761), // &NotSucceedsSlantEqual -> ⋡ + new Transition (5766, 5767), // &NotSucceedsTilde -> ≿̸ + new Transition (5773, 5774), // &NotSuperset -> ⊃⃒ + new Transition (5779, 5780), // &NotSupersetEqual -> ⊉ + new Transition (5785, 5786), // &NotTilde -> ≁ + new Transition (5791, 5792), // &NotTildeEqual -> ≄ + new Transition (5801, 5802), // &NotTildeFullEqual -> ≇ + new Transition (5807, 5808), // &NotTildeTilde -> ≉ + new Transition (5819, 5820), // &NotVerticalBar -> ∤ + new Transition (5823, 5824), // &npar -> ∦ + new Transition (5829, 5830), // &nparallel -> ∦ + new Transition (5832, 5833), // &nparsl -> ⫽⃥ + new Transition (5834, 5835), // &npart -> ∂̸ + new Transition (5840, 5841), // &npolint -> ⨔ + new Transition (5842, 5843), // &npr -> ⊀ + new Transition (5846, 5847), // &nprcue -> ⋠ + new Transition (5848, 5849), // &npre -> ⪯̸ + new Transition (5850, 5851), // &nprec -> ⊀ + new Transition (5853, 5854), // &npreceq -> ⪯̸ + new Transition (5858, 5859), // &nrArr -> ⇏ + new Transition (5862, 5863), // &nrarr -> ↛ + new Transition (5864, 5865), // &nrarrc -> ⤳̸ + new Transition (5866, 5867), // &nrarrw -> ↝̸ + new Transition (5877, 5878), // &nRightarrow -> ⇏ + new Transition (5887, 5888), // &nrightarrow -> ↛ + new Transition (5891, 5892), // &nrtri -> ⋫ + new Transition (5893, 5894), // &nrtrie -> ⋭ + new Transition (5896, 5897), // &nsc -> ⊁ + new Transition (5900, 5901), // &nsccue -> ⋡ + new Transition (5902, 5903), // &nsce -> ⪰̸ + new Transition (5906, 5907), // &Nscr -> 𝒩 + new Transition (5908, 5909), // &nscr -> 𝓃 + new Transition (5916, 5917), // &nshortmid -> ∤ + new Transition (5925, 5926), // &nshortparallel -> ∦ + new Transition (5928, 5929), // &nsim -> ≁ + new Transition (5930, 5931), // &nsime -> ≄ + new Transition (5932, 5933), // &nsimeq -> ≄ + new Transition (5936, 5937), // &nsmid -> ∤ + new Transition (5940, 5941), // &nspar -> ∦ + new Transition (5946, 5947), // &nsqsube -> ⋢ + new Transition (5949, 5950), // &nsqsupe -> ⋣ + new Transition (5952, 5953), // &nsub -> ⊄ + new Transition (5954, 5955), // &nsubE -> ⫅̸ + new Transition (5956, 5957), // &nsube -> ⊈ + new Transition (5960, 5961), // &nsubset -> ⊂⃒ + new Transition (5963, 5964), // &nsubseteq -> ⊈ + new Transition (5965, 5966), // &nsubseteqq -> ⫅̸ + new Transition (5968, 5969), // &nsucc -> ⊁ + new Transition (5971, 5972), // &nsucceq -> ⪰̸ + new Transition (5973, 5974), // &nsup -> ⊅ + new Transition (5975, 5976), // &nsupE -> ⫆̸ + new Transition (5977, 5978), // &nsupe -> ⊉ + new Transition (5981, 5982), // &nsupset -> ⊃⃒ + new Transition (5984, 5985), // &nsupseteq -> ⊉ + new Transition (5986, 5987), // &nsupseteqq -> ⫆̸ + new Transition (5990, 5991), // &ntgl -> ≹ + new Transition (5996, 5997), // Ñ -> Ñ + new Transition (6001, 6002), // ñ -> ñ + new Transition (6004, 6005), // &ntlg -> ≸ + new Transition (6016, 6017), // &ntriangleleft -> ⋪ + new Transition (6019, 6020), // &ntrianglelefteq -> ⋬ + new Transition (6025, 6026), // &ntriangleright -> ⋫ + new Transition (6028, 6029), // &ntrianglerighteq -> ⋭ + new Transition (6030, 6031), // &Nu -> Ν + new Transition (6032, 6033), // &nu -> ν + new Transition (6034, 6035), // &num -> # + new Transition (6038, 6039), // &numero -> № + new Transition (6041, 6042), // &numsp ->   + new Transition (6045, 6046), // &nvap -> ≍⃒ + new Transition (6051, 6052), // &nVDash -> ⊯ + new Transition (6056, 6057), // &nVdash -> ⊮ + new Transition (6061, 6062), // &nvDash -> ⊭ + new Transition (6066, 6067), // &nvdash -> ⊬ + new Transition (6069, 6070), // &nvge -> ≥⃒ + new Transition (6071, 6072), // &nvgt -> >⃒ + new Transition (6076, 6077), // &nvHarr -> ⤄ + new Transition (6082, 6083), // &nvinfin -> ⧞ + new Transition (6087, 6088), // &nvlArr -> ⤂ + new Transition (6089, 6090), // &nvle -> ≤⃒ + new Transition (6091, 6092), // &nvlt -> <⃒ + new Transition (6095, 6096), // &nvltrie -> ⊴⃒ + new Transition (6100, 6101), // &nvrArr -> ⤃ + new Transition (6105, 6106), // &nvrtrie -> ⊵⃒ + new Transition (6109, 6110), // &nvsim -> ∼⃒ + new Transition (6115, 6116), // &nwarhk -> ⤣ + new Transition (6119, 6120), // &nwArr -> ⇖ + new Transition (6121, 6122), // &nwarr -> ↖ + new Transition (6124, 6125), // &nwarrow -> ↖ + new Transition (6129, 6130), // &nwnear -> ⤧ + new Transition (6136, 6137), // Ó -> Ó + new Transition (6143, 6144), // ó -> ó + new Transition (6146, 6147), // &oast -> ⊛ + new Transition (6150, 6151), // &ocir -> ⊚ + new Transition (6155, 6156), // Ô -> Ô + new Transition (6157, 6158), // ô -> ô + new Transition (6159, 6160), // &Ocy -> О + new Transition (6161, 6162), // &ocy -> о + new Transition (6166, 6167), // &odash -> ⊝ + new Transition (6172, 6173), // &Odblac -> Ő + new Transition (6177, 6178), // &odblac -> ő + new Transition (6180, 6181), // &odiv -> ⨸ + new Transition (6183, 6184), // &odot -> ⊙ + new Transition (6188, 6189), // &odsold -> ⦼ + new Transition (6193, 6194), // &OElig -> Œ + new Transition (6198, 6199), // &oelig -> œ + new Transition (6203, 6204), // &ofcir -> ⦿ + new Transition (6206, 6207), // &Ofr -> 𝔒 + new Transition (6208, 6209), // &ofr -> 𝔬 + new Transition (6212, 6213), // &ogon -> ˛ + new Transition (6218, 6219), // Ò -> Ò + new Transition (6223, 6224), // ò -> ò + new Transition (6225, 6226), // &ogt -> ⧁ + new Transition (6230, 6231), // &ohbar -> ⦵ + new Transition (6232, 6233), // &ohm -> Ω + new Transition (6236, 6237), // &oint -> ∮ + new Transition (6241, 6242), // &olarr -> ↺ + new Transition (6245, 6246), // &olcir -> ⦾ + new Transition (6250, 6251), // &olcross -> ⦻ + new Transition (6254, 6255), // &oline -> ‾ + new Transition (6256, 6257), // &olt -> ⧀ + new Transition (6261, 6262), // &Omacr -> Ō + new Transition (6266, 6267), // &omacr -> ō + new Transition (6270, 6271), // &Omega -> Ω + new Transition (6274, 6275), // &omega -> ω + new Transition (6280, 6281), // &Omicron -> Ο + new Transition (6286, 6287), // &omicron -> ο + new Transition (6288, 6289), // &omid -> ⦶ + new Transition (6292, 6293), // &ominus -> ⊖ + new Transition (6296, 6297), // &Oopf -> 𝕆 + new Transition (6300, 6301), // &oopf -> 𝕠 + new Transition (6304, 6305), // &opar -> ⦷ + new Transition (6324, 6325), // &OpenCurlyDoubleQuote -> “ + new Transition (6330, 6331), // &OpenCurlyQuote -> ‘ + new Transition (6334, 6335), // &operp -> ⦹ + new Transition (6338, 6339), // &oplus -> ⊕ + new Transition (6340, 6341), // &Or -> ⩔ + new Transition (6342, 6343), // &or -> ∨ + new Transition (6346, 6347), // &orarr -> ↻ + new Transition (6348, 6349), // &ord -> ⩝ + new Transition (6351, 6352), // &order -> ℴ + new Transition (6354, 6355), // &orderof -> ℴ + new Transition (6356, 6357), // ª -> ª + new Transition (6358, 6359), // º -> º + new Transition (6363, 6364), // &origof -> ⊶ + new Transition (6366, 6367), // &oror -> ⩖ + new Transition (6372, 6373), // &orslope -> ⩗ + new Transition (6374, 6375), // &orv -> ⩛ + new Transition (6376, 6377), // &oS -> Ⓢ + new Transition (6380, 6381), // &Oscr -> 𝒪 + new Transition (6384, 6385), // &oscr -> ℴ + new Transition (6389, 6390), // Ø -> Ø + new Transition (6394, 6395), // ø -> ø + new Transition (6397, 6398), // &osol -> ⊘ + new Transition (6403, 6404), // Õ -> Õ + new Transition (6409, 6410), // õ -> õ + new Transition (6413, 6414), // &Otimes -> ⨷ + new Transition (6417, 6418), // &otimes -> ⊗ + new Transition (6420, 6421), // &otimesas -> ⨶ + new Transition (6424, 6425), // Ö -> Ö + new Transition (6428, 6429), // ö -> ö + new Transition (6433, 6434), // &ovbar -> ⌽ + new Transition (6440, 6441), // &OverBar -> ‾ + new Transition (6445, 6446), // &OverBrace -> ⏞ + new Transition (6449, 6450), // &OverBracket -> ⎴ + new Transition (6461, 6462), // &OverParenthesis -> ⏜ + new Transition (6465, 6466), // &par -> ∥ + new Transition (6467, 6468), // ¶ -> ¶ + new Transition (6472, 6473), // ¶llel -> ∥ + new Transition (6476, 6477), // &parsim -> ⫳ + new Transition (6478, 6479), // &parsl -> ⫽ + new Transition (6480, 6481), // &part -> ∂ + new Transition (6489, 6490), // &PartialD -> ∂ + new Transition (6492, 6493), // &Pcy -> П + new Transition (6495, 6496), // &pcy -> п + new Transition (6501, 6502), // &percnt -> % + new Transition (6505, 6506), // &period -> . + new Transition (6509, 6510), // &permil -> ‰ + new Transition (6511, 6512), // &perp -> ⊥ + new Transition (6516, 6517), // &pertenk -> ‱ + new Transition (6519, 6520), // &Pfr -> 𝔓 + new Transition (6522, 6523), // &pfr -> 𝔭 + new Transition (6525, 6526), // &Phi -> Φ + new Transition (6528, 6529), // &phi -> φ + new Transition (6530, 6531), // &phiv -> ϕ + new Transition (6535, 6536), // &phmmat -> ℳ + new Transition (6539, 6540), // &phone -> ☎ + new Transition (6541, 6542), // &Pi -> Π + new Transition (6543, 6544), // &pi -> π + new Transition (6551, 6552), // &pitchfork -> ⋔ + new Transition (6553, 6554), // &piv -> ϖ + new Transition (6559, 6560), // &planck -> ℏ + new Transition (6561, 6562), // &planckh -> ℎ + new Transition (6564, 6565), // &plankv -> ℏ + new Transition (6567, 6568), // &plus -> + + new Transition (6572, 6573), // &plusacir -> ⨣ + new Transition (6574, 6575), // &plusb -> ⊞ + new Transition (6578, 6579), // &pluscir -> ⨢ + new Transition (6581, 6582), // &plusdo -> ∔ + new Transition (6583, 6584), // &plusdu -> ⨥ + new Transition (6585, 6586), // &pluse -> ⩲ + new Transition (6594, 6595), // &PlusMinus -> ± + new Transition (6597, 6598), // ± -> ± + new Transition (6601, 6602), // &plussim -> ⨦ + new Transition (6605, 6606), // &plustwo -> ⨧ + new Transition (6607, 6608), // &pm -> ± + new Transition (6620, 6621), // &Poincareplane -> ℌ + new Transition (6628, 6629), // &pointint -> ⨕ + new Transition (6631, 6632), // &Popf -> ℙ + new Transition (6634, 6635), // &popf -> 𝕡 + new Transition (6638, 6639), // £ -> £ + new Transition (6640, 6641), // &Pr -> ⪻ + new Transition (6642, 6643), // &pr -> ≺ + new Transition (6645, 6646), // &prap -> ⪷ + new Transition (6649, 6650), // &prcue -> ≼ + new Transition (6651, 6652), // &prE -> ⪳ + new Transition (6653, 6654), // &pre -> ⪯ + new Transition (6655, 6656), // &prec -> ≺ + new Transition (6662, 6663), // &precapprox -> ⪷ + new Transition (6670, 6671), // &preccurlyeq -> ≼ + new Transition (6677, 6678), // &Precedes -> ≺ + new Transition (6683, 6684), // &PrecedesEqual -> ⪯ + new Transition (6694, 6695), // &PrecedesSlantEqual -> ≼ + new Transition (6700, 6701), // &PrecedesTilde -> ≾ + new Transition (6703, 6704), // &preceq -> ⪯ + new Transition (6711, 6712), // &precnapprox -> ⪹ + new Transition (6715, 6716), // &precneqq -> ⪵ + new Transition (6719, 6720), // &precnsim -> ⋨ + new Transition (6723, 6724), // &precsim -> ≾ + new Transition (6727, 6728), // &Prime -> ″ + new Transition (6731, 6732), // &prime -> ′ + new Transition (6733, 6734), // &primes -> ℙ + new Transition (6737, 6738), // &prnap -> ⪹ + new Transition (6739, 6740), // &prnE -> ⪵ + new Transition (6743, 6744), // &prnsim -> ⋨ + new Transition (6746, 6747), // &prod -> ∏ + new Transition (6752, 6753), // &Product -> ∏ + new Transition (6758, 6759), // &profalar -> ⌮ + new Transition (6763, 6764), // &profline -> ⌒ + new Transition (6768, 6769), // &profsurf -> ⌓ + new Transition (6770, 6771), // &prop -> ∝ + new Transition (6778, 6779), // &Proportion -> ∷ + new Transition (6781, 6782), // &Proportional -> ∝ + new Transition (6784, 6785), // &propto -> ∝ + new Transition (6788, 6789), // &prsim -> ≾ + new Transition (6793, 6794), // &prurel -> ⊰ + new Transition (6797, 6798), // &Pscr -> 𝒫 + new Transition (6801, 6802), // &pscr -> 𝓅 + new Transition (6803, 6804), // &Psi -> Ψ + new Transition (6805, 6806), // &psi -> ψ + new Transition (6811, 6812), // &puncsp ->   + new Transition (6815, 6816), // &Qfr -> 𝔔 + new Transition (6819, 6820), // &qfr -> 𝔮 + new Transition (6823, 6824), // &qint -> ⨌ + new Transition (6827, 6828), // &Qopf -> ℚ + new Transition (6831, 6832), // &qopf -> 𝕢 + new Transition (6837, 6838), // &qprime -> ⁗ + new Transition (6841, 6842), // &Qscr -> 𝒬 + new Transition (6845, 6846), // &qscr -> 𝓆 + new Transition (6856, 6857), // &quaternions -> ℍ + new Transition (6860, 6861), // &quatint -> ⨖ + new Transition (6864, 6865), // &quest -> ? + new Transition (6867, 6868), // &questeq -> ≟ + new Transition (6871, 6872), // " -> " + new Transition (6874, 6875), // " -> " + new Transition (6880, 6881), // &rAarr -> ⇛ + new Transition (6884, 6885), // &race -> ∽̱ + new Transition (6891, 6892), // &Racute -> Ŕ + new Transition (6895, 6896), // &racute -> ŕ + new Transition (6899, 6900), // &radic -> √ + new Transition (6906, 6907), // &raemptyv -> ⦳ + new Transition (6909, 6910), // &Rang -> ⟫ + new Transition (6912, 6913), // &rang -> 〉 + new Transition (6914, 6915), // &rangd -> ⦒ + new Transition (6916, 6917), // &range -> ⦥ + new Transition (6919, 6920), // &rangle -> ⟩ + new Transition (6923, 6924), // » -> » + new Transition (6926, 6927), // &Rarr -> ↠ + new Transition (6929, 6930), // &rArr -> ⇒ + new Transition (6932, 6933), // &rarr -> → + new Transition (6935, 6936), // &rarrap -> ⥵ + new Transition (6937, 6938), // &rarrb -> ⇥ + new Transition (6940, 6941), // &rarrbfs -> ⤠ + new Transition (6942, 6943), // &rarrc -> ⤳ + new Transition (6945, 6946), // &rarrfs -> ⤞ + new Transition (6948, 6949), // &rarrhk -> ↪ + new Transition (6951, 6952), // &rarrlp -> ↬ + new Transition (6954, 6955), // &rarrpl -> ⥅ + new Transition (6958, 6959), // &rarrsim -> ⥴ + new Transition (6961, 6962), // &Rarrtl -> ⤖ + new Transition (6964, 6965), // &rarrtl -> ↣ + new Transition (6966, 6967), // &rarrw -> ↝ + new Transition (6971, 6972), // &rAtail -> ⤜ + new Transition (6976, 6977), // &ratail -> ⤚ + new Transition (6979, 6980), // &ratio -> ∶ + new Transition (6984, 6985), // &rationals -> ℚ + new Transition (6989, 6990), // &RBarr -> ⤐ + new Transition (6994, 6995), // &rBarr -> ⤏ + new Transition (6999, 7000), // &rbarr -> ⤍ + new Transition (7003, 7004), // &rbbrk -> ❳ + new Transition (7008, 7009), // &rbrace -> } + new Transition (7010, 7011), // &rbrack -> ] + new Transition (7013, 7014), // &rbrke -> ⦌ + new Transition (7017, 7018), // &rbrksld -> ⦎ + new Transition (7019, 7020), // &rbrkslu -> ⦐ + new Transition (7025, 7026), // &Rcaron -> Ř + new Transition (7031, 7032), // &rcaron -> ř + new Transition (7036, 7037), // &Rcedil -> Ŗ + new Transition (7041, 7042), // &rcedil -> ŗ + new Transition (7044, 7045), // &rceil -> ⌉ + new Transition (7047, 7048), // &rcub -> } + new Transition (7049, 7050), // &Rcy -> Р + new Transition (7051, 7052), // &rcy -> р + new Transition (7055, 7056), // &rdca -> ⤷ + new Transition (7061, 7062), // &rdldhar -> ⥩ + new Transition (7065, 7066), // &rdquo -> ” + new Transition (7067, 7068), // &rdquor -> ” + new Transition (7070, 7071), // &rdsh -> ↳ + new Transition (7072, 7073), // &Re -> ℜ + new Transition (7076, 7077), // &real -> ℜ + new Transition (7080, 7081), // &realine -> ℛ + new Transition (7085, 7086), // &realpart -> ℜ + new Transition (7087, 7088), // &reals -> ℝ + new Transition (7090, 7091), // &rect -> ▭ + new Transition (7093, 7094), // ® -> ® + new Transition (7095, 7096), // ® -> ® + new Transition (7108, 7109), // &ReverseElement -> ∋ + new Transition (7119, 7120), // &ReverseEquilibrium -> ⇋ + new Transition (7133, 7134), // &ReverseUpEquilibrium -> ⥯ + new Transition (7139, 7140), // &rfisht -> ⥽ + new Transition (7144, 7145), // &rfloor -> ⌋ + new Transition (7147, 7148), // &Rfr -> ℜ + new Transition (7149, 7150), // &rfr -> 𝔯 + new Transition (7153, 7154), // &rHar -> ⥤ + new Transition (7158, 7159), // &rhard -> ⇁ + new Transition (7160, 7161), // &rharu -> ⇀ + new Transition (7162, 7163), // &rharul -> ⥬ + new Transition (7165, 7166), // &Rho -> Ρ + new Transition (7167, 7168), // &rho -> ρ + new Transition (7169, 7170), // &rhov -> ϱ + new Transition (7186, 7187), // &RightAngleBracket -> ⟩ + new Transition (7191, 7192), // &RightArrow -> → + new Transition (7197, 7198), // &Rightarrow -> ⇒ + new Transition (7207, 7208), // &rightarrow -> → + new Transition (7211, 7212), // &RightArrowBar -> ⇥ + new Transition (7221, 7222), // &RightArrowLeftArrow -> ⇄ + new Transition (7226, 7227), // &rightarrowtail -> ↣ + new Transition (7234, 7235), // &RightCeiling -> ⌉ + new Transition (7248, 7249), // &RightDoubleBracket -> ⟧ + new Transition (7260, 7261), // &RightDownTeeVector -> ⥝ + new Transition (7267, 7268), // &RightDownVector -> ⇂ + new Transition (7271, 7272), // &RightDownVectorBar -> ⥕ + new Transition (7277, 7278), // &RightFloor -> ⌋ + new Transition (7289, 7290), // &rightharpoondown -> ⇁ + new Transition (7292, 7293), // &rightharpoonup -> ⇀ + new Transition (7303, 7304), // &rightleftarrows -> ⇄ + new Transition (7312, 7313), // &rightleftharpoons -> ⇌ + new Transition (7324, 7325), // &rightrightarrows -> ⇉ + new Transition (7335, 7336), // &rightsquigarrow -> ↝ + new Transition (7339, 7340), // &RightTee -> ⊢ + new Transition (7345, 7346), // &RightTeeArrow -> ↦ + new Transition (7352, 7353), // &RightTeeVector -> ⥛ + new Transition (7363, 7364), // &rightthreetimes -> ⋌ + new Transition (7371, 7372), // &RightTriangle -> ⊳ + new Transition (7375, 7376), // &RightTriangleBar -> ⧐ + new Transition (7381, 7382), // &RightTriangleEqual -> ⊵ + new Transition (7394, 7395), // &RightUpDownVector -> ⥏ + new Transition (7404, 7405), // &RightUpTeeVector -> ⥜ + new Transition (7411, 7412), // &RightUpVector -> ↾ + new Transition (7415, 7416), // &RightUpVectorBar -> ⥔ + new Transition (7422, 7423), // &RightVector -> ⇀ + new Transition (7426, 7427), // &RightVectorBar -> ⥓ + new Transition (7429, 7430), // &ring -> ˚ + new Transition (7440, 7441), // &risingdotseq -> ≓ + new Transition (7445, 7446), // &rlarr -> ⇄ + new Transition (7449, 7450), // &rlhar -> ⇌ + new Transition (7451, 7452), // &rlm -> + new Transition (7457, 7458), // &rmoust -> ⎱ + new Transition (7462, 7463), // &rmoustache -> ⎱ + new Transition (7467, 7468), // &rnmid -> ⫮ + new Transition (7472, 7473), // &roang -> ⟭ + new Transition (7475, 7476), // &roarr -> ⇾ + new Transition (7479, 7480), // &robrk -> ⟧ + new Transition (7483, 7484), // &ropar -> ⦆ + new Transition (7487, 7488), // &Ropf -> ℝ + new Transition (7489, 7490), // &ropf -> 𝕣 + new Transition (7493, 7494), // &roplus -> ⨮ + new Transition (7499, 7500), // &rotimes -> ⨵ + new Transition (7510, 7511), // &RoundImplies -> ⥰ + new Transition (7514, 7515), // &rpar -> ) + new Transition (7517, 7518), // &rpargt -> ⦔ + new Transition (7524, 7525), // &rppolint -> ⨒ + new Transition (7529, 7530), // &rrarr -> ⇉ + new Transition (7540, 7541), // &Rrightarrow -> ⇛ + new Transition (7546, 7547), // &rsaquo -> › + new Transition (7550, 7551), // &Rscr -> ℛ + new Transition (7553, 7554), // &rscr -> 𝓇 + new Transition (7555, 7556), // &Rsh -> ↱ + new Transition (7557, 7558), // &rsh -> ↱ + new Transition (7560, 7561), // &rsqb -> ] + new Transition (7563, 7564), // &rsquo -> ’ + new Transition (7565, 7566), // &rsquor -> ’ + new Transition (7571, 7572), // &rthree -> ⋌ + new Transition (7576, 7577), // &rtimes -> ⋊ + new Transition (7579, 7580), // &rtri -> ▹ + new Transition (7581, 7582), // &rtrie -> ⊵ + new Transition (7583, 7584), // &rtrif -> ▸ + new Transition (7588, 7589), // &rtriltri -> ⧎ + new Transition (7599, 7600), // &RuleDelayed -> ⧴ + new Transition (7606, 7607), // &ruluhar -> ⥨ + new Transition (7608, 7609), // &rx -> ℞ + new Transition (7615, 7616), // &Sacute -> Ś + new Transition (7622, 7623), // &sacute -> ś + new Transition (7627, 7628), // &sbquo -> ‚ + new Transition (7629, 7630), // &Sc -> ⪼ + new Transition (7631, 7632), // &sc -> ≻ + new Transition (7634, 7635), // &scap -> ⪸ + new Transition (7639, 7640), // &Scaron -> Š + new Transition (7643, 7644), // &scaron -> š + new Transition (7647, 7648), // &sccue -> ≽ + new Transition (7649, 7650), // &scE -> ⪴ + new Transition (7651, 7652), // &sce -> ⪰ + new Transition (7656, 7657), // &Scedil -> Ş + new Transition (7660, 7661), // &scedil -> ş + new Transition (7664, 7665), // &Scirc -> Ŝ + new Transition (7668, 7669), // &scirc -> ŝ + new Transition (7672, 7673), // &scnap -> ⪺ + new Transition (7674, 7675), // &scnE -> ⪶ + new Transition (7678, 7679), // &scnsim -> ⋩ + new Transition (7685, 7686), // &scpolint -> ⨓ + new Transition (7689, 7690), // &scsim -> ≿ + new Transition (7691, 7692), // &Scy -> С + new Transition (7693, 7694), // &scy -> с + new Transition (7697, 7698), // &sdot -> ⋅ + new Transition (7699, 7700), // &sdotb -> ⊡ + new Transition (7701, 7702), // &sdote -> ⩦ + new Transition (7707, 7708), // &searhk -> ⤥ + new Transition (7711, 7712), // &seArr -> ⇘ + new Transition (7713, 7714), // &searr -> ↘ + new Transition (7716, 7717), // &searrow -> ↘ + new Transition (7719, 7720), // § -> § + new Transition (7722, 7723), // &semi -> ; + new Transition (7727, 7728), // &seswar -> ⤩ + new Transition (7734, 7735), // &setminus -> ∖ + new Transition (7736, 7737), // &setmn -> ∖ + new Transition (7739, 7740), // &sext -> ✶ + new Transition (7742, 7743), // &Sfr -> 𝔖 + new Transition (7745, 7746), // &sfr -> 𝔰 + new Transition (7749, 7750), // &sfrown -> ⌢ + new Transition (7754, 7755), // &sharp -> ♯ + new Transition (7760, 7761), // &SHCHcy -> Щ + new Transition (7765, 7766), // &shchcy -> щ + new Transition (7768, 7769), // &SHcy -> Ш + new Transition (7770, 7771), // &shcy -> ш + new Transition (7784, 7785), // &ShortDownArrow -> ↓ + new Transition (7794, 7795), // &ShortLeftArrow -> ← + new Transition (7801, 7802), // &shortmid -> ∣ + new Transition (7810, 7811), // &shortparallel -> ∥ + new Transition (7821, 7822), // &ShortRightArrow -> → + new Transition (7829, 7830), // &ShortUpArrow -> ↑ + new Transition (7831, 7832), // ­ -> + new Transition (7836, 7837), // &Sigma -> Σ + new Transition (7841, 7842), // &sigma -> σ + new Transition (7843, 7844), // &sigmaf -> ς + new Transition (7845, 7846), // &sigmav -> ς + new Transition (7847, 7848), // &sim -> ∼ + new Transition (7851, 7852), // &simdot -> ⩪ + new Transition (7853, 7854), // &sime -> ≃ + new Transition (7855, 7856), // &simeq -> ≃ + new Transition (7857, 7858), // &simg -> ⪞ + new Transition (7859, 7860), // &simgE -> ⪠ + new Transition (7861, 7862), // &siml -> ⪝ + new Transition (7863, 7864), // &simlE -> ⪟ + new Transition (7866, 7867), // &simne -> ≆ + new Transition (7871, 7872), // &simplus -> ⨤ + new Transition (7876, 7877), // &simrarr -> ⥲ + new Transition (7881, 7882), // &slarr -> ← + new Transition (7892, 7893), // &SmallCircle -> ∘ + new Transition (7905, 7906), // &smallsetminus -> ∖ + new Transition (7909, 7910), // &smashp -> ⨳ + new Transition (7916, 7917), // &smeparsl -> ⧤ + new Transition (7919, 7920), // &smid -> ∣ + new Transition (7922, 7923), // &smile -> ⌣ + new Transition (7924, 7925), // &smt -> ⪪ + new Transition (7926, 7927), // &smte -> ⪬ + new Transition (7928, 7929), // &smtes -> ⪬︀ + new Transition (7934, 7935), // &SOFTcy -> Ь + new Transition (7940, 7941), // &softcy -> ь + new Transition (7942, 7943), // &sol -> / + new Transition (7944, 7945), // &solb -> ⧄ + new Transition (7947, 7948), // &solbar -> ⌿ + new Transition (7951, 7952), // &Sopf -> 𝕊 + new Transition (7954, 7955), // &sopf -> 𝕤 + new Transition (7960, 7961), // &spades -> ♠ + new Transition (7964, 7965), // &spadesuit -> ♠ + new Transition (7966, 7967), // &spar -> ∥ + new Transition (7971, 7972), // &sqcap -> ⊓ + new Transition (7973, 7974), // &sqcaps -> ⊓︀ + new Transition (7976, 7977), // &sqcup -> ⊔ + new Transition (7978, 7979), // &sqcups -> ⊔︀ + new Transition (7982, 7983), // &Sqrt -> √ + new Transition (7986, 7987), // &sqsub -> ⊏ + new Transition (7988, 7989), // &sqsube -> ⊑ + new Transition (7992, 7993), // &sqsubset -> ⊏ + new Transition (7995, 7996), // &sqsubseteq -> ⊑ + new Transition (7997, 7998), // &sqsup -> ⊐ + new Transition (7999, 8000), // &sqsupe -> ⊒ + new Transition (8003, 8004), // &sqsupset -> ⊐ + new Transition (8006, 8007), // &sqsupseteq -> ⊒ + new Transition (8008, 8009), // &squ -> □ + new Transition (8013, 8014), // &Square -> □ + new Transition (8017, 8018), // &square -> □ + new Transition (8030, 8031), // &SquareIntersection -> ⊓ + new Transition (8037, 8038), // &SquareSubset -> ⊏ + new Transition (8043, 8044), // &SquareSubsetEqual -> ⊑ + new Transition (8050, 8051), // &SquareSuperset -> ⊐ + new Transition (8056, 8057), // &SquareSupersetEqual -> ⊒ + new Transition (8062, 8063), // &SquareUnion -> ⊔ + new Transition (8064, 8065), // &squarf -> ▪ + new Transition (8066, 8067), // &squf -> ▪ + new Transition (8071, 8072), // &srarr -> → + new Transition (8075, 8076), // &Sscr -> 𝒮 + new Transition (8079, 8080), // &sscr -> 𝓈 + new Transition (8084, 8085), // &ssetmn -> ∖ + new Transition (8089, 8090), // &ssmile -> ⌣ + new Transition (8094, 8095), // &sstarf -> ⋆ + new Transition (8098, 8099), // &Star -> ⋆ + new Transition (8102, 8103), // &star -> ☆ + new Transition (8104, 8105), // &starf -> ★ + new Transition (8118, 8119), // &straightepsilon -> ϵ + new Transition (8122, 8123), // &straightphi -> ϕ + new Transition (8125, 8126), // &strns -> ¯ + new Transition (8128, 8129), // &Sub -> ⋐ + new Transition (8131, 8132), // &sub -> ⊂ + new Transition (8135, 8136), // &subdot -> ⪽ + new Transition (8137, 8138), // &subE -> ⫅ + new Transition (8139, 8140), // &sube -> ⊆ + new Transition (8143, 8144), // &subedot -> ⫃ + new Transition (8148, 8149), // &submult -> ⫁ + new Transition (8151, 8152), // &subnE -> ⫋ + new Transition (8153, 8154), // &subne -> ⊊ + new Transition (8158, 8159), // &subplus -> ⪿ + new Transition (8163, 8164), // &subrarr -> ⥹ + new Transition (8167, 8168), // &Subset -> ⋐ + new Transition (8171, 8172), // &subset -> ⊂ + new Transition (8174, 8175), // &subseteq -> ⊆ + new Transition (8176, 8177), // &subseteqq -> ⫅ + new Transition (8182, 8183), // &SubsetEqual -> ⊆ + new Transition (8186, 8187), // &subsetneq -> ⊊ + new Transition (8188, 8189), // &subsetneqq -> ⫋ + new Transition (8191, 8192), // &subsim -> ⫇ + new Transition (8194, 8195), // &subsub -> ⫕ + new Transition (8196, 8197), // &subsup -> ⫓ + new Transition (8199, 8200), // &succ -> ≻ + new Transition (8206, 8207), // &succapprox -> ⪸ + new Transition (8214, 8215), // &succcurlyeq -> ≽ + new Transition (8221, 8222), // &Succeeds -> ≻ + new Transition (8227, 8228), // &SucceedsEqual -> ⪰ + new Transition (8238, 8239), // &SucceedsSlantEqual -> ≽ + new Transition (8244, 8245), // &SucceedsTilde -> ≿ + new Transition (8247, 8248), // &succeq -> ⪰ + new Transition (8255, 8256), // &succnapprox -> ⪺ + new Transition (8259, 8260), // &succneqq -> ⪶ + new Transition (8263, 8264), // &succnsim -> ⋩ + new Transition (8267, 8268), // &succsim -> ≿ + new Transition (8273, 8274), // &SuchThat -> ∋ + new Transition (8275, 8276), // &Sum -> ∑ + new Transition (8277, 8278), // &sum -> ∑ + new Transition (8280, 8281), // &sung -> ♪ + new Transition (8282, 8283), // &Sup -> ⋑ + new Transition (8284, 8285), // &sup -> ⊃ + new Transition (8286, 8287), // ¹ -> ¹ + new Transition (8288, 8289), // ² -> ² + new Transition (8290, 8291), // ³ -> ³ + new Transition (8294, 8295), // &supdot -> ⪾ + new Transition (8298, 8299), // &supdsub -> ⫘ + new Transition (8300, 8301), // &supE -> ⫆ + new Transition (8302, 8303), // &supe -> ⊇ + new Transition (8306, 8307), // &supedot -> ⫄ + new Transition (8312, 8313), // &Superset -> ⊃ + new Transition (8318, 8319), // &SupersetEqual -> ⊇ + new Transition (8323, 8324), // &suphsol -> ⟉ + new Transition (8326, 8327), // &suphsub -> ⫗ + new Transition (8331, 8332), // &suplarr -> ⥻ + new Transition (8336, 8337), // &supmult -> ⫂ + new Transition (8339, 8340), // &supnE -> ⫌ + new Transition (8341, 8342), // &supne -> ⊋ + new Transition (8346, 8347), // &supplus -> ⫀ + new Transition (8350, 8351), // &Supset -> ⋑ + new Transition (8354, 8355), // &supset -> ⊃ + new Transition (8357, 8358), // &supseteq -> ⊇ + new Transition (8359, 8360), // &supseteqq -> ⫆ + new Transition (8363, 8364), // &supsetneq -> ⊋ + new Transition (8365, 8366), // &supsetneqq -> ⫌ + new Transition (8368, 8369), // &supsim -> ⫈ + new Transition (8371, 8372), // &supsub -> ⫔ + new Transition (8373, 8374), // &supsup -> ⫖ + new Transition (8379, 8380), // &swarhk -> ⤦ + new Transition (8383, 8384), // &swArr -> ⇙ + new Transition (8385, 8386), // &swarr -> ↙ + new Transition (8388, 8389), // &swarrow -> ↙ + new Transition (8393, 8394), // &swnwar -> ⤪ + new Transition (8398, 8399), // ß -> ß + new Transition (8402, 8403), // &Tab -> 	 + new Transition (8409, 8410), // &target -> ⌖ + new Transition (8411, 8412), // &Tau -> Τ + new Transition (8413, 8414), // &tau -> τ + new Transition (8417, 8418), // &tbrk -> ⎴ + new Transition (8423, 8424), // &Tcaron -> Ť + new Transition (8429, 8430), // &tcaron -> ť + new Transition (8434, 8435), // &Tcedil -> Ţ + new Transition (8439, 8440), // &tcedil -> ţ + new Transition (8441, 8442), // &Tcy -> Т + new Transition (8443, 8444), // &tcy -> т + new Transition (8447, 8448), // &tdot -> ⃛ + new Transition (8453, 8454), // &telrec -> ⌕ + new Transition (8456, 8457), // &Tfr -> 𝔗 + new Transition (8459, 8460), // &tfr -> 𝔱 + new Transition (8465, 8466), // &there4 -> ∴ + new Transition (8474, 8475), // &Therefore -> ∴ + new Transition (8479, 8480), // &therefore -> ∴ + new Transition (8482, 8483), // &Theta -> Θ + new Transition (8485, 8486), // &theta -> θ + new Transition (8489, 8490), // &thetasym -> ϑ + new Transition (8491, 8492), // &thetav -> ϑ + new Transition (8501, 8502), // &thickapprox -> ≈ + new Transition (8505, 8506), // &thicksim -> ∼ + new Transition (8514, 8515), // &ThickSpace ->    + new Transition (8518, 8519), // &thinsp -> + new Transition (8525, 8526), // &ThinSpace ->   + new Transition (8529, 8530), // &thkap -> ≈ + new Transition (8533, 8534), // &thksim -> ∼ + new Transition (8538, 8539), // Þ -> Þ + new Transition (8542, 8543), // þ -> þ + new Transition (8547, 8548), // &Tilde -> ∼ + new Transition (8552, 8553), // &tilde -> ˜ + new Transition (8558, 8559), // &TildeEqual -> ≃ + new Transition (8568, 8569), // &TildeFullEqual -> ≅ + new Transition (8574, 8575), // &TildeTilde -> ≈ + new Transition (8578, 8579), // × -> × + new Transition (8580, 8581), // ×b -> ⊠ + new Transition (8583, 8584), // ×bar -> ⨱ + new Transition (8585, 8586), // ×d -> ⨰ + new Transition (8588, 8589), // &tint -> ∭ + new Transition (8592, 8593), // &toea -> ⤨ + new Transition (8594, 8595), // &top -> ⊤ + new Transition (8598, 8599), // &topbot -> ⌶ + new Transition (8602, 8603), // &topcir -> ⫱ + new Transition (8606, 8607), // &Topf -> 𝕋 + new Transition (8608, 8609), // &topf -> 𝕥 + new Transition (8612, 8613), // &topfork -> ⫚ + new Transition (8615, 8616), // &tosa -> ⤩ + new Transition (8621, 8622), // &tprime -> ‴ + new Transition (8626, 8627), // &TRADE -> ™ + new Transition (8631, 8632), // &trade -> ™ + new Transition (8638, 8639), // &triangle -> ▵ + new Transition (8643, 8644), // &triangledown -> ▿ + new Transition (8648, 8649), // &triangleleft -> ◃ + new Transition (8651, 8652), // &trianglelefteq -> ⊴ + new Transition (8653, 8654), // &triangleq -> ≜ + new Transition (8659, 8660), // &triangleright -> ▹ + new Transition (8662, 8663), // &trianglerighteq -> ⊵ + new Transition (8666, 8667), // &tridot -> ◬ + new Transition (8668, 8669), // &trie -> ≜ + new Transition (8674, 8675), // &triminus -> ⨺ + new Transition (8683, 8684), // &TripleDot -> ⃛ + new Transition (8688, 8689), // &triplus -> ⨹ + new Transition (8691, 8692), // &trisb -> ⧍ + new Transition (8696, 8697), // &tritime -> ⨻ + new Transition (8703, 8704), // &trpezium -> ⏢ + new Transition (8707, 8708), // &Tscr -> 𝒯 + new Transition (8711, 8712), // &tscr -> 𝓉 + new Transition (8715, 8716), // &TScy -> Ц + new Transition (8717, 8718), // &tscy -> ц + new Transition (8721, 8722), // &TSHcy -> Ћ + new Transition (8725, 8726), // &tshcy -> ћ + new Transition (8730, 8731), // &Tstrok -> Ŧ + new Transition (8735, 8736), // &tstrok -> ŧ + new Transition (8740, 8741), // &twixt -> ≬ + new Transition (8755, 8756), // &twoheadleftarrow -> ↞ + new Transition (8766, 8767), // &twoheadrightarrow -> ↠ + new Transition (8773, 8774), // Ú -> Ú + new Transition (8780, 8781), // ú -> ú + new Transition (8783, 8784), // &Uarr -> ↟ + new Transition (8787, 8788), // &uArr -> ⇑ + new Transition (8790, 8791), // &uarr -> ↑ + new Transition (8795, 8796), // &Uarrocir -> ⥉ + new Transition (8800, 8801), // &Ubrcy -> Ў + new Transition (8805, 8806), // &ubrcy -> ў + new Transition (8809, 8810), // &Ubreve -> Ŭ + new Transition (8813, 8814), // &ubreve -> ŭ + new Transition (8818, 8819), // Û -> Û + new Transition (8823, 8824), // û -> û + new Transition (8825, 8826), // &Ucy -> У + new Transition (8827, 8828), // &ucy -> у + new Transition (8832, 8833), // &udarr -> ⇅ + new Transition (8838, 8839), // &Udblac -> Ű + new Transition (8843, 8844), // &udblac -> ű + new Transition (8847, 8848), // &udhar -> ⥮ + new Transition (8853, 8854), // &ufisht -> ⥾ + new Transition (8856, 8857), // &Ufr -> 𝔘 + new Transition (8858, 8859), // &ufr -> 𝔲 + new Transition (8864, 8865), // Ù -> Ù + new Transition (8870, 8871), // ù -> ù + new Transition (8874, 8875), // &uHar -> ⥣ + new Transition (8879, 8880), // &uharl -> ↿ + new Transition (8881, 8882), // &uharr -> ↾ + new Transition (8885, 8886), // &uhblk -> ▀ + new Transition (8891, 8892), // &ulcorn -> ⌜ + new Transition (8894, 8895), // &ulcorner -> ⌜ + new Transition (8898, 8899), // &ulcrop -> ⌏ + new Transition (8902, 8903), // &ultri -> ◸ + new Transition (8907, 8908), // &Umacr -> Ū + new Transition (8912, 8913), // &umacr -> ū + new Transition (8914, 8915), // ¨ -> ¨ + new Transition (8922, 8923), // &UnderBar -> _ + new Transition (8927, 8928), // &UnderBrace -> ⏟ + new Transition (8931, 8932), // &UnderBracket -> ⎵ + new Transition (8943, 8944), // &UnderParenthesis -> ⏝ + new Transition (8947, 8948), // &Union -> ⋃ + new Transition (8952, 8953), // &UnionPlus -> ⊎ + new Transition (8957, 8958), // &Uogon -> Ų + new Transition (8962, 8963), // &uogon -> ų + new Transition (8965, 8966), // &Uopf -> 𝕌 + new Transition (8968, 8969), // &uopf -> 𝕦 + new Transition (8975, 8976), // &UpArrow -> ↑ + new Transition (8981, 8982), // &Uparrow -> ⇑ + new Transition (8988, 8989), // &uparrow -> ↑ + new Transition (8992, 8993), // &UpArrowBar -> ⤒ + new Transition (9002, 9003), // &UpArrowDownArrow -> ⇅ + new Transition (9012, 9013), // &UpDownArrow -> ↕ + new Transition (9022, 9023), // &Updownarrow -> ⇕ + new Transition (9032, 9033), // &updownarrow -> ↕ + new Transition (9044, 9045), // &UpEquilibrium -> ⥮ + new Transition (9056, 9057), // &upharpoonleft -> ↿ + new Transition (9062, 9063), // &upharpoonright -> ↾ + new Transition (9066, 9067), // &uplus -> ⊎ + new Transition (9079, 9080), // &UpperLeftArrow -> ↖ + new Transition (9090, 9091), // &UpperRightArrow -> ↗ + new Transition (9093, 9094), // &Upsi -> ϒ + new Transition (9096, 9097), // &upsi -> υ + new Transition (9098, 9099), // &upsih -> ϒ + new Transition (9102, 9103), // &Upsilon -> Υ + new Transition (9106, 9107), // &upsilon -> υ + new Transition (9110, 9111), // &UpTee -> ⊥ + new Transition (9116, 9117), // &UpTeeArrow -> ↥ + new Transition (9125, 9126), // &upuparrows -> ⇈ + new Transition (9131, 9132), // &urcorn -> ⌝ + new Transition (9134, 9135), // &urcorner -> ⌝ + new Transition (9138, 9139), // &urcrop -> ⌎ + new Transition (9143, 9144), // &Uring -> Ů + new Transition (9147, 9148), // &uring -> ů + new Transition (9151, 9152), // &urtri -> ◹ + new Transition (9155, 9156), // &Uscr -> 𝒰 + new Transition (9159, 9160), // &uscr -> 𝓊 + new Transition (9164, 9165), // &utdot -> ⋰ + new Transition (9170, 9171), // &Utilde -> Ũ + new Transition (9175, 9176), // &utilde -> ũ + new Transition (9178, 9179), // &utri -> ▵ + new Transition (9180, 9181), // &utrif -> ▴ + new Transition (9185, 9186), // &uuarr -> ⇈ + new Transition (9189, 9190), // Ü -> Ü + new Transition (9192, 9193), // ü -> ü + new Transition (9199, 9200), // &uwangle -> ⦧ + new Transition (9206, 9207), // &vangrt -> ⦜ + new Transition (9215, 9216), // &varepsilon -> ϵ + new Transition (9221, 9222), // &varkappa -> ϰ + new Transition (9229, 9230), // &varnothing -> ∅ + new Transition (9233, 9234), // &varphi -> ϕ + new Transition (9235, 9236), // &varpi -> ϖ + new Transition (9241, 9242), // &varpropto -> ∝ + new Transition (9245, 9246), // &vArr -> ⇕ + new Transition (9247, 9248), // &varr -> ↕ + new Transition (9250, 9251), // &varrho -> ϱ + new Transition (9256, 9257), // &varsigma -> ς + new Transition (9265, 9266), // &varsubsetneq -> ⊊︀ + new Transition (9267, 9268), // &varsubsetneqq -> ⫋︀ + new Transition (9275, 9276), // &varsupsetneq -> ⊋︀ + new Transition (9277, 9278), // &varsupsetneqq -> ⫌︀ + new Transition (9283, 9284), // &vartheta -> ϑ + new Transition (9295, 9296), // &vartriangleleft -> ⊲ + new Transition (9301, 9302), // &vartriangleright -> ⊳ + new Transition (9306, 9307), // &Vbar -> ⫫ + new Transition (9310, 9311), // &vBar -> ⫨ + new Transition (9312, 9313), // &vBarv -> ⫩ + new Transition (9315, 9316), // &Vcy -> В + new Transition (9318, 9319), // &vcy -> в + new Transition (9323, 9324), // &VDash -> ⊫ + new Transition (9328, 9329), // &Vdash -> ⊩ + new Transition (9333, 9334), // &vDash -> ⊨ + new Transition (9338, 9339), // &vdash -> ⊢ + new Transition (9340, 9341), // &Vdashl -> ⫦ + new Transition (9343, 9344), // &Vee -> ⋁ + new Transition (9346, 9347), // &vee -> ∨ + new Transition (9350, 9351), // &veebar -> ⊻ + new Transition (9353, 9354), // &veeeq -> ≚ + new Transition (9358, 9359), // &vellip -> ⋮ + new Transition (9363, 9364), // &Verbar -> ‖ + new Transition (9368, 9369), // &verbar -> | + new Transition (9370, 9371), // &Vert -> ‖ + new Transition (9372, 9373), // &vert -> | + new Transition (9380, 9381), // &VerticalBar -> ∣ + new Transition (9385, 9386), // &VerticalLine -> | + new Transition (9395, 9396), // &VerticalSeparator -> ❘ + new Transition (9401, 9402), // &VerticalTilde -> ≀ + new Transition (9412, 9413), // &VeryThinSpace ->   + new Transition (9415, 9416), // &Vfr -> 𝔙 + new Transition (9418, 9419), // &vfr -> 𝔳 + new Transition (9423, 9424), // &vltri -> ⊲ + new Transition (9428, 9429), // &vnsub -> ⊂⃒ + new Transition (9430, 9431), // &vnsup -> ⊃⃒ + new Transition (9434, 9435), // &Vopf -> 𝕍 + new Transition (9438, 9439), // &vopf -> 𝕧 + new Transition (9443, 9444), // &vprop -> ∝ + new Transition (9448, 9449), // &vrtri -> ⊳ + new Transition (9452, 9453), // &Vscr -> 𝒱 + new Transition (9456, 9457), // &vscr -> 𝓋 + new Transition (9461, 9462), // &vsubnE -> ⫋︀ + new Transition (9463, 9464), // &vsubne -> ⊊︀ + new Transition (9467, 9468), // &vsupnE -> ⫌︀ + new Transition (9469, 9470), // &vsupne -> ⊋︀ + new Transition (9475, 9476), // &Vvdash -> ⊪ + new Transition (9482, 9483), // &vzigzag -> ⦚ + new Transition (9488, 9489), // &Wcirc -> Ŵ + new Transition (9494, 9495), // &wcirc -> ŵ + new Transition (9500, 9501), // &wedbar -> ⩟ + new Transition (9505, 9506), // &Wedge -> ⋀ + new Transition (9508, 9509), // &wedge -> ∧ + new Transition (9510, 9511), // &wedgeq -> ≙ + new Transition (9515, 9516), // &weierp -> ℘ + new Transition (9518, 9519), // &Wfr -> 𝔚 + new Transition (9521, 9522), // &wfr -> 𝔴 + new Transition (9525, 9526), // &Wopf -> 𝕎 + new Transition (9529, 9530), // &wopf -> 𝕨 + new Transition (9531, 9532), // &wp -> ℘ + new Transition (9533, 9534), // &wr -> ≀ + new Transition (9538, 9539), // &wreath -> ≀ + new Transition (9542, 9543), // &Wscr -> 𝒲 + new Transition (9546, 9547), // &wscr -> 𝓌 + new Transition (9551, 9552), // &xcap -> ⋂ + new Transition (9555, 9556), // &xcirc -> ◯ + new Transition (9558, 9559), // &xcup -> ⋃ + new Transition (9563, 9564), // &xdtri -> ▽ + new Transition (9567, 9568), // &Xfr -> 𝔛 + new Transition (9570, 9571), // &xfr -> 𝔵 + new Transition (9575, 9576), // &xhArr -> ⟺ + new Transition (9579, 9580), // &xharr -> ⟷ + new Transition (9581, 9582), // &Xi -> Ξ + new Transition (9583, 9584), // &xi -> ξ + new Transition (9588, 9589), // &xlArr -> ⟸ + new Transition (9592, 9593), // &xlarr -> ⟵ + new Transition (9596, 9597), // &xmap -> ⟼ + new Transition (9600, 9601), // &xnis -> ⋻ + new Transition (9605, 9606), // &xodot -> ⨀ + new Transition (9609, 9610), // &Xopf -> 𝕏 + new Transition (9612, 9613), // &xopf -> 𝕩 + new Transition (9616, 9617), // &xoplus -> ⨁ + new Transition (9621, 9622), // &xotime -> ⨂ + new Transition (9626, 9627), // &xrArr -> ⟹ + new Transition (9630, 9631), // &xrarr -> ⟶ + new Transition (9634, 9635), // &Xscr -> 𝒳 + new Transition (9638, 9639), // &xscr -> 𝓍 + new Transition (9643, 9644), // &xsqcup -> ⨆ + new Transition (9649, 9650), // &xuplus -> ⨄ + new Transition (9653, 9654), // &xutri -> △ + new Transition (9657, 9658), // &xvee -> ⋁ + new Transition (9663, 9664), // &xwedge -> ⋀ + new Transition (9670, 9671), // Ý -> Ý + new Transition (9677, 9678), // ý -> ý + new Transition (9681, 9682), // &YAcy -> Я + new Transition (9683, 9684), // &yacy -> я + new Transition (9688, 9689), // &Ycirc -> Ŷ + new Transition (9693, 9694), // &ycirc -> ŷ + new Transition (9695, 9696), // &Ycy -> Ы + new Transition (9697, 9698), // &ycy -> ы + new Transition (9700, 9701), // ¥ -> ¥ + new Transition (9703, 9704), // &Yfr -> 𝔜 + new Transition (9706, 9707), // &yfr -> 𝔶 + new Transition (9710, 9711), // &YIcy -> Ї + new Transition (9714, 9715), // &yicy -> ї + new Transition (9718, 9719), // &Yopf -> 𝕐 + new Transition (9722, 9723), // &yopf -> 𝕪 + new Transition (9726, 9727), // &Yscr -> 𝒴 + new Transition (9730, 9731), // &yscr -> 𝓎 + new Transition (9734, 9735), // &YUcy -> Ю + new Transition (9738, 9739), // &yucy -> ю + new Transition (9742, 9743), // &Yuml -> Ÿ + new Transition (9745, 9746), // ÿ -> ÿ + new Transition (9752, 9753), // &Zacute -> Ź + new Transition (9759, 9760), // &zacute -> ź + new Transition (9765, 9766), // &Zcaron -> Ž + new Transition (9771, 9772), // &zcaron -> ž + new Transition (9773, 9774), // &Zcy -> З + new Transition (9775, 9776), // &zcy -> з + new Transition (9779, 9780), // &Zdot -> Ż + new Transition (9783, 9784), // &zdot -> ż + new Transition (9789, 9790), // &zeetrf -> ℨ + new Transition (9803, 9804), // &ZeroWidthSpace -> ​ + new Transition (9806, 9807), // &Zeta -> Ζ + new Transition (9809, 9810), // &zeta -> ζ + new Transition (9812, 9813), // &Zfr -> ℨ + new Transition (9815, 9816), // &zfr -> 𝔷 + new Transition (9819, 9820), // &ZHcy -> Ж + new Transition (9823, 9824), // &zhcy -> ж + new Transition (9830, 9831), // &zigrarr -> ⇝ + new Transition (9834, 9835), // &Zopf -> ℤ + new Transition (9838, 9839), // &zopf -> 𝕫 + new Transition (9842, 9843), // &Zscr -> 𝒵 + new Transition (9846, 9847), // &zscr -> 𝓏 + new Transition (9849, 9850), // &zwj -> + new Transition (9852, 9853) // &zwnj -> + }; + TransitionTable_A = new Transition[59] { + new Transition (0, 1), // & -> &A + new Transition (1432, 1447), // &d -> &dA + new Transition (1566, 1567), // &Diacritical -> &DiacriticalA + new Transition (1580, 1581), // &DiacriticalDouble -> &DiacriticalDoubleA + new Transition (1769, 1770), // &DoubleDown -> &DoubleDownA + new Transition (1779, 1780), // &DoubleLeft -> &DoubleLeftA + new Transition (1790, 1791), // &DoubleLeftRight -> &DoubleLeftRightA + new Transition (1807, 1808), // &DoubleLongLeft -> &DoubleLongLeftA + new Transition (1818, 1819), // &DoubleLongLeftRight -> &DoubleLongLeftRightA + new Transition (1829, 1830), // &DoubleLongRight -> &DoubleLongRightA + new Transition (1840, 1841), // &DoubleRight -> &DoubleRightA + new Transition (1852, 1853), // &DoubleUp -> &DoubleUpA + new Transition (1862, 1863), // &DoubleUpDown -> &DoubleUpDownA + new Transition (1882, 1883), // &Down -> &DownA + new Transition (1908, 1909), // &DownArrowUp -> &DownArrowUpA + new Transition (2015, 2017), // &DownTee -> &DownTeeA + new Transition (2616, 2617), // &For -> &ForA + new Transition (3014, 3035), // &H -> &HA + new Transition (3020, 3046), // &h -> &hA + new Transition (3692, 3693), // &l -> &lA + new Transition (3900, 3901), // &Left -> &LeftA + new Transition (3941, 3942), // &LeftArrowRight -> &LeftArrowRightA + new Transition (4034, 4035), // &LeftRight -> &LeftRightA + new Transition (4094, 4096), // &LeftTee -> &LeftTeeA + new Transition (4440, 4441), // &LongLeft -> &LongLeftA + new Transition (4473, 4474), // &LongLeftRight -> &LongLeftRightA + new Transition (4513, 4514), // &LongRight -> &LongRightA + new Transition (4594, 4595), // &LowerLeft -> &LowerLeftA + new Transition (4605, 4606), // &LowerRight -> &LowerRightA + new Transition (5064, 5071), // &ne -> &neA + new Transition (5227, 5228), // &nh -> &nhA + new Transition (5256, 5257), // &nl -> &nlA + new Transition (5855, 5856), // &nr -> &nrA + new Transition (6084, 6085), // &nvl -> &nvlA + new Transition (6097, 6098), // &nvr -> &nvrA + new Transition (6111, 6117), // &nw -> &nwA + new Transition (6876, 6877), // &r -> &rA + new Transition (7174, 7175), // &Right -> &RightA + new Transition (7216, 7217), // &RightArrowLeft -> &RightArrowLeftA + new Transition (7339, 7341), // &RightTee -> &RightTeeA + new Transition (7703, 7709), // &se -> &seA + new Transition (7779, 7780), // &ShortDown -> &ShortDownA + new Transition (7789, 7790), // &ShortLeft -> &ShortLeftA + new Transition (7816, 7817), // &ShortRight -> &ShortRightA + new Transition (7824, 7825), // &ShortUp -> &ShortUpA + new Transition (8375, 8381), // &sw -> &swA + new Transition (8623, 8624), // &TR -> &TRA + new Transition (8775, 8785), // &u -> &uA + new Transition (8970, 8971), // &Up -> &UpA + new Transition (8997, 8998), // &UpArrowDown -> &UpArrowDownA + new Transition (9007, 9008), // &UpDown -> &UpDownA + new Transition (9074, 9075), // &UpperLeft -> &UpperLeftA + new Transition (9085, 9086), // &UpperRight -> &UpperRightA + new Transition (9110, 9112), // &UpTee -> &UpTeeA + new Transition (9201, 9243), // &v -> &vA + new Transition (9572, 9573), // &xh -> &xhA + new Transition (9585, 9586), // &xl -> &xlA + new Transition (9623, 9624), // &xr -> &xrA + new Transition (9665, 9679) // &Y -> &YA + }; + TransitionTable_B = new Transition[34] { + new Transition (0, 331), // & -> &B + new Transition (1876, 1877), // &DoubleVertical -> &DoubleVerticalB + new Transition (1882, 1915), // &Down -> &DownB + new Transition (1887, 1903), // &DownArrow -> &DownArrowB + new Transition (1981, 1983), // &DownLeftVector -> &DownLeftVectorB + new Transition (2007, 2009), // &DownRightVector -> &DownRightVectorB + new Transition (3692, 3807), // &l -> &lB + new Transition (3905, 3906), // &LeftAngle -> &LeftAngleB + new Transition (3917, 3933), // &LeftArrow -> &LeftArrowB + new Transition (3966, 3967), // &LeftDouble -> &LeftDoubleB + new Transition (3992, 3994), // &LeftDownVector -> &LeftDownVectorB + new Transition (4126, 4128), // &LeftTriangle -> &LeftTriangleB + new Transition (4166, 4168), // &LeftUpVector -> &LeftUpVectorB + new Transition (4177, 4179), // &LeftVector -> &LeftVectorB + new Transition (5347, 5348), // &No -> &NoB + new Transition (5354, 5355), // &Non -> &NonB + new Transition (5409, 5410), // &NotDoubleVertical -> &NotDoubleVerticalB + new Transition (5539, 5541), // &NotLeftTriangle -> &NotLeftTriangleB + new Transition (5682, 5684), // &NotRightTriangle -> &NotRightTriangleB + new Transition (5816, 5817), // &NotVertical -> &NotVerticalB + new Transition (6437, 6438), // &Over -> &OverB + new Transition (6876, 6991), // &r -> &rB + new Transition (6886, 6986), // &R -> &RB + new Transition (7179, 7180), // &RightAngle -> &RightAngleB + new Transition (7191, 7209), // &RightArrow -> &RightArrowB + new Transition (7241, 7242), // &RightDouble -> &RightDoubleB + new Transition (7267, 7269), // &RightDownVector -> &RightDownVectorB + new Transition (7371, 7373), // &RightTriangle -> &RightTriangleB + new Transition (7411, 7413), // &RightUpVector -> &RightUpVectorB + new Transition (7422, 7424), // &RightVector -> &RightVectorB + new Transition (8919, 8920), // &Under -> &UnderB + new Transition (8975, 8990), // &UpArrow -> &UpArrowB + new Transition (9201, 9308), // &v -> &vB + new Transition (9377, 9378) // &Vertical -> &VerticalB + }; + TransitionTable_C = new Transition[15] { + new Transition (0, 789), // & -> &C + new Transition (1075, 1076), // &Clockwise -> &ClockwiseC + new Transition (1093, 1094), // &Close -> &CloseC + new Transition (1230, 1231), // &Counter -> &CounterC + new Transition (1239, 1240), // &CounterClockwise -> &CounterClockwiseC + new Transition (1316, 1326), // &Cup -> &CupC + new Transition (1747, 1748), // &Double -> &DoubleC + new Transition (3450, 3451), // &Invisible -> &InvisibleC + new Transition (3900, 3953), // &Left -> &LeftC + new Transition (5376, 5380), // &Not -> &NotC + new Transition (5391, 5392), // &NotCup -> &NotCupC + new Transition (6308, 6309), // &Open -> &OpenC + new Transition (7174, 7228), // &Right -> &RightC + new Transition (7756, 7757), // &SH -> &SHC + new Transition (7886, 7887) // &Small -> &SmallC + }; + TransitionTable_D = new Transition[43] { + new Transition (0, 1425), // & -> &D + new Transition (613, 618), // &box -> &boxD + new Transition (636, 640), // &boxH -> &boxHD + new Transition (638, 644), // &boxh -> &boxhD + new Transition (831, 832), // &Capital -> &CapitalD + new Transition (843, 844), // &CapitalDifferential -> &CapitalDifferentialD + new Transition (939, 940), // &Center -> &CenterD + new Transition (1023, 1024), // &Circle -> &CircleD + new Transition (1098, 1099), // &CloseCurly -> &CloseCurlyD + new Transition (1425, 1490), // &D -> &DD + new Transition (1566, 1573), // &Diacritical -> &DiacriticalD + new Transition (1630, 1631), // &Differential -> &DifferentialD + new Transition (1692, 1696), // &Dot -> &DotD + new Transition (1747, 1764), // &Double -> &DoubleD + new Transition (1852, 1859), // &DoubleUp -> &DoubleUpD + new Transition (2115, 2157), // &e -> &eD + new Transition (2157, 2158), // &eD -> &eDD + new Transition (2175, 2176), // &ef -> &efD + new Transition (2397, 2399), // &equiv -> &equivD + new Transition (2399, 2400), // &equivD -> &equivDD + new Transition (2409, 2414), // &er -> &erD + new Transition (3036, 3037), // &HAR -> &HARD + new Transition (3209, 3210), // &Hump -> &HumpD + new Transition (3900, 3961), // &Left -> &LeftD + new Transition (4139, 4140), // &LeftUp -> &LeftUpD + new Transition (4767, 4825), // &m -> &mD + new Transition (4825, 4826), // &mD -> &mDD + new Transition (5376, 5396), // &Not -> &NotD + new Transition (5496, 5497), // &NotHump -> &NotHumpD + new Transition (6043, 6058), // &nv -> &nvD + new Transition (6047, 6048), // &nV -> &nVD + new Transition (6313, 6314), // &OpenCurly -> &OpenCurlyD + new Transition (6488, 6489), // &Partial -> &PartialD + new Transition (7174, 7236), // &Right -> &RightD + new Transition (7384, 7385), // &RightUp -> &RightUpD + new Transition (7592, 7593), // &Rule -> &RuleD + new Transition (7775, 7776), // &Short -> &ShortD + new Transition (8624, 8625), // &TRA -> &TRAD + new Transition (8680, 8681), // &Triple -> &TripleD + new Transition (8970, 9004), // &Up -> &UpD + new Transition (8975, 8994), // &UpArrow -> &UpArrowD + new Transition (9201, 9330), // &v -> &vD + new Transition (9303, 9320) // &V -> &VD + }; + TransitionTable_E = new Transition[81] { + new Transition (0, 2108), // & -> &E + new Transition (1, 50), // &A -> &AE + new Transition (27, 31), // &ac -> &acE + new Transition (199, 206), // &ap -> &apE + new Transition (775, 777), // &bump -> &bumpE + new Transition (979, 1049), // &cir -> &cirE + new Transition (1692, 1707), // &Dot -> &DotE + new Transition (2490, 2491), // &Exponential -> &ExponentialE + new Transition (2701, 2763), // &g -> &gE + new Transition (2824, 2828), // &gl -> &glE + new Transition (2832, 2841), // &gn -> &gnE + new Transition (2871, 2872), // &Greater -> &GreaterE + new Transition (2886, 2887), // &GreaterFull -> &GreaterFullE + new Transition (2910, 2911), // &GreaterSlant -> &GreaterSlantE + new Transition (3011, 3012), // &gvn -> &gvnE + new Transition (3209, 3219), // &Hump -> &HumpE + new Transition (3236, 3269), // &I -> &IE + new Transition (3512, 3518), // &isin -> &isinE + new Transition (3692, 3894), // &l -> &lE + new Transition (4126, 4132), // &LeftTriangle -> &LeftTriangleE + new Transition (4239, 4240), // &Less -> &LessE + new Transition (4256, 4257), // &LessFull -> &LessFullE + new Transition (4288, 4289), // &LessSlant -> &LessSlantE + new Transition (4317, 4319), // &lg -> &lgE + new Transition (4401, 4410), // &ln -> &lnE + new Transition (4764, 4765), // &lvn -> &lvnE + new Transition (4986, 4988), // &nap -> &napE + new Transition (5195, 5196), // &ng -> &ngE + new Transition (5256, 5268), // &nl -> &nlE + new Transition (5376, 5414), // &Not -> &NotE + new Transition (5445, 5447), // &NotGreater -> &NotGreaterE + new Transition (5456, 5457), // &NotGreaterFull -> &NotGreaterFullE + new Transition (5480, 5481), // &NotGreaterSlant -> &NotGreaterSlantE + new Transition (5496, 5506), // &NotHump -> &NotHumpE + new Transition (5513, 5519), // ¬in -> ¬inE + new Transition (5539, 5545), // &NotLeftTriangle -> &NotLeftTriangleE + new Transition (5552, 5554), // &NotLess -> &NotLessE + new Transition (5577, 5578), // &NotLessSlant -> &NotLessSlantE + new Transition (5637, 5639), // &NotPrecedes -> &NotPrecedesE + new Transition (5649, 5650), // &NotPrecedesSlant -> &NotPrecedesSlantE + new Transition (5662, 5663), // &NotReverse -> &NotReverseE + new Transition (5682, 5688), // &NotRightTriangle -> &NotRightTriangleE + new Transition (5705, 5707), // &NotSquareSubset -> &NotSquareSubsetE + new Transition (5718, 5720), // &NotSquareSuperset -> &NotSquareSupersetE + new Transition (5730, 5732), // &NotSubset -> &NotSubsetE + new Transition (5743, 5745), // &NotSucceeds -> &NotSucceedsE + new Transition (5755, 5756), // &NotSucceedsSlant -> &NotSucceedsSlantE + new Transition (5773, 5775), // &NotSuperset -> &NotSupersetE + new Transition (5785, 5787), // &NotTilde -> &NotTildeE + new Transition (5796, 5797), // &NotTildeFull -> &NotTildeFullE + new Transition (5952, 5954), // &nsub -> &nsubE + new Transition (5973, 5975), // &nsup -> &nsupE + new Transition (6131, 6190), // &O -> &OE + new Transition (6642, 6651), // &pr -> &prE + new Transition (6677, 6679), // &Precedes -> &PrecedesE + new Transition (6689, 6690), // &PrecedesSlant -> &PrecedesSlantE + new Transition (6735, 6739), // &prn -> &prnE + new Transition (6886, 7092), // &R -> &RE + new Transition (7101, 7102), // &Reverse -> &ReverseE + new Transition (7122, 7123), // &ReverseUp -> &ReverseUpE + new Transition (7371, 7377), // &RightTriangle -> &RightTriangleE + new Transition (7631, 7649), // &sc -> &scE + new Transition (7670, 7674), // &scn -> &scnE + new Transition (7857, 7859), // &simg -> &simgE + new Transition (7861, 7863), // &siml -> &simlE + new Transition (8037, 8039), // &SquareSubset -> &SquareSubsetE + new Transition (8050, 8052), // &SquareSuperset -> &SquareSupersetE + new Transition (8131, 8137), // &sub -> &subE + new Transition (8150, 8151), // &subn -> &subnE + new Transition (8167, 8178), // &Subset -> &SubsetE + new Transition (8221, 8223), // &Succeeds -> &SucceedsE + new Transition (8233, 8234), // &SucceedsSlant -> &SucceedsSlantE + new Transition (8284, 8300), // &sup -> &supE + new Transition (8312, 8314), // &Superset -> &SupersetE + new Transition (8338, 8339), // &supn -> &supnE + new Transition (8547, 8554), // &Tilde -> &TildeE + new Transition (8563, 8564), // &TildeFull -> &TildeFullE + new Transition (8625, 8626), // &TRAD -> &TRADE + new Transition (8970, 9034), // &Up -> &UpE + new Transition (9460, 9461), // &vsubn -> &vsubnE + new Transition (9466, 9467) // &vsupn -> &vsupnE + }; + TransitionTable_F = new Transition[10] { + new Transition (0, 2517), // & -> &F + new Transition (219, 220), // &Apply -> &ApplyF + new Transition (2871, 2883), // &Greater -> &GreaterF + new Transition (3900, 3998), // &Left -> &LeftF + new Transition (4239, 4253), // &Less -> &LessF + new Transition (5445, 5453), // &NotGreater -> &NotGreaterF + new Transition (5785, 5793), // &NotTilde -> &NotTildeF + new Transition (7174, 7273), // &Right -> &RightF + new Transition (7930, 7931), // &SO -> &SOF + new Transition (8547, 8560) // &Tilde -> &TildeF + }; + TransitionTable_G = new Transition[15] { + new Transition (0, 2708), // & -> &G + new Transition (1566, 1587), // &Diacritical -> &DiacriticalG + new Transition (2287, 2288), // &EN -> &ENG + new Transition (2871, 2893), // &Greater -> &GreaterG + new Transition (4239, 4263), // &Less -> &LessG + new Transition (4244, 4245), // &LessEqual -> &LessEqualG + new Transition (4965, 5212), // &n -> &nG + new Transition (5151, 5152), // &Nested -> &NestedG + new Transition (5158, 5159), // &NestedGreater -> &NestedGreaterG + new Transition (5376, 5439), // &Not -> &NotG + new Transition (5445, 5463), // &NotGreater -> &NotGreaterG + new Transition (5552, 5560), // &NotLess -> &NotLessG + new Transition (5595, 5596), // &NotNested -> &NotNestedG + new Transition (5602, 5603), // &NotNestedGreater -> &NotNestedGreaterG + new Transition (7092, 7093) // &RE -> ® + }; + TransitionTable_H = new Transition[20] { + new Transition (0, 3014), // & -> &H + new Transition (613, 636), // &box -> &boxH + new Transition (691, 695), // &boxV -> &boxVH + new Transition (693, 699), // &boxv -> &boxvH + new Transition (789, 956), // &C -> &CH + new Transition (1432, 1546), // &d -> &dH + new Transition (2442, 2443), // &ET -> Ð + new Transition (3213, 3214), // &HumpDown -> &HumpDownH + new Transition (3618, 3660), // &K -> &KH + new Transition (3692, 4321), // &l -> &lH + new Transition (5376, 5493), // &Not -> &NotH + new Transition (5500, 5501), // &NotHumpDown -> &NotHumpDownH + new Transition (6043, 6073), // &nv -> &nvH + new Transition (6876, 7151), // &r -> &rH + new Transition (7610, 7756), // &S -> &SH + new Transition (7757, 7758), // &SHC -> &SHCH + new Transition (8400, 8535), // &T -> &TH + new Transition (8713, 8719), // &TS -> &TSH + new Transition (8775, 8872), // &u -> &uH + new Transition (9747, 9817) // &Z -> &ZH + }; + TransitionTable_I = new Transition[9] { + new Transition (0, 3236), // & -> &I + new Transition (1082, 1083), // &ClockwiseContour -> &ClockwiseContourI + new Transition (1190, 1191), // &Contour -> &ContourI + new Transition (1246, 1247), // &CounterClockwiseContour -> &CounterClockwiseContourI + new Transition (1754, 1755), // &DoubleContour -> &DoubleContourI + new Transition (3349, 3350), // &Imaginary -> &ImaginaryI + new Transition (7503, 7504), // &Round -> &RoundI + new Transition (8013, 8019), // &Square -> &SquareI + new Transition (9665, 9708) // &Y -> &YI + }; + TransitionTable_J = new Transition[7] { + new Transition (0, 3555), // & -> &J + new Transition (1425, 1661), // &D -> &DJ + new Transition (2708, 2816), // &G -> &GJ + new Transition (3236, 3320), // &I -> &IJ + new Transition (3618, 3668), // &K -> &KJ + new Transition (3698, 4338), // &L -> &LJ + new Transition (4971, 5248) // &N -> &NJ + }; + TransitionTable_K = new Transition[1] { + new Transition (0, 3618) // & -> &K + }; + TransitionTable_L = new Transition[29] { + new Transition (0, 3698), // & -> &L + new Transition (618, 619), // &boxD -> &boxDL + new Transition (623, 624), // &boxd -> &boxdL + new Transition (673, 674), // &boxU -> &boxUL + new Transition (678, 679), // &boxu -> &boxuL + new Transition (691, 703), // &boxV -> &boxVL + new Transition (693, 707), // &boxv -> &boxvL + new Transition (1747, 1776), // &Double -> &DoubleL + new Transition (1803, 1804), // &DoubleLong -> &DoubleLongL + new Transition (1882, 1950), // &Down -> &DownL + new Transition (2871, 2901), // &Greater -> &GreaterL + new Transition (2876, 2878), // &GreaterEqual -> &GreaterEqualL + new Transition (3178, 3179), // &Horizontal -> &HorizontalL + new Transition (4239, 4275), // &Less -> &LessL + new Transition (4436, 4437), // &Long -> &LongL + new Transition (4590, 4591), // &Lower -> &LowerL + new Transition (4965, 5272), // &n -> &nL + new Transition (5151, 5167), // &Nested -> &NestedL + new Transition (5170, 5171), // &NestedLess -> &NestedLessL + new Transition (5176, 5177), // &New -> &NewL + new Transition (5376, 5528), // &Not -> &NotL + new Transition (5445, 5471), // &NotGreater -> &NotGreaterL + new Transition (5552, 5568), // &NotLess -> &NotLessL + new Transition (5595, 5611), // &NotNested -> &NotNestedL + new Transition (5614, 5615), // &NotNestedLess -> &NotNestedLessL + new Transition (7191, 7213), // &RightArrow -> &RightArrowL + new Transition (7775, 7786), // &Short -> &ShortL + new Transition (9070, 9071), // &Upper -> &UpperL + new Transition (9377, 9382) // &Vertical -> &VerticalL + }; + TransitionTable_M = new Transition[5] { + new Transition (0, 4781), // & -> &M + new Transition (1, 111), // &A -> &AM + new Transition (1023, 1032), // &Circle -> &CircleM + new Transition (5090, 5091), // &Negative -> &NegativeM + new Transition (6589, 6590) // &Plus -> &PlusM + }; + TransitionTable_N = new Transition[5] { + new Transition (0, 4971), // & -> &N + new Transition (301, 587), // &b -> &bN + new Transition (2108, 2287), // &E -> &EN + new Transition (5376, 5590), // &Not -> &NotN + new Transition (8537, 8538) // &THOR -> Þ + }; + TransitionTable_O = new Transition[6] { + new Transition (0, 6131), // & -> &O + new Transition (789, 1217), // &C -> &CO + new Transition (3236, 3463), // &I -> &IO + new Transition (6869, 6870), // &QU -> &QUO + new Transition (7610, 7930), // &S -> &SO + new Transition (8535, 8536) // &TH -> &THO + }; + TransitionTable_P = new Transition[11] { + new Transition (0, 6482), // & -> &P + new Transition (111, 112), // &AM -> & + new Transition (1023, 1038), // &Circle -> &CircleP + new Transition (1217, 1218), // &CO -> &COP + new Transition (2954, 2955), // >l -> >lP + new Transition (4731, 4738), // <r -> <rP + new Transition (4903, 4904), // &Minus -> &MinusP + new Transition (5376, 5630), // &Not -> &NotP + new Transition (6437, 6451), // &Over -> &OverP + new Transition (8919, 8933), // &Under -> &UnderP + new Transition (8947, 8949) // &Union -> &UnionP + }; + TransitionTable_Q = new Transition[5] { + new Transition (0, 6813), // & -> &Q + new Transition (1098, 1111), // &CloseCurly -> &CloseCurlyQ + new Transition (1104, 1105), // &CloseCurlyDouble -> &CloseCurlyDoubleQ + new Transition (6313, 6326), // &OpenCurly -> &OpenCurlyQ + new Transition (6319, 6320) // &OpenCurlyDouble -> &OpenCurlyDoubleQ + }; + TransitionTable_R = new Transition[26] { + new Transition (0, 6886), // & -> &R + new Transition (618, 628), // &boxD -> &boxDR + new Transition (623, 632), // &boxd -> &boxdR + new Transition (673, 683), // &boxU -> &boxUR + new Transition (678, 687), // &boxu -> &boxuR + new Transition (691, 711), // &boxV -> &boxVR + new Transition (693, 715), // &boxv -> &boxvR + new Transition (1004, 1028), // &circled -> &circledR + new Transition (1747, 1836), // &Double -> &DoubleR + new Transition (1779, 1786), // &DoubleLeft -> &DoubleLeftR + new Transition (1803, 1825), // &DoubleLong -> &DoubleLongR + new Transition (1807, 1814), // &DoubleLongLeft -> &DoubleLongLeftR + new Transition (1882, 1987), // &Down -> &DownR + new Transition (1953, 1954), // &DownLeft -> &DownLeftR + new Transition (3035, 3036), // &HA -> &HAR + new Transition (3900, 4030), // &Left -> &LeftR + new Transition (3917, 3937), // &LeftArrow -> &LeftArrowR + new Transition (4436, 4509), // &Long -> &LongR + new Transition (4440, 4469), // &LongLeft -> &LongLeftR + new Transition (4590, 4601), // &Lower -> &LowerR + new Transition (4965, 5868), // &n -> &nR + new Transition (5376, 5656), // &Not -> &NotR + new Transition (7775, 7812), // &Short -> &ShortR + new Transition (8400, 8623), // &T -> &TR + new Transition (8536, 8537), // &THO -> &THOR + new Transition (9070, 9081) // &Upper -> &UpperR + }; + TransitionTable_S = new Transition[36] { + new Transition (0, 7610), // & -> &S + new Transition (1004, 1030), // &circled -> &circledS + new Transition (1425, 2048), // &D -> &DS + new Transition (2248, 2249), // &Empty -> &EmptyS + new Transition (2253, 2254), // &EmptySmall -> &EmptySmallS + new Transition (2266, 2267), // &EmptyVery -> &EmptyVeryS + new Transition (2271, 2272), // &EmptyVerySmall -> &EmptyVerySmallS + new Transition (2558, 2559), // &Filled -> &FilledS + new Transition (2563, 2564), // &FilledSmall -> &FilledSmallS + new Transition (2574, 2575), // &FilledVery -> &FilledVeryS + new Transition (2579, 2580), // &FilledVerySmall -> &FilledVerySmallS + new Transition (2871, 2906), // &Greater -> &GreaterS + new Transition (3105, 3106), // &Hilbert -> &HilbertS + new Transition (4239, 4284), // &Less -> &LessS + new Transition (4847, 4848), // &Medium -> &MediumS + new Transition (5096, 5097), // &NegativeMedium -> &NegativeMediumS + new Transition (5107, 5108), // &NegativeThick -> &NegativeThickS + new Transition (5114, 5115), // &NegativeThin -> &NegativeThinS + new Transition (5128, 5129), // &NegativeVeryThin -> &NegativeVeryThinS + new Transition (5362, 5363), // &NonBreaking -> &NonBreakingS + new Transition (5376, 5694), // &Not -> &NotS + new Transition (5445, 5476), // &NotGreater -> &NotGreaterS + new Transition (5552, 5573), // &NotLess -> &NotLessS + new Transition (5637, 5645), // &NotPrecedes -> &NotPrecedesS + new Transition (5699, 5700), // &NotSquare -> &NotSquareS + new Transition (5743, 5751), // &NotSucceeds -> &NotSucceedsS + new Transition (6138, 6376), // &o -> &oS + new Transition (6677, 6685), // &Precedes -> &PrecedesS + new Transition (8013, 8032), // &Square -> &SquareS + new Transition (8221, 8229), // &Succeeds -> &SucceedsS + new Transition (8400, 8713), // &T -> &TS + new Transition (8509, 8510), // &Thick -> &ThickS + new Transition (8520, 8521), // &Thin -> &ThinS + new Transition (9377, 9387), // &Vertical -> &VerticalS + new Transition (9407, 9408), // &VeryThin -> &VeryThinS + new Transition (9798, 9799) // &ZeroWidth -> &ZeroWidthS + }; + TransitionTable_T = new Transition[40] { + new Transition (0, 8400), // & -> &T + new Transition (1023, 1043), // &Circle -> &CircleT + new Transition (1566, 1593), // &Diacritical -> &DiacriticalT + new Transition (1779, 1797), // &DoubleLeft -> &DoubleLeftT + new Transition (1840, 1847), // &DoubleRight -> &DoubleRightT + new Transition (1882, 2013), // &Down -> &DownT + new Transition (1953, 1966), // &DownLeft -> &DownLeftT + new Transition (1991, 1992), // &DownRight -> &DownRightT + new Transition (2108, 2442), // &E -> &ET + new Transition (2370, 2377), // &Equal -> &EqualT + new Transition (2708, 2938), // &G -> > + new Transition (2871, 2917), // &Greater -> &GreaterT + new Transition (3450, 3457), // &Invisible -> &InvisibleT + new Transition (3698, 4694), // &L -> < + new Transition (3900, 4092), // &Left -> &LeftT + new Transition (3976, 3977), // &LeftDown -> &LeftDownT + new Transition (4139, 4151), // &LeftUp -> &LeftUpT + new Transition (4239, 4295), // &Less -> &LessT + new Transition (5090, 5103), // &Negative -> &NegativeT + new Transition (5124, 5125), // &NegativeVery -> &NegativeVeryT + new Transition (5376, 5781), // &Not -> &NotT + new Transition (5425, 5427), // &NotEqual -> &NotEqualT + new Transition (5445, 5487), // &NotGreater -> &NotGreaterT + new Transition (5531, 5532), // &NotLeft -> &NotLeftT + new Transition (5552, 5584), // &NotLess -> &NotLessT + new Transition (5674, 5675), // &NotRight -> &NotRightT + new Transition (5743, 5762), // &NotSucceeds -> &NotSucceedsT + new Transition (5785, 5803), // &NotTilde -> &NotTildeT + new Transition (6677, 6696), // &Precedes -> &PrecedesT + new Transition (6870, 6871), // &QUO -> " + new Transition (7174, 7337), // &Right -> &RightT + new Transition (7251, 7252), // &RightDown -> &RightDownT + new Transition (7384, 7396), // &RightUp -> &RightUpT + new Transition (7931, 7932), // &SOF -> &SOFT + new Transition (8221, 8240), // &Succeeds -> &SucceedsT + new Transition (8269, 8270), // &Such -> &SuchT + new Transition (8547, 8570), // &Tilde -> &TildeT + new Transition (8970, 9108), // &Up -> &UpT + new Transition (9377, 9397), // &Vertical -> &VerticalT + new Transition (9403, 9404) // &Very -> &VeryT + }; + TransitionTable_U = new Transition[13] { + new Transition (0, 8768), // & -> &U + new Transition (613, 673), // &box -> &boxU + new Transition (636, 648), // &boxH -> &boxHU + new Transition (638, 652), // &boxh -> &boxhU + new Transition (1747, 1851), // &Double -> &DoubleU + new Transition (1887, 1907), // &DownArrow -> &DownArrowU + new Transition (3900, 4138), // &Left -> &LeftU + new Transition (6813, 6869), // &Q -> &QU + new Transition (7101, 7121), // &Reverse -> &ReverseU + new Transition (7174, 7383), // &Right -> &RightU + new Transition (7775, 7823), // &Short -> &ShortU + new Transition (8013, 8058), // &Square -> &SquareU + new Transition (9665, 9732) // &Y -> &YU + }; + TransitionTable_V = new Transition[29] { + new Transition (0, 9303), // & -> &V + new Transition (613, 691), // &box -> &boxV + new Transition (1747, 1869), // &Double -> &DoubleV + new Transition (1953, 1976), // &DownLeft -> &DownLeftV + new Transition (1958, 1959), // &DownLeftRight -> &DownLeftRightV + new Transition (1968, 1969), // &DownLeftTee -> &DownLeftTeeV + new Transition (1991, 2002), // &DownRight -> &DownRightV + new Transition (1994, 1995), // &DownRightTee -> &DownRightTeeV + new Transition (2248, 2263), // &Empty -> &EmptyV + new Transition (2558, 2571), // &Filled -> &FilledV + new Transition (3900, 4172), // &Left -> &LeftV + new Transition (3976, 3987), // &LeftDown -> &LeftDownV + new Transition (3979, 3980), // &LeftDownTee -> &LeftDownTeeV + new Transition (4034, 4085), // &LeftRight -> &LeftRightV + new Transition (4094, 4102), // &LeftTee -> &LeftTeeV + new Transition (4139, 4161), // &LeftUp -> &LeftUpV + new Transition (4143, 4144), // &LeftUpDown -> &LeftUpDownV + new Transition (4153, 4154), // &LeftUpTee -> &LeftUpTeeV + new Transition (4965, 6047), // &n -> &nV + new Transition (5090, 5121), // &Negative -> &NegativeV + new Transition (5376, 5809), // &Not -> &NotV + new Transition (5401, 5402), // &NotDouble -> &NotDoubleV + new Transition (7174, 7417), // &Right -> &RightV + new Transition (7251, 7262), // &RightDown -> &RightDownV + new Transition (7254, 7255), // &RightDownTee -> &RightDownTeeV + new Transition (7339, 7347), // &RightTee -> &RightTeeV + new Transition (7384, 7406), // &RightUp -> &RightUpV + new Transition (7388, 7389), // &RightUpDown -> &RightUpDownV + new Transition (7398, 7399) // &RightUpTee -> &RightUpTeeV + }; + TransitionTable_W = new Transition[2] { + new Transition (0, 9484), // & -> &W + new Transition (9793, 9794) // &Zero -> &ZeroW + }; + TransitionTable_X = new Transition[1] { + new Transition (0, 9565) // & -> &X + }; + TransitionTable_Y = new Transition[2] { + new Transition (0, 9665), // & -> &Y + new Transition (1218, 1219) // &COP -> © + }; + TransitionTable_Z = new Transition[2] { + new Transition (0, 9747), // & -> &Z + new Transition (1425, 2093) // &D -> &DZ + }; + TransitionTable_a = new Transition[555] { + new Transition (0, 8), // & -> &a + new Transition (1, 2), // &A -> &Aa + new Transition (8, 9), // &a -> &aa + new Transition (68, 69), // &Agr -> &Agra + new Transition (74, 75), // &agr -> &agra + new Transition (91, 92), // &Alph -> &Alpha + new Transition (95, 96), // &alph -> &alpha + new Transition (98, 99), // &Am -> &Ama + new Transition (103, 104), // &am -> &ama + new Transition (120, 122), // &and -> &anda + new Transition (145, 147), // &angmsd -> &angmsda + new Transition (147, 148), // &angmsda -> &angmsdaa + new Transition (178, 179), // &angz -> &angza + new Transition (199, 201), // &ap -> &apa + new Transition (301, 302), // &b -> &ba + new Transition (331, 332), // &B -> &Ba + new Transition (336, 337), // &Backsl -> &Backsla + new Transition (385, 386), // &bec -> &beca + new Transition (391, 392), // &Bec -> &Beca + new Transition (423, 424), // &Bet -> &Beta + new Transition (426, 427), // &bet -> &beta + new Transition (444, 445), // &bigc -> &bigca + new Transition (477, 478), // &bigst -> &bigsta + new Transition (483, 484), // &bigtri -> &bigtria + new Transition (513, 514), // &bk -> &bka + new Transition (519, 520), // &bl -> &bla + new Transition (533, 534), // &blacksqu -> &blacksqua + new Transition (540, 541), // &blacktri -> &blacktria + new Transition (736, 737), // &brvb -> &brvba + new Transition (789, 790), // &C -> &Ca + new Transition (796, 797), // &c -> &ca + new Transition (805, 807), // &cap -> &capa + new Transition (817, 818), // &capc -> &capca + new Transition (829, 830), // &Capit -> &Capita + new Transition (841, 842), // &CapitalDifferenti -> &CapitalDifferentia + new Transition (861, 862), // &cc -> &cca + new Transition (866, 867), // &Cc -> &Cca + new Transition (924, 925), // &Cedill -> &Cedilla + new Transition (968, 969), // &checkm -> &checkma + new Transition (987, 988), // &circle -> &circlea + new Transition (1004, 1005), // &circled -> &circleda + new Transition (1014, 1015), // &circledd -> &circledda + new Transition (1088, 1089), // &ClockwiseContourIntegr -> &ClockwiseContourIntegra + new Transition (1143, 1144), // &comm -> &comma + new Transition (1196, 1197), // &ContourIntegr -> &ContourIntegra + new Transition (1252, 1253), // &CounterClockwiseContourIntegr -> &CounterClockwiseContourIntegra + new Transition (1256, 1257), // &cr -> &cra + new Transition (1293, 1294), // &cud -> &cuda + new Transition (1308, 1309), // &cul -> &cula + new Transition (1322, 1323), // &cupbrc -> &cupbrca + new Transition (1326, 1327), // &CupC -> &CupCa + new Transition (1330, 1331), // &cupc -> &cupca + new Transition (1346, 1347), // &cur -> &cura + new Transition (1382, 1383), // &curve -> &curvea + new Transition (1425, 1426), // &D -> &Da + new Transition (1432, 1433), // &d -> &da + new Transition (1464, 1465), // &dbk -> &dbka + new Transition (1470, 1471), // &dbl -> &dbla + new Transition (1474, 1475), // &Dc -> &Dca + new Transition (1480, 1481), // &dc -> &dca + new Transition (1492, 1494), // &dd -> &dda + new Transition (1505, 1506), // &DDotr -> &DDotra + new Transition (1522, 1523), // &Delt -> &Delta + new Transition (1526, 1527), // &delt -> &delta + new Transition (1546, 1547), // &dH -> &dHa + new Transition (1550, 1551), // &dh -> &dha + new Transition (1557, 1558), // &Di -> &Dia + new Transition (1564, 1565), // &Diacritic -> &Diacritica + new Transition (1588, 1589), // &DiacriticalGr -> &DiacriticalGra + new Transition (1599, 1600), // &di -> &dia + new Transition (1628, 1629), // &Differenti -> &Differentia + new Transition (1633, 1634), // &dig -> &diga + new Transition (1636, 1637), // &digamm -> &digamma + new Transition (1681, 1682), // &doll -> &dolla + new Transition (1709, 1710), // &DotEqu -> &DotEqua + new Transition (1726, 1727), // &dotsqu -> &dotsqua + new Transition (1735, 1736), // &doubleb -> &doubleba + new Transition (1760, 1761), // &DoubleContourIntegr -> &DoubleContourIntegra + new Transition (1874, 1875), // &DoubleVertic -> &DoubleVertica + new Transition (1877, 1878), // &DoubleVerticalB -> &DoubleVerticalBa + new Transition (1882, 1889), // &Down -> &Downa + new Transition (1896, 1897), // &down -> &downa + new Transition (1903, 1904), // &DownArrowB -> &DownArrowBa + new Transition (1924, 1925), // &downdown -> &downdowna + new Transition (1932, 1933), // &downh -> &downha + new Transition (1983, 1984), // &DownLeftVectorB -> &DownLeftVectorBa + new Transition (2009, 2010), // &DownRightVectorB -> &DownRightVectorBa + new Transition (2025, 2026), // &drbk -> &drbka + new Transition (2077, 2078), // &du -> &dua + new Transition (2082, 2083), // &duh -> &duha + new Transition (2086, 2087), // &dw -> &dwa + new Transition (2103, 2104), // &dzigr -> &dzigra + new Transition (2108, 2109), // &E -> &Ea + new Transition (2115, 2116), // &e -> &ea + new Transition (2127, 2128), // &Ec -> &Eca + new Transition (2133, 2134), // &ec -> &eca + new Transition (2188, 2189), // &Egr -> &Egra + new Transition (2193, 2194), // &egr -> &egra + new Transition (2228, 2229), // &Em -> &Ema + new Transition (2233, 2234), // &em -> &ema + new Transition (2250, 2251), // &EmptySm -> &EmptySma + new Transition (2256, 2257), // &EmptySmallSqu -> &EmptySmallSqua + new Transition (2268, 2269), // &EmptyVerySm -> &EmptyVerySma + new Transition (2274, 2275), // &EmptyVerySmallSqu -> &EmptyVerySmallSqua + new Transition (2312, 2313), // &ep -> &epa + new Transition (2354, 2355), // &eqsl -> &eqsla + new Transition (2368, 2369), // &Equ -> &Equa + new Transition (2372, 2373), // &equ -> &equa + new Transition (2403, 2404), // &eqvp -> &eqvpa + new Transition (2409, 2410), // &er -> &era + new Transition (2436, 2437), // &Et -> &Eta + new Transition (2439, 2440), // &et -> &eta + new Transition (2475, 2476), // &expect -> &expecta + new Transition (2488, 2489), // &Exponenti -> &Exponentia + new Transition (2498, 2499), // &exponenti -> &exponentia + new Transition (2503, 2504), // &f -> &fa + new Transition (2525, 2526), // &fem -> &fema + new Transition (2560, 2561), // &FilledSm -> &FilledSma + new Transition (2566, 2567), // &FilledSmallSqu -> &FilledSmallSqua + new Transition (2576, 2577), // &FilledVerySm -> &FilledVerySma + new Transition (2582, 2583), // &FilledVerySmallSqu -> &FilledVerySmallSqua + new Transition (2592, 2593), // &fl -> &fla + new Transition (2621, 2622), // &for -> &fora + new Transition (2639, 2640), // &fp -> &fpa + new Transition (2647, 2648), // &fr -> &fra + new Transition (2701, 2702), // &g -> &ga + new Transition (2708, 2709), // &G -> &Ga + new Transition (2711, 2712), // &Gamm -> &Gamma + new Transition (2715, 2716), // &gamm -> &gamma + new Transition (2776, 2777), // &geqsl -> &geqsla + new Transition (2824, 2826), // &gl -> &gla + new Transition (2832, 2833), // &gn -> &gna + new Transition (2861, 2862), // &gr -> &gra + new Transition (2867, 2868), // &Gre -> &Grea + new Transition (2874, 2875), // &GreaterEqu -> &GreaterEqua + new Transition (2889, 2890), // &GreaterFullEqu -> &GreaterFullEqua + new Transition (2895, 2896), // &GreaterGre -> &GreaterGrea + new Transition (2907, 2908), // &GreaterSl -> &GreaterSla + new Transition (2913, 2914), // &GreaterSlantEqu -> &GreaterSlantEqua + new Transition (2955, 2956), // >lP -> >lPa + new Transition (2965, 2966), // >r -> >ra + new Transition (3014, 3015), // &H -> &Ha + new Transition (3020, 3021), // &h -> &ha + new Transition (3060, 3061), // &hb -> &hba + new Transition (3074, 3075), // &he -> &hea + new Transition (3107, 3108), // &HilbertSp -> &HilbertSpa + new Transition (3114, 3115), // &hkse -> &hksea + new Transition (3120, 3121), // &hksw -> &hkswa + new Transition (3126, 3127), // &ho -> &hoa + new Transition (3141, 3142), // &hookleft -> &hooklefta + new Transition (3152, 3153), // &hookright -> &hookrighta + new Transition (3167, 3168), // &horb -> &horba + new Transition (3176, 3177), // &Horizont -> &Horizonta + new Transition (3192, 3193), // &hsl -> &hsla + new Transition (3221, 3222), // &HumpEqu -> &HumpEqua + new Transition (3236, 3237), // &I -> &Ia + new Transition (3243, 3244), // &i -> &ia + new Transition (3290, 3291), // &Igr -> &Igra + new Transition (3296, 3297), // &igr -> &igra + new Transition (3317, 3318), // &iiot -> &iiota + new Transition (3330, 3332), // &Im -> &Ima + new Transition (3336, 3337), // &im -> &ima + new Transition (3346, 3347), // &Imagin -> &Imagina + new Transition (3357, 3358), // &imagp -> &imagpa + new Transition (3380, 3381), // &inc -> &inca + new Transition (3403, 3404), // &intc -> &intca + new Transition (3415, 3416), // &Integr -> &Integra + new Transition (3420, 3421), // &interc -> &interca + new Transition (3433, 3434), // &intl -> &intla + new Transition (3454, 3455), // &InvisibleComm -> &InvisibleComma + new Transition (3486, 3487), // &Iot -> &Iota + new Transition (3489, 3490), // &iot -> &iota + new Transition (3577, 3578), // &jm -> &jma + new Transition (3618, 3619), // &K -> &Ka + new Transition (3621, 3622), // &Kapp -> &Kappa + new Transition (3624, 3625), // &k -> &ka + new Transition (3627, 3628), // &kapp -> &kappa + new Transition (3692, 3705), // &l -> &la + new Transition (3693, 3694), // &lA -> &lAa + new Transition (3698, 3699), // &L -> &La + new Transition (3719, 3720), // &lagr -> &lagra + new Transition (3725, 3726), // &Lambd -> &Lambda + new Transition (3730, 3731), // &lambd -> &lambda + new Transition (3747, 3748), // &Lapl -> &Lapla + new Transition (3792, 3799), // &lat -> &lata + new Transition (3794, 3795), // &lAt -> &lAta + new Transition (3807, 3808), // &lB -> &lBa + new Transition (3812, 3813), // &lb -> &lba + new Transition (3821, 3822), // &lbr -> &lbra + new Transition (3837, 3838), // &Lc -> &Lca + new Transition (3843, 3844), // &lc -> &lca + new Transition (3870, 3871), // &ldc -> &ldca + new Transition (3881, 3882), // &ldrdh -> &ldrdha + new Transition (3887, 3888), // &ldrush -> &ldrusha + new Transition (3900, 3919), // &Left -> &Lefta + new Transition (3907, 3908), // &LeftAngleBr -> &LeftAngleBra + new Transition (3926, 3927), // &left -> &lefta + new Transition (3933, 3934), // &LeftArrowB -> &LeftArrowBa + new Transition (3948, 3949), // &leftarrowt -> &leftarrowta + new Transition (3968, 3969), // &LeftDoubleBr -> &LeftDoubleBra + new Transition (3994, 3995), // &LeftDownVectorB -> &LeftDownVectorBa + new Transition (4004, 4005), // &lefth -> &leftha + new Transition (4022, 4023), // &leftleft -> &leftlefta + new Transition (4045, 4046), // &Leftright -> &Leftrighta + new Transition (4056, 4057), // &leftright -> &leftrighta + new Transition (4065, 4066), // &leftrighth -> &leftrightha + new Transition (4078, 4079), // &leftrightsquig -> &leftrightsquiga + new Transition (4121, 4122), // &LeftTri -> &LeftTria + new Transition (4128, 4129), // &LeftTriangleB -> &LeftTriangleBa + new Transition (4134, 4135), // &LeftTriangleEqu -> &LeftTriangleEqua + new Transition (4168, 4169), // &LeftUpVectorB -> &LeftUpVectorBa + new Transition (4179, 4180), // &LeftVectorB -> &LeftVectorBa + new Transition (4192, 4193), // &leqsl -> &leqsla + new Transition (4215, 4216), // &less -> &lessa + new Transition (4242, 4243), // &LessEqu -> &LessEqua + new Transition (4247, 4248), // &LessEqualGre -> &LessEqualGrea + new Transition (4259, 4260), // &LessFullEqu -> &LessFullEqua + new Transition (4265, 4266), // &LessGre -> &LessGrea + new Transition (4285, 4286), // &LessSl -> &LessSla + new Transition (4291, 4292), // &LessSlantEqu -> &LessSlantEqua + new Transition (4321, 4322), // &lH -> &lHa + new Transition (4325, 4326), // &lh -> &lha + new Transition (4348, 4350), // &ll -> &lla + new Transition (4363, 4364), // &Lleft -> &Llefta + new Transition (4370, 4371), // &llh -> &llha + new Transition (4394, 4396), // &lmoust -> &lmousta + new Transition (4401, 4402), // &ln -> &lna + new Transition (4422, 4423), // &lo -> &loa + new Transition (4450, 4451), // &Longleft -> &Longlefta + new Transition (4462, 4463), // &longleft -> &longlefta + new Transition (4484, 4485), // &Longleftright -> &Longleftrighta + new Transition (4495, 4496), // &longleftright -> &longleftrighta + new Transition (4502, 4503), // &longm -> &longma + new Transition (4524, 4525), // &Longright -> &Longrighta + new Transition (4535, 4536), // &longright -> &longrighta + new Transition (4543, 4544), // &loop -> &loopa + new Transition (4560, 4561), // &lop -> &lopa + new Transition (4579, 4580), // &low -> &lowa + new Transition (4584, 4585), // &lowb -> &lowba + new Transition (4621, 4622), // &lp -> &lpa + new Transition (4628, 4629), // &lr -> &lra + new Transition (4640, 4641), // &lrh -> &lrha + new Transition (4652, 4653), // &ls -> &lsa + new Transition (4720, 4721), // <l -> <la + new Transition (4738, 4739), // <rP -> <rPa + new Transition (4746, 4747), // &lurdsh -> &lurdsha + new Transition (4751, 4752), // &luruh -> &luruha + new Transition (4767, 4768), // &m -> &ma + new Transition (4781, 4782), // &M -> &Ma + new Transition (4812, 4813), // &mcomm -> &mcomma + new Transition (4820, 4821), // &md -> &mda + new Transition (4830, 4831), // &me -> &mea + new Transition (4836, 4837), // &measured -> &measureda + new Transition (4849, 4850), // &MediumSp -> &MediumSpa + new Transition (4876, 4878), // &mid -> &mida + new Transition (4957, 4958), // &multim -> &multima + new Transition (4961, 4962), // &mum -> &muma + new Transition (4965, 4966), // &n -> &na + new Transition (4968, 4969), // &nabl -> &nabla + new Transition (4971, 4972), // &N -> &Na + new Transition (5003, 5005), // &natur -> &natura + new Transition (5020, 5021), // &nc -> &nca + new Transition (5024, 5025), // &Nc -> &Nca + new Transition (5059, 5060), // &nd -> &nda + new Transition (5064, 5066), // &ne -> &nea + new Transition (5085, 5086), // &Neg -> &Nega + new Transition (5098, 5099), // &NegativeMediumSp -> &NegativeMediumSpa + new Transition (5109, 5110), // &NegativeThickSp -> &NegativeThickSpa + new Transition (5116, 5117), // &NegativeThinSp -> &NegativeThinSpa + new Transition (5130, 5131), // &NegativeVeryThinSp -> &NegativeVeryThinSpa + new Transition (5141, 5142), // &nese -> &nesea + new Transition (5154, 5155), // &NestedGre -> &NestedGrea + new Transition (5161, 5162), // &NestedGreaterGre -> &NestedGreaterGrea + new Transition (5205, 5206), // &ngeqsl -> &ngeqsla + new Transition (5227, 5232), // &nh -> &nha + new Transition (5236, 5237), // &nhp -> &nhpa + new Transition (5256, 5261), // &nl -> &nla + new Transition (5275, 5276), // &nLeft -> &nLefta + new Transition (5283, 5284), // &nleft -> &nlefta + new Transition (5294, 5295), // &nLeftright -> &nLeftrighta + new Transition (5305, 5306), // &nleftright -> &nleftrighta + new Transition (5317, 5318), // &nleqsl -> &nleqsla + new Transition (5350, 5351), // &NoBre -> &NoBrea + new Transition (5357, 5358), // &NonBre -> &NonBrea + new Transition (5364, 5365), // &NonBreakingSp -> &NonBreakingSpa + new Transition (5392, 5393), // &NotCupC -> &NotCupCa + new Transition (5407, 5408), // &NotDoubleVertic -> &NotDoubleVertica + new Transition (5410, 5411), // &NotDoubleVerticalB -> &NotDoubleVerticalBa + new Transition (5423, 5424), // &NotEqu -> &NotEqua + new Transition (5441, 5442), // &NotGre -> &NotGrea + new Transition (5449, 5450), // &NotGreaterEqu -> &NotGreaterEqua + new Transition (5459, 5460), // &NotGreaterFullEqu -> &NotGreaterFullEqua + new Transition (5465, 5466), // &NotGreaterGre -> &NotGreaterGrea + new Transition (5477, 5478), // &NotGreaterSl -> &NotGreaterSla + new Transition (5483, 5484), // &NotGreaterSlantEqu -> &NotGreaterSlantEqua + new Transition (5508, 5509), // &NotHumpEqu -> &NotHumpEqua + new Transition (5521, 5522), // ¬inv -> ¬inva + new Transition (5534, 5535), // &NotLeftTri -> &NotLeftTria + new Transition (5541, 5542), // &NotLeftTriangleB -> &NotLeftTriangleBa + new Transition (5547, 5548), // &NotLeftTriangleEqu -> &NotLeftTriangleEqua + new Transition (5556, 5557), // &NotLessEqu -> &NotLessEqua + new Transition (5562, 5563), // &NotLessGre -> &NotLessGrea + new Transition (5574, 5575), // &NotLessSl -> &NotLessSla + new Transition (5580, 5581), // &NotLessSlantEqu -> &NotLessSlantEqua + new Transition (5598, 5599), // &NotNestedGre -> &NotNestedGrea + new Transition (5605, 5606), // &NotNestedGreaterGre -> &NotNestedGreaterGrea + new Transition (5623, 5624), // ¬niv -> ¬niva + new Transition (5641, 5642), // &NotPrecedesEqu -> &NotPrecedesEqua + new Transition (5646, 5647), // &NotPrecedesSl -> &NotPrecedesSla + new Transition (5652, 5653), // &NotPrecedesSlantEqu -> &NotPrecedesSlantEqua + new Transition (5677, 5678), // &NotRightTri -> &NotRightTria + new Transition (5684, 5685), // &NotRightTriangleB -> &NotRightTriangleBa + new Transition (5690, 5691), // &NotRightTriangleEqu -> &NotRightTriangleEqua + new Transition (5696, 5697), // &NotSqu -> &NotSqua + new Transition (5709, 5710), // &NotSquareSubsetEqu -> &NotSquareSubsetEqua + new Transition (5722, 5723), // &NotSquareSupersetEqu -> &NotSquareSupersetEqua + new Transition (5734, 5735), // &NotSubsetEqu -> &NotSubsetEqua + new Transition (5747, 5748), // &NotSucceedsEqu -> &NotSucceedsEqua + new Transition (5752, 5753), // &NotSucceedsSl -> &NotSucceedsSla + new Transition (5758, 5759), // &NotSucceedsSlantEqu -> &NotSucceedsSlantEqua + new Transition (5777, 5778), // &NotSupersetEqu -> &NotSupersetEqua + new Transition (5789, 5790), // &NotTildeEqu -> &NotTildeEqua + new Transition (5799, 5800), // &NotTildeFullEqu -> &NotTildeFullEqua + new Transition (5814, 5815), // &NotVertic -> &NotVertica + new Transition (5817, 5818), // &NotVerticalB -> &NotVerticalBa + new Transition (5821, 5822), // &np -> &npa + new Transition (5823, 5825), // &npar -> &npara + new Transition (5855, 5860), // &nr -> &nra + new Transition (5872, 5873), // &nRight -> &nRighta + new Transition (5882, 5883), // &nright -> &nrighta + new Transition (5918, 5919), // &nshortp -> &nshortpa + new Transition (5920, 5921), // &nshortpar -> &nshortpara + new Transition (5938, 5939), // &nsp -> &nspa + new Transition (6007, 6008), // &ntri -> &ntria + new Transition (6043, 6044), // &nv -> &nva + new Transition (6048, 6049), // &nVD -> &nVDa + new Transition (6053, 6054), // &nVd -> &nVda + new Transition (6058, 6059), // &nvD -> &nvDa + new Transition (6063, 6064), // &nvd -> &nvda + new Transition (6073, 6074), // &nvH -> &nvHa + new Transition (6111, 6112), // &nw -> &nwa + new Transition (6127, 6128), // &nwne -> &nwnea + new Transition (6131, 6132), // &O -> &Oa + new Transition (6138, 6139), // &o -> &oa + new Transition (6163, 6164), // &od -> &oda + new Transition (6170, 6171), // &Odbl -> &Odbla + new Transition (6175, 6176), // &odbl -> &odbla + new Transition (6215, 6216), // &Ogr -> &Ogra + new Transition (6220, 6221), // &ogr -> &ogra + new Transition (6228, 6229), // &ohb -> &ohba + new Transition (6238, 6239), // &ol -> &ola + new Transition (6258, 6259), // &Om -> &Oma + new Transition (6263, 6264), // &om -> &oma + new Transition (6269, 6270), // &Omeg -> &Omega + new Transition (6273, 6274), // &omeg -> &omega + new Transition (6302, 6303), // &op -> &opa + new Transition (6342, 6344), // &or -> &ora + new Transition (6386, 6387), // &Osl -> &Osla + new Transition (6391, 6392), // &osl -> &osla + new Transition (6417, 6419), // &otimes -> &otimesa + new Transition (6431, 6432), // &ovb -> &ovba + new Transition (6438, 6439), // &OverB -> &OverBa + new Transition (6442, 6443), // &OverBr -> &OverBra + new Transition (6451, 6452), // &OverP -> &OverPa + new Transition (6463, 6464), // &p -> &pa + new Transition (6465, 6467), // &par -> ¶ + new Transition (6482, 6483), // &P -> &Pa + new Transition (6486, 6487), // &Parti -> &Partia + new Transition (6533, 6534), // &phmm -> &phmma + new Transition (6555, 6556), // &pl -> &pla + new Transition (6567, 6569), // &plus -> &plusa + new Transition (6612, 6613), // &Poinc -> &Poinca + new Transition (6617, 6618), // &Poincarepl -> &Poincarepla + new Transition (6642, 6644), // &pr -> &pra + new Transition (6655, 6657), // &prec -> &preca + new Transition (6681, 6682), // &PrecedesEqu -> &PrecedesEqua + new Transition (6686, 6687), // &PrecedesSl -> &PrecedesSla + new Transition (6692, 6693), // &PrecedesSlantEqu -> &PrecedesSlantEqua + new Transition (6705, 6706), // &precn -> &precna + new Transition (6735, 6736), // &prn -> &prna + new Transition (6754, 6755), // &prof -> &profa + new Transition (6756, 6757), // &profal -> &profala + new Transition (6778, 6780), // &Proportion -> &Proportiona + new Transition (6847, 6848), // &qu -> &qua + new Transition (6876, 6882), // &r -> &ra + new Transition (6877, 6878), // &rA -> &rAa + new Transition (6886, 6887), // &R -> &Ra + new Transition (6932, 6934), // &rarr -> &rarra + new Transition (6968, 6969), // &rAt -> &rAta + new Transition (6973, 6974), // &rat -> &rata + new Transition (6981, 6982), // &ration -> &rationa + new Transition (6986, 6987), // &RB -> &RBa + new Transition (6991, 6992), // &rB -> &rBa + new Transition (6996, 6997), // &rb -> &rba + new Transition (7005, 7006), // &rbr -> &rbra + new Transition (7021, 7022), // &Rc -> &Rca + new Transition (7027, 7028), // &rc -> &rca + new Transition (7054, 7055), // &rdc -> &rdca + new Transition (7059, 7060), // &rdldh -> &rdldha + new Transition (7074, 7075), // &re -> &rea + new Transition (7082, 7083), // &realp -> &realpa + new Transition (7151, 7152), // &rH -> &rHa + new Transition (7155, 7156), // &rh -> &rha + new Transition (7174, 7193), // &Right -> &Righta + new Transition (7181, 7182), // &RightAngleBr -> &RightAngleBra + new Transition (7202, 7203), // &right -> &righta + new Transition (7209, 7210), // &RightArrowB -> &RightArrowBa + new Transition (7223, 7224), // &rightarrowt -> &rightarrowta + new Transition (7243, 7244), // &RightDoubleBr -> &RightDoubleBra + new Transition (7269, 7270), // &RightDownVectorB -> &RightDownVectorBa + new Transition (7279, 7280), // &righth -> &rightha + new Transition (7297, 7298), // &rightleft -> &rightlefta + new Transition (7305, 7306), // &rightlefth -> &rightleftha + new Transition (7318, 7319), // &rightright -> &rightrighta + new Transition (7330, 7331), // &rightsquig -> &rightsquiga + new Transition (7366, 7367), // &RightTri -> &RightTria + new Transition (7373, 7374), // &RightTriangleB -> &RightTriangleBa + new Transition (7379, 7380), // &RightTriangleEqu -> &RightTriangleEqua + new Transition (7413, 7414), // &RightUpVectorB -> &RightUpVectorBa + new Transition (7424, 7425), // &RightVectorB -> &RightVectorBa + new Transition (7442, 7443), // &rl -> &rla + new Transition (7447, 7448), // &rlh -> &rlha + new Transition (7457, 7459), // &rmoust -> &rmousta + new Transition (7469, 7470), // &ro -> &roa + new Transition (7481, 7482), // &rop -> &ropa + new Transition (7512, 7513), // &rp -> &rpa + new Transition (7526, 7527), // &rr -> &rra + new Transition (7535, 7536), // &Rright -> &Rrighta + new Transition (7542, 7543), // &rs -> &rsa + new Transition (7595, 7596), // &RuleDel -> &RuleDela + new Transition (7604, 7605), // &ruluh -> &ruluha + new Transition (7610, 7611), // &S -> &Sa + new Transition (7617, 7618), // &s -> &sa + new Transition (7629, 7636), // &Sc -> &Sca + new Transition (7631, 7633), // &sc -> &sca + new Transition (7670, 7671), // &scn -> &scna + new Transition (7703, 7704), // &se -> &sea + new Transition (7725, 7726), // &sesw -> &seswa + new Transition (7751, 7752), // &sh -> &sha + new Transition (7803, 7804), // &shortp -> &shortpa + new Transition (7805, 7806), // &shortpar -> &shortpara + new Transition (7835, 7836), // &Sigm -> &Sigma + new Transition (7840, 7841), // &sigm -> &sigma + new Transition (7873, 7874), // &simr -> &simra + new Transition (7878, 7879), // &sl -> &sla + new Transition (7883, 7884), // &Sm -> &Sma + new Transition (7894, 7895), // &sm -> &sma + new Transition (7912, 7913), // &smep -> &smepa + new Transition (7944, 7946), // &solb -> &solba + new Transition (7956, 7957), // &sp -> &spa + new Transition (7969, 7970), // &sqc -> &sqca + new Transition (8008, 8015), // &squ -> &squa + new Transition (8010, 8011), // &Squ -> &Squa + new Transition (8041, 8042), // &SquareSubsetEqu -> &SquareSubsetEqua + new Transition (8054, 8055), // &SquareSupersetEqu -> &SquareSupersetEqua + new Transition (8068, 8069), // &sr -> &sra + new Transition (8091, 8092), // &sst -> &ssta + new Transition (8096, 8097), // &St -> &Sta + new Transition (8100, 8101), // &st -> &sta + new Transition (8106, 8107), // &str -> &stra + new Transition (8160, 8161), // &subr -> &subra + new Transition (8180, 8181), // &SubsetEqu -> &SubsetEqua + new Transition (8199, 8201), // &succ -> &succa + new Transition (8225, 8226), // &SucceedsEqu -> &SucceedsEqua + new Transition (8230, 8231), // &SucceedsSl -> &SucceedsSla + new Transition (8236, 8237), // &SucceedsSlantEqu -> &SucceedsSlantEqua + new Transition (8249, 8250), // &succn -> &succna + new Transition (8271, 8272), // &SuchTh -> &SuchTha + new Transition (8316, 8317), // &SupersetEqu -> &SupersetEqua + new Transition (8328, 8329), // &supl -> &supla + new Transition (8375, 8376), // &sw -> &swa + new Transition (8391, 8392), // &swnw -> &swnwa + new Transition (8400, 8401), // &T -> &Ta + new Transition (8404, 8405), // &t -> &ta + new Transition (8419, 8420), // &Tc -> &Tca + new Transition (8425, 8426), // &tc -> &tca + new Transition (8481, 8482), // &Thet -> &Theta + new Transition (8484, 8485), // &thet -> &theta + new Transition (8495, 8496), // &thick -> &thicka + new Transition (8511, 8512), // &ThickSp -> &ThickSpa + new Transition (8522, 8523), // &ThinSp -> &ThinSpa + new Transition (8527, 8528), // &thk -> &thka + new Transition (8556, 8557), // &TildeEqu -> &TildeEqua + new Transition (8566, 8567), // &TildeFullEqu -> &TildeFullEqua + new Transition (8580, 8582), // ×b -> ×ba + new Transition (8591, 8592), // &toe -> &toea + new Transition (8614, 8615), // &tos -> &tosa + new Transition (8628, 8629), // &tr -> &tra + new Transition (8633, 8634), // &tri -> &tria + new Transition (8744, 8745), // &twohe -> &twohea + new Transition (8750, 8751), // &twoheadleft -> &twoheadlefta + new Transition (8761, 8762), // &twoheadright -> &twoheadrighta + new Transition (8768, 8769), // &U -> &Ua + new Transition (8775, 8776), // &u -> &ua + new Transition (8829, 8830), // &ud -> &uda + new Transition (8836, 8837), // &Udbl -> &Udbla + new Transition (8841, 8842), // &udbl -> &udbla + new Transition (8845, 8846), // &udh -> &udha + new Transition (8861, 8862), // &Ugr -> &Ugra + new Transition (8867, 8868), // &ugr -> &ugra + new Transition (8872, 8873), // &uH -> &uHa + new Transition (8876, 8877), // &uh -> &uha + new Transition (8904, 8905), // &Um -> &Uma + new Transition (8909, 8910), // &um -> &uma + new Transition (8920, 8921), // &UnderB -> &UnderBa + new Transition (8924, 8925), // &UnderBr -> &UnderBra + new Transition (8933, 8934), // &UnderP -> &UnderPa + new Transition (8970, 8977), // &Up -> &Upa + new Transition (8983, 8984), // &up -> &upa + new Transition (8990, 8991), // &UpArrowB -> &UpArrowBa + new Transition (9017, 9018), // &Updown -> &Updowna + new Transition (9027, 9028), // &updown -> &updowna + new Transition (9046, 9047), // &uph -> &upha + new Transition (9119, 9120), // &upup -> &upupa + new Transition (9182, 9183), // &uu -> &uua + new Transition (9194, 9195), // &uw -> &uwa + new Transition (9201, 9202), // &v -> &va + new Transition (9217, 9218), // &vark -> &varka + new Transition (9220, 9221), // &varkapp -> &varkappa + new Transition (9255, 9256), // &varsigm -> &varsigma + new Transition (9282, 9283), // &varthet -> &vartheta + new Transition (9286, 9287), // &vartri -> &vartria + new Transition (9304, 9305), // &Vb -> &Vba + new Transition (9308, 9309), // &vB -> &vBa + new Transition (9320, 9321), // &VD -> &VDa + new Transition (9325, 9326), // &Vd -> &Vda + new Transition (9330, 9331), // &vD -> &vDa + new Transition (9335, 9336), // &vd -> &vda + new Transition (9348, 9349), // &veeb -> &veeba + new Transition (9361, 9362), // &Verb -> &Verba + new Transition (9366, 9367), // &verb -> &verba + new Transition (9375, 9376), // &Vertic -> &Vertica + new Transition (9378, 9379), // &VerticalB -> &VerticalBa + new Transition (9389, 9390), // &VerticalSep -> &VerticalSepa + new Transition (9391, 9392), // &VerticalSepar -> &VerticalSepara + new Transition (9409, 9410), // &VeryThinSp -> &VeryThinSpa + new Transition (9472, 9473), // &Vvd -> &Vvda + new Transition (9480, 9481), // &vzigz -> &vzigza + new Transition (9498, 9499), // &wedb -> &wedba + new Transition (9535, 9536), // &wre -> &wrea + new Transition (9549, 9550), // &xc -> &xca + new Transition (9572, 9577), // &xh -> &xha + new Transition (9585, 9590), // &xl -> &xla + new Transition (9594, 9595), // &xm -> &xma + new Transition (9623, 9628), // &xr -> &xra + new Transition (9665, 9666), // &Y -> &Ya + new Transition (9672, 9673), // &y -> &ya + new Transition (9747, 9748), // &Z -> &Za + new Transition (9754, 9755), // &z -> &za + new Transition (9761, 9762), // &Zc -> &Zca + new Transition (9767, 9768), // &zc -> &zca + new Transition (9800, 9801), // &ZeroWidthSp -> &ZeroWidthSpa + new Transition (9805, 9806), // &Zet -> &Zeta + new Transition (9808, 9809), // &zet -> &zeta + new Transition (9827, 9828) // &zigr -> &zigra + }; + TransitionTable_b = new Transition[96] { + new Transition (0, 301), // & -> &b + new Transition (1, 15), // &A -> &Ab + new Transition (8, 21), // &a -> &ab + new Transition (147, 150), // &angmsda -> &angmsdab + new Transition (167, 168), // &angrtv -> &angrtvb + new Transition (301, 360), // &b -> &bb + new Transition (364, 365), // &bbrkt -> &bbrktb + new Transition (613, 614), // &box -> &boxb + new Transition (735, 736), // &brv -> &brvb + new Transition (758, 760), // &bsol -> &bsolb + new Transition (764, 765), // &bsolhsu -> &bsolhsub + new Transition (805, 811), // &cap -> &capb + new Transition (1101, 1102), // &CloseCurlyDou -> &CloseCurlyDoub + new Transition (1118, 1119), // &clu -> &club + new Transition (1278, 1279), // &csu -> &csub + new Transition (1318, 1320), // &cup -> &cupb + new Transition (1432, 1463), // &d -> &db + new Transition (1577, 1578), // &DiacriticalDou -> &DiacriticalDoub + new Transition (1731, 1732), // &dou -> &doub + new Transition (1734, 1735), // &double -> &doubleb + new Transition (1744, 1745), // &Dou -> &Doub + new Transition (2023, 2024), // &dr -> &drb + new Transition (2389, 2390), // &Equili -> &Equilib + new Transition (2701, 2730), // &g -> &gb + new Transition (2708, 2724), // &G -> &Gb + new Transition (3020, 3060), // &h -> &hb + new Transition (3101, 3102), // &Hil -> &Hilb + new Transition (3166, 3167), // &hor -> &horb + new Transition (3225, 3226), // &hy -> &hyb + new Transition (3447, 3448), // &Invisi -> &Invisib + new Transition (3692, 3812), // &l -> &lb + new Transition (3723, 3724), // &Lam -> &Lamb + new Transition (3728, 3729), // &lam -> &lamb + new Transition (3766, 3768), // &larr -> &larrb + new Transition (3812, 3817), // &lb -> &lbb + new Transition (3862, 3863), // &lcu -> &lcub + new Transition (3963, 3964), // &LeftDou -> &LeftDoub + new Transition (4325, 4334), // &lh -> &lhb + new Transition (4422, 4430), // &lo -> &lob + new Transition (4579, 4584), // &low -> &lowb + new Transition (4676, 4677), // &lsq -> &lsqb + new Transition (4892, 4894), // &minus -> &minusb + new Transition (4965, 5010), // &n -> &nb + new Transition (4966, 4967), // &na -> &nab + new Transition (5398, 5399), // &NotDou -> &NotDoub + new Transition (5521, 5524), // ¬inv -> ¬invb + new Transition (5623, 5626), // ¬niv -> ¬nivb + new Transition (5701, 5702), // &NotSquareSu -> &NotSquareSub + new Transition (5726, 5727), // &NotSu -> &NotSub + new Transition (5944, 5945), // &nsqsu -> &nsqsub + new Transition (5951, 5952), // &nsu -> &nsub + new Transition (6163, 6174), // &od -> &odb + new Transition (6168, 6169), // &Od -> &Odb + new Transition (6227, 6228), // &oh -> &ohb + new Transition (6316, 6317), // &OpenCurlyDou -> &OpenCurlyDoub + new Transition (6430, 6431), // &ov -> &ovb + new Transition (6567, 6574), // &plus -> &plusb + new Transition (6876, 6996), // &r -> &rb + new Transition (6932, 6937), // &rarr -> &rarrb + new Transition (6996, 7001), // &rb -> &rbb + new Transition (7046, 7047), // &rcu -> &rcub + new Transition (7114, 7115), // &ReverseEquili -> &ReverseEquilib + new Transition (7128, 7129), // &ReverseUpEquili -> &ReverseUpEquilib + new Transition (7238, 7239), // &RightDou -> &RightDoub + new Transition (7469, 7477), // &ro -> &rob + new Transition (7559, 7560), // &rsq -> &rsqb + new Transition (7617, 7624), // &s -> &sb + new Transition (7697, 7699), // &sdot -> &sdotb + new Transition (7942, 7944), // &sol -> &solb + new Transition (7985, 7986), // &sqsu -> &sqsub + new Transition (8033, 8034), // &SquareSu -> &SquareSub + new Transition (8127, 8128), // &Su -> &Sub + new Transition (8130, 8131), // &su -> &sub + new Transition (8193, 8194), // &subsu -> &subsub + new Transition (8297, 8298), // &supdsu -> &supdsub + new Transition (8325, 8326), // &suphsu -> &suphsub + new Transition (8370, 8371), // &supsu -> &supsub + new Transition (8401, 8402), // &Ta -> &Tab + new Transition (8404, 8415), // &t -> &tb + new Transition (8578, 8580), // × -> ×b + new Transition (8594, 8596), // &top -> &topb + new Transition (8690, 8691), // &tris -> &trisb + new Transition (8768, 8797), // &U -> &Ub + new Transition (8775, 8802), // &u -> &ub + new Transition (8829, 8840), // &ud -> &udb + new Transition (8834, 8835), // &Ud -> &Udb + new Transition (8876, 8883), // &uh -> &uhb + new Transition (9039, 9040), // &UpEquili -> &UpEquilib + new Transition (9258, 9259), // &varsu -> &varsub + new Transition (9303, 9304), // &V -> &Vb + new Transition (9346, 9348), // &vee -> &veeb + new Transition (9360, 9361), // &Ver -> &Verb + new Transition (9365, 9366), // &ver -> &verb + new Transition (9427, 9428), // &vnsu -> &vnsub + new Transition (9458, 9459), // &vsu -> &vsub + new Transition (9497, 9498) // &wed -> &wedb + }; + TransitionTable_c = new Transition[377] { + new Transition (0, 796), // & -> &c + new Transition (1, 33), // &A -> &Ac + new Transition (2, 3), // &Aa -> &Aac + new Transition (8, 27), // &a -> &ac + new Transition (9, 10), // &aa -> &aac + new Transition (35, 36), // &Acir ->  + new Transition (39, 40), // &acir -> â + new Transition (99, 100), // &Ama -> &Amac + new Transition (104, 105), // &ama -> &amac + new Transition (147, 152), // &angmsda -> &angmsdac + new Transition (201, 202), // &apa -> &apac + new Transition (222, 223), // &ApplyFun -> &ApplyFunc + new Transition (247, 248), // &As -> &Asc + new Transition (251, 252), // &as -> &asc + new Transition (289, 290), // &aw -> &awc + new Transition (301, 369), // &b -> &bc + new Transition (302, 303), // &ba -> &bac + new Transition (304, 305), // &back -> &backc + new Transition (331, 374), // &B -> &Bc + new Transition (332, 333), // &Ba -> &Bac + new Transition (384, 385), // &be -> &bec + new Transition (390, 391), // &Be -> &Bec + new Transition (443, 444), // &big -> &bigc + new Transition (449, 450), // &bigcir -> &bigcirc + new Transition (472, 473), // &bigsq -> &bigsqc + new Transition (520, 521), // &bla -> &blac + new Transition (575, 576), // &blo -> &bloc + new Transition (740, 741), // &Bs -> &Bsc + new Transition (744, 745), // &bs -> &bsc + new Transition (789, 866), // &C -> &Cc + new Transition (790, 791), // &Ca -> &Cac + new Transition (796, 861), // &c -> &cc + new Transition (797, 798), // &ca -> &cac + new Transition (805, 817), // &cap -> &capc + new Transition (812, 813), // &capbr -> &capbrc + new Transition (887, 888), // &Ccir -> &Ccirc + new Transition (891, 892), // &ccir -> &ccirc + new Transition (956, 957), // &CH -> &CHc + new Transition (960, 961), // &ch -> &chc + new Transition (964, 965), // &che -> &chec + new Transition (979, 981), // &cir -> &circ + new Transition (1004, 1009), // &circled -> &circledc + new Transition (1011, 1012), // &circledcir -> &circledcirc + new Transition (1020, 1021), // &Cir -> &Circ + new Transition (1063, 1064), // &cirs -> &cirsc + new Transition (1069, 1070), // &Clo -> &Cloc + new Transition (1213, 1214), // &Coprodu -> &Coproduc + new Transition (1233, 1234), // &CounterClo -> &CounterCloc + new Transition (1270, 1271), // &Cs -> &Csc + new Transition (1274, 1275), // &cs -> &csc + new Transition (1305, 1306), // &cues -> &cuesc + new Transition (1318, 1330), // &cup -> &cupc + new Transition (1321, 1322), // &cupbr -> &cupbrc + new Transition (1359, 1360), // &curlyeqpre -> &curlyeqprec + new Transition (1363, 1364), // &curlyeqsu -> &curlyeqsuc + new Transition (1364, 1365), // &curlyeqsuc -> &curlyeqsucc + new Transition (1407, 1408), // &cw -> &cwc + new Transition (1420, 1421), // &cyl -> &cylc + new Transition (1425, 1474), // &D -> &Dc + new Transition (1432, 1480), // &d -> &dc + new Transition (1471, 1472), // &dbla -> &dblac + new Transition (1558, 1559), // &Dia -> &Diac + new Transition (1563, 1564), // &Diacriti -> &Diacritic + new Transition (1567, 1568), // &DiacriticalA -> &DiacriticalAc + new Transition (1581, 1582), // &DiacriticalDoubleA -> &DiacriticalDoubleAc + new Transition (1661, 1662), // &DJ -> &DJc + new Transition (1665, 1666), // &dj -> &djc + new Transition (1669, 1670), // &dl -> &dlc + new Transition (1873, 1874), // &DoubleVerti -> &DoubleVertic + new Transition (1960, 1961), // &DownLeftRightVe -> &DownLeftRightVec + new Transition (1970, 1971), // &DownLeftTeeVe -> &DownLeftTeeVec + new Transition (1977, 1978), // &DownLeftVe -> &DownLeftVec + new Transition (1996, 1997), // &DownRightTeeVe -> &DownRightTeeVec + new Transition (2003, 2004), // &DownRightVe -> &DownRightVec + new Transition (2023, 2031), // &dr -> &drc + new Transition (2040, 2041), // &Ds -> &Dsc + new Transition (2044, 2045), // &ds -> &dsc + new Transition (2048, 2049), // &DS -> &DSc + new Transition (2093, 2094), // &DZ -> &DZc + new Transition (2097, 2098), // &dz -> &dzc + new Transition (2108, 2127), // &E -> &Ec + new Transition (2109, 2110), // &Ea -> &Eac + new Transition (2115, 2133), // &e -> &ec + new Transition (2116, 2117), // &ea -> &eac + new Transition (2140, 2146), // &ecir -> ê + new Transition (2143, 2144), // &Ecir -> Ê + new Transition (2229, 2230), // &Ema -> &Emac + new Transition (2234, 2235), // &ema -> &emac + new Transition (2339, 2340), // &eq -> &eqc + new Transition (2342, 2343), // &eqcir -> &eqcirc + new Transition (2418, 2419), // &Es -> &Esc + new Transition (2422, 2423), // &es -> &esc + new Transition (2458, 2459), // &ex -> &exc + new Transition (2473, 2474), // &expe -> &expec + new Transition (2503, 2521), // &f -> &fc + new Transition (2517, 2518), // &F -> &Fc + new Transition (2648, 2649), // &fra -> &frac + new Transition (2693, 2694), // &Fs -> &Fsc + new Transition (2697, 2698), // &fs -> &fsc + new Transition (2701, 2746), // &g -> &gc + new Transition (2702, 2703), // &ga -> &gac + new Transition (2708, 2736), // &G -> &Gc + new Transition (2743, 2744), // &Gcir -> &Gcirc + new Transition (2748, 2749), // &gcir -> &gcirc + new Transition (2781, 2783), // &ges -> &gesc + new Transition (2783, 2784), // &gesc -> &gescc + new Transition (2816, 2817), // &GJ -> &GJc + new Transition (2820, 2821), // &gj -> &gjc + new Transition (2923, 2924), // &Gs -> &Gsc + new Transition (2927, 2928), // &gs -> &gsc + new Transition (2942, 2944), // > -> >c + new Transition (2944, 2945), // >c -> >cc + new Transition (3014, 3064), // &H -> &Hc + new Transition (3015, 3016), // &Ha -> &Hac + new Transition (3020, 3069), // &h -> &hc + new Transition (3037, 3038), // &HARD -> &HARDc + new Transition (3042, 3043), // &hard -> &hardc + new Transition (3050, 3052), // &harr -> &harrc + new Transition (3066, 3067), // &Hcir -> &Hcirc + new Transition (3071, 3072), // &hcir -> &hcirc + new Transition (3089, 3090), // &her -> &herc + new Transition (3108, 3109), // &HilbertSpa -> &HilbertSpac + new Transition (3184, 3185), // &Hs -> &Hsc + new Transition (3188, 3189), // &hs -> &hsc + new Transition (3236, 3252), // &I -> &Ic + new Transition (3237, 3238), // &Ia -> &Iac + new Transition (3243, 3250), // &i -> &ic + new Transition (3244, 3245), // &ia -> &iac + new Transition (3254, 3255), // &Icir -> Î + new Transition (3258, 3259), // &icir -> î + new Transition (3269, 3270), // &IE -> &IEc + new Transition (3273, 3274), // &ie -> &iec + new Transition (3277, 3278), // &iex -> &iexc + new Transition (3332, 3333), // &Ima -> &Imac + new Transition (3337, 3338), // &ima -> &imac + new Transition (3378, 3380), // &in -> &inc + new Transition (3401, 3403), // &int -> &intc + new Transition (3419, 3420), // &inter -> &interc + new Transition (3426, 3427), // &Interse -> &Intersec + new Transition (3463, 3464), // &IO -> &IOc + new Transition (3467, 3468), // &io -> &ioc + new Transition (3503, 3504), // &Is -> &Isc + new Transition (3507, 3508), // &is -> &isc + new Transition (3540, 3541), // &Iuk -> &Iukc + new Transition (3545, 3546), // &iuk -> &iukc + new Transition (3555, 3556), // &J -> &Jc + new Transition (3558, 3559), // &Jcir -> &Jcirc + new Transition (3561, 3562), // &j -> &jc + new Transition (3564, 3565), // &jcir -> &jcirc + new Transition (3590, 3591), // &Js -> &Jsc + new Transition (3594, 3595), // &js -> &jsc + new Transition (3599, 3600), // &Jser -> &Jserc + new Transition (3604, 3605), // &jser -> &jserc + new Transition (3609, 3610), // &Juk -> &Jukc + new Transition (3614, 3615), // &juk -> &jukc + new Transition (3618, 3632), // &K -> &Kc + new Transition (3624, 3638), // &k -> &kc + new Transition (3660, 3661), // &KH -> &KHc + new Transition (3664, 3665), // &kh -> &khc + new Transition (3668, 3669), // &KJ -> &KJc + new Transition (3672, 3673), // &kj -> &kjc + new Transition (3684, 3685), // &Ks -> &Ksc + new Transition (3688, 3689), // &ks -> &ksc + new Transition (3692, 3843), // &l -> &lc + new Transition (3698, 3837), // &L -> &Lc + new Transition (3699, 3700), // &La -> &Lac + new Transition (3705, 3706), // &la -> &lac + new Transition (3748, 3749), // &Lapla -> &Laplac + new Transition (3822, 3823), // &lbra -> &lbrac + new Transition (3869, 3870), // &ld -> &ldc + new Transition (3908, 3909), // &LeftAngleBra -> &LeftAngleBrac + new Transition (3969, 3970), // &LeftDoubleBra -> &LeftDoubleBrac + new Transition (3981, 3982), // &LeftDownTeeVe -> &LeftDownTeeVec + new Transition (3988, 3989), // &LeftDownVe -> &LeftDownVec + new Transition (4086, 4087), // &LeftRightVe -> &LeftRightVec + new Transition (4103, 4104), // &LeftTeeVe -> &LeftTeeVec + new Transition (4145, 4146), // &LeftUpDownVe -> &LeftUpDownVec + new Transition (4155, 4156), // &LeftUpTeeVe -> &LeftUpTeeVec + new Transition (4162, 4163), // &LeftUpVe -> &LeftUpVec + new Transition (4173, 4174), // &LeftVe -> &LeftVec + new Transition (4197, 4199), // &les -> &lesc + new Transition (4199, 4200), // &lesc -> &lescc + new Transition (4338, 4339), // &LJ -> &LJc + new Transition (4342, 4343), // &lj -> &ljc + new Transition (4348, 4354), // &ll -> &llc + new Transition (4396, 4397), // &lmousta -> &lmoustac + new Transition (4628, 4633), // &lr -> &lrc + new Transition (4652, 4662), // &ls -> &lsc + new Transition (4658, 4659), // &Ls -> &Lsc + new Transition (4698, 4700), // < -> <c + new Transition (4700, 4701), // <c -> <cc + new Transition (4767, 4809), // &m -> &mc + new Transition (4768, 4769), // &ma -> &mac + new Transition (4781, 4815), // &M -> &Mc + new Transition (4850, 4851), // &MediumSpa -> &MediumSpac + new Transition (4871, 4872), // &mi -> &mic + new Transition (4876, 4882), // &mid -> &midc + new Transition (4909, 4910), // &ml -> &mlc + new Transition (4937, 4938), // &Ms -> &Msc + new Transition (4941, 4942), // &ms -> &msc + new Transition (4965, 5020), // &n -> &nc + new Transition (4966, 4978), // &na -> &nac + new Transition (4971, 5024), // &N -> &Nc + new Transition (4972, 4973), // &Na -> &Nac + new Transition (5099, 5100), // &NegativeMediumSpa -> &NegativeMediumSpac + new Transition (5105, 5106), // &NegativeThi -> &NegativeThic + new Transition (5110, 5111), // &NegativeThickSpa -> &NegativeThickSpac + new Transition (5117, 5118), // &NegativeThinSpa -> &NegativeThinSpac + new Transition (5131, 5132), // &NegativeVeryThinSpa -> &NegativeVeryThinSpac + new Transition (5248, 5249), // &NJ -> &NJc + new Transition (5252, 5253), // &nj -> &njc + new Transition (5365, 5366), // &NonBreakingSpa -> &NonBreakingSpac + new Transition (5406, 5407), // &NotDoubleVerti -> &NotDoubleVertic + new Transition (5521, 5526), // ¬inv -> ¬invc + new Transition (5623, 5628), // ¬niv -> ¬nivc + new Transition (5632, 5633), // &NotPre -> &NotPrec + new Transition (5726, 5738), // &NotSu -> &NotSuc + new Transition (5738, 5739), // &NotSuc -> &NotSucc + new Transition (5813, 5814), // &NotVerti -> &NotVertic + new Transition (5842, 5844), // &npr -> &nprc + new Transition (5848, 5850), // &npre -> &nprec + new Transition (5862, 5864), // &nrarr -> &nrarrc + new Transition (5895, 5896), // &ns -> &nsc + new Transition (5896, 5898), // &nsc -> &nscc + new Transition (5904, 5905), // &Ns -> &Nsc + new Transition (5951, 5967), // &nsu -> &nsuc + new Transition (5967, 5968), // &nsuc -> &nsucc + new Transition (6131, 6152), // &O -> &Oc + new Transition (6132, 6133), // &Oa -> &Oac + new Transition (6138, 6148), // &o -> &oc + new Transition (6139, 6140), // &oa -> &oac + new Transition (6150, 6157), // &ocir -> ô + new Transition (6154, 6155), // &Ocir -> Ô + new Transition (6171, 6172), // &Odbla -> &Odblac + new Transition (6176, 6177), // &odbla -> &odblac + new Transition (6200, 6201), // &of -> &ofc + new Transition (6238, 6243), // &ol -> &olc + new Transition (6259, 6260), // &Oma -> &Omac + new Transition (6264, 6265), // &oma -> &omac + new Transition (6276, 6277), // &Omi -> &Omic + new Transition (6282, 6283), // &omi -> &omic + new Transition (6378, 6379), // &Os -> &Osc + new Transition (6382, 6383), // &os -> &osc + new Transition (6443, 6444), // &OverBra -> &OverBrac + new Transition (6463, 6494), // &p -> &pc + new Transition (6482, 6491), // &P -> &Pc + new Transition (6498, 6499), // &per -> &perc + new Transition (6545, 6546), // &pit -> &pitc + new Transition (6557, 6558), // &plan -> &planc + new Transition (6567, 6576), // &plus -> &plusc + new Transition (6569, 6570), // &plusa -> &plusac + new Transition (6611, 6612), // &Poin -> &Poinc + new Transition (6642, 6647), // &pr -> &prc + new Transition (6653, 6655), // &pre -> &prec + new Transition (6655, 6664), // &prec -> &precc + new Transition (6672, 6673), // &Pre -> &Prec + new Transition (6750, 6751), // &Produ -> &Produc + new Transition (6795, 6796), // &Ps -> &Psc + new Transition (6799, 6800), // &ps -> &psc + new Transition (6808, 6809), // &pun -> &punc + new Transition (6839, 6840), // &Qs -> &Qsc + new Transition (6843, 6844), // &qs -> &qsc + new Transition (6876, 7027), // &r -> &rc + new Transition (6882, 6883), // &ra -> &rac + new Transition (6886, 7021), // &R -> &Rc + new Transition (6887, 6888), // &Ra -> &Rac + new Transition (6898, 6899), // &radi -> &radic + new Transition (6932, 6942), // &rarr -> &rarrc + new Transition (7006, 7007), // &rbra -> &rbrac + new Transition (7053, 7054), // &rd -> &rdc + new Transition (7074, 7089), // &re -> &rec + new Transition (7182, 7183), // &RightAngleBra -> &RightAngleBrac + new Transition (7244, 7245), // &RightDoubleBra -> &RightDoubleBrac + new Transition (7256, 7257), // &RightDownTeeVe -> &RightDownTeeVec + new Transition (7263, 7264), // &RightDownVe -> &RightDownVec + new Transition (7348, 7349), // &RightTeeVe -> &RightTeeVec + new Transition (7390, 7391), // &RightUpDownVe -> &RightUpDownVec + new Transition (7400, 7401), // &RightUpTeeVe -> &RightUpTeeVec + new Transition (7407, 7408), // &RightUpVe -> &RightUpVec + new Transition (7418, 7419), // &RightVe -> &RightVec + new Transition (7459, 7460), // &rmousta -> &rmoustac + new Transition (7542, 7552), // &rs -> &rsc + new Transition (7548, 7549), // &Rs -> &Rsc + new Transition (7610, 7629), // &S -> &Sc + new Transition (7611, 7612), // &Sa -> &Sac + new Transition (7617, 7631), // &s -> &sc + new Transition (7618, 7619), // &sa -> &sac + new Transition (7631, 7645), // &sc -> &scc + new Transition (7663, 7664), // &Scir -> &Scirc + new Transition (7667, 7668), // &scir -> &scirc + new Transition (7703, 7718), // &se -> &sec + new Transition (7751, 7762), // &sh -> &shc + new Transition (7756, 7767), // &SH -> &SHc + new Transition (7758, 7759), // &SHCH -> &SHCHc + new Transition (7763, 7764), // &shch -> &shchc + new Transition (7889, 7890), // &SmallCir -> &SmallCirc + new Transition (7932, 7933), // &SOFT -> &SOFTc + new Transition (7938, 7939), // &soft -> &softc + new Transition (7968, 7969), // &sq -> &sqc + new Transition (8025, 8026), // &SquareInterse -> &SquareIntersec + new Transition (8073, 8074), // &Ss -> &Ssc + new Transition (8077, 8078), // &ss -> &ssc + new Transition (8127, 8216), // &Su -> &Suc + new Transition (8130, 8198), // &su -> &suc + new Transition (8198, 8199), // &suc -> &succ + new Transition (8199, 8208), // &succ -> &succc + new Transition (8216, 8217), // &Suc -> &Succ + new Transition (8400, 8419), // &T -> &Tc + new Transition (8404, 8425), // &t -> &tc + new Transition (8452, 8453), // &telre -> &telrec + new Transition (8493, 8494), // &thi -> &thic + new Transition (8507, 8508), // &Thi -> &Thic + new Transition (8512, 8513), // &ThickSpa -> &ThickSpac + new Transition (8523, 8524), // &ThinSpa -> &ThinSpac + new Transition (8594, 8600), // &top -> &topc + new Transition (8705, 8706), // &Ts -> &Tsc + new Transition (8709, 8710), // &ts -> &tsc + new Transition (8713, 8714), // &TS -> &TSc + new Transition (8719, 8720), // &TSH -> &TSHc + new Transition (8723, 8724), // &tsh -> &tshc + new Transition (8768, 8815), // &U -> &Uc + new Transition (8769, 8770), // &Ua -> &Uac + new Transition (8775, 8820), // &u -> &uc + new Transition (8776, 8777), // &ua -> &uac + new Transition (8792, 8793), // &Uarro -> &Uarroc + new Transition (8798, 8799), // &Ubr -> &Ubrc + new Transition (8803, 8804), // &ubr -> &ubrc + new Transition (8817, 8818), // &Ucir -> Û + new Transition (8822, 8823), // &ucir -> û + new Transition (8837, 8838), // &Udbla -> &Udblac + new Transition (8842, 8843), // &udbla -> &udblac + new Transition (8887, 8888), // &ul -> &ulc + new Transition (8905, 8906), // &Uma -> &Umac + new Transition (8910, 8911), // &uma -> &umac + new Transition (8925, 8926), // &UnderBra -> &UnderBrac + new Transition (9127, 9128), // &ur -> &urc + new Transition (9153, 9154), // &Us -> &Usc + new Transition (9157, 9158), // &us -> &usc + new Transition (9201, 9317), // &v -> &vc + new Transition (9303, 9314), // &V -> &Vc + new Transition (9374, 9375), // &Verti -> &Vertic + new Transition (9410, 9411), // &VeryThinSpa -> &VeryThinSpac + new Transition (9450, 9451), // &Vs -> &Vsc + new Transition (9454, 9455), // &vs -> &vsc + new Transition (9484, 9485), // &W -> &Wc + new Transition (9487, 9488), // &Wcir -> &Wcirc + new Transition (9490, 9491), // &w -> &wc + new Transition (9493, 9494), // &wcir -> &wcirc + new Transition (9540, 9541), // &Ws -> &Wsc + new Transition (9544, 9545), // &ws -> &wsc + new Transition (9548, 9549), // &x -> &xc + new Transition (9554, 9555), // &xcir -> &xcirc + new Transition (9632, 9633), // &Xs -> &Xsc + new Transition (9636, 9637), // &xs -> &xsc + new Transition (9640, 9641), // &xsq -> &xsqc + new Transition (9665, 9685), // &Y -> &Yc + new Transition (9666, 9667), // &Ya -> &Yac + new Transition (9672, 9690), // &y -> &yc + new Transition (9673, 9674), // &ya -> &yac + new Transition (9679, 9680), // &YA -> &YAc + new Transition (9687, 9688), // &Ycir -> &Ycirc + new Transition (9692, 9693), // &ycir -> &ycirc + new Transition (9708, 9709), // &YI -> &YIc + new Transition (9712, 9713), // &yi -> &yic + new Transition (9724, 9725), // &Ys -> &Ysc + new Transition (9728, 9729), // &ys -> &ysc + new Transition (9732, 9733), // &YU -> &YUc + new Transition (9736, 9737), // &yu -> &yuc + new Transition (9747, 9761), // &Z -> &Zc + new Transition (9748, 9749), // &Za -> &Zac + new Transition (9754, 9767), // &z -> &zc + new Transition (9755, 9756), // &za -> &zac + new Transition (9801, 9802), // &ZeroWidthSpa -> &ZeroWidthSpac + new Transition (9817, 9818), // &ZH -> &ZHc + new Transition (9821, 9822), // &zh -> &zhc + new Transition (9840, 9841), // &Zs -> &Zsc + new Transition (9844, 9845) // &zs -> &zsc + }; + TransitionTable_d = new Transition[206] { + new Transition (0, 1432), // & -> &d + new Transition (27, 29), // &ac -> &acd + new Transition (116, 117), // &An -> &And + new Transition (119, 120), // &an -> &and + new Transition (120, 126), // &and -> &andd + new Transition (123, 124), // &andan -> &andand + new Transition (144, 145), // &angms -> &angmsd + new Transition (147, 154), // &angmsda -> &angmsdad + new Transition (168, 170), // &angrtvb -> &angrtvbd + new Transition (210, 211), // &api -> &apid + new Transition (271, 272), // &Atil -> &Atild + new Transition (277, 278), // &atil -> &atild + new Transition (301, 379), // &b -> &bd + new Transition (350, 351), // &Barwe -> &Barwed + new Transition (354, 355), // &barwe -> &barwed + new Transition (455, 456), // &bigo -> &bigod + new Transition (488, 489), // &bigtriangle -> &bigtriangled + new Transition (508, 509), // &bigwe -> &bigwed + new Transition (545, 547), // &blacktriangle -> &blacktriangled + new Transition (613, 623), // &box -> &boxd + new Transition (636, 642), // &boxH -> &boxHd + new Transition (638, 646), // &boxh -> &boxhd + new Transition (789, 907), // &C -> &Cd + new Transition (796, 911), // &c -> &cd + new Transition (805, 824), // &cap -> &capd + new Transition (808, 809), // &capan -> &capand + new Transition (876, 877), // &Cce -> &Cced + new Transition (881, 882), // &cce -> &cced + new Transition (915, 916), // &ce -> &ced + new Transition (920, 921), // &Ce -> &Ced + new Transition (945, 946), // ¢er -> ¢erd + new Transition (987, 1004), // &circle -> &circled + new Transition (1004, 1014), // &circled -> &circledd + new Transition (1060, 1061), // &cirmi -> &cirmid + new Transition (1165, 1167), // &cong -> &congd + new Transition (1207, 1208), // &copro -> &coprod + new Transition (1211, 1212), // &Copro -> &Coprod + new Transition (1287, 1288), // &ct -> &ctd + new Transition (1292, 1293), // &cu -> &cud + new Transition (1318, 1337), // &cup -> &cupd + new Transition (1372, 1373), // &curlywe -> &curlywed + new Transition (1404, 1405), // &cuwe -> &cuwed + new Transition (1432, 1492), // &d -> &dd + new Transition (1507, 1508), // &DDotrah -> &DDotrahd + new Transition (1595, 1596), // &DiacriticalTil -> &DiacriticalTild + new Transition (1605, 1606), // &Diamon -> &Diamond + new Transition (1609, 1610), // &diamon -> &diamond + new Transition (1645, 1646), // &divi -> &divid + new Transition (1701, 1703), // &doteq -> &doteqd + new Transition (1739, 1740), // &doublebarwe -> &doublebarwed + new Transition (1896, 1921), // &down -> &downd + new Transition (2067, 2068), // &dt -> &dtd + new Transition (2108, 2162), // &E -> &Ed + new Transition (2115, 2169), // &e -> &ed + new Transition (2198, 2200), // &egs -> &egsd + new Transition (2222, 2224), // &els -> &elsd + new Transition (2379, 2380), // &EqualTil -> &EqualTild + new Transition (2422, 2426), // &es -> &esd + new Transition (2509, 2510), // &falling -> &fallingd + new Transition (2557, 2558), // &Fille -> &Filled + new Transition (2701, 2759), // &g -> &gd + new Transition (2708, 2755), // &G -> &Gd + new Transition (2712, 2718), // &Gamma -> &Gammad + new Transition (2716, 2720), // &gamma -> &gammad + new Transition (2737, 2738), // &Gce -> &Gced + new Transition (2781, 2786), // &ges -> &gesd + new Transition (2919, 2920), // &GreaterTil -> &GreaterTild + new Transition (2942, 2950), // > -> >d + new Transition (2965, 2976), // >r -> >rd + new Transition (3041, 3042), // &har -> &hard + new Transition (3236, 3265), // &I -> &Id + new Transition (3369, 3370), // &impe -> &imped + new Transition (3393, 3394), // &ino -> &inod + new Transition (3441, 3442), // &intpro -> &intprod + new Transition (3494, 3495), // &ipro -> &iprod + new Transition (3512, 3514), // &isin -> &isind + new Transition (3530, 3531), // &Itil -> &Itild + new Transition (3535, 3536), // &itil -> &itild + new Transition (3633, 3634), // &Kce -> &Kced + new Transition (3639, 3640), // &kce -> &kced + new Transition (3692, 3869), // &l -> &ld + new Transition (3724, 3725), // &Lamb -> &Lambd + new Transition (3729, 3730), // &lamb -> &lambd + new Transition (3737, 3739), // &lang -> &langd + new Transition (3832, 3833), // &lbrksl -> &lbrksld + new Transition (3849, 3850), // &Lce -> &Lced + new Transition (3854, 3855), // &lce -> &lced + new Transition (3879, 3880), // &ldr -> &ldrd + new Transition (4010, 4011), // &leftharpoon -> &leftharpoond + new Transition (4197, 4202), // &les -> &lesd + new Transition (4215, 4223), // &less -> &lessd + new Transition (4297, 4298), // &LessTil -> &LessTild + new Transition (4327, 4328), // &lhar -> &lhard + new Transition (4372, 4373), // &llhar -> &llhard + new Transition (4380, 4381), // &Lmi -> &Lmid + new Transition (4386, 4387), // &lmi -> &lmid + new Transition (4642, 4644), // &lrhar -> &lrhard + new Transition (4698, 4706), // < -> <d + new Transition (4743, 4744), // &lur -> &lurd + new Transition (4767, 4820), // &m -> &md + new Transition (4789, 4791), // &mapsto -> &mapstod + new Transition (4835, 4836), // &measure -> &measured + new Transition (4843, 4844), // &Me -> &Med + new Transition (4871, 4876), // &mi -> &mid + new Transition (4876, 4886), // &mid -> &midd + new Transition (4892, 4896), // &minus -> &minusd + new Transition (4909, 4913), // &ml -> &mld + new Transition (4922, 4923), // &mo -> &mod + new Transition (4965, 5059), // &n -> &nd + new Transition (4990, 4991), // &napi -> &napid + new Transition (5034, 5035), // &Nce -> &Nced + new Transition (5039, 5040), // &nce -> &nced + new Transition (5046, 5048), // &ncong -> &ncongd + new Transition (5064, 5080), // &ne -> &ned + new Transition (5092, 5093), // &NegativeMe -> &NegativeMed + new Transition (5150, 5151), // &Neste -> &Nested + new Transition (5242, 5244), // &nis -> &nisd + new Transition (5256, 5265), // &nl -> &nld + new Transition (5344, 5345), // &nmi -> &nmid + new Transition (5429, 5430), // &NotEqualTil -> &NotEqualTild + new Transition (5489, 5490), // &NotGreaterTil -> &NotGreaterTild + new Transition (5513, 5515), // ¬in -> ¬ind + new Transition (5586, 5587), // &NotLessTil -> &NotLessTild + new Transition (5594, 5595), // &NotNeste -> &NotNested + new Transition (5634, 5635), // &NotPrece -> &NotPreced + new Transition (5741, 5742), // &NotSuccee -> &NotSucceed + new Transition (5764, 5765), // &NotSucceedsTil -> &NotSucceedsTild + new Transition (5783, 5784), // &NotTil -> &NotTild + new Transition (5805, 5806), // &NotTildeTil -> &NotTildeTild + new Transition (5915, 5916), // &nshortmi -> &nshortmid + new Transition (5935, 5936), // &nsmi -> &nsmid + new Transition (5994, 5995), // &Ntil -> &Ntild + new Transition (5999, 6000), // &ntil -> &ntild + new Transition (6043, 6063), // &nv -> &nvd + new Transition (6047, 6053), // &nV -> &nVd + new Transition (6131, 6168), // &O -> &Od + new Transition (6138, 6163), // &o -> &od + new Transition (6187, 6188), // &odsol -> &odsold + new Transition (6282, 6288), // &omi -> &omid + new Transition (6342, 6348), // &or -> &ord + new Transition (6401, 6402), // &Otil -> &Otild + new Transition (6407, 6408), // &otil -> &otild + new Transition (6504, 6505), // &perio -> &period + new Transition (6567, 6580), // &plus -> &plusd + new Transition (6637, 6638), // &poun -> £ + new Transition (6674, 6675), // &Prece -> &Preced + new Transition (6698, 6699), // &PrecedesTil -> &PrecedesTild + new Transition (6745, 6746), // &pro -> &prod + new Transition (6748, 6749), // &Pro -> &Prod + new Transition (6876, 7053), // &r -> &rd + new Transition (6882, 6897), // &ra -> &rad + new Transition (6912, 6914), // &rang -> &rangd + new Transition (7016, 7017), // &rbrksl -> &rbrksld + new Transition (7033, 7034), // &Rce -> &Rced + new Transition (7038, 7039), // &rce -> &rced + new Transition (7057, 7058), // &rdl -> &rdld + new Transition (7157, 7158), // &rhar -> &rhard + new Transition (7285, 7286), // &rightharpoon -> &rightharpoond + new Transition (7434, 7435), // &rising -> &risingd + new Transition (7466, 7467), // &rnmi -> &rnmid + new Transition (7502, 7503), // &Roun -> &Round + new Transition (7598, 7599), // &RuleDelaye -> &RuleDelayed + new Transition (7617, 7695), // &s -> &sd + new Transition (7651, 7658), // &sce -> &sced + new Transition (7653, 7654), // &Sce -> &Sced + new Transition (7800, 7801), // &shortmi -> &shortmid + new Transition (7847, 7849), // &sim -> &simd + new Transition (7918, 7919), // &smi -> &smid + new Transition (7957, 7958), // &spa -> &spad + new Transition (8131, 8133), // &sub -> &subd + new Transition (8139, 8141), // &sube -> &subed + new Transition (8219, 8220), // &Succee -> &Succeed + new Transition (8242, 8243), // &SucceedsTil -> &SucceedsTild + new Transition (8284, 8292), // &sup -> &supd + new Transition (8302, 8304), // &supe -> &suped + new Transition (8404, 8445), // &t -> &td + new Transition (8431, 8432), // &Tce -> &Tced + new Transition (8436, 8437), // &tce -> &tced + new Transition (8545, 8546), // &Til -> &Tild + new Transition (8550, 8551), // &til -> &tild + new Transition (8572, 8573), // &TildeTil -> &TildeTild + new Transition (8578, 8585), // × -> ×d + new Transition (8629, 8630), // &tra -> &trad + new Transition (8633, 8664), // &tri -> &trid + new Transition (8638, 8640), // &triangle -> &triangled + new Transition (8745, 8746), // &twohea -> &twohead + new Transition (8768, 8834), // &U -> &Ud + new Transition (8775, 8829), // &u -> &ud + new Transition (8916, 8917), // &Un -> &Und + new Transition (8970, 9014), // &Up -> &Upd + new Transition (8983, 9024), // &up -> &upd + new Transition (9161, 9162), // &ut -> &utd + new Transition (9168, 9169), // &Util -> &Utild + new Transition (9173, 9174), // &util -> &utild + new Transition (9201, 9335), // &v -> &vd + new Transition (9303, 9325), // &V -> &Vd + new Transition (9399, 9400), // &VerticalTil -> &VerticalTild + new Transition (9471, 9472), // &Vv -> &Vvd + new Transition (9496, 9497), // &we -> &wed + new Transition (9502, 9503), // &We -> &Wed + new Transition (9548, 9560), // &x -> &xd + new Transition (9602, 9603), // &xo -> &xod + new Transition (9660, 9661), // &xwe -> &xwed + new Transition (9747, 9777), // &Z -> &Zd + new Transition (9754, 9781), // &z -> &zd + new Transition (9795, 9796) // &ZeroWi -> &ZeroWid + }; + TransitionTable_e = new Transition[674] { + new Transition (0, 2115), // & -> &e + new Transition (5, 6), // &Aacut -> Á + new Transition (8, 55), // &a -> &ae + new Transition (12, 13), // &aacut -> á + new Transition (16, 17), // &Abr -> &Abre + new Transition (18, 19), // &Abrev -> &Abreve + new Transition (22, 23), // &abr -> &abre + new Transition (24, 25), // &abrev -> &abreve + new Transition (43, 44), // &acut -> ´ + new Transition (70, 71), // &Agrav -> À + new Transition (76, 77), // &agrav -> à + new Transition (79, 80), // &al -> &ale + new Transition (131, 132), // &andslop -> &andslope + new Transition (136, 138), // &ang -> &ange + new Transition (140, 141), // &angl -> &angle + new Transition (147, 156), // &angmsda -> &angmsdae + new Transition (199, 208), // &ap -> &ape + new Transition (232, 234), // &approx -> &approxe + new Transition (264, 266), // &asymp -> &asympe + new Transition (272, 273), // &Atild -> à + new Transition (278, 279), // &atild -> ã + new Transition (301, 384), // &b -> &be + new Transition (304, 310), // &back -> &backe + new Transition (321, 322), // &backprim -> &backprime + new Transition (326, 328), // &backsim -> &backsime + new Transition (331, 390), // &B -> &Be + new Transition (345, 346), // &barv -> &barve + new Transition (346, 347), // &barve -> &barvee + new Transition (349, 350), // &Barw -> &Barwe + new Transition (353, 354), // &barw -> &barwe + new Transition (357, 358), // &barwedg -> &barwedge + new Transition (388, 397), // &becaus -> &because + new Transition (394, 395), // &Becaus -> &Because + new Transition (431, 432), // &betw -> &betwe + new Transition (432, 433), // &betwe -> &betwee + new Transition (467, 468), // &bigotim -> &bigotime + new Transition (487, 488), // &bigtriangl -> &bigtriangle + new Transition (503, 504), // &bigv -> &bigve + new Transition (504, 505), // &bigve -> &bigvee + new Transition (507, 508), // &bigw -> &bigwe + new Transition (510, 511), // &bigwedg -> &bigwedge + new Transition (525, 526), // &blackloz -> &blackloze + new Transition (528, 529), // &blacklozeng -> &blacklozenge + new Transition (535, 536), // &blacksquar -> &blacksquare + new Transition (544, 545), // &blacktriangl -> &blacktriangle + new Transition (552, 553), // &blacktrianglel -> &blacktrianglele + new Transition (579, 580), // &bn -> &bne + new Transition (610, 611), // &bowti -> &bowtie + new Transition (669, 670), // &boxtim -> &boxtime + new Transition (722, 723), // &bprim -> &bprime + new Transition (725, 726), // &Br -> &Bre + new Transition (727, 728), // &Brev -> &Breve + new Transition (730, 731), // &br -> &bre + new Transition (732, 733), // &brev -> &breve + new Transition (744, 748), // &bs -> &bse + new Transition (753, 755), // &bsim -> &bsime + new Transition (769, 771), // &bull -> &bulle + new Transition (775, 779), // &bump -> &bumpe + new Transition (783, 784), // &Bump -> &Bumpe + new Transition (789, 920), // &C -> &Ce + new Transition (793, 794), // &Cacut -> &Cacute + new Transition (796, 915), // &c -> &ce + new Transition (800, 801), // &cacut -> &cacute + new Transition (835, 836), // &CapitalDiff -> &CapitalDiffe + new Transition (837, 838), // &CapitalDiffer -> &CapitalDiffere + new Transition (848, 849), // &car -> &care + new Transition (856, 857), // &Cayl -> &Cayle + new Transition (861, 881), // &cc -> &cce + new Transition (866, 876), // &Cc -> &Cce + new Transition (934, 944), // ¢ -> ¢e + new Transition (937, 938), // &Cent -> &Cente + new Transition (960, 964), // &ch -> &che + new Transition (979, 1051), // &cir -> &cire + new Transition (981, 983), // &circ -> &circe + new Transition (986, 987), // &circl -> &circle + new Transition (993, 994), // &circlearrowl -> &circlearrowle + new Transition (1022, 1023), // &Circl -> &Circle + new Transition (1045, 1046), // &CircleTim -> &CircleTime + new Transition (1074, 1075), // &Clockwis -> &Clockwise + new Transition (1085, 1086), // &ClockwiseContourInt -> &ClockwiseContourInte + new Transition (1092, 1093), // &Clos -> &Close + new Transition (1103, 1104), // &CloseCurlyDoubl -> &CloseCurlyDouble + new Transition (1108, 1109), // &CloseCurlyDoubleQuot -> &CloseCurlyDoubleQuote + new Transition (1114, 1115), // &CloseCurlyQuot -> &CloseCurlyQuote + new Transition (1129, 1136), // &Colon -> &Colone + new Transition (1134, 1138), // &colon -> &colone + new Transition (1153, 1154), // &compl -> &comple + new Transition (1155, 1156), // &complem -> &compleme + new Transition (1160, 1161), // &complex -> &complexe + new Transition (1174, 1175), // &Congru -> &Congrue + new Transition (1193, 1194), // &ContourInt -> &ContourInte + new Transition (1228, 1229), // &Count -> &Counte + new Transition (1238, 1239), // &CounterClockwis -> &CounterClockwise + new Transition (1249, 1250), // &CounterClockwiseContourInt -> &CounterClockwiseContourInte + new Transition (1279, 1281), // &csub -> &csube + new Transition (1283, 1285), // &csup -> &csupe + new Transition (1292, 1301), // &cu -> &cue + new Transition (1354, 1355), // &curly -> &curlye + new Transition (1358, 1359), // &curlyeqpr -> &curlyeqpre + new Transition (1367, 1368), // &curlyv -> &curlyve + new Transition (1368, 1369), // &curlyve -> &curlyvee + new Transition (1371, 1372), // &curlyw -> &curlywe + new Transition (1374, 1375), // &curlywedg -> &curlywedge + new Transition (1377, 1378), // &curr -> &curre + new Transition (1381, 1382), // &curv -> &curve + new Transition (1388, 1389), // &curvearrowl -> &curvearrowle + new Transition (1399, 1400), // &cuv -> &cuve + new Transition (1400, 1401), // &cuve -> &cuvee + new Transition (1403, 1404), // &cuw -> &cuwe + new Transition (1425, 1519), // &D -> &De + new Transition (1428, 1429), // &Dagg -> &Dagge + new Transition (1432, 1516), // &d -> &de + new Transition (1435, 1436), // &dagg -> &dagge + new Transition (1439, 1440), // &dal -> &dale + new Transition (1496, 1497), // &ddagg -> &ddagge + new Transition (1512, 1513), // &ddots -> &ddotse + new Transition (1570, 1571), // &DiacriticalAcut -> &DiacriticalAcute + new Transition (1579, 1580), // &DiacriticalDoubl -> &DiacriticalDouble + new Transition (1584, 1585), // &DiacriticalDoubleAcut -> &DiacriticalDoubleAcute + new Transition (1590, 1591), // &DiacriticalGrav -> &DiacriticalGrave + new Transition (1596, 1597), // &DiacriticalTild -> &DiacriticalTilde + new Transition (1599, 1619), // &di -> &die + new Transition (1622, 1623), // &Diff -> &Diffe + new Transition (1624, 1625), // &Differ -> &Differe + new Transition (1646, 1647), // &divid -> ÷ + new Transition (1653, 1654), // ÷ontim -> ÷ontime + new Transition (1694, 1700), // &dot -> &dote + new Transition (1728, 1729), // &dotsquar -> &dotsquare + new Transition (1733, 1734), // &doubl -> &double + new Transition (1738, 1739), // &doublebarw -> &doublebarwe + new Transition (1741, 1742), // &doublebarwedg -> &doublebarwedge + new Transition (1746, 1747), // &Doubl -> &Double + new Transition (1757, 1758), // &DoubleContourInt -> &DoubleContourInte + new Transition (1776, 1777), // &DoubleL -> &DoubleLe + new Transition (1797, 1798), // &DoubleLeftT -> &DoubleLeftTe + new Transition (1798, 1799), // &DoubleLeftTe -> &DoubleLeftTee + new Transition (1804, 1805), // &DoubleLongL -> &DoubleLongLe + new Transition (1847, 1848), // &DoubleRightT -> &DoubleRightTe + new Transition (1848, 1849), // &DoubleRightTe -> &DoubleRightTee + new Transition (1869, 1870), // &DoubleV -> &DoubleVe + new Transition (1916, 1917), // &DownBr -> &DownBre + new Transition (1918, 1919), // &DownBrev -> &DownBreve + new Transition (1939, 1940), // &downharpoonl -> &downharpoonle + new Transition (1950, 1951), // &DownL -> &DownLe + new Transition (1959, 1960), // &DownLeftRightV -> &DownLeftRightVe + new Transition (1966, 1967), // &DownLeftT -> &DownLeftTe + new Transition (1967, 1968), // &DownLeftTe -> &DownLeftTee + new Transition (1969, 1970), // &DownLeftTeeV -> &DownLeftTeeVe + new Transition (1976, 1977), // &DownLeftV -> &DownLeftVe + new Transition (1992, 1993), // &DownRightT -> &DownRightTe + new Transition (1993, 1994), // &DownRightTe -> &DownRightTee + new Transition (1995, 1996), // &DownRightTeeV -> &DownRightTeeVe + new Transition (2002, 2003), // &DownRightV -> &DownRightVe + new Transition (2013, 2014), // &DownT -> &DownTe + new Transition (2014, 2015), // &DownTe -> &DownTee + new Transition (2090, 2091), // &dwangl -> &dwangle + new Transition (2112, 2113), // &Eacut -> É + new Transition (2115, 2173), // &e -> &ee + new Transition (2119, 2120), // &eacut -> é + new Transition (2123, 2124), // &east -> &easte + new Transition (2190, 2191), // &Egrav -> È + new Transition (2195, 2196), // &egrav -> è + new Transition (2206, 2207), // &El -> &Ele + new Transition (2208, 2209), // &Elem -> &Eleme + new Transition (2215, 2216), // &elint -> &elinte + new Transition (2242, 2243), // &emptys -> &emptyse + new Transition (2258, 2259), // &EmptySmallSquar -> &EmptySmallSquare + new Transition (2263, 2264), // &EmptyV -> &EmptyVe + new Transition (2276, 2277), // &EmptyVerySmallSquar -> &EmptyVerySmallSquare + new Transition (2362, 2363), // &eqslantl -> &eqslantle + new Transition (2372, 2383), // &equ -> &eque + new Transition (2380, 2381), // &EqualTild -> &EqualTilde + new Transition (2472, 2473), // &exp -> &expe + new Transition (2484, 2485), // &Expon -> &Expone + new Transition (2494, 2495), // &expon -> &expone + new Transition (2500, 2501), // &exponential -> &exponentiale + new Transition (2503, 2524), // &f -> &fe + new Transition (2513, 2514), // &fallingdots -> &fallingdotse + new Transition (2527, 2528), // &femal -> &female + new Transition (2556, 2557), // &Fill -> &Fille + new Transition (2568, 2569), // &FilledSmallSquar -> &FilledSmallSquare + new Transition (2571, 2572), // &FilledV -> &FilledVe + new Transition (2584, 2585), // &FilledVerySmallSquar -> &FilledVerySmallSquare + new Transition (2632, 2633), // &Fouri -> &Fourie + new Transition (2701, 2765), // &g -> &ge + new Transition (2705, 2706), // &gacut -> &gacute + new Transition (2725, 2726), // &Gbr -> &Gbre + new Transition (2727, 2728), // &Gbrev -> &Gbreve + new Transition (2731, 2732), // &gbr -> &gbre + new Transition (2733, 2734), // &gbrev -> &gbreve + new Transition (2736, 2737), // &Gc -> &Gce + new Transition (2794, 2796), // &gesl -> &gesle + new Transition (2812, 2813), // &gim -> &gime + new Transition (2832, 2843), // &gn -> &gne + new Transition (2863, 2864), // &grav -> &grave + new Transition (2866, 2867), // &Gr -> &Gre + new Transition (2869, 2870), // &Great -> &Greate + new Transition (2878, 2879), // &GreaterEqualL -> &GreaterEqualLe + new Transition (2894, 2895), // &GreaterGr -> &GreaterGre + new Transition (2897, 2898), // &GreaterGreat -> &GreaterGreate + new Transition (2901, 2902), // &GreaterL -> &GreaterLe + new Transition (2920, 2921), // &GreaterTild -> &GreaterTilde + new Transition (2932, 2934), // &gsim -> &gsime + new Transition (2960, 2961), // >qu -> >que + new Transition (2965, 2980), // >r -> >re + new Transition (2982, 2983), // >reql -> >reqle + new Transition (2988, 2989), // >reqql -> >reqqle + new Transition (2993, 2994), // >rl -> >rle + new Transition (3002, 3003), // &gv -> &gve + new Transition (3006, 3007), // &gvertn -> &gvertne + new Transition (3016, 3017), // &Hac -> &Hace + new Transition (3020, 3074), // &h -> &he + new Transition (3102, 3103), // &Hilb -> &Hilbe + new Transition (3109, 3110), // &HilbertSpac -> &HilbertSpace + new Transition (3113, 3114), // &hks -> &hkse + new Transition (3138, 3139), // &hookl -> &hookle + new Transition (3181, 3182), // &HorizontalLin -> &HorizontalLine + new Transition (3232, 3233), // &hyph -> &hyphe + new Transition (3240, 3241), // &Iacut -> Í + new Transition (3243, 3273), // &i -> &ie + new Transition (3247, 3248), // &iacut -> í + new Transition (3292, 3293), // &Igrav -> Ì + new Transition (3298, 3299), // &igrav -> ì + new Transition (3341, 3342), // &imag -> &image + new Transition (3354, 3355), // &imaglin -> &imagline + new Transition (3368, 3369), // &imp -> &impe + new Transition (3374, 3375), // &Impli -> &Implie + new Transition (3382, 3383), // &incar -> &incare + new Transition (3390, 3391), // &infinti -> &infintie + new Transition (3399, 3413), // &Int -> &Inte + new Transition (3401, 3407), // &int -> &inte + new Transition (3408, 3409), // &integ -> &intege + new Transition (3425, 3426), // &Inters -> &Interse + new Transition (3449, 3450), // &Invisibl -> &Invisible + new Transition (3459, 3460), // &InvisibleTim -> &InvisibleTime + new Transition (3498, 3499), // &iqu -> &ique + new Transition (3531, 3532), // &Itild -> &Itilde + new Transition (3536, 3537), // &itild -> &itilde + new Transition (3590, 3598), // &Js -> &Jse + new Transition (3594, 3603), // &js -> &jse + new Transition (3632, 3633), // &Kc -> &Kce + new Transition (3638, 3639), // &kc -> &kce + new Transition (3655, 3656), // &kgr -> &kgre + new Transition (3656, 3657), // &kgre -> &kgree + new Transition (3692, 3896), // &l -> &le + new Transition (3698, 3898), // &L -> &Le + new Transition (3702, 3703), // &Lacut -> &Lacute + new Transition (3705, 3711), // &la -> &lae + new Transition (3708, 3709), // &lacut -> &lacute + new Transition (3741, 3742), // &langl -> &langle + new Transition (3749, 3750), // &Laplac -> &Laplace + new Transition (3792, 3803), // &lat -> &late + new Transition (3823, 3824), // &lbrac -> &lbrace + new Transition (3828, 3829), // &lbrk -> &lbrke + new Transition (3837, 3849), // &Lc -> &Lce + new Transition (3843, 3854), // &lc -> &lce + new Transition (3904, 3905), // &LeftAngl -> &LeftAngle + new Transition (3910, 3911), // &LeftAngleBrack -> &LeftAngleBracke + new Transition (3953, 3954), // &LeftC -> &LeftCe + new Transition (3965, 3966), // &LeftDoubl -> &LeftDouble + new Transition (3971, 3972), // &LeftDoubleBrack -> &LeftDoubleBracke + new Transition (3977, 3978), // &LeftDownT -> &LeftDownTe + new Transition (3978, 3979), // &LeftDownTe -> &LeftDownTee + new Transition (3980, 3981), // &LeftDownTeeV -> &LeftDownTeeVe + new Transition (3987, 3988), // &LeftDownV -> &LeftDownVe + new Transition (4019, 4020), // &leftl -> &leftle + new Transition (4085, 4086), // &LeftRightV -> &LeftRightVe + new Transition (4092, 4093), // &LeftT -> &LeftTe + new Transition (4093, 4094), // &LeftTe -> &LeftTee + new Transition (4102, 4103), // &LeftTeeV -> &LeftTeeVe + new Transition (4111, 4112), // &leftthr -> &leftthre + new Transition (4112, 4113), // &leftthre -> &leftthree + new Transition (4116, 4117), // &leftthreetim -> &leftthreetime + new Transition (4125, 4126), // &LeftTriangl -> &LeftTriangle + new Transition (4144, 4145), // &LeftUpDownV -> &LeftUpDownVe + new Transition (4151, 4152), // &LeftUpT -> &LeftUpTe + new Transition (4152, 4153), // &LeftUpTe -> &LeftUpTee + new Transition (4154, 4155), // &LeftUpTeeV -> &LeftUpTeeVe + new Transition (4161, 4162), // &LeftUpV -> &LeftUpVe + new Transition (4172, 4173), // &LeftV -> &LeftVe + new Transition (4210, 4212), // &lesg -> &lesge + new Transition (4215, 4227), // &less -> &lesse + new Transition (4246, 4247), // &LessEqualGr -> &LessEqualGre + new Transition (4249, 4250), // &LessEqualGreat -> &LessEqualGreate + new Transition (4264, 4265), // &LessGr -> &LessGre + new Transition (4267, 4268), // &LessGreat -> &LessGreate + new Transition (4275, 4276), // &LessL -> &LessLe + new Transition (4298, 4299), // &LessTild -> &LessTilde + new Transition (4346, 4361), // &Ll -> &Lle + new Transition (4357, 4358), // &llcorn -> &llcorne + new Transition (4398, 4399), // &lmoustach -> &lmoustache + new Transition (4401, 4412), // &ln -> &lne + new Transition (4437, 4438), // &LongL -> &LongLe + new Transition (4447, 4448), // &Longl -> &Longle + new Transition (4459, 4460), // &longl -> &longle + new Transition (4549, 4550), // &looparrowl -> &looparrowle + new Transition (4575, 4576), // &lotim -> &lotime + new Transition (4588, 4589), // &Low -> &Lowe + new Transition (4591, 4592), // &LowerL -> &LowerLe + new Transition (4612, 4614), // &loz -> &loze + new Transition (4616, 4617), // &lozeng -> &lozenge + new Transition (4636, 4637), // &lrcorn -> &lrcorne + new Transition (4670, 4672), // &lsim -> &lsime + new Transition (4711, 4712), // <hr -> <hre + new Transition (4712, 4713), // <hre -> <hree + new Transition (4716, 4717), // <im -> <ime + new Transition (4726, 4727), // <qu -> <que + new Transition (4732, 4734), // <ri -> <rie + new Transition (4755, 4756), // &lv -> &lve + new Transition (4759, 4760), // &lvertn -> &lvertne + new Transition (4767, 4830), // &m -> &me + new Transition (4772, 4773), // &mal -> &male + new Transition (4775, 4777), // &malt -> &malte + new Transition (4778, 4779), // &maltes -> &maltese + new Transition (4781, 4843), // &M -> &Me + new Transition (4796, 4797), // &mapstol -> &mapstole + new Transition (4805, 4806), // &mark -> &marke + new Transition (4834, 4835), // &measur -> &measure + new Transition (4840, 4841), // &measuredangl -> &measuredangle + new Transition (4851, 4852), // &MediumSpac -> &MediumSpace + new Transition (4923, 4924), // &mod -> &mode + new Transition (4965, 5064), // &n -> &ne + new Transition (4971, 5084), // &N -> &Ne + new Transition (4975, 4976), // &Nacut -> &Nacute + new Transition (4980, 4981), // &nacut -> &nacute + new Transition (5016, 5018), // &nbump -> &nbumpe + new Transition (5020, 5039), // &nc -> &nce + new Transition (5024, 5034), // &Nc -> &Nce + new Transition (5089, 5090), // &Negativ -> &Negative + new Transition (5091, 5092), // &NegativeM -> &NegativeMe + new Transition (5100, 5101), // &NegativeMediumSpac -> &NegativeMediumSpace + new Transition (5111, 5112), // &NegativeThickSpac -> &NegativeThickSpace + new Transition (5118, 5119), // &NegativeThinSpac -> &NegativeThinSpace + new Transition (5121, 5122), // &NegativeV -> &NegativeVe + new Transition (5132, 5133), // &NegativeVeryThinSpac -> &NegativeVeryThinSpace + new Transition (5140, 5141), // &nes -> &nese + new Transition (5149, 5150), // &Nest -> &Neste + new Transition (5153, 5154), // &NestedGr -> &NestedGre + new Transition (5156, 5157), // &NestedGreat -> &NestedGreate + new Transition (5160, 5161), // &NestedGreaterGr -> &NestedGreaterGre + new Transition (5163, 5164), // &NestedGreaterGreat -> &NestedGreaterGreate + new Transition (5167, 5168), // &NestedL -> &NestedLe + new Transition (5171, 5172), // &NestedLessL -> &NestedLessLe + new Transition (5179, 5180), // &NewLin -> &NewLine + new Transition (5195, 5198), // &ng -> &nge + new Transition (5256, 5270), // &nl -> &nle + new Transition (5272, 5273), // &nL -> &nLe + new Transition (5337, 5339), // &nltri -> &nltrie + new Transition (5349, 5350), // &NoBr -> &NoBre + new Transition (5356, 5357), // &NonBr -> &NonBre + new Transition (5366, 5367), // &NonBreakingSpac -> &NonBreakingSpace + new Transition (5385, 5386), // &NotCongru -> &NotCongrue + new Transition (5400, 5401), // &NotDoubl -> &NotDouble + new Transition (5402, 5403), // &NotDoubleV -> &NotDoubleVe + new Transition (5415, 5416), // &NotEl -> &NotEle + new Transition (5417, 5418), // &NotElem -> &NotEleme + new Transition (5430, 5431), // &NotEqualTild -> &NotEqualTilde + new Transition (5440, 5441), // &NotGr -> &NotGre + new Transition (5443, 5444), // &NotGreat -> &NotGreate + new Transition (5464, 5465), // &NotGreaterGr -> &NotGreaterGre + new Transition (5467, 5468), // &NotGreaterGreat -> &NotGreaterGreate + new Transition (5471, 5472), // &NotGreaterL -> &NotGreaterLe + new Transition (5490, 5491), // &NotGreaterTild -> &NotGreaterTilde + new Transition (5528, 5529), // &NotL -> &NotLe + new Transition (5538, 5539), // &NotLeftTriangl -> &NotLeftTriangle + new Transition (5561, 5562), // &NotLessGr -> &NotLessGre + new Transition (5564, 5565), // &NotLessGreat -> &NotLessGreate + new Transition (5568, 5569), // &NotLessL -> &NotLessLe + new Transition (5587, 5588), // &NotLessTild -> &NotLessTilde + new Transition (5590, 5591), // &NotN -> &NotNe + new Transition (5593, 5594), // &NotNest -> &NotNeste + new Transition (5597, 5598), // &NotNestedGr -> &NotNestedGre + new Transition (5600, 5601), // &NotNestedGreat -> &NotNestedGreate + new Transition (5604, 5605), // &NotNestedGreaterGr -> &NotNestedGreaterGre + new Transition (5607, 5608), // &NotNestedGreaterGreat -> &NotNestedGreaterGreate + new Transition (5611, 5612), // &NotNestedL -> &NotNestedLe + new Transition (5615, 5616), // &NotNestedLessL -> &NotNestedLessLe + new Transition (5631, 5632), // &NotPr -> &NotPre + new Transition (5633, 5634), // &NotPrec -> &NotPrece + new Transition (5635, 5636), // &NotPreced -> &NotPrecede + new Transition (5656, 5657), // &NotR -> &NotRe + new Transition (5658, 5659), // &NotRev -> &NotReve + new Transition (5661, 5662), // &NotRevers -> &NotReverse + new Transition (5664, 5665), // &NotReverseEl -> &NotReverseEle + new Transition (5666, 5667), // &NotReverseElem -> &NotReverseEleme + new Transition (5681, 5682), // &NotRightTriangl -> &NotRightTriangle + new Transition (5698, 5699), // &NotSquar -> &NotSquare + new Transition (5703, 5704), // &NotSquareSubs -> &NotSquareSubse + new Transition (5713, 5714), // &NotSquareSup -> &NotSquareSupe + new Transition (5716, 5717), // &NotSquareSupers -> &NotSquareSuperse + new Transition (5728, 5729), // &NotSubs -> &NotSubse + new Transition (5739, 5740), // &NotSucc -> &NotSucce + new Transition (5740, 5741), // &NotSucce -> &NotSuccee + new Transition (5765, 5766), // &NotSucceedsTild -> &NotSucceedsTilde + new Transition (5768, 5769), // &NotSup -> &NotSupe + new Transition (5771, 5772), // &NotSupers -> &NotSuperse + new Transition (5784, 5785), // &NotTild -> &NotTilde + new Transition (5806, 5807), // &NotTildeTild -> &NotTildeTilde + new Transition (5809, 5810), // &NotV -> &NotVe + new Transition (5827, 5828), // &nparall -> &nparalle + new Transition (5842, 5848), // &npr -> &npre + new Transition (5845, 5846), // &nprcu -> &nprcue + new Transition (5850, 5852), // &nprec -> &nprece + new Transition (5891, 5893), // &nrtri -> &nrtrie + new Transition (5896, 5902), // &nsc -> &nsce + new Transition (5899, 5900), // &nsccu -> &nsccue + new Transition (5923, 5924), // &nshortparall -> &nshortparalle + new Transition (5928, 5930), // &nsim -> &nsime + new Transition (5945, 5946), // &nsqsub -> &nsqsube + new Transition (5948, 5949), // &nsqsup -> &nsqsupe + new Transition (5952, 5956), // &nsub -> &nsube + new Transition (5958, 5959), // &nsubs -> &nsubse + new Transition (5960, 5962), // &nsubset -> &nsubsete + new Transition (5968, 5970), // &nsucc -> &nsucce + new Transition (5973, 5977), // &nsup -> &nsupe + new Transition (5979, 5980), // &nsups -> &nsupse + new Transition (5981, 5983), // &nsupset -> &nsupsete + new Transition (5995, 5996), // &Ntild -> Ñ + new Transition (6000, 6001), // &ntild -> ñ + new Transition (6011, 6012), // &ntriangl -> &ntriangle + new Transition (6013, 6014), // &ntrianglel -> &ntrianglele + new Transition (6016, 6018), // &ntriangleleft -> &ntrianglelefte + new Transition (6025, 6027), // &ntriangleright -> &ntrianglerighte + new Transition (6034, 6036), // &num -> &nume + new Transition (6068, 6069), // &nvg -> &nvge + new Transition (6084, 6089), // &nvl -> &nvle + new Transition (6094, 6095), // &nvltri -> &nvltrie + new Transition (6104, 6105), // &nvrtri -> &nvrtrie + new Transition (6126, 6127), // &nwn -> &nwne + new Transition (6135, 6136), // &Oacut -> Ó + new Transition (6138, 6195), // &o -> &oe + new Transition (6142, 6143), // &oacut -> ó + new Transition (6217, 6218), // &Ograv -> Ò + new Transition (6222, 6223), // &ograv -> ò + new Transition (6253, 6254), // &olin -> &oline + new Transition (6258, 6268), // &Om -> &Ome + new Transition (6263, 6272), // &om -> &ome + new Transition (6302, 6332), // &op -> &ope + new Transition (6306, 6307), // &Op -> &Ope + new Transition (6318, 6319), // &OpenCurlyDoubl -> &OpenCurlyDouble + new Transition (6323, 6324), // &OpenCurlyDoubleQuot -> &OpenCurlyDoubleQuote + new Transition (6329, 6330), // &OpenCurlyQuot -> &OpenCurlyQuote + new Transition (6348, 6350), // &ord -> &orde + new Transition (6371, 6372), // &orslop -> &orslope + new Transition (6402, 6403), // &Otild -> Õ + new Transition (6408, 6409), // &otild -> õ + new Transition (6411, 6412), // &Otim -> &Otime + new Transition (6415, 6416), // &otim -> &otime + new Transition (6435, 6436), // &Ov -> &Ove + new Transition (6444, 6445), // &OverBrac -> &OverBrace + new Transition (6447, 6448), // &OverBrack -> &OverBracke + new Transition (6453, 6454), // &OverPar -> &OverPare + new Transition (6457, 6458), // &OverParenth -> &OverParenthe + new Transition (6463, 6497), // &p -> &pe + new Transition (6470, 6471), // ¶ll -> ¶lle + new Transition (6513, 6514), // &pert -> &perte + new Transition (6538, 6539), // &phon -> &phone + new Transition (6567, 6585), // &plus -> &pluse + new Transition (6614, 6615), // &Poincar -> &Poincare + new Transition (6619, 6620), // &Poincareplan -> &Poincareplane + new Transition (6640, 6672), // &Pr -> &Pre + new Transition (6642, 6653), // &pr -> &pre + new Transition (6648, 6649), // &prcu -> &prcue + new Transition (6655, 6702), // &prec -> &prece + new Transition (6668, 6669), // &preccurly -> &preccurlye + new Transition (6673, 6674), // &Prec -> &Prece + new Transition (6675, 6676), // &Preced -> &Precede + new Transition (6699, 6700), // &PrecedesTild -> &PrecedesTilde + new Transition (6705, 6713), // &precn -> &precne + new Transition (6726, 6727), // &Prim -> &Prime + new Transition (6730, 6731), // &prim -> &prime + new Transition (6762, 6763), // &proflin -> &profline + new Transition (6791, 6792), // &prur -> &prure + new Transition (6836, 6837), // &qprim -> &qprime + new Transition (6847, 6862), // &qu -> &que + new Transition (6849, 6850), // &quat -> &quate + new Transition (6864, 6866), // &quest -> &queste + new Transition (6876, 7074), // &r -> &re + new Transition (6882, 6901), // &ra -> &rae + new Transition (6883, 6884), // &rac -> &race + new Transition (6886, 7072), // &R -> &Re + new Transition (6890, 6891), // &Racut -> &Racute + new Transition (6894, 6895), // &racut -> &racute + new Transition (6912, 6916), // &rang -> &range + new Transition (6918, 6919), // &rangl -> &rangle + new Transition (7007, 7008), // &rbrac -> &rbrace + new Transition (7012, 7013), // &rbrk -> &rbrke + new Transition (7021, 7033), // &Rc -> &Rce + new Transition (7027, 7038), // &rc -> &rce + new Transition (7079, 7080), // &realin -> &realine + new Transition (7097, 7098), // &Rev -> &Reve + new Transition (7100, 7101), // &Revers -> &Reverse + new Transition (7103, 7104), // &ReverseEl -> &ReverseEle + new Transition (7105, 7106), // &ReverseElem -> &ReverseEleme + new Transition (7178, 7179), // &RightAngl -> &RightAngle + new Transition (7184, 7185), // &RightAngleBrack -> &RightAngleBracke + new Transition (7213, 7214), // &RightArrowL -> &RightArrowLe + new Transition (7228, 7229), // &RightC -> &RightCe + new Transition (7240, 7241), // &RightDoubl -> &RightDouble + new Transition (7246, 7247), // &RightDoubleBrack -> &RightDoubleBracke + new Transition (7252, 7253), // &RightDownT -> &RightDownTe + new Transition (7253, 7254), // &RightDownTe -> &RightDownTee + new Transition (7255, 7256), // &RightDownTeeV -> &RightDownTeeVe + new Transition (7262, 7263), // &RightDownV -> &RightDownVe + new Transition (7294, 7295), // &rightl -> &rightle + new Transition (7337, 7338), // &RightT -> &RightTe + new Transition (7338, 7339), // &RightTe -> &RightTee + new Transition (7347, 7348), // &RightTeeV -> &RightTeeVe + new Transition (7356, 7357), // &rightthr -> &rightthre + new Transition (7357, 7358), // &rightthre -> &rightthree + new Transition (7361, 7362), // &rightthreetim -> &rightthreetime + new Transition (7370, 7371), // &RightTriangl -> &RightTriangle + new Transition (7389, 7390), // &RightUpDownV -> &RightUpDownVe + new Transition (7396, 7397), // &RightUpT -> &RightUpTe + new Transition (7397, 7398), // &RightUpTe -> &RightUpTee + new Transition (7399, 7400), // &RightUpTeeV -> &RightUpTeeVe + new Transition (7406, 7407), // &RightUpV -> &RightUpVe + new Transition (7417, 7418), // &RightV -> &RightVe + new Transition (7438, 7439), // &risingdots -> &risingdotse + new Transition (7461, 7462), // &rmoustach -> &rmoustache + new Transition (7497, 7498), // &rotim -> &rotime + new Transition (7508, 7509), // &RoundImpli -> &RoundImplie + new Transition (7569, 7570), // &rthr -> &rthre + new Transition (7570, 7571), // &rthre -> &rthree + new Transition (7574, 7575), // &rtim -> &rtime + new Transition (7579, 7581), // &rtri -> &rtrie + new Transition (7591, 7592), // &Rul -> &Rule + new Transition (7593, 7594), // &RuleD -> &RuleDe + new Transition (7597, 7598), // &RuleDelay -> &RuleDelaye + new Transition (7614, 7615), // &Sacut -> &Sacute + new Transition (7617, 7703), // &s -> &se + new Transition (7621, 7622), // &sacut -> &sacute + new Transition (7629, 7653), // &Sc -> &Sce + new Transition (7631, 7651), // &sc -> &sce + new Transition (7646, 7647), // &sccu -> &sccue + new Transition (7697, 7701), // &sdot -> &sdote + new Transition (7786, 7787), // &ShortL -> &ShortLe + new Transition (7808, 7809), // &shortparall -> &shortparalle + new Transition (7847, 7853), // &sim -> &sime + new Transition (7865, 7866), // &simn -> &simne + new Transition (7891, 7892), // &SmallCircl -> &SmallCircle + new Transition (7894, 7911), // &sm -> &sme + new Transition (7898, 7899), // &smalls -> &smallse + new Transition (7921, 7922), // &smil -> &smile + new Transition (7924, 7926), // &smt -> &smte + new Transition (7958, 7959), // &spad -> &spade + new Transition (7986, 7988), // &sqsub -> &sqsube + new Transition (7990, 7991), // &sqsubs -> &sqsubse + new Transition (7992, 7994), // &sqsubset -> &sqsubsete + new Transition (7997, 7999), // &sqsup -> &sqsupe + new Transition (8001, 8002), // &sqsups -> &sqsupse + new Transition (8003, 8005), // &sqsupset -> &sqsupsete + new Transition (8012, 8013), // &Squar -> &Square + new Transition (8016, 8017), // &squar -> &square + new Transition (8021, 8022), // &SquareInt -> &SquareInte + new Transition (8024, 8025), // &SquareInters -> &SquareInterse + new Transition (8035, 8036), // &SquareSubs -> &SquareSubse + new Transition (8045, 8046), // &SquareSup -> &SquareSupe + new Transition (8048, 8049), // &SquareSupers -> &SquareSuperse + new Transition (8077, 8081), // &ss -> &sse + new Transition (8088, 8089), // &ssmil -> &ssmile + new Transition (8111, 8112), // &straight -> &straighte + new Transition (8131, 8139), // &sub -> &sube + new Transition (8150, 8153), // &subn -> &subne + new Transition (8165, 8166), // &Subs -> &Subse + new Transition (8169, 8170), // &subs -> &subse + new Transition (8171, 8173), // &subset -> &subsete + new Transition (8184, 8185), // &subsetn -> &subsetne + new Transition (8199, 8246), // &succ -> &succe + new Transition (8212, 8213), // &succcurly -> &succcurlye + new Transition (8217, 8218), // &Succ -> &Succe + new Transition (8218, 8219), // &Succe -> &Succee + new Transition (8243, 8244), // &SucceedsTild -> &SucceedsTilde + new Transition (8249, 8257), // &succn -> &succne + new Transition (8282, 8308), // &Sup -> &Supe + new Transition (8284, 8302), // &sup -> &supe + new Transition (8310, 8311), // &Supers -> &Superse + new Transition (8338, 8341), // &supn -> &supne + new Transition (8348, 8349), // &Sups -> &Supse + new Transition (8352, 8353), // &sups -> &supse + new Transition (8354, 8356), // &supset -> &supsete + new Transition (8361, 8362), // &supsetn -> &supsetne + new Transition (8404, 8449), // &t -> &te + new Transition (8407, 8408), // &targ -> &targe + new Transition (8419, 8431), // &Tc -> &Tce + new Transition (8425, 8436), // &tc -> &tce + new Transition (8451, 8452), // &telr -> &telre + new Transition (8461, 8462), // &th -> &the + new Transition (8463, 8464), // &ther -> &there + new Transition (8467, 8468), // &Th -> &The + new Transition (8469, 8470), // &Ther -> &There + new Transition (8473, 8474), // &Therefor -> &Therefore + new Transition (8478, 8479), // &therefor -> &therefore + new Transition (8513, 8514), // &ThickSpac -> &ThickSpace + new Transition (8524, 8525), // &ThinSpac -> &ThinSpace + new Transition (8546, 8547), // &Tild -> &Tilde + new Transition (8551, 8552), // &tild -> &tilde + new Transition (8573, 8574), // &TildeTild -> &TildeTilde + new Transition (8576, 8577), // &tim -> &time + new Transition (8590, 8591), // &to -> &toe + new Transition (8620, 8621), // &tprim -> &tprime + new Transition (8630, 8631), // &trad -> &trade + new Transition (8633, 8668), // &tri -> &trie + new Transition (8637, 8638), // &triangl -> &triangle + new Transition (8645, 8646), // &trianglel -> &trianglele + new Transition (8648, 8650), // &triangleleft -> &trianglelefte + new Transition (8659, 8661), // &triangleright -> &trianglerighte + new Transition (8679, 8680), // &Tripl -> &Triple + new Transition (8695, 8696), // &tritim -> &tritime + new Transition (8698, 8699), // &trp -> &trpe + new Transition (8743, 8744), // &twoh -> &twohe + new Transition (8747, 8748), // &twoheadl -> &twoheadle + new Transition (8772, 8773), // &Uacut -> Ú + new Transition (8779, 8780), // &uacut -> ú + new Transition (8798, 8807), // &Ubr -> &Ubre + new Transition (8803, 8811), // &ubr -> &ubre + new Transition (8808, 8809), // &Ubrev -> &Ubreve + new Transition (8812, 8813), // &ubrev -> &ubreve + new Transition (8863, 8864), // &Ugrav -> Ù + new Transition (8869, 8870), // &ugrav -> ù + new Transition (8891, 8893), // &ulcorn -> &ulcorne + new Transition (8917, 8918), // &Und -> &Unde + new Transition (8926, 8927), // &UnderBrac -> &UnderBrace + new Transition (8929, 8930), // &UnderBrack -> &UnderBracke + new Transition (8935, 8936), // &UnderPar -> &UnderPare + new Transition (8939, 8940), // &UnderParenth -> &UnderParenthe + new Transition (9053, 9054), // &upharpoonl -> &upharpoonle + new Transition (9068, 9069), // &Upp -> &Uppe + new Transition (9071, 9072), // &UpperL -> &UpperLe + new Transition (9108, 9109), // &UpT -> &UpTe + new Transition (9109, 9110), // &UpTe -> &UpTee + new Transition (9131, 9133), // &urcorn -> &urcorne + new Transition (9169, 9170), // &Utild -> &Utilde + new Transition (9174, 9175), // &utild -> &utilde + new Transition (9198, 9199), // &uwangl -> &uwangle + new Transition (9201, 9345), // &v -> &ve + new Transition (9208, 9209), // &var -> &vare + new Transition (9260, 9261), // &varsubs -> &varsubse + new Transition (9263, 9264), // &varsubsetn -> &varsubsetne + new Transition (9270, 9271), // &varsups -> &varsupse + new Transition (9273, 9274), // &varsupsetn -> &varsupsetne + new Transition (9280, 9281), // &varth -> &varthe + new Transition (9290, 9291), // &vartriangl -> &vartriangle + new Transition (9292, 9293), // &vartrianglel -> &vartrianglele + new Transition (9303, 9342), // &V -> &Ve + new Transition (9342, 9343), // &Ve -> &Vee + new Transition (9345, 9346), // &ve -> &vee + new Transition (9346, 9352), // &vee -> &veee + new Transition (9384, 9385), // &VerticalLin -> &VerticalLine + new Transition (9387, 9388), // &VerticalS -> &VerticalSe + new Transition (9400, 9401), // &VerticalTild -> &VerticalTilde + new Transition (9411, 9412), // &VeryThinSpac -> &VeryThinSpace + new Transition (9460, 9463), // &vsubn -> &vsubne + new Transition (9466, 9469), // &vsupn -> &vsupne + new Transition (9484, 9502), // &W -> &We + new Transition (9490, 9496), // &w -> &we + new Transition (9504, 9505), // &Wedg -> &Wedge + new Transition (9507, 9508), // &wedg -> &wedge + new Transition (9512, 9513), // &wei -> &weie + new Transition (9533, 9535), // &wr -> &wre + new Transition (9620, 9621), // &xotim -> &xotime + new Transition (9655, 9656), // &xv -> &xve + new Transition (9656, 9657), // &xve -> &xvee + new Transition (9659, 9660), // &xw -> &xwe + new Transition (9662, 9663), // &xwedg -> &xwedge + new Transition (9669, 9670), // &Yacut -> Ý + new Transition (9672, 9699), // &y -> &ye + new Transition (9676, 9677), // &yacut -> ý + new Transition (9747, 9791), // &Z -> &Ze + new Transition (9751, 9752), // &Zacut -> &Zacute + new Transition (9754, 9785), // &z -> &ze + new Transition (9758, 9759), // &zacut -> &zacute + new Transition (9785, 9786), // &ze -> &zee + new Transition (9802, 9803) // &ZeroWidthSpac -> &ZeroWidthSpace + }; + TransitionTable_f = new Transition[177] { + new Transition (0, 2503), // & -> &f + new Transition (1, 62), // &A -> &Af + new Transition (8, 60), // &a -> &af + new Transition (80, 81), // &ale -> &alef + new Transition (147, 158), // &angmsda -> &angmsdaf + new Transition (193, 194), // &Aop -> &Aopf + new Transition (196, 197), // &aop -> &aopf + new Transition (301, 439), // &b -> &bf + new Transition (331, 436), // &B -> &Bf + new Transition (553, 554), // &blacktrianglele -> &blacktrianglelef + new Transition (595, 596), // &Bop -> &Bopf + new Transition (599, 600), // &bop -> &bopf + new Transition (789, 950), // &C -> &Cf + new Transition (796, 953), // &c -> &cf + new Transition (833, 834), // &CapitalDi -> &CapitalDif + new Transition (834, 835), // &CapitalDif -> &CapitalDiff + new Transition (979, 1053), // &cir -> &cirf + new Transition (994, 995), // &circlearrowle -> &circlearrowlef + new Transition (1148, 1150), // &comp -> &compf + new Transition (1200, 1201), // &Cop -> &Copf + new Transition (1203, 1204), // &cop -> &copf + new Transition (1389, 1390), // &curvearrowle -> &curvearrowlef + new Transition (1425, 1541), // &D -> &Df + new Transition (1432, 1535), // &d -> &df + new Transition (1557, 1621), // &Di -> &Dif + new Transition (1621, 1622), // &Dif -> &Diff + new Transition (1686, 1687), // &Dop -> &Dopf + new Transition (1689, 1690), // &dop -> &dopf + new Transition (1777, 1778), // &DoubleLe -> &DoubleLef + new Transition (1805, 1806), // &DoubleLongLe -> &DoubleLongLef + new Transition (1940, 1941), // &downharpoonle -> &downharpoonlef + new Transition (1951, 1952), // &DownLe -> &DownLef + new Transition (2073, 2075), // &dtri -> &dtrif + new Transition (2108, 2180), // &E -> &Ef + new Transition (2115, 2175), // &e -> &ef + new Transition (2306, 2307), // &Eop -> &Eopf + new Transition (2309, 2310), // &eop -> &eopf + new Transition (2503, 2530), // &f -> &ff + new Transition (2517, 2544), // &F -> &Ff + new Transition (2605, 2606), // &fno -> &fnof + new Transition (2609, 2610), // &Fop -> &Fopf + new Transition (2613, 2614), // &fop -> &fopf + new Transition (2636, 2637), // &Fouriertr -> &Fouriertrf + new Transition (2701, 2802), // &g -> &gf + new Transition (2708, 2799), // &G -> &Gf + new Transition (2854, 2855), // &Gop -> &Gopf + new Transition (2858, 2859), // &gop -> &gopf + new Transition (3014, 3094), // &H -> &Hf + new Transition (3020, 3097), // &h -> &hf + new Transition (3027, 3028), // &hal -> &half + new Transition (3139, 3140), // &hookle -> &hooklef + new Transition (3160, 3161), // &Hop -> &Hopf + new Transition (3163, 3164), // &hop -> &hopf + new Transition (3236, 3284), // &I -> &If + new Transition (3243, 3281), // &i -> &if + new Transition (3281, 3282), // &if -> &iff + new Transition (3311, 3312), // &iin -> &iinf + new Transition (3365, 3366), // &imo -> &imof + new Transition (3378, 3385), // &in -> &inf + new Transition (3480, 3481), // &Iop -> &Iopf + new Transition (3483, 3484), // &iop -> &iopf + new Transition (3555, 3571), // &J -> &Jf + new Transition (3561, 3574), // &j -> &jf + new Transition (3583, 3584), // &Jop -> &Jopf + new Transition (3587, 3588), // &jop -> &jopf + new Transition (3618, 3648), // &K -> &Kf + new Transition (3624, 3651), // &k -> &kf + new Transition (3677, 3678), // &Kop -> &Kopf + new Transition (3681, 3682), // &kop -> &kopf + new Transition (3692, 4301), // &l -> &lf + new Transition (3698, 4312), // &L -> &Lf + new Transition (3752, 3753), // &Laplacetr -> &Laplacetrf + new Transition (3766, 3773), // &larr -> &larrf + new Transition (3768, 3770), // &larrb -> &larrbf + new Transition (3896, 3925), // &le -> &lef + new Transition (3898, 3899), // &Le -> &Lef + new Transition (4020, 4021), // &leftle -> &leftlef + new Transition (4361, 4362), // &Lle -> &Llef + new Transition (4438, 4439), // &LongLe -> &LongLef + new Transition (4448, 4449), // &Longle -> &Longlef + new Transition (4460, 4461), // &longle -> &longlef + new Transition (4550, 4551), // &looparrowle -> &looparrowlef + new Transition (4560, 4567), // &lop -> &lopf + new Transition (4564, 4565), // &Lop -> &Lopf + new Transition (4592, 4593), // &LowerLe -> &LowerLef + new Transition (4612, 4619), // &loz -> &lozf + new Transition (4732, 4736), // <ri -> <rif + new Transition (4767, 4865), // &m -> &mf + new Transition (4781, 4862), // &M -> &Mf + new Transition (4797, 4798), // &mapstole -> &mapstolef + new Transition (4859, 4860), // &Mellintr -> &Mellintrf + new Transition (4929, 4930), // &Mop -> &Mopf + new Transition (4932, 4933), // &mop -> &mopf + new Transition (4965, 5192), // &n -> &nf + new Transition (4971, 5189), // &N -> &Nf + new Transition (5270, 5282), // &nle -> &nlef + new Transition (5273, 5274), // &nLe -> &nLef + new Transition (5369, 5370), // &Nop -> &Nopf + new Transition (5373, 5374), // &nop -> &nopf + new Transition (5529, 5530), // &NotLe -> &NotLef + new Transition (6014, 6015), // &ntrianglele -> &ntrianglelef + new Transition (6079, 6080), // &nvin -> &nvinf + new Transition (6131, 6205), // &O -> &Of + new Transition (6138, 6200), // &o -> &of + new Transition (6295, 6296), // &Oop -> &Oopf + new Transition (6299, 6300), // &oop -> &oopf + new Transition (6348, 6356), // &ord -> ª + new Transition (6353, 6354), // &ordero -> &orderof + new Transition (6362, 6363), // &origo -> &origof + new Transition (6463, 6521), // &p -> &pf + new Transition (6482, 6518), // &P -> &Pf + new Transition (6547, 6548), // &pitch -> &pitchf + new Transition (6630, 6631), // &Pop -> &Popf + new Transition (6633, 6634), // &pop -> &popf + new Transition (6745, 6754), // &pro -> &prof + new Transition (6767, 6768), // &profsur -> &profsurf + new Transition (6813, 6814), // &Q -> &Qf + new Transition (6817, 6818), // &q -> &qf + new Transition (6826, 6827), // &Qop -> &Qopf + new Transition (6830, 6831), // &qop -> &qopf + new Transition (6876, 7135), // &r -> &rf + new Transition (6886, 7146), // &R -> &Rf + new Transition (6932, 6944), // &rarr -> &rarrf + new Transition (6937, 6939), // &rarrb -> &rarrbf + new Transition (7214, 7215), // &RightArrowLe -> &RightArrowLef + new Transition (7295, 7296), // &rightle -> &rightlef + new Transition (7481, 7489), // &rop -> &ropf + new Transition (7486, 7487), // &Rop -> &Ropf + new Transition (7579, 7583), // &rtri -> &rtrif + new Transition (7610, 7741), // &S -> &Sf + new Transition (7617, 7744), // &s -> &sf + new Transition (7787, 7788), // &ShortLe -> &ShortLef + new Transition (7841, 7843), // &sigma -> &sigmaf + new Transition (7936, 7937), // &so -> &sof + new Transition (7950, 7951), // &Sop -> &Sopf + new Transition (7953, 7954), // &sop -> &sopf + new Transition (8008, 8066), // &squ -> &squf + new Transition (8016, 8064), // &squar -> &squarf + new Transition (8093, 8094), // &sstar -> &sstarf + new Transition (8102, 8104), // &star -> &starf + new Transition (8400, 8455), // &T -> &Tf + new Transition (8404, 8458), // &t -> &tf + new Transition (8464, 8476), // &there -> &theref + new Transition (8470, 8471), // &There -> &Theref + new Transition (8594, 8608), // &top -> &topf + new Transition (8605, 8606), // &Top -> &Topf + new Transition (8646, 8647), // &trianglele -> &trianglelef + new Transition (8748, 8749), // &twoheadle -> &twoheadlef + new Transition (8768, 8855), // &U -> &Uf + new Transition (8775, 8849), // &u -> &uf + new Transition (8964, 8965), // &Uop -> &Uopf + new Transition (8967, 8968), // &uop -> &uopf + new Transition (9054, 9055), // &upharpoonle -> &upharpoonlef + new Transition (9072, 9073), // &UpperLe -> &UpperLef + new Transition (9178, 9180), // &utri -> &utrif + new Transition (9201, 9417), // &v -> &vf + new Transition (9293, 9294), // &vartrianglele -> &vartrianglelef + new Transition (9303, 9414), // &V -> &Vf + new Transition (9433, 9434), // &Vop -> &Vopf + new Transition (9437, 9438), // &vop -> &vopf + new Transition (9484, 9517), // &W -> &Wf + new Transition (9490, 9520), // &w -> &wf + new Transition (9524, 9525), // &Wop -> &Wopf + new Transition (9528, 9529), // &wop -> &wopf + new Transition (9548, 9569), // &x -> &xf + new Transition (9565, 9566), // &X -> &Xf + new Transition (9608, 9609), // &Xop -> &Xopf + new Transition (9611, 9612), // &xop -> &xopf + new Transition (9665, 9702), // &Y -> &Yf + new Transition (9672, 9705), // &y -> &yf + new Transition (9717, 9718), // &Yop -> &Yopf + new Transition (9721, 9722), // &yop -> &yopf + new Transition (9747, 9811), // &Z -> &Zf + new Transition (9754, 9814), // &z -> &zf + new Transition (9788, 9789), // &zeetr -> &zeetrf + new Transition (9833, 9834), // &Zop -> &Zopf + new Transition (9837, 9838) // &zop -> &zopf + }; + TransitionTable_g = new Transition[182] { + new Transition (0, 2701), // & -> &g + new Transition (1, 67), // &A -> &Ag + new Transition (8, 73), // &a -> &ag + new Transition (52, 53), // &AEli -> Æ + new Transition (57, 58), // &aeli -> æ + new Transition (108, 109), // &amal -> &amalg + new Transition (119, 136), // &an -> &ang + new Transition (147, 160), // &angmsda -> &angmsdag + new Transition (183, 184), // &Ao -> &Aog + new Transition (188, 189), // &ao -> &aog + new Transition (239, 240), // &Arin -> Å + new Transition (244, 245), // &arin -> å + new Transition (256, 257), // &Assi -> &Assig + new Transition (307, 308), // &backcon -> &backcong + new Transition (355, 357), // &barwed -> &barwedg + new Transition (371, 372), // &bcon -> &bcong + new Transition (442, 443), // &bi -> &big + new Transition (485, 486), // &bigtrian -> &bigtriang + new Transition (509, 510), // &bigwed -> &bigwedg + new Transition (527, 528), // &blacklozen -> &blacklozeng + new Transition (542, 543), // &blacktrian -> &blacktriang + new Transition (558, 559), // &blacktriangleri -> &blacktrianglerig + new Transition (999, 1000), // &circlearrowri -> &circlearrowrig + new Transition (1086, 1087), // &ClockwiseContourInte -> &ClockwiseContourInteg + new Transition (1164, 1165), // &con -> &cong + new Transition (1171, 1172), // &Con -> &Cong + new Transition (1194, 1195), // &ContourInte -> &ContourInteg + new Transition (1250, 1251), // &CounterClockwiseContourInte -> &CounterClockwiseContourInteg + new Transition (1373, 1374), // &curlywed -> &curlywedg + new Transition (1394, 1395), // &curvearrowri -> &curvearrowrig + new Transition (1426, 1427), // &Da -> &Dag + new Transition (1427, 1428), // &Dag -> &Dagg + new Transition (1433, 1434), // &da -> &dag + new Transition (1434, 1435), // &dag -> &dagg + new Transition (1494, 1495), // &dda -> &ddag + new Transition (1495, 1496), // &ddag -> &ddagg + new Transition (1516, 1517), // &de -> ° + new Transition (1599, 1633), // &di -> &dig + new Transition (1740, 1741), // &doublebarwed -> &doublebarwedg + new Transition (1758, 1759), // &DoubleContourInte -> &DoubleContourInteg + new Transition (1787, 1788), // &DoubleLeftRi -> &DoubleLeftRig + new Transition (1802, 1803), // &DoubleLon -> &DoubleLong + new Transition (1815, 1816), // &DoubleLongLeftRi -> &DoubleLongLeftRig + new Transition (1826, 1827), // &DoubleLongRi -> &DoubleLongRig + new Transition (1837, 1838), // &DoubleRi -> &DoubleRig + new Transition (1945, 1946), // &downharpoonri -> &downharpoonrig + new Transition (1955, 1956), // &DownLeftRi -> &DownLeftRig + new Transition (1988, 1989), // &DownRi -> &DownRig + new Transition (2088, 2089), // &dwan -> &dwang + new Transition (2101, 2102), // &dzi -> &dzig + new Transition (2108, 2187), // &E -> &Eg + new Transition (2115, 2185), // &e -> &eg + new Transition (2290, 2291), // &en -> &eng + new Transition (2296, 2297), // &Eo -> &Eog + new Transition (2301, 2302), // &eo -> &eog + new Transition (2357, 2358), // &eqslant -> &eqslantg + new Transition (2508, 2509), // &fallin -> &falling + new Transition (2533, 2534), // &ffili -> &ffilig + new Transition (2537, 2538), // &ffli -> &fflig + new Transition (2541, 2542), // &fflli -> &ffllig + new Transition (2551, 2552), // &fili -> &filig + new Transition (2589, 2590), // &fjli -> &fjlig + new Transition (2597, 2598), // &flli -> &fllig + new Transition (2701, 2807), // &g -> &gg + new Transition (2708, 2805), // &G -> &Gg + new Transition (2807, 2809), // &gg -> &ggg + new Transition (3149, 3150), // &hookri -> &hookrig + new Transition (3236, 3289), // &I -> &Ig + new Transition (3243, 3295), // &i -> &ig + new Transition (3322, 3323), // &IJli -> &IJlig + new Transition (3327, 3328), // &ijli -> &ijlig + new Transition (3332, 3344), // &Ima -> &Imag + new Transition (3337, 3341), // &ima -> &imag + new Transition (3407, 3408), // &inte -> &integ + new Transition (3413, 3414), // &Inte -> &Integ + new Transition (3467, 3476), // &io -> &iog + new Transition (3471, 3472), // &Io -> &Iog + new Transition (3624, 3654), // &k -> &kg + new Transition (3692, 4317), // &l -> &lg + new Transition (3705, 3718), // &la -> &lag + new Transition (3733, 3734), // &Lan -> &Lang + new Transition (3736, 3737), // &lan -> &lang + new Transition (3894, 4183), // &lE -> &lEg + new Transition (3896, 4185), // &le -> &leg + new Transition (3902, 3903), // &LeftAn -> &LeftAng + new Transition (3938, 3939), // &LeftArrowRi -> &LeftArrowRig + new Transition (3958, 3959), // &LeftCeilin -> &LeftCeiling + new Transition (4031, 4032), // &LeftRi -> &LeftRig + new Transition (4042, 4043), // &Leftri -> &Leftrig + new Transition (4053, 4054), // &leftri -> &leftrig + new Transition (4077, 4078), // &leftrightsqui -> &leftrightsquig + new Transition (4123, 4124), // &LeftTrian -> &LeftTriang + new Transition (4197, 4210), // &les -> &lesg + new Transition (4215, 4271), // &less -> &lessg + new Transition (4228, 4229), // &lesseq -> &lesseqg + new Transition (4233, 4234), // &lesseqq -> &lesseqqg + new Transition (4424, 4425), // &loan -> &loang + new Transition (4435, 4436), // &Lon -> &Long + new Transition (4457, 4458), // &lon -> &long + new Transition (4470, 4471), // &LongLeftRi -> &LongLeftRig + new Transition (4481, 4482), // &Longleftri -> &Longleftrig + new Transition (4492, 4493), // &longleftri -> &longleftrig + new Transition (4510, 4511), // &LongRi -> &LongRig + new Transition (4521, 4522), // &Longri -> &Longrig + new Transition (4532, 4533), // &longri -> &longrig + new Transition (4555, 4556), // &looparrowri -> &looparrowrig + new Transition (4602, 4603), // &LowerRi -> &LowerRig + new Transition (4615, 4616), // &lozen -> &lozeng + new Transition (4670, 4674), // &lsim -> &lsimg + new Transition (4838, 4839), // &measuredan -> &measuredang + new Transition (4965, 5195), // &n -> &ng + new Transition (4983, 4984), // &nan -> &nang + new Transition (5045, 5046), // &ncon -> &ncong + new Transition (5084, 5085), // &Ne -> &Neg + new Transition (5212, 5213), // &nG -> &nGg + new Transition (5291, 5292), // &nLeftri -> &nLeftrig + new Transition (5302, 5303), // &nleftri -> &nleftrig + new Transition (5361, 5362), // &NonBreakin -> &NonBreaking + new Transition (5382, 5383), // &NotCon -> &NotCong + new Transition (5536, 5537), // &NotLeftTrian -> &NotLeftTriang + new Transition (5671, 5672), // &NotRi -> &NotRig + new Transition (5679, 5680), // &NotRightTrian -> &NotRightTriang + new Transition (5869, 5870), // &nRi -> &nRig + new Transition (5879, 5880), // &nri -> &nrig + new Transition (5988, 5989), // &nt -> &ntg + new Transition (6003, 6004), // &ntl -> &ntlg + new Transition (6009, 6010), // &ntrian -> &ntriang + new Transition (6022, 6023), // &ntriangleri -> &ntrianglerig + new Transition (6043, 6068), // &nv -> &nvg + new Transition (6131, 6214), // &O -> &Og + new Transition (6138, 6210), // &o -> &og + new Transition (6192, 6193), // &OEli -> &OElig + new Transition (6197, 6198), // &oeli -> &oelig + new Transition (6268, 6269), // &Ome -> &Omeg + new Transition (6272, 6273), // &ome -> &omeg + new Transition (6360, 6361), // &ori -> &orig + new Transition (6908, 6909), // &Ran -> &Rang + new Transition (6911, 6912), // &ran -> &rang + new Transition (7074, 7095), // &re -> ® + new Transition (7171, 7172), // &Ri -> &Rig + new Transition (7176, 7177), // &RightAn -> &RightAng + new Transition (7199, 7200), // &ri -> &rig + new Transition (7233, 7234), // &RightCeilin -> &RightCeiling + new Transition (7315, 7316), // &rightri -> &rightrig + new Transition (7329, 7330), // &rightsqui -> &rightsquig + new Transition (7368, 7369), // &RightTrian -> &RightTriang + new Transition (7428, 7429), // &rin -> &ring + new Transition (7433, 7434), // &risin -> &rising + new Transition (7471, 7472), // &roan -> &roang + new Transition (7514, 7516), // &rpar -> &rparg + new Transition (7532, 7533), // &Rri -> &Rrig + new Transition (7813, 7814), // &ShortRi -> &ShortRig + new Transition (7833, 7834), // &Si -> &Sig + new Transition (7838, 7839), // &si -> &sig + new Transition (7847, 7857), // &sim -> &simg + new Transition (8108, 8109), // &strai -> &straig + new Transition (8279, 8280), // &sun -> &sung + new Transition (8397, 8398), // &szli -> ß + new Transition (8406, 8407), // &tar -> &targ + new Transition (8635, 8636), // &trian -> &triang + new Transition (8656, 8657), // &triangleri -> &trianglerig + new Transition (8758, 8759), // &twoheadri -> &twoheadrig + new Transition (8768, 8860), // &U -> &Ug + new Transition (8775, 8866), // &u -> &ug + new Transition (8954, 8955), // &Uo -> &Uog + new Transition (8959, 8960), // &uo -> &uog + new Transition (9059, 9060), // &upharpoonri -> &upharpoonrig + new Transition (9082, 9083), // &UpperRi -> &UpperRig + new Transition (9142, 9143), // &Urin -> &Uring + new Transition (9146, 9147), // &urin -> &uring + new Transition (9196, 9197), // &uwan -> &uwang + new Transition (9203, 9204), // &van -> &vang + new Transition (9228, 9229), // &varnothin -> &varnothing + new Transition (9253, 9254), // &varsi -> &varsig + new Transition (9288, 9289), // &vartrian -> &vartriang + new Transition (9298, 9299), // &vartriangleri -> &vartrianglerig + new Transition (9478, 9479), // &vzi -> &vzig + new Transition (9481, 9482), // &vzigza -> &vzigzag + new Transition (9497, 9507), // &wed -> &wedg + new Transition (9503, 9504), // &Wed -> &Wedg + new Transition (9661, 9662), // &xwed -> &xwedg + new Transition (9825, 9826) // &zi -> &zig + }; + TransitionTable_h = new Transition[159] { + new Transition (0, 3020), // & -> &h + new Transition (86, 87), // &alep -> &aleph + new Transition (90, 91), // &Alp -> &Alph + new Transition (94, 95), // &alp -> &alph + new Transition (147, 162), // &angmsda -> &angmsdah + new Transition (173, 174), // &angsp -> &angsph + new Transition (338, 339), // &Backslas -> &Backslash + new Transition (426, 429), // &bet -> &beth + new Transition (559, 560), // &blacktrianglerig -> &blacktrianglerigh + new Transition (613, 638), // &box -> &boxh + new Transition (691, 697), // &boxV -> &boxVh + new Transition (693, 701), // &boxv -> &boxvh + new Transition (758, 762), // &bsol -> &bsolh + new Transition (789, 973), // &C -> &Ch + new Transition (796, 960), // &c -> &ch + new Transition (1000, 1001), // &circlearrowrig -> &circlearrowrigh + new Transition (1016, 1017), // &circleddas -> &circleddash + new Transition (1395, 1396), // &curvearrowrig -> &curvearrowrigh + new Transition (1432, 1550), // &d -> &dh + new Transition (1441, 1442), // &dalet -> &daleth + new Transition (1454, 1455), // &das -> &dash + new Transition (1457, 1458), // &Das -> &Dash + new Transition (1506, 1507), // &DDotra -> &DDotrah + new Transition (1537, 1538), // &dfis -> &dfish + new Transition (1788, 1789), // &DoubleLeftRig -> &DoubleLeftRigh + new Transition (1816, 1817), // &DoubleLongLeftRig -> &DoubleLongLeftRigh + new Transition (1827, 1828), // &DoubleLongRig -> &DoubleLongRigh + new Transition (1838, 1839), // &DoubleRig -> &DoubleRigh + new Transition (1896, 1932), // &down -> &downh + new Transition (1946, 1947), // &downharpoonrig -> &downharpoonrigh + new Transition (1956, 1957), // &DownLeftRig -> &DownLeftRigh + new Transition (1989, 1990), // &DownRig -> &DownRigh + new Transition (2077, 2082), // &du -> &duh + new Transition (2439, 2445), // &et -> ð + new Transition (3132, 3133), // &homt -> &homth + new Transition (3150, 3151), // &hookrig -> &hookrigh + new Transition (3194, 3195), // &hslas -> &hslash + new Transition (3231, 3232), // &hyp -> &hyph + new Transition (3362, 3363), // &imat -> &imath + new Transition (3435, 3436), // &intlar -> &intlarh + new Transition (3579, 3580), // &jmat -> &jmath + new Transition (3624, 3664), // &k -> &kh + new Transition (3692, 4325), // &l -> &lh + new Transition (3766, 3776), // &larr -> &larrh + new Transition (3880, 3881), // &ldrd -> &ldrdh + new Transition (3886, 3887), // &ldrus -> &ldrush + new Transition (3891, 3892), // &lds -> &ldsh + new Transition (3926, 4004), // &left -> &lefth + new Transition (3939, 3940), // &LeftArrowRig -> &LeftArrowRigh + new Transition (4032, 4033), // &LeftRig -> &LeftRigh + new Transition (4043, 4044), // &Leftrig -> &Leftrigh + new Transition (4054, 4055), // &leftrig -> &leftrigh + new Transition (4056, 4065), // &leftright -> &leftrighth + new Transition (4109, 4110), // &leftt -> &leftth + new Transition (4303, 4304), // &lfis -> &lfish + new Transition (4348, 4370), // &ll -> &llh + new Transition (4397, 4398), // &lmoustac -> &lmoustach + new Transition (4471, 4472), // &LongLeftRig -> &LongLeftRigh + new Transition (4482, 4483), // &Longleftrig -> &Longleftrigh + new Transition (4493, 4494), // &longleftrig -> &longleftrigh + new Transition (4511, 4512), // &LongRig -> &LongRigh + new Transition (4522, 4523), // &Longrig -> &Longrigh + new Transition (4533, 4534), // &longrig -> &longrigh + new Transition (4556, 4557), // &looparrowrig -> &looparrowrigh + new Transition (4603, 4604), // &LowerRig -> &LowerRigh + new Transition (4628, 4640), // &lr -> &lrh + new Transition (4652, 4667), // &ls -> &lsh + new Transition (4658, 4665), // &Ls -> &Lsh + new Transition (4698, 4710), // < -> <h + new Transition (4745, 4746), // &lurds -> &lurdsh + new Transition (4750, 4751), // &luru -> &luruh + new Transition (4767, 4868), // &m -> &mh + new Transition (4822, 4823), // &mdas -> &mdash + new Transition (4965, 5227), // &n -> &nh + new Transition (5061, 5062), // &ndas -> &ndash + new Transition (5067, 5068), // &near -> &nearh + new Transition (5103, 5104), // &NegativeT -> &NegativeTh + new Transition (5125, 5126), // &NegativeVeryT -> &NegativeVeryTh + new Transition (5292, 5293), // &nLeftrig -> &nLeftrigh + new Transition (5303, 5304), // &nleftrig -> &nleftrigh + new Transition (5672, 5673), // &NotRig -> &NotRigh + new Transition (5870, 5871), // &nRig -> &nRigh + new Transition (5880, 5881), // &nrig -> &nrigh + new Transition (5895, 5910), // &ns -> &nsh + new Transition (6023, 6024), // &ntrianglerig -> &ntrianglerigh + new Transition (6050, 6051), // &nVDas -> &nVDash + new Transition (6055, 6056), // &nVdas -> &nVdash + new Transition (6060, 6061), // &nvDas -> &nvDash + new Transition (6065, 6066), // &nvdas -> &nvdash + new Transition (6113, 6114), // &nwar -> &nwarh + new Transition (6138, 6227), // &o -> &oh + new Transition (6165, 6166), // &odas -> &odash + new Transition (6388, 6389), // &Oslas -> Ø + new Transition (6393, 6394), // &oslas -> ø + new Transition (6456, 6457), // &OverParent -> &OverParenth + new Transition (6463, 6527), // &p -> &ph + new Transition (6482, 6524), // &P -> &Ph + new Transition (6546, 6547), // &pitc -> &pitch + new Transition (6559, 6561), // &planck -> &planckh + new Transition (6876, 7155), // &r -> &rh + new Transition (6886, 7164), // &R -> &Rh + new Transition (6932, 6947), // &rarr -> &rarrh + new Transition (7058, 7059), // &rdld -> &rdldh + new Transition (7069, 7070), // &rds -> &rdsh + new Transition (7137, 7138), // &rfis -> &rfish + new Transition (7172, 7173), // &Rig -> &Righ + new Transition (7200, 7201), // &rig -> &righ + new Transition (7202, 7279), // &right -> &righth + new Transition (7297, 7305), // &rightleft -> &rightlefth + new Transition (7316, 7317), // &rightrig -> &rightrigh + new Transition (7354, 7355), // &rightt -> &rightth + new Transition (7442, 7447), // &rl -> &rlh + new Transition (7460, 7461), // &rmoustac -> &rmoustach + new Transition (7533, 7534), // &Rrig -> &Rrigh + new Transition (7542, 7557), // &rs -> &rsh + new Transition (7548, 7555), // &Rs -> &Rsh + new Transition (7567, 7568), // &rt -> &rth + new Transition (7603, 7604), // &rulu -> &ruluh + new Transition (7610, 7772), // &S -> &Sh + new Transition (7617, 7751), // &s -> &sh + new Transition (7705, 7706), // &sear -> &searh + new Transition (7762, 7763), // &shc -> &shch + new Transition (7814, 7815), // &ShortRig -> &ShortRigh + new Transition (7907, 7908), // &smas -> &smash + new Transition (8109, 8110), // &straig -> &straigh + new Transition (8120, 8121), // &straightp -> &straightph + new Transition (8216, 8269), // &Suc -> &Such + new Transition (8270, 8271), // &SuchT -> &SuchTh + new Transition (8284, 8320), // &sup -> &suph + new Transition (8377, 8378), // &swar -> &swarh + new Transition (8400, 8467), // &T -> &Th + new Transition (8404, 8461), // &t -> &th + new Transition (8657, 8658), // &trianglerig -> &trianglerigh + new Transition (8709, 8723), // &ts -> &tsh + new Transition (8742, 8743), // &two -> &twoh + new Transition (8759, 8760), // &twoheadrig -> &twoheadrigh + new Transition (8775, 8876), // &u -> &uh + new Transition (8829, 8845), // &ud -> &udh + new Transition (8851, 8852), // &ufis -> &ufish + new Transition (8938, 8939), // &UnderParent -> &UnderParenth + new Transition (8983, 9046), // &up -> &uph + new Transition (9060, 9061), // &upharpoonrig -> &upharpoonrigh + new Transition (9083, 9084), // &UpperRig -> &UpperRigh + new Transition (9096, 9098), // &upsi -> &upsih + new Transition (9225, 9226), // &varnot -> &varnoth + new Transition (9231, 9232), // &varp -> &varph + new Transition (9247, 9249), // &varr -> &varrh + new Transition (9279, 9280), // &vart -> &varth + new Transition (9299, 9300), // &vartrianglerig -> &vartrianglerigh + new Transition (9322, 9323), // &VDas -> &VDash + new Transition (9327, 9328), // &Vdas -> &Vdash + new Transition (9332, 9333), // &vDas -> &vDash + new Transition (9337, 9338), // &vdas -> &vdash + new Transition (9404, 9405), // &VeryT -> &VeryTh + new Transition (9474, 9475), // &Vvdas -> &Vvdash + new Transition (9537, 9538), // &wreat -> &wreath + new Transition (9548, 9572), // &x -> &xh + new Transition (9754, 9821), // &z -> &zh + new Transition (9797, 9798) // &ZeroWidt -> &ZeroWidth + }; + TransitionTable_i = new Transition[428] { + new Transition (0, 3243), // & -> &i + new Transition (27, 38), // &ac -> &aci + new Transition (33, 34), // &Ac -> &Aci + new Transition (51, 52), // &AEl -> &AEli + new Transition (56, 57), // &ael -> &aeli + new Transition (199, 210), // &ap -> &api + new Transition (202, 203), // &apac -> &apaci + new Transition (224, 225), // &ApplyFunct -> &ApplyFuncti + new Transition (237, 238), // &Ar -> &Ari + new Transition (242, 243), // &ar -> &ari + new Transition (255, 256), // &Ass -> &Assi + new Transition (269, 270), // &At -> &Ati + new Transition (275, 276), // &at -> &ati + new Transition (289, 297), // &aw -> &awi + new Transition (292, 293), // &awcon -> &awconi + new Transition (301, 442), // &b -> &bi + new Transition (312, 313), // &backeps -> &backepsi + new Transition (319, 320), // &backpr -> &backpri + new Transition (324, 325), // &backs -> &backsi + new Transition (406, 407), // &beps -> &bepsi + new Transition (419, 420), // &Bernoull -> &Bernoulli + new Transition (444, 448), // &bigc -> &bigci + new Transition (465, 466), // &bigot -> &bigoti + new Transition (482, 483), // &bigtr -> &bigtri + new Transition (539, 540), // &blacktr -> &blacktri + new Transition (557, 558), // &blacktriangler -> &blacktriangleri + new Transition (583, 584), // &bnequ -> &bnequi + new Transition (609, 610), // &bowt -> &bowti + new Transition (656, 657), // &boxm -> &boxmi + new Transition (667, 668), // &boxt -> &boxti + new Transition (720, 721), // &bpr -> &bpri + new Transition (744, 752), // &bs -> &bsi + new Transition (749, 750), // &bsem -> &bsemi + new Transition (789, 1019), // &C -> &Ci + new Transition (796, 978), // &c -> &ci + new Transition (803, 828), // &Cap -> &Capi + new Transition (832, 833), // &CapitalD -> &CapitalDi + new Transition (840, 841), // &CapitalDifferent -> &CapitalDifferenti + new Transition (861, 890), // &cc -> &cci + new Transition (866, 886), // &Cc -> &Cci + new Transition (877, 878), // &Cced -> &Ccedi + new Transition (882, 883), // &cced -> &ccedi + new Transition (895, 896), // &Ccon -> &Cconi + new Transition (916, 917), // &ced -> &cedi + new Transition (921, 922), // &Ced -> &Cedi + new Transition (960, 976), // &ch -> &chi + new Transition (973, 974), // &Ch -> &Chi + new Transition (998, 999), // &circlearrowr -> &circlearrowri + new Transition (1009, 1010), // &circledc -> &circledci + new Transition (1032, 1033), // &CircleM -> &CircleMi + new Transition (1043, 1044), // &CircleT -> &CircleTi + new Transition (1054, 1055), // &cirfn -> &cirfni + new Transition (1059, 1060), // &cirm -> &cirmi + new Transition (1064, 1065), // &cirsc -> &cirsci + new Transition (1072, 1073), // &Clockw -> &Clockwi + new Transition (1122, 1123), // &clubsu -> &clubsui + new Transition (1164, 1183), // &con -> &coni + new Transition (1171, 1179), // &Con -> &Coni + new Transition (1236, 1237), // &CounterClockw -> &CounterClockwi + new Transition (1393, 1394), // &curvearrowr -> &curvearrowri + new Transition (1407, 1415), // &cw -> &cwi + new Transition (1410, 1411), // &cwcon -> &cwconi + new Transition (1425, 1557), // &D -> &Di + new Transition (1432, 1599), // &d -> &di + new Transition (1535, 1536), // &df -> &dfi + new Transition (1560, 1561), // &Diacr -> &Diacri + new Transition (1562, 1563), // &Diacrit -> &Diacriti + new Transition (1593, 1594), // &DiacriticalT -> &DiacriticalTi + new Transition (1613, 1614), // &diamondsu -> &diamondsui + new Transition (1627, 1628), // &Different -> &Differenti + new Transition (1639, 1640), // &dis -> &disi + new Transition (1643, 1645), // &div -> &divi + new Transition (1651, 1652), // ÷ont -> ÷onti + new Transition (1713, 1714), // &dotm -> &dotmi + new Transition (1786, 1787), // &DoubleLeftR -> &DoubleLeftRi + new Transition (1814, 1815), // &DoubleLongLeftR -> &DoubleLongLeftRi + new Transition (1825, 1826), // &DoubleLongR -> &DoubleLongRi + new Transition (1836, 1837), // &DoubleR -> &DoubleRi + new Transition (1872, 1873), // &DoubleVert -> &DoubleVerti + new Transition (1944, 1945), // &downharpoonr -> &downharpoonri + new Transition (1954, 1955), // &DownLeftR -> &DownLeftRi + new Transition (1987, 1988), // &DownR -> &DownRi + new Transition (2072, 2073), // &dtr -> &dtri + new Transition (2097, 2101), // &dz -> &dzi + new Transition (2127, 2142), // &Ec -> &Eci + new Transition (2133, 2139), // &ec -> &eci + new Transition (2204, 2213), // &el -> &eli + new Transition (2323, 2324), // &eps -> &epsi + new Transition (2327, 2328), // &Eps -> &Epsi + new Transition (2340, 2341), // &eqc -> &eqci + new Transition (2350, 2351), // &eqs -> &eqsi + new Transition (2368, 2387), // &Equ -> &Equi + new Transition (2372, 2396), // &equ -> &equi + new Transition (2377, 2378), // &EqualT -> &EqualTi + new Transition (2388, 2389), // &Equil -> &Equili + new Transition (2391, 2392), // &Equilibr -> &Equilibri + new Transition (2418, 2430), // &Es -> &Esi + new Transition (2422, 2433), // &es -> &esi + new Transition (2458, 2462), // &ex -> &exi + new Transition (2466, 2467), // &Ex -> &Exi + new Transition (2477, 2478), // &expectat -> &expectati + new Transition (2487, 2488), // &Exponent -> &Exponenti + new Transition (2497, 2498), // &exponent -> &exponenti + new Transition (2503, 2549), // &f -> &fi + new Transition (2506, 2507), // &fall -> &falli + new Transition (2517, 2554), // &F -> &Fi + new Transition (2530, 2531), // &ff -> &ffi + new Transition (2532, 2533), // &ffil -> &ffili + new Transition (2536, 2537), // &ffl -> &ffli + new Transition (2540, 2541), // &ffll -> &fflli + new Transition (2550, 2551), // &fil -> &fili + new Transition (2588, 2589), // &fjl -> &fjli + new Transition (2596, 2597), // &fll -> &flli + new Transition (2631, 2632), // &Four -> &Fouri + new Transition (2642, 2643), // &fpart -> &fparti + new Transition (2701, 2811), // &g -> &gi + new Transition (2736, 2742), // &Gc -> &Gci + new Transition (2738, 2739), // &Gced -> &Gcedi + new Transition (2746, 2747), // &gc -> &gci + new Transition (2849, 2850), // &gns -> &gnsi + new Transition (2917, 2918), // &GreaterT -> &GreaterTi + new Transition (2927, 2931), // &gs -> &gsi + new Transition (2944, 2947), // >c -> >ci + new Transition (2998, 2999), // >rs -> >rsi + new Transition (3014, 3100), // &H -> &Hi + new Transition (3021, 3022), // &ha -> &hai + new Transition (3030, 3031), // &ham -> &hami + new Transition (3052, 3053), // &harrc -> &harrci + new Transition (3064, 3065), // &Hc -> &Hci + new Transition (3069, 3070), // &hc -> &hci + new Transition (3080, 3081), // &heartsu -> &heartsui + new Transition (3085, 3086), // &hell -> &helli + new Transition (3148, 3149), // &hookr -> &hookri + new Transition (3171, 3172), // &Hor -> &Hori + new Transition (3179, 3180), // &HorizontalL -> &HorizontalLi + new Transition (3243, 3301), // &i -> &ii + new Transition (3250, 3257), // &ic -> &ici + new Transition (3252, 3253), // &Ic -> &Ici + new Transition (3301, 3303), // &ii -> &iii + new Transition (3303, 3304), // &iii -> &iiii + new Transition (3312, 3313), // &iinf -> &iinfi + new Transition (3321, 3322), // &IJl -> &IJli + new Transition (3326, 3327), // &ijl -> &ijli + new Transition (3344, 3345), // &Imag -> &Imagi + new Transition (3352, 3353), // &imagl -> &imagli + new Transition (3373, 3374), // &Impl -> &Impli + new Transition (3385, 3386), // &inf -> &infi + new Transition (3389, 3390), // &infint -> &infinti + new Transition (3428, 3429), // &Intersect -> &Intersecti + new Transition (3444, 3445), // &Inv -> &Invi + new Transition (3446, 3447), // &Invis -> &Invisi + new Transition (3457, 3458), // &InvisibleT -> &InvisibleTi + new Transition (3507, 3511), // &is -> &isi + new Transition (3526, 3534), // &it -> &iti + new Transition (3528, 3529), // &It -> &Iti + new Transition (3556, 3557), // &Jc -> &Jci + new Transition (3562, 3563), // &jc -> &jci + new Transition (3634, 3635), // &Kced -> &Kcedi + new Transition (3640, 3641), // &kced -> &kcedi + new Transition (3785, 3786), // &larrs -> &larrsi + new Transition (3795, 3796), // &lAta -> &lAtai + new Transition (3799, 3800), // &lata -> &latai + new Transition (3850, 3851), // &Lced -> &Lcedi + new Transition (3854, 3859), // &lce -> &lcei + new Transition (3855, 3856), // &lced -> &lcedi + new Transition (3937, 3938), // &LeftArrowR -> &LeftArrowRi + new Transition (3949, 3950), // &leftarrowta -> &leftarrowtai + new Transition (3954, 3955), // &LeftCe -> &LeftCei + new Transition (3956, 3957), // &LeftCeil -> &LeftCeili + new Transition (4030, 4031), // &LeftR -> &LeftRi + new Transition (4041, 4042), // &Leftr -> &Leftri + new Transition (4052, 4053), // &leftr -> &leftri + new Transition (4076, 4077), // &leftrightsqu -> &leftrightsqui + new Transition (4114, 4115), // &leftthreet -> &leftthreeti + new Transition (4120, 4121), // &LeftTr -> &LeftTri + new Transition (4280, 4281), // &lesss -> &lesssi + new Transition (4295, 4296), // &LessT -> &LessTi + new Transition (4301, 4302), // &lf -> &lfi + new Transition (4376, 4377), // &lltr -> &lltri + new Transition (4379, 4380), // &Lm -> &Lmi + new Transition (4385, 4386), // &lm -> &lmi + new Transition (4418, 4419), // &lns -> &lnsi + new Transition (4469, 4470), // &LongLeftR -> &LongLeftRi + new Transition (4480, 4481), // &Longleftr -> &Longleftri + new Transition (4491, 4492), // &longleftr -> &longleftri + new Transition (4509, 4510), // &LongR -> &LongRi + new Transition (4520, 4521), // &Longr -> &Longri + new Transition (4531, 4532), // &longr -> &longri + new Transition (4554, 4555), // &looparrowr -> &looparrowri + new Transition (4573, 4574), // &lot -> &loti + new Transition (4601, 4602), // &LowerR -> &LowerRi + new Transition (4649, 4650), // &lrtr -> &lrtri + new Transition (4652, 4669), // &ls -> &lsi + new Transition (4698, 4715), // < -> <i + new Transition (4700, 4703), // <c -> <ci + new Transition (4731, 4732), // <r -> <ri + new Transition (4767, 4871), // &m -> &mi + new Transition (4781, 4900), // &M -> &Mi + new Transition (4844, 4845), // &Med -> &Medi + new Transition (4855, 4856), // &Mell -> &Melli + new Transition (4882, 4883), // &midc -> &midci + new Transition (4955, 4956), // &mult -> &multi + new Transition (4965, 5240), // &n -> &ni + new Transition (4986, 4990), // &nap -> &napi + new Transition (5035, 5036), // &Nced -> &Ncedi + new Transition (5040, 5041), // &nced -> &ncedi + new Transition (5087, 5088), // &Negat -> &Negati + new Transition (5093, 5094), // &NegativeMed -> &NegativeMedi + new Transition (5104, 5105), // &NegativeTh -> &NegativeThi + new Transition (5126, 5127), // &NegativeVeryTh -> &NegativeVeryThi + new Transition (5136, 5137), // &nequ -> &nequi + new Transition (5140, 5145), // &nes -> &nesi + new Transition (5177, 5178), // &NewL -> &NewLi + new Transition (5182, 5183), // &nex -> &nexi + new Transition (5215, 5216), // &ngs -> &ngsi + new Transition (5290, 5291), // &nLeftr -> &nLeftri + new Transition (5301, 5302), // &nleftr -> &nleftri + new Transition (5328, 5329), // &nls -> &nlsi + new Transition (5336, 5337), // &nltr -> &nltri + new Transition (5343, 5344), // &nm -> &nmi + new Transition (5359, 5360), // &NonBreak -> &NonBreaki + new Transition (5378, 5512), // ¬ -> ¬i + new Transition (5405, 5406), // &NotDoubleVert -> &NotDoubleVerti + new Transition (5427, 5428), // &NotEqualT -> &NotEqualTi + new Transition (5433, 5434), // &NotEx -> &NotExi + new Transition (5487, 5488), // &NotGreaterT -> &NotGreaterTi + new Transition (5533, 5534), // &NotLeftTr -> &NotLeftTri + new Transition (5584, 5585), // &NotLessT -> &NotLessTi + new Transition (5620, 5621), // ¬n -> ¬ni + new Transition (5656, 5671), // &NotR -> &NotRi + new Transition (5676, 5677), // &NotRightTr -> &NotRightTri + new Transition (5762, 5763), // &NotSucceedsT -> &NotSucceedsTi + new Transition (5781, 5782), // &NotT -> &NotTi + new Transition (5803, 5804), // &NotTildeT -> &NotTildeTi + new Transition (5812, 5813), // &NotVert -> &NotVerti + new Transition (5837, 5838), // &npol -> &npoli + new Transition (5855, 5879), // &nr -> &nri + new Transition (5868, 5869), // &nR -> &nRi + new Transition (5890, 5891), // &nrtr -> &nrtri + new Transition (5895, 5927), // &ns -> &nsi + new Transition (5914, 5915), // &nshortm -> &nshortmi + new Transition (5934, 5935), // &nsm -> &nsmi + new Transition (5988, 5998), // &nt -> &nti + new Transition (5992, 5993), // &Nt -> &Nti + new Transition (6006, 6007), // &ntr -> &ntri + new Transition (6021, 6022), // &ntriangler -> &ntriangleri + new Transition (6043, 6078), // &nv -> &nvi + new Transition (6080, 6081), // &nvinf -> &nvinfi + new Transition (6093, 6094), // &nvltr -> &nvltri + new Transition (6103, 6104), // &nvrtr -> &nvrtri + new Transition (6107, 6108), // &nvs -> &nvsi + new Transition (6138, 6234), // &o -> &oi + new Transition (6148, 6149), // &oc -> &oci + new Transition (6152, 6153), // &Oc -> &Oci + new Transition (6163, 6179), // &od -> &odi + new Transition (6191, 6192), // &OEl -> &OEli + new Transition (6196, 6197), // &oel -> &oeli + new Transition (6201, 6202), // &ofc -> &ofci + new Transition (6238, 6252), // &ol -> &oli + new Transition (6243, 6244), // &olc -> &olci + new Transition (6258, 6276), // &Om -> &Omi + new Transition (6263, 6282), // &om -> &omi + new Transition (6342, 6360), // &or -> &ori + new Transition (6399, 6400), // &Ot -> &Oti + new Transition (6405, 6406), // &ot -> &oti + new Transition (6459, 6460), // &OverParenthes -> &OverParenthesi + new Transition (6463, 6543), // &p -> &pi + new Transition (6474, 6475), // &pars -> &parsi + new Transition (6482, 6541), // &P -> &Pi + new Transition (6485, 6486), // &Part -> &Parti + new Transition (6498, 6503), // &per -> &peri + new Transition (6507, 6508), // &perm -> &permi + new Transition (6524, 6525), // &Ph -> &Phi + new Transition (6527, 6528), // &ph -> &phi + new Transition (6570, 6571), // &plusac -> &plusaci + new Transition (6576, 6577), // &plusc -> &plusci + new Transition (6590, 6591), // &PlusM -> &PlusMi + new Transition (6599, 6600), // &pluss -> &plussi + new Transition (6609, 6610), // &Po -> &Poi + new Transition (6622, 6623), // &po -> &poi + new Transition (6625, 6626), // &point -> &pointi + new Transition (6640, 6725), // &Pr -> &Pri + new Transition (6642, 6729), // &pr -> &pri + new Transition (6696, 6697), // &PrecedesT -> &PrecedesTi + new Transition (6717, 6718), // &precns -> &precnsi + new Transition (6721, 6722), // &precs -> &precsi + new Transition (6741, 6742), // &prns -> &prnsi + new Transition (6760, 6761), // &profl -> &profli + new Transition (6775, 6776), // &Proport -> &Proporti + new Transition (6786, 6787), // &prs -> &prsi + new Transition (6795, 6803), // &Ps -> &Psi + new Transition (6799, 6805), // &ps -> &psi + new Transition (6817, 6821), // &q -> &qi + new Transition (6834, 6835), // &qpr -> &qpri + new Transition (6849, 6858), // &quat -> &quati + new Transition (6852, 6853), // &quatern -> &quaterni + new Transition (6876, 7199), // &r -> &ri + new Transition (6886, 7171), // &R -> &Ri + new Transition (6897, 6898), // &rad -> &radi + new Transition (6956, 6957), // &rarrs -> &rarrsi + new Transition (6969, 6970), // &rAta -> &rAtai + new Transition (6973, 6978), // &rat -> &rati + new Transition (6974, 6975), // &rata -> &ratai + new Transition (7034, 7035), // &Rced -> &Rcedi + new Transition (7038, 7043), // &rce -> &rcei + new Transition (7039, 7040), // &rced -> &rcedi + new Transition (7076, 7078), // &real -> &reali + new Transition (7111, 7112), // &ReverseEqu -> &ReverseEqui + new Transition (7113, 7114), // &ReverseEquil -> &ReverseEquili + new Transition (7116, 7117), // &ReverseEquilibr -> &ReverseEquilibri + new Transition (7125, 7126), // &ReverseUpEqu -> &ReverseUpEqui + new Transition (7127, 7128), // &ReverseUpEquil -> &ReverseUpEquili + new Transition (7130, 7131), // &ReverseUpEquilibr -> &ReverseUpEquilibri + new Transition (7135, 7136), // &rf -> &rfi + new Transition (7224, 7225), // &rightarrowta -> &rightarrowtai + new Transition (7229, 7230), // &RightCe -> &RightCei + new Transition (7231, 7232), // &RightCeil -> &RightCeili + new Transition (7314, 7315), // &rightr -> &rightri + new Transition (7328, 7329), // &rightsqu -> &rightsqui + new Transition (7359, 7360), // &rightthreet -> &rightthreeti + new Transition (7365, 7366), // &RightTr -> &RightTri + new Transition (7431, 7432), // &ris -> &risi + new Transition (7465, 7466), // &rnm -> &rnmi + new Transition (7495, 7496), // &rot -> &roti + new Transition (7507, 7508), // &RoundImpl -> &RoundImpli + new Transition (7521, 7522), // &rppol -> &rppoli + new Transition (7531, 7532), // &Rr -> &Rri + new Transition (7567, 7573), // &rt -> &rti + new Transition (7578, 7579), // &rtr -> &rtri + new Transition (7587, 7588), // &rtriltr -> &rtriltri + new Transition (7610, 7833), // &S -> &Si + new Transition (7617, 7838), // &s -> &si + new Transition (7629, 7662), // &Sc -> &Sci + new Transition (7631, 7666), // &sc -> &sci + new Transition (7654, 7655), // &Sced -> &Scedi + new Transition (7658, 7659), // &sced -> &scedi + new Transition (7676, 7677), // &scns -> &scnsi + new Transition (7682, 7683), // &scpol -> &scpoli + new Transition (7687, 7688), // &scs -> &scsi + new Transition (7721, 7722), // &sem -> &semi + new Transition (7730, 7731), // &setm -> &setmi + new Transition (7799, 7800), // &shortm -> &shortmi + new Transition (7812, 7813), // &ShortR -> &ShortRi + new Transition (7887, 7888), // &SmallC -> &SmallCi + new Transition (7894, 7918), // &sm -> &smi + new Transition (7901, 7902), // &smallsetm -> &smallsetmi + new Transition (7962, 7963), // &spadesu -> &spadesui + new Transition (8027, 8028), // &SquareIntersect -> &SquareIntersecti + new Transition (8059, 8060), // &SquareUn -> &SquareUni + new Transition (8086, 8087), // &ssm -> &ssmi + new Transition (8107, 8108), // &stra -> &strai + new Transition (8114, 8115), // &straighteps -> &straightepsi + new Transition (8121, 8122), // &straightph -> &straightphi + new Transition (8169, 8190), // &subs -> &subsi + new Transition (8240, 8241), // &SucceedsT -> &SucceedsTi + new Transition (8261, 8262), // &succns -> &succnsi + new Transition (8265, 8266), // &succs -> &succsi + new Transition (8352, 8367), // &sups -> &supsi + new Transition (8396, 8397), // &szl -> &szli + new Transition (8400, 8544), // &T -> &Ti + new Transition (8404, 8549), // &t -> &ti + new Transition (8432, 8433), // &Tced -> &Tcedi + new Transition (8437, 8438), // &tced -> &tcedi + new Transition (8461, 8493), // &th -> &thi + new Transition (8467, 8507), // &Th -> &Thi + new Transition (8503, 8504), // &thicks -> &thicksi + new Transition (8531, 8532), // &thks -> &thksi + new Transition (8570, 8571), // &TildeT -> &TildeTi + new Transition (8600, 8601), // &topc -> &topci + new Transition (8618, 8619), // &tpr -> &tpri + new Transition (8628, 8633), // &tr -> &tri + new Transition (8655, 8656), // &triangler -> &triangleri + new Transition (8670, 8671), // &trim -> &trimi + new Transition (8676, 8677), // &Tr -> &Tri + new Transition (8693, 8694), // &trit -> &triti + new Transition (8700, 8701), // &trpez -> &trpezi + new Transition (8737, 8738), // &tw -> &twi + new Transition (8757, 8758), // &twoheadr -> &twoheadri + new Transition (8793, 8794), // &Uarroc -> &Uarroci + new Transition (8815, 8816), // &Uc -> &Uci + new Transition (8820, 8821), // &uc -> &uci + new Transition (8849, 8850), // &uf -> &ufi + new Transition (8901, 8902), // &ultr -> &ultri + new Transition (8916, 8945), // &Un -> &Uni + new Transition (8941, 8942), // &UnderParenthes -> &UnderParenthesi + new Transition (9036, 9037), // &UpEqu -> &UpEqui + new Transition (9038, 9039), // &UpEquil -> &UpEquili + new Transition (9041, 9042), // &UpEquilibr -> &UpEquilibri + new Transition (9058, 9059), // &upharpoonr -> &upharpoonri + new Transition (9081, 9082), // &UpperR -> &UpperRi + new Transition (9092, 9093), // &Ups -> &Upsi + new Transition (9095, 9096), // &ups -> &upsi + new Transition (9127, 9145), // &ur -> &uri + new Transition (9140, 9141), // &Ur -> &Uri + new Transition (9150, 9151), // &urtr -> &urtri + new Transition (9161, 9172), // &ut -> &uti + new Transition (9166, 9167), // &Ut -> &Uti + new Transition (9177, 9178), // &utr -> &utri + new Transition (9211, 9212), // &vareps -> &varepsi + new Transition (9226, 9227), // &varnoth -> &varnothi + new Transition (9231, 9235), // &varp -> &varpi + new Transition (9232, 9233), // &varph -> &varphi + new Transition (9252, 9253), // &vars -> &varsi + new Transition (9285, 9286), // &vartr -> &vartri + new Transition (9297, 9298), // &vartriangler -> &vartriangleri + new Transition (9356, 9357), // &vell -> &velli + new Transition (9370, 9374), // &Vert -> &Verti + new Transition (9382, 9383), // &VerticalL -> &VerticalLi + new Transition (9397, 9398), // &VerticalT -> &VerticalTi + new Transition (9405, 9406), // &VeryTh -> &VeryThi + new Transition (9422, 9423), // &vltr -> &vltri + new Transition (9447, 9448), // &vrtr -> &vrtri + new Transition (9477, 9478), // &vz -> &vzi + new Transition (9485, 9486), // &Wc -> &Wci + new Transition (9491, 9492), // &wc -> &wci + new Transition (9496, 9512), // &we -> &wei + new Transition (9548, 9583), // &x -> &xi + new Transition (9549, 9553), // &xc -> &xci + new Transition (9562, 9563), // &xdtr -> &xdtri + new Transition (9565, 9581), // &X -> &Xi + new Transition (9598, 9599), // &xn -> &xni + new Transition (9618, 9619), // &xot -> &xoti + new Transition (9652, 9653), // &xutr -> &xutri + new Transition (9672, 9712), // &y -> &yi + new Transition (9685, 9686), // &Yc -> &Yci + new Transition (9690, 9691), // &yc -> &yci + new Transition (9754, 9825), // &z -> &zi + new Transition (9794, 9795) // &ZeroW -> &ZeroWi + }; + TransitionTable_j = new Transition[11] { + new Transition (0, 3561), // & -> &j + new Transition (1432, 1665), // &d -> &dj + new Transition (2503, 2587), // &f -> &fj + new Transition (2701, 2820), // &g -> &gj + new Transition (2824, 2830), // &gl -> &glj + new Transition (3243, 3325), // &i -> &ij + new Transition (3624, 3672), // &k -> &kj + new Transition (3692, 4342), // &l -> &lj + new Transition (4965, 5252), // &n -> &nj + new Transition (9848, 9849), // &zw -> &zwj + new Transition (9851, 9852) // &zwn -> &zwnj + }; + TransitionTable_k = new Transition[69] { + new Transition (0, 3624), // & -> &k + new Transition (301, 513), // &b -> &bk + new Transition (303, 304), // &bac -> &back + new Transition (333, 334), // &Bac -> &Back + new Transition (361, 362), // &bbr -> &bbrk + new Transition (366, 367), // &bbrktbr -> &bbrktbrk + new Transition (519, 566), // &bl -> &blk + new Transition (521, 522), // &blac -> &black + new Transition (563, 564), // &blan -> &blank + new Transition (576, 577), // &bloc -> &block + new Transition (965, 966), // &chec -> &check + new Transition (970, 971), // &checkmar -> &checkmark + new Transition (1070, 1071), // &Cloc -> &Clock + new Transition (1234, 1235), // &CounterCloc -> &CounterClock + new Transition (1463, 1464), // &db -> &dbk + new Transition (2024, 2025), // &drb -> &drbk + new Transition (2059, 2060), // &Dstro -> &Dstrok + new Transition (2064, 2065), // &dstro -> &dstrok + new Transition (2621, 2626), // &for -> &fork + new Transition (3017, 3018), // &Hace -> &Hacek + new Transition (3020, 3112), // &h -> &hk + new Transition (3136, 3137), // &hoo -> &hook + new Transition (3199, 3200), // &Hstro -> &Hstrok + new Transition (3204, 3205), // &hstro -> &hstrok + new Transition (3436, 3437), // &intlarh -> &intlarhk + new Transition (3539, 3540), // &Iu -> &Iuk + new Transition (3544, 3545), // &iu -> &iuk + new Transition (3608, 3609), // &Ju -> &Juk + new Transition (3613, 3614), // &ju -> &juk + new Transition (3776, 3777), // &larrh -> &larrhk + new Transition (3818, 3819), // &lbbr -> &lbbrk + new Transition (3821, 3828), // &lbr -> &lbrk + new Transition (3823, 3826), // &lbrac -> &lbrack + new Transition (3909, 3910), // &LeftAngleBrac -> &LeftAngleBrack + new Transition (3970, 3971), // &LeftDoubleBrac -> &LeftDoubleBrack + new Transition (4335, 4336), // &lhbl -> &lhblk + new Transition (4431, 4432), // &lobr -> &lobrk + new Transition (4686, 4687), // &Lstro -> &Lstrok + new Transition (4691, 4692), // &lstro -> &lstrok + new Transition (4804, 4805), // &mar -> &mark + new Transition (5068, 5069), // &nearh -> &nearhk + new Transition (5106, 5107), // &NegativeThic -> &NegativeThick + new Transition (5351, 5352), // &NoBrea -> &NoBreak + new Transition (5358, 5359), // &NonBrea -> &NonBreak + new Transition (6114, 6115), // &nwarh -> &nwarhk + new Transition (6444, 6447), // &OverBrac -> &OverBrack + new Transition (6515, 6516), // &perten -> &pertenk + new Transition (6550, 6551), // &pitchfor -> &pitchfork + new Transition (6557, 6563), // &plan -> &plank + new Transition (6558, 6559), // &planc -> &planck + new Transition (6947, 6948), // &rarrh -> &rarrhk + new Transition (7002, 7003), // &rbbr -> &rbbrk + new Transition (7005, 7012), // &rbr -> &rbrk + new Transition (7007, 7010), // &rbrac -> &rbrack + new Transition (7183, 7184), // &RightAngleBrac -> &RightAngleBrack + new Transition (7245, 7246), // &RightDoubleBrac -> &RightDoubleBrack + new Transition (7478, 7479), // &robr -> &robrk + new Transition (7706, 7707), // &searh -> &searhk + new Transition (8378, 8379), // &swarh -> &swarhk + new Transition (8416, 8417), // &tbr -> &tbrk + new Transition (8461, 8527), // &th -> &thk + new Transition (8494, 8495), // &thic -> &thick + new Transition (8508, 8509), // &Thic -> &Thick + new Transition (8611, 8612), // &topfor -> &topfork + new Transition (8729, 8730), // &Tstro -> &Tstrok + new Transition (8734, 8735), // &tstro -> &tstrok + new Transition (8884, 8885), // &uhbl -> &uhblk + new Transition (8926, 8929), // &UnderBrac -> &UnderBrack + new Transition (9208, 9217) // &var -> &vark + }; + TransitionTable_l = new Transition[438] { + new Transition (0, 3692), // & -> &l + new Transition (1, 89), // &A -> &Al + new Transition (8, 79), // &a -> &al + new Transition (50, 51), // &AE -> &AEl + new Transition (55, 56), // &ae -> &ael + new Transition (104, 108), // &ama -> &amal + new Transition (128, 129), // &ands -> &andsl + new Transition (136, 140), // &ang -> &angl + new Transition (217, 218), // &App -> &Appl + new Transition (270, 271), // &Ati -> &Atil + new Transition (276, 277), // &ati -> &atil + new Transition (282, 283), // &Aum -> Ä + new Transition (286, 287), // &aum -> ä + new Transition (301, 519), // &b -> &bl + new Transition (313, 314), // &backepsi -> &backepsil + new Transition (335, 336), // &Backs -> &Backsl + new Transition (417, 418), // &Bernou -> &Bernoul + new Transition (418, 419), // &Bernoul -> &Bernoull + new Transition (460, 461), // &bigop -> &bigopl + new Transition (486, 487), // &bigtriang -> &bigtriangl + new Transition (498, 499), // &bigup -> &bigupl + new Transition (522, 523), // &black -> &blackl + new Transition (543, 544), // &blacktriang -> &blacktriangl + new Transition (545, 552), // &blacktriangle -> &blacktrianglel + new Transition (618, 621), // &boxD -> &boxDl + new Transition (623, 626), // &boxd -> &boxdl + new Transition (662, 663), // &boxp -> &boxpl + new Transition (673, 676), // &boxU -> &boxUl + new Transition (678, 681), // &boxu -> &boxul + new Transition (691, 705), // &boxV -> &boxVl + new Transition (693, 709), // &boxv -> &boxvl + new Transition (757, 758), // &bso -> &bsol + new Transition (767, 768), // &bu -> &bul + new Transition (768, 769), // &bul -> &bull + new Transition (789, 1068), // &C -> &Cl + new Transition (796, 1117), // &c -> &cl + new Transition (830, 831), // &Capita -> &Capital + new Transition (842, 843), // &CapitalDifferentia -> &CapitalDifferential + new Transition (855, 856), // &Cay -> &Cayl + new Transition (878, 879), // &Ccedi -> Ç + new Transition (883, 884), // &ccedi -> ç + new Transition (917, 918), // &cedi -> ¸ + new Transition (922, 923), // &Cedi -> &Cedil + new Transition (923, 924), // &Cedil -> &Cedill + new Transition (981, 986), // &circ -> &circl + new Transition (992, 993), // &circlearrow -> &circlearrowl + new Transition (1021, 1022), // &Circ -> &Circl + new Transition (1038, 1039), // &CircleP -> &CirclePl + new Transition (1089, 1090), // &ClockwiseContourIntegra -> &ClockwiseContourIntegral + new Transition (1096, 1097), // &CloseCur -> &CloseCurl + new Transition (1102, 1103), // &CloseCurlyDoub -> &CloseCurlyDoubl + new Transition (1126, 1127), // &Co -> &Col + new Transition (1131, 1132), // &co -> &col + new Transition (1148, 1153), // &comp -> &compl + new Transition (1197, 1198), // &ContourIntegra -> &ContourIntegral + new Transition (1231, 1232), // &CounterC -> &CounterCl + new Transition (1253, 1254), // &CounterClockwiseContourIntegra -> &CounterClockwiseContourIntegral + new Transition (1292, 1308), // &cu -> &cul + new Transition (1296, 1297), // &cudarr -> &cudarrl + new Transition (1346, 1353), // &cur -> &curl + new Transition (1387, 1388), // &curvearrow -> &curvearrowl + new Transition (1419, 1420), // &cy -> &cyl + new Transition (1432, 1669), // &d -> &dl + new Transition (1433, 1439), // &da -> &dal + new Transition (1463, 1470), // &db -> &dbl + new Transition (1516, 1525), // &de -> &del + new Transition (1519, 1520), // &De -> &Del + new Transition (1552, 1553), // &dhar -> &dharl + new Transition (1565, 1566), // &Diacritica -> &Diacritical + new Transition (1578, 1579), // &DiacriticalDoub -> &DiacriticalDoubl + new Transition (1594, 1595), // &DiacriticalTi -> &DiacriticalTil + new Transition (1629, 1630), // &Differentia -> &Differential + new Transition (1679, 1680), // &do -> &dol + new Transition (1680, 1681), // &dol -> &doll + new Transition (1710, 1711), // &DotEqua -> &DotEqual + new Transition (1719, 1720), // &dotp -> &dotpl + new Transition (1732, 1733), // &doub -> &doubl + new Transition (1745, 1746), // &Doub -> &Doubl + new Transition (1761, 1762), // &DoubleContourIntegra -> &DoubleContourIntegral + new Transition (1875, 1876), // &DoubleVertica -> &DoubleVertical + new Transition (1938, 1939), // &downharpoon -> &downharpoonl + new Transition (2054, 2055), // &dso -> &dsol + new Transition (2089, 2090), // &dwang -> &dwangl + new Transition (2108, 2206), // &E -> &El + new Transition (2115, 2204), // &e -> &el + new Transition (2148, 2149), // &eco -> &ecol + new Transition (2204, 2220), // &el -> &ell + new Transition (2251, 2252), // &EmptySma -> &EmptySmal + new Transition (2252, 2253), // &EmptySmal -> &EmptySmall + new Transition (2269, 2270), // &EmptyVerySma -> &EmptyVerySmal + new Transition (2270, 2271), // &EmptyVerySmal -> &EmptyVerySmall + new Transition (2312, 2319), // &ep -> &epl + new Transition (2316, 2317), // &epars -> &eparsl + new Transition (2324, 2333), // &epsi -> &epsil + new Transition (2328, 2329), // &Epsi -> &Epsil + new Transition (2345, 2346), // &eqco -> &eqcol + new Transition (2350, 2354), // &eqs -> &eqsl + new Transition (2357, 2362), // &eqslant -> &eqslantl + new Transition (2369, 2370), // &Equa -> &Equal + new Transition (2373, 2374), // &equa -> &equal + new Transition (2378, 2379), // &EqualTi -> &EqualTil + new Transition (2387, 2388), // &Equi -> &Equil + new Transition (2406, 2407), // &eqvpars -> &eqvparsl + new Transition (2448, 2449), // &Eum -> Ë + new Transition (2452, 2453), // &eum -> ë + new Transition (2459, 2460), // &exc -> &excl + new Transition (2489, 2490), // &Exponentia -> &Exponential + new Transition (2499, 2500), // &exponentia -> &exponential + new Transition (2503, 2592), // &f -> &fl + new Transition (2504, 2505), // &fa -> &fal + new Transition (2505, 2506), // &fal -> &fall + new Transition (2526, 2527), // &fema -> &femal + new Transition (2530, 2536), // &ff -> &ffl + new Transition (2531, 2532), // &ffi -> &ffil + new Transition (2536, 2540), // &ffl -> &ffll + new Transition (2549, 2550), // &fi -> &fil + new Transition (2554, 2555), // &Fi -> &Fil + new Transition (2555, 2556), // &Fil -> &Fill + new Transition (2561, 2562), // &FilledSma -> &FilledSmal + new Transition (2562, 2563), // &FilledSmal -> &FilledSmall + new Transition (2577, 2578), // &FilledVerySma -> &FilledVerySmal + new Transition (2578, 2579), // &FilledVerySmal -> &FilledVerySmall + new Transition (2587, 2588), // &fj -> &fjl + new Transition (2592, 2596), // &fl -> &fll + new Transition (2617, 2618), // &ForA -> &ForAl + new Transition (2618, 2619), // &ForAl -> &ForAll + new Transition (2622, 2623), // &fora -> &foral + new Transition (2623, 2624), // &foral -> &forall + new Transition (2686, 2687), // &fras -> &frasl + new Transition (2701, 2824), // &g -> &gl + new Transition (2739, 2740), // &Gcedi -> &Gcedil + new Transition (2763, 2767), // &gE -> &gEl + new Transition (2765, 2769), // &ge -> &gel + new Transition (2775, 2776), // &geqs -> &geqsl + new Transition (2781, 2794), // &ges -> &gesl + new Transition (2790, 2792), // &gesdoto -> &gesdotol + new Transition (2813, 2814), // &gime -> &gimel + new Transition (2875, 2876), // &GreaterEqua -> &GreaterEqual + new Transition (2884, 2885), // &GreaterFu -> &GreaterFul + new Transition (2885, 2886), // &GreaterFul -> &GreaterFull + new Transition (2890, 2891), // &GreaterFullEqua -> &GreaterFullEqual + new Transition (2906, 2907), // &GreaterS -> &GreaterSl + new Transition (2914, 2915), // &GreaterSlantEqua -> &GreaterSlantEqual + new Transition (2918, 2919), // &GreaterTi -> &GreaterTil + new Transition (2932, 2936), // &gsim -> &gsiml + new Transition (2942, 2954), // > -> >l + new Transition (2965, 2993), // >r -> >rl + new Transition (2981, 2982), // >req -> >reql + new Transition (2987, 2988), // >reqq -> >reqql + new Transition (3021, 3027), // &ha -> &hal + new Transition (3031, 3032), // &hami -> &hamil + new Transition (3074, 3084), // &he -> &hel + new Transition (3084, 3085), // &hel -> &hell + new Transition (3100, 3101), // &Hi -> &Hil + new Transition (3137, 3138), // &hook -> &hookl + new Transition (3177, 3178), // &Horizonta -> &Horizontal + new Transition (3188, 3192), // &hs -> &hsl + new Transition (3222, 3223), // &HumpEqua -> &HumpEqual + new Transition (3227, 3228), // &hybu -> &hybul + new Transition (3228, 3229), // &hybul -> &hybull + new Transition (3278, 3279), // &iexc -> ¡ + new Transition (3320, 3321), // &IJ -> &IJl + new Transition (3325, 3326), // &ij -> &ijl + new Transition (3341, 3352), // &imag -> &imagl + new Transition (3372, 3373), // &Imp -> &Impl + new Transition (3401, 3433), // &int -> &intl + new Transition (3404, 3405), // &intca -> &intcal + new Transition (3416, 3417), // &Integra -> &Integral + new Transition (3421, 3422), // &interca -> &intercal + new Transition (3448, 3449), // &Invisib -> &Invisibl + new Transition (3529, 3530), // &Iti -> &Itil + new Transition (3534, 3535), // &iti -> &itil + new Transition (3549, 3550), // &Ium -> Ï + new Transition (3552, 3553), // &ium -> ï + new Transition (3635, 3636), // &Kcedi -> &Kcedil + new Transition (3641, 3642), // &kcedi -> &kcedil + new Transition (3692, 4348), // &l -> &ll + new Transition (3698, 4346), // &L -> &Ll + new Transition (3737, 3741), // &lang -> &langl + new Transition (3746, 3747), // &Lap -> &Lapl + new Transition (3766, 3779), // &larr -> &larrl + new Transition (3782, 3783), // &larrp -> &larrpl + new Transition (3789, 3790), // &larrt -> &larrtl + new Transition (3796, 3797), // &lAtai -> &lAtail + new Transition (3800, 3801), // &latai -> &latail + new Transition (3831, 3832), // &lbrks -> &lbrksl + new Transition (3851, 3852), // &Lcedi -> &Lcedil + new Transition (3856, 3857), // &lcedi -> &lcedil + new Transition (3859, 3860), // &lcei -> &lceil + new Transition (3903, 3904), // &LeftAng -> &LeftAngl + new Transition (3926, 4019), // &left -> &leftl + new Transition (3950, 3951), // &leftarrowtai -> &leftarrowtail + new Transition (3955, 3956), // &LeftCei -> &LeftCeil + new Transition (3964, 3965), // &LeftDoub -> &LeftDoubl + new Transition (3998, 3999), // &LeftF -> &LeftFl + new Transition (4124, 4125), // &LeftTriang -> &LeftTriangl + new Transition (4135, 4136), // &LeftTriangleEqua -> &LeftTriangleEqual + new Transition (4191, 4192), // &leqs -> &leqsl + new Transition (4243, 4244), // &LessEqua -> &LessEqual + new Transition (4254, 4255), // &LessFu -> &LessFul + new Transition (4255, 4256), // &LessFul -> &LessFull + new Transition (4260, 4261), // &LessFullEqua -> &LessFullEqual + new Transition (4284, 4285), // &LessS -> &LessSl + new Transition (4292, 4293), // &LessSlantEqua -> &LessSlantEqual + new Transition (4296, 4297), // &LessTi -> &LessTil + new Transition (4301, 4307), // &lf -> &lfl + new Transition (4330, 4332), // &lharu -> &lharul + new Transition (4334, 4335), // &lhb -> &lhbl + new Transition (4436, 4447), // &Long -> &Longl + new Transition (4458, 4459), // &long -> &longl + new Transition (4548, 4549), // &looparrow -> &looparrowl + new Transition (4560, 4569), // &lop -> &lopl + new Transition (4623, 4625), // &lpar -> &lparl + new Transition (4698, 4720), // < -> <l + new Transition (4767, 4909), // &m -> &ml + new Transition (4768, 4772), // &ma -> &mal + new Transition (4789, 4796), // &mapsto -> &mapstol + new Transition (4839, 4840), // &measuredang -> &measuredangl + new Transition (4843, 4854), // &Me -> &Mel + new Transition (4854, 4855), // &Mel -> &Mell + new Transition (4904, 4905), // &MinusP -> &MinusPl + new Transition (4917, 4918), // &mnp -> &mnpl + new Transition (4924, 4925), // &mode -> &model + new Transition (4952, 4954), // &mu -> &mul + new Transition (4965, 5256), // &n -> &nl + new Transition (4967, 4968), // &nab -> &nabl + new Transition (5005, 5006), // &natura -> &natural + new Transition (5036, 5037), // &Ncedi -> &Ncedil + new Transition (5041, 5042), // &ncedi -> &ncedil + new Transition (5204, 5205), // &ngeqs -> &ngeqsl + new Transition (5272, 5326), // &nL -> &nLl + new Transition (5316, 5317), // &nleqs -> &nleqsl + new Transition (5399, 5400), // &NotDoub -> &NotDoubl + new Transition (5408, 5409), // &NotDoubleVertica -> &NotDoubleVertical + new Transition (5414, 5415), // &NotE -> &NotEl + new Transition (5424, 5425), // &NotEqua -> &NotEqual + new Transition (5428, 5429), // &NotEqualTi -> &NotEqualTil + new Transition (5450, 5451), // &NotGreaterEqua -> &NotGreaterEqual + new Transition (5454, 5455), // &NotGreaterFu -> &NotGreaterFul + new Transition (5455, 5456), // &NotGreaterFul -> &NotGreaterFull + new Transition (5460, 5461), // &NotGreaterFullEqua -> &NotGreaterFullEqual + new Transition (5476, 5477), // &NotGreaterS -> &NotGreaterSl + new Transition (5484, 5485), // &NotGreaterSlantEqua -> &NotGreaterSlantEqual + new Transition (5488, 5489), // &NotGreaterTi -> &NotGreaterTil + new Transition (5509, 5510), // &NotHumpEqua -> &NotHumpEqual + new Transition (5537, 5538), // &NotLeftTriang -> &NotLeftTriangl + new Transition (5548, 5549), // &NotLeftTriangleEqua -> &NotLeftTriangleEqual + new Transition (5557, 5558), // &NotLessEqua -> &NotLessEqual + new Transition (5573, 5574), // &NotLessS -> &NotLessSl + new Transition (5581, 5582), // &NotLessSlantEqua -> &NotLessSlantEqual + new Transition (5585, 5586), // &NotLessTi -> &NotLessTil + new Transition (5642, 5643), // &NotPrecedesEqua -> &NotPrecedesEqual + new Transition (5645, 5646), // &NotPrecedesS -> &NotPrecedesSl + new Transition (5653, 5654), // &NotPrecedesSlantEqua -> &NotPrecedesSlantEqual + new Transition (5663, 5664), // &NotReverseE -> &NotReverseEl + new Transition (5680, 5681), // &NotRightTriang -> &NotRightTriangl + new Transition (5691, 5692), // &NotRightTriangleEqua -> &NotRightTriangleEqual + new Transition (5710, 5711), // &NotSquareSubsetEqua -> &NotSquareSubsetEqual + new Transition (5723, 5724), // &NotSquareSupersetEqua -> &NotSquareSupersetEqual + new Transition (5735, 5736), // &NotSubsetEqua -> &NotSubsetEqual + new Transition (5748, 5749), // &NotSucceedsEqua -> &NotSucceedsEqual + new Transition (5751, 5752), // &NotSucceedsS -> &NotSucceedsSl + new Transition (5759, 5760), // &NotSucceedsSlantEqua -> &NotSucceedsSlantEqual + new Transition (5763, 5764), // &NotSucceedsTi -> &NotSucceedsTil + new Transition (5778, 5779), // &NotSupersetEqua -> &NotSupersetEqual + new Transition (5782, 5783), // &NotTi -> &NotTil + new Transition (5790, 5791), // &NotTildeEqua -> &NotTildeEqual + new Transition (5794, 5795), // &NotTildeFu -> &NotTildeFul + new Transition (5795, 5796), // &NotTildeFul -> &NotTildeFull + new Transition (5800, 5801), // &NotTildeFullEqua -> &NotTildeFullEqual + new Transition (5804, 5805), // &NotTildeTi -> &NotTildeTil + new Transition (5815, 5816), // &NotVertica -> &NotVertical + new Transition (5825, 5826), // &npara -> &nparal + new Transition (5826, 5827), // &nparal -> &nparall + new Transition (5828, 5829), // &nparalle -> &nparallel + new Transition (5831, 5832), // &npars -> &nparsl + new Transition (5836, 5837), // &npo -> &npol + new Transition (5921, 5922), // &nshortpara -> &nshortparal + new Transition (5922, 5923), // &nshortparal -> &nshortparall + new Transition (5924, 5925), // &nshortparalle -> &nshortparallel + new Transition (5988, 6003), // &nt -> &ntl + new Transition (5989, 5990), // &ntg -> &ntgl + new Transition (5993, 5994), // &Nti -> &Ntil + new Transition (5998, 5999), // &nti -> &ntil + new Transition (6010, 6011), // &ntriang -> &ntriangl + new Transition (6012, 6013), // &ntriangle -> &ntrianglel + new Transition (6043, 6084), // &nv -> &nvl + new Transition (6138, 6238), // &o -> &ol + new Transition (6169, 6170), // &Odb -> &Odbl + new Transition (6174, 6175), // &odb -> &odbl + new Transition (6186, 6187), // &odso -> &odsol + new Transition (6190, 6191), // &OE -> &OEl + new Transition (6195, 6196), // &oe -> &oel + new Transition (6302, 6336), // &op -> &opl + new Transition (6311, 6312), // &OpenCur -> &OpenCurl + new Transition (6317, 6318), // &OpenCurlyDoub -> &OpenCurlyDoubl + new Transition (6368, 6369), // &ors -> &orsl + new Transition (6378, 6386), // &Os -> &Osl + new Transition (6382, 6391), // &os -> &osl + new Transition (6396, 6397), // &oso -> &osol + new Transition (6400, 6401), // &Oti -> &Otil + new Transition (6406, 6407), // &oti -> &otil + new Transition (6423, 6424), // &Oum -> Ö + new Transition (6427, 6428), // &oum -> ö + new Transition (6463, 6555), // &p -> &pl + new Transition (6467, 6469), // ¶ -> ¶l + new Transition (6469, 6470), // ¶l -> ¶ll + new Transition (6471, 6472), // ¶lle -> ¶llel + new Transition (6474, 6478), // &pars -> &parsl + new Transition (6482, 6587), // &P -> &Pl + new Transition (6487, 6488), // &Partia -> &Partial + new Transition (6508, 6509), // &permi -> &permil + new Transition (6616, 6617), // &Poincarep -> &Poincarepl + new Transition (6666, 6667), // &preccur -> &preccurl + new Transition (6682, 6683), // &PrecedesEqua -> &PrecedesEqual + new Transition (6685, 6686), // &PrecedesS -> &PrecedesSl + new Transition (6693, 6694), // &PrecedesSlantEqua -> &PrecedesSlantEqual + new Transition (6697, 6698), // &PrecedesTi -> &PrecedesTil + new Transition (6754, 6760), // &prof -> &profl + new Transition (6755, 6756), // &profa -> &profal + new Transition (6780, 6781), // &Proportiona -> &Proportional + new Transition (6792, 6793), // &prure -> &prurel + new Transition (6876, 7442), // &r -> &rl + new Transition (6912, 6918), // &rang -> &rangl + new Transition (6932, 6950), // &rarr -> &rarrl + new Transition (6953, 6954), // &rarrp -> &rarrpl + new Transition (6960, 6961), // &Rarrt -> &Rarrtl + new Transition (6963, 6964), // &rarrt -> &rarrtl + new Transition (6970, 6971), // &rAtai -> &rAtail + new Transition (6975, 6976), // &ratai -> &ratail + new Transition (6982, 6983), // &rationa -> &rational + new Transition (7015, 7016), // &rbrks -> &rbrksl + new Transition (7035, 7036), // &Rcedi -> &Rcedil + new Transition (7040, 7041), // &rcedi -> &rcedil + new Transition (7043, 7044), // &rcei -> &rceil + new Transition (7053, 7057), // &rd -> &rdl + new Transition (7075, 7076), // &rea -> &real + new Transition (7102, 7103), // &ReverseE -> &ReverseEl + new Transition (7112, 7113), // &ReverseEqui -> &ReverseEquil + new Transition (7126, 7127), // &ReverseUpEqui -> &ReverseUpEquil + new Transition (7135, 7141), // &rf -> &rfl + new Transition (7160, 7162), // &rharu -> &rharul + new Transition (7177, 7178), // &RightAng -> &RightAngl + new Transition (7202, 7294), // &right -> &rightl + new Transition (7225, 7226), // &rightarrowtai -> &rightarrowtail + new Transition (7230, 7231), // &RightCei -> &RightCeil + new Transition (7239, 7240), // &RightDoub -> &RightDoubl + new Transition (7273, 7274), // &RightF -> &RightFl + new Transition (7369, 7370), // &RightTriang -> &RightTriangl + new Transition (7380, 7381), // &RightTriangleEqua -> &RightTriangleEqual + new Transition (7481, 7491), // &rop -> &ropl + new Transition (7506, 7507), // &RoundImp -> &RoundImpl + new Transition (7520, 7521), // &rppo -> &rppol + new Transition (7579, 7585), // &rtri -> &rtril + new Transition (7590, 7591), // &Ru -> &Rul + new Transition (7594, 7595), // &RuleDe -> &RuleDel + new Transition (7601, 7602), // &ru -> &rul + new Transition (7617, 7878), // &s -> &sl + new Transition (7655, 7656), // &Scedi -> &Scedil + new Transition (7659, 7660), // &scedi -> &scedil + new Transition (7681, 7682), // &scpo -> &scpol + new Transition (7806, 7807), // &shortpara -> &shortparal + new Transition (7807, 7808), // &shortparal -> &shortparall + new Transition (7809, 7810), // &shortparalle -> &shortparallel + new Transition (7847, 7861), // &sim -> &siml + new Transition (7868, 7869), // &simp -> &simpl + new Transition (7884, 7885), // &Sma -> &Smal + new Transition (7885, 7886), // &Smal -> &Small + new Transition (7890, 7891), // &SmallCirc -> &SmallCircl + new Transition (7895, 7896), // &sma -> &smal + new Transition (7896, 7897), // &smal -> &small + new Transition (7915, 7916), // &smepars -> &smeparsl + new Transition (7918, 7921), // &smi -> &smil + new Transition (7936, 7942), // &so -> &sol + new Transition (8042, 8043), // &SquareSubsetEqua -> &SquareSubsetEqual + new Transition (8055, 8056), // &SquareSupersetEqua -> &SquareSupersetEqual + new Transition (8087, 8088), // &ssmi -> &ssmil + new Transition (8115, 8116), // &straightepsi -> &straightepsil + new Transition (8146, 8147), // &submu -> &submul + new Transition (8155, 8156), // &subp -> &subpl + new Transition (8181, 8182), // &SubsetEqua -> &SubsetEqual + new Transition (8210, 8211), // &succcur -> &succcurl + new Transition (8226, 8227), // &SucceedsEqua -> &SucceedsEqual + new Transition (8229, 8230), // &SucceedsS -> &SucceedsSl + new Transition (8237, 8238), // &SucceedsSlantEqua -> &SucceedsSlantEqual + new Transition (8241, 8242), // &SucceedsTi -> &SucceedsTil + new Transition (8284, 8328), // &sup -> &supl + new Transition (8317, 8318), // &SupersetEqua -> &SupersetEqual + new Transition (8322, 8323), // &suphso -> &suphsol + new Transition (8334, 8335), // &supmu -> &supmul + new Transition (8343, 8344), // &supp -> &suppl + new Transition (8395, 8396), // &sz -> &szl + new Transition (8433, 8434), // &Tcedi -> &Tcedil + new Transition (8438, 8439), // &tcedi -> &tcedil + new Transition (8449, 8450), // &te -> &tel + new Transition (8544, 8545), // &Ti -> &Til + new Transition (8549, 8550), // &ti -> &til + new Transition (8557, 8558), // &TildeEqua -> &TildeEqual + new Transition (8561, 8562), // &TildeFu -> &TildeFul + new Transition (8562, 8563), // &TildeFul -> &TildeFull + new Transition (8567, 8568), // &TildeFullEqua -> &TildeFullEqual + new Transition (8571, 8572), // &TildeTi -> &TildeTil + new Transition (8636, 8637), // &triang -> &triangl + new Transition (8638, 8645), // &triangle -> &trianglel + new Transition (8678, 8679), // &Trip -> &Tripl + new Transition (8685, 8686), // &trip -> &tripl + new Transition (8746, 8747), // &twohead -> &twoheadl + new Transition (8775, 8887), // &u -> &ul + new Transition (8835, 8836), // &Udb -> &Udbl + new Transition (8840, 8841), // &udb -> &udbl + new Transition (8878, 8879), // &uhar -> &uharl + new Transition (8883, 8884), // &uhb -> &uhbl + new Transition (8909, 8914), // &um -> ¨ + new Transition (8949, 8950), // &UnionP -> &UnionPl + new Transition (8983, 9064), // &up -> &upl + new Transition (9037, 9038), // &UpEqui -> &UpEquil + new Transition (9052, 9053), // &upharpoon -> &upharpoonl + new Transition (9093, 9100), // &Upsi -> &Upsil + new Transition (9096, 9104), // &upsi -> &upsil + new Transition (9167, 9168), // &Uti -> &Util + new Transition (9172, 9173), // &uti -> &util + new Transition (9188, 9189), // &Uum -> Ü + new Transition (9191, 9192), // &uum -> ü + new Transition (9197, 9198), // &uwang -> &uwangl + new Transition (9201, 9420), // &v -> &vl + new Transition (9212, 9213), // &varepsi -> &varepsil + new Transition (9289, 9290), // &vartriang -> &vartriangl + new Transition (9291, 9292), // &vartriangle -> &vartrianglel + new Transition (9328, 9340), // &Vdash -> &Vdashl + new Transition (9345, 9355), // &ve -> &vel + new Transition (9355, 9356), // &vel -> &vell + new Transition (9376, 9377), // &Vertica -> &Vertical + new Transition (9398, 9399), // &VerticalTi -> &VerticalTil + new Transition (9548, 9585), // &x -> &xl + new Transition (9611, 9614), // &xop -> &xopl + new Transition (9646, 9647), // &xup -> &xupl + new Transition (9741, 9742), // &Yum -> &Yuml + new Transition (9744, 9745) // &yum -> ÿ + }; + TransitionTable_m = new Transition[177] { + new Transition (0, 4767), // & -> &m + new Transition (1, 98), // &A -> &Am + new Transition (8, 103), // &a -> &am + new Transition (83, 84), // &alefsy -> &alefsym + new Transition (136, 143), // &ang -> &angm + new Transition (262, 263), // &asy -> &asym + new Transition (281, 282), // &Au -> &Aum + new Transition (285, 286), // &au -> &aum + new Transition (320, 321), // &backpri -> &backprim + new Transition (325, 326), // &backsi -> &backsim + new Transition (384, 399), // &be -> &bem + new Transition (466, 467), // &bigoti -> &bigotim + new Transition (605, 606), // &botto -> &bottom + new Transition (613, 656), // &box -> &boxm + new Transition (668, 669), // &boxti -> &boxtim + new Transition (721, 722), // &bpri -> &bprim + new Transition (748, 749), // &bse -> &bsem + new Transition (752, 753), // &bsi -> &bsim + new Transition (767, 774), // &bu -> &bum + new Transition (781, 782), // &Bu -> &Bum + new Transition (904, 905), // &ccupss -> &ccupssm + new Transition (915, 927), // &ce -> &cem + new Transition (966, 968), // &check -> &checkm + new Transition (979, 1059), // &cir -> &cirm + new Transition (1044, 1045), // &CircleTi -> &CircleTim + new Transition (1131, 1142), // &co -> &com + new Transition (1142, 1143), // &com -> &comm + new Transition (1154, 1155), // &comple -> &complem + new Transition (1349, 1351), // &curarr -> &curarrm + new Transition (1516, 1529), // &de -> &dem + new Transition (1558, 1603), // &Dia -> &Diam + new Transition (1600, 1601), // &dia -> &diam + new Transition (1634, 1635), // &diga -> &digam + new Transition (1635, 1636), // &digam -> &digamm + new Transition (1652, 1653), // ÷onti -> ÷ontim + new Transition (1694, 1713), // &dot -> &dotm + new Transition (2108, 2228), // &E -> &Em + new Transition (2115, 2233), // &e -> &em + new Transition (2207, 2208), // &Ele -> &Elem + new Transition (2249, 2250), // &EmptyS -> &EmptySm + new Transition (2267, 2268), // &EmptyVeryS -> &EmptyVerySm + new Transition (2351, 2352), // &eqsi -> &eqsim + new Transition (2393, 2394), // &Equilibriu -> &Equilibrium + new Transition (2430, 2431), // &Esi -> &Esim + new Transition (2433, 2434), // &esi -> &esim + new Transition (2447, 2448), // &Eu -> &Eum + new Transition (2451, 2452), // &eu -> &eum + new Transition (2524, 2525), // &fe -> &fem + new Transition (2559, 2560), // &FilledS -> &FilledSm + new Transition (2575, 2576), // &FilledVeryS -> &FilledVerySm + new Transition (2702, 2714), // &ga -> &gam + new Transition (2709, 2710), // &Ga -> &Gam + new Transition (2710, 2711), // &Gam -> &Gamm + new Transition (2714, 2715), // &gam -> &gamm + new Transition (2811, 2812), // &gi -> &gim + new Transition (2850, 2851), // &gnsi -> &gnsim + new Transition (2931, 2932), // &gsi -> &gsim + new Transition (2999, 3000), // >rsi -> >rsim + new Transition (3021, 3030), // &ha -> &ham + new Transition (3126, 3131), // &ho -> &hom + new Transition (3207, 3208), // &Hu -> &Hum + new Transition (3215, 3216), // &HumpDownHu -> &HumpDownHum + new Transition (3236, 3330), // &I -> &Im + new Transition (3243, 3336), // &i -> &im + new Transition (3452, 3453), // &InvisibleCo -> &InvisibleCom + new Transition (3453, 3454), // &InvisibleCom -> &InvisibleComm + new Transition (3458, 3459), // &InvisibleTi -> &InvisibleTim + new Transition (3539, 3549), // &Iu -> &Ium + new Transition (3544, 3552), // &iu -> &ium + new Transition (3561, 3577), // &j -> &jm + new Transition (3692, 4385), // &l -> &lm + new Transition (3698, 4379), // &L -> &Lm + new Transition (3699, 3723), // &La -> &Lam + new Transition (3705, 3728), // &la -> &lam + new Transition (3711, 3712), // &lae -> &laem + new Transition (3786, 3787), // &larrsi -> &larrsim + new Transition (4115, 4116), // &leftthreeti -> &leftthreetim + new Transition (4281, 4282), // &lesssi -> &lesssim + new Transition (4419, 4420), // &lnsi -> &lnsim + new Transition (4458, 4502), // &long -> &longm + new Transition (4574, 4575), // &loti -> &lotim + new Transition (4628, 4646), // &lr -> &lrm + new Transition (4669, 4670), // &lsi -> &lsim + new Transition (4715, 4716), // <i -> <im + new Transition (4810, 4811), // &mco -> &mcom + new Transition (4811, 4812), // &mcom -> &mcomm + new Transition (4846, 4847), // &Mediu -> &Medium + new Transition (4952, 4961), // &mu -> &mum + new Transition (4956, 4957), // &multi -> &multim + new Transition (4965, 5343), // &n -> &nm + new Transition (5014, 5015), // &nbu -> &nbum + new Transition (5095, 5096), // &NegativeMediu -> &NegativeMedium + new Transition (5145, 5146), // &nesi -> &nesim + new Transition (5216, 5217), // &ngsi -> &ngsim + new Transition (5329, 5330), // &nlsi -> &nlsim + new Transition (5416, 5417), // &NotEle -> &NotElem + new Transition (5494, 5495), // &NotHu -> &NotHum + new Transition (5502, 5503), // &NotHumpDownHu -> &NotHumpDownHum + new Transition (5665, 5666), // &NotReverseEle -> &NotReverseElem + new Transition (5895, 5934), // &ns -> &nsm + new Transition (5913, 5914), // &nshort -> &nshortm + new Transition (5927, 5928), // &nsi -> &nsim + new Transition (6032, 6034), // &nu -> &num + new Transition (6108, 6109), // &nvsi -> &nvsim + new Transition (6131, 6258), // &O -> &Om + new Transition (6138, 6263), // &o -> &om + new Transition (6227, 6232), // &oh -> &ohm + new Transition (6348, 6358), // &ord -> º + new Transition (6400, 6411), // &Oti -> &Otim + new Transition (6406, 6415), // &oti -> &otim + new Transition (6422, 6423), // &Ou -> &Oum + new Transition (6426, 6427), // &ou -> &oum + new Transition (6463, 6607), // &p -> &pm + new Transition (6475, 6476), // &parsi -> &parsim + new Transition (6498, 6507), // &per -> &perm + new Transition (6527, 6532), // &ph -> &phm + new Transition (6532, 6533), // &phm -> &phmm + new Transition (6567, 6596), // &plus -> &plusm + new Transition (6600, 6601), // &plussi -> &plussim + new Transition (6718, 6719), // &precnsi -> &precnsim + new Transition (6722, 6723), // &precsi -> &precsim + new Transition (6725, 6726), // &Pri -> &Prim + new Transition (6729, 6730), // &pri -> &prim + new Transition (6742, 6743), // &prnsi -> &prnsim + new Transition (6787, 6788), // &prsi -> &prsim + new Transition (6835, 6836), // &qpri -> &qprim + new Transition (6876, 7453), // &r -> &rm + new Transition (6901, 6902), // &rae -> &raem + new Transition (6957, 6958), // &rarrsi -> &rarrsim + new Transition (7104, 7105), // &ReverseEle -> &ReverseElem + new Transition (7118, 7119), // &ReverseEquilibriu -> &ReverseEquilibrium + new Transition (7132, 7133), // &ReverseUpEquilibriu -> &ReverseUpEquilibrium + new Transition (7360, 7361), // &rightthreeti -> &rightthreetim + new Transition (7442, 7451), // &rl -> &rlm + new Transition (7464, 7465), // &rn -> &rnm + new Transition (7496, 7497), // &roti -> &rotim + new Transition (7504, 7505), // &RoundI -> &RoundIm + new Transition (7573, 7574), // &rti -> &rtim + new Transition (7610, 7883), // &S -> &Sm + new Transition (7617, 7894), // &s -> &sm + new Transition (7677, 7678), // &scnsi -> &scnsim + new Transition (7688, 7689), // &scsi -> &scsim + new Transition (7703, 7721), // &se -> &sem + new Transition (7729, 7730), // &set -> &setm + new Transition (7798, 7799), // &short -> &shortm + new Transition (7834, 7835), // &Sig -> &Sigm + new Transition (7838, 7847), // &si -> &sim + new Transition (7839, 7840), // &sig -> &sigm + new Transition (7900, 7901), // &smallset -> &smallsetm + new Transition (8077, 8086), // &ss -> &ssm + new Transition (8082, 8083), // &sset -> &ssetm + new Transition (8127, 8275), // &Su -> &Sum + new Transition (8130, 8277), // &su -> &sum + new Transition (8131, 8145), // &sub -> &subm + new Transition (8190, 8191), // &subsi -> &subsim + new Transition (8262, 8263), // &succnsi -> &succnsim + new Transition (8266, 8267), // &succsi -> &succsim + new Transition (8284, 8333), // &sup -> &supm + new Transition (8367, 8368), // &supsi -> &supsim + new Transition (8488, 8489), // &thetasy -> &thetasym + new Transition (8504, 8505), // &thicksi -> &thicksim + new Transition (8532, 8533), // &thksi -> &thksim + new Transition (8549, 8576), // &ti -> &tim + new Transition (8619, 8620), // &tpri -> &tprim + new Transition (8633, 8670), // &tri -> &trim + new Transition (8694, 8695), // &triti -> &tritim + new Transition (8702, 8703), // &trpeziu -> &trpezium + new Transition (8768, 8904), // &U -> &Um + new Transition (8775, 8909), // &u -> &um + new Transition (9043, 9044), // &UpEquilibriu -> &UpEquilibrium + new Transition (9182, 9191), // &uu -> &uum + new Transition (9187, 9188), // &Uu -> &Uum + new Transition (9254, 9255), // &varsig -> &varsigm + new Transition (9548, 9594), // &x -> &xm + new Transition (9619, 9620), // &xoti -> &xotim + new Transition (9736, 9744), // &yu -> &yum + new Transition (9740, 9741) // &Yu -> &Yum + }; + TransitionTable_n = new Transition[303] { + new Transition (0, 4965), // & -> &n + new Transition (1, 116), // &A -> &An + new Transition (8, 119), // &a -> &an + new Transition (122, 123), // &anda -> &andan + new Transition (185, 186), // &Aogo -> &Aogon + new Transition (190, 191), // &aogo -> &aogon + new Transition (221, 222), // &ApplyFu -> &ApplyFun + new Transition (226, 227), // &ApplyFunctio -> &ApplyFunction + new Transition (238, 239), // &Ari -> &Arin + new Transition (243, 244), // &ari -> &arin + new Transition (257, 258), // &Assig -> &Assign + new Transition (291, 292), // &awco -> &awcon + new Transition (293, 294), // &awconi -> &awconin + new Transition (297, 298), // &awi -> &awin + new Transition (301, 579), // &b -> &bn + new Transition (306, 307), // &backco -> &backcon + new Transition (315, 316), // &backepsilo -> &backepsilon + new Transition (370, 371), // &bco -> &bcon + new Transition (409, 410), // &ber -> &bern + new Transition (414, 415), // &Ber -> &Bern + new Transition (433, 434), // &betwee -> &between + new Transition (484, 485), // &bigtria -> &bigtrian + new Transition (491, 492), // &bigtriangledow -> &bigtriangledown + new Transition (520, 563), // &bla -> &blan + new Transition (526, 527), // &blackloze -> &blacklozen + new Transition (541, 542), // &blacktria -> &blacktrian + new Transition (549, 550), // &blacktriangledow -> &blacktriangledown + new Transition (657, 658), // &boxmi -> &boxmin + new Transition (807, 808), // &capa -> &capan + new Transition (838, 839), // &CapitalDiffere -> &CapitalDifferen + new Transition (852, 853), // &caro -> &caron + new Transition (869, 870), // &Ccaro -> &Ccaron + new Transition (873, 874), // &ccaro -> &ccaron + new Transition (894, 895), // &Cco -> &Ccon + new Transition (896, 897), // &Cconi -> &Cconin + new Transition (915, 933), // &ce -> &cen + new Transition (920, 936), // &Ce -> &Cen + new Transition (1033, 1034), // &CircleMi -> &CircleMin + new Transition (1053, 1054), // &cirf -> &cirfn + new Transition (1055, 1056), // &cirfni -> &cirfnin + new Transition (1077, 1078), // &ClockwiseCo -> &ClockwiseCon + new Transition (1083, 1084), // &ClockwiseContourI -> &ClockwiseContourIn + new Transition (1126, 1171), // &Co -> &Con + new Transition (1128, 1129), // &Colo -> &Colon + new Transition (1131, 1164), // &co -> &con + new Transition (1133, 1134), // &colo -> &colon + new Transition (1150, 1151), // &compf -> &compfn + new Transition (1156, 1157), // &compleme -> &complemen + new Transition (1175, 1176), // &Congrue -> &Congruen + new Transition (1179, 1180), // &Coni -> &Conin + new Transition (1183, 1184), // &coni -> &conin + new Transition (1191, 1192), // &ContourI -> &ContourIn + new Transition (1226, 1227), // &Cou -> &Coun + new Transition (1241, 1242), // &CounterClockwiseCo -> &CounterClockwiseCon + new Transition (1247, 1248), // &CounterClockwiseContourI -> &CounterClockwiseContourIn + new Transition (1378, 1379), // &curre -> ¤ + new Transition (1409, 1410), // &cwco -> &cwcon + new Transition (1411, 1412), // &cwconi -> &cwconin + new Transition (1415, 1416), // &cwi -> &cwin + new Transition (1477, 1478), // &Dcaro -> &Dcaron + new Transition (1483, 1484), // &dcaro -> &dcaron + new Transition (1604, 1605), // &Diamo -> &Diamon + new Transition (1608, 1609), // &diamo -> &diamon + new Transition (1625, 1626), // &Differe -> &Differen + new Transition (1640, 1641), // &disi -> &disin + new Transition (1649, 1650), // ÷o -> ÷on + new Transition (1657, 1658), // &divo -> &divon + new Transition (1672, 1673), // &dlcor -> &dlcorn + new Transition (1714, 1715), // &dotmi -> &dotmin + new Transition (1749, 1750), // &DoubleCo -> &DoubleCon + new Transition (1755, 1756), // &DoubleContourI -> &DoubleContourIn + new Transition (1768, 1769), // &DoubleDow -> &DoubleDown + new Transition (1801, 1802), // &DoubleLo -> &DoubleLon + new Transition (1861, 1862), // &DoubleUpDow -> &DoubleUpDown + new Transition (1881, 1882), // &Dow -> &Down + new Transition (1895, 1896), // &dow -> &down + new Transition (1923, 1924), // &downdow -> &downdown + new Transition (1937, 1938), // &downharpoo -> &downharpoon + new Transition (2033, 2034), // &drcor -> &drcorn + new Transition (2087, 2088), // &dwa -> &dwan + new Transition (2115, 2290), // &e -> &en + new Transition (2130, 2131), // &Ecaro -> &Ecaron + new Transition (2136, 2137), // &ecaro -> &ecaron + new Transition (2150, 2151), // &ecolo -> &ecolon + new Transition (2209, 2210), // &Eleme -> &Elemen + new Transition (2213, 2214), // &eli -> &elin + new Transition (2298, 2299), // &Eogo -> &Eogon + new Transition (2303, 2304), // &eogo -> &eogon + new Transition (2330, 2331), // &Epsilo -> &Epsilon + new Transition (2334, 2335), // &epsilo -> &epsilon + new Transition (2347, 2348), // &eqcolo -> &eqcolon + new Transition (2355, 2356), // &eqsla -> &eqslan + new Transition (2479, 2480), // &expectatio -> &expectation + new Transition (2483, 2484), // &Expo -> &Expon + new Transition (2485, 2486), // &Expone -> &Exponen + new Transition (2493, 2494), // &expo -> &expon + new Transition (2495, 2496), // &expone -> &exponen + new Transition (2503, 2604), // &f -> &fn + new Transition (2507, 2508), // &falli -> &fallin + new Transition (2600, 2601), // &flt -> &fltn + new Transition (2643, 2644), // &fparti -> &fpartin + new Transition (2690, 2691), // &frow -> &frown + new Transition (2701, 2832), // &g -> &gn + new Transition (2777, 2778), // &geqsla -> &geqslan + new Transition (2908, 2909), // &GreaterSla -> &GreaterSlan + new Transition (3002, 3011), // &gv -> &gvn + new Transition (3005, 3006), // &gvert -> &gvertn + new Transition (3091, 3092), // &herco -> &hercon + new Transition (3174, 3175), // &Horizo -> &Horizon + new Transition (3180, 3181), // &HorizontalLi -> &HorizontalLin + new Transition (3212, 3213), // &HumpDow -> &HumpDown + new Transition (3233, 3234), // &hyphe -> &hyphen + new Transition (3236, 3398), // &I -> &In + new Transition (3243, 3378), // &i -> &in + new Transition (3301, 3311), // &ii -> &iin + new Transition (3303, 3308), // &iii -> &iiin + new Transition (3304, 3305), // &iiii -> &iiiin + new Transition (3313, 3314), // &iinfi -> &iinfin + new Transition (3345, 3346), // &Imagi -> &Imagin + new Transition (3353, 3354), // &imagli -> &imaglin + new Transition (3386, 3387), // &infi -> &infin + new Transition (3430, 3431), // &Intersectio -> &Intersection + new Transition (3473, 3474), // &Iogo -> &Iogon + new Transition (3477, 3478), // &iogo -> &iogon + new Transition (3511, 3512), // &isi -> &isin + new Transition (3657, 3658), // &kgree -> &kgreen + new Transition (3692, 4401), // &l -> &ln + new Transition (3699, 3733), // &La -> &Lan + new Transition (3705, 3736), // &la -> &lan + new Transition (3720, 3721), // &lagra -> &lagran + new Transition (3840, 3841), // &Lcaro -> &Lcaron + new Transition (3846, 3847), // &lcaro -> &lcaron + new Transition (3901, 3902), // &LeftA -> &LeftAn + new Transition (3957, 3958), // &LeftCeili -> &LeftCeilin + new Transition (3975, 3976), // &LeftDow -> &LeftDown + new Transition (4009, 4010), // &leftharpoo -> &leftharpoon + new Transition (4013, 4014), // &leftharpoondow -> &leftharpoondown + new Transition (4070, 4071), // &leftrightharpoo -> &leftrightharpoon + new Transition (4122, 4123), // &LeftTria -> &LeftTrian + new Transition (4142, 4143), // &LeftUpDow -> &LeftUpDown + new Transition (4193, 4194), // &leqsla -> &leqslan + new Transition (4286, 4287), // &LessSla -> &LessSlan + new Transition (4356, 4357), // &llcor -> &llcorn + new Transition (4422, 4457), // &lo -> &lon + new Transition (4423, 4424), // &loa -> &loan + new Transition (4434, 4435), // &Lo -> &Lon + new Transition (4614, 4615), // &loze -> &lozen + new Transition (4635, 4636), // &lrcor -> &lrcorn + new Transition (4755, 4764), // &lv -> &lvn + new Transition (4758, 4759), // &lvert -> &lvertn + new Transition (4767, 4916), // &m -> &mn + new Transition (4793, 4794), // &mapstodow -> &mapstodown + new Transition (4837, 4838), // &measureda -> &measuredan + new Transition (4856, 4857), // &Melli -> &Mellin + new Transition (4871, 4890), // &mi -> &min + new Transition (4900, 4901), // &Mi -> &Min + new Transition (4966, 4983), // &na -> &nan + new Transition (5027, 5028), // &Ncaro -> &Ncaron + new Transition (5031, 5032), // &ncaro -> &ncaron + new Transition (5044, 5045), // &nco -> &ncon + new Transition (5105, 5114), // &NegativeThi -> &NegativeThin + new Transition (5127, 5128), // &NegativeVeryThi -> &NegativeVeryThin + new Transition (5178, 5179), // &NewLi -> &NewLin + new Transition (5206, 5207), // &ngeqsla -> &ngeqslan + new Transition (5318, 5319), // &nleqsla -> &nleqslan + new Transition (5347, 5354), // &No -> &Non + new Transition (5360, 5361), // &NonBreaki -> &NonBreakin + new Transition (5378, 5620), // ¬ -> ¬n + new Transition (5381, 5382), // &NotCo -> &NotCon + new Transition (5386, 5387), // &NotCongrue -> &NotCongruen + new Transition (5418, 5419), // &NotEleme -> &NotElemen + new Transition (5478, 5479), // &NotGreaterSla -> &NotGreaterSlan + new Transition (5499, 5500), // &NotHumpDow -> &NotHumpDown + new Transition (5512, 5513), // ¬i -> ¬in + new Transition (5535, 5536), // &NotLeftTria -> &NotLeftTrian + new Transition (5575, 5576), // &NotLessSla -> &NotLessSlan + new Transition (5647, 5648), // &NotPrecedesSla -> &NotPrecedesSlan + new Transition (5667, 5668), // &NotReverseEleme -> &NotReverseElemen + new Transition (5678, 5679), // &NotRightTria -> &NotRightTrian + new Transition (5753, 5754), // &NotSucceedsSla -> &NotSucceedsSlan + new Transition (5838, 5839), // &npoli -> &npolin + new Transition (6008, 6009), // &ntria -> &ntrian + new Transition (6078, 6079), // &nvi -> &nvin + new Transition (6081, 6082), // &nvinfi -> &nvinfin + new Transition (6111, 6126), // &nw -> &nwn + new Transition (6211, 6212), // &ogo -> &ogon + new Transition (6234, 6235), // &oi -> &oin + new Transition (6252, 6253), // &oli -> &olin + new Transition (6279, 6280), // &Omicro -> &Omicron + new Transition (6282, 6290), // &omi -> &omin + new Transition (6285, 6286), // &omicro -> &omicron + new Transition (6307, 6308), // &Ope -> &Open + new Transition (6454, 6455), // &OverPare -> &OverParen + new Transition (6499, 6500), // &perc -> &percn + new Transition (6514, 6515), // &perte -> &perten + new Transition (6537, 6538), // &pho -> &phon + new Transition (6556, 6557), // &pla -> &plan + new Transition (6591, 6592), // &PlusMi -> &PlusMin + new Transition (6596, 6597), // &plusm -> ± + new Transition (6610, 6611), // &Poi -> &Poin + new Transition (6618, 6619), // &Poincarepla -> &Poincareplan + new Transition (6623, 6624), // &poi -> &poin + new Transition (6626, 6627), // &pointi -> &pointin + new Transition (6636, 6637), // &pou -> &poun + new Transition (6642, 6735), // &pr -> &prn + new Transition (6655, 6705), // &prec -> &precn + new Transition (6687, 6688), // &PrecedesSla -> &PrecedesSlan + new Transition (6761, 6762), // &profli -> &proflin + new Transition (6777, 6778), // &Proportio -> &Proportion + new Transition (6807, 6808), // &pu -> &pun + new Transition (6821, 6822), // &qi -> &qin + new Transition (6851, 6852), // &quater -> &quatern + new Transition (6854, 6855), // &quaternio -> &quaternion + new Transition (6858, 6859), // &quati -> &quatin + new Transition (6876, 7464), // &r -> &rn + new Transition (6882, 6911), // &ra -> &ran + new Transition (6887, 6908), // &Ra -> &Ran + new Transition (6979, 6981), // &ratio -> &ration + new Transition (7024, 7025), // &Rcaro -> &Rcaron + new Transition (7030, 7031), // &rcaro -> &rcaron + new Transition (7078, 7079), // &reali -> &realin + new Transition (7106, 7107), // &ReverseEleme -> &ReverseElemen + new Transition (7175, 7176), // &RightA -> &RightAn + new Transition (7199, 7428), // &ri -> &rin + new Transition (7232, 7233), // &RightCeili -> &RightCeilin + new Transition (7250, 7251), // &RightDow -> &RightDown + new Transition (7284, 7285), // &rightharpoo -> &rightharpoon + new Transition (7288, 7289), // &rightharpoondow -> &rightharpoondown + new Transition (7310, 7311), // &rightleftharpoo -> &rightleftharpoon + new Transition (7367, 7368), // &RightTria -> &RightTrian + new Transition (7387, 7388), // &RightUpDow -> &RightUpDown + new Transition (7432, 7433), // &risi -> &risin + new Transition (7470, 7471), // &roa -> &roan + new Transition (7501, 7502), // &Rou -> &Roun + new Transition (7522, 7523), // &rppoli -> &rppolin + new Transition (7631, 7670), // &sc -> &scn + new Transition (7638, 7639), // &Scaro -> &Scaron + new Transition (7642, 7643), // &scaro -> &scaron + new Transition (7683, 7684), // &scpoli -> &scpolin + new Transition (7730, 7736), // &setm -> &setmn + new Transition (7731, 7732), // &setmi -> &setmin + new Transition (7748, 7749), // &sfrow -> &sfrown + new Transition (7778, 7779), // &ShortDow -> &ShortDown + new Transition (7847, 7865), // &sim -> &simn + new Transition (7902, 7903), // &smallsetmi -> &smallsetmin + new Transition (8019, 8020), // &SquareI -> &SquareIn + new Transition (8029, 8030), // &SquareIntersectio -> &SquareIntersection + new Transition (8058, 8059), // &SquareU -> &SquareUn + new Transition (8061, 8062), // &SquareUnio -> &SquareUnion + new Transition (8083, 8084), // &ssetm -> &ssetmn + new Transition (8106, 8124), // &str -> &strn + new Transition (8117, 8118), // &straightepsilo -> &straightepsilon + new Transition (8130, 8279), // &su -> &sun + new Transition (8131, 8150), // &sub -> &subn + new Transition (8171, 8184), // &subset -> &subsetn + new Transition (8199, 8249), // &succ -> &succn + new Transition (8231, 8232), // &SucceedsSla -> &SucceedsSlan + new Transition (8284, 8338), // &sup -> &supn + new Transition (8354, 8361), // &supset -> &supsetn + new Transition (8375, 8390), // &sw -> &swn + new Transition (8422, 8423), // &Tcaro -> &Tcaron + new Transition (8428, 8429), // &tcaro -> &tcaron + new Transition (8493, 8516), // &thi -> &thin + new Transition (8507, 8520), // &Thi -> &Thin + new Transition (8541, 8542), // &thor -> þ + new Transition (8549, 8587), // &ti -> &tin + new Transition (8634, 8635), // &tria -> &trian + new Transition (8642, 8643), // &triangledow -> &triangledown + new Transition (8671, 8672), // &trimi -> &trimin + new Transition (8768, 8916), // &U -> &Un + new Transition (8890, 8891), // &ulcor -> &ulcorn + new Transition (8936, 8937), // &UnderPare -> &UnderParen + new Transition (8946, 8947), // &Unio -> &Union + new Transition (8956, 8957), // &Uogo -> &Uogon + new Transition (8961, 8962), // &uogo -> &uogon + new Transition (8996, 8997), // &UpArrowDow -> &UpArrowDown + new Transition (9006, 9007), // &UpDow -> &UpDown + new Transition (9016, 9017), // &Updow -> &Updown + new Transition (9026, 9027), // &updow -> &updown + new Transition (9051, 9052), // &upharpoo -> &upharpoon + new Transition (9101, 9102), // &Upsilo -> &Upsilon + new Transition (9105, 9106), // &upsilo -> &upsilon + new Transition (9130, 9131), // &urcor -> &urcorn + new Transition (9141, 9142), // &Uri -> &Urin + new Transition (9145, 9146), // &uri -> &urin + new Transition (9195, 9196), // &uwa -> &uwan + new Transition (9201, 9425), // &v -> &vn + new Transition (9202, 9203), // &va -> &van + new Transition (9208, 9223), // &var -> &varn + new Transition (9214, 9215), // &varepsilo -> &varepsilon + new Transition (9227, 9228), // &varnothi -> &varnothin + new Transition (9262, 9263), // &varsubset -> &varsubsetn + new Transition (9272, 9273), // &varsupset -> &varsupsetn + new Transition (9287, 9288), // &vartria -> &vartrian + new Transition (9383, 9384), // &VerticalLi -> &VerticalLin + new Transition (9406, 9407), // &VeryThi -> &VeryThin + new Transition (9459, 9460), // &vsub -> &vsubn + new Transition (9465, 9466), // &vsup -> &vsupn + new Transition (9548, 9598), // &x -> &xn + new Transition (9699, 9700), // &ye -> ¥ + new Transition (9764, 9765), // &Zcaro -> &Zcaron + new Transition (9770, 9771), // &zcaro -> &zcaron + new Transition (9848, 9851) // &zw -> &zwn + }; + TransitionTable_o = new Transition[460] { + new Transition (0, 6138), // & -> &o + new Transition (1, 183), // &A -> &Ao + new Transition (8, 188), // &a -> &ao + new Transition (129, 130), // &andsl -> &andslo + new Transition (184, 185), // &Aog -> &Aogo + new Transition (189, 190), // &aog -> &aogo + new Transition (199, 213), // &ap -> &apo + new Transition (225, 226), // &ApplyFuncti -> &ApplyFunctio + new Transition (230, 231), // &appr -> &appro + new Transition (290, 291), // &awc -> &awco + new Transition (301, 598), // &b -> &bo + new Transition (305, 306), // &backc -> &backco + new Transition (314, 315), // &backepsil -> &backepsilo + new Transition (331, 594), // &B -> &Bo + new Transition (369, 370), // &bc -> &bco + new Transition (381, 382), // &bdqu -> &bdquo + new Transition (410, 411), // &bern -> &berno + new Transition (415, 416), // &Bern -> &Berno + new Transition (443, 455), // &big -> &bigo + new Transition (456, 457), // &bigod -> &bigodo + new Transition (489, 490), // &bigtriangled -> &bigtriangledo + new Transition (515, 516), // &bkar -> &bkaro + new Transition (519, 575), // &bl -> &blo + new Transition (523, 524), // &blackl -> &blacklo + new Transition (547, 548), // &blacktriangled -> &blacktriangledo + new Transition (579, 591), // &bn -> &bno + new Transition (587, 588), // &bN -> &bNo + new Transition (604, 605), // &bott -> &botto + new Transition (614, 615), // &boxb -> &boxbo + new Transition (744, 757), // &bs -> &bso + new Transition (789, 1126), // &C -> &Co + new Transition (796, 1131), // &c -> &co + new Transition (824, 825), // &capd -> &capdo + new Transition (848, 852), // &car -> &caro + new Transition (866, 894), // &Cc -> &Cco + new Transition (868, 869), // &Ccar -> &Ccaro + new Transition (872, 873), // &ccar -> &ccaro + new Transition (907, 908), // &Cd -> &Cdo + new Transition (911, 912), // &cd -> &cdo + new Transition (940, 941), // &CenterD -> &CenterDo + new Transition (946, 947), // ¢erd -> ¢erdo + new Transition (990, 991), // &circlearr -> &circlearro + new Transition (1024, 1025), // &CircleD -> &CircleDo + new Transition (1068, 1069), // &Cl -> &Clo + new Transition (1076, 1077), // &ClockwiseC -> &ClockwiseCo + new Transition (1079, 1080), // &ClockwiseCont -> &ClockwiseConto + new Transition (1099, 1100), // &CloseCurlyD -> &CloseCurlyDo + new Transition (1106, 1107), // &CloseCurlyDoubleQu -> &CloseCurlyDoubleQuo + new Transition (1112, 1113), // &CloseCurlyQu -> &CloseCurlyQuo + new Transition (1127, 1128), // &Col -> &Colo + new Transition (1132, 1133), // &col -> &colo + new Transition (1167, 1168), // &congd -> &congdo + new Transition (1187, 1188), // &Cont -> &Conto + new Transition (1206, 1207), // &copr -> &copro + new Transition (1210, 1211), // &Copr -> &Copro + new Transition (1232, 1233), // &CounterCl -> &CounterClo + new Transition (1240, 1241), // &CounterClockwiseC -> &CounterClockwiseCo + new Transition (1243, 1244), // &CounterClockwiseCont -> &CounterClockwiseConto + new Transition (1256, 1266), // &cr -> &cro + new Transition (1261, 1262), // &Cr -> &Cro + new Transition (1288, 1289), // &ctd -> &ctdo + new Transition (1318, 1341), // &cup -> &cupo + new Transition (1337, 1338), // &cupd -> &cupdo + new Transition (1385, 1386), // &curvearr -> &curvearro + new Transition (1408, 1409), // &cwc -> &cwco + new Transition (1425, 1685), // &D -> &Do + new Transition (1432, 1679), // &d -> &do + new Transition (1466, 1467), // &dbkar -> &dbkaro + new Transition (1476, 1477), // &Dcar -> &Dcaro + new Transition (1482, 1483), // &dcar -> &dcaro + new Transition (1490, 1503), // &DD -> &DDo + new Transition (1492, 1510), // &dd -> &ddo + new Transition (1573, 1574), // &DiacriticalD -> &DiacriticalDo + new Transition (1601, 1608), // &diam -> &diamo + new Transition (1603, 1604), // &Diam -> &Diamo + new Transition (1643, 1657), // &div -> &divo + new Transition (1647, 1649), // ÷ -> ÷o + new Transition (1670, 1671), // &dlc -> &dlco + new Transition (1675, 1676), // &dlcr -> &dlcro + new Transition (1696, 1697), // &DotD -> &DotDo + new Transition (1703, 1704), // &doteqd -> &doteqdo + new Transition (1748, 1749), // &DoubleC -> &DoubleCo + new Transition (1751, 1752), // &DoubleCont -> &DoubleConto + new Transition (1764, 1765), // &DoubleD -> &DoubleDo + new Transition (1772, 1773), // &DoubleDownArr -> &DoubleDownArro + new Transition (1776, 1801), // &DoubleL -> &DoubleLo + new Transition (1782, 1783), // &DoubleLeftArr -> &DoubleLeftArro + new Transition (1793, 1794), // &DoubleLeftRightArr -> &DoubleLeftRightArro + new Transition (1810, 1811), // &DoubleLongLeftArr -> &DoubleLongLeftArro + new Transition (1821, 1822), // &DoubleLongLeftRightArr -> &DoubleLongLeftRightArro + new Transition (1832, 1833), // &DoubleLongRightArr -> &DoubleLongRightArro + new Transition (1843, 1844), // &DoubleRightArr -> &DoubleRightArro + new Transition (1855, 1856), // &DoubleUpArr -> &DoubleUpArro + new Transition (1859, 1860), // &DoubleUpD -> &DoubleUpDo + new Transition (1865, 1866), // &DoubleUpDownArr -> &DoubleUpDownArro + new Transition (1885, 1886), // &DownArr -> &DownArro + new Transition (1891, 1892), // &Downarr -> &Downarro + new Transition (1899, 1900), // &downarr -> &downarro + new Transition (1911, 1912), // &DownArrowUpArr -> &DownArrowUpArro + new Transition (1921, 1922), // &downd -> &downdo + new Transition (1927, 1928), // &downdownarr -> &downdownarro + new Transition (1935, 1936), // &downharp -> &downharpo + new Transition (1936, 1937), // &downharpo -> &downharpoo + new Transition (1962, 1963), // &DownLeftRightVect -> &DownLeftRightVecto + new Transition (1972, 1973), // &DownLeftTeeVect -> &DownLeftTeeVecto + new Transition (1979, 1980), // &DownLeftVect -> &DownLeftVecto + new Transition (1998, 1999), // &DownRightTeeVect -> &DownRightTeeVecto + new Transition (2005, 2006), // &DownRightVect -> &DownRightVecto + new Transition (2019, 2020), // &DownTeeArr -> &DownTeeArro + new Transition (2027, 2028), // &drbkar -> &drbkaro + new Transition (2031, 2032), // &drc -> &drco + new Transition (2036, 2037), // &drcr -> &drcro + new Transition (2044, 2054), // &ds -> &dso + new Transition (2058, 2059), // &Dstr -> &Dstro + new Transition (2063, 2064), // &dstr -> &dstro + new Transition (2068, 2069), // &dtd -> &dtdo + new Transition (2108, 2296), // &E -> &Eo + new Transition (2115, 2301), // &e -> &eo + new Transition (2129, 2130), // &Ecar -> &Ecaro + new Transition (2133, 2148), // &ec -> &eco + new Transition (2135, 2136), // &ecar -> &ecaro + new Transition (2149, 2150), // &ecol -> &ecolo + new Transition (2157, 2166), // &eD -> &eDo + new Transition (2158, 2159), // &eDD -> &eDDo + new Transition (2162, 2163), // &Ed -> &Edo + new Transition (2169, 2170), // &ed -> &edo + new Transition (2176, 2177), // &efD -> &efDo + new Transition (2200, 2201), // &egsd -> &egsdo + new Transition (2224, 2225), // &elsd -> &elsdo + new Transition (2297, 2298), // &Eog -> &Eogo + new Transition (2302, 2303), // &eog -> &eogo + new Transition (2329, 2330), // &Epsil -> &Epsilo + new Transition (2333, 2334), // &epsil -> &epsilo + new Transition (2340, 2345), // &eqc -> &eqco + new Transition (2346, 2347), // &eqcol -> &eqcolo + new Transition (2414, 2415), // &erD -> &erDo + new Transition (2426, 2427), // &esd -> &esdo + new Transition (2455, 2456), // &eur -> &euro + new Transition (2472, 2493), // &exp -> &expo + new Transition (2478, 2479), // &expectati -> &expectatio + new Transition (2482, 2483), // &Exp -> &Expo + new Transition (2503, 2612), // &f -> &fo + new Transition (2510, 2511), // &fallingd -> &fallingdo + new Transition (2517, 2608), // &F -> &Fo + new Transition (2604, 2605), // &fn -> &fno + new Transition (2647, 2689), // &fr -> &fro + new Transition (2701, 2857), // &g -> &go + new Transition (2708, 2853), // &G -> &Go + new Transition (2755, 2756), // &Gd -> &Gdo + new Transition (2759, 2760), // &gd -> &gdo + new Transition (2786, 2787), // &gesd -> &gesdo + new Transition (2788, 2790), // &gesdot -> &gesdoto + new Transition (2837, 2838), // &gnappr -> &gnappro + new Transition (2950, 2951), // >d -> >do + new Transition (2969, 2970), // >rappr -> >rappro + new Transition (2976, 2977), // >rd -> >rdo + new Transition (3014, 3159), // &H -> &Ho + new Transition (3020, 3126), // &h -> &ho + new Transition (3090, 3091), // &herc -> &herco + new Transition (3116, 3117), // &hksear -> &hksearo + new Transition (3122, 3123), // &hkswar -> &hkswaro + new Transition (3126, 3136), // &ho -> &hoo + new Transition (3144, 3145), // &hookleftarr -> &hookleftarro + new Transition (3155, 3156), // &hookrightarr -> &hookrightarro + new Transition (3173, 3174), // &Horiz -> &Horizo + new Transition (3198, 3199), // &Hstr -> &Hstro + new Transition (3203, 3204), // &hstr -> &hstro + new Transition (3210, 3211), // &HumpD -> &HumpDo + new Transition (3236, 3471), // &I -> &Io + new Transition (3243, 3467), // &i -> &io + new Transition (3265, 3266), // &Id -> &Ido + new Transition (3301, 3316), // &ii -> &iio + new Transition (3336, 3365), // &im -> &imo + new Transition (3378, 3393), // &in -> &ino + new Transition (3394, 3395), // &inod -> &inodo + new Transition (3429, 3430), // &Intersecti -> &Intersectio + new Transition (3440, 3441), // &intpr -> &intpro + new Transition (3451, 3452), // &InvisibleC -> &InvisibleCo + new Transition (3472, 3473), // &Iog -> &Iogo + new Transition (3476, 3477), // &iog -> &iogo + new Transition (3493, 3494), // &ipr -> &ipro + new Transition (3514, 3515), // &isind -> &isindo + new Transition (3555, 3582), // &J -> &Jo + new Transition (3561, 3586), // &j -> &jo + new Transition (3618, 3676), // &K -> &Ko + new Transition (3624, 3680), // &k -> &ko + new Transition (3692, 4422), // &l -> &lo + new Transition (3698, 4434), // &L -> &Lo + new Transition (3756, 3757), // &laqu -> « + new Transition (3839, 3840), // &Lcar -> &Lcaro + new Transition (3845, 3846), // &lcar -> &lcaro + new Transition (3874, 3875), // &ldqu -> &ldquo + new Transition (3915, 3916), // &LeftArr -> &LeftArro + new Transition (3921, 3922), // &Leftarr -> &Leftarro + new Transition (3929, 3930), // &leftarr -> &leftarro + new Transition (3944, 3945), // &LeftArrowRightArr -> &LeftArrowRightArro + new Transition (3961, 3962), // &LeftD -> &LeftDo + new Transition (3983, 3984), // &LeftDownTeeVect -> &LeftDownTeeVecto + new Transition (3990, 3991), // &LeftDownVect -> &LeftDownVecto + new Transition (3999, 4000), // &LeftFl -> &LeftFlo + new Transition (4000, 4001), // &LeftFlo -> &LeftFloo + new Transition (4007, 4008), // &leftharp -> &leftharpo + new Transition (4008, 4009), // &leftharpo -> &leftharpoo + new Transition (4011, 4012), // &leftharpoond -> &leftharpoondo + new Transition (4025, 4026), // &leftleftarr -> &leftleftarro + new Transition (4037, 4038), // &LeftRightArr -> &LeftRightArro + new Transition (4048, 4049), // &Leftrightarr -> &Leftrightarro + new Transition (4059, 4060), // &leftrightarr -> &leftrightarro + new Transition (4068, 4069), // &leftrightharp -> &leftrightharpo + new Transition (4069, 4070), // &leftrightharpo -> &leftrightharpoo + new Transition (4081, 4082), // &leftrightsquigarr -> &leftrightsquigarro + new Transition (4088, 4089), // &LeftRightVect -> &LeftRightVecto + new Transition (4098, 4099), // &LeftTeeArr -> &LeftTeeArro + new Transition (4105, 4106), // &LeftTeeVect -> &LeftTeeVecto + new Transition (4140, 4141), // &LeftUpD -> &LeftUpDo + new Transition (4147, 4148), // &LeftUpDownVect -> &LeftUpDownVecto + new Transition (4157, 4158), // &LeftUpTeeVect -> &LeftUpTeeVecto + new Transition (4164, 4165), // &LeftUpVect -> &LeftUpVecto + new Transition (4175, 4176), // &LeftVect -> &LeftVecto + new Transition (4202, 4203), // &lesd -> &lesdo + new Transition (4204, 4206), // &lesdot -> &lesdoto + new Transition (4219, 4220), // &lessappr -> &lessappro + new Transition (4223, 4224), // &lessd -> &lessdo + new Transition (4307, 4308), // &lfl -> &lflo + new Transition (4308, 4309), // &lflo -> &lfloo + new Transition (4354, 4355), // &llc -> &llco + new Transition (4366, 4367), // &Lleftarr -> &Lleftarro + new Transition (4381, 4382), // &Lmid -> &Lmido + new Transition (4385, 4391), // &lm -> &lmo + new Transition (4387, 4388), // &lmid -> &lmido + new Transition (4406, 4407), // &lnappr -> &lnappro + new Transition (4422, 4542), // &lo -> &loo + new Transition (4443, 4444), // &LongLeftArr -> &LongLeftArro + new Transition (4453, 4454), // &Longleftarr -> &Longleftarro + new Transition (4465, 4466), // &longleftarr -> &longleftarro + new Transition (4476, 4477), // &LongLeftRightArr -> &LongLeftRightArro + new Transition (4487, 4488), // &Longleftrightarr -> &Longleftrightarro + new Transition (4498, 4499), // &longleftrightarr -> &longleftrightarro + new Transition (4506, 4507), // &longmapst -> &longmapsto + new Transition (4516, 4517), // &LongRightArr -> &LongRightArro + new Transition (4527, 4528), // &Longrightarr -> &Longrightarro + new Transition (4538, 4539), // &longrightarr -> &longrightarro + new Transition (4546, 4547), // &looparr -> &looparro + new Transition (4597, 4598), // &LowerLeftArr -> &LowerLeftArro + new Transition (4608, 4609), // &LowerRightArr -> &LowerRightArro + new Transition (4633, 4634), // &lrc -> &lrco + new Transition (4655, 4656), // &lsaqu -> &lsaquo + new Transition (4679, 4680), // &lsqu -> &lsquo + new Transition (4685, 4686), // &Lstr -> &Lstro + new Transition (4690, 4691), // &lstr -> &lstro + new Transition (4706, 4707), // <d -> <do + new Transition (4767, 4922), // &m -> &mo + new Transition (4781, 4928), // &M -> &Mo + new Transition (4788, 4789), // &mapst -> &mapsto + new Transition (4791, 4792), // &mapstod -> &mapstodo + new Transition (4809, 4810), // &mc -> &mco + new Transition (4826, 4827), // &mDD -> &mDDo + new Transition (4868, 4869), // &mh -> &mho + new Transition (4873, 4874), // &micr -> µ + new Transition (4886, 4887), // &midd -> &middo + new Transition (4946, 4947), // &mstp -> &mstpo + new Transition (4965, 5372), // &n -> &no + new Transition (4971, 5347), // &N -> &No + new Transition (4986, 4993), // &nap -> &napo + new Transition (4997, 4998), // &nappr -> &nappro + new Transition (5020, 5044), // &nc -> &nco + new Transition (5026, 5027), // &Ncar -> &Ncaro + new Transition (5030, 5031), // &ncar -> &ncaro + new Transition (5048, 5049), // &ncongd -> &ncongdo + new Transition (5075, 5077), // &nearr -> &nearro + new Transition (5080, 5081), // &ned -> &nedo + new Transition (5278, 5279), // &nLeftarr -> &nLeftarro + new Transition (5286, 5287), // &nleftarr -> &nleftarro + new Transition (5297, 5298), // &nLeftrightarr -> &nLeftrightarro + new Transition (5308, 5309), // &nleftrightarr -> &nleftrightarro + new Transition (5380, 5381), // &NotC -> &NotCo + new Transition (5396, 5397), // &NotD -> &NotDo + new Transition (5497, 5498), // &NotHumpD -> &NotHumpDo + new Transition (5515, 5516), // ¬ind -> ¬indo + new Transition (5821, 5836), // &np -> &npo + new Transition (5875, 5876), // &nRightarr -> &nRightarro + new Transition (5885, 5886), // &nrightarr -> &nrightarro + new Transition (5910, 5911), // &nsh -> &nsho + new Transition (6037, 6038), // &numer -> &numero + new Transition (6121, 6123), // &nwarr -> &nwarro + new Transition (6131, 6294), // &O -> &Oo + new Transition (6138, 6298), // &o -> &oo + new Transition (6163, 6182), // &od -> &odo + new Transition (6185, 6186), // &ods -> &odso + new Transition (6210, 6211), // &og -> &ogo + new Transition (6247, 6248), // &olcr -> &olcro + new Transition (6278, 6279), // &Omicr -> &Omicro + new Transition (6284, 6285), // &omicr -> &omicro + new Transition (6314, 6315), // &OpenCurlyD -> &OpenCurlyDo + new Transition (6321, 6322), // &OpenCurlyDoubleQu -> &OpenCurlyDoubleQuo + new Transition (6327, 6328), // &OpenCurlyQu -> &OpenCurlyQuo + new Transition (6342, 6365), // &or -> &oro + new Transition (6351, 6353), // &order -> &ordero + new Transition (6361, 6362), // &orig -> &origo + new Transition (6369, 6370), // &orsl -> &orslo + new Transition (6382, 6396), // &os -> &oso + new Transition (6463, 6622), // &p -> &po + new Transition (6482, 6609), // &P -> &Po + new Transition (6503, 6504), // &peri -> &perio + new Transition (6527, 6537), // &ph -> &pho + new Transition (6548, 6549), // &pitchf -> &pitchfo + new Transition (6580, 6581), // &plusd -> &plusdo + new Transition (6604, 6605), // &plustw -> &plustwo + new Transition (6640, 6748), // &Pr -> &Pro + new Transition (6642, 6745), // &pr -> &pro + new Transition (6660, 6661), // &precappr -> &precappro + new Transition (6709, 6710), // &precnappr -> &precnappro + new Transition (6772, 6773), // &Prop -> &Propo + new Transition (6776, 6777), // &Proporti -> &Proportio + new Transition (6783, 6784), // &propt -> &propto + new Transition (6813, 6825), // &Q -> &Qo + new Transition (6817, 6829), // &q -> &qo + new Transition (6847, 6873), // &qu -> &quo + new Transition (6853, 6854), // &quaterni -> &quaternio + new Transition (6876, 7469), // &r -> &ro + new Transition (6886, 7485), // &R -> &Ro + new Transition (6922, 6923), // &raqu -> » + new Transition (6978, 6979), // &rati -> &ratio + new Transition (7023, 7024), // &Rcar -> &Rcaro + new Transition (7029, 7030), // &rcar -> &rcaro + new Transition (7064, 7065), // &rdqu -> &rdquo + new Transition (7141, 7142), // &rfl -> &rflo + new Transition (7142, 7143), // &rflo -> &rfloo + new Transition (7155, 7167), // &rh -> &rho + new Transition (7164, 7165), // &Rh -> &Rho + new Transition (7189, 7190), // &RightArr -> &RightArro + new Transition (7195, 7196), // &Rightarr -> &Rightarro + new Transition (7205, 7206), // &rightarr -> &rightarro + new Transition (7219, 7220), // &RightArrowLeftArr -> &RightArrowLeftArro + new Transition (7236, 7237), // &RightD -> &RightDo + new Transition (7258, 7259), // &RightDownTeeVect -> &RightDownTeeVecto + new Transition (7265, 7266), // &RightDownVect -> &RightDownVecto + new Transition (7274, 7275), // &RightFl -> &RightFlo + new Transition (7275, 7276), // &RightFlo -> &RightFloo + new Transition (7282, 7283), // &rightharp -> &rightharpo + new Transition (7283, 7284), // &rightharpo -> &rightharpoo + new Transition (7286, 7287), // &rightharpoond -> &rightharpoondo + new Transition (7300, 7301), // &rightleftarr -> &rightleftarro + new Transition (7308, 7309), // &rightleftharp -> &rightleftharpo + new Transition (7309, 7310), // &rightleftharpo -> &rightleftharpoo + new Transition (7321, 7322), // &rightrightarr -> &rightrightarro + new Transition (7333, 7334), // &rightsquigarr -> &rightsquigarro + new Transition (7343, 7344), // &RightTeeArr -> &RightTeeArro + new Transition (7350, 7351), // &RightTeeVect -> &RightTeeVecto + new Transition (7385, 7386), // &RightUpD -> &RightUpDo + new Transition (7392, 7393), // &RightUpDownVect -> &RightUpDownVecto + new Transition (7402, 7403), // &RightUpTeeVect -> &RightUpTeeVecto + new Transition (7409, 7410), // &RightUpVect -> &RightUpVecto + new Transition (7420, 7421), // &RightVect -> &RightVecto + new Transition (7435, 7436), // &risingd -> &risingdo + new Transition (7453, 7454), // &rm -> &rmo + new Transition (7519, 7520), // &rpp -> &rppo + new Transition (7538, 7539), // &Rrightarr -> &Rrightarro + new Transition (7545, 7546), // &rsaqu -> &rsaquo + new Transition (7562, 7563), // &rsqu -> &rsquo + new Transition (7610, 7949), // &S -> &So + new Transition (7617, 7936), // &s -> &so + new Transition (7626, 7627), // &sbqu -> &sbquo + new Transition (7637, 7638), // &Scar -> &Scaro + new Transition (7641, 7642), // &scar -> &scaro + new Transition (7680, 7681), // &scp -> &scpo + new Transition (7695, 7696), // &sd -> &sdo + new Transition (7713, 7715), // &searr -> &searro + new Transition (7745, 7747), // &sfr -> &sfro + new Transition (7751, 7796), // &sh -> &sho + new Transition (7772, 7773), // &Sh -> &Sho + new Transition (7776, 7777), // &ShortD -> &ShortDo + new Transition (7782, 7783), // &ShortDownArr -> &ShortDownArro + new Transition (7792, 7793), // &ShortLeftArr -> &ShortLeftArro + new Transition (7819, 7820), // &ShortRightArr -> &ShortRightArro + new Transition (7827, 7828), // &ShortUpArr -> &ShortUpArro + new Transition (7849, 7850), // &simd -> &simdo + new Transition (8028, 8029), // &SquareIntersecti -> &SquareIntersectio + new Transition (8060, 8061), // &SquareUni -> &SquareUnio + new Transition (8116, 8117), // &straightepsil -> &straightepsilo + new Transition (8133, 8134), // &subd -> &subdo + new Transition (8141, 8142), // &subed -> &subedo + new Transition (8204, 8205), // &succappr -> &succappro + new Transition (8253, 8254), // &succnappr -> &succnappro + new Transition (8292, 8293), // &supd -> &supdo + new Transition (8304, 8305), // &suped -> &supedo + new Transition (8321, 8322), // &suphs -> &suphso + new Transition (8385, 8387), // &swarr -> &swarro + new Transition (8400, 8604), // &T -> &To + new Transition (8404, 8590), // &t -> &to + new Transition (8421, 8422), // &Tcar -> &Tcaro + new Transition (8427, 8428), // &tcar -> &tcaro + new Transition (8445, 8446), // &td -> &tdo + new Transition (8461, 8540), // &th -> &tho + new Transition (8471, 8472), // &Theref -> &Therefo + new Transition (8476, 8477), // &theref -> &therefo + new Transition (8499, 8500), // &thickappr -> &thickappro + new Transition (8596, 8597), // &topb -> &topbo + new Transition (8608, 8610), // &topf -> &topfo + new Transition (8640, 8641), // &triangled -> &triangledo + new Transition (8664, 8665), // &trid -> &trido + new Transition (8681, 8682), // &TripleD -> &TripleDo + new Transition (8728, 8729), // &Tstr -> &Tstro + new Transition (8733, 8734), // &tstr -> &tstro + new Transition (8737, 8742), // &tw -> &two + new Transition (8753, 8754), // &twoheadleftarr -> &twoheadleftarro + new Transition (8764, 8765), // &twoheadrightarr -> &twoheadrightarro + new Transition (8768, 8954), // &U -> &Uo + new Transition (8775, 8959), // &u -> &uo + new Transition (8783, 8792), // &Uarr -> &Uarro + new Transition (8888, 8889), // &ulc -> &ulco + new Transition (8896, 8897), // &ulcr -> &ulcro + new Transition (8945, 8946), // &Uni -> &Unio + new Transition (8955, 8956), // &Uog -> &Uogo + new Transition (8960, 8961), // &uog -> &uogo + new Transition (8973, 8974), // &UpArr -> &UpArro + new Transition (8979, 8980), // &Uparr -> &Uparro + new Transition (8986, 8987), // &uparr -> &uparro + new Transition (8994, 8995), // &UpArrowD -> &UpArrowDo + new Transition (9000, 9001), // &UpArrowDownArr -> &UpArrowDownArro + new Transition (9004, 9005), // &UpD -> &UpDo + new Transition (9010, 9011), // &UpDownArr -> &UpDownArro + new Transition (9014, 9015), // &Upd -> &Updo + new Transition (9020, 9021), // &Updownarr -> &Updownarro + new Transition (9024, 9025), // &upd -> &updo + new Transition (9030, 9031), // &updownarr -> &updownarro + new Transition (9049, 9050), // &upharp -> &upharpo + new Transition (9050, 9051), // &upharpo -> &upharpoo + new Transition (9077, 9078), // &UpperLeftArr -> &UpperLeftArro + new Transition (9088, 9089), // &UpperRightArr -> &UpperRightArro + new Transition (9100, 9101), // &Upsil -> &Upsilo + new Transition (9104, 9105), // &upsil -> &upsilo + new Transition (9114, 9115), // &UpTeeArr -> &UpTeeArro + new Transition (9122, 9123), // &upuparr -> &upuparro + new Transition (9128, 9129), // &urc -> &urco + new Transition (9136, 9137), // &urcr -> &urcro + new Transition (9162, 9163), // &utd -> &utdo + new Transition (9201, 9436), // &v -> &vo + new Transition (9213, 9214), // &varepsil -> &varepsilo + new Transition (9223, 9224), // &varn -> &varno + new Transition (9237, 9238), // &varpr -> &varpro + new Transition (9240, 9241), // &varpropt -> &varpropto + new Transition (9249, 9250), // &varrh -> &varrho + new Transition (9303, 9432), // &V -> &Vo + new Transition (9393, 9394), // &VerticalSeparat -> &VerticalSeparato + new Transition (9441, 9442), // &vpr -> &vpro + new Transition (9484, 9523), // &W -> &Wo + new Transition (9490, 9527), // &w -> &wo + new Transition (9548, 9602), // &x -> &xo + new Transition (9565, 9607), // &X -> &Xo + new Transition (9603, 9604), // &xod -> &xodo + new Transition (9665, 9716), // &Y -> &Yo + new Transition (9672, 9720), // &y -> &yo + new Transition (9747, 9832), // &Z -> &Zo + new Transition (9754, 9836), // &z -> &zo + new Transition (9763, 9764), // &Zcar -> &Zcaro + new Transition (9769, 9770), // &zcar -> &zcaro + new Transition (9777, 9778), // &Zd -> &Zdo + new Transition (9781, 9782), // &zd -> &zdo + new Transition (9792, 9793) // &Zer -> &Zero + }; + TransitionTable_p = new Transition[278] { + new Transition (0, 6463), // & -> &p + new Transition (1, 216), // &A -> &Ap + new Transition (8, 199), // &a -> &ap + new Transition (79, 94), // &al -> &alp + new Transition (80, 86), // &ale -> &alep + new Transition (89, 90), // &Al -> &Alp + new Transition (103, 114), // &am -> & + new Transition (130, 131), // &andslo -> &andslop + new Transition (172, 173), // &angs -> &angsp + new Transition (183, 193), // &Ao -> &Aop + new Transition (188, 196), // &ao -> &aop + new Transition (199, 229), // &ap -> &app + new Transition (216, 217), // &Ap -> &App + new Transition (263, 264), // &asym -> &asymp + new Transition (301, 719), // &b -> &bp + new Transition (304, 318), // &back -> &backp + new Transition (310, 311), // &backe -> &backep + new Transition (384, 405), // &be -> &bep + new Transition (399, 400), // &bem -> &bemp + new Transition (445, 446), // &bigca -> &bigcap + new Transition (452, 453), // &bigcu -> &bigcup + new Transition (455, 460), // &bigo -> &bigop + new Transition (474, 475), // &bigsqcu -> &bigsqcup + new Transition (494, 495), // &bigtriangleu -> &bigtriangleup + new Transition (497, 498), // &bigu -> &bigup + new Transition (594, 595), // &Bo -> &Bop + new Transition (598, 599), // &bo -> &bop + new Transition (613, 662), // &box -> &boxp + new Transition (774, 775), // &bum -> &bump + new Transition (782, 783), // &Bum -> &Bump + new Transition (790, 803), // &Ca -> &Cap + new Transition (797, 805), // &ca -> &cap + new Transition (814, 815), // &capbrcu -> &capbrcup + new Transition (818, 819), // &capca -> &capcap + new Transition (821, 822), // &capcu -> &capcup + new Transition (862, 863), // &cca -> &ccap + new Transition (900, 901), // &ccu -> &ccup + new Transition (927, 928), // &cem -> &cemp + new Transition (1126, 1200), // &Co -> &Cop + new Transition (1131, 1203), // &co -> &cop + new Transition (1142, 1148), // &com -> &comp + new Transition (1278, 1283), // &csu -> &csup + new Transition (1292, 1318), // &cu -> &cup + new Transition (1301, 1302), // &cue -> &cuep + new Transition (1311, 1313), // &cularr -> &cularrp + new Transition (1315, 1316), // &Cu -> &Cup + new Transition (1323, 1324), // &cupbrca -> &cupbrcap + new Transition (1327, 1328), // &CupCa -> &CupCap + new Transition (1331, 1332), // &cupca -> &cupcap + new Transition (1334, 1335), // &cupcu -> &cupcup + new Transition (1356, 1357), // &curlyeq -> &curlyeqp + new Transition (1529, 1530), // &dem -> &demp + new Transition (1676, 1677), // &dlcro -> &dlcrop + new Transition (1679, 1689), // &do -> &dop + new Transition (1685, 1686), // &Do -> &Dop + new Transition (1694, 1719), // &dot -> &dotp + new Transition (1851, 1852), // &DoubleU -> &DoubleUp + new Transition (1907, 1908), // &DownArrowU -> &DownArrowUp + new Transition (1934, 1935), // &downhar -> &downharp + new Transition (2037, 2038), // &drcro -> &drcrop + new Transition (2108, 2326), // &E -> &Ep + new Transition (2115, 2312), // &e -> &ep + new Transition (2228, 2246), // &Em -> &Emp + new Transition (2233, 2238), // &em -> &emp + new Transition (2279, 2280), // &ems -> &emsp + new Transition (2293, 2294), // &ens -> &ensp + new Transition (2296, 2306), // &Eo -> &Eop + new Transition (2301, 2309), // &eo -> &eop + new Transition (2402, 2403), // &eqv -> &eqvp + new Transition (2458, 2472), // &ex -> &exp + new Transition (2466, 2482), // &Ex -> &Exp + new Transition (2503, 2639), // &f -> &fp + new Transition (2608, 2609), // &Fo -> &Fop + new Transition (2612, 2613), // &fo -> &fop + new Transition (2702, 2722), // &ga -> &gap + new Transition (2833, 2834), // &gna -> &gnap + new Transition (2834, 2836), // &gnap -> &gnapp + new Transition (2853, 2854), // &Go -> &Gop + new Transition (2857, 2858), // &go -> &gop + new Transition (2966, 2967), // >ra -> >rap + new Transition (2967, 2968), // >rap -> >rapp + new Transition (3024, 3025), // &hairs -> &hairsp + new Transition (3086, 3087), // &helli -> &hellip + new Transition (3106, 3107), // &HilbertS -> &HilbertSp + new Transition (3126, 3163), // &ho -> &hop + new Transition (3159, 3160), // &Ho -> &Hop + new Transition (3208, 3209), // &Hum -> &Hump + new Transition (3216, 3217), // &HumpDownHum -> &HumpDownHump + new Transition (3225, 3231), // &hy -> &hyp + new Transition (3243, 3492), // &i -> &ip + new Transition (3330, 3372), // &Im -> &Imp + new Transition (3336, 3368), // &im -> &imp + new Transition (3341, 3357), // &imag -> &imagp + new Transition (3401, 3439), // &int -> &intp + new Transition (3467, 3483), // &io -> &iop + new Transition (3471, 3480), // &Io -> &Iop + new Transition (3582, 3583), // &Jo -> &Jop + new Transition (3586, 3587), // &jo -> &jop + new Transition (3619, 3620), // &Ka -> &Kap + new Transition (3620, 3621), // &Kap -> &Kapp + new Transition (3625, 3626), // &ka -> &kap + new Transition (3626, 3627), // &kap -> &kapp + new Transition (3676, 3677), // &Ko -> &Kop + new Transition (3680, 3681), // &ko -> &kop + new Transition (3692, 4621), // &l -> &lp + new Transition (3699, 3746), // &La -> &Lap + new Transition (3705, 3744), // &la -> &lap + new Transition (3712, 3713), // &laem -> &laemp + new Transition (3766, 3782), // &larr -> &larrp + new Transition (3779, 3780), // &larrl -> &larrlp + new Transition (4006, 4007), // &lefthar -> &leftharp + new Transition (4016, 4017), // &leftharpoonu -> &leftharpoonup + new Transition (4067, 4068), // &leftrighthar -> &leftrightharp + new Transition (4138, 4139), // &LeftU -> &LeftUp + new Transition (4216, 4217), // &lessa -> &lessap + new Transition (4217, 4218), // &lessap -> &lessapp + new Transition (4402, 4403), // &lna -> &lnap + new Transition (4403, 4405), // &lnap -> &lnapp + new Transition (4422, 4560), // &lo -> &lop + new Transition (4434, 4564), // &Lo -> &Lop + new Transition (4503, 4504), // &longma -> &longmap + new Transition (4542, 4543), // &loo -> &loop + new Transition (4767, 4935), // &m -> &mp + new Transition (4768, 4785), // &ma -> &map + new Transition (4782, 4783), // &Ma -> &Map + new Transition (4801, 4802), // &mapstou -> &mapstoup + new Transition (4848, 4849), // &MediumS -> &MediumSp + new Transition (4910, 4911), // &mlc -> &mlcp + new Transition (4916, 4917), // &mn -> &mnp + new Transition (4922, 4932), // &mo -> &mop + new Transition (4928, 4929), // &Mo -> &Mop + new Transition (4945, 4946), // &mst -> &mstp + new Transition (4958, 4959), // &multima -> &multimap + new Transition (4962, 4963), // &muma -> &mumap + new Transition (4965, 5821), // &n -> &np + new Transition (4966, 4986), // &na -> &nap + new Transition (4986, 4996), // &nap -> &napp + new Transition (5011, 5012), // &nbs ->   + new Transition (5015, 5016), // &nbum -> &nbump + new Transition (5021, 5022), // &nca -> &ncap + new Transition (5052, 5053), // &ncu -> &ncup + new Transition (5097, 5098), // &NegativeMediumS -> &NegativeMediumSp + new Transition (5108, 5109), // &NegativeThickS -> &NegativeThickSp + new Transition (5115, 5116), // &NegativeThinS -> &NegativeThinSp + new Transition (5129, 5130), // &NegativeVeryThinS -> &NegativeVeryThinSp + new Transition (5227, 5236), // &nh -> &nhp + new Transition (5347, 5369), // &No -> &Nop + new Transition (5363, 5364), // &NonBreakingS -> &NonBreakingSp + new Transition (5372, 5373), // &no -> &nop + new Transition (5390, 5391), // &NotCu -> &NotCup + new Transition (5393, 5394), // &NotCupCa -> &NotCupCap + new Transition (5495, 5496), // &NotHum -> &NotHump + new Transition (5503, 5504), // &NotHumpDownHum -> &NotHumpDownHump + new Transition (5701, 5713), // &NotSquareSu -> &NotSquareSup + new Transition (5726, 5768), // &NotSu -> &NotSup + new Transition (5895, 5938), // &ns -> &nsp + new Transition (5913, 5918), // &nshort -> &nshortp + new Transition (5944, 5948), // &nsqsu -> &nsqsup + new Transition (5951, 5973), // &nsu -> &nsup + new Transition (6040, 6041), // &nums -> &numsp + new Transition (6044, 6045), // &nva -> &nvap + new Transition (6131, 6306), // &O -> &Op + new Transition (6138, 6302), // &o -> &op + new Transition (6294, 6295), // &Oo -> &Oop + new Transition (6298, 6299), // &oo -> &oop + new Transition (6333, 6334), // &oper -> &operp + new Transition (6370, 6371), // &orslo -> &orslop + new Transition (6498, 6511), // &per -> &perp + new Transition (6609, 6630), // &Po -> &Pop + new Transition (6615, 6616), // &Poincare -> &Poincarep + new Transition (6622, 6633), // &po -> &pop + new Transition (6644, 6645), // &pra -> &prap + new Transition (6657, 6658), // &preca -> &precap + new Transition (6658, 6659), // &precap -> &precapp + new Transition (6706, 6707), // &precna -> &precnap + new Transition (6707, 6708), // &precnap -> &precnapp + new Transition (6736, 6737), // &prna -> &prnap + new Transition (6745, 6770), // &pro -> &prop + new Transition (6748, 6772), // &Pro -> &Prop + new Transition (6810, 6811), // &puncs -> &puncsp + new Transition (6817, 6833), // &q -> &qp + new Transition (6825, 6826), // &Qo -> &Qop + new Transition (6829, 6830), // &qo -> &qop + new Transition (6876, 7512), // &r -> &rp + new Transition (6902, 6903), // &raem -> &raemp + new Transition (6932, 6953), // &rarr -> &rarrp + new Transition (6934, 6935), // &rarra -> &rarrap + new Transition (6950, 6951), // &rarrl -> &rarrlp + new Transition (7076, 7082), // &real -> &realp + new Transition (7121, 7122), // &ReverseU -> &ReverseUp + new Transition (7281, 7282), // &righthar -> &rightharp + new Transition (7291, 7292), // &rightharpoonu -> &rightharpoonup + new Transition (7307, 7308), // &rightlefthar -> &rightleftharp + new Transition (7383, 7384), // &RightU -> &RightUp + new Transition (7469, 7481), // &ro -> &rop + new Transition (7485, 7486), // &Ro -> &Rop + new Transition (7505, 7506), // &RoundIm -> &RoundImp + new Transition (7512, 7519), // &rp -> &rpp + new Transition (7617, 7956), // &s -> &sp + new Transition (7631, 7680), // &sc -> &scp + new Transition (7633, 7634), // &sca -> &scap + new Transition (7671, 7672), // &scna -> &scnap + new Transition (7753, 7754), // &shar -> &sharp + new Transition (7798, 7803), // &short -> &shortp + new Transition (7823, 7824), // &ShortU -> &ShortUp + new Transition (7847, 7868), // &sim -> &simp + new Transition (7908, 7909), // &smash -> &smashp + new Transition (7911, 7912), // &sme -> &smep + new Transition (7936, 7953), // &so -> &sop + new Transition (7949, 7950), // &So -> &Sop + new Transition (7970, 7971), // &sqca -> &sqcap + new Transition (7975, 7976), // &sqcu -> &sqcup + new Transition (7985, 7997), // &sqsu -> &sqsup + new Transition (8033, 8045), // &SquareSu -> &SquareSup + new Transition (8111, 8120), // &straight -> &straightp + new Transition (8112, 8113), // &straighte -> &straightep + new Transition (8127, 8282), // &Su -> &Sup + new Transition (8130, 8284), // &su -> &sup + new Transition (8131, 8155), // &sub -> &subp + new Transition (8193, 8196), // &subsu -> &subsup + new Transition (8201, 8202), // &succa -> &succap + new Transition (8202, 8203), // &succap -> &succapp + new Transition (8250, 8251), // &succna -> &succnap + new Transition (8251, 8252), // &succnap -> &succnapp + new Transition (8284, 8343), // &sup -> &supp + new Transition (8370, 8373), // &supsu -> &supsup + new Transition (8404, 8617), // &t -> &tp + new Transition (8496, 8497), // &thicka -> &thickap + new Transition (8497, 8498), // &thickap -> &thickapp + new Transition (8510, 8511), // &ThickS -> &ThickSp + new Transition (8517, 8518), // &thins -> &thinsp + new Transition (8521, 8522), // &ThinS -> &ThinSp + new Transition (8528, 8529), // &thka -> &thkap + new Transition (8590, 8594), // &to -> &top + new Transition (8604, 8605), // &To -> &Top + new Transition (8628, 8698), // &tr -> &trp + new Transition (8633, 8685), // &tri -> &trip + new Transition (8677, 8678), // &Tri -> &Trip + new Transition (8768, 8970), // &U -> &Up + new Transition (8775, 8983), // &u -> &up + new Transition (8897, 8898), // &ulcro -> &ulcrop + new Transition (8954, 8964), // &Uo -> &Uop + new Transition (8959, 8967), // &uo -> &uop + new Transition (8970, 9068), // &Up -> &Upp + new Transition (9048, 9049), // &uphar -> &upharp + new Transition (9118, 9119), // &upu -> &upup + new Transition (9137, 9138), // &urcro -> &urcrop + new Transition (9201, 9440), // &v -> &vp + new Transition (9208, 9231), // &var -> &varp + new Transition (9209, 9210), // &vare -> &varep + new Transition (9218, 9219), // &varka -> &varkap + new Transition (9219, 9220), // &varkap -> &varkapp + new Transition (9238, 9239), // &varpro -> &varprop + new Transition (9258, 9269), // &varsu -> &varsup + new Transition (9357, 9358), // &velli -> &vellip + new Transition (9388, 9389), // &VerticalSe -> &VerticalSep + new Transition (9408, 9409), // &VeryThinS -> &VeryThinSp + new Transition (9427, 9430), // &vnsu -> &vnsup + new Transition (9432, 9433), // &Vo -> &Vop + new Transition (9436, 9437), // &vo -> &vop + new Transition (9442, 9443), // &vpro -> &vprop + new Transition (9458, 9465), // &vsu -> &vsup + new Transition (9490, 9531), // &w -> &wp + new Transition (9514, 9515), // &weier -> &weierp + new Transition (9523, 9524), // &Wo -> &Wop + new Transition (9527, 9528), // &wo -> &wop + new Transition (9550, 9551), // &xca -> &xcap + new Transition (9557, 9558), // &xcu -> &xcup + new Transition (9595, 9596), // &xma -> &xmap + new Transition (9602, 9611), // &xo -> &xop + new Transition (9607, 9608), // &Xo -> &Xop + new Transition (9642, 9643), // &xsqcu -> &xsqcup + new Transition (9645, 9646), // &xu -> &xup + new Transition (9716, 9717), // &Yo -> &Yop + new Transition (9720, 9721), // &yo -> &yop + new Transition (9799, 9800), // &ZeroWidthS -> &ZeroWidthSp + new Transition (9832, 9833), // &Zo -> &Zop + new Transition (9836, 9837) // &zo -> &zop + }; + TransitionTable_q = new Transition[144] { + new Transition (0, 6817), // & -> &q + new Transition (234, 235), // &approxe -> &approxeq + new Transition (266, 267), // &asympe -> &asympeq + new Transition (328, 329), // &backsime -> &backsimeq + new Transition (379, 380), // &bd -> &bdq + new Transition (471, 472), // &bigs -> &bigsq + new Transition (531, 532), // &blacks -> &blacksq + new Transition (580, 582), // &bne -> &bneq + new Transition (779, 787), // &bumpe -> &bumpeq + new Transition (784, 785), // &Bumpe -> &Bumpeq + new Transition (983, 984), // &circe -> &circeq + new Transition (1138, 1140), // &colone -> &coloneq + new Transition (1355, 1356), // &curlye -> &curlyeq + new Transition (1513, 1514), // &ddotse -> &ddotseq + new Transition (1700, 1701), // &dote -> &doteq + new Transition (1707, 1708), // &DotE -> &DotEq + new Transition (1724, 1725), // &dots -> &dotsq + new Transition (2108, 2367), // &E -> &Eq + new Transition (2115, 2339), // &e -> &eq + new Transition (2254, 2255), // &EmptySmallS -> &EmptySmallSq + new Transition (2272, 2273), // &EmptyVerySmallS -> &EmptyVerySmallSq + new Transition (2514, 2515), // &fallingdotse -> &fallingdotseq + new Transition (2564, 2565), // &FilledSmallS -> &FilledSmallSq + new Transition (2580, 2581), // &FilledVerySmallS -> &FilledVerySmallSq + new Transition (2765, 2771), // &ge -> &geq + new Transition (2771, 2773), // &geq -> &geqq + new Transition (2843, 2845), // &gne -> &gneq + new Transition (2845, 2847), // &gneq -> &gneqq + new Transition (2872, 2873), // &GreaterE -> &GreaterEq + new Transition (2887, 2888), // &GreaterFullE -> &GreaterFullEq + new Transition (2911, 2912), // &GreaterSlantE -> &GreaterSlantEq + new Transition (2942, 2959), // > -> >q + new Transition (2980, 2981), // >re -> >req + new Transition (2981, 2987), // >req -> >reqq + new Transition (3007, 3008), // &gvertne -> &gvertneq + new Transition (3008, 3009), // &gvertneq -> &gvertneqq + new Transition (3219, 3220), // &HumpE -> &HumpEq + new Transition (3243, 3497), // &i -> &iq + new Transition (3705, 3755), // &la -> &laq + new Transition (3869, 3873), // &ld -> &ldq + new Transition (3896, 4187), // &le -> &leq + new Transition (4074, 4075), // &leftrights -> &leftrightsq + new Transition (4132, 4133), // &LeftTriangleE -> &LeftTriangleEq + new Transition (4187, 4189), // &leq -> &leqq + new Transition (4227, 4228), // &lesse -> &lesseq + new Transition (4228, 4233), // &lesseq -> &lesseqq + new Transition (4240, 4241), // &LessE -> &LessEq + new Transition (4257, 4258), // &LessFullE -> &LessFullEq + new Transition (4289, 4290), // &LessSlantE -> &LessSlantEq + new Transition (4412, 4414), // &lne -> &lneq + new Transition (4414, 4416), // &lneq -> &lneqq + new Transition (4652, 4676), // &ls -> &lsq + new Transition (4653, 4654), // &lsa -> &lsaq + new Transition (4698, 4725), // < -> <q + new Transition (4760, 4761), // &lvertne -> &lvertneq + new Transition (4761, 4762), // &lvertneq -> &lvertneqq + new Transition (5064, 5135), // &ne -> &neq + new Transition (5198, 5200), // &nge -> &ngeq + new Transition (5200, 5202), // &ngeq -> &ngeqq + new Transition (5270, 5312), // &nle -> &nleq + new Transition (5312, 5314), // &nleq -> &nleqq + new Transition (5414, 5422), // &NotE -> &NotEq + new Transition (5447, 5448), // &NotGreaterE -> &NotGreaterEq + new Transition (5457, 5458), // &NotGreaterFullE -> &NotGreaterFullEq + new Transition (5481, 5482), // &NotGreaterSlantE -> &NotGreaterSlantEq + new Transition (5506, 5507), // &NotHumpE -> &NotHumpEq + new Transition (5545, 5546), // &NotLeftTriangleE -> &NotLeftTriangleEq + new Transition (5554, 5555), // &NotLessE -> &NotLessEq + new Transition (5578, 5579), // &NotLessSlantE -> &NotLessSlantEq + new Transition (5639, 5640), // &NotPrecedesE -> &NotPrecedesEq + new Transition (5650, 5651), // &NotPrecedesSlantE -> &NotPrecedesSlantEq + new Transition (5688, 5689), // &NotRightTriangleE -> &NotRightTriangleEq + new Transition (5694, 5695), // &NotS -> &NotSq + new Transition (5707, 5708), // &NotSquareSubsetE -> &NotSquareSubsetEq + new Transition (5720, 5721), // &NotSquareSupersetE -> &NotSquareSupersetEq + new Transition (5732, 5733), // &NotSubsetE -> &NotSubsetEq + new Transition (5745, 5746), // &NotSucceedsE -> &NotSucceedsEq + new Transition (5756, 5757), // &NotSucceedsSlantE -> &NotSucceedsSlantEq + new Transition (5775, 5776), // &NotSupersetE -> &NotSupersetEq + new Transition (5787, 5788), // &NotTildeE -> &NotTildeEq + new Transition (5797, 5798), // &NotTildeFullE -> &NotTildeFullEq + new Transition (5852, 5853), // &nprece -> &npreceq + new Transition (5895, 5942), // &ns -> &nsq + new Transition (5930, 5932), // &nsime -> &nsimeq + new Transition (5962, 5963), // &nsubsete -> &nsubseteq + new Transition (5963, 5965), // &nsubseteq -> &nsubseteqq + new Transition (5970, 5971), // &nsucce -> &nsucceq + new Transition (5983, 5984), // &nsupsete -> &nsupseteq + new Transition (5984, 5986), // &nsupseteq -> &nsupseteqq + new Transition (6018, 6019), // &ntrianglelefte -> &ntrianglelefteq + new Transition (6027, 6028), // &ntrianglerighte -> &ntrianglerighteq + new Transition (6669, 6670), // &preccurlye -> &preccurlyeq + new Transition (6679, 6680), // &PrecedesE -> &PrecedesEq + new Transition (6690, 6691), // &PrecedesSlantE -> &PrecedesSlantEq + new Transition (6702, 6703), // &prece -> &preceq + new Transition (6713, 6714), // &precne -> &precneq + new Transition (6714, 6715), // &precneq -> &precneqq + new Transition (6866, 6867), // &queste -> &questeq + new Transition (6882, 6921), // &ra -> &raq + new Transition (7053, 7063), // &rd -> &rdq + new Transition (7102, 7110), // &ReverseE -> &ReverseEq + new Transition (7123, 7124), // &ReverseUpE -> &ReverseUpEq + new Transition (7326, 7327), // &rights -> &rightsq + new Transition (7377, 7378), // &RightTriangleE -> &RightTriangleEq + new Transition (7439, 7440), // &risingdotse -> &risingdotseq + new Transition (7542, 7559), // &rs -> &rsq + new Transition (7543, 7544), // &rsa -> &rsaq + new Transition (7610, 7980), // &S -> &Sq + new Transition (7617, 7968), // &s -> &sq + new Transition (7624, 7625), // &sb -> &sbq + new Transition (7853, 7855), // &sime -> &simeq + new Transition (7994, 7995), // &sqsubsete -> &sqsubseteq + new Transition (8005, 8006), // &sqsupsete -> &sqsupseteq + new Transition (8039, 8040), // &SquareSubsetE -> &SquareSubsetEq + new Transition (8052, 8053), // &SquareSupersetE -> &SquareSupersetEq + new Transition (8173, 8174), // &subsete -> &subseteq + new Transition (8174, 8176), // &subseteq -> &subseteqq + new Transition (8178, 8179), // &SubsetE -> &SubsetEq + new Transition (8185, 8186), // &subsetne -> &subsetneq + new Transition (8186, 8188), // &subsetneq -> &subsetneqq + new Transition (8213, 8214), // &succcurlye -> &succcurlyeq + new Transition (8223, 8224), // &SucceedsE -> &SucceedsEq + new Transition (8234, 8235), // &SucceedsSlantE -> &SucceedsSlantEq + new Transition (8246, 8247), // &succe -> &succeq + new Transition (8257, 8258), // &succne -> &succneq + new Transition (8258, 8259), // &succneq -> &succneqq + new Transition (8314, 8315), // &SupersetE -> &SupersetEq + new Transition (8356, 8357), // &supsete -> &supseteq + new Transition (8357, 8359), // &supseteq -> &supseteqq + new Transition (8362, 8363), // &supsetne -> &supsetneq + new Transition (8363, 8365), // &supsetneq -> &supsetneqq + new Transition (8554, 8555), // &TildeE -> &TildeEq + new Transition (8564, 8565), // &TildeFullE -> &TildeFullEq + new Transition (8638, 8653), // &triangle -> &triangleq + new Transition (8650, 8651), // &trianglelefte -> &trianglelefteq + new Transition (8661, 8662), // &trianglerighte -> &trianglerighteq + new Transition (9034, 9035), // &UpE -> &UpEq + new Transition (9264, 9265), // &varsubsetne -> &varsubsetneq + new Transition (9265, 9267), // &varsubsetneq -> &varsubsetneqq + new Transition (9274, 9275), // &varsupsetne -> &varsupsetneq + new Transition (9275, 9277), // &varsupsetneq -> &varsupsetneqq + new Transition (9352, 9353), // &veee -> &veeeq + new Transition (9508, 9510), // &wedge -> &wedgeq + new Transition (9636, 9640) // &xs -> &xsq + }; + TransitionTable_r = new Transition[942] { + new Transition (0, 6876), // & -> &r + new Transition (1, 237), // &A -> &Ar + new Transition (8, 242), // &a -> &ar + new Transition (15, 16), // &Ab -> &Abr + new Transition (21, 22), // &ab -> &abr + new Transition (34, 35), // &Aci -> &Acir + new Transition (38, 39), // &aci -> &acir + new Transition (60, 65), // &af -> &afr + new Transition (62, 63), // &Af -> &Afr + new Transition (67, 68), // &Ag -> &Agr + new Transition (73, 74), // &ag -> &agr + new Transition (100, 101), // &Amac -> &Amacr + new Transition (105, 106), // &amac -> &amacr + new Transition (136, 164), // &ang -> &angr + new Transition (179, 180), // &angza -> &angzar + new Transition (180, 181), // &angzar -> &angzarr + new Transition (203, 204), // &apaci -> &apacir + new Transition (229, 230), // &app -> &appr + new Transition (248, 249), // &Asc -> &Ascr + new Transition (252, 253), // &asc -> &ascr + new Transition (301, 730), // &b -> &br + new Transition (302, 344), // &ba -> &bar + new Transition (318, 319), // &backp -> &backpr + new Transition (331, 725), // &B -> &Br + new Transition (332, 341), // &Ba -> &Bar + new Transition (360, 361), // &bb -> &bbr + new Transition (365, 366), // &bbrktb -> &bbrktbr + new Transition (384, 409), // &be -> &ber + new Transition (390, 414), // &Be -> &Ber + new Transition (436, 437), // &Bf -> &Bfr + new Transition (439, 440), // &bf -> &bfr + new Transition (448, 449), // &bigci -> &bigcir + new Transition (478, 479), // &bigsta -> &bigstar + new Transition (481, 482), // &bigt -> &bigtr + new Transition (514, 515), // &bka -> &bkar + new Transition (534, 535), // &blacksqua -> &blacksquar + new Transition (538, 539), // &blackt -> &blacktr + new Transition (545, 557), // &blacktriangle -> &blacktriangler + new Transition (618, 630), // &boxD -> &boxDr + new Transition (623, 634), // &boxd -> &boxdr + new Transition (673, 685), // &boxU -> &boxUr + new Transition (678, 689), // &boxu -> &boxur + new Transition (691, 713), // &boxV -> &boxVr + new Transition (693, 717), // &boxv -> &boxvr + new Transition (719, 720), // &bp -> &bpr + new Transition (737, 738), // &brvba -> ¦ + new Transition (741, 742), // &Bsc -> &Bscr + new Transition (745, 746), // &bsc -> &bscr + new Transition (789, 1261), // &C -> &Cr + new Transition (796, 1256), // &c -> &cr + new Transition (797, 848), // &ca -> &car + new Transition (811, 812), // &capb -> &capbr + new Transition (836, 837), // &CapitalDiffe -> &CapitalDiffer + new Transition (862, 872), // &cca -> &ccar + new Transition (867, 868), // &Cca -> &Ccar + new Transition (886, 887), // &Cci -> &Ccir + new Transition (890, 891), // &cci -> &ccir + new Transition (938, 939), // &Cente -> &Center + new Transition (944, 945), // ¢e -> ¢er + new Transition (950, 951), // &Cf -> &Cfr + new Transition (953, 954), // &cf -> &cfr + new Transition (969, 970), // &checkma -> &checkmar + new Transition (978, 979), // &ci -> &cir + new Transition (988, 989), // &circlea -> &circlear + new Transition (989, 990), // &circlear -> &circlearr + new Transition (992, 998), // &circlearrow -> &circlearrowr + new Transition (1010, 1011), // &circledci -> &circledcir + new Transition (1019, 1020), // &Ci -> &Cir + new Transition (1065, 1066), // &cirsci -> &cirscir + new Transition (1081, 1082), // &ClockwiseContou -> &ClockwiseContour + new Transition (1087, 1088), // &ClockwiseContourInteg -> &ClockwiseContourIntegr + new Transition (1095, 1096), // &CloseCu -> &CloseCur + new Transition (1172, 1173), // &Cong -> &Congr + new Transition (1189, 1190), // &Contou -> &Contour + new Transition (1195, 1196), // &ContourInteg -> &ContourIntegr + new Transition (1200, 1210), // &Cop -> &Copr + new Transition (1203, 1206), // &cop -> &copr + new Transition (1223, 1224), // ©s -> ©sr + new Transition (1229, 1230), // &Counte -> &Counter + new Transition (1245, 1246), // &CounterClockwiseContou -> &CounterClockwiseContour + new Transition (1251, 1252), // &CounterClockwiseContourInteg -> &CounterClockwiseContourIntegr + new Transition (1257, 1258), // &cra -> &crar + new Transition (1258, 1259), // &crar -> &crarr + new Transition (1271, 1272), // &Csc -> &Cscr + new Transition (1275, 1276), // &csc -> &cscr + new Transition (1292, 1346), // &cu -> &cur + new Transition (1294, 1295), // &cuda -> &cudar + new Transition (1295, 1296), // &cudar -> &cudarr + new Transition (1296, 1299), // &cudarr -> &cudarrr + new Transition (1302, 1303), // &cuep -> &cuepr + new Transition (1309, 1310), // &cula -> &cular + new Transition (1310, 1311), // &cular -> &cularr + new Transition (1320, 1321), // &cupb -> &cupbr + new Transition (1341, 1342), // &cupo -> &cupor + new Transition (1346, 1377), // &cur -> &curr + new Transition (1347, 1348), // &cura -> &curar + new Transition (1348, 1349), // &curar -> &curarr + new Transition (1357, 1358), // &curlyeqp -> &curlyeqpr + new Transition (1383, 1384), // &curvea -> &curvear + new Transition (1384, 1385), // &curvear -> &curvearr + new Transition (1387, 1393), // &curvearrow -> &curvearrowr + new Transition (1426, 1444), // &Da -> &Dar + new Transition (1429, 1430), // &Dagge -> &Dagger + new Transition (1432, 2023), // &d -> &dr + new Transition (1433, 1451), // &da -> &dar + new Transition (1436, 1437), // &dagge -> &dagger + new Transition (1444, 1445), // &Dar -> &Darr + new Transition (1447, 1448), // &dA -> &dAr + new Transition (1448, 1449), // &dAr -> &dArr + new Transition (1451, 1452), // &dar -> &darr + new Transition (1465, 1466), // &dbka -> &dbkar + new Transition (1475, 1476), // &Dca -> &Dcar + new Transition (1481, 1482), // &dca -> &dcar + new Transition (1494, 1500), // &dda -> &ddar + new Transition (1497, 1498), // &ddagge -> &ddagger + new Transition (1500, 1501), // &ddar -> &ddarr + new Transition (1504, 1505), // &DDot -> &DDotr + new Transition (1535, 1544), // &df -> &dfr + new Transition (1541, 1542), // &Df -> &Dfr + new Transition (1547, 1548), // &dHa -> &dHar + new Transition (1551, 1552), // &dha -> &dhar + new Transition (1552, 1555), // &dhar -> &dharr + new Transition (1559, 1560), // &Diac -> &Diacr + new Transition (1587, 1588), // &DiacriticalG -> &DiacriticalGr + new Transition (1623, 1624), // &Diffe -> &Differ + new Transition (1670, 1675), // &dlc -> &dlcr + new Transition (1671, 1672), // &dlco -> &dlcor + new Transition (1682, 1683), // &dolla -> &dollar + new Transition (1727, 1728), // &dotsqua -> &dotsquar + new Transition (1736, 1737), // &doubleba -> &doublebar + new Transition (1753, 1754), // &DoubleContou -> &DoubleContour + new Transition (1759, 1760), // &DoubleContourInteg -> &DoubleContourIntegr + new Transition (1770, 1771), // &DoubleDownA -> &DoubleDownAr + new Transition (1771, 1772), // &DoubleDownAr -> &DoubleDownArr + new Transition (1780, 1781), // &DoubleLeftA -> &DoubleLeftAr + new Transition (1781, 1782), // &DoubleLeftAr -> &DoubleLeftArr + new Transition (1791, 1792), // &DoubleLeftRightA -> &DoubleLeftRightAr + new Transition (1792, 1793), // &DoubleLeftRightAr -> &DoubleLeftRightArr + new Transition (1808, 1809), // &DoubleLongLeftA -> &DoubleLongLeftAr + new Transition (1809, 1810), // &DoubleLongLeftAr -> &DoubleLongLeftArr + new Transition (1819, 1820), // &DoubleLongLeftRightA -> &DoubleLongLeftRightAr + new Transition (1820, 1821), // &DoubleLongLeftRightAr -> &DoubleLongLeftRightArr + new Transition (1830, 1831), // &DoubleLongRightA -> &DoubleLongRightAr + new Transition (1831, 1832), // &DoubleLongRightAr -> &DoubleLongRightArr + new Transition (1841, 1842), // &DoubleRightA -> &DoubleRightAr + new Transition (1842, 1843), // &DoubleRightAr -> &DoubleRightArr + new Transition (1853, 1854), // &DoubleUpA -> &DoubleUpAr + new Transition (1854, 1855), // &DoubleUpAr -> &DoubleUpArr + new Transition (1863, 1864), // &DoubleUpDownA -> &DoubleUpDownAr + new Transition (1864, 1865), // &DoubleUpDownAr -> &DoubleUpDownArr + new Transition (1870, 1871), // &DoubleVe -> &DoubleVer + new Transition (1878, 1879), // &DoubleVerticalBa -> &DoubleVerticalBar + new Transition (1883, 1884), // &DownA -> &DownAr + new Transition (1884, 1885), // &DownAr -> &DownArr + new Transition (1889, 1890), // &Downa -> &Downar + new Transition (1890, 1891), // &Downar -> &Downarr + new Transition (1897, 1898), // &downa -> &downar + new Transition (1898, 1899), // &downar -> &downarr + new Transition (1904, 1905), // &DownArrowBa -> &DownArrowBar + new Transition (1909, 1910), // &DownArrowUpA -> &DownArrowUpAr + new Transition (1910, 1911), // &DownArrowUpAr -> &DownArrowUpArr + new Transition (1915, 1916), // &DownB -> &DownBr + new Transition (1925, 1926), // &downdowna -> &downdownar + new Transition (1926, 1927), // &downdownar -> &downdownarr + new Transition (1933, 1934), // &downha -> &downhar + new Transition (1938, 1944), // &downharpoon -> &downharpoonr + new Transition (1963, 1964), // &DownLeftRightVecto -> &DownLeftRightVector + new Transition (1973, 1974), // &DownLeftTeeVecto -> &DownLeftTeeVector + new Transition (1980, 1981), // &DownLeftVecto -> &DownLeftVector + new Transition (1984, 1985), // &DownLeftVectorBa -> &DownLeftVectorBar + new Transition (1999, 2000), // &DownRightTeeVecto -> &DownRightTeeVector + new Transition (2006, 2007), // &DownRightVecto -> &DownRightVector + new Transition (2010, 2011), // &DownRightVectorBa -> &DownRightVectorBar + new Transition (2017, 2018), // &DownTeeA -> &DownTeeAr + new Transition (2018, 2019), // &DownTeeAr -> &DownTeeArr + new Transition (2026, 2027), // &drbka -> &drbkar + new Transition (2031, 2036), // &drc -> &drcr + new Transition (2032, 2033), // &drco -> &drcor + new Transition (2041, 2042), // &Dsc -> &Dscr + new Transition (2045, 2046), // &dsc -> &dscr + new Transition (2057, 2058), // &Dst -> &Dstr + new Transition (2062, 2063), // &dst -> &dstr + new Transition (2067, 2072), // &dt -> &dtr + new Transition (2078, 2079), // &dua -> &duar + new Transition (2079, 2080), // &duar -> &duarr + new Transition (2083, 2084), // &duha -> &duhar + new Transition (2102, 2103), // &dzig -> &dzigr + new Transition (2104, 2105), // &dzigra -> &dzigrar + new Transition (2105, 2106), // &dzigrar -> &dzigrarr + new Transition (2115, 2409), // &e -> &er + new Transition (2124, 2125), // &easte -> &easter + new Transition (2128, 2129), // &Eca -> &Ecar + new Transition (2134, 2135), // &eca -> &ecar + new Transition (2139, 2140), // &eci -> &ecir + new Transition (2142, 2143), // &Eci -> &Ecir + new Transition (2175, 2183), // &ef -> &efr + new Transition (2180, 2181), // &Ef -> &Efr + new Transition (2185, 2193), // &eg -> &egr + new Transition (2187, 2188), // &Eg -> &Egr + new Transition (2216, 2217), // &elinte -> &elinter + new Transition (2230, 2231), // &Emac -> &Emacr + new Transition (2235, 2236), // &emac -> &emacr + new Transition (2257, 2258), // &EmptySmallSqua -> &EmptySmallSquar + new Transition (2264, 2265), // &EmptyVe -> &EmptyVer + new Transition (2275, 2276), // &EmptyVerySmallSqua -> &EmptyVerySmallSquar + new Transition (2313, 2314), // &epa -> &epar + new Transition (2341, 2342), // &eqci -> &eqcir + new Transition (2359, 2360), // &eqslantgt -> &eqslantgtr + new Transition (2390, 2391), // &Equilib -> &Equilibr + new Transition (2404, 2405), // &eqvpa -> &eqvpar + new Transition (2410, 2411), // &era -> &erar + new Transition (2411, 2412), // &erar -> &erarr + new Transition (2419, 2420), // &Esc -> &Escr + new Transition (2423, 2424), // &esc -> &escr + new Transition (2451, 2455), // &eu -> &eur + new Transition (2503, 2647), // &f -> &fr + new Transition (2530, 2547), // &ff -> &ffr + new Transition (2544, 2545), // &Ff -> &Ffr + new Transition (2567, 2568), // &FilledSmallSqua -> &FilledSmallSquar + new Transition (2572, 2573), // &FilledVe -> &FilledVer + new Transition (2583, 2584), // &FilledVerySmallSqua -> &FilledVerySmallSquar + new Transition (2608, 2616), // &Fo -> &For + new Transition (2612, 2621), // &fo -> &for + new Transition (2630, 2631), // &Fou -> &Four + new Transition (2633, 2634), // &Fourie -> &Fourier + new Transition (2635, 2636), // &Fouriert -> &Fouriertr + new Transition (2640, 2641), // &fpa -> &fpar + new Transition (2694, 2695), // &Fsc -> &Fscr + new Transition (2698, 2699), // &fsc -> &fscr + new Transition (2701, 2861), // &g -> &gr + new Transition (2708, 2866), // &G -> &Gr + new Transition (2724, 2725), // &Gb -> &Gbr + new Transition (2730, 2731), // &gb -> &gbr + new Transition (2742, 2743), // &Gci -> &Gcir + new Transition (2747, 2748), // &gci -> &gcir + new Transition (2799, 2800), // &Gf -> &Gfr + new Transition (2802, 2803), // &gf -> &gfr + new Transition (2836, 2837), // &gnapp -> &gnappr + new Transition (2870, 2871), // &Greate -> &Greater + new Transition (2893, 2894), // &GreaterG -> &GreaterGr + new Transition (2898, 2899), // &GreaterGreate -> &GreaterGreater + new Transition (2924, 2925), // &Gsc -> &Gscr + new Transition (2928, 2929), // &gsc -> &gscr + new Transition (2942, 2965), // > -> >r + new Transition (2947, 2948), // >ci -> >cir + new Transition (2956, 2957), // >lPa -> >lPar + new Transition (2966, 2973), // >ra -> >rar + new Transition (2968, 2969), // >rapp -> >rappr + new Transition (2973, 2974), // >rar -> >rarr + new Transition (3003, 3004), // &gve -> &gver + new Transition (3021, 3041), // &ha -> &har + new Transition (3022, 3023), // &hai -> &hair + new Transition (3041, 3050), // &har -> &harr + new Transition (3046, 3047), // &hA -> &hAr + new Transition (3047, 3048), // &hAr -> &hArr + new Transition (3053, 3054), // &harrci -> &harrcir + new Transition (3061, 3062), // &hba -> &hbar + new Transition (3065, 3066), // &Hci -> &Hcir + new Transition (3070, 3071), // &hci -> &hcir + new Transition (3074, 3089), // &he -> &her + new Transition (3075, 3076), // &hea -> &hear + new Transition (3094, 3095), // &Hf -> &Hfr + new Transition (3097, 3098), // &hf -> &hfr + new Transition (3103, 3104), // &Hilbe -> &Hilber + new Transition (3115, 3116), // &hksea -> &hksear + new Transition (3121, 3122), // &hkswa -> &hkswar + new Transition (3126, 3166), // &ho -> &hor + new Transition (3127, 3128), // &hoa -> &hoar + new Transition (3128, 3129), // &hoar -> &hoarr + new Transition (3137, 3148), // &hook -> &hookr + new Transition (3142, 3143), // &hooklefta -> &hookleftar + new Transition (3143, 3144), // &hookleftar -> &hookleftarr + new Transition (3153, 3154), // &hookrighta -> &hookrightar + new Transition (3154, 3155), // &hookrightar -> &hookrightarr + new Transition (3159, 3171), // &Ho -> &Hor + new Transition (3168, 3169), // &horba -> &horbar + new Transition (3185, 3186), // &Hsc -> &Hscr + new Transition (3189, 3190), // &hsc -> &hscr + new Transition (3197, 3198), // &Hst -> &Hstr + new Transition (3202, 3203), // &hst -> &hstr + new Transition (3253, 3254), // &Ici -> &Icir + new Transition (3257, 3258), // &ici -> &icir + new Transition (3281, 3287), // &if -> &ifr + new Transition (3284, 3285), // &If -> &Ifr + new Transition (3289, 3290), // &Ig -> &Igr + new Transition (3295, 3296), // &ig -> &igr + new Transition (3333, 3334), // &Imac -> &Imacr + new Transition (3338, 3339), // &imac -> &imacr + new Transition (3347, 3348), // &Imagina -> &Imaginar + new Transition (3358, 3359), // &imagpa -> &imagpar + new Transition (3381, 3382), // &inca -> &incar + new Transition (3407, 3419), // &inte -> &inter + new Transition (3409, 3410), // &intege -> &integer + new Transition (3413, 3424), // &Inte -> &Inter + new Transition (3414, 3415), // &Integ -> &Integr + new Transition (3434, 3435), // &intla -> &intlar + new Transition (3439, 3440), // &intp -> &intpr + new Transition (3492, 3493), // &ip -> &ipr + new Transition (3504, 3505), // &Isc -> &Iscr + new Transition (3508, 3509), // &isc -> &iscr + new Transition (3557, 3558), // &Jci -> &Jcir + new Transition (3563, 3564), // &jci -> &jcir + new Transition (3571, 3572), // &Jf -> &Jfr + new Transition (3574, 3575), // &jf -> &jfr + new Transition (3591, 3592), // &Jsc -> &Jscr + new Transition (3595, 3596), // &jsc -> &jscr + new Transition (3598, 3599), // &Jse -> &Jser + new Transition (3603, 3604), // &jse -> &jser + new Transition (3648, 3649), // &Kf -> &Kfr + new Transition (3651, 3652), // &kf -> &kfr + new Transition (3654, 3655), // &kg -> &kgr + new Transition (3685, 3686), // &Ksc -> &Kscr + new Transition (3689, 3690), // &ksc -> &kscr + new Transition (3692, 4628), // &l -> &lr + new Transition (3693, 3762), // &lA -> &lAr + new Transition (3694, 3695), // &lAa -> &lAar + new Transition (3695, 3696), // &lAar -> &lAarr + new Transition (3699, 3759), // &La -> &Lar + new Transition (3705, 3765), // &la -> &lar + new Transition (3718, 3719), // &lag -> &lagr + new Transition (3751, 3752), // &Laplacet -> &Laplacetr + new Transition (3759, 3760), // &Lar -> &Larr + new Transition (3762, 3763), // &lAr -> &lArr + new Transition (3765, 3766), // &lar -> &larr + new Transition (3808, 3809), // &lBa -> &lBar + new Transition (3809, 3810), // &lBar -> &lBarr + new Transition (3812, 3821), // &lb -> &lbr + new Transition (3813, 3814), // &lba -> &lbar + new Transition (3814, 3815), // &lbar -> &lbarr + new Transition (3817, 3818), // &lbb -> &lbbr + new Transition (3838, 3839), // &Lca -> &Lcar + new Transition (3844, 3845), // &lca -> &lcar + new Transition (3869, 3879), // &ld -> &ldr + new Transition (3875, 3877), // &ldquo -> &ldquor + new Transition (3882, 3883), // &ldrdha -> &ldrdhar + new Transition (3888, 3889), // &ldrusha -> &ldrushar + new Transition (3900, 4041), // &Left -> &Leftr + new Transition (3901, 3914), // &LeftA -> &LeftAr + new Transition (3906, 3907), // &LeftAngleB -> &LeftAngleBr + new Transition (3914, 3915), // &LeftAr -> &LeftArr + new Transition (3919, 3920), // &Lefta -> &Leftar + new Transition (3920, 3921), // &Leftar -> &Leftarr + new Transition (3926, 4052), // &left -> &leftr + new Transition (3927, 3928), // &lefta -> &leftar + new Transition (3928, 3929), // &leftar -> &leftarr + new Transition (3934, 3935), // &LeftArrowBa -> &LeftArrowBar + new Transition (3942, 3943), // &LeftArrowRightA -> &LeftArrowRightAr + new Transition (3943, 3944), // &LeftArrowRightAr -> &LeftArrowRightArr + new Transition (3967, 3968), // &LeftDoubleB -> &LeftDoubleBr + new Transition (3984, 3985), // &LeftDownTeeVecto -> &LeftDownTeeVector + new Transition (3991, 3992), // &LeftDownVecto -> &LeftDownVector + new Transition (3995, 3996), // &LeftDownVectorBa -> &LeftDownVectorBar + new Transition (4001, 4002), // &LeftFloo -> &LeftFloor + new Transition (4005, 4006), // &leftha -> &lefthar + new Transition (4023, 4024), // &leftlefta -> &leftleftar + new Transition (4024, 4025), // &leftleftar -> &leftleftarr + new Transition (4035, 4036), // &LeftRightA -> &LeftRightAr + new Transition (4036, 4037), // &LeftRightAr -> &LeftRightArr + new Transition (4046, 4047), // &Leftrighta -> &Leftrightar + new Transition (4047, 4048), // &Leftrightar -> &Leftrightarr + new Transition (4057, 4058), // &leftrighta -> &leftrightar + new Transition (4058, 4059), // &leftrightar -> &leftrightarr + new Transition (4066, 4067), // &leftrightha -> &leftrighthar + new Transition (4079, 4080), // &leftrightsquiga -> &leftrightsquigar + new Transition (4080, 4081), // &leftrightsquigar -> &leftrightsquigarr + new Transition (4089, 4090), // &LeftRightVecto -> &LeftRightVector + new Transition (4092, 4120), // &LeftT -> &LeftTr + new Transition (4096, 4097), // &LeftTeeA -> &LeftTeeAr + new Transition (4097, 4098), // &LeftTeeAr -> &LeftTeeArr + new Transition (4106, 4107), // &LeftTeeVecto -> &LeftTeeVector + new Transition (4110, 4111), // &leftth -> &leftthr + new Transition (4129, 4130), // &LeftTriangleBa -> &LeftTriangleBar + new Transition (4148, 4149), // &LeftUpDownVecto -> &LeftUpDownVector + new Transition (4158, 4159), // &LeftUpTeeVecto -> &LeftUpTeeVector + new Transition (4165, 4166), // &LeftUpVecto -> &LeftUpVector + new Transition (4169, 4170), // &LeftUpVectorBa -> &LeftUpVectorBar + new Transition (4176, 4177), // &LeftVecto -> &LeftVector + new Transition (4180, 4181), // &LeftVectorBa -> &LeftVectorBar + new Transition (4206, 4208), // &lesdoto -> &lesdotor + new Transition (4218, 4219), // &lessapp -> &lessappr + new Transition (4230, 4231), // &lesseqgt -> &lesseqgtr + new Transition (4235, 4236), // &lesseqqgt -> &lesseqqgtr + new Transition (4245, 4246), // &LessEqualG -> &LessEqualGr + new Transition (4250, 4251), // &LessEqualGreate -> &LessEqualGreater + new Transition (4263, 4264), // &LessG -> &LessGr + new Transition (4268, 4269), // &LessGreate -> &LessGreater + new Transition (4272, 4273), // &lessgt -> &lessgtr + new Transition (4301, 4315), // &lf -> &lfr + new Transition (4309, 4310), // &lfloo -> &lfloor + new Transition (4312, 4313), // &Lf -> &Lfr + new Transition (4322, 4323), // &lHa -> &lHar + new Transition (4326, 4327), // &lha -> &lhar + new Transition (4350, 4351), // &lla -> &llar + new Transition (4351, 4352), // &llar -> &llarr + new Transition (4355, 4356), // &llco -> &llcor + new Transition (4358, 4359), // &llcorne -> &llcorner + new Transition (4364, 4365), // &Llefta -> &Lleftar + new Transition (4365, 4366), // &Lleftar -> &Lleftarr + new Transition (4371, 4372), // &llha -> &llhar + new Transition (4375, 4376), // &llt -> &lltr + new Transition (4405, 4406), // &lnapp -> &lnappr + new Transition (4423, 4427), // &loa -> &loar + new Transition (4427, 4428), // &loar -> &loarr + new Transition (4430, 4431), // &lob -> &lobr + new Transition (4436, 4520), // &Long -> &Longr + new Transition (4441, 4442), // &LongLeftA -> &LongLeftAr + new Transition (4442, 4443), // &LongLeftAr -> &LongLeftArr + new Transition (4450, 4480), // &Longleft -> &Longleftr + new Transition (4451, 4452), // &Longlefta -> &Longleftar + new Transition (4452, 4453), // &Longleftar -> &Longleftarr + new Transition (4458, 4531), // &long -> &longr + new Transition (4462, 4491), // &longleft -> &longleftr + new Transition (4463, 4464), // &longlefta -> &longleftar + new Transition (4464, 4465), // &longleftar -> &longleftarr + new Transition (4474, 4475), // &LongLeftRightA -> &LongLeftRightAr + new Transition (4475, 4476), // &LongLeftRightAr -> &LongLeftRightArr + new Transition (4485, 4486), // &Longleftrighta -> &Longleftrightar + new Transition (4486, 4487), // &Longleftrightar -> &Longleftrightarr + new Transition (4496, 4497), // &longleftrighta -> &longleftrightar + new Transition (4497, 4498), // &longleftrightar -> &longleftrightarr + new Transition (4514, 4515), // &LongRightA -> &LongRightAr + new Transition (4515, 4516), // &LongRightAr -> &LongRightArr + new Transition (4525, 4526), // &Longrighta -> &Longrightar + new Transition (4526, 4527), // &Longrightar -> &Longrightarr + new Transition (4536, 4537), // &longrighta -> &longrightar + new Transition (4537, 4538), // &longrightar -> &longrightarr + new Transition (4544, 4545), // &loopa -> &loopar + new Transition (4545, 4546), // &loopar -> &looparr + new Transition (4548, 4554), // &looparrow -> &looparrowr + new Transition (4561, 4562), // &lopa -> &lopar + new Transition (4585, 4586), // &lowba -> &lowbar + new Transition (4589, 4590), // &Lowe -> &Lower + new Transition (4595, 4596), // &LowerLeftA -> &LowerLeftAr + new Transition (4596, 4597), // &LowerLeftAr -> &LowerLeftArr + new Transition (4606, 4607), // &LowerRightA -> &LowerRightAr + new Transition (4607, 4608), // &LowerRightAr -> &LowerRightArr + new Transition (4622, 4623), // &lpa -> &lpar + new Transition (4629, 4630), // &lra -> &lrar + new Transition (4630, 4631), // &lrar -> &lrarr + new Transition (4634, 4635), // &lrco -> &lrcor + new Transition (4637, 4638), // &lrcorne -> &lrcorner + new Transition (4641, 4642), // &lrha -> &lrhar + new Transition (4648, 4649), // &lrt -> &lrtr + new Transition (4659, 4660), // &Lsc -> &Lscr + new Transition (4662, 4663), // &lsc -> &lscr + new Transition (4680, 4682), // &lsquo -> &lsquor + new Transition (4684, 4685), // &Lst -> &Lstr + new Transition (4689, 4690), // &lst -> &lstr + new Transition (4698, 4731), // < -> <r + new Transition (4703, 4704), // <ci -> <cir + new Transition (4710, 4711), // <h -> <hr + new Transition (4721, 4722), // <la -> <lar + new Transition (4722, 4723), // <lar -> <larr + new Transition (4739, 4740), // <rPa -> <rPar + new Transition (4742, 4743), // &lu -> &lur + new Transition (4747, 4748), // &lurdsha -> &lurdshar + new Transition (4752, 4753), // &luruha -> &luruhar + new Transition (4756, 4757), // &lve -> &lver + new Transition (4768, 4804), // &ma -> &mar + new Transition (4769, 4770), // &mac -> ¯ + new Transition (4806, 4807), // &marke -> &marker + new Transition (4833, 4834), // &measu -> &measur + new Transition (4858, 4859), // &Mellint -> &Mellintr + new Transition (4862, 4863), // &Mf -> &Mfr + new Transition (4865, 4866), // &mf -> &mfr + new Transition (4872, 4873), // &mic -> &micr + new Transition (4883, 4884), // &midci -> &midcir + new Transition (4913, 4914), // &mld -> &mldr + new Transition (4938, 4939), // &Msc -> &Mscr + new Transition (4942, 4943), // &msc -> &mscr + new Transition (4965, 5855), // &n -> &nr + new Transition (4996, 4997), // &napp -> &nappr + new Transition (5002, 5003), // &natu -> &natur + new Transition (5021, 5030), // &nca -> &ncar + new Transition (5025, 5026), // &Nca -> &Ncar + new Transition (5066, 5067), // &nea -> &near + new Transition (5067, 5075), // &near -> &nearr + new Transition (5071, 5072), // &neA -> &neAr + new Transition (5072, 5073), // &neAr -> &neArr + new Transition (5122, 5123), // &NegativeVe -> &NegativeVer + new Transition (5142, 5143), // &nesea -> &nesear + new Transition (5152, 5153), // &NestedG -> &NestedGr + new Transition (5157, 5158), // &NestedGreate -> &NestedGreater + new Transition (5159, 5160), // &NestedGreaterG -> &NestedGreaterGr + new Transition (5164, 5165), // &NestedGreaterGreate -> &NestedGreaterGreater + new Transition (5189, 5190), // &Nf -> &Nfr + new Transition (5192, 5193), // &nf -> &nfr + new Transition (5221, 5223), // &ngt -> &ngtr + new Transition (5228, 5229), // &nhA -> &nhAr + new Transition (5229, 5230), // &nhAr -> &nhArr + new Transition (5232, 5233), // &nha -> &nhar + new Transition (5233, 5234), // &nhar -> &nharr + new Transition (5237, 5238), // &nhpa -> &nhpar + new Transition (5257, 5258), // &nlA -> &nlAr + new Transition (5258, 5259), // &nlAr -> &nlArr + new Transition (5261, 5262), // &nla -> &nlar + new Transition (5262, 5263), // &nlar -> &nlarr + new Transition (5265, 5266), // &nld -> &nldr + new Transition (5275, 5290), // &nLeft -> &nLeftr + new Transition (5276, 5277), // &nLefta -> &nLeftar + new Transition (5277, 5278), // &nLeftar -> &nLeftarr + new Transition (5283, 5301), // &nleft -> &nleftr + new Transition (5284, 5285), // &nlefta -> &nleftar + new Transition (5285, 5286), // &nleftar -> &nleftarr + new Transition (5295, 5296), // &nLeftrighta -> &nLeftrightar + new Transition (5296, 5297), // &nLeftrightar -> &nLeftrightarr + new Transition (5306, 5307), // &nleftrighta -> &nleftrightar + new Transition (5307, 5308), // &nleftrightar -> &nleftrightarr + new Transition (5334, 5336), // &nlt -> &nltr + new Transition (5348, 5349), // &NoB -> &NoBr + new Transition (5355, 5356), // &NonB -> &NonBr + new Transition (5383, 5384), // &NotCong -> &NotCongr + new Transition (5403, 5404), // &NotDoubleVe -> &NotDoubleVer + new Transition (5411, 5412), // &NotDoubleVerticalBa -> &NotDoubleVerticalBar + new Transition (5439, 5440), // &NotG -> &NotGr + new Transition (5444, 5445), // &NotGreate -> &NotGreater + new Transition (5463, 5464), // &NotGreaterG -> &NotGreaterGr + new Transition (5468, 5469), // &NotGreaterGreate -> &NotGreaterGreater + new Transition (5532, 5533), // &NotLeftT -> &NotLeftTr + new Transition (5542, 5543), // &NotLeftTriangleBa -> &NotLeftTriangleBar + new Transition (5560, 5561), // &NotLessG -> &NotLessGr + new Transition (5565, 5566), // &NotLessGreate -> &NotLessGreater + new Transition (5596, 5597), // &NotNestedG -> &NotNestedGr + new Transition (5601, 5602), // &NotNestedGreate -> &NotNestedGreater + new Transition (5603, 5604), // &NotNestedGreaterG -> &NotNestedGreaterGr + new Transition (5608, 5609), // &NotNestedGreaterGreate -> &NotNestedGreaterGreater + new Transition (5630, 5631), // &NotP -> &NotPr + new Transition (5659, 5660), // &NotReve -> &NotRever + new Transition (5675, 5676), // &NotRightT -> &NotRightTr + new Transition (5685, 5686), // &NotRightTriangleBa -> &NotRightTriangleBar + new Transition (5697, 5698), // &NotSqua -> &NotSquar + new Transition (5714, 5715), // &NotSquareSupe -> &NotSquareSuper + new Transition (5769, 5770), // &NotSupe -> &NotSuper + new Transition (5810, 5811), // &NotVe -> &NotVer + new Transition (5818, 5819), // &NotVerticalBa -> &NotVerticalBar + new Transition (5821, 5842), // &np -> &npr + new Transition (5822, 5823), // &npa -> &npar + new Transition (5856, 5857), // &nrA -> &nrAr + new Transition (5857, 5858), // &nrAr -> &nrArr + new Transition (5860, 5861), // &nra -> &nrar + new Transition (5861, 5862), // &nrar -> &nrarr + new Transition (5873, 5874), // &nRighta -> &nRightar + new Transition (5874, 5875), // &nRightar -> &nRightarr + new Transition (5883, 5884), // &nrighta -> &nrightar + new Transition (5884, 5885), // &nrightar -> &nrightarr + new Transition (5889, 5890), // &nrt -> &nrtr + new Transition (5896, 5908), // &nsc -> &nscr + new Transition (5905, 5906), // &Nsc -> &Nscr + new Transition (5911, 5912), // &nsho -> &nshor + new Transition (5919, 5920), // &nshortpa -> &nshortpar + new Transition (5939, 5940), // &nspa -> &nspar + new Transition (5988, 6006), // &nt -> &ntr + new Transition (6012, 6021), // &ntriangle -> &ntriangler + new Transition (6036, 6037), // &nume -> &numer + new Transition (6043, 6097), // &nv -> &nvr + new Transition (6074, 6075), // &nvHa -> &nvHar + new Transition (6075, 6076), // &nvHar -> &nvHarr + new Transition (6085, 6086), // &nvlA -> &nvlAr + new Transition (6086, 6087), // &nvlAr -> &nvlArr + new Transition (6091, 6093), // &nvlt -> &nvltr + new Transition (6098, 6099), // &nvrA -> &nvrAr + new Transition (6099, 6100), // &nvrAr -> &nvrArr + new Transition (6102, 6103), // &nvrt -> &nvrtr + new Transition (6112, 6113), // &nwa -> &nwar + new Transition (6113, 6121), // &nwar -> &nwarr + new Transition (6117, 6118), // &nwA -> &nwAr + new Transition (6118, 6119), // &nwAr -> &nwArr + new Transition (6128, 6129), // &nwnea -> &nwnear + new Transition (6131, 6340), // &O -> &Or + new Transition (6138, 6342), // &o -> &or + new Transition (6149, 6150), // &oci -> &ocir + new Transition (6153, 6154), // &Oci -> &Ocir + new Transition (6200, 6208), // &of -> &ofr + new Transition (6202, 6203), // &ofci -> &ofcir + new Transition (6205, 6206), // &Of -> &Ofr + new Transition (6210, 6220), // &og -> &ogr + new Transition (6214, 6215), // &Og -> &Ogr + new Transition (6229, 6230), // &ohba -> &ohbar + new Transition (6239, 6240), // &ola -> &olar + new Transition (6240, 6241), // &olar -> &olarr + new Transition (6243, 6247), // &olc -> &olcr + new Transition (6244, 6245), // &olci -> &olcir + new Transition (6260, 6261), // &Omac -> &Omacr + new Transition (6265, 6266), // &omac -> &omacr + new Transition (6277, 6278), // &Omic -> &Omicr + new Transition (6283, 6284), // &omic -> &omicr + new Transition (6303, 6304), // &opa -> &opar + new Transition (6310, 6311), // &OpenCu -> &OpenCur + new Transition (6332, 6333), // &ope -> &oper + new Transition (6344, 6345), // &ora -> &orar + new Transition (6345, 6346), // &orar -> &orarr + new Transition (6350, 6351), // &orde -> &order + new Transition (6365, 6366), // &oro -> &oror + new Transition (6379, 6380), // &Osc -> &Oscr + new Transition (6383, 6384), // &osc -> &oscr + new Transition (6432, 6433), // &ovba -> &ovbar + new Transition (6436, 6437), // &Ove -> &Over + new Transition (6438, 6442), // &OverB -> &OverBr + new Transition (6439, 6440), // &OverBa -> &OverBar + new Transition (6452, 6453), // &OverPa -> &OverPar + new Transition (6463, 6642), // &p -> &pr + new Transition (6464, 6465), // &pa -> &par + new Transition (6482, 6640), // &P -> &Pr + new Transition (6483, 6484), // &Pa -> &Par + new Transition (6497, 6498), // &pe -> &per + new Transition (6518, 6519), // &Pf -> &Pfr + new Transition (6521, 6522), // &pf -> &pfr + new Transition (6549, 6550), // &pitchfo -> &pitchfor + new Transition (6571, 6572), // &plusaci -> &plusacir + new Transition (6577, 6578), // &plusci -> &pluscir + new Transition (6613, 6614), // &Poinca -> &Poincar + new Transition (6659, 6660), // &precapp -> &precappr + new Transition (6665, 6666), // &preccu -> &preccur + new Transition (6708, 6709), // &precnapp -> &precnappr + new Transition (6757, 6758), // &profala -> &profalar + new Transition (6766, 6767), // &profsu -> &profsur + new Transition (6773, 6774), // &Propo -> &Propor + new Transition (6790, 6791), // &pru -> &prur + new Transition (6796, 6797), // &Psc -> &Pscr + new Transition (6800, 6801), // &psc -> &pscr + new Transition (6814, 6815), // &Qf -> &Qfr + new Transition (6818, 6819), // &qf -> &qfr + new Transition (6833, 6834), // &qp -> &qpr + new Transition (6840, 6841), // &Qsc -> &Qscr + new Transition (6844, 6845), // &qsc -> &qscr + new Transition (6850, 6851), // &quate -> &quater + new Transition (6876, 7526), // &r -> &rr + new Transition (6877, 6928), // &rA -> &rAr + new Transition (6878, 6879), // &rAa -> &rAar + new Transition (6879, 6880), // &rAar -> &rAarr + new Transition (6882, 6931), // &ra -> &rar + new Transition (6886, 7531), // &R -> &Rr + new Transition (6887, 6925), // &Ra -> &Rar + new Transition (6925, 6926), // &Rar -> &Rarr + new Transition (6928, 6929), // &rAr -> &rArr + new Transition (6931, 6932), // &rar -> &rarr + new Transition (6987, 6988), // &RBa -> &RBar + new Transition (6988, 6989), // &RBar -> &RBarr + new Transition (6992, 6993), // &rBa -> &rBar + new Transition (6993, 6994), // &rBar -> &rBarr + new Transition (6996, 7005), // &rb -> &rbr + new Transition (6997, 6998), // &rba -> &rbar + new Transition (6998, 6999), // &rbar -> &rbarr + new Transition (7001, 7002), // &rbb -> &rbbr + new Transition (7022, 7023), // &Rca -> &Rcar + new Transition (7028, 7029), // &rca -> &rcar + new Transition (7060, 7061), // &rdldha -> &rdldhar + new Transition (7065, 7067), // &rdquo -> &rdquor + new Transition (7083, 7084), // &realpa -> &realpar + new Transition (7098, 7099), // &Reve -> &Rever + new Transition (7115, 7116), // &ReverseEquilib -> &ReverseEquilibr + new Transition (7129, 7130), // &ReverseUpEquilib -> &ReverseUpEquilibr + new Transition (7135, 7149), // &rf -> &rfr + new Transition (7143, 7144), // &rfloo -> &rfloor + new Transition (7146, 7147), // &Rf -> &Rfr + new Transition (7152, 7153), // &rHa -> &rHar + new Transition (7156, 7157), // &rha -> &rhar + new Transition (7175, 7188), // &RightA -> &RightAr + new Transition (7180, 7181), // &RightAngleB -> &RightAngleBr + new Transition (7188, 7189), // &RightAr -> &RightArr + new Transition (7193, 7194), // &Righta -> &Rightar + new Transition (7194, 7195), // &Rightar -> &Rightarr + new Transition (7202, 7314), // &right -> &rightr + new Transition (7203, 7204), // &righta -> &rightar + new Transition (7204, 7205), // &rightar -> &rightarr + new Transition (7210, 7211), // &RightArrowBa -> &RightArrowBar + new Transition (7217, 7218), // &RightArrowLeftA -> &RightArrowLeftAr + new Transition (7218, 7219), // &RightArrowLeftAr -> &RightArrowLeftArr + new Transition (7242, 7243), // &RightDoubleB -> &RightDoubleBr + new Transition (7259, 7260), // &RightDownTeeVecto -> &RightDownTeeVector + new Transition (7266, 7267), // &RightDownVecto -> &RightDownVector + new Transition (7270, 7271), // &RightDownVectorBa -> &RightDownVectorBar + new Transition (7276, 7277), // &RightFloo -> &RightFloor + new Transition (7280, 7281), // &rightha -> &righthar + new Transition (7298, 7299), // &rightlefta -> &rightleftar + new Transition (7299, 7300), // &rightleftar -> &rightleftarr + new Transition (7306, 7307), // &rightleftha -> &rightlefthar + new Transition (7319, 7320), // &rightrighta -> &rightrightar + new Transition (7320, 7321), // &rightrightar -> &rightrightarr + new Transition (7331, 7332), // &rightsquiga -> &rightsquigar + new Transition (7332, 7333), // &rightsquigar -> &rightsquigarr + new Transition (7337, 7365), // &RightT -> &RightTr + new Transition (7341, 7342), // &RightTeeA -> &RightTeeAr + new Transition (7342, 7343), // &RightTeeAr -> &RightTeeArr + new Transition (7351, 7352), // &RightTeeVecto -> &RightTeeVector + new Transition (7355, 7356), // &rightth -> &rightthr + new Transition (7374, 7375), // &RightTriangleBa -> &RightTriangleBar + new Transition (7393, 7394), // &RightUpDownVecto -> &RightUpDownVector + new Transition (7403, 7404), // &RightUpTeeVecto -> &RightUpTeeVector + new Transition (7410, 7411), // &RightUpVecto -> &RightUpVector + new Transition (7414, 7415), // &RightUpVectorBa -> &RightUpVectorBar + new Transition (7421, 7422), // &RightVecto -> &RightVector + new Transition (7425, 7426), // &RightVectorBa -> &RightVectorBar + new Transition (7443, 7444), // &rla -> &rlar + new Transition (7444, 7445), // &rlar -> &rlarr + new Transition (7448, 7449), // &rlha -> &rlhar + new Transition (7470, 7474), // &roa -> &roar + new Transition (7474, 7475), // &roar -> &roarr + new Transition (7477, 7478), // &rob -> &robr + new Transition (7482, 7483), // &ropa -> &ropar + new Transition (7513, 7514), // &rpa -> &rpar + new Transition (7527, 7528), // &rra -> &rrar + new Transition (7528, 7529), // &rrar -> &rrarr + new Transition (7536, 7537), // &Rrighta -> &Rrightar + new Transition (7537, 7538), // &Rrightar -> &Rrightarr + new Transition (7549, 7550), // &Rsc -> &Rscr + new Transition (7552, 7553), // &rsc -> &rscr + new Transition (7563, 7565), // &rsquo -> &rsquor + new Transition (7567, 7578), // &rt -> &rtr + new Transition (7568, 7569), // &rth -> &rthr + new Transition (7586, 7587), // &rtrilt -> &rtriltr + new Transition (7605, 7606), // &ruluha -> &ruluhar + new Transition (7617, 8068), // &s -> &sr + new Transition (7633, 7641), // &sca -> &scar + new Transition (7636, 7637), // &Sca -> &Scar + new Transition (7662, 7663), // &Sci -> &Scir + new Transition (7666, 7667), // &sci -> &scir + new Transition (7704, 7705), // &sea -> &sear + new Transition (7705, 7713), // &sear -> &searr + new Transition (7709, 7710), // &seA -> &seAr + new Transition (7710, 7711), // &seAr -> &seArr + new Transition (7726, 7727), // &seswa -> &seswar + new Transition (7741, 7742), // &Sf -> &Sfr + new Transition (7744, 7745), // &sf -> &sfr + new Transition (7752, 7753), // &sha -> &shar + new Transition (7773, 7774), // &Sho -> &Shor + new Transition (7780, 7781), // &ShortDownA -> &ShortDownAr + new Transition (7781, 7782), // &ShortDownAr -> &ShortDownArr + new Transition (7790, 7791), // &ShortLeftA -> &ShortLeftAr + new Transition (7791, 7792), // &ShortLeftAr -> &ShortLeftArr + new Transition (7796, 7797), // &sho -> &shor + new Transition (7804, 7805), // &shortpa -> &shortpar + new Transition (7817, 7818), // &ShortRightA -> &ShortRightAr + new Transition (7818, 7819), // &ShortRightAr -> &ShortRightArr + new Transition (7825, 7826), // &ShortUpA -> &ShortUpAr + new Transition (7826, 7827), // &ShortUpAr -> &ShortUpArr + new Transition (7847, 7873), // &sim -> &simr + new Transition (7874, 7875), // &simra -> &simrar + new Transition (7875, 7876), // &simrar -> &simrarr + new Transition (7879, 7880), // &sla -> &slar + new Transition (7880, 7881), // &slar -> &slarr + new Transition (7888, 7889), // &SmallCi -> &SmallCir + new Transition (7913, 7914), // &smepa -> &smepar + new Transition (7946, 7947), // &solba -> &solbar + new Transition (7957, 7966), // &spa -> &spar + new Transition (7980, 7981), // &Sq -> &Sqr + new Transition (8011, 8012), // &Squa -> &Squar + new Transition (8015, 8016), // &squa -> &squar + new Transition (8022, 8023), // &SquareInte -> &SquareInter + new Transition (8046, 8047), // &SquareSupe -> &SquareSuper + new Transition (8069, 8070), // &sra -> &srar + new Transition (8070, 8071), // &srar -> &srarr + new Transition (8074, 8075), // &Ssc -> &Sscr + new Transition (8078, 8079), // &ssc -> &sscr + new Transition (8092, 8093), // &ssta -> &sstar + new Transition (8097, 8098), // &Sta -> &Star + new Transition (8100, 8106), // &st -> &str + new Transition (8101, 8102), // &sta -> &star + new Transition (8131, 8160), // &sub -> &subr + new Transition (8161, 8162), // &subra -> &subrar + new Transition (8162, 8163), // &subrar -> &subrarr + new Transition (8203, 8204), // &succapp -> &succappr + new Transition (8209, 8210), // &succcu -> &succcur + new Transition (8252, 8253), // &succnapp -> &succnappr + new Transition (8308, 8309), // &Supe -> &Super + new Transition (8329, 8330), // &supla -> &suplar + new Transition (8330, 8331), // &suplar -> &suplarr + new Transition (8376, 8377), // &swa -> &swar + new Transition (8377, 8385), // &swar -> &swarr + new Transition (8381, 8382), // &swA -> &swAr + new Transition (8382, 8383), // &swAr -> &swArr + new Transition (8392, 8393), // &swnwa -> &swnwar + new Transition (8400, 8676), // &T -> &Tr + new Transition (8404, 8628), // &t -> &tr + new Transition (8405, 8406), // &ta -> &tar + new Transition (8415, 8416), // &tb -> &tbr + new Transition (8420, 8421), // &Tca -> &Tcar + new Transition (8426, 8427), // &tca -> &tcar + new Transition (8450, 8451), // &tel -> &telr + new Transition (8455, 8456), // &Tf -> &Tfr + new Transition (8458, 8459), // &tf -> &tfr + new Transition (8462, 8463), // &the -> &ther + new Transition (8468, 8469), // &The -> &Ther + new Transition (8472, 8473), // &Therefo -> &Therefor + new Transition (8477, 8478), // &therefo -> &therefor + new Transition (8498, 8499), // &thickapp -> &thickappr + new Transition (8540, 8541), // &tho -> &thor + new Transition (8582, 8583), // ×ba -> ×bar + new Transition (8601, 8602), // &topci -> &topcir + new Transition (8610, 8611), // &topfo -> &topfor + new Transition (8617, 8618), // &tp -> &tpr + new Transition (8638, 8655), // &triangle -> &triangler + new Transition (8706, 8707), // &Tsc -> &Tscr + new Transition (8710, 8711), // &tsc -> &tscr + new Transition (8727, 8728), // &Tst -> &Tstr + new Transition (8732, 8733), // &tst -> &tstr + new Transition (8746, 8757), // &twohead -> &twoheadr + new Transition (8751, 8752), // &twoheadlefta -> &twoheadleftar + new Transition (8752, 8753), // &twoheadleftar -> &twoheadleftarr + new Transition (8762, 8763), // &twoheadrighta -> &twoheadrightar + new Transition (8763, 8764), // &twoheadrightar -> &twoheadrightarr + new Transition (8768, 9140), // &U -> &Ur + new Transition (8769, 8782), // &Ua -> &Uar + new Transition (8775, 9127), // &u -> &ur + new Transition (8776, 8789), // &ua -> &uar + new Transition (8782, 8783), // &Uar -> &Uarr + new Transition (8785, 8786), // &uA -> &uAr + new Transition (8786, 8787), // &uAr -> &uArr + new Transition (8789, 8790), // &uar -> &uarr + new Transition (8794, 8795), // &Uarroci -> &Uarrocir + new Transition (8797, 8798), // &Ub -> &Ubr + new Transition (8802, 8803), // &ub -> &ubr + new Transition (8816, 8817), // &Uci -> &Ucir + new Transition (8821, 8822), // &uci -> &ucir + new Transition (8830, 8831), // &uda -> &udar + new Transition (8831, 8832), // &udar -> &udarr + new Transition (8846, 8847), // &udha -> &udhar + new Transition (8849, 8858), // &uf -> &ufr + new Transition (8855, 8856), // &Uf -> &Ufr + new Transition (8860, 8861), // &Ug -> &Ugr + new Transition (8866, 8867), // &ug -> &ugr + new Transition (8873, 8874), // &uHa -> &uHar + new Transition (8877, 8878), // &uha -> &uhar + new Transition (8878, 8881), // &uhar -> &uharr + new Transition (8888, 8896), // &ulc -> &ulcr + new Transition (8889, 8890), // &ulco -> &ulcor + new Transition (8893, 8894), // &ulcorne -> &ulcorner + new Transition (8900, 8901), // &ult -> &ultr + new Transition (8906, 8907), // &Umac -> &Umacr + new Transition (8911, 8912), // &umac -> &umacr + new Transition (8918, 8919), // &Unde -> &Under + new Transition (8920, 8924), // &UnderB -> &UnderBr + new Transition (8921, 8922), // &UnderBa -> &UnderBar + new Transition (8934, 8935), // &UnderPa -> &UnderPar + new Transition (8971, 8972), // &UpA -> &UpAr + new Transition (8972, 8973), // &UpAr -> &UpArr + new Transition (8977, 8978), // &Upa -> &Upar + new Transition (8978, 8979), // &Upar -> &Uparr + new Transition (8984, 8985), // &upa -> &upar + new Transition (8985, 8986), // &upar -> &uparr + new Transition (8991, 8992), // &UpArrowBa -> &UpArrowBar + new Transition (8998, 8999), // &UpArrowDownA -> &UpArrowDownAr + new Transition (8999, 9000), // &UpArrowDownAr -> &UpArrowDownArr + new Transition (9008, 9009), // &UpDownA -> &UpDownAr + new Transition (9009, 9010), // &UpDownAr -> &UpDownArr + new Transition (9018, 9019), // &Updowna -> &Updownar + new Transition (9019, 9020), // &Updownar -> &Updownarr + new Transition (9028, 9029), // &updowna -> &updownar + new Transition (9029, 9030), // &updownar -> &updownarr + new Transition (9040, 9041), // &UpEquilib -> &UpEquilibr + new Transition (9047, 9048), // &upha -> &uphar + new Transition (9052, 9058), // &upharpoon -> &upharpoonr + new Transition (9069, 9070), // &Uppe -> &Upper + new Transition (9075, 9076), // &UpperLeftA -> &UpperLeftAr + new Transition (9076, 9077), // &UpperLeftAr -> &UpperLeftArr + new Transition (9086, 9087), // &UpperRightA -> &UpperRightAr + new Transition (9087, 9088), // &UpperRightAr -> &UpperRightArr + new Transition (9112, 9113), // &UpTeeA -> &UpTeeAr + new Transition (9113, 9114), // &UpTeeAr -> &UpTeeArr + new Transition (9120, 9121), // &upupa -> &upupar + new Transition (9121, 9122), // &upupar -> &upuparr + new Transition (9128, 9136), // &urc -> &urcr + new Transition (9129, 9130), // &urco -> &urcor + new Transition (9133, 9134), // &urcorne -> &urcorner + new Transition (9149, 9150), // &urt -> &urtr + new Transition (9154, 9155), // &Usc -> &Uscr + new Transition (9158, 9159), // &usc -> &uscr + new Transition (9161, 9177), // &ut -> &utr + new Transition (9183, 9184), // &uua -> &uuar + new Transition (9184, 9185), // &uuar -> &uuarr + new Transition (9201, 9445), // &v -> &vr + new Transition (9202, 9208), // &va -> &var + new Transition (9204, 9205), // &vang -> &vangr + new Transition (9208, 9247), // &var -> &varr + new Transition (9231, 9237), // &varp -> &varpr + new Transition (9243, 9244), // &vA -> &vAr + new Transition (9244, 9245), // &vAr -> &vArr + new Transition (9279, 9285), // &vart -> &vartr + new Transition (9291, 9297), // &vartriangle -> &vartriangler + new Transition (9305, 9306), // &Vba -> &Vbar + new Transition (9309, 9310), // &vBa -> &vBar + new Transition (9342, 9360), // &Ve -> &Ver + new Transition (9345, 9365), // &ve -> &ver + new Transition (9349, 9350), // &veeba -> &veebar + new Transition (9362, 9363), // &Verba -> &Verbar + new Transition (9367, 9368), // &verba -> &verbar + new Transition (9379, 9380), // &VerticalBa -> &VerticalBar + new Transition (9390, 9391), // &VerticalSepa -> &VerticalSepar + new Transition (9394, 9395), // &VerticalSeparato -> &VerticalSeparator + new Transition (9414, 9415), // &Vf -> &Vfr + new Transition (9417, 9418), // &vf -> &vfr + new Transition (9421, 9422), // &vlt -> &vltr + new Transition (9440, 9441), // &vp -> &vpr + new Transition (9446, 9447), // &vrt -> &vrtr + new Transition (9451, 9452), // &Vsc -> &Vscr + new Transition (9455, 9456), // &vsc -> &vscr + new Transition (9486, 9487), // &Wci -> &Wcir + new Transition (9490, 9533), // &w -> &wr + new Transition (9492, 9493), // &wci -> &wcir + new Transition (9499, 9500), // &wedba -> &wedbar + new Transition (9513, 9514), // &weie -> &weier + new Transition (9517, 9518), // &Wf -> &Wfr + new Transition (9520, 9521), // &wf -> &wfr + new Transition (9541, 9542), // &Wsc -> &Wscr + new Transition (9545, 9546), // &wsc -> &wscr + new Transition (9548, 9623), // &x -> &xr + new Transition (9553, 9554), // &xci -> &xcir + new Transition (9561, 9562), // &xdt -> &xdtr + new Transition (9566, 9567), // &Xf -> &Xfr + new Transition (9569, 9570), // &xf -> &xfr + new Transition (9573, 9574), // &xhA -> &xhAr + new Transition (9574, 9575), // &xhAr -> &xhArr + new Transition (9577, 9578), // &xha -> &xhar + new Transition (9578, 9579), // &xhar -> &xharr + new Transition (9586, 9587), // &xlA -> &xlAr + new Transition (9587, 9588), // &xlAr -> &xlArr + new Transition (9590, 9591), // &xla -> &xlar + new Transition (9591, 9592), // &xlar -> &xlarr + new Transition (9624, 9625), // &xrA -> &xrAr + new Transition (9625, 9626), // &xrAr -> &xrArr + new Transition (9628, 9629), // &xra -> &xrar + new Transition (9629, 9630), // &xrar -> &xrarr + new Transition (9633, 9634), // &Xsc -> &Xscr + new Transition (9637, 9638), // &xsc -> &xscr + new Transition (9651, 9652), // &xut -> &xutr + new Transition (9686, 9687), // &Yci -> &Ycir + new Transition (9691, 9692), // &yci -> &ycir + new Transition (9702, 9703), // &Yf -> &Yfr + new Transition (9705, 9706), // &yf -> &yfr + new Transition (9725, 9726), // &Ysc -> &Yscr + new Transition (9729, 9730), // &ysc -> &yscr + new Transition (9762, 9763), // &Zca -> &Zcar + new Transition (9768, 9769), // &zca -> &zcar + new Transition (9787, 9788), // &zeet -> &zeetr + new Transition (9791, 9792), // &Ze -> &Zer + new Transition (9811, 9812), // &Zf -> &Zfr + new Transition (9814, 9815), // &zf -> &zfr + new Transition (9826, 9827), // &zig -> &zigr + new Transition (9828, 9829), // &zigra -> &zigrar + new Transition (9829, 9830), // &zigrar -> &zigrarr + new Transition (9841, 9842), // &Zsc -> &Zscr + new Transition (9845, 9846) // &zsc -> &zscr + }; + TransitionTable_s = new Transition[368] { + new Transition (0, 7617), // & -> &s + new Transition (1, 247), // &A -> &As + new Transition (8, 251), // &a -> &as + new Transition (81, 82), // &alef -> &alefs + new Transition (120, 128), // &and -> &ands + new Transition (136, 172), // &ang -> &angs + new Transition (143, 144), // &angm -> &angms + new Transition (213, 214), // &apo -> &apos + new Transition (247, 255), // &As -> &Ass + new Transition (301, 744), // &b -> &bs + new Transition (304, 324), // &back -> &backs + new Transition (311, 312), // &backep -> &backeps + new Transition (331, 740), // &B -> &Bs + new Transition (334, 335), // &Back -> &Backs + new Transition (337, 338), // &Backsla -> &Backslas + new Transition (387, 388), // &becau -> &becaus + new Transition (393, 394), // &Becau -> &Becaus + new Transition (405, 406), // &bep -> &beps + new Transition (420, 421), // &Bernoulli -> &Bernoullis + new Transition (443, 471), // &big -> &bigs + new Transition (462, 463), // &bigoplu -> &bigoplus + new Transition (468, 469), // &bigotime -> &bigotimes + new Transition (500, 501), // &biguplu -> &biguplus + new Transition (522, 531), // &black -> &blacks + new Transition (659, 660), // &boxminu -> &boxminus + new Transition (664, 665), // &boxplu -> &boxplus + new Transition (670, 671), // &boxtime -> &boxtimes + new Transition (762, 763), // &bsolh -> &bsolhs + new Transition (789, 1270), // &C -> &Cs + new Transition (796, 1274), // &c -> &cs + new Transition (805, 846), // &cap -> &caps + new Transition (858, 859), // &Cayley -> &Cayleys + new Transition (863, 864), // &ccap -> &ccaps + new Transition (901, 902), // &ccup -> &ccups + new Transition (902, 904), // &ccups -> &ccupss + new Transition (979, 1063), // &cir -> &cirs + new Transition (1005, 1006), // &circleda -> &circledas + new Transition (1015, 1016), // &circledda -> &circleddas + new Transition (1035, 1036), // &CircleMinu -> &CircleMinus + new Transition (1040, 1041), // &CirclePlu -> &CirclePlus + new Transition (1046, 1047), // &CircleTime -> &CircleTimes + new Transition (1069, 1092), // &Clo -> &Clos + new Transition (1073, 1074), // &Clockwi -> &Clockwis + new Transition (1119, 1120), // &club -> &clubs + new Transition (1161, 1162), // &complexe -> &complexes + new Transition (1221, 1223), // © -> ©s + new Transition (1237, 1238), // &CounterClockwi -> &CounterClockwis + new Transition (1262, 1263), // &Cro -> &Cros + new Transition (1263, 1264), // &Cros -> &Cross + new Transition (1266, 1267), // &cro -> &cros + new Transition (1267, 1268), // &cros -> &cross + new Transition (1301, 1305), // &cue -> &cues + new Transition (1318, 1344), // &cup -> &cups + new Transition (1356, 1362), // &curlyeq -> &curlyeqs + new Transition (1425, 2040), // &D -> &Ds + new Transition (1426, 1457), // &Da -> &Das + new Transition (1432, 2044), // &d -> &ds + new Transition (1433, 1454), // &da -> &das + new Transition (1511, 1512), // &ddot -> &ddots + new Transition (1536, 1537), // &dfi -> &dfis + new Transition (1599, 1639), // &di -> &dis + new Transition (1601, 1617), // &diam -> &diams + new Transition (1610, 1612), // &diamond -> &diamonds + new Transition (1654, 1655), // ÷ontime -> ÷ontimes + new Transition (1694, 1724), // &dot -> &dots + new Transition (1716, 1717), // &dotminu -> &dotminus + new Transition (1721, 1722), // &dotplu -> &dotplus + new Transition (1929, 1930), // &downdownarrow -> &downdownarrows + new Transition (2108, 2418), // &E -> &Es + new Transition (2115, 2422), // &e -> &es + new Transition (2116, 2122), // &ea -> &eas + new Transition (2185, 2198), // &eg -> &egs + new Transition (2204, 2222), // &el -> &els + new Transition (2217, 2218), // &elinter -> &elinters + new Transition (2233, 2279), // &em -> &ems + new Transition (2240, 2242), // &empty -> &emptys + new Transition (2290, 2293), // &en -> &ens + new Transition (2312, 2323), // &ep -> &eps + new Transition (2314, 2316), // &epar -> &epars + new Transition (2320, 2321), // &eplu -> &eplus + new Transition (2326, 2327), // &Ep -> &Eps + new Transition (2339, 2350), // &eq -> &eqs + new Transition (2363, 2364), // &eqslantle -> &eqslantles + new Transition (2364, 2365), // &eqslantles -> &eqslantless + new Transition (2374, 2375), // &equal -> &equals + new Transition (2383, 2384), // &eque -> &eques + new Transition (2405, 2406), // &eqvpar -> &eqvpars + new Transition (2462, 2463), // &exi -> &exis + new Transition (2467, 2468), // &Exi -> &Exis + new Transition (2469, 2470), // &Exist -> &Exists + new Transition (2503, 2697), // &f -> &fs + new Transition (2512, 2513), // &fallingdot -> &fallingdots + new Transition (2517, 2693), // &F -> &Fs + new Transition (2601, 2602), // &fltn -> &fltns + new Transition (2648, 2686), // &fra -> &fras + new Transition (2701, 2927), // &g -> &gs + new Transition (2708, 2923), // &G -> &Gs + new Transition (2765, 2781), // &ge -> &ges + new Transition (2771, 2775), // &geq -> &geqs + new Transition (2796, 2797), // &gesle -> &gesles + new Transition (2832, 2849), // &gn -> &gns + new Transition (2879, 2880), // &GreaterEqualLe -> &GreaterEqualLes + new Transition (2880, 2881), // &GreaterEqualLes -> &GreaterEqualLess + new Transition (2902, 2903), // &GreaterLe -> &GreaterLes + new Transition (2903, 2904), // &GreaterLes -> &GreaterLess + new Transition (2961, 2962), // >que -> >ques + new Transition (2965, 2998), // >r -> >rs + new Transition (2983, 2984), // >reqle -> >reqles + new Transition (2984, 2985), // >reqles -> >reqless + new Transition (2989, 2990), // >reqqle -> >reqqles + new Transition (2990, 2991), // >reqqles -> >reqqless + new Transition (2994, 2995), // >rle -> >rles + new Transition (2995, 2996), // >rles -> >rless + new Transition (3014, 3184), // &H -> &Hs + new Transition (3020, 3188), // &h -> &hs + new Transition (3023, 3024), // &hair -> &hairs + new Transition (3077, 3078), // &heart -> &hearts + new Transition (3112, 3113), // &hk -> &hks + new Transition (3193, 3194), // &hsla -> &hslas + new Transition (3236, 3503), // &I -> &Is + new Transition (3243, 3507), // &i -> &is + new Transition (3375, 3376), // &Implie -> &Implies + new Transition (3410, 3411), // &integer -> &integers + new Transition (3424, 3425), // &Inter -> &Inters + new Transition (3445, 3446), // &Invi -> &Invis + new Transition (3460, 3461), // &InvisibleTime -> &InvisibleTimes + new Transition (3499, 3500), // &ique -> &iques + new Transition (3512, 3520), // &isin -> &isins + new Transition (3555, 3590), // &J -> &Js + new Transition (3561, 3594), // &j -> &js + new Transition (3618, 3684), // &K -> &Ks + new Transition (3624, 3688), // &k -> &ks + new Transition (3692, 4652), // &l -> &ls + new Transition (3698, 4658), // &L -> &Ls + new Transition (3766, 3785), // &larr -> &larrs + new Transition (3770, 3771), // &larrbf -> &larrbfs + new Transition (3773, 3774), // &larrf -> &larrfs + new Transition (3803, 3805), // &late -> &lates + new Transition (3828, 3831), // &lbrk -> &lbrks + new Transition (3869, 3891), // &ld -> &lds + new Transition (3885, 3886), // &ldru -> &ldrus + new Transition (3896, 4197), // &le -> &les + new Transition (3898, 4238), // &Le -> &Les + new Transition (4027, 4028), // &leftleftarrow -> &leftleftarrows + new Transition (4056, 4074), // &leftright -> &leftrights + new Transition (4061, 4063), // &leftrightarrow -> &leftrightarrows + new Transition (4071, 4072), // &leftrightharpoon -> &leftrightharpoons + new Transition (4117, 4118), // &leftthreetime -> &leftthreetimes + new Transition (4187, 4191), // &leq -> &leqs + new Transition (4197, 4215), // &les -> &less + new Transition (4212, 4213), // &lesge -> &lesges + new Transition (4215, 4280), // &less -> &lesss + new Transition (4238, 4239), // &Les -> &Less + new Transition (4276, 4277), // &LessLe -> &LessLes + new Transition (4277, 4278), // &LessLes -> &LessLess + new Transition (4302, 4303), // &lfi -> &lfis + new Transition (4392, 4393), // &lmou -> &lmous + new Transition (4401, 4418), // &ln -> &lns + new Transition (4504, 4505), // &longmap -> &longmaps + new Transition (4570, 4571), // &loplu -> &loplus + new Transition (4576, 4577), // &lotime -> &lotimes + new Transition (4580, 4581), // &lowa -> &lowas + new Transition (4717, 4718), // <ime -> <imes + new Transition (4727, 4728), // <que -> <ques + new Transition (4744, 4745), // &lurd -> &lurds + new Transition (4767, 4941), // &m -> &ms + new Transition (4777, 4778), // &malte -> &maltes + new Transition (4781, 4937), // &M -> &Ms + new Transition (4785, 4787), // &map -> &maps + new Transition (4821, 4822), // &mda -> &mdas + new Transition (4831, 4832), // &mea -> &meas + new Transition (4878, 4879), // &mida -> &midas + new Transition (4891, 4892), // &minu -> &minus + new Transition (4902, 4903), // &Minu -> &Minus + new Transition (4906, 4907), // &MinusPlu -> &MinusPlus + new Transition (4919, 4920), // &mnplu -> &mnplus + new Transition (4925, 4926), // &model -> &models + new Transition (4947, 4948), // &mstpo -> &mstpos + new Transition (4965, 5895), // &n -> &ns + new Transition (4971, 5904), // &N -> &Ns + new Transition (4993, 4994), // &napo -> &napos + new Transition (5006, 5008), // &natural -> &naturals + new Transition (5010, 5011), // &nb -> &nbs + new Transition (5060, 5061), // &nda -> &ndas + new Transition (5064, 5140), // &ne -> &nes + new Transition (5084, 5148), // &Ne -> &Nes + new Transition (5168, 5169), // &NestedLe -> &NestedLes + new Transition (5169, 5170), // &NestedLes -> &NestedLess + new Transition (5172, 5173), // &NestedLessLe -> &NestedLessLes + new Transition (5173, 5174), // &NestedLessLes -> &NestedLessLess + new Transition (5183, 5184), // &nexi -> &nexis + new Transition (5185, 5187), // &nexist -> &nexists + new Transition (5195, 5215), // &ng -> &ngs + new Transition (5198, 5210), // &nge -> &nges + new Transition (5200, 5204), // &ngeq -> &ngeqs + new Transition (5240, 5242), // &ni -> &nis + new Transition (5256, 5328), // &nl -> &nls + new Transition (5270, 5322), // &nle -> &nles + new Transition (5312, 5316), // &nleq -> &nleqs + new Transition (5322, 5324), // &nles -> &nless + new Transition (5434, 5435), // &NotExi -> &NotExis + new Transition (5436, 5437), // &NotExist -> &NotExists + new Transition (5472, 5473), // &NotGreaterLe -> &NotGreaterLes + new Transition (5473, 5474), // &NotGreaterLes -> &NotGreaterLess + new Transition (5529, 5551), // &NotLe -> &NotLes + new Transition (5551, 5552), // &NotLes -> &NotLess + new Transition (5569, 5570), // &NotLessLe -> &NotLessLes + new Transition (5570, 5571), // &NotLessLes -> &NotLessLess + new Transition (5591, 5592), // &NotNe -> &NotNes + new Transition (5612, 5613), // &NotNestedLe -> &NotNestedLes + new Transition (5613, 5614), // &NotNestedLes -> &NotNestedLess + new Transition (5616, 5617), // &NotNestedLessLe -> &NotNestedLessLes + new Transition (5617, 5618), // &NotNestedLessLes -> &NotNestedLessLess + new Transition (5636, 5637), // &NotPrecede -> &NotPrecedes + new Transition (5660, 5661), // &NotRever -> &NotRevers + new Transition (5702, 5703), // &NotSquareSub -> &NotSquareSubs + new Transition (5715, 5716), // &NotSquareSuper -> &NotSquareSupers + new Transition (5727, 5728), // &NotSub -> &NotSubs + new Transition (5742, 5743), // &NotSucceed -> &NotSucceeds + new Transition (5770, 5771), // &NotSuper -> &NotSupers + new Transition (5823, 5831), // &npar -> &npars + new Transition (5942, 5943), // &nsq -> &nsqs + new Transition (5952, 5958), // &nsub -> &nsubs + new Transition (5973, 5979), // &nsup -> &nsups + new Transition (6034, 6040), // &num -> &nums + new Transition (6043, 6107), // &nv -> &nvs + new Transition (6049, 6050), // &nVDa -> &nVDas + new Transition (6054, 6055), // &nVda -> &nVdas + new Transition (6059, 6060), // &nvDa -> &nvDas + new Transition (6064, 6065), // &nvda -> &nvdas + new Transition (6131, 6378), // &O -> &Os + new Transition (6138, 6382), // &o -> &os + new Transition (6139, 6145), // &oa -> &oas + new Transition (6163, 6185), // &od -> &ods + new Transition (6164, 6165), // &oda -> &odas + new Transition (6248, 6249), // &olcro -> &olcros + new Transition (6249, 6250), // &olcros -> &olcross + new Transition (6291, 6292), // &ominu -> &ominus + new Transition (6337, 6338), // &oplu -> &oplus + new Transition (6342, 6368), // &or -> &ors + new Transition (6387, 6388), // &Osla -> &Oslas + new Transition (6392, 6393), // &osla -> &oslas + new Transition (6412, 6413), // &Otime -> &Otimes + new Transition (6416, 6417), // &otime -> &otimes + new Transition (6419, 6420), // &otimesa -> &otimesas + new Transition (6458, 6459), // &OverParenthe -> &OverParenthes + new Transition (6460, 6461), // &OverParenthesi -> &OverParenthesis + new Transition (6463, 6799), // &p -> &ps + new Transition (6465, 6474), // &par -> &pars + new Transition (6482, 6795), // &P -> &Ps + new Transition (6566, 6567), // &plu -> &plus + new Transition (6567, 6599), // &plus -> &pluss + new Transition (6588, 6589), // &Plu -> &Plus + new Transition (6593, 6594), // &PlusMinu -> &PlusMinus + new Transition (6642, 6786), // &pr -> &prs + new Transition (6655, 6721), // &prec -> &precs + new Transition (6676, 6677), // &Precede -> &Precedes + new Transition (6705, 6717), // &precn -> &precns + new Transition (6731, 6733), // &prime -> &primes + new Transition (6735, 6741), // &prn -> &prns + new Transition (6754, 6765), // &prof -> &profs + new Transition (6809, 6810), // &punc -> &puncs + new Transition (6813, 6839), // &Q -> &Qs + new Transition (6817, 6843), // &q -> &qs + new Transition (6855, 6856), // &quaternion -> &quaternions + new Transition (6862, 6863), // &que -> &ques + new Transition (6876, 7542), // &r -> &rs + new Transition (6886, 7548), // &R -> &Rs + new Transition (6932, 6956), // &rarr -> &rarrs + new Transition (6939, 6940), // &rarrbf -> &rarrbfs + new Transition (6944, 6945), // &rarrf -> &rarrfs + new Transition (6983, 6984), // &rational -> &rationals + new Transition (7012, 7015), // &rbrk -> &rbrks + new Transition (7053, 7069), // &rd -> &rds + new Transition (7076, 7087), // &real -> &reals + new Transition (7099, 7100), // &Rever -> &Revers + new Transition (7136, 7137), // &rfi -> &rfis + new Transition (7199, 7431), // &ri -> &ris + new Transition (7202, 7326), // &right -> &rights + new Transition (7302, 7303), // &rightleftarrow -> &rightleftarrows + new Transition (7311, 7312), // &rightleftharpoon -> &rightleftharpoons + new Transition (7323, 7324), // &rightrightarrow -> &rightrightarrows + new Transition (7362, 7363), // &rightthreetime -> &rightthreetimes + new Transition (7437, 7438), // &risingdot -> &risingdots + new Transition (7455, 7456), // &rmou -> &rmous + new Transition (7492, 7493), // &roplu -> &roplus + new Transition (7498, 7499), // &rotime -> &rotimes + new Transition (7509, 7510), // &RoundImplie -> &RoundImplies + new Transition (7575, 7576), // &rtime -> &rtimes + new Transition (7610, 8073), // &S -> &Ss + new Transition (7617, 8077), // &s -> &ss + new Transition (7631, 7687), // &sc -> &scs + new Transition (7670, 7676), // &scn -> &scns + new Transition (7703, 7724), // &se -> &ses + new Transition (7733, 7734), // &setminu -> &setminus + new Transition (7870, 7871), // &simplu -> &simplus + new Transition (7895, 7907), // &sma -> &smas + new Transition (7897, 7898), // &small -> &smalls + new Transition (7904, 7905), // &smallsetminu -> &smallsetminus + new Transition (7914, 7915), // &smepar -> &smepars + new Transition (7926, 7928), // &smte -> &smtes + new Transition (7959, 7960), // &spade -> &spades + new Transition (7968, 7984), // &sq -> &sqs + new Transition (7971, 7973), // &sqcap -> &sqcaps + new Transition (7976, 7978), // &sqcup -> &sqcups + new Transition (7986, 7990), // &sqsub -> &sqsubs + new Transition (7997, 8001), // &sqsup -> &sqsups + new Transition (8023, 8024), // &SquareInter -> &SquareInters + new Transition (8034, 8035), // &SquareSub -> &SquareSubs + new Transition (8047, 8048), // &SquareSuper -> &SquareSupers + new Transition (8113, 8114), // &straightep -> &straighteps + new Transition (8124, 8125), // &strn -> &strns + new Transition (8128, 8165), // &Sub -> &Subs + new Transition (8131, 8169), // &sub -> &subs + new Transition (8157, 8158), // &subplu -> &subplus + new Transition (8199, 8265), // &succ -> &succs + new Transition (8220, 8221), // &Succeed -> &Succeeds + new Transition (8249, 8261), // &succn -> &succns + new Transition (8282, 8348), // &Sup -> &Sups + new Transition (8284, 8352), // &sup -> &sups + new Transition (8292, 8296), // &supd -> &supds + new Transition (8309, 8310), // &Super -> &Supers + new Transition (8320, 8321), // &suph -> &suphs + new Transition (8345, 8346), // &supplu -> &supplus + new Transition (8400, 8705), // &T -> &Ts + new Transition (8404, 8709), // &t -> &ts + new Transition (8485, 8487), // &theta -> &thetas + new Transition (8495, 8503), // &thick -> &thicks + new Transition (8516, 8517), // &thin -> &thins + new Transition (8527, 8531), // &thk -> &thks + new Transition (8577, 8578), // &time -> × + new Transition (8590, 8614), // &to -> &tos + new Transition (8633, 8690), // &tri -> &tris + new Transition (8673, 8674), // &triminu -> &triminus + new Transition (8687, 8688), // &triplu -> &triplus + new Transition (8768, 9153), // &U -> &Us + new Transition (8775, 9157), // &u -> &us + new Transition (8850, 8851), // &ufi -> &ufis + new Transition (8940, 8941), // &UnderParenthe -> &UnderParenthes + new Transition (8942, 8943), // &UnderParenthesi -> &UnderParenthesis + new Transition (8951, 8952), // &UnionPlu -> &UnionPlus + new Transition (8970, 9092), // &Up -> &Ups + new Transition (8983, 9095), // &up -> &ups + new Transition (9065, 9066), // &uplu -> &uplus + new Transition (9124, 9125), // &upuparrow -> &upuparrows + new Transition (9201, 9454), // &v -> &vs + new Transition (9208, 9252), // &var -> &vars + new Transition (9210, 9211), // &varep -> &vareps + new Transition (9259, 9260), // &varsub -> &varsubs + new Transition (9269, 9270), // &varsup -> &varsups + new Transition (9303, 9450), // &V -> &Vs + new Transition (9321, 9322), // &VDa -> &VDas + new Transition (9326, 9327), // &Vda -> &Vdas + new Transition (9331, 9332), // &vDa -> &vDas + new Transition (9336, 9337), // &vda -> &vdas + new Transition (9425, 9426), // &vn -> &vns + new Transition (9473, 9474), // &Vvda -> &Vvdas + new Transition (9484, 9540), // &W -> &Ws + new Transition (9490, 9544), // &w -> &ws + new Transition (9548, 9636), // &x -> &xs + new Transition (9565, 9632), // &X -> &Xs + new Transition (9599, 9600), // &xni -> &xnis + new Transition (9615, 9616), // &xoplu -> &xoplus + new Transition (9648, 9649), // &xuplu -> &xuplus + new Transition (9665, 9724), // &Y -> &Ys + new Transition (9672, 9728), // &y -> &ys + new Transition (9747, 9840), // &Z -> &Zs + new Transition (9754, 9844) // &z -> &zs + }; + TransitionTable_t = new Transition[499] { + new Transition (0, 8404), // & -> &t + new Transition (1, 269), // &A -> &At + new Transition (4, 5), // &Aacu -> &Aacut + new Transition (8, 275), // &a -> &at + new Transition (11, 12), // &aacu -> &aacut + new Transition (42, 43), // &acu -> &acut + new Transition (164, 165), // &angr -> &angrt + new Transition (172, 176), // &angs -> &angst + new Transition (223, 224), // &ApplyFunc -> &ApplyFunct + new Transition (251, 260), // &as -> &ast + new Transition (294, 295), // &awconin -> &awconint + new Transition (298, 299), // &awin -> &awint + new Transition (362, 364), // &bbrk -> &bbrkt + new Transition (384, 426), // &be -> &bet + new Transition (390, 423), // &Be -> &Bet + new Transition (400, 401), // &bemp -> &bempt + new Transition (443, 481), // &big -> &bigt + new Transition (455, 465), // &bigo -> &bigot + new Transition (457, 458), // &bigodo -> &bigodot + new Transition (471, 477), // &bigs -> &bigst + new Transition (522, 538), // &black -> &blackt + new Transition (554, 555), // &blacktrianglelef -> &blacktriangleleft + new Transition (560, 561), // &blacktrianglerigh -> &blacktriangleright + new Transition (588, 589), // &bNo -> &bNot + new Transition (591, 592), // &bno -> &bnot + new Transition (598, 602), // &bo -> &bot + new Transition (602, 604), // &bot -> &bott + new Transition (608, 609), // &bow -> &bowt + new Transition (613, 667), // &box -> &boxt + new Transition (771, 772), // &bulle -> &bullet + new Transition (792, 793), // &Cacu -> &Cacut + new Transition (796, 1287), // &c -> &ct + new Transition (799, 800), // &cacu -> &cacut + new Transition (825, 826), // &capdo -> &capdot + new Transition (828, 829), // &Capi -> &Capit + new Transition (839, 840), // &CapitalDifferen -> &CapitalDifferent + new Transition (849, 850), // &care -> &caret + new Transition (897, 898), // &Cconin -> &Cconint + new Transition (908, 909), // &Cdo -> &Cdot + new Transition (912, 913), // &cdo -> &cdot + new Transition (928, 929), // &cemp -> &cempt + new Transition (933, 934), // &cen -> ¢ + new Transition (936, 937), // &Cen -> &Cent + new Transition (941, 942), // &CenterDo -> &CenterDot + new Transition (947, 948), // ¢erdo -> ¢erdot + new Transition (995, 996), // &circlearrowlef -> &circlearrowleft + new Transition (1001, 1002), // &circlearrowrigh -> &circlearrowright + new Transition (1006, 1007), // &circledas -> &circledast + new Transition (1025, 1026), // &CircleDo -> &CircleDot + new Transition (1056, 1057), // &cirfnin -> &cirfnint + new Transition (1078, 1079), // &ClockwiseCon -> &ClockwiseCont + new Transition (1084, 1085), // &ClockwiseContourIn -> &ClockwiseContourInt + new Transition (1107, 1108), // &CloseCurlyDoubleQuo -> &CloseCurlyDoubleQuot + new Transition (1113, 1114), // &CloseCurlyQuo -> &CloseCurlyQuot + new Transition (1123, 1124), // &clubsui -> &clubsuit + new Transition (1144, 1146), // &comma -> &commat + new Transition (1157, 1158), // &complemen -> &complement + new Transition (1168, 1169), // &congdo -> &congdot + new Transition (1171, 1187), // &Con -> &Cont + new Transition (1176, 1177), // &Congruen -> &Congruent + new Transition (1180, 1181), // &Conin -> &Conint + new Transition (1184, 1185), // &conin -> &conint + new Transition (1192, 1193), // &ContourIn -> &ContourInt + new Transition (1214, 1215), // &Coproduc -> &Coproduct + new Transition (1227, 1228), // &Coun -> &Count + new Transition (1242, 1243), // &CounterClockwiseCon -> &CounterClockwiseCont + new Transition (1248, 1249), // &CounterClockwiseContourIn -> &CounterClockwiseContourInt + new Transition (1289, 1290), // &ctdo -> &ctdot + new Transition (1338, 1339), // &cupdo -> &cupdot + new Transition (1390, 1391), // &curvearrowlef -> &curvearrowleft + new Transition (1396, 1397), // &curvearrowrigh -> &curvearrowright + new Transition (1412, 1413), // &cwconin -> &cwconint + new Transition (1416, 1417), // &cwin -> &cwint + new Transition (1421, 1422), // &cylc -> &cylct + new Transition (1432, 2067), // &d -> &dt + new Transition (1440, 1441), // &dale -> &dalet + new Transition (1503, 1504), // &DDo -> &DDot + new Transition (1510, 1511), // &ddo -> &ddot + new Transition (1520, 1522), // &Del -> &Delt + new Transition (1525, 1526), // &del -> &delt + new Transition (1530, 1531), // &demp -> &dempt + new Transition (1538, 1539), // &dfish -> &dfisht + new Transition (1561, 1562), // &Diacri -> &Diacrit + new Transition (1569, 1570), // &DiacriticalAcu -> &DiacriticalAcut + new Transition (1574, 1575), // &DiacriticalDo -> &DiacriticalDot + new Transition (1583, 1584), // &DiacriticalDoubleAcu -> &DiacriticalDoubleAcut + new Transition (1614, 1615), // &diamondsui -> &diamondsuit + new Transition (1626, 1627), // &Differen -> &Different + new Transition (1650, 1651), // ÷on -> ÷ont + new Transition (1679, 1694), // &do -> &dot + new Transition (1685, 1692), // &Do -> &Dot + new Transition (1697, 1698), // &DotDo -> &DotDot + new Transition (1704, 1705), // &doteqdo -> &doteqdot + new Transition (1750, 1751), // &DoubleCon -> &DoubleCont + new Transition (1756, 1757), // &DoubleContourIn -> &DoubleContourInt + new Transition (1765, 1766), // &DoubleDo -> &DoubleDot + new Transition (1778, 1779), // &DoubleLef -> &DoubleLeft + new Transition (1789, 1790), // &DoubleLeftRigh -> &DoubleLeftRight + new Transition (1806, 1807), // &DoubleLongLef -> &DoubleLongLeft + new Transition (1817, 1818), // &DoubleLongLeftRigh -> &DoubleLongLeftRight + new Transition (1828, 1829), // &DoubleLongRigh -> &DoubleLongRight + new Transition (1839, 1840), // &DoubleRigh -> &DoubleRight + new Transition (1871, 1872), // &DoubleVer -> &DoubleVert + new Transition (1941, 1942), // &downharpoonlef -> &downharpoonleft + new Transition (1947, 1948), // &downharpoonrigh -> &downharpoonright + new Transition (1952, 1953), // &DownLef -> &DownLeft + new Transition (1957, 1958), // &DownLeftRigh -> &DownLeftRight + new Transition (1961, 1962), // &DownLeftRightVec -> &DownLeftRightVect + new Transition (1971, 1972), // &DownLeftTeeVec -> &DownLeftTeeVect + new Transition (1978, 1979), // &DownLeftVec -> &DownLeftVect + new Transition (1990, 1991), // &DownRigh -> &DownRight + new Transition (1997, 1998), // &DownRightTeeVec -> &DownRightTeeVect + new Transition (2004, 2005), // &DownRightVec -> &DownRightVect + new Transition (2040, 2057), // &Ds -> &Dst + new Transition (2044, 2062), // &ds -> &dst + new Transition (2069, 2070), // &dtdo -> &dtdot + new Transition (2108, 2436), // &E -> &Et + new Transition (2111, 2112), // &Eacu -> &Eacut + new Transition (2115, 2439), // &e -> &et + new Transition (2118, 2119), // &eacu -> &eacut + new Transition (2122, 2123), // &eas -> &east + new Transition (2159, 2160), // &eDDo -> &eDDot + new Transition (2163, 2164), // &Edo -> &Edot + new Transition (2166, 2167), // &eDo -> &eDot + new Transition (2170, 2171), // &edo -> &edot + new Transition (2177, 2178), // &efDo -> &efDot + new Transition (2201, 2202), // &egsdo -> &egsdot + new Transition (2210, 2211), // &Elemen -> &Element + new Transition (2214, 2215), // &elin -> &elint + new Transition (2225, 2226), // &elsdo -> &elsdot + new Transition (2238, 2239), // &emp -> &empt + new Transition (2243, 2244), // &emptyse -> &emptyset + new Transition (2246, 2247), // &Emp -> &Empt + new Transition (2356, 2357), // &eqslan -> &eqslant + new Transition (2358, 2359), // &eqslantg -> &eqslantgt + new Transition (2384, 2385), // &eques -> &equest + new Transition (2415, 2416), // &erDo -> &erDot + new Transition (2427, 2428), // &esdo -> &esdot + new Transition (2463, 2464), // &exis -> &exist + new Transition (2468, 2469), // &Exis -> &Exist + new Transition (2474, 2475), // &expec -> &expect + new Transition (2476, 2477), // &expecta -> &expectat + new Transition (2486, 2487), // &Exponen -> &Exponent + new Transition (2496, 2497), // &exponen -> &exponent + new Transition (2511, 2512), // &fallingdo -> &fallingdot + new Transition (2592, 2600), // &fl -> &flt + new Transition (2593, 2594), // &fla -> &flat + new Transition (2634, 2635), // &Fourier -> &Fouriert + new Transition (2641, 2642), // &fpar -> &fpart + new Transition (2644, 2645), // &fpartin -> &fpartint + new Transition (2701, 2942), // &g -> > + new Transition (2704, 2705), // &gacu -> &gacut + new Transition (2708, 2940), // &G -> &Gt + new Transition (2756, 2757), // &Gdo -> &Gdot + new Transition (2760, 2761), // &gdo -> &gdot + new Transition (2778, 2779), // &geqslan -> &geqslant + new Transition (2787, 2788), // &gesdo -> &gesdot + new Transition (2868, 2869), // &Grea -> &Great + new Transition (2896, 2897), // &GreaterGrea -> &GreaterGreat + new Transition (2909, 2910), // &GreaterSlan -> &GreaterSlant + new Transition (2951, 2952), // >do -> >dot + new Transition (2962, 2963), // >ques -> >quest + new Transition (2977, 2978), // >rdo -> >rdot + new Transition (3004, 3005), // &gver -> &gvert + new Transition (3015, 3058), // &Ha -> &Hat + new Transition (3032, 3033), // &hamil -> &hamilt + new Transition (3076, 3077), // &hear -> &heart + new Transition (3081, 3082), // &heartsui -> &heartsuit + new Transition (3104, 3105), // &Hilber -> &Hilbert + new Transition (3131, 3132), // &hom -> &homt + new Transition (3133, 3134), // &homth -> &homtht + new Transition (3140, 3141), // &hooklef -> &hookleft + new Transition (3151, 3152), // &hookrigh -> &hookright + new Transition (3175, 3176), // &Horizon -> &Horizont + new Transition (3184, 3197), // &Hs -> &Hst + new Transition (3188, 3202), // &hs -> &hst + new Transition (3236, 3528), // &I -> &It + new Transition (3239, 3240), // &Iacu -> &Iacut + new Transition (3243, 3526), // &i -> &it + new Transition (3246, 3247), // &iacu -> &iacut + new Transition (3266, 3267), // &Ido -> &Idot + new Transition (3305, 3306), // &iiiin -> &iiiint + new Transition (3308, 3309), // &iiin -> &iiint + new Transition (3316, 3317), // &iio -> &iiot + new Transition (3337, 3362), // &ima -> &imat + new Transition (3359, 3360), // &imagpar -> &imagpart + new Transition (3378, 3401), // &in -> &int + new Transition (3387, 3389), // &infin -> &infint + new Transition (3395, 3396), // &inodo -> &inodot + new Transition (3398, 3399), // &In -> &Int + new Transition (3427, 3428), // &Intersec -> &Intersect + new Transition (3467, 3489), // &io -> &iot + new Transition (3471, 3486), // &Io -> &Iot + new Transition (3500, 3501), // &iques -> ¿ + new Transition (3515, 3516), // &isindo -> &isindot + new Transition (3578, 3579), // &jma -> &jmat + new Transition (3692, 4698), // &l -> < + new Transition (3693, 3794), // &lA -> &lAt + new Transition (3698, 4696), // &L -> &Lt + new Transition (3701, 3702), // &Lacu -> &Lacut + new Transition (3705, 3792), // &la -> &lat + new Transition (3707, 3708), // &lacu -> &lacut + new Transition (3713, 3714), // &laemp -> &laempt + new Transition (3750, 3751), // &Laplace -> &Laplacet + new Transition (3766, 3789), // &larr -> &larrt + new Transition (3899, 3900), // &Lef -> &Left + new Transition (3911, 3912), // &LeftAngleBracke -> &LeftAngleBracket + new Transition (3925, 3926), // &lef -> &left + new Transition (3926, 4109), // &left -> &leftt + new Transition (3931, 3948), // &leftarrow -> &leftarrowt + new Transition (3940, 3941), // &LeftArrowRigh -> &LeftArrowRight + new Transition (3972, 3973), // &LeftDoubleBracke -> &LeftDoubleBracket + new Transition (3982, 3983), // &LeftDownTeeVec -> &LeftDownTeeVect + new Transition (3989, 3990), // &LeftDownVec -> &LeftDownVect + new Transition (4021, 4022), // &leftlef -> &leftleft + new Transition (4033, 4034), // &LeftRigh -> &LeftRight + new Transition (4044, 4045), // &Leftrigh -> &Leftright + new Transition (4055, 4056), // &leftrigh -> &leftright + new Transition (4087, 4088), // &LeftRightVec -> &LeftRightVect + new Transition (4104, 4105), // &LeftTeeVec -> &LeftTeeVect + new Transition (4113, 4114), // &leftthree -> &leftthreet + new Transition (4146, 4147), // &LeftUpDownVec -> &LeftUpDownVect + new Transition (4156, 4157), // &LeftUpTeeVec -> &LeftUpTeeVect + new Transition (4163, 4164), // &LeftUpVec -> &LeftUpVect + new Transition (4174, 4175), // &LeftVec -> &LeftVect + new Transition (4194, 4195), // &leqslan -> &leqslant + new Transition (4203, 4204), // &lesdo -> &lesdot + new Transition (4224, 4225), // &lessdo -> &lessdot + new Transition (4229, 4230), // &lesseqg -> &lesseqgt + new Transition (4234, 4235), // &lesseqqg -> &lesseqqgt + new Transition (4248, 4249), // &LessEqualGrea -> &LessEqualGreat + new Transition (4266, 4267), // &LessGrea -> &LessGreat + new Transition (4271, 4272), // &lessg -> &lessgt + new Transition (4287, 4288), // &LessSlan -> &LessSlant + new Transition (4304, 4305), // &lfish -> &lfisht + new Transition (4348, 4375), // &ll -> &llt + new Transition (4362, 4363), // &Llef -> &Lleft + new Transition (4382, 4383), // &Lmido -> &Lmidot + new Transition (4388, 4389), // &lmido -> &lmidot + new Transition (4393, 4394), // &lmous -> &lmoust + new Transition (4422, 4573), // &lo -> &lot + new Transition (4439, 4440), // &LongLef -> &LongLeft + new Transition (4449, 4450), // &Longlef -> &Longleft + new Transition (4461, 4462), // &longlef -> &longleft + new Transition (4472, 4473), // &LongLeftRigh -> &LongLeftRight + new Transition (4483, 4484), // &Longleftrigh -> &Longleftright + new Transition (4494, 4495), // &longleftrigh -> &longleftright + new Transition (4505, 4506), // &longmaps -> &longmapst + new Transition (4512, 4513), // &LongRigh -> &LongRight + new Transition (4523, 4524), // &Longrigh -> &Longright + new Transition (4534, 4535), // &longrigh -> &longright + new Transition (4551, 4552), // &looparrowlef -> &looparrowleft + new Transition (4557, 4558), // &looparrowrigh -> &looparrowright + new Transition (4581, 4582), // &lowas -> &lowast + new Transition (4593, 4594), // &LowerLef -> &LowerLeft + new Transition (4604, 4605), // &LowerRigh -> &LowerRight + new Transition (4625, 4626), // &lparl -> &lparlt + new Transition (4628, 4648), // &lr -> &lrt + new Transition (4652, 4689), // &ls -> &lst + new Transition (4658, 4684), // &Ls -> &Lst + new Transition (4707, 4708), // <do -> <dot + new Transition (4728, 4729), // <ques -> <quest + new Transition (4757, 4758), // &lver -> &lvert + new Transition (4772, 4775), // &mal -> &malt + new Transition (4787, 4788), // &maps -> &mapst + new Transition (4798, 4799), // &mapstolef -> &mapstoleft + new Transition (4827, 4828), // &mDDo -> &mDDot + new Transition (4857, 4858), // &Mellin -> &Mellint + new Transition (4879, 4880), // &midas -> &midast + new Transition (4887, 4888), // &middo -> · + new Transition (4941, 4945), // &ms -> &mst + new Transition (4954, 4955), // &mul -> &mult + new Transition (4965, 5988), // &n -> &nt + new Transition (4966, 5001), // &na -> &nat + new Transition (4971, 5992), // &N -> &Nt + new Transition (4974, 4975), // &Nacu -> &Nacut + new Transition (4979, 4980), // &nacu -> &nacut + new Transition (5049, 5050), // &ncongdo -> &ncongdot + new Transition (5081, 5082), // &nedo -> &nedot + new Transition (5086, 5087), // &Nega -> &Negat + new Transition (5148, 5149), // &Nes -> &Nest + new Transition (5155, 5156), // &NestedGrea -> &NestedGreat + new Transition (5162, 5163), // &NestedGreaterGrea -> &NestedGreaterGreat + new Transition (5184, 5185), // &nexis -> &nexist + new Transition (5195, 5221), // &ng -> &ngt + new Transition (5207, 5208), // &ngeqslan -> &ngeqslant + new Transition (5212, 5219), // &nG -> &nGt + new Transition (5256, 5334), // &nl -> &nlt + new Transition (5272, 5332), // &nL -> &nLt + new Transition (5274, 5275), // &nLef -> &nLeft + new Transition (5282, 5283), // &nlef -> &nleft + new Transition (5293, 5294), // &nLeftrigh -> &nLeftright + new Transition (5304, 5305), // &nleftrigh -> &nleftright + new Transition (5319, 5320), // &nleqslan -> &nleqslant + new Transition (5347, 5376), // &No -> &Not + new Transition (5372, 5378), // &no -> ¬ + new Transition (5387, 5388), // &NotCongruen -> &NotCongruent + new Transition (5404, 5405), // &NotDoubleVer -> &NotDoubleVert + new Transition (5419, 5420), // &NotElemen -> &NotElement + new Transition (5435, 5436), // &NotExis -> &NotExist + new Transition (5442, 5443), // &NotGrea -> &NotGreat + new Transition (5466, 5467), // &NotGreaterGrea -> &NotGreaterGreat + new Transition (5479, 5480), // &NotGreaterSlan -> &NotGreaterSlant + new Transition (5516, 5517), // ¬indo -> ¬indot + new Transition (5530, 5531), // &NotLef -> &NotLeft + new Transition (5563, 5564), // &NotLessGrea -> &NotLessGreat + new Transition (5576, 5577), // &NotLessSlan -> &NotLessSlant + new Transition (5592, 5593), // &NotNes -> &NotNest + new Transition (5599, 5600), // &NotNestedGrea -> &NotNestedGreat + new Transition (5606, 5607), // &NotNestedGreaterGrea -> &NotNestedGreaterGreat + new Transition (5648, 5649), // &NotPrecedesSlan -> &NotPrecedesSlant + new Transition (5668, 5669), // &NotReverseElemen -> &NotReverseElement + new Transition (5673, 5674), // &NotRigh -> &NotRight + new Transition (5704, 5705), // &NotSquareSubse -> &NotSquareSubset + new Transition (5717, 5718), // &NotSquareSuperse -> &NotSquareSuperset + new Transition (5729, 5730), // &NotSubse -> &NotSubset + new Transition (5754, 5755), // &NotSucceedsSlan -> &NotSucceedsSlant + new Transition (5772, 5773), // &NotSuperse -> &NotSuperset + new Transition (5811, 5812), // &NotVer -> &NotVert + new Transition (5823, 5834), // &npar -> &npart + new Transition (5839, 5840), // &npolin -> &npolint + new Transition (5855, 5889), // &nr -> &nrt + new Transition (5871, 5872), // &nRigh -> &nRight + new Transition (5881, 5882), // &nrigh -> &nright + new Transition (5912, 5913), // &nshor -> &nshort + new Transition (5959, 5960), // &nsubse -> &nsubset + new Transition (5980, 5981), // &nsupse -> &nsupset + new Transition (6015, 6016), // &ntrianglelef -> &ntriangleleft + new Transition (6024, 6025), // &ntrianglerigh -> &ntriangleright + new Transition (6068, 6071), // &nvg -> &nvgt + new Transition (6084, 6091), // &nvl -> &nvlt + new Transition (6097, 6102), // &nvr -> &nvrt + new Transition (6131, 6399), // &O -> &Ot + new Transition (6134, 6135), // &Oacu -> &Oacut + new Transition (6138, 6405), // &o -> &ot + new Transition (6141, 6142), // &oacu -> &oacut + new Transition (6145, 6146), // &oas -> &oast + new Transition (6182, 6183), // &odo -> &odot + new Transition (6210, 6225), // &og -> &ogt + new Transition (6235, 6236), // &oin -> &oint + new Transition (6238, 6256), // &ol -> &olt + new Transition (6322, 6323), // &OpenCurlyDoubleQuo -> &OpenCurlyDoubleQuot + new Transition (6328, 6329), // &OpenCurlyQuo -> &OpenCurlyQuot + new Transition (6448, 6449), // &OverBracke -> &OverBracket + new Transition (6455, 6456), // &OverParen -> &OverParent + new Transition (6465, 6480), // &par -> &part + new Transition (6484, 6485), // &Par -> &Part + new Transition (6498, 6513), // &per -> &pert + new Transition (6500, 6501), // &percn -> &percnt + new Transition (6534, 6535), // &phmma -> &phmmat + new Transition (6543, 6545), // &pi -> &pit + new Transition (6567, 6603), // &plus -> &plust + new Transition (6624, 6625), // &poin -> &point + new Transition (6627, 6628), // &pointin -> &pointint + new Transition (6688, 6689), // &PrecedesSlan -> &PrecedesSlant + new Transition (6751, 6752), // &Produc -> &Product + new Transition (6770, 6783), // &prop -> &propt + new Transition (6774, 6775), // &Propor -> &Proport + new Transition (6822, 6823), // &qin -> &qint + new Transition (6848, 6849), // &qua -> &quat + new Transition (6859, 6860), // &quatin -> &quatint + new Transition (6863, 6864), // &ques -> &quest + new Transition (6873, 6874), // &quo -> " + new Transition (6876, 7567), // &r -> &rt + new Transition (6877, 6968), // &rA -> &rAt + new Transition (6882, 6973), // &ra -> &rat + new Transition (6889, 6890), // &Racu -> &Racut + new Transition (6893, 6894), // &racu -> &racut + new Transition (6903, 6904), // &raemp -> &raempt + new Transition (6926, 6960), // &Rarr -> &Rarrt + new Transition (6932, 6963), // &rarr -> &rarrt + new Transition (7084, 7085), // &realpar -> &realpart + new Transition (7089, 7090), // &rec -> &rect + new Transition (7107, 7108), // &ReverseElemen -> &ReverseElement + new Transition (7138, 7139), // &rfish -> &rfisht + new Transition (7173, 7174), // &Righ -> &Right + new Transition (7185, 7186), // &RightAngleBracke -> &RightAngleBracket + new Transition (7201, 7202), // &righ -> &right + new Transition (7202, 7354), // &right -> &rightt + new Transition (7207, 7223), // &rightarrow -> &rightarrowt + new Transition (7215, 7216), // &RightArrowLef -> &RightArrowLeft + new Transition (7247, 7248), // &RightDoubleBracke -> &RightDoubleBracket + new Transition (7257, 7258), // &RightDownTeeVec -> &RightDownTeeVect + new Transition (7264, 7265), // &RightDownVec -> &RightDownVect + new Transition (7296, 7297), // &rightlef -> &rightleft + new Transition (7317, 7318), // &rightrigh -> &rightright + new Transition (7349, 7350), // &RightTeeVec -> &RightTeeVect + new Transition (7358, 7359), // &rightthree -> &rightthreet + new Transition (7391, 7392), // &RightUpDownVec -> &RightUpDownVect + new Transition (7401, 7402), // &RightUpTeeVec -> &RightUpTeeVect + new Transition (7408, 7409), // &RightUpVec -> &RightUpVect + new Transition (7419, 7420), // &RightVec -> &RightVect + new Transition (7436, 7437), // &risingdo -> &risingdot + new Transition (7456, 7457), // &rmous -> &rmoust + new Transition (7469, 7495), // &ro -> &rot + new Transition (7516, 7517), // &rparg -> &rpargt + new Transition (7523, 7524), // &rppolin -> &rppolint + new Transition (7534, 7535), // &Rrigh -> &Rright + new Transition (7585, 7586), // &rtril -> &rtrilt + new Transition (7610, 8096), // &S -> &St + new Transition (7613, 7614), // &Sacu -> &Sacut + new Transition (7617, 8100), // &s -> &st + new Transition (7620, 7621), // &sacu -> &sacut + new Transition (7684, 7685), // &scpolin -> &scpolint + new Transition (7696, 7697), // &sdo -> &sdot + new Transition (7703, 7729), // &se -> &set + new Transition (7718, 7719), // &sec -> § + new Transition (7738, 7739), // &sex -> &sext + new Transition (7774, 7775), // &Shor -> &Short + new Transition (7788, 7789), // &ShortLef -> &ShortLeft + new Transition (7797, 7798), // &shor -> &short + new Transition (7815, 7816), // &ShortRigh -> &ShortRight + new Transition (7850, 7851), // &simdo -> &simdot + new Transition (7894, 7924), // &sm -> &smt + new Transition (7899, 7900), // &smallse -> &smallset + new Transition (7937, 7938), // &sof -> &soft + new Transition (7963, 7964), // &spadesui -> &spadesuit + new Transition (7981, 7982), // &Sqr -> &Sqrt + new Transition (7991, 7992), // &sqsubse -> &sqsubset + new Transition (8002, 8003), // &sqsupse -> &sqsupset + new Transition (8020, 8021), // &SquareIn -> &SquareInt + new Transition (8026, 8027), // &SquareIntersec -> &SquareIntersect + new Transition (8036, 8037), // &SquareSubse -> &SquareSubset + new Transition (8049, 8050), // &SquareSuperse -> &SquareSuperset + new Transition (8077, 8091), // &ss -> &sst + new Transition (8081, 8082), // &sse -> &sset + new Transition (8110, 8111), // &straigh -> &straight + new Transition (8134, 8135), // &subdo -> &subdot + new Transition (8142, 8143), // &subedo -> &subedot + new Transition (8147, 8148), // &submul -> &submult + new Transition (8166, 8167), // &Subse -> &Subset + new Transition (8170, 8171), // &subse -> &subset + new Transition (8232, 8233), // &SucceedsSlan -> &SucceedsSlant + new Transition (8272, 8273), // &SuchTha -> &SuchThat + new Transition (8293, 8294), // &supdo -> &supdot + new Transition (8305, 8306), // &supedo -> &supedot + new Transition (8311, 8312), // &Superse -> &Superset + new Transition (8335, 8336), // &supmul -> &supmult + new Transition (8349, 8350), // &Supse -> &Supset + new Transition (8353, 8354), // &supse -> &supset + new Transition (8408, 8409), // &targe -> &target + new Transition (8446, 8447), // &tdo -> &tdot + new Transition (8462, 8484), // &the -> &thet + new Transition (8468, 8481), // &The -> &Thet + new Transition (8587, 8588), // &tin -> &tint + new Transition (8597, 8598), // &topbo -> &topbot + new Transition (8633, 8693), // &tri -> &trit + new Transition (8647, 8648), // &trianglelef -> &triangleleft + new Transition (8658, 8659), // &trianglerigh -> &triangleright + new Transition (8665, 8666), // &trido -> &tridot + new Transition (8682, 8683), // &TripleDo -> &TripleDot + new Transition (8705, 8727), // &Ts -> &Tst + new Transition (8709, 8732), // &ts -> &tst + new Transition (8739, 8740), // &twix -> &twixt + new Transition (8749, 8750), // &twoheadlef -> &twoheadleft + new Transition (8760, 8761), // &twoheadrigh -> &twoheadright + new Transition (8768, 9166), // &U -> &Ut + new Transition (8771, 8772), // &Uacu -> &Uacut + new Transition (8775, 9161), // &u -> &ut + new Transition (8778, 8779), // &uacu -> &uacut + new Transition (8852, 8853), // &ufish -> &ufisht + new Transition (8887, 8900), // &ul -> &ult + new Transition (8930, 8931), // &UnderBracke -> &UnderBracket + new Transition (8937, 8938), // &UnderParen -> &UnderParent + new Transition (9055, 9056), // &upharpoonlef -> &upharpoonleft + new Transition (9061, 9062), // &upharpoonrigh -> &upharpoonright + new Transition (9073, 9074), // &UpperLef -> &UpperLeft + new Transition (9084, 9085), // &UpperRigh -> &UpperRight + new Transition (9127, 9149), // &ur -> &urt + new Transition (9163, 9164), // &utdo -> &utdot + new Transition (9205, 9206), // &vangr -> &vangrt + new Transition (9208, 9279), // &var -> &vart + new Transition (9224, 9225), // &varno -> &varnot + new Transition (9239, 9240), // &varprop -> &varpropt + new Transition (9261, 9262), // &varsubse -> &varsubset + new Transition (9271, 9272), // &varsupse -> &varsupset + new Transition (9281, 9282), // &varthe -> &varthet + new Transition (9294, 9295), // &vartrianglelef -> &vartriangleleft + new Transition (9300, 9301), // &vartrianglerigh -> &vartriangleright + new Transition (9360, 9370), // &Ver -> &Vert + new Transition (9365, 9372), // &ver -> &vert + new Transition (9392, 9393), // &VerticalSepara -> &VerticalSeparat + new Transition (9420, 9421), // &vl -> &vlt + new Transition (9445, 9446), // &vr -> &vrt + new Transition (9536, 9537), // &wrea -> &wreat + new Transition (9560, 9561), // &xd -> &xdt + new Transition (9602, 9618), // &xo -> &xot + new Transition (9604, 9605), // &xodo -> &xodot + new Transition (9645, 9651), // &xu -> &xut + new Transition (9668, 9669), // &Yacu -> &Yacut + new Transition (9675, 9676), // &yacu -> &yacut + new Transition (9750, 9751), // &Zacu -> &Zacut + new Transition (9757, 9758), // &zacu -> &zacut + new Transition (9778, 9779), // &Zdo -> &Zdot + new Transition (9782, 9783), // &zdo -> &zdot + new Transition (9785, 9808), // &ze -> &zet + new Transition (9786, 9787), // &zee -> &zeet + new Transition (9791, 9805), // &Ze -> &Zet + new Transition (9796, 9797) // &ZeroWid -> &ZeroWidt + }; + TransitionTable_u = new Transition[278] { + new Transition (0, 8775), // & -> &u + new Transition (1, 281), // &A -> &Au + new Transition (3, 4), // &Aac -> &Aacu + new Transition (8, 285), // &a -> &au + new Transition (10, 11), // &aac -> &aacu + new Transition (27, 42), // &ac -> &acu + new Transition (220, 221), // &ApplyF -> &ApplyFu + new Transition (301, 767), // &b -> &bu + new Transition (331, 781), // &B -> &Bu + new Transition (380, 381), // &bdq -> &bdqu + new Transition (386, 387), // &beca -> &becau + new Transition (392, 393), // &Beca -> &Becau + new Transition (411, 412), // &berno -> &bernou + new Transition (416, 417), // &Berno -> &Bernou + new Transition (443, 497), // &big -> &bigu + new Transition (444, 452), // &bigc -> &bigcu + new Transition (461, 462), // &bigopl -> &bigoplu + new Transition (473, 474), // &bigsqc -> &bigsqcu + new Transition (488, 494), // &bigtriangle -> &bigtriangleu + new Transition (499, 500), // &bigupl -> &biguplu + new Transition (532, 533), // &blacksq -> &blacksqu + new Transition (582, 583), // &bneq -> &bnequ + new Transition (613, 678), // &box -> &boxu + new Transition (636, 650), // &boxH -> &boxHu + new Transition (638, 654), // &boxh -> &boxhu + new Transition (658, 659), // &boxmin -> &boxminu + new Transition (663, 664), // &boxpl -> &boxplu + new Transition (763, 764), // &bsolhs -> &bsolhsu + new Transition (789, 1315), // &C -> &Cu + new Transition (791, 792), // &Cac -> &Cacu + new Transition (796, 1292), // &c -> &cu + new Transition (798, 799), // &cac -> &cacu + new Transition (813, 814), // &capbrc -> &capbrcu + new Transition (817, 821), // &capc -> &capcu + new Transition (861, 900), // &cc -> &ccu + new Transition (1034, 1035), // &CircleMin -> &CircleMinu + new Transition (1039, 1040), // &CirclePl -> &CirclePlu + new Transition (1080, 1081), // &ClockwiseConto -> &ClockwiseContou + new Transition (1094, 1095), // &CloseC -> &CloseCu + new Transition (1100, 1101), // &CloseCurlyDo -> &CloseCurlyDou + new Transition (1105, 1106), // &CloseCurlyDoubleQ -> &CloseCurlyDoubleQu + new Transition (1111, 1112), // &CloseCurlyQ -> &CloseCurlyQu + new Transition (1117, 1118), // &cl -> &clu + new Transition (1120, 1122), // &clubs -> &clubsu + new Transition (1126, 1226), // &Co -> &Cou + new Transition (1173, 1174), // &Congr -> &Congru + new Transition (1188, 1189), // &Conto -> &Contou + new Transition (1212, 1213), // &Coprod -> &Coprodu + new Transition (1244, 1245), // &CounterClockwiseConto -> &CounterClockwiseContou + new Transition (1274, 1278), // &cs -> &csu + new Transition (1330, 1334), // &cupc -> &cupcu + new Transition (1362, 1363), // &curlyeqs -> &curlyeqsu + new Transition (1432, 2077), // &d -> &du + new Transition (1568, 1569), // &DiacriticalAc -> &DiacriticalAcu + new Transition (1574, 1577), // &DiacriticalDo -> &DiacriticalDou + new Transition (1582, 1583), // &DiacriticalDoubleAc -> &DiacriticalDoubleAcu + new Transition (1612, 1613), // &diamonds -> &diamondsu + new Transition (1679, 1731), // &do -> &dou + new Transition (1685, 1744), // &Do -> &Dou + new Transition (1708, 1709), // &DotEq -> &DotEqu + new Transition (1715, 1716), // &dotmin -> &dotminu + new Transition (1720, 1721), // &dotpl -> &dotplu + new Transition (1725, 1726), // &dotsq -> &dotsqu + new Transition (1752, 1753), // &DoubleConto -> &DoubleContou + new Transition (2108, 2447), // &E -> &Eu + new Transition (2110, 2111), // &Eac -> &Eacu + new Transition (2115, 2451), // &e -> &eu + new Transition (2117, 2118), // &eac -> &eacu + new Transition (2255, 2256), // &EmptySmallSq -> &EmptySmallSqu + new Transition (2273, 2274), // &EmptyVerySmallSq -> &EmptyVerySmallSqu + new Transition (2319, 2320), // &epl -> &eplu + new Transition (2339, 2372), // &eq -> &equ + new Transition (2367, 2368), // &Eq -> &Equ + new Transition (2392, 2393), // &Equilibri -> &Equilibriu + new Transition (2565, 2566), // &FilledSmallSq -> &FilledSmallSqu + new Transition (2581, 2582), // &FilledVerySmallSq -> &FilledVerySmallSqu + new Transition (2608, 2630), // &Fo -> &Fou + new Transition (2703, 2704), // &gac -> &gacu + new Transition (2873, 2874), // &GreaterEq -> &GreaterEqu + new Transition (2883, 2884), // &GreaterF -> &GreaterFu + new Transition (2888, 2889), // &GreaterFullEq -> &GreaterFullEqu + new Transition (2912, 2913), // &GreaterSlantEq -> &GreaterSlantEqu + new Transition (2959, 2960), // >q -> >qu + new Transition (3014, 3207), // &H -> &Hu + new Transition (3078, 3080), // &hearts -> &heartsu + new Transition (3214, 3215), // &HumpDownH -> &HumpDownHu + new Transition (3220, 3221), // &HumpEq -> &HumpEqu + new Transition (3226, 3227), // &hyb -> &hybu + new Transition (3236, 3539), // &I -> &Iu + new Transition (3238, 3239), // &Iac -> &Iacu + new Transition (3243, 3544), // &i -> &iu + new Transition (3245, 3246), // &iac -> &iacu + new Transition (3497, 3498), // &iq -> &iqu + new Transition (3555, 3608), // &J -> &Ju + new Transition (3561, 3613), // &j -> &ju + new Transition (3692, 4742), // &l -> &lu + new Transition (3700, 3701), // &Lac -> &Lacu + new Transition (3706, 3707), // &lac -> &lacu + new Transition (3755, 3756), // &laq -> &laqu + new Transition (3832, 3835), // &lbrksl -> &lbrkslu + new Transition (3843, 3862), // &lc -> &lcu + new Transition (3873, 3874), // &ldq -> &ldqu + new Transition (3879, 3885), // &ldr -> &ldru + new Transition (3962, 3963), // &LeftDo -> &LeftDou + new Transition (4010, 4016), // &leftharpoon -> &leftharpoonu + new Transition (4075, 4076), // &leftrightsq -> &leftrightsqu + new Transition (4133, 4134), // &LeftTriangleEq -> &LeftTriangleEqu + new Transition (4241, 4242), // &LessEq -> &LessEqu + new Transition (4253, 4254), // &LessF -> &LessFu + new Transition (4258, 4259), // &LessFullEq -> &LessFullEqu + new Transition (4290, 4291), // &LessSlantEq -> &LessSlantEqu + new Transition (4327, 4330), // &lhar -> &lharu + new Transition (4391, 4392), // &lmo -> &lmou + new Transition (4569, 4570), // &lopl -> &loplu + new Transition (4654, 4655), // &lsaq -> &lsaqu + new Transition (4676, 4679), // &lsq -> &lsqu + new Transition (4725, 4726), // <q -> <qu + new Transition (4743, 4750), // &lur -> &luru + new Transition (4767, 4952), // &m -> &mu + new Transition (4781, 4950), // &M -> &Mu + new Transition (4789, 4801), // &mapsto -> &mapstou + new Transition (4832, 4833), // &meas -> &measu + new Transition (4845, 4846), // &Medi -> &Mediu + new Transition (4890, 4891), // &min -> &minu + new Transition (4896, 4898), // &minusd -> &minusdu + new Transition (4901, 4902), // &Min -> &Minu + new Transition (4905, 4906), // &MinusPl -> &MinusPlu + new Transition (4918, 4919), // &mnpl -> &mnplu + new Transition (4965, 6032), // &n -> &nu + new Transition (4971, 6030), // &N -> &Nu + new Transition (4973, 4974), // &Nac -> &Nacu + new Transition (4978, 4979), // &nac -> &nacu + new Transition (5001, 5002), // &nat -> &natu + new Transition (5010, 5014), // &nb -> &nbu + new Transition (5020, 5052), // &nc -> &ncu + new Transition (5094, 5095), // &NegativeMedi -> &NegativeMediu + new Transition (5135, 5136), // &neq -> &nequ + new Transition (5380, 5390), // &NotC -> &NotCu + new Transition (5384, 5385), // &NotCongr -> &NotCongru + new Transition (5397, 5398), // &NotDo -> &NotDou + new Transition (5422, 5423), // &NotEq -> &NotEqu + new Transition (5448, 5449), // &NotGreaterEq -> &NotGreaterEqu + new Transition (5453, 5454), // &NotGreaterF -> &NotGreaterFu + new Transition (5458, 5459), // &NotGreaterFullEq -> &NotGreaterFullEqu + new Transition (5482, 5483), // &NotGreaterSlantEq -> &NotGreaterSlantEqu + new Transition (5493, 5494), // &NotH -> &NotHu + new Transition (5501, 5502), // &NotHumpDownH -> &NotHumpDownHu + new Transition (5507, 5508), // &NotHumpEq -> &NotHumpEqu + new Transition (5546, 5547), // &NotLeftTriangleEq -> &NotLeftTriangleEqu + new Transition (5555, 5556), // &NotLessEq -> &NotLessEqu + new Transition (5579, 5580), // &NotLessSlantEq -> &NotLessSlantEqu + new Transition (5640, 5641), // &NotPrecedesEq -> &NotPrecedesEqu + new Transition (5651, 5652), // &NotPrecedesSlantEq -> &NotPrecedesSlantEqu + new Transition (5689, 5690), // &NotRightTriangleEq -> &NotRightTriangleEqu + new Transition (5694, 5726), // &NotS -> &NotSu + new Transition (5695, 5696), // &NotSq -> &NotSqu + new Transition (5700, 5701), // &NotSquareS -> &NotSquareSu + new Transition (5708, 5709), // &NotSquareSubsetEq -> &NotSquareSubsetEqu + new Transition (5721, 5722), // &NotSquareSupersetEq -> &NotSquareSupersetEqu + new Transition (5733, 5734), // &NotSubsetEq -> &NotSubsetEqu + new Transition (5746, 5747), // &NotSucceedsEq -> &NotSucceedsEqu + new Transition (5757, 5758), // &NotSucceedsSlantEq -> &NotSucceedsSlantEqu + new Transition (5776, 5777), // &NotSupersetEq -> &NotSupersetEqu + new Transition (5788, 5789), // &NotTildeEq -> &NotTildeEqu + new Transition (5793, 5794), // &NotTildeF -> &NotTildeFu + new Transition (5798, 5799), // &NotTildeFullEq -> &NotTildeFullEqu + new Transition (5844, 5845), // &nprc -> &nprcu + new Transition (5895, 5951), // &ns -> &nsu + new Transition (5898, 5899), // &nscc -> &nsccu + new Transition (5943, 5944), // &nsqs -> &nsqsu + new Transition (6131, 6422), // &O -> &Ou + new Transition (6133, 6134), // &Oac -> &Oacu + new Transition (6138, 6426), // &o -> &ou + new Transition (6140, 6141), // &oac -> &oacu + new Transition (6290, 6291), // &omin -> &ominu + new Transition (6309, 6310), // &OpenC -> &OpenCu + new Transition (6315, 6316), // &OpenCurlyDo -> &OpenCurlyDou + new Transition (6320, 6321), // &OpenCurlyDoubleQ -> &OpenCurlyDoubleQu + new Transition (6326, 6327), // &OpenCurlyQ -> &OpenCurlyQu + new Transition (6336, 6337), // &opl -> &oplu + new Transition (6463, 6807), // &p -> &pu + new Transition (6555, 6566), // &pl -> &plu + new Transition (6580, 6583), // &plusd -> &plusdu + new Transition (6587, 6588), // &Pl -> &Plu + new Transition (6592, 6593), // &PlusMin -> &PlusMinu + new Transition (6622, 6636), // &po -> &pou + new Transition (6642, 6790), // &pr -> &pru + new Transition (6647, 6648), // &prc -> &prcu + new Transition (6664, 6665), // &precc -> &preccu + new Transition (6680, 6681), // &PrecedesEq -> &PrecedesEqu + new Transition (6691, 6692), // &PrecedesSlantEq -> &PrecedesSlantEqu + new Transition (6749, 6750), // &Prod -> &Produ + new Transition (6765, 6766), // &profs -> &profsu + new Transition (6817, 6847), // &q -> &qu + new Transition (6876, 7601), // &r -> &ru + new Transition (6883, 6893), // &rac -> &racu + new Transition (6886, 7590), // &R -> &Ru + new Transition (6888, 6889), // &Rac -> &Racu + new Transition (6921, 6922), // &raq -> &raqu + new Transition (7016, 7019), // &rbrksl -> &rbrkslu + new Transition (7027, 7046), // &rc -> &rcu + new Transition (7063, 7064), // &rdq -> &rdqu + new Transition (7110, 7111), // &ReverseEq -> &ReverseEqu + new Transition (7117, 7118), // &ReverseEquilibri -> &ReverseEquilibriu + new Transition (7124, 7125), // &ReverseUpEq -> &ReverseUpEqu + new Transition (7131, 7132), // &ReverseUpEquilibri -> &ReverseUpEquilibriu + new Transition (7157, 7160), // &rhar -> &rharu + new Transition (7237, 7238), // &RightDo -> &RightDou + new Transition (7285, 7291), // &rightharpoon -> &rightharpoonu + new Transition (7327, 7328), // &rightsq -> &rightsqu + new Transition (7378, 7379), // &RightTriangleEq -> &RightTriangleEqu + new Transition (7454, 7455), // &rmo -> &rmou + new Transition (7485, 7501), // &Ro -> &Rou + new Transition (7491, 7492), // &ropl -> &roplu + new Transition (7544, 7545), // &rsaq -> &rsaqu + new Transition (7559, 7562), // &rsq -> &rsqu + new Transition (7602, 7603), // &rul -> &rulu + new Transition (7610, 8127), // &S -> &Su + new Transition (7612, 7613), // &Sac -> &Sacu + new Transition (7617, 8130), // &s -> &su + new Transition (7619, 7620), // &sac -> &sacu + new Transition (7625, 7626), // &sbq -> &sbqu + new Transition (7645, 7646), // &scc -> &sccu + new Transition (7732, 7733), // &setmin -> &setminu + new Transition (7869, 7870), // &simpl -> &simplu + new Transition (7903, 7904), // &smallsetmin -> &smallsetminu + new Transition (7960, 7962), // &spades -> &spadesu + new Transition (7968, 8008), // &sq -> &squ + new Transition (7969, 7975), // &sqc -> &sqcu + new Transition (7980, 8010), // &Sq -> &Squ + new Transition (7984, 7985), // &sqs -> &sqsu + new Transition (8032, 8033), // &SquareS -> &SquareSu + new Transition (8040, 8041), // &SquareSubsetEq -> &SquareSubsetEqu + new Transition (8053, 8054), // &SquareSupersetEq -> &SquareSupersetEqu + new Transition (8145, 8146), // &subm -> &submu + new Transition (8156, 8157), // &subpl -> &subplu + new Transition (8169, 8193), // &subs -> &subsu + new Transition (8179, 8180), // &SubsetEq -> &SubsetEqu + new Transition (8208, 8209), // &succc -> &succcu + new Transition (8224, 8225), // &SucceedsEq -> &SucceedsEqu + new Transition (8235, 8236), // &SucceedsSlantEq -> &SucceedsSlantEqu + new Transition (8296, 8297), // &supds -> &supdsu + new Transition (8315, 8316), // &SupersetEq -> &SupersetEqu + new Transition (8321, 8325), // &suphs -> &suphsu + new Transition (8333, 8334), // &supm -> &supmu + new Transition (8344, 8345), // &suppl -> &supplu + new Transition (8352, 8370), // &sups -> &supsu + new Transition (8401, 8411), // &Ta -> &Tau + new Transition (8405, 8413), // &ta -> &tau + new Transition (8555, 8556), // &TildeEq -> &TildeEqu + new Transition (8560, 8561), // &TildeF -> &TildeFu + new Transition (8565, 8566), // &TildeFullEq -> &TildeFullEqu + new Transition (8672, 8673), // &trimin -> &triminu + new Transition (8686, 8687), // &tripl -> &triplu + new Transition (8701, 8702), // &trpezi -> &trpeziu + new Transition (8768, 9187), // &U -> &Uu + new Transition (8770, 8771), // &Uac -> &Uacu + new Transition (8775, 9182), // &u -> &uu + new Transition (8777, 8778), // &uac -> &uacu + new Transition (8950, 8951), // &UnionPl -> &UnionPlu + new Transition (8983, 9118), // &up -> &upu + new Transition (9035, 9036), // &UpEq -> &UpEqu + new Transition (9042, 9043), // &UpEquilibri -> &UpEquilibriu + new Transition (9064, 9065), // &upl -> &uplu + new Transition (9252, 9258), // &vars -> &varsu + new Transition (9426, 9427), // &vns -> &vnsu + new Transition (9454, 9458), // &vs -> &vsu + new Transition (9548, 9645), // &x -> &xu + new Transition (9549, 9557), // &xc -> &xcu + new Transition (9614, 9615), // &xopl -> &xoplu + new Transition (9641, 9642), // &xsqc -> &xsqcu + new Transition (9647, 9648), // &xupl -> &xuplu + new Transition (9665, 9740), // &Y -> &Yu + new Transition (9667, 9668), // &Yac -> &Yacu + new Transition (9672, 9736), // &y -> &yu + new Transition (9674, 9675), // &yac -> &yacu + new Transition (9749, 9750), // &Zac -> &Zacu + new Transition (9756, 9757) // &zac -> &zacu + }; + TransitionTable_v = new Transition[75] { + new Transition (0, 9201), // & -> &v + new Transition (17, 18), // &Abre -> &Abrev + new Transition (23, 24), // &abre -> &abrev + new Transition (69, 70), // &Agra -> &Agrav + new Transition (75, 76), // &agra -> &agrav + new Transition (120, 134), // &and -> &andv + new Transition (165, 167), // &angrt -> &angrtv + new Transition (341, 342), // &Bar -> &Barv + new Transition (344, 345), // &bar -> &barv + new Transition (402, 403), // &bempty -> &bemptyv + new Transition (443, 503), // &big -> &bigv + new Transition (584, 585), // &bnequi -> &bnequiv + new Transition (613, 693), // &box -> &boxv + new Transition (726, 727), // &Bre -> &Brev + new Transition (730, 735), // &br -> &brv + new Transition (731, 732), // &bre -> &brev + new Transition (930, 931), // &cempty -> &cemptyv + new Transition (1292, 1399), // &cu -> &cuv + new Transition (1346, 1381), // &cur -> &curv + new Transition (1354, 1367), // &curly -> &curlyv + new Transition (1455, 1461), // &dash -> &dashv + new Transition (1458, 1459), // &Dash -> &Dashv + new Transition (1532, 1533), // &dempty -> &demptyv + new Transition (1589, 1590), // &DiacriticalGra -> &DiacriticalGrav + new Transition (1599, 1643), // &di -> &div + new Transition (1917, 1918), // &DownBre -> &DownBrev + new Transition (2189, 2190), // &Egra -> &Egrav + new Transition (2194, 2195), // &egra -> &egrav + new Transition (2240, 2261), // &empty -> &emptyv + new Transition (2324, 2337), // &epsi -> &epsiv + new Transition (2339, 2402), // &eq -> &eqv + new Transition (2396, 2397), // &equi -> &equiv + new Transition (2626, 2628), // &fork -> &forkv + new Transition (2701, 3002), // &g -> &gv + new Transition (2726, 2727), // &Gbre -> &Gbrev + new Transition (2732, 2733), // &gbre -> &gbrev + new Transition (2862, 2863), // &gra -> &grav + new Transition (3291, 3292), // &Igra -> &Igrav + new Transition (3297, 3298), // &igra -> &igrav + new Transition (3398, 3444), // &In -> &Inv + new Transition (3512, 3524), // &isin -> &isinv + new Transition (3520, 3522), // &isins -> &isinsv + new Transition (3628, 3630), // &kappa -> &kappav + new Transition (3692, 4755), // &l -> &lv + new Transition (3715, 3716), // &laempty -> &laemptyv + new Transition (4965, 6043), // &n -> &nv + new Transition (5088, 5089), // &Negati -> &Negativ + new Transition (5137, 5138), // &nequi -> &nequiv + new Transition (5219, 5225), // &nGt -> &nGtv + new Transition (5240, 5246), // &ni -> &niv + new Transition (5332, 5341), // &nLt -> &nLtv + new Transition (5513, 5521), // ¬in -> ¬inv + new Transition (5621, 5623), // ¬ni -> ¬niv + new Transition (5657, 5658), // &NotRe -> &NotRev + new Transition (6131, 6435), // &O -> &Ov + new Transition (6138, 6430), // &o -> &ov + new Transition (6179, 6180), // &odi -> &odiv + new Transition (6216, 6217), // &Ogra -> &Ograv + new Transition (6221, 6222), // &ogra -> &ograv + new Transition (6342, 6374), // &or -> &orv + new Transition (6528, 6530), // &phi -> &phiv + new Transition (6543, 6553), // &pi -> &piv + new Transition (6563, 6564), // &plank -> &plankv + new Transition (6905, 6906), // &raempty -> &raemptyv + new Transition (7072, 7097), // &Re -> &Rev + new Transition (7167, 7169), // &rho -> &rhov + new Transition (7841, 7845), // &sigma -> &sigmav + new Transition (8485, 8491), // &theta -> &thetav + new Transition (8807, 8808), // &Ubre -> &Ubrev + new Transition (8811, 8812), // &ubre -> &ubrev + new Transition (8862, 8863), // &Ugra -> &Ugrav + new Transition (8868, 8869), // &ugra -> &ugrav + new Transition (9303, 9471), // &V -> &Vv + new Transition (9310, 9312), // &vBar -> &vBarv + new Transition (9548, 9655) // &x -> &xv + }; + TransitionTable_w = new Transition[137] { + new Transition (0, 9490), // & -> &w + new Transition (8, 289), // &a -> &aw + new Transition (341, 349), // &Bar -> &Barw + new Transition (344, 353), // &bar -> &barw + new Transition (426, 431), // &bet -> &betw + new Transition (443, 507), // &big -> &bigw + new Transition (490, 491), // &bigtriangledo -> &bigtriangledow + new Transition (516, 517), // &bkaro -> &bkarow + new Transition (548, 549), // &blacktriangledo -> &blacktriangledow + new Transition (598, 608), // &bo -> &bow + new Transition (796, 1407), // &c -> &cw + new Transition (991, 992), // &circlearro -> &circlearrow + new Transition (1071, 1072), // &Clock -> &Clockw + new Transition (1235, 1236), // &CounterClock -> &CounterClockw + new Transition (1292, 1403), // &cu -> &cuw + new Transition (1354, 1371), // &curly -> &curlyw + new Transition (1386, 1387), // &curvearro -> &curvearrow + new Transition (1432, 2086), // &d -> &dw + new Transition (1467, 1468), // &dbkaro -> &dbkarow + new Transition (1679, 1895), // &do -> &dow + new Transition (1685, 1881), // &Do -> &Dow + new Transition (1737, 1738), // &doublebar -> &doublebarw + new Transition (1765, 1768), // &DoubleDo -> &DoubleDow + new Transition (1773, 1774), // &DoubleDownArro -> &DoubleDownArrow + new Transition (1783, 1784), // &DoubleLeftArro -> &DoubleLeftArrow + new Transition (1794, 1795), // &DoubleLeftRightArro -> &DoubleLeftRightArrow + new Transition (1811, 1812), // &DoubleLongLeftArro -> &DoubleLongLeftArrow + new Transition (1822, 1823), // &DoubleLongLeftRightArro -> &DoubleLongLeftRightArrow + new Transition (1833, 1834), // &DoubleLongRightArro -> &DoubleLongRightArrow + new Transition (1844, 1845), // &DoubleRightArro -> &DoubleRightArrow + new Transition (1856, 1857), // &DoubleUpArro -> &DoubleUpArrow + new Transition (1860, 1861), // &DoubleUpDo -> &DoubleUpDow + new Transition (1866, 1867), // &DoubleUpDownArro -> &DoubleUpDownArrow + new Transition (1886, 1887), // &DownArro -> &DownArrow + new Transition (1892, 1893), // &Downarro -> &Downarrow + new Transition (1900, 1901), // &downarro -> &downarrow + new Transition (1912, 1913), // &DownArrowUpArro -> &DownArrowUpArrow + new Transition (1922, 1923), // &downdo -> &downdow + new Transition (1928, 1929), // &downdownarro -> &downdownarrow + new Transition (2020, 2021), // &DownTeeArro -> &DownTeeArrow + new Transition (2028, 2029), // &drbkaro -> &drbkarow + new Transition (2689, 2690), // &fro -> &frow + new Transition (3050, 3056), // &harr -> &harrw + new Transition (3113, 3120), // &hks -> &hksw + new Transition (3117, 3118), // &hksearo -> &hksearow + new Transition (3123, 3124), // &hkswaro -> &hkswarow + new Transition (3145, 3146), // &hookleftarro -> &hookleftarrow + new Transition (3156, 3157), // &hookrightarro -> &hookrightarrow + new Transition (3211, 3212), // &HumpDo -> &HumpDow + new Transition (3916, 3917), // &LeftArro -> &LeftArrow + new Transition (3922, 3923), // &Leftarro -> &Leftarrow + new Transition (3930, 3931), // &leftarro -> &leftarrow + new Transition (3945, 3946), // &LeftArrowRightArro -> &LeftArrowRightArrow + new Transition (3962, 3975), // &LeftDo -> &LeftDow + new Transition (4012, 4013), // &leftharpoondo -> &leftharpoondow + new Transition (4026, 4027), // &leftleftarro -> &leftleftarrow + new Transition (4038, 4039), // &LeftRightArro -> &LeftRightArrow + new Transition (4049, 4050), // &Leftrightarro -> &Leftrightarrow + new Transition (4060, 4061), // &leftrightarro -> &leftrightarrow + new Transition (4082, 4083), // &leftrightsquigarro -> &leftrightsquigarrow + new Transition (4099, 4100), // &LeftTeeArro -> &LeftTeeArrow + new Transition (4141, 4142), // &LeftUpDo -> &LeftUpDow + new Transition (4367, 4368), // &Lleftarro -> &Lleftarrow + new Transition (4422, 4579), // &lo -> &low + new Transition (4434, 4588), // &Lo -> &Low + new Transition (4444, 4445), // &LongLeftArro -> &LongLeftArrow + new Transition (4454, 4455), // &Longleftarro -> &Longleftarrow + new Transition (4466, 4467), // &longleftarro -> &longleftarrow + new Transition (4477, 4478), // &LongLeftRightArro -> &LongLeftRightArrow + new Transition (4488, 4489), // &Longleftrightarro -> &Longleftrightarrow + new Transition (4499, 4500), // &longleftrightarro -> &longleftrightarrow + new Transition (4517, 4518), // &LongRightArro -> &LongRightArrow + new Transition (4528, 4529), // &Longrightarro -> &Longrightarrow + new Transition (4539, 4540), // &longrightarro -> &longrightarrow + new Transition (4547, 4548), // &looparro -> &looparrow + new Transition (4598, 4599), // &LowerLeftArro -> &LowerLeftArrow + new Transition (4609, 4610), // &LowerRightArro -> &LowerRightArrow + new Transition (4792, 4793), // &mapstodo -> &mapstodow + new Transition (4965, 6111), // &n -> &nw + new Transition (5077, 5078), // &nearro -> &nearrow + new Transition (5084, 5176), // &Ne -> &New + new Transition (5279, 5280), // &nLeftarro -> &nLeftarrow + new Transition (5287, 5288), // &nleftarro -> &nleftarrow + new Transition (5298, 5299), // &nLeftrightarro -> &nLeftrightarrow + new Transition (5309, 5310), // &nleftrightarro -> &nleftrightarrow + new Transition (5498, 5499), // &NotHumpDo -> &NotHumpDow + new Transition (5862, 5866), // &nrarr -> &nrarrw + new Transition (5876, 5877), // &nRightarro -> &nRightarrow + new Transition (5886, 5887), // &nrightarro -> &nrightarrow + new Transition (6123, 6124), // &nwarro -> &nwarrow + new Transition (6603, 6604), // &plust -> &plustw + new Transition (6932, 6966), // &rarr -> &rarrw + new Transition (7190, 7191), // &RightArro -> &RightArrow + new Transition (7196, 7197), // &Rightarro -> &Rightarrow + new Transition (7206, 7207), // &rightarro -> &rightarrow + new Transition (7220, 7221), // &RightArrowLeftArro -> &RightArrowLeftArrow + new Transition (7237, 7250), // &RightDo -> &RightDow + new Transition (7287, 7288), // &rightharpoondo -> &rightharpoondow + new Transition (7301, 7302), // &rightleftarro -> &rightleftarrow + new Transition (7322, 7323), // &rightrightarro -> &rightrightarrow + new Transition (7334, 7335), // &rightsquigarro -> &rightsquigarrow + new Transition (7344, 7345), // &RightTeeArro -> &RightTeeArrow + new Transition (7386, 7387), // &RightUpDo -> &RightUpDow + new Transition (7539, 7540), // &Rrightarro -> &Rrightarrow + new Transition (7617, 8375), // &s -> &sw + new Transition (7715, 7716), // &searro -> &searrow + new Transition (7724, 7725), // &ses -> &sesw + new Transition (7747, 7748), // &sfro -> &sfrow + new Transition (7777, 7778), // &ShortDo -> &ShortDow + new Transition (7783, 7784), // &ShortDownArro -> &ShortDownArrow + new Transition (7793, 7794), // &ShortLeftArro -> &ShortLeftArrow + new Transition (7820, 7821), // &ShortRightArro -> &ShortRightArrow + new Transition (7828, 7829), // &ShortUpArro -> &ShortUpArrow + new Transition (8387, 8388), // &swarro -> &swarrow + new Transition (8390, 8391), // &swn -> &swnw + new Transition (8404, 8737), // &t -> &tw + new Transition (8641, 8642), // &triangledo -> &triangledow + new Transition (8754, 8755), // &twoheadleftarro -> &twoheadleftarrow + new Transition (8765, 8766), // &twoheadrightarro -> &twoheadrightarrow + new Transition (8775, 9194), // &u -> &uw + new Transition (8974, 8975), // &UpArro -> &UpArrow + new Transition (8980, 8981), // &Uparro -> &Uparrow + new Transition (8987, 8988), // &uparro -> &uparrow + new Transition (8995, 8996), // &UpArrowDo -> &UpArrowDow + new Transition (9001, 9002), // &UpArrowDownArro -> &UpArrowDownArrow + new Transition (9005, 9006), // &UpDo -> &UpDow + new Transition (9011, 9012), // &UpDownArro -> &UpDownArrow + new Transition (9015, 9016), // &Updo -> &Updow + new Transition (9021, 9022), // &Updownarro -> &Updownarrow + new Transition (9025, 9026), // &updo -> &updow + new Transition (9031, 9032), // &updownarro -> &updownarrow + new Transition (9078, 9079), // &UpperLeftArro -> &UpperLeftArrow + new Transition (9089, 9090), // &UpperRightArro -> &UpperRightArrow + new Transition (9115, 9116), // &UpTeeArro -> &UpTeeArrow + new Transition (9123, 9124), // &upuparro -> &upuparrow + new Transition (9548, 9659), // &x -> &xw + new Transition (9754, 9848) // &z -> &zw + }; + TransitionTable_x = new Transition[24] { + new Transition (0, 9548), // & -> &x + new Transition (231, 232), // &appro -> &approx + new Transition (598, 613), // &bo -> &box + new Transition (615, 616), // &boxbo -> &boxbox + new Transition (1154, 1160), // &comple -> &complex + new Transition (1658, 1659), // &divon -> &divonx + new Transition (2108, 2466), // &E -> &Ex + new Transition (2115, 2458), // &e -> &ex + new Transition (2838, 2839), // &gnappro -> &gnapprox + new Transition (2970, 2971), // >rappro -> >rapprox + new Transition (3273, 3277), // &ie -> &iex + new Transition (4220, 4221), // &lessappro -> &lessapprox + new Transition (4407, 4408), // &lnappro -> &lnapprox + new Transition (4998, 4999), // &nappro -> &napprox + new Transition (5064, 5182), // &ne -> &nex + new Transition (5414, 5433), // &NotE -> &NotEx + new Transition (6661, 6662), // &precappro -> &precapprox + new Transition (6710, 6711), // &precnappro -> &precnapprox + new Transition (6876, 7608), // &r -> &rx + new Transition (7703, 7738), // &se -> &sex + new Transition (8205, 8206), // &succappro -> &succapprox + new Transition (8254, 8255), // &succnappro -> &succnapprox + new Transition (8500, 8501), // &thickappro -> &thickapprox + new Transition (8738, 8739) // &twi -> &twix + }; + TransitionTable_y = new Transition[122] { + new Transition (0, 9672), // & -> &y + new Transition (27, 48), // &ac -> &acy + new Transition (33, 46), // &Ac -> &Acy + new Transition (82, 83), // &alefs -> &alefsy + new Transition (218, 219), // &Appl -> &Apply + new Transition (251, 262), // &as -> &asy + new Transition (369, 377), // &bc -> &bcy + new Transition (374, 375), // &Bc -> &Bcy + new Transition (401, 402), // &bempt -> &bempty + new Transition (790, 855), // &Ca -> &Cay + new Transition (796, 1419), // &c -> &cy + new Transition (857, 858), // &Cayle -> &Cayley + new Transition (929, 930), // &cempt -> &cempty + new Transition (957, 958), // &CHc -> &CHcy + new Transition (961, 962), // &chc -> &chcy + new Transition (1097, 1098), // &CloseCurl -> &CloseCurly + new Transition (1203, 1221), // &cop -> © + new Transition (1353, 1354), // &curl -> &curly + new Transition (1422, 1423), // &cylct -> &cylcty + new Transition (1474, 1486), // &Dc -> &Dcy + new Transition (1480, 1488), // &dc -> &dcy + new Transition (1531, 1532), // &dempt -> &dempty + new Transition (1662, 1663), // &DJc -> &DJcy + new Transition (1666, 1667), // &djc -> &djcy + new Transition (2045, 2052), // &dsc -> &dscy + new Transition (2049, 2050), // &DSc -> &DScy + new Transition (2094, 2095), // &DZc -> &DZcy + new Transition (2098, 2099), // &dzc -> &dzcy + new Transition (2127, 2153), // &Ec -> &Ecy + new Transition (2133, 2155), // &ec -> &ecy + new Transition (2239, 2240), // &empt -> &empty + new Transition (2247, 2248), // &Empt -> &Empty + new Transition (2265, 2266), // &EmptyVer -> &EmptyVery + new Transition (2518, 2519), // &Fc -> &Fcy + new Transition (2521, 2522), // &fc -> &fcy + new Transition (2573, 2574), // &FilledVer -> &FilledVery + new Transition (2736, 2751), // &Gc -> &Gcy + new Transition (2746, 2753), // &gc -> &gcy + new Transition (2817, 2818), // &GJc -> &GJcy + new Transition (2821, 2822), // &gjc -> &gjcy + new Transition (3020, 3225), // &h -> &hy + new Transition (3038, 3039), // &HARDc -> &HARDcy + new Transition (3043, 3044), // &hardc -> &hardcy + new Transition (3250, 3263), // &ic -> &icy + new Transition (3252, 3261), // &Ic -> &Icy + new Transition (3270, 3271), // &IEc -> &IEcy + new Transition (3274, 3275), // &iec -> &iecy + new Transition (3348, 3349), // &Imaginar -> &Imaginary + new Transition (3464, 3465), // &IOc -> &IOcy + new Transition (3468, 3469), // &ioc -> &iocy + new Transition (3541, 3542), // &Iukc -> &Iukcy + new Transition (3546, 3547), // &iukc -> &iukcy + new Transition (3556, 3567), // &Jc -> &Jcy + new Transition (3562, 3569), // &jc -> &jcy + new Transition (3600, 3601), // &Jserc -> &Jsercy + new Transition (3605, 3606), // &jserc -> &jsercy + new Transition (3610, 3611), // &Jukc -> &Jukcy + new Transition (3615, 3616), // &jukc -> &jukcy + new Transition (3632, 3644), // &Kc -> &Kcy + new Transition (3638, 3646), // &kc -> &kcy + new Transition (3661, 3662), // &KHc -> &KHcy + new Transition (3665, 3666), // &khc -> &khcy + new Transition (3669, 3670), // &KJc -> &KJcy + new Transition (3673, 3674), // &kjc -> &kjcy + new Transition (3714, 3715), // &laempt -> &laempty + new Transition (3837, 3865), // &Lc -> &Lcy + new Transition (3843, 3867), // &lc -> &lcy + new Transition (4339, 4340), // &LJc -> &LJcy + new Transition (4343, 4344), // &ljc -> &ljcy + new Transition (4809, 4818), // &mc -> &mcy + new Transition (4815, 4816), // &Mc -> &Mcy + new Transition (5020, 5057), // &nc -> &ncy + new Transition (5024, 5055), // &Nc -> &Ncy + new Transition (5123, 5124), // &NegativeVer -> &NegativeVery + new Transition (5249, 5250), // &NJc -> &NJcy + new Transition (5253, 5254), // &njc -> &njcy + new Transition (6148, 6161), // &oc -> &ocy + new Transition (6152, 6159), // &Oc -> &Ocy + new Transition (6312, 6313), // &OpenCurl -> &OpenCurly + new Transition (6491, 6492), // &Pc -> &Pcy + new Transition (6494, 6495), // &pc -> &pcy + new Transition (6667, 6668), // &preccurl -> &preccurly + new Transition (6904, 6905), // &raempt -> &raempty + new Transition (7021, 7049), // &Rc -> &Rcy + new Transition (7027, 7051), // &rc -> &rcy + new Transition (7596, 7597), // &RuleDela -> &RuleDelay + new Transition (7629, 7691), // &Sc -> &Scy + new Transition (7631, 7693), // &sc -> &scy + new Transition (7751, 7831), // &sh -> ­ + new Transition (7759, 7760), // &SHCHc -> &SHCHcy + new Transition (7762, 7770), // &shc -> &shcy + new Transition (7764, 7765), // &shchc -> &shchcy + new Transition (7767, 7768), // &SHc -> &SHcy + new Transition (7933, 7934), // &SOFTc -> &SOFTcy + new Transition (7939, 7940), // &softc -> &softcy + new Transition (8211, 8212), // &succcurl -> &succcurly + new Transition (8419, 8441), // &Tc -> &Tcy + new Transition (8425, 8443), // &tc -> &tcy + new Transition (8487, 8488), // &thetas -> &thetasy + new Transition (8710, 8717), // &tsc -> &tscy + new Transition (8714, 8715), // &TSc -> &TScy + new Transition (8720, 8721), // &TSHc -> &TSHcy + new Transition (8724, 8725), // &tshc -> &tshcy + new Transition (8799, 8800), // &Ubrc -> &Ubrcy + new Transition (8804, 8805), // &ubrc -> &ubrcy + new Transition (8815, 8825), // &Uc -> &Ucy + new Transition (8820, 8827), // &uc -> &ucy + new Transition (9314, 9315), // &Vc -> &Vcy + new Transition (9317, 9318), // &vc -> &vcy + new Transition (9360, 9403), // &Ver -> &Very + new Transition (9674, 9683), // &yac -> &yacy + new Transition (9680, 9681), // &YAc -> &YAcy + new Transition (9685, 9695), // &Yc -> &Ycy + new Transition (9690, 9697), // &yc -> &ycy + new Transition (9709, 9710), // &YIc -> &YIcy + new Transition (9713, 9714), // &yic -> &yicy + new Transition (9733, 9734), // &YUc -> &YUcy + new Transition (9737, 9738), // &yuc -> &yucy + new Transition (9761, 9773), // &Zc -> &Zcy + new Transition (9767, 9775), // &zc -> &zcy + new Transition (9818, 9819), // &ZHc -> &ZHcy + new Transition (9822, 9823) // &zhc -> &zhcy + }; + TransitionTable_z = new Transition[10] { + new Transition (0, 9754), // & -> &z + new Transition (136, 178), // &ang -> &angz + new Transition (524, 525), // &blacklo -> &blackloz + new Transition (1432, 2097), // &d -> &dz + new Transition (3172, 3173), // &Hori -> &Horiz + new Transition (4422, 4612), // &lo -> &loz + new Transition (7617, 8395), // &s -> &sz + new Transition (8699, 8700), // &trpe -> &trpez + new Transition (9201, 9477), // &v -> &vz + new Transition (9479, 9480) // &vzig -> &vzigz + }; + + NamedEntities = new Dictionary { + [6] = "\u00C1", // Á + [7] = "\u00C1", // Á + [13] = "\u00E1", // á + [14] = "\u00E1", // á + [20] = "\u0102", // Ă + [26] = "\u0103", // ă + [28] = "\u223E", // ∾ + [30] = "\u223F", // ∿ + [32] = "\u223E\u0333", // ∾̳ + [36] = "\u00C2", //  + [37] = "\u00C2", //  + [40] = "\u00E2", // â + [41] = "\u00E2", // â + [44] = "\u00B4", // ´ + [45] = "\u00B4", // ´ + [47] = "\u0410", // А + [49] = "\u0430", // а + [53] = "\u00C6", // Æ + [54] = "\u00C6", // Æ + [58] = "\u00E6", // æ + [59] = "\u00E6", // æ + [61] = "\u2061", // ⁡ + [64] = "\uD835\uDD04", // 𝔄 + [66] = "\uD835\uDD1E", // 𝔞 + [71] = "\u00C0", // À + [72] = "\u00C0", // À + [77] = "\u00E0", // à + [78] = "\u00E0", // à + [85] = "\u2135", // ℵ + [88] = "\u2135", // ℵ + [93] = "\u0391", // Α + [97] = "\u03B1", // α + [102] = "\u0100", // Ā + [107] = "\u0101", // ā + [110] = "\u2A3F", // ⨿ + [112] = "\u0026", // & + [113] = "\u0026", // & + [114] = "\u0026", // & + [115] = "\u0026", // & + [118] = "\u2A53", // ⩓ + [121] = "\u2227", // ∧ + [125] = "\u2A55", // ⩕ + [127] = "\u2A5C", // ⩜ + [133] = "\u2A58", // ⩘ + [135] = "\u2A5A", // ⩚ + [137] = "\u2220", // ∠ + [139] = "\u29A4", // ⦤ + [142] = "\u2220", // ∠ + [146] = "\u2221", // ∡ + [149] = "\u29A8", // ⦨ + [151] = "\u29A9", // ⦩ + [153] = "\u29AA", // ⦪ + [155] = "\u29AB", // ⦫ + [157] = "\u29AC", // ⦬ + [159] = "\u29AD", // ⦭ + [161] = "\u29AE", // ⦮ + [163] = "\u29AF", // ⦯ + [166] = "\u221F", // ∟ + [169] = "\u22BE", // ⊾ + [171] = "\u299D", // ⦝ + [175] = "\u2222", // ∢ + [177] = "\u00C5", // Å + [182] = "\u237C", // ⍼ + [187] = "\u0104", // Ą + [192] = "\u0105", // ą + [195] = "\uD835\uDD38", // 𝔸 + [198] = "\uD835\uDD52", // 𝕒 + [200] = "\u2248", // ≈ + [205] = "\u2A6F", // ⩯ + [207] = "\u2A70", // ⩰ + [209] = "\u224A", // ≊ + [212] = "\u224B", // ≋ + [215] = "\u0027", // ' + [228] = "\u2061", // ⁡ + [233] = "\u2248", // ≈ + [236] = "\u224A", // ≊ + [240] = "\u00C5", // Å + [241] = "\u00C5", // Å + [245] = "\u00E5", // å + [246] = "\u00E5", // å + [250] = "\uD835\uDC9C", // 𝒜 + [254] = "\uD835\uDCB6", // 𝒶 + [259] = "\u2254", // ≔ + [261] = "\u002A", // * + [265] = "\u2248", // ≈ + [268] = "\u224D", // ≍ + [273] = "\u00C3", // à + [274] = "\u00C3", // à + [279] = "\u00E3", // ã + [280] = "\u00E3", // ã + [283] = "\u00C4", // Ä + [284] = "\u00C4", // Ä + [287] = "\u00E4", // ä + [288] = "\u00E4", // ä + [296] = "\u2233", // ∳ + [300] = "\u2A11", // ⨑ + [309] = "\u224C", // ≌ + [317] = "\u03F6", // ϶ + [323] = "\u2035", // ‵ + [327] = "\u223D", // ∽ + [330] = "\u22CD", // ⋍ + [340] = "\u2216", // ∖ + [343] = "\u2AE7", // ⫧ + [348] = "\u22BD", // ⊽ + [352] = "\u2306", // ⌆ + [356] = "\u2305", // ⌅ + [359] = "\u2305", // ⌅ + [363] = "\u23B5", // ⎵ + [368] = "\u23B6", // ⎶ + [373] = "\u224C", // ≌ + [376] = "\u0411", // Б + [378] = "\u0431", // б + [383] = "\u201E", // „ + [389] = "\u2235", // ∵ + [396] = "\u2235", // ∵ + [398] = "\u2235", // ∵ + [404] = "\u29B0", // ⦰ + [408] = "\u03F6", // ϶ + [413] = "\u212C", // ℬ + [422] = "\u212C", // ℬ + [425] = "\u0392", // Β + [428] = "\u03B2", // β + [430] = "\u2136", // ℶ + [435] = "\u226C", // ≬ + [438] = "\uD835\uDD05", // 𝔅 + [441] = "\uD835\uDD1F", // 𝔟 + [447] = "\u22C2", // ⋂ + [451] = "\u25EF", // ◯ + [454] = "\u22C3", // ⋃ + [459] = "\u2A00", // ⨀ + [464] = "\u2A01", // ⨁ + [470] = "\u2A02", // ⨂ + [476] = "\u2A06", // ⨆ + [480] = "\u2605", // ★ + [493] = "\u25BD", // ▽ + [496] = "\u25B3", // △ + [502] = "\u2A04", // ⨄ + [506] = "\u22C1", // ⋁ + [512] = "\u22C0", // ⋀ + [518] = "\u290D", // ⤍ + [530] = "\u29EB", // ⧫ + [537] = "\u25AA", // ▪ + [546] = "\u25B4", // ▴ + [551] = "\u25BE", // ▾ + [556] = "\u25C2", // ◂ + [562] = "\u25B8", // ▸ + [565] = "\u2423", // ␣ + [569] = "\u2592", // ▒ + [571] = "\u2591", // ░ + [574] = "\u2593", // ▓ + [578] = "\u2588", // █ + [581] = "\u003D\u20E5", // =⃥ + [586] = "\u2261\u20E5", // ≡⃥ + [590] = "\u2AED", // ⫭ + [593] = "\u2310", // ⌐ + [597] = "\uD835\uDD39", // 𝔹 + [601] = "\uD835\uDD53", // 𝕓 + [603] = "\u22A5", // ⊥ + [607] = "\u22A5", // ⊥ + [612] = "\u22C8", // ⋈ + [617] = "\u29C9", // ⧉ + [620] = "\u2557", // ╗ + [622] = "\u2556", // ╖ + [625] = "\u2555", // ╕ + [627] = "\u2510", // ┐ + [629] = "\u2554", // ╔ + [631] = "\u2553", // ╓ + [633] = "\u2552", // ╒ + [635] = "\u250C", // ┌ + [637] = "\u2550", // ═ + [639] = "\u2500", // ─ + [641] = "\u2566", // ╦ + [643] = "\u2564", // ╤ + [645] = "\u2565", // ╥ + [647] = "\u252C", // ┬ + [649] = "\u2569", // ╩ + [651] = "\u2567", // ╧ + [653] = "\u2568", // ╨ + [655] = "\u2534", // ┴ + [661] = "\u229F", // ⊟ + [666] = "\u229E", // ⊞ + [672] = "\u22A0", // ⊠ + [675] = "\u255D", // ╝ + [677] = "\u255C", // ╜ + [680] = "\u255B", // ╛ + [682] = "\u2518", // ┘ + [684] = "\u255A", // ╚ + [686] = "\u2559", // ╙ + [688] = "\u2558", // ╘ + [690] = "\u2514", // └ + [692] = "\u2551", // ║ + [694] = "\u2502", // │ + [696] = "\u256C", // ╬ + [698] = "\u256B", // ╫ + [700] = "\u256A", // ╪ + [702] = "\u253C", // ┼ + [704] = "\u2563", // ╣ + [706] = "\u2562", // ╢ + [708] = "\u2561", // ╡ + [710] = "\u2524", // ┤ + [712] = "\u2560", // ╠ + [714] = "\u255F", // ╟ + [716] = "\u255E", // ╞ + [718] = "\u251C", // ├ + [724] = "\u2035", // ‵ + [729] = "\u02D8", // ˘ + [734] = "\u02D8", // ˘ + [738] = "\u00A6", // ¦ + [739] = "\u00A6", // ¦ + [743] = "\u212C", // ℬ + [747] = "\uD835\uDCB7", // 𝒷 + [751] = "\u204F", // ⁏ + [754] = "\u223D", // ∽ + [756] = "\u22CD", // ⋍ + [759] = "\u005C", // \ + [761] = "\u29C5", // ⧅ + [766] = "\u27C8", // ⟈ + [770] = "\u2022", // • + [773] = "\u2022", // • + [776] = "\u224E", // ≎ + [778] = "\u2AAE", // ⪮ + [780] = "\u224F", // ≏ + [786] = "\u224E", // ≎ + [788] = "\u224F", // ≏ + [795] = "\u0106", // Ć + [802] = "\u0107", // ć + [804] = "\u22D2", // ⋒ + [806] = "\u2229", // ∩ + [810] = "\u2A44", // ⩄ + [816] = "\u2A49", // ⩉ + [820] = "\u2A4B", // ⩋ + [823] = "\u2A47", // ⩇ + [827] = "\u2A40", // ⩀ + [845] = "\u2145", // ⅅ + [847] = "\u2229\uFE00", // ∩︀ + [851] = "\u2041", // ⁁ + [854] = "\u02C7", // ˇ + [860] = "\u212D", // ℭ + [865] = "\u2A4D", // ⩍ + [871] = "\u010C", // Č + [875] = "\u010D", // č + [879] = "\u00C7", // Ç + [880] = "\u00C7", // Ç + [884] = "\u00E7", // ç + [885] = "\u00E7", // ç + [889] = "\u0108", // Ĉ + [893] = "\u0109", // ĉ + [899] = "\u2230", // ∰ + [903] = "\u2A4C", // ⩌ + [906] = "\u2A50", // ⩐ + [910] = "\u010A", // Ċ + [914] = "\u010B", // ċ + [918] = "\u00B8", // ¸ + [919] = "\u00B8", // ¸ + [926] = "\u00B8", // ¸ + [932] = "\u29B2", // ⦲ + [934] = "\u00A2", // ¢ + [935] = "\u00A2", // ¢ + [943] = "\u00B7", // · + [949] = "\u00B7", // · + [952] = "\u212D", // ℭ + [955] = "\uD835\uDD20", // 𝔠 + [959] = "\u0427", // Ч + [963] = "\u0447", // ч + [967] = "\u2713", // ✓ + [972] = "\u2713", // ✓ + [975] = "\u03A7", // Χ + [977] = "\u03C7", // χ + [980] = "\u25CB", // ○ + [982] = "\u02C6", // ˆ + [985] = "\u2257", // ≗ + [997] = "\u21BA", // ↺ + [1003] = "\u21BB", // ↻ + [1008] = "\u229B", // ⊛ + [1013] = "\u229A", // ⊚ + [1018] = "\u229D", // ⊝ + [1027] = "\u2299", // ⊙ + [1029] = "\u00AE", // ® + [1031] = "\u24C8", // Ⓢ + [1037] = "\u2296", // ⊖ + [1042] = "\u2295", // ⊕ + [1048] = "\u2297", // ⊗ + [1050] = "\u29C3", // ⧃ + [1052] = "\u2257", // ≗ + [1058] = "\u2A10", // ⨐ + [1062] = "\u2AEF", // ⫯ + [1067] = "\u29C2", // ⧂ + [1091] = "\u2232", // ∲ + [1110] = "\u201D", // ” + [1116] = "\u2019", // ’ + [1121] = "\u2663", // ♣ + [1125] = "\u2663", // ♣ + [1130] = "\u2237", // ∷ + [1135] = "\u003A", // : + [1137] = "\u2A74", // ⩴ + [1139] = "\u2254", // ≔ + [1141] = "\u2254", // ≔ + [1145] = "\u002C", // , + [1147] = "\u0040", // @ + [1149] = "\u2201", // ∁ + [1152] = "\u2218", // ∘ + [1159] = "\u2201", // ∁ + [1163] = "\u2102", // ℂ + [1166] = "\u2245", // ≅ + [1170] = "\u2A6D", // ⩭ + [1178] = "\u2261", // ≡ + [1182] = "\u222F", // ∯ + [1186] = "\u222E", // ∮ + [1199] = "\u222E", // ∮ + [1202] = "\u2102", // ℂ + [1205] = "\uD835\uDD54", // 𝕔 + [1209] = "\u2210", // ∐ + [1216] = "\u2210", // ∐ + [1219] = "\u00A9", // © + [1220] = "\u00A9", // © + [1221] = "\u00A9", // © + [1222] = "\u00A9", // © + [1225] = "\u2117", // ℗ + [1255] = "\u2233", // ∳ + [1260] = "\u21B5", // ↵ + [1265] = "\u2A2F", // ⨯ + [1269] = "\u2717", // ✗ + [1273] = "\uD835\uDC9E", // 𝒞 + [1277] = "\uD835\uDCB8", // 𝒸 + [1280] = "\u2ACF", // ⫏ + [1282] = "\u2AD1", // ⫑ + [1284] = "\u2AD0", // ⫐ + [1286] = "\u2AD2", // ⫒ + [1291] = "\u22EF", // ⋯ + [1298] = "\u2938", // ⤸ + [1300] = "\u2935", // ⤵ + [1304] = "\u22DE", // ⋞ + [1307] = "\u22DF", // ⋟ + [1312] = "\u21B6", // ↶ + [1314] = "\u293D", // ⤽ + [1317] = "\u22D3", // ⋓ + [1319] = "\u222A", // ∪ + [1325] = "\u2A48", // ⩈ + [1329] = "\u224D", // ≍ + [1333] = "\u2A46", // ⩆ + [1336] = "\u2A4A", // ⩊ + [1340] = "\u228D", // ⊍ + [1343] = "\u2A45", // ⩅ + [1345] = "\u222A\uFE00", // ∪︀ + [1350] = "\u21B7", // ↷ + [1352] = "\u293C", // ⤼ + [1361] = "\u22DE", // ⋞ + [1366] = "\u22DF", // ⋟ + [1370] = "\u22CE", // ⋎ + [1376] = "\u22CF", // ⋏ + [1379] = "\u00A4", // ¤ + [1380] = "\u00A4", // ¤ + [1392] = "\u21B6", // ↶ + [1398] = "\u21B7", // ↷ + [1402] = "\u22CE", // ⋎ + [1406] = "\u22CF", // ⋏ + [1414] = "\u2232", // ∲ + [1418] = "\u2231", // ∱ + [1424] = "\u232D", // ⌭ + [1431] = "\u2021", // ‡ + [1438] = "\u2020", // † + [1443] = "\u2138", // ℸ + [1446] = "\u21A1", // ↡ + [1450] = "\u21D3", // ⇓ + [1453] = "\u2193", // ↓ + [1456] = "\u2010", // ‐ + [1460] = "\u2AE4", // ⫤ + [1462] = "\u22A3", // ⊣ + [1469] = "\u290F", // ⤏ + [1473] = "\u02DD", // ˝ + [1479] = "\u010E", // Ď + [1485] = "\u010F", // ď + [1487] = "\u0414", // Д + [1489] = "\u0434", // д + [1491] = "\u2145", // ⅅ + [1493] = "\u2146", // ⅆ + [1499] = "\u2021", // ‡ + [1502] = "\u21CA", // ⇊ + [1509] = "\u2911", // ⤑ + [1515] = "\u2A77", // ⩷ + [1517] = "\u00B0", // ° + [1518] = "\u00B0", // ° + [1521] = "\u2207", // ∇ + [1524] = "\u0394", // Δ + [1528] = "\u03B4", // δ + [1534] = "\u29B1", // ⦱ + [1540] = "\u297F", // ⥿ + [1543] = "\uD835\uDD07", // 𝔇 + [1545] = "\uD835\uDD21", // 𝔡 + [1549] = "\u2965", // ⥥ + [1554] = "\u21C3", // ⇃ + [1556] = "\u21C2", // ⇂ + [1572] = "\u00B4", // ´ + [1576] = "\u02D9", // ˙ + [1586] = "\u02DD", // ˝ + [1592] = "\u0060", // ` + [1598] = "\u02DC", // ˜ + [1602] = "\u22C4", // ⋄ + [1607] = "\u22C4", // ⋄ + [1611] = "\u22C4", // ⋄ + [1616] = "\u2666", // ♦ + [1618] = "\u2666", // ♦ + [1620] = "\u00A8", // ¨ + [1632] = "\u2146", // ⅆ + [1638] = "\u03DD", // ϝ + [1642] = "\u22F2", // ⋲ + [1644] = "\u00F7", // ÷ + [1647] = "\u00F7", // ÷ + [1648] = "\u00F7", // ÷ + [1656] = "\u22C7", // ⋇ + [1660] = "\u22C7", // ⋇ + [1664] = "\u0402", // Ђ + [1668] = "\u0452", // ђ + [1674] = "\u231E", // ⌞ + [1678] = "\u230D", // ⌍ + [1684] = "\u0024", // $ + [1688] = "\uD835\uDD3B", // 𝔻 + [1691] = "\uD835\uDD55", // 𝕕 + [1693] = "\u00A8", // ¨ + [1695] = "\u02D9", // ˙ + [1699] = "\u20DC", // ⃜ + [1702] = "\u2250", // ≐ + [1706] = "\u2251", // ≑ + [1712] = "\u2250", // ≐ + [1718] = "\u2238", // ∸ + [1723] = "\u2214", // ∔ + [1730] = "\u22A1", // ⊡ + [1743] = "\u2306", // ⌆ + [1763] = "\u222F", // ∯ + [1767] = "\u00A8", // ¨ + [1775] = "\u21D3", // ⇓ + [1785] = "\u21D0", // ⇐ + [1796] = "\u21D4", // ⇔ + [1800] = "\u2AE4", // ⫤ + [1813] = "\u27F8", // ⟸ + [1824] = "\u27FA", // ⟺ + [1835] = "\u27F9", // ⟹ + [1846] = "\u21D2", // ⇒ + [1850] = "\u22A8", // ⊨ + [1858] = "\u21D1", // ⇑ + [1868] = "\u21D5", // ⇕ + [1880] = "\u2225", // ∥ + [1888] = "\u2193", // ↓ + [1894] = "\u21D3", // ⇓ + [1902] = "\u2193", // ↓ + [1906] = "\u2913", // ⤓ + [1914] = "\u21F5", // ⇵ + [1920] = "\u0311", // ̑ + [1931] = "\u21CA", // ⇊ + [1943] = "\u21C3", // ⇃ + [1949] = "\u21C2", // ⇂ + [1965] = "\u2950", // ⥐ + [1975] = "\u295E", // ⥞ + [1982] = "\u21BD", // ↽ + [1986] = "\u2956", // ⥖ + [2001] = "\u295F", // ⥟ + [2008] = "\u21C1", // ⇁ + [2012] = "\u2957", // ⥗ + [2016] = "\u22A4", // ⊤ + [2022] = "\u21A7", // ↧ + [2030] = "\u2910", // ⤐ + [2035] = "\u231F", // ⌟ + [2039] = "\u230C", // ⌌ + [2043] = "\uD835\uDC9F", // 𝒟 + [2047] = "\uD835\uDCB9", // 𝒹 + [2051] = "\u0405", // Ѕ + [2053] = "\u0455", // ѕ + [2056] = "\u29F6", // ⧶ + [2061] = "\u0110", // Đ + [2066] = "\u0111", // đ + [2071] = "\u22F1", // ⋱ + [2074] = "\u25BF", // ▿ + [2076] = "\u25BE", // ▾ + [2081] = "\u21F5", // ⇵ + [2085] = "\u296F", // ⥯ + [2092] = "\u29A6", // ⦦ + [2096] = "\u040F", // Џ + [2100] = "\u045F", // џ + [2107] = "\u27FF", // ⟿ + [2113] = "\u00C9", // É + [2114] = "\u00C9", // É + [2120] = "\u00E9", // é + [2121] = "\u00E9", // é + [2126] = "\u2A6E", // ⩮ + [2132] = "\u011A", // Ě + [2138] = "\u011B", // ě + [2141] = "\u2256", // ≖ + [2144] = "\u00CA", // Ê + [2145] = "\u00CA", // Ê + [2146] = "\u00EA", // ê + [2147] = "\u00EA", // ê + [2152] = "\u2255", // ≕ + [2154] = "\u042D", // Э + [2156] = "\u044D", // э + [2161] = "\u2A77", // ⩷ + [2165] = "\u0116", // Ė + [2168] = "\u2251", // ≑ + [2172] = "\u0117", // ė + [2174] = "\u2147", // ⅇ + [2179] = "\u2252", // ≒ + [2182] = "\uD835\uDD08", // 𝔈 + [2184] = "\uD835\uDD22", // 𝔢 + [2186] = "\u2A9A", // ⪚ + [2191] = "\u00C8", // È + [2192] = "\u00C8", // È + [2196] = "\u00E8", // è + [2197] = "\u00E8", // è + [2199] = "\u2A96", // ⪖ + [2203] = "\u2A98", // ⪘ + [2205] = "\u2A99", // ⪙ + [2212] = "\u2208", // ∈ + [2219] = "\u23E7", // ⏧ + [2221] = "\u2113", // ℓ + [2223] = "\u2A95", // ⪕ + [2227] = "\u2A97", // ⪗ + [2232] = "\u0112", // Ē + [2237] = "\u0113", // ē + [2241] = "\u2205", // ∅ + [2245] = "\u2205", // ∅ + [2260] = "\u25FB", // ◻ + [2262] = "\u2205", // ∅ + [2278] = "\u25AB", // ▫ + [2281] = "\u2003", // + [2284] = "\u2004", //   + [2286] = "\u2005", //   + [2289] = "\u014A", // Ŋ + [2292] = "\u014B", // ŋ + [2295] = "\u2002", // + [2300] = "\u0118", // Ę + [2305] = "\u0119", // ę + [2308] = "\uD835\uDD3C", // 𝔼 + [2311] = "\uD835\uDD56", // 𝕖 + [2315] = "\u22D5", // ⋕ + [2318] = "\u29E3", // ⧣ + [2322] = "\u2A71", // ⩱ + [2325] = "\u03B5", // ε + [2332] = "\u0395", // Ε + [2336] = "\u03B5", // ε + [2338] = "\u03F5", // ϵ + [2344] = "\u2256", // ≖ + [2349] = "\u2255", // ≕ + [2353] = "\u2242", // ≂ + [2361] = "\u2A96", // ⪖ + [2366] = "\u2A95", // ⪕ + [2371] = "\u2A75", // ⩵ + [2376] = "\u003D", // = + [2382] = "\u2242", // ≂ + [2386] = "\u225F", // ≟ + [2395] = "\u21CC", // ⇌ + [2398] = "\u2261", // ≡ + [2401] = "\u2A78", // ⩸ + [2408] = "\u29E5", // ⧥ + [2413] = "\u2971", // ⥱ + [2417] = "\u2253", // ≓ + [2421] = "\u2130", // ℰ + [2425] = "\u212F", // ℯ + [2429] = "\u2250", // ≐ + [2432] = "\u2A73", // ⩳ + [2435] = "\u2242", // ≂ + [2438] = "\u0397", // Η + [2441] = "\u03B7", // η + [2443] = "\u00D0", // Ð + [2444] = "\u00D0", // Ð + [2445] = "\u00F0", // ð + [2446] = "\u00F0", // ð + [2449] = "\u00CB", // Ë + [2450] = "\u00CB", // Ë + [2453] = "\u00EB", // ë + [2454] = "\u00EB", // ë + [2457] = "\u20AC", // € + [2461] = "\u0021", // ! + [2465] = "\u2203", // ∃ + [2471] = "\u2203", // ∃ + [2481] = "\u2130", // ℰ + [2492] = "\u2147", // ⅇ + [2502] = "\u2147", // ⅇ + [2516] = "\u2252", // ≒ + [2520] = "\u0424", // Ф + [2523] = "\u0444", // ф + [2529] = "\u2640", // ♀ + [2535] = "\uFB03", // ffi + [2539] = "\uFB00", // ff + [2543] = "\uFB04", // ffl + [2546] = "\uD835\uDD09", // 𝔉 + [2548] = "\uD835\uDD23", // 𝔣 + [2553] = "\uFB01", // fi + [2570] = "\u25FC", // ◼ + [2586] = "\u25AA", // ▪ + [2591] = "\u0066\u006A", // fj + [2595] = "\u266D", // ♭ + [2599] = "\uFB02", // fl + [2603] = "\u25B1", // ▱ + [2607] = "\u0192", // ƒ + [2611] = "\uD835\uDD3D", // 𝔽 + [2615] = "\uD835\uDD57", // 𝕗 + [2620] = "\u2200", // ∀ + [2625] = "\u2200", // ∀ + [2627] = "\u22D4", // ⋔ + [2629] = "\u2AD9", // ⫙ + [2638] = "\u2131", // ℱ + [2646] = "\u2A0D", // ⨍ + [2651] = "\u00BD", // ½ + [2652] = "\u00BD", // ½ + [2654] = "\u2153", // ⅓ + [2655] = "\u00BC", // ¼ + [2656] = "\u00BC", // ¼ + [2658] = "\u2155", // ⅕ + [2660] = "\u2159", // ⅙ + [2662] = "\u215B", // ⅛ + [2665] = "\u2154", // ⅔ + [2667] = "\u2156", // ⅖ + [2669] = "\u00BE", // ¾ + [2670] = "\u00BE", // ¾ + [2672] = "\u2157", // ⅗ + [2674] = "\u215C", // ⅜ + [2677] = "\u2158", // ⅘ + [2680] = "\u215A", // ⅚ + [2682] = "\u215D", // ⅝ + [2685] = "\u215E", // ⅞ + [2688] = "\u2044", // ⁄ + [2692] = "\u2322", // ⌢ + [2696] = "\u2131", // ℱ + [2700] = "\uD835\uDCBB", // 𝒻 + [2707] = "\u01F5", // ǵ + [2713] = "\u0393", // Γ + [2717] = "\u03B3", // γ + [2719] = "\u03DC", // Ϝ + [2721] = "\u03DD", // ϝ + [2723] = "\u2A86", // ⪆ + [2729] = "\u011E", // Ğ + [2735] = "\u011F", // ğ + [2741] = "\u0122", // Ģ + [2745] = "\u011C", // Ĝ + [2750] = "\u011D", // ĝ + [2752] = "\u0413", // Г + [2754] = "\u0433", // г + [2758] = "\u0120", // Ġ + [2762] = "\u0121", // ġ + [2764] = "\u2267", // ≧ + [2766] = "\u2265", // ≥ + [2768] = "\u2A8C", // ⪌ + [2770] = "\u22DB", // ⋛ + [2772] = "\u2265", // ≥ + [2774] = "\u2267", // ≧ + [2780] = "\u2A7E", // ⩾ + [2782] = "\u2A7E", // ⩾ + [2785] = "\u2AA9", // ⪩ + [2789] = "\u2A80", // ⪀ + [2791] = "\u2A82", // ⪂ + [2793] = "\u2A84", // ⪄ + [2795] = "\u22DB\uFE00", // ⋛︀ + [2798] = "\u2A94", // ⪔ + [2801] = "\uD835\uDD0A", // 𝔊 + [2804] = "\uD835\uDD24", // 𝔤 + [2806] = "\u22D9", // ⋙ + [2808] = "\u226B", // ≫ + [2810] = "\u22D9", // ⋙ + [2815] = "\u2137", // ℷ + [2819] = "\u0403", // Ѓ + [2823] = "\u0453", // ѓ + [2825] = "\u2277", // ≷ + [2827] = "\u2AA5", // ⪥ + [2829] = "\u2A92", // ⪒ + [2831] = "\u2AA4", // ⪤ + [2835] = "\u2A8A", // ⪊ + [2840] = "\u2A8A", // ⪊ + [2842] = "\u2269", // ≩ + [2844] = "\u2A88", // ⪈ + [2846] = "\u2A88", // ⪈ + [2848] = "\u2269", // ≩ + [2852] = "\u22E7", // ⋧ + [2856] = "\uD835\uDD3E", // 𝔾 + [2860] = "\uD835\uDD58", // 𝕘 + [2865] = "\u0060", // ` + [2877] = "\u2265", // ≥ + [2882] = "\u22DB", // ⋛ + [2892] = "\u2267", // ≧ + [2900] = "\u2AA2", // ⪢ + [2905] = "\u2277", // ≷ + [2916] = "\u2A7E", // ⩾ + [2922] = "\u2273", // ≳ + [2926] = "\uD835\uDCA2", // 𝒢 + [2930] = "\u210A", // ℊ + [2933] = "\u2273", // ≳ + [2935] = "\u2A8E", // ⪎ + [2937] = "\u2A90", // ⪐ + [2938] = "\u003E", // > + [2939] = "\u003E", // > + [2941] = "\u226B", // ≫ + [2942] = "\u003E", // > + [2943] = "\u003E", // > + [2946] = "\u2AA7", // ⪧ + [2949] = "\u2A7A", // ⩺ + [2953] = "\u22D7", // ⋗ + [2958] = "\u2995", // ⦕ + [2964] = "\u2A7C", // ⩼ + [2972] = "\u2A86", // ⪆ + [2975] = "\u2978", // ⥸ + [2979] = "\u22D7", // ⋗ + [2986] = "\u22DB", // ⋛ + [2992] = "\u2A8C", // ⪌ + [2997] = "\u2277", // ≷ + [3001] = "\u2273", // ≳ + [3010] = "\u2269\uFE00", // ≩︀ + [3013] = "\u2269\uFE00", // ≩︀ + [3019] = "\u02C7", // ˇ + [3026] = "\u200A", //   + [3029] = "\u00BD", // ½ + [3034] = "\u210B", // ℋ + [3040] = "\u042A", // Ъ + [3045] = "\u044A", // ъ + [3049] = "\u21D4", // ⇔ + [3051] = "\u2194", // ↔ + [3055] = "\u2948", // ⥈ + [3057] = "\u21AD", // ↭ + [3059] = "\u005E", // ^ + [3063] = "\u210F", // ℏ + [3068] = "\u0124", // Ĥ + [3073] = "\u0125", // ĥ + [3079] = "\u2665", // ♥ + [3083] = "\u2665", // ♥ + [3088] = "\u2026", // … + [3093] = "\u22B9", // ⊹ + [3096] = "\u210C", // ℌ + [3099] = "\uD835\uDD25", // 𝔥 + [3111] = "\u210B", // ℋ + [3119] = "\u2925", // ⤥ + [3125] = "\u2926", // ⤦ + [3130] = "\u21FF", // ⇿ + [3135] = "\u223B", // ∻ + [3147] = "\u21A9", // ↩ + [3158] = "\u21AA", // ↪ + [3162] = "\u210D", // ℍ + [3165] = "\uD835\uDD59", // 𝕙 + [3170] = "\u2015", // ― + [3183] = "\u2500", // ─ + [3187] = "\u210B", // ℋ + [3191] = "\uD835\uDCBD", // 𝒽 + [3196] = "\u210F", // ℏ + [3201] = "\u0126", // Ħ + [3206] = "\u0127", // ħ + [3218] = "\u224E", // ≎ + [3224] = "\u224F", // ≏ + [3230] = "\u2043", // ⁃ + [3235] = "\u2010", // ‐ + [3241] = "\u00CD", // Í + [3242] = "\u00CD", // Í + [3248] = "\u00ED", // í + [3249] = "\u00ED", // í + [3251] = "\u2063", // ⁣ + [3255] = "\u00CE", // Î + [3256] = "\u00CE", // Î + [3259] = "\u00EE", // î + [3260] = "\u00EE", // î + [3262] = "\u0418", // И + [3264] = "\u0438", // и + [3268] = "\u0130", // İ + [3272] = "\u0415", // Е + [3276] = "\u0435", // е + [3279] = "\u00A1", // ¡ + [3280] = "\u00A1", // ¡ + [3283] = "\u21D4", // ⇔ + [3286] = "\u2111", // ℑ + [3288] = "\uD835\uDD26", // 𝔦 + [3293] = "\u00CC", // Ì + [3294] = "\u00CC", // Ì + [3299] = "\u00EC", // ì + [3300] = "\u00EC", // ì + [3302] = "\u2148", // ⅈ + [3307] = "\u2A0C", // ⨌ + [3310] = "\u222D", // ∭ + [3315] = "\u29DC", // ⧜ + [3319] = "\u2129", // ℩ + [3324] = "\u0132", // IJ + [3329] = "\u0133", // ij + [3331] = "\u2111", // ℑ + [3335] = "\u012A", // Ī + [3340] = "\u012B", // ī + [3343] = "\u2111", // ℑ + [3351] = "\u2148", // ⅈ + [3356] = "\u2110", // ℐ + [3361] = "\u2111", // ℑ + [3364] = "\u0131", // ı + [3367] = "\u22B7", // ⊷ + [3371] = "\u01B5", // Ƶ + [3377] = "\u21D2", // ⇒ + [3379] = "\u2208", // ∈ + [3384] = "\u2105", // ℅ + [3388] = "\u221E", // ∞ + [3392] = "\u29DD", // ⧝ + [3397] = "\u0131", // ı + [3400] = "\u222C", // ∬ + [3402] = "\u222B", // ∫ + [3406] = "\u22BA", // ⊺ + [3412] = "\u2124", // ℤ + [3418] = "\u222B", // ∫ + [3423] = "\u22BA", // ⊺ + [3432] = "\u22C2", // ⋂ + [3438] = "\u2A17", // ⨗ + [3443] = "\u2A3C", // ⨼ + [3456] = "\u2063", // ⁣ + [3462] = "\u2062", // ⁢ + [3466] = "\u0401", // Ё + [3470] = "\u0451", // ё + [3475] = "\u012E", // Į + [3479] = "\u012F", // į + [3482] = "\uD835\uDD40", // 𝕀 + [3485] = "\uD835\uDD5A", // 𝕚 + [3488] = "\u0399", // Ι + [3491] = "\u03B9", // ι + [3496] = "\u2A3C", // ⨼ + [3501] = "\u00BF", // ¿ + [3502] = "\u00BF", // ¿ + [3506] = "\u2110", // ℐ + [3510] = "\uD835\uDCBE", // 𝒾 + [3513] = "\u2208", // ∈ + [3517] = "\u22F5", // ⋵ + [3519] = "\u22F9", // ⋹ + [3521] = "\u22F4", // ⋴ + [3523] = "\u22F3", // ⋳ + [3525] = "\u2208", // ∈ + [3527] = "\u2062", // ⁢ + [3533] = "\u0128", // Ĩ + [3538] = "\u0129", // ĩ + [3543] = "\u0406", // І + [3548] = "\u0456", // і + [3550] = "\u00CF", // Ï + [3551] = "\u00CF", // Ï + [3553] = "\u00EF", // ï + [3554] = "\u00EF", // ï + [3560] = "\u0134", // Ĵ + [3566] = "\u0135", // ĵ + [3568] = "\u0419", // Й + [3570] = "\u0439", // й + [3573] = "\uD835\uDD0D", // 𝔍 + [3576] = "\uD835\uDD27", // 𝔧 + [3581] = "\u0237", // ȷ + [3585] = "\uD835\uDD41", // 𝕁 + [3589] = "\uD835\uDD5B", // 𝕛 + [3593] = "\uD835\uDCA5", // 𝒥 + [3597] = "\uD835\uDCBF", // 𝒿 + [3602] = "\u0408", // Ј + [3607] = "\u0458", // ј + [3612] = "\u0404", // Є + [3617] = "\u0454", // є + [3623] = "\u039A", // Κ + [3629] = "\u03BA", // κ + [3631] = "\u03F0", // ϰ + [3637] = "\u0136", // Ķ + [3643] = "\u0137", // ķ + [3645] = "\u041A", // К + [3647] = "\u043A", // к + [3650] = "\uD835\uDD0E", // 𝔎 + [3653] = "\uD835\uDD28", // 𝔨 + [3659] = "\u0138", // ĸ + [3663] = "\u0425", // Х + [3667] = "\u0445", // х + [3671] = "\u040C", // Ќ + [3675] = "\u045C", // ќ + [3679] = "\uD835\uDD42", // 𝕂 + [3683] = "\uD835\uDD5C", // 𝕜 + [3687] = "\uD835\uDCA6", // 𝒦 + [3691] = "\uD835\uDCC0", // 𝓀 + [3697] = "\u21DA", // ⇚ + [3704] = "\u0139", // Ĺ + [3710] = "\u013A", // ĺ + [3717] = "\u29B4", // ⦴ + [3722] = "\u2112", // ℒ + [3727] = "\u039B", // Λ + [3732] = "\u03BB", // λ + [3735] = "\u27EA", // ⟪ + [3738] = "\u27E8", // 〈 + [3740] = "\u2991", // ⦑ + [3743] = "\u27E8", // ⟨ + [3745] = "\u2A85", // ⪅ + [3754] = "\u2112", // ℒ + [3757] = "\u00AB", // « + [3758] = "\u00AB", // « + [3761] = "\u219E", // ↞ + [3764] = "\u21D0", // ⇐ + [3767] = "\u2190", // ← + [3769] = "\u21E4", // ⇤ + [3772] = "\u291F", // ⤟ + [3775] = "\u291D", // ⤝ + [3778] = "\u21A9", // ↩ + [3781] = "\u21AB", // ↫ + [3784] = "\u2939", // ⤹ + [3788] = "\u2973", // ⥳ + [3791] = "\u21A2", // ↢ + [3793] = "\u2AAB", // ⪫ + [3798] = "\u291B", // ⤛ + [3802] = "\u2919", // ⤙ + [3804] = "\u2AAD", // ⪭ + [3806] = "\u2AAD\uFE00", // ⪭︀ + [3811] = "\u290E", // ⤎ + [3816] = "\u290C", // ⤌ + [3820] = "\u2772", // ❲ + [3825] = "\u007B", // { + [3827] = "\u005B", // [ + [3830] = "\u298B", // ⦋ + [3834] = "\u298F", // ⦏ + [3836] = "\u298D", // ⦍ + [3842] = "\u013D", // Ľ + [3848] = "\u013E", // ľ + [3853] = "\u013B", // Ļ + [3858] = "\u013C", // ļ + [3861] = "\u2308", // ⌈ + [3864] = "\u007B", // { + [3866] = "\u041B", // Л + [3868] = "\u043B", // л + [3872] = "\u2936", // ⤶ + [3876] = "\u201C", // “ + [3878] = "\u201E", // „ + [3884] = "\u2967", // ⥧ + [3890] = "\u294B", // ⥋ + [3893] = "\u21B2", // ↲ + [3895] = "\u2266", // ≦ + [3897] = "\u2264", // ≤ + [3913] = "\u27E8", // ⟨ + [3918] = "\u2190", // ← + [3924] = "\u21D0", // ⇐ + [3932] = "\u2190", // ← + [3936] = "\u21E4", // ⇤ + [3947] = "\u21C6", // ⇆ + [3952] = "\u21A2", // ↢ + [3960] = "\u2308", // ⌈ + [3974] = "\u27E6", // ⟦ + [3986] = "\u2961", // ⥡ + [3993] = "\u21C3", // ⇃ + [3997] = "\u2959", // ⥙ + [4003] = "\u230A", // ⌊ + [4015] = "\u21BD", // ↽ + [4018] = "\u21BC", // ↼ + [4029] = "\u21C7", // ⇇ + [4040] = "\u2194", // ↔ + [4051] = "\u21D4", // ⇔ + [4062] = "\u2194", // ↔ + [4064] = "\u21C6", // ⇆ + [4073] = "\u21CB", // ⇋ + [4084] = "\u21AD", // ↭ + [4091] = "\u294E", // ⥎ + [4095] = "\u22A3", // ⊣ + [4101] = "\u21A4", // ↤ + [4108] = "\u295A", // ⥚ + [4119] = "\u22CB", // ⋋ + [4127] = "\u22B2", // ⊲ + [4131] = "\u29CF", // ⧏ + [4137] = "\u22B4", // ⊴ + [4150] = "\u2951", // ⥑ + [4160] = "\u2960", // ⥠ + [4167] = "\u21BF", // ↿ + [4171] = "\u2958", // ⥘ + [4178] = "\u21BC", // ↼ + [4182] = "\u2952", // ⥒ + [4184] = "\u2A8B", // ⪋ + [4186] = "\u22DA", // ⋚ + [4188] = "\u2264", // ≤ + [4190] = "\u2266", // ≦ + [4196] = "\u2A7D", // ⩽ + [4198] = "\u2A7D", // ⩽ + [4201] = "\u2AA8", // ⪨ + [4205] = "\u2A7F", // ⩿ + [4207] = "\u2A81", // ⪁ + [4209] = "\u2A83", // ⪃ + [4211] = "\u22DA\uFE00", // ⋚︀ + [4214] = "\u2A93", // ⪓ + [4222] = "\u2A85", // ⪅ + [4226] = "\u22D6", // ⋖ + [4232] = "\u22DA", // ⋚ + [4237] = "\u2A8B", // ⪋ + [4252] = "\u22DA", // ⋚ + [4262] = "\u2266", // ≦ + [4270] = "\u2276", // ≶ + [4274] = "\u2276", // ≶ + [4279] = "\u2AA1", // ⪡ + [4283] = "\u2272", // ≲ + [4294] = "\u2A7D", // ⩽ + [4300] = "\u2272", // ≲ + [4306] = "\u297C", // ⥼ + [4311] = "\u230A", // ⌊ + [4314] = "\uD835\uDD0F", // 𝔏 + [4316] = "\uD835\uDD29", // 𝔩 + [4318] = "\u2276", // ≶ + [4320] = "\u2A91", // ⪑ + [4324] = "\u2962", // ⥢ + [4329] = "\u21BD", // ↽ + [4331] = "\u21BC", // ↼ + [4333] = "\u296A", // ⥪ + [4337] = "\u2584", // ▄ + [4341] = "\u0409", // Љ + [4345] = "\u0459", // љ + [4347] = "\u22D8", // ⋘ + [4349] = "\u226A", // ≪ + [4353] = "\u21C7", // ⇇ + [4360] = "\u231E", // ⌞ + [4369] = "\u21DA", // ⇚ + [4374] = "\u296B", // ⥫ + [4378] = "\u25FA", // ◺ + [4384] = "\u013F", // Ŀ + [4390] = "\u0140", // ŀ + [4395] = "\u23B0", // ⎰ + [4400] = "\u23B0", // ⎰ + [4404] = "\u2A89", // ⪉ + [4409] = "\u2A89", // ⪉ + [4411] = "\u2268", // ≨ + [4413] = "\u2A87", // ⪇ + [4415] = "\u2A87", // ⪇ + [4417] = "\u2268", // ≨ + [4421] = "\u22E6", // ⋦ + [4426] = "\u27EC", // ⟬ + [4429] = "\u21FD", // ⇽ + [4433] = "\u27E6", // ⟦ + [4446] = "\u27F5", // ⟵ + [4456] = "\u27F8", // ⟸ + [4468] = "\u27F5", // ⟵ + [4479] = "\u27F7", // ⟷ + [4490] = "\u27FA", // ⟺ + [4501] = "\u27F7", // ⟷ + [4508] = "\u27FC", // ⟼ + [4519] = "\u27F6", // ⟶ + [4530] = "\u27F9", // ⟹ + [4541] = "\u27F6", // ⟶ + [4553] = "\u21AB", // ↫ + [4559] = "\u21AC", // ↬ + [4563] = "\u2985", // ⦅ + [4566] = "\uD835\uDD43", // 𝕃 + [4568] = "\uD835\uDD5D", // 𝕝 + [4572] = "\u2A2D", // ⨭ + [4578] = "\u2A34", // ⨴ + [4583] = "\u2217", // ∗ + [4587] = "\u005F", // _ + [4600] = "\u2199", // ↙ + [4611] = "\u2198", // ↘ + [4613] = "\u25CA", // ◊ + [4618] = "\u25CA", // ◊ + [4620] = "\u29EB", // ⧫ + [4624] = "\u0028", // ( + [4627] = "\u2993", // ⦓ + [4632] = "\u21C6", // ⇆ + [4639] = "\u231F", // ⌟ + [4643] = "\u21CB", // ⇋ + [4645] = "\u296D", // ⥭ + [4647] = "\u200E", // + [4651] = "\u22BF", // ⊿ + [4657] = "\u2039", // ‹ + [4661] = "\u2112", // ℒ + [4664] = "\uD835\uDCC1", // 𝓁 + [4666] = "\u21B0", // ↰ + [4668] = "\u21B0", // ↰ + [4671] = "\u2272", // ≲ + [4673] = "\u2A8D", // ⪍ + [4675] = "\u2A8F", // ⪏ + [4678] = "\u005B", // [ + [4681] = "\u2018", // ‘ + [4683] = "\u201A", // ‚ + [4688] = "\u0141", // Ł + [4693] = "\u0142", // ł + [4694] = "\u003C", // < + [4695] = "\u003C", // < + [4697] = "\u226A", // ≪ + [4698] = "\u003C", // < + [4699] = "\u003C", // < + [4702] = "\u2AA6", // ⪦ + [4705] = "\u2A79", // ⩹ + [4709] = "\u22D6", // ⋖ + [4714] = "\u22CB", // ⋋ + [4719] = "\u22C9", // ⋉ + [4724] = "\u2976", // ⥶ + [4730] = "\u2A7B", // ⩻ + [4733] = "\u25C3", // ◃ + [4735] = "\u22B4", // ⊴ + [4737] = "\u25C2", // ◂ + [4741] = "\u2996", // ⦖ + [4749] = "\u294A", // ⥊ + [4754] = "\u2966", // ⥦ + [4763] = "\u2268\uFE00", // ≨︀ + [4766] = "\u2268\uFE00", // ≨︀ + [4770] = "\u00AF", // ¯ + [4771] = "\u00AF", // ¯ + [4774] = "\u2642", // ♂ + [4776] = "\u2720", // ✠ + [4780] = "\u2720", // ✠ + [4784] = "\u2905", // ⤅ + [4786] = "\u21A6", // ↦ + [4790] = "\u21A6", // ↦ + [4795] = "\u21A7", // ↧ + [4800] = "\u21A4", // ↤ + [4803] = "\u21A5", // ↥ + [4808] = "\u25AE", // ▮ + [4814] = "\u2A29", // ⨩ + [4817] = "\u041C", // М + [4819] = "\u043C", // м + [4824] = "\u2014", // — + [4829] = "\u223A", // ∺ + [4842] = "\u2221", // ∡ + [4853] = "\u205F", //   + [4861] = "\u2133", // ℳ + [4864] = "\uD835\uDD10", // 𝔐 + [4867] = "\uD835\uDD2A", // 𝔪 + [4870] = "\u2127", // ℧ + [4874] = "\u00B5", // µ + [4875] = "\u00B5", // µ + [4877] = "\u2223", // ∣ + [4881] = "\u002A", // * + [4885] = "\u2AF0", // ⫰ + [4888] = "\u00B7", // · + [4889] = "\u00B7", // · + [4893] = "\u2212", // − + [4895] = "\u229F", // ⊟ + [4897] = "\u2238", // ∸ + [4899] = "\u2A2A", // ⨪ + [4908] = "\u2213", // ∓ + [4912] = "\u2ADB", // ⫛ + [4915] = "\u2026", // … + [4921] = "\u2213", // ∓ + [4927] = "\u22A7", // ⊧ + [4931] = "\uD835\uDD44", // 𝕄 + [4934] = "\uD835\uDD5E", // 𝕞 + [4936] = "\u2213", // ∓ + [4940] = "\u2133", // ℳ + [4944] = "\uD835\uDCC2", // 𝓂 + [4949] = "\u223E", // ∾ + [4951] = "\u039C", // Μ + [4953] = "\u03BC", // μ + [4960] = "\u22B8", // ⊸ + [4964] = "\u22B8", // ⊸ + [4970] = "\u2207", // ∇ + [4977] = "\u0143", // Ń + [4982] = "\u0144", // ń + [4985] = "\u2220\u20D2", // ∠⃒ + [4987] = "\u2249", // ≉ + [4989] = "\u2A70\u0338", // ⩰̸ + [4992] = "\u224B\u0338", // ≋̸ + [4995] = "\u0149", // ʼn + [5000] = "\u2249", // ≉ + [5004] = "\u266E", // ♮ + [5007] = "\u266E", // ♮ + [5009] = "\u2115", // ℕ + [5012] = "\u00A0", //   + [5013] = "\u00A0", // + [5017] = "\u224E\u0338", // ≎̸ + [5019] = "\u224F\u0338", // ≏̸ + [5023] = "\u2A43", // ⩃ + [5029] = "\u0147", // Ň + [5033] = "\u0148", // ň + [5038] = "\u0145", // Ņ + [5043] = "\u0146", // ņ + [5047] = "\u2247", // ≇ + [5051] = "\u2A6D\u0338", // ⩭̸ + [5054] = "\u2A42", // ⩂ + [5056] = "\u041D", // Н + [5058] = "\u043D", // н + [5063] = "\u2013", // – + [5065] = "\u2260", // ≠ + [5070] = "\u2924", // ⤤ + [5074] = "\u21D7", // ⇗ + [5076] = "\u2197", // ↗ + [5079] = "\u2197", // ↗ + [5083] = "\u2250\u0338", // ≐̸ + [5102] = "\u200B", // ​ + [5113] = "\u200B", // ​ + [5120] = "\u200B", // ​ + [5134] = "\u200B", // ​ + [5139] = "\u2262", // ≢ + [5144] = "\u2928", // ⤨ + [5147] = "\u2242\u0338", // ≂̸ + [5166] = "\u226B", // ≫ + [5175] = "\u226A", // ≪ + [5181] = "\u000A", // 
 + [5186] = "\u2204", // ∄ + [5188] = "\u2204", // ∄ + [5191] = "\uD835\uDD11", // 𝔑 + [5194] = "\uD835\uDD2B", // 𝔫 + [5197] = "\u2267\u0338", // ≧̸ + [5199] = "\u2271", // ≱ + [5201] = "\u2271", // ≱ + [5203] = "\u2267\u0338", // ≧̸ + [5209] = "\u2A7E\u0338", // ⩾̸ + [5211] = "\u2A7E\u0338", // ⩾̸ + [5214] = "\u22D9\u0338", // ⋙̸ + [5218] = "\u2275", // ≵ + [5220] = "\u226B\u20D2", // ≫⃒ + [5222] = "\u226F", // ≯ + [5224] = "\u226F", // ≯ + [5226] = "\u226B\u0338", // ≫̸ + [5231] = "\u21CE", // ⇎ + [5235] = "\u21AE", // ↮ + [5239] = "\u2AF2", // ⫲ + [5241] = "\u220B", // ∋ + [5243] = "\u22FC", // ⋼ + [5245] = "\u22FA", // ⋺ + [5247] = "\u220B", // ∋ + [5251] = "\u040A", // Њ + [5255] = "\u045A", // њ + [5260] = "\u21CD", // ⇍ + [5264] = "\u219A", // ↚ + [5267] = "\u2025", // ‥ + [5269] = "\u2266\u0338", // ≦̸ + [5271] = "\u2270", // ≰ + [5281] = "\u21CD", // ⇍ + [5289] = "\u219A", // ↚ + [5300] = "\u21CE", // ⇎ + [5311] = "\u21AE", // ↮ + [5313] = "\u2270", // ≰ + [5315] = "\u2266\u0338", // ≦̸ + [5321] = "\u2A7D\u0338", // ⩽̸ + [5323] = "\u2A7D\u0338", // ⩽̸ + [5325] = "\u226E", // ≮ + [5327] = "\u22D8\u0338", // ⋘̸ + [5331] = "\u2274", // ≴ + [5333] = "\u226A\u20D2", // ≪⃒ + [5335] = "\u226E", // ≮ + [5338] = "\u22EA", // ⋪ + [5340] = "\u22EC", // ⋬ + [5342] = "\u226A\u0338", // ≪̸ + [5346] = "\u2224", // ∤ + [5353] = "\u2060", // ⁠ + [5368] = "\u00A0", //   + [5371] = "\u2115", // ℕ + [5375] = "\uD835\uDD5F", // 𝕟 + [5377] = "\u2AEC", // ⫬ + [5378] = "\u00AC", // ¬ + [5379] = "\u00AC", // ¬ + [5389] = "\u2262", // ≢ + [5395] = "\u226D", // ≭ + [5413] = "\u2226", // ∦ + [5421] = "\u2209", // ∉ + [5426] = "\u2260", // ≠ + [5432] = "\u2242\u0338", // ≂̸ + [5438] = "\u2204", // ∄ + [5446] = "\u226F", // ≯ + [5452] = "\u2271", // ≱ + [5462] = "\u2267\u0338", // ≧̸ + [5470] = "\u226B\u0338", // ≫̸ + [5475] = "\u2279", // ≹ + [5486] = "\u2A7E\u0338", // ⩾̸ + [5492] = "\u2275", // ≵ + [5505] = "\u224E\u0338", // ≎̸ + [5511] = "\u224F\u0338", // ≏̸ + [5514] = "\u2209", // ∉ + [5518] = "\u22F5\u0338", // ⋵̸ + [5520] = "\u22F9\u0338", // ⋹̸ + [5523] = "\u2209", // ∉ + [5525] = "\u22F7", // ⋷ + [5527] = "\u22F6", // ⋶ + [5540] = "\u22EA", // ⋪ + [5544] = "\u29CF\u0338", // ⧏̸ + [5550] = "\u22EC", // ⋬ + [5553] = "\u226E", // ≮ + [5559] = "\u2270", // ≰ + [5567] = "\u2278", // ≸ + [5572] = "\u226A\u0338", // ≪̸ + [5583] = "\u2A7D\u0338", // ⩽̸ + [5589] = "\u2274", // ≴ + [5610] = "\u2AA2\u0338", // ⪢̸ + [5619] = "\u2AA1\u0338", // ⪡̸ + [5622] = "\u220C", // ∌ + [5625] = "\u220C", // ∌ + [5627] = "\u22FE", // ⋾ + [5629] = "\u22FD", // ⋽ + [5638] = "\u2280", // ⊀ + [5644] = "\u2AAF\u0338", // ⪯̸ + [5655] = "\u22E0", // ⋠ + [5670] = "\u220C", // ∌ + [5683] = "\u22EB", // ⋫ + [5687] = "\u29D0\u0338", // ⧐̸ + [5693] = "\u22ED", // ⋭ + [5706] = "\u228F\u0338", // ⊏̸ + [5712] = "\u22E2", // ⋢ + [5719] = "\u2290\u0338", // ⊐̸ + [5725] = "\u22E3", // ⋣ + [5731] = "\u2282\u20D2", // ⊂⃒ + [5737] = "\u2288", // ⊈ + [5744] = "\u2281", // ⊁ + [5750] = "\u2AB0\u0338", // ⪰̸ + [5761] = "\u22E1", // ⋡ + [5767] = "\u227F\u0338", // ≿̸ + [5774] = "\u2283\u20D2", // ⊃⃒ + [5780] = "\u2289", // ⊉ + [5786] = "\u2241", // ≁ + [5792] = "\u2244", // ≄ + [5802] = "\u2247", // ≇ + [5808] = "\u2249", // ≉ + [5820] = "\u2224", // ∤ + [5824] = "\u2226", // ∦ + [5830] = "\u2226", // ∦ + [5833] = "\u2AFD\u20E5", // ⫽⃥ + [5835] = "\u2202\u0338", // ∂̸ + [5841] = "\u2A14", // ⨔ + [5843] = "\u2280", // ⊀ + [5847] = "\u22E0", // ⋠ + [5849] = "\u2AAF\u0338", // ⪯̸ + [5851] = "\u2280", // ⊀ + [5854] = "\u2AAF\u0338", // ⪯̸ + [5859] = "\u21CF", // ⇏ + [5863] = "\u219B", // ↛ + [5865] = "\u2933\u0338", // ⤳̸ + [5867] = "\u219D\u0338", // ↝̸ + [5878] = "\u21CF", // ⇏ + [5888] = "\u219B", // ↛ + [5892] = "\u22EB", // ⋫ + [5894] = "\u22ED", // ⋭ + [5897] = "\u2281", // ⊁ + [5901] = "\u22E1", // ⋡ + [5903] = "\u2AB0\u0338", // ⪰̸ + [5907] = "\uD835\uDCA9", // 𝒩 + [5909] = "\uD835\uDCC3", // 𝓃 + [5917] = "\u2224", // ∤ + [5926] = "\u2226", // ∦ + [5929] = "\u2241", // ≁ + [5931] = "\u2244", // ≄ + [5933] = "\u2244", // ≄ + [5937] = "\u2224", // ∤ + [5941] = "\u2226", // ∦ + [5947] = "\u22E2", // ⋢ + [5950] = "\u22E3", // ⋣ + [5953] = "\u2284", // ⊄ + [5955] = "\u2AC5\u0338", // ⫅̸ + [5957] = "\u2288", // ⊈ + [5961] = "\u2282\u20D2", // ⊂⃒ + [5964] = "\u2288", // ⊈ + [5966] = "\u2AC5\u0338", // ⫅̸ + [5969] = "\u2281", // ⊁ + [5972] = "\u2AB0\u0338", // ⪰̸ + [5974] = "\u2285", // ⊅ + [5976] = "\u2AC6\u0338", // ⫆̸ + [5978] = "\u2289", // ⊉ + [5982] = "\u2283\u20D2", // ⊃⃒ + [5985] = "\u2289", // ⊉ + [5987] = "\u2AC6\u0338", // ⫆̸ + [5991] = "\u2279", // ≹ + [5996] = "\u00D1", // Ñ + [5997] = "\u00D1", // Ñ + [6001] = "\u00F1", // ñ + [6002] = "\u00F1", // ñ + [6005] = "\u2278", // ≸ + [6017] = "\u22EA", // ⋪ + [6020] = "\u22EC", // ⋬ + [6026] = "\u22EB", // ⋫ + [6029] = "\u22ED", // ⋭ + [6031] = "\u039D", // Ν + [6033] = "\u03BD", // ν + [6035] = "\u0023", // # + [6039] = "\u2116", // № + [6042] = "\u2007", //   + [6046] = "\u224D\u20D2", // ≍⃒ + [6052] = "\u22AF", // ⊯ + [6057] = "\u22AE", // ⊮ + [6062] = "\u22AD", // ⊭ + [6067] = "\u22AC", // ⊬ + [6070] = "\u2265\u20D2", // ≥⃒ + [6072] = "\u003E\u20D2", // >⃒ + [6077] = "\u2904", // ⤄ + [6083] = "\u29DE", // ⧞ + [6088] = "\u2902", // ⤂ + [6090] = "\u2264\u20D2", // ≤⃒ + [6092] = "\u003C\u20D2", // <⃒ + [6096] = "\u22B4\u20D2", // ⊴⃒ + [6101] = "\u2903", // ⤃ + [6106] = "\u22B5\u20D2", // ⊵⃒ + [6110] = "\u223C\u20D2", // ∼⃒ + [6116] = "\u2923", // ⤣ + [6120] = "\u21D6", // ⇖ + [6122] = "\u2196", // ↖ + [6125] = "\u2196", // ↖ + [6130] = "\u2927", // ⤧ + [6136] = "\u00D3", // Ó + [6137] = "\u00D3", // Ó + [6143] = "\u00F3", // ó + [6144] = "\u00F3", // ó + [6147] = "\u229B", // ⊛ + [6151] = "\u229A", // ⊚ + [6155] = "\u00D4", // Ô + [6156] = "\u00D4", // Ô + [6157] = "\u00F4", // ô + [6158] = "\u00F4", // ô + [6160] = "\u041E", // О + [6162] = "\u043E", // о + [6167] = "\u229D", // ⊝ + [6173] = "\u0150", // Ő + [6178] = "\u0151", // ő + [6181] = "\u2A38", // ⨸ + [6184] = "\u2299", // ⊙ + [6189] = "\u29BC", // ⦼ + [6194] = "\u0152", // Œ + [6199] = "\u0153", // œ + [6204] = "\u29BF", // ⦿ + [6207] = "\uD835\uDD12", // 𝔒 + [6209] = "\uD835\uDD2C", // 𝔬 + [6213] = "\u02DB", // ˛ + [6218] = "\u00D2", // Ò + [6219] = "\u00D2", // Ò + [6223] = "\u00F2", // ò + [6224] = "\u00F2", // ò + [6226] = "\u29C1", // ⧁ + [6231] = "\u29B5", // ⦵ + [6233] = "\u03A9", // Ω + [6237] = "\u222E", // ∮ + [6242] = "\u21BA", // ↺ + [6246] = "\u29BE", // ⦾ + [6251] = "\u29BB", // ⦻ + [6255] = "\u203E", // ‾ + [6257] = "\u29C0", // ⧀ + [6262] = "\u014C", // Ō + [6267] = "\u014D", // ō + [6271] = "\u03A9", // Ω + [6275] = "\u03C9", // ω + [6281] = "\u039F", // Ο + [6287] = "\u03BF", // ο + [6289] = "\u29B6", // ⦶ + [6293] = "\u2296", // ⊖ + [6297] = "\uD835\uDD46", // 𝕆 + [6301] = "\uD835\uDD60", // 𝕠 + [6305] = "\u29B7", // ⦷ + [6325] = "\u201C", // “ + [6331] = "\u2018", // ‘ + [6335] = "\u29B9", // ⦹ + [6339] = "\u2295", // ⊕ + [6341] = "\u2A54", // ⩔ + [6343] = "\u2228", // ∨ + [6347] = "\u21BB", // ↻ + [6349] = "\u2A5D", // ⩝ + [6352] = "\u2134", // ℴ + [6355] = "\u2134", // ℴ + [6356] = "\u00AA", // ª + [6357] = "\u00AA", // ª + [6358] = "\u00BA", // º + [6359] = "\u00BA", // º + [6364] = "\u22B6", // ⊶ + [6367] = "\u2A56", // ⩖ + [6373] = "\u2A57", // ⩗ + [6375] = "\u2A5B", // ⩛ + [6377] = "\u24C8", // Ⓢ + [6381] = "\uD835\uDCAA", // 𝒪 + [6385] = "\u2134", // ℴ + [6389] = "\u00D8", // Ø + [6390] = "\u00D8", // Ø + [6394] = "\u00F8", // ø + [6395] = "\u00F8", // ø + [6398] = "\u2298", // ⊘ + [6403] = "\u00D5", // Õ + [6404] = "\u00D5", // Õ + [6409] = "\u00F5", // õ + [6410] = "\u00F5", // õ + [6414] = "\u2A37", // ⨷ + [6418] = "\u2297", // ⊗ + [6421] = "\u2A36", // ⨶ + [6424] = "\u00D6", // Ö + [6425] = "\u00D6", // Ö + [6428] = "\u00F6", // ö + [6429] = "\u00F6", // ö + [6434] = "\u233D", // ⌽ + [6441] = "\u203E", // ‾ + [6446] = "\u23DE", // ⏞ + [6450] = "\u23B4", // ⎴ + [6462] = "\u23DC", // ⏜ + [6466] = "\u2225", // ∥ + [6467] = "\u00B6", // ¶ + [6468] = "\u00B6", // ¶ + [6473] = "\u2225", // ∥ + [6477] = "\u2AF3", // ⫳ + [6479] = "\u2AFD", // ⫽ + [6481] = "\u2202", // ∂ + [6490] = "\u2202", // ∂ + [6493] = "\u041F", // П + [6496] = "\u043F", // п + [6502] = "\u0025", // % + [6506] = "\u002E", // . + [6510] = "\u2030", // ‰ + [6512] = "\u22A5", // ⊥ + [6517] = "\u2031", // ‱ + [6520] = "\uD835\uDD13", // 𝔓 + [6523] = "\uD835\uDD2D", // 𝔭 + [6526] = "\u03A6", // Φ + [6529] = "\u03C6", // φ + [6531] = "\u03D5", // ϕ + [6536] = "\u2133", // ℳ + [6540] = "\u260E", // ☎ + [6542] = "\u03A0", // Π + [6544] = "\u03C0", // π + [6552] = "\u22D4", // ⋔ + [6554] = "\u03D6", // ϖ + [6560] = "\u210F", // ℏ + [6562] = "\u210E", // ℎ + [6565] = "\u210F", // ℏ + [6568] = "\u002B", // + + [6573] = "\u2A23", // ⨣ + [6575] = "\u229E", // ⊞ + [6579] = "\u2A22", // ⨢ + [6582] = "\u2214", // ∔ + [6584] = "\u2A25", // ⨥ + [6586] = "\u2A72", // ⩲ + [6595] = "\u00B1", // ± + [6597] = "\u00B1", // ± + [6598] = "\u00B1", // ± + [6602] = "\u2A26", // ⨦ + [6606] = "\u2A27", // ⨧ + [6608] = "\u00B1", // ± + [6621] = "\u210C", // ℌ + [6629] = "\u2A15", // ⨕ + [6632] = "\u2119", // ℙ + [6635] = "\uD835\uDD61", // 𝕡 + [6638] = "\u00A3", // £ + [6639] = "\u00A3", // £ + [6641] = "\u2ABB", // ⪻ + [6643] = "\u227A", // ≺ + [6646] = "\u2AB7", // ⪷ + [6650] = "\u227C", // ≼ + [6652] = "\u2AB3", // ⪳ + [6654] = "\u2AAF", // ⪯ + [6656] = "\u227A", // ≺ + [6663] = "\u2AB7", // ⪷ + [6671] = "\u227C", // ≼ + [6678] = "\u227A", // ≺ + [6684] = "\u2AAF", // ⪯ + [6695] = "\u227C", // ≼ + [6701] = "\u227E", // ≾ + [6704] = "\u2AAF", // ⪯ + [6712] = "\u2AB9", // ⪹ + [6716] = "\u2AB5", // ⪵ + [6720] = "\u22E8", // ⋨ + [6724] = "\u227E", // ≾ + [6728] = "\u2033", // ″ + [6732] = "\u2032", // ′ + [6734] = "\u2119", // ℙ + [6738] = "\u2AB9", // ⪹ + [6740] = "\u2AB5", // ⪵ + [6744] = "\u22E8", // ⋨ + [6747] = "\u220F", // ∏ + [6753] = "\u220F", // ∏ + [6759] = "\u232E", // ⌮ + [6764] = "\u2312", // ⌒ + [6769] = "\u2313", // ⌓ + [6771] = "\u221D", // ∝ + [6779] = "\u2237", // ∷ + [6782] = "\u221D", // ∝ + [6785] = "\u221D", // ∝ + [6789] = "\u227E", // ≾ + [6794] = "\u22B0", // ⊰ + [6798] = "\uD835\uDCAB", // 𝒫 + [6802] = "\uD835\uDCC5", // 𝓅 + [6804] = "\u03A8", // Ψ + [6806] = "\u03C8", // ψ + [6812] = "\u2008", //   + [6816] = "\uD835\uDD14", // 𝔔 + [6820] = "\uD835\uDD2E", // 𝔮 + [6824] = "\u2A0C", // ⨌ + [6828] = "\u211A", // ℚ + [6832] = "\uD835\uDD62", // 𝕢 + [6838] = "\u2057", // ⁗ + [6842] = "\uD835\uDCAC", // 𝒬 + [6846] = "\uD835\uDCC6", // 𝓆 + [6857] = "\u210D", // ℍ + [6861] = "\u2A16", // ⨖ + [6865] = "\u003F", // ? + [6868] = "\u225F", // ≟ + [6871] = "\u0022", // " + [6872] = "\u0022", // " + [6874] = "\u0022", // " + [6875] = "\u0022", // " + [6881] = "\u21DB", // ⇛ + [6885] = "\u223D\u0331", // ∽̱ + [6892] = "\u0154", // Ŕ + [6896] = "\u0155", // ŕ + [6900] = "\u221A", // √ + [6907] = "\u29B3", // ⦳ + [6910] = "\u27EB", // ⟫ + [6913] = "\u27E9", // 〉 + [6915] = "\u2992", // ⦒ + [6917] = "\u29A5", // ⦥ + [6920] = "\u27E9", // ⟩ + [6923] = "\u00BB", // » + [6924] = "\u00BB", // » + [6927] = "\u21A0", // ↠ + [6930] = "\u21D2", // ⇒ + [6933] = "\u2192", // → + [6936] = "\u2975", // ⥵ + [6938] = "\u21E5", // ⇥ + [6941] = "\u2920", // ⤠ + [6943] = "\u2933", // ⤳ + [6946] = "\u291E", // ⤞ + [6949] = "\u21AA", // ↪ + [6952] = "\u21AC", // ↬ + [6955] = "\u2945", // ⥅ + [6959] = "\u2974", // ⥴ + [6962] = "\u2916", // ⤖ + [6965] = "\u21A3", // ↣ + [6967] = "\u219D", // ↝ + [6972] = "\u291C", // ⤜ + [6977] = "\u291A", // ⤚ + [6980] = "\u2236", // ∶ + [6985] = "\u211A", // ℚ + [6990] = "\u2910", // ⤐ + [6995] = "\u290F", // ⤏ + [7000] = "\u290D", // ⤍ + [7004] = "\u2773", // ❳ + [7009] = "\u007D", // } + [7011] = "\u005D", // ] + [7014] = "\u298C", // ⦌ + [7018] = "\u298E", // ⦎ + [7020] = "\u2990", // ⦐ + [7026] = "\u0158", // Ř + [7032] = "\u0159", // ř + [7037] = "\u0156", // Ŗ + [7042] = "\u0157", // ŗ + [7045] = "\u2309", // ⌉ + [7048] = "\u007D", // } + [7050] = "\u0420", // Р + [7052] = "\u0440", // р + [7056] = "\u2937", // ⤷ + [7062] = "\u2969", // ⥩ + [7066] = "\u201D", // ” + [7068] = "\u201D", // ” + [7071] = "\u21B3", // ↳ + [7073] = "\u211C", // ℜ + [7077] = "\u211C", // ℜ + [7081] = "\u211B", // ℛ + [7086] = "\u211C", // ℜ + [7088] = "\u211D", // ℝ + [7091] = "\u25AD", // ▭ + [7093] = "\u00AE", // ® + [7094] = "\u00AE", // ® + [7095] = "\u00AE", // ® + [7096] = "\u00AE", // ® + [7109] = "\u220B", // ∋ + [7120] = "\u21CB", // ⇋ + [7134] = "\u296F", // ⥯ + [7140] = "\u297D", // ⥽ + [7145] = "\u230B", // ⌋ + [7148] = "\u211C", // ℜ + [7150] = "\uD835\uDD2F", // 𝔯 + [7154] = "\u2964", // ⥤ + [7159] = "\u21C1", // ⇁ + [7161] = "\u21C0", // ⇀ + [7163] = "\u296C", // ⥬ + [7166] = "\u03A1", // Ρ + [7168] = "\u03C1", // ρ + [7170] = "\u03F1", // ϱ + [7187] = "\u27E9", // ⟩ + [7192] = "\u2192", // → + [7198] = "\u21D2", // ⇒ + [7208] = "\u2192", // → + [7212] = "\u21E5", // ⇥ + [7222] = "\u21C4", // ⇄ + [7227] = "\u21A3", // ↣ + [7235] = "\u2309", // ⌉ + [7249] = "\u27E7", // ⟧ + [7261] = "\u295D", // ⥝ + [7268] = "\u21C2", // ⇂ + [7272] = "\u2955", // ⥕ + [7278] = "\u230B", // ⌋ + [7290] = "\u21C1", // ⇁ + [7293] = "\u21C0", // ⇀ + [7304] = "\u21C4", // ⇄ + [7313] = "\u21CC", // ⇌ + [7325] = "\u21C9", // ⇉ + [7336] = "\u219D", // ↝ + [7340] = "\u22A2", // ⊢ + [7346] = "\u21A6", // ↦ + [7353] = "\u295B", // ⥛ + [7364] = "\u22CC", // ⋌ + [7372] = "\u22B3", // ⊳ + [7376] = "\u29D0", // ⧐ + [7382] = "\u22B5", // ⊵ + [7395] = "\u294F", // ⥏ + [7405] = "\u295C", // ⥜ + [7412] = "\u21BE", // ↾ + [7416] = "\u2954", // ⥔ + [7423] = "\u21C0", // ⇀ + [7427] = "\u2953", // ⥓ + [7430] = "\u02DA", // ˚ + [7441] = "\u2253", // ≓ + [7446] = "\u21C4", // ⇄ + [7450] = "\u21CC", // ⇌ + [7452] = "\u200F", // + [7458] = "\u23B1", // ⎱ + [7463] = "\u23B1", // ⎱ + [7468] = "\u2AEE", // ⫮ + [7473] = "\u27ED", // ⟭ + [7476] = "\u21FE", // ⇾ + [7480] = "\u27E7", // ⟧ + [7484] = "\u2986", // ⦆ + [7488] = "\u211D", // ℝ + [7490] = "\uD835\uDD63", // 𝕣 + [7494] = "\u2A2E", // ⨮ + [7500] = "\u2A35", // ⨵ + [7511] = "\u2970", // ⥰ + [7515] = "\u0029", // ) + [7518] = "\u2994", // ⦔ + [7525] = "\u2A12", // ⨒ + [7530] = "\u21C9", // ⇉ + [7541] = "\u21DB", // ⇛ + [7547] = "\u203A", // › + [7551] = "\u211B", // ℛ + [7554] = "\uD835\uDCC7", // 𝓇 + [7556] = "\u21B1", // ↱ + [7558] = "\u21B1", // ↱ + [7561] = "\u005D", // ] + [7564] = "\u2019", // ’ + [7566] = "\u2019", // ’ + [7572] = "\u22CC", // ⋌ + [7577] = "\u22CA", // ⋊ + [7580] = "\u25B9", // ▹ + [7582] = "\u22B5", // ⊵ + [7584] = "\u25B8", // ▸ + [7589] = "\u29CE", // ⧎ + [7600] = "\u29F4", // ⧴ + [7607] = "\u2968", // ⥨ + [7609] = "\u211E", // ℞ + [7616] = "\u015A", // Ś + [7623] = "\u015B", // ś + [7628] = "\u201A", // ‚ + [7630] = "\u2ABC", // ⪼ + [7632] = "\u227B", // ≻ + [7635] = "\u2AB8", // ⪸ + [7640] = "\u0160", // Š + [7644] = "\u0161", // š + [7648] = "\u227D", // ≽ + [7650] = "\u2AB4", // ⪴ + [7652] = "\u2AB0", // ⪰ + [7657] = "\u015E", // Ş + [7661] = "\u015F", // ş + [7665] = "\u015C", // Ŝ + [7669] = "\u015D", // ŝ + [7673] = "\u2ABA", // ⪺ + [7675] = "\u2AB6", // ⪶ + [7679] = "\u22E9", // ⋩ + [7686] = "\u2A13", // ⨓ + [7690] = "\u227F", // ≿ + [7692] = "\u0421", // С + [7694] = "\u0441", // с + [7698] = "\u22C5", // ⋅ + [7700] = "\u22A1", // ⊡ + [7702] = "\u2A66", // ⩦ + [7708] = "\u2925", // ⤥ + [7712] = "\u21D8", // ⇘ + [7714] = "\u2198", // ↘ + [7717] = "\u2198", // ↘ + [7719] = "\u00A7", // § + [7720] = "\u00A7", // § + [7723] = "\u003B", // ; + [7728] = "\u2929", // ⤩ + [7735] = "\u2216", // ∖ + [7737] = "\u2216", // ∖ + [7740] = "\u2736", // ✶ + [7743] = "\uD835\uDD16", // 𝔖 + [7746] = "\uD835\uDD30", // 𝔰 + [7750] = "\u2322", // ⌢ + [7755] = "\u266F", // ♯ + [7761] = "\u0429", // Щ + [7766] = "\u0449", // щ + [7769] = "\u0428", // Ш + [7771] = "\u0448", // ш + [7785] = "\u2193", // ↓ + [7795] = "\u2190", // ← + [7802] = "\u2223", // ∣ + [7811] = "\u2225", // ∥ + [7822] = "\u2192", // → + [7830] = "\u2191", // ↑ + [7831] = "\u00AD", // ­ + [7832] = "\u00AD", // + [7837] = "\u03A3", // Σ + [7842] = "\u03C3", // σ + [7844] = "\u03C2", // ς + [7846] = "\u03C2", // ς + [7848] = "\u223C", // ∼ + [7852] = "\u2A6A", // ⩪ + [7854] = "\u2243", // ≃ + [7856] = "\u2243", // ≃ + [7858] = "\u2A9E", // ⪞ + [7860] = "\u2AA0", // ⪠ + [7862] = "\u2A9D", // ⪝ + [7864] = "\u2A9F", // ⪟ + [7867] = "\u2246", // ≆ + [7872] = "\u2A24", // ⨤ + [7877] = "\u2972", // ⥲ + [7882] = "\u2190", // ← + [7893] = "\u2218", // ∘ + [7906] = "\u2216", // ∖ + [7910] = "\u2A33", // ⨳ + [7917] = "\u29E4", // ⧤ + [7920] = "\u2223", // ∣ + [7923] = "\u2323", // ⌣ + [7925] = "\u2AAA", // ⪪ + [7927] = "\u2AAC", // ⪬ + [7929] = "\u2AAC\uFE00", // ⪬︀ + [7935] = "\u042C", // Ь + [7941] = "\u044C", // ь + [7943] = "\u002F", // / + [7945] = "\u29C4", // ⧄ + [7948] = "\u233F", // ⌿ + [7952] = "\uD835\uDD4A", // 𝕊 + [7955] = "\uD835\uDD64", // 𝕤 + [7961] = "\u2660", // ♠ + [7965] = "\u2660", // ♠ + [7967] = "\u2225", // ∥ + [7972] = "\u2293", // ⊓ + [7974] = "\u2293\uFE00", // ⊓︀ + [7977] = "\u2294", // ⊔ + [7979] = "\u2294\uFE00", // ⊔︀ + [7983] = "\u221A", // √ + [7987] = "\u228F", // ⊏ + [7989] = "\u2291", // ⊑ + [7993] = "\u228F", // ⊏ + [7996] = "\u2291", // ⊑ + [7998] = "\u2290", // ⊐ + [8000] = "\u2292", // ⊒ + [8004] = "\u2290", // ⊐ + [8007] = "\u2292", // ⊒ + [8009] = "\u25A1", // □ + [8014] = "\u25A1", // □ + [8018] = "\u25A1", // □ + [8031] = "\u2293", // ⊓ + [8038] = "\u228F", // ⊏ + [8044] = "\u2291", // ⊑ + [8051] = "\u2290", // ⊐ + [8057] = "\u2292", // ⊒ + [8063] = "\u2294", // ⊔ + [8065] = "\u25AA", // ▪ + [8067] = "\u25AA", // ▪ + [8072] = "\u2192", // → + [8076] = "\uD835\uDCAE", // 𝒮 + [8080] = "\uD835\uDCC8", // 𝓈 + [8085] = "\u2216", // ∖ + [8090] = "\u2323", // ⌣ + [8095] = "\u22C6", // ⋆ + [8099] = "\u22C6", // ⋆ + [8103] = "\u2606", // ☆ + [8105] = "\u2605", // ★ + [8119] = "\u03F5", // ϵ + [8123] = "\u03D5", // ϕ + [8126] = "\u00AF", // ¯ + [8129] = "\u22D0", // ⋐ + [8132] = "\u2282", // ⊂ + [8136] = "\u2ABD", // ⪽ + [8138] = "\u2AC5", // ⫅ + [8140] = "\u2286", // ⊆ + [8144] = "\u2AC3", // ⫃ + [8149] = "\u2AC1", // ⫁ + [8152] = "\u2ACB", // ⫋ + [8154] = "\u228A", // ⊊ + [8159] = "\u2ABF", // ⪿ + [8164] = "\u2979", // ⥹ + [8168] = "\u22D0", // ⋐ + [8172] = "\u2282", // ⊂ + [8175] = "\u2286", // ⊆ + [8177] = "\u2AC5", // ⫅ + [8183] = "\u2286", // ⊆ + [8187] = "\u228A", // ⊊ + [8189] = "\u2ACB", // ⫋ + [8192] = "\u2AC7", // ⫇ + [8195] = "\u2AD5", // ⫕ + [8197] = "\u2AD3", // ⫓ + [8200] = "\u227B", // ≻ + [8207] = "\u2AB8", // ⪸ + [8215] = "\u227D", // ≽ + [8222] = "\u227B", // ≻ + [8228] = "\u2AB0", // ⪰ + [8239] = "\u227D", // ≽ + [8245] = "\u227F", // ≿ + [8248] = "\u2AB0", // ⪰ + [8256] = "\u2ABA", // ⪺ + [8260] = "\u2AB6", // ⪶ + [8264] = "\u22E9", // ⋩ + [8268] = "\u227F", // ≿ + [8274] = "\u220B", // ∋ + [8276] = "\u2211", // ∑ + [8278] = "\u2211", // ∑ + [8281] = "\u266A", // ♪ + [8283] = "\u22D1", // ⋑ + [8285] = "\u2283", // ⊃ + [8286] = "\u00B9", // ¹ + [8287] = "\u00B9", // ¹ + [8288] = "\u00B2", // ² + [8289] = "\u00B2", // ² + [8290] = "\u00B3", // ³ + [8291] = "\u00B3", // ³ + [8295] = "\u2ABE", // ⪾ + [8299] = "\u2AD8", // ⫘ + [8301] = "\u2AC6", // ⫆ + [8303] = "\u2287", // ⊇ + [8307] = "\u2AC4", // ⫄ + [8313] = "\u2283", // ⊃ + [8319] = "\u2287", // ⊇ + [8324] = "\u27C9", // ⟉ + [8327] = "\u2AD7", // ⫗ + [8332] = "\u297B", // ⥻ + [8337] = "\u2AC2", // ⫂ + [8340] = "\u2ACC", // ⫌ + [8342] = "\u228B", // ⊋ + [8347] = "\u2AC0", // ⫀ + [8351] = "\u22D1", // ⋑ + [8355] = "\u2283", // ⊃ + [8358] = "\u2287", // ⊇ + [8360] = "\u2AC6", // ⫆ + [8364] = "\u228B", // ⊋ + [8366] = "\u2ACC", // ⫌ + [8369] = "\u2AC8", // ⫈ + [8372] = "\u2AD4", // ⫔ + [8374] = "\u2AD6", // ⫖ + [8380] = "\u2926", // ⤦ + [8384] = "\u21D9", // ⇙ + [8386] = "\u2199", // ↙ + [8389] = "\u2199", // ↙ + [8394] = "\u292A", // ⤪ + [8398] = "\u00DF", // ß + [8399] = "\u00DF", // ß + [8403] = "\u0009", // 	 + [8410] = "\u2316", // ⌖ + [8412] = "\u03A4", // Τ + [8414] = "\u03C4", // τ + [8418] = "\u23B4", // ⎴ + [8424] = "\u0164", // Ť + [8430] = "\u0165", // ť + [8435] = "\u0162", // Ţ + [8440] = "\u0163", // ţ + [8442] = "\u0422", // Т + [8444] = "\u0442", // т + [8448] = "\u20DB", // ⃛ + [8454] = "\u2315", // ⌕ + [8457] = "\uD835\uDD17", // 𝔗 + [8460] = "\uD835\uDD31", // 𝔱 + [8466] = "\u2234", // ∴ + [8475] = "\u2234", // ∴ + [8480] = "\u2234", // ∴ + [8483] = "\u0398", // Θ + [8486] = "\u03B8", // θ + [8490] = "\u03D1", // ϑ + [8492] = "\u03D1", // ϑ + [8502] = "\u2248", // ≈ + [8506] = "\u223C", // ∼ + [8515] = "\u205F\u200A", //    + [8519] = "\u2009", // + [8526] = "\u2009", //   + [8530] = "\u2248", // ≈ + [8534] = "\u223C", // ∼ + [8538] = "\u00DE", // Þ + [8539] = "\u00DE", // Þ + [8542] = "\u00FE", // þ + [8543] = "\u00FE", // þ + [8548] = "\u223C", // ∼ + [8553] = "\u02DC", // ˜ + [8559] = "\u2243", // ≃ + [8569] = "\u2245", // ≅ + [8575] = "\u2248", // ≈ + [8578] = "\u00D7", // × + [8579] = "\u00D7", // × + [8581] = "\u22A0", // ⊠ + [8584] = "\u2A31", // ⨱ + [8586] = "\u2A30", // ⨰ + [8589] = "\u222D", // ∭ + [8593] = "\u2928", // ⤨ + [8595] = "\u22A4", // ⊤ + [8599] = "\u2336", // ⌶ + [8603] = "\u2AF1", // ⫱ + [8607] = "\uD835\uDD4B", // 𝕋 + [8609] = "\uD835\uDD65", // 𝕥 + [8613] = "\u2ADA", // ⫚ + [8616] = "\u2929", // ⤩ + [8622] = "\u2034", // ‴ + [8627] = "\u2122", // ™ + [8632] = "\u2122", // ™ + [8639] = "\u25B5", // ▵ + [8644] = "\u25BF", // ▿ + [8649] = "\u25C3", // ◃ + [8652] = "\u22B4", // ⊴ + [8654] = "\u225C", // ≜ + [8660] = "\u25B9", // ▹ + [8663] = "\u22B5", // ⊵ + [8667] = "\u25EC", // ◬ + [8669] = "\u225C", // ≜ + [8675] = "\u2A3A", // ⨺ + [8684] = "\u20DB", // ⃛ + [8689] = "\u2A39", // ⨹ + [8692] = "\u29CD", // ⧍ + [8697] = "\u2A3B", // ⨻ + [8704] = "\u23E2", // ⏢ + [8708] = "\uD835\uDCAF", // 𝒯 + [8712] = "\uD835\uDCC9", // 𝓉 + [8716] = "\u0426", // Ц + [8718] = "\u0446", // ц + [8722] = "\u040B", // Ћ + [8726] = "\u045B", // ћ + [8731] = "\u0166", // Ŧ + [8736] = "\u0167", // ŧ + [8741] = "\u226C", // ≬ + [8756] = "\u219E", // ↞ + [8767] = "\u21A0", // ↠ + [8773] = "\u00DA", // Ú + [8774] = "\u00DA", // Ú + [8780] = "\u00FA", // ú + [8781] = "\u00FA", // ú + [8784] = "\u219F", // ↟ + [8788] = "\u21D1", // ⇑ + [8791] = "\u2191", // ↑ + [8796] = "\u2949", // ⥉ + [8801] = "\u040E", // Ў + [8806] = "\u045E", // ў + [8810] = "\u016C", // Ŭ + [8814] = "\u016D", // ŭ + [8818] = "\u00DB", // Û + [8819] = "\u00DB", // Û + [8823] = "\u00FB", // û + [8824] = "\u00FB", // û + [8826] = "\u0423", // У + [8828] = "\u0443", // у + [8833] = "\u21C5", // ⇅ + [8839] = "\u0170", // Ű + [8844] = "\u0171", // ű + [8848] = "\u296E", // ⥮ + [8854] = "\u297E", // ⥾ + [8857] = "\uD835\uDD18", // 𝔘 + [8859] = "\uD835\uDD32", // 𝔲 + [8864] = "\u00D9", // Ù + [8865] = "\u00D9", // Ù + [8870] = "\u00F9", // ù + [8871] = "\u00F9", // ù + [8875] = "\u2963", // ⥣ + [8880] = "\u21BF", // ↿ + [8882] = "\u21BE", // ↾ + [8886] = "\u2580", // ▀ + [8892] = "\u231C", // ⌜ + [8895] = "\u231C", // ⌜ + [8899] = "\u230F", // ⌏ + [8903] = "\u25F8", // ◸ + [8908] = "\u016A", // Ū + [8913] = "\u016B", // ū + [8914] = "\u00A8", // ¨ + [8915] = "\u00A8", // ¨ + [8923] = "\u005F", // _ + [8928] = "\u23DF", // ⏟ + [8932] = "\u23B5", // ⎵ + [8944] = "\u23DD", // ⏝ + [8948] = "\u22C3", // ⋃ + [8953] = "\u228E", // ⊎ + [8958] = "\u0172", // Ų + [8963] = "\u0173", // ų + [8966] = "\uD835\uDD4C", // 𝕌 + [8969] = "\uD835\uDD66", // 𝕦 + [8976] = "\u2191", // ↑ + [8982] = "\u21D1", // ⇑ + [8989] = "\u2191", // ↑ + [8993] = "\u2912", // ⤒ + [9003] = "\u21C5", // ⇅ + [9013] = "\u2195", // ↕ + [9023] = "\u21D5", // ⇕ + [9033] = "\u2195", // ↕ + [9045] = "\u296E", // ⥮ + [9057] = "\u21BF", // ↿ + [9063] = "\u21BE", // ↾ + [9067] = "\u228E", // ⊎ + [9080] = "\u2196", // ↖ + [9091] = "\u2197", // ↗ + [9094] = "\u03D2", // ϒ + [9097] = "\u03C5", // υ + [9099] = "\u03D2", // ϒ + [9103] = "\u03A5", // Υ + [9107] = "\u03C5", // υ + [9111] = "\u22A5", // ⊥ + [9117] = "\u21A5", // ↥ + [9126] = "\u21C8", // ⇈ + [9132] = "\u231D", // ⌝ + [9135] = "\u231D", // ⌝ + [9139] = "\u230E", // ⌎ + [9144] = "\u016E", // Ů + [9148] = "\u016F", // ů + [9152] = "\u25F9", // ◹ + [9156] = "\uD835\uDCB0", // 𝒰 + [9160] = "\uD835\uDCCA", // 𝓊 + [9165] = "\u22F0", // ⋰ + [9171] = "\u0168", // Ũ + [9176] = "\u0169", // ũ + [9179] = "\u25B5", // ▵ + [9181] = "\u25B4", // ▴ + [9186] = "\u21C8", // ⇈ + [9189] = "\u00DC", // Ü + [9190] = "\u00DC", // Ü + [9192] = "\u00FC", // ü + [9193] = "\u00FC", // ü + [9200] = "\u29A7", // ⦧ + [9207] = "\u299C", // ⦜ + [9216] = "\u03F5", // ϵ + [9222] = "\u03F0", // ϰ + [9230] = "\u2205", // ∅ + [9234] = "\u03D5", // ϕ + [9236] = "\u03D6", // ϖ + [9242] = "\u221D", // ∝ + [9246] = "\u21D5", // ⇕ + [9248] = "\u2195", // ↕ + [9251] = "\u03F1", // ϱ + [9257] = "\u03C2", // ς + [9266] = "\u228A\uFE00", // ⊊︀ + [9268] = "\u2ACB\uFE00", // ⫋︀ + [9276] = "\u228B\uFE00", // ⊋︀ + [9278] = "\u2ACC\uFE00", // ⫌︀ + [9284] = "\u03D1", // ϑ + [9296] = "\u22B2", // ⊲ + [9302] = "\u22B3", // ⊳ + [9307] = "\u2AEB", // ⫫ + [9311] = "\u2AE8", // ⫨ + [9313] = "\u2AE9", // ⫩ + [9316] = "\u0412", // В + [9319] = "\u0432", // в + [9324] = "\u22AB", // ⊫ + [9329] = "\u22A9", // ⊩ + [9334] = "\u22A8", // ⊨ + [9339] = "\u22A2", // ⊢ + [9341] = "\u2AE6", // ⫦ + [9344] = "\u22C1", // ⋁ + [9347] = "\u2228", // ∨ + [9351] = "\u22BB", // ⊻ + [9354] = "\u225A", // ≚ + [9359] = "\u22EE", // ⋮ + [9364] = "\u2016", // ‖ + [9369] = "\u007C", // | + [9371] = "\u2016", // ‖ + [9373] = "\u007C", // | + [9381] = "\u2223", // ∣ + [9386] = "\u007C", // | + [9396] = "\u2758", // ❘ + [9402] = "\u2240", // ≀ + [9413] = "\u200A", //   + [9416] = "\uD835\uDD19", // 𝔙 + [9419] = "\uD835\uDD33", // 𝔳 + [9424] = "\u22B2", // ⊲ + [9429] = "\u2282\u20D2", // ⊂⃒ + [9431] = "\u2283\u20D2", // ⊃⃒ + [9435] = "\uD835\uDD4D", // 𝕍 + [9439] = "\uD835\uDD67", // 𝕧 + [9444] = "\u221D", // ∝ + [9449] = "\u22B3", // ⊳ + [9453] = "\uD835\uDCB1", // 𝒱 + [9457] = "\uD835\uDCCB", // 𝓋 + [9462] = "\u2ACB\uFE00", // ⫋︀ + [9464] = "\u228A\uFE00", // ⊊︀ + [9468] = "\u2ACC\uFE00", // ⫌︀ + [9470] = "\u228B\uFE00", // ⊋︀ + [9476] = "\u22AA", // ⊪ + [9483] = "\u299A", // ⦚ + [9489] = "\u0174", // Ŵ + [9495] = "\u0175", // ŵ + [9501] = "\u2A5F", // ⩟ + [9506] = "\u22C0", // ⋀ + [9509] = "\u2227", // ∧ + [9511] = "\u2259", // ≙ + [9516] = "\u2118", // ℘ + [9519] = "\uD835\uDD1A", // 𝔚 + [9522] = "\uD835\uDD34", // 𝔴 + [9526] = "\uD835\uDD4E", // 𝕎 + [9530] = "\uD835\uDD68", // 𝕨 + [9532] = "\u2118", // ℘ + [9534] = "\u2240", // ≀ + [9539] = "\u2240", // ≀ + [9543] = "\uD835\uDCB2", // 𝒲 + [9547] = "\uD835\uDCCC", // 𝓌 + [9552] = "\u22C2", // ⋂ + [9556] = "\u25EF", // ◯ + [9559] = "\u22C3", // ⋃ + [9564] = "\u25BD", // ▽ + [9568] = "\uD835\uDD1B", // 𝔛 + [9571] = "\uD835\uDD35", // 𝔵 + [9576] = "\u27FA", // ⟺ + [9580] = "\u27F7", // ⟷ + [9582] = "\u039E", // Ξ + [9584] = "\u03BE", // ξ + [9589] = "\u27F8", // ⟸ + [9593] = "\u27F5", // ⟵ + [9597] = "\u27FC", // ⟼ + [9601] = "\u22FB", // ⋻ + [9606] = "\u2A00", // ⨀ + [9610] = "\uD835\uDD4F", // 𝕏 + [9613] = "\uD835\uDD69", // 𝕩 + [9617] = "\u2A01", // ⨁ + [9622] = "\u2A02", // ⨂ + [9627] = "\u27F9", // ⟹ + [9631] = "\u27F6", // ⟶ + [9635] = "\uD835\uDCB3", // 𝒳 + [9639] = "\uD835\uDCCD", // 𝓍 + [9644] = "\u2A06", // ⨆ + [9650] = "\u2A04", // ⨄ + [9654] = "\u25B3", // △ + [9658] = "\u22C1", // ⋁ + [9664] = "\u22C0", // ⋀ + [9670] = "\u00DD", // Ý + [9671] = "\u00DD", // Ý + [9677] = "\u00FD", // ý + [9678] = "\u00FD", // ý + [9682] = "\u042F", // Я + [9684] = "\u044F", // я + [9689] = "\u0176", // Ŷ + [9694] = "\u0177", // ŷ + [9696] = "\u042B", // Ы + [9698] = "\u044B", // ы + [9700] = "\u00A5", // ¥ + [9701] = "\u00A5", // ¥ + [9704] = "\uD835\uDD1C", // 𝔜 + [9707] = "\uD835\uDD36", // 𝔶 + [9711] = "\u0407", // Ї + [9715] = "\u0457", // ї + [9719] = "\uD835\uDD50", // 𝕐 + [9723] = "\uD835\uDD6A", // 𝕪 + [9727] = "\uD835\uDCB4", // 𝒴 + [9731] = "\uD835\uDCCE", // 𝓎 + [9735] = "\u042E", // Ю + [9739] = "\u044E", // ю + [9743] = "\u0178", // Ÿ + [9745] = "\u00FF", // ÿ + [9746] = "\u00FF", // ÿ + [9753] = "\u0179", // Ź + [9760] = "\u017A", // ź + [9766] = "\u017D", // Ž + [9772] = "\u017E", // ž + [9774] = "\u0417", // З + [9776] = "\u0437", // з + [9780] = "\u017B", // Ż + [9784] = "\u017C", // ż + [9790] = "\u2128", // ℨ + [9804] = "\u200B", // ​ + [9807] = "\u0396", // Ζ + [9810] = "\u03B6", // ζ + [9813] = "\u2128", // ℨ + [9816] = "\uD835\uDD37", // 𝔷 + [9820] = "\u0416", // Ж + [9824] = "\u0436", // ж + [9831] = "\u21DD", // ⇝ + [9835] = "\u2124", // ℤ + [9839] = "\uD835\uDD6B", // 𝕫 + [9843] = "\uD835\uDCB5", // 𝒵 + [9847] = "\uD835\uDCCF", // 𝓏 + [9850] = "\u200D", // + [9853] = "\u200C", // + }; + } + + static int BinarySearchNextState (Transition[] transitions, int state) + { + int min = 0, max = transitions.Length; + + do { + int i = min + ((max - min) / 2); + + if (state > transitions[i].From) { + min = i + 1; + } else if (state < transitions[i].From) { + max = i; + } else { + return transitions[i].To; + } + } while (min < max); + + return -1; + } bool PushNamedEntity (char c) { - int state = states[index - 1]; + int next, state = states[index - 1]; + Transition[] table = null; switch (c) { - case '1': - switch (state) { - case 436: state = 437; break; // &blk -> &blk1 - case 1791: state = 1792; break; // &emsp -> &emsp1 - case 2085: state = 2086; break; // &frac -> &frac1 - case 6500: state = 6501; break; // &sup -> ¹ - default: return false; - } - break; - case '2': - switch (state) { - case 437: state = 438; break; // &blk1 -> &blk12 - case 2085: state = 2093; break; // &frac -> &frac2 - case 2086: state = 2087; break; // &frac1 -> ½ - case 6500: state = 6502; break; // &sup -> ² - default: return false; - } - break; - case '3': - switch (state) { - case 436: state = 440; break; // &blk -> &blk3 - case 1792: state = 1793; break; // &emsp1 -> &emsp13 - case 2085: state = 2096; break; // &frac -> &frac3 - case 2086: state = 2088; break; // &frac1 -> &frac13 - case 2093: state = 2094; break; // &frac2 -> &frac23 - case 6500: state = 6503; break; // &sup -> ³ - default: return false; - } - break; - case '4': - switch (state) { - case 437: state = 439; break; // &blk1 -> &blk14 - case 440: state = 441; break; // &blk3 -> &blk34 - case 1792: state = 1794; break; // &emsp1 -> &emsp14 - case 2085: state = 2100; break; // &frac -> &frac4 - case 2086: state = 2089; break; // &frac1 -> ¼ - case 2096: state = 2097; break; // &frac3 -> ¾ - case 6632: state = 6633; break; // &there -> &there4 - default: return false; - } - break; - case '5': - switch (state) { - case 2085: state = 2102; break; // &frac -> &frac5 - case 2086: state = 2090; break; // &frac1 -> &frac15 - case 2093: state = 2095; break; // &frac2 -> &frac25 - case 2096: state = 2098; break; // &frac3 -> &frac35 - case 2100: state = 2101; break; // &frac4 -> &frac45 - default: return false; - } - break; - case '6': - switch (state) { - case 2086: state = 2091; break; // &frac1 -> &frac16 - case 2102: state = 2103; break; // &frac5 -> &frac56 - default: return false; - } - break; - case '7': - switch (state) { - case 2085: state = 2105; break; // &frac -> &frac7 - default: return false; - } - break; - case '8': - switch (state) { - case 2086: state = 2092; break; // &frac1 -> &frac18 - case 2096: state = 2099; break; // &frac3 -> &frac38 - case 2102: state = 2104; break; // &frac5 -> &frac58 - case 2105: state = 2106; break; // &frac7 -> &frac78 - default: return false; - } - break; - case 'A': - switch (state) { - case 0: state = 1; break; // & -> &A - case 1097: state = 1109; break; // &d -> &dA - case 1200: state = 1201; break; // &Diacritical -> &DiacriticalA - case 1212: state = 1213; break; // &DiacriticalDouble -> &DiacriticalDoubleA - case 1366: state = 1367; break; // &DoubleDown -> &DoubleDownA - case 1375: state = 1376; break; // &DoubleLeft -> &DoubleLeftA - case 1385: state = 1386; break; // &DoubleLeftRight -> &DoubleLeftRightA - case 1400: state = 1401; break; // &DoubleLongLeft -> &DoubleLongLeftA - case 1410: state = 1411; break; // &DoubleLongLeftRight -> &DoubleLongLeftRightA - case 1420: state = 1421; break; // &DoubleLongRight -> &DoubleLongRightA - case 1430: state = 1431; break; // &DoubleRight -> &DoubleRightA - case 1440: state = 1441; break; // &DoubleUp -> &DoubleUpA - case 1449: state = 1450; break; // &DoubleUpDown -> &DoubleUpDownA - case 1467: state = 1468; break; // &Down -> &DownA - case 1489: state = 1490; break; // &DownArrowUp -> &DownArrowUpA - case 1584: state = 1585; break; // &DownTee -> &DownTeeA - case 2058: state = 2059; break; // &For -> &ForA - case 2351: state = 2368; break; // &H -> &HA - case 2356: state = 2377; break; // &h -> &hA - case 2881: state = 2882; break; // &l -> &lA - case 3035: state = 3036; break; // &Left -> &LeftA - case 3071: state = 3072; break; // &LeftArrowRight -> &LeftArrowRightA - case 3153: state = 3154; break; // &LeftRight -> &LeftRightA - case 3206: state = 3207; break; // &LeftTee -> &LeftTeeA - case 3481: state = 3482; break; // &LongLeft -> &LongLeftA - case 3511: state = 3512; break; // &LongLeftRight -> &LongLeftRightA - case 3547: state = 3548; break; // &LongRight -> &LongRightA - case 3616: state = 3617; break; // &LowerLeft -> &LowerLeftA - case 3626: state = 3627; break; // &LowerRight -> &LowerRightA - case 3970: state = 3975; break; // &ne -> &neA - case 4101: state = 4102; break; // &nh -> &nhA - case 4121: state = 4122; break; // &nl -> &nlA - case 4621: state = 4622; break; // &nr -> &nrA - case 4792: state = 4793; break; // &nvl -> &nvlA - case 4801: state = 4802; break; // &nvr -> &nvrA - case 4812: state = 4817; break; // &nw -> &nwA - case 5397: state = 5398; break; // &r -> &rA - case 5620: state = 5621; break; // &Right -> &RightA - case 5657: state = 5658; break; // &RightArrowLeft -> &RightArrowLeftA - case 5766: state = 5767; break; // &RightTee -> &RightTeeA - case 6053: state = 6058; break; // &se -> &seA - case 6111: state = 6112; break; // &ShortDown -> &ShortDownA - case 6120: state = 6121; break; // &ShortLeft -> &ShortLeftA - case 6144: state = 6145; break; // &ShortRight -> &ShortRightA - case 6151: state = 6152; break; // &ShortUp -> &ShortUpA - case 6564: state = 6569; break; // &sw -> &swA - case 6756: state = 6757; break; // &TR -> &TRA - case 6879: state = 6887; break; // &u -> &uA - case 7031: state = 7032; break; // &Up -> &UpA - case 7054: state = 7055; break; // &UpArrowDown -> &UpArrowDownA - case 7063: state = 7064; break; // &UpDown -> &UpDownA - case 7123: state = 7124; break; // &UpperLeft -> &UpperLeftA - case 7133: state = 7134; break; // &UpperRight -> &UpperRightA - case 7152: state = 7153; break; // &UpTee -> &UpTeeA - case 7223: state = 7258; break; // &v -> &vA - case 7513: state = 7514; break; // &xh -> &xhA - case 7522: state = 7523; break; // &xl -> &xlA - case 7551: state = 7552; break; // &xr -> &xrA - case 7584: state = 7596; break; // &Y -> &YA - default: return false; - } - break; - case 'B': - switch (state) { - case 0: state = 247; break; // & -> &B - case 1462: state = 1463; break; // &DoubleVertical -> &DoubleVerticalB - case 1467: state = 1495; break; // &Down -> &DownB - case 1472: state = 1485; break; // &DownArrow -> &DownArrowB - case 1555: state = 1556; break; // &DownLeftVector -> &DownLeftVectorB - case 1578: state = 1579; break; // &DownRightVector -> &DownRightVectorB - case 2881: state = 2966; break; // &l -> &lB - case 3040: state = 3041; break; // &LeftAngle -> &LeftAngleB - case 3051: state = 3064; break; // &LeftArrow -> &LeftArrowB - case 3093: state = 3094; break; // &LeftDouble -> &LeftDoubleB - case 3117: state = 3118; break; // &LeftDownVector -> &LeftDownVectorB - case 3234: state = 3235; break; // &LeftTriangle -> &LeftTriangleB - case 3269: state = 3270; break; // &LeftUpVector -> &LeftUpVectorB - case 3278: state = 3279; break; // &LeftVector -> &LeftVectorB - case 4190: state = 4191; break; // &No -> &NoB - case 4196: state = 4197; break; // &Non -> &NonB - case 4244: state = 4245; break; // &NotDoubleVertical -> &NotDoubleVerticalB - case 4354: state = 4355; break; // &NotLeftTriangle -> &NotLeftTriangleB - case 4478: state = 4479; break; // &NotRightTriangle -> &NotRightTriangleB - case 4593: state = 4594; break; // &NotVertical -> &NotVerticalB - case 5061: state = 5062; break; // &Over -> &OverB - case 5397: state = 5480; break; // &r -> &rB - case 5405: state = 5476; break; // &R -> &RB - case 5625: state = 5626; break; // &RightAngle -> &RightAngleB - case 5636: state = 5651; break; // &RightArrow -> &RightArrowB - case 5679: state = 5680; break; // &RightDouble -> &RightDoubleB - case 5703: state = 5704; break; // &RightDownVector -> &RightDownVectorB - case 5794: state = 5795; break; // &RightTriangle -> &RightTriangleB - case 5829: state = 5830; break; // &RightUpVector -> &RightUpVectorB - case 5838: state = 5839; break; // &RightVector -> &RightVectorB - case 6990: state = 6991; break; // &Under -> &UnderB - case 7036: state = 7048; break; // &UpArrow -> &UpArrowB - case 7223: state = 7311; break; // &v -> &vB - case 7362: state = 7363; break; // &Vertical -> &VerticalB - default: return false; - } - break; - case 'C': - switch (state) { - case 0: state = 583; break; // & -> &C - case 810: state = 811; break; // &Clockwise -> &ClockwiseC - case 827: state = 828; break; // &Close -> &CloseC - case 936: state = 937; break; // &Counter -> &CounterC - case 945: state = 946; break; // &CounterClockwise -> &CounterClockwiseC - case 1005: state = 1012; break; // &Cup -> &CupC - case 1346: state = 1347; break; // &Double -> &DoubleC - case 2699: state = 2700; break; // &Invisible -> &InvisibleC - case 3035: state = 3081; break; // &Left -> &LeftC - case 4215: state = 4217; break; // &Not -> &NotC - case 4227: state = 4228; break; // &NotCup -> &NotCupC - case 4962: state = 4963; break; // &Open -> &OpenC - case 5620: state = 5667; break; // &Right -> &RightC - case 6092: state = 6093; break; // &SH -> &SHC - case 6195: state = 6196; break; // &Small -> &SmallC - default: return false; - } - break; - case 'D': - switch (state) { - case 0: state = 1091; break; // & -> &D - case 470: state = 474; break; // &box -> &boxD - case 484: state = 486; break; // &boxH -> &boxHD - case 485: state = 488; break; // &boxh -> &boxhD - case 616: state = 617; break; // &Capital -> &CapitalD - case 628: state = 629; break; // &CapitalDifferential -> &CapitalDifferentialD - case 703: state = 704; break; // &Center -> &CenterD - case 769: state = 770; break; // &Circle -> &CircleD - case 832: state = 833; break; // &CloseCurly -> &CloseCurlyD - case 1091: state = 1141; break; // &D -> &DD - case 1200: state = 1206; break; // &Diacritical -> &DiacriticalD - case 1253: state = 1254; break; // &Differential -> &DifferentialD - case 1301: state = 1303; break; // &Dot -> &DotD - case 1346: state = 1362; break; // &Double -> &DoubleD - case 1440: state = 1446; break; // &DoubleUp -> &DoubleUpD - case 1662: state = 1694; break; // &e -> &eD - case 1694: state = 1695; break; // &eD -> &eDD - case 1707: state = 1708; break; // &ef -> &efD - case 1881: state = 1882; break; // &equiv -> &equivD - case 1882: state = 1883; break; // &equivD -> &equivDD - case 1890: state = 1894; break; // &er -> &erD - case 2369: state = 2370; break; // &HAR -> &HARD - case 2510: state = 2511; break; // &Hump -> &HumpD - case 3035: state = 3088; break; // &Left -> &LeftD - case 3244: state = 3245; break; // &LeftUp -> &LeftUpD - case 3745: state = 3788; break; // &m -> &mD - case 3788: state = 3789; break; // &mD -> &mDD - case 4215: state = 4231; break; // &Not -> &NotD - case 4319: state = 4320; break; // &NotHump -> &NotHumpD - case 4760: state = 4772; break; // &nv -> &nvD - case 4763: state = 4764; break; // &nV -> &nVD - case 4967: state = 4968; break; // &OpenCurly -> &OpenCurlyD - case 5102: state = 5103; break; // &Partial -> &PartialD - case 5620: state = 5674; break; // &Right -> &RightD - case 5804: state = 5805; break; // &RightUp -> &RightUpD - case 5970: state = 5971; break; // &Rule -> &RuleD - case 6107: state = 6108; break; // &Short -> &ShortD - case 6757: state = 6758; break; // &TRA -> &TRAD - case 6801: state = 6802; break; // &Triple -> &TripleD - case 7031: state = 7060; break; // &Up -> &UpD - case 7036: state = 7051; break; // &UpArrow -> &UpArrowD - case 7223: state = 7327; break; // &v -> &vD - case 7307: state = 7319; break; // &V -> &VD - default: return false; - } - break; - case 'E': - switch (state) { - case 0: state = 1656; break; // & -> &E - case 1: state = 38; break; // &A -> &AE - case 23: state = 25; break; // &ac -> &acE - case 143: state = 148; break; // &ap -> &apE - case 574: state = 575; break; // &bump -> &bumpE - case 733: state = 789; break; // &cir -> &cirE - case 1301: state = 1311; break; // &Dot -> &DotE - case 1953: state = 1954; break; // &Exponential -> &ExponentialE - case 2118: state = 2165; break; // &g -> &gE - case 2204: state = 2206; break; // &gl -> &glE - case 2208: state = 2215; break; // &gn -> &gnE - case 2237: state = 2238; break; // &Greater -> &GreaterE - case 2250: state = 2251; break; // &GreaterFull -> &GreaterFullE - case 2271: state = 2272; break; // &GreaterSlant -> &GreaterSlantE - case 2349: state = 2350; break; // &gvn -> &gvnE - case 2510: state = 2519; break; // &Hump -> &HumpE - case 2533: state = 2558; break; // &I -> &IE - case 2747: state = 2751; break; // &isin -> &isinE - case 2881: state = 3031; break; // &l -> &lE - case 3234: state = 3238; break; // &LeftTriangle -> &LeftTriangleE - case 3322: state = 3323; break; // &Less -> &LessE - case 3338: state = 3339; break; // &LessFull -> &LessFullE - case 3365: state = 3366; break; // &LessSlant -> &LessSlantE - case 3388: state = 3389; break; // &lg -> &lgE - case 3452: state = 3459; break; // &ln -> &lnE - case 3743: state = 3744; break; // &lvn -> &lvnE - case 3914: state = 3915; break; // &nap -> &napE - case 4081: state = 4082; break; // &ng -> &ngE - case 4121: state = 4130; break; // &nl -> &nlE - case 4215: state = 4248; break; // &Not -> &NotE - case 4275: state = 4276; break; // &NotGreater -> &NotGreaterE - case 4284: state = 4285; break; // &NotGreaterFull -> &NotGreaterFullE - case 4305: state = 4306; break; // &NotGreaterSlant -> &NotGreaterSlantE - case 4319: state = 4328; break; // &NotHump -> &NotHumpE - case 4334: state = 4338; break; // ¬in -> ¬inE - case 4354: state = 4358; break; // &NotLeftTriangle -> &NotLeftTriangleE - case 4364: state = 4365; break; // &NotLess -> &NotLessE - case 4385: state = 4386; break; // &NotLessSlant -> &NotLessSlantE - case 4437: state = 4438; break; // &NotPrecedes -> &NotPrecedesE - case 4447: state = 4448; break; // &NotPrecedesSlant -> &NotPrecedesSlantE - case 4459: state = 4460; break; // &NotReverse -> &NotReverseE - case 4478: state = 4482; break; // &NotRightTriangle -> &NotRightTriangleE - case 4498: state = 4499; break; // &NotSquareSubset -> &NotSquareSubsetE - case 4509: state = 4510; break; // &NotSquareSuperset -> &NotSquareSupersetE - case 4519: state = 4520; break; // &NotSubset -> &NotSubsetE - case 4530: state = 4531; break; // &NotSucceeds -> &NotSucceedsE - case 4540: state = 4541; break; // &NotSucceedsSlant -> &NotSucceedsSlantE - case 4556: state = 4557; break; // &NotSuperset -> &NotSupersetE - case 4566: state = 4567; break; // &NotTilde -> &NotTildeE - case 4575: state = 4576; break; // &NotTildeFull -> &NotTildeFullE - case 4696: state = 4697; break; // &nsub -> &nsubE - case 4709: state = 4710; break; // &nsup -> &nsupE - case 4827: state = 4872; break; // &O -> &OE - case 5216: state = 5222; break; // &pr -> &prE - case 5243: state = 5244; break; // &Precedes -> &PrecedesE - case 5253: state = 5254; break; // &PrecedesSlant -> &PrecedesSlantE - case 5289: state = 5292; break; // &prn -> &prnE - case 5405: state = 5554; break; // &R -> &RE - case 5561: state = 5562; break; // &Reverse -> &ReverseE - case 5580: state = 5581; break; // &ReverseUp -> &ReverseUpE - case 5794: state = 5798; break; // &RightTriangle -> &RightTriangleE - case 6002: state = 6015; break; // &sc -> &scE - case 6030: state = 6033; break; // &scn -> &scnE - case 6174: state = 6175; break; // &simg -> &simgE - case 6176: state = 6177; break; // &siml -> &simlE - case 6310: state = 6311; break; // &SquareSubset -> &SquareSubsetE - case 6321: state = 6322; break; // &SquareSuperset -> &SquareSupersetE - case 6384: state = 6388; break; // &sub -> &subE - case 6397: state = 6398; break; // &subn -> &subnE - case 6410: state = 6417; break; // &Subset -> &SubsetE - case 6451: state = 6452; break; // &Succeeds -> &SucceedsE - case 6461: state = 6462; break; // &SucceedsSlant -> &SucceedsSlantE - case 6500: state = 6510; break; // &sup -> &supE - case 6519: state = 6520; break; // &Superset -> &SupersetE - case 6539: state = 6540; break; // &supn -> &supnE - case 6699: state = 6704; break; // &Tilde -> &TildeE - case 6712: state = 6713; break; // &TildeFull -> &TildeFullE - case 6758: state = 6759; break; // &TRAD -> &TRADE - case 7031: state = 7087; break; // &Up -> &UpE - case 7429: state = 7430; break; // &vsubn -> &vsubnE - case 7433: state = 7434; break; // &vsupn -> &vsupnE - default: return false; - } - break; - case 'F': - switch (state) { - case 0: state = 1977; break; // & -> &F - case 157: state = 158; break; // &Apply -> &ApplyF - case 2237: state = 2247; break; // &Greater -> &GreaterF - case 3035: state = 3121; break; // &Left -> &LeftF - case 3322: state = 3335; break; // &Less -> &LessF - case 4275: state = 4281; break; // &NotGreater -> &NotGreaterF - case 4566: state = 4572; break; // &NotTilde -> &NotTildeF - case 5620: state = 5707; break; // &Right -> &RightF - case 6230: state = 6231; break; // &SO -> &SOF - case 6699: state = 6709; break; // &Tilde -> &TildeF - default: return false; - } - break; - case 'G': - switch (state) { - case 0: state = 2124; break; // & -> &G - case 1200: state = 1218; break; // &Diacritical -> &DiacriticalG - case 1795: state = 1796; break; // &EN -> &ENG - case 2237: state = 2256; break; // &Greater -> &GreaterG - case 3322: state = 3344; break; // &Less -> &LessG - case 3327: state = 3328; break; // &LessEqual -> &LessEqualG - case 3897: state = 4092; break; // &n -> &nG - case 4044: state = 4045; break; // &Nested -> &NestedG - case 4051: state = 4052; break; // &NestedGreater -> &NestedGreaterG - case 4215: state = 4269; break; // &Not -> &NotG - case 4275: state = 4290; break; // &NotGreater -> &NotGreaterG - case 4364: state = 4370; break; // &NotLess -> &NotLessG - case 4401: state = 4402; break; // &NotNested -> &NotNestedG - case 4408: state = 4409; break; // &NotNestedGreater -> &NotNestedGreaterG - case 5554: state = 5555; break; // &RE -> ® - default: return false; - } - break; - case 'H': - switch (state) { - case 0: state = 2351; break; // & -> &H - case 470: state = 484; break; // &box -> &boxH - case 518: state = 520; break; // &boxV -> &boxVH - case 519: state = 522; break; // &boxv -> &boxvH - case 583: state = 716; break; // &C -> &CH - case 1097: state = 1183; break; // &d -> &dH - case 1914: state = 1915; break; // &ET -> Ð - case 2514: state = 2515; break; // &HumpDown -> &HumpDownH - case 2825: state = 2857; break; // &K -> &KH - case 2881: state = 3390; break; // &l -> &lH - case 4215: state = 4316; break; // &Not -> &NotH - case 4323: state = 4324; break; // &NotHumpDown -> &NotHumpDownH - case 4760: state = 4783; break; // &nv -> &nvH - case 5397: state = 5604; break; // &r -> &rH - case 5985: state = 6092; break; // &S -> &SH - case 6093: state = 6094; break; // &SHC -> &SHCH - case 6583: state = 6689; break; // &T -> &TH - case 6827: state = 6831; break; // &TS -> &TSH - case 6879: state = 6954; break; // &u -> &uH - case 7645: state = 7701; break; // &Z -> &ZH - default: return false; - } - break; - case 'I': - switch (state) { - case 0: state = 2533; break; // & -> &I - case 817: state = 818; break; // &ClockwiseContour -> &ClockwiseContourI - case 904: state = 905; break; // &Contour -> &ContourI - case 952: state = 953; break; // &CounterClockwiseContour -> &CounterClockwiseContourI - case 1353: state = 1354; break; // &DoubleContour -> &DoubleContourI - case 2619: state = 2620; break; // &Imaginary -> &ImaginaryI - case 5901: state = 5902; break; // &Round -> &RoundI - case 6289: state = 6293; break; // &Square -> &SquareI - case 7584: state = 7616; break; // &Y -> &YI - default: return false; - } - break; - case 'J': - switch (state) { - case 0: state = 2777; break; // & -> &J - case 1091: state = 1277; break; // &D -> &DJ - case 2124: state = 2198; break; // &G -> &GJ - case 2533: state = 2596; break; // &I -> &IJ - case 2825: state = 2863; break; // &K -> &KJ - case 2886: state = 3402; break; // &L -> &LJ - case 3902: state = 4115; break; // &N -> &NJ - default: return false; - } - break; - case 'K': - switch (state) { - case 0: state = 2825; break; // & -> &K - default: return false; - } - break; - case 'L': - switch (state) { - case 0: state = 2886; break; // & -> &L - case 474: state = 475; break; // &boxD -> &boxDL - case 477: state = 478; break; // &boxd -> &boxdL - case 508: state = 509; break; // &boxU -> &boxUL - case 511: state = 512; break; // &boxu -> &boxuL - case 518: state = 524; break; // &boxV -> &boxVL - case 519: state = 526; break; // &boxv -> &boxvL - case 1346: state = 1372; break; // &Double -> &DoubleL - case 1396: state = 1397; break; // &DoubleLong -> &DoubleLongL - case 1467: state = 1526; break; // &Down -> &DownL - case 2237: state = 2263; break; // &Greater -> &GreaterL - case 2242: state = 2243; break; // &GreaterEqual -> &GreaterEqualL - case 2485: state = 2486; break; // &Horizontal -> &HorizontalL - case 3322: state = 3354; break; // &Less -> &LessL - case 3477: state = 3478; break; // &Long -> &LongL - case 3612: state = 3613; break; // &Lower -> &LowerL - case 3897: state = 4132; break; // &n -> &nL - case 4044: state = 4059; break; // &Nested -> &NestedL - case 4062: state = 4063; break; // &NestedLess -> &NestedLessL - case 4067: state = 4068; break; // &New -> &NewL - case 4215: state = 4343; break; // &Not -> &NotL - case 4275: state = 4297; break; // &NotGreater -> &NotGreaterL - case 4364: state = 4377; break; // &NotLess -> &NotLessL - case 4401: state = 4416; break; // &NotNested -> &NotNestedL - case 4419: state = 4420; break; // &NotNestedLess -> &NotNestedLessL - case 5636: state = 5654; break; // &RightArrow -> &RightArrowL - case 6107: state = 6117; break; // &Short -> &ShortL - case 7119: state = 7120; break; // &Upper -> &UpperL - case 7362: state = 7366; break; // &Vertical -> &VerticalL - default: return false; - } - break; - case 'M': - switch (state) { - case 0: state = 3755; break; // & -> &M - case 1: state = 85; break; // &A -> &AM - case 769: state = 775; break; // &Circle -> &CircleM - case 3990: state = 3991; break; // &Negative -> &NegativeM - case 5174: state = 5175; break; // &Plus -> &PlusM - default: return false; - } - break; - case 'N': - switch (state) { - case 0: state = 3902; break; // & -> &N - case 222: state = 451; break; // &b -> &bN - case 1656: state = 1795; break; // &E -> &EN - case 4215: state = 4396; break; // &Not -> &NotN - case 6691: state = 6692; break; // &THOR -> Þ - default: return false; - } - break; - case 'O': - switch (state) { - case 0: state = 4827; break; // & -> &O - case 583: state = 926; break; // &C -> &CO - case 2533: state = 2710; break; // &I -> &IO - case 5392: state = 5393; break; // &QU -> &QUO - case 5985: state = 6230; break; // &S -> &SO - case 6689: state = 6690; break; // &TH -> &THO - default: return false; - } - break; - case 'P': - switch (state) { - case 0: state = 5096; break; // & -> &P - case 85: state = 86; break; // &AM -> & - case 769: state = 780; break; // &Circle -> &CircleP - case 926: state = 927; break; // &CO -> &COP - case 2302: state = 2303; break; // >l -> >lP - case 3717: state = 3721; break; // <r -> <rP - case 3850: state = 3851; break; // &Minus -> &MinusP - case 4215: state = 4430; break; // &Not -> &NotP - case 5061: state = 5072; break; // &Over -> &OverP - case 6990: state = 7001; break; // &Under -> &UnderP - case 7014: state = 7015; break; // &Union -> &UnionP - default: return false; - } - break; - case 'Q': - switch (state) { - case 0: state = 5348; break; // & -> &Q - case 832: state = 844; break; // &CloseCurly -> &CloseCurlyQ - case 838: state = 839; break; // &CloseCurlyDouble -> &CloseCurlyDoubleQ - case 4967: state = 4979; break; // &OpenCurly -> &OpenCurlyQ - case 4973: state = 4974; break; // &OpenCurlyDouble -> &OpenCurlyDoubleQ - default: return false; - } - break; - case 'R': - switch (state) { - case 0: state = 5405; break; // & -> &R - case 474: state = 480; break; // &boxD -> &boxDR - case 477: state = 482; break; // &boxd -> &boxdR - case 508: state = 514; break; // &boxU -> &boxUR - case 511: state = 516; break; // &boxu -> &boxuR - case 518: state = 528; break; // &boxV -> &boxVR - case 519: state = 530; break; // &boxv -> &boxvR - case 753: state = 773; break; // &circled -> &circledR - case 1346: state = 1426; break; // &Double -> &DoubleR - case 1375: state = 1381; break; // &DoubleLeft -> &DoubleLeftR - case 1396: state = 1416; break; // &DoubleLong -> &DoubleLongR - case 1400: state = 1406; break; // &DoubleLongLeft -> &DoubleLongLeftR - case 1467: state = 1559; break; // &Down -> &DownR - case 1529: state = 1530; break; // &DownLeft -> &DownLeftR - case 2368: state = 2369; break; // &HA -> &HAR - case 3035: state = 3149; break; // &Left -> &LeftR - case 3051: state = 3067; break; // &LeftArrow -> &LeftArrowR - case 3477: state = 3543; break; // &Long -> &LongR - case 3481: state = 3507; break; // &LongLeft -> &LongLeftR - case 3612: state = 3622; break; // &Lower -> &LowerR - case 3897: state = 4630; break; // &n -> &nR - case 4215: state = 4453; break; // &Not -> &NotR - case 6107: state = 6140; break; // &Short -> &ShortR - case 6583: state = 6756; break; // &T -> &TR - case 6690: state = 6691; break; // &THO -> &THOR - case 7119: state = 7129; break; // &Upper -> &UpperR - default: return false; - } - break; - case 'S': - switch (state) { - case 0: state = 5985; break; // & -> &S - case 753: state = 774; break; // &circled -> &circledS - case 1091: state = 1610; break; // &D -> &DS - case 1762: state = 1763; break; // &Empty -> &EmptyS - case 1767: state = 1768; break; // &EmptySmall -> &EmptySmallS - case 1778: state = 1779; break; // &EmptyVery -> &EmptyVeryS - case 1783: state = 1784; break; // &EmptyVerySmall -> &EmptyVerySmallS - case 2009: state = 2010; break; // &Filled -> &FilledS - case 2014: state = 2015; break; // &FilledSmall -> &FilledSmallS - case 2024: state = 2025; break; // &FilledVery -> &FilledVeryS - case 2029: state = 2030; break; // &FilledVerySmall -> &FilledVerySmallS - case 2237: state = 2267; break; // &Greater -> &GreaterS - case 2422: state = 2423; break; // &Hilbert -> &HilbertS - case 3322: state = 3361; break; // &Less -> &LessS - case 3808: state = 3809; break; // &Medium -> &MediumS - case 3996: state = 3997; break; // &NegativeMedium -> &NegativeMediumS - case 4006: state = 4007; break; // &NegativeThick -> &NegativeThickS - case 4012: state = 4013; break; // &NegativeThin -> &NegativeThinS - case 4025: state = 4026; break; // &NegativeVeryThin -> &NegativeVeryThinS - case 4204: state = 4205; break; // &NonBreaking -> &NonBreakingS - case 4215: state = 4487; break; // &Not -> &NotS - case 4275: state = 4301; break; // &NotGreater -> &NotGreaterS - case 4364: state = 4381; break; // &NotLess -> &NotLessS - case 4437: state = 4443; break; // &NotPrecedes -> &NotPrecedesS - case 4492: state = 4493; break; // &NotSquare -> &NotSquareS - case 4530: state = 4536; break; // &NotSucceeds -> &NotSucceedsS - case 4833: state = 5014; break; // &o -> &oS - case 5243: state = 5249; break; // &Precedes -> &PrecedesS - case 6289: state = 6305; break; // &Square -> &SquareS - case 6451: state = 6457; break; // &Succeeds -> &SucceedsS - case 6583: state = 6827; break; // &T -> &TS - case 6668: state = 6669; break; // &Thick -> &ThickS - case 6677: state = 6678; break; // &Thin -> &ThinS - case 7362: state = 7370; break; // &Vertical -> &VerticalS - case 7388: state = 7389; break; // &VeryThin -> &VeryThinS - case 7687: state = 7688; break; // &ZeroWidth -> &ZeroWidthS - default: return false; - } - break; - case 'T': - switch (state) { - case 0: state = 6583; break; // & -> &T - case 769: state = 784; break; // &Circle -> &CircleT - case 1200: state = 1223; break; // &Diacritical -> &DiacriticalT - case 1375: state = 1391; break; // &DoubleLeft -> &DoubleLeftT - case 1430: state = 1436; break; // &DoubleRight -> &DoubleRightT - case 1467: state = 1582; break; // &Down -> &DownT - case 1529: state = 1541; break; // &DownLeft -> &DownLeftT - case 1563: state = 1564; break; // &DownRight -> &DownRightT - case 1656: state = 1914; break; // &E -> &ET - case 1859: state = 1864; break; // &Equal -> &EqualT - case 2124: state = 2292; break; // &G -> > - case 2237: state = 2277; break; // &Greater -> &GreaterT - case 2699: state = 2705; break; // &Invisible -> &InvisibleT - case 2886: state = 3690; break; // &L -> < - case 3035: state = 3204; break; // &Left -> &LeftT - case 3102: state = 3103; break; // &LeftDown -> &LeftDownT - case 3244: state = 3255; break; // &LeftUp -> &LeftUpT - case 3322: state = 3371; break; // &Less -> &LessT - case 3990: state = 4002; break; // &Negative -> &NegativeT - case 4021: state = 4022; break; // &NegativeVery -> &NegativeVeryT - case 4215: state = 4562; break; // &Not -> &NotT - case 4258: state = 4259; break; // &NotEqual -> &NotEqualT - case 4275: state = 4311; break; // &NotGreater -> &NotGreaterT - case 4346: state = 4347; break; // &NotLeft -> &NotLeftT - case 4364: state = 4391; break; // &NotLess -> &NotLessT - case 4470: state = 4471; break; // &NotRight -> &NotRightT - case 4530: state = 4546; break; // &NotSucceeds -> &NotSucceedsT - case 4566: state = 4581; break; // &NotTilde -> &NotTildeT - case 5243: state = 5259; break; // &Precedes -> &PrecedesT - case 5393: state = 5394; break; // &QUO -> " - case 5620: state = 5764; break; // &Right -> &RightT - case 5688: state = 5689; break; // &RightDown -> &RightDownT - case 5804: state = 5815; break; // &RightUp -> &RightUpT - case 6231: state = 6232; break; // &SOF -> &SOFT - case 6451: state = 6467; break; // &Succeeds -> &SucceedsT - case 6490: state = 6491; break; // &Such -> &SuchT - case 6699: state = 6718; break; // &Tilde -> &TildeT - case 7031: state = 7150; break; // &Up -> &UpT - case 7362: state = 7379; break; // &Vertical -> &VerticalT - case 7384: state = 7385; break; // &Very -> &VeryT - default: return false; - } - break; - case 'U': - switch (state) { - case 0: state = 6873; break; // & -> &U - case 470: state = 508; break; // &box -> &boxU - case 484: state = 490; break; // &boxH -> &boxHU - case 485: state = 492; break; // &boxh -> &boxhU - case 1346: state = 1439; break; // &Double -> &DoubleU - case 1472: state = 1488; break; // &DownArrow -> &DownArrowU - case 3035: state = 3243; break; // &Left -> &LeftU - case 5348: state = 5392; break; // &Q -> &QU - case 5561: state = 5579; break; // &Reverse -> &ReverseU - case 5620: state = 5803; break; // &Right -> &RightU - case 6107: state = 6150; break; // &Short -> &ShortU - case 6289: state = 6327; break; // &Square -> &SquareU - case 7584: state = 7634; break; // &Y -> &YU - default: return false; - } - break; - case 'V': - switch (state) { - case 0: state = 7307; break; // & -> &V - case 470: state = 518; break; // &box -> &boxV - case 1346: state = 1455; break; // &Double -> &DoubleV - case 1529: state = 1550; break; // &DownLeft -> &DownLeftV - case 1534: state = 1535; break; // &DownLeftRight -> &DownLeftRightV - case 1543: state = 1544; break; // &DownLeftTee -> &DownLeftTeeV - case 1563: state = 1573; break; // &DownRight -> &DownRightV - case 1566: state = 1567; break; // &DownRightTee -> &DownRightTeeV - case 1762: state = 1775; break; // &Empty -> &EmptyV - case 2009: state = 2021; break; // &Filled -> &FilledV - case 3035: state = 3273; break; // &Left -> &LeftV - case 3102: state = 3112; break; // &LeftDown -> &LeftDownV - case 3105: state = 3106; break; // &LeftDownTee -> &LeftDownTeeV - case 3153: state = 3198; break; // &LeftRight -> &LeftRightV - case 3206: state = 3212; break; // &LeftTee -> &LeftTeeV - case 3244: state = 3264; break; // &LeftUp -> &LeftUpV - case 3248: state = 3249; break; // &LeftUpDown -> &LeftUpDownV - case 3257: state = 3258; break; // &LeftUpTee -> &LeftUpTeeV - case 3897: state = 4763; break; // &n -> &nV - case 3990: state = 4018; break; // &Negative -> &NegativeV - case 4215: state = 4586; break; // &Not -> &NotV - case 4236: state = 4237; break; // &NotDouble -> &NotDoubleV - case 5620: state = 5833; break; // &Right -> &RightV - case 5688: state = 5698; break; // &RightDown -> &RightDownV - case 5691: state = 5692; break; // &RightDownTee -> &RightDownTeeV - case 5766: state = 5772; break; // &RightTee -> &RightTeeV - case 5804: state = 5824; break; // &RightUp -> &RightUpV - case 5808: state = 5809; break; // &RightUpDown -> &RightUpDownV - case 5817: state = 5818; break; // &RightUpTee -> &RightUpTeeV - default: return false; - } - break; - case 'W': - switch (state) { - case 0: state = 7447; break; // & -> &W - case 7682: state = 7683; break; // &Zero -> &ZeroW - default: return false; - } - break; - case 'X': - switch (state) { - case 0: state = 7508; break; // & -> &X - default: return false; - } - break; - case 'Y': - switch (state) { - case 0: state = 7584; break; // & -> &Y - case 927: state = 928; break; // &COP -> © - default: return false; - } - break; - case 'Z': - switch (state) { - case 0: state = 7645; break; // & -> &Z - case 1091: state = 1644; break; // &D -> &DZ - default: return false; - } - break; - case 'a': - switch (state) { - case 0: state = 7; break; // & -> &a - case 1: state = 2; break; // &A -> &Aa - case 7: state = 8; break; // &a -> &aa - case 51: state = 52; break; // &Agr -> &Agra - case 56: state = 57; break; // &agr -> &agra - case 70: state = 71; break; // &Alph -> &Alpha - case 73: state = 74; break; // &alph -> &alpha - case 75: state = 76; break; // &Am -> &Ama - case 79: state = 80; break; // &am -> &ama - case 91: state = 92; break; // &and -> &anda - case 108: state = 109; break; // &angmsd -> &angmsda - case 109: state = 110; break; // &angmsda -> &angmsdaa - case 127: state = 128; break; // &angz -> &angza - case 143: state = 144; break; // &ap -> &apa - case 222: state = 223; break; // &b -> &ba - case 247: state = 248; break; // &B -> &Ba - case 252: state = 253; break; // &Backsl -> &Backsla - case 289: state = 290; break; // &bec -> &beca - case 294: state = 295; break; // &Bec -> &Beca - case 320: state = 321; break; // &Bet -> &Beta - case 322: state = 323; break; // &bet -> &beta - case 335: state = 336; break; // &bigc -> &bigca - case 361: state = 362; break; // &bigst -> &bigsta - case 366: state = 367; break; // &bigtri -> &bigtria - case 391: state = 392; break; // &bk -> &bka - case 396: state = 397; break; // &bl -> &bla - case 409: state = 410; break; // &blacksqu -> &blacksqua - case 415: state = 416; break; // &blacktri -> &blacktria - case 546: state = 547; break; // &brvb -> &brvba - case 583: state = 584; break; // &C -> &Ca - case 589: state = 590; break; // &c -> &ca - case 596: state = 597; break; // &cap -> &capa - case 605: state = 606; break; // &capc -> &capca - case 614: state = 615; break; // &Capit -> &Capita - case 626: state = 627; break; // &CapitalDifferenti -> &CapitalDifferentia - case 641: state = 642; break; // &cc -> &cca - case 645: state = 646; break; // &Cc -> &Cca - case 691: state = 692; break; // &Cedill -> &Cedilla - case 725: state = 726; break; // &checkm -> &checkma - case 738: state = 739; break; // &circle -> &circlea - case 753: state = 754; break; // &circled -> &circleda - case 761: state = 762; break; // &circledd -> &circledda - case 823: state = 824; break; // &ClockwiseContourIntegr -> &ClockwiseContourIntegra - case 868: state = 869; break; // &comm -> &comma - case 910: state = 911; break; // &ContourIntegr -> &ContourIntegra - case 958: state = 959; break; // &CounterClockwiseContourIntegr -> &CounterClockwiseContourIntegra - case 961: state = 962; break; // &cr -> &cra - case 988: state = 989; break; // &cud -> &cuda - case 999: state = 1000; break; // &cul -> &cula - case 1009: state = 1010; break; // &cupbrc -> &cupbrca - case 1012: state = 1013; break; // &CupC -> &CupCa - case 1015: state = 1016; break; // &cupc -> &cupca - case 1026: state = 1027; break; // &cur -> &cura - case 1055: state = 1056; break; // &curve -> &curvea - case 1091: state = 1092; break; // &D -> &Da - case 1097: state = 1098; break; // &d -> &da - case 1121: state = 1122; break; // &dbk -> &dbka - case 1126: state = 1127; break; // &dbl -> &dbla - case 1129: state = 1130; break; // &Dc -> &Dca - case 1134: state = 1135; break; // &dc -> &dca - case 1142: state = 1143; break; // &dd -> &dda - case 1152: state = 1153; break; // &DDotr -> &DDotra - case 1165: state = 1166; break; // &Delt -> &Delta - case 1168: state = 1169; break; // &delt -> &delta - case 1183: state = 1184; break; // &dH -> &dHa - case 1186: state = 1187; break; // &dh -> &dha - case 1191: state = 1192; break; // &Di -> &Dia - case 1198: state = 1199; break; // &Diacritic -> &Diacritica - case 1219: state = 1220; break; // &DiacriticalGr -> &DiacriticalGra - case 1228: state = 1229; break; // &di -> &dia - case 1251: state = 1252; break; // &Differenti -> &Differentia - case 1255: state = 1256; break; // &dig -> &diga - case 1258: state = 1259; break; // &digamm -> &digamma - case 1293: state = 1294; break; // &doll -> &dolla - case 1313: state = 1314; break; // &DotEqu -> &DotEqua - case 1327: state = 1328; break; // &dotsqu -> &dotsqua - case 1335: state = 1336; break; // &doubleb -> &doubleba - case 1359: state = 1360; break; // &DoubleContourIntegr -> &DoubleContourIntegra - case 1460: state = 1461; break; // &DoubleVertic -> &DoubleVertica - case 1463: state = 1464; break; // &DoubleVerticalB -> &DoubleVerticalBa - case 1467: state = 1473; break; // &Down -> &Downa - case 1479: state = 1480; break; // &down -> &downa - case 1485: state = 1486; break; // &DownArrowB -> &DownArrowBa - case 1503: state = 1504; break; // &downdown -> &downdowna - case 1510: state = 1511; break; // &downh -> &downha - case 1556: state = 1557; break; // &DownLeftVectorB -> &DownLeftVectorBa - case 1579: state = 1580; break; // &DownRightVectorB -> &DownRightVectorBa - case 1592: state = 1593; break; // &drbk -> &drbka - case 1631: state = 1632; break; // &du -> &dua - case 1635: state = 1636; break; // &duh -> &duha - case 1638: state = 1639; break; // &dw -> &dwa - case 1652: state = 1653; break; // &dzigr -> &dzigra - case 1656: state = 1657; break; // &E -> &Ea - case 1662: state = 1663; break; // &e -> &ea - case 1672: state = 1673; break; // &Ec -> &Eca - case 1677: state = 1678; break; // &ec -> &eca - case 1716: state = 1717; break; // &Egr -> &Egra - case 1720: state = 1721; break; // &egr -> &egra - case 1746: state = 1747; break; // &Em -> &Ema - case 1750: state = 1751; break; // &em -> &ema - case 1764: state = 1765; break; // &EmptySm -> &EmptySma - case 1770: state = 1771; break; // &EmptySmallSqu -> &EmptySmallSqua - case 1780: state = 1781; break; // &EmptyVerySm -> &EmptyVerySma - case 1786: state = 1787; break; // &EmptyVerySmallSqu -> &EmptyVerySmallSqua - case 1813: state = 1814; break; // &ep -> &epa - case 1845: state = 1846; break; // &eqsl -> &eqsla - case 1857: state = 1858; break; // &Equ -> &Equa - case 1860: state = 1861; break; // &equ -> &equa - case 1885: state = 1886; break; // &eqvp -> &eqvpa - case 1890: state = 1891; break; // &er -> &era - case 1910: state = 1911; break; // &Et -> &Eta - case 1912: state = 1913; break; // &et -> &eta - case 1939: state = 1940; break; // &expect -> &expecta - case 1951: state = 1952; break; // &Exponenti -> &Exponentia - case 1960: state = 1961; break; // &exponenti -> &exponentia - case 1964: state = 1965; break; // &f -> &fa - case 1983: state = 1984; break; // &fem -> &fema - case 2011: state = 2012; break; // &FilledSm -> &FilledSma - case 2017: state = 2018; break; // &FilledSmallSqu -> &FilledSmallSqua - case 2026: state = 2027; break; // &FilledVerySm -> &FilledVerySma - case 2032: state = 2033; break; // &FilledVerySmallSqu -> &FilledVerySmallSqua - case 2040: state = 2041; break; // &fl -> &fla - case 2062: state = 2063; break; // &for -> &fora - case 2076: state = 2077; break; // &fp -> &fpa - case 2083: state = 2084; break; // &fr -> &fra - case 2118: state = 2119; break; // &g -> &ga - case 2124: state = 2125; break; // &G -> &Ga - case 2127: state = 2128; break; // &Gamm -> &Gamma - case 2130: state = 2131; break; // &gamm -> &gamma - case 2172: state = 2173; break; // &geqsl -> &geqsla - case 2204: state = 2205; break; // &gl -> &gla - case 2208: state = 2209; break; // &gn -> &gna - case 2228: state = 2229; break; // &gr -> &gra - case 2233: state = 2234; break; // &Gre -> &Grea - case 2240: state = 2241; break; // &GreaterEqu -> &GreaterEqua - case 2253: state = 2254; break; // &GreaterFullEqu -> &GreaterFullEqua - case 2258: state = 2259; break; // &GreaterGre -> &GreaterGrea - case 2268: state = 2269; break; // &GreaterSl -> &GreaterSla - case 2274: state = 2275; break; // &GreaterSlantEqu -> &GreaterSlantEqua - case 2303: state = 2304; break; // >lP -> >lPa - case 2311: state = 2312; break; // >r -> >ra - case 2351: state = 2352; break; // &H -> &Ha - case 2356: state = 2357; break; // &h -> &ha - case 2386: state = 2387; break; // &hb -> &hba - case 2397: state = 2398; break; // &he -> &hea - case 2424: state = 2425; break; // &HilbertSp -> &HilbertSpa - case 2430: state = 2431; break; // &hkse -> &hksea - case 2435: state = 2436; break; // &hksw -> &hkswa - case 2440: state = 2441; break; // &ho -> &hoa - case 2453: state = 2454; break; // &hookleft -> &hooklefta - case 2463: state = 2464; break; // &hookright -> &hookrighta - case 2475: state = 2476; break; // &horb -> &horba - case 2483: state = 2484; break; // &Horizont -> &Horizonta - case 2496: state = 2497; break; // &hsl -> &hsla - case 2521: state = 2522; break; // &HumpEqu -> &HumpEqua - case 2533: state = 2534; break; // &I -> &Ia - case 2539: state = 2540; break; // &i -> &ia - case 2573: state = 2574; break; // &Igr -> &Igra - case 2578: state = 2579; break; // &igr -> &igra - case 2594: state = 2595; break; // &iiot -> &iiota - case 2604: state = 2605; break; // &Im -> &Ima - case 2608: state = 2609; break; // &im -> &ima - case 2616: state = 2617; break; // &Imagin -> &Imagina - case 2625: state = 2626; break; // &imagp -> &imagpa - case 2642: state = 2643; break; // &inc -> &inca - case 2659: state = 2660; break; // &intc -> &intca - case 2669: state = 2670; break; // &Integr -> &Integra - case 2673: state = 2674; break; // &interc -> &interca - case 2684: state = 2685; break; // &intl -> &intla - case 2703: state = 2704; break; // &InvisibleComm -> &InvisibleComma - case 2727: state = 2728; break; // &Iot -> &Iota - case 2729: state = 2730; break; // &iot -> &iota - case 2793: state = 2794; break; // &jm -> &jma - case 2825: state = 2826; break; // &K -> &Ka - case 2828: state = 2829; break; // &Kapp -> &Kappa - case 2830: state = 2831; break; // &k -> &ka - case 2833: state = 2834; break; // &kapp -> &kappa - case 2881: state = 2892; break; // &l -> &la - case 2882: state = 2883; break; // &lA -> &lAa - case 2886: state = 2887; break; // &L -> &La - case 2904: state = 2905; break; // &lagr -> &lagra - case 2909: state = 2910; break; // &Lambd -> &Lambda - case 2913: state = 2914; break; // &lambd -> &lambda - case 2924: state = 2925; break; // &Lapl -> &Lapla - case 2956: state = 2961; break; // &lat -> &lata - case 2957: state = 2958; break; // &lAt -> &lAta - case 2966: state = 2967; break; // &lB -> &lBa - case 2970: state = 2971; break; // &lb -> &lba - case 2977: state = 2978; break; // &lbr -> &lbra - case 2988: state = 2989; break; // &Lc -> &Lca - case 2993: state = 2994; break; // &lc -> &lca - case 3013: state = 3014; break; // &ldc -> &ldca - case 3021: state = 3022; break; // &ldrdh -> &ldrdha - case 3026: state = 3027; break; // &ldrush -> &ldrusha - case 3035: state = 3052; break; // &Left -> &Lefta - case 3042: state = 3043; break; // &LeftAngleBr -> &LeftAngleBra - case 3058: state = 3059; break; // &left -> &lefta - case 3064: state = 3065; break; // &LeftArrowB -> &LeftArrowBa - case 3077: state = 3078; break; // &leftarrowt -> &leftarrowta - case 3095: state = 3096; break; // &LeftDoubleBr -> &LeftDoubleBra - case 3118: state = 3119; break; // &LeftDownVectorB -> &LeftDownVectorBa - case 3126: state = 3127; break; // &lefth -> &leftha - case 3142: state = 3143; break; // &leftleft -> &leftlefta - case 3163: state = 3164; break; // &Leftright -> &Leftrighta - case 3173: state = 3174; break; // &leftright -> &leftrighta - case 3180: state = 3181; break; // &leftrighth -> &leftrightha - case 3192: state = 3193; break; // &leftrightsquig -> &leftrightsquiga - case 3229: state = 3230; break; // &LeftTri -> &LeftTria - case 3235: state = 3236; break; // &LeftTriangleB -> &LeftTriangleBa - case 3240: state = 3241; break; // &LeftTriangleEqu -> &LeftTriangleEqua - case 3270: state = 3271; break; // &LeftUpVectorB -> &LeftUpVectorBa - case 3279: state = 3280; break; // &LeftVectorB -> &LeftVectorBa - case 3287: state = 3288; break; // &leqsl -> &leqsla - case 3302: state = 3303; break; // &less -> &lessa - case 3325: state = 3326; break; // &LessEqu -> &LessEqua - case 3330: state = 3331; break; // &LessEqualGre -> &LessEqualGrea - case 3341: state = 3342; break; // &LessFullEqu -> &LessFullEqua - case 3346: state = 3347; break; // &LessGre -> &LessGrea - case 3362: state = 3363; break; // &LessSl -> &LessSla - case 3368: state = 3369; break; // &LessSlantEqu -> &LessSlantEqua - case 3390: state = 3391; break; // &lH -> &lHa - case 3393: state = 3394; break; // &lh -> &lha - case 3409: state = 3410; break; // &ll -> &lla - case 3421: state = 3422; break; // &Lleft -> &Llefta - case 3427: state = 3428; break; // &llh -> &llha - case 3447: state = 3448; break; // &lmoust -> &lmousta - case 3452: state = 3453; break; // &ln -> &lna - case 3466: state = 3467; break; // &lo -> &loa - case 3490: state = 3491; break; // &Longleft -> &Longlefta - case 3501: state = 3502; break; // &longleft -> &longlefta - case 3521: state = 3522; break; // &Longleftright -> &Longleftrighta - case 3531: state = 3532; break; // &longleftright -> &longleftrighta - case 3537: state = 3538; break; // &longm -> &longma - case 3557: state = 3558; break; // &Longright -> &Longrighta - case 3567: state = 3568; break; // &longright -> &longrighta - case 3574: state = 3575; break; // &loop -> &loopa - case 3589: state = 3590; break; // &lop -> &lopa - case 3603: state = 3604; break; // &low -> &lowa - case 3607: state = 3608; break; // &lowb -> &lowba - case 3638: state = 3639; break; // &lp -> &lpa - case 3643: state = 3644; break; // &lr -> &lra - case 3653: state = 3654; break; // &lrh -> &lrha - case 3661: state = 3662; break; // &ls -> &lsa - case 3708: state = 3709; break; // <l -> <la - case 3721: state = 3722; break; // <rP -> <rPa - case 3728: state = 3729; break; // &lurdsh -> &lurdsha - case 3732: state = 3733; break; // &luruh -> &luruha - case 3745: state = 3746; break; // &m -> &ma - case 3755: state = 3756; break; // &M -> &Ma - case 3779: state = 3780; break; // &mcomm -> &mcomma - case 3784: state = 3785; break; // &md -> &mda - case 3792: state = 3793; break; // &me -> &mea - case 3798: state = 3799; break; // &measured -> &measureda - case 3810: state = 3811; break; // &MediumSp -> &MediumSpa - case 3831: state = 3832; break; // &mid -> &mida - case 3891: state = 3892; break; // &multim -> &multima - case 3894: state = 3895; break; // &mum -> &muma - case 3897: state = 3898; break; // &n -> &na - case 3900: state = 3901; break; // &nabl -> &nabla - case 3902: state = 3903; break; // &N -> &Na - case 3926: state = 3927; break; // &natur -> &natura - case 3937: state = 3938; break; // &nc -> &nca - case 3940: state = 3941; break; // &Nc -> &Nca - case 3966: state = 3967; break; // &nd -> &nda - case 3970: state = 3971; break; // &ne -> &nea - case 3985: state = 3986; break; // &Neg -> &Nega - case 3998: state = 3999; break; // &NegativeMediumSp -> &NegativeMediumSpa - case 4008: state = 4009; break; // &NegativeThickSp -> &NegativeThickSpa - case 4014: state = 4015; break; // &NegativeThinSp -> &NegativeThinSpa - case 4027: state = 4028; break; // &NegativeVeryThinSp -> &NegativeVeryThinSpa - case 4036: state = 4037; break; // &nese -> &nesea - case 4047: state = 4048; break; // &NestedGre -> &NestedGrea - case 4054: state = 4055; break; // &NestedGreaterGre -> &NestedGreaterGrea - case 4087: state = 4088; break; // &ngeqsl -> &ngeqsla - case 4101: state = 4105; break; // &nh -> &nha - case 4108: state = 4109; break; // &nhp -> &nhpa - case 4121: state = 4125; break; // &nl -> &nla - case 4135: state = 4136; break; // &nLeft -> &nLefta - case 4142: state = 4143; break; // &nleft -> &nlefta - case 4152: state = 4153; break; // &nLeftright -> &nLeftrighta - case 4162: state = 4163; break; // &nleftright -> &nleftrighta - case 4171: state = 4172; break; // &nleqsl -> &nleqsla - case 4193: state = 4194; break; // &NoBre -> &NoBrea - case 4199: state = 4200; break; // &NonBre -> &NonBrea - case 4206: state = 4207; break; // &NonBreakingSp -> &NonBreakingSpa - case 4228: state = 4229; break; // &NotCupC -> &NotCupCa - case 4242: state = 4243; break; // &NotDoubleVertic -> &NotDoubleVertica - case 4245: state = 4246; break; // &NotDoubleVerticalB -> &NotDoubleVerticalBa - case 4256: state = 4257; break; // &NotEqu -> &NotEqua - case 4271: state = 4272; break; // &NotGre -> &NotGrea - case 4278: state = 4279; break; // &NotGreaterEqu -> &NotGreaterEqua - case 4287: state = 4288; break; // &NotGreaterFullEqu -> &NotGreaterFullEqua - case 4292: state = 4293; break; // &NotGreaterGre -> &NotGreaterGrea - case 4302: state = 4303; break; // &NotGreaterSl -> &NotGreaterSla - case 4308: state = 4309; break; // &NotGreaterSlantEqu -> &NotGreaterSlantEqua - case 4330: state = 4331; break; // &NotHumpEqu -> &NotHumpEqua - case 4339: state = 4340; break; // ¬inv -> ¬inva - case 4349: state = 4350; break; // &NotLeftTri -> &NotLeftTria - case 4355: state = 4356; break; // &NotLeftTriangleB -> &NotLeftTriangleBa - case 4360: state = 4361; break; // &NotLeftTriangleEqu -> &NotLeftTriangleEqua - case 4367: state = 4368; break; // &NotLessEqu -> &NotLessEqua - case 4372: state = 4373; break; // &NotLessGre -> &NotLessGrea - case 4382: state = 4383; break; // &NotLessSl -> &NotLessSla - case 4388: state = 4389; break; // &NotLessSlantEqu -> &NotLessSlantEqua - case 4404: state = 4405; break; // &NotNestedGre -> &NotNestedGrea - case 4411: state = 4412; break; // &NotNestedGreaterGre -> &NotNestedGreaterGrea - case 4426: state = 4427; break; // ¬niv -> ¬niva - case 4440: state = 4441; break; // &NotPrecedesEqu -> &NotPrecedesEqua - case 4444: state = 4445; break; // &NotPrecedesSl -> &NotPrecedesSla - case 4450: state = 4451; break; // &NotPrecedesSlantEqu -> &NotPrecedesSlantEqua - case 4473: state = 4474; break; // &NotRightTri -> &NotRightTria - case 4479: state = 4480; break; // &NotRightTriangleB -> &NotRightTriangleBa - case 4484: state = 4485; break; // &NotRightTriangleEqu -> &NotRightTriangleEqua - case 4489: state = 4490; break; // &NotSqu -> &NotSqua - case 4501: state = 4502; break; // &NotSquareSubsetEqu -> &NotSquareSubsetEqua - case 4512: state = 4513; break; // &NotSquareSupersetEqu -> &NotSquareSupersetEqua - case 4522: state = 4523; break; // &NotSubsetEqu -> &NotSubsetEqua - case 4533: state = 4534; break; // &NotSucceedsEqu -> &NotSucceedsEqua - case 4537: state = 4538; break; // &NotSucceedsSl -> &NotSucceedsSla - case 4543: state = 4544; break; // &NotSucceedsSlantEqu -> &NotSucceedsSlantEqua - case 4559: state = 4560; break; // &NotSupersetEqu -> &NotSupersetEqua - case 4569: state = 4570; break; // &NotTildeEqu -> &NotTildeEqua - case 4578: state = 4579; break; // &NotTildeFullEqu -> &NotTildeFullEqua - case 4591: state = 4592; break; // &NotVertic -> &NotVertica - case 4594: state = 4595; break; // &NotVerticalB -> &NotVerticalBa - case 4597: state = 4598; break; // &np -> &npa - case 4599: state = 4600; break; // &npar -> &npara - case 4621: state = 4625; break; // &nr -> &nra - case 4634: state = 4635; break; // &nRight -> &nRighta - case 4643: state = 4644; break; // &nright -> &nrighta - case 4670: state = 4671; break; // &nshortp -> &nshortpa - case 4672: state = 4673; break; // &nshortpar -> &nshortpara - case 4685: state = 4686; break; // &nsp -> &nspa - case 4733: state = 4734; break; // &ntri -> &ntria - case 4760: state = 4761; break; // &nv -> &nva - case 4764: state = 4765; break; // &nVD -> &nVDa - case 4768: state = 4769; break; // &nVd -> &nVda - case 4772: state = 4773; break; // &nvD -> &nvDa - case 4776: state = 4777; break; // &nvd -> &nvda - case 4783: state = 4784; break; // &nvH -> &nvHa - case 4812: state = 4813; break; // &nw -> &nwa - case 4824: state = 4825; break; // &nwne -> &nwnea - case 4827: state = 4828; break; // &O -> &Oa - case 4833: state = 4834; break; // &o -> &oa - case 4851: state = 4852; break; // &od -> &oda - case 4857: state = 4858; break; // &Odbl -> &Odbla - case 4861: state = 4862; break; // &odbl -> &odbla - case 4891: state = 4892; break; // &Ogr -> &Ogra - case 4895: state = 4896; break; // &ogr -> &ogra - case 4901: state = 4902; break; // &ohb -> &ohba - case 4908: state = 4909; break; // &ol -> &ola - case 4923: state = 4924; break; // &Om -> &Oma - case 4927: state = 4928; break; // &om -> &oma - case 4932: state = 4933; break; // &Omeg -> &Omega - case 4935: state = 4936; break; // &omeg -> &omega - case 4957: state = 4958; break; // &op -> &opa - case 4991: state = 4992; break; // &or -> &ora - case 5021: state = 5022; break; // &Osl -> &Osla - case 5025: state = 5026; break; // &osl -> &osla - case 5046: state = 5047; break; // &otimes -> &otimesa - case 5056: state = 5057; break; // &ovb -> &ovba - case 5062: state = 5063; break; // &OverB -> &OverBa - case 5065: state = 5066; break; // &OverBr -> &OverBra - case 5072: state = 5073; break; // &OverP -> &OverPa - case 5083: state = 5084; break; // &p -> &pa - case 5085: state = 5086; break; // &par -> ¶ - case 5096: state = 5097; break; // &P -> &Pa - case 5100: state = 5101; break; // &Parti -> &Partia - case 5134: state = 5135; break; // &phmm -> &phmma - case 5150: state = 5151; break; // &pl -> &pla - case 5159: state = 5160; break; // &plus -> &plusa - case 5192: state = 5193; break; // &Poinc -> &Poinca - case 5197: state = 5198; break; // &Poincarepl -> &Poincarepla - case 5216: state = 5217; break; // &pr -> &pra - case 5224: state = 5225; break; // &prec -> &preca - case 5246: state = 5247; break; // &PrecedesEqu -> &PrecedesEqua - case 5250: state = 5251; break; // &PrecedesSl -> &PrecedesSla - case 5256: state = 5257; break; // &PrecedesSlantEqu -> &PrecedesSlantEqua - case 5266: state = 5267; break; // &precn -> &precna - case 5289: state = 5290; break; // &prn -> &prna - case 5303: state = 5304; break; // &prof -> &profa - case 5305: state = 5306; break; // &profal -> &profala - case 5323: state = 5324; break; // &Proportion -> &Proportiona - case 5374: state = 5375; break; // &qu -> &qua - case 5397: state = 5402; break; // &r -> &ra - case 5398: state = 5399; break; // &rA -> &rAa - case 5405: state = 5406; break; // &R -> &Ra - case 5439: state = 5440; break; // &rarr -> &rarra - case 5462: state = 5463; break; // &rAt -> &rAta - case 5466: state = 5467; break; // &rat -> &rata - case 5472: state = 5473; break; // &ration -> &rationa - case 5476: state = 5477; break; // &RB -> &RBa - case 5480: state = 5481; break; // &rB -> &rBa - case 5484: state = 5485; break; // &rb -> &rba - case 5491: state = 5492; break; // &rbr -> &rbra - case 5502: state = 5503; break; // &Rc -> &Rca - case 5507: state = 5508; break; // &rc -> &rca - case 5527: state = 5528; break; // &rdc -> &rdca - case 5531: state = 5532; break; // &rdldh -> &rdldha - case 5541: state = 5542; break; // &re -> &rea - case 5547: state = 5548; break; // &realp -> &realpa - case 5604: state = 5605; break; // &rH -> &rHa - case 5607: state = 5608; break; // &rh -> &rha - case 5620: state = 5637; break; // &Right -> &Righta - case 5627: state = 5628; break; // &RightAngleBr -> &RightAngleBra - case 5645: state = 5646; break; // &right -> &righta - case 5651: state = 5652; break; // &RightArrowB -> &RightArrowBa - case 5663: state = 5664; break; // &rightarrowt -> &rightarrowta - case 5681: state = 5682; break; // &RightDoubleBr -> &RightDoubleBra - case 5704: state = 5705; break; // &RightDownVectorB -> &RightDownVectorBa - case 5712: state = 5713; break; // &righth -> &rightha - case 5728: state = 5729; break; // &rightleft -> &rightlefta - case 5735: state = 5736; break; // &rightlefth -> &rightleftha - case 5747: state = 5748; break; // &rightright -> &rightrighta - case 5758: state = 5759; break; // &rightsquig -> &rightsquiga - case 5789: state = 5790; break; // &RightTri -> &RightTria - case 5795: state = 5796; break; // &RightTriangleB -> &RightTriangleBa - case 5800: state = 5801; break; // &RightTriangleEqu -> &RightTriangleEqua - case 5830: state = 5831; break; // &RightUpVectorB -> &RightUpVectorBa - case 5839: state = 5840; break; // &RightVectorB -> &RightVectorBa - case 5854: state = 5855; break; // &rl -> &rla - case 5858: state = 5859; break; // &rlh -> &rlha - case 5866: state = 5867; break; // &rmoust -> &rmousta - case 5875: state = 5876; break; // &ro -> &roa - case 5884: state = 5885; break; // &rop -> &ropa - case 5909: state = 5910; break; // &rp -> &rpa - case 5920: state = 5921; break; // &rr -> &rra - case 5928: state = 5929; break; // &Rright -> &Rrighta - case 5934: state = 5935; break; // &rs -> &rsa - case 5973: state = 5974; break; // &RuleDel -> &RuleDela - case 5981: state = 5982; break; // &ruluh -> &ruluha - case 5985: state = 5986; break; // &S -> &Sa - case 5991: state = 5992; break; // &s -> &sa - case 6001: state = 6005; break; // &Sc -> &Sca - case 6002: state = 6003; break; // &sc -> &sca - case 6030: state = 6031; break; // &scn -> &scna - case 6053: state = 6054; break; // &se -> &sea - case 6069: state = 6070; break; // &sesw -> &seswa - case 6088: state = 6089; break; // &sh -> &sha - case 6132: state = 6133; break; // &shortp -> &shortpa - case 6134: state = 6135; break; // &shortpar -> &shortpara - case 6160: state = 6161; break; // &Sigm -> &Sigma - case 6164: state = 6165; break; // &sigm -> &sigma - case 6184: state = 6185; break; // &simr -> &simra - case 6188: state = 6189; break; // &sl -> &sla - case 6192: state = 6193; break; // &Sm -> &Sma - case 6202: state = 6203; break; // &sm -> &sma - case 6218: state = 6219; break; // &smep -> &smepa - case 6241: state = 6242; break; // &solb -> &solba - case 6249: state = 6250; break; // &sp -> &spa - case 6259: state = 6260; break; // &sqc -> &sqca - case 6285: state = 6290; break; // &squ -> &squa - case 6286: state = 6287; break; // &Squ -> &Squa - case 6313: state = 6314; break; // &SquareSubsetEqu -> &SquareSubsetEqua - case 6324: state = 6325; break; // &SquareSupersetEqu -> &SquareSupersetEqua - case 6334: state = 6335; break; // &sr -> &sra - case 6352: state = 6353; break; // &sst -> &ssta - case 6356: state = 6357; break; // &St -> &Sta - case 6359: state = 6360; break; // &st -> &sta - case 6363: state = 6364; break; // &str -> &stra - case 6404: state = 6405; break; // &subr -> &subra - case 6419: state = 6420; break; // &SubsetEqu -> &SubsetEqua - case 6432: state = 6433; break; // &succ -> &succa - case 6454: state = 6455; break; // &SucceedsEqu -> &SucceedsEqua - case 6458: state = 6459; break; // &SucceedsSl -> &SucceedsSla - case 6464: state = 6465; break; // &SucceedsSlantEqu -> &SucceedsSlantEqua - case 6474: state = 6475; break; // &succn -> &succna - case 6492: state = 6493; break; // &SuchTh -> &SuchTha - case 6522: state = 6523; break; // &SupersetEqu -> &SupersetEqua - case 6531: state = 6532; break; // &supl -> &supla - case 6564: state = 6565; break; // &sw -> &swa - case 6576: state = 6577; break; // &swnw -> &swnwa - case 6583: state = 6584; break; // &T -> &Ta - case 6586: state = 6587; break; // &t -> &ta - case 6597: state = 6598; break; // &Tc -> &Tca - case 6602: state = 6603; break; // &tc -> &tca - case 6646: state = 6647; break; // &Thet -> &Theta - case 6648: state = 6649; break; // &thet -> &theta - case 6656: state = 6657; break; // &thick -> &thicka - case 6670: state = 6671; break; // &ThickSp -> &ThickSpa - case 6679: state = 6680; break; // &ThinSp -> &ThinSpa - case 6683: state = 6684; break; // &thk -> &thka - case 6706: state = 6707; break; // &TildeEqu -> &TildeEqua - case 6715: state = 6716; break; // &TildeFullEqu -> &TildeFullEqua - case 6726: state = 6727; break; // ×b -> ×ba - case 6733: state = 6734; break; // &toe -> &toea - case 6749: state = 6750; break; // &tos -> &tosa - case 6760: state = 6761; break; // &tr -> &tra - case 6764: state = 6765; break; // &tri -> &tria - case 6851: state = 6852; break; // &twohe -> &twohea - case 6857: state = 6858; break; // &twoheadleft -> &twoheadlefta - case 6867: state = 6868; break; // &twoheadright -> &twoheadrighta - case 6873: state = 6874; break; // &U -> &Ua - case 6879: state = 6880; break; // &u -> &ua - case 6920: state = 6921; break; // &ud -> &uda - case 6926: state = 6927; break; // &Udbl -> &Udbla - case 6930: state = 6931; break; // &udbl -> &udbla - case 6933: state = 6934; break; // &udh -> &udha - case 6945: state = 6946; break; // &Ugr -> &Ugra - case 6950: state = 6951; break; // &ugr -> &ugra - case 6954: state = 6955; break; // &uH -> &uHa - case 6957: state = 6958; break; // &uh -> &uha - case 6978: state = 6979; break; // &Um -> &Uma - case 6982: state = 6983; break; // &um -> &uma - case 6991: state = 6992; break; // &UnderB -> &UnderBa - case 6994: state = 6995; break; // &UnderBr -> &UnderBra - case 7001: state = 7002; break; // &UnderP -> &UnderPa - case 7031: state = 7037; break; // &Up -> &Upa - case 7042: state = 7043; break; // &up -> &upa - case 7048: state = 7049; break; // &UpArrowB -> &UpArrowBa - case 7072: state = 7073; break; // &Updown -> &Updowna - case 7081: state = 7082; break; // &updown -> &updowna - case 7098: state = 7099; break; // &uph -> &upha - case 7159: state = 7160; break; // &upup -> &upupa - case 7208: state = 7209; break; // &uu -> &uua - case 7217: state = 7218; break; // &uw -> &uwa - case 7223: state = 7224; break; // &v -> &va - case 7237: state = 7238; break; // &vark -> &varka - case 7240: state = 7241; break; // &varkapp -> &varkappa - case 7267: state = 7268; break; // &varsigm -> &varsigma - case 7289: state = 7290; break; // &varthet -> &vartheta - case 7292: state = 7293; break; // &vartri -> &vartria - case 7308: state = 7309; break; // &Vb -> &Vba - case 7311: state = 7312; break; // &vB -> &vBa - case 7319: state = 7320; break; // &VD -> &VDa - case 7323: state = 7324; break; // &Vd -> &Vda - case 7327: state = 7328; break; // &vD -> &vDa - case 7331: state = 7332; break; // &vd -> &vda - case 7340: state = 7341; break; // &veeb -> &veeba - case 7350: state = 7351; break; // &Verb -> &Verba - case 7354: state = 7355; break; // &verb -> &verba - case 7360: state = 7361; break; // &Vertic -> &Vertica - case 7363: state = 7364; break; // &VerticalB -> &VerticalBa - case 7372: state = 7373; break; // &VerticalSep -> &VerticalSepa - case 7374: state = 7375; break; // &VerticalSepar -> &VerticalSepara - case 7390: state = 7391; break; // &VeryThinSp -> &VeryThinSpa - case 7437: state = 7438; break; // &Vvd -> &Vvda - case 7444: state = 7445; break; // &vzigz -> &vzigza - case 7459: state = 7460; break; // &wedb -> &wedba - case 7485: state = 7486; break; // &wre -> &wrea - case 7496: state = 7497; break; // &xc -> &xca - case 7513: state = 7517; break; // &xh -> &xha - case 7522: state = 7526; break; // &xl -> &xla - case 7529: state = 7530; break; // &xm -> &xma - case 7551: state = 7555; break; // &xr -> &xra - case 7584: state = 7585; break; // &Y -> &Ya - case 7590: state = 7591; break; // &y -> &ya - case 7645: state = 7646; break; // &Z -> &Za - case 7651: state = 7652; break; // &z -> &za - case 7657: state = 7658; break; // &Zc -> &Zca - case 7662: state = 7663; break; // &zc -> &zca - case 7689: state = 7690; break; // &ZeroWidthSp -> &ZeroWidthSpa - case 7693: state = 7694; break; // &Zet -> &Zeta - case 7695: state = 7696; break; // &zet -> &zeta - case 7709: state = 7710; break; // &zigr -> &zigra - default: return false; - } - break; - case 'b': - switch (state) { - case 0: state = 222; break; // & -> &b - case 1: state = 13; break; // &A -> &Ab - case 7: state = 18; break; // &a -> &ab - case 109: state = 111; break; // &angmsda -> &angmsdab - case 120: state = 121; break; // &angrtv -> &angrtvb - case 222: state = 270; break; // &b -> &bb - case 273: state = 274; break; // &bbrkt -> &bbrktb - case 470: state = 471; break; // &box -> &boxb - case 545: state = 546; break; // &brv -> &brvb - case 562: state = 563; break; // &bsol -> &bsolb - case 566: state = 567; break; // &bsolhsu -> &bsolhsub - case 596: state = 600; break; // &cap -> &capb - case 835: state = 836; break; // &CloseCurlyDou -> &CloseCurlyDoub - case 850: state = 851; break; // &clu -> &club - case 978: state = 979; break; // &csu -> &csub - case 1006: state = 1007; break; // &cup -> &cupb - case 1097: state = 1120; break; // &d -> &db - case 1209: state = 1210; break; // &DiacriticalDou -> &DiacriticalDoub - case 1331: state = 1332; break; // &dou -> &doub - case 1334: state = 1335; break; // &double -> &doubleb - case 1343: state = 1344; break; // &Dou -> &Doub - case 1590: state = 1591; break; // &dr -> &drb - case 1874: state = 1875; break; // &Equili -> &Equilib - case 2118: state = 2140; break; // &g -> &gb - case 2124: state = 2135; break; // &G -> &Gb - case 2356: state = 2386; break; // &h -> &hb - case 2418: state = 2419; break; // &Hil -> &Hilb - case 2474: state = 2475; break; // &hor -> &horb - case 2524: state = 2525; break; // &hy -> &hyb - case 2696: state = 2697; break; // &Invisi -> &Invisib - case 2881: state = 2970; break; // &l -> &lb - case 2907: state = 2908; break; // &Lam -> &Lamb - case 2911: state = 2912; break; // &lam -> &lamb - case 2939: state = 2940; break; // &larr -> &larrb - case 2970: state = 2974; break; // &lb -> &lbb - case 3008: state = 3009; break; // &lcu -> &lcub - case 3090: state = 3091; break; // &LeftDou -> &LeftDoub - case 3393: state = 3399; break; // &lh -> &lhb - case 3466: state = 3472; break; // &lo -> &lob - case 3603: state = 3607; break; // &low -> &lowb - case 3677: state = 3678; break; // &lsq -> &lsqb - case 3843: state = 3844; break; // &minus -> &minusb - case 3897: state = 3930; break; // &n -> &nb - case 3898: state = 3899; break; // &na -> &nab - case 4233: state = 4234; break; // &NotDou -> &NotDoub - case 4339: state = 4341; break; // ¬inv -> ¬invb - case 4426: state = 4428; break; // ¬niv -> ¬nivb - case 4494: state = 4495; break; // &NotSquareSu -> &NotSquareSub - case 4515: state = 4516; break; // &NotSu -> &NotSub - case 4690: state = 4691; break; // &nsqsu -> &nsqsub - case 4695: state = 4696; break; // &nsu -> &nsub - case 4851: state = 4860; break; // &od -> &odb - case 4855: state = 4856; break; // &Od -> &Odb - case 4900: state = 4901; break; // &oh -> &ohb - case 4970: state = 4971; break; // &OpenCurlyDou -> &OpenCurlyDoub - case 5055: state = 5056; break; // &ov -> &ovb - case 5159: state = 5164; break; // &plus -> &plusb - case 5397: state = 5484; break; // &r -> &rb - case 5439: state = 5442; break; // &rarr -> &rarrb - case 5484: state = 5488; break; // &rb -> &rbb - case 5522: state = 5523; break; // &rcu -> &rcub - case 5573: state = 5574; break; // &ReverseEquili -> &ReverseEquilib - case 5586: state = 5587; break; // &ReverseUpEquili -> &ReverseUpEquilib - case 5676: state = 5677; break; // &RightDou -> &RightDoub - case 5875: state = 5881; break; // &ro -> &rob - case 5946: state = 5947; break; // &rsq -> &rsqb - case 5991: state = 5997; break; // &s -> &sb - case 6050: state = 6051; break; // &sdot -> &sdotb - case 6240: state = 6241; break; // &sol -> &solb - case 6270: state = 6271; break; // &sqsu -> &sqsub - case 6306: state = 6307; break; // &SquareSu -> &SquareSub - case 6381: state = 6382; break; // &Su -> &Sub - case 6383: state = 6384; break; // &su -> &sub - case 6428: state = 6429; break; // &subsu -> &subsub - case 6508: state = 6509; break; // &supdsu -> &supdsub - case 6529: state = 6530; break; // &suphsu -> &suphsub - case 6561: state = 6562; break; // &supsu -> &supsub - case 6584: state = 6585; break; // &Ta -> &Tab - case 6586: state = 6594; break; // &t -> &tb - case 6725: state = 6726; break; // × -> ×b - case 6735: state = 6736; break; // &top -> &topb - case 6809: state = 6810; break; // &tris -> &trisb - case 6873: state = 6896; break; // &U -> &Ub - case 6879: state = 6900; break; // &u -> &ub - case 6920: state = 6929; break; // &ud -> &udb - case 6924: state = 6925; break; // &Ud -> &Udb - case 6957: state = 6962; break; // &uh -> &uhb - case 7092: state = 7093; break; // &UpEquili -> &UpEquilib - case 7269: state = 7270; break; // &varsu -> &varsub - case 7307: state = 7308; break; // &V -> &Vb - case 7339: state = 7340; break; // &vee -> &veeb - case 7349: state = 7350; break; // &Ver -> &Verb - case 7353: state = 7354; break; // &ver -> &verb - case 7404: state = 7405; break; // &vnsu -> &vnsub - case 7427: state = 7428; break; // &vsu -> &vsub - case 7458: state = 7459; break; // &wed -> &wedb - default: return false; - } - break; - case 'c': - switch (state) { - case 0: state = 589; break; // & -> &c - case 1: state = 26; break; // &A -> &Ac - case 2: state = 3; break; // &Aa -> &Aac - case 7: state = 23; break; // &a -> &ac - case 8: state = 9; break; // &aa -> &aac - case 28: state = 29; break; // &Acir ->  - case 31: state = 32; break; // &acir -> â - case 76: state = 77; break; // &Ama -> &Amac - case 80: state = 81; break; // &ama -> &amac - case 109: state = 112; break; // &angmsda -> &angmsdac - case 144: state = 145; break; // &apa -> &apac - case 160: state = 161; break; // &ApplyFun -> &ApplyFunc - case 180: state = 181; break; // &As -> &Asc - case 183: state = 184; break; // &as -> &asc - case 212: state = 213; break; // &aw -> &awc - case 222: state = 277; break; // &b -> &bc - case 223: state = 224; break; // &ba -> &bac - case 225: state = 226; break; // &back -> &backc - case 247: state = 281; break; // &B -> &Bc - case 248: state = 249; break; // &Ba -> &Bac - case 288: state = 289; break; // &be -> &bec - case 293: state = 294; break; // &Be -> &Bec - case 334: state = 335; break; // &big -> &bigc - case 339: state = 340; break; // &bigcir -> &bigcirc - case 357: state = 358; break; // &bigsq -> &bigsqc - case 397: state = 398; break; // &bla -> &blac - case 442: state = 443; break; // &blo -> &bloc - case 549: state = 550; break; // &Bs -> &Bsc - case 552: state = 553; break; // &bs -> &bsc - case 583: state = 645; break; // &C -> &Cc - case 584: state = 585; break; // &Ca -> &Cac - case 589: state = 641; break; // &c -> &cc - case 590: state = 591; break; // &ca -> &cac - case 596: state = 605; break; // &cap -> &capc - case 601: state = 602; break; // &capbr -> &capbrc - case 662: state = 663; break; // &Ccir -> &Ccirc - case 665: state = 666; break; // &ccir -> &ccirc - case 716: state = 717; break; // &CH -> &CHc - case 719: state = 720; break; // &ch -> &chc - case 722: state = 723; break; // &che -> &chec - case 733: state = 734; break; // &cir -> &circ - case 753: state = 757; break; // &circled -> &circledc - case 759: state = 760; break; // &circledcir -> &circledcirc - case 766: state = 767; break; // &Cir -> &Circ - case 799: state = 800; break; // &cirs -> &cirsc - case 804: state = 805; break; // &Clo -> &Cloc - case 923: state = 924; break; // &Coprodu -> &Coproduc - case 939: state = 940; break; // &CounterClo -> &CounterCloc - case 972: state = 973; break; // &Cs -> &Csc - case 975: state = 976; break; // &cs -> &csc - case 997: state = 998; break; // &cues -> &cuesc - case 1006: state = 1015; break; // &cup -> &cupc - case 1008: state = 1009; break; // &cupbr -> &cupbrc - case 1037: state = 1038; break; // &curlyeqpre -> &curlyeqprec - case 1040: state = 1041; break; // &curlyeqsu -> &curlyeqsuc - case 1041: state = 1042; break; // &curlyeqsuc -> &curlyeqsucc - case 1076: state = 1077; break; // &cw -> &cwc - case 1087: state = 1088; break; // &cyl -> &cylc - case 1091: state = 1129; break; // &D -> &Dc - case 1097: state = 1134; break; // &d -> &dc - case 1127: state = 1128; break; // &dbla -> &dblac - case 1192: state = 1193; break; // &Dia -> &Diac - case 1197: state = 1198; break; // &Diacriti -> &Diacritic - case 1201: state = 1202; break; // &DiacriticalA -> &DiacriticalAc - case 1213: state = 1214; break; // &DiacriticalDoubleA -> &DiacriticalDoubleAc - case 1277: state = 1278; break; // &DJ -> &DJc - case 1280: state = 1281; break; // &dj -> &djc - case 1283: state = 1284; break; // &dl -> &dlc - case 1459: state = 1460; break; // &DoubleVerti -> &DoubleVertic - case 1536: state = 1537; break; // &DownLeftRightVe -> &DownLeftRightVec - case 1545: state = 1546; break; // &DownLeftTeeVe -> &DownLeftTeeVec - case 1551: state = 1552; break; // &DownLeftVe -> &DownLeftVec - case 1568: state = 1569; break; // &DownRightTeeVe -> &DownRightTeeVec - case 1574: state = 1575; break; // &DownRightVe -> &DownRightVec - case 1590: state = 1597; break; // &dr -> &drc - case 1604: state = 1605; break; // &Ds -> &Dsc - case 1607: state = 1608; break; // &ds -> &dsc - case 1610: state = 1611; break; // &DS -> &DSc - case 1644: state = 1645; break; // &DZ -> &DZc - case 1647: state = 1648; break; // &dz -> &dzc - case 1656: state = 1672; break; // &E -> &Ec - case 1657: state = 1658; break; // &Ea -> &Eac - case 1662: state = 1677; break; // &e -> &ec - case 1663: state = 1664; break; // &ea -> &eac - case 1683: state = 1687; break; // &ecir -> ê - case 1685: state = 1686; break; // &Ecir -> Ê - case 1747: state = 1748; break; // &Ema -> &Emac - case 1751: state = 1752; break; // &ema -> &emac - case 1833: state = 1834; break; // &eq -> &eqc - case 1836: state = 1837; break; // &eqcir -> &eqcirc - case 1897: state = 1898; break; // &Es -> &Esc - case 1900: state = 1901; break; // &es -> &esc - case 1925: state = 1926; break; // &ex -> &exc - case 1937: state = 1938; break; // &expe -> &expec - case 1964: state = 1980; break; // &f -> &fc - case 1977: state = 1978; break; // &F -> &Fc - case 2084: state = 2085; break; // &fra -> &frac - case 2112: state = 2113; break; // &Fs -> &Fsc - case 2115: state = 2116; break; // &fs -> &fsc - case 2118: state = 2153; break; // &g -> &gc - case 2119: state = 2120; break; // &ga -> &gac - case 2124: state = 2145; break; // &G -> &Gc - case 2151: state = 2152; break; // &Gcir -> &Gcirc - case 2155: state = 2156; break; // &gcir -> &gcirc - case 2176: state = 2177; break; // &ges -> &gesc - case 2177: state = 2178; break; // &gesc -> &gescc - case 2198: state = 2199; break; // &GJ -> &GJc - case 2201: state = 2202; break; // &gj -> &gjc - case 2282: state = 2283; break; // &Gs -> &Gsc - case 2285: state = 2286; break; // &gs -> &gsc - case 2294: state = 2295; break; // > -> >c - case 2295: state = 2296; break; // >c -> >cc - case 2351: state = 2389; break; // &H -> &Hc - case 2352: state = 2353; break; // &Ha -> &Hac - case 2356: state = 2393; break; // &h -> &hc - case 2370: state = 2371; break; // &HARD -> &HARDc - case 2374: state = 2375; break; // &hard -> &hardc - case 2380: state = 2381; break; // &harr -> &harrc - case 2391: state = 2392; break; // &Hcir -> &Hcirc - case 2395: state = 2396; break; // &hcir -> &hcirc - case 2409: state = 2410; break; // &her -> &herc - case 2425: state = 2426; break; // &HilbertSpa -> &HilbertSpac - case 2490: state = 2491; break; // &Hs -> &Hsc - case 2493: state = 2494; break; // &hs -> &hsc - case 2533: state = 2546; break; // &I -> &Ic - case 2534: state = 2535; break; // &Ia -> &Iac - case 2539: state = 2545; break; // &i -> &ic - case 2540: state = 2541; break; // &ia -> &iac - case 2548: state = 2549; break; // &Icir -> Î - case 2551: state = 2552; break; // &icir -> î - case 2558: state = 2559; break; // &IE -> &IEc - case 2561: state = 2562; break; // &ie -> &iec - case 2564: state = 2565; break; // &iex -> &iexc - case 2605: state = 2606; break; // &Ima -> &Imac - case 2609: state = 2610; break; // &ima -> &imac - case 2641: state = 2642; break; // &in -> &inc - case 2658: state = 2659; break; // &int -> &intc - case 2672: state = 2673; break; // &inter -> &interc - case 2678: state = 2679; break; // &Interse -> &Intersec - case 2710: state = 2711; break; // &IO -> &IOc - case 2713: state = 2714; break; // &io -> &ioc - case 2740: state = 2741; break; // &Is -> &Isc - case 2743: state = 2744; break; // &is -> &isc - case 2766: state = 2767; break; // &Iuk -> &Iukc - case 2770: state = 2771; break; // &iuk -> &iukc - case 2777: state = 2778; break; // &J -> &Jc - case 2780: state = 2781; break; // &Jcir -> &Jcirc - case 2782: state = 2783; break; // &j -> &jc - case 2785: state = 2786; break; // &jcir -> &jcirc - case 2803: state = 2804; break; // &Js -> &Jsc - case 2806: state = 2807; break; // &js -> &jsc - case 2810: state = 2811; break; // &Jser -> &Jserc - case 2814: state = 2815; break; // &jser -> &jserc - case 2818: state = 2819; break; // &Juk -> &Jukc - case 2822: state = 2823; break; // &juk -> &jukc - case 2825: state = 2836; break; // &K -> &Kc - case 2830: state = 2841; break; // &k -> &kc - case 2857: state = 2858; break; // &KH -> &KHc - case 2860: state = 2861; break; // &kh -> &khc - case 2863: state = 2864; break; // &KJ -> &KJc - case 2866: state = 2867; break; // &kj -> &kjc - case 2875: state = 2876; break; // &Ks -> &Ksc - case 2878: state = 2879; break; // &ks -> &ksc - case 2881: state = 2993; break; // &l -> &lc - case 2886: state = 2988; break; // &L -> &Lc - case 2887: state = 2888; break; // &La -> &Lac - case 2892: state = 2893; break; // &la -> &lac - case 2925: state = 2926; break; // &Lapla -> &Laplac - case 2978: state = 2979; break; // &lbra -> &lbrac - case 3012: state = 3013; break; // &ld -> &ldc - case 3043: state = 3044; break; // &LeftAngleBra -> &LeftAngleBrac - case 3096: state = 3097; break; // &LeftDoubleBra -> &LeftDoubleBrac - case 3107: state = 3108; break; // &LeftDownTeeVe -> &LeftDownTeeVec - case 3113: state = 3114; break; // &LeftDownVe -> &LeftDownVec - case 3199: state = 3200; break; // &LeftRightVe -> &LeftRightVec - case 3213: state = 3214; break; // &LeftTeeVe -> &LeftTeeVec - case 3250: state = 3251; break; // &LeftUpDownVe -> &LeftUpDownVec - case 3259: state = 3260; break; // &LeftUpTeeVe -> &LeftUpTeeVec - case 3265: state = 3266; break; // &LeftUpVe -> &LeftUpVec - case 3274: state = 3275; break; // &LeftVe -> &LeftVec - case 3291: state = 3292; break; // &les -> &lesc - case 3292: state = 3293; break; // &lesc -> &lescc - case 3402: state = 3403; break; // &LJ -> &LJc - case 3405: state = 3406; break; // &lj -> &ljc - case 3409: state = 3413; break; // &ll -> &llc - case 3448: state = 3449; break; // &lmousta -> &lmoustac - case 3643: state = 3647; break; // &lr -> &lrc - case 3661: state = 3669; break; // &ls -> &lsc - case 3666: state = 3667; break; // &Ls -> &Lsc - case 3692: state = 3693; break; // < -> <c - case 3693: state = 3694; break; // <c -> <cc - case 3745: state = 3776; break; // &m -> &mc - case 3746: state = 3747; break; // &ma -> &mac - case 3755: state = 3781; break; // &M -> &Mc - case 3811: state = 3812; break; // &MediumSpa -> &MediumSpac - case 3827: state = 3828; break; // &mi -> &mic - case 3831: state = 3835; break; // &mid -> &midc - case 3855: state = 3856; break; // &ml -> &mlc - case 3876: state = 3877; break; // &Ms -> &Msc - case 3879: state = 3880; break; // &ms -> &msc - case 3897: state = 3937; break; // &n -> &nc - case 3898: state = 3908; break; // &na -> &nac - case 3902: state = 3940; break; // &N -> &Nc - case 3903: state = 3904; break; // &Na -> &Nac - case 3999: state = 4000; break; // &NegativeMediumSpa -> &NegativeMediumSpac - case 4004: state = 4005; break; // &NegativeThi -> &NegativeThic - case 4009: state = 4010; break; // &NegativeThickSpa -> &NegativeThickSpac - case 4015: state = 4016; break; // &NegativeThinSpa -> &NegativeThinSpac - case 4028: state = 4029; break; // &NegativeVeryThinSpa -> &NegativeVeryThinSpac - case 4115: state = 4116; break; // &NJ -> &NJc - case 4118: state = 4119; break; // &nj -> &njc - case 4207: state = 4208; break; // &NonBreakingSpa -> &NonBreakingSpac - case 4241: state = 4242; break; // &NotDoubleVerti -> &NotDoubleVertic - case 4339: state = 4342; break; // ¬inv -> ¬invc - case 4426: state = 4429; break; // ¬niv -> ¬nivc - case 4432: state = 4433; break; // &NotPre -> &NotPrec - case 4515: state = 4525; break; // &NotSu -> &NotSuc - case 4525: state = 4526; break; // &NotSuc -> &NotSucc - case 4590: state = 4591; break; // &NotVerti -> &NotVertic - case 4613: state = 4614; break; // &npr -> &nprc - case 4617: state = 4618; break; // &npre -> &nprec - case 4627: state = 4628; break; // &nrarr -> &nrarrc - case 4653: state = 4654; break; // &ns -> &nsc - case 4654: state = 4655; break; // &nsc -> &nscc - case 4659: state = 4660; break; // &Ns -> &Nsc - case 4695: state = 4705; break; // &nsu -> &nsuc - case 4705: state = 4706; break; // &nsuc -> &nsucc - case 4827: state = 4844; break; // &O -> &Oc - case 4828: state = 4829; break; // &Oa -> &Oac - case 4833: state = 4841; break; // &o -> &oc - case 4834: state = 4835; break; // &oa -> &oac - case 4843: state = 4848; break; // &ocir -> ô - case 4846: state = 4847; break; // &Ocir -> Ô - case 4858: state = 4859; break; // &Odbla -> &Odblac - case 4862: state = 4863; break; // &odbla -> &odblac - case 4880: state = 4881; break; // &of -> &ofc - case 4908: state = 4912; break; // &ol -> &olc - case 4924: state = 4925; break; // &Oma -> &Omac - case 4928: state = 4929; break; // &oma -> &omac - case 4937: state = 4938; break; // &Omi -> &Omic - case 4942: state = 4943; break; // &omi -> &omic - case 5015: state = 5016; break; // &Os -> &Osc - case 5018: state = 5019; break; // &os -> &osc - case 5066: state = 5067; break; // &OverBra -> &OverBrac - case 5083: state = 5106; break; // &p -> &pc - case 5096: state = 5104; break; // &P -> &Pc - case 5109: state = 5110; break; // &per -> &perc - case 5142: state = 5143; break; // &pit -> &pitc - case 5152: state = 5153; break; // &plan -> &planc - case 5159: state = 5165; break; // &plus -> &plusc - case 5160: state = 5161; break; // &plusa -> &plusac - case 5191: state = 5192; break; // &Poin -> &Poinc - case 5216: state = 5219; break; // &pr -> &prc - case 5223: state = 5224; break; // &pre -> &prec - case 5224: state = 5231; break; // &prec -> &precc - case 5238: state = 5239; break; // &Pre -> &Prec - case 5300: state = 5301; break; // &Produ -> &Produc - case 5335: state = 5336; break; // &Ps -> &Psc - case 5338: state = 5339; break; // &ps -> &psc - case 5344: state = 5345; break; // &pun -> &punc - case 5368: state = 5369; break; // &Qs -> &Qsc - case 5371: state = 5372; break; // &qs -> &qsc - case 5397: state = 5507; break; // &r -> &rc - case 5402: state = 5403; break; // &ra -> &rac - case 5405: state = 5502; break; // &R -> &Rc - case 5406: state = 5407; break; // &Ra -> &Rac - case 5415: state = 5416; break; // &radi -> &radic - case 5439: state = 5445; break; // &rarr -> &rarrc - case 5492: state = 5493; break; // &rbra -> &rbrac - case 5526: state = 5527; break; // &rd -> &rdc - case 5541: state = 5552; break; // &re -> &rec - case 5628: state = 5629; break; // &RightAngleBra -> &RightAngleBrac - case 5682: state = 5683; break; // &RightDoubleBra -> &RightDoubleBrac - case 5693: state = 5694; break; // &RightDownTeeVe -> &RightDownTeeVec - case 5699: state = 5700; break; // &RightDownVe -> &RightDownVec - case 5773: state = 5774; break; // &RightTeeVe -> &RightTeeVec - case 5810: state = 5811; break; // &RightUpDownVe -> &RightUpDownVec - case 5819: state = 5820; break; // &RightUpTeeVe -> &RightUpTeeVec - case 5825: state = 5826; break; // &RightUpVe -> &RightUpVec - case 5834: state = 5835; break; // &RightVe -> &RightVec - case 5867: state = 5868; break; // &rmousta -> &rmoustac - case 5934: state = 5942; break; // &rs -> &rsc - case 5939: state = 5940; break; // &Rs -> &Rsc - case 5985: state = 6001; break; // &S -> &Sc - case 5986: state = 5987; break; // &Sa -> &Sac - case 5991: state = 6002; break; // &s -> &sc - case 5992: state = 5993; break; // &sa -> &sac - case 6002: state = 6012; break; // &sc -> &scc - case 6025: state = 6026; break; // &Scir -> &Scirc - case 6028: state = 6029; break; // &scir -> &scirc - case 6053: state = 6064; break; // &se -> &sec - case 6088: state = 6097; break; // &sh -> &shc - case 6092: state = 6101; break; // &SH -> &SHc - case 6094: state = 6095; break; // &SHCH -> &SHCHc - case 6098: state = 6099; break; // &shch -> &shchc - case 6198: state = 6199; break; // &SmallCir -> &SmallCirc - case 6232: state = 6233; break; // &SOFT -> &SOFTc - case 6237: state = 6238; break; // &soft -> &softc - case 6258: state = 6259; break; // &sq -> &sqc - case 6299: state = 6300; break; // &SquareInterse -> &SquareIntersec - case 6338: state = 6339; break; // &Ss -> &Ssc - case 6341: state = 6342; break; // &ss -> &ssc - case 6381: state = 6446; break; // &Su -> &Suc - case 6383: state = 6431; break; // &su -> &suc - case 6431: state = 6432; break; // &suc -> &succ - case 6432: state = 6439; break; // &succ -> &succc - case 6446: state = 6447; break; // &Suc -> &Succ - case 6583: state = 6597; break; // &T -> &Tc - case 6586: state = 6602; break; // &t -> &tc - case 6623: state = 6624; break; // &telre -> &telrec - case 6654: state = 6655; break; // &thi -> &thic - case 6666: state = 6667; break; // &Thi -> &Thic - case 6671: state = 6672; break; // &ThickSpa -> &ThickSpac - case 6680: state = 6681; break; // &ThinSpa -> &ThinSpac - case 6735: state = 6739; break; // &top -> &topc - case 6821: state = 6822; break; // &Ts -> &Tsc - case 6824: state = 6825; break; // &ts -> &tsc - case 6827: state = 6828; break; // &TS -> &TSc - case 6831: state = 6832; break; // &TSH -> &TSHc - case 6834: state = 6835; break; // &tsh -> &tshc - case 6873: state = 6910; break; // &U -> &Uc - case 6874: state = 6875; break; // &Ua -> &Uac - case 6879: state = 6914; break; // &u -> &uc - case 6880: state = 6881; break; // &ua -> &uac - case 6892: state = 6893; break; // &Uarro -> &Uarroc - case 6897: state = 6898; break; // &Ubr -> &Ubrc - case 6901: state = 6902; break; // &ubr -> &ubrc - case 6912: state = 6913; break; // &Ucir -> Û - case 6916: state = 6917; break; // &ucir -> û - case 6927: state = 6928; break; // &Udbla -> &Udblac - case 6931: state = 6932; break; // &udbla -> &udblac - case 6965: state = 6966; break; // &ul -> &ulc - case 6979: state = 6980; break; // &Uma -> &Umac - case 6983: state = 6984; break; // &uma -> &umac - case 6995: state = 6996; break; // &UnderBra -> &UnderBrac - case 7166: state = 7167; break; // &ur -> &urc - case 7186: state = 7187; break; // &Us -> &Usc - case 7189: state = 7190; break; // &us -> &usc - case 7223: state = 7317; break; // &v -> &vc - case 7307: state = 7315; break; // &V -> &Vc - case 7359: state = 7360; break; // &Verti -> &Vertic - case 7391: state = 7392; break; // &VeryThinSpa -> &VeryThinSpac - case 7421: state = 7422; break; // &Vs -> &Vsc - case 7424: state = 7425; break; // &vs -> &vsc - case 7447: state = 7448; break; // &W -> &Wc - case 7450: state = 7451; break; // &Wcir -> &Wcirc - case 7452: state = 7453; break; // &w -> &wc - case 7455: state = 7456; break; // &wcir -> &wcirc - case 7489: state = 7490; break; // &Ws -> &Wsc - case 7492: state = 7493; break; // &ws -> &wsc - case 7495: state = 7496; break; // &x -> &xc - case 7500: state = 7501; break; // &xcir -> &xcirc - case 7558: state = 7559; break; // &Xs -> &Xsc - case 7561: state = 7562; break; // &xs -> &xsc - case 7564: state = 7565; break; // &xsq -> &xsqc - case 7584: state = 7600; break; // &Y -> &Yc - case 7585: state = 7586; break; // &Ya -> &Yac - case 7590: state = 7604; break; // &y -> &yc - case 7591: state = 7592; break; // &ya -> &yac - case 7596: state = 7597; break; // &YA -> &YAc - case 7602: state = 7603; break; // &Ycir -> &Ycirc - case 7606: state = 7607; break; // &ycir -> &ycirc - case 7616: state = 7617; break; // &YI -> &YIc - case 7619: state = 7620; break; // &yi -> &yic - case 7628: state = 7629; break; // &Ys -> &Ysc - case 7631: state = 7632; break; // &ys -> &ysc - case 7634: state = 7635; break; // &YU -> &YUc - case 7637: state = 7638; break; // &yu -> &yuc - case 7645: state = 7657; break; // &Z -> &Zc - case 7646: state = 7647; break; // &Za -> &Zac - case 7651: state = 7662; break; // &z -> &zc - case 7652: state = 7653; break; // &za -> &zac - case 7690: state = 7691; break; // &ZeroWidthSpa -> &ZeroWidthSpac - case 7701: state = 7702; break; // &ZH -> &ZHc - case 7704: state = 7705; break; // &zh -> &zhc - case 7719: state = 7720; break; // &Zs -> &Zsc - case 7722: state = 7723; break; // &zs -> &zsc - default: return false; - } - break; - case 'd': - switch (state) { - case 0: state = 1097; break; // & -> &d - case 23: state = 24; break; // &ac -> &acd - case 88: state = 89; break; // &An -> &And - case 90: state = 91; break; // &an -> &and - case 91: state = 95; break; // &and -> &andd - case 93: state = 94; break; // &andan -> &andand - case 107: state = 108; break; // &angms -> &angmsd - case 109: state = 113; break; // &angmsda -> &angmsdad - case 121: state = 122; break; // &angrtvb -> &angrtvbd - case 150: state = 151; break; // &api -> &apid - case 198: state = 199; break; // &Atil -> &Atild - case 203: state = 204; break; // &atil -> &atild - case 222: state = 284; break; // &b -> &bd - case 263: state = 264; break; // &Barwe -> &Barwed - case 266: state = 267; break; // &barwe -> &barwed - case 343: state = 344; break; // &bigo -> &bigod - case 371: state = 372; break; // &bigtriangle -> &bigtriangled - case 387: state = 388; break; // &bigwe -> &bigwed - case 420: state = 421; break; // &blacktriangle -> &blacktriangled - case 470: state = 477; break; // &box -> &boxd - case 484: state = 487; break; // &boxH -> &boxHd - case 485: state = 489; break; // &boxh -> &boxhd - case 583: state = 677; break; // &C -> &Cd - case 589: state = 680; break; // &c -> &cd - case 596: state = 610; break; // &cap -> &capd - case 598: state = 599; break; // &capan -> &capand - case 653: state = 654; break; // &Cce -> &Cced - case 657: state = 658; break; // &cce -> &cced - case 683: state = 684; break; // &ce -> &ced - case 687: state = 688; break; // &Ce -> &Ced - case 708: state = 709; break; // ¢er -> ¢erd - case 738: state = 753; break; // &circle -> &circled - case 753: state = 761; break; // &circled -> &circledd - case 797: state = 798; break; // &cirmi -> &cirmid - case 884: state = 885; break; // &cong -> &congd - case 918: state = 919; break; // &copro -> &coprod - case 921: state = 922; break; // &Copro -> &Coprod - case 983: state = 984; break; // &ct -> &ctd - case 987: state = 988; break; // &cu -> &cud - case 1006: state = 1020; break; // &cup -> &cupd - case 1047: state = 1048; break; // &curlywe -> &curlywed - case 1074: state = 1075; break; // &cuwe -> &cuwed - case 1097: state = 1142; break; // &d -> &dd - case 1154: state = 1155; break; // &DDotrah -> &DDotrahd - case 1225: state = 1226; break; // &DiacriticalTil -> &DiacriticalTild - case 1233: state = 1234; break; // &Diamon -> &Diamond - case 1236: state = 1237; break; // &diamon -> &diamond - case 1264: state = 1265; break; // &divi -> &divid - case 1307: state = 1308; break; // &doteq -> &doteqd - case 1339: state = 1340; break; // &doublebarwe -> &doublebarwed - case 1479: state = 1500; break; // &down -> &downd - case 1624: state = 1625; break; // &dt -> &dtd - case 1656: state = 1698; break; // &E -> &Ed - case 1662: state = 1703; break; // &e -> &ed - case 1724: state = 1725; break; // &egs -> &egsd - case 1742: state = 1743; break; // &els -> &elsd - case 1866: state = 1867; break; // &EqualTil -> &EqualTild - case 1900: state = 1903; break; // &es -> &esd - case 1970: state = 1971; break; // &falling -> &fallingd - case 2008: state = 2009; break; // &Fille -> &Filled - case 2118: state = 2162; break; // &g -> &gd - case 2124: state = 2159; break; // &G -> &Gd - case 2128: state = 2132; break; // &Gamma -> &Gammad - case 2131: state = 2133; break; // &gamma -> &gammad - case 2146: state = 2147; break; // &Gce -> &Gced - case 2176: state = 2179; break; // &ges -> &gesd - case 2279: state = 2280; break; // &GreaterTil -> &GreaterTild - case 2294: state = 2299; break; // > -> >d - case 2311: state = 2320; break; // >r -> >rd - case 2373: state = 2374; break; // &har -> &hard - case 2533: state = 2555; break; // &I -> &Id - case 2634: state = 2635; break; // &impe -> &imped - case 2652: state = 2653; break; // &ino -> &inod - case 2691: state = 2692; break; // &intpro -> &intprod - case 2733: state = 2734; break; // &ipro -> &iprod - case 2747: state = 2748; break; // &isin -> &isind - case 2758: state = 2759; break; // &Itil -> &Itild - case 2762: state = 2763; break; // &itil -> &itild - case 2837: state = 2838; break; // &Kce -> &Kced - case 2842: state = 2843; break; // &kce -> &kced - case 2881: state = 3012; break; // &l -> &ld - case 2908: state = 2909; break; // &Lamb -> &Lambd - case 2912: state = 2913; break; // &lamb -> &lambd - case 2918: state = 2919; break; // &lang -> &langd - case 2985: state = 2986; break; // &lbrksl -> &lbrksld - case 2998: state = 2999; break; // &Lce -> &Lced - case 3002: state = 3003; break; // &lce -> &lced - case 3019: state = 3020; break; // &ldr -> &ldrd - case 3132: state = 3133; break; // &leftharpoon -> &leftharpoond - case 3291: state = 3294; break; // &les -> &lesd - case 3302: state = 3309; break; // &less -> &lessd - case 3373: state = 3374; break; // &LessTil -> &LessTild - case 3395: state = 3396; break; // &lhar -> &lhard - case 3429: state = 3430; break; // &llhar -> &llhard - case 3435: state = 3436; break; // &Lmi -> &Lmid - case 3440: state = 3441; break; // &lmi -> &lmid - case 3655: state = 3656; break; // &lrhar -> &lrhard - case 3692: state = 3697; break; // < -> <d - case 3725: state = 3726; break; // &lur -> &lurd - case 3745: state = 3784; break; // &m -> &md - case 3761: state = 3762; break; // &mapsto -> &mapstod - case 3797: state = 3798; break; // &measure -> &measured - case 3804: state = 3805; break; // &Me -> &Med - case 3827: state = 3831; break; // &mi -> &mid - case 3831: state = 3838; break; // &mid -> &midd - case 3843: state = 3845; break; // &minus -> &minusd - case 3855: state = 3858; break; // &ml -> &mld - case 3865: state = 3866; break; // &mo -> &mod - case 3897: state = 3966; break; // &n -> &nd - case 3916: state = 3917; break; // &napi -> &napid - case 3948: state = 3949; break; // &Nce -> &Nced - case 3952: state = 3953; break; // &nce -> &nced - case 3958: state = 3959; break; // &ncong -> &ncongd - case 3970: state = 3981; break; // &ne -> &ned - case 3992: state = 3993; break; // &NegativeMe -> &NegativeMed - case 4043: state = 4044; break; // &Neste -> &Nested - case 4112: state = 4113; break; // &nis -> &nisd - case 4121: state = 4128; break; // &nl -> &nld - case 4188: state = 4189; break; // &nmi -> &nmid - case 4261: state = 4262; break; // &NotEqualTil -> &NotEqualTild - case 4313: state = 4314; break; // &NotGreaterTil -> &NotGreaterTild - case 4334: state = 4335; break; // ¬in -> ¬ind - case 4393: state = 4394; break; // &NotLessTil -> &NotLessTild - case 4400: state = 4401; break; // &NotNeste -> &NotNested - case 4434: state = 4435; break; // &NotPrece -> &NotPreced - case 4528: state = 4529; break; // &NotSuccee -> &NotSucceed - case 4548: state = 4549; break; // &NotSucceedsTil -> &NotSucceedsTild - case 4564: state = 4565; break; // &NotTil -> &NotTild - case 4583: state = 4584; break; // &NotTildeTil -> &NotTildeTild - case 4668: state = 4669; break; // &nshortmi -> &nshortmid - case 4683: state = 4684; break; // &nsmi -> &nsmid - case 4723: state = 4724; break; // &Ntil -> &Ntild - case 4727: state = 4728; break; // &ntil -> &ntild - case 4760: state = 4776; break; // &nv -> &nvd - case 4763: state = 4768; break; // &nV -> &nVd - case 4827: state = 4855; break; // &O -> &Od - case 4833: state = 4851; break; // &o -> &od - case 4870: state = 4871; break; // &odsol -> &odsold - case 4942: state = 4947; break; // &omi -> &omid - case 4991: state = 4995; break; // &or -> &ord - case 5033: state = 5034; break; // &Otil -> &Otild - case 5038: state = 5039; break; // &otil -> &otild - case 5114: state = 5115; break; // &perio -> &period - case 5159: state = 5168; break; // &plus -> &plusd - case 5213: state = 5214; break; // &poun -> £ - case 5240: state = 5241; break; // &Prece -> &Preced - case 5261: state = 5262; break; // &PrecedesTil -> &PrecedesTild - case 5296: state = 5297; break; // &pro -> &prod - case 5298: state = 5299; break; // &Pro -> &Prod - case 5397: state = 5526; break; // &r -> &rd - case 5402: state = 5414; break; // &ra -> &rad - case 5426: state = 5427; break; // &rang -> &rangd - case 5499: state = 5500; break; // &rbrksl -> &rbrksld - case 5512: state = 5513; break; // &Rce -> &Rced - case 5516: state = 5517; break; // &rce -> &rced - case 5529: state = 5530; break; // &rdl -> &rdld - case 5609: state = 5610; break; // &rhar -> &rhard - case 5718: state = 5719; break; // &rightharpoon -> &rightharpoond - case 5847: state = 5848; break; // &rising -> &risingd - case 5873: state = 5874; break; // &rnmi -> &rnmid - case 5900: state = 5901; break; // &Roun -> &Round - case 5976: state = 5977; break; // &RuleDelaye -> &RuleDelayed - case 5991: state = 6048; break; // &s -> &sd - case 6016: state = 6021; break; // &sce -> &sced - case 6017: state = 6018; break; // &Sce -> &Sced - case 6130: state = 6131; break; // &shortmi -> &shortmid - case 6168: state = 6169; break; // &sim -> &simd - case 6223: state = 6224; break; // &smi -> &smid - case 6250: state = 6251; break; // &spa -> &spad - case 6384: state = 6385; break; // &sub -> &subd - case 6389: state = 6390; break; // &sube -> &subed - case 6449: state = 6450; break; // &Succee -> &Succeed - case 6469: state = 6470; break; // &SucceedsTil -> &SucceedsTild - case 6500: state = 6504; break; // &sup -> &supd - case 6511: state = 6512; break; // &supe -> &suped - case 6586: state = 6617; break; // &t -> &td - case 6607: state = 6608; break; // &Tce -> &Tced - case 6611: state = 6612; break; // &tce -> &tced - case 6697: state = 6698; break; // &Til -> &Tild - case 6701: state = 6702; break; // &til -> &tild - case 6720: state = 6721; break; // &TildeTil -> &TildeTild - case 6725: state = 6729; break; // × -> ×d - case 6761: state = 6762; break; // &tra -> &trad - case 6764: state = 6788; break; // &tri -> &trid - case 6769: state = 6770; break; // &triangle -> &triangled - case 6852: state = 6853; break; // &twohea -> &twohead - case 6873: state = 6924; break; // &U -> &Ud - case 6879: state = 6920; break; // &u -> &ud - case 6987: state = 6988; break; // &Un -> &Und - case 7031: state = 7069; break; // &Up -> &Upd - case 7042: state = 7078; break; // &up -> &upd - case 7192: state = 7193; break; // &ut -> &utd - case 7198: state = 7199; break; // &Util -> &Utild - case 7202: state = 7203; break; // &util -> &utild - case 7223: state = 7331; break; // &v -> &vd - case 7307: state = 7323; break; // &V -> &Vd - case 7381: state = 7382; break; // &VerticalTil -> &VerticalTild - case 7436: state = 7437; break; // &Vv -> &Vvd - case 7457: state = 7458; break; // &we -> &wed - case 7462: state = 7463; break; // &We -> &Wed - case 7495: state = 7504; break; // &x -> &xd - case 7535: state = 7536; break; // &xo -> &xod - case 7580: state = 7581; break; // &xwe -> &xwed - case 7645: state = 7669; break; // &Z -> &Zd - case 7651: state = 7672; break; // &z -> &zd - case 7684: state = 7685; break; // &ZeroWi -> &ZeroWid - default: return false; - } - break; - case 'e': - switch (state) { - case 0: state = 1662; break; // & -> &e - case 5: state = 6; break; // &Aacut -> Á - case 7: state = 42; break; // &a -> &ae - case 11: state = 12; break; // &aacut -> á - case 14: state = 15; break; // &Abr -> &Abre - case 16: state = 17; break; // &Abrev -> &Abreve - case 19: state = 20; break; // &abr -> &abre - case 21: state = 22; break; // &abrev -> &abreve - case 34: state = 35; break; // &acut -> ´ - case 53: state = 54; break; // &Agrav -> À - case 58: state = 59; break; // &agrav -> à - case 60: state = 61; break; // &al -> &ale - case 99: state = 100; break; // &andslop -> &andslope - case 102: state = 103; break; // &ang -> &ange - case 104: state = 105; break; // &angl -> &angle - case 109: state = 114; break; // &angmsda -> &angmsdae - case 143: state = 149; break; // &ap -> &ape - case 169: state = 170; break; // &approx -> &approxe - case 193: state = 194; break; // &asymp -> &asympe - case 199: state = 200; break; // &Atild -> à - case 204: state = 205; break; // &atild -> ã - case 222: state = 288; break; // &b -> &be - case 225: state = 230; break; // &back -> &backe - case 240: state = 241; break; // &backprim -> &backprime - case 244: state = 245; break; // &backsim -> &backsime - case 247: state = 293; break; // &B -> &Be - case 259: state = 260; break; // &barv -> &barve - case 260: state = 261; break; // &barve -> &barvee - case 262: state = 263; break; // &Barw -> &Barwe - case 265: state = 266; break; // &barw -> &barwe - case 268: state = 269; break; // &barwedg -> &barwedge - case 292: state = 299; break; // &becaus -> &because - case 297: state = 298; break; // &Becaus -> &Because - case 325: state = 326; break; // &betw -> &betwe - case 326: state = 327; break; // &betwe -> &betwee - case 353: state = 354; break; // &bigotim -> &bigotime - case 370: state = 371; break; // &bigtriangl -> &bigtriangle - case 383: state = 384; break; // &bigv -> &bigve - case 384: state = 385; break; // &bigve -> &bigvee - case 386: state = 387; break; // &bigw -> &bigwe - case 389: state = 390; break; // &bigwedg -> &bigwedge - case 402: state = 403; break; // &blackloz -> &blackloze - case 405: state = 406; break; // &blacklozeng -> &blacklozenge - case 411: state = 412; break; // &blacksquar -> &blacksquare - case 419: state = 420; break; // &blacktriangl -> &blacktriangle - case 425: state = 426; break; // &blacktrianglel -> &blacktrianglele - case 445: state = 446; break; // &bn -> &bne - case 468: state = 469; break; // &bowti -> &bowtie - case 505: state = 506; break; // &boxtim -> &boxtime - case 535: state = 536; break; // &bprim -> &bprime - case 537: state = 538; break; // &Br -> &Bre - case 539: state = 540; break; // &Brev -> &Breve - case 541: state = 542; break; // &br -> &bre - case 543: state = 544; break; // &brev -> &breve - case 552: state = 555; break; // &bs -> &bse - case 559: state = 560; break; // &bsim -> &bsime - case 570: state = 571; break; // &bull -> &bulle - case 574: state = 576; break; // &bump -> &bumpe - case 579: state = 580; break; // &Bump -> &Bumpe - case 583: state = 687; break; // &C -> &Ce - case 587: state = 588; break; // &Cacut -> &Cacute - case 589: state = 683; break; // &c -> &ce - case 593: state = 594; break; // &cacut -> &cacute - case 620: state = 621; break; // &CapitalDiff -> &CapitalDiffe - case 622: state = 623; break; // &CapitalDiffer -> &CapitalDiffere - case 631: state = 632; break; // &car -> &care - case 637: state = 638; break; // &Cayl -> &Cayle - case 641: state = 657; break; // &cc -> &cce - case 645: state = 653; break; // &Cc -> &Cce - case 699: state = 707; break; // ¢ -> ¢e - case 701: state = 702; break; // &Cent -> &Cente - case 719: state = 722; break; // &ch -> &che - case 733: state = 790; break; // &cir -> &cire - case 734: state = 735; break; // &circ -> &circe - case 737: state = 738; break; // &circl -> &circle - case 744: state = 745; break; // &circlearrowl -> &circlearrowle - case 768: state = 769; break; // &Circl -> &Circle - case 786: state = 787; break; // &CircleTim -> &CircleTime - case 809: state = 810; break; // &Clockwis -> &Clockwise - case 820: state = 821; break; // &ClockwiseContourInt -> &ClockwiseContourInte - case 826: state = 827; break; // &Clos -> &Close - case 837: state = 838; break; // &CloseCurlyDoubl -> &CloseCurlyDouble - case 842: state = 843; break; // &CloseCurlyDoubleQuot -> &CloseCurlyDoubleQuote - case 847: state = 848; break; // &CloseCurlyQuot -> &CloseCurlyQuote - case 859: state = 864; break; // &Colon -> &Colone - case 863: state = 865; break; // &colon -> &colone - case 874: state = 875; break; // &compl -> &comple - case 876: state = 877; break; // &complem -> &compleme - case 880: state = 881; break; // &complex -> &complexe - case 891: state = 892; break; // &Congru -> &Congrue - case 907: state = 908; break; // &ContourInt -> &ContourInte - case 934: state = 935; break; // &Count -> &Counte - case 944: state = 945; break; // &CounterClockwis -> &CounterClockwise - case 955: state = 956; break; // &CounterClockwiseContourInt -> &CounterClockwiseContourInte - case 979: state = 980; break; // &csub -> &csube - case 981: state = 982; break; // &csup -> &csupe - case 987: state = 994; break; // &cu -> &cue - case 1032: state = 1033; break; // &curly -> &curlye - case 1036: state = 1037; break; // &curlyeqpr -> &curlyeqpre - case 1043: state = 1044; break; // &curlyv -> &curlyve - case 1044: state = 1045; break; // &curlyve -> &curlyvee - case 1046: state = 1047; break; // &curlyw -> &curlywe - case 1049: state = 1050; break; // &curlywedg -> &curlywedge - case 1051: state = 1052; break; // &curr -> &curre - case 1054: state = 1055; break; // &curv -> &curve - case 1061: state = 1062; break; // &curvearrowl -> &curvearrowle - case 1070: state = 1071; break; // &cuv -> &cuve - case 1071: state = 1072; break; // &cuve -> &cuvee - case 1073: state = 1074; break; // &cuw -> &cuwe - case 1091: state = 1163; break; // &D -> &De - case 1094: state = 1095; break; // &Dagg -> &Dagge - case 1097: state = 1161; break; // &d -> &de - case 1100: state = 1101; break; // &dagg -> &dagge - case 1103: state = 1104; break; // &dal -> &dale - case 1145: state = 1146; break; // &ddagg -> &ddagge - case 1158: state = 1159; break; // &ddots -> &ddotse - case 1204: state = 1205; break; // &DiacriticalAcut -> &DiacriticalAcute - case 1211: state = 1212; break; // &DiacriticalDoubl -> &DiacriticalDouble - case 1216: state = 1217; break; // &DiacriticalDoubleAcut -> &DiacriticalDoubleAcute - case 1221: state = 1222; break; // &DiacriticalGrav -> &DiacriticalGrave - case 1226: state = 1227; break; // &DiacriticalTild -> &DiacriticalTilde - case 1228: state = 1243; break; // &di -> &die - case 1245: state = 1246; break; // &Diff -> &Diffe - case 1247: state = 1248; break; // &Differ -> &Differe - case 1265: state = 1266; break; // &divid -> ÷ - case 1271: state = 1272; break; // ÷ontim -> ÷ontime - case 1302: state = 1306; break; // &dot -> &dote - case 1329: state = 1330; break; // &dotsquar -> &dotsquare - case 1333: state = 1334; break; // &doubl -> &double - case 1338: state = 1339; break; // &doublebarw -> &doublebarwe - case 1341: state = 1342; break; // &doublebarwedg -> &doublebarwedge - case 1345: state = 1346; break; // &Doubl -> &Double - case 1356: state = 1357; break; // &DoubleContourInt -> &DoubleContourInte - case 1372: state = 1373; break; // &DoubleL -> &DoubleLe - case 1391: state = 1392; break; // &DoubleLeftT -> &DoubleLeftTe - case 1392: state = 1393; break; // &DoubleLeftTe -> &DoubleLeftTee - case 1397: state = 1398; break; // &DoubleLongL -> &DoubleLongLe - case 1436: state = 1437; break; // &DoubleRightT -> &DoubleRightTe - case 1437: state = 1438; break; // &DoubleRightTe -> &DoubleRightTee - case 1455: state = 1456; break; // &DoubleV -> &DoubleVe - case 1496: state = 1497; break; // &DownBr -> &DownBre - case 1498: state = 1499; break; // &DownBrev -> &DownBreve - case 1517: state = 1518; break; // &downharpoonl -> &downharpoonle - case 1526: state = 1527; break; // &DownL -> &DownLe - case 1535: state = 1536; break; // &DownLeftRightV -> &DownLeftRightVe - case 1541: state = 1542; break; // &DownLeftT -> &DownLeftTe - case 1542: state = 1543; break; // &DownLeftTe -> &DownLeftTee - case 1544: state = 1545; break; // &DownLeftTeeV -> &DownLeftTeeVe - case 1550: state = 1551; break; // &DownLeftV -> &DownLeftVe - case 1564: state = 1565; break; // &DownRightT -> &DownRightTe - case 1565: state = 1566; break; // &DownRightTe -> &DownRightTee - case 1567: state = 1568; break; // &DownRightTeeV -> &DownRightTeeVe - case 1573: state = 1574; break; // &DownRightV -> &DownRightVe - case 1582: state = 1583; break; // &DownT -> &DownTe - case 1583: state = 1584; break; // &DownTe -> &DownTee - case 1642: state = 1643; break; // &dwangl -> &dwangle - case 1660: state = 1661; break; // &Eacut -> É - case 1662: state = 1706; break; // &e -> &ee - case 1666: state = 1667; break; // &eacut -> é - case 1669: state = 1670; break; // &east -> &easte - case 1718: state = 1719; break; // &Egrav -> È - case 1722: state = 1723; break; // &egrav -> è - case 1729: state = 1730; break; // &El -> &Ele - case 1731: state = 1732; break; // &Elem -> &Eleme - case 1737: state = 1738; break; // &elint -> &elinte - case 1757: state = 1758; break; // &emptys -> &emptyse - case 1772: state = 1773; break; // &EmptySmallSquar -> &EmptySmallSquare - case 1775: state = 1776; break; // &EmptyV -> &EmptyVe - case 1788: state = 1789; break; // &EmptyVerySmallSquar -> &EmptyVerySmallSquare - case 1852: state = 1853; break; // &eqslantl -> &eqslantle - case 1860: state = 1869; break; // &equ -> &eque - case 1867: state = 1868; break; // &EqualTild -> &EqualTilde - case 1936: state = 1937; break; // &exp -> &expe - case 1947: state = 1948; break; // &Expon -> &Expone - case 1956: state = 1957; break; // &expon -> &expone - case 1962: state = 1963; break; // &exponential -> &exponentiale - case 1964: state = 1982; break; // &f -> &fe - case 1974: state = 1975; break; // &fallingdots -> &fallingdotse - case 1985: state = 1986; break; // &femal -> &female - case 2007: state = 2008; break; // &Fill -> &Fille - case 2019: state = 2020; break; // &FilledSmallSquar -> &FilledSmallSquare - case 2021: state = 2022; break; // &FilledV -> &FilledVe - case 2034: state = 2035; break; // &FilledVerySmallSquar -> &FilledVerySmallSquare - case 2070: state = 2071; break; // &Fouri -> &Fourie - case 2118: state = 2166; break; // &g -> &ge - case 2122: state = 2123; break; // &gacut -> &gacute - case 2136: state = 2137; break; // &Gbr -> &Gbre - case 2138: state = 2139; break; // &Gbrev -> &Gbreve - case 2141: state = 2142; break; // &gbr -> &gbre - case 2143: state = 2144; break; // &gbrev -> &gbreve - case 2145: state = 2146; break; // &Gc -> &Gce - case 2184: state = 2185; break; // &gesl -> &gesle - case 2195: state = 2196; break; // &gim -> &gime - case 2208: state = 2216; break; // &gn -> &gne - case 2230: state = 2231; break; // &grav -> &grave - case 2232: state = 2233; break; // &Gr -> &Gre - case 2235: state = 2236; break; // &Great -> &Greate - case 2243: state = 2244; break; // &GreaterEqualL -> &GreaterEqualLe - case 2257: state = 2258; break; // &GreaterGr -> &GreaterGre - case 2260: state = 2261; break; // &GreaterGreat -> &GreaterGreate - case 2263: state = 2264; break; // &GreaterL -> &GreaterLe - case 2280: state = 2281; break; // &GreaterTild -> &GreaterTilde - case 2289: state = 2290; break; // &gsim -> &gsime - case 2307: state = 2308; break; // >qu -> >que - case 2311: state = 2323; break; // >r -> >re - case 2325: state = 2326; break; // >reql -> >reqle - case 2330: state = 2331; break; // >reqql -> >reqqle - case 2334: state = 2335; break; // >rl -> >rle - case 2341: state = 2342; break; // &gv -> &gve - case 2345: state = 2346; break; // &gvertn -> &gvertne - case 2353: state = 2354; break; // &Hac -> &Hace - case 2356: state = 2397; break; // &h -> &he - case 2419: state = 2420; break; // &Hilb -> &Hilbe - case 2426: state = 2427; break; // &HilbertSpac -> &HilbertSpace - case 2429: state = 2430; break; // &hks -> &hkse - case 2450: state = 2451; break; // &hookl -> &hookle - case 2488: state = 2489; break; // &HorizontalLin -> &HorizontalLine - case 2530: state = 2531; break; // &hyph -> &hyphe - case 2537: state = 2538; break; // &Iacut -> Í - case 2539: state = 2561; break; // &i -> &ie - case 2543: state = 2544; break; // &iacut -> í - case 2575: state = 2576; break; // &Igrav -> Ì - case 2580: state = 2581; break; // &igrav -> ì - case 2612: state = 2613; break; // &imag -> &image - case 2623: state = 2624; break; // &imaglin -> &imagline - case 2633: state = 2634; break; // &imp -> &impe - case 2638: state = 2639; break; // &Impli -> &Implie - case 2644: state = 2645; break; // &incar -> &incare - case 2650: state = 2651; break; // &infinti -> &infintie - case 2657: state = 2667; break; // &Int -> &Inte - case 2658: state = 2662; break; // &int -> &inte - case 2663: state = 2664; break; // &integ -> &intege - case 2677: state = 2678; break; // &Inters -> &Interse - case 2698: state = 2699; break; // &Invisibl -> &Invisible - case 2707: state = 2708; break; // &InvisibleTim -> &InvisibleTime - case 2736: state = 2737; break; // &iqu -> &ique - case 2759: state = 2760; break; // &Itild -> &Itilde - case 2763: state = 2764; break; // &itild -> &itilde - case 2803: state = 2809; break; // &Js -> &Jse - case 2806: state = 2813; break; // &js -> &jse - case 2836: state = 2837; break; // &Kc -> &Kce - case 2841: state = 2842; break; // &kc -> &kce - case 2853: state = 2854; break; // &kgr -> &kgre - case 2854: state = 2855; break; // &kgre -> &kgree - case 2881: state = 3032; break; // &l -> &le - case 2886: state = 3033; break; // &L -> &Le - case 2890: state = 2891; break; // &Lacut -> &Lacute - case 2892: state = 2897; break; // &la -> &lae - case 2895: state = 2896; break; // &lacut -> &lacute - case 2920: state = 2921; break; // &langl -> &langle - case 2926: state = 2927; break; // &Laplac -> &Laplace - case 2956: state = 2964; break; // &lat -> &late - case 2979: state = 2980; break; // &lbrac -> &lbrace - case 2982: state = 2983; break; // &lbrk -> &lbrke - case 2988: state = 2998; break; // &Lc -> &Lce - case 2993: state = 3002; break; // &lc -> &lce - case 3039: state = 3040; break; // &LeftAngl -> &LeftAngle - case 3045: state = 3046; break; // &LeftAngleBrack -> &LeftAngleBracke - case 3081: state = 3082; break; // &LeftC -> &LeftCe - case 3092: state = 3093; break; // &LeftDoubl -> &LeftDouble - case 3098: state = 3099; break; // &LeftDoubleBrack -> &LeftDoubleBracke - case 3103: state = 3104; break; // &LeftDownT -> &LeftDownTe - case 3104: state = 3105; break; // &LeftDownTe -> &LeftDownTee - case 3106: state = 3107; break; // &LeftDownTeeV -> &LeftDownTeeVe - case 3112: state = 3113; break; // &LeftDownV -> &LeftDownVe - case 3139: state = 3140; break; // &leftl -> &leftle - case 3198: state = 3199; break; // &LeftRightV -> &LeftRightVe - case 3204: state = 3205; break; // &LeftT -> &LeftTe - case 3205: state = 3206; break; // &LeftTe -> &LeftTee - case 3212: state = 3213; break; // &LeftTeeV -> &LeftTeeVe - case 3220: state = 3221; break; // &leftthr -> &leftthre - case 3221: state = 3222; break; // &leftthre -> &leftthree - case 3225: state = 3226; break; // &leftthreetim -> &leftthreetime - case 3233: state = 3234; break; // &LeftTriangl -> &LeftTriangle - case 3249: state = 3250; break; // &LeftUpDownV -> &LeftUpDownVe - case 3255: state = 3256; break; // &LeftUpT -> &LeftUpTe - case 3256: state = 3257; break; // &LeftUpTe -> &LeftUpTee - case 3258: state = 3259; break; // &LeftUpTeeV -> &LeftUpTeeVe - case 3264: state = 3265; break; // &LeftUpV -> &LeftUpVe - case 3273: state = 3274; break; // &LeftV -> &LeftVe - case 3299: state = 3300; break; // &lesg -> &lesge - case 3302: state = 3312; break; // &less -> &lesse - case 3329: state = 3330; break; // &LessEqualGr -> &LessEqualGre - case 3332: state = 3333; break; // &LessEqualGreat -> &LessEqualGreate - case 3345: state = 3346; break; // &LessGr -> &LessGre - case 3348: state = 3349; break; // &LessGreat -> &LessGreate - case 3354: state = 3355; break; // &LessL -> &LessLe - case 3374: state = 3375; break; // &LessTild -> &LessTilde - case 3408: state = 3419; break; // &Ll -> &Lle - case 3416: state = 3417; break; // &llcorn -> &llcorne - case 3450: state = 3451; break; // &lmoustach -> &lmoustache - case 3452: state = 3460; break; // &ln -> &lne - case 3478: state = 3479; break; // &LongL -> &LongLe - case 3487: state = 3488; break; // &Longl -> &Longle - case 3498: state = 3499; break; // &longl -> &longle - case 3580: state = 3581; break; // &looparrowl -> &looparrowle - case 3600: state = 3601; break; // &lotim -> &lotime - case 3610: state = 3611; break; // &Low -> &Lowe - case 3613: state = 3614; break; // &LowerL -> &LowerLe - case 3632: state = 3633; break; // &loz -> &loze - case 3635: state = 3636; break; // &lozeng -> &lozenge - case 3650: state = 3651; break; // &lrcorn -> &lrcorne - case 3674: state = 3675; break; // &lsim -> &lsime - case 3701: state = 3702; break; // <hr -> <hre - case 3702: state = 3703; break; // <hre -> <hree - case 3705: state = 3706; break; // <im -> <ime - case 3713: state = 3714; break; // <qu -> <que - case 3718: state = 3719; break; // <ri -> <rie - case 3735: state = 3736; break; // &lv -> &lve - case 3739: state = 3740; break; // &lvertn -> &lvertne - case 3745: state = 3792; break; // &m -> &me - case 3749: state = 3750; break; // &mal -> &male - case 3751: state = 3752; break; // &malt -> &malte - case 3753: state = 3754; break; // &maltes -> &maltese - case 3755: state = 3804; break; // &M -> &Me - case 3766: state = 3767; break; // &mapstol -> &mapstole - case 3773: state = 3774; break; // &mark -> &marke - case 3796: state = 3797; break; // &measur -> &measure - case 3802: state = 3803; break; // &measuredangl -> &measuredangle - case 3812: state = 3813; break; // &MediumSpac -> &MediumSpace - case 3866: state = 3867; break; // &mod -> &mode - case 3897: state = 3970; break; // &n -> &ne - case 3902: state = 3984; break; // &N -> &Ne - case 3906: state = 3907; break; // &Nacut -> &Nacute - case 3910: state = 3911; break; // &nacut -> &nacute - case 3935: state = 3936; break; // &nbump -> &nbumpe - case 3937: state = 3952; break; // &nc -> &nce - case 3940: state = 3948; break; // &Nc -> &Nce - case 3989: state = 3990; break; // &Negativ -> &Negative - case 3991: state = 3992; break; // &NegativeM -> &NegativeMe - case 4000: state = 4001; break; // &NegativeMediumSpac -> &NegativeMediumSpace - case 4010: state = 4011; break; // &NegativeThickSpac -> &NegativeThickSpace - case 4016: state = 4017; break; // &NegativeThinSpac -> &NegativeThinSpace - case 4018: state = 4019; break; // &NegativeV -> &NegativeVe - case 4029: state = 4030; break; // &NegativeVeryThinSpac -> &NegativeVeryThinSpace - case 4035: state = 4036; break; // &nes -> &nese - case 4042: state = 4043; break; // &Nest -> &Neste - case 4046: state = 4047; break; // &NestedGr -> &NestedGre - case 4049: state = 4050; break; // &NestedGreat -> &NestedGreate - case 4053: state = 4054; break; // &NestedGreaterGr -> &NestedGreaterGre - case 4056: state = 4057; break; // &NestedGreaterGreat -> &NestedGreaterGreate - case 4059: state = 4060; break; // &NestedL -> &NestedLe - case 4063: state = 4064; break; // &NestedLessL -> &NestedLessLe - case 4070: state = 4071; break; // &NewLin -> &NewLine - case 4081: state = 4083; break; // &ng -> &nge - case 4121: state = 4131; break; // &nl -> &nle - case 4132: state = 4133; break; // &nL -> &nLe - case 4184: state = 4185; break; // &nltri -> &nltrie - case 4192: state = 4193; break; // &NoBr -> &NoBre - case 4198: state = 4199; break; // &NonBr -> &NonBre - case 4208: state = 4209; break; // &NonBreakingSpac -> &NonBreakingSpace - case 4222: state = 4223; break; // &NotCongru -> &NotCongrue - case 4235: state = 4236; break; // &NotDoubl -> &NotDouble - case 4237: state = 4238; break; // &NotDoubleV -> &NotDoubleVe - case 4249: state = 4250; break; // &NotEl -> &NotEle - case 4251: state = 4252; break; // &NotElem -> &NotEleme - case 4262: state = 4263; break; // &NotEqualTild -> &NotEqualTilde - case 4270: state = 4271; break; // &NotGr -> &NotGre - case 4273: state = 4274; break; // &NotGreat -> &NotGreate - case 4291: state = 4292; break; // &NotGreaterGr -> &NotGreaterGre - case 4294: state = 4295; break; // &NotGreaterGreat -> &NotGreaterGreate - case 4297: state = 4298; break; // &NotGreaterL -> &NotGreaterLe - case 4314: state = 4315; break; // &NotGreaterTild -> &NotGreaterTilde - case 4343: state = 4344; break; // &NotL -> &NotLe - case 4353: state = 4354; break; // &NotLeftTriangl -> &NotLeftTriangle - case 4371: state = 4372; break; // &NotLessGr -> &NotLessGre - case 4374: state = 4375; break; // &NotLessGreat -> &NotLessGreate - case 4377: state = 4378; break; // &NotLessL -> &NotLessLe - case 4394: state = 4395; break; // &NotLessTild -> &NotLessTilde - case 4396: state = 4397; break; // &NotN -> &NotNe - case 4399: state = 4400; break; // &NotNest -> &NotNeste - case 4403: state = 4404; break; // &NotNestedGr -> &NotNestedGre - case 4406: state = 4407; break; // &NotNestedGreat -> &NotNestedGreate - case 4410: state = 4411; break; // &NotNestedGreaterGr -> &NotNestedGreaterGre - case 4413: state = 4414; break; // &NotNestedGreaterGreat -> &NotNestedGreaterGreate - case 4416: state = 4417; break; // &NotNestedL -> &NotNestedLe - case 4420: state = 4421; break; // &NotNestedLessL -> &NotNestedLessLe - case 4431: state = 4432; break; // &NotPr -> &NotPre - case 4433: state = 4434; break; // &NotPrec -> &NotPrece - case 4435: state = 4436; break; // &NotPreced -> &NotPrecede - case 4453: state = 4454; break; // &NotR -> &NotRe - case 4455: state = 4456; break; // &NotRev -> &NotReve - case 4458: state = 4459; break; // &NotRevers -> &NotReverse - case 4461: state = 4462; break; // &NotReverseEl -> &NotReverseEle - case 4463: state = 4464; break; // &NotReverseElem -> &NotReverseEleme - case 4477: state = 4478; break; // &NotRightTriangl -> &NotRightTriangle - case 4491: state = 4492; break; // &NotSquar -> &NotSquare - case 4496: state = 4497; break; // &NotSquareSubs -> &NotSquareSubse - case 4504: state = 4505; break; // &NotSquareSup -> &NotSquareSupe - case 4507: state = 4508; break; // &NotSquareSupers -> &NotSquareSuperse - case 4517: state = 4518; break; // &NotSubs -> &NotSubse - case 4526: state = 4527; break; // &NotSucc -> &NotSucce - case 4527: state = 4528; break; // &NotSucce -> &NotSuccee - case 4549: state = 4550; break; // &NotSucceedsTild -> &NotSucceedsTilde - case 4551: state = 4552; break; // &NotSup -> &NotSupe - case 4554: state = 4555; break; // &NotSupers -> &NotSuperse - case 4565: state = 4566; break; // &NotTild -> &NotTilde - case 4584: state = 4585; break; // &NotTildeTild -> &NotTildeTilde - case 4586: state = 4587; break; // &NotV -> &NotVe - case 4602: state = 4603; break; // &nparall -> &nparalle - case 4613: state = 4617; break; // &npr -> &npre - case 4615: state = 4616; break; // &nprcu -> &nprcue - case 4618: state = 4619; break; // &nprec -> &nprece - case 4651: state = 4652; break; // &nrtri -> &nrtrie - case 4654: state = 4658; break; // &nsc -> &nsce - case 4656: state = 4657; break; // &nsccu -> &nsccue - case 4675: state = 4676; break; // &nshortparall -> &nshortparalle - case 4679: state = 4680; break; // &nsim -> &nsime - case 4691: state = 4692; break; // &nsqsub -> &nsqsube - case 4693: state = 4694; break; // &nsqsup -> &nsqsupe - case 4696: state = 4698; break; // &nsub -> &nsube - case 4699: state = 4700; break; // &nsubs -> &nsubse - case 4701: state = 4702; break; // &nsubset -> &nsubsete - case 4706: state = 4707; break; // &nsucc -> &nsucce - case 4709: state = 4711; break; // &nsup -> &nsupe - case 4712: state = 4713; break; // &nsups -> &nsupse - case 4714: state = 4715; break; // &nsupset -> &nsupsete - case 4724: state = 4725; break; // &Ntild -> Ñ - case 4728: state = 4729; break; // &ntild -> ñ - case 4737: state = 4738; break; // &ntriangl -> &ntriangle - case 4739: state = 4740; break; // &ntrianglel -> &ntrianglele - case 4742: state = 4743; break; // &ntriangleleft -> &ntrianglelefte - case 4749: state = 4750; break; // &ntriangleright -> &ntrianglerighte - case 4754: state = 4755; break; // &num -> &nume - case 4780: state = 4781; break; // &nvg -> &nvge - case 4792: state = 4796; break; // &nvl -> &nvle - case 4799: state = 4800; break; // &nvltri -> &nvltrie - case 4807: state = 4808; break; // &nvrtri -> &nvrtrie - case 4823: state = 4824; break; // &nwn -> &nwne - case 4831: state = 4832; break; // &Oacut -> Ó - case 4833: state = 4876; break; // &o -> &oe - case 4837: state = 4838; break; // &oacut -> ó - case 4893: state = 4894; break; // &Ograv -> Ò - case 4897: state = 4898; break; // &ograv -> ò - case 4920: state = 4921; break; // &olin -> &oline - case 4923: state = 4931; break; // &Om -> &Ome - case 4927: state = 4934; break; // &om -> &ome - case 4957: state = 4984; break; // &op -> &ope - case 4960: state = 4961; break; // &Op -> &Ope - case 4972: state = 4973; break; // &OpenCurlyDoubl -> &OpenCurlyDouble - case 4977: state = 4978; break; // &OpenCurlyDoubleQuot -> &OpenCurlyDoubleQuote - case 4982: state = 4983; break; // &OpenCurlyQuot -> &OpenCurlyQuote - case 4995: state = 4996; break; // &ord -> &orde - case 5011: state = 5012; break; // &orslop -> &orslope - case 5034: state = 5035; break; // &Otild -> Õ - case 5039: state = 5040; break; // &otild -> õ - case 5041: state = 5042; break; // &Otim -> &Otime - case 5044: state = 5045; break; // &otim -> &otime - case 5059: state = 5060; break; // &Ov -> &Ove - case 5067: state = 5068; break; // &OverBrac -> &OverBrace - case 5069: state = 5070; break; // &OverBrack -> &OverBracke - case 5074: state = 5075; break; // &OverPar -> &OverPare - case 5078: state = 5079; break; // &OverParenth -> &OverParenthe - case 5083: state = 5108; break; // &p -> &pe - case 5088: state = 5089; break; // ¶ll -> ¶lle - case 5120: state = 5121; break; // &pert -> &perte - case 5138: state = 5139; break; // &phon -> &phone - case 5159: state = 5171; break; // &plus -> &pluse - case 5194: state = 5195; break; // &Poincar -> &Poincare - case 5199: state = 5200; break; // &Poincareplan -> &Poincareplane - case 5215: state = 5238; break; // &Pr -> &Pre - case 5216: state = 5223; break; // &pr -> &pre - case 5220: state = 5221; break; // &prcu -> &prcue - case 5224: state = 5264; break; // &prec -> &prece - case 5235: state = 5236; break; // &preccurly -> &preccurlye - case 5239: state = 5240; break; // &Prec -> &Prece - case 5241: state = 5242; break; // &Preced -> &Precede - case 5262: state = 5263; break; // &PrecedesTild -> &PrecedesTilde - case 5266: state = 5273; break; // &precn -> &precne - case 5283: state = 5284; break; // &Prim -> &Prime - case 5286: state = 5287; break; // &prim -> &prime - case 5310: state = 5311; break; // &proflin -> &profline - case 5332: state = 5333; break; // &prur -> &prure - case 5366: state = 5367; break; // &qprim -> &qprime - case 5374: state = 5387; break; // &qu -> &que - case 5376: state = 5377; break; // &quat -> &quate - case 5389: state = 5390; break; // &quest -> &queste - case 5397: state = 5541; break; // &r -> &re - case 5402: state = 5417; break; // &ra -> &rae - case 5403: state = 5404; break; // &rac -> &race - case 5405: state = 5540; break; // &R -> &Re - case 5409: state = 5410; break; // &Racut -> &Racute - case 5412: state = 5413; break; // &racut -> &racute - case 5426: state = 5428; break; // &rang -> &range - case 5429: state = 5430; break; // &rangl -> &rangle - case 5493: state = 5494; break; // &rbrac -> &rbrace - case 5496: state = 5497; break; // &rbrk -> &rbrke - case 5502: state = 5512; break; // &Rc -> &Rce - case 5507: state = 5516; break; // &rc -> &rce - case 5545: state = 5546; break; // &realin -> &realine - case 5557: state = 5558; break; // &Rev -> &Reve - case 5560: state = 5561; break; // &Revers -> &Reverse - case 5563: state = 5564; break; // &ReverseEl -> &ReverseEle - case 5565: state = 5566; break; // &ReverseElem -> &ReverseEleme - case 5624: state = 5625; break; // &RightAngl -> &RightAngle - case 5630: state = 5631; break; // &RightAngleBrack -> &RightAngleBracke - case 5654: state = 5655; break; // &RightArrowL -> &RightArrowLe - case 5667: state = 5668; break; // &RightC -> &RightCe - case 5678: state = 5679; break; // &RightDoubl -> &RightDouble - case 5684: state = 5685; break; // &RightDoubleBrack -> &RightDoubleBracke - case 5689: state = 5690; break; // &RightDownT -> &RightDownTe - case 5690: state = 5691; break; // &RightDownTe -> &RightDownTee - case 5692: state = 5693; break; // &RightDownTeeV -> &RightDownTeeVe - case 5698: state = 5699; break; // &RightDownV -> &RightDownVe - case 5725: state = 5726; break; // &rightl -> &rightle - case 5764: state = 5765; break; // &RightT -> &RightTe - case 5765: state = 5766; break; // &RightTe -> &RightTee - case 5772: state = 5773; break; // &RightTeeV -> &RightTeeVe - case 5780: state = 5781; break; // &rightthr -> &rightthre - case 5781: state = 5782; break; // &rightthre -> &rightthree - case 5785: state = 5786; break; // &rightthreetim -> &rightthreetime - case 5793: state = 5794; break; // &RightTriangl -> &RightTriangle - case 5809: state = 5810; break; // &RightUpDownV -> &RightUpDownVe - case 5815: state = 5816; break; // &RightUpT -> &RightUpTe - case 5816: state = 5817; break; // &RightUpTe -> &RightUpTee - case 5818: state = 5819; break; // &RightUpTeeV -> &RightUpTeeVe - case 5824: state = 5825; break; // &RightUpV -> &RightUpVe - case 5833: state = 5834; break; // &RightV -> &RightVe - case 5851: state = 5852; break; // &risingdots -> &risingdotse - case 5869: state = 5870; break; // &rmoustach -> &rmoustache - case 5896: state = 5897; break; // &rotim -> &rotime - case 5906: state = 5907; break; // &RoundImpli -> &RoundImplie - case 5953: state = 5954; break; // &rthr -> &rthre - case 5954: state = 5955; break; // &rthre -> &rthree - case 5957: state = 5958; break; // &rtim -> &rtime - case 5961: state = 5962; break; // &rtri -> &rtrie - case 5969: state = 5970; break; // &Rul -> &Rule - case 5971: state = 5972; break; // &RuleD -> &RuleDe - case 5975: state = 5976; break; // &RuleDelay -> &RuleDelaye - case 5989: state = 5990; break; // &Sacut -> &Sacute - case 5991: state = 6053; break; // &s -> &se - case 5995: state = 5996; break; // &sacut -> &sacute - case 6001: state = 6017; break; // &Sc -> &Sce - case 6002: state = 6016; break; // &sc -> &sce - case 6013: state = 6014; break; // &sccu -> &sccue - case 6050: state = 6052; break; // &sdot -> &sdote - case 6117: state = 6118; break; // &ShortL -> &ShortLe - case 6137: state = 6138; break; // &shortparall -> &shortparalle - case 6168: state = 6172; break; // &sim -> &sime - case 6178: state = 6179; break; // &simn -> &simne - case 6200: state = 6201; break; // &SmallCircl -> &SmallCircle - case 6202: state = 6217; break; // &sm -> &sme - case 6206: state = 6207; break; // &smalls -> &smallse - case 6225: state = 6226; break; // &smil -> &smile - case 6227: state = 6228; break; // &smt -> &smte - case 6251: state = 6252; break; // &spad -> &spade - case 6271: state = 6272; break; // &sqsub -> &sqsube - case 6273: state = 6274; break; // &sqsubs -> &sqsubse - case 6275: state = 6276; break; // &sqsubset -> &sqsubsete - case 6278: state = 6279; break; // &sqsup -> &sqsupe - case 6280: state = 6281; break; // &sqsups -> &sqsupse - case 6282: state = 6283; break; // &sqsupset -> &sqsupsete - case 6288: state = 6289; break; // &Squar -> &Square - case 6291: state = 6292; break; // &squar -> &square - case 6295: state = 6296; break; // &SquareInt -> &SquareInte - case 6298: state = 6299; break; // &SquareInters -> &SquareInterse - case 6308: state = 6309; break; // &SquareSubs -> &SquareSubse - case 6316: state = 6317; break; // &SquareSup -> &SquareSupe - case 6319: state = 6320; break; // &SquareSupers -> &SquareSuperse - case 6341: state = 6344; break; // &ss -> &sse - case 6350: state = 6351; break; // &ssmil -> &ssmile - case 6368: state = 6369; break; // &straight -> &straighte - case 6384: state = 6389; break; // &sub -> &sube - case 6397: state = 6399; break; // &subn -> &subne - case 6408: state = 6409; break; // &Subs -> &Subse - case 6411: state = 6412; break; // &subs -> &subse - case 6413: state = 6414; break; // &subset -> &subsete - case 6422: state = 6423; break; // &subsetn -> &subsetne - case 6432: state = 6472; break; // &succ -> &succe - case 6443: state = 6444; break; // &succcurly -> &succcurlye - case 6447: state = 6448; break; // &Succ -> &Succe - case 6448: state = 6449; break; // &Succe -> &Succee - case 6470: state = 6471; break; // &SucceedsTild -> &SucceedsTilde - case 6474: state = 6481; break; // &succn -> &succne - case 6499: state = 6515; break; // &Sup -> &Supe - case 6500: state = 6511; break; // &sup -> &supe - case 6517: state = 6518; break; // &Supers -> &Superse - case 6539: state = 6541; break; // &supn -> &supne - case 6546: state = 6547; break; // &Sups -> &Supse - case 6549: state = 6550; break; // &sups -> &supse - case 6551: state = 6552; break; // &supset -> &supsete - case 6555: state = 6556; break; // &supsetn -> &supsetne - case 6586: state = 6620; break; // &t -> &te - case 6589: state = 6590; break; // &targ -> &targe - case 6597: state = 6607; break; // &Tc -> &Tce - case 6602: state = 6611; break; // &tc -> &tce - case 6622: state = 6623; break; // &telr -> &telre - case 6629: state = 6630; break; // &th -> &the - case 6631: state = 6632; break; // &ther -> &there - case 6634: state = 6635; break; // &Th -> &The - case 6636: state = 6637; break; // &Ther -> &There - case 6640: state = 6641; break; // &Therefor -> &Therefore - case 6644: state = 6645; break; // &therefor -> &therefore - case 6672: state = 6673; break; // &ThickSpac -> &ThickSpace - case 6681: state = 6682; break; // &ThinSpac -> &ThinSpace - case 6698: state = 6699; break; // &Tild -> &Tilde - case 6702: state = 6703; break; // &tild -> &tilde - case 6721: state = 6722; break; // &TildeTild -> &TildeTilde - case 6723: state = 6724; break; // &tim -> &time - case 6732: state = 6733; break; // &to -> &toe - case 6754: state = 6755; break; // &tprim -> &tprime - case 6762: state = 6763; break; // &trad -> &trade - case 6764: state = 6791; break; // &tri -> &trie - case 6768: state = 6769; break; // &triangl -> &triangle - case 6774: state = 6775; break; // &trianglel -> &trianglele - case 6777: state = 6778; break; // &triangleleft -> &trianglelefte - case 6785: state = 6786; break; // &triangleright -> &trianglerighte - case 6800: state = 6801; break; // &Tripl -> &Triple - case 6813: state = 6814; break; // &tritim -> &tritime - case 6815: state = 6816; break; // &trp -> &trpe - case 6850: state = 6851; break; // &twoh -> &twohe - case 6854: state = 6855; break; // &twoheadl -> &twoheadle - case 6877: state = 6878; break; // &Uacut -> Ú - case 6883: state = 6884; break; // &uacut -> ú - case 6897: state = 6904; break; // &Ubr -> &Ubre - case 6901: state = 6907; break; // &ubr -> &ubre - case 6905: state = 6906; break; // &Ubrev -> &Ubreve - case 6908: state = 6909; break; // &ubrev -> &ubreve - case 6947: state = 6948; break; // &Ugrav -> Ù - case 6952: state = 6953; break; // &ugrav -> ù - case 6969: state = 6970; break; // &ulcorn -> &ulcorne - case 6988: state = 6989; break; // &Und -> &Unde - case 6996: state = 6997; break; // &UnderBrac -> &UnderBrace - case 6998: state = 6999; break; // &UnderBrack -> &UnderBracke - case 7003: state = 7004; break; // &UnderPar -> &UnderPare - case 7007: state = 7008; break; // &UnderParenth -> &UnderParenthe - case 7105: state = 7106; break; // &upharpoonl -> &upharpoonle - case 7117: state = 7118; break; // &Upp -> &Uppe - case 7120: state = 7121; break; // &UpperL -> &UpperLe - case 7150: state = 7151; break; // &UpT -> &UpTe - case 7151: state = 7152; break; // &UpTe -> &UpTee - case 7170: state = 7171; break; // &urcorn -> &urcorne - case 7199: state = 7200; break; // &Utild -> &Utilde - case 7203: state = 7204; break; // &utild -> &utilde - case 7221: state = 7222; break; // &uwangl -> &uwangle - case 7223: state = 7338; break; // &v -> &ve - case 7229: state = 7230; break; // &var -> &vare - case 7271: state = 7272; break; // &varsubs -> &varsubse - case 7274: state = 7275; break; // &varsubsetn -> &varsubsetne - case 7279: state = 7280; break; // &varsups -> &varsupse - case 7282: state = 7283; break; // &varsupsetn -> &varsupsetne - case 7287: state = 7288; break; // &varth -> &varthe - case 7296: state = 7297; break; // &vartriangl -> &vartriangle - case 7298: state = 7299; break; // &vartrianglel -> &vartrianglele - case 7307: state = 7336; break; // &V -> &Ve - case 7336: state = 7337; break; // &Ve -> &Vee - case 7338: state = 7339; break; // &ve -> &vee - case 7339: state = 7343; break; // &vee -> &veee - case 7368: state = 7369; break; // &VerticalLin -> &VerticalLine - case 7370: state = 7371; break; // &VerticalS -> &VerticalSe - case 7382: state = 7383; break; // &VerticalTild -> &VerticalTilde - case 7392: state = 7393; break; // &VeryThinSpac -> &VeryThinSpace - case 7429: state = 7431; break; // &vsubn -> &vsubne - case 7433: state = 7435; break; // &vsupn -> &vsupne - case 7447: state = 7462; break; // &W -> &We - case 7452: state = 7457; break; // &w -> &we - case 7464: state = 7465; break; // &Wedg -> &Wedge - case 7466: state = 7467; break; // &wedg -> &wedge - case 7469: state = 7470; break; // &wei -> &weie - case 7484: state = 7485; break; // &wr -> &wre - case 7549: state = 7550; break; // &xotim -> &xotime - case 7576: state = 7577; break; // &xv -> &xve - case 7577: state = 7578; break; // &xve -> &xvee - case 7579: state = 7580; break; // &xw -> &xwe - case 7582: state = 7583; break; // &xwedg -> &xwedge - case 7588: state = 7589; break; // &Yacut -> Ý - case 7590: state = 7610; break; // &y -> &ye - case 7594: state = 7595; break; // &yacut -> ý - case 7645: state = 7680; break; // &Z -> &Ze - case 7649: state = 7650; break; // &Zacut -> &Zacute - case 7651: state = 7675; break; // &z -> &ze - case 7655: state = 7656; break; // &zacut -> &zacute - case 7675: state = 7676; break; // &ze -> &zee - case 7691: state = 7692; break; // &ZeroWidthSpac -> &ZeroWidthSpace - default: return false; - } - break; - case 'f': - switch (state) { - case 0: state = 1964; break; // & -> &f - case 1: state = 47; break; // &A -> &Af - case 7: state = 46; break; // &a -> &af - case 61: state = 62; break; // &ale -> &alef - case 109: state = 115; break; // &angmsda -> &angmsdaf - case 139: state = 140; break; // &Aop -> &Aopf - case 141: state = 142; break; // &aop -> &aopf - case 222: state = 331; break; // &b -> &bf - case 247: state = 329; break; // &B -> &Bf - case 426: state = 427; break; // &blacktrianglele -> &blacktrianglelef - case 457: state = 458; break; // &Bop -> &Bopf - case 460: state = 461; break; // &bop -> &bopf - case 583: state = 712; break; // &C -> &Cf - case 589: state = 714; break; // &c -> &cf - case 618: state = 619; break; // &CapitalDi -> &CapitalDif - case 619: state = 620; break; // &CapitalDif -> &CapitalDiff - case 733: state = 791; break; // &cir -> &cirf - case 745: state = 746; break; // &circlearrowle -> &circlearrowlef - case 871: state = 872; break; // &comp -> &compf - case 913: state = 914; break; // &Cop -> &Copf - case 915: state = 916; break; // &cop -> &copf - case 1062: state = 1063; break; // &curvearrowle -> &curvearrowlef - case 1091: state = 1180; break; // &D -> &Df - case 1097: state = 1175; break; // &d -> &df - case 1191: state = 1244; break; // &Di -> &Dif - case 1244: state = 1245; break; // &Dif -> &Diff - case 1297: state = 1298; break; // &Dop -> &Dopf - case 1299: state = 1300; break; // &dop -> &dopf - case 1373: state = 1374; break; // &DoubleLe -> &DoubleLef - case 1398: state = 1399; break; // &DoubleLongLe -> &DoubleLongLef - case 1518: state = 1519; break; // &downharpoonle -> &downharpoonlef - case 1527: state = 1528; break; // &DownLe -> &DownLef - case 1629: state = 1630; break; // &dtri -> &dtrif - case 1656: state = 1711; break; // &E -> &Ef - case 1662: state = 1707; break; // &e -> &ef - case 1809: state = 1810; break; // &Eop -> &Eopf - case 1811: state = 1812; break; // &eop -> &eopf - case 1964: state = 1987; break; // &f -> &ff - case 1977: state = 1998; break; // &F -> &Ff - case 2050: state = 2051; break; // &fno -> &fnof - case 2053: state = 2054; break; // &Fop -> &Fopf - case 2056: state = 2057; break; // &fop -> &fopf - case 2074: state = 2075; break; // &Fouriertr -> &Fouriertrf - case 2118: state = 2189; break; // &g -> &gf - case 2124: state = 2187; break; // &G -> &Gf - case 2223: state = 2224; break; // &Gop -> &Gopf - case 2226: state = 2227; break; // &gop -> &gopf - case 2351: state = 2413; break; // &H -> &Hf - case 2356: state = 2415; break; // &h -> &hf - case 2362: state = 2363; break; // &hal -> &half - case 2451: state = 2452; break; // &hookle -> &hooklef - case 2470: state = 2471; break; // &Hop -> &Hopf - case 2472: state = 2473; break; // &hop -> &hopf - case 2533: state = 2569; break; // &I -> &If - case 2539: state = 2567; break; // &i -> &if - case 2567: state = 2568; break; // &if -> &iff - case 2589: state = 2590; break; // &iin -> &iinf - case 2631: state = 2632; break; // &imo -> &imof - case 2641: state = 2646; break; // &in -> &inf - case 2723: state = 2724; break; // &Iop -> &Iopf - case 2725: state = 2726; break; // &iop -> &iopf - case 2777: state = 2789; break; // &J -> &Jf - case 2782: state = 2791; break; // &j -> &jf - case 2798: state = 2799; break; // &Jop -> &Jopf - case 2801: state = 2802; break; // &jop -> &jopf - case 2825: state = 2848; break; // &K -> &Kf - case 2830: state = 2850; break; // &k -> &kf - case 2870: state = 2871; break; // &Kop -> &Kopf - case 2873: state = 2874; break; // &kop -> &kopf - case 2881: state = 3376; break; // &l -> &lf - case 2886: state = 3385; break; // &L -> &Lf - case 2929: state = 2930; break; // &Laplacetr -> &Laplacetrf - case 2939: state = 2943; break; // &larr -> &larrf - case 2940: state = 2941; break; // &larrb -> &larrbf - case 3032: state = 3057; break; // &le -> &lef - case 3033: state = 3034; break; // &Le -> &Lef - case 3140: state = 3141; break; // &leftle -> &leftlef - case 3419: state = 3420; break; // &Lle -> &Llef - case 3479: state = 3480; break; // &LongLe -> &LongLef - case 3488: state = 3489; break; // &Longle -> &Longlef - case 3499: state = 3500; break; // &longle -> &longlef - case 3581: state = 3582; break; // &looparrowle -> &looparrowlef - case 3589: state = 3594; break; // &lop -> &lopf - case 3592: state = 3593; break; // &Lop -> &Lopf - case 3614: state = 3615; break; // &LowerLe -> &LowerLef - case 3632: state = 3637; break; // &loz -> &lozf - case 3718: state = 3720; break; // <ri -> <rif - case 3745: state = 3823; break; // &m -> &mf - case 3755: state = 3821; break; // &M -> &Mf - case 3767: state = 3768; break; // &mapstole -> &mapstolef - case 3819: state = 3820; break; // &Mellintr -> &Mellintrf - case 3871: state = 3872; break; // &Mop -> &Mopf - case 3873: state = 3874; break; // &mop -> &mopf - case 3897: state = 4079; break; // &n -> &nf - case 3902: state = 4077; break; // &N -> &Nf - case 4131: state = 4141; break; // &nle -> &nlef - case 4133: state = 4134; break; // &nLe -> &nLef - case 4210: state = 4211; break; // &Nop -> &Nopf - case 4213: state = 4214; break; // &nop -> &nopf - case 4344: state = 4345; break; // &NotLe -> &NotLef - case 4740: state = 4741; break; // &ntrianglele -> &ntrianglelef - case 4788: state = 4789; break; // &nvin -> &nvinf - case 4827: state = 4884; break; // &O -> &Of - case 4833: state = 4880; break; // &o -> &of - case 4952: state = 4953; break; // &Oop -> &Oopf - case 4955: state = 4956; break; // &oop -> &oopf - case 4995: state = 5000; break; // &ord -> ª - case 4998: state = 4999; break; // &ordero -> &orderof - case 5004: state = 5005; break; // &origo -> &origof - case 5083: state = 5126; break; // &p -> &pf - case 5096: state = 5124; break; // &P -> &Pf - case 5144: state = 5145; break; // &pitch -> &pitchf - case 5208: state = 5209; break; // &Pop -> &Popf - case 5210: state = 5211; break; // &pop -> &popf - case 5296: state = 5303; break; // &pro -> &prof - case 5314: state = 5315; break; // &profsur -> &profsurf - case 5348: state = 5349; break; // &Q -> &Qf - case 5351: state = 5352; break; // &q -> &qf - case 5358: state = 5359; break; // &Qop -> &Qopf - case 5361: state = 5362; break; // &qop -> &qopf - case 5397: state = 5592; break; // &r -> &rf - case 5405: state = 5601; break; // &R -> &Rf - case 5439: state = 5446; break; // &rarr -> &rarrf - case 5442: state = 5443; break; // &rarrb -> &rarrbf - case 5655: state = 5656; break; // &RightArrowLe -> &RightArrowLef - case 5726: state = 5727; break; // &rightle -> &rightlef - case 5884: state = 5890; break; // &rop -> &ropf - case 5888: state = 5889; break; // &Rop -> &Ropf - case 5961: state = 5963; break; // &rtri -> &rtrif - case 5985: state = 6081; break; // &S -> &Sf - case 5991: state = 6083; break; // &s -> &sf - case 6118: state = 6119; break; // &ShortLe -> &ShortLef - case 6165: state = 6166; break; // &sigma -> &sigmaf - case 6235: state = 6236; break; // &so -> &sof - case 6245: state = 6246; break; // &Sop -> &Sopf - case 6247: state = 6248; break; // &sop -> &sopf - case 6285: state = 6333; break; // &squ -> &squf - case 6291: state = 6332; break; // &squar -> &squarf - case 6354: state = 6355; break; // &sstar -> &sstarf - case 6361: state = 6362; break; // &star -> &starf - case 6583: state = 6625; break; // &T -> &Tf - case 6586: state = 6627; break; // &t -> &tf - case 6632: state = 6642; break; // &there -> &theref - case 6637: state = 6638; break; // &There -> &Theref - case 6735: state = 6745; break; // &top -> &topf - case 6743: state = 6744; break; // &Top -> &Topf - case 6775: state = 6776; break; // &trianglele -> &trianglelef - case 6855: state = 6856; break; // &twoheadle -> &twoheadlef - case 6873: state = 6941; break; // &U -> &Uf - case 6879: state = 6936; break; // &u -> &uf - case 7027: state = 7028; break; // &Uop -> &Uopf - case 7029: state = 7030; break; // &uop -> &uopf - case 7106: state = 7107; break; // &upharpoonle -> &upharpoonlef - case 7121: state = 7122; break; // &UpperLe -> &UpperLef - case 7206: state = 7207; break; // &utri -> &utrif - case 7223: state = 7396; break; // &v -> &vf - case 7299: state = 7300; break; // &vartrianglele -> &vartrianglelef - case 7307: state = 7394; break; // &V -> &Vf - case 7408: state = 7409; break; // &Vop -> &Vopf - case 7411: state = 7412; break; // &vop -> &vopf - case 7447: state = 7473; break; // &W -> &Wf - case 7452: state = 7475; break; // &w -> &wf - case 7478: state = 7479; break; // &Wop -> &Wopf - case 7481: state = 7482; break; // &wop -> &wopf - case 7495: state = 7511; break; // &x -> &xf - case 7508: state = 7509; break; // &X -> &Xf - case 7540: state = 7541; break; // &Xop -> &Xopf - case 7542: state = 7543; break; // &xop -> &xopf - case 7584: state = 7612; break; // &Y -> &Yf - case 7590: state = 7614; break; // &y -> &yf - case 7623: state = 7624; break; // &Yop -> &Yopf - case 7626: state = 7627; break; // &yop -> &yopf - case 7645: state = 7697; break; // &Z -> &Zf - case 7651: state = 7699; break; // &z -> &zf - case 7678: state = 7679; break; // &zeetr -> &zeetrf - case 7714: state = 7715; break; // &Zop -> &Zopf - case 7717: state = 7718; break; // &zop -> &zopf - default: return false; - } - break; - case 'g': - switch (state) { - case 0: state = 2118; break; // & -> &g - case 1: state = 50; break; // &A -> &Ag - case 7: state = 55; break; // &a -> &ag - case 40: state = 41; break; // &AEli -> Æ - case 44: state = 45; break; // &aeli -> æ - case 83: state = 84; break; // &amal -> &amalg - case 90: state = 102; break; // &an -> &ang - case 109: state = 116; break; // &angmsda -> &angmsdag - case 131: state = 132; break; // &Ao -> &Aog - case 135: state = 136; break; // &ao -> &aog - case 174: state = 175; break; // &Arin -> Å - case 178: state = 179; break; // &arin -> å - case 187: state = 188; break; // &Assi -> &Assig - case 228: state = 229; break; // &backcon -> &backcong - case 267: state = 268; break; // &barwed -> &barwedg - case 279: state = 280; break; // &bcon -> &bcong - case 333: state = 334; break; // &bi -> &big - case 368: state = 369; break; // &bigtrian -> &bigtriang - case 388: state = 389; break; // &bigwed -> &bigwedg - case 404: state = 405; break; // &blacklozen -> &blacklozeng - case 417: state = 418; break; // &blacktrian -> &blacktriang - case 430: state = 431; break; // &blacktriangleri -> &blacktrianglerig - case 749: state = 750; break; // &circlearrowri -> &circlearrowrig - case 821: state = 822; break; // &ClockwiseContourInte -> &ClockwiseContourInteg - case 883: state = 884; break; // &con -> &cong - case 888: state = 889; break; // &Con -> &Cong - case 908: state = 909; break; // &ContourInte -> &ContourInteg - case 956: state = 957; break; // &CounterClockwiseContourInte -> &CounterClockwiseContourInteg - case 1048: state = 1049; break; // &curlywed -> &curlywedg - case 1066: state = 1067; break; // &curvearrowri -> &curvearrowrig - case 1092: state = 1093; break; // &Da -> &Dag - case 1093: state = 1094; break; // &Dag -> &Dagg - case 1098: state = 1099; break; // &da -> &dag - case 1099: state = 1100; break; // &dag -> &dagg - case 1143: state = 1144; break; // &dda -> &ddag - case 1144: state = 1145; break; // &ddag -> &ddagg - case 1161: state = 1162; break; // &de -> ° - case 1228: state = 1255; break; // &di -> &dig - case 1340: state = 1341; break; // &doublebarwed -> &doublebarwedg - case 1357: state = 1358; break; // &DoubleContourInte -> &DoubleContourInteg - case 1382: state = 1383; break; // &DoubleLeftRi -> &DoubleLeftRig - case 1395: state = 1396; break; // &DoubleLon -> &DoubleLong - case 1407: state = 1408; break; // &DoubleLongLeftRi -> &DoubleLongLeftRig - case 1417: state = 1418; break; // &DoubleLongRi -> &DoubleLongRig - case 1427: state = 1428; break; // &DoubleRi -> &DoubleRig - case 1522: state = 1523; break; // &downharpoonri -> &downharpoonrig - case 1531: state = 1532; break; // &DownLeftRi -> &DownLeftRig - case 1560: state = 1561; break; // &DownRi -> &DownRig - case 1640: state = 1641; break; // &dwan -> &dwang - case 1650: state = 1651; break; // &dzi -> &dzig - case 1656: state = 1715; break; // &E -> &Eg - case 1662: state = 1714; break; // &e -> &eg - case 1797: state = 1798; break; // &en -> &eng - case 1801: state = 1802; break; // &Eo -> &Eog - case 1805: state = 1806; break; // &eo -> &eog - case 1848: state = 1849; break; // &eqslant -> &eqslantg - case 1969: state = 1970; break; // &fallin -> &falling - case 1990: state = 1991; break; // &ffili -> &ffilig - case 1993: state = 1994; break; // &ffli -> &fflig - case 1996: state = 1997; break; // &fflli -> &ffllig - case 2003: state = 2004; break; // &fili -> &filig - case 2038: state = 2039; break; // &fjli -> &fjlig - case 2044: state = 2045; break; // &flli -> &fllig - case 2118: state = 2192; break; // &g -> &gg - case 2124: state = 2191; break; // &G -> &Gg - case 2192: state = 2193; break; // &gg -> &ggg - case 2460: state = 2461; break; // &hookri -> &hookrig - case 2533: state = 2572; break; // &I -> &Ig - case 2539: state = 2577; break; // &i -> &ig - case 2598: state = 2599; break; // &IJli -> &IJlig - case 2602: state = 2603; break; // &ijli -> &ijlig - case 2605: state = 2614; break; // &Ima -> &Imag - case 2609: state = 2612; break; // &ima -> &imag - case 2662: state = 2663; break; // &inte -> &integ - case 2667: state = 2668; break; // &Inte -> &Integ - case 2713: state = 2720; break; // &io -> &iog - case 2716: state = 2717; break; // &Io -> &Iog - case 2830: state = 2852; break; // &k -> &kg - case 2881: state = 3388; break; // &l -> &lg - case 2892: state = 2903; break; // &la -> &lag - case 2915: state = 2916; break; // &Lan -> &Lang - case 2917: state = 2918; break; // &lan -> &lang - case 3031: state = 3282; break; // &lE -> &lEg - case 3032: state = 3283; break; // &le -> &leg - case 3037: state = 3038; break; // &LeftAn -> &LeftAng - case 3068: state = 3069; break; // &LeftArrowRi -> &LeftArrowRig - case 3086: state = 3087; break; // &LeftCeilin -> &LeftCeiling - case 3150: state = 3151; break; // &LeftRi -> &LeftRig - case 3160: state = 3161; break; // &Leftri -> &Leftrig - case 3170: state = 3171; break; // &leftri -> &leftrig - case 3191: state = 3192; break; // &leftrightsqui -> &leftrightsquig - case 3231: state = 3232; break; // &LeftTrian -> &LeftTriang - case 3291: state = 3299; break; // &les -> &lesg - case 3302: state = 3351; break; // &less -> &lessg - case 3313: state = 3314; break; // &lesseq -> &lesseqg - case 3317: state = 3318; break; // &lesseqq -> &lesseqqg - case 3468: state = 3469; break; // &loan -> &loang - case 3476: state = 3477; break; // &Lon -> &Long - case 3496: state = 3497; break; // &lon -> &long - case 3508: state = 3509; break; // &LongLeftRi -> &LongLeftRig - case 3518: state = 3519; break; // &Longleftri -> &Longleftrig - case 3528: state = 3529; break; // &longleftri -> &longleftrig - case 3544: state = 3545; break; // &LongRi -> &LongRig - case 3554: state = 3555; break; // &Longri -> &Longrig - case 3564: state = 3565; break; // &longri -> &longrig - case 3585: state = 3586; break; // &looparrowri -> &looparrowrig - case 3623: state = 3624; break; // &LowerRi -> &LowerRig - case 3634: state = 3635; break; // &lozen -> &lozeng - case 3674: state = 3676; break; // &lsim -> &lsimg - case 3800: state = 3801; break; // &measuredan -> &measuredang - case 3897: state = 4081; break; // &n -> &ng - case 3912: state = 3913; break; // &nan -> &nang - case 3957: state = 3958; break; // &ncon -> &ncong - case 3984: state = 3985; break; // &Ne -> &Neg - case 4092: state = 4093; break; // &nG -> &nGg - case 4149: state = 4150; break; // &nLeftri -> &nLeftrig - case 4159: state = 4160; break; // &nleftri -> &nleftrig - case 4203: state = 4204; break; // &NonBreakin -> &NonBreaking - case 4219: state = 4220; break; // &NotCon -> &NotCong - case 4351: state = 4352; break; // &NotLeftTrian -> &NotLeftTriang - case 4467: state = 4468; break; // &NotRi -> &NotRig - case 4475: state = 4476; break; // &NotRightTrian -> &NotRightTriang - case 4631: state = 4632; break; // &nRi -> &nRig - case 4640: state = 4641; break; // &nri -> &nrig - case 4718: state = 4719; break; // &nt -> &ntg - case 4730: state = 4731; break; // &ntl -> &ntlg - case 4735: state = 4736; break; // &ntrian -> &ntriang - case 4746: state = 4747; break; // &ntriangleri -> &ntrianglerig - case 4760: state = 4780; break; // &nv -> &nvg - case 4827: state = 4890; break; // &O -> &Og - case 4833: state = 4887; break; // &o -> &og - case 4874: state = 4875; break; // &OEli -> &OElig - case 4878: state = 4879; break; // &oeli -> &oelig - case 4931: state = 4932; break; // &Ome -> &Omeg - case 4934: state = 4935; break; // &ome -> &omeg - case 5002: state = 5003; break; // &ori -> &orig - case 5423: state = 5424; break; // &Ran -> &Rang - case 5425: state = 5426; break; // &ran -> &rang - case 5541: state = 5556; break; // &re -> ® - case 5617: state = 5618; break; // &Ri -> &Rig - case 5622: state = 5623; break; // &RightAn -> &RightAng - case 5642: state = 5643; break; // &ri -> &rig - case 5672: state = 5673; break; // &RightCeilin -> &RightCeiling - case 5744: state = 5745; break; // &rightri -> &rightrig - case 5757: state = 5758; break; // &rightsqui -> &rightsquig - case 5791: state = 5792; break; // &RightTrian -> &RightTriang - case 5842: state = 5843; break; // &rin -> &ring - case 5846: state = 5847; break; // &risin -> &rising - case 5877: state = 5878; break; // &roan -> &roang - case 5911: state = 5912; break; // &rpar -> &rparg - case 5925: state = 5926; break; // &Rri -> &Rrig - case 6141: state = 6142; break; // &ShortRi -> &ShortRig - case 6158: state = 6159; break; // &Si -> &Sig - case 6162: state = 6163; break; // &si -> &sig - case 6168: state = 6174; break; // &sim -> &simg - case 6365: state = 6366; break; // &strai -> &straig - case 6497: state = 6498; break; // &sun -> &sung - case 6581: state = 6582; break; // &szli -> ß - case 6588: state = 6589; break; // &tar -> &targ - case 6766: state = 6767; break; // &trian -> &triang - case 6782: state = 6783; break; // &triangleri -> &trianglerig - case 6864: state = 6865; break; // &twoheadri -> &twoheadrig - case 6873: state = 6944; break; // &U -> &Ug - case 6879: state = 6949; break; // &u -> &ug - case 7019: state = 7020; break; // &Uo -> &Uog - case 7023: state = 7024; break; // &uo -> &uog - case 7110: state = 7111; break; // &upharpoonri -> &upharpoonrig - case 7130: state = 7131; break; // &UpperRi -> &UpperRig - case 7178: state = 7179; break; // &Urin -> &Uring - case 7181: state = 7182; break; // &urin -> &uring - case 7219: state = 7220; break; // &uwan -> &uwang - case 7225: state = 7226; break; // &van -> &vang - case 7247: state = 7248; break; // &varnothin -> &varnothing - case 7265: state = 7266; break; // &varsi -> &varsig - case 7294: state = 7295; break; // &vartrian -> &vartriang - case 7303: state = 7304; break; // &vartriangleri -> &vartrianglerig - case 7442: state = 7443; break; // &vzi -> &vzig - case 7445: state = 7446; break; // &vzigza -> &vzigzag - case 7458: state = 7466; break; // &wed -> &wedg - case 7463: state = 7464; break; // &Wed -> &Wedg - case 7581: state = 7582; break; // &xwed -> &xwedg - case 7707: state = 7708; break; // &zi -> &zig - default: return false; - } - break; - case 'h': - switch (state) { - case 0: state = 2356; break; // & -> &h - case 66: state = 67; break; // &alep -> &aleph - case 69: state = 70; break; // &Alp -> &Alph - case 72: state = 73; break; // &alp -> &alph - case 109: state = 117; break; // &angmsda -> &angmsdah - case 124: state = 125; break; // &angsp -> &angsph - case 254: state = 255; break; // &Backslas -> &Backslash - case 322: state = 324; break; // &bet -> &beth - case 431: state = 432; break; // &blacktrianglerig -> &blacktrianglerigh - case 470: state = 485; break; // &box -> &boxh - case 518: state = 521; break; // &boxV -> &boxVh - case 519: state = 523; break; // &boxv -> &boxvh - case 562: state = 564; break; // &bsol -> &bsolh - case 583: state = 729; break; // &C -> &Ch - case 589: state = 719; break; // &c -> &ch - case 750: state = 751; break; // &circlearrowrig -> &circlearrowrigh - case 763: state = 764; break; // &circleddas -> &circleddash - case 1067: state = 1068; break; // &curvearrowrig -> &curvearrowrigh - case 1097: state = 1186; break; // &d -> &dh - case 1105: state = 1106; break; // &dalet -> &daleth - case 1114: state = 1115; break; // &das -> &dash - case 1116: state = 1117; break; // &Das -> &Dash - case 1153: state = 1154; break; // &DDotra -> &DDotrah - case 1177: state = 1178; break; // &dfis -> &dfish - case 1383: state = 1384; break; // &DoubleLeftRig -> &DoubleLeftRigh - case 1408: state = 1409; break; // &DoubleLongLeftRig -> &DoubleLongLeftRigh - case 1418: state = 1419; break; // &DoubleLongRig -> &DoubleLongRigh - case 1428: state = 1429; break; // &DoubleRig -> &DoubleRigh - case 1479: state = 1510; break; // &down -> &downh - case 1523: state = 1524; break; // &downharpoonrig -> &downharpoonrigh - case 1532: state = 1533; break; // &DownLeftRig -> &DownLeftRigh - case 1561: state = 1562; break; // &DownRig -> &DownRigh - case 1631: state = 1635; break; // &du -> &duh - case 1912: state = 1916; break; // &et -> ð - case 2445: state = 2446; break; // &homt -> &homth - case 2461: state = 2462; break; // &hookrig -> &hookrigh - case 2498: state = 2499; break; // &hslas -> &hslash - case 2529: state = 2530; break; // &hyp -> &hyph - case 2629: state = 2630; break; // &imat -> &imath - case 2686: state = 2687; break; // &intlar -> &intlarh - case 2795: state = 2796; break; // &jmat -> &jmath - case 2830: state = 2860; break; // &k -> &kh - case 2881: state = 3393; break; // &l -> &lh - case 2939: state = 2945; break; // &larr -> &larrh - case 3020: state = 3021; break; // &ldrd -> &ldrdh - case 3025: state = 3026; break; // &ldrus -> &ldrush - case 3029: state = 3030; break; // &lds -> &ldsh - case 3058: state = 3126; break; // &left -> &lefth - case 3069: state = 3070; break; // &LeftArrowRig -> &LeftArrowRigh - case 3151: state = 3152; break; // &LeftRig -> &LeftRigh - case 3161: state = 3162; break; // &Leftrig -> &Leftrigh - case 3171: state = 3172; break; // &leftrig -> &leftrigh - case 3173: state = 3180; break; // &leftright -> &leftrighth - case 3218: state = 3219; break; // &leftt -> &leftth - case 3378: state = 3379; break; // &lfis -> &lfish - case 3409: state = 3427; break; // &ll -> &llh - case 3449: state = 3450; break; // &lmoustac -> &lmoustach - case 3509: state = 3510; break; // &LongLeftRig -> &LongLeftRigh - case 3519: state = 3520; break; // &Longleftrig -> &Longleftrigh - case 3529: state = 3530; break; // &longleftrig -> &longleftrigh - case 3545: state = 3546; break; // &LongRig -> &LongRigh - case 3555: state = 3556; break; // &Longrig -> &Longrigh - case 3565: state = 3566; break; // &longrig -> &longrigh - case 3586: state = 3587; break; // &looparrowrig -> &looparrowrigh - case 3624: state = 3625; break; // &LowerRig -> &LowerRigh - case 3643: state = 3653; break; // &lr -> &lrh - case 3661: state = 3672; break; // &ls -> &lsh - case 3666: state = 3671; break; // &Ls -> &Lsh - case 3692: state = 3700; break; // < -> <h - case 3727: state = 3728; break; // &lurds -> &lurdsh - case 3731: state = 3732; break; // &luru -> &luruh - case 3745: state = 3825; break; // &m -> &mh - case 3786: state = 3787; break; // &mdas -> &mdash - case 3897: state = 4101; break; // &n -> &nh - case 3968: state = 3969; break; // &ndas -> &ndash - case 3972: state = 3973; break; // &near -> &nearh - case 4002: state = 4003; break; // &NegativeT -> &NegativeTh - case 4022: state = 4023; break; // &NegativeVeryT -> &NegativeVeryTh - case 4150: state = 4151; break; // &nLeftrig -> &nLeftrigh - case 4160: state = 4161; break; // &nleftrig -> &nleftrigh - case 4468: state = 4469; break; // &NotRig -> &NotRigh - case 4632: state = 4633; break; // &nRig -> &nRigh - case 4641: state = 4642; break; // &nrig -> &nrigh - case 4653: state = 4663; break; // &ns -> &nsh - case 4747: state = 4748; break; // &ntrianglerig -> &ntrianglerigh - case 4766: state = 4767; break; // &nVDas -> &nVDash - case 4770: state = 4771; break; // &nVdas -> &nVdash - case 4774: state = 4775; break; // &nvDas -> &nvDash - case 4778: state = 4779; break; // &nvdas -> &nvdash - case 4814: state = 4815; break; // &nwar -> &nwarh - case 4833: state = 4900; break; // &o -> &oh - case 4853: state = 4854; break; // &odas -> &odash - case 5023: state = 5024; break; // &Oslas -> Ø - case 5027: state = 5028; break; // &oslas -> ø - case 5077: state = 5078; break; // &OverParent -> &OverParenth - case 5083: state = 5130; break; // &p -> &ph - case 5096: state = 5128; break; // &P -> &Ph - case 5143: state = 5144; break; // &pitc -> &pitch - case 5154: state = 5155; break; // &planck -> &planckh - case 5397: state = 5607; break; // &r -> &rh - case 5405: state = 5613; break; // &R -> &Rh - case 5439: state = 5448; break; // &rarr -> &rarrh - case 5530: state = 5531; break; // &rdld -> &rdldh - case 5538: state = 5539; break; // &rds -> &rdsh - case 5594: state = 5595; break; // &rfis -> &rfish - case 5618: state = 5619; break; // &Rig -> &Righ - case 5643: state = 5644; break; // &rig -> &righ - case 5645: state = 5712; break; // &right -> &righth - case 5728: state = 5735; break; // &rightleft -> &rightlefth - case 5745: state = 5746; break; // &rightrig -> &rightrigh - case 5778: state = 5779; break; // &rightt -> &rightth - case 5854: state = 5858; break; // &rl -> &rlh - case 5868: state = 5869; break; // &rmoustac -> &rmoustach - case 5926: state = 5927; break; // &Rrig -> &Rrigh - case 5934: state = 5945; break; // &rs -> &rsh - case 5939: state = 5944; break; // &Rs -> &Rsh - case 5951: state = 5952; break; // &rt -> &rth - case 5980: state = 5981; break; // &rulu -> &ruluh - case 5985: state = 6104; break; // &S -> &Sh - case 5991: state = 6088; break; // &s -> &sh - case 6055: state = 6056; break; // &sear -> &searh - case 6097: state = 6098; break; // &shc -> &shch - case 6142: state = 6143; break; // &ShortRig -> &ShortRigh - case 6214: state = 6215; break; // &smas -> &smash - case 6366: state = 6367; break; // &straig -> &straigh - case 6376: state = 6377; break; // &straightp -> &straightph - case 6446: state = 6490; break; // &Suc -> &Such - case 6491: state = 6492; break; // &SuchT -> &SuchTh - case 6500: state = 6525; break; // &sup -> &suph - case 6566: state = 6567; break; // &swar -> &swarh - case 6583: state = 6634; break; // &T -> &Th - case 6586: state = 6629; break; // &t -> &th - case 6783: state = 6784; break; // &trianglerig -> &trianglerigh - case 6824: state = 6834; break; // &ts -> &tsh - case 6849: state = 6850; break; // &two -> &twoh - case 6865: state = 6866; break; // &twoheadrig -> &twoheadrigh - case 6879: state = 6957; break; // &u -> &uh - case 6920: state = 6933; break; // &ud -> &udh - case 6938: state = 6939; break; // &ufis -> &ufish - case 7006: state = 7007; break; // &UnderParent -> &UnderParenth - case 7042: state = 7098; break; // &up -> &uph - case 7111: state = 7112; break; // &upharpoonrig -> &upharpoonrigh - case 7131: state = 7132; break; // &UpperRig -> &UpperRigh - case 7142: state = 7143; break; // &upsi -> &upsih - case 7244: state = 7245; break; // &varnot -> &varnoth - case 7249: state = 7250; break; // &varp -> &varph - case 7261: state = 7262; break; // &varr -> &varrh - case 7286: state = 7287; break; // &vart -> &varth - case 7304: state = 7305; break; // &vartrianglerig -> &vartrianglerigh - case 7321: state = 7322; break; // &VDas -> &VDash - case 7325: state = 7326; break; // &Vdas -> &Vdash - case 7329: state = 7330; break; // &vDas -> &vDash - case 7333: state = 7334; break; // &vdas -> &vdash - case 7385: state = 7386; break; // &VeryT -> &VeryTh - case 7439: state = 7440; break; // &Vvdas -> &Vvdash - case 7487: state = 7488; break; // &wreat -> &wreath - case 7495: state = 7513; break; // &x -> &xh - case 7651: state = 7704; break; // &z -> &zh - case 7686: state = 7687; break; // &ZeroWidt -> &ZeroWidth - default: return false; - } - break; - case 'i': - switch (state) { - case 0: state = 2539; break; // & -> &i - case 23: state = 30; break; // &ac -> &aci - case 26: state = 27; break; // &Ac -> &Aci - case 39: state = 40; break; // &AEl -> &AEli - case 43: state = 44; break; // &ael -> &aeli - case 143: state = 150; break; // &ap -> &api - case 145: state = 146; break; // &apac -> &apaci - case 162: state = 163; break; // &ApplyFunct -> &ApplyFuncti - case 172: state = 173; break; // &Ar -> &Ari - case 176: state = 177; break; // &ar -> &ari - case 186: state = 187; break; // &Ass -> &Assi - case 196: state = 197; break; // &At -> &Ati - case 201: state = 202; break; // &at -> &ati - case 212: state = 219; break; // &aw -> &awi - case 215: state = 216; break; // &awcon -> &awconi - case 222: state = 333; break; // &b -> &bi - case 232: state = 233; break; // &backeps -> &backepsi - case 238: state = 239; break; // &backpr -> &backpri - case 242: state = 243; break; // &backs -> &backsi - case 306: state = 307; break; // &beps -> &bepsi - case 317: state = 318; break; // &Bernoull -> &Bernoulli - case 335: state = 338; break; // &bigc -> &bigci - case 351: state = 352; break; // &bigot -> &bigoti - case 365: state = 366; break; // &bigtr -> &bigtri - case 414: state = 415; break; // &blacktr -> &blacktri - case 429: state = 430; break; // &blacktriangler -> &blacktriangleri - case 448: state = 449; break; // &bnequ -> &bnequi - case 467: state = 468; break; // &bowt -> &bowti - case 494: state = 495; break; // &boxm -> &boxmi - case 503: state = 504; break; // &boxt -> &boxti - case 533: state = 534; break; // &bpr -> &bpri - case 552: state = 558; break; // &bs -> &bsi - case 556: state = 557; break; // &bsem -> &bsemi - case 583: state = 765; break; // &C -> &Ci - case 589: state = 732; break; // &c -> &ci - case 595: state = 613; break; // &Cap -> &Capi - case 617: state = 618; break; // &CapitalD -> &CapitalDi - case 625: state = 626; break; // &CapitalDifferent -> &CapitalDifferenti - case 641: state = 664; break; // &cc -> &cci - case 645: state = 661; break; // &Cc -> &Cci - case 654: state = 655; break; // &Cced -> &Ccedi - case 658: state = 659; break; // &cced -> &ccedi - case 668: state = 669; break; // &Ccon -> &Cconi - case 684: state = 685; break; // &ced -> &cedi - case 688: state = 689; break; // &Ced -> &Cedi - case 719: state = 731; break; // &ch -> &chi - case 729: state = 730; break; // &Ch -> &Chi - case 748: state = 749; break; // &circlearrowr -> &circlearrowri - case 757: state = 758; break; // &circledc -> &circledci - case 775: state = 776; break; // &CircleM -> &CircleMi - case 784: state = 785; break; // &CircleT -> &CircleTi - case 792: state = 793; break; // &cirfn -> &cirfni - case 796: state = 797; break; // &cirm -> &cirmi - case 800: state = 801; break; // &cirsc -> &cirsci - case 807: state = 808; break; // &Clockw -> &Clockwi - case 853: state = 854; break; // &clubsu -> &clubsui - case 883: state = 898; break; // &con -> &coni - case 888: state = 895; break; // &Con -> &Coni - case 942: state = 943; break; // &CounterClockw -> &CounterClockwi - case 1065: state = 1066; break; // &curvearrowr -> &curvearrowri - case 1076: state = 1083; break; // &cw -> &cwi - case 1079: state = 1080; break; // &cwcon -> &cwconi - case 1091: state = 1191; break; // &D -> &Di - case 1097: state = 1228; break; // &d -> &di - case 1175: state = 1176; break; // &df -> &dfi - case 1194: state = 1195; break; // &Diacr -> &Diacri - case 1196: state = 1197; break; // &Diacrit -> &Diacriti - case 1223: state = 1224; break; // &DiacriticalT -> &DiacriticalTi - case 1239: state = 1240; break; // &diamondsu -> &diamondsui - case 1250: state = 1251; break; // &Different -> &Differenti - case 1260: state = 1261; break; // &dis -> &disi - case 1263: state = 1264; break; // &div -> &divi - case 1269: state = 1270; break; // ÷ont -> ÷onti - case 1316: state = 1317; break; // &dotm -> &dotmi - case 1381: state = 1382; break; // &DoubleLeftR -> &DoubleLeftRi - case 1406: state = 1407; break; // &DoubleLongLeftR -> &DoubleLongLeftRi - case 1416: state = 1417; break; // &DoubleLongR -> &DoubleLongRi - case 1426: state = 1427; break; // &DoubleR -> &DoubleRi - case 1458: state = 1459; break; // &DoubleVert -> &DoubleVerti - case 1521: state = 1522; break; // &downharpoonr -> &downharpoonri - case 1530: state = 1531; break; // &DownLeftR -> &DownLeftRi - case 1559: state = 1560; break; // &DownR -> &DownRi - case 1628: state = 1629; break; // &dtr -> &dtri - case 1647: state = 1650; break; // &dz -> &dzi - case 1672: state = 1684; break; // &Ec -> &Eci - case 1677: state = 1682; break; // &ec -> &eci - case 1728: state = 1735; break; // &el -> &eli - case 1821: state = 1822; break; // &eps -> &epsi - case 1824: state = 1825; break; // &Eps -> &Epsi - case 1834: state = 1835; break; // &eqc -> &eqci - case 1842: state = 1843; break; // &eqs -> &eqsi - case 1857: state = 1872; break; // &Equ -> &Equi - case 1860: state = 1880; break; // &equ -> &equi - case 1864: state = 1865; break; // &EqualT -> &EqualTi - case 1873: state = 1874; break; // &Equil -> &Equili - case 1876: state = 1877; break; // &Equilibr -> &Equilibri - case 1897: state = 1906; break; // &Es -> &Esi - case 1900: state = 1908; break; // &es -> &esi - case 1925: state = 1928; break; // &ex -> &exi - case 1931: state = 1932; break; // &Ex -> &Exi - case 1941: state = 1942; break; // &expectat -> &expectati - case 1950: state = 1951; break; // &Exponent -> &Exponenti - case 1959: state = 1960; break; // &exponent -> &exponenti - case 1964: state = 2001; break; // &f -> &fi - case 1967: state = 1968; break; // &fall -> &falli - case 1977: state = 2005; break; // &F -> &Fi - case 1987: state = 1988; break; // &ff -> &ffi - case 1989: state = 1990; break; // &ffil -> &ffili - case 1992: state = 1993; break; // &ffl -> &ffli - case 1995: state = 1996; break; // &ffll -> &fflli - case 2002: state = 2003; break; // &fil -> &fili - case 2037: state = 2038; break; // &fjl -> &fjli - case 2043: state = 2044; break; // &fll -> &flli - case 2069: state = 2070; break; // &Four -> &Fouri - case 2079: state = 2080; break; // &fpart -> &fparti - case 2118: state = 2194; break; // &g -> &gi - case 2145: state = 2150; break; // &Gc -> &Gci - case 2147: state = 2148; break; // &Gced -> &Gcedi - case 2153: state = 2154; break; // &gc -> &gci - case 2219: state = 2220; break; // &gns -> &gnsi - case 2277: state = 2278; break; // &GreaterT -> &GreaterTi - case 2285: state = 2288; break; // &gs -> &gsi - case 2295: state = 2297; break; // >c -> >ci - case 2338: state = 2339; break; // >rs -> >rsi - case 2351: state = 2417; break; // &H -> &Hi - case 2357: state = 2358; break; // &ha -> &hai - case 2364: state = 2365; break; // &ham -> &hami - case 2381: state = 2382; break; // &harrc -> &harrci - case 2389: state = 2390; break; // &Hc -> &Hci - case 2393: state = 2394; break; // &hc -> &hci - case 2402: state = 2403; break; // &heartsu -> &heartsui - case 2406: state = 2407; break; // &hell -> &helli - case 2459: state = 2460; break; // &hookr -> &hookri - case 2478: state = 2479; break; // &Hor -> &Hori - case 2486: state = 2487; break; // &HorizontalL -> &HorizontalLi - case 2539: state = 2582; break; // &i -> &ii - case 2545: state = 2550; break; // &ic -> &ici - case 2546: state = 2547; break; // &Ic -> &Ici - case 2582: state = 2583; break; // &ii -> &iii - case 2583: state = 2584; break; // &iii -> &iiii - case 2590: state = 2591; break; // &iinf -> &iinfi - case 2597: state = 2598; break; // &IJl -> &IJli - case 2601: state = 2602; break; // &ijl -> &ijli - case 2614: state = 2615; break; // &Imag -> &Imagi - case 2621: state = 2622; break; // &imagl -> &imagli - case 2637: state = 2638; break; // &Impl -> &Impli - case 2646: state = 2647; break; // &inf -> &infi - case 2649: state = 2650; break; // &infint -> &infinti - case 2680: state = 2681; break; // &Intersect -> &Intersecti - case 2693: state = 2694; break; // &Inv -> &Invi - case 2695: state = 2696; break; // &Invis -> &Invisi - case 2705: state = 2706; break; // &InvisibleT -> &InvisibleTi - case 2743: state = 2746; break; // &is -> &isi - case 2755: state = 2761; break; // &it -> &iti - case 2756: state = 2757; break; // &It -> &Iti - case 2778: state = 2779; break; // &Jc -> &Jci - case 2783: state = 2784; break; // &jc -> &jci - case 2838: state = 2839; break; // &Kced -> &Kcedi - case 2843: state = 2844; break; // &kced -> &kcedi - case 2951: state = 2952; break; // &larrs -> &larrsi - case 2958: state = 2959; break; // &lAta -> &lAtai - case 2961: state = 2962; break; // &lata -> &latai - case 2999: state = 3000; break; // &Lced -> &Lcedi - case 3002: state = 3006; break; // &lce -> &lcei - case 3003: state = 3004; break; // &lced -> &lcedi - case 3067: state = 3068; break; // &LeftArrowR -> &LeftArrowRi - case 3078: state = 3079; break; // &leftarrowta -> &leftarrowtai - case 3082: state = 3083; break; // &LeftCe -> &LeftCei - case 3084: state = 3085; break; // &LeftCeil -> &LeftCeili - case 3149: state = 3150; break; // &LeftR -> &LeftRi - case 3159: state = 3160; break; // &Leftr -> &Leftri - case 3169: state = 3170; break; // &leftr -> &leftri - case 3190: state = 3191; break; // &leftrightsqu -> &leftrightsqui - case 3223: state = 3224; break; // &leftthreet -> &leftthreeti - case 3228: state = 3229; break; // &LeftTr -> &LeftTri - case 3358: state = 3359; break; // &lesss -> &lesssi - case 3371: state = 3372; break; // &LessT -> &LessTi - case 3376: state = 3377; break; // &lf -> &lfi - case 3432: state = 3433; break; // &lltr -> &lltri - case 3434: state = 3435; break; // &Lm -> &Lmi - case 3439: state = 3440; break; // &lm -> &lmi - case 3463: state = 3464; break; // &lns -> &lnsi - case 3507: state = 3508; break; // &LongLeftR -> &LongLeftRi - case 3517: state = 3518; break; // &Longleftr -> &Longleftri - case 3527: state = 3528; break; // &longleftr -> &longleftri - case 3543: state = 3544; break; // &LongR -> &LongRi - case 3553: state = 3554; break; // &Longr -> &Longri - case 3563: state = 3564; break; // &longr -> &longri - case 3584: state = 3585; break; // &looparrowr -> &looparrowri - case 3598: state = 3599; break; // &lot -> &loti - case 3622: state = 3623; break; // &LowerR -> &LowerRi - case 3659: state = 3660; break; // &lrtr -> &lrtri - case 3661: state = 3673; break; // &ls -> &lsi - case 3692: state = 3704; break; // < -> <i - case 3693: state = 3695; break; // <c -> <ci - case 3717: state = 3718; break; // <r -> <ri - case 3745: state = 3827; break; // &m -> &mi - case 3755: state = 3847; break; // &M -> &Mi - case 3805: state = 3806; break; // &Med -> &Medi - case 3815: state = 3816; break; // &Mell -> &Melli - case 3835: state = 3836; break; // &midc -> &midci - case 3889: state = 3890; break; // &mult -> &multi - case 3897: state = 4111; break; // &n -> &ni - case 3914: state = 3916; break; // &nap -> &napi - case 3949: state = 3950; break; // &Nced -> &Ncedi - case 3953: state = 3954; break; // &nced -> &ncedi - case 3987: state = 3988; break; // &Negat -> &Negati - case 3993: state = 3994; break; // &NegativeMed -> &NegativeMedi - case 4003: state = 4004; break; // &NegativeTh -> &NegativeThi - case 4023: state = 4024; break; // &NegativeVeryTh -> &NegativeVeryThi - case 4032: state = 4033; break; // &nequ -> &nequi - case 4035: state = 4039; break; // &nes -> &nesi - case 4068: state = 4069; break; // &NewL -> &NewLi - case 4072: state = 4073; break; // &nex -> &nexi - case 4094: state = 4095; break; // &ngs -> &ngsi - case 4148: state = 4149; break; // &nLeftr -> &nLeftri - case 4158: state = 4159; break; // &nleftr -> &nleftri - case 4178: state = 4179; break; // &nls -> &nlsi - case 4183: state = 4184; break; // &nltr -> &nltri - case 4187: state = 4188; break; // &nm -> &nmi - case 4201: state = 4202; break; // &NonBreak -> &NonBreaki - case 4216: state = 4333; break; // ¬ -> ¬i - case 4240: state = 4241; break; // &NotDoubleVert -> &NotDoubleVerti - case 4259: state = 4260; break; // &NotEqualT -> &NotEqualTi - case 4264: state = 4265; break; // &NotEx -> &NotExi - case 4311: state = 4312; break; // &NotGreaterT -> &NotGreaterTi - case 4348: state = 4349; break; // &NotLeftTr -> &NotLeftTri - case 4391: state = 4392; break; // &NotLessT -> &NotLessTi - case 4424: state = 4425; break; // ¬n -> ¬ni - case 4453: state = 4467; break; // &NotR -> &NotRi - case 4472: state = 4473; break; // &NotRightTr -> &NotRightTri - case 4546: state = 4547; break; // &NotSucceedsT -> &NotSucceedsTi - case 4562: state = 4563; break; // &NotT -> &NotTi - case 4581: state = 4582; break; // &NotTildeT -> &NotTildeTi - case 4589: state = 4590; break; // &NotVert -> &NotVerti - case 4609: state = 4610; break; // &npol -> &npoli - case 4621: state = 4640; break; // &nr -> &nri - case 4630: state = 4631; break; // &nR -> &nRi - case 4650: state = 4651; break; // &nrtr -> &nrtri - case 4653: state = 4678; break; // &ns -> &nsi - case 4667: state = 4668; break; // &nshortm -> &nshortmi - case 4682: state = 4683; break; // &nsm -> &nsmi - case 4718: state = 4726; break; // &nt -> &nti - case 4721: state = 4722; break; // &Nt -> &Nti - case 4732: state = 4733; break; // &ntr -> &ntri - case 4745: state = 4746; break; // &ntriangler -> &ntriangleri - case 4760: state = 4787; break; // &nv -> &nvi - case 4789: state = 4790; break; // &nvinf -> &nvinfi - case 4798: state = 4799; break; // &nvltr -> &nvltri - case 4806: state = 4807; break; // &nvrtr -> &nvrtri - case 4809: state = 4810; break; // &nvs -> &nvsi - case 4833: state = 4905; break; // &o -> &oi - case 4841: state = 4842; break; // &oc -> &oci - case 4844: state = 4845; break; // &Oc -> &Oci - case 4851: state = 4864; break; // &od -> &odi - case 4873: state = 4874; break; // &OEl -> &OEli - case 4877: state = 4878; break; // &oel -> &oeli - case 4881: state = 4882; break; // &ofc -> &ofci - case 4908: state = 4919; break; // &ol -> &oli - case 4912: state = 4913; break; // &olc -> &olci - case 4923: state = 4937; break; // &Om -> &Omi - case 4927: state = 4942; break; // &om -> &omi - case 4991: state = 5002; break; // &or -> &ori - case 5031: state = 5032; break; // &Ot -> &Oti - case 5036: state = 5037; break; // &ot -> &oti - case 5080: state = 5081; break; // &OverParenthes -> &OverParenthesi - case 5083: state = 5141; break; // &p -> &pi - case 5091: state = 5092; break; // &pars -> &parsi - case 5096: state = 5140; break; // &P -> &Pi - case 5099: state = 5100; break; // &Part -> &Parti - case 5109: state = 5113; break; // &per -> &peri - case 5116: state = 5117; break; // &perm -> &permi - case 5128: state = 5129; break; // &Ph -> &Phi - case 5130: state = 5131; break; // &ph -> &phi - case 5161: state = 5162; break; // &plusac -> &plusaci - case 5165: state = 5166; break; // &plusc -> &plusci - case 5175: state = 5176; break; // &PlusM -> &PlusMi - case 5182: state = 5183; break; // &pluss -> &plussi - case 5189: state = 5190; break; // &Po -> &Poi - case 5201: state = 5202; break; // &po -> &poi - case 5204: state = 5205; break; // &point -> &pointi - case 5215: state = 5282; break; // &Pr -> &Pri - case 5216: state = 5285; break; // &pr -> &pri - case 5259: state = 5260; break; // &PrecedesT -> &PrecedesTi - case 5276: state = 5277; break; // &precns -> &precnsi - case 5279: state = 5280; break; // &precs -> &precsi - case 5293: state = 5294; break; // &prns -> &prnsi - case 5308: state = 5309; break; // &profl -> &profli - case 5320: state = 5321; break; // &Proport -> &Proporti - case 5328: state = 5329; break; // &prs -> &prsi - case 5335: state = 5341; break; // &Ps -> &Psi - case 5338: state = 5342; break; // &ps -> &psi - case 5351: state = 5354; break; // &q -> &qi - case 5364: state = 5365; break; // &qpr -> &qpri - case 5376: state = 5384; break; // &quat -> &quati - case 5379: state = 5380; break; // &quatern -> &quaterni - case 5397: state = 5642; break; // &r -> &ri - case 5405: state = 5617; break; // &R -> &Ri - case 5414: state = 5415; break; // &rad -> &radi - case 5454: state = 5455; break; // &rarrs -> &rarrsi - case 5463: state = 5464; break; // &rAta -> &rAtai - case 5466: state = 5470; break; // &rat -> &rati - case 5467: state = 5468; break; // &rata -> &ratai - case 5513: state = 5514; break; // &Rced -> &Rcedi - case 5516: state = 5520; break; // &rce -> &rcei - case 5517: state = 5518; break; // &rced -> &rcedi - case 5543: state = 5544; break; // &real -> &reali - case 5570: state = 5571; break; // &ReverseEqu -> &ReverseEqui - case 5572: state = 5573; break; // &ReverseEquil -> &ReverseEquili - case 5575: state = 5576; break; // &ReverseEquilibr -> &ReverseEquilibri - case 5583: state = 5584; break; // &ReverseUpEqu -> &ReverseUpEqui - case 5585: state = 5586; break; // &ReverseUpEquil -> &ReverseUpEquili - case 5588: state = 5589; break; // &ReverseUpEquilibr -> &ReverseUpEquilibri - case 5592: state = 5593; break; // &rf -> &rfi - case 5664: state = 5665; break; // &rightarrowta -> &rightarrowtai - case 5668: state = 5669; break; // &RightCe -> &RightCei - case 5670: state = 5671; break; // &RightCeil -> &RightCeili - case 5743: state = 5744; break; // &rightr -> &rightri - case 5756: state = 5757; break; // &rightsqu -> &rightsqui - case 5783: state = 5784; break; // &rightthreet -> &rightthreeti - case 5788: state = 5789; break; // &RightTr -> &RightTri - case 5844: state = 5845; break; // &ris -> &risi - case 5872: state = 5873; break; // &rnm -> &rnmi - case 5894: state = 5895; break; // &rot -> &roti - case 5905: state = 5906; break; // &RoundImpl -> &RoundImpli - case 5916: state = 5917; break; // &rppol -> &rppoli - case 5924: state = 5925; break; // &Rr -> &Rri - case 5951: state = 5956; break; // &rt -> &rti - case 5960: state = 5961; break; // &rtr -> &rtri - case 5966: state = 5967; break; // &rtriltr -> &rtriltri - case 5985: state = 6158; break; // &S -> &Si - case 5991: state = 6162; break; // &s -> &si - case 6001: state = 6024; break; // &Sc -> &Sci - case 6002: state = 6027; break; // &sc -> &sci - case 6018: state = 6019; break; // &Sced -> &Scedi - case 6021: state = 6022; break; // &sced -> &scedi - case 6034: state = 6035; break; // &scns -> &scnsi - case 6039: state = 6040; break; // &scpol -> &scpoli - case 6043: state = 6044; break; // &scs -> &scsi - case 6066: state = 6067; break; // &sem -> &semi - case 6073: state = 6074; break; // &setm -> &setmi - case 6129: state = 6130; break; // &shortm -> &shortmi - case 6140: state = 6141; break; // &ShortR -> &ShortRi - case 6196: state = 6197; break; // &SmallC -> &SmallCi - case 6202: state = 6223; break; // &sm -> &smi - case 6209: state = 6210; break; // &smallsetm -> &smallsetmi - case 6254: state = 6255; break; // &spadesu -> &spadesui - case 6301: state = 6302; break; // &SquareIntersect -> &SquareIntersecti - case 6328: state = 6329; break; // &SquareUn -> &SquareUni - case 6348: state = 6349; break; // &ssm -> &ssmi - case 6364: state = 6365; break; // &stra -> &strai - case 6371: state = 6372; break; // &straighteps -> &straightepsi - case 6377: state = 6378; break; // &straightph -> &straightphi - case 6411: state = 6426; break; // &subs -> &subsi - case 6467: state = 6468; break; // &SucceedsT -> &SucceedsTi - case 6484: state = 6485; break; // &succns -> &succnsi - case 6487: state = 6488; break; // &succs -> &succsi - case 6549: state = 6559; break; // &sups -> &supsi - case 6580: state = 6581; break; // &szl -> &szli - case 6583: state = 6696; break; // &T -> &Ti - case 6586: state = 6700; break; // &t -> &ti - case 6608: state = 6609; break; // &Tced -> &Tcedi - case 6612: state = 6613; break; // &tced -> &tcedi - case 6629: state = 6654; break; // &th -> &thi - case 6634: state = 6666; break; // &Th -> &Thi - case 6663: state = 6664; break; // &thicks -> &thicksi - case 6686: state = 6687; break; // &thks -> &thksi - case 6718: state = 6719; break; // &TildeT -> &TildeTi - case 6739: state = 6740; break; // &topc -> &topci - case 6752: state = 6753; break; // &tpr -> &tpri - case 6760: state = 6764; break; // &tr -> &tri - case 6781: state = 6782; break; // &triangler -> &triangleri - case 6792: state = 6793; break; // &trim -> &trimi - case 6797: state = 6798; break; // &Tr -> &Tri - case 6811: state = 6812; break; // &trit -> &triti - case 6817: state = 6818; break; // &trpez -> &trpezi - case 6845: state = 6846; break; // &tw -> &twi - case 6863: state = 6864; break; // &twoheadr -> &twoheadri - case 6893: state = 6894; break; // &Uarroc -> &Uarroci - case 6910: state = 6911; break; // &Uc -> &Uci - case 6914: state = 6915; break; // &uc -> &uci - case 6936: state = 6937; break; // &uf -> &ufi - case 6976: state = 6977; break; // &ultr -> &ultri - case 6987: state = 7012; break; // &Un -> &Uni - case 7009: state = 7010; break; // &UnderParenthes -> &UnderParenthesi - case 7089: state = 7090; break; // &UpEqu -> &UpEqui - case 7091: state = 7092; break; // &UpEquil -> &UpEquili - case 7094: state = 7095; break; // &UpEquilibr -> &UpEquilibri - case 7109: state = 7110; break; // &upharpoonr -> &upharpoonri - case 7129: state = 7130; break; // &UpperR -> &UpperRi - case 7139: state = 7140; break; // &Ups -> &Upsi - case 7141: state = 7142; break; // &ups -> &upsi - case 7166: state = 7180; break; // &ur -> &uri - case 7176: state = 7177; break; // &Ur -> &Uri - case 7184: state = 7185; break; // &urtr -> &urtri - case 7192: state = 7201; break; // &ut -> &uti - case 7196: state = 7197; break; // &Ut -> &Uti - case 7205: state = 7206; break; // &utr -> &utri - case 7232: state = 7233; break; // &vareps -> &varepsi - case 7245: state = 7246; break; // &varnoth -> &varnothi - case 7249: state = 7252; break; // &varp -> &varpi - case 7250: state = 7251; break; // &varph -> &varphi - case 7264: state = 7265; break; // &vars -> &varsi - case 7291: state = 7292; break; // &vartr -> &vartri - case 7302: state = 7303; break; // &vartriangler -> &vartriangleri - case 7346: state = 7347; break; // &vell -> &velli - case 7357: state = 7359; break; // &Vert -> &Verti - case 7366: state = 7367; break; // &VerticalL -> &VerticalLi - case 7379: state = 7380; break; // &VerticalT -> &VerticalTi - case 7386: state = 7387; break; // &VeryTh -> &VeryThi - case 7400: state = 7401; break; // &vltr -> &vltri - case 7419: state = 7420; break; // &vrtr -> &vrtri - case 7441: state = 7442; break; // &vz -> &vzi - case 7448: state = 7449; break; // &Wc -> &Wci - case 7453: state = 7454; break; // &wc -> &wci - case 7457: state = 7469; break; // &we -> &wei - case 7495: state = 7521; break; // &x -> &xi - case 7496: state = 7499; break; // &xc -> &xci - case 7506: state = 7507; break; // &xdtr -> &xdtri - case 7508: state = 7520; break; // &X -> &Xi - case 7532: state = 7533; break; // &xn -> &xni - case 7547: state = 7548; break; // &xot -> &xoti - case 7574: state = 7575; break; // &xutr -> &xutri - case 7590: state = 7619; break; // &y -> &yi - case 7600: state = 7601; break; // &Yc -> &Yci - case 7604: state = 7605; break; // &yc -> &yci - case 7651: state = 7707; break; // &z -> &zi - case 7683: state = 7684; break; // &ZeroW -> &ZeroWi - default: return false; - } - break; - case 'j': - switch (state) { - case 0: state = 2782; break; // & -> &j - case 1097: state = 1280; break; // &d -> &dj - case 1964: state = 2036; break; // &f -> &fj - case 2118: state = 2201; break; // &g -> &gj - case 2204: state = 2207; break; // &gl -> &glj - case 2539: state = 2600; break; // &i -> &ij - case 2830: state = 2866; break; // &k -> &kj - case 2881: state = 3405; break; // &l -> &lj - case 3897: state = 4118; break; // &n -> &nj - case 7725: state = 7726; break; // &zw -> &zwj - case 7727: state = 7728; break; // &zwn -> &zwnj - default: return false; - } - break; - case 'k': - switch (state) { - case 0: state = 2830; break; // & -> &k - case 222: state = 391; break; // &b -> &bk - case 224: state = 225; break; // &bac -> &back - case 249: state = 250; break; // &Bac -> &Back - case 271: state = 272; break; // &bbr -> &bbrk - case 275: state = 276; break; // &bbrktbr -> &bbrktbrk - case 396: state = 436; break; // &bl -> &blk - case 398: state = 399; break; // &blac -> &black - case 434: state = 435; break; // &blan -> &blank - case 443: state = 444; break; // &bloc -> &block - case 723: state = 724; break; // &chec -> &check - case 727: state = 728; break; // &checkmar -> &checkmark - case 805: state = 806; break; // &Cloc -> &Clock - case 940: state = 941; break; // &CounterCloc -> &CounterClock - case 1120: state = 1121; break; // &db -> &dbk - case 1591: state = 1592; break; // &drb -> &drbk - case 1618: state = 1619; break; // &Dstro -> &Dstrok - case 1622: state = 1623; break; // &dstro -> &dstrok - case 2062: state = 2066; break; // &for -> &fork - case 2354: state = 2355; break; // &Hace -> &Hacek - case 2356: state = 2428; break; // &h -> &hk - case 2448: state = 2449; break; // &hoo -> &hook - case 2502: state = 2503; break; // &Hstro -> &Hstrok - case 2506: state = 2507; break; // &hstro -> &hstrok - case 2687: state = 2688; break; // &intlarh -> &intlarhk - case 2765: state = 2766; break; // &Iu -> &Iuk - case 2769: state = 2770; break; // &iu -> &iuk - case 2817: state = 2818; break; // &Ju -> &Juk - case 2821: state = 2822; break; // &ju -> &juk - case 2945: state = 2946; break; // &larrh -> &larrhk - case 2975: state = 2976; break; // &lbbr -> &lbbrk - case 2977: state = 2982; break; // &lbr -> &lbrk - case 2979: state = 2981; break; // &lbrac -> &lbrack - case 3044: state = 3045; break; // &LeftAngleBrac -> &LeftAngleBrack - case 3097: state = 3098; break; // &LeftDoubleBrac -> &LeftDoubleBrack - case 3400: state = 3401; break; // &lhbl -> &lhblk - case 3473: state = 3474; break; // &lobr -> &lobrk - case 3684: state = 3685; break; // &Lstro -> &Lstrok - case 3688: state = 3689; break; // &lstro -> &lstrok - case 3772: state = 3773; break; // &mar -> &mark - case 3973: state = 3974; break; // &nearh -> &nearhk - case 4005: state = 4006; break; // &NegativeThic -> &NegativeThick - case 4194: state = 4195; break; // &NoBrea -> &NoBreak - case 4200: state = 4201; break; // &NonBrea -> &NonBreak - case 4815: state = 4816; break; // &nwarh -> &nwarhk - case 5067: state = 5069; break; // &OverBrac -> &OverBrack - case 5122: state = 5123; break; // &perten -> &pertenk - case 5147: state = 5148; break; // &pitchfor -> &pitchfork - case 5152: state = 5156; break; // &plan -> &plank - case 5153: state = 5154; break; // &planc -> &planck - case 5448: state = 5449; break; // &rarrh -> &rarrhk - case 5489: state = 5490; break; // &rbbr -> &rbbrk - case 5491: state = 5496; break; // &rbr -> &rbrk - case 5493: state = 5495; break; // &rbrac -> &rbrack - case 5629: state = 5630; break; // &RightAngleBrac -> &RightAngleBrack - case 5683: state = 5684; break; // &RightDoubleBrac -> &RightDoubleBrack - case 5882: state = 5883; break; // &robr -> &robrk - case 6056: state = 6057; break; // &searh -> &searhk - case 6567: state = 6568; break; // &swarh -> &swarhk - case 6595: state = 6596; break; // &tbr -> &tbrk - case 6629: state = 6683; break; // &th -> &thk - case 6655: state = 6656; break; // &thic -> &thick - case 6667: state = 6668; break; // &Thic -> &Thick - case 6747: state = 6748; break; // &topfor -> &topfork - case 6839: state = 6840; break; // &Tstro -> &Tstrok - case 6843: state = 6844; break; // &tstro -> &tstrok - case 6963: state = 6964; break; // &uhbl -> &uhblk - case 6996: state = 6998; break; // &UnderBrac -> &UnderBrack - case 7229: state = 7237; break; // &var -> &vark - default: return false; - } - break; - case 'l': - switch (state) { - case 0: state = 2881; break; // & -> &l - case 1: state = 68; break; // &A -> &Al - case 7: state = 60; break; // &a -> &al - case 38: state = 39; break; // &AE -> &AEl - case 42: state = 43; break; // &ae -> &ael - case 80: state = 83; break; // &ama -> &amal - case 96: state = 97; break; // &ands -> &andsl - case 102: state = 104; break; // &ang -> &angl - case 155: state = 156; break; // &App -> &Appl - case 197: state = 198; break; // &Ati -> &Atil - case 202: state = 203; break; // &ati -> &atil - case 207: state = 208; break; // &Aum -> Ä - case 210: state = 211; break; // &aum -> ä - case 222: state = 396; break; // &b -> &bl - case 233: state = 234; break; // &backepsi -> &backepsil - case 251: state = 252; break; // &Backs -> &Backsl - case 315: state = 316; break; // &Bernou -> &Bernoul - case 316: state = 317; break; // &Bernoul -> &Bernoull - case 347: state = 348; break; // &bigop -> &bigopl - case 369: state = 370; break; // &bigtriang -> &bigtriangl - case 379: state = 380; break; // &bigup -> &bigupl - case 399: state = 400; break; // &black -> &blackl - case 418: state = 419; break; // &blacktriang -> &blacktriangl - case 420: state = 425; break; // &blacktriangle -> &blacktrianglel - case 474: state = 476; break; // &boxD -> &boxDl - case 477: state = 479; break; // &boxd -> &boxdl - case 499: state = 500; break; // &boxp -> &boxpl - case 508: state = 510; break; // &boxU -> &boxUl - case 511: state = 513; break; // &boxu -> &boxul - case 518: state = 525; break; // &boxV -> &boxVl - case 519: state = 527; break; // &boxv -> &boxvl - case 561: state = 562; break; // &bso -> &bsol - case 568: state = 569; break; // &bu -> &bul - case 569: state = 570; break; // &bul -> &bull - case 583: state = 803; break; // &C -> &Cl - case 589: state = 849; break; // &c -> &cl - case 615: state = 616; break; // &Capita -> &Capital - case 627: state = 628; break; // &CapitalDifferentia -> &CapitalDifferential - case 636: state = 637; break; // &Cay -> &Cayl - case 655: state = 656; break; // &Ccedi -> Ç - case 659: state = 660; break; // &ccedi -> ç - case 685: state = 686; break; // &cedi -> ¸ - case 689: state = 690; break; // &Cedi -> &Cedil - case 690: state = 691; break; // &Cedil -> &Cedill - case 734: state = 737; break; // &circ -> &circl - case 743: state = 744; break; // &circlearrow -> &circlearrowl - case 767: state = 768; break; // &Circ -> &Circl - case 780: state = 781; break; // &CircleP -> &CirclePl - case 824: state = 825; break; // &ClockwiseContourIntegra -> &ClockwiseContourIntegral - case 830: state = 831; break; // &CloseCur -> &CloseCurl - case 836: state = 837; break; // &CloseCurlyDoub -> &CloseCurlyDoubl - case 856: state = 857; break; // &Co -> &Col - case 860: state = 861; break; // &co -> &col - case 871: state = 874; break; // &comp -> &compl - case 911: state = 912; break; // &ContourIntegra -> &ContourIntegral - case 937: state = 938; break; // &CounterC -> &CounterCl - case 959: state = 960; break; // &CounterClockwiseContourIntegra -> &CounterClockwiseContourIntegral - case 987: state = 999; break; // &cu -> &cul - case 991: state = 992; break; // &cudarr -> &cudarrl - case 1026: state = 1031; break; // &cur -> &curl - case 1060: state = 1061; break; // &curvearrow -> &curvearrowl - case 1086: state = 1087; break; // &cy -> &cyl - case 1097: state = 1283; break; // &d -> &dl - case 1098: state = 1103; break; // &da -> &dal - case 1120: state = 1126; break; // &db -> &dbl - case 1161: state = 1167; break; // &de -> &del - case 1163: state = 1164; break; // &De -> &Del - case 1188: state = 1189; break; // &dhar -> &dharl - case 1199: state = 1200; break; // &Diacritica -> &Diacritical - case 1210: state = 1211; break; // &DiacriticalDoub -> &DiacriticalDoubl - case 1224: state = 1225; break; // &DiacriticalTi -> &DiacriticalTil - case 1252: state = 1253; break; // &Differentia -> &Differential - case 1291: state = 1292; break; // &do -> &dol - case 1292: state = 1293; break; // &dol -> &doll - case 1314: state = 1315; break; // &DotEqua -> &DotEqual - case 1321: state = 1322; break; // &dotp -> &dotpl - case 1332: state = 1333; break; // &doub -> &doubl - case 1344: state = 1345; break; // &Doub -> &Doubl - case 1360: state = 1361; break; // &DoubleContourIntegra -> &DoubleContourIntegral - case 1461: state = 1462; break; // &DoubleVertica -> &DoubleVertical - case 1516: state = 1517; break; // &downharpoon -> &downharpoonl - case 1614: state = 1615; break; // &dso -> &dsol - case 1641: state = 1642; break; // &dwang -> &dwangl - case 1656: state = 1729; break; // &E -> &El - case 1662: state = 1728; break; // &e -> &el - case 1688: state = 1689; break; // &eco -> &ecol - case 1728: state = 1741; break; // &el -> &ell - case 1765: state = 1766; break; // &EmptySma -> &EmptySmal - case 1766: state = 1767; break; // &EmptySmal -> &EmptySmall - case 1781: state = 1782; break; // &EmptyVerySma -> &EmptyVerySmal - case 1782: state = 1783; break; // &EmptyVerySmal -> &EmptyVerySmall - case 1813: state = 1818; break; // &ep -> &epl - case 1816: state = 1817; break; // &epars -> &eparsl - case 1822: state = 1829; break; // &epsi -> &epsil - case 1825: state = 1826; break; // &Epsi -> &Epsil - case 1838: state = 1839; break; // &eqco -> &eqcol - case 1842: state = 1845; break; // &eqs -> &eqsl - case 1848: state = 1852; break; // &eqslant -> &eqslantl - case 1858: state = 1859; break; // &Equa -> &Equal - case 1861: state = 1862; break; // &equa -> &equal - case 1865: state = 1866; break; // &EqualTi -> &EqualTil - case 1872: state = 1873; break; // &Equi -> &Equil - case 1888: state = 1889; break; // &eqvpars -> &eqvparsl - case 1918: state = 1919; break; // &Eum -> Ë - case 1921: state = 1922; break; // &eum -> ë - case 1926: state = 1927; break; // &exc -> &excl - case 1952: state = 1953; break; // &Exponentia -> &Exponential - case 1961: state = 1962; break; // &exponentia -> &exponential - case 1964: state = 2040; break; // &f -> &fl - case 1965: state = 1966; break; // &fa -> &fal - case 1966: state = 1967; break; // &fal -> &fall - case 1984: state = 1985; break; // &fema -> &femal - case 1987: state = 1992; break; // &ff -> &ffl - case 1988: state = 1989; break; // &ffi -> &ffil - case 1992: state = 1995; break; // &ffl -> &ffll - case 2001: state = 2002; break; // &fi -> &fil - case 2005: state = 2006; break; // &Fi -> &Fil - case 2006: state = 2007; break; // &Fil -> &Fill - case 2012: state = 2013; break; // &FilledSma -> &FilledSmal - case 2013: state = 2014; break; // &FilledSmal -> &FilledSmall - case 2027: state = 2028; break; // &FilledVerySma -> &FilledVerySmal - case 2028: state = 2029; break; // &FilledVerySmal -> &FilledVerySmall - case 2036: state = 2037; break; // &fj -> &fjl - case 2040: state = 2043; break; // &fl -> &fll - case 2059: state = 2060; break; // &ForA -> &ForAl - case 2060: state = 2061; break; // &ForAl -> &ForAll - case 2063: state = 2064; break; // &fora -> &foral - case 2064: state = 2065; break; // &foral -> &forall - case 2107: state = 2108; break; // &fras -> &frasl - case 2118: state = 2204; break; // &g -> &gl - case 2148: state = 2149; break; // &Gcedi -> &Gcedil - case 2165: state = 2167; break; // &gE -> &gEl - case 2166: state = 2168; break; // &ge -> &gel - case 2171: state = 2172; break; // &geqs -> &geqsl - case 2176: state = 2184; break; // &ges -> &gesl - case 2182: state = 2183; break; // &gesdoto -> &gesdotol - case 2196: state = 2197; break; // &gime -> &gimel - case 2241: state = 2242; break; // &GreaterEqua -> &GreaterEqual - case 2248: state = 2249; break; // &GreaterFu -> &GreaterFul - case 2249: state = 2250; break; // &GreaterFul -> &GreaterFull - case 2254: state = 2255; break; // &GreaterFullEqua -> &GreaterFullEqual - case 2267: state = 2268; break; // &GreaterS -> &GreaterSl - case 2275: state = 2276; break; // &GreaterSlantEqua -> &GreaterSlantEqual - case 2278: state = 2279; break; // &GreaterTi -> &GreaterTil - case 2289: state = 2291; break; // &gsim -> &gsiml - case 2294: state = 2302; break; // > -> >l - case 2311: state = 2334; break; // >r -> >rl - case 2324: state = 2325; break; // >req -> >reql - case 2329: state = 2330; break; // >reqq -> >reqql - case 2357: state = 2362; break; // &ha -> &hal - case 2365: state = 2366; break; // &hami -> &hamil - case 2397: state = 2405; break; // &he -> &hel - case 2405: state = 2406; break; // &hel -> &hell - case 2417: state = 2418; break; // &Hi -> &Hil - case 2449: state = 2450; break; // &hook -> &hookl - case 2484: state = 2485; break; // &Horizonta -> &Horizontal - case 2493: state = 2496; break; // &hs -> &hsl - case 2522: state = 2523; break; // &HumpEqua -> &HumpEqual - case 2526: state = 2527; break; // &hybu -> &hybul - case 2527: state = 2528; break; // &hybul -> &hybull - case 2565: state = 2566; break; // &iexc -> ¡ - case 2596: state = 2597; break; // &IJ -> &IJl - case 2600: state = 2601; break; // &ij -> &ijl - case 2612: state = 2621; break; // &imag -> &imagl - case 2636: state = 2637; break; // &Imp -> &Impl - case 2658: state = 2684; break; // &int -> &intl - case 2660: state = 2661; break; // &intca -> &intcal - case 2670: state = 2671; break; // &Integra -> &Integral - case 2674: state = 2675; break; // &interca -> &intercal - case 2697: state = 2698; break; // &Invisib -> &Invisibl - case 2757: state = 2758; break; // &Iti -> &Itil - case 2761: state = 2762; break; // &iti -> &itil - case 2773: state = 2774; break; // &Ium -> Ï - case 2775: state = 2776; break; // &ium -> ï - case 2839: state = 2840; break; // &Kcedi -> &Kcedil - case 2844: state = 2845; break; // &kcedi -> &kcedil - case 2881: state = 3409; break; // &l -> &ll - case 2886: state = 3408; break; // &L -> &Ll - case 2918: state = 2920; break; // &lang -> &langl - case 2923: state = 2924; break; // &Lap -> &Lapl - case 2939: state = 2947; break; // &larr -> &larrl - case 2949: state = 2950; break; // &larrp -> &larrpl - case 2954: state = 2955; break; // &larrt -> &larrtl - case 2959: state = 2960; break; // &lAtai -> &lAtail - case 2962: state = 2963; break; // &latai -> &latail - case 2984: state = 2985; break; // &lbrks -> &lbrksl - case 3000: state = 3001; break; // &Lcedi -> &Lcedil - case 3004: state = 3005; break; // &lcedi -> &lcedil - case 3006: state = 3007; break; // &lcei -> &lceil - case 3038: state = 3039; break; // &LeftAng -> &LeftAngl - case 3058: state = 3139; break; // &left -> &leftl - case 3079: state = 3080; break; // &leftarrowtai -> &leftarrowtail - case 3083: state = 3084; break; // &LeftCei -> &LeftCeil - case 3091: state = 3092; break; // &LeftDoub -> &LeftDoubl - case 3121: state = 3122; break; // &LeftF -> &LeftFl - case 3232: state = 3233; break; // &LeftTriang -> &LeftTriangl - case 3241: state = 3242; break; // &LeftTriangleEqua -> &LeftTriangleEqual - case 3286: state = 3287; break; // &leqs -> &leqsl - case 3326: state = 3327; break; // &LessEqua -> &LessEqual - case 3336: state = 3337; break; // &LessFu -> &LessFul - case 3337: state = 3338; break; // &LessFul -> &LessFull - case 3342: state = 3343; break; // &LessFullEqua -> &LessFullEqual - case 3361: state = 3362; break; // &LessS -> &LessSl - case 3369: state = 3370; break; // &LessSlantEqua -> &LessSlantEqual - case 3372: state = 3373; break; // &LessTi -> &LessTil - case 3376: state = 3381; break; // &lf -> &lfl - case 3397: state = 3398; break; // &lharu -> &lharul - case 3399: state = 3400; break; // &lhb -> &lhbl - case 3477: state = 3487; break; // &Long -> &Longl - case 3497: state = 3498; break; // &long -> &longl - case 3579: state = 3580; break; // &looparrow -> &looparrowl - case 3589: state = 3595; break; // &lop -> &lopl - case 3640: state = 3641; break; // &lpar -> &lparl - case 3692: state = 3708; break; // < -> <l - case 3745: state = 3855; break; // &m -> &ml - case 3746: state = 3749; break; // &ma -> &mal - case 3761: state = 3766; break; // &mapsto -> &mapstol - case 3801: state = 3802; break; // &measuredang -> &measuredangl - case 3804: state = 3814; break; // &Me -> &Mel - case 3814: state = 3815; break; // &Mel -> &Mell - case 3851: state = 3852; break; // &MinusP -> &MinusPl - case 3861: state = 3862; break; // &mnp -> &mnpl - case 3867: state = 3868; break; // &mode -> &model - case 3887: state = 3888; break; // &mu -> &mul - case 3897: state = 4121; break; // &n -> &nl - case 3899: state = 3900; break; // &nab -> &nabl - case 3927: state = 3928; break; // &natura -> &natural - case 3950: state = 3951; break; // &Ncedi -> &Ncedil - case 3954: state = 3955; break; // &ncedi -> &ncedil - case 4086: state = 4087; break; // &ngeqs -> &ngeqsl - case 4132: state = 4177; break; // &nL -> &nLl - case 4170: state = 4171; break; // &nleqs -> &nleqsl - case 4234: state = 4235; break; // &NotDoub -> &NotDoubl - case 4243: state = 4244; break; // &NotDoubleVertica -> &NotDoubleVertical - case 4248: state = 4249; break; // &NotE -> &NotEl - case 4257: state = 4258; break; // &NotEqua -> &NotEqual - case 4260: state = 4261; break; // &NotEqualTi -> &NotEqualTil - case 4279: state = 4280; break; // &NotGreaterEqua -> &NotGreaterEqual - case 4282: state = 4283; break; // &NotGreaterFu -> &NotGreaterFul - case 4283: state = 4284; break; // &NotGreaterFul -> &NotGreaterFull - case 4288: state = 4289; break; // &NotGreaterFullEqua -> &NotGreaterFullEqual - case 4301: state = 4302; break; // &NotGreaterS -> &NotGreaterSl - case 4309: state = 4310; break; // &NotGreaterSlantEqua -> &NotGreaterSlantEqual - case 4312: state = 4313; break; // &NotGreaterTi -> &NotGreaterTil - case 4331: state = 4332; break; // &NotHumpEqua -> &NotHumpEqual - case 4352: state = 4353; break; // &NotLeftTriang -> &NotLeftTriangl - case 4361: state = 4362; break; // &NotLeftTriangleEqua -> &NotLeftTriangleEqual - case 4368: state = 4369; break; // &NotLessEqua -> &NotLessEqual - case 4381: state = 4382; break; // &NotLessS -> &NotLessSl - case 4389: state = 4390; break; // &NotLessSlantEqua -> &NotLessSlantEqual - case 4392: state = 4393; break; // &NotLessTi -> &NotLessTil - case 4441: state = 4442; break; // &NotPrecedesEqua -> &NotPrecedesEqual - case 4443: state = 4444; break; // &NotPrecedesS -> &NotPrecedesSl - case 4451: state = 4452; break; // &NotPrecedesSlantEqua -> &NotPrecedesSlantEqual - case 4460: state = 4461; break; // &NotReverseE -> &NotReverseEl - case 4476: state = 4477; break; // &NotRightTriang -> &NotRightTriangl - case 4485: state = 4486; break; // &NotRightTriangleEqua -> &NotRightTriangleEqual - case 4502: state = 4503; break; // &NotSquareSubsetEqua -> &NotSquareSubsetEqual - case 4513: state = 4514; break; // &NotSquareSupersetEqua -> &NotSquareSupersetEqual - case 4523: state = 4524; break; // &NotSubsetEqua -> &NotSubsetEqual - case 4534: state = 4535; break; // &NotSucceedsEqua -> &NotSucceedsEqual - case 4536: state = 4537; break; // &NotSucceedsS -> &NotSucceedsSl - case 4544: state = 4545; break; // &NotSucceedsSlantEqua -> &NotSucceedsSlantEqual - case 4547: state = 4548; break; // &NotSucceedsTi -> &NotSucceedsTil - case 4560: state = 4561; break; // &NotSupersetEqua -> &NotSupersetEqual - case 4563: state = 4564; break; // &NotTi -> &NotTil - case 4570: state = 4571; break; // &NotTildeEqua -> &NotTildeEqual - case 4573: state = 4574; break; // &NotTildeFu -> &NotTildeFul - case 4574: state = 4575; break; // &NotTildeFul -> &NotTildeFull - case 4579: state = 4580; break; // &NotTildeFullEqua -> &NotTildeFullEqual - case 4582: state = 4583; break; // &NotTildeTi -> &NotTildeTil - case 4592: state = 4593; break; // &NotVertica -> &NotVertical - case 4600: state = 4601; break; // &npara -> &nparal - case 4601: state = 4602; break; // &nparal -> &nparall - case 4603: state = 4604; break; // &nparalle -> &nparallel - case 4605: state = 4606; break; // &npars -> &nparsl - case 4608: state = 4609; break; // &npo -> &npol - case 4673: state = 4674; break; // &nshortpara -> &nshortparal - case 4674: state = 4675; break; // &nshortparal -> &nshortparall - case 4676: state = 4677; break; // &nshortparalle -> &nshortparallel - case 4718: state = 4730; break; // &nt -> &ntl - case 4719: state = 4720; break; // &ntg -> &ntgl - case 4722: state = 4723; break; // &Nti -> &Ntil - case 4726: state = 4727; break; // &nti -> &ntil - case 4736: state = 4737; break; // &ntriang -> &ntriangl - case 4738: state = 4739; break; // &ntriangle -> &ntrianglel - case 4760: state = 4792; break; // &nv -> &nvl - case 4833: state = 4908; break; // &o -> &ol - case 4856: state = 4857; break; // &Odb -> &Odbl - case 4860: state = 4861; break; // &odb -> &odbl - case 4869: state = 4870; break; // &odso -> &odsol - case 4872: state = 4873; break; // &OE -> &OEl - case 4876: state = 4877; break; // &oe -> &oel - case 4957: state = 4987; break; // &op -> &opl - case 4965: state = 4966; break; // &OpenCur -> &OpenCurl - case 4971: state = 4972; break; // &OpenCurlyDoub -> &OpenCurlyDoubl - case 5008: state = 5009; break; // &ors -> &orsl - case 5015: state = 5021; break; // &Os -> &Osl - case 5018: state = 5025; break; // &os -> &osl - case 5029: state = 5030; break; // &oso -> &osol - case 5032: state = 5033; break; // &Oti -> &Otil - case 5037: state = 5038; break; // &oti -> &otil - case 5050: state = 5051; break; // &Oum -> Ö - case 5053: state = 5054; break; // &oum -> ö - case 5083: state = 5150; break; // &p -> &pl - case 5086: state = 5087; break; // ¶ -> ¶l - case 5087: state = 5088; break; // ¶l -> ¶ll - case 5089: state = 5090; break; // ¶lle -> ¶llel - case 5091: state = 5094; break; // &pars -> &parsl - case 5096: state = 5172; break; // &P -> &Pl - case 5101: state = 5102; break; // &Partia -> &Partial - case 5117: state = 5118; break; // &permi -> &permil - case 5196: state = 5197; break; // &Poincarep -> &Poincarepl - case 5233: state = 5234; break; // &preccur -> &preccurl - case 5247: state = 5248; break; // &PrecedesEqua -> &PrecedesEqual - case 5249: state = 5250; break; // &PrecedesS -> &PrecedesSl - case 5257: state = 5258; break; // &PrecedesSlantEqua -> &PrecedesSlantEqual - case 5260: state = 5261; break; // &PrecedesTi -> &PrecedesTil - case 5303: state = 5308; break; // &prof -> &profl - case 5304: state = 5305; break; // &profa -> &profal - case 5324: state = 5325; break; // &Proportiona -> &Proportional - case 5333: state = 5334; break; // &prure -> &prurel - case 5397: state = 5854; break; // &r -> &rl - case 5426: state = 5429; break; // &rang -> &rangl - case 5439: state = 5450; break; // &rarr -> &rarrl - case 5452: state = 5453; break; // &rarrp -> &rarrpl - case 5457: state = 5458; break; // &Rarrt -> &Rarrtl - case 5459: state = 5460; break; // &rarrt -> &rarrtl - case 5464: state = 5465; break; // &rAtai -> &rAtail - case 5468: state = 5469; break; // &ratai -> &ratail - case 5473: state = 5474; break; // &rationa -> &rational - case 5498: state = 5499; break; // &rbrks -> &rbrksl - case 5514: state = 5515; break; // &Rcedi -> &Rcedil - case 5518: state = 5519; break; // &rcedi -> &rcedil - case 5520: state = 5521; break; // &rcei -> &rceil - case 5526: state = 5529; break; // &rd -> &rdl - case 5542: state = 5543; break; // &rea -> &real - case 5562: state = 5563; break; // &ReverseE -> &ReverseEl - case 5571: state = 5572; break; // &ReverseEqui -> &ReverseEquil - case 5584: state = 5585; break; // &ReverseUpEqui -> &ReverseUpEquil - case 5592: state = 5597; break; // &rf -> &rfl - case 5611: state = 5612; break; // &rharu -> &rharul - case 5623: state = 5624; break; // &RightAng -> &RightAngl - case 5645: state = 5725; break; // &right -> &rightl - case 5665: state = 5666; break; // &rightarrowtai -> &rightarrowtail - case 5669: state = 5670; break; // &RightCei -> &RightCeil - case 5677: state = 5678; break; // &RightDoub -> &RightDoubl - case 5707: state = 5708; break; // &RightF -> &RightFl - case 5792: state = 5793; break; // &RightTriang -> &RightTriangl - case 5801: state = 5802; break; // &RightTriangleEqua -> &RightTriangleEqual - case 5884: state = 5891; break; // &rop -> &ropl - case 5904: state = 5905; break; // &RoundImp -> &RoundImpl - case 5915: state = 5916; break; // &rppo -> &rppol - case 5961: state = 5964; break; // &rtri -> &rtril - case 5968: state = 5969; break; // &Ru -> &Rul - case 5972: state = 5973; break; // &RuleDe -> &RuleDel - case 5978: state = 5979; break; // &ru -> &rul - case 5991: state = 6188; break; // &s -> &sl - case 6019: state = 6020; break; // &Scedi -> &Scedil - case 6022: state = 6023; break; // &scedi -> &scedil - case 6038: state = 6039; break; // &scpo -> &scpol - case 6135: state = 6136; break; // &shortpara -> &shortparal - case 6136: state = 6137; break; // &shortparal -> &shortparall - case 6138: state = 6139; break; // &shortparalle -> &shortparallel - case 6168: state = 6176; break; // &sim -> &siml - case 6180: state = 6181; break; // &simp -> &simpl - case 6193: state = 6194; break; // &Sma -> &Smal - case 6194: state = 6195; break; // &Smal -> &Small - case 6199: state = 6200; break; // &SmallCirc -> &SmallCircl - case 6203: state = 6204; break; // &sma -> &smal - case 6204: state = 6205; break; // &smal -> &small - case 6221: state = 6222; break; // &smepars -> &smeparsl - case 6223: state = 6225; break; // &smi -> &smil - case 6235: state = 6240; break; // &so -> &sol - case 6314: state = 6315; break; // &SquareSubsetEqua -> &SquareSubsetEqual - case 6325: state = 6326; break; // &SquareSupersetEqua -> &SquareSupersetEqual - case 6349: state = 6350; break; // &ssmi -> &ssmil - case 6372: state = 6373; break; // &straightepsi -> &straightepsil - case 6394: state = 6395; break; // &submu -> &submul - case 6400: state = 6401; break; // &subp -> &subpl - case 6420: state = 6421; break; // &SubsetEqua -> &SubsetEqual - case 6441: state = 6442; break; // &succcur -> &succcurl - case 6455: state = 6456; break; // &SucceedsEqua -> &SucceedsEqual - case 6457: state = 6458; break; // &SucceedsS -> &SucceedsSl - case 6465: state = 6466; break; // &SucceedsSlantEqua -> &SucceedsSlantEqual - case 6468: state = 6469; break; // &SucceedsTi -> &SucceedsTil - case 6500: state = 6531; break; // &sup -> &supl - case 6523: state = 6524; break; // &SupersetEqua -> &SupersetEqual - case 6527: state = 6528; break; // &suphso -> &suphsol - case 6536: state = 6537; break; // &supmu -> &supmul - case 6542: state = 6543; break; // &supp -> &suppl - case 6579: state = 6580; break; // &sz -> &szl - case 6609: state = 6610; break; // &Tcedi -> &Tcedil - case 6613: state = 6614; break; // &tcedi -> &tcedil - case 6620: state = 6621; break; // &te -> &tel - case 6696: state = 6697; break; // &Ti -> &Til - case 6700: state = 6701; break; // &ti -> &til - case 6707: state = 6708; break; // &TildeEqua -> &TildeEqual - case 6710: state = 6711; break; // &TildeFu -> &TildeFul - case 6711: state = 6712; break; // &TildeFul -> &TildeFull - case 6716: state = 6717; break; // &TildeFullEqua -> &TildeFullEqual - case 6719: state = 6720; break; // &TildeTi -> &TildeTil - case 6767: state = 6768; break; // &triang -> &triangl - case 6769: state = 6774; break; // &triangle -> &trianglel - case 6799: state = 6800; break; // &Trip -> &Tripl - case 6805: state = 6806; break; // &trip -> &tripl - case 6853: state = 6854; break; // &twohead -> &twoheadl - case 6879: state = 6965; break; // &u -> &ul - case 6925: state = 6926; break; // &Udb -> &Udbl - case 6929: state = 6930; break; // &udb -> &udbl - case 6959: state = 6960; break; // &uhar -> &uharl - case 6962: state = 6963; break; // &uhb -> &uhbl - case 6982: state = 6986; break; // &um -> ¨ - case 7015: state = 7016; break; // &UnionP -> &UnionPl - case 7042: state = 7114; break; // &up -> &upl - case 7090: state = 7091; break; // &UpEqui -> &UpEquil - case 7104: state = 7105; break; // &upharpoon -> &upharpoonl - case 7140: state = 7144; break; // &Upsi -> &Upsil - case 7142: state = 7147; break; // &upsi -> &upsil - case 7197: state = 7198; break; // &Uti -> &Util - case 7201: state = 7202; break; // &uti -> &util - case 7213: state = 7214; break; // &Uum -> Ü - case 7215: state = 7216; break; // &uum -> ü - case 7220: state = 7221; break; // &uwang -> &uwangl - case 7223: state = 7398; break; // &v -> &vl - case 7233: state = 7234; break; // &varepsi -> &varepsil - case 7295: state = 7296; break; // &vartriang -> &vartriangl - case 7297: state = 7298; break; // &vartriangle -> &vartrianglel - case 7326: state = 7335; break; // &Vdash -> &Vdashl - case 7338: state = 7345; break; // &ve -> &vel - case 7345: state = 7346; break; // &vel -> &vell - case 7361: state = 7362; break; // &Vertica -> &Vertical - case 7380: state = 7381; break; // &VerticalTi -> &VerticalTil - case 7495: state = 7522; break; // &x -> &xl - case 7542: state = 7544; break; // &xop -> &xopl - case 7569: state = 7570; break; // &xup -> &xupl - case 7641: state = 7642; break; // &Yum -> &Yuml - case 7643: state = 7644; break; // &yum -> ÿ - default: return false; - } - break; - case 'm': - switch (state) { - case 0: state = 3745; break; // & -> &m - case 1: state = 75; break; // &A -> &Am - case 7: state = 79; break; // &a -> &am - case 64: state = 65; break; // &alefsy -> &alefsym - case 102: state = 106; break; // &ang -> &angm - case 191: state = 192; break; // &asy -> &asym - case 206: state = 207; break; // &Au -> &Aum - case 209: state = 210; break; // &au -> &aum - case 239: state = 240; break; // &backpri -> &backprim - case 243: state = 244; break; // &backsi -> &backsim - case 288: state = 300; break; // &be -> &bem - case 352: state = 353; break; // &bigoti -> &bigotim - case 464: state = 465; break; // &botto -> &bottom - case 470: state = 494; break; // &box -> &boxm - case 504: state = 505; break; // &boxti -> &boxtim - case 534: state = 535; break; // &bpri -> &bprim - case 555: state = 556; break; // &bse -> &bsem - case 558: state = 559; break; // &bsi -> &bsim - case 568: state = 573; break; // &bu -> &bum - case 577: state = 578; break; // &Bu -> &Bum - case 675: state = 676; break; // &ccupss -> &ccupssm - case 683: state = 693; break; // &ce -> &cem - case 724: state = 725; break; // &check -> &checkm - case 733: state = 796; break; // &cir -> &cirm - case 785: state = 786; break; // &CircleTi -> &CircleTim - case 860: state = 867; break; // &co -> &com - case 867: state = 868; break; // &com -> &comm - case 875: state = 876; break; // &comple -> &complem - case 1029: state = 1030; break; // &curarr -> &curarrm - case 1161: state = 1170; break; // &de -> &dem - case 1192: state = 1231; break; // &Dia -> &Diam - case 1229: state = 1230; break; // &dia -> &diam - case 1256: state = 1257; break; // &diga -> &digam - case 1257: state = 1258; break; // &digam -> &digamm - case 1270: state = 1271; break; // ÷onti -> ÷ontim - case 1302: state = 1316; break; // &dot -> &dotm - case 1656: state = 1746; break; // &E -> &Em - case 1662: state = 1750; break; // &e -> &em - case 1730: state = 1731; break; // &Ele -> &Elem - case 1763: state = 1764; break; // &EmptyS -> &EmptySm - case 1779: state = 1780; break; // &EmptyVeryS -> &EmptyVerySm - case 1843: state = 1844; break; // &eqsi -> &eqsim - case 1878: state = 1879; break; // &Equilibriu -> &Equilibrium - case 1906: state = 1907; break; // &Esi -> &Esim - case 1908: state = 1909; break; // &esi -> &esim - case 1917: state = 1918; break; // &Eu -> &Eum - case 1920: state = 1921; break; // &eu -> &eum - case 1982: state = 1983; break; // &fe -> &fem - case 2010: state = 2011; break; // &FilledS -> &FilledSm - case 2025: state = 2026; break; // &FilledVeryS -> &FilledVerySm - case 2119: state = 2129; break; // &ga -> &gam - case 2125: state = 2126; break; // &Ga -> &Gam - case 2126: state = 2127; break; // &Gam -> &Gamm - case 2129: state = 2130; break; // &gam -> &gamm - case 2194: state = 2195; break; // &gi -> &gim - case 2220: state = 2221; break; // &gnsi -> &gnsim - case 2288: state = 2289; break; // &gsi -> &gsim - case 2339: state = 2340; break; // >rsi -> >rsim - case 2357: state = 2364; break; // &ha -> &ham - case 2440: state = 2444; break; // &ho -> &hom - case 2508: state = 2509; break; // &Hu -> &Hum - case 2516: state = 2517; break; // &HumpDownHu -> &HumpDownHum - case 2533: state = 2604; break; // &I -> &Im - case 2539: state = 2608; break; // &i -> &im - case 2701: state = 2702; break; // &InvisibleCo -> &InvisibleCom - case 2702: state = 2703; break; // &InvisibleCom -> &InvisibleComm - case 2706: state = 2707; break; // &InvisibleTi -> &InvisibleTim - case 2765: state = 2773; break; // &Iu -> &Ium - case 2769: state = 2775; break; // &iu -> &ium - case 2782: state = 2793; break; // &j -> &jm - case 2881: state = 3439; break; // &l -> &lm - case 2886: state = 3434; break; // &L -> &Lm - case 2887: state = 2907; break; // &La -> &Lam - case 2892: state = 2911; break; // &la -> &lam - case 2897: state = 2898; break; // &lae -> &laem - case 2952: state = 2953; break; // &larrsi -> &larrsim - case 3224: state = 3225; break; // &leftthreeti -> &leftthreetim - case 3359: state = 3360; break; // &lesssi -> &lesssim - case 3464: state = 3465; break; // &lnsi -> &lnsim - case 3497: state = 3537; break; // &long -> &longm - case 3599: state = 3600; break; // &loti -> &lotim - case 3643: state = 3657; break; // &lr -> &lrm - case 3673: state = 3674; break; // &lsi -> &lsim - case 3704: state = 3705; break; // <i -> <im - case 3777: state = 3778; break; // &mco -> &mcom - case 3778: state = 3779; break; // &mcom -> &mcomm - case 3807: state = 3808; break; // &Mediu -> &Medium - case 3887: state = 3894; break; // &mu -> &mum - case 3890: state = 3891; break; // &multi -> &multim - case 3897: state = 4187; break; // &n -> &nm - case 3933: state = 3934; break; // &nbu -> &nbum - case 3995: state = 3996; break; // &NegativeMediu -> &NegativeMedium - case 4039: state = 4040; break; // &nesi -> &nesim - case 4095: state = 4096; break; // &ngsi -> &ngsim - case 4179: state = 4180; break; // &nlsi -> &nlsim - case 4250: state = 4251; break; // &NotEle -> &NotElem - case 4317: state = 4318; break; // &NotHu -> &NotHum - case 4325: state = 4326; break; // &NotHumpDownHu -> &NotHumpDownHum - case 4462: state = 4463; break; // &NotReverseEle -> &NotReverseElem - case 4653: state = 4682; break; // &ns -> &nsm - case 4666: state = 4667; break; // &nshort -> &nshortm - case 4678: state = 4679; break; // &nsi -> &nsim - case 4753: state = 4754; break; // &nu -> &num - case 4810: state = 4811; break; // &nvsi -> &nvsim - case 4827: state = 4923; break; // &O -> &Om - case 4833: state = 4927; break; // &o -> &om - case 4900: state = 4904; break; // &oh -> &ohm - case 4995: state = 5001; break; // &ord -> º - case 5032: state = 5041; break; // &Oti -> &Otim - case 5037: state = 5044; break; // &oti -> &otim - case 5049: state = 5050; break; // &Ou -> &Oum - case 5052: state = 5053; break; // &ou -> &oum - case 5083: state = 5188; break; // &p -> &pm - case 5092: state = 5093; break; // &parsi -> &parsim - case 5109: state = 5116; break; // &per -> &perm - case 5130: state = 5133; break; // &ph -> &phm - case 5133: state = 5134; break; // &phm -> &phmm - case 5159: state = 5180; break; // &plus -> &plusm - case 5183: state = 5184; break; // &plussi -> &plussim - case 5277: state = 5278; break; // &precnsi -> &precnsim - case 5280: state = 5281; break; // &precsi -> &precsim - case 5282: state = 5283; break; // &Pri -> &Prim - case 5285: state = 5286; break; // &pri -> &prim - case 5294: state = 5295; break; // &prnsi -> &prnsim - case 5329: state = 5330; break; // &prsi -> &prsim - case 5365: state = 5366; break; // &qpri -> &qprim - case 5397: state = 5862; break; // &r -> &rm - case 5417: state = 5418; break; // &rae -> &raem - case 5455: state = 5456; break; // &rarrsi -> &rarrsim - case 5564: state = 5565; break; // &ReverseEle -> &ReverseElem - case 5577: state = 5578; break; // &ReverseEquilibriu -> &ReverseEquilibrium - case 5590: state = 5591; break; // &ReverseUpEquilibriu -> &ReverseUpEquilibrium - case 5784: state = 5785; break; // &rightthreeti -> &rightthreetim - case 5854: state = 5861; break; // &rl -> &rlm - case 5871: state = 5872; break; // &rn -> &rnm - case 5895: state = 5896; break; // &roti -> &rotim - case 5902: state = 5903; break; // &RoundI -> &RoundIm - case 5956: state = 5957; break; // &rti -> &rtim - case 5985: state = 6192; break; // &S -> &Sm - case 5991: state = 6202; break; // &s -> &sm - case 6035: state = 6036; break; // &scnsi -> &scnsim - case 6044: state = 6045; break; // &scsi -> &scsim - case 6053: state = 6066; break; // &se -> &sem - case 6072: state = 6073; break; // &set -> &setm - case 6128: state = 6129; break; // &short -> &shortm - case 6159: state = 6160; break; // &Sig -> &Sigm - case 6162: state = 6168; break; // &si -> &sim - case 6163: state = 6164; break; // &sig -> &sigm - case 6208: state = 6209; break; // &smallset -> &smallsetm - case 6341: state = 6348; break; // &ss -> &ssm - case 6345: state = 6346; break; // &sset -> &ssetm - case 6381: state = 6495; break; // &Su -> &Sum - case 6383: state = 6496; break; // &su -> &sum - case 6384: state = 6393; break; // &sub -> &subm - case 6426: state = 6427; break; // &subsi -> &subsim - case 6485: state = 6486; break; // &succnsi -> &succnsim - case 6488: state = 6489; break; // &succsi -> &succsim - case 6500: state = 6535; break; // &sup -> &supm - case 6559: state = 6560; break; // &supsi -> &supsim - case 6651: state = 6652; break; // &thetasy -> &thetasym - case 6664: state = 6665; break; // &thicksi -> &thicksim - case 6687: state = 6688; break; // &thksi -> &thksim - case 6700: state = 6723; break; // &ti -> &tim - case 6753: state = 6754; break; // &tpri -> &tprim - case 6764: state = 6792; break; // &tri -> &trim - case 6812: state = 6813; break; // &triti -> &tritim - case 6819: state = 6820; break; // &trpeziu -> &trpezium - case 6873: state = 6978; break; // &U -> &Um - case 6879: state = 6982; break; // &u -> &um - case 7096: state = 7097; break; // &UpEquilibriu -> &UpEquilibrium - case 7208: state = 7215; break; // &uu -> &uum - case 7212: state = 7213; break; // &Uu -> &Uum - case 7266: state = 7267; break; // &varsig -> &varsigm - case 7495: state = 7529; break; // &x -> &xm - case 7548: state = 7549; break; // &xoti -> &xotim - case 7637: state = 7643; break; // &yu -> &yum - case 7640: state = 7641; break; // &Yu -> &Yum - default: return false; - } - break; - case 'n': - switch (state) { - case 0: state = 3897; break; // & -> &n - case 1: state = 88; break; // &A -> &An - case 7: state = 90; break; // &a -> &an - case 92: state = 93; break; // &anda -> &andan - case 133: state = 134; break; // &Aogo -> &Aogon - case 137: state = 138; break; // &aogo -> &aogon - case 159: state = 160; break; // &ApplyFu -> &ApplyFun - case 164: state = 165; break; // &ApplyFunctio -> &ApplyFunction - case 173: state = 174; break; // &Ari -> &Arin - case 177: state = 178; break; // &ari -> &arin - case 188: state = 189; break; // &Assig -> &Assign - case 214: state = 215; break; // &awco -> &awcon - case 216: state = 217; break; // &awconi -> &awconin - case 219: state = 220; break; // &awi -> &awin - case 222: state = 445; break; // &b -> &bn - case 227: state = 228; break; // &backco -> &backcon - case 235: state = 236; break; // &backepsilo -> &backepsilon - case 278: state = 279; break; // &bco -> &bcon - case 308: state = 309; break; // &ber -> &bern - case 312: state = 313; break; // &Ber -> &Bern - case 327: state = 328; break; // &betwee -> &between - case 367: state = 368; break; // &bigtria -> &bigtrian - case 374: state = 375; break; // &bigtriangledow -> &bigtriangledown - case 397: state = 434; break; // &bla -> &blan - case 403: state = 404; break; // &blackloze -> &blacklozen - case 416: state = 417; break; // &blacktria -> &blacktrian - case 423: state = 424; break; // &blacktriangledow -> &blacktriangledown - case 495: state = 496; break; // &boxmi -> &boxmin - case 597: state = 598; break; // &capa -> &capan - case 623: state = 624; break; // &CapitalDiffere -> &CapitalDifferen - case 634: state = 635; break; // &caro -> &caron - case 648: state = 649; break; // &Ccaro -> &Ccaron - case 651: state = 652; break; // &ccaro -> &ccaron - case 667: state = 668; break; // &Cco -> &Ccon - case 669: state = 670; break; // &Cconi -> &Cconin - case 683: state = 698; break; // &ce -> &cen - case 687: state = 700; break; // &Ce -> &Cen - case 776: state = 777; break; // &CircleMi -> &CircleMin - case 791: state = 792; break; // &cirf -> &cirfn - case 793: state = 794; break; // &cirfni -> &cirfnin - case 812: state = 813; break; // &ClockwiseCo -> &ClockwiseCon - case 818: state = 819; break; // &ClockwiseContourI -> &ClockwiseContourIn - case 856: state = 888; break; // &Co -> &Con - case 858: state = 859; break; // &Colo -> &Colon - case 860: state = 883; break; // &co -> &con - case 862: state = 863; break; // &colo -> &colon - case 872: state = 873; break; // &compf -> &compfn - case 877: state = 878; break; // &compleme -> &complemen - case 892: state = 893; break; // &Congrue -> &Congruen - case 895: state = 896; break; // &Coni -> &Conin - case 898: state = 899; break; // &coni -> &conin - case 905: state = 906; break; // &ContourI -> &ContourIn - case 932: state = 933; break; // &Cou -> &Coun - case 947: state = 948; break; // &CounterClockwiseCo -> &CounterClockwiseCon - case 953: state = 954; break; // &CounterClockwiseContourI -> &CounterClockwiseContourIn - case 1052: state = 1053; break; // &curre -> ¤ - case 1078: state = 1079; break; // &cwco -> &cwcon - case 1080: state = 1081; break; // &cwconi -> &cwconin - case 1083: state = 1084; break; // &cwi -> &cwin - case 1132: state = 1133; break; // &Dcaro -> &Dcaron - case 1137: state = 1138; break; // &dcaro -> &dcaron - case 1232: state = 1233; break; // &Diamo -> &Diamon - case 1235: state = 1236; break; // &diamo -> &diamon - case 1248: state = 1249; break; // &Differe -> &Differen - case 1261: state = 1262; break; // &disi -> &disin - case 1267: state = 1268; break; // ÷o -> ÷on - case 1274: state = 1275; break; // &divo -> &divon - case 1286: state = 1287; break; // &dlcor -> &dlcorn - case 1317: state = 1318; break; // &dotmi -> &dotmin - case 1348: state = 1349; break; // &DoubleCo -> &DoubleCon - case 1354: state = 1355; break; // &DoubleContourI -> &DoubleContourIn - case 1365: state = 1366; break; // &DoubleDow -> &DoubleDown - case 1394: state = 1395; break; // &DoubleLo -> &DoubleLon - case 1448: state = 1449; break; // &DoubleUpDow -> &DoubleUpDown - case 1466: state = 1467; break; // &Dow -> &Down - case 1478: state = 1479; break; // &dow -> &down - case 1502: state = 1503; break; // &downdow -> &downdown - case 1515: state = 1516; break; // &downharpoo -> &downharpoon - case 1599: state = 1600; break; // &drcor -> &drcorn - case 1639: state = 1640; break; // &dwa -> &dwan - case 1662: state = 1797; break; // &e -> &en - case 1675: state = 1676; break; // &Ecaro -> &Ecaron - case 1680: state = 1681; break; // &ecaro -> &ecaron - case 1690: state = 1691; break; // &ecolo -> &ecolon - case 1732: state = 1733; break; // &Eleme -> &Elemen - case 1735: state = 1736; break; // &eli -> &elin - case 1803: state = 1804; break; // &Eogo -> &Eogon - case 1807: state = 1808; break; // &eogo -> &eogon - case 1827: state = 1828; break; // &Epsilo -> &Epsilon - case 1830: state = 1831; break; // &epsilo -> &epsilon - case 1840: state = 1841; break; // &eqcolo -> &eqcolon - case 1846: state = 1847; break; // &eqsla -> &eqslan - case 1943: state = 1944; break; // &expectatio -> &expectation - case 1946: state = 1947; break; // &Expo -> &Expon - case 1948: state = 1949; break; // &Expone -> &Exponen - case 1955: state = 1956; break; // &expo -> &expon - case 1957: state = 1958; break; // &expone -> &exponen - case 1964: state = 2049; break; // &f -> &fn - case 1968: state = 1969; break; // &falli -> &fallin - case 2046: state = 2047; break; // &flt -> &fltn - case 2080: state = 2081; break; // &fparti -> &fpartin - case 2110: state = 2111; break; // &frow -> &frown - case 2118: state = 2208; break; // &g -> &gn - case 2173: state = 2174; break; // &geqsla -> &geqslan - case 2269: state = 2270; break; // &GreaterSla -> &GreaterSlan - case 2341: state = 2349; break; // &gv -> &gvn - case 2344: state = 2345; break; // &gvert -> &gvertn - case 2411: state = 2412; break; // &herco -> &hercon - case 2481: state = 2482; break; // &Horizo -> &Horizon - case 2487: state = 2488; break; // &HorizontalLi -> &HorizontalLin - case 2513: state = 2514; break; // &HumpDow -> &HumpDown - case 2531: state = 2532; break; // &hyphe -> &hyphen - case 2533: state = 2656; break; // &I -> &In - case 2539: state = 2641; break; // &i -> &in - case 2582: state = 2589; break; // &ii -> &iin - case 2583: state = 2587; break; // &iii -> &iiin - case 2584: state = 2585; break; // &iiii -> &iiiin - case 2591: state = 2592; break; // &iinfi -> &iinfin - case 2615: state = 2616; break; // &Imagi -> &Imagin - case 2622: state = 2623; break; // &imagli -> &imaglin - case 2647: state = 2648; break; // &infi -> &infin - case 2682: state = 2683; break; // &Intersectio -> &Intersection - case 2718: state = 2719; break; // &Iogo -> &Iogon - case 2721: state = 2722; break; // &iogo -> &iogon - case 2746: state = 2747; break; // &isi -> &isin - case 2855: state = 2856; break; // &kgree -> &kgreen - case 2881: state = 3452; break; // &l -> &ln - case 2887: state = 2915; break; // &La -> &Lan - case 2892: state = 2917; break; // &la -> &lan - case 2905: state = 2906; break; // &lagra -> &lagran - case 2991: state = 2992; break; // &Lcaro -> &Lcaron - case 2996: state = 2997; break; // &lcaro -> &lcaron - case 3036: state = 3037; break; // &LeftA -> &LeftAn - case 3085: state = 3086; break; // &LeftCeili -> &LeftCeilin - case 3101: state = 3102; break; // &LeftDow -> &LeftDown - case 3131: state = 3132; break; // &leftharpoo -> &leftharpoon - case 3135: state = 3136; break; // &leftharpoondow -> &leftharpoondown - case 3185: state = 3186; break; // &leftrightharpoo -> &leftrightharpoon - case 3230: state = 3231; break; // &LeftTria -> &LeftTrian - case 3247: state = 3248; break; // &LeftUpDow -> &LeftUpDown - case 3288: state = 3289; break; // &leqsla -> &leqslan - case 3363: state = 3364; break; // &LessSla -> &LessSlan - case 3415: state = 3416; break; // &llcor -> &llcorn - case 3466: state = 3496; break; // &lo -> &lon - case 3467: state = 3468; break; // &loa -> &loan - case 3475: state = 3476; break; // &Lo -> &Lon - case 3633: state = 3634; break; // &loze -> &lozen - case 3649: state = 3650; break; // &lrcor -> &lrcorn - case 3735: state = 3743; break; // &lv -> &lvn - case 3738: state = 3739; break; // &lvert -> &lvertn - case 3745: state = 3860; break; // &m -> &mn - case 3764: state = 3765; break; // &mapstodow -> &mapstodown - case 3799: state = 3800; break; // &measureda -> &measuredan - case 3816: state = 3817; break; // &Melli -> &Mellin - case 3827: state = 3841; break; // &mi -> &min - case 3847: state = 3848; break; // &Mi -> &Min - case 3898: state = 3912; break; // &na -> &nan - case 3943: state = 3944; break; // &Ncaro -> &Ncaron - case 3946: state = 3947; break; // &ncaro -> &ncaron - case 3956: state = 3957; break; // &nco -> &ncon - case 4004: state = 4012; break; // &NegativeThi -> &NegativeThin - case 4024: state = 4025; break; // &NegativeVeryThi -> &NegativeVeryThin - case 4069: state = 4070; break; // &NewLi -> &NewLin - case 4088: state = 4089; break; // &ngeqsla -> &ngeqslan - case 4172: state = 4173; break; // &nleqsla -> &nleqslan - case 4190: state = 4196; break; // &No -> &Non - case 4202: state = 4203; break; // &NonBreaki -> &NonBreakin - case 4216: state = 4424; break; // ¬ -> ¬n - case 4218: state = 4219; break; // &NotCo -> &NotCon - case 4223: state = 4224; break; // &NotCongrue -> &NotCongruen - case 4252: state = 4253; break; // &NotEleme -> &NotElemen - case 4303: state = 4304; break; // &NotGreaterSla -> &NotGreaterSlan - case 4322: state = 4323; break; // &NotHumpDow -> &NotHumpDown - case 4333: state = 4334; break; // ¬i -> ¬in - case 4350: state = 4351; break; // &NotLeftTria -> &NotLeftTrian - case 4383: state = 4384; break; // &NotLessSla -> &NotLessSlan - case 4445: state = 4446; break; // &NotPrecedesSla -> &NotPrecedesSlan - case 4464: state = 4465; break; // &NotReverseEleme -> &NotReverseElemen - case 4474: state = 4475; break; // &NotRightTria -> &NotRightTrian - case 4538: state = 4539; break; // &NotSucceedsSla -> &NotSucceedsSlan - case 4610: state = 4611; break; // &npoli -> &npolin - case 4734: state = 4735; break; // &ntria -> &ntrian - case 4787: state = 4788; break; // &nvi -> &nvin - case 4790: state = 4791; break; // &nvinfi -> &nvinfin - case 4812: state = 4823; break; // &nw -> &nwn - case 4888: state = 4889; break; // &ogo -> &ogon - case 4905: state = 4906; break; // &oi -> &oin - case 4919: state = 4920; break; // &oli -> &olin - case 4940: state = 4941; break; // &Omicro -> &Omicron - case 4942: state = 4948; break; // &omi -> &omin - case 4945: state = 4946; break; // &omicro -> &omicron - case 4961: state = 4962; break; // &Ope -> &Open - case 5075: state = 5076; break; // &OverPare -> &OverParen - case 5110: state = 5111; break; // &perc -> &percn - case 5121: state = 5122; break; // &perte -> &perten - case 5137: state = 5138; break; // &pho -> &phon - case 5151: state = 5152; break; // &pla -> &plan - case 5176: state = 5177; break; // &PlusMi -> &PlusMin - case 5180: state = 5181; break; // &plusm -> ± - case 5190: state = 5191; break; // &Poi -> &Poin - case 5198: state = 5199; break; // &Poincarepla -> &Poincareplan - case 5202: state = 5203; break; // &poi -> &poin - case 5205: state = 5206; break; // &pointi -> &pointin - case 5212: state = 5213; break; // &pou -> &poun - case 5216: state = 5289; break; // &pr -> &prn - case 5224: state = 5266; break; // &prec -> &precn - case 5251: state = 5252; break; // &PrecedesSla -> &PrecedesSlan - case 5309: state = 5310; break; // &profli -> &proflin - case 5322: state = 5323; break; // &Proportio -> &Proportion - case 5343: state = 5344; break; // &pu -> &pun - case 5354: state = 5355; break; // &qi -> &qin - case 5378: state = 5379; break; // &quater -> &quatern - case 5381: state = 5382; break; // &quaternio -> &quaternion - case 5384: state = 5385; break; // &quati -> &quatin - case 5397: state = 5871; break; // &r -> &rn - case 5402: state = 5425; break; // &ra -> &ran - case 5406: state = 5423; break; // &Ra -> &Ran - case 5471: state = 5472; break; // &ratio -> &ration - case 5505: state = 5506; break; // &Rcaro -> &Rcaron - case 5510: state = 5511; break; // &rcaro -> &rcaron - case 5544: state = 5545; break; // &reali -> &realin - case 5566: state = 5567; break; // &ReverseEleme -> &ReverseElemen - case 5621: state = 5622; break; // &RightA -> &RightAn - case 5642: state = 5842; break; // &ri -> &rin - case 5671: state = 5672; break; // &RightCeili -> &RightCeilin - case 5687: state = 5688; break; // &RightDow -> &RightDown - case 5717: state = 5718; break; // &rightharpoo -> &rightharpoon - case 5721: state = 5722; break; // &rightharpoondow -> &rightharpoondown - case 5740: state = 5741; break; // &rightleftharpoo -> &rightleftharpoon - case 5790: state = 5791; break; // &RightTria -> &RightTrian - case 5807: state = 5808; break; // &RightUpDow -> &RightUpDown - case 5845: state = 5846; break; // &risi -> &risin - case 5876: state = 5877; break; // &roa -> &roan - case 5899: state = 5900; break; // &Rou -> &Roun - case 5917: state = 5918; break; // &rppoli -> &rppolin - case 6002: state = 6030; break; // &sc -> &scn - case 6007: state = 6008; break; // &Scaro -> &Scaron - case 6010: state = 6011; break; // &scaro -> &scaron - case 6040: state = 6041; break; // &scpoli -> &scpolin - case 6073: state = 6078; break; // &setm -> &setmn - case 6074: state = 6075; break; // &setmi -> &setmin - case 6086: state = 6087; break; // &sfrow -> &sfrown - case 6110: state = 6111; break; // &ShortDow -> &ShortDown - case 6168: state = 6178; break; // &sim -> &simn - case 6210: state = 6211; break; // &smallsetmi -> &smallsetmin - case 6293: state = 6294; break; // &SquareI -> &SquareIn - case 6303: state = 6304; break; // &SquareIntersectio -> &SquareIntersection - case 6327: state = 6328; break; // &SquareU -> &SquareUn - case 6330: state = 6331; break; // &SquareUnio -> &SquareUnion - case 6346: state = 6347; break; // &ssetm -> &ssetmn - case 6363: state = 6379; break; // &str -> &strn - case 6374: state = 6375; break; // &straightepsilo -> &straightepsilon - case 6383: state = 6497; break; // &su -> &sun - case 6384: state = 6397; break; // &sub -> &subn - case 6413: state = 6422; break; // &subset -> &subsetn - case 6432: state = 6474; break; // &succ -> &succn - case 6459: state = 6460; break; // &SucceedsSla -> &SucceedsSlan - case 6500: state = 6539; break; // &sup -> &supn - case 6551: state = 6555; break; // &supset -> &supsetn - case 6564: state = 6575; break; // &sw -> &swn - case 6600: state = 6601; break; // &Tcaro -> &Tcaron - case 6605: state = 6606; break; // &tcaro -> &tcaron - case 6654: state = 6674; break; // &thi -> &thin - case 6666: state = 6677; break; // &Thi -> &Thin - case 6694: state = 6695; break; // &thor -> þ - case 6700: state = 6730; break; // &ti -> &tin - case 6765: state = 6766; break; // &tria -> &trian - case 6772: state = 6773; break; // &triangledow -> &triangledown - case 6793: state = 6794; break; // &trimi -> &trimin - case 6873: state = 6987; break; // &U -> &Un - case 6968: state = 6969; break; // &ulcor -> &ulcorn - case 7004: state = 7005; break; // &UnderPare -> &UnderParen - case 7013: state = 7014; break; // &Unio -> &Union - case 7021: state = 7022; break; // &Uogo -> &Uogon - case 7025: state = 7026; break; // &uogo -> &uogon - case 7053: state = 7054; break; // &UpArrowDow -> &UpArrowDown - case 7062: state = 7063; break; // &UpDow -> &UpDown - case 7071: state = 7072; break; // &Updow -> &Updown - case 7080: state = 7081; break; // &updow -> &updown - case 7103: state = 7104; break; // &upharpoo -> &upharpoon - case 7145: state = 7146; break; // &Upsilo -> &Upsilon - case 7148: state = 7149; break; // &upsilo -> &upsilon - case 7169: state = 7170; break; // &urcor -> &urcorn - case 7177: state = 7178; break; // &Uri -> &Urin - case 7180: state = 7181; break; // &uri -> &urin - case 7218: state = 7219; break; // &uwa -> &uwan - case 7223: state = 7402; break; // &v -> &vn - case 7224: state = 7225; break; // &va -> &van - case 7229: state = 7242; break; // &var -> &varn - case 7235: state = 7236; break; // &varepsilo -> &varepsilon - case 7246: state = 7247; break; // &varnothi -> &varnothin - case 7273: state = 7274; break; // &varsubset -> &varsubsetn - case 7281: state = 7282; break; // &varsupset -> &varsupsetn - case 7293: state = 7294; break; // &vartria -> &vartrian - case 7367: state = 7368; break; // &VerticalLi -> &VerticalLin - case 7387: state = 7388; break; // &VeryThi -> &VeryThin - case 7428: state = 7429; break; // &vsub -> &vsubn - case 7432: state = 7433; break; // &vsup -> &vsupn - case 7495: state = 7532; break; // &x -> &xn - case 7610: state = 7611; break; // &ye -> ¥ - case 7660: state = 7661; break; // &Zcaro -> &Zcaron - case 7665: state = 7666; break; // &zcaro -> &zcaron - case 7725: state = 7727; break; // &zw -> &zwn - default: return false; - } - break; - case 'o': - switch (state) { - case 0: state = 4833; break; // & -> &o - case 1: state = 131; break; // &A -> &Ao - case 7: state = 135; break; // &a -> &ao - case 97: state = 98; break; // &andsl -> &andslo - case 132: state = 133; break; // &Aog -> &Aogo - case 136: state = 137; break; // &aog -> &aogo - case 143: state = 152; break; // &ap -> &apo - case 163: state = 164; break; // &ApplyFuncti -> &ApplyFunctio - case 167: state = 168; break; // &appr -> &appro - case 213: state = 214; break; // &awc -> &awco - case 222: state = 459; break; // &b -> &bo - case 226: state = 227; break; // &backc -> &backco - case 234: state = 235; break; // &backepsil -> &backepsilo - case 247: state = 456; break; // &B -> &Bo - case 277: state = 278; break; // &bc -> &bco - case 286: state = 287; break; // &bdqu -> &bdquo - case 309: state = 310; break; // &bern -> &berno - case 313: state = 314; break; // &Bern -> &Berno - case 334: state = 343; break; // &big -> &bigo - case 344: state = 345; break; // &bigod -> &bigodo - case 372: state = 373; break; // &bigtriangled -> &bigtriangledo - case 393: state = 394; break; // &bkar -> &bkaro - case 396: state = 442; break; // &bl -> &blo - case 400: state = 401; break; // &blackl -> &blacklo - case 421: state = 422; break; // &blacktriangled -> &blacktriangledo - case 445: state = 454; break; // &bn -> &bno - case 451: state = 452; break; // &bN -> &bNo - case 463: state = 464; break; // &bott -> &botto - case 471: state = 472; break; // &boxb -> &boxbo - case 552: state = 561; break; // &bs -> &bso - case 583: state = 856; break; // &C -> &Co - case 589: state = 860; break; // &c -> &co - case 610: state = 611; break; // &capd -> &capdo - case 631: state = 634; break; // &car -> &caro - case 645: state = 667; break; // &Cc -> &Cco - case 647: state = 648; break; // &Ccar -> &Ccaro - case 650: state = 651; break; // &ccar -> &ccaro - case 677: state = 678; break; // &Cd -> &Cdo - case 680: state = 681; break; // &cd -> &cdo - case 704: state = 705; break; // &CenterD -> &CenterDo - case 709: state = 710; break; // ¢erd -> ¢erdo - case 741: state = 742; break; // &circlearr -> &circlearro - case 770: state = 771; break; // &CircleD -> &CircleDo - case 803: state = 804; break; // &Cl -> &Clo - case 811: state = 812; break; // &ClockwiseC -> &ClockwiseCo - case 814: state = 815; break; // &ClockwiseCont -> &ClockwiseConto - case 833: state = 834; break; // &CloseCurlyD -> &CloseCurlyDo - case 840: state = 841; break; // &CloseCurlyDoubleQu -> &CloseCurlyDoubleQuo - case 845: state = 846; break; // &CloseCurlyQu -> &CloseCurlyQuo - case 857: state = 858; break; // &Col -> &Colo - case 861: state = 862; break; // &col -> &colo - case 885: state = 886; break; // &congd -> &congdo - case 901: state = 902; break; // &Cont -> &Conto - case 917: state = 918; break; // &copr -> &copro - case 920: state = 921; break; // &Copr -> &Copro - case 938: state = 939; break; // &CounterCl -> &CounterClo - case 946: state = 947; break; // &CounterClockwiseC -> &CounterClockwiseCo - case 949: state = 950; break; // &CounterClockwiseCont -> &CounterClockwiseConto - case 961: state = 969; break; // &cr -> &cro - case 965: state = 966; break; // &Cr -> &Cro - case 984: state = 985; break; // &ctd -> &ctdo - case 1006: state = 1023; break; // &cup -> &cupo - case 1020: state = 1021; break; // &cupd -> &cupdo - case 1058: state = 1059; break; // &curvearr -> &curvearro - case 1077: state = 1078; break; // &cwc -> &cwco - case 1091: state = 1296; break; // &D -> &Do - case 1097: state = 1291; break; // &d -> &do - case 1123: state = 1124; break; // &dbkar -> &dbkaro - case 1131: state = 1132; break; // &Dcar -> &Dcaro - case 1136: state = 1137; break; // &dcar -> &dcaro - case 1141: state = 1150; break; // &DD -> &DDo - case 1142: state = 1156; break; // &dd -> &ddo - case 1206: state = 1207; break; // &DiacriticalD -> &DiacriticalDo - case 1230: state = 1235; break; // &diam -> &diamo - case 1231: state = 1232; break; // &Diam -> &Diamo - case 1263: state = 1274; break; // &div -> &divo - case 1266: state = 1267; break; // ÷ -> ÷o - case 1284: state = 1285; break; // &dlc -> &dlco - case 1288: state = 1289; break; // &dlcr -> &dlcro - case 1303: state = 1304; break; // &DotD -> &DotDo - case 1308: state = 1309; break; // &doteqd -> &doteqdo - case 1347: state = 1348; break; // &DoubleC -> &DoubleCo - case 1350: state = 1351; break; // &DoubleCont -> &DoubleConto - case 1362: state = 1363; break; // &DoubleD -> &DoubleDo - case 1369: state = 1370; break; // &DoubleDownArr -> &DoubleDownArro - case 1372: state = 1394; break; // &DoubleL -> &DoubleLo - case 1378: state = 1379; break; // &DoubleLeftArr -> &DoubleLeftArro - case 1388: state = 1389; break; // &DoubleLeftRightArr -> &DoubleLeftRightArro - case 1403: state = 1404; break; // &DoubleLongLeftArr -> &DoubleLongLeftArro - case 1413: state = 1414; break; // &DoubleLongLeftRightArr -> &DoubleLongLeftRightArro - case 1423: state = 1424; break; // &DoubleLongRightArr -> &DoubleLongRightArro - case 1433: state = 1434; break; // &DoubleRightArr -> &DoubleRightArro - case 1443: state = 1444; break; // &DoubleUpArr -> &DoubleUpArro - case 1446: state = 1447; break; // &DoubleUpD -> &DoubleUpDo - case 1452: state = 1453; break; // &DoubleUpDownArr -> &DoubleUpDownArro - case 1470: state = 1471; break; // &DownArr -> &DownArro - case 1475: state = 1476; break; // &Downarr -> &Downarro - case 1482: state = 1483; break; // &downarr -> &downarro - case 1492: state = 1493; break; // &DownArrowUpArr -> &DownArrowUpArro - case 1500: state = 1501; break; // &downd -> &downdo - case 1506: state = 1507; break; // &downdownarr -> &downdownarro - case 1513: state = 1514; break; // &downharp -> &downharpo - case 1514: state = 1515; break; // &downharpo -> &downharpoo - case 1538: state = 1539; break; // &DownLeftRightVect -> &DownLeftRightVecto - case 1547: state = 1548; break; // &DownLeftTeeVect -> &DownLeftTeeVecto - case 1553: state = 1554; break; // &DownLeftVect -> &DownLeftVecto - case 1570: state = 1571; break; // &DownRightTeeVect -> &DownRightTeeVecto - case 1576: state = 1577; break; // &DownRightVect -> &DownRightVecto - case 1587: state = 1588; break; // &DownTeeArr -> &DownTeeArro - case 1594: state = 1595; break; // &drbkar -> &drbkaro - case 1597: state = 1598; break; // &drc -> &drco - case 1601: state = 1602; break; // &drcr -> &drcro - case 1607: state = 1614; break; // &ds -> &dso - case 1617: state = 1618; break; // &Dstr -> &Dstro - case 1621: state = 1622; break; // &dstr -> &dstro - case 1625: state = 1626; break; // &dtd -> &dtdo - case 1656: state = 1801; break; // &E -> &Eo - case 1662: state = 1805; break; // &e -> &eo - case 1674: state = 1675; break; // &Ecar -> &Ecaro - case 1677: state = 1688; break; // &ec -> &eco - case 1679: state = 1680; break; // &ecar -> &ecaro - case 1689: state = 1690; break; // &ecol -> &ecolo - case 1694: state = 1701; break; // &eD -> &eDo - case 1695: state = 1696; break; // &eDD -> &eDDo - case 1698: state = 1699; break; // &Ed -> &Edo - case 1703: state = 1704; break; // &ed -> &edo - case 1708: state = 1709; break; // &efD -> &efDo - case 1725: state = 1726; break; // &egsd -> &egsdo - case 1743: state = 1744; break; // &elsd -> &elsdo - case 1802: state = 1803; break; // &Eog -> &Eogo - case 1806: state = 1807; break; // &eog -> &eogo - case 1826: state = 1827; break; // &Epsil -> &Epsilo - case 1829: state = 1830; break; // &epsil -> &epsilo - case 1834: state = 1838; break; // &eqc -> &eqco - case 1839: state = 1840; break; // &eqcol -> &eqcolo - case 1894: state = 1895; break; // &erD -> &erDo - case 1903: state = 1904; break; // &esd -> &esdo - case 1923: state = 1924; break; // &eur -> &euro - case 1936: state = 1955; break; // &exp -> &expo - case 1942: state = 1943; break; // &expectati -> &expectatio - case 1945: state = 1946; break; // &Exp -> &Expo - case 1964: state = 2055; break; // &f -> &fo - case 1971: state = 1972; break; // &fallingd -> &fallingdo - case 1977: state = 2052; break; // &F -> &Fo - case 2049: state = 2050; break; // &fn -> &fno - case 2083: state = 2109; break; // &fr -> &fro - case 2118: state = 2225; break; // &g -> &go - case 2124: state = 2222; break; // &G -> &Go - case 2159: state = 2160; break; // &Gd -> &Gdo - case 2162: state = 2163; break; // &gd -> &gdo - case 2179: state = 2180; break; // &gesd -> &gesdo - case 2181: state = 2182; break; // &gesdot -> &gesdoto - case 2212: state = 2213; break; // &gnappr -> &gnappro - case 2299: state = 2300; break; // >d -> >do - case 2315: state = 2316; break; // >rappr -> >rappro - case 2320: state = 2321; break; // >rd -> >rdo - case 2351: state = 2469; break; // &H -> &Ho - case 2356: state = 2440; break; // &h -> &ho - case 2410: state = 2411; break; // &herc -> &herco - case 2432: state = 2433; break; // &hksear -> &hksearo - case 2437: state = 2438; break; // &hkswar -> &hkswaro - case 2440: state = 2448; break; // &ho -> &hoo - case 2456: state = 2457; break; // &hookleftarr -> &hookleftarro - case 2466: state = 2467; break; // &hookrightarr -> &hookrightarro - case 2480: state = 2481; break; // &Horiz -> &Horizo - case 2501: state = 2502; break; // &Hstr -> &Hstro - case 2505: state = 2506; break; // &hstr -> &hstro - case 2511: state = 2512; break; // &HumpD -> &HumpDo - case 2533: state = 2716; break; // &I -> &Io - case 2539: state = 2713; break; // &i -> &io - case 2555: state = 2556; break; // &Id -> &Ido - case 2582: state = 2593; break; // &ii -> &iio - case 2608: state = 2631; break; // &im -> &imo - case 2641: state = 2652; break; // &in -> &ino - case 2653: state = 2654; break; // &inod -> &inodo - case 2681: state = 2682; break; // &Intersecti -> &Intersectio - case 2690: state = 2691; break; // &intpr -> &intpro - case 2700: state = 2701; break; // &InvisibleC -> &InvisibleCo - case 2717: state = 2718; break; // &Iog -> &Iogo - case 2720: state = 2721; break; // &iog -> &iogo - case 2732: state = 2733; break; // &ipr -> &ipro - case 2748: state = 2749; break; // &isind -> &isindo - case 2777: state = 2797; break; // &J -> &Jo - case 2782: state = 2800; break; // &j -> &jo - case 2825: state = 2869; break; // &K -> &Ko - case 2830: state = 2872; break; // &k -> &ko - case 2881: state = 3466; break; // &l -> &lo - case 2886: state = 3475; break; // &L -> &Lo - case 2932: state = 2933; break; // &laqu -> « - case 2990: state = 2991; break; // &Lcar -> &Lcaro - case 2995: state = 2996; break; // &lcar -> &lcaro - case 3016: state = 3017; break; // &ldqu -> &ldquo - case 3049: state = 3050; break; // &LeftArr -> &LeftArro - case 3054: state = 3055; break; // &Leftarr -> &Leftarro - case 3061: state = 3062; break; // &leftarr -> &leftarro - case 3074: state = 3075; break; // &LeftArrowRightArr -> &LeftArrowRightArro - case 3088: state = 3089; break; // &LeftD -> &LeftDo - case 3109: state = 3110; break; // &LeftDownTeeVect -> &LeftDownTeeVecto - case 3115: state = 3116; break; // &LeftDownVect -> &LeftDownVecto - case 3122: state = 3123; break; // &LeftFl -> &LeftFlo - case 3123: state = 3124; break; // &LeftFlo -> &LeftFloo - case 3129: state = 3130; break; // &leftharp -> &leftharpo - case 3130: state = 3131; break; // &leftharpo -> &leftharpoo - case 3133: state = 3134; break; // &leftharpoond -> &leftharpoondo - case 3145: state = 3146; break; // &leftleftarr -> &leftleftarro - case 3156: state = 3157; break; // &LeftRightArr -> &LeftRightArro - case 3166: state = 3167; break; // &Leftrightarr -> &Leftrightarro - case 3176: state = 3177; break; // &leftrightarr -> &leftrightarro - case 3183: state = 3184; break; // &leftrightharp -> &leftrightharpo - case 3184: state = 3185; break; // &leftrightharpo -> &leftrightharpoo - case 3195: state = 3196; break; // &leftrightsquigarr -> &leftrightsquigarro - case 3201: state = 3202; break; // &LeftRightVect -> &LeftRightVecto - case 3209: state = 3210; break; // &LeftTeeArr -> &LeftTeeArro - case 3215: state = 3216; break; // &LeftTeeVect -> &LeftTeeVecto - case 3245: state = 3246; break; // &LeftUpD -> &LeftUpDo - case 3252: state = 3253; break; // &LeftUpDownVect -> &LeftUpDownVecto - case 3261: state = 3262; break; // &LeftUpTeeVect -> &LeftUpTeeVecto - case 3267: state = 3268; break; // &LeftUpVect -> &LeftUpVecto - case 3276: state = 3277; break; // &LeftVect -> &LeftVecto - case 3294: state = 3295; break; // &lesd -> &lesdo - case 3296: state = 3297; break; // &lesdot -> &lesdoto - case 3306: state = 3307; break; // &lessappr -> &lessappro - case 3309: state = 3310; break; // &lessd -> &lessdo - case 3381: state = 3382; break; // &lfl -> &lflo - case 3382: state = 3383; break; // &lflo -> &lfloo - case 3413: state = 3414; break; // &llc -> &llco - case 3424: state = 3425; break; // &Lleftarr -> &Lleftarro - case 3436: state = 3437; break; // &Lmid -> &Lmido - case 3439: state = 3444; break; // &lm -> &lmo - case 3441: state = 3442; break; // &lmid -> &lmido - case 3456: state = 3457; break; // &lnappr -> &lnappro - case 3466: state = 3573; break; // &lo -> &loo - case 3484: state = 3485; break; // &LongLeftArr -> &LongLeftArro - case 3493: state = 3494; break; // &Longleftarr -> &Longleftarro - case 3504: state = 3505; break; // &longleftarr -> &longleftarro - case 3514: state = 3515; break; // &LongLeftRightArr -> &LongLeftRightArro - case 3524: state = 3525; break; // &Longleftrightarr -> &Longleftrightarro - case 3534: state = 3535; break; // &longleftrightarr -> &longleftrightarro - case 3541: state = 3542; break; // &longmapst -> &longmapsto - case 3550: state = 3551; break; // &LongRightArr -> &LongRightArro - case 3560: state = 3561; break; // &Longrightarr -> &Longrightarro - case 3570: state = 3571; break; // &longrightarr -> &longrightarro - case 3577: state = 3578; break; // &looparr -> &looparro - case 3619: state = 3620; break; // &LowerLeftArr -> &LowerLeftArro - case 3629: state = 3630; break; // &LowerRightArr -> &LowerRightArro - case 3647: state = 3648; break; // &lrc -> &lrco - case 3664: state = 3665; break; // &lsaqu -> &lsaquo - case 3679: state = 3680; break; // &lsqu -> &lsquo - case 3683: state = 3684; break; // &Lstr -> &Lstro - case 3687: state = 3688; break; // &lstr -> &lstro - case 3697: state = 3698; break; // <d -> <do - case 3745: state = 3865; break; // &m -> &mo - case 3755: state = 3870; break; // &M -> &Mo - case 3760: state = 3761; break; // &mapst -> &mapsto - case 3762: state = 3763; break; // &mapstod -> &mapstodo - case 3776: state = 3777; break; // &mc -> &mco - case 3789: state = 3790; break; // &mDD -> &mDDo - case 3825: state = 3826; break; // &mh -> &mho - case 3829: state = 3830; break; // &micr -> µ - case 3838: state = 3839; break; // &midd -> &middo - case 3883: state = 3884; break; // &mstp -> &mstpo - case 3897: state = 4212; break; // &n -> &no - case 3902: state = 4190; break; // &N -> &No - case 3914: state = 3918; break; // &nap -> &napo - case 3921: state = 3922; break; // &nappr -> &nappro - case 3937: state = 3956; break; // &nc -> &nco - case 3942: state = 3943; break; // &Ncar -> &Ncaro - case 3945: state = 3946; break; // &ncar -> &ncaro - case 3959: state = 3960; break; // &ncongd -> &ncongdo - case 3978: state = 3979; break; // &nearr -> &nearro - case 3981: state = 3982; break; // &ned -> &nedo - case 4138: state = 4139; break; // &nLeftarr -> &nLeftarro - case 4145: state = 4146; break; // &nleftarr -> &nleftarro - case 4155: state = 4156; break; // &nLeftrightarr -> &nLeftrightarro - case 4165: state = 4166; break; // &nleftrightarr -> &nleftrightarro - case 4217: state = 4218; break; // &NotC -> &NotCo - case 4231: state = 4232; break; // &NotD -> &NotDo - case 4320: state = 4321; break; // &NotHumpD -> &NotHumpDo - case 4335: state = 4336; break; // ¬ind -> ¬indo - case 4597: state = 4608; break; // &np -> &npo - case 4637: state = 4638; break; // &nRightarr -> &nRightarro - case 4646: state = 4647; break; // &nrightarr -> &nrightarro - case 4663: state = 4664; break; // &nsh -> &nsho - case 4756: state = 4757; break; // &numer -> &numero - case 4820: state = 4821; break; // &nwarr -> &nwarro - case 4827: state = 4951; break; // &O -> &Oo - case 4833: state = 4954; break; // &o -> &oo - case 4851: state = 4866; break; // &od -> &odo - case 4868: state = 4869; break; // &ods -> &odso - case 4887: state = 4888; break; // &og -> &ogo - case 4915: state = 4916; break; // &olcr -> &olcro - case 4939: state = 4940; break; // &Omicr -> &Omicro - case 4944: state = 4945; break; // &omicr -> &omicro - case 4968: state = 4969; break; // &OpenCurlyD -> &OpenCurlyDo - case 4975: state = 4976; break; // &OpenCurlyDoubleQu -> &OpenCurlyDoubleQuo - case 4980: state = 4981; break; // &OpenCurlyQu -> &OpenCurlyQuo - case 4991: state = 5006; break; // &or -> &oro - case 4997: state = 4998; break; // &order -> &ordero - case 5003: state = 5004; break; // &orig -> &origo - case 5009: state = 5010; break; // &orsl -> &orslo - case 5018: state = 5029; break; // &os -> &oso - case 5083: state = 5201; break; // &p -> &po - case 5096: state = 5189; break; // &P -> &Po - case 5113: state = 5114; break; // &peri -> &perio - case 5130: state = 5137; break; // &ph -> &pho - case 5145: state = 5146; break; // &pitchf -> &pitchfo - case 5168: state = 5169; break; // &plusd -> &plusdo - case 5186: state = 5187; break; // &plustw -> &plustwo - case 5215: state = 5298; break; // &Pr -> &Pro - case 5216: state = 5296; break; // &pr -> &pro - case 5228: state = 5229; break; // &precappr -> &precappro - case 5270: state = 5271; break; // &precnappr -> &precnappro - case 5317: state = 5318; break; // &Prop -> &Propo - case 5321: state = 5322; break; // &Proporti -> &Proportio - case 5326: state = 5327; break; // &propt -> &propto - case 5348: state = 5357; break; // &Q -> &Qo - case 5351: state = 5360; break; // &q -> &qo - case 5374: state = 5395; break; // &qu -> &quo - case 5380: state = 5381; break; // &quaterni -> &quaternio - case 5397: state = 5875; break; // &r -> &ro - case 5405: state = 5887; break; // &R -> &Ro - case 5432: state = 5433; break; // &raqu -> » - case 5470: state = 5471; break; // &rati -> &ratio - case 5504: state = 5505; break; // &Rcar -> &Rcaro - case 5509: state = 5510; break; // &rcar -> &rcaro - case 5535: state = 5536; break; // &rdqu -> &rdquo - case 5597: state = 5598; break; // &rfl -> &rflo - case 5598: state = 5599; break; // &rflo -> &rfloo - case 5607: state = 5615; break; // &rh -> &rho - case 5613: state = 5614; break; // &Rh -> &Rho - case 5634: state = 5635; break; // &RightArr -> &RightArro - case 5639: state = 5640; break; // &Rightarr -> &Rightarro - case 5648: state = 5649; break; // &rightarr -> &rightarro - case 5660: state = 5661; break; // &RightArrowLeftArr -> &RightArrowLeftArro - case 5674: state = 5675; break; // &RightD -> &RightDo - case 5695: state = 5696; break; // &RightDownTeeVect -> &RightDownTeeVecto - case 5701: state = 5702; break; // &RightDownVect -> &RightDownVecto - case 5708: state = 5709; break; // &RightFl -> &RightFlo - case 5709: state = 5710; break; // &RightFlo -> &RightFloo - case 5715: state = 5716; break; // &rightharp -> &rightharpo - case 5716: state = 5717; break; // &rightharpo -> &rightharpoo - case 5719: state = 5720; break; // &rightharpoond -> &rightharpoondo - case 5731: state = 5732; break; // &rightleftarr -> &rightleftarro - case 5738: state = 5739; break; // &rightleftharp -> &rightleftharpo - case 5739: state = 5740; break; // &rightleftharpo -> &rightleftharpoo - case 5750: state = 5751; break; // &rightrightarr -> &rightrightarro - case 5761: state = 5762; break; // &rightsquigarr -> &rightsquigarro - case 5769: state = 5770; break; // &RightTeeArr -> &RightTeeArro - case 5775: state = 5776; break; // &RightTeeVect -> &RightTeeVecto - case 5805: state = 5806; break; // &RightUpD -> &RightUpDo - case 5812: state = 5813; break; // &RightUpDownVect -> &RightUpDownVecto - case 5821: state = 5822; break; // &RightUpTeeVect -> &RightUpTeeVecto - case 5827: state = 5828; break; // &RightUpVect -> &RightUpVecto - case 5836: state = 5837; break; // &RightVect -> &RightVecto - case 5848: state = 5849; break; // &risingd -> &risingdo - case 5862: state = 5863; break; // &rm -> &rmo - case 5914: state = 5915; break; // &rpp -> &rppo - case 5931: state = 5932; break; // &Rrightarr -> &Rrightarro - case 5937: state = 5938; break; // &rsaqu -> &rsaquo - case 5948: state = 5949; break; // &rsqu -> &rsquo - case 5985: state = 6244; break; // &S -> &So - case 5991: state = 6235; break; // &s -> &so - case 5999: state = 6000; break; // &sbqu -> &sbquo - case 6006: state = 6007; break; // &Scar -> &Scaro - case 6009: state = 6010; break; // &scar -> &scaro - case 6037: state = 6038; break; // &scp -> &scpo - case 6048: state = 6049; break; // &sd -> &sdo - case 6061: state = 6062; break; // &searr -> &searro - case 6084: state = 6085; break; // &sfr -> &sfro - case 6088: state = 6126; break; // &sh -> &sho - case 6104: state = 6105; break; // &Sh -> &Sho - case 6108: state = 6109; break; // &ShortD -> &ShortDo - case 6114: state = 6115; break; // &ShortDownArr -> &ShortDownArro - case 6123: state = 6124; break; // &ShortLeftArr -> &ShortLeftArro - case 6147: state = 6148; break; // &ShortRightArr -> &ShortRightArro - case 6154: state = 6155; break; // &ShortUpArr -> &ShortUpArro - case 6169: state = 6170; break; // &simd -> &simdo - case 6302: state = 6303; break; // &SquareIntersecti -> &SquareIntersectio - case 6329: state = 6330; break; // &SquareUni -> &SquareUnio - case 6373: state = 6374; break; // &straightepsil -> &straightepsilo - case 6385: state = 6386; break; // &subd -> &subdo - case 6390: state = 6391; break; // &subed -> &subedo - case 6436: state = 6437; break; // &succappr -> &succappro - case 6478: state = 6479; break; // &succnappr -> &succnappro - case 6504: state = 6505; break; // &supd -> &supdo - case 6512: state = 6513; break; // &suped -> &supedo - case 6526: state = 6527; break; // &suphs -> &suphso - case 6572: state = 6573; break; // &swarr -> &swarro - case 6583: state = 6742; break; // &T -> &To - case 6586: state = 6732; break; // &t -> &to - case 6599: state = 6600; break; // &Tcar -> &Tcaro - case 6604: state = 6605; break; // &tcar -> &tcaro - case 6617: state = 6618; break; // &td -> &tdo - case 6629: state = 6693; break; // &th -> &tho - case 6638: state = 6639; break; // &Theref -> &Therefo - case 6642: state = 6643; break; // &theref -> &therefo - case 6660: state = 6661; break; // &thickappr -> &thickappro - case 6736: state = 6737; break; // &topb -> &topbo - case 6745: state = 6746; break; // &topf -> &topfo - case 6770: state = 6771; break; // &triangled -> &triangledo - case 6788: state = 6789; break; // &trid -> &trido - case 6802: state = 6803; break; // &TripleD -> &TripleDo - case 6838: state = 6839; break; // &Tstr -> &Tstro - case 6842: state = 6843; break; // &tstr -> &tstro - case 6845: state = 6849; break; // &tw -> &two - case 6860: state = 6861; break; // &twoheadleftarr -> &twoheadleftarro - case 6870: state = 6871; break; // &twoheadrightarr -> &twoheadrightarro - case 6873: state = 7019; break; // &U -> &Uo - case 6879: state = 7023; break; // &u -> &uo - case 6886: state = 6892; break; // &Uarr -> &Uarro - case 6966: state = 6967; break; // &ulc -> &ulco - case 6972: state = 6973; break; // &ulcr -> &ulcro - case 7012: state = 7013; break; // &Uni -> &Unio - case 7020: state = 7021; break; // &Uog -> &Uogo - case 7024: state = 7025; break; // &uog -> &uogo - case 7034: state = 7035; break; // &UpArr -> &UpArro - case 7039: state = 7040; break; // &Uparr -> &Uparro - case 7045: state = 7046; break; // &uparr -> &uparro - case 7051: state = 7052; break; // &UpArrowD -> &UpArrowDo - case 7057: state = 7058; break; // &UpArrowDownArr -> &UpArrowDownArro - case 7060: state = 7061; break; // &UpD -> &UpDo - case 7066: state = 7067; break; // &UpDownArr -> &UpDownArro - case 7069: state = 7070; break; // &Upd -> &Updo - case 7075: state = 7076; break; // &Updownarr -> &Updownarro - case 7078: state = 7079; break; // &upd -> &updo - case 7084: state = 7085; break; // &updownarr -> &updownarro - case 7101: state = 7102; break; // &upharp -> &upharpo - case 7102: state = 7103; break; // &upharpo -> &upharpoo - case 7126: state = 7127; break; // &UpperLeftArr -> &UpperLeftArro - case 7136: state = 7137; break; // &UpperRightArr -> &UpperRightArro - case 7144: state = 7145; break; // &Upsil -> &Upsilo - case 7147: state = 7148; break; // &upsil -> &upsilo - case 7155: state = 7156; break; // &UpTeeArr -> &UpTeeArro - case 7162: state = 7163; break; // &upuparr -> &upuparro - case 7167: state = 7168; break; // &urc -> &urco - case 7173: state = 7174; break; // &urcr -> &urcro - case 7193: state = 7194; break; // &utd -> &utdo - case 7223: state = 7410; break; // &v -> &vo - case 7234: state = 7235; break; // &varepsil -> &varepsilo - case 7242: state = 7243; break; // &varn -> &varno - case 7253: state = 7254; break; // &varpr -> &varpro - case 7256: state = 7257; break; // &varpropt -> &varpropto - case 7262: state = 7263; break; // &varrh -> &varrho - case 7307: state = 7407; break; // &V -> &Vo - case 7376: state = 7377; break; // &VerticalSeparat -> &VerticalSeparato - case 7414: state = 7415; break; // &vpr -> &vpro - case 7447: state = 7477; break; // &W -> &Wo - case 7452: state = 7480; break; // &w -> &wo - case 7495: state = 7535; break; // &x -> &xo - case 7508: state = 7539; break; // &X -> &Xo - case 7536: state = 7537; break; // &xod -> &xodo - case 7584: state = 7622; break; // &Y -> &Yo - case 7590: state = 7625; break; // &y -> &yo - case 7645: state = 7713; break; // &Z -> &Zo - case 7651: state = 7716; break; // &z -> &zo - case 7659: state = 7660; break; // &Zcar -> &Zcaro - case 7664: state = 7665; break; // &zcar -> &zcaro - case 7669: state = 7670; break; // &Zd -> &Zdo - case 7672: state = 7673; break; // &zd -> &zdo - case 7681: state = 7682; break; // &Zer -> &Zero - default: return false; - } - break; - case 'p': - switch (state) { - case 0: state = 5083; break; // & -> &p - case 1: state = 154; break; // &A -> &Ap - case 7: state = 143; break; // &a -> &ap - case 60: state = 72; break; // &al -> &alp - case 61: state = 66; break; // &ale -> &alep - case 68: state = 69; break; // &Al -> &Alp - case 79: state = 87; break; // &am -> & - case 98: state = 99; break; // &andslo -> &andslop - case 123: state = 124; break; // &angs -> &angsp - case 131: state = 139; break; // &Ao -> &Aop - case 135: state = 141; break; // &ao -> &aop - case 143: state = 166; break; // &ap -> &app - case 154: state = 155; break; // &Ap -> &App - case 192: state = 193; break; // &asym -> &asymp - case 222: state = 532; break; // &b -> &bp - case 225: state = 237; break; // &back -> &backp - case 230: state = 231; break; // &backe -> &backep - case 288: state = 305; break; // &be -> &bep - case 300: state = 301; break; // &bem -> &bemp - case 336: state = 337; break; // &bigca -> &bigcap - case 341: state = 342; break; // &bigcu -> &bigcup - case 343: state = 347; break; // &bigo -> &bigop - case 359: state = 360; break; // &bigsqcu -> &bigsqcup - case 376: state = 377; break; // &bigtriangleu -> &bigtriangleup - case 378: state = 379; break; // &bigu -> &bigup - case 456: state = 457; break; // &Bo -> &Bop - case 459: state = 460; break; // &bo -> &bop - case 470: state = 499; break; // &box -> &boxp - case 573: state = 574; break; // &bum -> &bump - case 578: state = 579; break; // &Bum -> &Bump - case 584: state = 595; break; // &Ca -> &Cap - case 590: state = 596; break; // &ca -> &cap - case 603: state = 604; break; // &capbrcu -> &capbrcup - case 606: state = 607; break; // &capca -> &capcap - case 608: state = 609; break; // &capcu -> &capcup - case 642: state = 643; break; // &cca -> &ccap - case 672: state = 673; break; // &ccu -> &ccup - case 693: state = 694; break; // &cem -> &cemp - case 856: state = 913; break; // &Co -> &Cop - case 860: state = 915; break; // &co -> &cop - case 867: state = 871; break; // &com -> &comp - case 978: state = 981; break; // &csu -> &csup - case 987: state = 1006; break; // &cu -> &cup - case 994: state = 995; break; // &cue -> &cuep - case 1002: state = 1003; break; // &cularr -> &cularrp - case 1004: state = 1005; break; // &Cu -> &Cup - case 1010: state = 1011; break; // &cupbrca -> &cupbrcap - case 1013: state = 1014; break; // &CupCa -> &CupCap - case 1016: state = 1017; break; // &cupca -> &cupcap - case 1018: state = 1019; break; // &cupcu -> &cupcup - case 1034: state = 1035; break; // &curlyeq -> &curlyeqp - case 1170: state = 1171; break; // &dem -> &demp - case 1289: state = 1290; break; // &dlcro -> &dlcrop - case 1291: state = 1299; break; // &do -> &dop - case 1296: state = 1297; break; // &Do -> &Dop - case 1302: state = 1321; break; // &dot -> &dotp - case 1439: state = 1440; break; // &DoubleU -> &DoubleUp - case 1488: state = 1489; break; // &DownArrowU -> &DownArrowUp - case 1512: state = 1513; break; // &downhar -> &downharp - case 1602: state = 1603; break; // &drcro -> &drcrop - case 1656: state = 1823; break; // &E -> &Ep - case 1662: state = 1813; break; // &e -> &ep - case 1746: state = 1760; break; // &Em -> &Emp - case 1750: state = 1754; break; // &em -> &emp - case 1790: state = 1791; break; // &ems -> &emsp - case 1799: state = 1800; break; // &ens -> &ensp - case 1801: state = 1809; break; // &Eo -> &Eop - case 1805: state = 1811; break; // &eo -> &eop - case 1884: state = 1885; break; // &eqv -> &eqvp - case 1925: state = 1936; break; // &ex -> &exp - case 1931: state = 1945; break; // &Ex -> &Exp - case 1964: state = 2076; break; // &f -> &fp - case 2052: state = 2053; break; // &Fo -> &Fop - case 2055: state = 2056; break; // &fo -> &fop - case 2119: state = 2134; break; // &ga -> &gap - case 2209: state = 2210; break; // &gna -> &gnap - case 2210: state = 2211; break; // &gnap -> &gnapp - case 2222: state = 2223; break; // &Go -> &Gop - case 2225: state = 2226; break; // &go -> &gop - case 2312: state = 2313; break; // >ra -> >rap - case 2313: state = 2314; break; // >rap -> >rapp - case 2360: state = 2361; break; // &hairs -> &hairsp - case 2407: state = 2408; break; // &helli -> &hellip - case 2423: state = 2424; break; // &HilbertS -> &HilbertSp - case 2440: state = 2472; break; // &ho -> &hop - case 2469: state = 2470; break; // &Ho -> &Hop - case 2509: state = 2510; break; // &Hum -> &Hump - case 2517: state = 2518; break; // &HumpDownHum -> &HumpDownHump - case 2524: state = 2529; break; // &hy -> &hyp - case 2539: state = 2731; break; // &i -> &ip - case 2604: state = 2636; break; // &Im -> &Imp - case 2608: state = 2633; break; // &im -> &imp - case 2612: state = 2625; break; // &imag -> &imagp - case 2658: state = 2689; break; // &int -> &intp - case 2713: state = 2725; break; // &io -> &iop - case 2716: state = 2723; break; // &Io -> &Iop - case 2797: state = 2798; break; // &Jo -> &Jop - case 2800: state = 2801; break; // &jo -> &jop - case 2826: state = 2827; break; // &Ka -> &Kap - case 2827: state = 2828; break; // &Kap -> &Kapp - case 2831: state = 2832; break; // &ka -> &kap - case 2832: state = 2833; break; // &kap -> &kapp - case 2869: state = 2870; break; // &Ko -> &Kop - case 2872: state = 2873; break; // &ko -> &kop - case 2881: state = 3638; break; // &l -> &lp - case 2887: state = 2923; break; // &La -> &Lap - case 2892: state = 2922; break; // &la -> &lap - case 2898: state = 2899; break; // &laem -> &laemp - case 2939: state = 2949; break; // &larr -> &larrp - case 2947: state = 2948; break; // &larrl -> &larrlp - case 3128: state = 3129; break; // &lefthar -> &leftharp - case 3137: state = 3138; break; // &leftharpoonu -> &leftharpoonup - case 3182: state = 3183; break; // &leftrighthar -> &leftrightharp - case 3243: state = 3244; break; // &LeftU -> &LeftUp - case 3303: state = 3304; break; // &lessa -> &lessap - case 3304: state = 3305; break; // &lessap -> &lessapp - case 3453: state = 3454; break; // &lna -> &lnap - case 3454: state = 3455; break; // &lnap -> &lnapp - case 3466: state = 3589; break; // &lo -> &lop - case 3475: state = 3592; break; // &Lo -> &Lop - case 3538: state = 3539; break; // &longma -> &longmap - case 3573: state = 3574; break; // &loo -> &loop - case 3745: state = 3875; break; // &m -> &mp - case 3746: state = 3758; break; // &ma -> &map - case 3756: state = 3757; break; // &Ma -> &Map - case 3770: state = 3771; break; // &mapstou -> &mapstoup - case 3809: state = 3810; break; // &MediumS -> &MediumSp - case 3856: state = 3857; break; // &mlc -> &mlcp - case 3860: state = 3861; break; // &mn -> &mnp - case 3865: state = 3873; break; // &mo -> &mop - case 3870: state = 3871; break; // &Mo -> &Mop - case 3882: state = 3883; break; // &mst -> &mstp - case 3892: state = 3893; break; // &multima -> &multimap - case 3895: state = 3896; break; // &muma -> &mumap - case 3897: state = 4597; break; // &n -> &np - case 3898: state = 3914; break; // &na -> &nap - case 3914: state = 3920; break; // &nap -> &napp - case 3931: state = 3932; break; // &nbs ->   - case 3934: state = 3935; break; // &nbum -> &nbump - case 3938: state = 3939; break; // &nca -> &ncap - case 3962: state = 3963; break; // &ncu -> &ncup - case 3997: state = 3998; break; // &NegativeMediumS -> &NegativeMediumSp - case 4007: state = 4008; break; // &NegativeThickS -> &NegativeThickSp - case 4013: state = 4014; break; // &NegativeThinS -> &NegativeThinSp - case 4026: state = 4027; break; // &NegativeVeryThinS -> &NegativeVeryThinSp - case 4101: state = 4108; break; // &nh -> &nhp - case 4190: state = 4210; break; // &No -> &Nop - case 4205: state = 4206; break; // &NonBreakingS -> &NonBreakingSp - case 4212: state = 4213; break; // &no -> &nop - case 4226: state = 4227; break; // &NotCu -> &NotCup - case 4229: state = 4230; break; // &NotCupCa -> &NotCupCap - case 4318: state = 4319; break; // &NotHum -> &NotHump - case 4326: state = 4327; break; // &NotHumpDownHum -> &NotHumpDownHump - case 4494: state = 4504; break; // &NotSquareSu -> &NotSquareSup - case 4515: state = 4551; break; // &NotSu -> &NotSup - case 4653: state = 4685; break; // &ns -> &nsp - case 4666: state = 4670; break; // &nshort -> &nshortp - case 4690: state = 4693; break; // &nsqsu -> &nsqsup - case 4695: state = 4709; break; // &nsu -> &nsup - case 4758: state = 4759; break; // &nums -> &numsp - case 4761: state = 4762; break; // &nva -> &nvap - case 4827: state = 4960; break; // &O -> &Op - case 4833: state = 4957; break; // &o -> &op - case 4951: state = 4952; break; // &Oo -> &Oop - case 4954: state = 4955; break; // &oo -> &oop - case 4985: state = 4986; break; // &oper -> &operp - case 5010: state = 5011; break; // &orslo -> &orslop - case 5109: state = 5119; break; // &per -> &perp - case 5189: state = 5208; break; // &Po -> &Pop - case 5195: state = 5196; break; // &Poincare -> &Poincarep - case 5201: state = 5210; break; // &po -> &pop - case 5217: state = 5218; break; // &pra -> &prap - case 5225: state = 5226; break; // &preca -> &precap - case 5226: state = 5227; break; // &precap -> &precapp - case 5267: state = 5268; break; // &precna -> &precnap - case 5268: state = 5269; break; // &precnap -> &precnapp - case 5290: state = 5291; break; // &prna -> &prnap - case 5296: state = 5316; break; // &pro -> &prop - case 5298: state = 5317; break; // &Pro -> &Prop - case 5346: state = 5347; break; // &puncs -> &puncsp - case 5351: state = 5363; break; // &q -> &qp - case 5357: state = 5358; break; // &Qo -> &Qop - case 5360: state = 5361; break; // &qo -> &qop - case 5397: state = 5909; break; // &r -> &rp - case 5418: state = 5419; break; // &raem -> &raemp - case 5439: state = 5452; break; // &rarr -> &rarrp - case 5440: state = 5441; break; // &rarra -> &rarrap - case 5450: state = 5451; break; // &rarrl -> &rarrlp - case 5543: state = 5547; break; // &real -> &realp - case 5579: state = 5580; break; // &ReverseU -> &ReverseUp - case 5714: state = 5715; break; // &righthar -> &rightharp - case 5723: state = 5724; break; // &rightharpoonu -> &rightharpoonup - case 5737: state = 5738; break; // &rightlefthar -> &rightleftharp - case 5803: state = 5804; break; // &RightU -> &RightUp - case 5875: state = 5884; break; // &ro -> &rop - case 5887: state = 5888; break; // &Ro -> &Rop - case 5903: state = 5904; break; // &RoundIm -> &RoundImp - case 5909: state = 5914; break; // &rp -> &rpp - case 5991: state = 6249; break; // &s -> &sp - case 6002: state = 6037; break; // &sc -> &scp - case 6003: state = 6004; break; // &sca -> &scap - case 6031: state = 6032; break; // &scna -> &scnap - case 6090: state = 6091; break; // &shar -> &sharp - case 6128: state = 6132; break; // &short -> &shortp - case 6150: state = 6151; break; // &ShortU -> &ShortUp - case 6168: state = 6180; break; // &sim -> &simp - case 6215: state = 6216; break; // &smash -> &smashp - case 6217: state = 6218; break; // &sme -> &smep - case 6235: state = 6247; break; // &so -> &sop - case 6244: state = 6245; break; // &So -> &Sop - case 6260: state = 6261; break; // &sqca -> &sqcap - case 6263: state = 6264; break; // &sqcu -> &sqcup - case 6270: state = 6278; break; // &sqsu -> &sqsup - case 6306: state = 6316; break; // &SquareSu -> &SquareSup - case 6368: state = 6376; break; // &straight -> &straightp - case 6369: state = 6370; break; // &straighte -> &straightep - case 6381: state = 6499; break; // &Su -> &Sup - case 6383: state = 6500; break; // &su -> &sup - case 6384: state = 6400; break; // &sub -> &subp - case 6428: state = 6430; break; // &subsu -> &subsup - case 6433: state = 6434; break; // &succa -> &succap - case 6434: state = 6435; break; // &succap -> &succapp - case 6475: state = 6476; break; // &succna -> &succnap - case 6476: state = 6477; break; // &succnap -> &succnapp - case 6500: state = 6542; break; // &sup -> &supp - case 6561: state = 6563; break; // &supsu -> &supsup - case 6586: state = 6751; break; // &t -> &tp - case 6657: state = 6658; break; // &thicka -> &thickap - case 6658: state = 6659; break; // &thickap -> &thickapp - case 6669: state = 6670; break; // &ThickS -> &ThickSp - case 6675: state = 6676; break; // &thins -> &thinsp - case 6678: state = 6679; break; // &ThinS -> &ThinSp - case 6684: state = 6685; break; // &thka -> &thkap - case 6732: state = 6735; break; // &to -> &top - case 6742: state = 6743; break; // &To -> &Top - case 6760: state = 6815; break; // &tr -> &trp - case 6764: state = 6805; break; // &tri -> &trip - case 6798: state = 6799; break; // &Tri -> &Trip - case 6873: state = 7031; break; // &U -> &Up - case 6879: state = 7042; break; // &u -> &up - case 6973: state = 6974; break; // &ulcro -> &ulcrop - case 7019: state = 7027; break; // &Uo -> &Uop - case 7023: state = 7029; break; // &uo -> &uop - case 7031: state = 7117; break; // &Up -> &Upp - case 7100: state = 7101; break; // &uphar -> &upharp - case 7158: state = 7159; break; // &upu -> &upup - case 7174: state = 7175; break; // &urcro -> &urcrop - case 7223: state = 7413; break; // &v -> &vp - case 7229: state = 7249; break; // &var -> &varp - case 7230: state = 7231; break; // &vare -> &varep - case 7238: state = 7239; break; // &varka -> &varkap - case 7239: state = 7240; break; // &varkap -> &varkapp - case 7254: state = 7255; break; // &varpro -> &varprop - case 7269: state = 7278; break; // &varsu -> &varsup - case 7347: state = 7348; break; // &velli -> &vellip - case 7371: state = 7372; break; // &VerticalSe -> &VerticalSep - case 7389: state = 7390; break; // &VeryThinS -> &VeryThinSp - case 7404: state = 7406; break; // &vnsu -> &vnsup - case 7407: state = 7408; break; // &Vo -> &Vop - case 7410: state = 7411; break; // &vo -> &vop - case 7415: state = 7416; break; // &vpro -> &vprop - case 7427: state = 7432; break; // &vsu -> &vsup - case 7452: state = 7483; break; // &w -> &wp - case 7471: state = 7472; break; // &weier -> &weierp - case 7477: state = 7478; break; // &Wo -> &Wop - case 7480: state = 7481; break; // &wo -> &wop - case 7497: state = 7498; break; // &xca -> &xcap - case 7502: state = 7503; break; // &xcu -> &xcup - case 7530: state = 7531; break; // &xma -> &xmap - case 7535: state = 7542; break; // &xo -> &xop - case 7539: state = 7540; break; // &Xo -> &Xop - case 7566: state = 7567; break; // &xsqcu -> &xsqcup - case 7568: state = 7569; break; // &xu -> &xup - case 7622: state = 7623; break; // &Yo -> &Yop - case 7625: state = 7626; break; // &yo -> &yop - case 7688: state = 7689; break; // &ZeroWidthS -> &ZeroWidthSp - case 7713: state = 7714; break; // &Zo -> &Zop - case 7716: state = 7717; break; // &zo -> &zop - default: return false; - } - break; - case 'q': - switch (state) { - case 0: state = 5351; break; // & -> &q - case 170: state = 171; break; // &approxe -> &approxeq - case 194: state = 195; break; // &asympe -> &asympeq - case 245: state = 246; break; // &backsime -> &backsimeq - case 284: state = 285; break; // &bd -> &bdq - case 356: state = 357; break; // &bigs -> &bigsq - case 407: state = 408; break; // &blacks -> &blacksq - case 446: state = 447; break; // &bne -> &bneq - case 576: state = 582; break; // &bumpe -> &bumpeq - case 580: state = 581; break; // &Bumpe -> &Bumpeq - case 735: state = 736; break; // &circe -> &circeq - case 865: state = 866; break; // &colone -> &coloneq - case 1033: state = 1034; break; // &curlye -> &curlyeq - case 1159: state = 1160; break; // &ddotse -> &ddotseq - case 1306: state = 1307; break; // &dote -> &doteq - case 1311: state = 1312; break; // &DotE -> &DotEq - case 1325: state = 1326; break; // &dots -> &dotsq - case 1656: state = 1856; break; // &E -> &Eq - case 1662: state = 1833; break; // &e -> &eq - case 1768: state = 1769; break; // &EmptySmallS -> &EmptySmallSq - case 1784: state = 1785; break; // &EmptyVerySmallS -> &EmptyVerySmallSq - case 1975: state = 1976; break; // &fallingdotse -> &fallingdotseq - case 2015: state = 2016; break; // &FilledSmallS -> &FilledSmallSq - case 2030: state = 2031; break; // &FilledVerySmallS -> &FilledVerySmallSq - case 2166: state = 2169; break; // &ge -> &geq - case 2169: state = 2170; break; // &geq -> &geqq - case 2216: state = 2217; break; // &gne -> &gneq - case 2217: state = 2218; break; // &gneq -> &gneqq - case 2238: state = 2239; break; // &GreaterE -> &GreaterEq - case 2251: state = 2252; break; // &GreaterFullE -> &GreaterFullEq - case 2272: state = 2273; break; // &GreaterSlantE -> &GreaterSlantEq - case 2294: state = 2306; break; // > -> >q - case 2323: state = 2324; break; // >re -> >req - case 2324: state = 2329; break; // >req -> >reqq - case 2346: state = 2347; break; // &gvertne -> &gvertneq - case 2347: state = 2348; break; // &gvertneq -> &gvertneqq - case 2519: state = 2520; break; // &HumpE -> &HumpEq - case 2539: state = 2735; break; // &i -> &iq - case 2892: state = 2931; break; // &la -> &laq - case 3012: state = 3015; break; // &ld -> &ldq - case 3032: state = 3284; break; // &le -> &leq - case 3188: state = 3189; break; // &leftrights -> &leftrightsq - case 3238: state = 3239; break; // &LeftTriangleE -> &LeftTriangleEq - case 3284: state = 3285; break; // &leq -> &leqq - case 3312: state = 3313; break; // &lesse -> &lesseq - case 3313: state = 3317; break; // &lesseq -> &lesseqq - case 3323: state = 3324; break; // &LessE -> &LessEq - case 3339: state = 3340; break; // &LessFullE -> &LessFullEq - case 3366: state = 3367; break; // &LessSlantE -> &LessSlantEq - case 3460: state = 3461; break; // &lne -> &lneq - case 3461: state = 3462; break; // &lneq -> &lneqq - case 3661: state = 3677; break; // &ls -> &lsq - case 3662: state = 3663; break; // &lsa -> &lsaq - case 3692: state = 3712; break; // < -> <q - case 3740: state = 3741; break; // &lvertne -> &lvertneq - case 3741: state = 3742; break; // &lvertneq -> &lvertneqq - case 3970: state = 4031; break; // &ne -> &neq - case 4083: state = 4084; break; // &nge -> &ngeq - case 4084: state = 4085; break; // &ngeq -> &ngeqq - case 4131: state = 4168; break; // &nle -> &nleq - case 4168: state = 4169; break; // &nleq -> &nleqq - case 4248: state = 4255; break; // &NotE -> &NotEq - case 4276: state = 4277; break; // &NotGreaterE -> &NotGreaterEq - case 4285: state = 4286; break; // &NotGreaterFullE -> &NotGreaterFullEq - case 4306: state = 4307; break; // &NotGreaterSlantE -> &NotGreaterSlantEq - case 4328: state = 4329; break; // &NotHumpE -> &NotHumpEq - case 4358: state = 4359; break; // &NotLeftTriangleE -> &NotLeftTriangleEq - case 4365: state = 4366; break; // &NotLessE -> &NotLessEq - case 4386: state = 4387; break; // &NotLessSlantE -> &NotLessSlantEq - case 4438: state = 4439; break; // &NotPrecedesE -> &NotPrecedesEq - case 4448: state = 4449; break; // &NotPrecedesSlantE -> &NotPrecedesSlantEq - case 4482: state = 4483; break; // &NotRightTriangleE -> &NotRightTriangleEq - case 4487: state = 4488; break; // &NotS -> &NotSq - case 4499: state = 4500; break; // &NotSquareSubsetE -> &NotSquareSubsetEq - case 4510: state = 4511; break; // &NotSquareSupersetE -> &NotSquareSupersetEq - case 4520: state = 4521; break; // &NotSubsetE -> &NotSubsetEq - case 4531: state = 4532; break; // &NotSucceedsE -> &NotSucceedsEq - case 4541: state = 4542; break; // &NotSucceedsSlantE -> &NotSucceedsSlantEq - case 4557: state = 4558; break; // &NotSupersetE -> &NotSupersetEq - case 4567: state = 4568; break; // &NotTildeE -> &NotTildeEq - case 4576: state = 4577; break; // &NotTildeFullE -> &NotTildeFullEq - case 4619: state = 4620; break; // &nprece -> &npreceq - case 4653: state = 4688; break; // &ns -> &nsq - case 4680: state = 4681; break; // &nsime -> &nsimeq - case 4702: state = 4703; break; // &nsubsete -> &nsubseteq - case 4703: state = 4704; break; // &nsubseteq -> &nsubseteqq - case 4707: state = 4708; break; // &nsucce -> &nsucceq - case 4715: state = 4716; break; // &nsupsete -> &nsupseteq - case 4716: state = 4717; break; // &nsupseteq -> &nsupseteqq - case 4743: state = 4744; break; // &ntrianglelefte -> &ntrianglelefteq - case 4750: state = 4751; break; // &ntrianglerighte -> &ntrianglerighteq - case 5236: state = 5237; break; // &preccurlye -> &preccurlyeq - case 5244: state = 5245; break; // &PrecedesE -> &PrecedesEq - case 5254: state = 5255; break; // &PrecedesSlantE -> &PrecedesSlantEq - case 5264: state = 5265; break; // &prece -> &preceq - case 5273: state = 5274; break; // &precne -> &precneq - case 5274: state = 5275; break; // &precneq -> &precneqq - case 5390: state = 5391; break; // &queste -> &questeq - case 5402: state = 5431; break; // &ra -> &raq - case 5526: state = 5534; break; // &rd -> &rdq - case 5562: state = 5569; break; // &ReverseE -> &ReverseEq - case 5581: state = 5582; break; // &ReverseUpE -> &ReverseUpEq - case 5754: state = 5755; break; // &rights -> &rightsq - case 5798: state = 5799; break; // &RightTriangleE -> &RightTriangleEq - case 5852: state = 5853; break; // &risingdotse -> &risingdotseq - case 5934: state = 5946; break; // &rs -> &rsq - case 5935: state = 5936; break; // &rsa -> &rsaq - case 5985: state = 6266; break; // &S -> &Sq - case 5991: state = 6258; break; // &s -> &sq - case 5997: state = 5998; break; // &sb -> &sbq - case 6172: state = 6173; break; // &sime -> &simeq - case 6276: state = 6277; break; // &sqsubsete -> &sqsubseteq - case 6283: state = 6284; break; // &sqsupsete -> &sqsupseteq - case 6311: state = 6312; break; // &SquareSubsetE -> &SquareSubsetEq - case 6322: state = 6323; break; // &SquareSupersetE -> &SquareSupersetEq - case 6414: state = 6415; break; // &subsete -> &subseteq - case 6415: state = 6416; break; // &subseteq -> &subseteqq - case 6417: state = 6418; break; // &SubsetE -> &SubsetEq - case 6423: state = 6424; break; // &subsetne -> &subsetneq - case 6424: state = 6425; break; // &subsetneq -> &subsetneqq - case 6444: state = 6445; break; // &succcurlye -> &succcurlyeq - case 6452: state = 6453; break; // &SucceedsE -> &SucceedsEq - case 6462: state = 6463; break; // &SucceedsSlantE -> &SucceedsSlantEq - case 6472: state = 6473; break; // &succe -> &succeq - case 6481: state = 6482; break; // &succne -> &succneq - case 6482: state = 6483; break; // &succneq -> &succneqq - case 6520: state = 6521; break; // &SupersetE -> &SupersetEq - case 6552: state = 6553; break; // &supsete -> &supseteq - case 6553: state = 6554; break; // &supseteq -> &supseteqq - case 6556: state = 6557; break; // &supsetne -> &supsetneq - case 6557: state = 6558; break; // &supsetneq -> &supsetneqq - case 6704: state = 6705; break; // &TildeE -> &TildeEq - case 6713: state = 6714; break; // &TildeFullE -> &TildeFullEq - case 6769: state = 6780; break; // &triangle -> &triangleq - case 6778: state = 6779; break; // &trianglelefte -> &trianglelefteq - case 6786: state = 6787; break; // &trianglerighte -> &trianglerighteq - case 7087: state = 7088; break; // &UpE -> &UpEq - case 7275: state = 7276; break; // &varsubsetne -> &varsubsetneq - case 7276: state = 7277; break; // &varsubsetneq -> &varsubsetneqq - case 7283: state = 7284; break; // &varsupsetne -> &varsupsetneq - case 7284: state = 7285; break; // &varsupsetneq -> &varsupsetneqq - case 7343: state = 7344; break; // &veee -> &veeeq - case 7467: state = 7468; break; // &wedge -> &wedgeq - case 7561: state = 7564; break; // &xs -> &xsq - default: return false; - } - break; - case 'r': - switch (state) { - case 0: state = 5397; break; // & -> &r - case 1: state = 172; break; // &A -> &Ar - case 7: state = 176; break; // &a -> &ar - case 13: state = 14; break; // &Ab -> &Abr - case 18: state = 19; break; // &ab -> &abr - case 27: state = 28; break; // &Aci -> &Acir - case 30: state = 31; break; // &aci -> &acir - case 46: state = 49; break; // &af -> &afr - case 47: state = 48; break; // &Af -> &Afr - case 50: state = 51; break; // &Ag -> &Agr - case 55: state = 56; break; // &ag -> &agr - case 77: state = 78; break; // &Amac -> &Amacr - case 81: state = 82; break; // &amac -> &amacr - case 102: state = 118; break; // &ang -> &angr - case 128: state = 129; break; // &angza -> &angzar - case 129: state = 130; break; // &angzar -> &angzarr - case 146: state = 147; break; // &apaci -> &apacir - case 166: state = 167; break; // &app -> &appr - case 181: state = 182; break; // &Asc -> &Ascr - case 184: state = 185; break; // &asc -> &ascr - case 222: state = 541; break; // &b -> &br - case 223: state = 258; break; // &ba -> &bar - case 237: state = 238; break; // &backp -> &backpr - case 247: state = 537; break; // &B -> &Br - case 248: state = 256; break; // &Ba -> &Bar - case 270: state = 271; break; // &bb -> &bbr - case 274: state = 275; break; // &bbrktb -> &bbrktbr - case 288: state = 308; break; // &be -> &ber - case 293: state = 312; break; // &Be -> &Ber - case 329: state = 330; break; // &Bf -> &Bfr - case 331: state = 332; break; // &bf -> &bfr - case 338: state = 339; break; // &bigci -> &bigcir - case 362: state = 363; break; // &bigsta -> &bigstar - case 364: state = 365; break; // &bigt -> &bigtr - case 392: state = 393; break; // &bka -> &bkar - case 410: state = 411; break; // &blacksqua -> &blacksquar - case 413: state = 414; break; // &blackt -> &blacktr - case 420: state = 429; break; // &blacktriangle -> &blacktriangler - case 474: state = 481; break; // &boxD -> &boxDr - case 477: state = 483; break; // &boxd -> &boxdr - case 508: state = 515; break; // &boxU -> &boxUr - case 511: state = 517; break; // &boxu -> &boxur - case 518: state = 529; break; // &boxV -> &boxVr - case 519: state = 531; break; // &boxv -> &boxvr - case 532: state = 533; break; // &bp -> &bpr - case 547: state = 548; break; // &brvba -> ¦ - case 550: state = 551; break; // &Bsc -> &Bscr - case 553: state = 554; break; // &bsc -> &bscr - case 583: state = 965; break; // &C -> &Cr - case 589: state = 961; break; // &c -> &cr - case 590: state = 631; break; // &ca -> &car - case 600: state = 601; break; // &capb -> &capbr - case 621: state = 622; break; // &CapitalDiffe -> &CapitalDiffer - case 642: state = 650; break; // &cca -> &ccar - case 646: state = 647; break; // &Cca -> &Ccar - case 661: state = 662; break; // &Cci -> &Ccir - case 664: state = 665; break; // &cci -> &ccir - case 702: state = 703; break; // &Cente -> &Center - case 707: state = 708; break; // ¢e -> ¢er - case 712: state = 713; break; // &Cf -> &Cfr - case 714: state = 715; break; // &cf -> &cfr - case 726: state = 727; break; // &checkma -> &checkmar - case 732: state = 733; break; // &ci -> &cir - case 739: state = 740; break; // &circlea -> &circlear - case 740: state = 741; break; // &circlear -> &circlearr - case 743: state = 748; break; // &circlearrow -> &circlearrowr - case 758: state = 759; break; // &circledci -> &circledcir - case 765: state = 766; break; // &Ci -> &Cir - case 801: state = 802; break; // &cirsci -> &cirscir - case 816: state = 817; break; // &ClockwiseContou -> &ClockwiseContour - case 822: state = 823; break; // &ClockwiseContourInteg -> &ClockwiseContourIntegr - case 829: state = 830; break; // &CloseCu -> &CloseCur - case 889: state = 890; break; // &Cong -> &Congr - case 903: state = 904; break; // &Contou -> &Contour - case 909: state = 910; break; // &ContourInteg -> &ContourIntegr - case 913: state = 920; break; // &Cop -> &Copr - case 915: state = 917; break; // &cop -> &copr - case 930: state = 931; break; // ©s -> ©sr - case 935: state = 936; break; // &Counte -> &Counter - case 951: state = 952; break; // &CounterClockwiseContou -> &CounterClockwiseContour - case 957: state = 958; break; // &CounterClockwiseContourInteg -> &CounterClockwiseContourIntegr - case 962: state = 963; break; // &cra -> &crar - case 963: state = 964; break; // &crar -> &crarr - case 973: state = 974; break; // &Csc -> &Cscr - case 976: state = 977; break; // &csc -> &cscr - case 987: state = 1026; break; // &cu -> &cur - case 989: state = 990; break; // &cuda -> &cudar - case 990: state = 991; break; // &cudar -> &cudarr - case 991: state = 993; break; // &cudarr -> &cudarrr - case 995: state = 996; break; // &cuep -> &cuepr - case 1000: state = 1001; break; // &cula -> &cular - case 1001: state = 1002; break; // &cular -> &cularr - case 1007: state = 1008; break; // &cupb -> &cupbr - case 1023: state = 1024; break; // &cupo -> &cupor - case 1026: state = 1051; break; // &cur -> &curr - case 1027: state = 1028; break; // &cura -> &curar - case 1028: state = 1029; break; // &curar -> &curarr - case 1035: state = 1036; break; // &curlyeqp -> &curlyeqpr - case 1056: state = 1057; break; // &curvea -> &curvear - case 1057: state = 1058; break; // &curvear -> &curvearr - case 1060: state = 1065; break; // &curvearrow -> &curvearrowr - case 1092: state = 1107; break; // &Da -> &Dar - case 1095: state = 1096; break; // &Dagge -> &Dagger - case 1097: state = 1590; break; // &d -> &dr - case 1098: state = 1112; break; // &da -> &dar - case 1101: state = 1102; break; // &dagge -> &dagger - case 1107: state = 1108; break; // &Dar -> &Darr - case 1109: state = 1110; break; // &dA -> &dAr - case 1110: state = 1111; break; // &dAr -> &dArr - case 1112: state = 1113; break; // &dar -> &darr - case 1122: state = 1123; break; // &dbka -> &dbkar - case 1130: state = 1131; break; // &Dca -> &Dcar - case 1135: state = 1136; break; // &dca -> &dcar - case 1143: state = 1148; break; // &dda -> &ddar - case 1146: state = 1147; break; // &ddagge -> &ddagger - case 1148: state = 1149; break; // &ddar -> &ddarr - case 1151: state = 1152; break; // &DDot -> &DDotr - case 1175: state = 1182; break; // &df -> &dfr - case 1180: state = 1181; break; // &Df -> &Dfr - case 1184: state = 1185; break; // &dHa -> &dHar - case 1187: state = 1188; break; // &dha -> &dhar - case 1188: state = 1190; break; // &dhar -> &dharr - case 1193: state = 1194; break; // &Diac -> &Diacr - case 1218: state = 1219; break; // &DiacriticalG -> &DiacriticalGr - case 1246: state = 1247; break; // &Diffe -> &Differ - case 1284: state = 1288; break; // &dlc -> &dlcr - case 1285: state = 1286; break; // &dlco -> &dlcor - case 1294: state = 1295; break; // &dolla -> &dollar - case 1328: state = 1329; break; // &dotsqua -> &dotsquar - case 1336: state = 1337; break; // &doubleba -> &doublebar - case 1352: state = 1353; break; // &DoubleContou -> &DoubleContour - case 1358: state = 1359; break; // &DoubleContourInteg -> &DoubleContourIntegr - case 1367: state = 1368; break; // &DoubleDownA -> &DoubleDownAr - case 1368: state = 1369; break; // &DoubleDownAr -> &DoubleDownArr - case 1376: state = 1377; break; // &DoubleLeftA -> &DoubleLeftAr - case 1377: state = 1378; break; // &DoubleLeftAr -> &DoubleLeftArr - case 1386: state = 1387; break; // &DoubleLeftRightA -> &DoubleLeftRightAr - case 1387: state = 1388; break; // &DoubleLeftRightAr -> &DoubleLeftRightArr - case 1401: state = 1402; break; // &DoubleLongLeftA -> &DoubleLongLeftAr - case 1402: state = 1403; break; // &DoubleLongLeftAr -> &DoubleLongLeftArr - case 1411: state = 1412; break; // &DoubleLongLeftRightA -> &DoubleLongLeftRightAr - case 1412: state = 1413; break; // &DoubleLongLeftRightAr -> &DoubleLongLeftRightArr - case 1421: state = 1422; break; // &DoubleLongRightA -> &DoubleLongRightAr - case 1422: state = 1423; break; // &DoubleLongRightAr -> &DoubleLongRightArr - case 1431: state = 1432; break; // &DoubleRightA -> &DoubleRightAr - case 1432: state = 1433; break; // &DoubleRightAr -> &DoubleRightArr - case 1441: state = 1442; break; // &DoubleUpA -> &DoubleUpAr - case 1442: state = 1443; break; // &DoubleUpAr -> &DoubleUpArr - case 1450: state = 1451; break; // &DoubleUpDownA -> &DoubleUpDownAr - case 1451: state = 1452; break; // &DoubleUpDownAr -> &DoubleUpDownArr - case 1456: state = 1457; break; // &DoubleVe -> &DoubleVer - case 1464: state = 1465; break; // &DoubleVerticalBa -> &DoubleVerticalBar - case 1468: state = 1469; break; // &DownA -> &DownAr - case 1469: state = 1470; break; // &DownAr -> &DownArr - case 1473: state = 1474; break; // &Downa -> &Downar - case 1474: state = 1475; break; // &Downar -> &Downarr - case 1480: state = 1481; break; // &downa -> &downar - case 1481: state = 1482; break; // &downar -> &downarr - case 1486: state = 1487; break; // &DownArrowBa -> &DownArrowBar - case 1490: state = 1491; break; // &DownArrowUpA -> &DownArrowUpAr - case 1491: state = 1492; break; // &DownArrowUpAr -> &DownArrowUpArr - case 1495: state = 1496; break; // &DownB -> &DownBr - case 1504: state = 1505; break; // &downdowna -> &downdownar - case 1505: state = 1506; break; // &downdownar -> &downdownarr - case 1511: state = 1512; break; // &downha -> &downhar - case 1516: state = 1521; break; // &downharpoon -> &downharpoonr - case 1539: state = 1540; break; // &DownLeftRightVecto -> &DownLeftRightVector - case 1548: state = 1549; break; // &DownLeftTeeVecto -> &DownLeftTeeVector - case 1554: state = 1555; break; // &DownLeftVecto -> &DownLeftVector - case 1557: state = 1558; break; // &DownLeftVectorBa -> &DownLeftVectorBar - case 1571: state = 1572; break; // &DownRightTeeVecto -> &DownRightTeeVector - case 1577: state = 1578; break; // &DownRightVecto -> &DownRightVector - case 1580: state = 1581; break; // &DownRightVectorBa -> &DownRightVectorBar - case 1585: state = 1586; break; // &DownTeeA -> &DownTeeAr - case 1586: state = 1587; break; // &DownTeeAr -> &DownTeeArr - case 1593: state = 1594; break; // &drbka -> &drbkar - case 1597: state = 1601; break; // &drc -> &drcr - case 1598: state = 1599; break; // &drco -> &drcor - case 1605: state = 1606; break; // &Dsc -> &Dscr - case 1608: state = 1609; break; // &dsc -> &dscr - case 1616: state = 1617; break; // &Dst -> &Dstr - case 1620: state = 1621; break; // &dst -> &dstr - case 1624: state = 1628; break; // &dt -> &dtr - case 1632: state = 1633; break; // &dua -> &duar - case 1633: state = 1634; break; // &duar -> &duarr - case 1636: state = 1637; break; // &duha -> &duhar - case 1651: state = 1652; break; // &dzig -> &dzigr - case 1653: state = 1654; break; // &dzigra -> &dzigrar - case 1654: state = 1655; break; // &dzigrar -> &dzigrarr - case 1662: state = 1890; break; // &e -> &er - case 1670: state = 1671; break; // &easte -> &easter - case 1673: state = 1674; break; // &Eca -> &Ecar - case 1678: state = 1679; break; // &eca -> &ecar - case 1682: state = 1683; break; // &eci -> &ecir - case 1684: state = 1685; break; // &Eci -> &Ecir - case 1707: state = 1713; break; // &ef -> &efr - case 1711: state = 1712; break; // &Ef -> &Efr - case 1714: state = 1720; break; // &eg -> &egr - case 1715: state = 1716; break; // &Eg -> &Egr - case 1738: state = 1739; break; // &elinte -> &elinter - case 1748: state = 1749; break; // &Emac -> &Emacr - case 1752: state = 1753; break; // &emac -> &emacr - case 1771: state = 1772; break; // &EmptySmallSqua -> &EmptySmallSquar - case 1776: state = 1777; break; // &EmptyVe -> &EmptyVer - case 1787: state = 1788; break; // &EmptyVerySmallSqua -> &EmptyVerySmallSquar - case 1814: state = 1815; break; // &epa -> &epar - case 1835: state = 1836; break; // &eqci -> &eqcir - case 1850: state = 1851; break; // &eqslantgt -> &eqslantgtr - case 1875: state = 1876; break; // &Equilib -> &Equilibr - case 1886: state = 1887; break; // &eqvpa -> &eqvpar - case 1891: state = 1892; break; // &era -> &erar - case 1892: state = 1893; break; // &erar -> &erarr - case 1898: state = 1899; break; // &Esc -> &Escr - case 1901: state = 1902; break; // &esc -> &escr - case 1920: state = 1923; break; // &eu -> &eur - case 1964: state = 2083; break; // &f -> &fr - case 1987: state = 2000; break; // &ff -> &ffr - case 1998: state = 1999; break; // &Ff -> &Ffr - case 2018: state = 2019; break; // &FilledSmallSqua -> &FilledSmallSquar - case 2022: state = 2023; break; // &FilledVe -> &FilledVer - case 2033: state = 2034; break; // &FilledVerySmallSqua -> &FilledVerySmallSquar - case 2052: state = 2058; break; // &Fo -> &For - case 2055: state = 2062; break; // &fo -> &for - case 2068: state = 2069; break; // &Fou -> &Four - case 2071: state = 2072; break; // &Fourie -> &Fourier - case 2073: state = 2074; break; // &Fouriert -> &Fouriertr - case 2077: state = 2078; break; // &fpa -> &fpar - case 2113: state = 2114; break; // &Fsc -> &Fscr - case 2116: state = 2117; break; // &fsc -> &fscr - case 2118: state = 2228; break; // &g -> &gr - case 2124: state = 2232; break; // &G -> &Gr - case 2135: state = 2136; break; // &Gb -> &Gbr - case 2140: state = 2141; break; // &gb -> &gbr - case 2150: state = 2151; break; // &Gci -> &Gcir - case 2154: state = 2155; break; // &gci -> &gcir - case 2187: state = 2188; break; // &Gf -> &Gfr - case 2189: state = 2190; break; // &gf -> &gfr - case 2211: state = 2212; break; // &gnapp -> &gnappr - case 2236: state = 2237; break; // &Greate -> &Greater - case 2256: state = 2257; break; // &GreaterG -> &GreaterGr - case 2261: state = 2262; break; // &GreaterGreate -> &GreaterGreater - case 2283: state = 2284; break; // &Gsc -> &Gscr - case 2286: state = 2287; break; // &gsc -> &gscr - case 2294: state = 2311; break; // > -> >r - case 2297: state = 2298; break; // >ci -> >cir - case 2304: state = 2305; break; // >lPa -> >lPar - case 2312: state = 2318; break; // >ra -> >rar - case 2314: state = 2315; break; // >rapp -> >rappr - case 2318: state = 2319; break; // >rar -> >rarr - case 2342: state = 2343; break; // &gve -> &gver - case 2357: state = 2373; break; // &ha -> &har - case 2358: state = 2359; break; // &hai -> &hair - case 2373: state = 2380; break; // &har -> &harr - case 2377: state = 2378; break; // &hA -> &hAr - case 2378: state = 2379; break; // &hAr -> &hArr - case 2382: state = 2383; break; // &harrci -> &harrcir - case 2387: state = 2388; break; // &hba -> &hbar - case 2390: state = 2391; break; // &Hci -> &Hcir - case 2394: state = 2395; break; // &hci -> &hcir - case 2397: state = 2409; break; // &he -> &her - case 2398: state = 2399; break; // &hea -> &hear - case 2413: state = 2414; break; // &Hf -> &Hfr - case 2415: state = 2416; break; // &hf -> &hfr - case 2420: state = 2421; break; // &Hilbe -> &Hilber - case 2431: state = 2432; break; // &hksea -> &hksear - case 2436: state = 2437; break; // &hkswa -> &hkswar - case 2440: state = 2474; break; // &ho -> &hor - case 2441: state = 2442; break; // &hoa -> &hoar - case 2442: state = 2443; break; // &hoar -> &hoarr - case 2449: state = 2459; break; // &hook -> &hookr - case 2454: state = 2455; break; // &hooklefta -> &hookleftar - case 2455: state = 2456; break; // &hookleftar -> &hookleftarr - case 2464: state = 2465; break; // &hookrighta -> &hookrightar - case 2465: state = 2466; break; // &hookrightar -> &hookrightarr - case 2469: state = 2478; break; // &Ho -> &Hor - case 2476: state = 2477; break; // &horba -> &horbar - case 2491: state = 2492; break; // &Hsc -> &Hscr - case 2494: state = 2495; break; // &hsc -> &hscr - case 2500: state = 2501; break; // &Hst -> &Hstr - case 2504: state = 2505; break; // &hst -> &hstr - case 2547: state = 2548; break; // &Ici -> &Icir - case 2550: state = 2551; break; // &ici -> &icir - case 2567: state = 2571; break; // &if -> &ifr - case 2569: state = 2570; break; // &If -> &Ifr - case 2572: state = 2573; break; // &Ig -> &Igr - case 2577: state = 2578; break; // &ig -> &igr - case 2606: state = 2607; break; // &Imac -> &Imacr - case 2610: state = 2611; break; // &imac -> &imacr - case 2617: state = 2618; break; // &Imagina -> &Imaginar - case 2626: state = 2627; break; // &imagpa -> &imagpar - case 2643: state = 2644; break; // &inca -> &incar - case 2662: state = 2672; break; // &inte -> &inter - case 2664: state = 2665; break; // &intege -> &integer - case 2667: state = 2676; break; // &Inte -> &Inter - case 2668: state = 2669; break; // &Integ -> &Integr - case 2685: state = 2686; break; // &intla -> &intlar - case 2689: state = 2690; break; // &intp -> &intpr - case 2731: state = 2732; break; // &ip -> &ipr - case 2741: state = 2742; break; // &Isc -> &Iscr - case 2744: state = 2745; break; // &isc -> &iscr - case 2779: state = 2780; break; // &Jci -> &Jcir - case 2784: state = 2785; break; // &jci -> &jcir - case 2789: state = 2790; break; // &Jf -> &Jfr - case 2791: state = 2792; break; // &jf -> &jfr - case 2804: state = 2805; break; // &Jsc -> &Jscr - case 2807: state = 2808; break; // &jsc -> &jscr - case 2809: state = 2810; break; // &Jse -> &Jser - case 2813: state = 2814; break; // &jse -> &jser - case 2848: state = 2849; break; // &Kf -> &Kfr - case 2850: state = 2851; break; // &kf -> &kfr - case 2852: state = 2853; break; // &kg -> &kgr - case 2876: state = 2877; break; // &Ksc -> &Kscr - case 2879: state = 2880; break; // &ksc -> &kscr - case 2881: state = 3643; break; // &l -> &lr - case 2882: state = 2936; break; // &lA -> &lAr - case 2883: state = 2884; break; // &lAa -> &lAar - case 2884: state = 2885; break; // &lAar -> &lAarr - case 2887: state = 2934; break; // &La -> &Lar - case 2892: state = 2938; break; // &la -> &lar - case 2903: state = 2904; break; // &lag -> &lagr - case 2928: state = 2929; break; // &Laplacet -> &Laplacetr - case 2934: state = 2935; break; // &Lar -> &Larr - case 2936: state = 2937; break; // &lAr -> &lArr - case 2938: state = 2939; break; // &lar -> &larr - case 2967: state = 2968; break; // &lBa -> &lBar - case 2968: state = 2969; break; // &lBar -> &lBarr - case 2970: state = 2977; break; // &lb -> &lbr - case 2971: state = 2972; break; // &lba -> &lbar - case 2972: state = 2973; break; // &lbar -> &lbarr - case 2974: state = 2975; break; // &lbb -> &lbbr - case 2989: state = 2990; break; // &Lca -> &Lcar - case 2994: state = 2995; break; // &lca -> &lcar - case 3012: state = 3019; break; // &ld -> &ldr - case 3017: state = 3018; break; // &ldquo -> &ldquor - case 3022: state = 3023; break; // &ldrdha -> &ldrdhar - case 3027: state = 3028; break; // &ldrusha -> &ldrushar - case 3035: state = 3159; break; // &Left -> &Leftr - case 3036: state = 3048; break; // &LeftA -> &LeftAr - case 3041: state = 3042; break; // &LeftAngleB -> &LeftAngleBr - case 3048: state = 3049; break; // &LeftAr -> &LeftArr - case 3052: state = 3053; break; // &Lefta -> &Leftar - case 3053: state = 3054; break; // &Leftar -> &Leftarr - case 3058: state = 3169; break; // &left -> &leftr - case 3059: state = 3060; break; // &lefta -> &leftar - case 3060: state = 3061; break; // &leftar -> &leftarr - case 3065: state = 3066; break; // &LeftArrowBa -> &LeftArrowBar - case 3072: state = 3073; break; // &LeftArrowRightA -> &LeftArrowRightAr - case 3073: state = 3074; break; // &LeftArrowRightAr -> &LeftArrowRightArr - case 3094: state = 3095; break; // &LeftDoubleB -> &LeftDoubleBr - case 3110: state = 3111; break; // &LeftDownTeeVecto -> &LeftDownTeeVector - case 3116: state = 3117; break; // &LeftDownVecto -> &LeftDownVector - case 3119: state = 3120; break; // &LeftDownVectorBa -> &LeftDownVectorBar - case 3124: state = 3125; break; // &LeftFloo -> &LeftFloor - case 3127: state = 3128; break; // &leftha -> &lefthar - case 3143: state = 3144; break; // &leftlefta -> &leftleftar - case 3144: state = 3145; break; // &leftleftar -> &leftleftarr - case 3154: state = 3155; break; // &LeftRightA -> &LeftRightAr - case 3155: state = 3156; break; // &LeftRightAr -> &LeftRightArr - case 3164: state = 3165; break; // &Leftrighta -> &Leftrightar - case 3165: state = 3166; break; // &Leftrightar -> &Leftrightarr - case 3174: state = 3175; break; // &leftrighta -> &leftrightar - case 3175: state = 3176; break; // &leftrightar -> &leftrightarr - case 3181: state = 3182; break; // &leftrightha -> &leftrighthar - case 3193: state = 3194; break; // &leftrightsquiga -> &leftrightsquigar - case 3194: state = 3195; break; // &leftrightsquigar -> &leftrightsquigarr - case 3202: state = 3203; break; // &LeftRightVecto -> &LeftRightVector - case 3204: state = 3228; break; // &LeftT -> &LeftTr - case 3207: state = 3208; break; // &LeftTeeA -> &LeftTeeAr - case 3208: state = 3209; break; // &LeftTeeAr -> &LeftTeeArr - case 3216: state = 3217; break; // &LeftTeeVecto -> &LeftTeeVector - case 3219: state = 3220; break; // &leftth -> &leftthr - case 3236: state = 3237; break; // &LeftTriangleBa -> &LeftTriangleBar - case 3253: state = 3254; break; // &LeftUpDownVecto -> &LeftUpDownVector - case 3262: state = 3263; break; // &LeftUpTeeVecto -> &LeftUpTeeVector - case 3268: state = 3269; break; // &LeftUpVecto -> &LeftUpVector - case 3271: state = 3272; break; // &LeftUpVectorBa -> &LeftUpVectorBar - case 3277: state = 3278; break; // &LeftVecto -> &LeftVector - case 3280: state = 3281; break; // &LeftVectorBa -> &LeftVectorBar - case 3297: state = 3298; break; // &lesdoto -> &lesdotor - case 3305: state = 3306; break; // &lessapp -> &lessappr - case 3315: state = 3316; break; // &lesseqgt -> &lesseqgtr - case 3319: state = 3320; break; // &lesseqqgt -> &lesseqqgtr - case 3328: state = 3329; break; // &LessEqualG -> &LessEqualGr - case 3333: state = 3334; break; // &LessEqualGreate -> &LessEqualGreater - case 3344: state = 3345; break; // &LessG -> &LessGr - case 3349: state = 3350; break; // &LessGreate -> &LessGreater - case 3352: state = 3353; break; // &lessgt -> &lessgtr - case 3376: state = 3387; break; // &lf -> &lfr - case 3383: state = 3384; break; // &lfloo -> &lfloor - case 3385: state = 3386; break; // &Lf -> &Lfr - case 3391: state = 3392; break; // &lHa -> &lHar - case 3394: state = 3395; break; // &lha -> &lhar - case 3410: state = 3411; break; // &lla -> &llar - case 3411: state = 3412; break; // &llar -> &llarr - case 3414: state = 3415; break; // &llco -> &llcor - case 3417: state = 3418; break; // &llcorne -> &llcorner - case 3422: state = 3423; break; // &Llefta -> &Lleftar - case 3423: state = 3424; break; // &Lleftar -> &Lleftarr - case 3428: state = 3429; break; // &llha -> &llhar - case 3431: state = 3432; break; // &llt -> &lltr - case 3455: state = 3456; break; // &lnapp -> &lnappr - case 3467: state = 3470; break; // &loa -> &loar - case 3470: state = 3471; break; // &loar -> &loarr - case 3472: state = 3473; break; // &lob -> &lobr - case 3477: state = 3553; break; // &Long -> &Longr - case 3482: state = 3483; break; // &LongLeftA -> &LongLeftAr - case 3483: state = 3484; break; // &LongLeftAr -> &LongLeftArr - case 3490: state = 3517; break; // &Longleft -> &Longleftr - case 3491: state = 3492; break; // &Longlefta -> &Longleftar - case 3492: state = 3493; break; // &Longleftar -> &Longleftarr - case 3497: state = 3563; break; // &long -> &longr - case 3501: state = 3527; break; // &longleft -> &longleftr - case 3502: state = 3503; break; // &longlefta -> &longleftar - case 3503: state = 3504; break; // &longleftar -> &longleftarr - case 3512: state = 3513; break; // &LongLeftRightA -> &LongLeftRightAr - case 3513: state = 3514; break; // &LongLeftRightAr -> &LongLeftRightArr - case 3522: state = 3523; break; // &Longleftrighta -> &Longleftrightar - case 3523: state = 3524; break; // &Longleftrightar -> &Longleftrightarr - case 3532: state = 3533; break; // &longleftrighta -> &longleftrightar - case 3533: state = 3534; break; // &longleftrightar -> &longleftrightarr - case 3548: state = 3549; break; // &LongRightA -> &LongRightAr - case 3549: state = 3550; break; // &LongRightAr -> &LongRightArr - case 3558: state = 3559; break; // &Longrighta -> &Longrightar - case 3559: state = 3560; break; // &Longrightar -> &Longrightarr - case 3568: state = 3569; break; // &longrighta -> &longrightar - case 3569: state = 3570; break; // &longrightar -> &longrightarr - case 3575: state = 3576; break; // &loopa -> &loopar - case 3576: state = 3577; break; // &loopar -> &looparr - case 3579: state = 3584; break; // &looparrow -> &looparrowr - case 3590: state = 3591; break; // &lopa -> &lopar - case 3608: state = 3609; break; // &lowba -> &lowbar - case 3611: state = 3612; break; // &Lowe -> &Lower - case 3617: state = 3618; break; // &LowerLeftA -> &LowerLeftAr - case 3618: state = 3619; break; // &LowerLeftAr -> &LowerLeftArr - case 3627: state = 3628; break; // &LowerRightA -> &LowerRightAr - case 3628: state = 3629; break; // &LowerRightAr -> &LowerRightArr - case 3639: state = 3640; break; // &lpa -> &lpar - case 3644: state = 3645; break; // &lra -> &lrar - case 3645: state = 3646; break; // &lrar -> &lrarr - case 3648: state = 3649; break; // &lrco -> &lrcor - case 3651: state = 3652; break; // &lrcorne -> &lrcorner - case 3654: state = 3655; break; // &lrha -> &lrhar - case 3658: state = 3659; break; // &lrt -> &lrtr - case 3667: state = 3668; break; // &Lsc -> &Lscr - case 3669: state = 3670; break; // &lsc -> &lscr - case 3680: state = 3681; break; // &lsquo -> &lsquor - case 3682: state = 3683; break; // &Lst -> &Lstr - case 3686: state = 3687; break; // &lst -> &lstr - case 3692: state = 3717; break; // < -> <r - case 3695: state = 3696; break; // <ci -> <cir - case 3700: state = 3701; break; // <h -> <hr - case 3709: state = 3710; break; // <la -> <lar - case 3710: state = 3711; break; // <lar -> <larr - case 3722: state = 3723; break; // <rPa -> <rPar - case 3724: state = 3725; break; // &lu -> &lur - case 3729: state = 3730; break; // &lurdsha -> &lurdshar - case 3733: state = 3734; break; // &luruha -> &luruhar - case 3736: state = 3737; break; // &lve -> &lver - case 3746: state = 3772; break; // &ma -> &mar - case 3747: state = 3748; break; // &mac -> ¯ - case 3774: state = 3775; break; // &marke -> &marker - case 3795: state = 3796; break; // &measu -> &measur - case 3818: state = 3819; break; // &Mellint -> &Mellintr - case 3821: state = 3822; break; // &Mf -> &Mfr - case 3823: state = 3824; break; // &mf -> &mfr - case 3828: state = 3829; break; // &mic -> &micr - case 3836: state = 3837; break; // &midci -> &midcir - case 3858: state = 3859; break; // &mld -> &mldr - case 3877: state = 3878; break; // &Msc -> &Mscr - case 3880: state = 3881; break; // &msc -> &mscr - case 3897: state = 4621; break; // &n -> &nr - case 3920: state = 3921; break; // &napp -> &nappr - case 3925: state = 3926; break; // &natu -> &natur - case 3938: state = 3945; break; // &nca -> &ncar - case 3941: state = 3942; break; // &Nca -> &Ncar - case 3971: state = 3972; break; // &nea -> &near - case 3972: state = 3978; break; // &near -> &nearr - case 3975: state = 3976; break; // &neA -> &neAr - case 3976: state = 3977; break; // &neAr -> &neArr - case 4019: state = 4020; break; // &NegativeVe -> &NegativeVer - case 4037: state = 4038; break; // &nesea -> &nesear - case 4045: state = 4046; break; // &NestedG -> &NestedGr - case 4050: state = 4051; break; // &NestedGreate -> &NestedGreater - case 4052: state = 4053; break; // &NestedGreaterG -> &NestedGreaterGr - case 4057: state = 4058; break; // &NestedGreaterGreate -> &NestedGreaterGreater - case 4077: state = 4078; break; // &Nf -> &Nfr - case 4079: state = 4080; break; // &nf -> &nfr - case 4098: state = 4099; break; // &ngt -> &ngtr - case 4102: state = 4103; break; // &nhA -> &nhAr - case 4103: state = 4104; break; // &nhAr -> &nhArr - case 4105: state = 4106; break; // &nha -> &nhar - case 4106: state = 4107; break; // &nhar -> &nharr - case 4109: state = 4110; break; // &nhpa -> &nhpar - case 4122: state = 4123; break; // &nlA -> &nlAr - case 4123: state = 4124; break; // &nlAr -> &nlArr - case 4125: state = 4126; break; // &nla -> &nlar - case 4126: state = 4127; break; // &nlar -> &nlarr - case 4128: state = 4129; break; // &nld -> &nldr - case 4135: state = 4148; break; // &nLeft -> &nLeftr - case 4136: state = 4137; break; // &nLefta -> &nLeftar - case 4137: state = 4138; break; // &nLeftar -> &nLeftarr - case 4142: state = 4158; break; // &nleft -> &nleftr - case 4143: state = 4144; break; // &nlefta -> &nleftar - case 4144: state = 4145; break; // &nleftar -> &nleftarr - case 4153: state = 4154; break; // &nLeftrighta -> &nLeftrightar - case 4154: state = 4155; break; // &nLeftrightar -> &nLeftrightarr - case 4163: state = 4164; break; // &nleftrighta -> &nleftrightar - case 4164: state = 4165; break; // &nleftrightar -> &nleftrightarr - case 4182: state = 4183; break; // &nlt -> &nltr - case 4191: state = 4192; break; // &NoB -> &NoBr - case 4197: state = 4198; break; // &NonB -> &NonBr - case 4220: state = 4221; break; // &NotCong -> &NotCongr - case 4238: state = 4239; break; // &NotDoubleVe -> &NotDoubleVer - case 4246: state = 4247; break; // &NotDoubleVerticalBa -> &NotDoubleVerticalBar - case 4269: state = 4270; break; // &NotG -> &NotGr - case 4274: state = 4275; break; // &NotGreate -> &NotGreater - case 4290: state = 4291; break; // &NotGreaterG -> &NotGreaterGr - case 4295: state = 4296; break; // &NotGreaterGreate -> &NotGreaterGreater - case 4347: state = 4348; break; // &NotLeftT -> &NotLeftTr - case 4356: state = 4357; break; // &NotLeftTriangleBa -> &NotLeftTriangleBar - case 4370: state = 4371; break; // &NotLessG -> &NotLessGr - case 4375: state = 4376; break; // &NotLessGreate -> &NotLessGreater - case 4402: state = 4403; break; // &NotNestedG -> &NotNestedGr - case 4407: state = 4408; break; // &NotNestedGreate -> &NotNestedGreater - case 4409: state = 4410; break; // &NotNestedGreaterG -> &NotNestedGreaterGr - case 4414: state = 4415; break; // &NotNestedGreaterGreate -> &NotNestedGreaterGreater - case 4430: state = 4431; break; // &NotP -> &NotPr - case 4456: state = 4457; break; // &NotReve -> &NotRever - case 4471: state = 4472; break; // &NotRightT -> &NotRightTr - case 4480: state = 4481; break; // &NotRightTriangleBa -> &NotRightTriangleBar - case 4490: state = 4491; break; // &NotSqua -> &NotSquar - case 4505: state = 4506; break; // &NotSquareSupe -> &NotSquareSuper - case 4552: state = 4553; break; // &NotSupe -> &NotSuper - case 4587: state = 4588; break; // &NotVe -> &NotVer - case 4595: state = 4596; break; // &NotVerticalBa -> &NotVerticalBar - case 4597: state = 4613; break; // &np -> &npr - case 4598: state = 4599; break; // &npa -> &npar - case 4622: state = 4623; break; // &nrA -> &nrAr - case 4623: state = 4624; break; // &nrAr -> &nrArr - case 4625: state = 4626; break; // &nra -> &nrar - case 4626: state = 4627; break; // &nrar -> &nrarr - case 4635: state = 4636; break; // &nRighta -> &nRightar - case 4636: state = 4637; break; // &nRightar -> &nRightarr - case 4644: state = 4645; break; // &nrighta -> &nrightar - case 4645: state = 4646; break; // &nrightar -> &nrightarr - case 4649: state = 4650; break; // &nrt -> &nrtr - case 4654: state = 4662; break; // &nsc -> &nscr - case 4660: state = 4661; break; // &Nsc -> &Nscr - case 4664: state = 4665; break; // &nsho -> &nshor - case 4671: state = 4672; break; // &nshortpa -> &nshortpar - case 4686: state = 4687; break; // &nspa -> &nspar - case 4718: state = 4732; break; // &nt -> &ntr - case 4738: state = 4745; break; // &ntriangle -> &ntriangler - case 4755: state = 4756; break; // &nume -> &numer - case 4760: state = 4801; break; // &nv -> &nvr - case 4784: state = 4785; break; // &nvHa -> &nvHar - case 4785: state = 4786; break; // &nvHar -> &nvHarr - case 4793: state = 4794; break; // &nvlA -> &nvlAr - case 4794: state = 4795; break; // &nvlAr -> &nvlArr - case 4797: state = 4798; break; // &nvlt -> &nvltr - case 4802: state = 4803; break; // &nvrA -> &nvrAr - case 4803: state = 4804; break; // &nvrAr -> &nvrArr - case 4805: state = 4806; break; // &nvrt -> &nvrtr - case 4813: state = 4814; break; // &nwa -> &nwar - case 4814: state = 4820; break; // &nwar -> &nwarr - case 4817: state = 4818; break; // &nwA -> &nwAr - case 4818: state = 4819; break; // &nwAr -> &nwArr - case 4825: state = 4826; break; // &nwnea -> &nwnear - case 4827: state = 4990; break; // &O -> &Or - case 4833: state = 4991; break; // &o -> &or - case 4842: state = 4843; break; // &oci -> &ocir - case 4845: state = 4846; break; // &Oci -> &Ocir - case 4880: state = 4886; break; // &of -> &ofr - case 4882: state = 4883; break; // &ofci -> &ofcir - case 4884: state = 4885; break; // &Of -> &Ofr - case 4887: state = 4895; break; // &og -> &ogr - case 4890: state = 4891; break; // &Og -> &Ogr - case 4902: state = 4903; break; // &ohba -> &ohbar - case 4909: state = 4910; break; // &ola -> &olar - case 4910: state = 4911; break; // &olar -> &olarr - case 4912: state = 4915; break; // &olc -> &olcr - case 4913: state = 4914; break; // &olci -> &olcir - case 4925: state = 4926; break; // &Omac -> &Omacr - case 4929: state = 4930; break; // &omac -> &omacr - case 4938: state = 4939; break; // &Omic -> &Omicr - case 4943: state = 4944; break; // &omic -> &omicr - case 4958: state = 4959; break; // &opa -> &opar - case 4964: state = 4965; break; // &OpenCu -> &OpenCur - case 4984: state = 4985; break; // &ope -> &oper - case 4992: state = 4993; break; // &ora -> &orar - case 4993: state = 4994; break; // &orar -> &orarr - case 4996: state = 4997; break; // &orde -> &order - case 5006: state = 5007; break; // &oro -> &oror - case 5016: state = 5017; break; // &Osc -> &Oscr - case 5019: state = 5020; break; // &osc -> &oscr - case 5057: state = 5058; break; // &ovba -> &ovbar - case 5060: state = 5061; break; // &Ove -> &Over - case 5062: state = 5065; break; // &OverB -> &OverBr - case 5063: state = 5064; break; // &OverBa -> &OverBar - case 5073: state = 5074; break; // &OverPa -> &OverPar - case 5083: state = 5216; break; // &p -> &pr - case 5084: state = 5085; break; // &pa -> &par - case 5096: state = 5215; break; // &P -> &Pr - case 5097: state = 5098; break; // &Pa -> &Par - case 5108: state = 5109; break; // &pe -> &per - case 5124: state = 5125; break; // &Pf -> &Pfr - case 5126: state = 5127; break; // &pf -> &pfr - case 5146: state = 5147; break; // &pitchfo -> &pitchfor - case 5162: state = 5163; break; // &plusaci -> &plusacir - case 5166: state = 5167; break; // &plusci -> &pluscir - case 5193: state = 5194; break; // &Poinca -> &Poincar - case 5227: state = 5228; break; // &precapp -> &precappr - case 5232: state = 5233; break; // &preccu -> &preccur - case 5269: state = 5270; break; // &precnapp -> &precnappr - case 5306: state = 5307; break; // &profala -> &profalar - case 5313: state = 5314; break; // &profsu -> &profsur - case 5318: state = 5319; break; // &Propo -> &Propor - case 5331: state = 5332; break; // &pru -> &prur - case 5336: state = 5337; break; // &Psc -> &Pscr - case 5339: state = 5340; break; // &psc -> &pscr - case 5349: state = 5350; break; // &Qf -> &Qfr - case 5352: state = 5353; break; // &qf -> &qfr - case 5363: state = 5364; break; // &qp -> &qpr - case 5369: state = 5370; break; // &Qsc -> &Qscr - case 5372: state = 5373; break; // &qsc -> &qscr - case 5377: state = 5378; break; // &quate -> &quater - case 5397: state = 5920; break; // &r -> &rr - case 5398: state = 5436; break; // &rA -> &rAr - case 5399: state = 5400; break; // &rAa -> &rAar - case 5400: state = 5401; break; // &rAar -> &rAarr - case 5402: state = 5438; break; // &ra -> &rar - case 5405: state = 5924; break; // &R -> &Rr - case 5406: state = 5434; break; // &Ra -> &Rar - case 5434: state = 5435; break; // &Rar -> &Rarr - case 5436: state = 5437; break; // &rAr -> &rArr - case 5438: state = 5439; break; // &rar -> &rarr - case 5477: state = 5478; break; // &RBa -> &RBar - case 5478: state = 5479; break; // &RBar -> &RBarr - case 5481: state = 5482; break; // &rBa -> &rBar - case 5482: state = 5483; break; // &rBar -> &rBarr - case 5484: state = 5491; break; // &rb -> &rbr - case 5485: state = 5486; break; // &rba -> &rbar - case 5486: state = 5487; break; // &rbar -> &rbarr - case 5488: state = 5489; break; // &rbb -> &rbbr - case 5503: state = 5504; break; // &Rca -> &Rcar - case 5508: state = 5509; break; // &rca -> &rcar - case 5532: state = 5533; break; // &rdldha -> &rdldhar - case 5536: state = 5537; break; // &rdquo -> &rdquor - case 5548: state = 5549; break; // &realpa -> &realpar - case 5558: state = 5559; break; // &Reve -> &Rever - case 5574: state = 5575; break; // &ReverseEquilib -> &ReverseEquilibr - case 5587: state = 5588; break; // &ReverseUpEquilib -> &ReverseUpEquilibr - case 5592: state = 5603; break; // &rf -> &rfr - case 5599: state = 5600; break; // &rfloo -> &rfloor - case 5601: state = 5602; break; // &Rf -> &Rfr - case 5605: state = 5606; break; // &rHa -> &rHar - case 5608: state = 5609; break; // &rha -> &rhar - case 5621: state = 5633; break; // &RightA -> &RightAr - case 5626: state = 5627; break; // &RightAngleB -> &RightAngleBr - case 5633: state = 5634; break; // &RightAr -> &RightArr - case 5637: state = 5638; break; // &Righta -> &Rightar - case 5638: state = 5639; break; // &Rightar -> &Rightarr - case 5645: state = 5743; break; // &right -> &rightr - case 5646: state = 5647; break; // &righta -> &rightar - case 5647: state = 5648; break; // &rightar -> &rightarr - case 5652: state = 5653; break; // &RightArrowBa -> &RightArrowBar - case 5658: state = 5659; break; // &RightArrowLeftA -> &RightArrowLeftAr - case 5659: state = 5660; break; // &RightArrowLeftAr -> &RightArrowLeftArr - case 5680: state = 5681; break; // &RightDoubleB -> &RightDoubleBr - case 5696: state = 5697; break; // &RightDownTeeVecto -> &RightDownTeeVector - case 5702: state = 5703; break; // &RightDownVecto -> &RightDownVector - case 5705: state = 5706; break; // &RightDownVectorBa -> &RightDownVectorBar - case 5710: state = 5711; break; // &RightFloo -> &RightFloor - case 5713: state = 5714; break; // &rightha -> &righthar - case 5729: state = 5730; break; // &rightlefta -> &rightleftar - case 5730: state = 5731; break; // &rightleftar -> &rightleftarr - case 5736: state = 5737; break; // &rightleftha -> &rightlefthar - case 5748: state = 5749; break; // &rightrighta -> &rightrightar - case 5749: state = 5750; break; // &rightrightar -> &rightrightarr - case 5759: state = 5760; break; // &rightsquiga -> &rightsquigar - case 5760: state = 5761; break; // &rightsquigar -> &rightsquigarr - case 5764: state = 5788; break; // &RightT -> &RightTr - case 5767: state = 5768; break; // &RightTeeA -> &RightTeeAr - case 5768: state = 5769; break; // &RightTeeAr -> &RightTeeArr - case 5776: state = 5777; break; // &RightTeeVecto -> &RightTeeVector - case 5779: state = 5780; break; // &rightth -> &rightthr - case 5796: state = 5797; break; // &RightTriangleBa -> &RightTriangleBar - case 5813: state = 5814; break; // &RightUpDownVecto -> &RightUpDownVector - case 5822: state = 5823; break; // &RightUpTeeVecto -> &RightUpTeeVector - case 5828: state = 5829; break; // &RightUpVecto -> &RightUpVector - case 5831: state = 5832; break; // &RightUpVectorBa -> &RightUpVectorBar - case 5837: state = 5838; break; // &RightVecto -> &RightVector - case 5840: state = 5841; break; // &RightVectorBa -> &RightVectorBar - case 5855: state = 5856; break; // &rla -> &rlar - case 5856: state = 5857; break; // &rlar -> &rlarr - case 5859: state = 5860; break; // &rlha -> &rlhar - case 5876: state = 5879; break; // &roa -> &roar - case 5879: state = 5880; break; // &roar -> &roarr - case 5881: state = 5882; break; // &rob -> &robr - case 5885: state = 5886; break; // &ropa -> &ropar - case 5910: state = 5911; break; // &rpa -> &rpar - case 5921: state = 5922; break; // &rra -> &rrar - case 5922: state = 5923; break; // &rrar -> &rrarr - case 5929: state = 5930; break; // &Rrighta -> &Rrightar - case 5930: state = 5931; break; // &Rrightar -> &Rrightarr - case 5940: state = 5941; break; // &Rsc -> &Rscr - case 5942: state = 5943; break; // &rsc -> &rscr - case 5949: state = 5950; break; // &rsquo -> &rsquor - case 5951: state = 5960; break; // &rt -> &rtr - case 5952: state = 5953; break; // &rth -> &rthr - case 5965: state = 5966; break; // &rtrilt -> &rtriltr - case 5982: state = 5983; break; // &ruluha -> &ruluhar - case 5991: state = 6334; break; // &s -> &sr - case 6003: state = 6009; break; // &sca -> &scar - case 6005: state = 6006; break; // &Sca -> &Scar - case 6024: state = 6025; break; // &Sci -> &Scir - case 6027: state = 6028; break; // &sci -> &scir - case 6054: state = 6055; break; // &sea -> &sear - case 6055: state = 6061; break; // &sear -> &searr - case 6058: state = 6059; break; // &seA -> &seAr - case 6059: state = 6060; break; // &seAr -> &seArr - case 6070: state = 6071; break; // &seswa -> &seswar - case 6081: state = 6082; break; // &Sf -> &Sfr - case 6083: state = 6084; break; // &sf -> &sfr - case 6089: state = 6090; break; // &sha -> &shar - case 6105: state = 6106; break; // &Sho -> &Shor - case 6112: state = 6113; break; // &ShortDownA -> &ShortDownAr - case 6113: state = 6114; break; // &ShortDownAr -> &ShortDownArr - case 6121: state = 6122; break; // &ShortLeftA -> &ShortLeftAr - case 6122: state = 6123; break; // &ShortLeftAr -> &ShortLeftArr - case 6126: state = 6127; break; // &sho -> &shor - case 6133: state = 6134; break; // &shortpa -> &shortpar - case 6145: state = 6146; break; // &ShortRightA -> &ShortRightAr - case 6146: state = 6147; break; // &ShortRightAr -> &ShortRightArr - case 6152: state = 6153; break; // &ShortUpA -> &ShortUpAr - case 6153: state = 6154; break; // &ShortUpAr -> &ShortUpArr - case 6168: state = 6184; break; // &sim -> &simr - case 6185: state = 6186; break; // &simra -> &simrar - case 6186: state = 6187; break; // &simrar -> &simrarr - case 6189: state = 6190; break; // &sla -> &slar - case 6190: state = 6191; break; // &slar -> &slarr - case 6197: state = 6198; break; // &SmallCi -> &SmallCir - case 6219: state = 6220; break; // &smepa -> &smepar - case 6242: state = 6243; break; // &solba -> &solbar - case 6250: state = 6257; break; // &spa -> &spar - case 6266: state = 6267; break; // &Sq -> &Sqr - case 6287: state = 6288; break; // &Squa -> &Squar - case 6290: state = 6291; break; // &squa -> &squar - case 6296: state = 6297; break; // &SquareInte -> &SquareInter - case 6317: state = 6318; break; // &SquareSupe -> &SquareSuper - case 6335: state = 6336; break; // &sra -> &srar - case 6336: state = 6337; break; // &srar -> &srarr - case 6339: state = 6340; break; // &Ssc -> &Sscr - case 6342: state = 6343; break; // &ssc -> &sscr - case 6353: state = 6354; break; // &ssta -> &sstar - case 6357: state = 6358; break; // &Sta -> &Star - case 6359: state = 6363; break; // &st -> &str - case 6360: state = 6361; break; // &sta -> &star - case 6384: state = 6404; break; // &sub -> &subr - case 6405: state = 6406; break; // &subra -> &subrar - case 6406: state = 6407; break; // &subrar -> &subrarr - case 6435: state = 6436; break; // &succapp -> &succappr - case 6440: state = 6441; break; // &succcu -> &succcur - case 6477: state = 6478; break; // &succnapp -> &succnappr - case 6515: state = 6516; break; // &Supe -> &Super - case 6532: state = 6533; break; // &supla -> &suplar - case 6533: state = 6534; break; // &suplar -> &suplarr - case 6565: state = 6566; break; // &swa -> &swar - case 6566: state = 6572; break; // &swar -> &swarr - case 6569: state = 6570; break; // &swA -> &swAr - case 6570: state = 6571; break; // &swAr -> &swArr - case 6577: state = 6578; break; // &swnwa -> &swnwar - case 6583: state = 6797; break; // &T -> &Tr - case 6586: state = 6760; break; // &t -> &tr - case 6587: state = 6588; break; // &ta -> &tar - case 6594: state = 6595; break; // &tb -> &tbr - case 6598: state = 6599; break; // &Tca -> &Tcar - case 6603: state = 6604; break; // &tca -> &tcar - case 6621: state = 6622; break; // &tel -> &telr - case 6625: state = 6626; break; // &Tf -> &Tfr - case 6627: state = 6628; break; // &tf -> &tfr - case 6630: state = 6631; break; // &the -> &ther - case 6635: state = 6636; break; // &The -> &Ther - case 6639: state = 6640; break; // &Therefo -> &Therefor - case 6643: state = 6644; break; // &therefo -> &therefor - case 6659: state = 6660; break; // &thickapp -> &thickappr - case 6693: state = 6694; break; // &tho -> &thor - case 6727: state = 6728; break; // ×ba -> ×bar - case 6740: state = 6741; break; // &topci -> &topcir - case 6746: state = 6747; break; // &topfo -> &topfor - case 6751: state = 6752; break; // &tp -> &tpr - case 6769: state = 6781; break; // &triangle -> &triangler - case 6822: state = 6823; break; // &Tsc -> &Tscr - case 6825: state = 6826; break; // &tsc -> &tscr - case 6837: state = 6838; break; // &Tst -> &Tstr - case 6841: state = 6842; break; // &tst -> &tstr - case 6853: state = 6863; break; // &twohead -> &twoheadr - case 6858: state = 6859; break; // &twoheadlefta -> &twoheadleftar - case 6859: state = 6860; break; // &twoheadleftar -> &twoheadleftarr - case 6868: state = 6869; break; // &twoheadrighta -> &twoheadrightar - case 6869: state = 6870; break; // &twoheadrightar -> &twoheadrightarr - case 6873: state = 7176; break; // &U -> &Ur - case 6874: state = 6885; break; // &Ua -> &Uar - case 6879: state = 7166; break; // &u -> &ur - case 6880: state = 6890; break; // &ua -> &uar - case 6885: state = 6886; break; // &Uar -> &Uarr - case 6887: state = 6888; break; // &uA -> &uAr - case 6888: state = 6889; break; // &uAr -> &uArr - case 6890: state = 6891; break; // &uar -> &uarr - case 6894: state = 6895; break; // &Uarroci -> &Uarrocir - case 6896: state = 6897; break; // &Ub -> &Ubr - case 6900: state = 6901; break; // &ub -> &ubr - case 6911: state = 6912; break; // &Uci -> &Ucir - case 6915: state = 6916; break; // &uci -> &ucir - case 6921: state = 6922; break; // &uda -> &udar - case 6922: state = 6923; break; // &udar -> &udarr - case 6934: state = 6935; break; // &udha -> &udhar - case 6936: state = 6943; break; // &uf -> &ufr - case 6941: state = 6942; break; // &Uf -> &Ufr - case 6944: state = 6945; break; // &Ug -> &Ugr - case 6949: state = 6950; break; // &ug -> &ugr - case 6955: state = 6956; break; // &uHa -> &uHar - case 6958: state = 6959; break; // &uha -> &uhar - case 6959: state = 6961; break; // &uhar -> &uharr - case 6966: state = 6972; break; // &ulc -> &ulcr - case 6967: state = 6968; break; // &ulco -> &ulcor - case 6970: state = 6971; break; // &ulcorne -> &ulcorner - case 6975: state = 6976; break; // &ult -> &ultr - case 6980: state = 6981; break; // &Umac -> &Umacr - case 6984: state = 6985; break; // &umac -> &umacr - case 6989: state = 6990; break; // &Unde -> &Under - case 6991: state = 6994; break; // &UnderB -> &UnderBr - case 6992: state = 6993; break; // &UnderBa -> &UnderBar - case 7002: state = 7003; break; // &UnderPa -> &UnderPar - case 7032: state = 7033; break; // &UpA -> &UpAr - case 7033: state = 7034; break; // &UpAr -> &UpArr - case 7037: state = 7038; break; // &Upa -> &Upar - case 7038: state = 7039; break; // &Upar -> &Uparr - case 7043: state = 7044; break; // &upa -> &upar - case 7044: state = 7045; break; // &upar -> &uparr - case 7049: state = 7050; break; // &UpArrowBa -> &UpArrowBar - case 7055: state = 7056; break; // &UpArrowDownA -> &UpArrowDownAr - case 7056: state = 7057; break; // &UpArrowDownAr -> &UpArrowDownArr - case 7064: state = 7065; break; // &UpDownA -> &UpDownAr - case 7065: state = 7066; break; // &UpDownAr -> &UpDownArr - case 7073: state = 7074; break; // &Updowna -> &Updownar - case 7074: state = 7075; break; // &Updownar -> &Updownarr - case 7082: state = 7083; break; // &updowna -> &updownar - case 7083: state = 7084; break; // &updownar -> &updownarr - case 7093: state = 7094; break; // &UpEquilib -> &UpEquilibr - case 7099: state = 7100; break; // &upha -> &uphar - case 7104: state = 7109; break; // &upharpoon -> &upharpoonr - case 7118: state = 7119; break; // &Uppe -> &Upper - case 7124: state = 7125; break; // &UpperLeftA -> &UpperLeftAr - case 7125: state = 7126; break; // &UpperLeftAr -> &UpperLeftArr - case 7134: state = 7135; break; // &UpperRightA -> &UpperRightAr - case 7135: state = 7136; break; // &UpperRightAr -> &UpperRightArr - case 7153: state = 7154; break; // &UpTeeA -> &UpTeeAr - case 7154: state = 7155; break; // &UpTeeAr -> &UpTeeArr - case 7160: state = 7161; break; // &upupa -> &upupar - case 7161: state = 7162; break; // &upupar -> &upuparr - case 7167: state = 7173; break; // &urc -> &urcr - case 7168: state = 7169; break; // &urco -> &urcor - case 7171: state = 7172; break; // &urcorne -> &urcorner - case 7183: state = 7184; break; // &urt -> &urtr - case 7187: state = 7188; break; // &Usc -> &Uscr - case 7190: state = 7191; break; // &usc -> &uscr - case 7192: state = 7205; break; // &ut -> &utr - case 7209: state = 7210; break; // &uua -> &uuar - case 7210: state = 7211; break; // &uuar -> &uuarr - case 7223: state = 7417; break; // &v -> &vr - case 7224: state = 7229; break; // &va -> &var - case 7226: state = 7227; break; // &vang -> &vangr - case 7229: state = 7261; break; // &var -> &varr - case 7249: state = 7253; break; // &varp -> &varpr - case 7258: state = 7259; break; // &vA -> &vAr - case 7259: state = 7260; break; // &vAr -> &vArr - case 7286: state = 7291; break; // &vart -> &vartr - case 7297: state = 7302; break; // &vartriangle -> &vartriangler - case 7309: state = 7310; break; // &Vba -> &Vbar - case 7312: state = 7313; break; // &vBa -> &vBar - case 7336: state = 7349; break; // &Ve -> &Ver - case 7338: state = 7353; break; // &ve -> &ver - case 7341: state = 7342; break; // &veeba -> &veebar - case 7351: state = 7352; break; // &Verba -> &Verbar - case 7355: state = 7356; break; // &verba -> &verbar - case 7364: state = 7365; break; // &VerticalBa -> &VerticalBar - case 7373: state = 7374; break; // &VerticalSepa -> &VerticalSepar - case 7377: state = 7378; break; // &VerticalSeparato -> &VerticalSeparator - case 7394: state = 7395; break; // &Vf -> &Vfr - case 7396: state = 7397; break; // &vf -> &vfr - case 7399: state = 7400; break; // &vlt -> &vltr - case 7413: state = 7414; break; // &vp -> &vpr - case 7418: state = 7419; break; // &vrt -> &vrtr - case 7422: state = 7423; break; // &Vsc -> &Vscr - case 7425: state = 7426; break; // &vsc -> &vscr - case 7449: state = 7450; break; // &Wci -> &Wcir - case 7452: state = 7484; break; // &w -> &wr - case 7454: state = 7455; break; // &wci -> &wcir - case 7460: state = 7461; break; // &wedba -> &wedbar - case 7470: state = 7471; break; // &weie -> &weier - case 7473: state = 7474; break; // &Wf -> &Wfr - case 7475: state = 7476; break; // &wf -> &wfr - case 7490: state = 7491; break; // &Wsc -> &Wscr - case 7493: state = 7494; break; // &wsc -> &wscr - case 7495: state = 7551; break; // &x -> &xr - case 7499: state = 7500; break; // &xci -> &xcir - case 7505: state = 7506; break; // &xdt -> &xdtr - case 7509: state = 7510; break; // &Xf -> &Xfr - case 7511: state = 7512; break; // &xf -> &xfr - case 7514: state = 7515; break; // &xhA -> &xhAr - case 7515: state = 7516; break; // &xhAr -> &xhArr - case 7517: state = 7518; break; // &xha -> &xhar - case 7518: state = 7519; break; // &xhar -> &xharr - case 7523: state = 7524; break; // &xlA -> &xlAr - case 7524: state = 7525; break; // &xlAr -> &xlArr - case 7526: state = 7527; break; // &xla -> &xlar - case 7527: state = 7528; break; // &xlar -> &xlarr - case 7552: state = 7553; break; // &xrA -> &xrAr - case 7553: state = 7554; break; // &xrAr -> &xrArr - case 7555: state = 7556; break; // &xra -> &xrar - case 7556: state = 7557; break; // &xrar -> &xrarr - case 7559: state = 7560; break; // &Xsc -> &Xscr - case 7562: state = 7563; break; // &xsc -> &xscr - case 7573: state = 7574; break; // &xut -> &xutr - case 7601: state = 7602; break; // &Yci -> &Ycir - case 7605: state = 7606; break; // &yci -> &ycir - case 7612: state = 7613; break; // &Yf -> &Yfr - case 7614: state = 7615; break; // &yf -> &yfr - case 7629: state = 7630; break; // &Ysc -> &Yscr - case 7632: state = 7633; break; // &ysc -> &yscr - case 7658: state = 7659; break; // &Zca -> &Zcar - case 7663: state = 7664; break; // &zca -> &zcar - case 7677: state = 7678; break; // &zeet -> &zeetr - case 7680: state = 7681; break; // &Ze -> &Zer - case 7697: state = 7698; break; // &Zf -> &Zfr - case 7699: state = 7700; break; // &zf -> &zfr - case 7708: state = 7709; break; // &zig -> &zigr - case 7710: state = 7711; break; // &zigra -> &zigrar - case 7711: state = 7712; break; // &zigrar -> &zigrarr - case 7720: state = 7721; break; // &Zsc -> &Zscr - case 7723: state = 7724; break; // &zsc -> &zscr - default: return false; - } - break; - case 's': - switch (state) { - case 0: state = 5991; break; // & -> &s - case 1: state = 180; break; // &A -> &As - case 7: state = 183; break; // &a -> &as - case 62: state = 63; break; // &alef -> &alefs - case 91: state = 96; break; // &and -> &ands - case 102: state = 123; break; // &ang -> &angs - case 106: state = 107; break; // &angm -> &angms - case 152: state = 153; break; // &apo -> &apos - case 180: state = 186; break; // &As -> &Ass - case 222: state = 552; break; // &b -> &bs - case 225: state = 242; break; // &back -> &backs - case 231: state = 232; break; // &backep -> &backeps - case 247: state = 549; break; // &B -> &Bs - case 250: state = 251; break; // &Back -> &Backs - case 253: state = 254; break; // &Backsla -> &Backslas - case 291: state = 292; break; // &becau -> &becaus - case 296: state = 297; break; // &Becau -> &Becaus - case 305: state = 306; break; // &bep -> &beps - case 318: state = 319; break; // &Bernoulli -> &Bernoullis - case 334: state = 356; break; // &big -> &bigs - case 349: state = 350; break; // &bigoplu -> &bigoplus - case 354: state = 355; break; // &bigotime -> &bigotimes - case 381: state = 382; break; // &biguplu -> &biguplus - case 399: state = 407; break; // &black -> &blacks - case 497: state = 498; break; // &boxminu -> &boxminus - case 501: state = 502; break; // &boxplu -> &boxplus - case 506: state = 507; break; // &boxtime -> &boxtimes - case 564: state = 565; break; // &bsolh -> &bsolhs - case 583: state = 972; break; // &C -> &Cs - case 589: state = 975; break; // &c -> &cs - case 596: state = 630; break; // &cap -> &caps - case 639: state = 640; break; // &Cayley -> &Cayleys - case 643: state = 644; break; // &ccap -> &ccaps - case 673: state = 674; break; // &ccup -> &ccups - case 674: state = 675; break; // &ccups -> &ccupss - case 733: state = 799; break; // &cir -> &cirs - case 754: state = 755; break; // &circleda -> &circledas - case 762: state = 763; break; // &circledda -> &circleddas - case 778: state = 779; break; // &CircleMinu -> &CircleMinus - case 782: state = 783; break; // &CirclePlu -> &CirclePlus - case 787: state = 788; break; // &CircleTime -> &CircleTimes - case 804: state = 826; break; // &Clo -> &Clos - case 808: state = 809; break; // &Clockwi -> &Clockwis - case 851: state = 852; break; // &club -> &clubs - case 881: state = 882; break; // &complexe -> &complexes - case 929: state = 930; break; // © -> ©s - case 943: state = 944; break; // &CounterClockwi -> &CounterClockwis - case 966: state = 967; break; // &Cro -> &Cros - case 967: state = 968; break; // &Cros -> &Cross - case 969: state = 970; break; // &cro -> &cros - case 970: state = 971; break; // &cros -> &cross - case 994: state = 997; break; // &cue -> &cues - case 1006: state = 1025; break; // &cup -> &cups - case 1034: state = 1039; break; // &curlyeq -> &curlyeqs - case 1091: state = 1604; break; // &D -> &Ds - case 1092: state = 1116; break; // &Da -> &Das - case 1097: state = 1607; break; // &d -> &ds - case 1098: state = 1114; break; // &da -> &das - case 1157: state = 1158; break; // &ddot -> &ddots - case 1176: state = 1177; break; // &dfi -> &dfis - case 1228: state = 1260; break; // &di -> &dis - case 1230: state = 1242; break; // &diam -> &diams - case 1237: state = 1238; break; // &diamond -> &diamonds - case 1272: state = 1273; break; // ÷ontime -> ÷ontimes - case 1302: state = 1325; break; // &dot -> &dots - case 1319: state = 1320; break; // &dotminu -> &dotminus - case 1323: state = 1324; break; // &dotplu -> &dotplus - case 1508: state = 1509; break; // &downdownarrow -> &downdownarrows - case 1656: state = 1897; break; // &E -> &Es - case 1662: state = 1900; break; // &e -> &es - case 1663: state = 1668; break; // &ea -> &eas - case 1714: state = 1724; break; // &eg -> &egs - case 1728: state = 1742; break; // &el -> &els - case 1739: state = 1740; break; // &elinter -> &elinters - case 1750: state = 1790; break; // &em -> &ems - case 1756: state = 1757; break; // &empty -> &emptys - case 1797: state = 1799; break; // &en -> &ens - case 1813: state = 1821; break; // &ep -> &eps - case 1815: state = 1816; break; // &epar -> &epars - case 1819: state = 1820; break; // &eplu -> &eplus - case 1823: state = 1824; break; // &Ep -> &Eps - case 1833: state = 1842; break; // &eq -> &eqs - case 1853: state = 1854; break; // &eqslantle -> &eqslantles - case 1854: state = 1855; break; // &eqslantles -> &eqslantless - case 1862: state = 1863; break; // &equal -> &equals - case 1869: state = 1870; break; // &eque -> &eques - case 1887: state = 1888; break; // &eqvpar -> &eqvpars - case 1928: state = 1929; break; // &exi -> &exis - case 1932: state = 1933; break; // &Exi -> &Exis - case 1934: state = 1935; break; // &Exist -> &Exists - case 1964: state = 2115; break; // &f -> &fs - case 1973: state = 1974; break; // &fallingdot -> &fallingdots - case 1977: state = 2112; break; // &F -> &Fs - case 2047: state = 2048; break; // &fltn -> &fltns - case 2084: state = 2107; break; // &fra -> &fras - case 2118: state = 2285; break; // &g -> &gs - case 2124: state = 2282; break; // &G -> &Gs - case 2166: state = 2176; break; // &ge -> &ges - case 2169: state = 2171; break; // &geq -> &geqs - case 2185: state = 2186; break; // &gesle -> &gesles - case 2208: state = 2219; break; // &gn -> &gns - case 2244: state = 2245; break; // &GreaterEqualLe -> &GreaterEqualLes - case 2245: state = 2246; break; // &GreaterEqualLes -> &GreaterEqualLess - case 2264: state = 2265; break; // &GreaterLe -> &GreaterLes - case 2265: state = 2266; break; // &GreaterLes -> &GreaterLess - case 2308: state = 2309; break; // >que -> >ques - case 2311: state = 2338; break; // >r -> >rs - case 2326: state = 2327; break; // >reqle -> >reqles - case 2327: state = 2328; break; // >reqles -> >reqless - case 2331: state = 2332; break; // >reqqle -> >reqqles - case 2332: state = 2333; break; // >reqqles -> >reqqless - case 2335: state = 2336; break; // >rle -> >rles - case 2336: state = 2337; break; // >rles -> >rless - case 2351: state = 2490; break; // &H -> &Hs - case 2356: state = 2493; break; // &h -> &hs - case 2359: state = 2360; break; // &hair -> &hairs - case 2400: state = 2401; break; // &heart -> &hearts - case 2428: state = 2429; break; // &hk -> &hks - case 2497: state = 2498; break; // &hsla -> &hslas - case 2533: state = 2740; break; // &I -> &Is - case 2539: state = 2743; break; // &i -> &is - case 2639: state = 2640; break; // &Implie -> &Implies - case 2665: state = 2666; break; // &integer -> &integers - case 2676: state = 2677; break; // &Inter -> &Inters - case 2694: state = 2695; break; // &Invi -> &Invis - case 2708: state = 2709; break; // &InvisibleTime -> &InvisibleTimes - case 2737: state = 2738; break; // &ique -> &iques - case 2747: state = 2752; break; // &isin -> &isins - case 2777: state = 2803; break; // &J -> &Js - case 2782: state = 2806; break; // &j -> &js - case 2825: state = 2875; break; // &K -> &Ks - case 2830: state = 2878; break; // &k -> &ks - case 2881: state = 3661; break; // &l -> &ls - case 2886: state = 3666; break; // &L -> &Ls - case 2939: state = 2951; break; // &larr -> &larrs - case 2941: state = 2942; break; // &larrbf -> &larrbfs - case 2943: state = 2944; break; // &larrf -> &larrfs - case 2964: state = 2965; break; // &late -> &lates - case 2982: state = 2984; break; // &lbrk -> &lbrks - case 3012: state = 3029; break; // &ld -> &lds - case 3024: state = 3025; break; // &ldru -> &ldrus - case 3032: state = 3291; break; // &le -> &les - case 3033: state = 3321; break; // &Le -> &Les - case 3147: state = 3148; break; // &leftleftarrow -> &leftleftarrows - case 3173: state = 3188; break; // &leftright -> &leftrights - case 3178: state = 3179; break; // &leftrightarrow -> &leftrightarrows - case 3186: state = 3187; break; // &leftrightharpoon -> &leftrightharpoons - case 3226: state = 3227; break; // &leftthreetime -> &leftthreetimes - case 3284: state = 3286; break; // &leq -> &leqs - case 3291: state = 3302; break; // &les -> &less - case 3300: state = 3301; break; // &lesge -> &lesges - case 3302: state = 3358; break; // &less -> &lesss - case 3321: state = 3322; break; // &Les -> &Less - case 3355: state = 3356; break; // &LessLe -> &LessLes - case 3356: state = 3357; break; // &LessLes -> &LessLess - case 3377: state = 3378; break; // &lfi -> &lfis - case 3445: state = 3446; break; // &lmou -> &lmous - case 3452: state = 3463; break; // &ln -> &lns - case 3539: state = 3540; break; // &longmap -> &longmaps - case 3596: state = 3597; break; // &loplu -> &loplus - case 3601: state = 3602; break; // &lotime -> &lotimes - case 3604: state = 3605; break; // &lowa -> &lowas - case 3706: state = 3707; break; // <ime -> <imes - case 3714: state = 3715; break; // <que -> <ques - case 3726: state = 3727; break; // &lurd -> &lurds - case 3745: state = 3879; break; // &m -> &ms - case 3752: state = 3753; break; // &malte -> &maltes - case 3755: state = 3876; break; // &M -> &Ms - case 3758: state = 3759; break; // &map -> &maps - case 3785: state = 3786; break; // &mda -> &mdas - case 3793: state = 3794; break; // &mea -> &meas - case 3832: state = 3833; break; // &mida -> &midas - case 3842: state = 3843; break; // &minu -> &minus - case 3849: state = 3850; break; // &Minu -> &Minus - case 3853: state = 3854; break; // &MinusPlu -> &MinusPlus - case 3863: state = 3864; break; // &mnplu -> &mnplus - case 3868: state = 3869; break; // &model -> &models - case 3884: state = 3885; break; // &mstpo -> &mstpos - case 3897: state = 4653; break; // &n -> &ns - case 3902: state = 4659; break; // &N -> &Ns - case 3918: state = 3919; break; // &napo -> &napos - case 3928: state = 3929; break; // &natural -> &naturals - case 3930: state = 3931; break; // &nb -> &nbs - case 3967: state = 3968; break; // &nda -> &ndas - case 3970: state = 4035; break; // &ne -> &nes - case 3984: state = 4041; break; // &Ne -> &Nes - case 4060: state = 4061; break; // &NestedLe -> &NestedLes - case 4061: state = 4062; break; // &NestedLes -> &NestedLess - case 4064: state = 4065; break; // &NestedLessLe -> &NestedLessLes - case 4065: state = 4066; break; // &NestedLessLes -> &NestedLessLess - case 4073: state = 4074; break; // &nexi -> &nexis - case 4075: state = 4076; break; // &nexist -> &nexists - case 4081: state = 4094; break; // &ng -> &ngs - case 4083: state = 4091; break; // &nge -> &nges - case 4084: state = 4086; break; // &ngeq -> &ngeqs - case 4111: state = 4112; break; // &ni -> &nis - case 4121: state = 4178; break; // &nl -> &nls - case 4131: state = 4175; break; // &nle -> &nles - case 4168: state = 4170; break; // &nleq -> &nleqs - case 4175: state = 4176; break; // &nles -> &nless - case 4265: state = 4266; break; // &NotExi -> &NotExis - case 4267: state = 4268; break; // &NotExist -> &NotExists - case 4298: state = 4299; break; // &NotGreaterLe -> &NotGreaterLes - case 4299: state = 4300; break; // &NotGreaterLes -> &NotGreaterLess - case 4344: state = 4363; break; // &NotLe -> &NotLes - case 4363: state = 4364; break; // &NotLes -> &NotLess - case 4378: state = 4379; break; // &NotLessLe -> &NotLessLes - case 4379: state = 4380; break; // &NotLessLes -> &NotLessLess - case 4397: state = 4398; break; // &NotNe -> &NotNes - case 4417: state = 4418; break; // &NotNestedLe -> &NotNestedLes - case 4418: state = 4419; break; // &NotNestedLes -> &NotNestedLess - case 4421: state = 4422; break; // &NotNestedLessLe -> &NotNestedLessLes - case 4422: state = 4423; break; // &NotNestedLessLes -> &NotNestedLessLess - case 4436: state = 4437; break; // &NotPrecede -> &NotPrecedes - case 4457: state = 4458; break; // &NotRever -> &NotRevers - case 4495: state = 4496; break; // &NotSquareSub -> &NotSquareSubs - case 4506: state = 4507; break; // &NotSquareSuper -> &NotSquareSupers - case 4516: state = 4517; break; // &NotSub -> &NotSubs - case 4529: state = 4530; break; // &NotSucceed -> &NotSucceeds - case 4553: state = 4554; break; // &NotSuper -> &NotSupers - case 4599: state = 4605; break; // &npar -> &npars - case 4688: state = 4689; break; // &nsq -> &nsqs - case 4696: state = 4699; break; // &nsub -> &nsubs - case 4709: state = 4712; break; // &nsup -> &nsups - case 4754: state = 4758; break; // &num -> &nums - case 4760: state = 4809; break; // &nv -> &nvs - case 4765: state = 4766; break; // &nVDa -> &nVDas - case 4769: state = 4770; break; // &nVda -> &nVdas - case 4773: state = 4774; break; // &nvDa -> &nvDas - case 4777: state = 4778; break; // &nvda -> &nvdas - case 4827: state = 5015; break; // &O -> &Os - case 4833: state = 5018; break; // &o -> &os - case 4834: state = 4839; break; // &oa -> &oas - case 4851: state = 4868; break; // &od -> &ods - case 4852: state = 4853; break; // &oda -> &odas - case 4916: state = 4917; break; // &olcro -> &olcros - case 4917: state = 4918; break; // &olcros -> &olcross - case 4949: state = 4950; break; // &ominu -> &ominus - case 4988: state = 4989; break; // &oplu -> &oplus - case 4991: state = 5008; break; // &or -> &ors - case 5022: state = 5023; break; // &Osla -> &Oslas - case 5026: state = 5027; break; // &osla -> &oslas - case 5042: state = 5043; break; // &Otime -> &Otimes - case 5045: state = 5046; break; // &otime -> &otimes - case 5047: state = 5048; break; // &otimesa -> &otimesas - case 5079: state = 5080; break; // &OverParenthe -> &OverParenthes - case 5081: state = 5082; break; // &OverParenthesi -> &OverParenthesis - case 5083: state = 5338; break; // &p -> &ps - case 5085: state = 5091; break; // &par -> &pars - case 5096: state = 5335; break; // &P -> &Ps - case 5158: state = 5159; break; // &plu -> &plus - case 5159: state = 5182; break; // &plus -> &pluss - case 5173: state = 5174; break; // &Plu -> &Plus - case 5178: state = 5179; break; // &PlusMinu -> &PlusMinus - case 5216: state = 5328; break; // &pr -> &prs - case 5224: state = 5279; break; // &prec -> &precs - case 5242: state = 5243; break; // &Precede -> &Precedes - case 5266: state = 5276; break; // &precn -> &precns - case 5287: state = 5288; break; // &prime -> &primes - case 5289: state = 5293; break; // &prn -> &prns - case 5303: state = 5312; break; // &prof -> &profs - case 5345: state = 5346; break; // &punc -> &puncs - case 5348: state = 5368; break; // &Q -> &Qs - case 5351: state = 5371; break; // &q -> &qs - case 5382: state = 5383; break; // &quaternion -> &quaternions - case 5387: state = 5388; break; // &que -> &ques - case 5397: state = 5934; break; // &r -> &rs - case 5405: state = 5939; break; // &R -> &Rs - case 5439: state = 5454; break; // &rarr -> &rarrs - case 5443: state = 5444; break; // &rarrbf -> &rarrbfs - case 5446: state = 5447; break; // &rarrf -> &rarrfs - case 5474: state = 5475; break; // &rational -> &rationals - case 5496: state = 5498; break; // &rbrk -> &rbrks - case 5526: state = 5538; break; // &rd -> &rds - case 5543: state = 5551; break; // &real -> &reals - case 5559: state = 5560; break; // &Rever -> &Revers - case 5593: state = 5594; break; // &rfi -> &rfis - case 5642: state = 5844; break; // &ri -> &ris - case 5645: state = 5754; break; // &right -> &rights - case 5733: state = 5734; break; // &rightleftarrow -> &rightleftarrows - case 5741: state = 5742; break; // &rightleftharpoon -> &rightleftharpoons - case 5752: state = 5753; break; // &rightrightarrow -> &rightrightarrows - case 5786: state = 5787; break; // &rightthreetime -> &rightthreetimes - case 5850: state = 5851; break; // &risingdot -> &risingdots - case 5864: state = 5865; break; // &rmou -> &rmous - case 5892: state = 5893; break; // &roplu -> &roplus - case 5897: state = 5898; break; // &rotime -> &rotimes - case 5907: state = 5908; break; // &RoundImplie -> &RoundImplies - case 5958: state = 5959; break; // &rtime -> &rtimes - case 5985: state = 6338; break; // &S -> &Ss - case 5991: state = 6341; break; // &s -> &ss - case 6002: state = 6043; break; // &sc -> &scs - case 6030: state = 6034; break; // &scn -> &scns - case 6053: state = 6068; break; // &se -> &ses - case 6076: state = 6077; break; // &setminu -> &setminus - case 6182: state = 6183; break; // &simplu -> &simplus - case 6203: state = 6214; break; // &sma -> &smas - case 6205: state = 6206; break; // &small -> &smalls - case 6212: state = 6213; break; // &smallsetminu -> &smallsetminus - case 6220: state = 6221; break; // &smepar -> &smepars - case 6228: state = 6229; break; // &smte -> &smtes - case 6252: state = 6253; break; // &spade -> &spades - case 6258: state = 6269; break; // &sq -> &sqs - case 6261: state = 6262; break; // &sqcap -> &sqcaps - case 6264: state = 6265; break; // &sqcup -> &sqcups - case 6271: state = 6273; break; // &sqsub -> &sqsubs - case 6278: state = 6280; break; // &sqsup -> &sqsups - case 6297: state = 6298; break; // &SquareInter -> &SquareInters - case 6307: state = 6308; break; // &SquareSub -> &SquareSubs - case 6318: state = 6319; break; // &SquareSuper -> &SquareSupers - case 6370: state = 6371; break; // &straightep -> &straighteps - case 6379: state = 6380; break; // &strn -> &strns - case 6382: state = 6408; break; // &Sub -> &Subs - case 6384: state = 6411; break; // &sub -> &subs - case 6402: state = 6403; break; // &subplu -> &subplus - case 6432: state = 6487; break; // &succ -> &succs - case 6450: state = 6451; break; // &Succeed -> &Succeeds - case 6474: state = 6484; break; // &succn -> &succns - case 6499: state = 6546; break; // &Sup -> &Sups - case 6500: state = 6549; break; // &sup -> &sups - case 6504: state = 6507; break; // &supd -> &supds - case 6516: state = 6517; break; // &Super -> &Supers - case 6525: state = 6526; break; // &suph -> &suphs - case 6544: state = 6545; break; // &supplu -> &supplus - case 6583: state = 6821; break; // &T -> &Ts - case 6586: state = 6824; break; // &t -> &ts - case 6649: state = 6650; break; // &theta -> &thetas - case 6656: state = 6663; break; // &thick -> &thicks - case 6674: state = 6675; break; // &thin -> &thins - case 6683: state = 6686; break; // &thk -> &thks - case 6724: state = 6725; break; // &time -> × - case 6732: state = 6749; break; // &to -> &tos - case 6764: state = 6809; break; // &tri -> &tris - case 6795: state = 6796; break; // &triminu -> &triminus - case 6807: state = 6808; break; // &triplu -> &triplus - case 6873: state = 7186; break; // &U -> &Us - case 6879: state = 7189; break; // &u -> &us - case 6937: state = 6938; break; // &ufi -> &ufis - case 7008: state = 7009; break; // &UnderParenthe -> &UnderParenthes - case 7010: state = 7011; break; // &UnderParenthesi -> &UnderParenthesis - case 7017: state = 7018; break; // &UnionPlu -> &UnionPlus - case 7031: state = 7139; break; // &Up -> &Ups - case 7042: state = 7141; break; // &up -> &ups - case 7115: state = 7116; break; // &uplu -> &uplus - case 7164: state = 7165; break; // &upuparrow -> &upuparrows - case 7223: state = 7424; break; // &v -> &vs - case 7229: state = 7264; break; // &var -> &vars - case 7231: state = 7232; break; // &varep -> &vareps - case 7270: state = 7271; break; // &varsub -> &varsubs - case 7278: state = 7279; break; // &varsup -> &varsups - case 7307: state = 7421; break; // &V -> &Vs - case 7320: state = 7321; break; // &VDa -> &VDas - case 7324: state = 7325; break; // &Vda -> &Vdas - case 7328: state = 7329; break; // &vDa -> &vDas - case 7332: state = 7333; break; // &vda -> &vdas - case 7402: state = 7403; break; // &vn -> &vns - case 7438: state = 7439; break; // &Vvda -> &Vvdas - case 7447: state = 7489; break; // &W -> &Ws - case 7452: state = 7492; break; // &w -> &ws - case 7495: state = 7561; break; // &x -> &xs - case 7508: state = 7558; break; // &X -> &Xs - case 7533: state = 7534; break; // &xni -> &xnis - case 7545: state = 7546; break; // &xoplu -> &xoplus - case 7571: state = 7572; break; // &xuplu -> &xuplus - case 7584: state = 7628; break; // &Y -> &Ys - case 7590: state = 7631; break; // &y -> &ys - case 7645: state = 7719; break; // &Z -> &Zs - case 7651: state = 7722; break; // &z -> &zs - default: return false; - } - break; - case 't': - switch (state) { - case 0: state = 6586; break; // & -> &t - case 1: state = 196; break; // &A -> &At - case 4: state = 5; break; // &Aacu -> &Aacut - case 7: state = 201; break; // &a -> &at - case 10: state = 11; break; // &aacu -> &aacut - case 33: state = 34; break; // &acu -> &acut - case 118: state = 119; break; // &angr -> &angrt - case 123: state = 126; break; // &angs -> &angst - case 161: state = 162; break; // &ApplyFunc -> &ApplyFunct - case 183: state = 190; break; // &as -> &ast - case 217: state = 218; break; // &awconin -> &awconint - case 220: state = 221; break; // &awin -> &awint - case 272: state = 273; break; // &bbrk -> &bbrkt - case 288: state = 322; break; // &be -> &bet - case 293: state = 320; break; // &Be -> &Bet - case 301: state = 302; break; // &bemp -> &bempt - case 334: state = 364; break; // &big -> &bigt - case 343: state = 351; break; // &bigo -> &bigot - case 345: state = 346; break; // &bigodo -> &bigodot - case 356: state = 361; break; // &bigs -> &bigst - case 399: state = 413; break; // &black -> &blackt - case 427: state = 428; break; // &blacktrianglelef -> &blacktriangleleft - case 432: state = 433; break; // &blacktrianglerigh -> &blacktriangleright - case 452: state = 453; break; // &bNo -> &bNot - case 454: state = 455; break; // &bno -> &bnot - case 459: state = 462; break; // &bo -> &bot - case 462: state = 463; break; // &bot -> &bott - case 466: state = 467; break; // &bow -> &bowt - case 470: state = 503; break; // &box -> &boxt - case 571: state = 572; break; // &bulle -> &bullet - case 586: state = 587; break; // &Cacu -> &Cacut - case 589: state = 983; break; // &c -> &ct - case 592: state = 593; break; // &cacu -> &cacut - case 611: state = 612; break; // &capdo -> &capdot - case 613: state = 614; break; // &Capi -> &Capit - case 624: state = 625; break; // &CapitalDifferen -> &CapitalDifferent - case 632: state = 633; break; // &care -> &caret - case 670: state = 671; break; // &Cconin -> &Cconint - case 678: state = 679; break; // &Cdo -> &Cdot - case 681: state = 682; break; // &cdo -> &cdot - case 694: state = 695; break; // &cemp -> &cempt - case 698: state = 699; break; // &cen -> ¢ - case 700: state = 701; break; // &Cen -> &Cent - case 705: state = 706; break; // &CenterDo -> &CenterDot - case 710: state = 711; break; // ¢erdo -> ¢erdot - case 746: state = 747; break; // &circlearrowlef -> &circlearrowleft - case 751: state = 752; break; // &circlearrowrigh -> &circlearrowright - case 755: state = 756; break; // &circledas -> &circledast - case 771: state = 772; break; // &CircleDo -> &CircleDot - case 794: state = 795; break; // &cirfnin -> &cirfnint - case 813: state = 814; break; // &ClockwiseCon -> &ClockwiseCont - case 819: state = 820; break; // &ClockwiseContourIn -> &ClockwiseContourInt - case 841: state = 842; break; // &CloseCurlyDoubleQuo -> &CloseCurlyDoubleQuot - case 846: state = 847; break; // &CloseCurlyQuo -> &CloseCurlyQuot - case 854: state = 855; break; // &clubsui -> &clubsuit - case 869: state = 870; break; // &comma -> &commat - case 878: state = 879; break; // &complemen -> &complement - case 886: state = 887; break; // &congdo -> &congdot - case 888: state = 901; break; // &Con -> &Cont - case 893: state = 894; break; // &Congruen -> &Congruent - case 896: state = 897; break; // &Conin -> &Conint - case 899: state = 900; break; // &conin -> &conint - case 906: state = 907; break; // &ContourIn -> &ContourInt - case 924: state = 925; break; // &Coproduc -> &Coproduct - case 933: state = 934; break; // &Coun -> &Count - case 948: state = 949; break; // &CounterClockwiseCon -> &CounterClockwiseCont - case 954: state = 955; break; // &CounterClockwiseContourIn -> &CounterClockwiseContourInt - case 985: state = 986; break; // &ctdo -> &ctdot - case 1021: state = 1022; break; // &cupdo -> &cupdot - case 1063: state = 1064; break; // &curvearrowlef -> &curvearrowleft - case 1068: state = 1069; break; // &curvearrowrigh -> &curvearrowright - case 1081: state = 1082; break; // &cwconin -> &cwconint - case 1084: state = 1085; break; // &cwin -> &cwint - case 1088: state = 1089; break; // &cylc -> &cylct - case 1097: state = 1624; break; // &d -> &dt - case 1104: state = 1105; break; // &dale -> &dalet - case 1150: state = 1151; break; // &DDo -> &DDot - case 1156: state = 1157; break; // &ddo -> &ddot - case 1164: state = 1165; break; // &Del -> &Delt - case 1167: state = 1168; break; // &del -> &delt - case 1171: state = 1172; break; // &demp -> &dempt - case 1178: state = 1179; break; // &dfish -> &dfisht - case 1195: state = 1196; break; // &Diacri -> &Diacrit - case 1203: state = 1204; break; // &DiacriticalAcu -> &DiacriticalAcut - case 1207: state = 1208; break; // &DiacriticalDo -> &DiacriticalDot - case 1215: state = 1216; break; // &DiacriticalDoubleAcu -> &DiacriticalDoubleAcut - case 1240: state = 1241; break; // &diamondsui -> &diamondsuit - case 1249: state = 1250; break; // &Differen -> &Different - case 1268: state = 1269; break; // ÷on -> ÷ont - case 1291: state = 1302; break; // &do -> &dot - case 1296: state = 1301; break; // &Do -> &Dot - case 1304: state = 1305; break; // &DotDo -> &DotDot - case 1309: state = 1310; break; // &doteqdo -> &doteqdot - case 1349: state = 1350; break; // &DoubleCon -> &DoubleCont - case 1355: state = 1356; break; // &DoubleContourIn -> &DoubleContourInt - case 1363: state = 1364; break; // &DoubleDo -> &DoubleDot - case 1374: state = 1375; break; // &DoubleLef -> &DoubleLeft - case 1384: state = 1385; break; // &DoubleLeftRigh -> &DoubleLeftRight - case 1399: state = 1400; break; // &DoubleLongLef -> &DoubleLongLeft - case 1409: state = 1410; break; // &DoubleLongLeftRigh -> &DoubleLongLeftRight - case 1419: state = 1420; break; // &DoubleLongRigh -> &DoubleLongRight - case 1429: state = 1430; break; // &DoubleRigh -> &DoubleRight - case 1457: state = 1458; break; // &DoubleVer -> &DoubleVert - case 1519: state = 1520; break; // &downharpoonlef -> &downharpoonleft - case 1524: state = 1525; break; // &downharpoonrigh -> &downharpoonright - case 1528: state = 1529; break; // &DownLef -> &DownLeft - case 1533: state = 1534; break; // &DownLeftRigh -> &DownLeftRight - case 1537: state = 1538; break; // &DownLeftRightVec -> &DownLeftRightVect - case 1546: state = 1547; break; // &DownLeftTeeVec -> &DownLeftTeeVect - case 1552: state = 1553; break; // &DownLeftVec -> &DownLeftVect - case 1562: state = 1563; break; // &DownRigh -> &DownRight - case 1569: state = 1570; break; // &DownRightTeeVec -> &DownRightTeeVect - case 1575: state = 1576; break; // &DownRightVec -> &DownRightVect - case 1604: state = 1616; break; // &Ds -> &Dst - case 1607: state = 1620; break; // &ds -> &dst - case 1626: state = 1627; break; // &dtdo -> &dtdot - case 1656: state = 1910; break; // &E -> &Et - case 1659: state = 1660; break; // &Eacu -> &Eacut - case 1662: state = 1912; break; // &e -> &et - case 1665: state = 1666; break; // &eacu -> &eacut - case 1668: state = 1669; break; // &eas -> &east - case 1696: state = 1697; break; // &eDDo -> &eDDot - case 1699: state = 1700; break; // &Edo -> &Edot - case 1701: state = 1702; break; // &eDo -> &eDot - case 1704: state = 1705; break; // &edo -> &edot - case 1709: state = 1710; break; // &efDo -> &efDot - case 1726: state = 1727; break; // &egsdo -> &egsdot - case 1733: state = 1734; break; // &Elemen -> &Element - case 1736: state = 1737; break; // &elin -> &elint - case 1744: state = 1745; break; // &elsdo -> &elsdot - case 1754: state = 1755; break; // &emp -> &empt - case 1758: state = 1759; break; // &emptyse -> &emptyset - case 1760: state = 1761; break; // &Emp -> &Empt - case 1847: state = 1848; break; // &eqslan -> &eqslant - case 1849: state = 1850; break; // &eqslantg -> &eqslantgt - case 1870: state = 1871; break; // &eques -> &equest - case 1895: state = 1896; break; // &erDo -> &erDot - case 1904: state = 1905; break; // &esdo -> &esdot - case 1929: state = 1930; break; // &exis -> &exist - case 1933: state = 1934; break; // &Exis -> &Exist - case 1938: state = 1939; break; // &expec -> &expect - case 1940: state = 1941; break; // &expecta -> &expectat - case 1949: state = 1950; break; // &Exponen -> &Exponent - case 1958: state = 1959; break; // &exponen -> &exponent - case 1972: state = 1973; break; // &fallingdo -> &fallingdot - case 2040: state = 2046; break; // &fl -> &flt - case 2041: state = 2042; break; // &fla -> &flat - case 2072: state = 2073; break; // &Fourier -> &Fouriert - case 2078: state = 2079; break; // &fpar -> &fpart - case 2081: state = 2082; break; // &fpartin -> &fpartint - case 2118: state = 2294; break; // &g -> > - case 2121: state = 2122; break; // &gacu -> &gacut - case 2124: state = 2293; break; // &G -> &Gt - case 2160: state = 2161; break; // &Gdo -> &Gdot - case 2163: state = 2164; break; // &gdo -> &gdot - case 2174: state = 2175; break; // &geqslan -> &geqslant - case 2180: state = 2181; break; // &gesdo -> &gesdot - case 2234: state = 2235; break; // &Grea -> &Great - case 2259: state = 2260; break; // &GreaterGrea -> &GreaterGreat - case 2270: state = 2271; break; // &GreaterSlan -> &GreaterSlant - case 2300: state = 2301; break; // >do -> >dot - case 2309: state = 2310; break; // >ques -> >quest - case 2321: state = 2322; break; // >rdo -> >rdot - case 2343: state = 2344; break; // &gver -> &gvert - case 2352: state = 2385; break; // &Ha -> &Hat - case 2366: state = 2367; break; // &hamil -> &hamilt - case 2399: state = 2400; break; // &hear -> &heart - case 2403: state = 2404; break; // &heartsui -> &heartsuit - case 2421: state = 2422; break; // &Hilber -> &Hilbert - case 2444: state = 2445; break; // &hom -> &homt - case 2446: state = 2447; break; // &homth -> &homtht - case 2452: state = 2453; break; // &hooklef -> &hookleft - case 2462: state = 2463; break; // &hookrigh -> &hookright - case 2482: state = 2483; break; // &Horizon -> &Horizont - case 2490: state = 2500; break; // &Hs -> &Hst - case 2493: state = 2504; break; // &hs -> &hst - case 2533: state = 2756; break; // &I -> &It - case 2536: state = 2537; break; // &Iacu -> &Iacut - case 2539: state = 2755; break; // &i -> &it - case 2542: state = 2543; break; // &iacu -> &iacut - case 2556: state = 2557; break; // &Ido -> &Idot - case 2585: state = 2586; break; // &iiiin -> &iiiint - case 2587: state = 2588; break; // &iiin -> &iiint - case 2593: state = 2594; break; // &iio -> &iiot - case 2609: state = 2629; break; // &ima -> &imat - case 2627: state = 2628; break; // &imagpar -> &imagpart - case 2641: state = 2658; break; // &in -> &int - case 2648: state = 2649; break; // &infin -> &infint - case 2654: state = 2655; break; // &inodo -> &inodot - case 2656: state = 2657; break; // &In -> &Int - case 2679: state = 2680; break; // &Intersec -> &Intersect - case 2713: state = 2729; break; // &io -> &iot - case 2716: state = 2727; break; // &Io -> &Iot - case 2738: state = 2739; break; // &iques -> ¿ - case 2749: state = 2750; break; // &isindo -> &isindot - case 2794: state = 2795; break; // &jma -> &jmat - case 2881: state = 3692; break; // &l -> < - case 2882: state = 2957; break; // &lA -> &lAt - case 2886: state = 3691; break; // &L -> &Lt - case 2889: state = 2890; break; // &Lacu -> &Lacut - case 2892: state = 2956; break; // &la -> &lat - case 2894: state = 2895; break; // &lacu -> &lacut - case 2899: state = 2900; break; // &laemp -> &laempt - case 2927: state = 2928; break; // &Laplace -> &Laplacet - case 2939: state = 2954; break; // &larr -> &larrt - case 3034: state = 3035; break; // &Lef -> &Left - case 3046: state = 3047; break; // &LeftAngleBracke -> &LeftAngleBracket - case 3057: state = 3058; break; // &lef -> &left - case 3058: state = 3218; break; // &left -> &leftt - case 3063: state = 3077; break; // &leftarrow -> &leftarrowt - case 3070: state = 3071; break; // &LeftArrowRigh -> &LeftArrowRight - case 3099: state = 3100; break; // &LeftDoubleBracke -> &LeftDoubleBracket - case 3108: state = 3109; break; // &LeftDownTeeVec -> &LeftDownTeeVect - case 3114: state = 3115; break; // &LeftDownVec -> &LeftDownVect - case 3141: state = 3142; break; // &leftlef -> &leftleft - case 3152: state = 3153; break; // &LeftRigh -> &LeftRight - case 3162: state = 3163; break; // &Leftrigh -> &Leftright - case 3172: state = 3173; break; // &leftrigh -> &leftright - case 3200: state = 3201; break; // &LeftRightVec -> &LeftRightVect - case 3214: state = 3215; break; // &LeftTeeVec -> &LeftTeeVect - case 3222: state = 3223; break; // &leftthree -> &leftthreet - case 3251: state = 3252; break; // &LeftUpDownVec -> &LeftUpDownVect - case 3260: state = 3261; break; // &LeftUpTeeVec -> &LeftUpTeeVect - case 3266: state = 3267; break; // &LeftUpVec -> &LeftUpVect - case 3275: state = 3276; break; // &LeftVec -> &LeftVect - case 3289: state = 3290; break; // &leqslan -> &leqslant - case 3295: state = 3296; break; // &lesdo -> &lesdot - case 3310: state = 3311; break; // &lessdo -> &lessdot - case 3314: state = 3315; break; // &lesseqg -> &lesseqgt - case 3318: state = 3319; break; // &lesseqqg -> &lesseqqgt - case 3331: state = 3332; break; // &LessEqualGrea -> &LessEqualGreat - case 3347: state = 3348; break; // &LessGrea -> &LessGreat - case 3351: state = 3352; break; // &lessg -> &lessgt - case 3364: state = 3365; break; // &LessSlan -> &LessSlant - case 3379: state = 3380; break; // &lfish -> &lfisht - case 3409: state = 3431; break; // &ll -> &llt - case 3420: state = 3421; break; // &Llef -> &Lleft - case 3437: state = 3438; break; // &Lmido -> &Lmidot - case 3442: state = 3443; break; // &lmido -> &lmidot - case 3446: state = 3447; break; // &lmous -> &lmoust - case 3466: state = 3598; break; // &lo -> &lot - case 3480: state = 3481; break; // &LongLef -> &LongLeft - case 3489: state = 3490; break; // &Longlef -> &Longleft - case 3500: state = 3501; break; // &longlef -> &longleft - case 3510: state = 3511; break; // &LongLeftRigh -> &LongLeftRight - case 3520: state = 3521; break; // &Longleftrigh -> &Longleftright - case 3530: state = 3531; break; // &longleftrigh -> &longleftright - case 3540: state = 3541; break; // &longmaps -> &longmapst - case 3546: state = 3547; break; // &LongRigh -> &LongRight - case 3556: state = 3557; break; // &Longrigh -> &Longright - case 3566: state = 3567; break; // &longrigh -> &longright - case 3582: state = 3583; break; // &looparrowlef -> &looparrowleft - case 3587: state = 3588; break; // &looparrowrigh -> &looparrowright - case 3605: state = 3606; break; // &lowas -> &lowast - case 3615: state = 3616; break; // &LowerLef -> &LowerLeft - case 3625: state = 3626; break; // &LowerRigh -> &LowerRight - case 3641: state = 3642; break; // &lparl -> &lparlt - case 3643: state = 3658; break; // &lr -> &lrt - case 3661: state = 3686; break; // &ls -> &lst - case 3666: state = 3682; break; // &Ls -> &Lst - case 3698: state = 3699; break; // <do -> <dot - case 3715: state = 3716; break; // <ques -> <quest - case 3737: state = 3738; break; // &lver -> &lvert - case 3749: state = 3751; break; // &mal -> &malt - case 3759: state = 3760; break; // &maps -> &mapst - case 3768: state = 3769; break; // &mapstolef -> &mapstoleft - case 3790: state = 3791; break; // &mDDo -> &mDDot - case 3817: state = 3818; break; // &Mellin -> &Mellint - case 3833: state = 3834; break; // &midas -> &midast - case 3839: state = 3840; break; // &middo -> · - case 3879: state = 3882; break; // &ms -> &mst - case 3888: state = 3889; break; // &mul -> &mult - case 3897: state = 4718; break; // &n -> &nt - case 3898: state = 3924; break; // &na -> &nat - case 3902: state = 4721; break; // &N -> &Nt - case 3905: state = 3906; break; // &Nacu -> &Nacut - case 3909: state = 3910; break; // &nacu -> &nacut - case 3960: state = 3961; break; // &ncongdo -> &ncongdot - case 3982: state = 3983; break; // &nedo -> &nedot - case 3986: state = 3987; break; // &Nega -> &Negat - case 4041: state = 4042; break; // &Nes -> &Nest - case 4048: state = 4049; break; // &NestedGrea -> &NestedGreat - case 4055: state = 4056; break; // &NestedGreaterGrea -> &NestedGreaterGreat - case 4074: state = 4075; break; // &nexis -> &nexist - case 4081: state = 4098; break; // &ng -> &ngt - case 4089: state = 4090; break; // &ngeqslan -> &ngeqslant - case 4092: state = 4097; break; // &nG -> &nGt - case 4121: state = 4182; break; // &nl -> &nlt - case 4132: state = 4181; break; // &nL -> &nLt - case 4134: state = 4135; break; // &nLef -> &nLeft - case 4141: state = 4142; break; // &nlef -> &nleft - case 4151: state = 4152; break; // &nLeftrigh -> &nLeftright - case 4161: state = 4162; break; // &nleftrigh -> &nleftright - case 4173: state = 4174; break; // &nleqslan -> &nleqslant - case 4190: state = 4215; break; // &No -> &Not - case 4212: state = 4216; break; // &no -> ¬ - case 4224: state = 4225; break; // &NotCongruen -> &NotCongruent - case 4239: state = 4240; break; // &NotDoubleVer -> &NotDoubleVert - case 4253: state = 4254; break; // &NotElemen -> &NotElement - case 4266: state = 4267; break; // &NotExis -> &NotExist - case 4272: state = 4273; break; // &NotGrea -> &NotGreat - case 4293: state = 4294; break; // &NotGreaterGrea -> &NotGreaterGreat - case 4304: state = 4305; break; // &NotGreaterSlan -> &NotGreaterSlant - case 4336: state = 4337; break; // ¬indo -> ¬indot - case 4345: state = 4346; break; // &NotLef -> &NotLeft - case 4373: state = 4374; break; // &NotLessGrea -> &NotLessGreat - case 4384: state = 4385; break; // &NotLessSlan -> &NotLessSlant - case 4398: state = 4399; break; // &NotNes -> &NotNest - case 4405: state = 4406; break; // &NotNestedGrea -> &NotNestedGreat - case 4412: state = 4413; break; // &NotNestedGreaterGrea -> &NotNestedGreaterGreat - case 4446: state = 4447; break; // &NotPrecedesSlan -> &NotPrecedesSlant - case 4465: state = 4466; break; // &NotReverseElemen -> &NotReverseElement - case 4469: state = 4470; break; // &NotRigh -> &NotRight - case 4497: state = 4498; break; // &NotSquareSubse -> &NotSquareSubset - case 4508: state = 4509; break; // &NotSquareSuperse -> &NotSquareSuperset - case 4518: state = 4519; break; // &NotSubse -> &NotSubset - case 4539: state = 4540; break; // &NotSucceedsSlan -> &NotSucceedsSlant - case 4555: state = 4556; break; // &NotSuperse -> &NotSuperset - case 4588: state = 4589; break; // &NotVer -> &NotVert - case 4599: state = 4607; break; // &npar -> &npart - case 4611: state = 4612; break; // &npolin -> &npolint - case 4621: state = 4649; break; // &nr -> &nrt - case 4633: state = 4634; break; // &nRigh -> &nRight - case 4642: state = 4643; break; // &nrigh -> &nright - case 4665: state = 4666; break; // &nshor -> &nshort - case 4700: state = 4701; break; // &nsubse -> &nsubset - case 4713: state = 4714; break; // &nsupse -> &nsupset - case 4741: state = 4742; break; // &ntrianglelef -> &ntriangleleft - case 4748: state = 4749; break; // &ntrianglerigh -> &ntriangleright - case 4780: state = 4782; break; // &nvg -> &nvgt - case 4792: state = 4797; break; // &nvl -> &nvlt - case 4801: state = 4805; break; // &nvr -> &nvrt - case 4827: state = 5031; break; // &O -> &Ot - case 4830: state = 4831; break; // &Oacu -> &Oacut - case 4833: state = 5036; break; // &o -> &ot - case 4836: state = 4837; break; // &oacu -> &oacut - case 4839: state = 4840; break; // &oas -> &oast - case 4866: state = 4867; break; // &odo -> &odot - case 4887: state = 4899; break; // &og -> &ogt - case 4906: state = 4907; break; // &oin -> &oint - case 4908: state = 4922; break; // &ol -> &olt - case 4976: state = 4977; break; // &OpenCurlyDoubleQuo -> &OpenCurlyDoubleQuot - case 4981: state = 4982; break; // &OpenCurlyQuo -> &OpenCurlyQuot - case 5070: state = 5071; break; // &OverBracke -> &OverBracket - case 5076: state = 5077; break; // &OverParen -> &OverParent - case 5085: state = 5095; break; // &par -> &part - case 5098: state = 5099; break; // &Par -> &Part - case 5109: state = 5120; break; // &per -> &pert - case 5111: state = 5112; break; // &percn -> &percnt - case 5135: state = 5136; break; // &phmma -> &phmmat - case 5141: state = 5142; break; // &pi -> &pit - case 5159: state = 5185; break; // &plus -> &plust - case 5203: state = 5204; break; // &poin -> &point - case 5206: state = 5207; break; // &pointin -> &pointint - case 5252: state = 5253; break; // &PrecedesSlan -> &PrecedesSlant - case 5301: state = 5302; break; // &Produc -> &Product - case 5316: state = 5326; break; // &prop -> &propt - case 5319: state = 5320; break; // &Propor -> &Proport - case 5355: state = 5356; break; // &qin -> &qint - case 5375: state = 5376; break; // &qua -> &quat - case 5385: state = 5386; break; // &quatin -> &quatint - case 5388: state = 5389; break; // &ques -> &quest - case 5395: state = 5396; break; // &quo -> " - case 5397: state = 5951; break; // &r -> &rt - case 5398: state = 5462; break; // &rA -> &rAt - case 5402: state = 5466; break; // &ra -> &rat - case 5408: state = 5409; break; // &Racu -> &Racut - case 5411: state = 5412; break; // &racu -> &racut - case 5419: state = 5420; break; // &raemp -> &raempt - case 5435: state = 5457; break; // &Rarr -> &Rarrt - case 5439: state = 5459; break; // &rarr -> &rarrt - case 5549: state = 5550; break; // &realpar -> &realpart - case 5552: state = 5553; break; // &rec -> &rect - case 5567: state = 5568; break; // &ReverseElemen -> &ReverseElement - case 5595: state = 5596; break; // &rfish -> &rfisht - case 5619: state = 5620; break; // &Righ -> &Right - case 5631: state = 5632; break; // &RightAngleBracke -> &RightAngleBracket - case 5644: state = 5645; break; // &righ -> &right - case 5645: state = 5778; break; // &right -> &rightt - case 5650: state = 5663; break; // &rightarrow -> &rightarrowt - case 5656: state = 5657; break; // &RightArrowLef -> &RightArrowLeft - case 5685: state = 5686; break; // &RightDoubleBracke -> &RightDoubleBracket - case 5694: state = 5695; break; // &RightDownTeeVec -> &RightDownTeeVect - case 5700: state = 5701; break; // &RightDownVec -> &RightDownVect - case 5727: state = 5728; break; // &rightlef -> &rightleft - case 5746: state = 5747; break; // &rightrigh -> &rightright - case 5774: state = 5775; break; // &RightTeeVec -> &RightTeeVect - case 5782: state = 5783; break; // &rightthree -> &rightthreet - case 5811: state = 5812; break; // &RightUpDownVec -> &RightUpDownVect - case 5820: state = 5821; break; // &RightUpTeeVec -> &RightUpTeeVect - case 5826: state = 5827; break; // &RightUpVec -> &RightUpVect - case 5835: state = 5836; break; // &RightVec -> &RightVect - case 5849: state = 5850; break; // &risingdo -> &risingdot - case 5865: state = 5866; break; // &rmous -> &rmoust - case 5875: state = 5894; break; // &ro -> &rot - case 5912: state = 5913; break; // &rparg -> &rpargt - case 5918: state = 5919; break; // &rppolin -> &rppolint - case 5927: state = 5928; break; // &Rrigh -> &Rright - case 5964: state = 5965; break; // &rtril -> &rtrilt - case 5985: state = 6356; break; // &S -> &St - case 5988: state = 5989; break; // &Sacu -> &Sacut - case 5991: state = 6359; break; // &s -> &st - case 5994: state = 5995; break; // &sacu -> &sacut - case 6041: state = 6042; break; // &scpolin -> &scpolint - case 6049: state = 6050; break; // &sdo -> &sdot - case 6053: state = 6072; break; // &se -> &set - case 6064: state = 6065; break; // &sec -> § - case 6079: state = 6080; break; // &sex -> &sext - case 6106: state = 6107; break; // &Shor -> &Short - case 6119: state = 6120; break; // &ShortLef -> &ShortLeft - case 6127: state = 6128; break; // &shor -> &short - case 6143: state = 6144; break; // &ShortRigh -> &ShortRight - case 6170: state = 6171; break; // &simdo -> &simdot - case 6202: state = 6227; break; // &sm -> &smt - case 6207: state = 6208; break; // &smallse -> &smallset - case 6236: state = 6237; break; // &sof -> &soft - case 6255: state = 6256; break; // &spadesui -> &spadesuit - case 6267: state = 6268; break; // &Sqr -> &Sqrt - case 6274: state = 6275; break; // &sqsubse -> &sqsubset - case 6281: state = 6282; break; // &sqsupse -> &sqsupset - case 6294: state = 6295; break; // &SquareIn -> &SquareInt - case 6300: state = 6301; break; // &SquareIntersec -> &SquareIntersect - case 6309: state = 6310; break; // &SquareSubse -> &SquareSubset - case 6320: state = 6321; break; // &SquareSuperse -> &SquareSuperset - case 6341: state = 6352; break; // &ss -> &sst - case 6344: state = 6345; break; // &sse -> &sset - case 6367: state = 6368; break; // &straigh -> &straight - case 6386: state = 6387; break; // &subdo -> &subdot - case 6391: state = 6392; break; // &subedo -> &subedot - case 6395: state = 6396; break; // &submul -> &submult - case 6409: state = 6410; break; // &Subse -> &Subset - case 6412: state = 6413; break; // &subse -> &subset - case 6460: state = 6461; break; // &SucceedsSlan -> &SucceedsSlant - case 6493: state = 6494; break; // &SuchTha -> &SuchThat - case 6505: state = 6506; break; // &supdo -> &supdot - case 6513: state = 6514; break; // &supedo -> &supedot - case 6518: state = 6519; break; // &Superse -> &Superset - case 6537: state = 6538; break; // &supmul -> &supmult - case 6547: state = 6548; break; // &Supse -> &Supset - case 6550: state = 6551; break; // &supse -> &supset - case 6590: state = 6591; break; // &targe -> &target - case 6618: state = 6619; break; // &tdo -> &tdot - case 6630: state = 6648; break; // &the -> &thet - case 6635: state = 6646; break; // &The -> &Thet - case 6730: state = 6731; break; // &tin -> &tint - case 6737: state = 6738; break; // &topbo -> &topbot - case 6764: state = 6811; break; // &tri -> &trit - case 6776: state = 6777; break; // &trianglelef -> &triangleleft - case 6784: state = 6785; break; // &trianglerigh -> &triangleright - case 6789: state = 6790; break; // &trido -> &tridot - case 6803: state = 6804; break; // &TripleDo -> &TripleDot - case 6821: state = 6837; break; // &Ts -> &Tst - case 6824: state = 6841; break; // &ts -> &tst - case 6847: state = 6848; break; // &twix -> &twixt - case 6856: state = 6857; break; // &twoheadlef -> &twoheadleft - case 6866: state = 6867; break; // &twoheadrigh -> &twoheadright - case 6873: state = 7196; break; // &U -> &Ut - case 6876: state = 6877; break; // &Uacu -> &Uacut - case 6879: state = 7192; break; // &u -> &ut - case 6882: state = 6883; break; // &uacu -> &uacut - case 6939: state = 6940; break; // &ufish -> &ufisht - case 6965: state = 6975; break; // &ul -> &ult - case 6999: state = 7000; break; // &UnderBracke -> &UnderBracket - case 7005: state = 7006; break; // &UnderParen -> &UnderParent - case 7107: state = 7108; break; // &upharpoonlef -> &upharpoonleft - case 7112: state = 7113; break; // &upharpoonrigh -> &upharpoonright - case 7122: state = 7123; break; // &UpperLef -> &UpperLeft - case 7132: state = 7133; break; // &UpperRigh -> &UpperRight - case 7166: state = 7183; break; // &ur -> &urt - case 7194: state = 7195; break; // &utdo -> &utdot - case 7227: state = 7228; break; // &vangr -> &vangrt - case 7229: state = 7286; break; // &var -> &vart - case 7243: state = 7244; break; // &varno -> &varnot - case 7255: state = 7256; break; // &varprop -> &varpropt - case 7272: state = 7273; break; // &varsubse -> &varsubset - case 7280: state = 7281; break; // &varsupse -> &varsupset - case 7288: state = 7289; break; // &varthe -> &varthet - case 7300: state = 7301; break; // &vartrianglelef -> &vartriangleleft - case 7305: state = 7306; break; // &vartrianglerigh -> &vartriangleright - case 7349: state = 7357; break; // &Ver -> &Vert - case 7353: state = 7358; break; // &ver -> &vert - case 7375: state = 7376; break; // &VerticalSepara -> &VerticalSeparat - case 7398: state = 7399; break; // &vl -> &vlt - case 7417: state = 7418; break; // &vr -> &vrt - case 7486: state = 7487; break; // &wrea -> &wreat - case 7504: state = 7505; break; // &xd -> &xdt - case 7535: state = 7547; break; // &xo -> &xot - case 7537: state = 7538; break; // &xodo -> &xodot - case 7568: state = 7573; break; // &xu -> &xut - case 7587: state = 7588; break; // &Yacu -> &Yacut - case 7593: state = 7594; break; // &yacu -> &yacut - case 7648: state = 7649; break; // &Zacu -> &Zacut - case 7654: state = 7655; break; // &zacu -> &zacut - case 7670: state = 7671; break; // &Zdo -> &Zdot - case 7673: state = 7674; break; // &zdo -> &zdot - case 7675: state = 7695; break; // &ze -> &zet - case 7676: state = 7677; break; // &zee -> &zeet - case 7680: state = 7693; break; // &Ze -> &Zet - case 7685: state = 7686; break; // &ZeroWid -> &ZeroWidt - default: return false; - } - break; - case 'u': - switch (state) { - case 0: state = 6879; break; // & -> &u - case 1: state = 206; break; // &A -> &Au - case 3: state = 4; break; // &Aac -> &Aacu - case 7: state = 209; break; // &a -> &au - case 9: state = 10; break; // &aac -> &aacu - case 23: state = 33; break; // &ac -> &acu - case 158: state = 159; break; // &ApplyF -> &ApplyFu - case 222: state = 568; break; // &b -> &bu - case 247: state = 577; break; // &B -> &Bu - case 285: state = 286; break; // &bdq -> &bdqu - case 290: state = 291; break; // &beca -> &becau - case 295: state = 296; break; // &Beca -> &Becau - case 310: state = 311; break; // &berno -> &bernou - case 314: state = 315; break; // &Berno -> &Bernou - case 334: state = 378; break; // &big -> &bigu - case 335: state = 341; break; // &bigc -> &bigcu - case 348: state = 349; break; // &bigopl -> &bigoplu - case 358: state = 359; break; // &bigsqc -> &bigsqcu - case 371: state = 376; break; // &bigtriangle -> &bigtriangleu - case 380: state = 381; break; // &bigupl -> &biguplu - case 408: state = 409; break; // &blacksq -> &blacksqu - case 447: state = 448; break; // &bneq -> &bnequ - case 470: state = 511; break; // &box -> &boxu - case 484: state = 491; break; // &boxH -> &boxHu - case 485: state = 493; break; // &boxh -> &boxhu - case 496: state = 497; break; // &boxmin -> &boxminu - case 500: state = 501; break; // &boxpl -> &boxplu - case 565: state = 566; break; // &bsolhs -> &bsolhsu - case 583: state = 1004; break; // &C -> &Cu - case 585: state = 586; break; // &Cac -> &Cacu - case 589: state = 987; break; // &c -> &cu - case 591: state = 592; break; // &cac -> &cacu - case 602: state = 603; break; // &capbrc -> &capbrcu - case 605: state = 608; break; // &capc -> &capcu - case 641: state = 672; break; // &cc -> &ccu - case 777: state = 778; break; // &CircleMin -> &CircleMinu - case 781: state = 782; break; // &CirclePl -> &CirclePlu - case 815: state = 816; break; // &ClockwiseConto -> &ClockwiseContou - case 828: state = 829; break; // &CloseC -> &CloseCu - case 834: state = 835; break; // &CloseCurlyDo -> &CloseCurlyDou - case 839: state = 840; break; // &CloseCurlyDoubleQ -> &CloseCurlyDoubleQu - case 844: state = 845; break; // &CloseCurlyQ -> &CloseCurlyQu - case 849: state = 850; break; // &cl -> &clu - case 852: state = 853; break; // &clubs -> &clubsu - case 856: state = 932; break; // &Co -> &Cou - case 890: state = 891; break; // &Congr -> &Congru - case 902: state = 903; break; // &Conto -> &Contou - case 922: state = 923; break; // &Coprod -> &Coprodu - case 950: state = 951; break; // &CounterClockwiseConto -> &CounterClockwiseContou - case 975: state = 978; break; // &cs -> &csu - case 1015: state = 1018; break; // &cupc -> &cupcu - case 1039: state = 1040; break; // &curlyeqs -> &curlyeqsu - case 1097: state = 1631; break; // &d -> &du - case 1202: state = 1203; break; // &DiacriticalAc -> &DiacriticalAcu - case 1207: state = 1209; break; // &DiacriticalDo -> &DiacriticalDou - case 1214: state = 1215; break; // &DiacriticalDoubleAc -> &DiacriticalDoubleAcu - case 1238: state = 1239; break; // &diamonds -> &diamondsu - case 1291: state = 1331; break; // &do -> &dou - case 1296: state = 1343; break; // &Do -> &Dou - case 1312: state = 1313; break; // &DotEq -> &DotEqu - case 1318: state = 1319; break; // &dotmin -> &dotminu - case 1322: state = 1323; break; // &dotpl -> &dotplu - case 1326: state = 1327; break; // &dotsq -> &dotsqu - case 1351: state = 1352; break; // &DoubleConto -> &DoubleContou - case 1656: state = 1917; break; // &E -> &Eu - case 1658: state = 1659; break; // &Eac -> &Eacu - case 1662: state = 1920; break; // &e -> &eu - case 1664: state = 1665; break; // &eac -> &eacu - case 1769: state = 1770; break; // &EmptySmallSq -> &EmptySmallSqu - case 1785: state = 1786; break; // &EmptyVerySmallSq -> &EmptyVerySmallSqu - case 1818: state = 1819; break; // &epl -> &eplu - case 1833: state = 1860; break; // &eq -> &equ - case 1856: state = 1857; break; // &Eq -> &Equ - case 1877: state = 1878; break; // &Equilibri -> &Equilibriu - case 2016: state = 2017; break; // &FilledSmallSq -> &FilledSmallSqu - case 2031: state = 2032; break; // &FilledVerySmallSq -> &FilledVerySmallSqu - case 2052: state = 2068; break; // &Fo -> &Fou - case 2120: state = 2121; break; // &gac -> &gacu - case 2239: state = 2240; break; // &GreaterEq -> &GreaterEqu - case 2247: state = 2248; break; // &GreaterF -> &GreaterFu - case 2252: state = 2253; break; // &GreaterFullEq -> &GreaterFullEqu - case 2273: state = 2274; break; // &GreaterSlantEq -> &GreaterSlantEqu - case 2306: state = 2307; break; // >q -> >qu - case 2351: state = 2508; break; // &H -> &Hu - case 2401: state = 2402; break; // &hearts -> &heartsu - case 2515: state = 2516; break; // &HumpDownH -> &HumpDownHu - case 2520: state = 2521; break; // &HumpEq -> &HumpEqu - case 2525: state = 2526; break; // &hyb -> &hybu - case 2533: state = 2765; break; // &I -> &Iu - case 2535: state = 2536; break; // &Iac -> &Iacu - case 2539: state = 2769; break; // &i -> &iu - case 2541: state = 2542; break; // &iac -> &iacu - case 2735: state = 2736; break; // &iq -> &iqu - case 2777: state = 2817; break; // &J -> &Ju - case 2782: state = 2821; break; // &j -> &ju - case 2881: state = 3724; break; // &l -> &lu - case 2888: state = 2889; break; // &Lac -> &Lacu - case 2893: state = 2894; break; // &lac -> &lacu - case 2931: state = 2932; break; // &laq -> &laqu - case 2985: state = 2987; break; // &lbrksl -> &lbrkslu - case 2993: state = 3008; break; // &lc -> &lcu - case 3015: state = 3016; break; // &ldq -> &ldqu - case 3019: state = 3024; break; // &ldr -> &ldru - case 3089: state = 3090; break; // &LeftDo -> &LeftDou - case 3132: state = 3137; break; // &leftharpoon -> &leftharpoonu - case 3189: state = 3190; break; // &leftrightsq -> &leftrightsqu - case 3239: state = 3240; break; // &LeftTriangleEq -> &LeftTriangleEqu - case 3324: state = 3325; break; // &LessEq -> &LessEqu - case 3335: state = 3336; break; // &LessF -> &LessFu - case 3340: state = 3341; break; // &LessFullEq -> &LessFullEqu - case 3367: state = 3368; break; // &LessSlantEq -> &LessSlantEqu - case 3395: state = 3397; break; // &lhar -> &lharu - case 3444: state = 3445; break; // &lmo -> &lmou - case 3595: state = 3596; break; // &lopl -> &loplu - case 3663: state = 3664; break; // &lsaq -> &lsaqu - case 3677: state = 3679; break; // &lsq -> &lsqu - case 3712: state = 3713; break; // <q -> <qu - case 3725: state = 3731; break; // &lur -> &luru - case 3745: state = 3887; break; // &m -> &mu - case 3755: state = 3886; break; // &M -> &Mu - case 3761: state = 3770; break; // &mapsto -> &mapstou - case 3794: state = 3795; break; // &meas -> &measu - case 3806: state = 3807; break; // &Medi -> &Mediu - case 3841: state = 3842; break; // &min -> &minu - case 3845: state = 3846; break; // &minusd -> &minusdu - case 3848: state = 3849; break; // &Min -> &Minu - case 3852: state = 3853; break; // &MinusPl -> &MinusPlu - case 3862: state = 3863; break; // &mnpl -> &mnplu - case 3897: state = 4753; break; // &n -> &nu - case 3902: state = 4752; break; // &N -> &Nu - case 3904: state = 3905; break; // &Nac -> &Nacu - case 3908: state = 3909; break; // &nac -> &nacu - case 3924: state = 3925; break; // &nat -> &natu - case 3930: state = 3933; break; // &nb -> &nbu - case 3937: state = 3962; break; // &nc -> &ncu - case 3994: state = 3995; break; // &NegativeMedi -> &NegativeMediu - case 4031: state = 4032; break; // &neq -> &nequ - case 4217: state = 4226; break; // &NotC -> &NotCu - case 4221: state = 4222; break; // &NotCongr -> &NotCongru - case 4232: state = 4233; break; // &NotDo -> &NotDou - case 4255: state = 4256; break; // &NotEq -> &NotEqu - case 4277: state = 4278; break; // &NotGreaterEq -> &NotGreaterEqu - case 4281: state = 4282; break; // &NotGreaterF -> &NotGreaterFu - case 4286: state = 4287; break; // &NotGreaterFullEq -> &NotGreaterFullEqu - case 4307: state = 4308; break; // &NotGreaterSlantEq -> &NotGreaterSlantEqu - case 4316: state = 4317; break; // &NotH -> &NotHu - case 4324: state = 4325; break; // &NotHumpDownH -> &NotHumpDownHu - case 4329: state = 4330; break; // &NotHumpEq -> &NotHumpEqu - case 4359: state = 4360; break; // &NotLeftTriangleEq -> &NotLeftTriangleEqu - case 4366: state = 4367; break; // &NotLessEq -> &NotLessEqu - case 4387: state = 4388; break; // &NotLessSlantEq -> &NotLessSlantEqu - case 4439: state = 4440; break; // &NotPrecedesEq -> &NotPrecedesEqu - case 4449: state = 4450; break; // &NotPrecedesSlantEq -> &NotPrecedesSlantEqu - case 4483: state = 4484; break; // &NotRightTriangleEq -> &NotRightTriangleEqu - case 4487: state = 4515; break; // &NotS -> &NotSu - case 4488: state = 4489; break; // &NotSq -> &NotSqu - case 4493: state = 4494; break; // &NotSquareS -> &NotSquareSu - case 4500: state = 4501; break; // &NotSquareSubsetEq -> &NotSquareSubsetEqu - case 4511: state = 4512; break; // &NotSquareSupersetEq -> &NotSquareSupersetEqu - case 4521: state = 4522; break; // &NotSubsetEq -> &NotSubsetEqu - case 4532: state = 4533; break; // &NotSucceedsEq -> &NotSucceedsEqu - case 4542: state = 4543; break; // &NotSucceedsSlantEq -> &NotSucceedsSlantEqu - case 4558: state = 4559; break; // &NotSupersetEq -> &NotSupersetEqu - case 4568: state = 4569; break; // &NotTildeEq -> &NotTildeEqu - case 4572: state = 4573; break; // &NotTildeF -> &NotTildeFu - case 4577: state = 4578; break; // &NotTildeFullEq -> &NotTildeFullEqu - case 4614: state = 4615; break; // &nprc -> &nprcu - case 4653: state = 4695; break; // &ns -> &nsu - case 4655: state = 4656; break; // &nscc -> &nsccu - case 4689: state = 4690; break; // &nsqs -> &nsqsu - case 4827: state = 5049; break; // &O -> &Ou - case 4829: state = 4830; break; // &Oac -> &Oacu - case 4833: state = 5052; break; // &o -> &ou - case 4835: state = 4836; break; // &oac -> &oacu - case 4948: state = 4949; break; // &omin -> &ominu - case 4963: state = 4964; break; // &OpenC -> &OpenCu - case 4969: state = 4970; break; // &OpenCurlyDo -> &OpenCurlyDou - case 4974: state = 4975; break; // &OpenCurlyDoubleQ -> &OpenCurlyDoubleQu - case 4979: state = 4980; break; // &OpenCurlyQ -> &OpenCurlyQu - case 4987: state = 4988; break; // &opl -> &oplu - case 5083: state = 5343; break; // &p -> &pu - case 5150: state = 5158; break; // &pl -> &plu - case 5168: state = 5170; break; // &plusd -> &plusdu - case 5172: state = 5173; break; // &Pl -> &Plu - case 5177: state = 5178; break; // &PlusMin -> &PlusMinu - case 5201: state = 5212; break; // &po -> &pou - case 5216: state = 5331; break; // &pr -> &pru - case 5219: state = 5220; break; // &prc -> &prcu - case 5231: state = 5232; break; // &precc -> &preccu - case 5245: state = 5246; break; // &PrecedesEq -> &PrecedesEqu - case 5255: state = 5256; break; // &PrecedesSlantEq -> &PrecedesSlantEqu - case 5299: state = 5300; break; // &Prod -> &Produ - case 5312: state = 5313; break; // &profs -> &profsu - case 5351: state = 5374; break; // &q -> &qu - case 5397: state = 5978; break; // &r -> &ru - case 5403: state = 5411; break; // &rac -> &racu - case 5405: state = 5968; break; // &R -> &Ru - case 5407: state = 5408; break; // &Rac -> &Racu - case 5431: state = 5432; break; // &raq -> &raqu - case 5499: state = 5501; break; // &rbrksl -> &rbrkslu - case 5507: state = 5522; break; // &rc -> &rcu - case 5534: state = 5535; break; // &rdq -> &rdqu - case 5569: state = 5570; break; // &ReverseEq -> &ReverseEqu - case 5576: state = 5577; break; // &ReverseEquilibri -> &ReverseEquilibriu - case 5582: state = 5583; break; // &ReverseUpEq -> &ReverseUpEqu - case 5589: state = 5590; break; // &ReverseUpEquilibri -> &ReverseUpEquilibriu - case 5609: state = 5611; break; // &rhar -> &rharu - case 5675: state = 5676; break; // &RightDo -> &RightDou - case 5718: state = 5723; break; // &rightharpoon -> &rightharpoonu - case 5755: state = 5756; break; // &rightsq -> &rightsqu - case 5799: state = 5800; break; // &RightTriangleEq -> &RightTriangleEqu - case 5863: state = 5864; break; // &rmo -> &rmou - case 5887: state = 5899; break; // &Ro -> &Rou - case 5891: state = 5892; break; // &ropl -> &roplu - case 5936: state = 5937; break; // &rsaq -> &rsaqu - case 5946: state = 5948; break; // &rsq -> &rsqu - case 5979: state = 5980; break; // &rul -> &rulu - case 5985: state = 6381; break; // &S -> &Su - case 5987: state = 5988; break; // &Sac -> &Sacu - case 5991: state = 6383; break; // &s -> &su - case 5993: state = 5994; break; // &sac -> &sacu - case 5998: state = 5999; break; // &sbq -> &sbqu - case 6012: state = 6013; break; // &scc -> &sccu - case 6075: state = 6076; break; // &setmin -> &setminu - case 6181: state = 6182; break; // &simpl -> &simplu - case 6211: state = 6212; break; // &smallsetmin -> &smallsetminu - case 6253: state = 6254; break; // &spades -> &spadesu - case 6258: state = 6285; break; // &sq -> &squ - case 6259: state = 6263; break; // &sqc -> &sqcu - case 6266: state = 6286; break; // &Sq -> &Squ - case 6269: state = 6270; break; // &sqs -> &sqsu - case 6305: state = 6306; break; // &SquareS -> &SquareSu - case 6312: state = 6313; break; // &SquareSubsetEq -> &SquareSubsetEqu - case 6323: state = 6324; break; // &SquareSupersetEq -> &SquareSupersetEqu - case 6393: state = 6394; break; // &subm -> &submu - case 6401: state = 6402; break; // &subpl -> &subplu - case 6411: state = 6428; break; // &subs -> &subsu - case 6418: state = 6419; break; // &SubsetEq -> &SubsetEqu - case 6439: state = 6440; break; // &succc -> &succcu - case 6453: state = 6454; break; // &SucceedsEq -> &SucceedsEqu - case 6463: state = 6464; break; // &SucceedsSlantEq -> &SucceedsSlantEqu - case 6507: state = 6508; break; // &supds -> &supdsu - case 6521: state = 6522; break; // &SupersetEq -> &SupersetEqu - case 6526: state = 6529; break; // &suphs -> &suphsu - case 6535: state = 6536; break; // &supm -> &supmu - case 6543: state = 6544; break; // &suppl -> &supplu - case 6549: state = 6561; break; // &sups -> &supsu - case 6584: state = 6592; break; // &Ta -> &Tau - case 6587: state = 6593; break; // &ta -> &tau - case 6705: state = 6706; break; // &TildeEq -> &TildeEqu - case 6709: state = 6710; break; // &TildeF -> &TildeFu - case 6714: state = 6715; break; // &TildeFullEq -> &TildeFullEqu - case 6794: state = 6795; break; // &trimin -> &triminu - case 6806: state = 6807; break; // &tripl -> &triplu - case 6818: state = 6819; break; // &trpezi -> &trpeziu - case 6873: state = 7212; break; // &U -> &Uu - case 6875: state = 6876; break; // &Uac -> &Uacu - case 6879: state = 7208; break; // &u -> &uu - case 6881: state = 6882; break; // &uac -> &uacu - case 7016: state = 7017; break; // &UnionPl -> &UnionPlu - case 7042: state = 7158; break; // &up -> &upu - case 7088: state = 7089; break; // &UpEq -> &UpEqu - case 7095: state = 7096; break; // &UpEquilibri -> &UpEquilibriu - case 7114: state = 7115; break; // &upl -> &uplu - case 7264: state = 7269; break; // &vars -> &varsu - case 7403: state = 7404; break; // &vns -> &vnsu - case 7424: state = 7427; break; // &vs -> &vsu - case 7495: state = 7568; break; // &x -> &xu - case 7496: state = 7502; break; // &xc -> &xcu - case 7544: state = 7545; break; // &xopl -> &xoplu - case 7565: state = 7566; break; // &xsqc -> &xsqcu - case 7570: state = 7571; break; // &xupl -> &xuplu - case 7584: state = 7640; break; // &Y -> &Yu - case 7586: state = 7587; break; // &Yac -> &Yacu - case 7590: state = 7637; break; // &y -> &yu - case 7592: state = 7593; break; // &yac -> &yacu - case 7647: state = 7648; break; // &Zac -> &Zacu - case 7653: state = 7654; break; // &zac -> &zacu - default: return false; - } - break; - case 'v': - switch (state) { - case 0: state = 7223; break; // & -> &v - case 15: state = 16; break; // &Abre -> &Abrev - case 20: state = 21; break; // &abre -> &abrev - case 52: state = 53; break; // &Agra -> &Agrav - case 57: state = 58; break; // &agra -> &agrav - case 91: state = 101; break; // &and -> &andv - case 119: state = 120; break; // &angrt -> &angrtv - case 256: state = 257; break; // &Bar -> &Barv - case 258: state = 259; break; // &bar -> &barv - case 303: state = 304; break; // &bempty -> &bemptyv - case 334: state = 383; break; // &big -> &bigv - case 449: state = 450; break; // &bnequi -> &bnequiv - case 470: state = 519; break; // &box -> &boxv - case 538: state = 539; break; // &Bre -> &Brev - case 541: state = 545; break; // &br -> &brv - case 542: state = 543; break; // &bre -> &brev - case 696: state = 697; break; // &cempty -> &cemptyv - case 987: state = 1070; break; // &cu -> &cuv - case 1026: state = 1054; break; // &cur -> &curv - case 1032: state = 1043; break; // &curly -> &curlyv - case 1115: state = 1119; break; // &dash -> &dashv - case 1117: state = 1118; break; // &Dash -> &Dashv - case 1173: state = 1174; break; // &dempty -> &demptyv - case 1220: state = 1221; break; // &DiacriticalGra -> &DiacriticalGrav - case 1228: state = 1263; break; // &di -> &div - case 1497: state = 1498; break; // &DownBre -> &DownBrev - case 1717: state = 1718; break; // &Egra -> &Egrav - case 1721: state = 1722; break; // &egra -> &egrav - case 1756: state = 1774; break; // &empty -> &emptyv - case 1822: state = 1832; break; // &epsi -> &epsiv - case 1833: state = 1884; break; // &eq -> &eqv - case 1880: state = 1881; break; // &equi -> &equiv - case 2066: state = 2067; break; // &fork -> &forkv - case 2118: state = 2341; break; // &g -> &gv - case 2137: state = 2138; break; // &Gbre -> &Gbrev - case 2142: state = 2143; break; // &gbre -> &gbrev - case 2229: state = 2230; break; // &gra -> &grav - case 2574: state = 2575; break; // &Igra -> &Igrav - case 2579: state = 2580; break; // &igra -> &igrav - case 2656: state = 2693; break; // &In -> &Inv - case 2747: state = 2754; break; // &isin -> &isinv - case 2752: state = 2753; break; // &isins -> &isinsv - case 2834: state = 2835; break; // &kappa -> &kappav - case 2881: state = 3735; break; // &l -> &lv - case 2901: state = 2902; break; // &laempty -> &laemptyv - case 3897: state = 4760; break; // &n -> &nv - case 3988: state = 3989; break; // &Negati -> &Negativ - case 4033: state = 4034; break; // &nequi -> &nequiv - case 4097: state = 4100; break; // &nGt -> &nGtv - case 4111: state = 4114; break; // &ni -> &niv - case 4181: state = 4186; break; // &nLt -> &nLtv - case 4334: state = 4339; break; // ¬in -> ¬inv - case 4425: state = 4426; break; // ¬ni -> ¬niv - case 4454: state = 4455; break; // &NotRe -> &NotRev - case 4827: state = 5059; break; // &O -> &Ov - case 4833: state = 5055; break; // &o -> &ov - case 4864: state = 4865; break; // &odi -> &odiv - case 4892: state = 4893; break; // &Ogra -> &Ograv - case 4896: state = 4897; break; // &ogra -> &ograv - case 4991: state = 5013; break; // &or -> &orv - case 5131: state = 5132; break; // &phi -> &phiv - case 5141: state = 5149; break; // &pi -> &piv - case 5156: state = 5157; break; // &plank -> &plankv - case 5421: state = 5422; break; // &raempty -> &raemptyv - case 5540: state = 5557; break; // &Re -> &Rev - case 5615: state = 5616; break; // &rho -> &rhov - case 6165: state = 6167; break; // &sigma -> &sigmav - case 6649: state = 6653; break; // &theta -> &thetav - case 6904: state = 6905; break; // &Ubre -> &Ubrev - case 6907: state = 6908; break; // &ubre -> &ubrev - case 6946: state = 6947; break; // &Ugra -> &Ugrav - case 6951: state = 6952; break; // &ugra -> &ugrav - case 7307: state = 7436; break; // &V -> &Vv - case 7313: state = 7314; break; // &vBar -> &vBarv - case 7495: state = 7576; break; // &x -> &xv - default: return false; - } - break; - case 'w': - switch (state) { - case 0: state = 7452; break; // & -> &w - case 7: state = 212; break; // &a -> &aw - case 256: state = 262; break; // &Bar -> &Barw - case 258: state = 265; break; // &bar -> &barw - case 322: state = 325; break; // &bet -> &betw - case 334: state = 386; break; // &big -> &bigw - case 373: state = 374; break; // &bigtriangledo -> &bigtriangledow - case 394: state = 395; break; // &bkaro -> &bkarow - case 422: state = 423; break; // &blacktriangledo -> &blacktriangledow - case 459: state = 466; break; // &bo -> &bow - case 589: state = 1076; break; // &c -> &cw - case 742: state = 743; break; // &circlearro -> &circlearrow - case 806: state = 807; break; // &Clock -> &Clockw - case 941: state = 942; break; // &CounterClock -> &CounterClockw - case 987: state = 1073; break; // &cu -> &cuw - case 1032: state = 1046; break; // &curly -> &curlyw - case 1059: state = 1060; break; // &curvearro -> &curvearrow - case 1097: state = 1638; break; // &d -> &dw - case 1124: state = 1125; break; // &dbkaro -> &dbkarow - case 1291: state = 1478; break; // &do -> &dow - case 1296: state = 1466; break; // &Do -> &Dow - case 1337: state = 1338; break; // &doublebar -> &doublebarw - case 1363: state = 1365; break; // &DoubleDo -> &DoubleDow - case 1370: state = 1371; break; // &DoubleDownArro -> &DoubleDownArrow - case 1379: state = 1380; break; // &DoubleLeftArro -> &DoubleLeftArrow - case 1389: state = 1390; break; // &DoubleLeftRightArro -> &DoubleLeftRightArrow - case 1404: state = 1405; break; // &DoubleLongLeftArro -> &DoubleLongLeftArrow - case 1414: state = 1415; break; // &DoubleLongLeftRightArro -> &DoubleLongLeftRightArrow - case 1424: state = 1425; break; // &DoubleLongRightArro -> &DoubleLongRightArrow - case 1434: state = 1435; break; // &DoubleRightArro -> &DoubleRightArrow - case 1444: state = 1445; break; // &DoubleUpArro -> &DoubleUpArrow - case 1447: state = 1448; break; // &DoubleUpDo -> &DoubleUpDow - case 1453: state = 1454; break; // &DoubleUpDownArro -> &DoubleUpDownArrow - case 1471: state = 1472; break; // &DownArro -> &DownArrow - case 1476: state = 1477; break; // &Downarro -> &Downarrow - case 1483: state = 1484; break; // &downarro -> &downarrow - case 1493: state = 1494; break; // &DownArrowUpArro -> &DownArrowUpArrow - case 1501: state = 1502; break; // &downdo -> &downdow - case 1507: state = 1508; break; // &downdownarro -> &downdownarrow - case 1588: state = 1589; break; // &DownTeeArro -> &DownTeeArrow - case 1595: state = 1596; break; // &drbkaro -> &drbkarow - case 2109: state = 2110; break; // &fro -> &frow - case 2380: state = 2384; break; // &harr -> &harrw - case 2429: state = 2435; break; // &hks -> &hksw - case 2433: state = 2434; break; // &hksearo -> &hksearow - case 2438: state = 2439; break; // &hkswaro -> &hkswarow - case 2457: state = 2458; break; // &hookleftarro -> &hookleftarrow - case 2467: state = 2468; break; // &hookrightarro -> &hookrightarrow - case 2512: state = 2513; break; // &HumpDo -> &HumpDow - case 3050: state = 3051; break; // &LeftArro -> &LeftArrow - case 3055: state = 3056; break; // &Leftarro -> &Leftarrow - case 3062: state = 3063; break; // &leftarro -> &leftarrow - case 3075: state = 3076; break; // &LeftArrowRightArro -> &LeftArrowRightArrow - case 3089: state = 3101; break; // &LeftDo -> &LeftDow - case 3134: state = 3135; break; // &leftharpoondo -> &leftharpoondow - case 3146: state = 3147; break; // &leftleftarro -> &leftleftarrow - case 3157: state = 3158; break; // &LeftRightArro -> &LeftRightArrow - case 3167: state = 3168; break; // &Leftrightarro -> &Leftrightarrow - case 3177: state = 3178; break; // &leftrightarro -> &leftrightarrow - case 3196: state = 3197; break; // &leftrightsquigarro -> &leftrightsquigarrow - case 3210: state = 3211; break; // &LeftTeeArro -> &LeftTeeArrow - case 3246: state = 3247; break; // &LeftUpDo -> &LeftUpDow - case 3425: state = 3426; break; // &Lleftarro -> &Lleftarrow - case 3466: state = 3603; break; // &lo -> &low - case 3475: state = 3610; break; // &Lo -> &Low - case 3485: state = 3486; break; // &LongLeftArro -> &LongLeftArrow - case 3494: state = 3495; break; // &Longleftarro -> &Longleftarrow - case 3505: state = 3506; break; // &longleftarro -> &longleftarrow - case 3515: state = 3516; break; // &LongLeftRightArro -> &LongLeftRightArrow - case 3525: state = 3526; break; // &Longleftrightarro -> &Longleftrightarrow - case 3535: state = 3536; break; // &longleftrightarro -> &longleftrightarrow - case 3551: state = 3552; break; // &LongRightArro -> &LongRightArrow - case 3561: state = 3562; break; // &Longrightarro -> &Longrightarrow - case 3571: state = 3572; break; // &longrightarro -> &longrightarrow - case 3578: state = 3579; break; // &looparro -> &looparrow - case 3620: state = 3621; break; // &LowerLeftArro -> &LowerLeftArrow - case 3630: state = 3631; break; // &LowerRightArro -> &LowerRightArrow - case 3763: state = 3764; break; // &mapstodo -> &mapstodow - case 3897: state = 4812; break; // &n -> &nw - case 3979: state = 3980; break; // &nearro -> &nearrow - case 3984: state = 4067; break; // &Ne -> &New - case 4139: state = 4140; break; // &nLeftarro -> &nLeftarrow - case 4146: state = 4147; break; // &nleftarro -> &nleftarrow - case 4156: state = 4157; break; // &nLeftrightarro -> &nLeftrightarrow - case 4166: state = 4167; break; // &nleftrightarro -> &nleftrightarrow - case 4321: state = 4322; break; // &NotHumpDo -> &NotHumpDow - case 4627: state = 4629; break; // &nrarr -> &nrarrw - case 4638: state = 4639; break; // &nRightarro -> &nRightarrow - case 4647: state = 4648; break; // &nrightarro -> &nrightarrow - case 4821: state = 4822; break; // &nwarro -> &nwarrow - case 5185: state = 5186; break; // &plust -> &plustw - case 5439: state = 5461; break; // &rarr -> &rarrw - case 5635: state = 5636; break; // &RightArro -> &RightArrow - case 5640: state = 5641; break; // &Rightarro -> &Rightarrow - case 5649: state = 5650; break; // &rightarro -> &rightarrow - case 5661: state = 5662; break; // &RightArrowLeftArro -> &RightArrowLeftArrow - case 5675: state = 5687; break; // &RightDo -> &RightDow - case 5720: state = 5721; break; // &rightharpoondo -> &rightharpoondow - case 5732: state = 5733; break; // &rightleftarro -> &rightleftarrow - case 5751: state = 5752; break; // &rightrightarro -> &rightrightarrow - case 5762: state = 5763; break; // &rightsquigarro -> &rightsquigarrow - case 5770: state = 5771; break; // &RightTeeArro -> &RightTeeArrow - case 5806: state = 5807; break; // &RightUpDo -> &RightUpDow - case 5932: state = 5933; break; // &Rrightarro -> &Rrightarrow - case 5991: state = 6564; break; // &s -> &sw - case 6062: state = 6063; break; // &searro -> &searrow - case 6068: state = 6069; break; // &ses -> &sesw - case 6085: state = 6086; break; // &sfro -> &sfrow - case 6109: state = 6110; break; // &ShortDo -> &ShortDow - case 6115: state = 6116; break; // &ShortDownArro -> &ShortDownArrow - case 6124: state = 6125; break; // &ShortLeftArro -> &ShortLeftArrow - case 6148: state = 6149; break; // &ShortRightArro -> &ShortRightArrow - case 6155: state = 6156; break; // &ShortUpArro -> &ShortUpArrow - case 6573: state = 6574; break; // &swarro -> &swarrow - case 6575: state = 6576; break; // &swn -> &swnw - case 6586: state = 6845; break; // &t -> &tw - case 6771: state = 6772; break; // &triangledo -> &triangledow - case 6861: state = 6862; break; // &twoheadleftarro -> &twoheadleftarrow - case 6871: state = 6872; break; // &twoheadrightarro -> &twoheadrightarrow - case 6879: state = 7217; break; // &u -> &uw - case 7035: state = 7036; break; // &UpArro -> &UpArrow - case 7040: state = 7041; break; // &Uparro -> &Uparrow - case 7046: state = 7047; break; // &uparro -> &uparrow - case 7052: state = 7053; break; // &UpArrowDo -> &UpArrowDow - case 7058: state = 7059; break; // &UpArrowDownArro -> &UpArrowDownArrow - case 7061: state = 7062; break; // &UpDo -> &UpDow - case 7067: state = 7068; break; // &UpDownArro -> &UpDownArrow - case 7070: state = 7071; break; // &Updo -> &Updow - case 7076: state = 7077; break; // &Updownarro -> &Updownarrow - case 7079: state = 7080; break; // &updo -> &updow - case 7085: state = 7086; break; // &updownarro -> &updownarrow - case 7127: state = 7128; break; // &UpperLeftArro -> &UpperLeftArrow - case 7137: state = 7138; break; // &UpperRightArro -> &UpperRightArrow - case 7156: state = 7157; break; // &UpTeeArro -> &UpTeeArrow - case 7163: state = 7164; break; // &upuparro -> &upuparrow - case 7495: state = 7579; break; // &x -> &xw - case 7651: state = 7725; break; // &z -> &zw - default: return false; - } - break; - case 'x': - switch (state) { - case 0: state = 7495; break; // & -> &x - case 168: state = 169; break; // &appro -> &approx - case 459: state = 470; break; // &bo -> &box - case 472: state = 473; break; // &boxbo -> &boxbox - case 875: state = 880; break; // &comple -> &complex - case 1275: state = 1276; break; // &divon -> &divonx - case 1656: state = 1931; break; // &E -> &Ex - case 1662: state = 1925; break; // &e -> &ex - case 2213: state = 2214; break; // &gnappro -> &gnapprox - case 2316: state = 2317; break; // >rappro -> >rapprox - case 2561: state = 2564; break; // &ie -> &iex - case 3307: state = 3308; break; // &lessappro -> &lessapprox - case 3457: state = 3458; break; // &lnappro -> &lnapprox - case 3922: state = 3923; break; // &nappro -> &napprox - case 3970: state = 4072; break; // &ne -> &nex - case 4248: state = 4264; break; // &NotE -> &NotEx - case 5229: state = 5230; break; // &precappro -> &precapprox - case 5271: state = 5272; break; // &precnappro -> &precnapprox - case 5397: state = 5984; break; // &r -> &rx - case 6053: state = 6079; break; // &se -> &sex - case 6437: state = 6438; break; // &succappro -> &succapprox - case 6479: state = 6480; break; // &succnappro -> &succnapprox - case 6661: state = 6662; break; // &thickappro -> &thickapprox - case 6846: state = 6847; break; // &twi -> &twix - default: return false; - } - break; - case 'y': - switch (state) { - case 0: state = 7590; break; // & -> &y - case 23: state = 37; break; // &ac -> &acy - case 26: state = 36; break; // &Ac -> &Acy - case 63: state = 64; break; // &alefs -> &alefsy - case 156: state = 157; break; // &Appl -> &Apply - case 183: state = 191; break; // &as -> &asy - case 277: state = 283; break; // &bc -> &bcy - case 281: state = 282; break; // &Bc -> &Bcy - case 302: state = 303; break; // &bempt -> &bempty - case 584: state = 636; break; // &Ca -> &Cay - case 589: state = 1086; break; // &c -> &cy - case 638: state = 639; break; // &Cayle -> &Cayley - case 695: state = 696; break; // &cempt -> &cempty - case 717: state = 718; break; // &CHc -> &CHcy - case 720: state = 721; break; // &chc -> &chcy - case 831: state = 832; break; // &CloseCurl -> &CloseCurly - case 915: state = 929; break; // &cop -> © - case 1031: state = 1032; break; // &curl -> &curly - case 1089: state = 1090; break; // &cylct -> &cylcty - case 1129: state = 1139; break; // &Dc -> &Dcy - case 1134: state = 1140; break; // &dc -> &dcy - case 1172: state = 1173; break; // &dempt -> &dempty - case 1278: state = 1279; break; // &DJc -> &DJcy - case 1281: state = 1282; break; // &djc -> &djcy - case 1608: state = 1613; break; // &dsc -> &dscy - case 1611: state = 1612; break; // &DSc -> &DScy - case 1645: state = 1646; break; // &DZc -> &DZcy - case 1648: state = 1649; break; // &dzc -> &dzcy - case 1672: state = 1692; break; // &Ec -> &Ecy - case 1677: state = 1693; break; // &ec -> &ecy - case 1755: state = 1756; break; // &empt -> &empty - case 1761: state = 1762; break; // &Empt -> &Empty - case 1777: state = 1778; break; // &EmptyVer -> &EmptyVery - case 1978: state = 1979; break; // &Fc -> &Fcy - case 1980: state = 1981; break; // &fc -> &fcy - case 2023: state = 2024; break; // &FilledVer -> &FilledVery - case 2145: state = 2157; break; // &Gc -> &Gcy - case 2153: state = 2158; break; // &gc -> &gcy - case 2199: state = 2200; break; // &GJc -> &GJcy - case 2202: state = 2203; break; // &gjc -> &gjcy - case 2356: state = 2524; break; // &h -> &hy - case 2371: state = 2372; break; // &HARDc -> &HARDcy - case 2375: state = 2376; break; // &hardc -> &hardcy - case 2545: state = 2554; break; // &ic -> &icy - case 2546: state = 2553; break; // &Ic -> &Icy - case 2559: state = 2560; break; // &IEc -> &IEcy - case 2562: state = 2563; break; // &iec -> &iecy - case 2618: state = 2619; break; // &Imaginar -> &Imaginary - case 2711: state = 2712; break; // &IOc -> &IOcy - case 2714: state = 2715; break; // &ioc -> &iocy - case 2767: state = 2768; break; // &Iukc -> &Iukcy - case 2771: state = 2772; break; // &iukc -> &iukcy - case 2778: state = 2787; break; // &Jc -> &Jcy - case 2783: state = 2788; break; // &jc -> &jcy - case 2811: state = 2812; break; // &Jserc -> &Jsercy - case 2815: state = 2816; break; // &jserc -> &jsercy - case 2819: state = 2820; break; // &Jukc -> &Jukcy - case 2823: state = 2824; break; // &jukc -> &jukcy - case 2836: state = 2846; break; // &Kc -> &Kcy - case 2841: state = 2847; break; // &kc -> &kcy - case 2858: state = 2859; break; // &KHc -> &KHcy - case 2861: state = 2862; break; // &khc -> &khcy - case 2864: state = 2865; break; // &KJc -> &KJcy - case 2867: state = 2868; break; // &kjc -> &kjcy - case 2900: state = 2901; break; // &laempt -> &laempty - case 2988: state = 3010; break; // &Lc -> &Lcy - case 2993: state = 3011; break; // &lc -> &lcy - case 3403: state = 3404; break; // &LJc -> &LJcy - case 3406: state = 3407; break; // &ljc -> &ljcy - case 3776: state = 3783; break; // &mc -> &mcy - case 3781: state = 3782; break; // &Mc -> &Mcy - case 3937: state = 3965; break; // &nc -> &ncy - case 3940: state = 3964; break; // &Nc -> &Ncy - case 4020: state = 4021; break; // &NegativeVer -> &NegativeVery - case 4116: state = 4117; break; // &NJc -> &NJcy - case 4119: state = 4120; break; // &njc -> &njcy - case 4841: state = 4850; break; // &oc -> &ocy - case 4844: state = 4849; break; // &Oc -> &Ocy - case 4966: state = 4967; break; // &OpenCurl -> &OpenCurly - case 5104: state = 5105; break; // &Pc -> &Pcy - case 5106: state = 5107; break; // &pc -> &pcy - case 5234: state = 5235; break; // &preccurl -> &preccurly - case 5420: state = 5421; break; // &raempt -> &raempty - case 5502: state = 5524; break; // &Rc -> &Rcy - case 5507: state = 5525; break; // &rc -> &rcy - case 5974: state = 5975; break; // &RuleDela -> &RuleDelay - case 6001: state = 6046; break; // &Sc -> &Scy - case 6002: state = 6047; break; // &sc -> &scy - case 6088: state = 6157; break; // &sh -> ­ - case 6095: state = 6096; break; // &SHCHc -> &SHCHcy - case 6097: state = 6103; break; // &shc -> &shcy - case 6099: state = 6100; break; // &shchc -> &shchcy - case 6101: state = 6102; break; // &SHc -> &SHcy - case 6233: state = 6234; break; // &SOFTc -> &SOFTcy - case 6238: state = 6239; break; // &softc -> &softcy - case 6442: state = 6443; break; // &succcurl -> &succcurly - case 6597: state = 6615; break; // &Tc -> &Tcy - case 6602: state = 6616; break; // &tc -> &tcy - case 6650: state = 6651; break; // &thetas -> &thetasy - case 6825: state = 6830; break; // &tsc -> &tscy - case 6828: state = 6829; break; // &TSc -> &TScy - case 6832: state = 6833; break; // &TSHc -> &TSHcy - case 6835: state = 6836; break; // &tshc -> &tshcy - case 6898: state = 6899; break; // &Ubrc -> &Ubrcy - case 6902: state = 6903; break; // &ubrc -> &ubrcy - case 6910: state = 6918; break; // &Uc -> &Ucy - case 6914: state = 6919; break; // &uc -> &ucy - case 7315: state = 7316; break; // &Vc -> &Vcy - case 7317: state = 7318; break; // &vc -> &vcy - case 7349: state = 7384; break; // &Ver -> &Very - case 7592: state = 7599; break; // &yac -> &yacy - case 7597: state = 7598; break; // &YAc -> &YAcy - case 7600: state = 7608; break; // &Yc -> &Ycy - case 7604: state = 7609; break; // &yc -> &ycy - case 7617: state = 7618; break; // &YIc -> &YIcy - case 7620: state = 7621; break; // &yic -> &yicy - case 7635: state = 7636; break; // &YUc -> &YUcy - case 7638: state = 7639; break; // &yuc -> &yucy - case 7657: state = 7667; break; // &Zc -> &Zcy - case 7662: state = 7668; break; // &zc -> &zcy - case 7702: state = 7703; break; // &ZHc -> &ZHcy - case 7705: state = 7706; break; // &zhc -> &zhcy - default: return false; - } - break; - case 'z': - switch (state) { - case 0: state = 7651; break; // & -> &z - case 102: state = 127; break; // &ang -> &angz - case 401: state = 402; break; // &blacklo -> &blackloz - case 1097: state = 1647; break; // &d -> &dz - case 2479: state = 2480; break; // &Hori -> &Horiz - case 3466: state = 3632; break; // &lo -> &loz - case 5991: state = 6579; break; // &s -> &sz - case 6816: state = 6817; break; // &trpe -> &trpez - case 7223: state = 7441; break; // &v -> &vz - case 7443: state = 7444; break; // &vzig -> &vzigz - default: return false; - } - break; + case '1': table = TransitionTable_1; break; + case '2': table = TransitionTable_2; break; + case '3': table = TransitionTable_3; break; + case '4': table = TransitionTable_4; break; + case '5': table = TransitionTable_5; break; + case '6': table = TransitionTable_6; break; + case '7': table = TransitionTable_7; break; + case '8': table = TransitionTable_8; break; + case ';': table = TransitionTable_semicolon; break; + case 'A': table = TransitionTable_A; break; + case 'B': table = TransitionTable_B; break; + case 'C': table = TransitionTable_C; break; + case 'D': table = TransitionTable_D; break; + case 'E': table = TransitionTable_E; break; + case 'F': table = TransitionTable_F; break; + case 'G': table = TransitionTable_G; break; + case 'H': table = TransitionTable_H; break; + case 'I': table = TransitionTable_I; break; + case 'J': table = TransitionTable_J; break; + case 'K': table = TransitionTable_K; break; + case 'L': table = TransitionTable_L; break; + case 'M': table = TransitionTable_M; break; + case 'N': table = TransitionTable_N; break; + case 'O': table = TransitionTable_O; break; + case 'P': table = TransitionTable_P; break; + case 'Q': table = TransitionTable_Q; break; + case 'R': table = TransitionTable_R; break; + case 'S': table = TransitionTable_S; break; + case 'T': table = TransitionTable_T; break; + case 'U': table = TransitionTable_U; break; + case 'V': table = TransitionTable_V; break; + case 'W': table = TransitionTable_W; break; + case 'X': table = TransitionTable_X; break; + case 'Y': table = TransitionTable_Y; break; + case 'Z': table = TransitionTable_Z; break; + case 'a': table = TransitionTable_a; break; + case 'b': table = TransitionTable_b; break; + case 'c': table = TransitionTable_c; break; + case 'd': table = TransitionTable_d; break; + case 'e': table = TransitionTable_e; break; + case 'f': table = TransitionTable_f; break; + case 'g': table = TransitionTable_g; break; + case 'h': table = TransitionTable_h; break; + case 'i': table = TransitionTable_i; break; + case 'j': table = TransitionTable_j; break; + case 'k': table = TransitionTable_k; break; + case 'l': table = TransitionTable_l; break; + case 'm': table = TransitionTable_m; break; + case 'n': table = TransitionTable_n; break; + case 'o': table = TransitionTable_o; break; + case 'p': table = TransitionTable_p; break; + case 'q': table = TransitionTable_q; break; + case 'r': table = TransitionTable_r; break; + case 's': table = TransitionTable_s; break; + case 't': table = TransitionTable_t; break; + case 'u': table = TransitionTable_u; break; + case 'v': table = TransitionTable_v; break; + case 'w': table = TransitionTable_w; break; + case 'x': table = TransitionTable_x; break; + case 'y': table = TransitionTable_y; break; + case 'z': table = TransitionTable_z; break; default: return false; } - states[index] = state; + if ((next = BinarySearchNextState (table, state)) == -1) + return false; + + states[index] = next; pushed[index] = c; index++; return true; } - static string GetNamedEntityValue (int state) - { - switch (state) { - case 6: return "\u00C1"; // Á - case 12: return "\u00E1"; // á - case 17: return "\u0102"; // &Abreve - case 22: return "\u0103"; // &abreve - case 23: return "\u223E"; // &ac - case 24: return "\u223F"; // &acd - case 25: return "\u223E\u0333"; // &acE - case 29: return "\u00C2"; //  - case 32: return "\u00E2"; // â - case 35: return "\u00B4"; // ´ - case 36: return "\u0410"; // &Acy - case 37: return "\u0430"; // &acy - case 41: return "\u00C6"; // Æ - case 45: return "\u00E6"; // æ - case 46: return "\u2061"; // &af - case 48: return "\uD835\uDD04"; // &Afr - case 49: return "\uD835\uDD1E"; // &afr - case 54: return "\u00C0"; // À - case 59: return "\u00E0"; // à - case 65: return "\u2135"; // &alefsym - case 67: return "\u2135"; // &aleph - case 71: return "\u0391"; // &Alpha - case 74: return "\u03B1"; // &alpha - case 78: return "\u0100"; // &Amacr - case 82: return "\u0101"; // &amacr - case 84: return "\u2A3F"; // &amalg - case 86: return "\u0026"; // & - case 87: return "\u0026"; // & - case 89: return "\u2A53"; // &And - case 91: return "\u2227"; // &and - case 94: return "\u2A55"; // &andand - case 95: return "\u2A5C"; // &andd - case 100: return "\u2A58"; // &andslope - case 101: return "\u2A5A"; // &andv - case 102: return "\u2220"; // &ang - case 103: return "\u29A4"; // &ange - case 105: return "\u2220"; // &angle - case 108: return "\u2221"; // &angmsd - case 110: return "\u29A8"; // &angmsdaa - case 111: return "\u29A9"; // &angmsdab - case 112: return "\u29AA"; // &angmsdac - case 113: return "\u29AB"; // &angmsdad - case 114: return "\u29AC"; // &angmsdae - case 115: return "\u29AD"; // &angmsdaf - case 116: return "\u29AE"; // &angmsdag - case 117: return "\u29AF"; // &angmsdah - case 119: return "\u221F"; // &angrt - case 121: return "\u22BE"; // &angrtvb - case 122: return "\u299D"; // &angrtvbd - case 125: return "\u2222"; // &angsph - case 126: return "\u00C5"; // &angst - case 130: return "\u237C"; // &angzarr - case 134: return "\u0104"; // &Aogon - case 138: return "\u0105"; // &aogon - case 140: return "\uD835\uDD38"; // &Aopf - case 142: return "\uD835\uDD52"; // &aopf - case 143: return "\u2248"; // &ap - case 147: return "\u2A6F"; // &apacir - case 148: return "\u2A70"; // &apE - case 149: return "\u224A"; // &ape - case 151: return "\u224B"; // &apid - case 153: return "\u0027"; // &apos - case 165: return "\u2061"; // &ApplyFunction - case 169: return "\u2248"; // &approx - case 171: return "\u224A"; // &approxeq - case 175: return "\u00C5"; // Å - case 179: return "\u00E5"; // å - case 182: return "\uD835\uDC9C"; // &Ascr - case 185: return "\uD835\uDCB6"; // &ascr - case 189: return "\u2254"; // &Assign - case 190: return "\u002A"; // &ast - case 193: return "\u2248"; // &asymp - case 195: return "\u224D"; // &asympeq - case 200: return "\u00C3"; // à - case 205: return "\u00E3"; // ã - case 208: return "\u00C4"; // Ä - case 211: return "\u00E4"; // ä - case 218: return "\u2233"; // &awconint - case 221: return "\u2A11"; // &awint - case 229: return "\u224C"; // &backcong - case 236: return "\u03F6"; // &backepsilon - case 241: return "\u2035"; // &backprime - case 244: return "\u223D"; // &backsim - case 246: return "\u22CD"; // &backsimeq - case 255: return "\u2216"; // &Backslash - case 257: return "\u2AE7"; // &Barv - case 261: return "\u22BD"; // &barvee - case 264: return "\u2306"; // &Barwed - case 267: return "\u2305"; // &barwed - case 269: return "\u2305"; // &barwedge - case 272: return "\u23B5"; // &bbrk - case 276: return "\u23B6"; // &bbrktbrk - case 280: return "\u224C"; // &bcong - case 282: return "\u0411"; // &Bcy - case 283: return "\u0431"; // &bcy - case 287: return "\u201E"; // &bdquo - case 292: return "\u2235"; // &becaus - case 298: return "\u2235"; // &Because - case 299: return "\u2235"; // &because - case 304: return "\u29B0"; // &bemptyv - case 307: return "\u03F6"; // &bepsi - case 311: return "\u212C"; // &bernou - case 319: return "\u212C"; // &Bernoullis - case 321: return "\u0392"; // &Beta - case 323: return "\u03B2"; // &beta - case 324: return "\u2136"; // &beth - case 328: return "\u226C"; // &between - case 330: return "\uD835\uDD05"; // &Bfr - case 332: return "\uD835\uDD1F"; // &bfr - case 337: return "\u22C2"; // &bigcap - case 340: return "\u25EF"; // &bigcirc - case 342: return "\u22C3"; // &bigcup - case 346: return "\u2A00"; // &bigodot - case 350: return "\u2A01"; // &bigoplus - case 355: return "\u2A02"; // &bigotimes - case 360: return "\u2A06"; // &bigsqcup - case 363: return "\u2605"; // &bigstar - case 375: return "\u25BD"; // &bigtriangledown - case 377: return "\u25B3"; // &bigtriangleup - case 382: return "\u2A04"; // &biguplus - case 385: return "\u22C1"; // &bigvee - case 390: return "\u22C0"; // &bigwedge - case 395: return "\u290D"; // &bkarow - case 406: return "\u29EB"; // &blacklozenge - case 412: return "\u25AA"; // &blacksquare - case 420: return "\u25B4"; // &blacktriangle - case 424: return "\u25BE"; // &blacktriangledown - case 428: return "\u25C2"; // &blacktriangleleft - case 433: return "\u25B8"; // &blacktriangleright - case 435: return "\u2423"; // &blank - case 438: return "\u2592"; // &blk12 - case 439: return "\u2591"; // &blk14 - case 441: return "\u2593"; // &blk34 - case 444: return "\u2588"; // &block - case 446: return "\u003D\u20E5"; // &bne - case 450: return "\u2261\u20E5"; // &bnequiv - case 453: return "\u2AED"; // &bNot - case 455: return "\u2310"; // &bnot - case 458: return "\uD835\uDD39"; // &Bopf - case 461: return "\uD835\uDD53"; // &bopf - case 462: return "\u22A5"; // &bot - case 465: return "\u22A5"; // &bottom - case 469: return "\u22C8"; // &bowtie - case 473: return "\u29C9"; // &boxbox - case 475: return "\u2557"; // &boxDL - case 476: return "\u2556"; // &boxDl - case 478: return "\u2555"; // &boxdL - case 479: return "\u2510"; // &boxdl - case 480: return "\u2554"; // &boxDR - case 481: return "\u2553"; // &boxDr - case 482: return "\u2552"; // &boxdR - case 483: return "\u250C"; // &boxdr - case 484: return "\u2550"; // &boxH - case 485: return "\u2500"; // &boxh - case 486: return "\u2566"; // &boxHD - case 487: return "\u2564"; // &boxHd - case 488: return "\u2565"; // &boxhD - case 489: return "\u252C"; // &boxhd - case 490: return "\u2569"; // &boxHU - case 491: return "\u2567"; // &boxHu - case 492: return "\u2568"; // &boxhU - case 493: return "\u2534"; // &boxhu - case 498: return "\u229F"; // &boxminus - case 502: return "\u229E"; // &boxplus - case 507: return "\u22A0"; // &boxtimes - case 509: return "\u255D"; // &boxUL - case 510: return "\u255C"; // &boxUl - case 512: return "\u255B"; // &boxuL - case 513: return "\u2518"; // &boxul - case 514: return "\u255A"; // &boxUR - case 515: return "\u2559"; // &boxUr - case 516: return "\u2558"; // &boxuR - case 517: return "\u2514"; // &boxur - case 518: return "\u2551"; // &boxV - case 519: return "\u2502"; // &boxv - case 520: return "\u256C"; // &boxVH - case 521: return "\u256B"; // &boxVh - case 522: return "\u256A"; // &boxvH - case 523: return "\u253C"; // &boxvh - case 524: return "\u2563"; // &boxVL - case 525: return "\u2562"; // &boxVl - case 526: return "\u2561"; // &boxvL - case 527: return "\u2524"; // &boxvl - case 528: return "\u2560"; // &boxVR - case 529: return "\u255F"; // &boxVr - case 530: return "\u255E"; // &boxvR - case 531: return "\u251C"; // &boxvr - case 536: return "\u2035"; // &bprime - case 540: return "\u02D8"; // &Breve - case 544: return "\u02D8"; // &breve - case 548: return "\u00A6"; // ¦ - case 551: return "\u212C"; // &Bscr - case 554: return "\uD835\uDCB7"; // &bscr - case 557: return "\u204F"; // &bsemi - case 559: return "\u223D"; // &bsim - case 560: return "\u22CD"; // &bsime - case 562: return "\u005C"; // &bsol - case 563: return "\u29C5"; // &bsolb - case 567: return "\u27C8"; // &bsolhsub - case 570: return "\u2022"; // &bull - case 572: return "\u2022"; // &bullet - case 574: return "\u224E"; // &bump - case 575: return "\u2AAE"; // &bumpE - case 576: return "\u224F"; // &bumpe - case 581: return "\u224E"; // &Bumpeq - case 582: return "\u224F"; // &bumpeq - case 588: return "\u0106"; // &Cacute - case 594: return "\u0107"; // &cacute - case 595: return "\u22D2"; // &Cap - case 596: return "\u2229"; // &cap - case 599: return "\u2A44"; // &capand - case 604: return "\u2A49"; // &capbrcup - case 607: return "\u2A4B"; // &capcap - case 609: return "\u2A47"; // &capcup - case 612: return "\u2A40"; // &capdot - case 629: return "\u2145"; // &CapitalDifferentialD - case 630: return "\u2229\uFE00"; // &caps - case 633: return "\u2041"; // &caret - case 635: return "\u02C7"; // &caron - case 640: return "\u212D"; // &Cayleys - case 644: return "\u2A4D"; // &ccaps - case 649: return "\u010C"; // &Ccaron - case 652: return "\u010D"; // &ccaron - case 656: return "\u00C7"; // Ç - case 660: return "\u00E7"; // ç - case 663: return "\u0108"; // &Ccirc - case 666: return "\u0109"; // &ccirc - case 671: return "\u2230"; // &Cconint - case 674: return "\u2A4C"; // &ccups - case 676: return "\u2A50"; // &ccupssm - case 679: return "\u010A"; // &Cdot - case 682: return "\u010B"; // &cdot - case 686: return "\u00B8"; // ¸ - case 692: return "\u00B8"; // &Cedilla - case 697: return "\u29B2"; // &cemptyv - case 699: return "\u00A2"; // ¢ - case 706: return "\u00B7"; // &CenterDot - case 711: return "\u00B7"; // ¢erdot - case 713: return "\u212D"; // &Cfr - case 715: return "\uD835\uDD20"; // &cfr - case 718: return "\u0427"; // &CHcy - case 721: return "\u0447"; // &chcy - case 724: return "\u2713"; // &check - case 728: return "\u2713"; // &checkmark - case 730: return "\u03A7"; // &Chi - case 731: return "\u03C7"; // &chi - case 733: return "\u25CB"; // &cir - case 734: return "\u02C6"; // &circ - case 736: return "\u2257"; // &circeq - case 747: return "\u21BA"; // &circlearrowleft - case 752: return "\u21BB"; // &circlearrowright - case 756: return "\u229B"; // &circledast - case 760: return "\u229A"; // &circledcirc - case 764: return "\u229D"; // &circleddash - case 772: return "\u2299"; // &CircleDot - case 773: return "\u00AE"; // &circledR - case 774: return "\u24C8"; // &circledS - case 779: return "\u2296"; // &CircleMinus - case 783: return "\u2295"; // &CirclePlus - case 788: return "\u2297"; // &CircleTimes - case 789: return "\u29C3"; // &cirE - case 790: return "\u2257"; // &cire - case 795: return "\u2A10"; // &cirfnint - case 798: return "\u2AEF"; // &cirmid - case 802: return "\u29C2"; // &cirscir - case 825: return "\u2232"; // &ClockwiseContourIntegral - case 843: return "\u201D"; // &CloseCurlyDoubleQuote - case 848: return "\u2019"; // &CloseCurlyQuote - case 852: return "\u2663"; // &clubs - case 855: return "\u2663"; // &clubsuit - case 859: return "\u2237"; // &Colon - case 863: return "\u003A"; // &colon - case 864: return "\u2A74"; // &Colone - case 865: return "\u2254"; // &colone - case 866: return "\u2254"; // &coloneq - case 869: return "\u002C"; // &comma - case 870: return "\u0040"; // &commat - case 871: return "\u2201"; // &comp - case 873: return "\u2218"; // &compfn - case 879: return "\u2201"; // &complement - case 882: return "\u2102"; // &complexes - case 884: return "\u2245"; // &cong - case 887: return "\u2A6D"; // &congdot - case 894: return "\u2261"; // &Congruent - case 897: return "\u222F"; // &Conint - case 900: return "\u222E"; // &conint - case 912: return "\u222E"; // &ContourIntegral - case 914: return "\u2102"; // &Copf - case 916: return "\uD835\uDD54"; // &copf - case 919: return "\u2210"; // &coprod - case 925: return "\u2210"; // &Coproduct - case 928: return "\u00A9"; // © - case 929: return "\u00A9"; // © - case 931: return "\u2117"; // ©sr - case 960: return "\u2233"; // &CounterClockwiseContourIntegral - case 964: return "\u21B5"; // &crarr - case 968: return "\u2A2F"; // &Cross - case 971: return "\u2717"; // &cross - case 974: return "\uD835\uDC9E"; // &Cscr - case 977: return "\uD835\uDCB8"; // &cscr - case 979: return "\u2ACF"; // &csub - case 980: return "\u2AD1"; // &csube - case 981: return "\u2AD0"; // &csup - case 982: return "\u2AD2"; // &csupe - case 986: return "\u22EF"; // &ctdot - case 992: return "\u2938"; // &cudarrl - case 993: return "\u2935"; // &cudarrr - case 996: return "\u22DE"; // &cuepr - case 998: return "\u22DF"; // &cuesc - case 1002: return "\u21B6"; // &cularr - case 1003: return "\u293D"; // &cularrp - case 1005: return "\u22D3"; // &Cup - case 1006: return "\u222A"; // &cup - case 1011: return "\u2A48"; // &cupbrcap - case 1014: return "\u224D"; // &CupCap - case 1017: return "\u2A46"; // &cupcap - case 1019: return "\u2A4A"; // &cupcup - case 1022: return "\u228D"; // &cupdot - case 1024: return "\u2A45"; // &cupor - case 1025: return "\u222A\uFE00"; // &cups - case 1029: return "\u21B7"; // &curarr - case 1030: return "\u293C"; // &curarrm - case 1038: return "\u22DE"; // &curlyeqprec - case 1042: return "\u22DF"; // &curlyeqsucc - case 1045: return "\u22CE"; // &curlyvee - case 1050: return "\u22CF"; // &curlywedge - case 1053: return "\u00A4"; // ¤ - case 1064: return "\u21B6"; // &curvearrowleft - case 1069: return "\u21B7"; // &curvearrowright - case 1072: return "\u22CE"; // &cuvee - case 1075: return "\u22CF"; // &cuwed - case 1082: return "\u2232"; // &cwconint - case 1085: return "\u2231"; // &cwint - case 1090: return "\u232D"; // &cylcty - case 1096: return "\u2021"; // &Dagger - case 1102: return "\u2020"; // &dagger - case 1106: return "\u2138"; // &daleth - case 1108: return "\u21A1"; // &Darr - case 1111: return "\u21D3"; // &dArr - case 1113: return "\u2193"; // &darr - case 1115: return "\u2010"; // &dash - case 1118: return "\u2AE4"; // &Dashv - case 1119: return "\u22A3"; // &dashv - case 1125: return "\u290F"; // &dbkarow - case 1128: return "\u02DD"; // &dblac - case 1133: return "\u010E"; // &Dcaron - case 1138: return "\u010F"; // &dcaron - case 1139: return "\u0414"; // &Dcy - case 1140: return "\u0434"; // &dcy - case 1141: return "\u2145"; // &DD - case 1142: return "\u2146"; // &dd - case 1147: return "\u2021"; // &ddagger - case 1149: return "\u21CA"; // &ddarr - case 1155: return "\u2911"; // &DDotrahd - case 1160: return "\u2A77"; // &ddotseq - case 1162: return "\u00B0"; // ° - case 1164: return "\u2207"; // &Del - case 1166: return "\u0394"; // &Delta - case 1169: return "\u03B4"; // &delta - case 1174: return "\u29B1"; // &demptyv - case 1179: return "\u297F"; // &dfisht - case 1181: return "\uD835\uDD07"; // &Dfr - case 1182: return "\uD835\uDD21"; // &dfr - case 1185: return "\u2965"; // &dHar - case 1189: return "\u21C3"; // &dharl - case 1190: return "\u21C2"; // &dharr - case 1205: return "\u00B4"; // &DiacriticalAcute - case 1208: return "\u02D9"; // &DiacriticalDot - case 1217: return "\u02DD"; // &DiacriticalDoubleAcute - case 1222: return "\u0060"; // &DiacriticalGrave - case 1227: return "\u02DC"; // &DiacriticalTilde - case 1230: return "\u22C4"; // &diam - case 1234: return "\u22C4"; // &Diamond - case 1237: return "\u22C4"; // &diamond - case 1241: return "\u2666"; // &diamondsuit - case 1242: return "\u2666"; // &diams - case 1243: return "\u00A8"; // &die - case 1254: return "\u2146"; // &DifferentialD - case 1259: return "\u03DD"; // &digamma - case 1262: return "\u22F2"; // &disin - case 1263: return "\u00F7"; // &div - case 1266: return "\u00F7"; // ÷ - case 1273: return "\u22C7"; // ÷ontimes - case 1276: return "\u22C7"; // &divonx - case 1279: return "\u0402"; // &DJcy - case 1282: return "\u0452"; // &djcy - case 1287: return "\u231E"; // &dlcorn - case 1290: return "\u230D"; // &dlcrop - case 1295: return "\u0024"; // &dollar - case 1298: return "\uD835\uDD3B"; // &Dopf - case 1300: return "\uD835\uDD55"; // &dopf - case 1301: return "\u00A8"; // &Dot - case 1302: return "\u02D9"; // &dot - case 1305: return "\u20DC"; // &DotDot - case 1307: return "\u2250"; // &doteq - case 1310: return "\u2251"; // &doteqdot - case 1315: return "\u2250"; // &DotEqual - case 1320: return "\u2238"; // &dotminus - case 1324: return "\u2214"; // &dotplus - case 1330: return "\u22A1"; // &dotsquare - case 1342: return "\u2306"; // &doublebarwedge - case 1361: return "\u222F"; // &DoubleContourIntegral - case 1364: return "\u00A8"; // &DoubleDot - case 1371: return "\u21D3"; // &DoubleDownArrow - case 1380: return "\u21D0"; // &DoubleLeftArrow - case 1390: return "\u21D4"; // &DoubleLeftRightArrow - case 1393: return "\u2AE4"; // &DoubleLeftTee - case 1405: return "\u27F8"; // &DoubleLongLeftArrow - case 1415: return "\u27FA"; // &DoubleLongLeftRightArrow - case 1425: return "\u27F9"; // &DoubleLongRightArrow - case 1435: return "\u21D2"; // &DoubleRightArrow - case 1438: return "\u22A8"; // &DoubleRightTee - case 1445: return "\u21D1"; // &DoubleUpArrow - case 1454: return "\u21D5"; // &DoubleUpDownArrow - case 1465: return "\u2225"; // &DoubleVerticalBar - case 1472: return "\u2193"; // &DownArrow - case 1477: return "\u21D3"; // &Downarrow - case 1484: return "\u2193"; // &downarrow - case 1487: return "\u2913"; // &DownArrowBar - case 1494: return "\u21F5"; // &DownArrowUpArrow - case 1499: return "\u0311"; // &DownBreve - case 1509: return "\u21CA"; // &downdownarrows - case 1520: return "\u21C3"; // &downharpoonleft - case 1525: return "\u21C2"; // &downharpoonright - case 1540: return "\u2950"; // &DownLeftRightVector - case 1549: return "\u295E"; // &DownLeftTeeVector - case 1555: return "\u21BD"; // &DownLeftVector - case 1558: return "\u2956"; // &DownLeftVectorBar - case 1572: return "\u295F"; // &DownRightTeeVector - case 1578: return "\u21C1"; // &DownRightVector - case 1581: return "\u2957"; // &DownRightVectorBar - case 1584: return "\u22A4"; // &DownTee - case 1589: return "\u21A7"; // &DownTeeArrow - case 1596: return "\u2910"; // &drbkarow - case 1600: return "\u231F"; // &drcorn - case 1603: return "\u230C"; // &drcrop - case 1606: return "\uD835\uDC9F"; // &Dscr - case 1609: return "\uD835\uDCB9"; // &dscr - case 1612: return "\u0405"; // &DScy - case 1613: return "\u0455"; // &dscy - case 1615: return "\u29F6"; // &dsol - case 1619: return "\u0110"; // &Dstrok - case 1623: return "\u0111"; // &dstrok - case 1627: return "\u22F1"; // &dtdot - case 1629: return "\u25BF"; // &dtri - case 1630: return "\u25BE"; // &dtrif - case 1634: return "\u21F5"; // &duarr - case 1637: return "\u296F"; // &duhar - case 1643: return "\u29A6"; // &dwangle - case 1646: return "\u040F"; // &DZcy - case 1649: return "\u045F"; // &dzcy - case 1655: return "\u27FF"; // &dzigrarr - case 1661: return "\u00C9"; // É - case 1667: return "\u00E9"; // é - case 1671: return "\u2A6E"; // &easter - case 1676: return "\u011A"; // &Ecaron - case 1681: return "\u011B"; // &ecaron - case 1683: return "\u2256"; // &ecir - case 1686: return "\u00CA"; // Ê - case 1687: return "\u00EA"; // ê - case 1691: return "\u2255"; // &ecolon - case 1692: return "\u042D"; // &Ecy - case 1693: return "\u044D"; // &ecy - case 1697: return "\u2A77"; // &eDDot - case 1700: return "\u0116"; // &Edot - case 1702: return "\u2251"; // &eDot - case 1705: return "\u0117"; // &edot - case 1706: return "\u2147"; // &ee - case 1710: return "\u2252"; // &efDot - case 1712: return "\uD835\uDD08"; // &Efr - case 1713: return "\uD835\uDD22"; // &efr - case 1714: return "\u2A9A"; // &eg - case 1719: return "\u00C8"; // È - case 1723: return "\u00E8"; // è - case 1724: return "\u2A96"; // &egs - case 1727: return "\u2A98"; // &egsdot - case 1728: return "\u2A99"; // &el - case 1734: return "\u2208"; // &Element - case 1740: return "\u23E7"; // &elinters - case 1741: return "\u2113"; // &ell - case 1742: return "\u2A95"; // &els - case 1745: return "\u2A97"; // &elsdot - case 1749: return "\u0112"; // &Emacr - case 1753: return "\u0113"; // &emacr - case 1756: return "\u2205"; // &empty - case 1759: return "\u2205"; // &emptyset - case 1773: return "\u25FB"; // &EmptySmallSquare - case 1774: return "\u2205"; // &emptyv - case 1789: return "\u25AB"; // &EmptyVerySmallSquare - case 1791: return "\u2003"; // &emsp - case 1793: return "\u2004"; // &emsp13 - case 1794: return "\u2005"; // &emsp14 - case 1796: return "\u014A"; // &ENG - case 1798: return "\u014B"; // &eng - case 1800: return "\u2002"; // &ensp - case 1804: return "\u0118"; // &Eogon - case 1808: return "\u0119"; // &eogon - case 1810: return "\uD835\uDD3C"; // &Eopf - case 1812: return "\uD835\uDD56"; // &eopf - case 1815: return "\u22D5"; // &epar - case 1817: return "\u29E3"; // &eparsl - case 1820: return "\u2A71"; // &eplus - case 1822: return "\u03B5"; // &epsi - case 1828: return "\u0395"; // &Epsilon - case 1831: return "\u03B5"; // &epsilon - case 1832: return "\u03F5"; // &epsiv - case 1837: return "\u2256"; // &eqcirc - case 1841: return "\u2255"; // &eqcolon - case 1844: return "\u2242"; // &eqsim - case 1851: return "\u2A96"; // &eqslantgtr - case 1855: return "\u2A95"; // &eqslantless - case 1859: return "\u2A75"; // &Equal - case 1863: return "\u003D"; // &equals - case 1868: return "\u2242"; // &EqualTilde - case 1871: return "\u225F"; // &equest - case 1879: return "\u21CC"; // &Equilibrium - case 1881: return "\u2261"; // &equiv - case 1883: return "\u2A78"; // &equivDD - case 1889: return "\u29E5"; // &eqvparsl - case 1893: return "\u2971"; // &erarr - case 1896: return "\u2253"; // &erDot - case 1899: return "\u2130"; // &Escr - case 1902: return "\u212F"; // &escr - case 1905: return "\u2250"; // &esdot - case 1907: return "\u2A73"; // &Esim - case 1909: return "\u2242"; // &esim - case 1911: return "\u0397"; // &Eta - case 1913: return "\u03B7"; // &eta - case 1915: return "\u00D0"; // Ð - case 1916: return "\u00F0"; // ð - case 1919: return "\u00CB"; // Ë - case 1922: return "\u00EB"; // ë - case 1924: return "\u20AC"; // &euro - case 1927: return "\u0021"; // &excl - case 1930: return "\u2203"; // &exist - case 1935: return "\u2203"; // &Exists - case 1944: return "\u2130"; // &expectation - case 1954: return "\u2147"; // &ExponentialE - case 1963: return "\u2147"; // &exponentiale - case 1976: return "\u2252"; // &fallingdotseq - case 1979: return "\u0424"; // &Fcy - case 1981: return "\u0444"; // &fcy - case 1986: return "\u2640"; // &female - case 1991: return "\uFB03"; // &ffilig - case 1994: return "\uFB00"; // &fflig - case 1997: return "\uFB04"; // &ffllig - case 1999: return "\uD835\uDD09"; // &Ffr - case 2000: return "\uD835\uDD23"; // &ffr - case 2004: return "\uFB01"; // &filig - case 2020: return "\u25FC"; // &FilledSmallSquare - case 2035: return "\u25AA"; // &FilledVerySmallSquare - case 2039: return "\u0066\u006A"; // &fjlig - case 2042: return "\u266D"; // &flat - case 2045: return "\uFB02"; // &fllig - case 2048: return "\u25B1"; // &fltns - case 2051: return "\u0192"; // &fnof - case 2054: return "\uD835\uDD3D"; // &Fopf - case 2057: return "\uD835\uDD57"; // &fopf - case 2061: return "\u2200"; // &ForAll - case 2065: return "\u2200"; // &forall - case 2066: return "\u22D4"; // &fork - case 2067: return "\u2AD9"; // &forkv - case 2075: return "\u2131"; // &Fouriertrf - case 2082: return "\u2A0D"; // &fpartint - case 2087: return "\u00BD"; // ½ - case 2088: return "\u2153"; // &frac13 - case 2089: return "\u00BC"; // ¼ - case 2090: return "\u2155"; // &frac15 - case 2091: return "\u2159"; // &frac16 - case 2092: return "\u215B"; // &frac18 - case 2094: return "\u2154"; // &frac23 - case 2095: return "\u2156"; // &frac25 - case 2097: return "\u00BE"; // ¾ - case 2098: return "\u2157"; // &frac35 - case 2099: return "\u215C"; // &frac38 - case 2101: return "\u2158"; // &frac45 - case 2103: return "\u215A"; // &frac56 - case 2104: return "\u215D"; // &frac58 - case 2106: return "\u215E"; // &frac78 - case 2108: return "\u2044"; // &frasl - case 2111: return "\u2322"; // &frown - case 2114: return "\u2131"; // &Fscr - case 2117: return "\uD835\uDCBB"; // &fscr - case 2123: return "\u01F5"; // &gacute - case 2128: return "\u0393"; // &Gamma - case 2131: return "\u03B3"; // &gamma - case 2132: return "\u03DC"; // &Gammad - case 2133: return "\u03DD"; // &gammad - case 2134: return "\u2A86"; // &gap - case 2139: return "\u011E"; // &Gbreve - case 2144: return "\u011F"; // &gbreve - case 2149: return "\u0122"; // &Gcedil - case 2152: return "\u011C"; // &Gcirc - case 2156: return "\u011D"; // &gcirc - case 2157: return "\u0413"; // &Gcy - case 2158: return "\u0433"; // &gcy - case 2161: return "\u0120"; // &Gdot - case 2164: return "\u0121"; // &gdot - case 2165: return "\u2267"; // &gE - case 2166: return "\u2265"; // &ge - case 2167: return "\u2A8C"; // &gEl - case 2168: return "\u22DB"; // &gel - case 2169: return "\u2265"; // &geq - case 2170: return "\u2267"; // &geqq - case 2175: return "\u2A7E"; // &geqslant - case 2176: return "\u2A7E"; // &ges - case 2178: return "\u2AA9"; // &gescc - case 2181: return "\u2A80"; // &gesdot - case 2182: return "\u2A82"; // &gesdoto - case 2183: return "\u2A84"; // &gesdotol - case 2184: return "\u22DB\uFE00"; // &gesl - case 2186: return "\u2A94"; // &gesles - case 2188: return "\uD835\uDD0A"; // &Gfr - case 2190: return "\uD835\uDD24"; // &gfr - case 2191: return "\u22D9"; // &Gg - case 2192: return "\u226B"; // &gg - case 2193: return "\u22D9"; // &ggg - case 2197: return "\u2137"; // &gimel - case 2200: return "\u0403"; // &GJcy - case 2203: return "\u0453"; // &gjcy - case 2204: return "\u2277"; // &gl - case 2205: return "\u2AA5"; // &gla - case 2206: return "\u2A92"; // &glE - case 2207: return "\u2AA4"; // &glj - case 2210: return "\u2A8A"; // &gnap - case 2214: return "\u2A8A"; // &gnapprox - case 2215: return "\u2269"; // &gnE - case 2216: return "\u2A88"; // &gne - case 2217: return "\u2A88"; // &gneq - case 2218: return "\u2269"; // &gneqq - case 2221: return "\u22E7"; // &gnsim - case 2224: return "\uD835\uDD3E"; // &Gopf - case 2227: return "\uD835\uDD58"; // &gopf - case 2231: return "\u0060"; // &grave - case 2242: return "\u2265"; // &GreaterEqual - case 2246: return "\u22DB"; // &GreaterEqualLess - case 2255: return "\u2267"; // &GreaterFullEqual - case 2262: return "\u2AA2"; // &GreaterGreater - case 2266: return "\u2277"; // &GreaterLess - case 2276: return "\u2A7E"; // &GreaterSlantEqual - case 2281: return "\u2273"; // &GreaterTilde - case 2284: return "\uD835\uDCA2"; // &Gscr - case 2287: return "\u210A"; // &gscr - case 2289: return "\u2273"; // &gsim - case 2290: return "\u2A8E"; // &gsime - case 2291: return "\u2A90"; // &gsiml - case 2292: return "\u003E"; // > - case 2293: return "\u226B"; // &Gt - case 2294: return "\u003E"; // > - case 2296: return "\u2AA7"; // >cc - case 2298: return "\u2A7A"; // >cir - case 2301: return "\u22D7"; // >dot - case 2305: return "\u2995"; // >lPar - case 2310: return "\u2A7C"; // >quest - case 2317: return "\u2A86"; // >rapprox - case 2319: return "\u2978"; // >rarr - case 2322: return "\u22D7"; // >rdot - case 2328: return "\u22DB"; // >reqless - case 2333: return "\u2A8C"; // >reqqless - case 2337: return "\u2277"; // >rless - case 2340: return "\u2273"; // >rsim - case 2348: return "\u2269\uFE00"; // &gvertneqq - case 2350: return "\u2269\uFE00"; // &gvnE - case 2355: return "\u02C7"; // &Hacek - case 2361: return "\u200A"; // &hairsp - case 2363: return "\u00BD"; // &half - case 2367: return "\u210B"; // &hamilt - case 2372: return "\u042A"; // &HARDcy - case 2376: return "\u044A"; // &hardcy - case 2379: return "\u21D4"; // &hArr - case 2380: return "\u2194"; // &harr - case 2383: return "\u2948"; // &harrcir - case 2384: return "\u21AD"; // &harrw - case 2385: return "\u005E"; // &Hat - case 2388: return "\u210F"; // &hbar - case 2392: return "\u0124"; // &Hcirc - case 2396: return "\u0125"; // &hcirc - case 2401: return "\u2665"; // &hearts - case 2404: return "\u2665"; // &heartsuit - case 2408: return "\u2026"; // &hellip - case 2412: return "\u22B9"; // &hercon - case 2414: return "\u210C"; // &Hfr - case 2416: return "\uD835\uDD25"; // &hfr - case 2427: return "\u210B"; // &HilbertSpace - case 2434: return "\u2925"; // &hksearow - case 2439: return "\u2926"; // &hkswarow - case 2443: return "\u21FF"; // &hoarr - case 2447: return "\u223B"; // &homtht - case 2458: return "\u21A9"; // &hookleftarrow - case 2468: return "\u21AA"; // &hookrightarrow - case 2471: return "\u210D"; // &Hopf - case 2473: return "\uD835\uDD59"; // &hopf - case 2477: return "\u2015"; // &horbar - case 2489: return "\u2500"; // &HorizontalLine - case 2492: return "\u210B"; // &Hscr - case 2495: return "\uD835\uDCBD"; // &hscr - case 2499: return "\u210F"; // &hslash - case 2503: return "\u0126"; // &Hstrok - case 2507: return "\u0127"; // &hstrok - case 2518: return "\u224E"; // &HumpDownHump - case 2523: return "\u224F"; // &HumpEqual - case 2528: return "\u2043"; // &hybull - case 2532: return "\u2010"; // &hyphen - case 2538: return "\u00CD"; // Í - case 2544: return "\u00ED"; // í - case 2545: return "\u2063"; // &ic - case 2549: return "\u00CE"; // Î - case 2552: return "\u00EE"; // î - case 2553: return "\u0418"; // &Icy - case 2554: return "\u0438"; // &icy - case 2557: return "\u0130"; // &Idot - case 2560: return "\u0415"; // &IEcy - case 2563: return "\u0435"; // &iecy - case 2566: return "\u00A1"; // ¡ - case 2568: return "\u21D4"; // &iff - case 2570: return "\u2111"; // &Ifr - case 2571: return "\uD835\uDD26"; // &ifr - case 2576: return "\u00CC"; // Ì - case 2581: return "\u00EC"; // ì - case 2582: return "\u2148"; // &ii - case 2586: return "\u2A0C"; // &iiiint - case 2588: return "\u222D"; // &iiint - case 2592: return "\u29DC"; // &iinfin - case 2595: return "\u2129"; // &iiota - case 2599: return "\u0132"; // &IJlig - case 2603: return "\u0133"; // &ijlig - case 2604: return "\u2111"; // &Im - case 2607: return "\u012A"; // &Imacr - case 2611: return "\u012B"; // &imacr - case 2613: return "\u2111"; // &image - case 2620: return "\u2148"; // &ImaginaryI - case 2624: return "\u2110"; // &imagline - case 2628: return "\u2111"; // &imagpart - case 2630: return "\u0131"; // &imath - case 2632: return "\u22B7"; // &imof - case 2635: return "\u01B5"; // &imped - case 2640: return "\u21D2"; // &Implies - case 2641: return "\u2208"; // &in - case 2645: return "\u2105"; // &incare - case 2648: return "\u221E"; // &infin - case 2651: return "\u29DD"; // &infintie - case 2655: return "\u0131"; // &inodot - case 2657: return "\u222C"; // &Int - case 2658: return "\u222B"; // &int - case 2661: return "\u22BA"; // &intcal - case 2666: return "\u2124"; // &integers - case 2671: return "\u222B"; // &Integral - case 2675: return "\u22BA"; // &intercal - case 2683: return "\u22C2"; // &Intersection - case 2688: return "\u2A17"; // &intlarhk - case 2692: return "\u2A3C"; // &intprod - case 2704: return "\u2063"; // &InvisibleComma - case 2709: return "\u2062"; // &InvisibleTimes - case 2712: return "\u0401"; // &IOcy - case 2715: return "\u0451"; // &iocy - case 2719: return "\u012E"; // &Iogon - case 2722: return "\u012F"; // &iogon - case 2724: return "\uD835\uDD40"; // &Iopf - case 2726: return "\uD835\uDD5A"; // &iopf - case 2728: return "\u0399"; // &Iota - case 2730: return "\u03B9"; // &iota - case 2734: return "\u2A3C"; // &iprod - case 2739: return "\u00BF"; // ¿ - case 2742: return "\u2110"; // &Iscr - case 2745: return "\uD835\uDCBE"; // &iscr - case 2747: return "\u2208"; // &isin - case 2750: return "\u22F5"; // &isindot - case 2751: return "\u22F9"; // &isinE - case 2752: return "\u22F4"; // &isins - case 2753: return "\u22F3"; // &isinsv - case 2754: return "\u2208"; // &isinv - case 2755: return "\u2062"; // &it - case 2760: return "\u0128"; // &Itilde - case 2764: return "\u0129"; // &itilde - case 2768: return "\u0406"; // &Iukcy - case 2772: return "\u0456"; // &iukcy - case 2774: return "\u00CF"; // Ï - case 2776: return "\u00EF"; // ï - case 2781: return "\u0134"; // &Jcirc - case 2786: return "\u0135"; // &jcirc - case 2787: return "\u0419"; // &Jcy - case 2788: return "\u0439"; // &jcy - case 2790: return "\uD835\uDD0D"; // &Jfr - case 2792: return "\uD835\uDD27"; // &jfr - case 2796: return "\u0237"; // &jmath - case 2799: return "\uD835\uDD41"; // &Jopf - case 2802: return "\uD835\uDD5B"; // &jopf - case 2805: return "\uD835\uDCA5"; // &Jscr - case 2808: return "\uD835\uDCBF"; // &jscr - case 2812: return "\u0408"; // &Jsercy - case 2816: return "\u0458"; // &jsercy - case 2820: return "\u0404"; // &Jukcy - case 2824: return "\u0454"; // &jukcy - case 2829: return "\u039A"; // &Kappa - case 2834: return "\u03BA"; // &kappa - case 2835: return "\u03F0"; // &kappav - case 2840: return "\u0136"; // &Kcedil - case 2845: return "\u0137"; // &kcedil - case 2846: return "\u041A"; // &Kcy - case 2847: return "\u043A"; // &kcy - case 2849: return "\uD835\uDD0E"; // &Kfr - case 2851: return "\uD835\uDD28"; // &kfr - case 2856: return "\u0138"; // &kgreen - case 2859: return "\u0425"; // &KHcy - case 2862: return "\u0445"; // &khcy - case 2865: return "\u040C"; // &KJcy - case 2868: return "\u045C"; // &kjcy - case 2871: return "\uD835\uDD42"; // &Kopf - case 2874: return "\uD835\uDD5C"; // &kopf - case 2877: return "\uD835\uDCA6"; // &Kscr - case 2880: return "\uD835\uDCC0"; // &kscr - case 2885: return "\u21DA"; // &lAarr - case 2891: return "\u0139"; // &Lacute - case 2896: return "\u013A"; // &lacute - case 2902: return "\u29B4"; // &laemptyv - case 2906: return "\u2112"; // &lagran - case 2910: return "\u039B"; // &Lambda - case 2914: return "\u03BB"; // &lambda - case 2916: return "\u27EA"; // &Lang - case 2918: return "\u27E8"; // &lang - case 2919: return "\u2991"; // &langd - case 2921: return "\u27E8"; // &langle - case 2922: return "\u2A85"; // &lap - case 2930: return "\u2112"; // &Laplacetrf - case 2933: return "\u00AB"; // « - case 2935: return "\u219E"; // &Larr - case 2937: return "\u21D0"; // &lArr - case 2939: return "\u2190"; // &larr - case 2940: return "\u21E4"; // &larrb - case 2942: return "\u291F"; // &larrbfs - case 2944: return "\u291D"; // &larrfs - case 2946: return "\u21A9"; // &larrhk - case 2948: return "\u21AB"; // &larrlp - case 2950: return "\u2939"; // &larrpl - case 2953: return "\u2973"; // &larrsim - case 2955: return "\u21A2"; // &larrtl - case 2956: return "\u2AAB"; // &lat - case 2960: return "\u291B"; // &lAtail - case 2963: return "\u2919"; // &latail - case 2964: return "\u2AAD"; // &late - case 2965: return "\u2AAD\uFE00"; // &lates - case 2969: return "\u290E"; // &lBarr - case 2973: return "\u290C"; // &lbarr - case 2976: return "\u2772"; // &lbbrk - case 2980: return "\u007B"; // &lbrace - case 2981: return "\u005B"; // &lbrack - case 2983: return "\u298B"; // &lbrke - case 2986: return "\u298F"; // &lbrksld - case 2987: return "\u298D"; // &lbrkslu - case 2992: return "\u013D"; // &Lcaron - case 2997: return "\u013E"; // &lcaron - case 3001: return "\u013B"; // &Lcedil - case 3005: return "\u013C"; // &lcedil - case 3007: return "\u2308"; // &lceil - case 3009: return "\u007B"; // &lcub - case 3010: return "\u041B"; // &Lcy - case 3011: return "\u043B"; // &lcy - case 3014: return "\u2936"; // &ldca - case 3017: return "\u201C"; // &ldquo - case 3018: return "\u201E"; // &ldquor - case 3023: return "\u2967"; // &ldrdhar - case 3028: return "\u294B"; // &ldrushar - case 3030: return "\u21B2"; // &ldsh - case 3031: return "\u2266"; // &lE - case 3032: return "\u2264"; // &le - case 3047: return "\u27E8"; // &LeftAngleBracket - case 3051: return "\u2190"; // &LeftArrow - case 3056: return "\u21D0"; // &Leftarrow - case 3063: return "\u2190"; // &leftarrow - case 3066: return "\u21E4"; // &LeftArrowBar - case 3076: return "\u21C6"; // &LeftArrowRightArrow - case 3080: return "\u21A2"; // &leftarrowtail - case 3087: return "\u2308"; // &LeftCeiling - case 3100: return "\u27E6"; // &LeftDoubleBracket - case 3111: return "\u2961"; // &LeftDownTeeVector - case 3117: return "\u21C3"; // &LeftDownVector - case 3120: return "\u2959"; // &LeftDownVectorBar - case 3125: return "\u230A"; // &LeftFloor - case 3136: return "\u21BD"; // &leftharpoondown - case 3138: return "\u21BC"; // &leftharpoonup - case 3148: return "\u21C7"; // &leftleftarrows - case 3158: return "\u2194"; // &LeftRightArrow - case 3168: return "\u21D4"; // &Leftrightarrow - case 3178: return "\u2194"; // &leftrightarrow - case 3179: return "\u21C6"; // &leftrightarrows - case 3187: return "\u21CB"; // &leftrightharpoons - case 3197: return "\u21AD"; // &leftrightsquigarrow - case 3203: return "\u294E"; // &LeftRightVector - case 3206: return "\u22A3"; // &LeftTee - case 3211: return "\u21A4"; // &LeftTeeArrow - case 3217: return "\u295A"; // &LeftTeeVector - case 3227: return "\u22CB"; // &leftthreetimes - case 3234: return "\u22B2"; // &LeftTriangle - case 3237: return "\u29CF"; // &LeftTriangleBar - case 3242: return "\u22B4"; // &LeftTriangleEqual - case 3254: return "\u2951"; // &LeftUpDownVector - case 3263: return "\u2960"; // &LeftUpTeeVector - case 3269: return "\u21BF"; // &LeftUpVector - case 3272: return "\u2958"; // &LeftUpVectorBar - case 3278: return "\u21BC"; // &LeftVector - case 3281: return "\u2952"; // &LeftVectorBar - case 3282: return "\u2A8B"; // &lEg - case 3283: return "\u22DA"; // &leg - case 3284: return "\u2264"; // &leq - case 3285: return "\u2266"; // &leqq - case 3290: return "\u2A7D"; // &leqslant - case 3291: return "\u2A7D"; // &les - case 3293: return "\u2AA8"; // &lescc - case 3296: return "\u2A7F"; // &lesdot - case 3297: return "\u2A81"; // &lesdoto - case 3298: return "\u2A83"; // &lesdotor - case 3299: return "\u22DA\uFE00"; // &lesg - case 3301: return "\u2A93"; // &lesges - case 3308: return "\u2A85"; // &lessapprox - case 3311: return "\u22D6"; // &lessdot - case 3316: return "\u22DA"; // &lesseqgtr - case 3320: return "\u2A8B"; // &lesseqqgtr - case 3334: return "\u22DA"; // &LessEqualGreater - case 3343: return "\u2266"; // &LessFullEqual - case 3350: return "\u2276"; // &LessGreater - case 3353: return "\u2276"; // &lessgtr - case 3357: return "\u2AA1"; // &LessLess - case 3360: return "\u2272"; // &lesssim - case 3370: return "\u2A7D"; // &LessSlantEqual - case 3375: return "\u2272"; // &LessTilde - case 3380: return "\u297C"; // &lfisht - case 3384: return "\u230A"; // &lfloor - case 3386: return "\uD835\uDD0F"; // &Lfr - case 3387: return "\uD835\uDD29"; // &lfr - case 3388: return "\u2276"; // &lg - case 3389: return "\u2A91"; // &lgE - case 3392: return "\u2962"; // &lHar - case 3396: return "\u21BD"; // &lhard - case 3397: return "\u21BC"; // &lharu - case 3398: return "\u296A"; // &lharul - case 3401: return "\u2584"; // &lhblk - case 3404: return "\u0409"; // &LJcy - case 3407: return "\u0459"; // &ljcy - case 3408: return "\u22D8"; // &Ll - case 3409: return "\u226A"; // &ll - case 3412: return "\u21C7"; // &llarr - case 3418: return "\u231E"; // &llcorner - case 3426: return "\u21DA"; // &Lleftarrow - case 3430: return "\u296B"; // &llhard - case 3433: return "\u25FA"; // &lltri - case 3438: return "\u013F"; // &Lmidot - case 3443: return "\u0140"; // &lmidot - case 3447: return "\u23B0"; // &lmoust - case 3451: return "\u23B0"; // &lmoustache - case 3454: return "\u2A89"; // &lnap - case 3458: return "\u2A89"; // &lnapprox - case 3459: return "\u2268"; // &lnE - case 3460: return "\u2A87"; // &lne - case 3461: return "\u2A87"; // &lneq - case 3462: return "\u2268"; // &lneqq - case 3465: return "\u22E6"; // &lnsim - case 3469: return "\u27EC"; // &loang - case 3471: return "\u21FD"; // &loarr - case 3474: return "\u27E6"; // &lobrk - case 3486: return "\u27F5"; // &LongLeftArrow - case 3495: return "\u27F8"; // &Longleftarrow - case 3506: return "\u27F5"; // &longleftarrow - case 3516: return "\u27F7"; // &LongLeftRightArrow - case 3526: return "\u27FA"; // &Longleftrightarrow - case 3536: return "\u27F7"; // &longleftrightarrow - case 3542: return "\u27FC"; // &longmapsto - case 3552: return "\u27F6"; // &LongRightArrow - case 3562: return "\u27F9"; // &Longrightarrow - case 3572: return "\u27F6"; // &longrightarrow - case 3583: return "\u21AB"; // &looparrowleft - case 3588: return "\u21AC"; // &looparrowright - case 3591: return "\u2985"; // &lopar - case 3593: return "\uD835\uDD43"; // &Lopf - case 3594: return "\uD835\uDD5D"; // &lopf - case 3597: return "\u2A2D"; // &loplus - case 3602: return "\u2A34"; // &lotimes - case 3606: return "\u2217"; // &lowast - case 3609: return "\u005F"; // &lowbar - case 3621: return "\u2199"; // &LowerLeftArrow - case 3631: return "\u2198"; // &LowerRightArrow - case 3632: return "\u25CA"; // &loz - case 3636: return "\u25CA"; // &lozenge - case 3637: return "\u29EB"; // &lozf - case 3640: return "\u0028"; // &lpar - case 3642: return "\u2993"; // &lparlt - case 3646: return "\u21C6"; // &lrarr - case 3652: return "\u231F"; // &lrcorner - case 3655: return "\u21CB"; // &lrhar - case 3656: return "\u296D"; // &lrhard - case 3657: return "\u200E"; // &lrm - case 3660: return "\u22BF"; // &lrtri - case 3665: return "\u2039"; // &lsaquo - case 3668: return "\u2112"; // &Lscr - case 3670: return "\uD835\uDCC1"; // &lscr - case 3671: return "\u21B0"; // &Lsh - case 3672: return "\u21B0"; // &lsh - case 3674: return "\u2272"; // &lsim - case 3675: return "\u2A8D"; // &lsime - case 3676: return "\u2A8F"; // &lsimg - case 3678: return "\u005B"; // &lsqb - case 3680: return "\u2018"; // &lsquo - case 3681: return "\u201A"; // &lsquor - case 3685: return "\u0141"; // &Lstrok - case 3689: return "\u0142"; // &lstrok - case 3690: return "\u003C"; // < - case 3691: return "\u226A"; // &Lt - case 3692: return "\u003C"; // < - case 3694: return "\u2AA6"; // <cc - case 3696: return "\u2A79"; // <cir - case 3699: return "\u22D6"; // <dot - case 3703: return "\u22CB"; // <hree - case 3707: return "\u22C9"; // <imes - case 3711: return "\u2976"; // <larr - case 3716: return "\u2A7B"; // <quest - case 3718: return "\u25C3"; // <ri - case 3719: return "\u22B4"; // <rie - case 3720: return "\u25C2"; // <rif - case 3723: return "\u2996"; // <rPar - case 3730: return "\u294A"; // &lurdshar - case 3734: return "\u2966"; // &luruhar - case 3742: return "\u2268\uFE00"; // &lvertneqq - case 3744: return "\u2268\uFE00"; // &lvnE - case 3748: return "\u00AF"; // ¯ - case 3750: return "\u2642"; // &male - case 3751: return "\u2720"; // &malt - case 3754: return "\u2720"; // &maltese - case 3757: return "\u2905"; // &Map - case 3758: return "\u21A6"; // &map - case 3761: return "\u21A6"; // &mapsto - case 3765: return "\u21A7"; // &mapstodown - case 3769: return "\u21A4"; // &mapstoleft - case 3771: return "\u21A5"; // &mapstoup - case 3775: return "\u25AE"; // &marker - case 3780: return "\u2A29"; // &mcomma - case 3782: return "\u041C"; // &Mcy - case 3783: return "\u043C"; // &mcy - case 3787: return "\u2014"; // &mdash - case 3791: return "\u223A"; // &mDDot - case 3803: return "\u2221"; // &measuredangle - case 3813: return "\u205F"; // &MediumSpace - case 3820: return "\u2133"; // &Mellintrf - case 3822: return "\uD835\uDD10"; // &Mfr - case 3824: return "\uD835\uDD2A"; // &mfr - case 3826: return "\u2127"; // &mho - case 3830: return "\u00B5"; // µ - case 3831: return "\u2223"; // &mid - case 3834: return "\u002A"; // &midast - case 3837: return "\u2AF0"; // &midcir - case 3840: return "\u00B7"; // · - case 3843: return "\u2212"; // &minus - case 3844: return "\u229F"; // &minusb - case 3845: return "\u2238"; // &minusd - case 3846: return "\u2A2A"; // &minusdu - case 3854: return "\u2213"; // &MinusPlus - case 3857: return "\u2ADB"; // &mlcp - case 3859: return "\u2026"; // &mldr - case 3864: return "\u2213"; // &mnplus - case 3869: return "\u22A7"; // &models - case 3872: return "\uD835\uDD44"; // &Mopf - case 3874: return "\uD835\uDD5E"; // &mopf - case 3875: return "\u2213"; // &mp - case 3878: return "\u2133"; // &Mscr - case 3881: return "\uD835\uDCC2"; // &mscr - case 3885: return "\u223E"; // &mstpos - case 3886: return "\u039C"; // &Mu - case 3887: return "\u03BC"; // &mu - case 3893: return "\u22B8"; // &multimap - case 3896: return "\u22B8"; // &mumap - case 3901: return "\u2207"; // &nabla - case 3907: return "\u0143"; // &Nacute - case 3911: return "\u0144"; // &nacute - case 3913: return "\u2220\u20D2"; // &nang - case 3914: return "\u2249"; // &nap - case 3915: return "\u2A70\u0338"; // &napE - case 3917: return "\u224B\u0338"; // &napid - case 3919: return "\u0149"; // &napos - case 3923: return "\u2249"; // &napprox - case 3926: return "\u266E"; // &natur - case 3928: return "\u266E"; // &natural - case 3929: return "\u2115"; // &naturals - case 3932: return "\u00A0"; //   - case 3935: return "\u224E\u0338"; // &nbump - case 3936: return "\u224F\u0338"; // &nbumpe - case 3939: return "\u2A43"; // &ncap - case 3944: return "\u0147"; // &Ncaron - case 3947: return "\u0148"; // &ncaron - case 3951: return "\u0145"; // &Ncedil - case 3955: return "\u0146"; // &ncedil - case 3958: return "\u2247"; // &ncong - case 3961: return "\u2A6D\u0338"; // &ncongdot - case 3963: return "\u2A42"; // &ncup - case 3964: return "\u041D"; // &Ncy - case 3965: return "\u043D"; // &ncy - case 3969: return "\u2013"; // &ndash - case 3970: return "\u2260"; // &ne - case 3974: return "\u2924"; // &nearhk - case 3977: return "\u21D7"; // &neArr - case 3978: return "\u2197"; // &nearr - case 3980: return "\u2197"; // &nearrow - case 3983: return "\u2250\u0338"; // &nedot - case 4001: return "\u200B"; // &NegativeMediumSpace - case 4011: return "\u200B"; // &NegativeThickSpace - case 4017: return "\u200B"; // &NegativeThinSpace - case 4030: return "\u200B"; // &NegativeVeryThinSpace - case 4034: return "\u2262"; // &nequiv - case 4038: return "\u2928"; // &nesear - case 4040: return "\u2242\u0338"; // &nesim - case 4058: return "\u226B"; // &NestedGreaterGreater - case 4066: return "\u226A"; // &NestedLessLess - case 4071: return "\u000A"; // &NewLine - case 4075: return "\u2204"; // &nexist - case 4076: return "\u2204"; // &nexists - case 4078: return "\uD835\uDD11"; // &Nfr - case 4080: return "\uD835\uDD2B"; // &nfr - case 4082: return "\u2267\u0338"; // &ngE - case 4083: return "\u2271"; // &nge - case 4084: return "\u2271"; // &ngeq - case 4085: return "\u2267\u0338"; // &ngeqq - case 4090: return "\u2A7E\u0338"; // &ngeqslant - case 4091: return "\u2A7E\u0338"; // &nges - case 4093: return "\u22D9\u0338"; // &nGg - case 4096: return "\u2275"; // &ngsim - case 4097: return "\u226B\u20D2"; // &nGt - case 4098: return "\u226F"; // &ngt - case 4099: return "\u226F"; // &ngtr - case 4100: return "\u226B\u0338"; // &nGtv - case 4104: return "\u21CE"; // &nhArr - case 4107: return "\u21AE"; // &nharr - case 4110: return "\u2AF2"; // &nhpar - case 4111: return "\u220B"; // &ni - case 4112: return "\u22FC"; // &nis - case 4113: return "\u22FA"; // &nisd - case 4114: return "\u220B"; // &niv - case 4117: return "\u040A"; // &NJcy - case 4120: return "\u045A"; // &njcy - case 4124: return "\u21CD"; // &nlArr - case 4127: return "\u219A"; // &nlarr - case 4129: return "\u2025"; // &nldr - case 4130: return "\u2266\u0338"; // &nlE - case 4131: return "\u2270"; // &nle - case 4140: return "\u21CD"; // &nLeftarrow - case 4147: return "\u219A"; // &nleftarrow - case 4157: return "\u21CE"; // &nLeftrightarrow - case 4167: return "\u21AE"; // &nleftrightarrow - case 4168: return "\u2270"; // &nleq - case 4169: return "\u2266\u0338"; // &nleqq - case 4174: return "\u2A7D\u0338"; // &nleqslant - case 4175: return "\u2A7D\u0338"; // &nles - case 4176: return "\u226E"; // &nless - case 4177: return "\u22D8\u0338"; // &nLl - case 4180: return "\u2274"; // &nlsim - case 4181: return "\u226A\u20D2"; // &nLt - case 4182: return "\u226E"; // &nlt - case 4184: return "\u22EA"; // &nltri - case 4185: return "\u22EC"; // &nltrie - case 4186: return "\u226A\u0338"; // &nLtv - case 4189: return "\u2224"; // &nmid - case 4195: return "\u2060"; // &NoBreak - case 4209: return "\u00A0"; // &NonBreakingSpace - case 4211: return "\u2115"; // &Nopf - case 4214: return "\uD835\uDD5F"; // &nopf - case 4215: return "\u2AEC"; // &Not - case 4216: return "\u00AC"; // ¬ - case 4225: return "\u2262"; // &NotCongruent - case 4230: return "\u226D"; // &NotCupCap - case 4247: return "\u2226"; // &NotDoubleVerticalBar - case 4254: return "\u2209"; // &NotElement - case 4258: return "\u2260"; // &NotEqual - case 4263: return "\u2242\u0338"; // &NotEqualTilde - case 4268: return "\u2204"; // &NotExists - case 4275: return "\u226F"; // &NotGreater - case 4280: return "\u2271"; // &NotGreaterEqual - case 4289: return "\u2267\u0338"; // &NotGreaterFullEqual - case 4296: return "\u226B\u0338"; // &NotGreaterGreater - case 4300: return "\u2279"; // &NotGreaterLess - case 4310: return "\u2A7E\u0338"; // &NotGreaterSlantEqual - case 4315: return "\u2275"; // &NotGreaterTilde - case 4327: return "\u224E\u0338"; // &NotHumpDownHump - case 4332: return "\u224F\u0338"; // &NotHumpEqual - case 4334: return "\u2209"; // ¬in - case 4337: return "\u22F5\u0338"; // ¬indot - case 4338: return "\u22F9\u0338"; // ¬inE - case 4340: return "\u2209"; // ¬inva - case 4341: return "\u22F7"; // ¬invb - case 4342: return "\u22F6"; // ¬invc - case 4354: return "\u22EA"; // &NotLeftTriangle - case 4357: return "\u29CF\u0338"; // &NotLeftTriangleBar - case 4362: return "\u22EC"; // &NotLeftTriangleEqual - case 4364: return "\u226E"; // &NotLess - case 4369: return "\u2270"; // &NotLessEqual - case 4376: return "\u2278"; // &NotLessGreater - case 4380: return "\u226A\u0338"; // &NotLessLess - case 4390: return "\u2A7D\u0338"; // &NotLessSlantEqual - case 4395: return "\u2274"; // &NotLessTilde - case 4415: return "\u2AA2\u0338"; // &NotNestedGreaterGreater - case 4423: return "\u2AA1\u0338"; // &NotNestedLessLess - case 4425: return "\u220C"; // ¬ni - case 4427: return "\u220C"; // ¬niva - case 4428: return "\u22FE"; // ¬nivb - case 4429: return "\u22FD"; // ¬nivc - case 4437: return "\u2280"; // &NotPrecedes - case 4442: return "\u2AAF\u0338"; // &NotPrecedesEqual - case 4452: return "\u22E0"; // &NotPrecedesSlantEqual - case 4466: return "\u220C"; // &NotReverseElement - case 4478: return "\u22EB"; // &NotRightTriangle - case 4481: return "\u29D0\u0338"; // &NotRightTriangleBar - case 4486: return "\u22ED"; // &NotRightTriangleEqual - case 4498: return "\u228F\u0338"; // &NotSquareSubset - case 4503: return "\u22E2"; // &NotSquareSubsetEqual - case 4509: return "\u2290\u0338"; // &NotSquareSuperset - case 4514: return "\u22E3"; // &NotSquareSupersetEqual - case 4519: return "\u2282\u20D2"; // &NotSubset - case 4524: return "\u2288"; // &NotSubsetEqual - case 4530: return "\u2281"; // &NotSucceeds - case 4535: return "\u2AB0\u0338"; // &NotSucceedsEqual - case 4545: return "\u22E1"; // &NotSucceedsSlantEqual - case 4550: return "\u227F\u0338"; // &NotSucceedsTilde - case 4556: return "\u2283\u20D2"; // &NotSuperset - case 4561: return "\u2289"; // &NotSupersetEqual - case 4566: return "\u2241"; // &NotTilde - case 4571: return "\u2244"; // &NotTildeEqual - case 4580: return "\u2247"; // &NotTildeFullEqual - case 4585: return "\u2249"; // &NotTildeTilde - case 4596: return "\u2224"; // &NotVerticalBar - case 4599: return "\u2226"; // &npar - case 4604: return "\u2226"; // &nparallel - case 4606: return "\u2AFD\u20E5"; // &nparsl - case 4607: return "\u2202\u0338"; // &npart - case 4612: return "\u2A14"; // &npolint - case 4613: return "\u2280"; // &npr - case 4616: return "\u22E0"; // &nprcue - case 4617: return "\u2AAF\u0338"; // &npre - case 4618: return "\u2280"; // &nprec - case 4620: return "\u2AAF\u0338"; // &npreceq - case 4624: return "\u21CF"; // &nrArr - case 4627: return "\u219B"; // &nrarr - case 4628: return "\u2933\u0338"; // &nrarrc - case 4629: return "\u219D\u0338"; // &nrarrw - case 4639: return "\u21CF"; // &nRightarrow - case 4648: return "\u219B"; // &nrightarrow - case 4651: return "\u22EB"; // &nrtri - case 4652: return "\u22ED"; // &nrtrie - case 4654: return "\u2281"; // &nsc - case 4657: return "\u22E1"; // &nsccue - case 4658: return "\u2AB0\u0338"; // &nsce - case 4661: return "\uD835\uDCA9"; // &Nscr - case 4662: return "\uD835\uDCC3"; // &nscr - case 4669: return "\u2224"; // &nshortmid - case 4677: return "\u2226"; // &nshortparallel - case 4679: return "\u2241"; // &nsim - case 4680: return "\u2244"; // &nsime - case 4681: return "\u2244"; // &nsimeq - case 4684: return "\u2224"; // &nsmid - case 4687: return "\u2226"; // &nspar - case 4692: return "\u22E2"; // &nsqsube - case 4694: return "\u22E3"; // &nsqsupe - case 4696: return "\u2284"; // &nsub - case 4697: return "\u2AC5\u0338"; // &nsubE - case 4698: return "\u2288"; // &nsube - case 4701: return "\u2282\u20D2"; // &nsubset - case 4703: return "\u2288"; // &nsubseteq - case 4704: return "\u2AC5\u0338"; // &nsubseteqq - case 4706: return "\u2281"; // &nsucc - case 4708: return "\u2AB0\u0338"; // &nsucceq - case 4709: return "\u2285"; // &nsup - case 4710: return "\u2AC6\u0338"; // &nsupE - case 4711: return "\u2289"; // &nsupe - case 4714: return "\u2283\u20D2"; // &nsupset - case 4716: return "\u2289"; // &nsupseteq - case 4717: return "\u2AC6\u0338"; // &nsupseteqq - case 4720: return "\u2279"; // &ntgl - case 4725: return "\u00D1"; // Ñ - case 4729: return "\u00F1"; // ñ - case 4731: return "\u2278"; // &ntlg - case 4742: return "\u22EA"; // &ntriangleleft - case 4744: return "\u22EC"; // &ntrianglelefteq - case 4749: return "\u22EB"; // &ntriangleright - case 4751: return "\u22ED"; // &ntrianglerighteq - case 4752: return "\u039D"; // &Nu - case 4753: return "\u03BD"; // &nu - case 4754: return "\u0023"; // &num - case 4757: return "\u2116"; // &numero - case 4759: return "\u2007"; // &numsp - case 4762: return "\u224D\u20D2"; // &nvap - case 4767: return "\u22AF"; // &nVDash - case 4771: return "\u22AE"; // &nVdash - case 4775: return "\u22AD"; // &nvDash - case 4779: return "\u22AC"; // &nvdash - case 4781: return "\u2265\u20D2"; // &nvge - case 4782: return "\u003E\u20D2"; // &nvgt - case 4786: return "\u2904"; // &nvHarr - case 4791: return "\u29DE"; // &nvinfin - case 4795: return "\u2902"; // &nvlArr - case 4796: return "\u2264\u20D2"; // &nvle - case 4797: return "\u003C\u20D2"; // &nvlt - case 4800: return "\u22B4\u20D2"; // &nvltrie - case 4804: return "\u2903"; // &nvrArr - case 4808: return "\u22B5\u20D2"; // &nvrtrie - case 4811: return "\u223C\u20D2"; // &nvsim - case 4816: return "\u2923"; // &nwarhk - case 4819: return "\u21D6"; // &nwArr - case 4820: return "\u2196"; // &nwarr - case 4822: return "\u2196"; // &nwarrow - case 4826: return "\u2927"; // &nwnear - case 4832: return "\u00D3"; // Ó - case 4838: return "\u00F3"; // ó - case 4840: return "\u229B"; // &oast - case 4843: return "\u229A"; // &ocir - case 4847: return "\u00D4"; // Ô - case 4848: return "\u00F4"; // ô - case 4849: return "\u041E"; // &Ocy - case 4850: return "\u043E"; // &ocy - case 4854: return "\u229D"; // &odash - case 4859: return "\u0150"; // &Odblac - case 4863: return "\u0151"; // &odblac - case 4865: return "\u2A38"; // &odiv - case 4867: return "\u2299"; // &odot - case 4871: return "\u29BC"; // &odsold - case 4875: return "\u0152"; // &OElig - case 4879: return "\u0153"; // &oelig - case 4883: return "\u29BF"; // &ofcir - case 4885: return "\uD835\uDD12"; // &Ofr - case 4886: return "\uD835\uDD2C"; // &ofr - case 4889: return "\u02DB"; // &ogon - case 4894: return "\u00D2"; // Ò - case 4898: return "\u00F2"; // ò - case 4899: return "\u29C1"; // &ogt - case 4903: return "\u29B5"; // &ohbar - case 4904: return "\u03A9"; // &ohm - case 4907: return "\u222E"; // &oint - case 4911: return "\u21BA"; // &olarr - case 4914: return "\u29BE"; // &olcir - case 4918: return "\u29BB"; // &olcross - case 4921: return "\u203E"; // &oline - case 4922: return "\u29C0"; // &olt - case 4926: return "\u014C"; // &Omacr - case 4930: return "\u014D"; // &omacr - case 4933: return "\u03A9"; // &Omega - case 4936: return "\u03C9"; // &omega - case 4941: return "\u039F"; // &Omicron - case 4946: return "\u03BF"; // &omicron - case 4947: return "\u29B6"; // &omid - case 4950: return "\u2296"; // &ominus - case 4953: return "\uD835\uDD46"; // &Oopf - case 4956: return "\uD835\uDD60"; // &oopf - case 4959: return "\u29B7"; // &opar - case 4978: return "\u201C"; // &OpenCurlyDoubleQuote - case 4983: return "\u2018"; // &OpenCurlyQuote - case 4986: return "\u29B9"; // &operp - case 4989: return "\u2295"; // &oplus - case 4990: return "\u2A54"; // &Or - case 4991: return "\u2228"; // &or - case 4994: return "\u21BB"; // &orarr - case 4995: return "\u2A5D"; // &ord - case 4997: return "\u2134"; // &order - case 4999: return "\u2134"; // &orderof - case 5000: return "\u00AA"; // ª - case 5001: return "\u00BA"; // º - case 5005: return "\u22B6"; // &origof - case 5007: return "\u2A56"; // &oror - case 5012: return "\u2A57"; // &orslope - case 5013: return "\u2A5B"; // &orv - case 5014: return "\u24C8"; // &oS - case 5017: return "\uD835\uDCAA"; // &Oscr - case 5020: return "\u2134"; // &oscr - case 5024: return "\u00D8"; // Ø - case 5028: return "\u00F8"; // ø - case 5030: return "\u2298"; // &osol - case 5035: return "\u00D5"; // Õ - case 5040: return "\u00F5"; // õ - case 5043: return "\u2A37"; // &Otimes - case 5046: return "\u2297"; // &otimes - case 5048: return "\u2A36"; // &otimesas - case 5051: return "\u00D6"; // Ö - case 5054: return "\u00F6"; // ö - case 5058: return "\u233D"; // &ovbar - case 5064: return "\u203E"; // &OverBar - case 5068: return "\u23DE"; // &OverBrace - case 5071: return "\u23B4"; // &OverBracket - case 5082: return "\u23DC"; // &OverParenthesis - case 5085: return "\u2225"; // &par - case 5086: return "\u00B6"; // ¶ - case 5090: return "\u2225"; // ¶llel - case 5093: return "\u2AF3"; // &parsim - case 5094: return "\u2AFD"; // &parsl - case 5095: return "\u2202"; // &part - case 5103: return "\u2202"; // &PartialD - case 5105: return "\u041F"; // &Pcy - case 5107: return "\u043F"; // &pcy - case 5112: return "\u0025"; // &percnt - case 5115: return "\u002E"; // &period - case 5118: return "\u2030"; // &permil - case 5119: return "\u22A5"; // &perp - case 5123: return "\u2031"; // &pertenk - case 5125: return "\uD835\uDD13"; // &Pfr - case 5127: return "\uD835\uDD2D"; // &pfr - case 5129: return "\u03A6"; // &Phi - case 5131: return "\u03C6"; // &phi - case 5132: return "\u03D5"; // &phiv - case 5136: return "\u2133"; // &phmmat - case 5139: return "\u260E"; // &phone - case 5140: return "\u03A0"; // &Pi - case 5141: return "\u03C0"; // &pi - case 5148: return "\u22D4"; // &pitchfork - case 5149: return "\u03D6"; // &piv - case 5154: return "\u210F"; // &planck - case 5155: return "\u210E"; // &planckh - case 5157: return "\u210F"; // &plankv - case 5159: return "\u002B"; // &plus - case 5163: return "\u2A23"; // &plusacir - case 5164: return "\u229E"; // &plusb - case 5167: return "\u2A22"; // &pluscir - case 5169: return "\u2214"; // &plusdo - case 5170: return "\u2A25"; // &plusdu - case 5171: return "\u2A72"; // &pluse - case 5179: return "\u00B1"; // &PlusMinus - case 5181: return "\u00B1"; // ± - case 5184: return "\u2A26"; // &plussim - case 5187: return "\u2A27"; // &plustwo - case 5188: return "\u00B1"; // &pm - case 5200: return "\u210C"; // &Poincareplane - case 5207: return "\u2A15"; // &pointint - case 5209: return "\u2119"; // &Popf - case 5211: return "\uD835\uDD61"; // &popf - case 5214: return "\u00A3"; // £ - case 5215: return "\u2ABB"; // &Pr - case 5216: return "\u227A"; // &pr - case 5218: return "\u2AB7"; // &prap - case 5221: return "\u227C"; // &prcue - case 5222: return "\u2AB3"; // &prE - case 5223: return "\u2AAF"; // &pre - case 5224: return "\u227A"; // &prec - case 5230: return "\u2AB7"; // &precapprox - case 5237: return "\u227C"; // &preccurlyeq - case 5243: return "\u227A"; // &Precedes - case 5248: return "\u2AAF"; // &PrecedesEqual - case 5258: return "\u227C"; // &PrecedesSlantEqual - case 5263: return "\u227E"; // &PrecedesTilde - case 5265: return "\u2AAF"; // &preceq - case 5272: return "\u2AB9"; // &precnapprox - case 5275: return "\u2AB5"; // &precneqq - case 5278: return "\u22E8"; // &precnsim - case 5281: return "\u227E"; // &precsim - case 5284: return "\u2033"; // &Prime - case 5287: return "\u2032"; // &prime - case 5288: return "\u2119"; // &primes - case 5291: return "\u2AB9"; // &prnap - case 5292: return "\u2AB5"; // &prnE - case 5295: return "\u22E8"; // &prnsim - case 5297: return "\u220F"; // &prod - case 5302: return "\u220F"; // &Product - case 5307: return "\u232E"; // &profalar - case 5311: return "\u2312"; // &profline - case 5315: return "\u2313"; // &profsurf - case 5316: return "\u221D"; // &prop - case 5323: return "\u2237"; // &Proportion - case 5325: return "\u221D"; // &Proportional - case 5327: return "\u221D"; // &propto - case 5330: return "\u227E"; // &prsim - case 5334: return "\u22B0"; // &prurel - case 5337: return "\uD835\uDCAB"; // &Pscr - case 5340: return "\uD835\uDCC5"; // &pscr - case 5341: return "\u03A8"; // &Psi - case 5342: return "\u03C8"; // &psi - case 5347: return "\u2008"; // &puncsp - case 5350: return "\uD835\uDD14"; // &Qfr - case 5353: return "\uD835\uDD2E"; // &qfr - case 5356: return "\u2A0C"; // &qint - case 5359: return "\u211A"; // &Qopf - case 5362: return "\uD835\uDD62"; // &qopf - case 5367: return "\u2057"; // &qprime - case 5370: return "\uD835\uDCAC"; // &Qscr - case 5373: return "\uD835\uDCC6"; // &qscr - case 5383: return "\u210D"; // &quaternions - case 5386: return "\u2A16"; // &quatint - case 5389: return "\u003F"; // &quest - case 5391: return "\u225F"; // &questeq - case 5394: return "\u0022"; // " - case 5396: return "\u0022"; // " - case 5401: return "\u21DB"; // &rAarr - case 5404: return "\u223D\u0331"; // &race - case 5410: return "\u0154"; // &Racute - case 5413: return "\u0155"; // &racute - case 5416: return "\u221A"; // &radic - case 5422: return "\u29B3"; // &raemptyv - case 5424: return "\u27EB"; // &Rang - case 5426: return "\u27E9"; // &rang - case 5427: return "\u2992"; // &rangd - case 5428: return "\u29A5"; // &range - case 5430: return "\u27E9"; // &rangle - case 5433: return "\u00BB"; // » - case 5435: return "\u21A0"; // &Rarr - case 5437: return "\u21D2"; // &rArr - case 5439: return "\u2192"; // &rarr - case 5441: return "\u2975"; // &rarrap - case 5442: return "\u21E5"; // &rarrb - case 5444: return "\u2920"; // &rarrbfs - case 5445: return "\u2933"; // &rarrc - case 5447: return "\u291E"; // &rarrfs - case 5449: return "\u21AA"; // &rarrhk - case 5451: return "\u21AC"; // &rarrlp - case 5453: return "\u2945"; // &rarrpl - case 5456: return "\u2974"; // &rarrsim - case 5458: return "\u2916"; // &Rarrtl - case 5460: return "\u21A3"; // &rarrtl - case 5461: return "\u219D"; // &rarrw - case 5465: return "\u291C"; // &rAtail - case 5469: return "\u291A"; // &ratail - case 5471: return "\u2236"; // &ratio - case 5475: return "\u211A"; // &rationals - case 5479: return "\u2910"; // &RBarr - case 5483: return "\u290F"; // &rBarr - case 5487: return "\u290D"; // &rbarr - case 5490: return "\u2773"; // &rbbrk - case 5494: return "\u007D"; // &rbrace - case 5495: return "\u005D"; // &rbrack - case 5497: return "\u298C"; // &rbrke - case 5500: return "\u298E"; // &rbrksld - case 5501: return "\u2990"; // &rbrkslu - case 5506: return "\u0158"; // &Rcaron - case 5511: return "\u0159"; // &rcaron - case 5515: return "\u0156"; // &Rcedil - case 5519: return "\u0157"; // &rcedil - case 5521: return "\u2309"; // &rceil - case 5523: return "\u007D"; // &rcub - case 5524: return "\u0420"; // &Rcy - case 5525: return "\u0440"; // &rcy - case 5528: return "\u2937"; // &rdca - case 5533: return "\u2969"; // &rdldhar - case 5536: return "\u201D"; // &rdquo - case 5537: return "\u201D"; // &rdquor - case 5539: return "\u21B3"; // &rdsh - case 5540: return "\u211C"; // &Re - case 5543: return "\u211C"; // &real - case 5546: return "\u211B"; // &realine - case 5550: return "\u211C"; // &realpart - case 5551: return "\u211D"; // &reals - case 5553: return "\u25AD"; // &rect - case 5555: return "\u00AE"; // ® - case 5556: return "\u00AE"; // ® - case 5568: return "\u220B"; // &ReverseElement - case 5578: return "\u21CB"; // &ReverseEquilibrium - case 5591: return "\u296F"; // &ReverseUpEquilibrium - case 5596: return "\u297D"; // &rfisht - case 5600: return "\u230B"; // &rfloor - case 5602: return "\u211C"; // &Rfr - case 5603: return "\uD835\uDD2F"; // &rfr - case 5606: return "\u2964"; // &rHar - case 5610: return "\u21C1"; // &rhard - case 5611: return "\u21C0"; // &rharu - case 5612: return "\u296C"; // &rharul - case 5614: return "\u03A1"; // &Rho - case 5615: return "\u03C1"; // &rho - case 5616: return "\u03F1"; // &rhov - case 5632: return "\u27E9"; // &RightAngleBracket - case 5636: return "\u2192"; // &RightArrow - case 5641: return "\u21D2"; // &Rightarrow - case 5650: return "\u2192"; // &rightarrow - case 5653: return "\u21E5"; // &RightArrowBar - case 5662: return "\u21C4"; // &RightArrowLeftArrow - case 5666: return "\u21A3"; // &rightarrowtail - case 5673: return "\u2309"; // &RightCeiling - case 5686: return "\u27E7"; // &RightDoubleBracket - case 5697: return "\u295D"; // &RightDownTeeVector - case 5703: return "\u21C2"; // &RightDownVector - case 5706: return "\u2955"; // &RightDownVectorBar - case 5711: return "\u230B"; // &RightFloor - case 5722: return "\u21C1"; // &rightharpoondown - case 5724: return "\u21C0"; // &rightharpoonup - case 5734: return "\u21C4"; // &rightleftarrows - case 5742: return "\u21CC"; // &rightleftharpoons - case 5753: return "\u21C9"; // &rightrightarrows - case 5763: return "\u219D"; // &rightsquigarrow - case 5766: return "\u22A2"; // &RightTee - case 5771: return "\u21A6"; // &RightTeeArrow - case 5777: return "\u295B"; // &RightTeeVector - case 5787: return "\u22CC"; // &rightthreetimes - case 5794: return "\u22B3"; // &RightTriangle - case 5797: return "\u29D0"; // &RightTriangleBar - case 5802: return "\u22B5"; // &RightTriangleEqual - case 5814: return "\u294F"; // &RightUpDownVector - case 5823: return "\u295C"; // &RightUpTeeVector - case 5829: return "\u21BE"; // &RightUpVector - case 5832: return "\u2954"; // &RightUpVectorBar - case 5838: return "\u21C0"; // &RightVector - case 5841: return "\u2953"; // &RightVectorBar - case 5843: return "\u02DA"; // &ring - case 5853: return "\u2253"; // &risingdotseq - case 5857: return "\u21C4"; // &rlarr - case 5860: return "\u21CC"; // &rlhar - case 5861: return "\u200F"; // &rlm - case 5866: return "\u23B1"; // &rmoust - case 5870: return "\u23B1"; // &rmoustache - case 5874: return "\u2AEE"; // &rnmid - case 5878: return "\u27ED"; // &roang - case 5880: return "\u21FE"; // &roarr - case 5883: return "\u27E7"; // &robrk - case 5886: return "\u2986"; // &ropar - case 5889: return "\u211D"; // &Ropf - case 5890: return "\uD835\uDD63"; // &ropf - case 5893: return "\u2A2E"; // &roplus - case 5898: return "\u2A35"; // &rotimes - case 5908: return "\u2970"; // &RoundImplies - case 5911: return "\u0029"; // &rpar - case 5913: return "\u2994"; // &rpargt - case 5919: return "\u2A12"; // &rppolint - case 5923: return "\u21C9"; // &rrarr - case 5933: return "\u21DB"; // &Rrightarrow - case 5938: return "\u203A"; // &rsaquo - case 5941: return "\u211B"; // &Rscr - case 5943: return "\uD835\uDCC7"; // &rscr - case 5944: return "\u21B1"; // &Rsh - case 5945: return "\u21B1"; // &rsh - case 5947: return "\u005D"; // &rsqb - case 5949: return "\u2019"; // &rsquo - case 5950: return "\u2019"; // &rsquor - case 5955: return "\u22CC"; // &rthree - case 5959: return "\u22CA"; // &rtimes - case 5961: return "\u25B9"; // &rtri - case 5962: return "\u22B5"; // &rtrie - case 5963: return "\u25B8"; // &rtrif - case 5967: return "\u29CE"; // &rtriltri - case 5977: return "\u29F4"; // &RuleDelayed - case 5983: return "\u2968"; // &ruluhar - case 5984: return "\u211E"; // &rx - case 5990: return "\u015A"; // &Sacute - case 5996: return "\u015B"; // &sacute - case 6000: return "\u201A"; // &sbquo - case 6001: return "\u2ABC"; // &Sc - case 6002: return "\u227B"; // &sc - case 6004: return "\u2AB8"; // &scap - case 6008: return "\u0160"; // &Scaron - case 6011: return "\u0161"; // &scaron - case 6014: return "\u227D"; // &sccue - case 6015: return "\u2AB4"; // &scE - case 6016: return "\u2AB0"; // &sce - case 6020: return "\u015E"; // &Scedil - case 6023: return "\u015F"; // &scedil - case 6026: return "\u015C"; // &Scirc - case 6029: return "\u015D"; // &scirc - case 6032: return "\u2ABA"; // &scnap - case 6033: return "\u2AB6"; // &scnE - case 6036: return "\u22E9"; // &scnsim - case 6042: return "\u2A13"; // &scpolint - case 6045: return "\u227F"; // &scsim - case 6046: return "\u0421"; // &Scy - case 6047: return "\u0441"; // &scy - case 6050: return "\u22C5"; // &sdot - case 6051: return "\u22A1"; // &sdotb - case 6052: return "\u2A66"; // &sdote - case 6057: return "\u2925"; // &searhk - case 6060: return "\u21D8"; // &seArr - case 6061: return "\u2198"; // &searr - case 6063: return "\u2198"; // &searrow - case 6065: return "\u00A7"; // § - case 6067: return "\u003B"; // &semi - case 6071: return "\u2929"; // &seswar - case 6077: return "\u2216"; // &setminus - case 6078: return "\u2216"; // &setmn - case 6080: return "\u2736"; // &sext - case 6082: return "\uD835\uDD16"; // &Sfr - case 6084: return "\uD835\uDD30"; // &sfr - case 6087: return "\u2322"; // &sfrown - case 6091: return "\u266F"; // &sharp - case 6096: return "\u0429"; // &SHCHcy - case 6100: return "\u0449"; // &shchcy - case 6102: return "\u0428"; // &SHcy - case 6103: return "\u0448"; // &shcy - case 6116: return "\u2193"; // &ShortDownArrow - case 6125: return "\u2190"; // &ShortLeftArrow - case 6131: return "\u2223"; // &shortmid - case 6139: return "\u2225"; // &shortparallel - case 6149: return "\u2192"; // &ShortRightArrow - case 6156: return "\u2191"; // &ShortUpArrow - case 6157: return "\u00AD"; // ­ - case 6161: return "\u03A3"; // &Sigma - case 6165: return "\u03C3"; // &sigma - case 6166: return "\u03C2"; // &sigmaf - case 6167: return "\u03C2"; // &sigmav - case 6168: return "\u223C"; // &sim - case 6171: return "\u2A6A"; // &simdot - case 6172: return "\u2243"; // &sime - case 6173: return "\u2243"; // &simeq - case 6174: return "\u2A9E"; // &simg - case 6175: return "\u2AA0"; // &simgE - case 6176: return "\u2A9D"; // &siml - case 6177: return "\u2A9F"; // &simlE - case 6179: return "\u2246"; // &simne - case 6183: return "\u2A24"; // &simplus - case 6187: return "\u2972"; // &simrarr - case 6191: return "\u2190"; // &slarr - case 6201: return "\u2218"; // &SmallCircle - case 6213: return "\u2216"; // &smallsetminus - case 6216: return "\u2A33"; // &smashp - case 6222: return "\u29E4"; // &smeparsl - case 6224: return "\u2223"; // &smid - case 6226: return "\u2323"; // &smile - case 6227: return "\u2AAA"; // &smt - case 6228: return "\u2AAC"; // &smte - case 6229: return "\u2AAC\uFE00"; // &smtes - case 6234: return "\u042C"; // &SOFTcy - case 6239: return "\u044C"; // &softcy - case 6240: return "\u002F"; // &sol - case 6241: return "\u29C4"; // &solb - case 6243: return "\u233F"; // &solbar - case 6246: return "\uD835\uDD4A"; // &Sopf - case 6248: return "\uD835\uDD64"; // &sopf - case 6253: return "\u2660"; // &spades - case 6256: return "\u2660"; // &spadesuit - case 6257: return "\u2225"; // &spar - case 6261: return "\u2293"; // &sqcap - case 6262: return "\u2293\uFE00"; // &sqcaps - case 6264: return "\u2294"; // &sqcup - case 6265: return "\u2294\uFE00"; // &sqcups - case 6268: return "\u221A"; // &Sqrt - case 6271: return "\u228F"; // &sqsub - case 6272: return "\u2291"; // &sqsube - case 6275: return "\u228F"; // &sqsubset - case 6277: return "\u2291"; // &sqsubseteq - case 6278: return "\u2290"; // &sqsup - case 6279: return "\u2292"; // &sqsupe - case 6282: return "\u2290"; // &sqsupset - case 6284: return "\u2292"; // &sqsupseteq - case 6285: return "\u25A1"; // &squ - case 6289: return "\u25A1"; // &Square - case 6292: return "\u25A1"; // &square - case 6304: return "\u2293"; // &SquareIntersection - case 6310: return "\u228F"; // &SquareSubset - case 6315: return "\u2291"; // &SquareSubsetEqual - case 6321: return "\u2290"; // &SquareSuperset - case 6326: return "\u2292"; // &SquareSupersetEqual - case 6331: return "\u2294"; // &SquareUnion - case 6332: return "\u25AA"; // &squarf - case 6333: return "\u25AA"; // &squf - case 6337: return "\u2192"; // &srarr - case 6340: return "\uD835\uDCAE"; // &Sscr - case 6343: return "\uD835\uDCC8"; // &sscr - case 6347: return "\u2216"; // &ssetmn - case 6351: return "\u2323"; // &ssmile - case 6355: return "\u22C6"; // &sstarf - case 6358: return "\u22C6"; // &Star - case 6361: return "\u2606"; // &star - case 6362: return "\u2605"; // &starf - case 6375: return "\u03F5"; // &straightepsilon - case 6378: return "\u03D5"; // &straightphi - case 6380: return "\u00AF"; // &strns - case 6382: return "\u22D0"; // &Sub - case 6384: return "\u2282"; // &sub - case 6387: return "\u2ABD"; // &subdot - case 6388: return "\u2AC5"; // &subE - case 6389: return "\u2286"; // &sube - case 6392: return "\u2AC3"; // &subedot - case 6396: return "\u2AC1"; // &submult - case 6398: return "\u2ACB"; // &subnE - case 6399: return "\u228A"; // &subne - case 6403: return "\u2ABF"; // &subplus - case 6407: return "\u2979"; // &subrarr - case 6410: return "\u22D0"; // &Subset - case 6413: return "\u2282"; // &subset - case 6415: return "\u2286"; // &subseteq - case 6416: return "\u2AC5"; // &subseteqq - case 6421: return "\u2286"; // &SubsetEqual - case 6424: return "\u228A"; // &subsetneq - case 6425: return "\u2ACB"; // &subsetneqq - case 6427: return "\u2AC7"; // &subsim - case 6429: return "\u2AD5"; // &subsub - case 6430: return "\u2AD3"; // &subsup - case 6432: return "\u227B"; // &succ - case 6438: return "\u2AB8"; // &succapprox - case 6445: return "\u227D"; // &succcurlyeq - case 6451: return "\u227B"; // &Succeeds - case 6456: return "\u2AB0"; // &SucceedsEqual - case 6466: return "\u227D"; // &SucceedsSlantEqual - case 6471: return "\u227F"; // &SucceedsTilde - case 6473: return "\u2AB0"; // &succeq - case 6480: return "\u2ABA"; // &succnapprox - case 6483: return "\u2AB6"; // &succneqq - case 6486: return "\u22E9"; // &succnsim - case 6489: return "\u227F"; // &succsim - case 6494: return "\u220B"; // &SuchThat - case 6495: return "\u2211"; // &Sum - case 6496: return "\u2211"; // &sum - case 6498: return "\u266A"; // &sung - case 6499: return "\u22D1"; // &Sup - case 6500: return "\u2283"; // &sup - case 6501: return "\u00B9"; // ¹ - case 6502: return "\u00B2"; // ² - case 6503: return "\u00B3"; // ³ - case 6506: return "\u2ABE"; // &supdot - case 6509: return "\u2AD8"; // &supdsub - case 6510: return "\u2AC6"; // &supE - case 6511: return "\u2287"; // &supe - case 6514: return "\u2AC4"; // &supedot - case 6519: return "\u2283"; // &Superset - case 6524: return "\u2287"; // &SupersetEqual - case 6528: return "\u27C9"; // &suphsol - case 6530: return "\u2AD7"; // &suphsub - case 6534: return "\u297B"; // &suplarr - case 6538: return "\u2AC2"; // &supmult - case 6540: return "\u2ACC"; // &supnE - case 6541: return "\u228B"; // &supne - case 6545: return "\u2AC0"; // &supplus - case 6548: return "\u22D1"; // &Supset - case 6551: return "\u2283"; // &supset - case 6553: return "\u2287"; // &supseteq - case 6554: return "\u2AC6"; // &supseteqq - case 6557: return "\u228B"; // &supsetneq - case 6558: return "\u2ACC"; // &supsetneqq - case 6560: return "\u2AC8"; // &supsim - case 6562: return "\u2AD4"; // &supsub - case 6563: return "\u2AD6"; // &supsup - case 6568: return "\u2926"; // &swarhk - case 6571: return "\u21D9"; // &swArr - case 6572: return "\u2199"; // &swarr - case 6574: return "\u2199"; // &swarrow - case 6578: return "\u292A"; // &swnwar - case 6582: return "\u00DF"; // ß - case 6585: return "\u0009"; // &Tab - case 6591: return "\u2316"; // &target - case 6592: return "\u03A4"; // &Tau - case 6593: return "\u03C4"; // &tau - case 6596: return "\u23B4"; // &tbrk - case 6601: return "\u0164"; // &Tcaron - case 6606: return "\u0165"; // &tcaron - case 6610: return "\u0162"; // &Tcedil - case 6614: return "\u0163"; // &tcedil - case 6615: return "\u0422"; // &Tcy - case 6616: return "\u0442"; // &tcy - case 6619: return "\u20DB"; // &tdot - case 6624: return "\u2315"; // &telrec - case 6626: return "\uD835\uDD17"; // &Tfr - case 6628: return "\uD835\uDD31"; // &tfr - case 6633: return "\u2234"; // &there4 - case 6641: return "\u2234"; // &Therefore - case 6645: return "\u2234"; // &therefore - case 6647: return "\u0398"; // &Theta - case 6649: return "\u03B8"; // &theta - case 6652: return "\u03D1"; // &thetasym - case 6653: return "\u03D1"; // &thetav - case 6662: return "\u2248"; // &thickapprox - case 6665: return "\u223C"; // &thicksim - case 6673: return "\u205F\u200A"; // &ThickSpace - case 6676: return "\u2009"; // &thinsp - case 6682: return "\u2009"; // &ThinSpace - case 6685: return "\u2248"; // &thkap - case 6688: return "\u223C"; // &thksim - case 6692: return "\u00DE"; // Þ - case 6695: return "\u00FE"; // þ - case 6699: return "\u223C"; // &Tilde - case 6703: return "\u02DC"; // &tilde - case 6708: return "\u2243"; // &TildeEqual - case 6717: return "\u2245"; // &TildeFullEqual - case 6722: return "\u2248"; // &TildeTilde - case 6725: return "\u00D7"; // × - case 6726: return "\u22A0"; // ×b - case 6728: return "\u2A31"; // ×bar - case 6729: return "\u2A30"; // ×d - case 6731: return "\u222D"; // &tint - case 6734: return "\u2928"; // &toea - case 6735: return "\u22A4"; // &top - case 6738: return "\u2336"; // &topbot - case 6741: return "\u2AF1"; // &topcir - case 6744: return "\uD835\uDD4B"; // &Topf - case 6745: return "\uD835\uDD65"; // &topf - case 6748: return "\u2ADA"; // &topfork - case 6750: return "\u2929"; // &tosa - case 6755: return "\u2034"; // &tprime - case 6759: return "\u2122"; // &TRADE - case 6763: return "\u2122"; // &trade - case 6769: return "\u25B5"; // &triangle - case 6773: return "\u25BF"; // &triangledown - case 6777: return "\u25C3"; // &triangleleft - case 6779: return "\u22B4"; // &trianglelefteq - case 6780: return "\u225C"; // &triangleq - case 6785: return "\u25B9"; // &triangleright - case 6787: return "\u22B5"; // &trianglerighteq - case 6790: return "\u25EC"; // &tridot - case 6791: return "\u225C"; // &trie - case 6796: return "\u2A3A"; // &triminus - case 6804: return "\u20DB"; // &TripleDot - case 6808: return "\u2A39"; // &triplus - case 6810: return "\u29CD"; // &trisb - case 6814: return "\u2A3B"; // &tritime - case 6820: return "\u23E2"; // &trpezium - case 6823: return "\uD835\uDCAF"; // &Tscr - case 6826: return "\uD835\uDCC9"; // &tscr - case 6829: return "\u0426"; // &TScy - case 6830: return "\u0446"; // &tscy - case 6833: return "\u040B"; // &TSHcy - case 6836: return "\u045B"; // &tshcy - case 6840: return "\u0166"; // &Tstrok - case 6844: return "\u0167"; // &tstrok - case 6848: return "\u226C"; // &twixt - case 6862: return "\u219E"; // &twoheadleftarrow - case 6872: return "\u21A0"; // &twoheadrightarrow - case 6878: return "\u00DA"; // Ú - case 6884: return "\u00FA"; // ú - case 6886: return "\u219F"; // &Uarr - case 6889: return "\u21D1"; // &uArr - case 6891: return "\u2191"; // &uarr - case 6895: return "\u2949"; // &Uarrocir - case 6899: return "\u040E"; // &Ubrcy - case 6903: return "\u045E"; // &ubrcy - case 6906: return "\u016C"; // &Ubreve - case 6909: return "\u016D"; // &ubreve - case 6913: return "\u00DB"; // Û - case 6917: return "\u00FB"; // û - case 6918: return "\u0423"; // &Ucy - case 6919: return "\u0443"; // &ucy - case 6923: return "\u21C5"; // &udarr - case 6928: return "\u0170"; // &Udblac - case 6932: return "\u0171"; // &udblac - case 6935: return "\u296E"; // &udhar - case 6940: return "\u297E"; // &ufisht - case 6942: return "\uD835\uDD18"; // &Ufr - case 6943: return "\uD835\uDD32"; // &ufr - case 6948: return "\u00D9"; // Ù - case 6953: return "\u00F9"; // ù - case 6956: return "\u2963"; // &uHar - case 6960: return "\u21BF"; // &uharl - case 6961: return "\u21BE"; // &uharr - case 6964: return "\u2580"; // &uhblk - case 6969: return "\u231C"; // &ulcorn - case 6971: return "\u231C"; // &ulcorner - case 6974: return "\u230F"; // &ulcrop - case 6977: return "\u25F8"; // &ultri - case 6981: return "\u016A"; // &Umacr - case 6985: return "\u016B"; // &umacr - case 6986: return "\u00A8"; // ¨ - case 6993: return "\u005F"; // &UnderBar - case 6997: return "\u23DF"; // &UnderBrace - case 7000: return "\u23B5"; // &UnderBracket - case 7011: return "\u23DD"; // &UnderParenthesis - case 7014: return "\u22C3"; // &Union - case 7018: return "\u228E"; // &UnionPlus - case 7022: return "\u0172"; // &Uogon - case 7026: return "\u0173"; // &uogon - case 7028: return "\uD835\uDD4C"; // &Uopf - case 7030: return "\uD835\uDD66"; // &uopf - case 7036: return "\u2191"; // &UpArrow - case 7041: return "\u21D1"; // &Uparrow - case 7047: return "\u2191"; // &uparrow - case 7050: return "\u2912"; // &UpArrowBar - case 7059: return "\u21C5"; // &UpArrowDownArrow - case 7068: return "\u2195"; // &UpDownArrow - case 7077: return "\u21D5"; // &Updownarrow - case 7086: return "\u2195"; // &updownarrow - case 7097: return "\u296E"; // &UpEquilibrium - case 7108: return "\u21BF"; // &upharpoonleft - case 7113: return "\u21BE"; // &upharpoonright - case 7116: return "\u228E"; // &uplus - case 7128: return "\u2196"; // &UpperLeftArrow - case 7138: return "\u2197"; // &UpperRightArrow - case 7140: return "\u03D2"; // &Upsi - case 7142: return "\u03C5"; // &upsi - case 7143: return "\u03D2"; // &upsih - case 7146: return "\u03A5"; // &Upsilon - case 7149: return "\u03C5"; // &upsilon - case 7152: return "\u22A5"; // &UpTee - case 7157: return "\u21A5"; // &UpTeeArrow - case 7165: return "\u21C8"; // &upuparrows - case 7170: return "\u231D"; // &urcorn - case 7172: return "\u231D"; // &urcorner - case 7175: return "\u230E"; // &urcrop - case 7179: return "\u016E"; // &Uring - case 7182: return "\u016F"; // &uring - case 7185: return "\u25F9"; // &urtri - case 7188: return "\uD835\uDCB0"; // &Uscr - case 7191: return "\uD835\uDCCA"; // &uscr - case 7195: return "\u22F0"; // &utdot - case 7200: return "\u0168"; // &Utilde - case 7204: return "\u0169"; // &utilde - case 7206: return "\u25B5"; // &utri - case 7207: return "\u25B4"; // &utrif - case 7211: return "\u21C8"; // &uuarr - case 7214: return "\u00DC"; // Ü - case 7216: return "\u00FC"; // ü - case 7222: return "\u29A7"; // &uwangle - case 7228: return "\u299C"; // &vangrt - case 7236: return "\u03F5"; // &varepsilon - case 7241: return "\u03F0"; // &varkappa - case 7248: return "\u2205"; // &varnothing - case 7251: return "\u03D5"; // &varphi - case 7252: return "\u03D6"; // &varpi - case 7257: return "\u221D"; // &varpropto - case 7260: return "\u21D5"; // &vArr - case 7261: return "\u2195"; // &varr - case 7263: return "\u03F1"; // &varrho - case 7268: return "\u03C2"; // &varsigma - case 7276: return "\u228A\uFE00"; // &varsubsetneq - case 7277: return "\u2ACB\uFE00"; // &varsubsetneqq - case 7284: return "\u228B\uFE00"; // &varsupsetneq - case 7285: return "\u2ACC\uFE00"; // &varsupsetneqq - case 7290: return "\u03D1"; // &vartheta - case 7301: return "\u22B2"; // &vartriangleleft - case 7306: return "\u22B3"; // &vartriangleright - case 7310: return "\u2AEB"; // &Vbar - case 7313: return "\u2AE8"; // &vBar - case 7314: return "\u2AE9"; // &vBarv - case 7316: return "\u0412"; // &Vcy - case 7318: return "\u0432"; // &vcy - case 7322: return "\u22AB"; // &VDash - case 7326: return "\u22A9"; // &Vdash - case 7330: return "\u22A8"; // &vDash - case 7334: return "\u22A2"; // &vdash - case 7335: return "\u2AE6"; // &Vdashl - case 7337: return "\u22C1"; // &Vee - case 7339: return "\u2228"; // &vee - case 7342: return "\u22BB"; // &veebar - case 7344: return "\u225A"; // &veeeq - case 7348: return "\u22EE"; // &vellip - case 7352: return "\u2016"; // &Verbar - case 7356: return "\u007C"; // &verbar - case 7357: return "\u2016"; // &Vert - case 7358: return "\u007C"; // &vert - case 7365: return "\u2223"; // &VerticalBar - case 7369: return "\u007C"; // &VerticalLine - case 7378: return "\u2758"; // &VerticalSeparator - case 7383: return "\u2240"; // &VerticalTilde - case 7393: return "\u200A"; // &VeryThinSpace - case 7395: return "\uD835\uDD19"; // &Vfr - case 7397: return "\uD835\uDD33"; // &vfr - case 7401: return "\u22B2"; // &vltri - case 7405: return "\u2282\u20D2"; // &vnsub - case 7406: return "\u2283\u20D2"; // &vnsup - case 7409: return "\uD835\uDD4D"; // &Vopf - case 7412: return "\uD835\uDD67"; // &vopf - case 7416: return "\u221D"; // &vprop - case 7420: return "\u22B3"; // &vrtri - case 7423: return "\uD835\uDCB1"; // &Vscr - case 7426: return "\uD835\uDCCB"; // &vscr - case 7430: return "\u2ACB\uFE00"; // &vsubnE - case 7431: return "\u228A\uFE00"; // &vsubne - case 7434: return "\u2ACC\uFE00"; // &vsupnE - case 7435: return "\u228B\uFE00"; // &vsupne - case 7440: return "\u22AA"; // &Vvdash - case 7446: return "\u299A"; // &vzigzag - case 7451: return "\u0174"; // &Wcirc - case 7456: return "\u0175"; // &wcirc - case 7461: return "\u2A5F"; // &wedbar - case 7465: return "\u22C0"; // &Wedge - case 7467: return "\u2227"; // &wedge - case 7468: return "\u2259"; // &wedgeq - case 7472: return "\u2118"; // &weierp - case 7474: return "\uD835\uDD1A"; // &Wfr - case 7476: return "\uD835\uDD34"; // &wfr - case 7479: return "\uD835\uDD4E"; // &Wopf - case 7482: return "\uD835\uDD68"; // &wopf - case 7483: return "\u2118"; // &wp - case 7484: return "\u2240"; // &wr - case 7488: return "\u2240"; // &wreath - case 7491: return "\uD835\uDCB2"; // &Wscr - case 7494: return "\uD835\uDCCC"; // &wscr - case 7498: return "\u22C2"; // &xcap - case 7501: return "\u25EF"; // &xcirc - case 7503: return "\u22C3"; // &xcup - case 7507: return "\u25BD"; // &xdtri - case 7510: return "\uD835\uDD1B"; // &Xfr - case 7512: return "\uD835\uDD35"; // &xfr - case 7516: return "\u27FA"; // &xhArr - case 7519: return "\u27F7"; // &xharr - case 7520: return "\u039E"; // &Xi - case 7521: return "\u03BE"; // &xi - case 7525: return "\u27F8"; // &xlArr - case 7528: return "\u27F5"; // &xlarr - case 7531: return "\u27FC"; // &xmap - case 7534: return "\u22FB"; // &xnis - case 7538: return "\u2A00"; // &xodot - case 7541: return "\uD835\uDD4F"; // &Xopf - case 7543: return "\uD835\uDD69"; // &xopf - case 7546: return "\u2A01"; // &xoplus - case 7550: return "\u2A02"; // &xotime - case 7554: return "\u27F9"; // &xrArr - case 7557: return "\u27F6"; // &xrarr - case 7560: return "\uD835\uDCB3"; // &Xscr - case 7563: return "\uD835\uDCCD"; // &xscr - case 7567: return "\u2A06"; // &xsqcup - case 7572: return "\u2A04"; // &xuplus - case 7575: return "\u25B3"; // &xutri - case 7578: return "\u22C1"; // &xvee - case 7583: return "\u22C0"; // &xwedge - case 7589: return "\u00DD"; // Ý - case 7595: return "\u00FD"; // ý - case 7598: return "\u042F"; // &YAcy - case 7599: return "\u044F"; // &yacy - case 7603: return "\u0176"; // &Ycirc - case 7607: return "\u0177"; // &ycirc - case 7608: return "\u042B"; // &Ycy - case 7609: return "\u044B"; // &ycy - case 7611: return "\u00A5"; // ¥ - case 7613: return "\uD835\uDD1C"; // &Yfr - case 7615: return "\uD835\uDD36"; // &yfr - case 7618: return "\u0407"; // &YIcy - case 7621: return "\u0457"; // &yicy - case 7624: return "\uD835\uDD50"; // &Yopf - case 7627: return "\uD835\uDD6A"; // &yopf - case 7630: return "\uD835\uDCB4"; // &Yscr - case 7633: return "\uD835\uDCCE"; // &yscr - case 7636: return "\u042E"; // &YUcy - case 7639: return "\u044E"; // &yucy - case 7642: return "\u0178"; // &Yuml - case 7644: return "\u00FF"; // ÿ - case 7650: return "\u0179"; // &Zacute - case 7656: return "\u017A"; // &zacute - case 7661: return "\u017D"; // &Zcaron - case 7666: return "\u017E"; // &zcaron - case 7667: return "\u0417"; // &Zcy - case 7668: return "\u0437"; // &zcy - case 7671: return "\u017B"; // &Zdot - case 7674: return "\u017C"; // &zdot - case 7679: return "\u2128"; // &zeetrf - case 7692: return "\u200B"; // &ZeroWidthSpace - case 7694: return "\u0396"; // &Zeta - case 7696: return "\u03B6"; // &zeta - case 7698: return "\u2128"; // &Zfr - case 7700: return "\uD835\uDD37"; // &zfr - case 7703: return "\u0416"; // &ZHcy - case 7706: return "\u0436"; // &zhcy - case 7712: return "\u21DD"; // &zigrarr - case 7715: return "\u2124"; // &Zopf - case 7718: return "\uD835\uDD6B"; // &zopf - case 7721: return "\uD835\uDCB5"; // &Zscr - case 7724: return "\uD835\uDCCF"; // &zscr - case 7726: return "\u200D"; // &zwj - case 7728: return "\u200C"; // &zwnj - default: return null; - } - } - string GetNamedEntityValue () { int startIndex = index; string decoded = null; while (startIndex > 0) { - if ((decoded = GetNamedEntityValue (states[startIndex - 1])) != null) + if (NamedEntities.TryGetValue (states[startIndex - 1], out decoded)) break; startIndex--; diff --git a/MimeKit/Text/HtmlNamespace.cs b/MimeKit/Text/HtmlNamespace.cs index 70966e8407..ee4d935c72 100644 --- a/MimeKit/Text/HtmlNamespace.cs +++ b/MimeKit/Text/HtmlNamespace.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Text/HtmlTagCallback.cs b/MimeKit/Text/HtmlTagCallback.cs index ce4e27cb4a..61deac18f1 100644 --- a/MimeKit/Text/HtmlTagCallback.cs +++ b/MimeKit/Text/HtmlTagCallback.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Text/HtmlTagContext.cs b/MimeKit/Text/HtmlTagContext.cs index 052e7ce103..ac36b76639 100644 --- a/MimeKit/Text/HtmlTagContext.cs +++ b/MimeKit/Text/HtmlTagContext.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -39,7 +39,7 @@ namespace MimeKit.Text { public abstract class HtmlTagContext { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new - ///. diff --git a/MimeKit/Text/HtmlTagId.cs b/MimeKit/Text/HtmlTagId.cs index ea480e6fa7..a6f57ce06a 100644 --- a/MimeKit/Text/HtmlTagId.cs +++ b/MimeKit/Text/HtmlTagId.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -654,9 +654,9 @@ public enum HtmlTagId { TH, /// - /// The HTML <thread> tag. + /// The HTML <thead> tag. /// - Thread, + THead, ////// The HTML <time> tag. @@ -766,7 +766,7 @@ public static string ToHtmlTagName (this HtmlTagId value) var name = value.ToString (); -#if PORTABLE || NETSTANDARD +#if NETSTANDARD1_3 || NETSTANDARD1_6 var field = typeof (HtmlTagId).GetTypeInfo ().GetDeclaredField (name); var attrs = field.GetCustomAttributes (typeof (HtmlTagNameAttribute), false).ToArray (); #else diff --git a/MimeKit/Text/HtmlTextPreviewer.cs b/MimeKit/Text/HtmlTextPreviewer.cs new file mode 100644 index 0000000000..b3e2e11943 --- /dev/null +++ b/MimeKit/Text/HtmlTextPreviewer.cs @@ -0,0 +1,253 @@ +// +// HtmlTextPreviewer.cs +// +// Author: Jeffrey Stedfast ///+// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.IO; +using System.Linq; +using System.Collections.Generic; + +namespace MimeKit.Text { + /// + /// A text previewer for HTML content. + /// + ///+ /// A text previewer for HTML content. + /// + public class HtmlTextPreviewer : TextPreviewer + { + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new previewer for HTML. + /// + public HtmlTextPreviewer () + { + } + + ///+ /// Get the input format. + /// + ///+ /// Gets the input format. + /// + ///The input format. + public override TextFormat InputFormat { + get { return TextFormat.Html; } + } + + static bool IsWhiteSpace (char c) + { + return char.IsWhiteSpace (c) || (c >= 0x200B && c <= 0x200D); + } + + static bool Append (char[] preview, ref int previewLength, string value, ref bool lwsp) + { + int i; + + for (i = 0; i < value.Length && previewLength < preview.Length; i++) { + if (IsWhiteSpace (value[i])) { + if (!lwsp) { + preview[previewLength++] = ' '; + lwsp = true; + } + } else { + preview[previewLength++] = value[i]; + lwsp = false; + } + } + + if (i < value.Length) { + if (lwsp) + previewLength--; + + preview[previewLength - 1] = '\u2026'; + lwsp = false; + return true; + } + + return false; + } + + sealed class HtmlTagContext + { + public HtmlTagContext (HtmlTagId id) + { + TagId = id; + } + + public HtmlTagId TagId { + get; + } + + public int ListIndex { + get; set; + } + + public bool SuppressInnerContent { + get; set; + } + } + + static void Pop (IListstack, HtmlTagId id) + { + for (int i = stack.Count; i > 0; i--) { + if (stack[i - 1].TagId == id) { + stack.RemoveAt (i - 1); + break; + } + } + } + + static bool ShouldSuppressInnerContent (HtmlTagId id) + { + switch (id) { + case HtmlTagId.OL: + case HtmlTagId.Script: + case HtmlTagId.Style: + case HtmlTagId.Table: + case HtmlTagId.TBody: + case HtmlTagId.THead: + case HtmlTagId.TR: + case HtmlTagId.UL: + return true; + default: + return false; + } + } + + static bool SuppressContent (IList stack) + { + int lastIndex = stack.Count - 1; + + return lastIndex >= 0 && stack[lastIndex].SuppressInnerContent; + } + + HtmlTagContext GetListItemContext (IList stack) + { + for (int i = stack.Count; i > 0; i--) { + var ctx = stack[i - 1]; + + if (ctx.TagId == HtmlTagId.OL || ctx.TagId == HtmlTagId.UL) + return ctx; + } + + return null; + } + + /// + /// Get a text preview of a stream of text. + /// + ///+ /// Gets a text preview of a stream of text. + /// + /// The original text stream. + ///A string representing a shortened preview of the original text. + ///+ /// + public override string GetPreviewText (TextReader reader) + { + if (reader == null) + throw new ArgumentNullException (nameof (reader)); + + var tokenizer = new HtmlTokenizer (reader) { IgnoreTruncatedTags = true }; + var preview = new char[MaximumPreviewLength]; + var stack = new Listis null . + ///(); + var prefix = string.Empty; + int previewLength = 0; + HtmlTagContext ctx; + HtmlAttribute attr; + bool body = false; + bool full = false; + bool lwsp = true; + HtmlToken token; + + while (!full && tokenizer.ReadNextToken (out token)) { + switch (token.Kind) { + case HtmlTokenKind.Tag: + var tag = (HtmlTagToken) token; + + if (!tag.IsEndTag) { + if (body) { + switch (tag.Id) { + case HtmlTagId.Image: + if ((attr = tag.Attributes.FirstOrDefault (x => x.Id == HtmlAttributeId.Alt)) != null) { + full = Append (preview, ref previewLength, prefix + attr.Value, ref lwsp); + prefix = string.Empty; + } + break; + case HtmlTagId.LI: + if ((ctx = GetListItemContext (stack)) != null) { + if (ctx.TagId == HtmlTagId.OL) { + full = Append (preview, ref previewLength, $" {++ctx.ListIndex}. ", ref lwsp); + prefix = string.Empty; + } else { + //full = Append (preview, ref previewLength, " \u2022 ", ref lwsp); + prefix = " "; + } + } + break; + case HtmlTagId.Br: + case HtmlTagId.P: + prefix = " "; + break; + } + + if (!tag.IsEmptyElement) { + ctx = new HtmlTagContext (tag.Id) { + SuppressInnerContent = ShouldSuppressInnerContent (tag.Id) + }; + stack.Add (ctx); + } + } else if (tag.Id == HtmlTagId.Body && !tag.IsEmptyElement) { + body = true; + } + } else if (tag.Id == HtmlTagId.Body) { + stack.Clear (); + body = false; + } else { + Pop (stack, tag.Id); + } + break; + case HtmlTokenKind.Data: + if (body && !SuppressContent (stack)) { + var data = (HtmlDataToken) token; + + full = Append (preview, ref previewLength, prefix + data.Data, ref lwsp); + prefix = string.Empty; + } + break; + } + } + + if (lwsp && previewLength > 0) + previewLength--; + + return new string (preview, 0, previewLength); + } + } +} diff --git a/MimeKit/Text/HtmlToHtml.cs b/MimeKit/Text/HtmlToHtml.cs index 4a94f9deea..c9b4abf9d6 100644 --- a/MimeKit/Text/HtmlToHtml.cs +++ b/MimeKit/Text/HtmlToHtml.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -54,7 +54,7 @@ public class HtmlToHtml : TextConverter //} /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new HTML to HTML converter. @@ -107,18 +107,6 @@ public bool FilterHtml { get; set; } - /// - /// Get or set the text that will be appended to the end of the output. - /// - ///- /// - ///Gets or sets the text that will be appended to the end of the output. - ///The footer must be set before conversion begins. - ///The footer. - public string Footer { - get; set; - } - ////// Get or set the footer format. /// @@ -130,18 +118,6 @@ public HeaderFooterFormat FooterFormat { get; set; } - ///- /// Get or set text that will be prepended to the beginning of the output. - /// - ///- /// - ///Gets or sets the text that will be prepended to the beginning of the output. - ///The header must be set before conversion begins. - ///The header. - public string Header { - get; set; - } - ////// Get or set the header format. /// @@ -276,7 +252,7 @@ public override void Convert (TextReader reader, TextWriter writer) if (!string.IsNullOrEmpty (Header)) { if (HeaderFormat == HeaderFooterFormat.Text) { - var converter = new TextToHtml (); + var converter = new TextToHtml { OutputHtmlFragment = true }; using (var sr = new StringReader (Header)) converter.Convert (sr, writer); @@ -368,7 +344,7 @@ public override void Convert (TextReader reader, TextWriter writer) if (!string.IsNullOrEmpty (Footer)) { if (FooterFormat == HeaderFooterFormat.Text) { - var converter = new TextToHtml (); + var converter = new TextToHtml { OutputHtmlFragment = true }; using (var sr = new StringReader (Footer)) converter.Convert (sr, writer); diff --git a/MimeKit/Text/HtmlToken.cs b/MimeKit/Text/HtmlToken.cs index 7d6edda7f6..fd85943a79 100644 --- a/MimeKit/Text/HtmlToken.cs +++ b/MimeKit/Text/HtmlToken.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -38,7 +38,7 @@ namespace MimeKit.Text { public abstract class HtmlToken { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -73,12 +73,12 @@ public HtmlTokenKind Kind { public abstract void WriteTo (TextWriter output); /// - /// Returns a ///that represents the current . + /// Returns a that represents the current . /// - /// Returns a - ///that represents the current . + /// Returns a that represents the current . /// A + ///that represents the current . A public override string ToString () { using (var output = new StringWriter ()) { @@ -98,7 +98,7 @@ public override string ToString () public class HtmlCommentToken : HtmlToken { ///that represents the current . - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -181,7 +181,7 @@ public override void WriteTo (TextWriter output) public class HtmlDataToken : HtmlToken { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -211,7 +211,7 @@ protected HtmlDataToken (HtmlTokenKind kind, string data) : base (kind) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -277,7 +277,7 @@ public override void WriteTo (TextWriter output) public class HtmlCDataToken : HtmlDataToken { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -321,7 +321,7 @@ public override void WriteTo (TextWriter output) public class HtmlScriptDataToken : HtmlDataToken { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -363,7 +363,7 @@ public override void WriteTo (TextWriter output) public class HtmlTagToken : HtmlToken { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -391,7 +391,7 @@ public HtmlTagToken (string name, IEnumerable attributes, bool is } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -512,7 +512,7 @@ public class HtmlDocTypeToken : HtmlToken string systemIdentifier; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new public class HtmlTokenizer { + // Specification: https://dev.w3.org/html5/spec-LC/tokenization.html const string DocType = "doctype"; const string CData = "[CDATA["; readonly HtmlEntityDecoder entity = new HtmlEntityDecoder (); - readonly StringBuilder data = new StringBuilder (); - readonly StringBuilder name = new StringBuilder (); + readonly CharBuffer data = new CharBuffer (2048); + readonly CharBuffer name = new CharBuffer (32); readonly char[] cdata = new char[3]; + readonly TextReader text; HtmlDocTypeToken doctype; HtmlAttribute attribute; string activeTagName; @@ -52,10 +54,8 @@ public class HtmlTokenizer bool bang; char quote; - TextReader text; - ///. diff --git a/MimeKit/Text/HtmlTokenKind.cs b/MimeKit/Text/HtmlTokenKind.cs index 83e0a32b2e..0ff55fa49b 100644 --- a/MimeKit/Text/HtmlTokenKind.cs +++ b/MimeKit/Text/HtmlTokenKind.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Text/HtmlTokenizer.cs b/MimeKit/Text/HtmlTokenizer.cs index 48f8374642..b414052c58 100644 --- a/MimeKit/Text/HtmlTokenizer.cs +++ b/MimeKit/Text/HtmlTokenizer.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,7 +25,7 @@ // using System.IO; -using System.Text; +using System.Runtime.CompilerServices; namespace MimeKit.Text { /// @@ -36,13 +36,15 @@ namespace MimeKit.Text { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -74,8 +74,8 @@ public HtmlTokenizer (TextReader reader) /// /// ///Gets or sets whether or not the tokenizer should decode character references. - ///+ /// Character references in attribute values will still be decoded - /// even if this value is set to false .Character references in attribute values will still be decoded + /// even if this value is set to ///false .public bool DecodeCharacterReferences { @@ -93,6 +93,19 @@ public HtmlNamespace HtmlNamespace { get; private set; } + /// true if character references should be decoded; otherwise,false .+ /// Get or set whether or not the tokenizer should ignore truncated tags. + /// + ///+ /// + ///Gets or sets whether or not the tokenizer should ignore truncated tags. + ///If + ///false and the stream abrubtly ends in the middle of an HTML tag, it will be + /// treated as aninstead. + public bool IgnoreTruncatedTags { + get; set; + } + /// true if truncated tags should be ignored; otherwise,false ./// Gets the current line number. /// @@ -116,7 +129,7 @@ public int LineNumber { ///Combined with ///, a value of 1,1 indicates /// the start of the document.The current line number. + ///The column position of the current line. public int LinePosition { get; private set; } @@ -231,19 +244,26 @@ protected virtual HtmlAttribute CreateAttribute (string name) return new HtmlAttribute (name); } - static bool IsAlphaNumeric (char c) + [MethodImpl (MethodImplOptions.AggressiveInlining)] + static bool IsAlphaNumeric (int c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'); + return ((uint) (c - 'A') <= 'Z' - 'A') || ((uint) (c - 'a') <= 'z' - 'a') || ((uint) (c - '0') <= '9' - '0'); } - static bool IsAsciiLetter (char c) + [MethodImpl (MethodImplOptions.AggressiveInlining)] + static bool IsAsciiLetter (int c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + return ((uint) (c - 'A') <= 'Z' - 'A') || ((uint) (c - 'a') <= 'z' - 'a'); } - static char ToLower (char c) + [MethodImpl (MethodImplOptions.AggressiveInlining)] + static char ToLower (int c) { - return (c >= 'A' && c <= 'Z') ? (char) (c + 0x20) : c; + // check if the char is within the uppercase range + if ((uint) (c - 'A') <= 'Z' - 'A') + return (char) (c + 0x20); + + return (char) c; } int Peek () @@ -299,7 +319,7 @@ HtmlToken EmitCommentToken (string comment, bool bogus = false) return token; } - HtmlToken EmitCommentToken (StringBuilder comment, bool bogus = false) + HtmlToken EmitCommentToken (CharBuffer comment, bool bogus = false) { return EmitCommentToken (comment.ToString (), bogus); } @@ -312,11 +332,16 @@ HtmlToken EmitDocType () return token; } - HtmlToken EmitDataToken (bool encodeEntities) + HtmlToken EmitDataToken (bool encodeEntities, bool truncated) { if (data.Length == 0) return null; + if (truncated && IgnoreTruncatedTags) { + data.Length = 0; + return null; + } + var token = CreateDataToken (data.ToString ()); token.EncodeEntities = encodeEntities; data.Length = 0; @@ -406,7 +431,7 @@ HtmlToken ReadCharacterReference (HtmlTokenizerState next) TokenizerState = HtmlTokenizerState.EndOfFile; data.Append ('&'); - return EmitDataToken (true); + return EmitDataToken (true, false); } c = (char) nc; @@ -424,12 +449,15 @@ HtmlToken ReadCharacterReference (HtmlTokenizerState next) while (entity.Push (c)) { Read (); + if (c == ';') + break; + if ((nc = Peek ()) == -1) { TokenizerState = HtmlTokenizerState.EndOfFile; data.Append (entity.GetPushedInput ()); entity.Reset (); - return EmitDataToken (true); + return EmitDataToken (true, false); } c = (char) nc; @@ -440,11 +468,6 @@ HtmlToken ReadCharacterReference (HtmlTokenizerState next) data.Append (entity.GetValue ()); entity.Reset (); - if (c == ';') { - // consume the ';' - Read (); - } - return null; } @@ -476,7 +499,7 @@ HtmlToken ReadGenericRawTextEndTagOpen (bool decoded, HtmlTokenizerState rawText if (nc == -1) { TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (decoded); + return EmitDataToken (decoded, true); } c = (char) nc; @@ -505,7 +528,7 @@ HtmlToken ReadGenericRawTextEndTagName (bool decoded, HtmlTokenizerState rawText TokenizerState = HtmlTokenizerState.EndOfFile; name.Length = 0; - return EmitDataToken (decoded); + return EmitDataToken (decoded, true); } c = (char) nc; @@ -585,7 +608,7 @@ HtmlToken ReadData () } } while (TokenizerState == HtmlTokenizerState.Data); - return EmitDataToken (DecodeCharacterReferences); + return EmitDataToken (DecodeCharacterReferences, false); } // 8.2.4.2 Character reference in data state @@ -618,14 +641,14 @@ HtmlToken ReadRcData () goto default; case '<': TokenizerState = HtmlTokenizerState.RcDataLessThan; - return EmitDataToken (DecodeCharacterReferences); + return EmitDataToken (DecodeCharacterReferences, false); default: data.Append (c == '\0' ? '\uFFFD' : c); break; } } while (TokenizerState == HtmlTokenizerState.RcData); - return EmitDataToken (DecodeCharacterReferences); + return EmitDataToken (DecodeCharacterReferences, false); } // 8.2.4.4 Character reference in RCDATA state @@ -651,14 +674,14 @@ HtmlToken ReadRawText () switch (c) { case '<': TokenizerState = HtmlTokenizerState.RawTextLessThan; - return EmitDataToken (false); + return EmitDataToken (false, false); default: data.Append (c == '\0' ? '\uFFFD' : c); break; } } while (TokenizerState == HtmlTokenizerState.RawText); - return EmitDataToken (false); + return EmitDataToken (false, false); } // 8.2.4.6 Script data state @@ -702,7 +725,7 @@ HtmlToken ReadPlainText () TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false); + return EmitDataToken (false, false); } // 8.2.4.8 Tag open state @@ -712,8 +735,8 @@ HtmlToken ReadTagOpen () char c; if (nc == -1) { + var token = IgnoreTruncatedTags ? null : CreateDataToken ("<"); TokenizerState = HtmlTokenizerState.EndOfFile; - var token = CreateDataToken ("<"); return token; } @@ -757,7 +780,7 @@ HtmlToken ReadEndTagOpen () if (nc == -1) { TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -797,7 +820,7 @@ HtmlToken ReadTagName () TokenizerState = HtmlTokenizerState.EndOfFile; name.Length = 0; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1416,7 +1439,7 @@ HtmlToken ReadBeforeAttributeName () TokenizerState = HtmlTokenizerState.EndOfFile; tag = null; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1455,7 +1478,7 @@ HtmlToken ReadAttributeName () name.Length = 0; tag = null; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1499,7 +1522,7 @@ HtmlToken ReadAfterAttributeName () TokenizerState = HtmlTokenizerState.EndOfFile; tag = null; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1540,7 +1563,7 @@ HtmlToken ReadBeforeAttributeValue () TokenizerState = HtmlTokenizerState.EndOfFile; tag = null; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1585,7 +1608,7 @@ HtmlToken ReadAttributeValueQuoted () TokenizerState = HtmlTokenizerState.EndOfFile; name.Length = 0; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1626,7 +1649,7 @@ HtmlToken ReadAttributeValueUnquoted () TokenizerState = HtmlTokenizerState.EndOfFile; name.Length = 0; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1666,14 +1689,13 @@ HtmlToken ReadCharacterReferenceInAttributeValue () { char additionalAllowedCharacter = quote == '\0' ? '>' : quote; int nc = Peek (); - bool consume; char c; if (nc == -1) { TokenizerState = HtmlTokenizerState.EndOfFile; name.Length = 0; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1682,13 +1704,11 @@ HtmlToken ReadCharacterReferenceInAttributeValue () case '\t': case '\r': case '\n': case '\f': case ' ': case '<': case '&': // no character is consumed, emit '&' name.Append ('&'); - consume = false; break; default: if (c == additionalAllowedCharacter) { // this is not a character reference, nothing is consumed name.Append ('&'); - consume = false; break; } @@ -1697,13 +1717,16 @@ HtmlToken ReadCharacterReferenceInAttributeValue () while (entity.Push (c)) { Read (); + if (c == ';') + break; + if ((nc = Peek ()) == -1) { TokenizerState = HtmlTokenizerState.EndOfFile; data.Length--; data.Append (entity.GetPushedInput ()); entity.Reset (); - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1720,7 +1743,6 @@ HtmlToken ReadCharacterReferenceInAttributeValue () data.Length--; data.Append (pushed); name.Append (value); - consume = c == ';'; entity.Reset (); break; } @@ -1730,9 +1752,6 @@ HtmlToken ReadCharacterReferenceInAttributeValue () else TokenizerState = HtmlTokenizerState.AttributeValueQuoted; - if (consume) - Read (); - return null; } @@ -1746,7 +1765,7 @@ HtmlToken ReadAfterAttributeValueQuoted () if (nc == -1) { TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1786,7 +1805,7 @@ HtmlToken ReadSelfClosingStartTag () if (nc == -1) { TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1838,7 +1857,7 @@ HtmlToken ReadMarkupDeclarationOpen () while (count < 2) { if ((nc = Peek ()) == -1) { TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false); + return EmitDataToken (false, true); } if ((c = (char) nc) != '-') @@ -1869,7 +1888,7 @@ HtmlToken ReadMarkupDeclarationOpen () while (count < 7) { if ((nc = Read ()) == -1) { TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; @@ -1901,7 +1920,7 @@ HtmlToken ReadMarkupDeclarationOpen () while (count < 7) { if ((nc = Read ()) == -1) { TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false); + return EmitDataToken (false, true); } c = (char) nc; diff --git a/MimeKit/Text/HtmlTokenizerState.cs b/MimeKit/Text/HtmlTokenizerState.cs index adde2a09f2..9737df10be 100644 --- a/MimeKit/Text/HtmlTokenizerState.cs +++ b/MimeKit/Text/HtmlTokenizerState.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Text/HtmlUtils.cs b/MimeKit/Text/HtmlUtils.cs index 7ccb4a490b..17dce53993 100644 --- a/MimeKit/Text/HtmlUtils.cs +++ b/MimeKit/Text/HtmlUtils.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -110,7 +110,7 @@ static void HtmlAttributeEncode (TextWriter output, ICharArray value, int startI } if (c > 255 && char.IsSurrogate (c)) { - if (index + 1 < endIndex && char.IsSurrogatePair (c, value[index])) { + if (index < endIndex && char.IsSurrogatePair (c, value[index])) { unichar = char.ConvertToUtf32 (c, value[index]); index++; } else { @@ -171,7 +171,7 @@ public static void HtmlAttributeEncode (TextWriter output, char[] value, int sta throw new ArgumentOutOfRangeException (nameof (count)); if (quote != '"' && quote != '\'') - throw new ArgumentException ("quote"); + throw new ArgumentException ("The quote character must either be '\"' or '\''.", nameof (quote)); HtmlAttributeEncode (output, new CharArray (value), startIndex, count, quote); } @@ -209,7 +209,7 @@ public static string HtmlAttributeEncode (char[] value, int startIndex, int coun throw new ArgumentOutOfRangeException (nameof (count)); if (quote != '"' && quote != '\'') - throw new ArgumentException ("quote"); + throw new ArgumentException ("The quote character must either be '\"' or '\''.", nameof (quote)); var encoded = new StringBuilder (); @@ -257,7 +257,7 @@ public static void HtmlAttributeEncode (TextWriter output, string value, int sta throw new ArgumentOutOfRangeException (nameof (count)); if (quote != '"' && quote != '\'') - throw new ArgumentException ("quote"); + throw new ArgumentException ("The quote character must either be '\"' or '\''.", nameof (quote)); HtmlAttributeEncode (output, new CharString (value), startIndex, count, quote); } @@ -288,7 +288,7 @@ public static void HtmlAttributeEncode (TextWriter output, string value, char qu throw new ArgumentNullException (nameof (value)); if (quote != '"' && quote != '\'') - throw new ArgumentException ("quote"); + throw new ArgumentException ("The quote character must either be '\"' or '\''.", nameof (quote)); HtmlAttributeEncode (output, new CharString (value), 0, value.Length, quote); } @@ -326,7 +326,7 @@ public static string HtmlAttributeEncode (string value, int startIndex, int coun throw new ArgumentOutOfRangeException (nameof (count)); if (quote != '"' && quote != '\'') - throw new ArgumentException ("quote"); + throw new ArgumentException ("The quote character must either be '\"' or '\''.", nameof (quote)); var encoded = new StringBuilder (); @@ -357,7 +357,7 @@ public static string HtmlAttributeEncode (string value, char quote = '"') throw new ArgumentNullException (nameof (value)); if (quote != '"' && quote != '\'') - throw new ArgumentException ("quote"); + throw new ArgumentException ("The quote character must either be '\"' or '\''.", nameof (quote)); var encoded = new StringBuilder (); @@ -414,7 +414,7 @@ static void HtmlEncode (TextWriter output, ICharArray data, int startIndex, int } if (c > 255 && char.IsSurrogate (c)) { - if (index + 1 < endIndex && char.IsSurrogatePair (c, data[index])) { + if (index < endIndex && char.IsSurrogatePair (c, data[index])) { unichar = char.ConvertToUtf32 (c, data[index]); index++; } else { @@ -667,9 +667,6 @@ public static void HtmlDecode (TextWriter output, string data, int startIndex, i output.Write (entity.GetValue ()); entity.Reset (); - - if (index < endIndex && data[index] == ';') - index++; } else { output.Write (data[index++]); } diff --git a/MimeKit/Text/HtmlWriter.cs b/MimeKit/Text/HtmlWriter.cs index c173954dc1..ee7cee8e9e 100644 --- a/MimeKit/Text/HtmlWriter.cs +++ b/MimeKit/Text/HtmlWriter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,12 +28,6 @@ using System.IO; using System.Text; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#else -using Encoding = System.Text.Encoding; -#endif - namespace MimeKit.Text { /// /// An HTML writer. @@ -50,7 +44,7 @@ public class HtmlWriter : IDisposable bool empty; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -74,7 +68,7 @@ public HtmlWriter (Stream stream, Encoding encoding) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new public class TextToFlowed : TextConverter { - const int OptimalLineLength = 66; const int MaxLineLength = 78; ///. @@ -93,11 +87,11 @@ public HtmlWriter (TextWriter output) /// /// Releas unmanaged resources and perform other cleanup operations before the - /// ///is reclaimed by garbage collection. + /// is reclaimed by garbage collection. /// /// Releases unmanaged resources and performs other cleanup operations before the - /// ~HtmlWriter () { @@ -918,12 +912,12 @@ protected virtual void Dispose (bool disposing) } ///is reclaimed by garbage collection. + /// is reclaimed by garbage collection. /// - /// Releases all resource used by the - ///object. + /// Releases all resource used by the object. /// Call + ///when you are finished using the . The - /// method leaves the in an unusable state. After calling - /// , you must release all references to the so the garbage - /// collector can reclaim the memory that the was occupying. Call public void Dispose () { Dispose (true); diff --git a/MimeKit/Text/HtmlWriterState.cs b/MimeKit/Text/HtmlWriterState.cs index f7dcf57239..2836a8d91a 100644 --- a/MimeKit/Text/HtmlWriterState.cs +++ b/MimeKit/Text/HtmlWriterState.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastwhen you are finished using the . The + /// method leaves the in an unusable state. After calling + /// , you must release all references to the so the garbage + /// collector can reclaim the memory that the was occupying. // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Text/ICharArray.cs b/MimeKit/Text/ICharArray.cs index 7c5d62556a..cefb92b9c0 100644 --- a/MimeKit/Text/ICharArray.cs +++ b/MimeKit/Text/ICharArray.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Text/PlainTextPreviewer.cs b/MimeKit/Text/PlainTextPreviewer.cs new file mode 100644 index 0000000000..9adffd091f --- /dev/null +++ b/MimeKit/Text/PlainTextPreviewer.cs @@ -0,0 +1,158 @@ +// +// PlainTextPreviewer.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.IO; + +namespace MimeKit.Text { + /// + /// A text previewer for plain text. + /// + ///+ /// A text previewer for plain text. + /// + public class PlainTextPreviewer : TextPreviewer + { + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new previewer for plain text. + /// + public PlainTextPreviewer () + { + } + + ///+ /// Get the input format. + /// + ///+ /// Gets the input format. + /// + ///The input format. + public override TextFormat InputFormat { + get { return TextFormat.Plain; } + } + + static bool IsWhiteSpace (char c) + { + return char.IsWhiteSpace (c) || (c >= 0x200B && c <= 0x200D); + } + + ///+ /// Get a text preview of a string of text. + /// + ///+ /// Gets a text preview of a string of text. + /// + /// The original text. + ///A string representing a shortened preview of the original text. + ///+ /// + public override string GetPreviewText (string text) + { + if (text == null) + throw new ArgumentNullException (nameof (text)); + + if (text.Length == 0) + return string.Empty; + + var preview = new char[Math.Min (MaximumPreviewLength, text.Length)]; + int previewLength = 0; + var lwsp = true; + int i; + + for (i = 0; i < text.Length && previewLength < preview.Length; i++) { + if (IsWhiteSpace (text[i])) { + if (!lwsp) { + preview[previewLength++] = ' '; + lwsp = true; + } + } else { + preview[previewLength++] = text[i]; + lwsp = false; + } + } + + if (lwsp && previewLength > 0) + previewLength--; + + if (i < text.Length) + preview[previewLength - 1] = '\u2026'; + + return new string (preview, 0, previewLength); + } + + ///is null . + ///+ /// Get a text preview of a stream of text. + /// + ///+ /// Gets a text preview of a stream of text. + /// + /// The original text stream. + ///A string representing a shortened preview of the original text. + ///+ /// + public override string GetPreviewText (TextReader reader) + { + if (reader == null) + throw new ArgumentNullException (nameof (reader)); + + var preview = new char[MaximumPreviewLength]; + var buffer = new char[4096]; + int previewLength = 0; + var lwsp = true; + int nread, i; + + while ((nread = reader.ReadBlock (buffer, 0, buffer.Length)) > 0) { + for (i = 0; i < nread && previewLength < preview.Length; i++) { + if (char.IsWhiteSpace (buffer[i])) { + if (!lwsp) { + preview[previewLength++] = ' '; + lwsp = true; + } + } else { + preview[previewLength++] = buffer[i]; + lwsp = false; + } + } + + if (i < nread) { + preview[previewLength - 1] = '\u2026'; + lwsp = false; + break; + } + } + + if (lwsp && previewLength > 0) + previewLength--; + + return new string (preview, 0, previewLength); + } + } +} diff --git a/MimeKit/Text/TextConverter.cs b/MimeKit/Text/TextConverter.cs index 78e5633e93..2003c39185 100644 --- a/MimeKit/Text/TextConverter.cs +++ b/MimeKit/Text/TextConverter.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastis null . + ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -29,16 +29,6 @@ using System.Text; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -using Encoder = Portable.Text.Encoder; -using Decoder = Portable.Text.Decoder; -#else -using Encoding = System.Text.Encoding; -using Encoder = System.Text.Encoder; -using Decoder = System.Text.Decoder; -#endif - namespace MimeKit.Text { /// /// An abstract class for converting text from one format to another. @@ -80,10 +70,10 @@ static TextConverter () } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// Initializes a new instance of the protected TextConverter () { @@ -204,14 +194,38 @@ public int OutputStreamBufferSize { } } + ///class. + /// Initialize a new instance of the class. /// + /// Get or set the text that will be appended to the end of the output. + /// + ///+ /// + ///Gets or sets the text that will be appended to the end of the output. + ///The footer must be set before conversion begins. + ///The footer. + public string Footer { + get; set; + } + + ///+ /// Get or set text that will be prepended to the beginning of the output. + /// + ///+ /// + ///Gets or sets the text that will be prepended to the beginning of the output. + ///The header must be set before conversion begins. + ///The header. + public string Header { + get; set; + } + TextReader CreateReader (Stream stream) { - return new StreamReader (stream, InputEncoding, DetectEncodingFromByteOrderMark, InputStreamBufferSize); + return new StreamReader (stream, InputEncoding, DetectEncodingFromByteOrderMark, InputStreamBufferSize, true); } TextWriter CreateWriter (Stream stream) { - return new StreamWriter (stream, OutputEncoding, OutputStreamBufferSize); + return new StreamWriter (stream, OutputEncoding, OutputStreamBufferSize, true); } ///@@ -237,7 +251,11 @@ public virtual void Convert (Stream source, Stream destination) if (destination == null) throw new ArgumentNullException (nameof (destination)); - Convert (CreateReader (source), CreateWriter (destination)); + using (var writer = CreateWriter (destination)) { + using (var reader = CreateReader (source)) + Convert (reader, writer); + writer.Flush (); + } } /// @@ -263,7 +281,8 @@ public virtual void Convert (Stream source, TextWriter writer) if (writer == null) throw new ArgumentNullException (nameof (writer)); - Convert (CreateReader (source), writer); + using (var reader = CreateReader (source)) + Convert (reader, writer); } /// @@ -289,7 +308,10 @@ public virtual void Convert (TextReader reader, Stream destination) if (destination == null) throw new ArgumentNullException (nameof (destination)); - Convert (reader, CreateWriter (destination)); + using (var writer = CreateWriter (destination)) { + Convert (reader, writer); + writer.Flush (); + } } /// diff --git a/MimeKit/Text/TextFormat.cs b/MimeKit/Text/TextFormat.cs index 3c394c0386..673f2a2ffb 100644 --- a/MimeKit/Text/TextFormat.cs +++ b/MimeKit/Text/TextFormat.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Text/TextPreviewer.cs b/MimeKit/Text/TextPreviewer.cs new file mode 100644 index 0000000000..b0b195cb3c --- /dev/null +++ b/MimeKit/Text/TextPreviewer.cs @@ -0,0 +1,239 @@ +// +// PreviewGenerator.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.IO; +using System.Text; + +using MimeKit.Utils; + +namespace MimeKit.Text { + /// + /// An abstract class for generating a text preview of a message. + /// + ///+ /// An abstract class for generating a text preview of a message. + /// + public abstract class TextPreviewer + { + int maximumPreviewLength; + + ///+ /// Initialize a new instance of the + ///class. + /// + /// Initialize a new instance of the + protected TextPreviewer () + { + maximumPreviewLength = 230; + } + + ///class. + /// + /// Get the input format. + /// + ///+ /// Gets the input format. + /// + ///The input format. + public abstract TextFormat InputFormat { + get; + } + + ///+ /// Get or set the maximum text preview length. + /// + ///+ /// + ///Gets or sets the maximum text preview length. + ///The default value is + ///230 which is what the GMail web API seems to use.The maximum text preview length. + ///+ /// + public int MaximumPreviewLength { + get { return maximumPreviewLength; } + set { + if (value < 1 || value > 1024) + throw new ArgumentOutOfRangeException (nameof (value)); + + maximumPreviewLength = value; + } + } + + static TextPreviewer Create (TextFormat format) + { + switch (format) { + case TextFormat.Html: return new HtmlTextPreviewer (); + default: return new PlainTextPreviewer (); + } + } + + ///is less than + ///1 or greater than1024 .+ /// Get a text preview of the text part. + /// + ///+ /// Gets a text preview of the text part. + /// + /// The text part. + ///A string representing a shortened preview of the original text. + ///+ /// + public static string GetPreviewText (TextPart body) + { + if (body == null) + throw new ArgumentNullException (nameof (body)); + + if (body.Content == null) + return string.Empty; + + var encoding = body.ContentType.CharsetEncoding; + var buffer = new byte[16 * 1024]; + int nread = 0; + + using (var content = body.Content.Open ()) { + int n; + + do { + if ((n = content.Read (buffer, nread, buffer.Length - nread)) <= 0) + break; + + nread += n; + } while (nread < buffer.Length); + } + + if (encoding == null) { + if (!CharsetUtils.TryGetBomEncoding (buffer, nread, out encoding)) + encoding = CharsetUtils.UTF8; + } + + using (var stream = new MemoryStream (buffer, 0, nread, false)) { + var previewer = Create (body.Format); + + try { + return previewer.GetPreviewText (stream, encoding); + } catch (DecoderFallbackException) { + stream.Position = 0; + + return previewer.GetPreviewText (stream, CharsetUtils.Latin1); + } + } + } + + ///is null . + ///+ /// Get a text preview of a string of text. + /// + ///+ /// Gets a text preview of a string of text. + /// + /// The original text. + ///A string representing a shortened preview of the original text. + ///+ /// + public virtual string GetPreviewText (string text) + { + if (text == null) + throw new ArgumentNullException (nameof (text)); + + using (var reader = new StringReader (text)) + return GetPreviewText (reader); + } + + ///is null . + ///+ /// Get a text preview of a stream of text in the specified charset. + /// + ///+ /// Get a text preview of a stream of text in the specified charset. + /// + /// The original text stream. + /// The charset encoding of the stream. + ///A string representing a shortened preview of the original text. + ///+ /// + public virtual string GetPreviewText (Stream stream, string charset) + { + if (stream == null) + throw new ArgumentNullException (nameof (stream)); + + if (charset == null) + throw new ArgumentNullException (nameof (charset)); + + Encoding encoding; + + try { + encoding = CharsetUtils.GetEncoding (charset); + } catch (NotSupportedException) { + encoding = CharsetUtils.UTF8; + } + + return GetPreviewText (stream, encoding); + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Get a text preview of a stream of text in the specified encoding. + /// + ///+ /// Get a text preview of a stream of text in the specified encoding. + /// + /// The original text stream. + /// The encoding of the stream. + ///A string representing a shortened preview of the original text. + ///+ /// + public virtual string GetPreviewText (Stream stream, Encoding encoding) + { + if (stream == null) + throw new ArgumentNullException (nameof (stream)); + + if (encoding == null) + throw new ArgumentNullException (nameof (encoding)); + + using (var reader = new StreamReader (stream, encoding, false, 4096, true)) + return GetPreviewText (reader); + } + + ///+ /// is null .-or- + ///+ /// is null .+ /// Get a text preview of a stream of text. + /// + ///+ /// Gets a text preview of a stream of text. + /// + /// The original text stream. + ///A string representing a shortened preview of the original text. + ///+ /// + public abstract string GetPreviewText (TextReader reader); + } +} diff --git a/MimeKit/Text/TextToFlowed.cs b/MimeKit/Text/TextToFlowed.cs index 7423a1dafa..c3263376a0 100644 --- a/MimeKit/Text/TextToFlowed.cs +++ b/MimeKit/Text/TextToFlowed.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastis null . + ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -39,11 +39,10 @@ namespace MimeKit.Text { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new text to flowed text converter. @@ -74,30 +73,6 @@ public override TextFormat OutputFormat { get { return TextFormat.Flowed; } } - /// - /// Get or set the text that will be appended to the end of the output. - /// - ///- /// - ///Gets or sets the text that will be appended to the end of the output. - ///The footer must be set before conversion begins. - ///The footer. - public string Footer { - get; set; - } - - ///- /// Get or set text that will be prepended to the beginning of the output. - /// - ///- /// - ///Gets or sets the text that will be prepended to the beginning of the output. - ///The header must be set before conversion begins. - ///The header. - public string Header { - get; set; - } - static string Unquote (string line, out int quoteDepth) { int index = 0; @@ -139,7 +114,7 @@ static string GetFlowedLine (StringBuilder flowed, string line, ref int index, i flowed.Append ('>', quoteDepth); // Space-stuffed lines which start with a space, "From ", or ">". - if (quoteDepth > 0 || StartsWith (line, index, "From ")) + if (quoteDepth > 0 || (line.Length > index && line[index] == ' ') || StartsWith (line, index, "From ")) flowed.Append (' '); if (flowed.Length + (line.Length - index) <= MaxLineLength) { @@ -150,18 +125,34 @@ static string GetFlowedLine (StringBuilder flowed, string line, ref int index, i } do { - do { - flowed.Append (line[index++]); - } while (flowed.Length + 1 < MaxLineLength && index < line.Length && line[index] != ' '); - - if (flowed.Length >= OptimalLineLength) { - flowed.Append (' '); + int nextSpace = line.IndexOf (' ', index); + int wordEnd = nextSpace == -1 ? line.Length : nextSpace; + int softBreak = nextSpace == -1 ? 0 : 2; // 2 = space + soft-break space + int wordLength = wordEnd - index; + + if (flowed.Length + wordLength + softBreak <= MaxLineLength) { + // The entire word will fit on the remainder of the line. + flowed.Append (line, index, wordLength); + index = wordEnd; + } else if (wordLength > MaxLineLength - (quoteDepth + 1)) { + // Even if we insert a soft-break here, the word is longer than what will fit on its own line. + // No matter what we do, we will need to break the word apart. + wordLength = MaxLineLength - (flowed.Length + 1); + flowed.Append (line, index, wordLength); + index += wordLength; + break; + } else { + // Only part of the word will fit on the remainder of this line, but it will easily fit + // on its own line. Insert a soft-break so that we don't break apart this word. break; } while (flowed.Length + 1 < MaxLineLength && index < line.Length && line[index] == ' ') flowed.Append (line[index++]); - } while (index < line.Length && flowed.Length < OptimalLineLength); + } while (index < line.Length && flowed.Length + 1 < MaxLineLength); + + if (index < line.Length) + flowed.Append (' '); return flowed.ToString (); } @@ -198,18 +189,17 @@ public override void Convert (TextReader reader, TextWriter writer) flowed = new StringBuilder (MaxLineLength); while ((line = reader.ReadLine ()) != null) { - int quoteDepth; - int index = 0; - // Trim spaces before user-inserted hard line breaks. line = line.TrimEnd (' '); - line = Unquote (line, out quoteDepth); + line = Unquote (line, out var quoteDepth); // Ensure all lines (fixed and flowed) are 78 characters or fewer in // length, counting any trailing space as well as a space added as // stuffing, but not counting the CRLF, unless a word by itself // exceeds 78 characters. + int index = 0; + do { var flowedLine = GetFlowedLine (flowed, line, ref index, quoteDepth); writer.WriteLine (flowedLine); diff --git a/MimeKit/Text/TextToHtml.cs b/MimeKit/Text/TextToHtml.cs index c7721289da..8613630cf5 100644 --- a/MimeKit/Text/TextToHtml.cs +++ b/MimeKit/Text/TextToHtml.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -43,7 +43,7 @@ public class TextToHtml : TextConverter readonly UrlScanner scanner; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new text to HTML converter. @@ -78,18 +78,6 @@ public override TextFormat OutputFormat { get { return TextFormat.Html; } } - /// - /// Get or set the text that will be appended to the end of the output. - /// - ///- /// - ///Gets or sets the text that will be appended to the end of the output. - ///The footer must be set before conversion begins. - ///The footer. - public string Footer { - get; set; - } - ////// Get or set the footer format. /// @@ -101,18 +89,6 @@ public HeaderFooterFormat FooterFormat { get; set; } - ///- /// Get or set text that will be prepended to the beginning of the output. - /// - ///- /// - ///Gets or sets the text that will be prepended to the beginning of the output. - ///The header must be set before conversion begins. - ///The header. - public string Header { - get; set; - } - ////// Get or set the header format. /// diff --git a/MimeKit/Text/TextToText.cs b/MimeKit/Text/TextToText.cs index dede8cd6c2..5df0bf1fdd 100644 --- a/MimeKit/Text/TextToText.cs +++ b/MimeKit/Text/TextToText.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -37,7 +37,7 @@ namespace MimeKit.Text { public class TextToText : TextConverter { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new text to text converter. @@ -68,41 +68,6 @@ public override TextFormat OutputFormat { get { return TextFormat.Plain; } } - /// - /// Get or set the text that will be appended to the end of the output. - /// - ///- /// - ///Gets or sets the text that will be appended to the end of the output. - ///The footer must be set before conversion begins. - ///The footer. - public string Footer { - get; set; - } - - ///- /// Get or set the footer format. - /// - ///- /// Gets or sets the footer format. - /// - ///The footer format. - public HeaderFooterFormat FooterFormat { - get; set; - } - - ///- /// Get or set text that will be prepended to the beginning of the output. - /// - ///- /// - ///Gets or sets the text that will be prepended to the beginning of the output. - ///The header must be set before conversion begins. - ///The header. - public string Header { - get; set; - } - ////// Convert the contents of from the to the /// and uses the to write the resulting text. diff --git a/MimeKit/Text/Trie.cs b/MimeKit/Text/Trie.cs index d03df99989..0862261477 100644 --- a/MimeKit/Text/Trie.cs +++ b/MimeKit/Text/Trie.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -66,7 +66,7 @@ public TrieMatch (char value) bool icase; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -80,7 +80,7 @@ public Trie (bool ignoreCase) } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -324,6 +324,9 @@ public int Search (char[] text, int startIndex, int count, out string pattern) /// // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -70,7 +70,7 @@ class UrlScanner const string AtomCharacters = "!#$%&'*+-/=?^_`{|}~"; const string UrlSafeCharacters = "$-_.+!*'(),{}|\\^~[]`#%\";/?:@&="; - readonly Dictionary patterns = new Dictionary (); + readonly Dictionary patterns = new Dictionary (StringComparer.Ordinal); readonly Trie trie = new Trie (true); public UrlScanner () diff --git a/MimeKit/TextPart.cs b/MimeKit/TextPart.cs index 2fcb633688..1f0cd42870 100644 --- a/MimeKit/TextPart.cs +++ b/MimeKit/TextPart.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,22 +28,10 @@ using System.IO; using System.Text; -#if PORTABLE -using EncoderReplacementFallback = Portable.Text.EncoderReplacementFallback; -using DecoderReplacementFallback = Portable.Text.DecoderReplacementFallback; -using EncoderExceptionFallback = Portable.Text.EncoderExceptionFallback; -using DecoderExceptionFallback = Portable.Text.DecoderExceptionFallback; -using EncoderFallbackException = Portable.Text.EncoderFallbackException; -using DecoderFallbackException = Portable.Text.DecoderFallbackException; -using DecoderFallbackBuffer = Portable.Text.DecoderFallbackBuffer; -using DecoderFallback = Portable.Text.DecoderFallback; -using Encoding = Portable.Text.Encoding; -using Encoder = Portable.Text.Encoder; -using Decoder = Portable.Text.Decoder; -#endif - +using MimeKit.IO; using MimeKit.Text; using MimeKit.Utils; +using MimeKit.IO.Filters; namespace MimeKit { /// @@ -61,10 +49,10 @@ namespace MimeKit { public class TextPart : MimePart { /// ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -75,7 +63,7 @@ public TextPart (MimeEntityConstructorArgs args) : base (args) } /// - /// Initializes a new instance of the ///+ /// Initialize a new instance of the /// class with the specified text subtype. /// @@ -114,8 +102,7 @@ public TextPart (string subtype, params object[] args) : this (subtype) if (obj == null || TryInit (obj)) continue; - var enc = obj as Encoding; - if (enc != null) { + if (obj is Encoding enc) { if (encoding != null) throw new ArgumentException ("An encoding should not be specified more than once."); @@ -123,8 +110,7 @@ public TextPart (string subtype, params object[] args) : this (subtype) continue; } - var str = obj as string; - if (str != null) { + if (obj is string str) { if (text != null) throw new ArgumentException ("The text should not be specified more than once."); @@ -146,7 +132,7 @@ internal TextPart (ContentType contentType) : base (contentType) } /// - /// Initializes a new instance of the ///+ /// Initialize a new instance of the /// class with the specified text subtype. /// @@ -185,7 +171,7 @@ static string GetMediaSubtype (TextFormat format) } /// - /// Initializes a new instance of the ///+ /// Initialize a new instance of the /// class with the specified text format. /// @@ -202,7 +188,7 @@ public TextPart (TextFormat format) : base ("text", GetMediaSubtype (format)) } /// - /// Initializes a new instance of the ///+ /// Initialize a new instance of the /// class with a Content-Type of text/plain. /// @@ -212,6 +198,42 @@ public TextPart () : base ("text", "plain") { } + /// ///+ /// Get the text format of the content. + /// + ///+ /// Gets the text format of the content. + /// + ///The text format of the content. + public TextFormat Format { + get { + if (ContentType.MediaType.Equals ("text", StringComparison.OrdinalIgnoreCase)) { + if (ContentType.MediaSubtype.Equals ("plain")) { + string format; + + if (ContentType.Parameters.TryGetValue ("format", out format)) { + format = format.Trim (); + + if (format.Equals ("flowed", StringComparison.OrdinalIgnoreCase)) + return TextFormat.Flowed; + } + } else if (ContentType.MediaSubtype.Equals ("html", StringComparison.OrdinalIgnoreCase)) { + return TextFormat.Html; + } else if (ContentType.MediaSubtype.Equals ("rtf", StringComparison.OrdinalIgnoreCase)) { + return TextFormat.RichText; + } else if (ContentType.MediaSubtype.Equals ("enriched", StringComparison.OrdinalIgnoreCase)) { + return TextFormat.Enriched; + } else if (ContentType.MediaSubtype.Equals ("richtext", StringComparison.OrdinalIgnoreCase)) { + return TextFormat.Enriched; + } + } else if (ContentType.IsMimeType ("application", "rtf")) { + return TextFormat.RichText; + } + + return TextFormat.Plain; + } + } + ////// Gets whether or not this text part contains enriched text. /// @@ -220,7 +242,7 @@ public TextPart () : base ("text", "plain") /// predecessor,text/richtext (not to be confused withtext/rtf ). ///- bool IsEnriched { + public bool IsEnriched { get { return ContentType.IsMimeType ("text", "enriched") || ContentType.IsMimeType ("text", "richtext"); } } @@ -244,7 +266,7 @@ public bool IsFlowed { format = format.Trim (); - return format.ToLowerInvariant () == "flowed"; + return format.Equals ("flowed", StringComparison.OrdinalIgnoreCase); } } @@ -285,52 +307,22 @@ public bool IsRichText { } /// true if the text is enriched; otherwise,false .- /// Gets the decoded text content. + /// Get the decoded text content. /// ////// - ///If the charset parameter on the + /// If that fails or if the charset parameter is not set, the first 2 bytes of + /// the content will be checked for a unicode BOM. If a BOM exists, then that + /// will be used for conversion. If no BOM is found, then UTF-8 is attempted. + /// If conversion fails, then iso-8859-1 will be used as the final fallback. ////// is set, it will be used in order to convert the raw content into unicode. - /// If that fails or if the charset parameter is not set, iso-8859-1 will be - /// used instead. For more control, use ////// or . The text. + ///The decocded text. public string Text { get { - if (Content == null) - return string.Empty; - - var charset = ContentType.Parameters["charset"]; - - using (var memory = new MemoryStream ()) { - Content.DecodeTo (memory); - -#if !PORTABLE && !NETSTANDARD - var content = memory.GetBuffer (); -#else - var content = memory.ToArray (); -#endif - Encoding encoding = null; - - if (charset != null) { - try { - encoding = CharsetUtils.GetEncoding (charset); - } catch (NotSupportedException) { - } - } - - if (encoding == null) { - try { - return CharsetUtils.UTF8.GetString (content, 0, (int) memory.Length); - } catch (DecoderFallbackException) { - // fall back to iso-8859-1 - encoding = CharsetUtils.Latin1; - } - } - - return encoding.GetString (content, 0, (int) memory.Length); - } + return GetText (out Encoding encoding); } set { SetText (Encoding.UTF8, value); @@ -341,12 +333,12 @@ public string Text { /// Dispatches to the specific visit method for this MIME entity. ///- /// This default implementation for /// The visitor. ///nodes - /// calls . Override this + /// This default implementation for nodes + /// calls . Override this /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still + /// of the class. However, it should still /// support unknown visitors by calling - /// . + /// . /// @@ -380,6 +372,47 @@ internal bool IsFormat (TextFormat format) } } + /// + /// Get the decoded text and the encoding used to convert it into unicode. + /// + ///+ /// + /// The encoding used to convert the text into unicode. + ///If the charset parameter on the + ///+ /// is set, it will be used in order to convert the raw content into unicode. + /// If that fails or if the charset parameter is not set, the first 2 bytes of + /// the content will be checked for a unicode BOM. If a BOM exists, then that + /// will be used for conversion. If no BOM is found, then UTF-8 is attempted. + /// If conversion fails, then iso-8859-1 will be used as the final fallback. For more control, use + ///+ /// or . The decoded text. + public string GetText (out Encoding encoding) + { + if (Content == null) { + encoding = Encoding.ASCII; + return string.Empty; + } + + encoding = ContentType.CharsetEncoding; + + if (encoding == null) { + try { + using (var content = Content.Open ()) { + if (!CharsetUtils.TryGetBomEncoding (content, out encoding)) + encoding = CharsetUtils.UTF8; + } + + return GetText (encoding); + } catch (DecoderFallbackException) { + // fall back to iso-8859-1 + encoding = CharsetUtils.Latin1; + } + } + + return GetText (encoding); + } + ////// Gets the decoded text content using the provided charset encoding to /// override the charset specified in the Content-Type parameters. @@ -403,15 +436,20 @@ public string GetText (Encoding encoding) return string.Empty; using (var memory = new MemoryStream ()) { - Content.DecodeTo (memory); + using (var filtered = new FilteredStream (memory)) { + filtered.Add (new CharsetFilter (encoding, CharsetUtils.UTF8)); + filtered.Add (FormatOptions.Default.CreateNewLineFilter ()); + Content.DecodeTo (filtered); + filtered.Flush (); + } -#if !PORTABLE && !NETSTANDARD +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 var buffer = memory.GetBuffer (); #else var buffer = memory.ToArray (); #endif - return encoding.GetString (buffer, 0, (int) memory.Length); + return CharsetUtils.UTF8.GetString (buffer, 0, (int) memory.Length); } } @@ -463,8 +501,8 @@ public void SetText (Encoding encoding, string text) if (text == null) throw new ArgumentNullException (nameof (text)); - ContentType.Parameters["charset"] = CharsetUtils.GetMimeCharset (encoding); var content = new MemoryStream (encoding.GetBytes (text)); + ContentType.CharsetEncoding = encoding; Content = new MimeContent (content); } diff --git a/MimeKit/TextRfc822Headers.cs b/MimeKit/TextRfc822Headers.cs new file mode 100644 index 0000000000..6312345114 --- /dev/null +++ b/MimeKit/TextRfc822Headers.cs @@ -0,0 +1,123 @@ +// +// TextRfc822Headers.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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; + +namespace MimeKit { + /// + /// A MIME part containing message headers as its content. + /// + ///+ /// Represents MIME entities with a Content-Type of text/rfc822-headers. + /// + public class TextRfc822Headers : MessagePart + { + ///+ /// Initialize a new instance of the + ///class. + /// + /// This constructor is used by + /// Information used by the constructor. + ///. + /// + /// + public TextRfc822Headers (MimeEntityConstructorArgs args) : base (args) + { + } + + ///is null . + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new + /// An array of initialization parameters: headers and message parts. + ///. + /// + /// + ///is null . + ///+ /// + public TextRfc822Headers (params object[] args) : this () + { + MimeMessage message = null; + + foreach (object obj in args) { + if (obj == null || TryInit (obj)) + continue; + + if (obj is MimeMessage mesg) { + if (message != null) + throw new ArgumentException ("MimeMessage should not be specified more than once."); + + message = mesg; + continue; + } + + throw new ArgumentException ("Unknown initialization parameter: " + obj.GetType ()); + } + + if (message != null) + Message = message; + } + + ///+ /// contains more than one . -or- + ///+ /// contains one or more arguments of an unknown type. + /// Initialize a new instance of the + ///class. + /// + /// Creates a new text/rfc822-headers MIME entity. + /// + public TextRfc822Headers () : base ("text", "rfc822-headers") + { + } + + ///+ /// Dispatches to the specific visit method for this MIME entity. + /// + ///+ /// This default implementation for + /// The visitor. + ///nodes + /// calls . Override this + /// method to call into a more specific method on a derived visitor class + /// of the class. However, it should still + /// support unknown visitors by calling + /// . + /// + /// + public override void Accept (MimeVisitor visitor) + { + if (visitor == null) + throw new ArgumentNullException (nameof (visitor)); + + visitor.VisitTextRfc822Headers (this); + } + } +} diff --git a/MimeKit/Tnef/RtfCompressedToRtf.cs b/MimeKit/Tnef/RtfCompressedToRtf.cs index 67a4b479ea..773c96f27c 100644 --- a/MimeKit/Tnef/RtfCompressedToRtf.cs +++ b/MimeKit/Tnef/RtfCompressedToRtf.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastis null . + ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,16 +25,11 @@ // using System; +using System.Text; using MimeKit.IO.Filters; using MimeKit.Utils; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#else -using Encoding = System.Text.Encoding; -#endif - namespace MimeKit.Tnef { /// /// A filter to decompress a compressed RTF stream. @@ -76,10 +71,10 @@ enum FilterState { int size; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// Creates a new public RtfCompressedToRtf () { @@ -112,6 +107,11 @@ public bool IsValidCrc32 { bool TryReadInt32 (byte[] buffer, ref int index, int endIndex, out int value) { + if (index == endIndex) { + value = saved; + return false; + } + int nread = (saved >> 24) & 0xFF; saved &= 0x00FFFFFF; @@ -242,7 +242,7 @@ protected override byte[] Filter (byte[] input, int startIndex, int length, out outputLength = 0; outputIndex = 0; - while (index < endIndex) { + while (index < endIndex && state != FilterState.Complete) { byte value = input[index++]; crc32.Update (value); @@ -314,8 +314,6 @@ protected override byte[] Filter (byte[] input, int startIndex, int length, out state = FilterState.BeginControlRun; } break; - case FilterState.Complete: - break; } } diff --git a/MimeKit/Tnef/RtfCompressionMode.cs b/MimeKit/Tnef/RtfCompressionMode.cs index 37c463c303..8949c0c630 100644 --- a/MimeKit/Tnef/RtfCompressionMode.cs +++ b/MimeKit/Tnef/RtfCompressionMode.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastconverter filter. + /// Creates a new converter filter. /// // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Tnef/TnefAttachFlags.cs b/MimeKit/Tnef/TnefAttachFlags.cs index be8321782f..7ec444b88c 100644 --- a/MimeKit/Tnef/TnefAttachFlags.cs +++ b/MimeKit/Tnef/TnefAttachFlags.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Tnef/TnefAttachMethod.cs b/MimeKit/Tnef/TnefAttachMethod.cs index 751b0f48d4..558e394c40 100644 --- a/MimeKit/Tnef/TnefAttachMethod.cs +++ b/MimeKit/Tnef/TnefAttachMethod.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Tnef/TnefAttributeLevel.cs b/MimeKit/Tnef/TnefAttributeLevel.cs index 52ca004ea1..8f8fe1e1c3 100644 --- a/MimeKit/Tnef/TnefAttributeLevel.cs +++ b/MimeKit/Tnef/TnefAttributeLevel.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Tnef/TnefAttributeTag.cs b/MimeKit/Tnef/TnefAttributeTag.cs index a831574daf..66bbee38ba 100644 --- a/MimeKit/Tnef/TnefAttributeTag.cs +++ b/MimeKit/Tnef/TnefAttributeTag.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Tnef/TnefComplianceMode.cs b/MimeKit/Tnef/TnefComplianceMode.cs index f7a4bbd01f..8466abc7f0 100644 --- a/MimeKit/Tnef/TnefComplianceMode.cs +++ b/MimeKit/Tnef/TnefComplianceMode.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Tnef/TnefComplianceStatus.cs b/MimeKit/Tnef/TnefComplianceStatus.cs index bb9360712a..201f953953 100644 --- a/MimeKit/Tnef/TnefComplianceStatus.cs +++ b/MimeKit/Tnef/TnefComplianceStatus.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Tnef/TnefException.cs b/MimeKit/Tnef/TnefException.cs index 559e2b01ba..5b8f2d9093 100644 --- a/MimeKit/Tnef/TnefException.cs +++ b/MimeKit/Tnef/TnefException.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -46,7 +46,7 @@ public class TnefException : FormatException { #if SERIALIZABLE /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -63,7 +63,7 @@ protected TnefException (SerializationInfo info, StreamingContext context) : bas #endif /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -77,7 +77,7 @@ public TnefException (TnefComplianceStatus error, string message, Exception inne } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -106,12 +106,9 @@ public TnefException (TnefComplianceStatus error, string message) : base (messag [SecurityCritical] public override void GetObjectData (SerializationInfo info, StreamingContext context) { - if (info == null) - throw new ArgumentNullException (nameof (info)); + base.GetObjectData (info, context); info.AddValue ("Error", Error); - - base.GetObjectData (info, context); } #endif diff --git a/MimeKit/Tnef/TnefNameId.cs b/MimeKit/Tnef/TnefNameId.cs index 35dc8d2961..201b0a95e2 100644 --- a/MimeKit/Tnef/TnefNameId.cs +++ b/MimeKit/Tnef/TnefNameId.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -85,7 +85,7 @@ public int Id { } /// - /// Initializes a new instance of the ///struct. + /// Initialize a new instance of the struct. /// /// Creates a new with the specified integer identifier. @@ -101,7 +101,7 @@ public TnefNameId (Guid propertySetGuid, int id) } /// - /// Initializes a new instance of the ///struct. + /// Initialize a new instance of the struct. /// /// Creates a new with the specified string identifier. @@ -117,10 +117,10 @@ public TnefNameId (Guid propertySetGuid, string name) } /// - /// Serves as a hash function for a ///object. + /// Serves as a hash function for a object. /// - /// Serves as a hash function for a ///object. + /// Serves as a hash function for a object. /// A hash code for this instance that is suitable for use in hashing algorithms /// and data structures such as a hash table. @@ -132,14 +132,14 @@ public override int GetHashCode () } ///- /// Determines whether the specified ///is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// Determines whether the specified - /// Theis equal to the current . + /// Determines whether the specified is equal to the current . /// to compare with the current . + /// The to compare with the current . /// + /// true if the specifiedis equal to the current - /// ; otherwise, false .; otherwise, false .// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Tnef/TnefPart.cs b/MimeKit/Tnef/TnefPart.cs index 5e3219fea8..c43663a200 100644 --- a/MimeKit/Tnef/TnefPart.cs +++ b/MimeKit/Tnef/TnefPart.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,14 +26,9 @@ using System; using System.IO; +using System.Text; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#else -using Encoding = System.Text.Encoding; -#endif - using MimeKit.IO; using MimeKit.Utils; using MimeKit.IO.Filters; @@ -50,10 +45,10 @@ namespace MimeKit.Tnef { public class TnefPart : MimePart { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// - /// This constructor is used by /// Information used by the constructor. ///. + /// This constructor is used by . /// @@ -64,7 +59,7 @@ public TnefPart (MimeEntityConstructorArgs args) : base (args) } /// public bool ReadNextProperty () { - if (propertyIndex >= propertyCount) - return false; - while (ReadNextValue ()) { - // skip over the value... + // skip over the remaining value(s) for the current property... } + if (propertyIndex >= propertyCount) + return false; + try { var type = (TnefPropertyType) ReadInt16 (); var id = (TnefPropertyId) ReadInt16 (); @@ -632,13 +624,13 @@ public bool ReadNextProperty () ///- /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new ///with a Content-Type of application/vnd.ms-tnef @@ -76,14 +71,40 @@ public TnefPart () : base ("application", "vnd.ms-tnef") FileName = "winmail.dat"; } + /// + /// Dispatches to the specific visit method for this MIME entity. + /// + ///+ /// This default implementation for + /// The visitor. + ///nodes + /// calls . Override this + /// method to call into a more specific method on a derived visitor class + /// of the class. However, it should still + /// support unknown visitors by calling + /// . + /// + /// + public override void Accept (MimeVisitor visitor) + { + if (visitor == null) + throw new ArgumentNullException (nameof (visitor)); + + visitor.VisitTnefPart (this); + } + static void ExtractRecipientTable (TnefReader reader, MimeMessage message) { var prop = reader.TnefPropertyReader; // Note: The RecipientTable uses rows of properties... while (prop.ReadNextRow ()) { + string transmitableDisplayName = null; + string recipientDisplayName = null; + string displayName = string.Empty; InternetAddressList list = null; - string name = null, addr = null; + string addr = null; while (prop.ReadNextProperty ()) { switch (prop.PropertyTag.Id) { @@ -96,11 +117,13 @@ static void ExtractRecipientTable (TnefReader reader, MimeMessage message) } break; case TnefPropertyId.TransmitableDisplayName: - if (string.IsNullOrEmpty (name)) - name = prop.ReadValueAsString (); + transmitableDisplayName = prop.ReadValueAsString (); + break; + case TnefPropertyId.RecipientDisplayName: + recipientDisplayName = prop.ReadValueAsString (); break; case TnefPropertyId.DisplayName: - name = prop.ReadValueAsString (); + displayName = prop.ReadValueAsString (); break; case TnefPropertyId.EmailAddress: if (string.IsNullOrEmpty (addr)) @@ -114,14 +137,64 @@ static void ExtractRecipientTable (TnefReader reader, MimeMessage message) } } - if (list != null && !string.IsNullOrEmpty (addr)) + if (list != null && !string.IsNullOrEmpty (addr)) { + var name = recipientDisplayName ?? transmitableDisplayName ?? displayName; + list.Add (new MailboxAddress (name, addr)); + } + } + } + + class EmailAddress + { + public string AddrType = "SMTP"; + public string SearchKey; + public string Name; + public string Addr; + + bool CanUseSearchKey { + get { + return SearchKey != null && SearchKey.Equals ("SMTP", StringComparison.OrdinalIgnoreCase) && + SearchKey.Length > AddrType.Length && SearchKey.StartsWith (AddrType, StringComparison.Ordinal) && + SearchKey[AddrType.Length] == ':'; + } + } + + //public bool IsComplete { + // get { + // return !string.IsNullOrEmpty (Addr) || CanUseSearchKey; + // } + //} + + public bool TryGetMailboxAddress (out MailboxAddress mailbox) + { + string addr; + + if (string.IsNullOrEmpty (Addr) && CanUseSearchKey) + addr = SearchKey.Substring (AddrType.Length + 1); + else + addr = Addr; + + if (string.IsNullOrEmpty (addr) || !MailboxAddress.TryParse (addr, out mailbox)) { + mailbox = null; + return false; + } + + mailbox.Name = Name; + + return true; } } static void ExtractMapiProperties (TnefReader reader, MimeMessage message, BodyBuilder builder) { var prop = reader.TnefPropertyReader; + var recipient = new EmailAddress (); + var sender = new EmailAddress (); + string normalizedSubject = null; + string subjectPrefix = null; + MailboxAddress mailbox; + var msgid = false; while (prop.ReadNextProperty ()) { switch (prop.PropertyTag.Id) { @@ -129,6 +202,25 @@ static void ExtractMapiProperties (TnefReader reader, MimeMessage message, BodyB if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { message.MessageId = prop.ReadValueAsString (); + msgid = true; + } + break; + case TnefPropertyId.TnefCorrelationKey: + // According to MSDN, PidTagTnefCorrelationKey is a unique key that is + // meant to be used to tie the TNEF attachment to the encapsulating + // message. It can be a string or a binary blob. It seems that most + // implementations use the Message-Id string, so if this property + // value looks like a Message-Id, then us it as one (unless we get a + // InternetMessageId property, in which case we use that instead. + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { + if (!msgid) { + var value = prop.ReadValueAsString (); + + if (value.Length > 5 && value[0] == '<' && value[value.Length - 1] == '>' && value.IndexOf ('@') != -1) + message.MessageId = value; + } } break; case TnefPropertyId.Subject: @@ -137,12 +229,73 @@ static void ExtractMapiProperties (TnefReader reader, MimeMessage message, BodyB message.Subject = prop.ReadValueAsString (); } break; + case TnefPropertyId.SubjectPrefix: + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { + subjectPrefix = prop.ReadValueAsString (); + } + break; + case TnefPropertyId.NormalizedSubject: + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { + normalizedSubject = prop.ReadValueAsString (); + } + break; + case TnefPropertyId.SenderName: + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { + sender.Name = prop.ReadValueAsString (); + } + break; + case TnefPropertyId.SenderEmailAddress: + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { + sender.Addr = prop.ReadValueAsString (); + } + break; + case TnefPropertyId.SenderSearchKey: + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { + sender.SearchKey = prop.ReadValueAsString (); + } + break; + case TnefPropertyId.SenderAddrtype: + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { + sender.AddrType = prop.ReadValueAsString (); + } + break; + case TnefPropertyId.ReceivedByName: + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { + recipient.Name = prop.ReadValueAsString (); + } + break; + case TnefPropertyId.ReceivedByEmailAddress: + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { + recipient.Addr = prop.ReadValueAsString (); + } + break; + case TnefPropertyId.ReceivedBySearchKey: + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { + recipient.SearchKey = prop.ReadValueAsString (); + } + break; + case TnefPropertyId.ReceivedByAddrtype: + if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || + prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { + recipient.AddrType = prop.ReadValueAsString (); + } + break; case TnefPropertyId.RtfCompressed: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var rtf = new TextPart ("rtf"); - rtf.ContentType.Name = "body.rtf"; var converter = new RtfCompressedToRtf (); var content = new MemoryBlockStream (); @@ -167,7 +320,6 @@ static void ExtractMapiProperties (TnefReader reader, MimeMessage message, BodyB prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var html = new TextPart ("html"); - html.ContentType.Name = "body.html"; Encoding encoding; if (prop.PropertyTag.ValueTnefType != TnefPropertyType.Unicode) @@ -185,7 +337,6 @@ static void ExtractMapiProperties (TnefReader reader, MimeMessage message, BodyB prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var plain = new TextPart ("plain"); - plain.ContentType.Name = "body.txt"; Encoding encoding; if (prop.PropertyTag.ValueTnefType != TnefPropertyType.Unicode) @@ -199,19 +350,61 @@ static void ExtractMapiProperties (TnefReader reader, MimeMessage message, BodyB } break; case TnefPropertyId.Importance: + // https://msdn.microsoft.com/en-us/library/ee237166(v=exchg.80).aspx switch (prop.ReadValueAsInt32 ()) { case 2: message.Importance = MessageImportance.High; break; + case 1: message.Importance = MessageImportance.Normal; break; case 0: message.Importance = MessageImportance.Low; break; } break; case TnefPropertyId.Priority: + // https://msdn.microsoft.com/en-us/library/ee159473(v=exchg.80).aspx switch (prop.ReadValueAsInt32 ()) { - case 2: message.Priority = MessagePriority.Urgent; break; - case 0: message.Priority = MessagePriority.NonUrgent; break; + case 1: message.Priority = MessagePriority.Urgent; break; + case 0: message.Priority = MessagePriority.Normal; break; + case -1: message.Priority = MessagePriority.NonUrgent; break; + } + break; + case TnefPropertyId.Sensitivity: + // https://msdn.microsoft.com/en-us/library/ee217353(v=exchg.80).aspx + // https://tools.ietf.org/html/rfc2156#section-5.3.4 + switch (prop.ReadValueAsInt32 ()) { + case 1: message.Headers[HeaderId.Sensitivity] = "Personal"; break; + case 2: message.Headers[HeaderId.Sensitivity] = "Private"; break; + case 3: message.Headers[HeaderId.Sensitivity] = "Company-Confidential"; break; + case 0: message.Headers.Remove (HeaderId.Sensitivity); break; } break; } } + + if (string.IsNullOrEmpty (message.Subject) && !string.IsNullOrEmpty (normalizedSubject)) { + if (!string.IsNullOrEmpty (subjectPrefix)) + message.Subject = subjectPrefix + normalizedSubject; + else + message.Subject = normalizedSubject; + } + + if (sender.TryGetMailboxAddress (out mailbox)) + message.From.Add (mailbox); + + if (recipient.TryGetMailboxAddress (out mailbox)) + message.To.Add (mailbox); + } + + static TnefPart PromoteToTnefPart (MimePart part) + { + var tnef = new TnefPart (); + + foreach (var param in part.ContentType.Parameters) + tnef.ContentType.Parameters[param.Name] = param.Value; + + if (part.ContentDisposition != null) + tnef.ContentDisposition = part.ContentDisposition; + + tnef.ContentTransferEncoding = part.ContentTransferEncoding; + + return tnef; } static void ExtractAttachments (TnefReader reader, BodyBuilder builder) @@ -239,6 +432,8 @@ static void ExtractAttachments (TnefReader reader, BodyBuilder builder) if (attachment == null) break; + attachData = null; + while (prop.ReadNextProperty ()) { switch (prop.PropertyTag.Id) { case TnefPropertyId.AttachLongFilename: @@ -249,60 +444,27 @@ static void ExtractAttachments (TnefReader reader, BodyBuilder builder) attachment.FileName = prop.ReadValueAsString (); break; case TnefPropertyId.AttachContentLocation: - text = prop.ReadValueAsString (); - if (Uri.IsWellFormedUriString (text, UriKind.Absolute)) - attachment.ContentLocation = new Uri (text, UriKind.Absolute); - else if (Uri.IsWellFormedUriString (text, UriKind.Relative)) - attachment.ContentLocation = new Uri (text, UriKind.Relative); + attachment.ContentLocation = prop.ReadValueAsUri (); break; case TnefPropertyId.AttachContentBase: - text = prop.ReadValueAsString (); - attachment.ContentBase = new Uri (text, UriKind.Absolute); + attachment.ContentBase = prop.ReadValueAsUri (); break; case TnefPropertyId.AttachContentId: - attachment.ContentId = prop.ReadValueAsString (); + text = prop.ReadValueAsString (); + + var buffer = CharsetUtils.UTF8.GetBytes (text); + int index = 0; + + if (ParseUtils.TryParseMsgId (buffer, ref index, buffer.Length, false, false, out string msgid)) + attachment.ContentId = msgid; break; case TnefPropertyId.AttachDisposition: text = prop.ReadValueAsString (); - if (attachment.ContentDisposition == null) - attachment.ContentDisposition = new ContentDisposition (text); - else - attachment.ContentDisposition.Disposition = text; + if (ContentDisposition.TryParse (text, out ContentDisposition disposition)) + attachment.ContentDisposition = disposition; break; case TnefPropertyId.AttachData: - var stream = prop.GetRawValueReadStream (); - var content = new MemoryStream (); - var guid = new byte[16]; - - if (attachMethod == TnefAttachMethod.EmbeddedMessage) { - var tnef = new TnefPart (); - - foreach (var param in attachment.ContentType.Parameters) - tnef.ContentType.Parameters[param.Name] = param.Value; - - if (attachment.ContentDisposition != null) - tnef.ContentDisposition = attachment.ContentDisposition; - - attachment = tnef; - } - - // read the GUID - stream.Read (guid, 0, 16); - - // the rest is content - using (var filtered = new FilteredStream (content)) { - filtered.Add (filter); - stream.CopyTo (filtered, 4096); - filtered.Flush (); - } - - content.Position = 0; - - attachment.ContentTransferEncoding = filter.GetBestEncoding (EncodingConstraint.SevenBit); - attachment.Content = new MimeContent (content); - filter.Reset (); - - builder.Attachments.Add (attachment); + attachData = prop.ReadValueAsBytes (); break; case TnefPropertyId.AttachMethod: attachMethod = (TnefAttachMethod) prop.ReadValueAsInt32 (); @@ -334,6 +496,27 @@ static void ExtractAttachments (TnefReader reader, BodyBuilder builder) break; } } + + if (attachData != null) { + int count = attachData.Length; + int index = 0; + + if (attachMethod == TnefAttachMethod.EmbeddedMessage) { + attachment.ContentTransferEncoding = ContentEncoding.Base64; + attachment = PromoteToTnefPart (attachment); + count -= 16; + index = 16; + } else if (attachment.ContentType.IsMimeType ("text", "*")) { + filter.Flush (attachData, index, count, out outIndex, out outLength); + attachment.ContentTransferEncoding = filter.GetBestEncoding (EncodingConstraint.SevenBit); + filter.Reset (); + } else { + attachment.ContentTransferEncoding = ContentEncoding.Base64; + } + + attachment.Content = new MimeContent (new MemoryStream (attachData, index, count, false)); + builder.Attachments.Add (attachment); + } break; case TnefAttributeTag.AttachCreateDate: if (attachment != null) { @@ -366,11 +549,16 @@ static void ExtractAttachments (TnefReader reader, BodyBuilder builder) break; attachData = prop.ReadValueAsBytes (); - filter.Flush (attachData, 0, attachData.Length, out outIndex, out outLength); - attachment.ContentTransferEncoding = filter.GetBestEncoding (EncodingConstraint.EightBit); - attachment.Content = new MimeContent (new MemoryStream (attachData, false)); - filter.Reset (); + if (attachment.ContentType.IsMimeType ("text", "*")) { + filter.Flush (attachData, 0, attachData.Length, out outIndex, out outLength); + attachment.ContentTransferEncoding = filter.GetBestEncoding (EncodingConstraint.SevenBit); + filter.Reset (); + } else { + attachment.ContentTransferEncoding = ContentEncoding.Base64; + } + + attachment.Content = new MimeContent (new MemoryStream (attachData, false)); builder.Attachments.Add (attachment); break; } @@ -382,6 +570,8 @@ static MimeMessage ExtractTnefMessage (TnefReader reader) var builder = new BodyBuilder (); var message = new MimeMessage (); + message.Headers.Remove (HeaderId.Date); + while (reader.ReadNextAttribute ()) { if (reader.AttributeLevel == TnefAttributeLevel.Attachment) break; @@ -413,16 +603,16 @@ static MimeMessage ExtractTnefMessage (TnefReader reader) } ///is null . + ///- /// Converts the TNEF content into a ///. + /// Converts the TNEF content into a . /// - /// TNEF data often contains properties that map to ///+ /// TNEF data often contains properties that map to /// headers. TNEF data also often contains file attachments which will be /// mapped to MIME parts. /// A message representing the TNEF data in MIME format. ///- /// The public MimeMessage ConvertToMessage () { @@ -449,7 +639,7 @@ public MimeMessage ConvertToMessage () ///property is null . + /// Theproperty is null . ///The attachments. ///- /// The public IEnumerableproperty is null . + /// Theproperty is null . ///ExtractAttachments () { diff --git a/MimeKit/Tnef/TnefPropertyId.cs b/MimeKit/Tnef/TnefPropertyId.cs index 381e109dac..d4405fb330 100644 --- a/MimeKit/Tnef/TnefPropertyId.cs +++ b/MimeKit/Tnef/TnefPropertyId.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -177,6 +177,26 @@ public enum TnefPropertyId : short { /// AttachLongPathname = 0x370D, + /// + /// The MAPI property PR_ATTACHMENT_CONTACTPHOTO. + /// + AttachmentContactPhoto = 0x7FFF, + + ///+ /// The MAPI property PR_ATTACHMENT_FLAGS. + /// + AttachmentFlags = 0x7FFD, + + ///+ /// The MAPI property PR_ATTACHMENT_HIDDEN. + /// + AttachmentHidden = 0x7FFE, + + ///+ /// The MAPI property PR_ATTACHMENT_LINKID. + /// + AttachmentLinkId = 0x7FFA, + ////// The MAPI property PR_ATTACHMENT_X400_PARAMETERS. /// @@ -727,6 +747,16 @@ public enum TnefPropertyId : short { /// EntryId = 0x0FFF, + ///+ /// The MAPI property PR_EXCEPTION_STARTTIME. + /// + ExceptionStartTime = 0x7FFB, + + ///+ /// The MAPI property PR_EXCEPTION_ENDTIME. + /// + ExceptionEndTime = 0x7FFC, + ////// The MAPI property PR_EXPAND_BEGIN_TIME. /// @@ -1117,6 +1147,11 @@ public enum TnefPropertyId : short { /// LastModificationTime = 0x3008, + ///+ /// The MAPI property PR_LAST_MODIFIER_NAME. + /// + LastModifierName = 0x3FFA, + ////// The MAPI property PR_LATEST_DELIVERY_TIME. /// @@ -1952,6 +1987,11 @@ public enum TnefPropertyId : short { /// RecipientCertificate = 0x0C13, + ///+ /// The MAPI property PR_RECIPIENT_DISPLAY_NAME. + /// + RecipientDisplayName = 0x5FF6, + ////// The MAPI property PR_RECIPIENT_NUMBER_FOR_ADVICE. /// @@ -2027,6 +2067,11 @@ public enum TnefPropertyId : short { /// ReplyRecipientNames = 0x0050, + ///+ /// The MAPI property PR_REPLY_RECIPIENT_SMTP_PROXIES. + /// + ReplyRecipientSmtpProxies = 0x3FFC, + ////// The MAPI property PR_REPLY_REQUESTED. /// @@ -2207,6 +2252,11 @@ public enum TnefPropertyId : short { /// SenderSearchKey = 0x0C1D, + ///+ /// The MAPI property PR_SENDER_SIMPLE_DISP_NAME. + /// + SenderSimpleDispName = 0x4030, + ////// The MAPI property PR_SEND_INTERNET_ENCODING. /// @@ -2257,6 +2307,11 @@ public enum TnefPropertyId : short { /// SentRepresentingSearchKey = 0x003B, + ///+ /// The MAPI property PR_SENT_REPRESENTING_SIMPLE_DISP_NAME. + /// + SentRepresentingSimpleDispName = 0x4031, + ////// The MAPI property PR_SERVICE_DELETE_FILES. /// diff --git a/MimeKit/Tnef/TnefPropertyReader.cs b/MimeKit/Tnef/TnefPropertyReader.cs index d8bbcbd443..ee7d10a428 100644 --- a/MimeKit/Tnef/TnefPropertyReader.cs +++ b/MimeKit/Tnef/TnefPropertyReader.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,20 +28,6 @@ using System.IO; using System.Text; -#if PORTABLE -using EncoderReplacementFallback = Portable.Text.EncoderReplacementFallback; -using DecoderReplacementFallback = Portable.Text.DecoderReplacementFallback; -using EncoderExceptionFallback = Portable.Text.EncoderExceptionFallback; -using DecoderExceptionFallback = Portable.Text.DecoderExceptionFallback; -using EncoderFallbackException = Portable.Text.EncoderFallbackException; -using DecoderFallbackException = Portable.Text.DecoderFallbackException; -using DecoderFallbackBuffer = Portable.Text.DecoderFallbackBuffer; -using DecoderFallback = Portable.Text.DecoderFallback; -using Encoding = Portable.Text.Encoding; -using Encoder = Portable.Text.Encoder; -using Decoder = Portable.Text.Decoder; -#endif - namespace MimeKit.Tnef { /// /// A TNEF property reader. @@ -329,7 +315,8 @@ public Stream GetRawValueReadStream () if (valueIndex >= valueCount) throw new InvalidOperationException (); - int end = RawValueStreamOffset + RawValueLength; + int startOffset = RawValueStreamOffset; + int length = RawValueLength; if (propertyCount > 0 && reader.StreamOffset == RawValueStreamOffset) { switch (propertyTag.ValueTnefType) { @@ -337,14 +324,19 @@ public Stream GetRawValueReadStream () case TnefPropertyType.String8: case TnefPropertyType.Binary: case TnefPropertyType.Object: - ReadInt32 (); + int n = ReadInt32 (); + if (n >= 0 && n + 4 < length) + length = n + 4; break; } } valueIndex++; - return new TnefReaderStream (reader, end); + int valueEndOffset = startOffset + RawValueLength; + int dataEndOffset = startOffset + length; + + return new TnefReaderStream (reader, dataEndOffset, valueEndOffset); } bool CheckRawValueLength () @@ -413,7 +405,7 @@ static long DoubleDateToTicks (double value) { // The check done this way will take care of NaN if (!(value < OADateMaxAsDouble) || !(value > OADateMinAsDouble)) - throw new ArgumentException ("Invalid OLE Automation Date."); + throw new ArgumentException ("Invalid OLE Automation Date.", nameof (value)); long millis = (long) (value * MillisPerDay + (value >= 0 ? 0.5 : -0.5)); @@ -423,7 +415,7 @@ static long DoubleDateToTicks (double value) millis += DoubleDateOffset / TicksPerMillisecond; if (millis < 0 || millis >= MaxMillis) - throw new ArgumentException ("Invalid OLE Automation Date."); + throw new ArgumentException ("Invalid OLE Automation Date.", nameof (value)); return millis * TicksPerMillisecond; } @@ -487,7 +479,7 @@ Encoding GetMessageEncoding () if (codepage != 0 && codepage != 1252) { try { - return Encoding.GetEncoding (codepage, new EncoderExceptionFallback (), new DecoderExceptionFallback ()); + return Encoding.GetEncoding (codepage); } catch { return DefaultEncoding; } @@ -584,13 +576,13 @@ void LoadPropertyName () /// - /// Serves as a hash function for a + ///object. + /// Reads the value as a Uri. + /// + /// Reads any string or binary blob values as a Uri. + /// + ///The value as a Uri. + ///+ /// There are no more values to read or the value could not be read as a string. + /// + ///+ /// The TNEF stream is truncated and the value could not be read. + /// + internal Uri ReadValueAsUri () + { + var value = ReadValueAsString (); + + if (Uri.IsWellFormedUriString (value, UriKind.Absolute)) + return new Uri (value, UriKind.Absolute); + + if (Uri.IsWellFormedUriString (value, UriKind.Relative)) + return new Uri (value, UriKind.Relative); + + return null; + } + + ///+ /// Serves as a hash function for a ///object. /// - /// Serves as a hash function for a ///object. + /// Serves as a hash function for a object. /// A hash code for this instance that is suitable for use in hashing algorithms /// and data structures such as a hash table. @@ -1531,14 +1549,14 @@ public override int GetHashCode () } ///- /// Determines whether the specified ///is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// Determines whether the specified - /// Theis equal to the current . + /// Determines whether the specified is equal to the current . /// to compare with the current . + /// The to compare with the current . /// + /// true if the specifiedis equal to the current - /// ; otherwise, false .; otherwise, false .// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -379,6 +379,38 @@ public struct TnefPropertyTag /// public static readonly TnefPropertyTag AttachLongPathnameW = new TnefPropertyTag (TnefPropertyId.AttachLongPathname, TnefPropertyType.Unicode); + /// + /// The MAPI property PR_ATTACHMENT_CONTACTPHOTO. + /// + ///+ /// The MAPI property PR_ATTACHMENT_CONTACTPHOTO. + /// + public static readonly TnefPropertyTag AttachmentContactPhoto = new TnefPropertyTag (TnefPropertyId.AttachmentContactPhoto, TnefPropertyType.Boolean); + + ///+ /// The MAPI property PR_ATTACHMENT_FLAGS. + /// + ///+ /// The MAPI property PR_ATTACHMENT_FLAGS. + /// + public static readonly TnefPropertyTag AttachmentFlags = new TnefPropertyTag (TnefPropertyId.AttachmentFlags, TnefPropertyType.Long); + + ///+ /// The MAPI property PR_ATTACHMENT_HIDDEN. + /// + ///+ /// The MAPI property PR_ATTACHMENT_HIDDEN. + /// + public static readonly TnefPropertyTag AttachmentHidden = new TnefPropertyTag (TnefPropertyId.AttachmentHidden, TnefPropertyType.Boolean); + + ///+ /// The MAPI property PR_ATTACHMENT_LINKID. + /// + ///+ /// The MAPI property PR_ATTACHMENT_LINKID. + /// + public static readonly TnefPropertyTag AttachmentLinkId = new TnefPropertyTag (TnefPropertyId.AttachmentLinkId, TnefPropertyType.Long); + ////// The MAPI property PR_ATTACHMENT_X400_PARAMETERS. /// @@ -2483,6 +2515,22 @@ public struct TnefPropertyTag /// public static readonly TnefPropertyTag LastModificationTime = new TnefPropertyTag (TnefPropertyId.LastModificationTime, TnefPropertyType.SysTime); + ///+ /// The MAPI property PR_LAST_MODIFIER_NAME. + /// + ///+ /// The MAPI property PR_LAST_MODIFIER_NAME. + /// + public static readonly TnefPropertyTag LastModifierNameA = new TnefPropertyTag (TnefPropertyId.LastModifierName, TnefPropertyType.String8); + + ///+ /// The MAPI property PR_LAST_MODIFIER_NAME. + /// + ///+ /// The MAPI property PR_LAST_MODIFIER_NAME. + /// + public static readonly TnefPropertyTag LastModifierNameW = new TnefPropertyTag (TnefPropertyId.LastModifierName, TnefPropertyType.Unicode); + ////// The MAPI property PR_LATEST_DELIVERY_TIME. /// @@ -4308,6 +4356,22 @@ public struct TnefPropertyTag /// public static readonly TnefPropertyTag RecipientCertificate = new TnefPropertyTag (TnefPropertyId.RecipientCertificate, TnefPropertyType.Binary); + ///+ /// The MAPI property PR_RECIPIENT_DISPLAY_NAME. + /// + ///+ /// The MAPI property PR_RECIPIENT_DISPLAY_NAME. + /// + public static readonly TnefPropertyTag RecipientDisplayNameA = new TnefPropertyTag (TnefPropertyId.RecipientDisplayName, TnefPropertyType.String8); + + ///+ /// The MAPI property PR_RECIPIENT_DISPLAY_NAME. + /// + ///+ /// The MAPI property PR_RECIPIENT_DISPLAY_NAME. + /// + public static readonly TnefPropertyTag RecipientDisplayNameW = new TnefPropertyTag (TnefPropertyId.RecipientDisplayName, TnefPropertyType.Unicode); + ////// The MAPI property PR_RECIPIENT_NUMBER_FOR_ADVICE. /// @@ -5556,7 +5620,7 @@ public TnefPropertyType ValueTnefType { } ///- /// Initializes a new instance of the ///struct. + /// Initialize a new instance of the struct. /// /// Creates a new based on a 32-bit integer tag as read from @@ -5576,7 +5640,7 @@ public TnefPropertyTag (int tag) } /// - /// Initializes a new instance of the ///struct. + /// Initialize a new instance of the struct. /// /// Creates a new based on a @@ -5617,10 +5681,10 @@ public static implicit operator int (TnefPropertyTag tag) } /// - /// Serves as a hash function for a ///object. + /// Serves as a hash function for a object. /// - /// Serves as a hash function for a ///object. + /// Serves as a hash function for a object. /// A hash code for this instance that is suitable for use in hashing algorithms /// and data structures such as a hash table. @@ -5630,14 +5694,14 @@ public override int GetHashCode () } ///- /// Determines whether the specified ///is equal to the current . + /// Determines whether the specified is equal to the current . /// - /// Determines whether the specified - /// Theis equal to the current . + /// Determines whether the specified is equal to the current . /// to compare with the current . + /// The to compare with the current . /// + /// true if the specifiedis equal to the current - /// ; otherwise, false .; otherwise, false .- /// Returns a ///that represents the current . + /// Returns a that represents the current . /// - /// Returns a - ///that represents the current . + /// Returns a that represents the current . /// A + ///that represents the current . A public override string ToString () { return string.Format ("{0} ({1})", Id, ValueTnefType); diff --git a/MimeKit/Tnef/TnefPropertyType.cs b/MimeKit/Tnef/TnefPropertyType.cs index 175ea65e82..b0270a6538 100644 --- a/MimeKit/Tnef/TnefPropertyType.cs +++ b/MimeKit/Tnef/TnefPropertyType.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastthat represents the current . // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Tnef/TnefReader.cs b/MimeKit/Tnef/TnefReader.cs index 6644a2d2cf..60717d2b26 100644 --- a/MimeKit/Tnef/TnefReader.cs +++ b/MimeKit/Tnef/TnefReader.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,7 @@ using System; using System.IO; using System.Text; - -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif +using System.Globalization; namespace MimeKit.Tnef { /// @@ -162,15 +159,10 @@ private set { try { var encoding = Encoding.GetEncoding (value); codepage = encoding.CodePage; - } catch (ArgumentOutOfRangeException ex) { - ComplianceStatus |= TnefComplianceStatus.InvalidMessageCodepage; - if (ComplianceMode == TnefComplianceMode.Strict) - throw new TnefException (TnefComplianceStatus.InvalidMessageCodepage, string.Format ("Invalid message codepage: {0}", value), ex); - codepage = 1252; - } catch (NotSupportedException ex) { + } catch (Exception ex) { ComplianceStatus |= TnefComplianceStatus.InvalidMessageCodepage; if (ComplianceMode == TnefComplianceMode.Strict) - throw new TnefException (TnefComplianceStatus.InvalidMessageCodepage, string.Format ("Unsupported message codepage: {0}", value), ex); + throw new TnefException (TnefComplianceStatus.InvalidMessageCodepage, string.Format (CultureInfo.InvariantCulture, "Invalid message codepage: {0}", value), ex); codepage = 1252; } } @@ -211,7 +203,7 @@ private set { if (value != 0x00010000) { ComplianceStatus |= TnefComplianceStatus.InvalidTnefVersion; if (ComplianceMode == TnefComplianceMode.Strict) - throw new TnefException (TnefComplianceStatus.InvalidTnefVersion, string.Format ("Invalid TNEF version: {0}", value)); + throw new TnefException (TnefComplianceStatus.InvalidTnefVersion, string.Format (CultureInfo.InvariantCulture, "Invalid TNEF version: {0}", value)); } version = value; @@ -219,7 +211,7 @@ private set { } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// When reading a TNEF stream using the mode, @@ -268,7 +260,7 @@ public TnefReader (Stream inputStream, int defaultMessageCodepage, TnefComplianc } /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new class TnefReaderStream : Stream { - readonly int valueEndOffset; + readonly int valueEndOffset, dataEndOffset; readonly TnefReader reader; bool disposed; ///for the specified input stream. @@ -283,19 +275,27 @@ public TnefReader (Stream inputStream) : this (inputStream, 0, TnefComplianceMod /// /// Releases unmanaged resources and performs other cleanup operations before the - /// ///is reclaimed by garbage collection. + /// is reclaimed by garbage collection. /// /// Releases unmanaged resources and performs other cleanup operations before the - /// ~TnefReader () { Dispose (false); } + void CheckDisposed () + { + if (closed) + throw new ObjectDisposedException ("TnefReader"); + } + internal int ReadAhead (int atleast) { + CheckDisposed (); + int left = inputEnd - inputIndex; if (left >= atleast || eos) @@ -384,16 +384,9 @@ void DecodeHeader () AttachmentKey = ReadInt16 (); } catch (EndOfStreamException) { SetComplianceError (TnefComplianceStatus.StreamTruncated); - throw; } } - void CheckDisposed () - { - if (closed) - throw new ObjectDisposedException ("TnefReader"); - } - void CheckAttributeLevel () { switch (AttributeLevel) { @@ -455,32 +448,6 @@ void CheckAttributeTag () } } - static unsafe void Load32BitValue (byte *dest, byte[] src, int startIndex) - { - if (BitConverter.IsLittleEndian) { - dest[0] = src[startIndex]; - dest[1] = src[startIndex + 1]; - dest[2] = src[startIndex + 2]; - dest[3] = src[startIndex + 3]; - } else { - dest[0] = src[startIndex + 3]; - dest[1] = src[startIndex + 2]; - dest[2] = src[startIndex + 1]; - dest[3] = src[startIndex]; - } - } - - static unsafe void Load64BitValue (byte *dest, byte[] src, int startIndex) - { - if (BitConverter.IsLittleEndian) { - for (int i = 0; i < 8; i++) - dest[i] = src[startIndex + i]; - } else { - for (int i = 0; i < 8; i++) - dest[i] = src[startIndex + (7 - i)]; - } - } - internal byte ReadByte () { if (ReadAhead (1) < 1) @@ -521,50 +488,47 @@ internal int PeekInt32 () (input[inputIndex + 2] << 16) | (input[inputIndex + 3] << 24); } - internal unsafe long ReadInt64 () + internal long ReadInt64 () { if (ReadAhead (8) < 8) throw new EndOfStreamException (); - long value; - - Load32BitValue ((byte *) &value, input, inputIndex); UpdateChecksum (input, inputIndex, 8); - inputIndex += 8; - return value; + return (long) input[inputIndex++] | ((long) input[inputIndex++] << 8) | + ((long) input[inputIndex++] << 16) | ((long) input[inputIndex++] << 24) | + ((long) input[inputIndex++] << 32) | ((long) input[inputIndex++] << 40) | + ((long) input[inputIndex++] << 48) | ((long) input[inputIndex++] << 56); } - internal unsafe float ReadSingle () + static unsafe float Int32BitsToSingle (int value) { - if (ReadAhead (4) < 4) - throw new EndOfStreamException (); - - float value; + return *((float*) &value); + } - Load32BitValue ((byte *) &value, input, inputIndex); - UpdateChecksum (input, inputIndex, 4); - inputIndex += 4; + internal float ReadSingle () + { + var value = ReadInt32 (); - return value; + return Int32BitsToSingle (value); } - internal unsafe double ReadDouble () + static unsafe double Int64BitsToDouble (long value) { - if (ReadAhead (8) < 8) - throw new EndOfStreamException (); - - double value; + return *((double*) &value); + } - Load64BitValue ((byte *) &value, input, inputIndex); - UpdateChecksum (input, inputIndex, 8); - inputIndex += 8; + internal double ReadDouble () + { + var value = ReadInt64 (); - return value; + return Int64BitsToDouble (value); } internal bool Seek (int offset) { + CheckDisposed (); + int left = offset - StreamOffset; if (left <= 0) @@ -625,6 +589,8 @@ bool SkipAttributeRawValue () /// public bool ReadNextAttribute () { + CheckDisposed (); + if (AttributeRawValueStreamOffset != 0 && !SkipAttributeRawValue ()) return false; @@ -648,8 +614,10 @@ public bool ReadNextAttribute () CheckAttributeTag (); - if (AttributeRawValueLength < 0) + if (AttributeRawValueLength < 0) { SetComplianceError (TnefComplianceStatus.InvalidAttributeLength); + return false; + } try { TnefPropertyReader.Load (); @@ -705,6 +673,8 @@ public int ReadAttributeRawValue (byte[] buffer, int offset, int count) if (count < 0 || count > (buffer.Length - offset)) throw new ArgumentOutOfRangeException (nameof (count)); + CheckDisposed (); + int dataEndOffset = AttributeRawValueStreamOffset + AttributeRawValueLength; int dataLeft = dataEndOffset - StreamOffset; @@ -766,16 +736,17 @@ public void Close () ///is reclaimed by garbage collection. + /// is reclaimed by garbage collection. /// false to release only the unmanaged resources. protected virtual void Dispose (bool disposing) { - InputStream.Dispose (); + if (disposing && !closed) + InputStream.Dispose (); } ///- /// Releases all resource used by the - ///object. + /// Releases all resource used by the object. /// Call + ///when you are finished using the . The - /// method leaves the in an unusable state. After calling - /// , you must release all references to the so the garbage - /// collector can reclaim the memory that the was occupying. Call public void Dispose () { Dispose (true); diff --git a/MimeKit/Tnef/TnefReaderStream.cs b/MimeKit/Tnef/TnefReaderStream.cs index 22f27c7687..006f3c4f3b 100644 --- a/MimeKit/Tnef/TnefReaderStream.cs +++ b/MimeKit/Tnef/TnefReaderStream.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastwhen you are finished using the . The + /// method leaves the in an unusable state. After calling + /// , you must release all references to the so the garbage + /// collector can reclaim the memory that the was occupying. // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -36,21 +36,23 @@ namespace MimeKit.Tnef { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a stream for reading a raw value from the /// The. /// . - /// The end offset. - public TnefReaderStream (TnefReader tnefReader, int endOffset) + /// The end offset of the data. + /// The end offset of the container value. + public TnefReaderStream (TnefReader tnefReader, int dataEndOffset, int valueEndOffset) { - valueEndOffset = endOffset; + this.valueEndOffset = valueEndOffset; + this.dataEndOffset = dataEndOffset; reader = tnefReader; } @@ -168,10 +170,21 @@ public override int Read (byte[] buffer, int offset, int count) CheckDisposed (); - int valueLeft = valueEndOffset - reader.StreamOffset; - int n = Math.Min (valueLeft, count); + int dataLeft = dataEndOffset - reader.StreamOffset; + int n = Math.Min (dataLeft, count); - return n > 0 ? reader.ReadAttributeRawValue (buffer, offset, n) : 0; + int nread = n > 0 ? reader.ReadAttributeRawValue (buffer, offset, n) : 0; + + dataLeft -= nread; + + if (dataLeft == 0 && valueEndOffset > reader.StreamOffset) { + int valueLeft = valueEndOffset - reader.StreamOffset; + var buf = new byte[valueLeft]; + + reader.ReadAttributeRawValue (buf, 0, valueLeft); + } + + return nread; } /// diff --git a/MimeKit/Utils/BufferPool.cs b/MimeKit/Utils/BufferPool.cs new file mode 100644 index 0000000000..eb831583ad --- /dev/null +++ b/MimeKit/Utils/BufferPool.cs @@ -0,0 +1,180 @@ +// +// BufferPool.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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. +// + +// Code based on Microsoft's System.Buffer.DefaultArrayPoolBucket class located at +// https://github.com/dotnet/corefx/blob/master/src/System.Buffers/src/System/Buffers/DefaultArrayPoolBucket.cs + +using System; +using System.Threading; +using System.Diagnostics; + +namespace MimeKit.Utils +{ + /// + /// Provides a pool of reusable buffers. + /// + ///+ /// + class BufferPool + { + readonly byte[][] buffers; + SpinLock spinLock; + int index; + + ///+ /// Renting and returning buffers with a + ///can increase performance + /// in situations where buffers are created and destroyed frequently, resulting in significant + /// memory pressure on the garbage collector. + /// + /// This class is thread-safe. All members may be used by multiple threads concurrently. + /// + ///+ /// Initialize a new instance of the + ///class. + /// + /// Creates a new buffer pool. + /// + /// The buffer size. + /// The maximum number of buffers that should be retained by the pool. + ///+ /// + public BufferPool (int bufferSize, int maxBufferCount) + { + if (bufferSize < 1) + throw new ArgumentOutOfRangeException (nameof (bufferSize)); + + if (maxBufferCount < 1) + throw new ArgumentOutOfRangeException (nameof (maxBufferCount)); + + buffers = new byte[maxBufferCount][]; + MaxBufferCount = maxBufferCount; + BufferSize = bufferSize; + + spinLock = new SpinLock (Debugger.IsAttached); + } + + ///+ /// is less than 1 .-or- + ///+ /// is less than 1 .+ /// Get the size of the buffers returned and/or retained by the pool. + /// + ///+ /// Gets the size of the buffers returned and/or retained by the pool. + /// + ///The size of the buffer. + public int BufferSize { + get; private set; + } + + ///+ /// Get the maximum number of buffers that the pool should retain. + /// + ///+ /// Gets the maximum number of buffers that the pool should retain. + /// + ///The max buffer count. + public int MaxBufferCount { + get; private set; + } + + ///+ /// Rent a buffer from the pool. + /// + ///+ /// Returns a buffer from the pool. This buffer should later be returned back to the pool using + /// + ///when the caller is finished using it so that it may be reused + /// in subsequent uses of . + /// The rented buffer. + ///true if the buffer should be cleared; otherwise, . + public byte[] Rent (bool clear = false) + { + byte[] buffer = null; + bool locked = false; + + try { + spinLock.Enter (ref locked); + + if (index < buffers.Length) { + buffer = buffers[index]; + buffers[index] = null; + index++; + } + } finally { + if (locked) + spinLock.Exit (false); + } + + if (buffer == null) + buffer = new byte[BufferSize]; + else if (clear) + Array.Clear (buffer, 0, BufferSize); + + return buffer; + } + + ///false + /// Return the specified buffer to the pool. + /// + ///+ /// Once a buffer has been returned to the pool, the caller gives up all ownership of the buffer + /// and must not use it. The reference returned from a given call to + /// The buffer. + ///must + /// only be returned via once. The default + /// may hold onto the returned buffer in order to rent it again, or it may release the returned buffer + /// if it is determined that the pool already contains the maximum number of buffers as specified by + /// . + /// + /// + ///is null . + ///+ /// The size of the + public void Return (byte[] buffer) + { + bool locked = false; + + if (buffer == null) + throw new ArgumentNullException (nameof (buffer)); + + if (buffer.Length != BufferSize) + throw new ArgumentException ("The size of the buffer does not match the size used by the pool.", nameof (buffer)); + + try { + spinLock.Enter (ref locked); + + if (index > 0) + buffers[--index] = buffer; + } finally { + if (locked) + spinLock.Exit (false); + } + } + } +} diff --git a/MimeKit/Utils/ByteExtensions.cs b/MimeKit/Utils/ByteExtensions.cs index ffceaad8f0..8edd7a1bb1 100644 --- a/MimeKit/Utils/ByteExtensions.cs +++ b/MimeKit/Utils/ByteExtensions.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfastdoes not match . + /// // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -44,6 +44,7 @@ enum CharType : ushort { IsTokenSpecial = (1 << 11), IsWhitespace = (1 << 12), IsXDigit = (1 << 13), + IsPhraseAtom = (1 << 14), IsAsciiAtom = IsAscii | IsAtom, } @@ -56,8 +57,8 @@ static class ByteExtensions const string DomainSpecials = "[]\\\r \t"; // not allowed in domains const string EncodedWordSpecials = "()<>@,;:\"/[]?.=_"; // rfc2047 5.1 const string EncodedPhraseSpecials = "!*+-/=_"; // rfc2047 5.3 - const string Specials = "()<>@,;:\\\".[]"; - internal const string TokenSpecials = "()<>@,;:\\\"/[]?="; + const string Specials = "()<>[]:;@\\,.\""; // rfc5322 3.2.3 + internal const string TokenSpecials = "()<>@,;:\\\"/[]?="; // rfc2045 5.1 const string Whitespace = " \t\r\n"; static readonly CharType[] table = new CharType[256]; @@ -110,7 +111,7 @@ static ByteExtensions () if ((i >= 33 && i <= 60) || (i >= 62 && i <= 126) || i == 32) table[i] |= (CharType.IsQuotedPrintableSafe | CharType.IsEncodedWordSafe); if ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z')) - table[i] |= CharType.IsEncodedPhraseSafe | CharType.IsAtom; + table[i] |= CharType.IsEncodedPhraseSafe | CharType.IsAtom | CharType.IsPhraseAtom; if ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'f') || (i >= 'A' && i <= 'F')) table[i] |= CharType.IsXDigit; @@ -119,7 +120,7 @@ static ByteExtensions () if (i == 127) table[i] |= CharType.IsAscii; else - table[i] |= CharType.IsAtom; + table[i] |= CharType.IsAtom | CharType.IsPhraseAtom; table[i] |= CharType.IsControl; } @@ -129,14 +130,18 @@ static ByteExtensions () table[' '] |= CharType.IsSpace | CharType.IsBlank; SetFlags (Whitespace, CharType.IsWhitespace, CharType.None, false); - SetFlags (AtomSafeCharacters, CharType.IsAtom, CharType.None, false); + SetFlags (AtomSafeCharacters, CharType.IsAtom | CharType.IsPhraseAtom, CharType.None, false); SetFlags (TokenSpecials, CharType.IsTokenSpecial, CharType.IsControl, false); SetFlags (Specials, CharType.IsSpecial, CharType.None, false); SetFlags (DomainSpecials, CharType.IsDomainSafe, CharType.None, true); - RemoveFlags (Specials, CharType.IsAtom); + RemoveFlags (Specials, CharType.IsAtom | CharType.IsPhraseAtom); RemoveFlags (EncodedWordSpecials, CharType.IsEncodedWordSafe); RemoveFlags (AttributeSpecials + TokenSpecials, CharType.IsAttrChar); SetFlags (EncodedPhraseSpecials, CharType.IsEncodedPhraseSafe, CharType.None, false); + + // Note: Allow '[' and ']' in the display-name of a mailbox address + table['['] |= CharType.IsPhraseAtom; + table[']'] |= CharType.IsPhraseAtom; } //public static bool IsAscii (this byte c) @@ -149,6 +154,11 @@ public static bool IsAsciiAtom (this byte c) return (table[c] & CharType.IsAsciiAtom) == CharType.IsAsciiAtom; } + public static bool IsPhraseAtom (this byte c) + { + return (table[c] & CharType.IsPhraseAtom) != 0; + } + public static bool IsAtom (this byte c) { return (table[c] & CharType.IsAtom) != 0; diff --git a/MimeKit/Utils/CharsetUtils.cs b/MimeKit/Utils/CharsetUtils.cs index bb08ad15a0..408179b164 100644 --- a/MimeKit/Utils/CharsetUtils.cs +++ b/MimeKit/Utils/CharsetUtils.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,23 +25,10 @@ // using System; +using System.IO; using System.Text; using System.Collections.Generic; -#if PORTABLE -using EncoderReplacementFallback = Portable.Text.EncoderReplacementFallback; -using DecoderReplacementFallback = Portable.Text.DecoderReplacementFallback; -using EncoderExceptionFallback = Portable.Text.EncoderExceptionFallback; -using DecoderExceptionFallback = Portable.Text.DecoderExceptionFallback; -using EncoderFallbackException = Portable.Text.EncoderFallbackException; -using DecoderFallbackException = Portable.Text.DecoderFallbackException; -using DecoderFallbackBuffer = Portable.Text.DecoderFallbackBuffer; -using DecoderFallback = Portable.Text.DecoderFallback; -using Encoding = Portable.Text.Encoding; -using Encoder = Portable.Text.Encoder; -using Decoder = Portable.Text.Decoder; -#endif - namespace MimeKit.Utils { public static class External { @@ -599,5 +586,28 @@ public static string ConvertToUnicode (Encoding encoding, byte[] buffer, int sta return new string (ConvertToUnicode (encoding, buffer, startIndex, length, out count), 0, count); } + + public static bool TryGetBomEncoding (byte[] buffer, int length, out Encoding encoding) + { + if (length >= 2 && buffer[0] == 0xFF && buffer[1] == 0xFE) + encoding = Encoding.Unicode; // UTF-16LE + else if (length >= 2 && buffer[0] == 0xFE && buffer[1] == 0xFF) + encoding = Encoding.BigEndianUnicode; // UTF-16BE + else if (length >= 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) + encoding = UTF8; // UTF-8 + else + encoding = null; + + return encoding != null; + } + + public static bool TryGetBomEncoding (Stream stream, out Encoding encoding) + { + var bom = new byte[3]; + + int n = stream.Read (bom, 0, bom.Length); + + return TryGetBomEncoding (bom, n, out encoding); + } } } diff --git a/MimeKit/Utils/Crc32.cs b/MimeKit/Utils/Crc32.cs index 55aeed495e..63730f642f 100644 --- a/MimeKit/Utils/Crc32.cs +++ b/MimeKit/Utils/Crc32.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -75,7 +75,7 @@ class Crc32 int crc; /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new CRC32 context. diff --git a/MimeKit/Utils/DateUtils.cs b/MimeKit/Utils/DateUtils.cs index a1f8d34aff..931fb0885f 100644 --- a/MimeKit/Utils/DateUtils.cs +++ b/MimeKit/Utils/DateUtils.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,12 +26,9 @@ using System; using System.Text; +using System.Globalization; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - namespace MimeKit.Utils { [Flags] enum DateTokenFlags : byte @@ -119,7 +116,7 @@ public static class DateUtils static DateUtils () { - timezones = new Dictionary { + timezones = new Dictionary (StringComparer.Ordinal) { { "UT", 0 }, { "UTC", 0 }, { "GMT", 0 }, { "EDT", -400 }, { "EST", -500 }, { "CDT", -500 }, { "CST", -600 }, @@ -177,10 +174,7 @@ static bool TryGetWeekday (DateToken token, byte[] text, out DayOfWeek weekday) if (!token.IsWeekday || token.Length < 3) return false; - var name = Encoding.ASCII.GetString (text, token.StartIndex, token.Length); - - if (name.Length > 3) - name = name.Substring (0, 3); + var name = Encoding.ASCII.GetString (text, token.StartIndex, 3); for (int day = 0; day < WeekDays.Length; day++) { if (WeekDays[day].Equals (name, StringComparison.OrdinalIgnoreCase)) { @@ -218,10 +212,7 @@ static bool TryGetMonth (DateToken token, byte[] text, out int month) if (!token.IsMonth || token.Length < 3) return false; - var name = Encoding.ASCII.GetString (text, token.StartIndex, token.Length); - - if (name.Length > 3) - name = name.Substring (0, 3); + var name = Encoding.ASCII.GetString (text, token.StartIndex, 3); for (int i = 0; i < Months.Length; i++) { if (Months[i].Equals (name, StringComparison.OrdinalIgnoreCase)) { @@ -530,7 +521,7 @@ static bool TryParseUnknownDateFormat (IList tokens, byte[] text, out } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses an rfc822 date and time from the supplied buffer starting at the given index @@ -573,7 +564,7 @@ public static bool TryParse (byte[] buffer, int startIndex, int length, out Date } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses an rfc822 date and time from the supplied buffer starting at the specified index. @@ -611,7 +602,7 @@ public static bool TryParse (byte[] buffer, int startIndex, out DateTimeOffset d } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses an rfc822 date and time from the specified buffer. @@ -641,7 +632,7 @@ public static bool TryParse (byte[] buffer, out DateTimeOffset date) } /// - /// Tries to parse the given input buffer into a new ///instance. + /// Try to parse the given input buffer into a new instance. /// /// Parses an rfc822 date and time from the specified text. @@ -725,7 +716,7 @@ internal static DateTime Parse (string text, string format) /// The date. public static string FormatDate (DateTimeOffset date) { - return string.Format ("{0}, {1:00} {2} {3:0000} {4:00}:{5:00}:{6:00} {7:+00;-00}{8:00}", + return string.Format (CultureInfo.InvariantCulture, "{0}, {1:00} {2} {3:0000} {4:00}:{5:00}:{6:00} {7:+00;-00}{8:00}", WeekDays[(int) date.DayOfWeek], date.Day, Months[date.Month - 1], date.Year, date.Hour, date.Minute, date.Second, date.Offset.Hours, date.Offset.Minutes); } diff --git a/MimeKit/Utils/MimeUtils.cs b/MimeKit/Utils/MimeUtils.cs index 8610955f56..f67fe14d47 100644 --- a/MimeKit/Utils/MimeUtils.cs +++ b/MimeKit/Utils/MimeUtils.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast public static class MimeUtils { -#if PORTABLE - static readonly Random random = new Random ((int) DateTime.Now.Ticks); -#endif const string base36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +#if !NETSTANDARD1_3 && !NETSTANDARD1_6 + static string DefaultHostName = null; +#endif ///// -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,11 +27,8 @@ using System; using System.Text; using System.Collections.Generic; - -#if !PORTABLE using System.Net.NetworkInformation; using System.Security.Cryptography; -#endif namespace MimeKit.Utils { /// @@ -42,10 +39,10 @@ namespace MimeKit.Utils { /// /// A string comparer that performs a case-insensitive ordinal string comparison. @@ -57,14 +54,8 @@ public static class MimeUtils internal static void GetRandomBytes (byte[] buffer) { -#if !PORTABLE using (var random = RandomNumberGenerator.Create ()) random.GetBytes (buffer); -#else - lock (random) { - random.NextBytes (buffer); - } -#endif } /// @@ -111,7 +102,7 @@ public static string GenerateMessageId (string domain) value /= 36; } while (value != 0); - id.Append ('@').Append (domain); + id.Append ('@').Append (ParseUtils.IdnEncode (domain)); return id.ToString (); } @@ -125,12 +116,16 @@ public static string GenerateMessageId (string domain) /// The message identifier. public static string GenerateMessageId () { -#if PORTABLE || NETSTANDARD +#if NETSTANDARD1_3 || NETSTANDARD1_6 return GenerateMessageId ("localhost.localdomain"); #else - var properties = IPGlobalProperties.GetIPGlobalProperties (); + if (DefaultHostName == null) { + var properties = IPGlobalProperties.GetIPGlobalProperties (); - return GenerateMessageId (properties.HostName); + DefaultHostName = properties.HostName; + } + + return GenerateMessageId (DefaultHostName); #endif } @@ -158,10 +153,8 @@ public static IEnumerableEnumerateReferences (byte[] buffer, int startI { ParseUtils.ValidateArguments (buffer, startIndex, length); - byte[] sentinels = { (byte) '>' }; int endIndex = startIndex + length; int index = startIndex; - string msgid; do { if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false)) @@ -171,74 +164,8 @@ public static IEnumerable EnumerateReferences (byte[] buffer, int startI break; if (buffer[index] == '<') { - // skip over the '<' - index++; - - if (index >= endIndex) - break; - - string localpart; - if (!InternetAddress.TryParseLocalPart (buffer, ref index, endIndex, false, out localpart)) - continue; - - if (index >= endIndex) - break; - - if (buffer[index] == (byte) '>') { - // The msgid token did not contain an @domain. Technically this is illegal, but for the - // sake of maximum compatibility, I guess we have no choice but to accept it... - index++; - - yield return localpart; - continue; - } - - if (buffer[index] != (byte) '@') { - // who the hell knows what we have here... ignore it and continue on? - continue; - } - - // skip over the '@' - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false)) - break; - - if (index >= endIndex) - break; - - if (buffer[index] == (byte) '>') { - // The msgid token was in the form " ". Technically this is illegal, but for - // the sake of maximum compatibility, I guess we have no choice but to accept it... - // https://github.com/jstedfast/MimeKit/issues/102 - index++; - - yield return localpart + "@"; - continue; - } - - string domain; - if (!ParseUtils.TryParseDomain (buffer, ref index, endIndex, sentinels, false, out domain)) - continue; - - msgid = localpart + "@" + domain; - - // Note: some Message-Id's are broken and in the form " " - // https://github.com/jstedfast/MailKit/issues/138 - while (index < endIndex && buffer[index] == (byte) '@') { - int saved = index; - - index++; - - if (!ParseUtils.TryParseDomain (buffer, ref index, endIndex, sentinels, false, out domain)) { - index = saved; - break; - } - - msgid += "@" + domain; - } - - yield return msgid; + if (ParseUtils.TryParseMsgId (buffer, ref index, endIndex, true, false, out string msgid)) + yield return msgid; } else if (!ParseUtils.SkipWord (buffer, ref index, endIndex, false)) { index++; } @@ -291,75 +218,11 @@ public static string ParseMessageId (byte[] buffer, int startIndex, int length) { ParseUtils.ValidateArguments (buffer, startIndex, length); - byte[] sentinels = { (byte) '>' }; int endIndex = startIndex + length; int index = startIndex; string msgid; - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false)) - return null; - - if (index >= endIndex) - return null; - - if (buffer[index] == '<') { - // skip over the '<' - index++; - - if (index >= endIndex) - return null; - } - - string localpart; - if (!InternetAddress.TryParseLocalPart (buffer, ref index, endIndex, false, out localpart)) - return null; - - if (index >= endIndex) - return null; - - if (buffer[index] == (byte) '>') { - // The msgid token did not contain an @domain. Technically this is illegal, but for the - // sake of maximum compatibility, I guess we have no choice but to accept it... - return localpart; - } - - if (buffer[index] != (byte) '@') { - // who the hell knows what we have here... - return null; - } - - // skip over the '@' - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false)) - return null; - - if (index >= endIndex) - return null; - - if (buffer[index] == (byte) '>') { - // The msgid token was in the form " ". Technically this is illegal, but for - // the sake of maximum compatibility, I guess we have no choice but to accept it... - // https://github.com/jstedfast/MimeKit/issues/102 - return localpart + "@"; - } - - string domain; - if (!ParseUtils.TryParseDomain (buffer, ref index, endIndex, sentinels, false, out domain)) - return null; - - msgid = localpart + "@" + domain; - - // Note: some Message-Id's are broken and in the form " " - // https://github.com/jstedfast/MailKit/issues/138 - while (index < endIndex && buffer[index] == (byte) '@') { - index++; - - if (!ParseUtils.TryParseDomain (buffer, ref index, endIndex, sentinels, false, out domain)) - break; - - msgid += "@" + domain; - } + ParseUtils.TryParseMsgId (buffer, ref index, endIndex, false, false, out msgid); return msgid; } @@ -386,7 +249,7 @@ public static string ParseMessageId (string text) } /// - /// Tries to parse a version from a header such as Mime-Version. + /// Try to parse a version from a header such as Mime-Version. /// ////// Parses a MIME version string from the supplied buffer starting at the given index @@ -445,7 +308,7 @@ public static bool TryParse (byte[] buffer, int startIndex, int length, out Vers } /// - /// Tries to parse a version from a header such as Mime-Version. + /// Try to parse a version from a header such as Mime-Version. /// ////// Parses a MIME version string from the specified text. @@ -467,7 +330,7 @@ public static bool TryParse (string text, out Version version) } /// - /// Tries to parse the value of a Content-Transfer-Encoding header. + /// Try to parse the value of a Content-Transfer-Encoding header. /// ////// Parses a Content-Transfer-Encoding header value. @@ -507,6 +370,7 @@ public static bool TryParse (string text, out ContentEncoding encoding) case "quoted-printable": encoding = ContentEncoding.QuotedPrintable; break; case "x-uuencode": encoding = ContentEncoding.UUEncode; break; case "uuencode": encoding = ContentEncoding.UUEncode; break; + case "x-uue": encoding = ContentEncoding.UUEncode; break; default: encoding = ContentEncoding.Default; break; } @@ -564,7 +428,7 @@ public static string Unquote (string text) if (index == -1) return text; - var builder = new StringBuilder (); + var builder = new StringBuilder (text.Length); bool escaped = false; bool quoted = false; diff --git a/MimeKit/Utils/OptimizedOrdinalComparer.cs b/MimeKit/Utils/OptimizedOrdinalComparer.cs index dd0a69fc94..b5ba6d3047 100644 --- a/MimeKit/Utils/OptimizedOrdinalComparer.cs +++ b/MimeKit/Utils/OptimizedOrdinalComparer.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace MimeKit.Utils { /// @@ -34,10 +35,10 @@ namespace MimeKit.Utils { /// /// An optimized version of - class OptimizedOrdinalIgnoreCaseComparer : IEqualityComparerStringComparer.OrdinalIgnoreCase . ///+ sealed class OptimizedOrdinalIgnoreCaseComparer : IEqualityComparer { /// - /// Initializes a new instance of the ///class. + /// Initialize a new instance of the class. /// /// Creates a new . @@ -46,9 +47,11 @@ public OptimizedOrdinalIgnoreCaseComparer () { } + [MethodImpl (MethodImplOptions.AggressiveInlining)] static int ToUpper (int c) { - if (c >= 0x61 && c <= 0x7A) + // check if the char is within the lowercase range + if ((uint) (c - 'a') <= 'z' - 'a') return c - 0x20; return c; @@ -85,7 +88,8 @@ public bool Equals (string x, string y) if (x.Length != y.Length) return false; - for (int i = 0; i < x.Length; i++) { + int length = x.Length; + for (int i = 0; i < length; i++) { if (ToUpper (x[i]) != ToUpper (y[i])) return false; } diff --git a/MimeKit/Utils/PackedByteArray.cs b/MimeKit/Utils/PackedByteArray.cs index 8f7be02e7d..5ea52ac4b8 100644 --- a/MimeKit/Utils/PackedByteArray.cs +++ b/MimeKit/Utils/PackedByteArray.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/Utils/ParseUtils.cs b/MimeKit/Utils/ParseUtils.cs index 25020e74ef..3ee7a5ab08 100644 --- a/MimeKit/Utils/ParseUtils.cs +++ b/MimeKit/Utils/ParseUtils.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,28 +26,12 @@ using System; using System.Text; -#if !PORTABLE using System.Globalization; -#else -using EncoderReplacementFallback = Portable.Text.EncoderReplacementFallback; -using DecoderReplacementFallback = Portable.Text.DecoderReplacementFallback; -using EncoderExceptionFallback = Portable.Text.EncoderExceptionFallback; -using DecoderExceptionFallback = Portable.Text.DecoderExceptionFallback; -using EncoderFallbackException = Portable.Text.EncoderFallbackException; -using DecoderFallbackException = Portable.Text.DecoderFallbackException; -using DecoderFallbackBuffer = Portable.Text.DecoderFallbackBuffer; -using DecoderFallback = Portable.Text.DecoderFallback; -using Encoding = Portable.Text.Encoding; -using Encoder = Portable.Text.Encoder; -using Decoder = Portable.Text.Decoder; -#endif namespace MimeKit.Utils { static class ParseUtils { -#if !PORTABLE static readonly IdnMapping idn = new IdnMapping (); -#endif public static void ValidateArguments (ParserOptions options, byte[] buffer, int startIndex, int length) { @@ -177,7 +161,7 @@ public static bool SkipCommentsAndWhiteSpace (byte[] text, ref int index, int en if (!SkipComment (text, ref index, endIndex)) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete comment token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete comment token at offset {0}", startIndex), startIndex, index); return false; } @@ -211,7 +195,7 @@ public static bool SkipQuoted (byte[] text, ref int index, int endIndex, bool th if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete quoted-string token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete quoted-string token at offset {0}", startIndex), startIndex, index); return false; } @@ -244,6 +228,17 @@ public static bool SkipAtom (byte[] text, ref int index, int endIndex) return index > start; } + // Note: a "phrase atom" is a more lenient atom (e.g. mailbox display-name phrase atom) + public static bool SkipPhraseAtom (byte[] text, ref int index, int endIndex) + { + int start = index; + + while (index < endIndex && text[index].IsPhraseAtom ()) + index++; + + return index > start; + } + public static bool SkipToken (byte[] text, ref int index, int endIndex) { int start = index; @@ -300,7 +295,7 @@ static bool TryParseDotAtom (byte[] text, ref int index, int endIndex, byte[] se do { if (!text[index].IsAtom ()) { if (throwOnError) - throw new ParseException (string.Format ("Invalid {0} token at offset {1}", tokenType, startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid {0} token at offset {1}", tokenType, startIndex), startIndex, index); return false; } @@ -364,7 +359,7 @@ static bool TryParseDomainLiteral (byte[] text, ref int index, int endIndex, boo if (index >= endIndex) { if (throwOnError) - throw new ParseException (string.Format ("Incomplete domain literal token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete domain literal token at offset {0}", startIndex), startIndex, index); return false; } @@ -374,7 +369,7 @@ static bool TryParseDomainLiteral (byte[] text, ref int index, int endIndex, boo if (!text[index].IsDomain ()) { if (throwOnError) - throw new ParseException (string.Format ("Invalid domain literal token at offset {0}", startIndex), startIndex, index); + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid domain literal token at offset {0}", startIndex), startIndex, index); return false; } @@ -396,6 +391,160 @@ public static bool TryParseDomain (byte[] text, ref int index, int endIndex, byt return TryParseDotAtom (text, ref index, endIndex, sentinels, throwOnError, "domain", out domain); } + static readonly byte[] GreaterThanOrAt = { (byte) '>', (byte) '@' }; + + public static bool TryParseMsgId (byte[] text, ref int index, int endIndex, bool requireAngleAddr, bool throwOnError, out string msgid) + { + //const CharType SpaceOrControl = CharType.IsWhitespace | CharType.IsControl; + var angleAddr = false; + + msgid = null; + + if (!SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex || (requireAngleAddr && text[index] != '<')) { + if (throwOnError) + throw new ParseException ("No msg-id token found.", index, index); + + return false; + } + + int tokenIndex = index; + + if (text[index] == '<') { + angleAddr = true; + index++; + } + + SkipWhiteSpace (text, ref index, endIndex); + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete msg-id token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + var token = new StringBuilder (); + + // consume the local-part of the msg-id using a very loose definition of 'local-part' + // + // See https://github.com/jstedfast/MimeKit/issues/472 for the reasons why. + do { + int start = index; + + if (text[index] == '"') { + if (!SkipQuoted (text, ref index, endIndex, throwOnError)) + return false; + } else { + while (index < endIndex && text[index] != (byte) '.' && text[index] != (byte) '@' && text[index] != '>' && !text[index].IsWhitespace ()) + index++; + } + + try { + token.Append (CharsetUtils.UTF8.GetString (text, start, index - start)); + } catch (DecoderFallbackException ex) { + if (throwOnError) + throw new ParseException ("Internationalized local-part tokens may only contain UTF-8 characters.", start, start, ex); + + return false; + } + + if (!SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (angleAddr) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete msg-id token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + // since the msg-id token did not start with a '<', we do not need a '>' + break; + } + + if (text[index] == (byte) '@' || text[index] == (byte) '>') + break; + + if (text[index] != (byte) '.') { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Invalid msg-id token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + token.Append ('.'); + index++; + + if (!SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index >= endIndex) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete msg-id at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + } while (true); + + if (index < endIndex && text[index] == (byte) '@') { + token.Append ('@'); + index++; + + if (!SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + + if (index < endIndex && text[index] != (byte) '>') { + // Note: some Message-Id's are broken and in the form " " + // https://github.com/jstedfast/MailKit/issues/138 + do { + if (!TryParseDomain (text, ref index, endIndex, GreaterThanOrAt, throwOnError, out string domain)) + return false; + + if (IsIdnEncoded (domain)) + domain = IdnDecode (domain); + + token.Append (domain); + + if (index >= endIndex || text[index] != (byte) '@') + break; + + token.Append ('@'); + index++; + } while (true); + + if (!SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) + return false; + } else { + // The msgid token was in the form " ". Technically this is illegal, but for + // the sake of maximum compatibility, I guess we have no choice but to accept it... + // https://github.com/jstedfast/MimeKit/issues/102 + + //if (throwOnError) + // throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete msg-id token at offset {0}", tokenIndex), tokenIndex, index); + + //return false; + } + } + + if (angleAddr && (index >= endIndex || text[index] != '>')) { + if (throwOnError) + throw new ParseException (string.Format (CultureInfo.InvariantCulture, "Incomplete msg-id token at offset {0}", tokenIndex), tokenIndex, index); + + return false; + } + + if (index < endIndex && text[index] == (byte) '>') + index++; + + msgid = token.ToString (); + + return true; + } + public static bool IsInternational (string value) { for (int i = 0; i < value.Length; i++) { @@ -414,7 +563,6 @@ public static bool IsIdnEncoded (string value) return value.IndexOf (".xn--", StringComparison.Ordinal) != -1; } -#if !PORTABLE public static string IdnEncode (string unicode) { try { @@ -432,16 +580,5 @@ public static string IdnDecode (string ascii) return ascii; } } -#else - public static string IdnEncode (string unicode) - { - return unicode; - } - - public static string IdnDecode (string ascii) - { - return ascii; - } -#endif } } diff --git a/MimeKit/Utils/Rfc2047.cs b/MimeKit/Utils/Rfc2047.cs index 80638c827c..948ec353ee 100644 --- a/MimeKit/Utils/Rfc2047.cs +++ b/MimeKit/Utils/Rfc2047.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,6 @@ using System.Text; using System.Collections.Generic; -#if PORTABLE -using Encoding = Portable.Text.Encoding; -#endif - using MimeKit.Encodings; namespace MimeKit.Utils { @@ -813,8 +809,9 @@ public static string DecodeText (byte[] text) static byte[] FoldTokens (FormatOptions options, IList tokens, string field, byte[] input) { - var output = new StringBuilder (input.Length + 2); + var output = new StringBuilder (input.Length + ((input.Length / options.MaxLineLength) * 2) + 2); int lineLength = field.Length + 2; + var firstToken = true; int lwsp = 0, tab = 0; Token token; @@ -845,6 +842,8 @@ static byte[] FoldTokens (FormatOptions options, IList tokens, string fie output.Append (' '); lineLength = 1; } + + firstToken = false; } else if (token.Encoding != ContentEncoding.Default) { string charset = token.CharsetCulture; @@ -857,7 +856,7 @@ static byte[] FoldTokens (FormatOptions options, IList tokens, string fie // break just before the last lwsp character output.Insert (lwsp, options.NewLine); lineLength = 1; - } else if (lineLength > 1) { + } else if (lineLength > 1 && !firstToken) { // force a line break... output.Append (options.NewLine); output.Append (' '); @@ -875,6 +874,7 @@ static byte[] FoldTokens (FormatOptions options, IList tokens, string fie output.Append ("?="); lineLength += token.Length + charset.Length + 7; + firstToken = false; lwsp = 0; tab = 0; } else if (lineLength + token.Length > options.MaxLineLength) { @@ -886,7 +886,7 @@ static byte[] FoldTokens (FormatOptions options, IList tokens, string fie // break just before the last lwsp character output.Insert (lwsp, options.NewLine); lineLength = 1; - } else if (lineLength > 1) { + } else if (lineLength > 1 && !firstToken) { // force a line break... output.Append (options.NewLine); output.Append (' '); @@ -913,6 +913,7 @@ static byte[] FoldTokens (FormatOptions options, IList tokens, string fie lineLength += token.Length; } + firstToken = false; lwsp = 0; tab = 0; } else { @@ -920,6 +921,7 @@ static byte[] FoldTokens (FormatOptions options, IList tokens, string fie output.Append ((char) input[n]); lineLength += token.Length; + firstToken = false; lwsp = 0; tab = 0; } @@ -1039,11 +1041,17 @@ enum WordType { EncodedWord } + enum WordEncoding { + Ascii, + Latin1, + UserSpecified + } + class Word { public WordType Type; public int StartIndex; public int CharCount; - public int Encoding; // 0 => ASCII, 1 => iso-8859-1, 2 => custom + public WordEncoding Encoding; public int ByteCount; public int EncodeCount; public int QuotedPairs; @@ -1105,10 +1113,10 @@ static bool ExceedsMaxLineLength (FormatOptions options, Encoding charset, Word switch (word.Type) { case WordType.EncodedWord: switch (word.Encoding) { - case 1: + case WordEncoding.Latin1: length = EstimateEncodedWordLength ("iso-8859-1", word.ByteCount, word.EncodeCount); break; - case 0: + case WordEncoding.Ascii: length = EstimateEncodedWordLength ("us-ascii", word.ByteCount, word.EncodeCount); break; default: @@ -1153,7 +1161,7 @@ static IList GetRfc822Words (FormatOptions options, Encoding charset, stri if (c < 127) { if (IsCtrl (c)) { - word.Encoding = Math.Max (word.Encoding, 1); + word.Encoding = (WordEncoding) Math.Max ((int) word.Encoding, (int) WordEncoding.Latin1); word.Type = WordType.EncodedWord; word.EncodeCount++; } else if (phrase && !IsAtom (c)) { @@ -1170,7 +1178,7 @@ static IList GetRfc822Words (FormatOptions options, Encoding charset, stri nchars = 1; } else if (c < 256) { // iso-8859-1 - word.Encoding = Math.Max (word.Encoding, 1); + word.Encoding = (WordEncoding) Math.Max ((int) word.Encoding, (int) WordEncoding.Latin1); word.Type = WordType.EncodedWord; word.EncodeCount++; word.ByteCount++; @@ -1192,11 +1200,11 @@ static IList GetRfc822Words (FormatOptions options, Encoding charset, stri n = 3; } + word.Encoding = WordEncoding.UserSpecified; word.Type = WordType.EncodedWord; word.CharCount += nchars; word.EncodeCount += n; word.ByteCount += n; - word.Encoding = 2; } if (ExceedsMaxLineLength (options, charset, word)) { @@ -1282,11 +1290,11 @@ static bool ShouldMergeWords (FormatOptions options, Encoding charset, IList Merge (FormatOptions options, Encoding charset, IList w if (words.Count < 2) return words; - int lwspCount, encoding, encoded, quoted, byteCount, length; + int lwspCount, encoded, quoted, byteCount, length; var merged = new List (); + WordEncoding encoding; Word word, next; word = words[0]; @@ -1317,18 +1326,18 @@ static IList Merge (FormatOptions options, Encoding charset, IList w next = words[i]; if (word.Type != WordType.Atom && word.Type == next.Type) { + encoding = (WordEncoding) Math.Max ((int) word.Encoding, (int) next.Encoding); lwspCount = next.StartIndex - (word.StartIndex + word.CharCount); byteCount = word.ByteCount + lwspCount + next.ByteCount; - encoding = Math.Max (word.Encoding, next.Encoding); encoded = word.EncodeCount + next.EncodeCount; quoted = word.QuotedPairs + next.QuotedPairs; if (word.Type == WordType.EncodedWord) { switch (encoding) { - case 1: + case WordEncoding.Latin1: length = EstimateEncodedWordLength ("iso-8859-1", byteCount, encoded); break; - case 0: + case WordEncoding.Ascii: length = EstimateEncodedWordLength ("us-ascii", byteCount, encoded); break; default: @@ -1370,7 +1379,7 @@ static IList Merge (FormatOptions options, Encoding charset, IList w word.Type = (WordType) Math.Max ((int) word.Type, (int) next.Type); word.CharCount = (next.StartIndex + next.CharCount) - word.StartIndex; word.ByteCount = word.ByteCount + lwspCount + next.ByteCount; - word.Encoding = Math.Max (word.Encoding, next.Encoding); + word.Encoding = (WordEncoding) Math.Max ((int) word.Encoding, (int) next.Encoding); word.EncodeCount = word.EncodeCount + next.EncodeCount; word.QuotedPairs = word.QuotedPairs + next.QuotedPairs; } else { @@ -1392,23 +1401,9 @@ static byte[] Encode (FormatOptions options, Encoding charset, string text, bool byte[] encoded; if (!options.AllowMixedHeaderCharsets) { - int maxEncoding = 0; - for (int i = 0; i < words.Count; i++) { - if (words[i].Type != WordType.EncodedWord || words[i].Encoding == maxEncoding) - continue; - - if (words[i].Encoding > maxEncoding) { - maxEncoding = words[i].Encoding; - for (int j = 0; j < i; j++) { - if (words[j].Type != WordType.EncodedWord) - continue; - - words[j].Encoding = maxEncoding; - } - } else { - words[i].Encoding = maxEncoding; - } + if (words[i].Type == WordType.EncodedWord) + words[i].Encoding = WordEncoding.UserSpecified; } } @@ -1441,10 +1436,10 @@ static byte[] Encode (FormatOptions options, Encoding charset, string text, bool } switch (word.Encoding) { - case 0: // us-ascii + case WordEncoding.Ascii: AppendEncodedWord (str, Encoding.ASCII, text, start, length, mode); break; - case 1: // iso-8859-1 + case WordEncoding.Latin1: AppendEncodedWord (str, CharsetUtils.Latin1, text, start, length, mode); break; default: // custom charset diff --git a/MimeKit/Utils/StringBuilderExtensions.cs b/MimeKit/Utils/StringBuilderExtensions.cs index 31c6fcf288..c9f90fd462 100644 --- a/MimeKit/Utils/StringBuilderExtensions.cs +++ b/MimeKit/Utils/StringBuilderExtensions.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,6 +25,7 @@ // using System.Text; +using System.Collections.Generic; namespace MimeKit.Utils { static class StringBuilderExtensions @@ -44,7 +45,33 @@ public static StringBuilder LineWrap (this StringBuilder text, FormatOptions opt return text; } - public static StringBuilder AppendFolded (this StringBuilder text, FormatOptions options, string value, ref int lineLength) + public static void AppendTokens (this StringBuilder text, FormatOptions options, ref int lineLength, List tokens) + { + var spaces = string.Empty; + + foreach (var token in tokens) { + if (string.IsNullOrWhiteSpace (token)) { + spaces = token; + continue; + } + + if (lineLength + spaces.Length + token.Length > options.MaxLineLength) { + text.Append (options.NewLine); + spaces = string.Empty; + text.Append ('\t'); + lineLength = 1; + } else { + lineLength += spaces.Length; + text.Append (spaces); + spaces = string.Empty; + } + + lineLength += token.Length; + text.Append (token); + } + } + + public static StringBuilder AppendFolded (this StringBuilder text, FormatOptions options, bool firstToken, string value, ref int lineLength) { int wordIndex = 0; int lwspIndex; @@ -66,6 +93,10 @@ public static StringBuilder AppendFolded (this StringBuilder text, FormatOptions lwspIndex++; } } + + // consume the end-quote + if (lwspIndex < value.Length) + lwspIndex++; } else { // normal word while (lwspIndex < value.Length && !char.IsWhiteSpace (value[lwspIndex])) @@ -73,13 +104,14 @@ public static StringBuilder AppendFolded (this StringBuilder text, FormatOptions } int length = lwspIndex - wordIndex; - if (lineLength > 1 && (lineLength + length) > options.MaxLineLength) { + if (!firstToken && lineLength > 1 && (lineLength + length) > options.MaxLineLength) { text.LineWrap (options); lineLength = 1; } text.Append (value, wordIndex, length); lineLength += length; + firstToken = false; wordIndex = lwspIndex; while (wordIndex < value.Length && char.IsWhiteSpace (value[wordIndex])) @@ -106,7 +138,7 @@ public static void AppendCStringByte (this StringBuilder text, byte c) case 0x0B: text.Append ("\\v"); break; case 0x0D: text.Append ("\\r"); break; default: - if (c < 020 || c > 0x7e) { + if (c < 0x20 || c > 0x7e) { text.AppendFormat ("\\x{0:x2}", c); } else { text.Append ((char) c); diff --git a/MimeKit/XMessagePriority.cs b/MimeKit/XMessagePriority.cs index 03b4926fa2..cf4a84ced8 100644 --- a/MimeKit/XMessagePriority.cs +++ b/MimeKit/XMessagePriority.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2018 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/MimeKit/packages.MimeKit.Mac.config b/MimeKit/packages.MimeKit.Mac.config deleted file mode 100644 index 3baddc3a52..0000000000 --- a/MimeKit/packages.MimeKit.Mac.config +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/MimeKit/packages.MimeKit.Net45.config b/MimeKit/packages.MimeKit.Net45.config deleted file mode 100644 index 730a601e5f..0000000000 --- a/MimeKit/packages.MimeKit.Net45.config +++ /dev/null @@ -1,4 +0,0 @@ - -- - \ No newline at end of file diff --git a/MimeKit/packages.MimeKit.Portable.config b/MimeKit/packages.MimeKit.Portable.config deleted file mode 100644 index 100af68f8f..0000000000 --- a/MimeKit/packages.MimeKit.Portable.config +++ /dev/null @@ -1,4 +0,0 @@ - -- - \ No newline at end of file diff --git a/MimeKit/packages.MimeKit.TvOS.config b/MimeKit/packages.MimeKit.TvOS.config deleted file mode 100644 index 3baddc3a52..0000000000 --- a/MimeKit/packages.MimeKit.TvOS.config +++ /dev/null @@ -1,4 +0,0 @@ - -- - diff --git a/MimeKit/packages.MimeKit.WatchOS.config b/MimeKit/packages.MimeKit.WatchOS.config deleted file mode 100644 index 3baddc3a52..0000000000 --- a/MimeKit/packages.MimeKit.WatchOS.config +++ /dev/null @@ -1,4 +0,0 @@ - -- - diff --git a/MimeKit/packages.MimeKit.WindowsUniversal81.config b/MimeKit/packages.MimeKit.WindowsUniversal81.config deleted file mode 100644 index c56390c4e2..0000000000 --- a/MimeKit/packages.MimeKit.WindowsUniversal81.config +++ /dev/null @@ -1,4 +0,0 @@ - -- - \ No newline at end of file diff --git a/MimeKitLite.sln b/MimeKitLite.sln index e75ca7a2fc..c3a067b93f 100644 --- a/MimeKitLite.sln +++ b/MimeKitLite.sln @@ -1,23 +1,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.12 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKitLite.Net45", "MimeKit\MimeKitLite.Net45.csproj", "{B4DA3323-F83C-4731-BE30-B1DA19B8C3E7}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A56B6ECE-F1EE-41A2-A92B-18D2BDAFB6A2}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKitLite.Android", "MimeKit\MimeKitLite.Android.csproj", "{D95A2734-2A81-4C75-985E-A33B15CC62BC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKitLite.iOS", "MimeKit\MimeKitLite.iOS.csproj", "{5F211544-940D-46C9-98EB-4FD8F62506AD}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Portable.Text.Encoding", "submodules\Portable.Text.Encoding\Portable.Text.Encoding\Portable.Text.Encoding.csproj", "{EEE48C75-11BE-4B50-B759-F85B5052D473}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Portable.Text.Encoding.WindowsUniversal81", "submodules\Portable.Text.Encoding\Portable.Text.Encoding\Portable.Text.Encoding.WindowsUniversal81.csproj", "{B76A64F9-B00E-4243-AE89-5D024CA3B436}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKitLite.Portable", "MimeKit\MimeKitLite.Portable.csproj", "{25C2DD3E-ED14-494F-9BD4-75536911C1BD}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKitLite.WindowsUniversal81", "MimeKit\MimeKitLite.WindowsUniversal81.csproj", "{5EB9F403-45B4-4F3A-B64E-0ECC94D14167}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MimeKitLite.NetStandard", "MimeKit\MimeKitLite.NetStandard.csproj", "{23F999AF-CF50-42FF-A011-D56D68E60FB9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MimeKitLite", "MimeKit\MimeKitLite.csproj", "{23F999AF-CF50-42FF-A011-D56D68E60FB9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -25,34 +16,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B4DA3323-F83C-4731-BE30-B1DA19B8C3E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B4DA3323-F83C-4731-BE30-B1DA19B8C3E7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B4DA3323-F83C-4731-BE30-B1DA19B8C3E7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B4DA3323-F83C-4731-BE30-B1DA19B8C3E7}.Release|Any CPU.Build.0 = Release|Any CPU - {D95A2734-2A81-4C75-985E-A33B15CC62BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D95A2734-2A81-4C75-985E-A33B15CC62BC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D95A2734-2A81-4C75-985E-A33B15CC62BC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D95A2734-2A81-4C75-985E-A33B15CC62BC}.Release|Any CPU.Build.0 = Release|Any CPU - {5F211544-940D-46C9-98EB-4FD8F62506AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5F211544-940D-46C9-98EB-4FD8F62506AD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5F211544-940D-46C9-98EB-4FD8F62506AD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5F211544-940D-46C9-98EB-4FD8F62506AD}.Release|Any CPU.Build.0 = Release|Any CPU - {EEE48C75-11BE-4B50-B759-F85B5052D473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EEE48C75-11BE-4B50-B759-F85B5052D473}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EEE48C75-11BE-4B50-B759-F85B5052D473}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EEE48C75-11BE-4B50-B759-F85B5052D473}.Release|Any CPU.Build.0 = Release|Any CPU - {B76A64F9-B00E-4243-AE89-5D024CA3B436}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B76A64F9-B00E-4243-AE89-5D024CA3B436}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B76A64F9-B00E-4243-AE89-5D024CA3B436}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B76A64F9-B00E-4243-AE89-5D024CA3B436}.Release|Any CPU.Build.0 = Release|Any CPU - {25C2DD3E-ED14-494F-9BD4-75536911C1BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {25C2DD3E-ED14-494F-9BD4-75536911C1BD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {25C2DD3E-ED14-494F-9BD4-75536911C1BD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {25C2DD3E-ED14-494F-9BD4-75536911C1BD}.Release|Any CPU.Build.0 = Release|Any CPU - {5EB9F403-45B4-4F3A-B64E-0ECC94D14167}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5EB9F403-45B4-4F3A-B64E-0ECC94D14167}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5EB9F403-45B4-4F3A-B64E-0ECC94D14167}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5EB9F403-45B4-4F3A-B64E-0ECC94D14167}.Release|Any CPU.Build.0 = Release|Any CPU {23F999AF-CF50-42FF-A011-D56D68E60FB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {23F999AF-CF50-42FF-A011-D56D68E60FB9}.Debug|Any CPU.Build.0 = Debug|Any CPU {23F999AF-CF50-42FF-A011-D56D68E60FB9}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -61,11 +24,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = MimeKit\MimeKitLite.Net45.csproj - Policies = $0 - $0.StandardHeader = $1 - $1.Text = @\n${FileName}\n \nAuthor: ${AuthorName} <${AuthorEmail}>\n\nCopyright (c) ${Year} ${CopyrightHolder}\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n - $0.TextStylePolicy = $4 + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3E07F961-6E08-4E6A-AF55-EC1428E15EAD} EndGlobalSection EndGlobal diff --git a/Mono.Data.Sqlite/Mono.Data.Sqlite.csproj b/Mono.Data.Sqlite/Mono.Data.Sqlite.csproj index e1f871b380..a929528ca4 100644 --- a/Mono.Data.Sqlite/Mono.Data.Sqlite.csproj +++ b/Mono.Data.Sqlite/Mono.Data.Sqlite.csproj @@ -1,4 +1,4 @@ - +- + \ No newline at end of file diff --git a/README.md b/README.md index ea33b100be..24b30477ce 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,32 @@ [![Join the chat at https://gitter.im/jstedfast/MimeKit](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jstedfast/MimeKit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Issue Stats](http://www.issuestats.com/github/jstedfast/MimeKit/badge/pr)](http://www.issuestats.com/github/jstedfast/MimeKit) -[![Issue Stats](http://www.issuestats.com/github/jstedfast/MimeKit/badge/issue)](http://www.issuestats.com/github/jstedfast/MimeKit) +| Package |Latest Release|Latest Build| +|:--------------|:------------:|:----------:| +|**MimeKit** |[![NuGet Badge MimeKit](https://buildstats.info/nuget/MimeKit)](https://www.nuget.org/packages/MimeKit)|[![MyGet Badge MimeKit](https://buildstats.info/myget/mimekit/MimeKit)](https://www.myget.org/feed/mimekit/package/nuget/MimeKit)| +|**MimeKitLite**|[![NuGet Badge MimeKitLite](https://buildstats.info/nuget/MimeKitLite)](https://www.nuget.org/packages/MimeKitLite)|| -| |Build Status|Code Coverage|Static Analysis| -|-------------|:----------:|:-----------:|:-------------:| -|**Linux/Mac**|[![Build Status](https://travis-ci.org/jstedfast/MimeKit.svg)](https://travis-ci.org/jstedfast/MimeKit)|[![Code Coverage](https://coveralls.io/repos/jstedfast/MimeKit/badge.svg?branch=HEAD&service=github)](https://coveralls.io/r/jstedfast/MimeKit?branch=HEAD)|[![Static Analysis](https://scan.coverity.com/projects/3201/badge.svg)](https://scan.coverity.com/projects/3201)| -|**Windows** |[![Build Status](https://ci.appveyor.com/api/projects/status/07h7cobihqficw3s/branch/master?svg=true)](https://ci.appveyor.com/project/jstedfast/mimekit/branch/master)|[![Code Coverage](https://coveralls.io/repos/jstedfast/MimeKit/badge.svg?branch=HEAD&service=github)](https://coveralls.io/r/jstedfast/MimeKit?branch=HEAD)|[![Static Analysis](https://scan.coverity.com/projects/3201/badge.svg)](https://scan.coverity.com/projects/3201)| +| Platform |Build Status|Code Coverage|Static Analysis| +|:-------------|:----------:|:-----------:|:-------------:| +|**Linux/Mac**|[![Build Status](https://travis-ci.org/jstedfast/MimeKit.svg)](https://travis-ci.org/jstedfast/MimeKit)|[![Code Coverage](https://coveralls.io/repos/jstedfast/MimeKit/badge.svg?branch=master)](https://coveralls.io/r/jstedfast/MimeKit?branch=master)|[![Static Analysis](https://scan.coverity.com/projects/3201/badge.svg)](https://scan.coverity.com/projects/3201)| +|**Windows** |[![Build Status](https://ci.appveyor.com/api/projects/status/07h7cobihqficw3s/branch/master?svg=true)](https://ci.appveyor.com/project/jstedfast/mimekit/branch/master)|[![Code Coverage](https://coveralls.io/repos/jstedfast/MimeKit/badge.svg?branch=master)](https://coveralls.io/r/jstedfast/MimeKit?branch=master)|[![Static Analysis](https://scan.coverity.com/projects/3201/badge.svg)](https://scan.coverity.com/projects/3201)| ## What is MimeKit? MimeKit is a C# library which may be used for the creation and parsing of messages using the Multipurpose Internet Mail Extension (MIME), as defined by [numerous IETF specifications](https://github.com/jstedfast/MimeKit/blob/master/RFCs.md). +## Donate + +MimeKit is a personal open source project that I have put thousands of hours into perfecting with the +goal of making it the very best MIME parser framework for .NET. I need your help to achieve this. + +Donating helps pay for things such as web hosting, domain registration and licenses for developer tools +such as a performance profiler, memory profiler, a static code analysis tool, and more. It also helps +motivate me to continue working on the project. + + + ## History As a developer and user of email clients, I had come to realize that the vast majority of email client @@ -73,32 +86,36 @@ How does your MIME parser compare? ## License Information -MimeKit is Copyright (C) 2012-2017 Xamarin Inc. and is licensed under the MIT license: - - 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. +``` +MIT License + +Copyright (C) 2012-2020 .NET Foundation and Contributors + +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. +``` ## Installing via NuGet The easiest way to install MimeKit is via [NuGet](https://www.nuget.org/packages/MimeKit/). In Visual Studio's [Package Manager Console](http://docs.nuget.org/docs/start-here/using-the-package-manager-console), -simply enter the following command: +enter the following command: Install-Package MimeKit @@ -136,16 +153,16 @@ directory and select **Git Sync...** in the menu. Once you do that, you'll need In the top-level MimeKit directory, there are a number of solution files; they are: -* **MimeKit.sln** - includes projects for .NET 4.5, .NETStandard, PCL (Profile7 and Profile111), - Xamarin.Android, and Xamarin.iOS as well as the unit tests. -* **MimeKit.Mobile.sln** - includes only the Xamarin.Android and Xamarin.iOS projects. -* **MimeKit.Net45.sln** - includes only the .NET 4.5 project and the unit tests. +* **MimeKit.sln** - includes projects for .NET 4.5/4.6/4.7/4.8, .NETStandard 1.3/1.6/2.0 as well as the unit tests. +* **MimeKitLite.sln** - includes projects for the stripped-down versions of MimeKit that drop support for crypto. -If you don't have the Xamarin products, you'll probably want to open the MimeKit.Net45.sln instead of MimeKit.sln. +Once you've opened the appropriate MimeKit solution file in [Visual Studio](https://www.visualstudio.com/downloads/), +you can choose the **Debug** or **Release** build configuration and then build. -Once you've opened the appropriate MimeKit solution file in either [Xamarin Studio](https://www.xamarin.com/download) -or [Visual Studio 2017](https://www.visualstudio.com/downloads/), you can simply choose the **Debug** or **Release** -build configuration and then build. +Both Visual Studio 2017 and Visual Studio 2019 should be able to build MimeKit without any issues, but older versions such as +Visual Studio 2015 will require modifications to the projects in order to build correctly. It has been reported that adding +NuGet package references to [Microsoft.Net.Compilers](https://www.nuget.org/packages/Microsoft.Net.Compilers/) >= 3.6.0 +and [System.ValueTuple](https://www.nuget.org/packages/System.ValueTuple/) >= 4.5.0 will allow MimeKit to build successfully. Note: The **Release** build will generate the xml API documentation, but the **Debug** build will not. @@ -731,7 +748,7 @@ would use an OpenPGP cryptography context. For example, you might use a subclass use `GnuPGContext` directly because it has no way of prompting the user for their passphrase). For the sake of this example, let's pretend that you've written a minimal subclass of -`MimeKit.Cryptography.GnuPGContext` that simply overrides the `GetPassword()` method and +`MimeKit.Cryptography.GnuPGContext` that only overrides the `GetPassword()` method and that this subclass is called `MyGnuPGContext`. ```csharp @@ -825,10 +842,9 @@ file called **privatekey.pem**: ```csharp var headers = new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.Date }; -var headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; -var bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; -var signer = new DkimSigner ("privatekey.pem") { - SignatureAlgorithm = DkimSignatureAlgorithm.RsaSha1, +var signer = new DkimSigner ("privatekey.pem", "example.com", "brisbane", DkimSignatureAlgorithm.RsaSha256) { + HeaderCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, + BodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, AgentOrUserIdentifier = "@eng.example.com", QueryMethod = "dns/txt", }; @@ -838,7 +854,7 @@ var signer = new DkimSigner ("privatekey.pem") { // then you can use `EncodingConstraint.EightBit` instead. message.Prepare (EncodingConstraint.SevenBit); -message.Sign (signer, headers, headerAlgorithm, bodyAlgorithm); +signer.Sign (message, headers); ``` As you can see, it's fairly straight forward. @@ -859,19 +875,21 @@ using System; using System.IO; using System.Text; using System.Threading; +using System.Threading.Tasks; using System.Collections.Generic; using Heijden.DNS; using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.OpenSsl; using MimeKit; using MimeKit.Cryptography; -namespace DkimVerifier +namespace DkimVerifierExample { - class DkimPublicKeyLocator : IDkimPublicKeyLocator + // Note: By using the DkimPublicKeyLocatorBase, we avoid having to parse the DNS TXT records + // in order to get the public key ourselves. + class DkimPublicKeyLocator : DkimPublicKeyLocatorBase { readonly Dictionary Debug @@ -45,18 +45,28 @@- + + Component +- - - + + +Component ++ +Component ++ Component +- + + Component +@@ -81,9 +91,7 @@ - - +- - cache; readonly Resolver resolver; @@ -907,66 +925,13 @@ namespace DkimVerifier } var txt = builder.ToString (); - string k = null, p = null; - int index = 0; - - // parse the response (will look something like: "k=rsa; p= ") - while (index < txt.Length) { - while (index < txt.Length && char.IsWhiteSpace (txt[index])) - index++; - - if (index == txt.Length) - break; - - // find the end of the key - int startIndex = index; - while (index < txt.Length && txt[index] != '=') - index++; - - if (index == txt.Length) - break; - - var key = txt.Substring (startIndex, index - startIndex); - - // skip over the '=' - index++; - - // find the end of the value - startIndex = index; - while (index < txt.Length && txt[index] != ';') - index++; - - var value = txt.Substring (startIndex, index - startIndex); - - switch (key) { - case "k": k = value; break; - case "p": p = value; break; - } - - // skip over the ';' - index++; - } - - if (k != null && p != null) { - var data = "-----BEGIN PUBLIC KEY-----\r\n" + p + "\r\n-----END PUBLIC KEY-----\r\n"; - var rawData = Encoding.ASCII.GetBytes (data); - - using (var stream = new MemoryStream (rawData, false)) { - using (var reader = new StreamReader (stream)) { - var pem = new PemReader (reader); - - pubkey = pem.ReadObject () as AsymmetricKeyParameter; - - if (pubkey != null) { - cache.Add (query, pubkey); - - return pubkey; - } - } - } - } - throw new Exception (string.Format ("Failed to look up public key for: {0}", domain)); + // DkimPublicKeyLocatorBase provides us with this helpful method. + pubkey = GetPublicKey (txt); + + cache.Add (query, pubkey); + + return pubkey; } public AsymmetricKeyParameter LocatePublicKey (string methods, string domain, string selector, CancellationToken cancellationToken = default (CancellationToken)) @@ -979,6 +944,13 @@ namespace DkimVerifier throw new NotSupportedException (string.Format ("{0} does not include any suported lookup methods.", methods)); } + + public Task LocatePublicKeyAsync (string methods, string domain, string selector, CancellationToken cancellationToken = default (CancellationToken)) + { + return Task.Run (() => { + return LocatePublicKey (methods, domain, selector, cancellationToken); + }, cancellationToken); + } } class Program @@ -998,6 +970,7 @@ namespace DkimVerifier } var locator = new DkimPublicKeyLocator (); + var verifier = new DkimVerifier (locator); for (int i = 0; i < args.Length; i++) { if (!File.Exists (args[i])) { @@ -1017,7 +990,7 @@ namespace DkimVerifier var dkim = message.Headers[index]; - if (message.Verify (dkim, locator)) { + if (verifier.Verify (message, dkim)) { // the DKIM-Signature header is valid! Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine ("VALID"); @@ -1042,45 +1015,229 @@ namespace DkimVerifier } ``` +### Signing Messages with ARC + +Signing with ARC is similar to DKIM but quite a bit more involved. In order to sign with +ARC, you must first validate that the existing message is authentictic and produce +an ARC-Authentication-Results header containing the methods that you used to +authenticate the message as well as their results. + +The abstract [ArcSigner](http://www.mimekit.net/docs/html/T_MimeKit_Cryptography_ArcSigner.htm) +class provided by MimeKit will need to be subclassed before it can be used. An example subclass +that provides 2 different implementations for generating the ARC-Authentication-Results header +can be seen below: + +```csharp +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; + +using MimeKit; +using MimeKit.Cryptography; + +namespace ArcSignerExample +{ + class MyArcSigner : ArcSigner + { + public MyArcSigner (string fileName, string domain, string selector, DkimSigningAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) + : base (fileName, domain, selector, algorithm) + { + } + + /// + /// Generate the ARC-Authentication-Results header. + /// + ///+ /// The ARC-Authentication-Results header contains information detailing the results of + /// authenticating/verifying the message via ARC, DKIM, SPF, etc. + /// + /// In the following implementation, we assume that all of these authentication results + /// have already been determined by other mail software that has added some Authentication-Results + /// headers containing this information. + /// + /// Note: This method is used when ArcSigner.Sign() is called instead of ArcSigner.SignAsync(). + /// + protected override AuthenticationResults GenerateArcAuthenticationResults (FormatOptions options, MimeMessage message, CancellationToken cancellationToken) + { + const string AuthenticationServiceIdentifier = "lists.example.com"; + + var results = new AuthenticationResults (AuthenticationServiceIdentifier); + + for (int i = 0; i < message.Headers.Count; i++) { + var header = message.Headers[i]; + + if (header.Id != HeaderId.AuthenticationResults) + continue; + + if (!AuthenticationResults.TryParse (header.RawValue, out AuthenticationResults authres)) + continue; + + if (authres.AuthenticationServiceIdentifier != AuthenticationServiceIdentifier) + continue; + + // Merge any authentication results that aren't already known. + foreach (var result in authres.Results) { + if (!results.Results.Any (r => r.Method == result.Method)) + results.Results.Add (result); + } + } + + return results; + } + + ///+ /// Generate the ARC-Authentication-Results asynchronously. + /// + ///+ /// The ARC-Authentication-Results header contains information detailing the results of + /// authenticating/verifying the message via ARC, DKIM, SPF, etc. + /// + /// In the following implementation, we assume that we have to verify all of the various + /// authentication methods ourselves. + /// + /// Note: This method is used when ArcSigner.SignAsync() is called instead of ArcSigner.Sign(). + /// + protected override async TaskGenerateArcAuthenticationResultsAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken) + { + const string AuthenticationServiceIdentifier = "lists.example.com"; + + var results = new AuthenticationResults (AuthenticationServiceIdentifier); + var locator = new DkimPublicKeyLocator (); // from the DKIM example above + var dkimVerifier = new DkimVerifier (locator); + var arcVerifier = new ArcVerifier (locator); + AuthenticationMethodResult method; + + // Add the ARC authentication results + try { + var arc = await arcVerifier.VerifyAsync (message, cancellationToken); + var result = arc.Chain.ToString ().ToLowerInvariant (); + + method = new AuthenticationMethodResult ("arc", result); + results.Results.Add (method); + } catch { + // Likely a DNS error + method = new AuthenticationMethodResult ("arc", "fail"); + method.Reason = "DNS error"; + results.Results.Add (method); + } + + // Add authentication results for each DKIM signature + foreach (var dkimHeader in message.Headers.Where (h => h.Id == HeaderId.DkimSignature)) { + string result; + + try { + if (await dkimVerifier.VerifyAsync (message, cancellationToken)) { + result = "pass"; + } else { + result = "fail"; + } + } catch { + result = "fail"; + } + + method = new AuthenticationMethodResult ("dkim", result); + + // Parse the DKIM-Signature header so that we can add some + // properties to our method result. + var params = dkimHeader.Value.Replace (" ", "").Split (new char[] { ';' }); + var i = params.FirstOrDefault (p => p.StartsWith ("i=", StringComparison.Ordinal)); + var b = params.FirstOrDefault (p => p.StartsWith ("b=", StringComparison.Ordinal)); + + if (i != null) + method.Parameters.Add ("header.i", i.Substring (2)); + + if (b != null) + method.Parameters.Add ("header.b", b.Substring (2, 8)); + + results.Results.Add (method); + } + + return results; + } + } +} +``` + +Once you have a custom `ArcSigner` class, the actual logic for signing is almost identical to DKIM. + +Note: As with the DKIM signing example above, assume that the private key is saved in a +file called **privatekey.pem**: + +```csharp +var headers = new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.Date }; +var signer = new MyArcSigner ("privatekey.pem", "example.com", "brisbane", DkimSignatureAlgorithm.RsaSha256) { + HeaderCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Relaxed, + BodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Relaxed, + AgentOrUserIdentifier = "@eng.example.com" +}; + +// Prepare the message body to be sent over a 7bit transport (such as older versions of SMTP). +// Note: If the SMTP server you will be sending the message over supports the 8BITMIME extension, +// then you can use `EncodingConstraint.EightBit` instead. +message.Prepare (EncodingConstraint.SevenBit); + +signer.Sign (message, headers); // or SignAsync +``` + +### Verifying ARC Signatures + +Just like with verifying DKIM signatures, you will need to implement the `IDkimPublicKeyLocator` +interface. To see an example of how to implement this interface, see the DKIM signature verification +example above. + +The `ArcVerifier` works exactly the same as the `DkimVerifier` except that it is not necessary +to provide a `Header` argument to the `Verify` or `VerifyAsync` method. + +```csharp +var verifier = new ArcVerifier (new DkimPublicKeyLocator ()); +var results = await verifier.VerifyAsync (message); + +// The Chain results are the only real important results. +Console.WriteLine ("ARC results: {0}", results.Chain); +``` + ## Contributing The first thing you'll need to do is fork MimeKit to your own GitHub repository. For instructions on how to do that, see the section titled **Getting the Source Code**. -If you use [Xamarin Studio](http://xamarin.com/studio) or [MonoDevelop](http://monodevelop.com), all of the -solution files are configured with the coding style used by MimeKit. If you use Visual Studio or some -other editor, please try to maintain the existing coding style as best as you can. +If you use [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/) or [MonoDevelop](http://monodevelop.com), +all of the solution files are configured with the coding style used by MimeKit. If you use Visual Studio on Windows or +some other editor, please try to maintain the existing coding style as best as you can. Once you've got some changes that you'd like to submit upstream to the official MimeKit repository, -simply send me a **Pull Request** and I will try to review your changes in a timely manner. +send me a **Pull Request** and I will try to review your changes in a timely manner. If you'd like to contribute but don't have any particular features in mind to work on, check out the issue tracker and look for something that might pique your interest! -## Donate +## Reporting Bugs -MimeKit is a personal open source project that I have put thousands of hours into perfecting with the -goal of making it not only the very best MIME parser framework for .NET, but the best MIME parser -framework for any programming language. I need your help to achieve this. +Have a bug or a feature request? Please open a new +[bug report](https://github.com/jstedfast/MimeKit/issues/new?template=bug_report.md) +or +[feature request](https://github.com/jstedfast/MimeKit/issues/new?template=feature_request.md). -Donating helps pay for things such as web hosting and licenses for developer tools such as a -performance profiler, memory profiler, a static code analysis tool, and more. +Before opening a new issue, please search through any [existing issues](https://github.com/jstedfast/MimeKit/issues) +to avoid submitting duplicates. It may also be worth checking the +[FAQ](https://github.com/jstedfast/MimeKit/blob/master/FAQ.md) for common questions that other developers +have had. - - - +If you are getting an exception from somewhere within MimeKit, don't just provide the `Exception.Message` +string. Please include the `Exception.StackTrace` as well. The `Message`, by itself, is often useless. -## Reporting Bugs +## Documentation -Have a bug or a feature request? [Please open a new issue](https://github.com/jstedfast/MimeKit/issues). +API documentation can be found at [http://www.mimekit.net/docs](http://www.mimekit.net/docs). -Before opening a new issue, please search for existing issues to avoid submitting duplicates. +A copy of the XML-formatted API reference documentation is also included in the NuGet package. -## Documentation +## .NET Foundation + +MimeKit is a [.NET Foundation](https://www.dotnetfoundation.org/projects) project. -API documentation can be found at [http://mimekit.net/docs](http://mimekit.net/docs). +This project has adopted the code of conduct defined by the [Contributor Covenant](http://contributor-covenant.org/) to clarify expected behavior in our community. For more information, see the [.NET Foundation Code of Conduct](http://www.dotnetfoundation.org/code-of-conduct). -A copy of the xml formatted API documentation is also included in the NuGet and/or -Xamarin Component package. +General .NET OSS discussions: [.NET Foundation forums](https://forums.dotnetfoundation.org) diff --git a/RFCs.md b/RFCs.md index 2cc789353c..500676104f 100644 --- a/RFCs.md +++ b/RFCs.md @@ -45,16 +45,27 @@ MimeKit implements the following IETF specifications defining Internet Mail and * [4262](http://www.ietf.org/rfc/rfc4262.txt): X.509 Certificate Extension for S/MIME Capabilities * [4871](http://www.ietf.org/rfc/rfc4871.txt): DomainKeys Identified Mail (DKIM) Signatures * [5322](http://www.ietf.org/rfc/rfc5322.txt): Internet Message Format (Obsoletes rfc2822) +* [5451](http://www.ietf.org/rfc/rfc5451.txt): Message Header Field for Indicating Message Authentication Status * [5672](http://www.ietf.org/rfc/rfc5672.txt): RFC 4871 DomainKeys Identified Mail (DKIM) Signatures -- Update * [5750](http://www.ietf.org/rfc/rfc5750.txt): S/MIME Version 3.2 Certificate Handling (Obsoletes rfc3850) * [5751](http://www.ietf.org/rfc/rfc5751.txt): S/MIME Version 3.2 Message Specification (Obsoletes rfc3851) * [6376](http://www.ietf.org/rfc/rfc6376.txt): DomainKeys Identified Mail (DKIM) Signatures (Obsoletes rfc4871) * [6522](http://www.ietf.org/rfc/rfc6522.txt): The Multipart/Report Media Type for the Reporting of Mail System Administrative Messages (Obsoletes rfc3462) * [6532](http://www.ietf.org/rfc/rfc6532.txt): Internationalized Email Headers +* [6533](http://www.ietf.org/rfc/rfc6533.txt): Internationalized Delivery Status and Disposition Notifications +* [7001](http://www.ietf.org/rfc/rfc7001.txt): Message Header Field for Indicating Message Authentication Status (Obsoletes rfc5451) +* [7601](http://www.ietf.org/rfc/rfc7601.txt): Message Header Field for Indicating Message Authentication Status (Obsoletes rfc7001) +* [8301](http://www.ietf.org/rfc/rfc8301.txt): Cryptographic Algorithm and Key Usage Update to DomainKeys Identified Mail (DKIM) +* [8463](http://www.ietf.org/rfc/rfc8463.txt): A New Cryptographic Signature Method for DomainKeys Identified Mail (DKIM) #### Other RFCs of interest: * [1523](http://www.ietf.org/rfc/rfc1523.txt): The text/enriched MIME Content-type * [1927](http://www.ietf.org/rfc/rfc1927.txt): Suggested Additional MIME Types for Associating Documents +* [2369](http://www.ietf.org/rfc/rfc2369.txt): The Use of URLs as Meta-Syntax for Core Mail List Commands and their Transport through Message Header Fields * [2388](http://www.ietf.org/rfc/rfc2388.txt): Returning Values from Forms: multipart/form-data +* [6577](http://www.ietf.org/rfc/rfc6577.txt): Authentication-Results Registration Update for Sender Policy Framework (SPF) Results * [7103](http://www.ietf.org/rfc/rfc7103.txt): Advice for Safe Handling of Malformed Messages +* [7410](http://www.ietf.org/rfc/rfc7410.txt): A Property Types Registry for the Authentication-Results Header Field +* [8550](http://www.ietf.org/rfc/rfc8550.txt): S/MIME Version 4.0 Certificate Handling (Obsoletes rfc5750) +* [8551](http://www.ietf.org/rfc/rfc8551.txt): S/MIME Version 4.0 Message Specification (Obsoletes rfc5751) diff --git a/ReleaseNotes.md b/ReleaseNotes.md index e4fbb7351d..044403a256 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,6 +1,397 @@ # Release Notes -### MimeKit 2.0.1 +### MimeKit 2.10.1 (2020-12-05) + +* Treat message/disposition-notification and message/delivery-status the same as text/* + when preparing for signing. (issue [#626](https://github.com/jstedfast/MimeKit/issues/626)) +* Always set Content-Disposition: inline for BodyBuilder.LinkedResources. This fixes a + regression introduced in 2.10.0. + (issue [#627](https://github.com/jstedfast/MimeKit/issues/627)) +* Fixed NuGet package references to System.Data.DataSetExtensions for netstandard2.1 and + net4x. + +### MimeKit 2.10.0 (2020-11-20) + +* Added SQL Server support. (issue [#619](https://github.com/jstedfast/MimeKit/issues/619)) +* Fixed a leak in SqlCertificateDatabase when creating the certificates database. +* Bumped BouncyCastle dependency to v1.8.8. (issue [#610](https://github.com/jstedfast/MimeKit/issues/610)) +* Exposed some ArcVerifier and DkimVerifier internal methods. + (issue [#601](https://github.com/jstedfast/MimeKit/issues/601)) +* Improved MimeParser performance. +* Fixed potential leaks in MimeParser when loading MimePart content in exception cases. +* Made use of ArrayPools for various buffers which may help performance. + (issue [#616](https://github.com/jstedfast/MimeKit/issues/616)) +* Fixed MimeUtils.GenerateMessageId() to encode international domain names. +* Fixed MimeUtils.GenerateMessageId() to cache the local hostname. + (issue [#612](https://github.com/jstedfast/MimeKit/issues/612)) +* Modified AttachmentCollection to use a custom implementation of Path.GetFileName() + that allows illegal path characters. +* Only generate a ContentId for the MultipartRelated Root if it is not the first part. + +### MimeKit 2.9.2 (2020-09-12) + +* Include WindowsSecureMimeContext in the .NET Standard 2.x build. + (issue [#600](https://github.com/jstedfast/MimeKit/issues/600)) +* Fixed message.Prepare() to never choose the quoted-printable encoding + for non-text based MimeParts. + (issue [#598](https://github.com/jstedfast/MimeKit/issues/598)) +* Added work-around for mailers that don't use a ';' between Content-Type + and Content-Disposition parameters. + (issue [#595](https://github.com/jstedfast/MimeKit/issues/595)) +* Added improved error reporting for ArcVerifier. + (issue [#591](https://github.com/jstedfast/MimeKit/issues/591)) +* Added another work-around for parsing Authentication-Results headers. + (issue [#590](https://github.com/jstedfast/MimeKit/issues/590)) +* MimeMessage.ToString() now adds an X-MimeKit-Warning header to the + beginning of the output string to make it clear to developers doing this + that they are Doing it Wrong(tm). +* Added a TLS-Required HeaderId enum value. + +### MimeKit 2.9.1 (2020-07-11) + +* Refactored OpenPgpContext to separate out key storage implementation. + (issue [#576](https://github.com/jstedfast/MimeKit/issues/576)) +* Fixed the TextToFlowed converter. + (issue [#580](https://github.com/jstedfast/MimeKit/issues/580)) +* Protect against ABRs in AuthenticationResults.TryParse(). + (issue [#581](https://github.com/jstedfast/MimeKit/issues/581)) +* The net45 version of MimeKit now depends on Portable.BouncyCastle instead of official + BouncyCastle. +* Added MimeParser events to report stream offsets for MimeMessages and MimeEntities. + (issue [#582](https://github.com/jstedfast/MimeKit/issues/582)) +* Fixed DkimPublicKeyLocatorBase to treat unspecified 'k' values in DKIM DNS records as + "k=rsa". + (issue [#583](https://github.com/jstedfast/MimeKit/issues/583)) +* Fixed date format serializer to use CultureInfo.InvariantCulture. +* Fixed AuthenticationResults parser to allow '_' characters in method results. + (issue [#584](https://github.com/jstedfast/MimeKit/issues/584)) +* Improved RSACng and DSACng support. + +### MimeKit 2.8.0 (2020-05-30) + +* Improved logic for verifying signatures for MimeParts containing mixed line endings. + (issue [#569](https://github.com/jstedfast/MimeKit/issues/569)) +* Fixed MailboxAddress parser to decode IDN-encoded local-parts of email addresses. + (MailKit issue [#1026](https://github.com/jstedfast/MailKit/issues/1026)) +* Added new MailboxAddress.GetAddress(bool idnEncode) method. +* Improved subclassability of OpenPgpContext by making a number of methods virtual. + (issue [#571](https://github.com/jstedfast/MimeKit/issues/571)) +* Added support for RSACng and DSACng. + (issue [#567](https://github.com/jstedfast/MimeKit/issues/567)) +* Dropped Xamarin platforms since they are compatible with netstandard2.0. + +### MimeKit 2.7.0 (2020-05-19) + +* Fixed InternetAddressList.Insert() to allow inserting at the end of the list. + (issue [#559](https://github.com/jstedfast/MimeKit/issues/559)) +* Added ParserOptions.MaxMimeDepth to allow developers to set the max nesting depth + allowed by the parser. +* Added logic to handle multipart children without any headers or content. +* Added a new Verify(bool verifySignatureOnly) method to IDigitalSignature for + developers who just want to be able to verify the signature without worrying + about the certificate chain. +* Fixed MimePart.WriteTo() to avoid canonicalizing line endings for MimeParts that + do not define a Content-Transfer-Encoding. + (issue [#569](https://github.com/jstedfast/MimeKit/issues/569)) +* NuGet packages now include the portable pdb's. + +### MimeKit 2.6.0 (2020-04-03) + +* Fixed the MimeEntity.ContentId setter to use ParseUtils.TryParseMsgId() instead of + MailboxAddress.TryParse() so that it is more lenient in what it accepts. + (issue [#542](https://github.com/jstedfast/MimeKit/issues/542)) +* Added an HtmlTokenizer.IgnoreTruncatedTags property which is useful when working with + truncated HTML. +* Optimized the heck out of HtmlEntityDecoder. +* Added a TextPart.Format property for a quick way to determine the type of text it + contains. +* Added text/plain and text/html preview/snippet generators (PlainTextPreviewer and + HtmlTextPreviewer, respectively). This is part of a larger improvement to MailKit's + text preview feature for IMAP. + (MailKit issue [#1001](https://github.com/jstedfast/MailKit/issues/1001)) +* Fixed SqlCertificateDatabase to accept null SubjectKeyIdentifiers. +* Changed Header.FormatRawValue() to be protected virtual and added Header.SetRawValue() + to allow developers to override the default formatting behavior by either subclassing + Header or by calling header.SetRawValue(). + (issue [#546](https://github.com/jstedfast/MimeKit/issues/546)) +* Switched MimeKit for Android and iOS over to using Portable.BouncyCastle. +* Added MimeTypes.Register() to allow developers to register their own mime-type mappings + to file extensions. + +### MimeKit 2.5.2 (2020-03-14) + +* Updated net46, net47, and net48 builds to reference Portable.BouncyCastle instead of + the standard BouncyCastle package, just like the netstandard builds. + (issue [#540](https://github.com/jstedfast/MimeKit/issues/540)) +* Fixed extraction of TNEF EmbeddedMessage attachment data to skip the leading GUID. + (issue [#538](https://github.com/jstedfast/MimeKit/issues/538)) +* Added a few more TNEF property tags. +* Fixed the HtmlEntityDecoder to require some named attributes to end with a `;`. + +### MimeKit 2.5.1 (2020-02-15) + +* Fixed parsing of email addresses containing unicode or other types of 8-bit text. + (issue [#536](https://github.com/jstedfast/MimeKit/issues/536)) +* Added a MimeTypes.TryGetExtension() method to try and get a file name extension + based on a mime-type. + (issue [#534](https://github.com/jstedfast/MimeKit/issues/534)) +* Updated mime-type mappings. + +### MimeKit 2.5.0 (2020-01-18) + +* Fixed message reserialization after prepending headers. + (issue [#524](https://github.com/jstedfast/MimeKit/issues/524)) +* Added a ContentType.CharsetEncoding property. + (issue [#526](https://github.com/jstedfast/MimeKit/issues/526)) +* Allow empty prop-spec token values in Authentication-Results headers. + (issue [#527](https://github.com/jstedfast/MimeKit/issues/527)) +* Added logic to quote Authentication-Results pvalue tokens if needed. +* Added support for converting RSACng keys into BouncyCastle keys for + net4x versions that support it. +* Added support for RSAES-OAEP for the BouncyCastle backend. + (issue [#528](https://github.com/jstedfast/MimeKit/issues/528)) +* Updated and changed the API for RSASSA-PSS. CmsSigner now has a + RsaSignaturePadding property which obsoletes the previous + RsaSignaturePaddingScheme property. +* Added more columns to the default SQLite database CERTIFICATES table + that allow more optimal SQL searches for certificates given various + matching criteria. +* Fixed WindowsSecureMimeContext.Decrypt() to make sure it doesn't stop + at the first failed recipient. + (issue [#530](https://github.com/jstedfast/MimeKit/issues/530)) +* Fixed splitting and reassembly of message/partial messages. +* Improved handling of Office365 Authentication-Results headers by adding + a Office365AuthenticationServiceIdentifier property to the + AuthenticationMethodResult class. +* Fixed mailbox address parser to be more lenient about `"["` and `"]"` + characters in the display-name. + (issue [#532](https://github.com/jstedfast/MimeKit/issues/532)) + +### MimeKit 2.4.1 (2019-11-10) + +* Don't use PublicSign on non-Windows NT machines when building. + (issue [#516](https://github.com/jstedfast/MimeKit/issues/516)) +* Improved BouncyCastleSecureMimeContext logic for building certificate chains so that + certificate chains are included in the S/MIME signature. + (issue [#515](https://github.com/jstedfast/MimeKit/issues/515)) +* Improved SqlCertificateDatabase.Find() by using more IX509Selector properties. +* Relaxed the Authentication-Results header parser a bit to allow '/' in pvalue tokens. + (issue [#518](https://github.com/jstedfast/MimeKit/issues/518)) + +### MimeKit 2.4.0 (2019-11-02) + +* Added the `text/csv` mime-type to the `MimeTypes` mapping table for files with a .csv extension. +* Expanded the .NETStandard API to match the .NET 4.5 API, so .NETStandard is now complete. +* Dropped support for .NETPortable and WindowsPhone/Universal v8.1. +* Added a net48 assembly to the NuGet package. +* Improved HTML tokenizer performance. +* Fixed X509Crl.IsDelta for CRLs without extensions. + (issue [#513](https://github.com/jstedfast/MimeKit/issues/513)) +* Added support for `message/global-delivery-status`, `message/global-disposition-notification`, + and `message/global-headers` to `MimeParser`. + (issue [#514](https://github.com/jstedfast/MimeKit/issues/514)) +* Fixed S/MIME signatures generated by a TemporarySecureMimeContext to include the certificate chain. + (issue [#515](https://github.com/jstedfast/MimeKit/issues/515)) + +### MimeKit 2.3.2 (2019-10-12) + +* Fixed reserialization of message/rfc822 parts to not add an extra new-line sequence + to the end of the message. (issue [#510](https://github.com/jstedfast/MimeKit/issues/510)) +* Fixed DefaultSecureMimeContext to build the cert chain outside of the private key query. + (issue [#508](https://github.com/jstedfast/MimeKit/issues/508)) +* Modified the Message-Id parser to gobble ctrl chars in the local-part. +* Fixed some buglets in the TextToFlowed converter involving space-stuffing lines. +* Fixed BodyBuilder logic for constructing a body with an HtmlBody set to string.Empty. + (issue [#506](https://github.com/jstedfast/MimeKit/issues/506)) +* Fixed potential memory leaks in WindowsSecureMimeContext and BouncyCastleSecureMimeContext + in the Export() methods in cases where an exception is throw while adding certificates. +* Removed MimeKit.Cryptography.NpgsqlCertificateDatabase. It is unlikely anyone actually + uses this. + +### MimeKit 2.3.1 (2019-09-08) + +* Updated CmsSigner's default DigestAlgorithm to Sha256 instead of Sha1 to match + System.Security.Cryptography.Pkcs.CmsSigner's default. +* Updated WindowsSecureMimeContext to default to IssuerAndSerialNumber for + System.Security.Cryptography.Pkcs.CmsSigner. +* Added support for the RSASSA-PSS signature padding algorithm when using the + BouncyCastle backend. +* Improved robustness of TNEF processing of email address fields. +* Modified FilteredStream.Flush*() to not flush the source stream. + (MailKit issue [#904](https://github.com/jstedfast/MailKit/issues/904)) +* Added net46 and net47 assemblies to the NuGet package. + +### MimeKit 2.3.0 (2019-08-24) + +* Fixed MultipartRelated to fall back to the multipart/related type parameter when + locating the Root. (issue [#489](https://github.com/jstedfast/MimeKit/issues/489)) +* Improved Authentication-Results parser to handle non-standard syntax. + (issue [#490](https://github.com/jstedfast/MimeKit/issues/490)) +* When FormatOptions.AllowMixedHeaderCharsets is disabled, always use the user-specified + charset. Previously this could/would still use us-ascii and/or iso-8859-1 if the entire + header could fit within one of those charsets. + (issue [#493](https://github.com/jstedfast/MimeKit/issues/493)) +* Fixed the line length calculations in the BestEncodingFilter. + (issue [#497](https://github.com/jstedfast/MimeKit/issues/497)) +* Fixed Multipart to properly ensure the epilogue ends w/ a new-line when + FormatOptions.EnsureNewLine is true. + (issue [#499](https://github.com/jstedfast/MimeKit/issues/499)) +* Modified Multipart.WriteTo[Async] to not ensure that a Content-Type boundary parameter + has been set. This code-path was only hit if the multipart was parsed by the parser and + did not have a boundary parameter in the first place. In the interest of preserving + byte-for-byte compatibility with the original input, this sanity check has been removed. + (issue [#499](https://github.com/jstedfast/MimeKit/issues/499)) + +### MimeKit 2.2.0 (2019-06-11) + +* Added support for [ARC](https://arc-spec.org). +* Added AuthenticationResults class for parsing and constructing Authentication-Results and + ARC-Authentication-Results headers. +* Added support for the Ed25519-SHA256 DKIM signature algorithm. +* Obsoleted MimeMessage DKIM API's in favor of the newer DKIM API's: + - MimeMessage.Sign (DkimSigner, ...) has been replaced by DkimSigner.Sign (MimeMessage, ...). + - MimeMessage.Verify (Header, ...) has been replaced by DkimVerifier.Verify (MimeMessage, Header, ...). +* Added DkimPublicKeyLocatorBase to help simplify implementing IDkimPublicKeyLocator. + +### MimeKit 2.1.5 (2019-05-13) + +* Updated the BouncyCastle assemblies to version 1.8.5 for iOS and Android. +* Fixed a possible NullReferenceException when decoding S/MIME digital signatures. +* Fixed the netstandard2.0 dependencies to no longer explicitly include System.Net.Http. + (issue [#482](https://github.com/jstedfast/MimeKit/issues/482)) +* Override Equals(object) and GetHashCode() for InternetAddress and InternetAddressList. + (issue [#481](https://github.com/jstedfast/MimeKit/issues/481)) +* Fixed TnefReader.Dispose() to avoid a potential NullReferenceException if double disposed. +* Fixed the Message-Id, Content-Id, References and In-Reply-To parsers to be more liberal + in what they accept in terms of the `msg-id` token. +* Changed the Header encoding logic for the In-Reply-To header to not rfc2047 encode the value + even if it is longer than the suggested line-length. + (issue [#479](https://github.com/jstedfast/MimeKit/issues/479)) +* Reduced netstandard dependencies. (issue [#475](https://github.com/jstedfast/MimeKit/issues/475)) + +### MimeKit 2.1.4 (2019-04-13) + +* Added a setter for FormatOptions.MaxLineLength, allowing developers to override this value. +* Improved TNEF handling of Content-Disposition and Content-Id properties. + (issue [#470](https://github.com/jstedfast/MimeKit/pull/470) and + issue [#471](https://github.com/jstedfast/MimeKit/pull/471)) +* Improved Content-Id parser to be more forgiving with improperly formatted IDs. + (issue [#472](https://github.com/jstedfast/MimeKit/issue/472)) +* Added support for the text/rfc822-headers MIME-type via the new TextRfc822Headers class. + (issue [#474](https://github.com/jstedfast/MimeKit/issue/474)) +* Added fallback logic for international email addresses that are not properly encoded in UTF-8. + (issue [#477](https://github.com/jstedfast/MimeKit/issue/477)) + +### MimeKit 2.1.3 (2019-02-24) + +* Fixed an NRE in X509CertificateDatabase.Dispose(). +* Fixed TextPart.Text and GetText() to properly canonicalize EOLN for multi-byte charsets + such as UTF-16. (issue [#442](https://github.com/jstedfast/MimeKit/issues/442)) +* Fixed System.Net.Mail.MailMessage cast to MimeMessage when the ContentStream of + the attachments has not been rewound to the beginning of the stream. + (issue [#467](https://github.com/jstedfast/MimeKit/issues/467)) +* Changed ParserOptions.AllowAddressesWithoutDomain to work as users expected and + moved the old logic into ParserOptions.AllowUnquotedCommasInAddresses. + (issue [#465](https://github.com/jstedfast/MimeKit/issues/465)) + +### MimeKit 2.1.2 (2018-12-30) + +* Fixed WindowsSecureMimeDigitalCertificate logic for ECDsa. +* Added X509Certificate.GetPublicKeyAlgorithm() extension method. +* Modified ApplicationPkcs7Mime to be less strict about the smime-type. + +### MimeKit 2.1.1 (2018-12-16) + +* Mapped the TNEF Sensitivity property to the Sensitivity message header when calling + TnefPart.ConvertToMessage(). +* Fixed the TNEF Importance and Priority mappings when calling TnefPart.ConvertToMessage(). +* Added more TnefPropertyId's that have been identified. +* Map PidTagTnefCorrelationKey to the Message-Id message header. +* When the TNEF data does not have a SentDate property, set the MimeMessage.Date property + to DateTimeOffset.MinValue instead of DateTimeOffset.Now. +* Fixed TnefPart.ConvertToMessage() to check the TNEF SubjectPrefix and NormalizedSubject + properties and use them if a TNEF Subject property is not available. +* Fixed TNEF logic for extracting attachment content to not truncate some bytes from the beginning + of the content. +* Added more fallbacks for attempting to extract the sender information out of the TNEF data. +* Bumped Android and iOS versions of BouncyCastle to v1.8.4. + +### MimeKit 2.1.0 (2018-12-01) + +* Optimized SecureMimeCryptographyContext.Supports() and OpenPgpCryptographyContext.Supports() + implementations. +* Optimized the OptimizedOrdinalIgnoreCaseComparer even more. +* Fixed OpenPgpDigitalCertificate.ExpirationDate for PGP keys that never expire. +* Reduced string allocations in MultipartSigned.Verify() and MultipartEncrypted.Decrypt(). +* Fixed OpenPgpContext.Decrypt() to make sure to always clean up MemoryBlockStreams. +* Added a bunch more HeaderId enum values. +* Improved header folding logic for headers with long words. + (issue [#451](https://github.com/jstedfast/MimeKit/issues/451)) + +### MimeKit 2.0.7 (2018-10-28) + +* Fixed a bug in the UUEncoder. +* Fixed a bug in MimeIterator.MoveTo(). +* Modified BodyBuilder.ToMessageBody() to avoid returning a multipart/mixed with only a single + child. (issue [#441](https://github.com/jstedfast/MimeKit/issues/441)) +* Modified TnefPart to no longer set the name parameter on the Content-Type header of + extracted message bodies. (issue [#435](https://github.com/jstedfast/MimeKit/issues/435)) +* Fixed various locations that loaded content from files to use FileShare.Read so as to avoid file + sharing violations if the application already has that file opened elsewhere. (issue [#426](https://github.com/jstedfast/MimeKit/issues/426)) +* Improved address parser to handle "local-part (User Name)" style addresses. +* Updated the iOS and Android BouncyCastle dependency to 1.8.3. +* Modified TextPart.Text and GetText() to canonicalize the newlines. (issue [#442](https://github.com/jstedfast/MimeKit/issues/442)) +* Fixed WindowsSecureMimeContext.EncapsulatedSign (CmsSigner, ...) and Sign (CmsSigner, ...). +* Added SecureMimeContext.Import(string, string) to import passworded pk12 files. +* Improved MimeParser's support of Content-Length. +* Fixed MimeParser.ParseEntity() and MimeEntity.Load() to throw a FormatException if the + stream does not have properly formatted headers. (issue [#443](https://github.com/jstedfast/MimeKit/issues/443)) +* Added support for message/global. + +### MimeKit 2.0.6 (2018-08-04) + +* Added more bounds checking for parsing mailbox addresses to fix IndexOutOfRangeExceptions + given an incomplete address like "Name <". (issue [#421](https://github.com/jstedfast/MimeKit/issues/421)) +* Fixed support for parsing mbox files using Content-Length. +* Modified the TextPart.Text getter property to check for a UTF-16 BOM and use an appropriate + UTF-16 System.Text.Encoding if found instead of simply assuming UTF-8 and falling back to + iso-8859-1. (issue [#417](https://github.com/jstedfast/MimeKit/issues/417)) +* Minor optimizations. + +### MimeKit 2.0.5 (2018-07-07) + +* Make sure messages created from System.Net.Mail.MailMessages have a Date header. (MailKit issue [#710](https://github.com/jstedfast/MailKit/issues/710)) +* Allow developers to pass in their own SecureRandom when generating PGP key pairs. (issue [#404](https://github.com/jstedfast/MimeKit/issues/404)) +* Modified MemoryBlockStream to use a shared buffer pool to relieve pressure on the GC. (MailKit issue [#725](https://github.com/jstedfast/MailKit/issues/725)) + +### MimeKit 2.0.4 (2018-05-21) + +* The default value of the `CheckCertificateRevocation` property located on the + `BouncyCastleSecureMimeContext` has been changed to `false` due to privacy concerns noted + in the Efail document published in May of 2018. Clients that + wish to continue automatic downloads of S/MIME CRLs can manually set the property to `true`. +* Properly wrap long mailbox names with quoted phrases. +* Fixed parsing of header blocks that span across read boundaries. (issue [#395](https://github.com/jstedfast/MimeKit/issues/395)) +* Added FormatOptions.EnsureNewLine property (MailKit issue [#251](https://github.com/jstedfast/MailKit/issues/251)) +* Enable System.Net.Mail support for .NET Core 2.0. (issue [#393](https://github.com/jstedfast/MimeKit/issues/393)) + +### MimeKit 2.0.3 (2018-04-15) + +* Allow empty TextBody and HtmlBody properties for BodyBuilder. (issue [#391](https://github.com/jstedfast/MimeKit/issues/391)) +* Fixed BodyBuilder.Attachments.Add() to properly handle message/rfc822 attachments. +* Fixed HTML entity encoder logic when a surrogate pair is at the end of the input. (issue [#385](https://github.com/jstedfast/MimeKit/issues/385)) + +### MimeKit 2.0.2 (2018-03-18) + +* IDN encode/decode the local part of mailbox addresses as well. (MailKit issue [#649](https://github.com/jstedfast/MailKit/issues/649)) +* Added a record for .epub to the MimeTypes database. (issue [#376](https://github.com/jstedfast/MimeKit/issues/376)) +* Explicitly pass 'false' as the silent argument to SignedCms.ComputeSignature(). (issue [#374](https://github.com/jstedfast/MimeKit/issues/374)) +* Make sure the MimeParser does not hang if the last header line is truncated before CRLF. +* Don't use Encoder/DecoderExceptionFallbacks in the TNEF reader. (issue [#370](https://github.com/jstedfast/MimeKit/issues/370)) +* Provide a better error message when the cert within a pkcs12 cannot digital sign. (issue [#367](https://github.com/jstedfast/MimeKit/issues/367)) +* Fixed TemporarySecureMimeContext to key off the certificate's fingerprint. + +### MimeKit 2.0.1 (2018-01-06) * Improved the HTML parser logic to better handle a number of edge cases. * MimeKit will now automatically download CRLs based on the CRL Distribution Point @@ -17,7 +408,7 @@ specifying the symmetric key algorithm to use in generating the key pair. This defaults to AES-256, which is the same value used in older versions of MimeKit. -### MimeKit 2.0.0 +### MimeKit 2.0.0 (2017-12-22) * Added IDkimPublicKeyLocator.LookupPublicKeyAsync() and MimeMessage.VerifyAsync() to support asynchronous DNS lookups of DKIM public keys. @@ -33,17 +424,17 @@ * Renamed the MimePart.ContentObject property to MimePart.Content. * Dropped support for .NET 3.5 and .NET 4.0. -### MimeKit 1.22.0 +### MimeKit 1.22.0 (2017-11-24) -* Fixed a buffering bug in MimeParser's header parser. (issue #358) -* Set the TnefReader charset on extracted text/plain and text/html bodies. (issue #357) +* Fixed a buffering bug in MimeParser's header parser. (issue [#358](https://github.com/jstedfast/MimeKit/issues/358)) +* Set the TnefReader charset on extracted text/plain and text/html bodies. (issue [#357](https://github.com/jstedfast/MimeKit/issues/357)) * Added safeguard to protect against malformed nested group addresses which could cause a stack overflow in the parser. ParserOptions now has a way of limiting the recursive - depth of rfc822 group addresses using the MaxAddressGroupDepth property. (issue #355) + depth of rfc822 group addresses using the MaxAddressGroupDepth property. (issue [#355](https://github.com/jstedfast/MimeKit/issues/355)) * Fixed the S/MIME certificate database for .NETStandard by using GetFieldValue() instead - of GetBytes() which is not supported on .NETStandard. (issue #351) + of GetBytes() which is not supported on .NETStandard. (issue [#351](https://github.com/jstedfast/MimeKit/issues/351)) -### MimeKit 1.20.0 +### MimeKit 1.20.0 (2017-10-28) * Added async support for writing MimeMessage, MimeEntity, HeaderList and ContentObject. * Added async support for parsing MimeMessage, MimeEntity, and HeaderList. @@ -51,16 +442,16 @@ * Removed methods marked [Obsolete] (which have been marked obsolete for several years now). * Improved performance of writing messages by a small amount. * Fixed SecureMimeDigitalSignature to capture the signature digest algorithm used by the sending - client. (issue #341) + client. (issue [#341](https://github.com/jstedfast/MimeKit/issues/341)) * Fixed the S/MIME decoder to correctly determine the RC2 algorithm used by the sending client. - (issue #337) + (issue [#337](https://github.com/jstedfast/MimeKit/issues/337)) * Fixed a bug in BoundStream.Seek(). -### MimeKit 1.18.1 +### MimeKit 1.18.1 (2017-09-03) * Added CanSign() and CanEncrypt() methods to CryptographyContext for checking - whether or not a mailbox can be used for signing or be encrypted to. (issue #325) -* Automatically register the CodePagesEncodingProvider when running on .NETStandard. (issue #330) + whether or not a mailbox can be used for signing or be encrypted to. (issue [#325](https://github.com/jstedfast/MimeKit/issues/325)) +* Automatically register the CodePagesEncodingProvider when running on .NETStandard. (issue [#330](https://github.com/jstedfast/MimeKit/issues/330)) * Fixed MimeMessage.TextBody to return null when the top-level MIME part is a TextPart marked as an attachment. * Fixed the HtmlToHtml converter to suppress comments if the HtmlTagContext's SuppressInnerContent @@ -68,35 +459,35 @@ * Documented OpenPgpContext.GenerateKeyPair() which was added in 1.18.0. * Added OpenPgpContext.Delete() methods to delete public and secret keyrings. * Added OpenPgpContext.SignKey(). -* Remove "Version:" header from armored OpenPGP output. (issue #319) +* Remove "Version:" header from armored OpenPGP output. (issue [#319](https://github.com/jstedfast/MimeKit/issues/319)) -### MimeKit 1.18.0 +### MimeKit 1.18.0 (2017-08-07) -* Allow importing of known PGP keys (needed when re-importing keys after signing them). (issue #315) +* Allow importing of known PGP keys (needed when re-importing keys after signing them). (issue [#315](https://github.com/jstedfast/MimeKit/issues/315)) * Added APIs to enumerate public and secret PGP keys. * Added an OpenPgpDetectionFilter to detect OpenPGP blocks and their stream offsets. * Added a MimeMessage.WriteTo() overload that takes a bool headersOnly argument. * Pushed SecureMimeContext's EncryptionAlgorithm preferences down into CryptographyContext. * Updated GnuPGContext to load algorithm preferences from gpg.conf. * Fixed TemporarySecureMimeContext to use the fingerprint in the certificate lookup methods - when the MailboxAddress argument is a SecureMailboxAddress. (issue #322) -* Fall back to using the Subject Alternative Rfc822 Name if the SubjectEmailAddress fails. (issue #323) + when the MailboxAddress argument is a SecureMailboxAddress. (issue [#322](https://github.com/jstedfast/MimeKit/issues/322)) +* Fall back to using the Subject Alternative Rfc822 Name if the SubjectEmailAddress fails. (issue [#323](https://github.com/jstedfast/MimeKit/issues/323)) -### MimeKit 1.16.2 +### MimeKit 1.16.2 (2017-07-01) -* Fixed a bug in the MailMessage to MimeMessage conversion which corrupted the Subject string. (issue #306) +* Fixed a bug in the MailMessage to MimeMessage conversion which corrupted the Subject string. (issue [#306](https://github.com/jstedfast/MimeKit/issues/306)) * If no KeyUsage extension exists for an X509 certificate, assume no restrictions on key usage. * Throw an exception if there is a problem building an X509 certificate chain when verifying S/MIME signatures. -### MimeKit 1.16.1 +### MimeKit 1.16.1 (2017-05-05) * Fixed TextToHtml and FlowedToHtml's OutputHtmlFragment property to work. -* Fixed EncodeAddrspec and DecodeAddrspec to handle string.Empty. (issue #302) -* Allow string.Empty as a valid addrspec for MailboxAddress. (issue #302) -* Catch exceptions trying to import CRLs and Certs when verifying S/MIME signatures. (issue #304) +* Fixed EncodeAddrspec and DecodeAddrspec to handle string.Empty. (issue [#302](https://github.com/jstedfast/MimeKit/issues/302)) +* Allow string.Empty as a valid addrspec for MailboxAddress. (issue [#302](https://github.com/jstedfast/MimeKit/issues/302)) +* Catch exceptions trying to import CRLs and Certs when verifying S/MIME signatures. (issue [#304](https://github.com/jstedfast/MimeKit/issues/304)) -### MimeKit 1.16.0 +### MimeKit 1.16.0 (2017-04-21) * Added new ParserOptions option to allow local-only mailbox addresses (e.g. no @domain). * Improved address parser to interpret unquoted names containing commas in email addresses @@ -104,7 +495,7 @@ * Greatly improved the WindowsSecureMimeContext backend. * A number of fixes to bugs exposed by an ever-increasing set of unit tests (up to 87% coverage). -### MimeKit 1.14.0 +### MimeKit 1.14.0 (2017-04-09) * Added International Domain Name support for email addresses. * Added a work-around for mailers that didn't provide a disposition value in a @@ -113,14 +504,14 @@ header. * Added automatic key retrieval functionality for the GnuPG crypto context. * Added a virtual DigestSigner property to DkimSigner so that consumers can hook into services - such as Azure. (issue #296) + such as Azure. (issue [#296](https://github.com/jstedfast/MimeKit/issues/296)) * Fixed a bug in the MimeFilterBase.SaveRemainingInput() logic. * Preserve munged From-lines at the start of message/rfc822 parts. * Map code page 50220 to iso-2022-jp. * Format Reply-To and Sender headers as address headers when using Header.SetValue(). -* Fixed MimeMessage.CreateFromMailMessage() to set MimeVersion. (issue #290) +* Fixed MimeMessage.CreateFromMailMessage() to set MimeVersion. (issue [#290](https://github.com/jstedfast/MimeKit/issues/290)) -### MimeKit 1.12.0 +### MimeKit 1.12.0 (2017-03-12) * Added new DKIM MimeMessage.Sign() methods that take an IList of header field names to sign. @@ -130,43 +521,43 @@ mailbox and group addresses. * Added support for CryptographyContext factories by adding new Register() methods that take function callbacks that return a SecureMimeContext or OpenPgpContext. Thanks to - Christoph Enzmann for this feature. (issue #283) + Christoph Enzmann for this feature. (issue [#283](https://github.com/jstedfast/MimeKit/issues/283)) * Fixed DefaultSecureMimeContext..cctor() to not call Directory.CreateDirectory() on the default database directory. Instead, let the .ctor() create it instead if and when - an instance of the DefaultSecureMimeContext is created. (issue #285) + an instance of the DefaultSecureMimeContext is created. (issue [#285](https://github.com/jstedfast/MimeKit/issues/285)) * Store DBNull in S/MIME SQL backends for null values (SQLite handles `null` but - databases such as Postgres do not). (issue #286) + databases such as Postgres do not). (issue [#286](https://github.com/jstedfast/MimeKit/issues/286)) -### MimeKit 1.10.1 +### MimeKit 1.10.1 (2017-01-28) * Fixed the Content-Type and Content-Disposition parameter parser to remove trailing lwsp from - unquoted parameter values. (issue #278) + unquoted parameter values. (issue [#278](https://github.com/jstedfast/MimeKit/issues/278)) * Fixed MimePart.WriteTo() to not necessarily force the content to end with a new-line. -### MimeKit 1.10.0 +### MimeKit 1.10.0 (2016-10-31) * Fixed OpenPgpContext.Verify() to throw FormatException if no data packets found. -* Added new MailboxAddress constructors that do not take a 'name' argument. (issue #267) -* Added an HtmlToHtml.FilterComments property to remove comments. (issue #271) +* Added new MailboxAddress constructors that do not take a 'name' argument. (issue [#267](https://github.com/jstedfast/MimeKit/issues/267)) +* Added an HtmlToHtml.FilterComments property to remove comments. (issue [#271](https://github.com/jstedfast/MimeKit/issues/271)) * Modified address parser to handle invalid addresses like "user@example.com ". -### MimeKit 1.8.0 +### MimeKit 1.8.0 (2016-09-25) * Improved parsing of malformed mailbox addresses. * Added DecompressTo() and DecryptTo() methods to SecureMimeContext. * Fixed MessagePartial.Split(). -### MimeKit 1.6.0 +### MimeKit 1.6.0 (2016-09-11) * Use RandomNumberGenerator.Create() for .NET Core instead of System.Random when generating multipart boundaries. -### MimeKit 1.4.2 +### MimeKit 1.4.2 (2016-08-14) * Strong-name the .NET Core assemblies. -* Fixed logic for selecting certificates from the Windows X.509 Store. (issue #262) +* Fixed logic for selecting certificates from the Windows X.509 Store. (issue [#262](https://github.com/jstedfast/MimeKit/issues/262)) -### MimeKit 1.4.1 +### MimeKit 1.4.1 (2016-07-17) * Fixed QuotedPrintableDecoder to handle soft breaks that fall on a buffer boundary. * Fixed MimeMessage.WriteTo() to properly respect the FormatOptions when writing the @@ -176,38 +567,38 @@ * Added new TextPart .ctor that takes a TextFormat argument so that developers that don't understand mime-types can more easily intuit what that argument should be. -### MimeKit 1.4.0 +### MimeKit 1.4.0 (2016-07-01) * Added support for .NET Core 1.0 * Changed the default value of FormatOptions.AllowMixedHeaderCharsets to false. -* Added a new DkimSigner .ctor that takes a stream of key data. (issue #255) +* Added a new DkimSigner .ctor that takes a stream of key data. (issue [#255](https://github.com/jstedfast/MimeKit/issues/255)) -### MimeKit 1.2.25 +### MimeKit 1.2.25 (2016-06-16) -* Fixed parsing bugs in MessageDeliveryStatus.StatusGroups. (issue #253) -* Fixed MimeParser.ParseHeaders() to handle header blocks that do not end with a blank line. (issue #250) +* Fixed parsing bugs in MessageDeliveryStatus.StatusGroups. (issue [#253](https://github.com/jstedfast/MimeKit/issues/253)) +* Fixed MimeParser.ParseHeaders() to handle header blocks that do not end with a blank line. (issue [#250](https://github.com/jstedfast/MimeKit/issues/250)) * Fixed the MailboxAddress parser to handle whitespace between '<' and the addr-spec. -* Fixed TemporarySecureMimeContext to handle certificates with null email addresses. (issue #252) +* Fixed TemporarySecureMimeContext to handle certificates with null email addresses. (issue [#252](https://github.com/jstedfast/MimeKit/issues/252)) -### MimeKit 1.2.24 +### MimeKit 1.2.24 (2016-05-22) -* Modified MimeMessage .ctor to not add an empty To: header by default. (issue #241) +* Modified MimeMessage .ctor to not add an empty To: header by default. (issue [#241](https://github.com/jstedfast/MimeKit/issues/241)) * Modified MimeMessage to remove address headers when all addresses in that field are removed. * Properly apply SecurityCriticalAttribute to GetObjectData() on custom Exceptions. * Fixed TnefPropertyReader to convert APPTIME values into DateTimes from the OLE Automation - Date format. (issue #245) + Date format. (issue [#245](https://github.com/jstedfast/MimeKit/issues/245)) -### MimeKit 1.2.23 +### MimeKit 1.2.23 (2016-05-07) -* Modified ParamaterList.TryParse() to handle quoted rfc2231-encoded param values. (issue #239) +* Modified ParamaterList.TryParse() to handle quoted rfc2231-encoded param values. (issue [#239](https://github.com/jstedfast/MimeKit/issues/239)) * Updated to reference BouncyCastle via NuGet packages rather than bundling the assemblies. * Fixed MimeParser to set a multipart's raw epilogue to null instead of an empty byte array. Fixes some issues with digital signature verification (as well as DKIM verification). * Added an HtmlWriter.WriteText() override with Console.WriteLine() style params. * Added convenience MimeMessage property for the X-Priority header. -* Fixed MimeMessage.ConvertFromMailMessage() to use appropriate MimeEntity subclasses. (issue #232) +* Fixed MimeMessage.ConvertFromMailMessage() to use appropriate MimeEntity subclasses. (issue [#232](https://github.com/jstedfast/MimeKit/issues/232)) -### MimeKit 1.2.22 +### MimeKit 1.2.22 (2016-02-28) * Added a new SecureMimeContext.Verify() overload that returns the extracted content stream. * Exposed the SecureMimeContext.GetDigitalSignatures() method as protected, allowing custom @@ -221,22 +612,22 @@ file or stream. * Fixed UrlScanner to properly deal with IPv6 literals in email addresses. -### MimeKit 1.2.21 +### MimeKit 1.2.21 (2016-02-13) * Added a MultipartReport class for multipart/report. -* Fixed serialization for embedded message/* parts. (issue #228) +* Fixed serialization for embedded message/* parts. (issue [#228](https://github.com/jstedfast/MimeKit/issues/228)) * Fixed MimeMessage.WriteTo() to only make sure that the stream ends with a newline if it - wasn't parsed. (issue #227) + wasn't parsed. (issue [#227](https://github.com/jstedfast/MimeKit/issues/227)) * Fixed MimeMessage to only set a MIME-Version if the message was not produced by the parser. * Ignore timezones outside the range of -1200 to +1400. * Added InternetAddress.Clone() to allow addresses to be cloned. * Properly serialize message/rfc822 parts that contain an mbox marker. -* Fixed MimeMessage.DkimSign() to not enforce 7bit encoding of the body. (issue #224) +* Fixed MimeMessage.DkimSign() to not enforce 7bit encoding of the body. (issue [#224](https://github.com/jstedfast/MimeKit/issues/224)) * Fixed ParameterList.IndexOf(string) to be case insensitive. -### MimeKit 1.2.20 +### MimeKit 1.2.20 (2016-01-24) -* Fixed serialization of mime parts with empty content. (issue #221) +* Fixed serialization of mime parts with empty content. (issue [#221](https://github.com/jstedfast/MimeKit/issues/221)) * Fixed a bug in the TnefPropertyReader that would break when not all properties were read by the consumer of the API. * Fixed the InternetAddress parser to throw a more informative error when parsing broken @@ -247,7 +638,7 @@ * Fixed HtmlUtils.HtmlAttributeEncode() to properly encode non-ascii characters as entities. * Fixed HtmlUtils.HtmlEncode() to properly encode non-ascii characters as entities. * Fixed MimeParser to track whether or not each multipart had an end boundary so that - when they get reserialized, they match the original. (issue #218) + when they get reserialized, they match the original. (issue [#218](https://github.com/jstedfast/MimeKit/issues/218)) * Implemented an optimized OrdinalIgnoreCase string comparer which improves the performance of the MimeParser slightly. * Fixed QuotedPrintableDecoder to properly handle "==" sequences. @@ -256,100 +647,100 @@ * Fixed MimeParser to trim the CR from the mbox From marker. * Fixed SqlCertificateDatabase to properly chain Dispose. -### MimeKit 1.2.19 +### MimeKit 1.2.19 (2016-01-01) -* Handle illegal Content-Id headers that do not enclose their values in <>'s. (issue #215) -* Fixed reserialization of MimeParts with empty content. (issue #213) +* Handle illegal Content-Id headers that do not enclose their values in <>'s. (issue [#215](https://github.com/jstedfast/MimeKit/issues/215)) +* Fixed reserialization of MimeParts with empty content. (issue [#213](https://github.com/jstedfast/MimeKit/issues/213)) * Improved parsing logic for malformed Content-Type headers. * Fixed HtmlTokenizer to work properly when some closing tags were not lowercase. * Bumped Bouncy Castle to v1.8.1. -### MimeKit 1.2.18 +### MimeKit 1.2.18 (2015-12-16) * Removed unimplemented TNEF APIs. * Use DateTime.UtcNow for S/MIME certificate validity checks. * Added ToString() methods on ContentType/Disposition that take FormatOptions. -* Added a new ToString() method to InternetAddress that takes a FormatOptions. (issue #208) -* Added a MimeEntity.WriteTo() method that takes a bool contentOnly parameter. (issue #207) +* Added a new ToString() method to InternetAddress that takes a FormatOptions. (issue [#208](https://github.com/jstedfast/MimeKit/issues/208)) +* Added a MimeEntity.WriteTo() method that takes a bool contentOnly parameter. (issue [#207](https://github.com/jstedfast/MimeKit/issues/207)) * Added support for encoding parameter values using rfc2047 encoded-words instead of the standard rfc2231 encoding. * Fixed SecureMailboxAddress's Fingerprint property to work with both the PGP key ID - *and* the fingerprint. Previously only worked with the PGP key id. (issue #203) -* Added GroupAddress.Parse() and MailboxAddress.Parse() methods. (issue #197) -* Set a default filename when generating application/pgp-signature parts. (issue #195) + *and* the fingerprint. Previously only worked with the PGP key id. (issue [#203](https://github.com/jstedfast/MimeKit/issues/203)) +* Added GroupAddress.Parse() and MailboxAddress.Parse() methods. (issue [#197](https://github.com/jstedfast/MimeKit/issues/197)) +* Set a default filename when generating application/pgp-signature parts. (issue [#195](https://github.com/jstedfast/MimeKit/issues/195)) -### MimeKit 1.2.17 +### MimeKit 1.2.17 (2015-12-05) * Fixed DkimRelaxedBodyFilter to properly handle CRLF split across buffers. * Added ContentType.IsMimeType method to replace CongtentType.Matches. * Added S/MIME, PGP and DKIM support to the PCL and WindowsUniversal versions of MimeKit. -* Fixed PGP key expiration calculation when encrypting. (issue #194) +* Fixed PGP key expiration calculation when encrypting. (issue [#194](https://github.com/jstedfast/MimeKit/issues/194)) -### MimeKit 1.2.16 +### MimeKit 1.2.16 (2015-11-29) -* Fixed relaxed body canonicalization logic for DKIM signatures. (issue #190) +* Fixed relaxed body canonicalization logic for DKIM signatures. (issue [#190](https://github.com/jstedfast/MimeKit/issues/190)) -### MimeKit 1.2.15 +### MimeKit 1.2.15 (2015-11-22) * Fixed the Date parser to catch exceptions thrown by the DateTimeOffset .ctor if any of the fields are out of range. -* Fixed logic for trimming trailing blank lines for the DKIM relaxed body algorithm. (issue #187) -* Fixed DKIM body filters to reserve extra space in the output buffer. (issue #188) +* Fixed logic for trimming trailing blank lines for the DKIM relaxed body algorithm. (issue [#187](https://github.com/jstedfast/MimeKit/issues/187)) +* Fixed DKIM body filters to reserve extra space in the output buffer. (issue [#188](https://github.com/jstedfast/MimeKit/issues/188)) * Allow specifying a charset encoding for each Content-Type/Disposition parameter. -### MimeKit 1.2.14 +### MimeKit 1.2.14 (2015-10-18) * Fixed DKIM-Signature signing logic to use a UTC-based timestamp value rather than a - timestamp based on the local-time. (issue #180) + timestamp based on the local-time. (issue [#180](https://github.com/jstedfast/MimeKit/issues/180)) * Fixed Multipart epilogue parsing and serialization logic to make sure that serializing a multipart is properly byte-for-byte identical to the original text. This fixes a corner-case that affected all types of digital signatures (DKIM, PGP, and S/MIME) - spanning across nested multiparts. (issue #181) + spanning across nested multiparts. (issue [#181](https://github.com/jstedfast/MimeKit/issues/181)) * Fixed MimeMessage.WriteTo() to ensure that the output stream always ends with a new-line. -### MimeKit 1.2.13 +### MimeKit 1.2.13 (2015-10-11) * Modified Base64Encoder's .ctor to allow specifying a maxLineLength. -* Fixed DKIM signing logic for multipart/alternative messages. (issue #178) +* Fixed DKIM signing logic for multipart/alternative messages. (issue [#178](https://github.com/jstedfast/MimeKit/issues/178)) -### MimeKit 1.2.12 +### MimeKit 1.2.12 (2015-09-20) * Prevent infinite loop when flushing CharsetFilter when there is no input data left. -### MimeKit 1.2.11 +### MimeKit 1.2.11 (2015-09-06) -* Fixed an IndexOutOfRangeException bug in the TextToHTML converter logic. (issue #165) +* Fixed an IndexOutOfRangeException bug in the TextToHTML converter logic. (issue [#165](https://github.com/jstedfast/MimeKit/issues/165)) * Fixed the DKIM-Signature verification logic to be more lenient in parsing DKIM-Signature - headers. (issue #166) + headers. (issue [#166](https://github.com/jstedfast/MimeKit/issues/166)) * Fixed the DKIM-Signature verification logic to error-out if the h= parameter does not - include the From header. (issue #167) + include the From header. (issue [#167](https://github.com/jstedfast/MimeKit/issues/167)) * Fixed the DKIM-Signature verification logic to make sure that the domain-name in the i= - param matches (or is a subdomain of) the d= value. (issue #169) + param matches (or is a subdomain of) the d= value. (issue [#169](https://github.com/jstedfast/MimeKit/issues/169)) * Fixed the CharsetFilter to avoid calling Convert() on empty input. * Fixed logic for canonicalizing header values using the relaxed DKIM algorithm. - (issue #171) + (issue [#171](https://github.com/jstedfast/MimeKit/issues/171)) * Fixed AttachmentCollection to mark embedded parts as inline instead of attachment. * Fixed the DKIM-Signature logic (both signing and verifying) to properly canonicalize the - body content. (issue #172) + body content. (issue [#172](https://github.com/jstedfast/MimeKit/issues/172)) -### MimeKit 1.2.10 +### MimeKit 1.2.10 (2015-08-16) * Added public Stream property to IContentObject. * Implemented a better fix for illegal unquoted multi-line Content-Type and - Content-Disposition parameter values. (issue #159) + Content-Disposition parameter values. (issue [#159](https://github.com/jstedfast/MimeKit/issues/159)) * Fixed the UrlScanner to properly handle "ftp." at the very end of the message text. - (issue #161) + (issue [#161](https://github.com/jstedfast/MimeKit/issues/161)) * Fixed charset handling logic to not override charset aliases already in the cache. -### MimeKit 1.2.9 +### MimeKit 1.2.9 (2015-08-08) -* Fixed WriteTo(string fileName) methods to overwrite the existing file. (issue #154) +* Fixed WriteTo(string fileName) methods to overwrite the existing file. (issue [#154](https://github.com/jstedfast/MimeKit/issues/154)) * Updated InternetAddressList to implement IComparable. * Fixed DKIM-Signature generation and verification. * Added support for Message-Id headers that do not properly use encapsulate the value with angle brackets. -### MimeKit 1.2.8 +### MimeKit 1.2.8 (2015-07-19) * Added a new MessageDeliveryStatus MimePart subclass to make message/delivery-status MIME parts easier to deal with. @@ -359,52 +750,52 @@ * Fixed MimeParser to handle a message stream of just "\r\n". * Add a leading space in the Sender and Resent-Sender header values. -### MimeKit 1.2.7 +### MimeKit 1.2.7 (2015-07-05) * Fixed encoding GroupAddress with multiple mailbox addresses. * Fixed MessageIdList to be less strict in what it will accept. * Fixed logic for DKIM-Signature header folding. -### MimeKit 1.2.6 +### MimeKit 1.2.6 (2015-06-25) * Fixed a bug in the HTML tokenizer to handle some weird HTML created by Outlook 15.0. -* Added CmsRecipient .ctor overloads that accept X509Certificate2. (issue #149) +* Added CmsRecipient .ctor overloads that accept X509Certificate2. (issue [#149](https://github.com/jstedfast/MimeKit/issues/149)) -### MimeKit 1.2.5 +### MimeKit 1.2.5 (2015-06-22) * Changed BodyParts and Attachments to be IEnumerable - - WARNING! This is an API change! (issue #148) + WARNING! This is an API change! (issue [#148](https://github.com/jstedfast/MimeKit/issues/148)) * Moved the IsAttachment property from MimePart down into MimeEntity. * Added MimeMessage.Importance and MimeMessage.Priority properties. * Vastly improved the HtmlToHtml text converter with a w3 compliant HTML tokenizer. -### MimeKit 1.2.4 +### MimeKit 1.2.4 (2015-06-14) * Added support for generating and verifying DKIM-Signature headers. * Improved error handling for Encoding.GetEncoding() in CharsetFilter constructors. * Fixed buffering in the HTML parser. * Fixed Windows and Temporary S/MIME contexts to use case-insensitive address - comparisons like the other backends do. (issue #146). + comparisons like the other backends do. (issue [#146](https://github.com/jstedfast/MimeKit/issues/146)). * Added HeaderList.LastIndexOf() convenience methods. * Added a new Prepare() method to prepare a message or entity for transport and/or signing (used by MultipartSigned and MailKit.SmtpClient) to reduce duplicated code. * Fixed FilteredStream.Flush() to flush filters even when no data has been written. -* Fixed the ChainedStream.Read() logic. (issue #143) +* Fixed the ChainedStream.Read() logic. (issue [#143](https://github.com/jstedfast/MimeKit/issues/143)) * Added EncoderFilter and DecoderFilter.Create() overloads that take an encoding name (string). * HeaderList.WriteTo() now adds a blank line to the end of the output instead of leaving this up to the MimeEntity.WriteTo() method. This was needed for the DKIM-Signatures feature. -### MimeKit 1.2.3 +### MimeKit 1.2.3 (2015-06-01) * Fixed TextToFlowed logic that stripped trailing spaces. * Switched to PCL Profile78 to support Xamarin.Forms. -### MimeKit 1.2.2 +### MimeKit 1.2.2 (2015-05-31) * Added a MultipartAlternative class which adds some useful convenience methods and properties for use with the multipart/alternative mime-type. @@ -417,7 +808,7 @@ * Added a MimeVisitor class that implements the visitor pattern for visiting MIME nodes. -### MimeKit 1.2.1 +### MimeKit 1.2.1 (2015-05-25) * Added a Format property to ContentType. * Added a TryGetValue() method to ParameterList. @@ -425,21 +816,21 @@ * Fixed the HtmlToHtml converter to properly handle HTML text that begins with leading text data. * Fixed MimeParser.ParseHeaders() to handle input that does not end with a - blank line. (issue #142) + blank line. (issue [#142](https://github.com/jstedfast/MimeKit/issues/142)) * Renamed MimeEntityConstructorInfo to MimeEntityConstructorArgs. * Modified the MimeParser to use TextPart to represent application/rtf. -### MimeKit 1.2.0 +### MimeKit 1.2.0 (2015-05-24) -* Force the use of the rfc2047 "B" encoding for ISO-2022-JP. (issue #139) +* Force the use of the rfc2047 "B" encoding for ISO-2022-JP. (issue [#139](https://github.com/jstedfast/MimeKit/issues/139)) * Added some text converters to convert between various text formats including format=flowed and HTML. -### MimeKit 1.0.15 +### MimeKit 1.0.15 (2015-05-12) -* Fixed MimeMessage.WriteTo() to be thread-safe. (issue #138) +* Fixed MimeMessage.WriteTo() to be thread-safe. (issue [#138](https://github.com/jstedfast/MimeKit/issues/138)) -### MimeKit 1.0.14 +### MimeKit 1.0.14 (2015-05-09) * Added support for .NET 3.5. * Added a convenience CmsSigner .ctor that takes an X509Certificate2 argument. @@ -447,70 +838,70 @@ * Fixed TextPart.GetText() to protect against NullReferenceExceptions if the ContentObject is null. * Fixed MimeFilterBase.EnsureOutputSize() to initialize OutputBuffer if it is - null. Prevents NullReferenceExceptions in obscure corner cases. (issue #135) + null. Prevents NullReferenceExceptions in obscure corner cases. (issue [#135](https://github.com/jstedfast/MimeKit/issues/135)) * Added a TnefAttachFlags enum which is used to determine if image attachments in MS-TNEF data are meant to have a Content-Disposition of "inline" when - extracted as MIME attachments. (issue #129) + extracted as MIME attachments. (issue [#129](https://github.com/jstedfast/MimeKit/issues/129)) * Fixed TnefPart.ConvertToMessage() and ExtractAttachments() to use the PR_ATTACH_MIME_TAG property to determine the intended mime-type for extracted attachments. * Catch DecoderFallbackExceptions in MimeMessage.ToString() and fall back to - Latin1. (issue #137) + Latin1. (issue [#137](https://github.com/jstedfast/MimeKit/issues/137)) -### MimeKit 1.0.13 +### MimeKit 1.0.13 (2015-04-11) * Added a work-around for a bug in Thunderbird's multipart/related implementation. - (issue #124) + (issue [#124](https://github.com/jstedfast/MimeKit/issues/124)) * Improved MimeMessage.CreateFromMailMessage() a bit more to avoid creating empty From, Reply-To, To, Cc and/or Bcc headers. * Modified the HeaderIdExtensions to only be available for the HeaderId enum values. -### MimeKit 1.0.12 +### MimeKit 1.0.12 (2015-03-29) * Modified InternetAddressList.Equals() to return true if the lists contain the same - addresses even if they are in different orders. (issue #118) + addresses even if they are in different orders. (issue [#118](https://github.com/jstedfast/MimeKit/issues/118)) * Allow S/MIME certificates with the NonRepudiation key usage to be used for signing. - (issue #119) + (issue [#119](https://github.com/jstedfast/MimeKit/issues/119)) * Don't change the Content-Transfer-Encoding of MIME parts being encrypted as part of - a multipart/encrypted. (issue #122) -* Fixed logic to decide if a PGP secret key is expired. (issue #120) + a multipart/encrypted. (issue [#122](https://github.com/jstedfast/MimeKit/issues/122)) +* Fixed logic to decide if a PGP secret key is expired. (issue [#120](https://github.com/jstedfast/MimeKit/issues/120)) * Added support for SecureMailboxAddresses to OpenPgpContext to allow key lookups by fingerprints instead of email addresses. -### MimeKit 1.0.11 +### MimeKit 1.0.11 (2015-03-21) * Added the ContentDisposition.FormData string constant. * Allow the ContentDisposition.Disposition property to be set to values other than - "attachment" and "inline". (issue #112) + "attachment" and "inline". (issue [#112](https://github.com/jstedfast/MimeKit/issues/112)) * Shortened the length of the local-part of auto-generated Message-Ids. * Fixed MimeMessage.CreateFromMailMessage() to not duplicate From/To/Cc/etc addresses if the System.Net.Mail.MailMessage has been sent via System.Net.Mail.SmtpClient - prior to calling MimeMessage.CreateFromMailMessage(). (issue #115) + prior to calling MimeMessage.CreateFromMailMessage(). (issue [#115](https://github.com/jstedfast/MimeKit/issues/115)) * When parsing S/MIME digital signatures, don't import the full certificate chain. - (issue #110) + (issue [#110](https://github.com/jstedfast/MimeKit/issues/110)) * Added immutability-friendly .ctor to MimeMessage for use with languages such as F#. - (issue #116) + (issue [#116](https://github.com/jstedfast/MimeKit/issues/116)) -### MimeKit 1.0.10 +### MimeKit 1.0.10 (2015-03-14) * Ignore semi-colons in Content-Transfer-Encoding headers to work around broken mailers. * Added ParserOptions.ParameterComplianceMode (defaults to RfcComoplianceMode.Loose) which works around unquoted parameter values in Content-Type and Content-Disposition - headers. (issue #106) + headers. (issue [#106](https://github.com/jstedfast/MimeKit/issues/106)) * Modified the MimeParser to handle whitespace between header field names and the ':'. * Probe to make sure that various System.Text.Encodings are available before adding aliases for them (some may not be available depending on the platform). * Added a MimePart.GetBestEncoding() overload that takes a maxLineLength argument. * Modified MultipartSigned to use 78 characters as the max line length rather than 998 - characters. (issue #107) + characters. (issue [#107](https://github.com/jstedfast/MimeKit/issues/107)) -### MimeKit 1.0.9 +### MimeKit 1.0.9 (2015-03-08) * Added a new MessageDispositionNotification MimePart subclass to represent message/disposition-notification parts. * Fixed the TNEF parser to gracefully deal with duplicate attachment properties. -### MimeKit 1.0.8 +### MimeKit 1.0.8 (2015-03-02) * Modified the parser to accept Message-Id values without a domain (i.e. " "). * Fixed a NullReferenceException in MimeMessage.BodyParts in cases where a MessagePart @@ -520,19 +911,19 @@ * Renamed MimeUtils.TryParseVersion() to MimeUtils.TryParse() (the old API still exists but has been marked [Obsolete]). * Fixed S/MIME support to gracefully deal with badly formatted signature timestamps - which incrorectly use leap seconds. (issue #103) + which incrorectly use leap seconds. (issue [#103](https://github.com/jstedfast/MimeKit/issues/103)) -### MimeKit 1.0.7 +### MimeKit 1.0.7 (2015-02-17) * Fixed TnefPropertyReader.GetEmbeddedMessageReader() to skip the Guid. * When decrypting PGP data, iterate over all encrypted packets to find one that can be decrypted (i.e. the private key exists in the user's keychain). * Updated WindowsSecureMimeContext to respect SecureMailboxAddresses like the - other backends. (issue #100) + other backends. (issue [#100](https://github.com/jstedfast/MimeKit/issues/100)) * Added a Pkcs9SigningTime attribute to the CmsSigner for WindowsSecureMimeContext. - (issue #101) + (issue [#101](https://github.com/jstedfast/MimeKit/issues/101)) -### MimeKit 1.0.6 +### MimeKit 1.0.6 (2015-01-18) * Vastly improved MS-TNEF support. In addition to being fixed to properly extract the AttachData property of an Attachment attribute, more metadata is captured @@ -544,38 +935,38 @@ Note: If you are not yet ready to port your iOS application to the Unified API, you will need to stick with the 1.0.5 release. The Classic MonoTouch API is no longer supported. -### MimeKit 1.0.5 +### MimeKit 1.0.5 (2015-01-10) * Fixed out-of-memory error when encoding some long non-ASCII parameter values in Content-Type and Content-Disposition headers. -### MimeKit 1.0.4 +### MimeKit 1.0.4 (2015-01-08) * Added workaround for msg-id tokens with multiple domains (e.g. id@domain1@domain2). * Added convenience methods to Header to allow the use of charset strings. * Added more HeaderList.Replace() method overloads for convenience. * Added a FormatOptions property to disallow the use of mixed charsets when - encoding headers (issue #139). + encoding headers (issue [#139](https://github.com/jstedfast/MimeKit/issues/139)). -### MimeKit 1.0.3 +### MimeKit 1.0.3 (2014-12-13) -* Improved MimeMessage.TextBody and MimeMessage.HtmlBody logic. (issue #87) +* Improved MimeMessage.TextBody and MimeMessage.HtmlBody logic. (issue [#87](https://github.com/jstedfast/MimeKit/issues/87)) * Added new overrides of TextPart.GetText() and SetText() methods that take a charset string argument instead of a System.Text.Encoding. * Fixed charset fallback logic to work properly (it incorrectly assumed that by default, Encoding.UTF8.GetString() would throw an exception when it - encountered illegal byte sequences). (issue #88) + encountered illegal byte sequences). (issue [#88](https://github.com/jstedfast/MimeKit/issues/88)) * Fixed S/MIME logic for finding X.509 certificates to use for encipherment. - (issue #89) + (issue [#89](https://github.com/jstedfast/MimeKit/issues/89)) -### MimeKit 1.0.2 +### MimeKit 1.0.2 (2014-12-05) * Fixed MimeMessage.HtmlBody and MimeMessage.TextBody to properly handle nested multipart/alternatives (only generated by automated mailers). -### MimeKit 1.0.1 +### MimeKit 1.0.1 (2014-11-23) * Added MimeMessage.HtmlBody and MimeMessage.TextBody convenience properties. * Added TextPart.IsPlain and TextPart.IsHtml convenience properties. diff --git a/TODO.md b/TODO.md index 58ee18df41..a8f4da4219 100644 --- a/TODO.md +++ b/TODO.md @@ -4,22 +4,14 @@ * It would be nice to make DKIM support not depend on BouncyCastle at all so that DKIM support could be added to MimeKitLite. See: https://github.com/jstedfast/MimeKit/pull/296#issuecomment-355656935 - * Replace the DkimSigner.DigestSigner property with a method called - CreateSigningContext() or some such that returns an IDisposable replacement - for ISigner. - * The returned context should be IDisposable because System.Security-based - implementations need to be able to dispose the RSACryptoServiceProvider. - * This would also help facilitate dropping the dependency on BouncyCastle - since we could create a new IDkimSigningContext interface to replace the - use of ISigner. - * Or maybe DkimSigner could *be* the signing context instead of having a - CreateSigningContext() method? * If we had a nice DNS library that supported async/await, we could drop the IDkimPublicKeyLocator interface or at least provide a default implementation. Is it time for me to write DnsKit?? * S/MIME * BouncyCastleSecureMimeContext * Add support for downloading CRLs via FTP? Is this needed or at all common? + * Add a RECORDVERSION table column to the SQL database so we can add columns + in the future and work around gaps in the row data? ### Pie-in-the-sky Ideas diff --git a/UnitTests/ArgumentExceptionTests.cs b/UnitTests/ArgumentExceptionTests.cs index 51523faccf..a09ae6b269 100644 --- a/UnitTests/ArgumentExceptionTests.cs +++ b/UnitTests/ArgumentExceptionTests.cs @@ -1,9 +1,9 @@ -// +// // ArgumentExceptionTests.cs // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -233,6 +233,17 @@ public void TestParseArguments () AssertParseArguments (typeof (MimeUtils)); } + [Test] + public void TestBufferPoolArguments () + { + Assert.Throws (() => new BufferPool (-1, 16)); + Assert.Throws (() => new BufferPool (1024, -1)); + + var pool = new BufferPool (16, 2); + Assert.Throws (() => pool.Return (null)); + Assert.Throws (() => pool.Return (new byte[8])); + } + static void AssertStreamArguments (Stream stream) { var buffer = new byte[1024]; @@ -259,30 +270,30 @@ static void AssertStreamArguments (Stream stream) "{0}.Read() does not throw an ArgumentOutOfRangeException when count > buffer length.", stream.GetType ().Name); Assert.AreEqual ("count", ex.ParamName); - ex = Assert.Throws (async () => await stream.ReadAsync (null, 0, 0), + ex = Assert.ThrowsAsync (async () => await stream.ReadAsync (null, 0, 0), "{0}.ReadAsync() does not throw an ArgumentNullException when buffer is null.", stream.GetType ().Name); Assert.AreEqual ("buffer", ex.ParamName); - ex = Assert.Throws (async () => await stream.ReadAsync (buffer, -1, 0), + ex = Assert.ThrowsAsync (async () => await stream.ReadAsync (buffer, -1, 0), "{0}.ReadAsync() does not throw an ArgumentOutOfRangeException when offset is -1.", stream.GetType ().Name); Assert.AreEqual ("offset", ex.ParamName); - ex = Assert.Throws (async () => await stream.ReadAsync (buffer, buffer.Length + 1, 0), + ex = Assert.ThrowsAsync (async () => await stream.ReadAsync (buffer, buffer.Length + 1, 0), "{0}.ReadAsync() does not throw an ArgumentOutOfRangeException when offset > buffer length.", stream.GetType ().Name); Assert.AreEqual ("offset", ex.ParamName); - ex = Assert.Throws (async () => await stream.ReadAsync (buffer, 0, -1), + ex = Assert.ThrowsAsync (async () => await stream.ReadAsync (buffer, 0, -1), "{0}.ReadAsync() does not throw an ArgumentOutOfRangeException when count is -1.", stream.GetType ().Name); Assert.AreEqual ("count", ex.ParamName); - ex = Assert.Throws (async () => await stream.ReadAsync (buffer, 0, buffer.Length + 1), + ex = Assert.ThrowsAsync (async () => await stream.ReadAsync (buffer, 0, buffer.Length + 1), "{0}.ReadAsync() does not throw an ArgumentOutOfRangeException when count > buffer length.", stream.GetType ().Name); Assert.AreEqual ("count", ex.ParamName); } else { Assert.Throws (() => stream.Read (buffer, 0, buffer.Length), "{0}.Read() does not throw a NotSupportedException when CanRead is false.", stream.GetType ().Name); - Assert.Throws (async () => await stream.ReadAsync (buffer, 0, buffer.Length), + Assert.ThrowsAsync (async () => await stream.ReadAsync (buffer, 0, buffer.Length), "{0}.ReadAsync() does not throw a NotSupportedException when CanRead is false.", stream.GetType ().Name); } @@ -307,30 +318,30 @@ static void AssertStreamArguments (Stream stream) "{0}.Write() does not throw an ArgumentOutOfRangeException when count > buffer length.", stream.GetType ().Name); Assert.AreEqual ("count", ex.ParamName); - ex = Assert.Throws (async () => await stream.WriteAsync (null, 0, 0), + ex = Assert.ThrowsAsync (async () => await stream.WriteAsync (null, 0, 0), "{0}.WriteAsync() does not throw an ArgumentNullException when buffer is null.", stream.GetType ().Name); Assert.AreEqual ("buffer", ex.ParamName); - ex = Assert.Throws (async () => await stream.WriteAsync (buffer, -1, 0), + ex = Assert.ThrowsAsync (async () => await stream.WriteAsync (buffer, -1, 0), "{0}.WriteAsync() does not throw an ArgumentOutOfRangeException when offset is -1.", stream.GetType ().Name); Assert.AreEqual ("offset", ex.ParamName); - ex = Assert.Throws (async () => await stream.WriteAsync (buffer, buffer.Length + 1, 0), + ex = Assert.ThrowsAsync (async () => await stream.WriteAsync (buffer, buffer.Length + 1, 0), "{0}.WriteAsync() does not throw an ArgumentOutOfRangeException when offset > buffer length.", stream.GetType ().Name); Assert.AreEqual ("offset", ex.ParamName); - ex = Assert.Throws (async () => await stream.WriteAsync (buffer, 0, -1), + ex = Assert.ThrowsAsync (async () => await stream.WriteAsync (buffer, 0, -1), "{0}.WriteAsync() does not throw an ArgumentOutOfRangeException when count is -1.", stream.GetType ().Name); Assert.AreEqual ("count", ex.ParamName); - ex = Assert.Throws (async () => await stream.WriteAsync (buffer, 0, buffer.Length + 1), + ex = Assert.ThrowsAsync (async () => await stream.WriteAsync (buffer, 0, buffer.Length + 1), "{0}.WriteAsync() does not throw an ArgumentOutOfRangeException when count > buffer length.", stream.GetType ().Name); Assert.AreEqual ("count", ex.ParamName); } else { Assert.Throws (() => stream.Write (buffer, 0, buffer.Length), "{0}.Write() does not throw a NotSupportedException when CanWrite is false.", stream.GetType ().Name); - Assert.Throws (async () => await stream.WriteAsync (buffer, 0, buffer.Length), + Assert.ThrowsAsync (async () => await stream.WriteAsync (buffer, 0, buffer.Length), "{0}.WriteAsync() does not throw a NotSupportedException when CanWrite is false.", stream.GetType ().Name); } @@ -354,6 +365,8 @@ public void TestStreamArguments () AssertStreamArguments (stream); using (var memory = new MemoryStream ()) { + Assert.Throws (() => new FilteredStream (null)); + using (var stream = new FilteredStream (memory)) AssertStreamArguments (stream); } @@ -411,21 +424,39 @@ public void TestHeaderListCollectionArguments () collection.Add (new HeaderList ()); Assert.Throws (() => collection[0] = null); + Assert.DoesNotThrow (() => collection[0] = new HeaderList ()); + } + + [Test] + public void TestMimeMessageBeginEventArgs () + { + Assert.Throws (() => new MimeMessageBeginEventArgs (null)); + Assert.Throws (() => new MimeMessageBeginEventArgs (null, new MessagePart ())); + Assert.Throws (() => new MimeMessageBeginEventArgs (new MimeMessage (), null)); + } + + [Test] + public void TestMimeMessageEndEventArgs () + { + Assert.Throws (() => new MimeMessageEndEventArgs (null)); + Assert.Throws (() => new MimeMessageEndEventArgs (null, new MessagePart ())); + Assert.Throws (() => new MimeMessageEndEventArgs (new MimeMessage (), null)); + } + + [Test] + public void TestMimeEntityBeginEventArgs () + { + Assert.Throws (() => new MimeEntityBeginEventArgs (null)); + Assert.Throws (() => new MimeEntityBeginEventArgs (null, new Multipart ())); + Assert.Throws (() => new MimeEntityBeginEventArgs (new MimePart (), null)); } [Test] - public void TestMimeIteratorArguments () + public void TestMimeEntityEndEventArgs () { - var iter = new MimeIterator (new MimeMessage { Body = new TextPart ("plain") }); - - Assert.Throws (() => new MimeIterator (null)); - Assert.Throws (() => { var x = iter.Depth; }); - Assert.Throws (() => { var x = iter.Current; }); - Assert.Throws (() => { var x = iter.Parent; }); - Assert.Throws (() => { var x = iter.PathSpecifier; }); - Assert.Throws (() => iter.MoveTo (null)); - Assert.Throws (() => iter.MoveTo (string.Empty)); - Assert.Throws (() => iter.MoveTo ("xyz")); + Assert.Throws (() => new MimeEntityEndEventArgs (null)); + Assert.Throws (() => new MimeEntityEndEventArgs (null, new Multipart ())); + Assert.Throws (() => new MimeEntityEndEventArgs (new MimePart (), null)); } } } diff --git a/UnitTests/AssortedTests.cs b/UnitTests/AssortedTests.cs index 1801908b11..ed8c15e0e0 100644 --- a/UnitTests/AssortedTests.cs +++ b/UnitTests/AssortedTests.cs @@ -1,9 +1,9 @@ -// +// // AssortedTests.cs // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,6 +28,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Globalization; using NUnit.Framework; @@ -201,7 +202,7 @@ public void TestCharsetUtilsGetCodePage () int expected, codepage; string name; - name = string.Format ("iso-8859-{0}", i); + name = string.Format (CultureInfo.InvariantCulture, "iso-8859-{0}", i); codepage = CharsetUtils.GetCodePage (name); switch (i) { @@ -233,17 +234,17 @@ public void TestCharsetUtilsGetCodePage () else expected = -1; - name = string.Format ("windows-125{0}", i); + name = string.Format (CultureInfo.InvariantCulture, "windows-125{0}", i); codepage = CharsetUtils.GetCodePage (name); Assert.AreEqual (expected, codepage, "Invalid codepage for: {0}", name); - name = string.Format ("windows-cp125{0}", i); + name = string.Format (CultureInfo.InvariantCulture, "windows-cp125{0}", i); codepage = CharsetUtils.GetCodePage (name); Assert.AreEqual (expected, codepage, "Invalid codepage for: {0}", name); - name = string.Format ("cp125{0}", i); + name = string.Format (CultureInfo.InvariantCulture, "cp125{0}", i); codepage = CharsetUtils.GetCodePage (name); Assert.AreEqual (expected, codepage, "Invalid codepage for: {0}", name); @@ -253,7 +254,7 @@ public void TestCharsetUtilsGetCodePage () int codepage; string name; - name = string.Format ("ibm-{0}", ibm); + name = string.Format (CultureInfo.InvariantCulture, "ibm-{0}", ibm); codepage = CharsetUtils.GetCodePage (name); Assert.AreEqual (ibm, codepage, "Invalid codepage for: {0}", name); diff --git a/UnitTests/AttachmentCollectionTests.cs b/UnitTests/AttachmentCollectionTests.cs index 335bfcfd3e..bc1df038fb 100644 --- a/UnitTests/AttachmentCollectionTests.cs +++ b/UnitTests/AttachmentCollectionTests.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -87,5 +87,252 @@ public void TestArgumentExceptions () Assert.Throws (() => attachments.Insert (0, null)); } } + + [Test] + public void TestAddFileName () + { + var fileName = Path.Combine (TestHelper.ProjectDir, "TestData", "images", "girl.jpg"); + var attachments = new AttachmentCollection (); + MimePart attachment; + + attachment = (MimePart) attachments.Add (fileName); + Assert.AreEqual ("image/jpeg", attachment.ContentType.MimeType); + Assert.AreEqual ("girl.jpg", attachment.ContentType.Name); + Assert.NotNull (attachment.ContentDisposition); + Assert.AreEqual ("attachment", attachment.ContentDisposition.Disposition); + Assert.AreEqual ("girl.jpg", attachment.ContentDisposition.FileName); + Assert.AreEqual ("girl.jpg", attachment.FileName); + Assert.AreEqual (ContentEncoding.Base64, attachment.ContentTransferEncoding); + Assert.AreEqual (1, attachments.Count); + + Assert.IsTrue (attachments.Contains (attachment), "Contains"); + Assert.AreEqual (0, attachments.IndexOf (attachment), "IndexOf"); + Assert.IsTrue (attachments.Remove (attachment), "Remove"); + Assert.AreEqual (0, attachments.Count); + attachments.Clear (); + } + + [Test] + public void TestAddInlineFileName () + { + var fileName = Path.Combine (TestHelper.ProjectDir, "TestData", "images", "girl.jpg"); + var attachments = new AttachmentCollection (true); + MimePart attachment; + + attachment = (MimePart) attachments.Add (fileName); + Assert.AreEqual ("image/jpeg", attachment.ContentType.MimeType); + Assert.AreEqual ("girl.jpg", attachment.ContentType.Name); + Assert.NotNull (attachment.ContentDisposition); + Assert.AreEqual ("inline", attachment.ContentDisposition.Disposition); + Assert.AreEqual ("girl.jpg", attachment.ContentDisposition.FileName); + Assert.AreEqual ("girl.jpg", attachment.FileName); + Assert.AreEqual (ContentEncoding.Base64, attachment.ContentTransferEncoding); + Assert.AreEqual (1, attachments.Count); + + Assert.IsTrue (attachments.Contains (attachment), "Contains"); + Assert.AreEqual (0, attachments.IndexOf (attachment), "IndexOf"); + Assert.IsTrue (attachments.Remove (attachment), "Remove"); + Assert.AreEqual (0, attachments.Count); + attachments.Clear (); + } + + [Test] + public void TestAddFileNameContentType () + { + var fileName = Path.Combine (TestHelper.ProjectDir, "TestData", "images", "girl.jpg"); + var contentType = new ContentType ("image", "gif"); + var attachments = new AttachmentCollection (); + MimePart attachment; + + attachment = (MimePart) attachments.Add (fileName, contentType); + Assert.AreEqual (contentType.MimeType, attachment.ContentType.MimeType); + Assert.AreEqual ("girl.jpg", attachment.ContentType.Name); + Assert.NotNull (attachment.ContentDisposition); + Assert.AreEqual ("attachment", attachment.ContentDisposition.Disposition); + Assert.AreEqual ("girl.jpg", attachment.ContentDisposition.FileName); + Assert.AreEqual ("girl.jpg", attachment.FileName); + Assert.AreEqual (ContentEncoding.Base64, attachment.ContentTransferEncoding); + Assert.AreEqual (1, attachments.Count); + + Assert.IsTrue (attachments.Contains (attachment), "Contains"); + Assert.AreEqual (0, attachments.IndexOf (attachment), "IndexOf"); + Assert.IsTrue (attachments.Remove (attachment), "Remove"); + Assert.AreEqual (0, attachments.Count); + attachments.Clear (); + } + + [Test] + public void TestAddData () + { + var fileName = Path.Combine (TestHelper.ProjectDir, "TestData", "images", "girl.jpg"); + var attachments = new AttachmentCollection (); + MimePart attachment; + + attachment = (MimePart) attachments.Add (fileName, File.ReadAllBytes (fileName)); + Assert.AreEqual ("image/jpeg", attachment.ContentType.MimeType); + Assert.AreEqual ("girl.jpg", attachment.ContentType.Name); + Assert.NotNull (attachment.ContentDisposition); + Assert.AreEqual ("attachment", attachment.ContentDisposition.Disposition); + Assert.AreEqual ("girl.jpg", attachment.ContentDisposition.FileName); + Assert.AreEqual ("girl.jpg", attachment.FileName); + Assert.AreEqual (ContentEncoding.Base64, attachment.ContentTransferEncoding); + Assert.AreEqual (1, attachments.Count); + + Assert.IsTrue (attachments.Contains (attachment), "Contains"); + Assert.AreEqual (0, attachments.IndexOf (attachment), "IndexOf"); + Assert.IsTrue (attachments.Remove (attachment), "Remove"); + Assert.AreEqual (0, attachments.Count); + attachments.Clear (); + } + + [Test] + public void TestAddDataContentType () + { + var fileName = Path.Combine (TestHelper.ProjectDir, "TestData", "images", "girl.jpg"); + var contentType = new ContentType ("image", "gif"); + var attachments = new AttachmentCollection (); + MimePart attachment; + + attachment = (MimePart) attachments.Add (fileName, File.ReadAllBytes (fileName), contentType); + Assert.AreEqual (contentType.MimeType, attachment.ContentType.MimeType); + Assert.AreEqual ("girl.jpg", attachment.ContentType.Name); + Assert.NotNull (attachment.ContentDisposition); + Assert.AreEqual ("attachment", attachment.ContentDisposition.Disposition); + Assert.AreEqual ("girl.jpg", attachment.ContentDisposition.FileName); + Assert.AreEqual ("girl.jpg", attachment.FileName); + Assert.AreEqual (ContentEncoding.Base64, attachment.ContentTransferEncoding); + Assert.AreEqual (1, attachments.Count); + + Assert.IsTrue (attachments.Contains (attachment), "Contains"); + Assert.AreEqual (0, attachments.IndexOf (attachment), "IndexOf"); + Assert.IsTrue (attachments.Remove (attachment), "Remove"); + Assert.AreEqual (0, attachments.Count); + attachments.Clear (); + } + + [Test] + public void TestAddStream () + { + var fileName = Path.Combine (TestHelper.ProjectDir, "TestData", "images", "girl.jpg"); + var attachments = new AttachmentCollection (); + MimePart attachment; + + using (var stream = File.OpenRead (fileName)) + attachment = (MimePart) attachments.Add (fileName, stream); + + Assert.AreEqual ("image/jpeg", attachment.ContentType.MimeType); + Assert.AreEqual ("girl.jpg", attachment.ContentType.Name); + Assert.NotNull (attachment.ContentDisposition); + Assert.AreEqual ("attachment", attachment.ContentDisposition.Disposition); + Assert.AreEqual ("girl.jpg", attachment.ContentDisposition.FileName); + Assert.AreEqual ("girl.jpg", attachment.FileName); + Assert.AreEqual (ContentEncoding.Base64, attachment.ContentTransferEncoding); + Assert.AreEqual (1, attachments.Count); + + Assert.IsTrue (attachments.Contains (attachment), "Contains"); + Assert.AreEqual (0, attachments.IndexOf (attachment), "IndexOf"); + Assert.IsTrue (attachments.Remove (attachment), "Remove"); + Assert.AreEqual (0, attachments.Count); + attachments.Clear (); + } + + [Test] + public void TestAddStreamContentType () + { + var fileName = Path.Combine (TestHelper.ProjectDir, "TestData", "images", "girl.jpg"); + var contentType = new ContentType ("image", "gif"); + var attachments = new AttachmentCollection (); + MimePart attachment; + + using (var stream = File.OpenRead (fileName)) + attachment = (MimePart) attachments.Add (fileName, stream, contentType); + + Assert.AreEqual (contentType.MimeType, attachment.ContentType.MimeType); + Assert.AreEqual ("girl.jpg", attachment.ContentType.Name); + Assert.NotNull (attachment.ContentDisposition); + Assert.AreEqual ("attachment", attachment.ContentDisposition.Disposition); + Assert.AreEqual ("girl.jpg", attachment.ContentDisposition.FileName); + Assert.AreEqual ("girl.jpg", attachment.FileName); + Assert.AreEqual (ContentEncoding.Base64, attachment.ContentTransferEncoding); + Assert.AreEqual (1, attachments.Count); + + Assert.IsTrue (attachments.Contains (attachment), "Contains"); + Assert.AreEqual (0, attachments.IndexOf (attachment), "IndexOf"); + Assert.IsTrue (attachments.Remove (attachment), "Remove"); + Assert.AreEqual (0, attachments.Count); + attachments.Clear (); + } + + [Test] + public void TestAddTextFileName () + { + var fileName = Path.Combine (TestHelper.ProjectDir, "TestData", "text", "lorem-ipsum.txt"); + var attachments = new AttachmentCollection (); + MimePart attachment; + + attachment = (MimePart) attachments.Add (fileName); + Assert.AreEqual ("text/plain", attachment.ContentType.MimeType); + Assert.AreEqual ("lorem-ipsum.txt", attachment.ContentType.Name); + Assert.NotNull (attachment.ContentDisposition); + Assert.AreEqual ("attachment", attachment.ContentDisposition.Disposition); + Assert.AreEqual ("lorem-ipsum.txt", attachment.ContentDisposition.FileName); + Assert.AreEqual ("lorem-ipsum.txt", attachment.FileName); + Assert.AreEqual (ContentEncoding.SevenBit, attachment.ContentTransferEncoding); + Assert.AreEqual (1, attachments.Count); + + Assert.IsTrue (attachments.Contains (attachment), "Contains"); + Assert.AreEqual (0, attachments.IndexOf (attachment), "IndexOf"); + Assert.IsTrue (attachments.Remove (attachment), "Remove"); + Assert.AreEqual (0, attachments.Count); + attachments.Clear (); + } + + [Test] + public void TestAddEmailMessage () + { + var fileName = Path.Combine (TestHelper.ProjectDir, "TestData", "messages", "body.1.txt"); + var attachments = new AttachmentCollection (); + MimeEntity attachment; + + using (var stream = File.OpenRead (fileName)) + attachment = attachments.Add ("message.eml", stream); + + Assert.AreEqual ("message/rfc822", attachment.ContentType.MimeType); + Assert.AreEqual ("message.eml", attachment.ContentType.Name); + Assert.NotNull (attachment.ContentDisposition); + Assert.AreEqual ("attachment", attachment.ContentDisposition.Disposition); + Assert.AreEqual ("message.eml", attachment.ContentDisposition.FileName); + Assert.AreEqual (1, attachments.Count); + + Assert.IsTrue (attachments.Contains (attachment), "Contains"); + Assert.AreEqual (0, attachments.IndexOf (attachment), "IndexOf"); + Assert.IsTrue (attachments.Remove (attachment), "Remove"); + Assert.AreEqual (0, attachments.Count); + attachments.Clear (); + } + + [Test] + public void TestAddInlineEmailMessage () + { + var fileName = Path.Combine (TestHelper.ProjectDir, "TestData", "messages", "body.1.txt"); + var attachments = new AttachmentCollection (true); + MimeEntity attachment; + + using (var stream = File.OpenRead (fileName)) + attachment = attachments.Add ("message.eml", stream); + + Assert.AreEqual ("message/rfc822", attachment.ContentType.MimeType); + Assert.AreEqual ("message.eml", attachment.ContentType.Name); + Assert.NotNull (attachment.ContentDisposition); + Assert.AreEqual ("inline", attachment.ContentDisposition.Disposition); + Assert.AreEqual ("message.eml", attachment.ContentDisposition.FileName); + Assert.AreEqual (1, attachments.Count); + + Assert.IsTrue (attachments.Contains (attachment), "Contains"); + Assert.AreEqual (0, attachments.IndexOf (attachment), "IndexOf"); + Assert.IsTrue (attachments.Remove (attachment), "Remove"); + Assert.AreEqual (0, attachments.Count); + attachments.Clear (); + } } } diff --git a/UnitTests/ConstructorTests.cs b/UnitTests/ConstructorTests.cs index a3c4e2c6c6..cb5da4db5d 100644 --- a/UnitTests/ConstructorTests.cs +++ b/UnitTests/ConstructorTests.cs @@ -1,9 +1,9 @@ -// +// // ConstructorTests.cs // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -52,6 +52,7 @@ public void TestArgumentExceptions () Assert.Throws (() => new MessagePart ("rfc822", (object[]) null)); Assert.Throws (() => new MessagePart ("rfc822", message, null, message)); Assert.Throws (() => new MessagePart ("rfc822", 5)); + Assert.DoesNotThrow (() => new MessagePart ("rfc822", message)); Assert.Throws (() => new MimePart ("text", "plain", (object[]) null)); Assert.Throws (() => new MimePart ("text", "plain", body.Content, body.Content.Stream)); diff --git a/UnitTests/ContentDispositionTests.cs b/UnitTests/ContentDispositionTests.cs index 143924653c..1360488026 100644 --- a/UnitTests/ContentDispositionTests.cs +++ b/UnitTests/ContentDispositionTests.cs @@ -1,9 +1,9 @@ -// +// // ContentDispositionTests.cs // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/UnitTests/ContentObjectTests.cs b/UnitTests/ContentObjectTests.cs index eda4e796f0..8b11c1f20b 100644 --- a/UnitTests/ContentObjectTests.cs +++ b/UnitTests/ContentObjectTests.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,7 +32,6 @@ using NUnit.Framework; using MimeKit; -using MimeKit.IO; namespace UnitTests { [TestFixture] @@ -45,9 +44,9 @@ public void TestArgumentExceptions () Assert.Throws (() => new MimeContent (null)); Assert.Throws (() => content.WriteTo (null)); - Assert.Throws (async () => await content.WriteToAsync (null)); + Assert.ThrowsAsync (async () => await content.WriteToAsync (null)); Assert.Throws (() => content.DecodeTo (null)); - Assert.Throws (async () => await content.DecodeToAsync (null)); + Assert.ThrowsAsync (async () => await content.DecodeToAsync (null)); } [Test] @@ -62,7 +61,7 @@ public void TestCancellation () Assert.Throws (() => content.WriteTo (dest, source.Token)); Assert.AreEqual (0, dest.Length); - Assert.Throws (async () => await content.WriteToAsync (dest, source.Token)); + Assert.ThrowsAsync (async () => await content.WriteToAsync (dest, source.Token)); Assert.AreEqual (0, dest.Length); } } diff --git a/UnitTests/ContentTypeTests.cs b/UnitTests/ContentTypeTests.cs index 0e2bb56284..ed7654e471 100644 --- a/UnitTests/ContentTypeTests.cs +++ b/UnitTests/ContentTypeTests.cs @@ -1,9 +1,9 @@ -// +// // ContentTypeTests.cs // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -252,6 +252,17 @@ public void TestContentTypeWithEmptyParameter () AssertParse (text, expected); } + // Tests the work-around for issue #595 + [Test] + public void TestContentTypeWithoutSemicolonBetweenParameters () + { + const string text = "application/x-pkcs7-mime;\n name=\"smime.p7m\"\n smime-type=enveloped-data"; + var expected = new ContentType ("application", "x-pkcs7-mime") { Name = "smime.p7m" }; + expected.Parameters.Add ("smime-type", "enveloped-data"); + + AssertParse (text, expected, true); + } + [Test] public void TestContentTypeAndContentTrafserEncodingOnOneLine () { @@ -260,7 +271,7 @@ public void TestContentTypeAndContentTrafserEncodingOnOneLine () // TryParse should "fail", but still produce a usable ContentType. // Parse will throw ParseException. - AssertParse (text, expected, false, 35, 35); + AssertParse (text, expected, false, 35, 60); } [Test] @@ -508,6 +519,69 @@ public void TestInvalidDataAfterMimeType () AssertParse (text, expected, false, 25, 25); } + [Test] + public void TestEmptyParameterName () + { + var expected = new ContentType ("text", "plain"); + const string text = "text/plain; ="; + + AssertParse (text, expected, false, 12, 12); + } + + [Test] + public void TestIncompleteParameterName () + { + var expected = new ContentType ("text", "plain"); + const string text = "text/plain; name"; + + AssertParse (text, expected, false, 12, 16); + } + + [Test] + public void TestIncompleteParameterNameWithStar () + { + var expected = new ContentType ("text", "plain"); + const string text = "text/plain; name*"; + + AssertParse (text, expected, false, 12, 17); + } + + [Test] + public void TestIncompleteParameterNameWithPartId () + { + var expected = new ContentType ("text", "plain"); + const string text = "text/plain; name*0"; + + AssertParse (text, expected, false, 12, 18); + } + + [Test] + public void TestIncompleteParameterNameWithPartIdStar () + { + var expected = new ContentType ("text", "plain"); + const string text = "text/plain; name*0*"; + + AssertParse (text, expected, false, 12, 19); + } + + [Test] + public void TestInvalidParameterNameWithPartId () + { + var expected = new ContentType ("text", "plain"); + const string text = "text/plain; name*0*x"; + + AssertParse (text, expected, false, 12, 19); + } + + [Test] + public void TestInncompleteParameterNameWithPartIdStarEqual () + { + var expected = new ContentType ("text", "plain"); + const string text = "text/plain; name*0*="; + + AssertParse (text, expected, false, 12, 20); + } + [Test] public void TestProperties () { @@ -539,5 +613,39 @@ public void TestProperties () type.Name = null; Assert.IsNull (type.Name); } + + [Test] + public void TestToString () + { + const string expected = "Content-Type: text/plain; format=\"flowed\"; charset=\"iso-8859-1\"; name=\"filename.txt\""; + var type = new ContentType ("text", "plain") { Format = "flowed", Charset = "iso-8859-1", Name = "filename.txt" }; + var value = type.ToString ().Replace ("\r\n", "\n"); + + Assert.AreEqual (expected, value); + } + + [Test] + public void TestToStringEncode () + { + const string rfc2231 = "Content-Type: text/plain; format=flowed; charset=utf-8;\n\tname*0*=utf-8''%D0%AD%D1%82%D0%BE%20%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%BE;\n\tname*1*=%D0%B5%20%D0%B8%D0%BC%D1%8F%20%D1%84%D0%B0%D0%B9%D0%BB%D0%B0.txt"; + const string rfc2047 = "Content-Type: text/plain; format=flowed; charset=utf-8;\n\tname=\"=?utf-8?b?0K3RgtC+INGA0YPRgdGB0LrQvtC1INC40LzRjyDRhNCw0LnQu9CwLnR4?=\n\t=?utf-8?q?t?=\""; + var type = new ContentType ("text", "plain") { Format = "flowed", Charset = "utf-8", Name = "Это русское имя файла.txt" }; + string value; + + value = type.ToString (Encoding.UTF8, true).Replace ("\r\n", "\n"); + Assert.AreEqual (rfc2231, value, "Default"); + + foreach (var parameter in type.Parameters) + parameter.EncodingMethod = ParameterEncodingMethod.Rfc2231; + + value = type.ToString (Encoding.UTF8, true).Replace ("\r\n", "\n"); + Assert.AreEqual (rfc2231, value, "Rfc2231"); + + foreach (var parameter in type.Parameters) + parameter.EncodingMethod = ParameterEncodingMethod.Rfc2047; + + value = type.ToString (Encoding.UTF8, true).Replace ("\r\n", "\n"); + Assert.AreEqual (rfc2047, value, "Rfc2047"); + } } } diff --git a/UnitTests/Cryptography/ApplicationPkcs7MimeTests.cs b/UnitTests/Cryptography/ApplicationPkcs7MimeTests.cs index 2115df1bd4..301801f006 100644 --- a/UnitTests/Cryptography/ApplicationPkcs7MimeTests.cs +++ b/UnitTests/Cryptography/ApplicationPkcs7MimeTests.cs @@ -3,7 +3,7 @@ // // Author: Jeffrey Stedfast // -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) +// Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -42,7 +42,7 @@ public class ApplicationPkcs7MimeTests [Test] public void TestArgumentExceptions () { - var path = Path.Combine ("..", "..", "TestData", "smime", "smime.p12"); + var path = Path.Combine (TestHelper.ProjectDir, "TestData", "smime", "smime.p12"); var entity = new TextPart ("plain") { Text = "This is some text..." }; var mailbox = new MailboxAddress ("MimeKit UnitTests", "mimekit@example.com"); var recipients = new CmsRecipientCollection (); @@ -52,8 +52,7 @@ public void TestArgumentExceptions () recipients.Add (new CmsRecipient (signer.Certificate)); using (var ctx = new TemporarySecureMimeContext ()) { - using (var file = File.OpenRead (path)) - ctx.Import (file, "no.secret"); + ctx.Import (path, "no.secret"); // Compress Assert.Throws (() => ApplicationPkcs7Mime.Compress (null, entity)); diff --git a/UnitTests/Cryptography/ArcSignerTests.cs b/UnitTests/Cryptography/ArcSignerTests.cs new file mode 100644 index 0000000000..722535b48c --- /dev/null +++ b/UnitTests/Cryptography/ArcSignerTests.cs @@ -0,0 +1,1394 @@ +// +// ArcSignerTests.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2020 .NET Foundation and Contributors +// +// 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.IO; +using System.Text; +using System.Collections.Generic; + +using NUnit.Framework; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Crypto.Parameters; + +using MimeKit; +using MimeKit.Cryptography; + +namespace UnitTests.Cryptography { + [TestFixture] + public class ArcSignerTests + { + [Test] + public void TestArcSignerCtors () + { + Assert.DoesNotThrow (() => { + var signer = new DummyArcSigner (Path.Combine (TestHelper.ProjectDir, "TestData", "dkim", "example.pem"), "example.com", "1433868189.example") { + SignatureAlgorithm = DkimSignatureAlgorithm.RsaSha256 + }; + }); + + AsymmetricCipherKeyPair keys; + + using (var stream = new StreamReader (Path.Combine (TestHelper.ProjectDir, "TestData", "dkim", "example.pem"))) { + var reader = new PemReader (stream); + + keys = reader.ReadObject () as AsymmetricCipherKeyPair; + } + + Assert.DoesNotThrow (() => { + var signer = new DummyArcSigner (keys.Private, "example.com", "1433868189.example") { + SignatureAlgorithm = DkimSignatureAlgorithm.RsaSha256 + }; + }); + } + + [Test] + public void TestArcSignerDefaults () + { + var path = Path.Combine (TestHelper.ProjectDir, "TestData", "dkim", "example.pem"); + AsymmetricCipherKeyPair keys; + ArcSigner signer; + + using (var stream = new StreamReader (path)) { + var reader = new PemReader (stream); + + keys = reader.ReadObject () as AsymmetricCipherKeyPair; + } + + signer = new DummyArcSigner (keys.Private, "example.com", "1433868189.example"); + Assert.AreEqual (DkimSignatureAlgorithm.RsaSha256, signer.SignatureAlgorithm, "SignatureAlgorithm #1"); + + signer = new DummyArcSigner (path, "example.com", "1433868189.example"); + Assert.AreEqual (DkimSignatureAlgorithm.RsaSha256, signer.SignatureAlgorithm, "SignatureAlgorithm #2"); + + using (var stream = File.OpenRead (path)) { + signer = new DummyArcSigner (stream, "example.com", "1433868189.example"); + Assert.AreEqual (DkimSignatureAlgorithm.RsaSha256, signer.SignatureAlgorithm, "SignatureAlgorithm #3"); + } + } + + [Test] + public void TestArgumentExceptions () + { + var path = Path.Combine (TestHelper.ProjectDir, "TestData", "dkim", "example.pem"); + var locator = new DkimPublicKeyLocator (); + var verifier = new DkimVerifier (locator); + var dkimHeader = new Header (HeaderId.DkimSignature, "value"); + var arcHeader = new Header (HeaderId.ArcMessageSignature, "value"); + var options = FormatOptions.Default; + var message = new MimeMessage (); + AsymmetricCipherKeyPair keys; + ArcSigner signer; + + using (var stream = new StreamReader (path)) { + var reader = new PemReader (stream); + + keys = reader.ReadObject () as AsymmetricCipherKeyPair; + } + + Assert.Throws (() => new DummyArcSigner ((AsymmetricKeyParameter) null, "domain", "selector")); + Assert.Throws (() => new DummyArcSigner (keys.Public, "domain", "selector")); + Assert.Throws (() => new DummyArcSigner (keys.Private, null, "selector")); + Assert.Throws (() => new DummyArcSigner (keys.Private, "domain", null)); + Assert.Throws (() => new DummyArcSigner ((string) null, "domain", "selector")); + Assert.Throws (() => new DummyArcSigner ("fileName", null, "selector")); + Assert.Throws (() => new DummyArcSigner ("fileName", "domain", null)); + Assert.Throws (() => new DummyArcSigner (string.Empty, "domain", "selector")); + Assert.Throws (() => new DummyArcSigner ((Stream) null, "domain", "selector")); + using (var stream = File.OpenRead (path)) { + Assert.Throws (() => new DummyArcSigner (stream, null, "selector")); + Assert.Throws (() => new DummyArcSigner (stream, "domain", null)); + + signer = new DummyArcSigner (stream, "example.com", "1433868189.example") { + SignatureAlgorithm = DkimSignatureAlgorithm.RsaSha1 + }; + } + + // Sign + + Assert.Throws (() => signer.Sign (null, new HeaderId[] { HeaderId.From })); + Assert.Throws (() => signer.Sign (message, (IList ) null)); + Assert.Throws (() => signer.Sign (message, new HeaderId[] { HeaderId.Unknown, HeaderId.From })); + Assert.Throws (() => signer.Sign (message, new HeaderId[] { HeaderId.Received, HeaderId.From })); + Assert.Throws (() => signer.Sign (message, new HeaderId[] { HeaderId.ContentType })); + Assert.Throws (() => signer.Sign (null, new string[] { "From" })); + Assert.Throws (() => signer.Sign (message, (IList ) null)); + Assert.Throws (() => signer.Sign (message, new string[] { "", "From" })); + Assert.Throws (() => signer.Sign (message, new string[] { null, "From" })); + Assert.Throws (() => signer.Sign (message, new string[] { "Received", "From" })); + Assert.Throws (() => signer.Sign (message, new string[] { "Content-Type" })); + + Assert.Throws (() => signer.Sign (null, message, new HeaderId[] { HeaderId.From })); + Assert.Throws (() => signer.Sign (options, null, new HeaderId[] { HeaderId.From })); + Assert.Throws (() => signer.Sign (options, message, new HeaderId[] { HeaderId.From, HeaderId.Unknown })); + Assert.Throws (() => signer.Sign (options, message, (IList ) null)); + + Assert.Throws (() => signer.Sign (null, message, new string[] { "From" })); + Assert.Throws (() => signer.Sign (options, null, new string[] { "From" })); + Assert.Throws (() => signer.Sign (options, message, new string[] { "From", null })); + Assert.Throws (() => signer.Sign (options, message, (IList ) null)); + + // SignAsync + + Assert.ThrowsAsync (async () => await signer.SignAsync (null, new HeaderId[] { HeaderId.From })); + Assert.ThrowsAsync (async () => await signer.SignAsync (message, (IList ) null)); + Assert.ThrowsAsync (async () => await signer.SignAsync (message, new HeaderId[] { HeaderId.Unknown, HeaderId.From })); + Assert.ThrowsAsync (async () => await signer.SignAsync (message, new HeaderId[] { HeaderId.Received, HeaderId.From })); + Assert.ThrowsAsync (async () => await signer.SignAsync (message, new HeaderId[] { HeaderId.ContentType })); + Assert.ThrowsAsync (async () => await signer.SignAsync (null, new string[] { "From" })); + Assert.ThrowsAsync (async () => await signer.SignAsync (message, (IList ) null)); + Assert.ThrowsAsync (async () => await signer.SignAsync (message, new string[] { "", "From" })); + Assert.ThrowsAsync (async () => await signer.SignAsync (message, new string[] { null, "From" })); + Assert.ThrowsAsync (async () => await signer.SignAsync (message, new string[] { "Received", "From" })); + Assert.ThrowsAsync (async () => await signer.SignAsync (message, new string[] { "Content-Type" })); + + Assert.ThrowsAsync (async () => await signer.SignAsync (null, message, new HeaderId[] { HeaderId.From })); + Assert.ThrowsAsync (async () => await signer.SignAsync (options, null, new HeaderId[] { HeaderId.From })); + Assert.ThrowsAsync (async () => await signer.SignAsync (options, message, new HeaderId[] { HeaderId.From, HeaderId.Unknown })); + Assert.ThrowsAsync (async () => await signer.SignAsync (options, message, (IList ) null)); + + Assert.ThrowsAsync (async () => await signer.SignAsync (null, message, new string[] { "From" })); + Assert.ThrowsAsync (async () => await signer.SignAsync (options, null, new string[] { "From" })); + Assert.ThrowsAsync (async () => await signer.SignAsync (options, message, new string[] { "From", null })); + Assert.ThrowsAsync (async () => await signer.SignAsync (options, message, (IList ) null)); + } + + static void AssertHeadersEqual (string description, HeaderId id, string expected, string actual) + { + var expectedTags = DkimVerifierBase.ParseParameterTags (id, expected); + var actualTags = DkimVerifierBase.ParseParameterTags (id, actual); + + Assert.AreEqual (expectedTags.Count, actualTags.Count, "{0} parameter counts do not match", id.ToHeaderName ()); + foreach (var tag in expectedTags) { + string value; + + if (tag.Key == "b") { + // Note: these values are affected by tag order, so MimeKit's results *will* differ + // from the results produced by the library that the tests are based on... + // + // We'll validate that these values are correct by using the ArcVerifier. + continue; + } + + Assert.IsTrue (actualTags.TryGetValue (tag.Key, out value), tag.Key); + Assert.AreEqual (tag.Value, value, "{0} {1}= values do not match", id.ToHeaderName (), tag.Key); + } + } + + static void AssertSignResults (string description, MimeMessage message, DkimPublicKeyLocator locator, DkimSignatureAlgorithm algorithm, string aar, string ams, string seal) + { + Header header; + int index; + + if (string.IsNullOrEmpty (seal)) { + index = message.Headers.IndexOf (HeaderId.ArcSeal); + + Assert.AreNotEqual (0, index, "Message should not have been signed."); + } else { + index = message.Headers.IndexOf (HeaderId.ArcAuthenticationResults); + Assert.AreEqual (2, index, "IndexOf ARC-Authentication-Results header"); + header = message.Headers[index]; + Assert.AreEqual (aar, header.Value, "ARC-Authentication-Results headers do not match"); + + index = message.Headers.IndexOf (HeaderId.ArcMessageSignature); + Assert.AreEqual (1, index, "IndexOf ARC-Message-Signature header"); + header = message.Headers[index]; + AssertHeadersEqual (description, HeaderId.ArcMessageSignature, ams, header.Value); + + index = message.Headers.IndexOf (HeaderId.ArcSeal); + Assert.AreEqual (0, index, "IndexOf ARC-Seal header"); + header = message.Headers[index]; + AssertHeadersEqual (description, HeaderId.ArcSeal, seal, header.Value); + + var expected = ArcSignatureValidationResult.Pass; + if (header.Value.Contains ("cv=fail;")) + expected = ArcSignatureValidationResult.Fail; + var verifier = new ArcVerifier (locator); + ArcValidationResult result; + + if (!verifier.IsEnabled (algorithm)) { + result = verifier.Verify (message); + + Assert.AreEqual (ArcSignatureValidationResult.Fail, result.Chain, "Disabled algorithm should fail"); + + verifier.Enable (algorithm); + } + + result = verifier.Verify (message); + + Assert.AreEqual (expected, result.Chain, "ArcSigner validation failed"); + } + } + + static void Sign (string description, string input, DkimPublicKeyLocator locator, string srvid, string domain, string selector, + string privateKey, long t, string[] hdrs, string aar, string ams, string seal, + DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256, + DkimCanonicalizationAlgorithm headerAlgorithm = DkimCanonicalizationAlgorithm.Relaxed, + DkimCanonicalizationAlgorithm bodyAlgorithm = DkimCanonicalizationAlgorithm.Relaxed) + { + DummyArcSigner signer; + + if (algorithm == DkimSignatureAlgorithm.Ed25519Sha256) { + var rawData = Convert.FromBase64String (privateKey); + var key = new Ed25519PrivateKeyParameters (rawData, 0); + + signer = new DummyArcSigner (key, domain, selector, algorithm); + } else { + using (var stream = new MemoryStream (Encoding.ASCII.GetBytes (privateKey), false)) { + signer = new DummyArcSigner (stream, domain, selector, algorithm); + } + } + + signer.HeaderCanonicalizationAlgorithm = headerAlgorithm; + signer.BodyCanonicalizationAlgorithm = bodyAlgorithm; + signer.PublicKeyLocator = locator; + signer.Timestamp = t; + signer.SrvId = srvid; + + using (var stream = new MemoryStream (Encoding.UTF8.GetBytes (input), false)) { + var message = MimeMessage.Load (stream); + + // Test Sign(..., string[] headers, ...) + signer.Sign (message, hdrs); + AssertSignResults (description, message, locator, algorithm, aar, ams, seal); + + stream.Position = 0; + message = MimeMessage.Load (stream); + + // Test SignAsync(..., string[] headers, ...) + signer.SignAsync (message, hdrs).GetAwaiter ().GetResult (); + AssertSignResults (description, message, locator, algorithm, aar, ams, seal); + + var ids = new HeaderId[hdrs.Length]; + for (int i = 0; i < hdrs.Length; i++) + ids[i] = hdrs[i].ToHeaderId (); + + stream.Position = 0; + message = MimeMessage.Load (stream); + + // Test Sign(..., HeaderId[] headers, ...) + signer.Sign (message, ids); + AssertSignResults (description, message, locator, algorithm, aar, ams, seal); + + stream.Position = 0; + message = MimeMessage.Load (stream); + + // Test SignAsync(..., HeaderId[] headers, ...) + signer.SignAsync (message, ids).GetAwaiter ().GetResult (); + AssertSignResults (description, message, locator, algorithm, aar, ams, seal); + } + } + + [Test] + public void sig_alg_rsa_sha1 () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "i=1; a=rsa-sha1; cv=none; d=example.org; s=dummy; t=12345; b=eF3i/65gJXfQu4kMAiK+EusLS0irqv6gIyJYEcpJTaASwWZd+vt6+nN7pjP7kDZYwno8gVFS5vFrkc959FBjJr1oWVVbVdrJenYqCvBBnFmrYyhG137vV+7RziHerQCJ44VkrF1VEaj7TQ+rp5rmwUMZQlpxdqUYWTIT9DpYAFs="; + const string ams = "i=1; a=rsa-sha1; d=example.org; s=dummy; c=relaxed/relaxed; t=12345; h=mime-version:date:from:to:subject; bh=bIxxaeIQvmOBdTAitYfSNFgzPP4=; b=qh2z8AFVMdUI4pG3EBTn9+3Af3KU0tmlLUOeUnCsWuigUdE7TTGLirz2ZOeFUAjAeHuKFxJjEPbtYtQh9ptuxZ8Mn9sE/AfIBd85q2yHWu3WOGQFwRrEuLekiH/zXG3d7VUhf8PNveHEXx/NI40qBpPLjXF4o8qxoApGEQZ6CjA="; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("a=rsa-sha1", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal, DkimSignatureAlgorithm.RsaSha1); + } + + [Test] + public void sig_alg_ed25519_sha256 () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = "nWGxne/9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A="; + const string seal = "i=1; a=ed25519-sha256; cv=none; d=example.org; s=dummy; t=12345; b=1VlNt2DJiRslGH64VEByTy/rviHk9vVjzaFb6Jd4C5V01Uy9pyrZwa6vwPdUZgrnymXzbz2qgtGyHKd3oYHABg=="; + const string ams = "i=1; a=ed25519-sha256; d=example.org; s=dummy; c=relaxed/relaxed; t=12345; h=mime-version:date:from:to:subject; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; b=f3EEIg6djos7u74HzwTwQaZmCt3jhoQe/PnDsLfnOd5i6slE0MJdoR4lgBOAZMh+nTLF7YfsHF/qopJwoPBQDg=="; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=ed25519; p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo="); + + Sign ("a=ed25519-sha256", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal, DkimSignatureAlgorithm.Ed25519Sha256); + } + + [Test] + public void c_simple_simple () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "i=1; a=rsa-sha256; cv=none; d=example.org; s=dummy; t=12345; b=utRLvF0lNhc2EUk27iMDUrE7jKP4eGWRy9iUKvmgcZtXY8f4x7r5zz8WzLzOUKo8KyUSgnzVWyyxtMqoAcGzzb/1HtwsEiT+B79pCkZDNqcH2E5dHHq7z8/QBwjScZWu3qFaFDtnMktVs13AMc6W1vgAueOyFjH3mdHmAbQBQaY="; + const string ams = "i=1; a=rsa-sha256; d=example.org; s=dummy; c=simple/simple; t=12345; h=mime-version:date:from:to:subject; bh=hhFbTjokraRYc/Af+8v4zyKm/9ApHGkBSLO129NtPbo=; b=GTpRE34Ud8TOECxNCRNw4jSncvc1nTlLNr6OJC6AjxmumsW2dvQglYIYJK5NO7zPKwzTVUlVB6ZznpIzaKJzsy5oBxV2frl5p5ZBFKGeiU94O0byPiATdwHe4lQiYUK5hBQV0Ei2sxSSlbQPXqjDymnZDcMPp/fLhDQETrjE6AI="; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("a=rsa-sha256", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal, DkimSignatureAlgorithm.RsaSha256, DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm.Simple); + } + + #region Canonicalization + + [Test] + public void message_body_eol_wsp () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=Pg8Yyk1AgYy2l+kb6iy+mY106AXm5EdgDwJhLP7+XyT6yaS38ZUho+bmgSDorV+LyARH4A 967A/oWMX3coyC7pAGyI+hA3+JifL7P3/aIVP4ooRJ/WUgT79snPuulxE15jg6FgQE68ObA1 /hy77BxdbD9EQxFGNcr/wCKQoeKJ8=; cv=none; d=example.org; i=1; s=dummy; t=12345"; + const string ams = "a=rsa-sha256; b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v rCB4wHD9GADeUKVfHzmpZhFuYOa88=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=1; s=dummy; t=12345"; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("message body eol whitespace ignored", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal); + } + + [Test] + public void message_body_inl_wsp () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=Pg8Yyk1AgYy2l+kb6iy+mY106AXm5EdgDwJhLP7+XyT6yaS38ZUho+bmgSDorV+LyARH4A 967A/oWMX3coyC7pAGyI+hA3+JifL7P3/aIVP4ooRJ/WUgT79snPuulxE15jg6FgQE68ObA1 /hy77BxdbD9EQxFGNcr/wCKQoeKJ8=; cv=none; d=example.org; i=1; s=dummy; t=12345"; + const string ams = "a=rsa-sha256; b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v rCB4wHD9GADeUKVfHzmpZhFuYOa88=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=1; s=dummy; t=12345"; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("message body inline whitespace reduced", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal); + } + + [Test] + public void message_body_end_lines () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=Pg8Yyk1AgYy2l+kb6iy+mY106AXm5EdgDwJhLP7+XyT6yaS38ZUho+bmgSDorV+LyARH4A 967A/oWMX3coyC7pAGyI+hA3+JifL7P3/aIVP4ooRJ/WUgT79snPuulxE15jg6FgQE68ObA1 /hy77BxdbD9EQxFGNcr/wCKQoeKJ8=; cv=none; d=example.org; i=1; s=dummy; t=12345"; + const string ams = "a=rsa-sha256; b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v rCB4wHD9GADeUKVfHzmpZhFuYOa88=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=1; s=dummy; t=12345"; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("message body ignore trailing empty lines", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal); + } + + [Test] + public void message_body_trail_crlf () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=Pg8Yyk1AgYy2l+kb6iy+mY106AXm5EdgDwJhLP7+XyT6yaS38ZUho+bmgSDorV+LyARH4A 967A/oWMX3coyC7pAGyI+hA3+JifL7P3/aIVP4ooRJ/WUgT79snPuulxE15jg6FgQE68ObA1 /hy77BxdbD9EQxFGNcr/wCKQoeKJ8=; cv=none; d=example.org; i=1; s=dummy; t=12345"; + const string ams = "a=rsa-sha256; b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v rCB4wHD9GADeUKVfHzmpZhFuYOa88=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=1; s=dummy; t=12345"; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("message body add crlf to end if na", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal); + } + + [Test] + public void headers_field_name_case () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +FROM: John Q Doe +to: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=Pg8Yyk1AgYy2l+kb6iy+mY106AXm5EdgDwJhLP7+XyT6yaS38ZUho+bmgSDorV+LyARH4A 967A/oWMX3coyC7pAGyI+hA3+JifL7P3/aIVP4ooRJ/WUgT79snPuulxE15jg6FgQE68ObA1 /hy77BxdbD9EQxFGNcr/wCKQoeKJ8=; cv=none; d=example.org; i=1; s=dummy; t=12345"; + const string ams = "a=rsa-sha256; b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v rCB4wHD9GADeUKVfHzmpZhFuYOa88=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=1; s=dummy; t=12345"; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("header field names case insensitive", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal); + } + + [Test] + public void headers_field_unfold () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe + +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=Pg8Yyk1AgYy2l+kb6iy+mY106AXm5EdgDwJhLP7+XyT6yaS38ZUho+bmgSDorV+LyARH4A 967A/oWMX3coyC7pAGyI+hA3+JifL7P3/aIVP4ooRJ/WUgT79snPuulxE15jg6FgQE68ObA1 /hy77BxdbD9EQxFGNcr/wCKQoeKJ8=; cv=none; d=example.org; i=1; s=dummy; t=12345"; + const string ams = "a=rsa-sha256; b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v rCB4wHD9GADeUKVfHzmpZhFuYOa88=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=1; s=dummy; t=12345"; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("header", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal); + } + + [Test] + public void headers_eol_wsp () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=Pg8Yyk1AgYy2l+kb6iy+mY106AXm5EdgDwJhLP7+XyT6yaS38ZUho+bmgSDorV+LyARH4A 967A/oWMX3coyC7pAGyI+hA3+JifL7P3/aIVP4ooRJ/WUgT79snPuulxE15jg6FgQE68ObA1 /hy77BxdbD9EQxFGNcr/wCKQoeKJ8=; cv=none; d=example.org; i=1; s=dummy; t=12345"; + const string ams = "a=rsa-sha256; b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v rCB4wHD9GADeUKVfHzmpZhFuYOa88=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=1; s=dummy; t=12345"; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("headers ignore eol whitespace", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal); + } + + [Test] + public void headers_inl_wsp () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=Pg8Yyk1AgYy2l+kb6iy+mY106AXm5EdgDwJhLP7+XyT6yaS38ZUho+bmgSDorV+LyARH4A 967A/oWMX3coyC7pAGyI+hA3+JifL7P3/aIVP4ooRJ/WUgT79snPuulxE15jg6FgQE68ObA1 /hy77BxdbD9EQxFGNcr/wCKQoeKJ8=; cv=none; d=example.org; i=1; s=dummy; t=12345"; + const string ams = "a=rsa-sha256; b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v rCB4wHD9GADeUKVfHzmpZhFuYOa88=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=1; s=dummy; t=12345"; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("header reduce inline whitespace", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal); + } + + [Test] + public void headers_col_wsp () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=Pg8Yyk1AgYy2l+kb6iy+mY106AXm5EdgDwJhLP7+XyT6yaS38ZUho+bmgSDorV+LyARH4A 967A/oWMX3coyC7pAGyI+hA3+JifL7P3/aIVP4ooRJ/WUgT79snPuulxE15jg6FgQE68ObA1 /hy77BxdbD9EQxFGNcr/wCKQoeKJ8=; cv=none; d=example.org; i=1; s=dummy; t=12345"; + const string ams = "a=rsa-sha256; b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v rCB4wHD9GADeUKVfHzmpZhFuYOa88=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=1; s=dummy; t=12345"; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("headers whitespace surrounding colon ignored", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal); + } + + #endregion + #region Existant Seal Headers + + [Test] + public void i0_base () + { + const string input = @"Authentication-Results: lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Return-Path: +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=Pg8Yyk1AgYy2l+kb6iy+mY106AXm5EdgDwJhLP7+XyT6yaS38ZUho+bmgSDorV+LyARH4A 967A/oWMX3coyC7pAGyI+hA3+JifL7P3/aIVP4ooRJ/WUgT79snPuulxE15jg6FgQE68ObA1 /hy77BxdbD9EQxFGNcr/wCKQoeKJ8=; cv=none; d=example.org; i=1; s=dummy; t=12345"; + const string ams = "a=rsa-sha256; b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v rCB4wHD9GADeUKVfHzmpZhFuYOa88=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=1; s=dummy; t=12345"; + const string aar = "i=1; lists.example.org; arc=none; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("i=0 basic test", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12345, hdrs, aar, ams, seal); + } + + [Test] + public void i1_base () + { + const string input = @"Authentication-Results: lists.example.org; arc=pass; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Received: by 10.157.11.240 with SMTP id 103csp420860oth; + Fri, 6 Jan 2017 14:27:31 -0800 (PST) +ARC-Seal: a=rsa-sha256; + b=KalMXVkx0O4PZIytFfe3i6B/c64408LkuF6rYR9HzBsTazolbsFg/nTah+zh9xmVnylcbg + gZnvu+Rte97HXb9fCK6/rAJQJ97NvYVgzwnEiAzCDts/3dS3SO+PyoAV2HxGMQlUgNeqidAc + mpm7kE3NbSpgq8Z/rsK5FZ7ADceXw=; cv=none; d=example.org; i=1; s=dummy; + t=12345 +ARC-Message-Signature: a=rsa-sha256; + b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm + 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v + rCB4wHD9GADeUKVfHzmpZhFuYOa88=; + bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; + d=example.org; h=mime-version:date:from:to:subject; + i=1; s=dummy; t=12345 +ARC-Authentication-Results: i=1; lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +Received: from segv.d1.example (segv.d1.example [72.52.75.15]) + by lists.example.org (8.14.5/8.14.5) with ESMTP id t0EKaNU9010123 + for ; Thu, 14 Jan 2015 15:01:30 -0800 (PST) + (envelope-from jqd@d1.example) +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=KiRMwS+rbu4ScgsYQGrZdW72DMVPKRnmkXigPU2FpNPTRViMIRIclMAa48kvbOJ/APWJuG eX3uW2PfI3u2EKtDitHFLvlU2LlCkHhyp8HSO5VJFr0aAk9Z3aQhcoE5hWJ061NXe8C1nafG 4ctcT8p0xkTjVrL9EVsz26o0mRlXY=; cv=pass; d=example.org; i=2; s=dummy; t=12346"; + const string ams = "a=rsa-sha256; b=UaNJhLFAa56Gpc+wKk0SL2Jq/LJgT9CYSZl59wcGYkpG0D5bjhDdj3qers6hD+3BpljNgn mFxq8zWssoPon3ydvTSCSjVwPRNgLol9zBP+FZo/QGQQbj74ZcGv04jOVe8TKDTFSaVe41L7 mH16ZdoLgRdSa2Ys+p9f0+DVFYTO4=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=2; s=dummy; t=12346"; + const string aar = "i=2; lists.example.org; arc=pass; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("i=1 basic test", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12346, hdrs, aar, ams, seal); + } + + [Test] + public void i2_base () + { + const string input = @"Authentication-Results: lists.example.org; arc=pass; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +ARC-Seal: a=rsa-sha256; + b=I8bdOhGPwqIRyhSYZysZdwFJmD/gRxZR3Dn8BQdKkv3fOsWG8A2aftWwnAHKsNreVi6MUF + W4M3tVxsG+pF52qzl3zQGn3bkQIS1N700fbu0z0Lg8IW/gcxziGJlLgK5Bk70uN1egGgdLwn + SiywwvouD7BX1ZlkxFk9i84SDf8/w=; cv=pass; d=example.org; i=2; s=dummy; + t=12346 +ARC-Message-Signature: a=rsa-sha256; + b=UaNJhLFAa56Gpc+wKk0SL2Jq/LJgT9CYSZl59wcGYkpG0D5bjhDdj3qers6hD+3BpljNgn + mFxq8zWssoPon3ydvTSCSjVwPRNgLol9zBP+FZo/QGQQbj74ZcGv04jOVe8TKDTFSaVe41L7 + mH16ZdoLgRdSa2Ys+p9f0+DVFYTO4=; + bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; + d=example.org; h=mime-version:date:from:to:subject; + i=2; s=dummy; t=12346 +ARC-Authentication-Results: i=2; lists.example.org; arc=pass; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +Received: by 10.157.11.240 with SMTP id 103csp420860oth; + Fri, 6 Jan 2017 14:27:31 -0800 (PST) +ARC-Seal: a=rsa-sha256; + b=KalMXVkx0O4PZIytFfe3i6B/c64408LkuF6rYR9HzBsTazolbsFg/nTah+zh9xmVnylcbg + gZnvu+Rte97HXb9fCK6/rAJQJ97NvYVgzwnEiAzCDts/3dS3SO+PyoAV2HxGMQlUgNeqidAc + mpm7kE3NbSpgq8Z/rsK5FZ7ADceXw=; cv=none; d=example.org; i=1; s=dummy; + t=12345 +ARC-Message-Signature: a=rsa-sha256; + b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm + 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v + rCB4wHD9GADeUKVfHzmpZhFuYOa88=; + bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; + d=example.org; h=mime-version:date:from:to:subject; + i=1; s=dummy; t=12345 +ARC-Authentication-Results: i=1; lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +Received: from segv.d1.example (segv.d1.example [72.52.75.15]) + by lists.example.org (8.14.5/8.14.5) with ESMTP id t0EKaNU9010123 + for ; Thu, 14 Jan 2015 15:01:30 -0800 (PST) + (envelope-from jqd@d1.example) +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe +To: arc@dmarc.org +Subject: Example 1 + +Hey gang, +This is a test message. +--J. +"; + const string keyblock = @"-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQi +Y/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqM +KrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB +AoGAH0cxOhFZDgzXWhDhnAJDw5s4roOXN4OhjiXa8W7Y3rhX3FJqmJSPuC8N9vQm +6SVbaLAE4SG5mLMueHlh4KXffEpuLEiNp9Ss3O4YfLiQpbRqE7Tm5SxKjvvQoZZe +zHorimOaChRL2it47iuWxzxSiRMv4c+j70GiWdxXnxe4UoECQQDzJB/0U58W7RZy +6enGVj2kWF732CoWFZWzi1FicudrBFoy63QwcowpoCazKtvZGMNlPWnC7x/6o8Gc +uSe0ga2xAkEA8C7PipPm1/1fTRQvj1o/dDmZp243044ZNyxjg+/OPN0oWCbXIGxy +WvmZbXriOWoSALJTjExEgraHEgnXssuk7QJBALl5ICsYMu6hMxO73gnfNayNgPxd +WFV6Z7ULnKyV7HSVYF0hgYOHjeYe9gaMtiJYoo0zGN+L3AAtNP9huqkWlzECQE1a +licIeVlo1e+qJ6Mgqr0Q7Aa7falZ448ccbSFYEPD6oFxiOl9Y9se9iYHZKKfIcst +o7DUw1/hz2Ck4N5JrgUCQQCyKveNvjzkkd8HjYs0SwM0fPjK16//5qDZ2UiDGnOe +uEzxBDAr518Z8VFbR41in3W4Y3yCDgQlLlcETrS+zYcL +-----END RSA PRIVATE KEY----- +"; + const string seal = "a=rsa-sha256; b=amYobvirySPk39Y45uHWIsJOGQ0pDhn3D8ncaOew7h+9cddITDFGght2qHYE0GLdpDtLUG J1CwEoM6hdVhG6BkJ80vHzy09Wj2id7B3DMpytPm9MjU7K6Le9VGdewFItwhmG+c15l8krp6 sKw7wUlgM60lSZT0EYTC2x8NXjNDI=; cv=pass; d=example.org; i=3; s=dummy; t=12347"; + const string ams = "a=rsa-sha256; b=QmCd8uJdwnr6wMmniYA/VHCuWButAIlcPZSpNWvk8KHgTuFMZlCPQToT2qVpf2BUfdNpnC mSCED02aLfV6Grc6caqO4PIaxyu3Z+/HNxh0NugIW2JVHT1cZicWkwlgZa4V9i+CYFBAYmzb L0n4ibTxSX8XPxR9ffZdknwiLmYsA=; bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; d=example.org; h=mime-version:date:from:to:subject; i=3; s=dummy; t=12347"; + const string aar = "i=3; lists.example.org; arc=pass; spf=pass smtp.mfrom=jqd@d1.example; dkim=pass (1024-bit key) header.i=@d1.example; dmarc=pass"; + var hdrs = new string[] { "mime-version", "date", "from", "to", "subject" }; + var locator = new DkimPublicKeyLocator (); + + locator.Add ("dummy._domainkey.example.org", "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkHlOQoBTzWRiGs5V6NpP3idY6Wk08a5qhdR6wy5bdOKb2jLQiY/J16JYi0Qvx/byYzCNb3W91y3FutACDfzwQ/BC/e/8uBsCR+yz1Lxj+PL6lHvqMKrM3rG4hstT5QjvHO9PzoxZyVYLzBfO2EeC3Ip3G+2kryOTIKT+l/K4w3QIDAQAB"); + + Sign ("i=2 basic test", input, locator, "lists.example.org", "example.org", "dummy", keyblock, 12347, hdrs, aar, ams, seal); + } + + [Test] + public void i1_base_fail () + { + const string input = @"Authentication-Results: lists.example.org; arc=fail; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +MIME-Version: 1.0 +Received: by 10.157.11.240 with SMTP id 103csp420860oth; + Fri, 6 Jan 2017 14:27:31 -0800 (PST) +ARC-Seal: a=rsa-sha256; + b=kalMXVkx0O4PZIytFfe3i6B/c64408LkuF6rYR9HzBsTazolbsFg/nTah+zh9xmVnylcbg + gZnvu+Rte97HXb9fCK6/rAJQJ97NvYVgzwnEiAzCDts/3dS3SO+PyoAV2HxGMQlUgNeqidAc + mpm7kE3NbSpgq8Z/rsK5FZ7ADceXw=; cv=none; d=example.org; i=1; s=dummy; + t=12345 +ARC-Message-Signature: a=rsa-sha256; + b=XWeK9DxQ8MUm+Me5GLZ5lQ3L49RdoFv7m7VlrAkKb3/C7jjw33TrTY0KYI5lkowvEGnAtm + 5lAqLz67FxA/VrJc2JiYFQR/mBoJLLz/hh9y77byYmSO9tLfIDe2A83+6QsXHO3K6PxTz7+v + rCB4wHD9GADeUKVfHzmpZhFuYOa88=; + bh=KWSe46TZKCcDbH4klJPo+tjk5LWJnVRlP5pvjXFZYLQ=; c=relaxed/relaxed; + d=example.org; h=mime-version:date:from:to:subject; + i=1; s=dummy; t=12345 +ARC-Authentication-Results: i=1; lists.example.org; arc=none; + spf=pass smtp.mfrom=jqd@d1.example; + dkim=pass (1024-bit key) header.i=@d1.example; + dmarc=pass +Received: from segv.d1.example (segv.d1.example [72.52.75.15]) + by lists.example.org (8.14.5/8.14.5) with ESMTP id t0EKaNU9010123 + for ; Thu, 14 Jan 2015 15:01:30 -0800 (PST) + (envelope-from jqd@d1.example) +Received: by 10.157.14.6 with HTTP; Tue, 3 Jan 2017 12:22:54 -0800 (PST) +Message-ID: <54B84785.1060301@d1.example.org> +Date: Thu, 14 Jan 2015 15:00:01 -0800 +From: John Q Doe