// (C) 2003 Aleksey Sanin (aleksey@aleksey.com)
//
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
using System.Collections;
using System.IO;
using System.Text;
// c14n parameters
private bool comments;
private bool exclusive;
+ string inclusiveNamespacesPrefixList;
// input/output
private XmlNodeList xnl;
private ArrayList visibleNamespaces;
private int prevVisibleNamespacesStart;
private int prevVisibleNamespacesEnd;
+ private Hashtable propagatedNss;
- public XmlCanonicalizer (bool withComments, bool excC14N)
+ public XmlCanonicalizer (bool withComments, bool excC14N, Hashtable propagatedNamespaces)
{
res = new StringBuilder ();
comments = withComments;
exclusive = excC14N;
+ propagatedNss = propagatedNamespaces;
+ }
+
+ void Initialize ()
+ {
state = XmlCanonicalizerState.BeforeDocElement;
visibleNamespaces = new ArrayList ();
prevVisibleNamespacesStart = 0;
prevVisibleNamespacesEnd = 0;
+ res.Length = 0;
}
public Stream Canonicalize (XmlDocument doc)
{
+ if (doc == null)
+ throw new ArgumentNullException ("doc");
+ Initialize ();
+
+ FillMissingPrefixes (doc, new XmlNamespaceManager (doc.NameTable), new ArrayList ());
WriteDocumentNode (doc);
UTF8Encoding utf8 = new UTF8Encoding ();
{
xnl = nodes;
if (nodes == null || nodes.Count < 1)
- return null;
- return Canonicalize (nodes[0].OwnerDocument);
+ return new MemoryStream ();
+ XmlNode n = nodes [0];
+ return Canonicalize (n.NodeType == XmlNodeType.Document ? n as XmlDocument : n.OwnerDocument);
}
+ // See xml-enc-c14n specification
+ public string InclusiveNamespacesPrefixList {
+ get { return inclusiveNamespacesPrefixList; }
+ set { inclusiveNamespacesPrefixList = value; }
+ }
+
+ XmlAttribute CreateXmlns (XmlNode n)
+ {
+ XmlAttribute a = n.Prefix.Length == 0 ?
+ n.OwnerDocument.CreateAttribute ("xmlns", "http://www.w3.org/2000/xmlns/") :
+ n.OwnerDocument.CreateAttribute ("xmlns", n.Prefix, "http://www.w3.org/2000/xmlns/");
+ a.Value = n.NamespaceURI;
+ return a;
+ }
+
+ // Note that this must be done *before* filtering nodes out
+ // by context node list.
+ private void FillMissingPrefixes (XmlNode n, XmlNamespaceManager nsmgr, ArrayList tmpList)
+ {
+ if (n.Prefix.Length == 0 && propagatedNss != null) {
+ foreach (DictionaryEntry de in propagatedNss)
+ if ((string) de.Value == n.NamespaceURI) {
+ n.Prefix = (string) de.Key;
+ break;
+ }
+ }
+
+ if (n.NodeType == XmlNodeType.Element && ((XmlElement) n).HasAttributes) {
+ foreach (XmlAttribute a in n.Attributes)
+ if (a.NamespaceURI == "http://www.w3.org/2000/xmlns/")
+ nsmgr.AddNamespace (a.Prefix.Length == 0 ? String.Empty : a.LocalName, a.Value);
+ nsmgr.PushScope ();
+ }
+
+ if (n.NamespaceURI.Length > 0 && nsmgr.LookupPrefix (n.NamespaceURI) == null)
+ tmpList.Add (CreateXmlns (n));
+
+ if (n.NodeType == XmlNodeType.Element && ((XmlElement) n).HasAttributes) {
+ foreach (XmlAttribute a in n.Attributes)
+ if (a.NamespaceURI.Length > 0 && nsmgr.LookupNamespace (a.Prefix) == null)
+ tmpList.Add (CreateXmlns (a));
+ }
+
+ foreach (XmlAttribute a in tmpList)
+ ((XmlElement) n).SetAttributeNode (a);
+ tmpList.Clear ();
+
+ if (n.HasChildNodes) {
+ for (XmlNode c = n.FirstChild; c != null; c = c.NextSibling)
+ if (c.NodeType == XmlNodeType.Element)
+ FillMissingPrefixes (c, nsmgr, tmpList);
+ }
+ nsmgr.PopScope ();
+ }
+
private void WriteNode (XmlNode node)
{
// Console.WriteLine ("C14N Debug: node=" + node.Name);
WriteProcessingInstructionNode (node, visible);
break;
case XmlNodeType.EntityReference:
- throw new XmlException ("Entity references should be resolved by parser");
+ for (int i = 0; i < node.ChildNodes.Count; i++)
+ WriteNode (node.ChildNodes [i]);
+ break;
case XmlNodeType.Attribute:
- throw new XmlException ("Attribute node is impossible here");
+ throw new XmlException ("Attribute node is impossible here", null);
case XmlNodeType.EndElement:
- throw new XmlException ("EndElement node is impossible here");
+ throw new XmlException ("EndElement node is impossible here", null);
case XmlNodeType.EndEntity:
- throw new XmlException ("EndEntity node is impossible here");
+ throw new XmlException ("EndEntity node is impossible here", null);
case XmlNodeType.DocumentType:
case XmlNodeType.Entity:
case XmlNodeType.Notation:
bool has_empty_namespace = false;
ArrayList list = new ArrayList ();
for (XmlNode cur = node; cur != null && cur != doc; cur = cur.ParentNode) {
- foreach (XmlNode attribute in cur.Attributes) {
+ foreach (XmlAttribute attribute in cur.Attributes) {
if (!IsNamespaceNode (attribute))
continue;
// check that we have not rendered it yet
bool rendered = IsNamespaceRendered (prefix, attribute.Value);
+ // For exc-c14n, only visibly utilized
+ // namespaces are written.
+ if (exclusive && !IsVisiblyUtilized (node as XmlElement, attribute))
+ continue;
+
// add to the visible namespaces stack
if (visible)
visibleNamespaces.Add (attribute);
}
}
- // add empty namespace if needed
- if (visible && !has_empty_namespace && !IsNamespaceRendered (string.Empty, string.Empty))
+ // add empty namespace if needed
+ if (visible && !has_empty_namespace && !IsNamespaceRendered (string.Empty, string.Empty) && node.NamespaceURI == String.Empty)
res.Append (" xmlns=\"\"");
list.Sort (new XmlDsigC14NTransformNamespacesComparer ());
// nodes of E's attribute axis that are in the node-set. The
// result of visiting the attribute axis is computed by
// processing the attribute nodes in this merged attribute list.
- if (!exclusive && node.ParentNode != null && !IsNodeVisible (node.ParentNode.ParentNode)) {
+ if (!exclusive && node.ParentNode != null && node.ParentNode.ParentNode != null && !IsNodeVisible (node.ParentNode.ParentNode)) {
// if we have whole document then the node.ParentNode.ParentNode
// is always visible
for (XmlNode cur = node.ParentNode; cur != null; cur = cur.ParentNode) {
+ if (cur.Attributes == null)
+ continue;
foreach (XmlNode attribute in cur.Attributes) {
// we are looking for "xml:*" attributes
if (attribute.Prefix != "xml")
{
// Console.WriteLine ("Debug: text node");
if (visible)
- res.Append (NormalizeString (node.Value, XmlNodeType.Text));
+ res.Append (NormalizeString (node.Value, node.NodeType));
+// res.Append (NormalizeString (node.Value, XmlNodeType.Text));
}
// Comment Nodes
res.Append ("?>");
}
}
-
+
+ // determines whether the node is in the node-set or not.
private bool IsNodeVisible (XmlNode node)
{
// if node list is empty then we process whole document
return false;
}
+
+ // This method assumes that the namespace node is *not*
+ // rendered yet.
+ private bool IsVisiblyUtilized (XmlElement owner, XmlAttribute ns)
+ {
+ if (owner == null)
+ return false;
+
+ string prefix = ns.LocalName == "xmlns" ? String.Empty : ns.LocalName;
+ if (owner.Prefix == prefix && owner.NamespaceURI == ns.Value)
+ return true;
+ if (!owner.HasAttributes)
+ return false;
+ foreach (XmlAttribute a in owner.Attributes) {
+ if (a.Prefix == String.Empty)
+ continue;
+ if (a.Prefix != prefix || a.NamespaceURI != ns.Value)
+ continue;
+ if (IsNodeVisible (a))
+ return true;
+ }
+ return false;
+ }
private bool IsNamespaceRendered (string prefix, string uri)
{
string p = string.Empty;
if (node.Prefix == "xmlns")
p = node.LocalName;
- if (p == prefix)
+ if (p == prefix)
return node.Value == uri;
}
}
return node.NamespaceURI == "http://www.w3.org/2000/xmlns/";
}
+ private bool IsTextNode (XmlNodeType type)
+ {
+ switch (type) {
+ case XmlNodeType.Text:
+ case XmlNodeType.CDATA:
+ case XmlNodeType.SignificantWhitespace:
+ case XmlNodeType.Whitespace:
+ return true;
+ }
+ return false;
+ }
+
private string NormalizeString (string input, XmlNodeType type)
{
StringBuilder sb = new StringBuilder ();
for (int i = 0; i < input.Length; i++) {
char ch = input[i];
- if (ch == '<' && (type == XmlNodeType.Attribute || type == XmlNodeType.Text))
+ if (ch == '<' && (type == XmlNodeType.Attribute || IsTextNode (type)))
sb.Append ("<");
- else if (ch == '>' && type == XmlNodeType.Text)
+ else if (ch == '>' && IsTextNode (type))
sb.Append (">");
- else if (ch == '&' && (type == XmlNodeType.Attribute || type == XmlNodeType.Text))
+ else if (ch == '&' && (type == XmlNodeType.Attribute || IsTextNode (type)))
sb.Append ("&");
else if (ch == '\"' && type == XmlNodeType.Attribute)
sb.Append (""");
sb.Append ("	");
else if (ch == '\x0A' && type == XmlNodeType.Attribute)
sb.Append ("
");
- else if (ch == '\x0D' && (type == XmlNodeType.Attribute ||
- type == XmlNodeType.Text ||
- type == XmlNodeType.Comment ||
- type == XmlNodeType.ProcessingInstruction))
+ else if (ch == '\x0D')
sb.Append ("
");
else
sb.Append (ch);