public bool IsTagReallySafe(HtmlTag tag) { switch (tag.name) { case "B": case "UL": case "LI": case "I": return tag.attributes.Count == 0; case "A": case "a": return tag.closing && tag.attributes.Count == 0; } return false; }
internal void RenderLink(Markdown m, StringBuilder b, string link_text) { if (url.StartsWith("mailto:")) { b.Append("<a href=\""); Utils.HtmlRandomize(b, url); b.Append('\"'); if (!String.IsNullOrEmpty(title)) { b.Append(" title=\""); Utils.SmartHtmlEncodeAmpsAndAngles(b, title); b.Append('\"'); } b.Append('>'); Utils.HtmlRandomize(b, link_text); b.Append("</a>"); } else { HtmlTag tag = new HtmlTag("a"); // encode url StringBuilder sb = m.GetStringBuilder(); Utils.SmartHtmlEncodeAmpsAndAngles(sb, url); tag.attributes["href"] = sb.ToString(); // encode title if (!String.IsNullOrEmpty(title )) { sb.Length = 0; Utils.SmartHtmlEncodeAmpsAndAngles(sb, title); tag.attributes["title"] = sb.ToString(); } // Do user processing m.OnPrepareLink(tag); // Render the opening tag tag.RenderOpening(b); b.Append(link_text); // Link text already escaped by SpanFormatter b.Append("</a>"); } }
internal void RenderImg(Markdown m, StringBuilder b, string alt_text) { HtmlTag tag = new HtmlTag("img"); // encode url StringBuilder sb = m.GetStringBuilder(); Utils.SmartHtmlEncodeAmpsAndAngles(sb, url); tag.attributes["src"] = sb.ToString(); // encode alt text if (!String.IsNullOrEmpty(alt_text)) { sb.Length = 0; Utils.SmartHtmlEncodeAmpsAndAngles(sb, alt_text); tag.attributes["alt"] = sb.ToString(); } // encode title if (!String.IsNullOrEmpty(title)) { sb.Length = 0; Utils.SmartHtmlEncodeAmpsAndAngles(sb, title); tag.attributes["title"] = sb.ToString(); } tag.closed = true; m.OnPrepareImage(tag, m.RenderingTitledImage); tag.RenderOpening(b); }
private static HtmlTag ParseHelper(StringScanner p) { // Does it look like a tag? if (p.current != '<') return null; // Skip '<' p.SkipForward(1); // Is it a comment? if (p.SkipString("!--")) { p.Mark(); if (p.Find("-->")) { var t = new HtmlTag("!"); t.m_attributes.Add("content", p.Extract()); t.m_closed = true; p.SkipForward(3); return t; } } // Is it a closing tag eg: </div> bool bClosing = p.SkipChar('/'); // Get the tag name string tagName=null; if (!p.SkipIdentifier(ref tagName)) return null; // Probably a tag, create the HtmlTag object now HtmlTag tag = new HtmlTag(tagName); tag.m_closing = bClosing; // If it's a closing tag, no attributes if (bClosing) { if (p.current != '>') return null; p.SkipForward(1); return tag; } while (!p.eof) { // Skip whitespace p.SkipWhitespace(); // Check for closed tag eg: <hr /> if (p.SkipString("/>")) { tag.m_closed=true; return tag; } // End of tag? if (p.SkipChar('>')) { return tag; } // attribute name string attributeName = null; if (!p.SkipIdentifier(ref attributeName)) return null; // Skip whitespace p.SkipWhitespace(); // Skip equal sign if (p.SkipChar('=')) { // Skip whitespace p.SkipWhitespace(); // Optional quotes if (p.SkipChar('\"')) { // Scan the value p.Mark(); if (!p.Find('\"')) return null; // Store the value tag.m_attributes.Add(attributeName, p.Extract()); // Skip closing quote p.SkipForward(1); } else { // Scan the value p.Mark(); while (!p.eof && !char.IsWhiteSpace(p.current) && p.current != '>' && p.current != '/') p.SkipForward(1); if (!p.eof) { // Store the value tag.m_attributes.Add(attributeName, p.Extract()); } } } else { tag.m_attributes.Add(attributeName, ""); } } return null; }
// Override to modify the attributes of a link public virtual void OnPrepareLink(HtmlTag tag) { if (PrepareLink != null) { if (PrepareLink(tag)) return; } string url = tag.attributes["href"]; // IsUrlFullyQualified analyzer bool? isUrlFullyQualified_cache = null; Func<bool> fncIsUrlFullyQualified = () => { if (!isUrlFullyQualified_cache.HasValue) isUrlFullyQualified_cache = Utils.IsUrlFullyQualified(url); return isUrlFullyQualified_cache.Value; }; // No follow? if (this.NoFollowLinks && fncIsUrlFullyQualified()) { tag.attributes["rel"] = "nofollow"; } // New window? if ( (NewWindowForExternalLinks && fncIsUrlFullyQualified()) || (NewWindowForLocalLinks && !fncIsUrlFullyQualified()) ) { tag.attributes["target"] = "_blank"; } // Qualify url tag.attributes["href"] = OnQualifyUrl(url); }
// Override to modify the attributes of an image public virtual void OnPrepareImage(HtmlTag tag, bool TitledImage) { if (PrepareImage != null) { if (PrepareImage(tag, TitledImage)) return; } // Try to determine width and height int width, height; if (OnGetImageSize(tag.attributes["src"], TitledImage, out width, out height)) { tag.attributes["width"] = width.ToString(); tag.attributes["height"] = height.ToString(); } // Now qualify the url tag.attributes["src"] = OnQualifyUrl(tag.attributes["src"]); }
internal bool ProcessMarkdownEnabledHtml(Block b, HtmlTag openingTag, MarkdownInHtmlMode mode) { // Current position is just after the opening tag // Scan until we find matching closing tag int inner_pos = position; int depth = 1; bool bHasUnsafeContent = false; while (!eof) { // Find next angle bracket if (!Find('<')) break; // Is it a html tag? int tagpos = position; HtmlTag tag = HtmlTag.Parse(this); if (tag == null) { // Nope, skip it SkipForward(1); continue; } // In markdown off mode, we need to check for unsafe tags if (m_markdown.SafeMode && mode == MarkdownInHtmlMode.Off && !bHasUnsafeContent) { if (!tag.IsSafe()) bHasUnsafeContent = true; } // Ignore self closing tags if (tag.closed) continue; // Same tag? if (tag.name == openingTag.name) { if (tag.closing) { depth--; if (depth == 0) { // End of tag? SkipLinespace(); SkipEol(); b.blockType = BlockType.HtmlTag; b.data = openingTag; b.contentEnd = position; switch (mode) { case MarkdownInHtmlMode.Span: { Block span = this.CreateBlock(); span.buf = input; span.blockType = BlockType.span; span.contentStart = inner_pos; span.contentLen = tagpos - inner_pos; b.children = new List<Block>(); b.children.Add(span); break; } case MarkdownInHtmlMode.Block: case MarkdownInHtmlMode.Deep: { // Scan the internal content var bp = new BlockProcessor(m_markdown, mode == MarkdownInHtmlMode.Deep); b.children = bp.ScanLines(input, inner_pos, tagpos - inner_pos); break; } case MarkdownInHtmlMode.Off: { if (bHasUnsafeContent) { b.blockType = BlockType.unsafe_html; b.contentEnd = position; } else { Block span = this.CreateBlock(); span.buf = input; span.blockType = BlockType.html; span.contentStart = inner_pos; span.contentLen = tagpos - inner_pos; b.children = new List<Block>(); b.children.Add(span); } break; } } return true; } } else { depth++; } } } // Missing closing tag(s). return false; }
internal MarkdownInHtmlMode GetMarkdownMode(HtmlTag tag) { // Get the markdown attribute string strMarkdownMode; if (!m_markdown.ExtraMode || !tag.attributes.TryGetValue("markdown", out strMarkdownMode)) { if (m_bMarkdownInHtml) return MarkdownInHtmlMode.Deep; else return MarkdownInHtmlMode.NA; } // Remove it tag.attributes.Remove("markdown"); // Parse mode if (strMarkdownMode == "1") return (tag.Flags & HtmlTagFlags.ContentAsSpan)!=0 ? MarkdownInHtmlMode.Span : MarkdownInHtmlMode.Block; if (strMarkdownMode == "block") return MarkdownInHtmlMode.Block; if (strMarkdownMode == "deep") return MarkdownInHtmlMode.Deep; if (strMarkdownMode == "span") return MarkdownInHtmlMode.Span; return MarkdownInHtmlMode.Off; }