Fossil

Check-in [382f3731ee]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Update the HTML safeing mechanism so that it does the entire Markdown output all at once. In this way it is better able to cope with block markup that spans multiple paragraphs.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 382f3731ee65dfd0ac5a9a39e725d6af11c2a3b242781cf7bcd869ad3a4866d8
User & Date: drh 2020-06-02 17:38:24.703
Context
2020-06-02
18:55
Documentation improvements on the HTML safer. Only apply safe-html to Forum posts for the moment. ... (check-in: 03ce4e70b6 user: drh tags: trunk)
17:38
Update the HTML safeing mechanism so that it does the entire Markdown output all at once. In this way it is better able to cope with block markup that spans multiple paragraphs. ... (check-in: 382f3731ee user: drh tags: trunk)
15:14
Only use safe-html markdown rendering for the Forum. This is a temporary measure to get most things working again while we devise improvements to the safe-html mechanism. ... (check-in: f4e6efb2da user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/browse.c.
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
        @ sandbox="allow-same-origin"
        @ onload="this.height=this.contentDocument.documentElement.scrollHeight;">
        @ </iframe>
      }else{
        Blob content;
        const char *zMime = mimetype_from_name(zName);
        content_get(rid, &content);
        wiki_render_by_mimetype(&content, zMime);
      }
    }
  }
  db_finalize(&q);
  style_footer();
}








|







388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
        @ sandbox="allow-same-origin"
        @ onload="this.height=this.contentDocument.documentElement.scrollHeight;">
        @ </iframe>
      }else{
        Blob content;
        const char *zMime = mimetype_from_name(zName);
        content_get(rid, &content);
        wiki_render_by_mimetype(&content, zMime, 0);
      }
    }
  }
  db_finalize(&q);
  style_footer();
}

Changes to src/event.c.
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
    wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
    @ </td></tr></table>
    @ </blockquote>
    @ <p><b>Page content preview:</b><p>
    @ <blockquote>
    blob_init(&event, 0, 0);
    blob_append(&event, zBody, -1);
    wiki_render_by_mimetype(&event, zMimetype);
    @ </blockquote><hr />
    blob_reset(&event);
  }
  for(n=2, z=zBody; z[0]; z++){
    if( z[0]=='\n' ) n++;
  }
  if( n<20 ) n = 20;







|







506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
    wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
    @ </td></tr></table>
    @ </blockquote>
    @ <p><b>Page content preview:</b><p>
    @ <blockquote>
    blob_init(&event, 0, 0);
    blob_append(&event, zBody, -1);
    wiki_render_by_mimetype(&event, zMimetype, 0);
    @ </blockquote><hr />
    blob_reset(&event);
  }
  for(n=2, z=zBody; z[0]; z++){
    if( z[0]=='\n' ) n++;
  }
  if( n<20 ) n = 20;
Changes to src/fileedit.c.
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
      break;
    }
    case FE_RENDER_HTML_INLINE:{
      CX("%b",pContent);
      break;
    }
    case FE_RENDER_WIKI:
      wiki_render_by_mimetype(pContent, zMime);
      break;
    default:{
      const char *zExt = strrchr(zFilename,'.');
      const char *zContent = blob_str(pContent);
      if(FE_PREVIEW_LINE_NUMBERS & flags){
        output_text_with_line_numbers(zContent, "on");
      }else if(zExt && zExt[1]){







|







974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
      break;
    }
    case FE_RENDER_HTML_INLINE:{
      CX("%b",pContent);
      break;
    }
    case FE_RENDER_WIKI:
      wiki_render_by_mimetype(pContent, zMime, 0);
      break;
    default:{
      const char *zExt = strrchr(zFilename,'.');
      const char *zContent = blob_str(pContent);
      if(FE_PREVIEW_LINE_NUMBERS & flags){
        output_text_with_line_numbers(zContent, "on");
      }else if(zExt && zExt[1]){
Changes to src/forum.c.
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
    if( bScroll ){
      @ <div class='forumPostBody'>
    }else{
      @ <div class='forumPostFullBody'>
    }
    blob_init(&x, 0, 0);
    blob_append(&x, zContent, -1);
    safe_html_enable(1);
    wiki_render_by_mimetype(&x, zMimetype);
    blob_reset(&x);
    @ </div>
  }else{
    @ <i>Deleted</i>
  }
  if( zClass ){
    @ </div>







<
|







338
339
340
341
342
343
344

345
346
347
348
349
350
351
352
    if( bScroll ){
      @ <div class='forumPostBody'>
    }else{
      @ <div class='forumPostFullBody'>
    }
    blob_init(&x, 0, 0);
    blob_append(&x, zContent, -1);

    wiki_render_by_mimetype(&x, zMimetype, WIKI_SAFE);
    blob_reset(&x);
    @ </div>
  }else{
    @ <i>Deleted</i>
  }
  if( zClass ){
    @ </div>
Changes to src/info.c.
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
    @ </form>
    @ </blockquote>
  }


  @ <div class="section">Content</div>
  blob_init(&wiki, pWiki->zWiki, -1);
  wiki_render_by_mimetype(&wiki, pWiki->zMimetype);
  blob_reset(&wiki);
  manifest_destroy(pWiki);
  style_footer();
}

/*
** Find an check-in based on query parameter zParam and parse its







|







1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
    @ </form>
    @ </blockquote>
  }


  @ <div class="section">Content</div>
  blob_init(&wiki, pWiki->zWiki, -1);
  wiki_render_by_mimetype(&wiki, pWiki->zMimetype, 0);
  blob_reset(&wiki);
  manifest_destroy(pWiki);
  style_footer();
}

/*
** Find an check-in based on query parameter zParam and parse its
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
  }
  if( descOnly ){
    style_submenu_element("Content", "%R/artifact/%s", zUuid);
  }else{
    @ <hr />
    content_get(rid, &content);
    if( renderAsWiki ){
      wiki_render_by_mimetype(&content, zMime);
    }else if( renderAsHtml ){
      @ <iframe src="%R/raw/%s(zUuid)"
      @ width="100%%" frameborder="0" marginwidth="0" marginheight="0"
      @ sandbox="allow-same-origin" id="ifm1">
      @ </iframe>
      @ <script nonce="%h(style_nonce())">
      @ document.getElementById("ifm1").addEventListener("load",







|







2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
  }
  if( descOnly ){
    style_submenu_element("Content", "%R/artifact/%s", zUuid);
  }else{
    @ <hr />
    content_get(rid, &content);
    if( renderAsWiki ){
      wiki_render_by_mimetype(&content, zMime, 0);
    }else if( renderAsHtml ){
      @ <iframe src="%R/raw/%s(zUuid)"
      @ width="100%%" frameborder="0" marginwidth="0" marginheight="0"
      @ sandbox="allow-same-origin" id="ifm1">
      @ </iframe>
      @ <script nonce="%h(style_nonce())">
      @ document.getElementById("ifm1").addEventListener("load",
Changes to src/markdown_html.c.
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
   * The empty string in the second argument leads to a syntax error
   * when the macro is not used with a string literal. Unfortunately
   * the error is not overly explicit.
   */

/* BLOB_APPEND_BLOB -- append blob contents to another */
#define BLOB_APPEND_BLOB(dest, src) \
  safe_html_append((dest), blob_buffer(src), blob_size(src))


/* HTML escapes
**
** html_escape() converts < to &lt;, > to &gt;, and & to &amp;.
** html_quote() goes further and converts " into &quot; and ' in &#39;.
*/







|







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
   * The empty string in the second argument leads to a syntax error
   * when the macro is not used with a string literal. Unfortunately
   * the error is not overly explicit.
   */

/* BLOB_APPEND_BLOB -- append blob contents to another */
#define BLOB_APPEND_BLOB(dest, src) \
  blob_append((dest), blob_buffer(src), blob_size(src))


/* HTML escapes
**
** html_escape() converts < to &lt;, > to &gt;, and & to &amp;.
** html_quote() goes further and converts " into &quot; and ' in &#39;.
*/
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
   && sqlite3_strnicmp("</h1>", &data[size-5],5)==0
  ){
    int nTag = html_tag_length(data);
    blob_append(title, data+nTag, size - nTag - 5);
    return;
  }
  INTER_BLOCK(ob);
  safe_html_append(ob, data, size);
  BLOB_APPEND_LITERAL(ob, "\n");
}

static void html_blockcode(struct Blob *ob, struct Blob *text, void *opaque){
  INTER_BLOCK(ob);
  BLOB_APPEND_LITERAL(ob, "<pre><code>");
  html_escape(ob, blob_buffer(text), blob_size(text));







|







145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
   && sqlite3_strnicmp("</h1>", &data[size-5],5)==0
  ){
    int nTag = html_tag_length(data);
    blob_append(title, data+nTag, size - nTag - 5);
    return;
  }
  INTER_BLOCK(ob);
  blob_append(ob, data, size);
  BLOB_APPEND_LITERAL(ob, "\n");
}

static void html_blockcode(struct Blob *ob, struct Blob *text, void *opaque){
  INTER_BLOCK(ob);
  BLOB_APPEND_LITERAL(ob, "<pre><code>");
  html_escape(ob, blob_buffer(text), blob_size(text));
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  int flags,
  void *opaque
){
  char *text_data = blob_buffer(text);
  size_t text_size = blob_size(text);
  while( text_size>0 && text_data[text_size-1]=='\n' ) text_size--;
  BLOB_APPEND_LITERAL(ob, "<li>");
  safe_html_append(ob, text_data, text_size);
  BLOB_APPEND_LITERAL(ob, "</li>\n");
}

static void html_paragraph(struct Blob *ob, struct Blob *text, void *opaque){
  INTER_BLOCK(ob);
  BLOB_APPEND_LITERAL(ob, "<p>");
  BLOB_APPEND_BLOB(ob, text);







|







213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  int flags,
  void *opaque
){
  char *text_data = blob_buffer(text);
  size_t text_size = blob_size(text);
  while( text_size>0 && text_data[text_size-1]=='\n' ) text_size--;
  BLOB_APPEND_LITERAL(ob, "<li>");
  blob_append(ob, text_data, text_size);
  BLOB_APPEND_LITERAL(ob, "</li>\n");
}

static void html_paragraph(struct Blob *ob, struct Blob *text, void *opaque){
  INTER_BLOCK(ob);
  BLOB_APPEND_LITERAL(ob, "<p>");
  BLOB_APPEND_BLOB(ob, text);
Changes to src/wiki.c.
186
187
188
189
190
191
192


193
194
195
196
197
198
199

200
201
202
203
204
205
206

/*
** Render wiki text according to its mimetype.
**
**   text/x-fossil-wiki      Fossil wiki
**   text/x-markdown         Markdown
**   anything else...        Plain text


*/
void wiki_render_by_mimetype(Blob *pWiki, const char *zMimetype){
  if( zMimetype==0 || fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){
    wiki_convert(pWiki, 0, 0);
  }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(pWiki, 0, &tail);

    @ %s(blob_str(&tail))
    blob_reset(&tail);
  }else{
    @ <pre class='textPlain'>
    @ %h(blob_str(pWiki))
    @ </pre>
  }







>
>

|

|



>







186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

/*
** Render wiki text according to its mimetype.
**
**   text/x-fossil-wiki      Fossil wiki
**   text/x-markdown         Markdown
**   anything else...        Plain text
**
** If zMimetype is a null pointer, then use "text/x-fossil-wiki".
*/
void wiki_render_by_mimetype(Blob *pWiki, const char *zMimetype, int flags){
  if( zMimetype==0 || fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){
    wiki_convert(pWiki, 0, flags);
  }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(pWiki, 0, &tail);
    if( flags & WIKI_SAFE ) safe_html(&tail);
    @ %s(blob_str(&tail))
    blob_reset(&tail);
  }else{
    @ <pre class='textPlain'>
    @ %h(blob_str(pWiki))
    @ </pre>
  }
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
  if( fTxt ){
    style_submenu_element("Formatted", "%R/md_rules");
  }else{
    style_submenu_element("Plain-Text", "%R/md_rules?txt=1");
  }
  blob_init(&x, builtin_text("markdown.md"), -1);
  blob_materialize(&x);
  wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-markdown");
  blob_reset(&x);
  style_footer();
}

/*
** WEBPAGE: wiki_rules
**
** Show a summary of the wiki formatting rules.
*/
void wiki_rules_page(void){
  Blob x;
  int fTxt = P("txt")!=0;
  style_header("Wiki Formatting Rules");
  if( fTxt ){
    style_submenu_element("Formatted", "%R/wiki_rules");
  }else{
    style_submenu_element("Plain-Text", "%R/wiki_rules?txt=1");
  }
  blob_init(&x, builtin_text("wiki.wiki"), -1);
  blob_materialize(&x);
  wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-fossil-wiki");
  blob_reset(&x);
  style_footer();
}

/*
** WEBPAGE: markup_help
**







|




















|







221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  if( fTxt ){
    style_submenu_element("Formatted", "%R/md_rules");
  }else{
    style_submenu_element("Plain-Text", "%R/md_rules?txt=1");
  }
  blob_init(&x, builtin_text("markdown.md"), -1);
  blob_materialize(&x);
  wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-markdown", 0);
  blob_reset(&x);
  style_footer();
}

/*
** WEBPAGE: wiki_rules
**
** Show a summary of the wiki formatting rules.
*/
void wiki_rules_page(void){
  Blob x;
  int fTxt = P("txt")!=0;
  style_header("Wiki Formatting Rules");
  if( fTxt ){
    style_submenu_element("Formatted", "%R/wiki_rules");
  }else{
    style_submenu_element("Plain-Text", "%R/wiki_rules?txt=1");
  }
  blob_init(&x, builtin_text("wiki.wiki"), -1);
  blob_materialize(&x);
  wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-fossil-wiki", 0);
  blob_reset(&x);
  style_footer();
}

/*
** WEBPAGE: markup_help
**
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
  if( !noSubmenu ){
    wiki_standard_submenu(submenuFlags);
  }
  if( zBody[0]==0 ){
    @ <i>This page has been deleted</i>
  }else{
    blob_init(&wiki, zBody, -1);
    wiki_render_by_mimetype(&wiki, zMimetype);
    blob_reset(&wiki);
  }
  attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
  manifest_destroy(pWiki);
  style_footer();
}








|







559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
  if( !noSubmenu ){
    wiki_standard_submenu(submenuFlags);
  }
  if( zBody[0]==0 ){
    @ <i>This page has been deleted</i>
  }else{
    blob_init(&wiki, zBody, -1);
    wiki_render_by_mimetype(&wiki, zMimetype, WIKI_SAFE);
    blob_reset(&wiki);
  }
  attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
  manifest_destroy(pWiki);
  style_footer();
}

743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
  blob_zero(&wiki);
  while( fossil_isspace(zBody[0]) ) zBody++;
  blob_append(&wiki, zBody, -1);
  if( P("preview")!=0 ){
    havePreview = 1;
    if( zBody[0] ){
      @ Preview:<hr />
      wiki_render_by_mimetype(&wiki, zMimetype);
      @ <hr />
      blob_reset(&wiki);
    }
  }
  for(n=2, z=zBody; z[0]; z++){
    if( z[0]=='\n' ) n++;
  }







|







746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
  blob_zero(&wiki);
  while( fossil_isspace(zBody[0]) ) zBody++;
  blob_append(&wiki, zBody, -1);
  if( P("preview")!=0 ){
    havePreview = 1;
    if( zBody[0] ){
      @ Preview:<hr />
      wiki_render_by_mimetype(&wiki, zMimetype, WIKI_SAFE);
      @ <hr />
      blob_reset(&wiki);
    }
  }
  for(n=2, z=zBody; z[0]; z++){
    if( z[0]=='\n' ) n++;
  }
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
    @ <p class="generalError">Error: Incorrect security code.</p>
  }
  if( P("preview")!=0 ){
    Blob preview;
    blob_zero(&preview);
    appendRemark(&preview, zMimetype);
    @ Preview:<hr />
    wiki_render_by_mimetype(&preview, zMimetype);
    @ <hr />
    blob_reset(&preview);
  }
  zUser = PD("u", g.zLogin);
  form_begin(0, "%R/wikiappend");
  login_insert_csrf_secret();
  @ <input type="hidden" name="name" value="%h(zPageName)" />







|







1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
    @ <p class="generalError">Error: Incorrect security code.</p>
  }
  if( P("preview")!=0 ){
    Blob preview;
    blob_zero(&preview);
    appendRemark(&preview, zMimetype);
    @ Preview:<hr />
    wiki_render_by_mimetype(&preview, zMimetype, WIKI_SAFE);
    @ <hr />
    blob_reset(&preview);
  }
  zUser = PD("u", g.zLogin);
  form_begin(0, "%R/wikiappend");
  login_insert_csrf_secret();
  @ <input type="hidden" name="name" value="%h(zPageName)" />
Changes to src/wikiformat.c.
29
30
31
32
33
34
35

36
37
38
39
40
41
42
#define WIKI_INLINE         0x002  /* Do not surround with <p>..</p> */
#define WIKI_NOBLOCK        0x004  /* No block markup of any kind */
#define WIKI_BUTTONS        0x008  /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS     0x010  /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY      0x020  /* No markup.  Only decorate links */
#define WIKI_NEWLINE        0x040  /* Honor \n - break lines at each \n */
#define WIKI_MARKDOWNLINKS  0x080  /* Resolve hyperlinks as in markdown */

#endif


/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {







>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#define WIKI_INLINE         0x002  /* Do not surround with <p>..</p> */
#define WIKI_NOBLOCK        0x004  /* No block markup of any kind */
#define WIKI_BUTTONS        0x008  /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS     0x010  /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY      0x020  /* No markup.  Only decorate links */
#define WIKI_NEWLINE        0x040  /* Honor \n - break lines at each \n */
#define WIKI_MARKDOWNLINKS  0x080  /* Resolve hyperlinks as in markdown */
#define WIKI_SAFE           0x100  /* Make the result safe for embedding */
#endif


/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
*/
void wiki_convert(Blob *pIn, Blob *pOut, int flags){
  Renderer renderer;

  memset(&renderer, 0, sizeof(renderer));
  renderer.renderFlags = flags;
  renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
  if( flags & WIKI_NOBLOCK ){
    renderer.state |= INLINE_MARKUP_ONLY;
  }
  if( flags & WIKI_INLINE ){
    renderer.wantAutoParagraph = 0;
  }else{
    renderer.wantAutoParagraph = 1;
  }
  if( wikiUsesHtml() ){
    renderer.state |= WIKI_HTMLONLY;







<
<
<







1743
1744
1745
1746
1747
1748
1749



1750
1751
1752
1753
1754
1755
1756
*/
void wiki_convert(Blob *pIn, Blob *pOut, int flags){
  Renderer renderer;

  memset(&renderer, 0, sizeof(renderer));
  renderer.renderFlags = flags;
  renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;



  if( flags & WIKI_INLINE ){
    renderer.wantAutoParagraph = 0;
  }else{
    renderer.wantAutoParagraph = 1;
  }
  if( wikiUsesHtml() ){
    renderer.state |= WIKI_HTMLONLY;
1816
1817
1818
1819
1820
1821
1822

1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834

1835
1836
1837
1838
1839
1840
1841
** Options:
**
**    --safe           Do "safe-html" rendering.
*/
void test_markdown_render(void){
  Blob in, out;
  int i;

  db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
  if( find_option("safe",0,0)!=0 ){
    safe_html_enable(1);
  }
  verify_all_options();
  for(i=2; i<g.argc; i++){
    blob_zero(&out);
    blob_read_from_file(&in, g.argv[i], ExtFILE);
    if( g.argc>3 ){
      fossil_print("<!------ %h ------->\n", g.argv[i]);
    }
    markdown_to_html(&in, 0, &out);

    blob_write_to_file(&out, "-");
    blob_reset(&in);
    blob_reset(&out);
  }
}

/*







>

|
<
<








>







1814
1815
1816
1817
1818
1819
1820
1821
1822
1823


1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
** Options:
**
**    --safe           Do "safe-html" rendering.
*/
void test_markdown_render(void){
  Blob in, out;
  int i;
  int bSafe = 0;
  db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
  bSafe = find_option("safe",0,0)!=0;


  verify_all_options();
  for(i=2; i<g.argc; i++){
    blob_zero(&out);
    blob_read_from_file(&in, g.argv[i], ExtFILE);
    if( g.argc>3 ){
      fossil_print("<!------ %h ------->\n", g.argv[i]);
    }
    markdown_to_html(&in, 0, &out);
    if( bSafe ) safe_html(&out);
    blob_write_to_file(&out, "-");
    blob_reset(&in);
    blob_reset(&out);
  }
}

/*
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
    e = p->aStack[--p->n];
    if( e==eEnd || (aMarkup[e].iType & MUTYPE_Nested)!=0 ){
      blob_appendf(pBlob, "</%s>", aMarkup[e].zName);
    }
  }while( e!=eEnd && p->n>0 );
}

/*
** Enable or disable the "safe-html" feature.  When enabled, the
** HTML generated by Markdown is adjusted so that it cannot cause
** problems when embedded in a larger document.
*/
static int safeHtml = 0;
void safe_html_enable(int v){
  safeHtml = v;
}

/*
** Append HTML text to a Blob object.
**
** If safe-html is enabled then the appended text is modified
** changed in the following ways:
**
**    1.  Omit any elements that are not on the AllowedMarkup list.







<
<
<
<
<
<
<
<
<
<







2505
2506
2507
2508
2509
2510
2511










2512
2513
2514
2515
2516
2517
2518
    e = p->aStack[--p->n];
    if( e==eEnd || (aMarkup[e].iType & MUTYPE_Nested)!=0 ){
      blob_appendf(pBlob, "</%s>", aMarkup[e].zName);
    }
  }while( e!=eEnd && p->n>0 );
}











/*
** Append HTML text to a Blob object.
**
** If safe-html is enabled then the appended text is modified
** changed in the following ways:
**
**    1.  Omit any elements that are not on the AllowedMarkup list.
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
void safe_html_append(Blob *pBlob, char *zHtml, int nHtml){
  char cLast;
  int i, j, n;
  HtmlTagStack s;
  ParsedMarkup markup;

  if( nHtml<=0 ) return;
  if( !safeHtml ){
    blob_append(pBlob, zHtml, nHtml);
    return;
  }
  cLast = zHtml[nHtml];
  zHtml[nHtml] = 0;
  html_tagstack_init(&s);

  i = 0;
  while( i<nHtml ){
    if( zHtml[i]=='<' ){







<
<
<
<







2539
2540
2541
2542
2543
2544
2545




2546
2547
2548
2549
2550
2551
2552
void safe_html_append(Blob *pBlob, char *zHtml, int nHtml){
  char cLast;
  int i, j, n;
  HtmlTagStack s;
  ParsedMarkup markup;

  if( nHtml<=0 ) return;




  cLast = zHtml[nHtml];
  zHtml[nHtml] = 0;
  html_tagstack_init(&s);

  i = 0;
  while( i<nHtml ){
    if( zHtml[i]=='<' ){
2584
2585
2586
2587
2588
2589
2590


2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
































2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
    }else{
      i = j + n;
    }
    parseMarkup(&markup, zHtml+j);
    if( markup.iCode==MARKUP_INVALID ){
      blob_appendf(pBlob, "<span class='error'>&lt;%.*s&gt;</span>",
                   n-2, zHtml+j+1);


    }else{
      if( markup.endTag ){
        html_tagstack_pop(&s, pBlob, markup.iCode);
      }else{
        renderMarkup(pBlob, &markup);
        html_tagstack_push(&s, markup.iCode);
      }
    }
    unparseMarkup(&markup);
  }
  html_tagstack_pop(&s, pBlob, 0);
  html_tagstack_clear(&s);
  zHtml[nHtml] = cLast;
}


































/*
** COMMAND: test-safe-html
**
** Usage: %fossil test-safe-html FILE ...
**
** Read files named on the command-line.  Send the text of each file
** through safe_html_append() and then write the result on
** standard output.
*/
void test_safe_html_cmd(void){
  int i;
  Blob x;
  Blob y;
  for(i=2; i<g.argc; i++){
    char *z;
    int n;
    blob_read_from_file(&x, g.argv[i], ExtFILE);
    blob_init(&y, 0, 0);
    blob_terminate(&x);
    safe_html_append(&y, blob_buffer(&x), blob_size(&x));
    blob_reset(&x);
    z = blob_str(&y);
    n = blob_size(&y);
    while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ) n--;
    fossil_print("%.*s\n", n, z);
    blob_reset(&y);
  }
}







>
>















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>













<




<

|
<
|
|


|


2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636

2637
2638
2639
2640

2641
2642

2643
2644
2645
2646
2647
2648
2649
    }else{
      i = j + n;
    }
    parseMarkup(&markup, zHtml+j);
    if( markup.iCode==MARKUP_INVALID ){
      blob_appendf(pBlob, "<span class='error'>&lt;%.*s&gt;</span>",
                   n-2, zHtml+j+1);
    }else if( (markup.iType & MUTYPE_Nested)==0 || markup.iCode==MARKUP_P ){
      renderMarkup(pBlob, &markup);
    }else{
      if( markup.endTag ){
        html_tagstack_pop(&s, pBlob, markup.iCode);
      }else{
        renderMarkup(pBlob, &markup);
        html_tagstack_push(&s, markup.iCode);
      }
    }
    unparseMarkup(&markup);
  }
  html_tagstack_pop(&s, pBlob, 0);
  html_tagstack_clear(&s);
  zHtml[nHtml] = cLast;
}

/*
** The input blob consists of HTML.  Convert it into "safe HTML".  Safe
** HTML has no potentially disruptive elements (ex: <script>, <style>)
** and it is embeddable, meaning that it won't close any outer elements
** from the script in which it is embedded, nor will it leave any open
** elements to affect the tail of the outer script.
*/
void safe_html(Blob *in){
  Blob out;
  char *z = blob_str(in);
  int n = blob_size(in);
  int k;
  blob_init(&out, 0, 0);
  while( fossil_isspace(z[0]) ){ z++; n--; }
  for(k=n-1; k>5 && fossil_isspace(z[k]); k--){}

  if( fossil_strnicmp(z, "<div",4)==0 && !fossil_isalpha(z[4])
   && fossil_strnicmp(z+k-5, "</div>",6)==0
  ){
    /* The input contains an outer <div>...</div>.  Preserve the
    ** full scope of that <div>. */
    int m = html_tag_length(z);
    k -= 5;
    blob_append(&out, z, m);
    safe_html_append(&out, z+m, k-m);
    blob_append(&out, z+k, n-k);
  }else{
    safe_html_append(&out, z, n);
  }
  blob_reset(in);
  *in = out;
}

/*
** COMMAND: test-safe-html
**
** Usage: %fossil test-safe-html FILE ...
**
** Read files named on the command-line.  Send the text of each file
** through safe_html_append() and then write the result on
** standard output.
*/
void test_safe_html_cmd(void){
  int i;
  Blob x;

  for(i=2; i<g.argc; i++){
    char *z;
    int n;
    blob_read_from_file(&x, g.argv[i], ExtFILE);

    blob_terminate(&x);
    safe_html(&x);

    z = blob_str(&x);
    n = blob_size(&x);
    while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ) n--;
    fossil_print("%.*s\n", n, z);
    blob_reset(&x);
  }
}