Fossil SCM

fossil-scm / src / doc.c
Source Blame History 1264 lines
5cf1206… drh 1 /*
c19f34c… drh 2 ** Copyright (c) 2007 D. Richard Hipp
5cf1206… drh 3 **
5cf1206… drh 4 ** This program is free software; you can redistribute it and/or
c06edd2… drh 5 ** modify it under the terms of the Simplified BSD License (also
c06edd2… drh 6 ** known as the "2-Clause License" or "FreeBSD License".)
03ce4e7… drh 7 **
5cf1206… drh 8 ** This program is distributed in the hope that it will be useful,
c06edd2… drh 9 ** but without any warranty; without even the implied warranty of
c06edd2… drh 10 ** merchantability or fitness for a particular purpose.
5cf1206… drh 11 **
5cf1206… drh 12 ** Author contact information:
5cf1206… drh 13 ** [email protected]
5cf1206… drh 14 ** http://www.hwaci.com/drh/
5cf1206… drh 15 **
5cf1206… drh 16 *******************************************************************************
5cf1206… drh 17 **
5cf1206… drh 18 ** This file contains code to implement the "/doc" web page and related
5cf1206… drh 19 ** pages.
5cf1206… drh 20 */
5cf1206… drh 21 #include "config.h"
5cf1206… drh 22 #include "doc.h"
5cf1206… drh 23 #include <assert.h>
5cf1206… drh 24
5cf1206… drh 25 /*
d14adf1… kejoki 26 ** Try to guess the mimetype from content.
d14adf1… kejoki 27 **
d14adf1… kejoki 28 ** If the content is pure text, return NULL.
d14adf1… kejoki 29 **
d14adf1… kejoki 30 ** For image types, attempt to return an appropriate mimetype
20d02ab… jan.nijtmans 31 ** name like "image/gif" or "image/jpeg".
d14adf1… kejoki 32 **
d14adf1… kejoki 33 ** For any other binary type, return "unknown/unknown".
d14adf1… kejoki 34 */
d14adf1… kejoki 35 const char *mimetype_from_content(Blob *pBlob){
d14adf1… kejoki 36 int i;
d14adf1… kejoki 37 int n;
d14adf1… kejoki 38 const unsigned char *x;
d14adf1… kejoki 39
d14adf1… kejoki 40 /* A table of mimetypes based on file content prefixes
d14adf1… kejoki 41 */
d14adf1… kejoki 42 static const struct {
6643d4a… drh 43 const char *z; /* Identifying file text */
6643d4a… drh 44 const unsigned char sz1; /* Length of the prefix */
6643d4a… drh 45 const unsigned char of2; /* Offset to the second segment */
6643d4a… drh 46 const unsigned char sz2; /* Size of the second segment */
6643d4a… drh 47 const unsigned char mn; /* Minimum size of input */
d14adf1… kejoki 48 const char *zMimetype; /* The corresponding mimetype */
d14adf1… kejoki 49 } aMime[] = {
6643d4a… drh 50 { "GIF87a", 6, 0, 0, 6, "image/gif" },
6643d4a… drh 51 { "GIF89a", 6, 0, 0, 6, "image/gif" },
6643d4a… drh 52 { "\211PNG\r\n\032\n", 8, 0, 0, 8, "image/png" },
6643d4a… drh 53 { "\377\332\377", 3, 0, 0, 3, "image/jpeg" },
6643d4a… drh 54 { "\377\330\377", 3, 0, 0, 3, "image/jpeg" },
6643d4a… drh 55 { "RIFFWAVEfmt", 4, 8, 7, 15, "sound/wav" },
d14adf1… kejoki 56 };
d14adf1… kejoki 57
b51ba29… jan.nijtmans 58 if( !looks_like_binary(pBlob) ) {
b51ba29… jan.nijtmans 59 return 0; /* Plain text */
b51ba29… jan.nijtmans 60 }
d14adf1… kejoki 61 x = (const unsigned char*)blob_buffer(pBlob);
d14adf1… kejoki 62 n = blob_size(pBlob);
3cb9ba4… andygoth 63 for(i=0; i<count(aMime); i++){
6643d4a… drh 64 if( n<aMime[i].mn ) continue;
6643d4a… drh 65 if( memcmp(x, aMime[i].z, aMime[i].sz1)!=0 ) continue;
6643d4a… drh 66 if( aMime[i].sz2
6643d4a… drh 67 && memcmp(x+aMime[i].of2, aMime[i].z+aMime[i].sz1, aMime[i].sz2)!=0
6643d4a… drh 68 ){
6643d4a… drh 69 continue;
b51ba29… jan.nijtmans 70 }
6643d4a… drh 71 return aMime[i].zMimetype;
d14adf1… kejoki 72 }
d14adf1… kejoki 73 return "unknown/unknown";
d14adf1… kejoki 74 }
d14adf1… kejoki 75
51751b0… drh 76 /* A table of mimetypes based on file suffixes.
51751b0… drh 77 ** Suffixes must be in sorted order so that we can do a binary
4cb50c4… stephan 78 ** search to find the mimetype.
51751b0… drh 79 */
51751b0… drh 80 static const struct {
51751b0… drh 81 const char *zSuffix; /* The file suffix */
51751b0… drh 82 int size; /* Length of the suffix */
51751b0… drh 83 const char *zMimetype; /* The corresponding mimetype */
51751b0… drh 84 } aMime[] = {
51751b0… drh 85 { "ai", 2, "application/postscript" },
51751b0… drh 86 { "aif", 3, "audio/x-aiff" },
51751b0… drh 87 { "aifc", 4, "audio/x-aiff" },
51751b0… drh 88 { "aiff", 4, "audio/x-aiff" },
51751b0… drh 89 { "arj", 3, "application/x-arj-compressed" },
51751b0… drh 90 { "asc", 3, "text/plain" },
51751b0… drh 91 { "asf", 3, "video/x-ms-asf" },
51751b0… drh 92 { "asx", 3, "video/x-ms-asx" },
51751b0… drh 93 { "au", 2, "audio/ulaw" },
51751b0… drh 94 { "avi", 3, "video/x-msvideo" },
51751b0… drh 95 { "bat", 3, "application/x-msdos-program" },
51751b0… drh 96 { "bcpio", 5, "application/x-bcpio" },
51751b0… drh 97 { "bin", 3, "application/octet-stream" },
613fe1b… jan.nijtmans 98 { "bmp", 3, "image/bmp" },
c7e9625… drh 99 { "bz2", 3, "application/x-bzip2" },
c7e9625… drh 100 { "bzip", 4, "application/x-bzip" },
51751b0… drh 101 { "c", 1, "text/plain" },
51751b0… drh 102 { "cc", 2, "text/plain" },
51751b0… drh 103 { "ccad", 4, "application/clariscad" },
51751b0… drh 104 { "cdf", 3, "application/x-netcdf" },
51751b0… drh 105 { "class", 5, "application/octet-stream" },
51751b0… drh 106 { "cod", 3, "application/vnd.rim.cod" },
51751b0… drh 107 { "com", 3, "application/x-msdos-program" },
51751b0… drh 108 { "cpio", 4, "application/x-cpio" },
51751b0… drh 109 { "cpt", 3, "application/mac-compactpro" },
5e81f4c… drh 110 { "cs", 2, "text/plain" },
51751b0… drh 111 { "csh", 3, "application/x-csh" },
51751b0… drh 112 { "css", 3, "text/css" },
e4c420b… drh 113 { "csv", 3, "text/csv" },
51751b0… drh 114 { "dcr", 3, "application/x-director" },
51751b0… drh 115 { "deb", 3, "application/x-debian-package" },
613fe1b… jan.nijtmans 116 { "dib", 3, "image/bmp" },
51751b0… drh 117 { "dir", 3, "application/x-director" },
51751b0… drh 118 { "dl", 2, "video/dl" },
51751b0… drh 119 { "dms", 3, "application/octet-stream" },
51751b0… drh 120 { "doc", 3, "application/msword" },
51751b0… drh 121 { "docx", 4, "application/vnd.openxmlformats-"
51751b0… drh 122 "officedocument.wordprocessingml.document"},
51751b0… drh 123 { "dot", 3, "application/msword" },
51751b0… drh 124 { "dotx", 4, "application/vnd.openxmlformats-"
51751b0… drh 125 "officedocument.wordprocessingml.template"},
51751b0… drh 126 { "drw", 3, "application/drafting" },
51751b0… drh 127 { "dvi", 3, "application/x-dvi" },
51751b0… drh 128 { "dwg", 3, "application/acad" },
51751b0… drh 129 { "dxf", 3, "application/dxf" },
51751b0… drh 130 { "dxr", 3, "application/x-director" },
51751b0… drh 131 { "eps", 3, "application/postscript" },
51751b0… drh 132 { "etx", 3, "text/x-setext" },
51751b0… drh 133 { "exe", 3, "application/octet-stream" },
51751b0… drh 134 { "ez", 2, "application/andrew-inset" },
51751b0… drh 135 { "f", 1, "text/plain" },
51751b0… drh 136 { "f90", 3, "text/plain" },
51751b0… drh 137 { "fli", 3, "video/fli" },
51751b0… drh 138 { "flv", 3, "video/flv" },
51751b0… drh 139 { "gif", 3, "image/gif" },
51751b0… drh 140 { "gl", 2, "video/gl" },
51751b0… drh 141 { "gtar", 4, "application/x-gtar" },
51751b0… drh 142 { "gz", 2, "application/x-gzip" },
51751b0… drh 143 { "h", 1, "text/plain" },
51751b0… drh 144 { "hdf", 3, "application/x-hdf" },
51751b0… drh 145 { "hh", 2, "text/plain" },
51751b0… drh 146 { "hqx", 3, "application/mac-binhex40" },
51751b0… drh 147 { "htm", 3, "text/html" },
51751b0… drh 148 { "html", 4, "text/html" },
51751b0… drh 149 { "ice", 3, "x-conference/x-cooltalk" },
613fe1b… jan.nijtmans 150 { "ico", 3, "image/vnd.microsoft.icon" },
51751b0… drh 151 { "ief", 3, "image/ief" },
51751b0… drh 152 { "iges", 4, "model/iges" },
51751b0… drh 153 { "igs", 3, "model/iges" },
51751b0… drh 154 { "ips", 3, "application/x-ipscript" },
51751b0… drh 155 { "ipx", 3, "application/x-ipix" },
51751b0… drh 156 { "jad", 3, "text/vnd.sun.j2me.app-descriptor" },
51751b0… drh 157 { "jar", 3, "application/java-archive" },
2669f49… stephan 158 { "jp2", 3, "image/jp2" },
51751b0… drh 159 { "jpe", 3, "image/jpeg" },
51751b0… drh 160 { "jpeg", 4, "image/jpeg" },
51751b0… drh 161 { "jpg", 3, "image/jpeg" },
7fcb462… stephan 162 { "js", 2, "text/javascript" },
7fcb462… stephan 163 /* application/javascript is commonly used for JS, but the
7fcb462… stephan 164 ** spec says text/javascript is correct:
7fcb462… stephan 165 ** https://html.spec.whatwg.org/multipage/scripting.html
7fcb462… stephan 166 ** #scriptingLanguages:javascript-mime-type */
7fcb462… stephan 167 { "json", 4, "application/json" },
51751b0… drh 168 { "kar", 3, "audio/midi" },
51751b0… drh 169 { "latex", 5, "application/x-latex" },
51751b0… drh 170 { "lha", 3, "application/octet-stream" },
51751b0… drh 171 { "lsp", 3, "application/x-lisp" },
51751b0… drh 172 { "lzh", 3, "application/octet-stream" },
51751b0… drh 173 { "m", 1, "text/plain" },
51751b0… drh 174 { "m3u", 3, "audio/x-mpegurl" },
addf43c… drh 175 { "man", 3, "text/plain" },
51751b0… drh 176 { "markdown", 8, "text/x-markdown" },
51751b0… drh 177 { "md", 2, "text/x-markdown" },
51751b0… drh 178 { "me", 2, "application/x-troff-me" },
51751b0… drh 179 { "mesh", 4, "model/mesh" },
51751b0… drh 180 { "mid", 3, "audio/midi" },
51751b0… drh 181 { "midi", 4, "audio/midi" },
51751b0… drh 182 { "mif", 3, "application/x-mif" },
51751b0… drh 183 { "mime", 4, "www/mime" },
95e5814… stephan 184 { "mjs", 3, "text/javascript" /*ES6 module*/ },
51751b0… drh 185 { "mkd", 3, "text/x-markdown" },
51751b0… drh 186 { "mov", 3, "video/quicktime" },
51751b0… drh 187 { "movie", 5, "video/x-sgi-movie" },
51751b0… drh 188 { "mp2", 3, "audio/mpeg" },
51751b0… drh 189 { "mp3", 3, "audio/mpeg" },
51751b0… drh 190 { "mp4", 3, "video/mp4" },
51751b0… drh 191 { "mpe", 3, "video/mpeg" },
51751b0… drh 192 { "mpeg", 4, "video/mpeg" },
51751b0… drh 193 { "mpg", 3, "video/mpeg" },
51751b0… drh 194 { "mpga", 4, "audio/mpeg" },
51751b0… drh 195 { "ms", 2, "application/x-troff-ms" },
51751b0… drh 196 { "msh", 3, "model/mesh" },
addf43c… drh 197 { "n", 1, "text/plain" },
51751b0… drh 198 { "nc", 2, "application/x-netcdf" },
51751b0… drh 199 { "oda", 3, "application/oda" },
04e6a82… jan.nijtmans 200 { "odp", 3, "application/vnd.oasis.opendocument.presentation" },
04e6a82… jan.nijtmans 201 { "ods", 3, "application/vnd.oasis.opendocument.spreadsheet" },
04e6a82… jan.nijtmans 202 { "odt", 3, "application/vnd.oasis.opendocument.text" },
51751b0… drh 203 { "ogg", 3, "application/ogg" },
51751b0… drh 204 { "ogm", 3, "application/ogg" },
422785d… stephan 205 { "otf", 3, "font/otf" },
51751b0… drh 206 { "pbm", 3, "image/x-portable-bitmap" },
51751b0… drh 207 { "pdb", 3, "chemical/x-pdb" },
51751b0… drh 208 { "pdf", 3, "application/pdf" },
51751b0… drh 209 { "pgm", 3, "image/x-portable-graymap" },
51751b0… drh 210 { "pgn", 3, "application/x-chess-pgn" },
51751b0… drh 211 { "pgp", 3, "application/pgp" },
e32214a… drh 212 { "pikchr", 6, "text/x-pikchr" },
51751b0… drh 213 { "pl", 2, "application/x-perl" },
51751b0… drh 214 { "pm", 2, "application/x-perl" },
51751b0… drh 215 { "png", 3, "image/png" },
51751b0… drh 216 { "pnm", 3, "image/x-portable-anymap" },
51751b0… drh 217 { "pot", 3, "application/mspowerpoint" },
51751b0… drh 218 { "potx", 4, "application/vnd.openxmlformats-"
51751b0… drh 219 "officedocument.presentationml.template"},
51751b0… drh 220 { "ppm", 3, "image/x-portable-pixmap" },
51751b0… drh 221 { "pps", 3, "application/mspowerpoint" },
51751b0… drh 222 { "ppsx", 4, "application/vnd.openxmlformats-"
51751b0… drh 223 "officedocument.presentationml.slideshow"},
51751b0… drh 224 { "ppt", 3, "application/mspowerpoint" },
51751b0… drh 225 { "pptx", 4, "application/vnd.openxmlformats-"
51751b0… drh 226 "officedocument.presentationml.presentation"},
51751b0… drh 227 { "ppz", 3, "application/mspowerpoint" },
51751b0… drh 228 { "pre", 3, "application/x-freelance" },
51751b0… drh 229 { "prt", 3, "application/pro_eng" },
51751b0… drh 230 { "ps", 2, "application/postscript" },
51751b0… drh 231 { "qt", 2, "video/quicktime" },
51751b0… drh 232 { "ra", 2, "audio/x-realaudio" },
51751b0… drh 233 { "ram", 3, "audio/x-pn-realaudio" },
51751b0… drh 234 { "rar", 3, "application/x-rar-compressed" },
51751b0… drh 235 { "ras", 3, "image/cmu-raster" },
51751b0… drh 236 { "rgb", 3, "image/x-rgb" },
51751b0… drh 237 { "rm", 2, "audio/x-pn-realaudio" },
51751b0… drh 238 { "roff", 4, "application/x-troff" },
51751b0… drh 239 { "rpm", 3, "audio/x-pn-realaudio-plugin" },
51751b0… drh 240 { "rtf", 3, "text/rtf" },
51751b0… drh 241 { "rtx", 3, "text/richtext" },
51751b0… drh 242 { "scm", 3, "application/x-lotusscreencam" },
51751b0… drh 243 { "set", 3, "application/set" },
51751b0… drh 244 { "sgm", 3, "text/sgml" },
51751b0… drh 245 { "sgml", 4, "text/sgml" },
51751b0… drh 246 { "sh", 2, "application/x-sh" },
51751b0… drh 247 { "shar", 4, "application/x-shar" },
51751b0… drh 248 { "silo", 4, "model/mesh" },
51751b0… drh 249 { "sit", 3, "application/x-stuffit" },
51751b0… drh 250 { "skd", 3, "application/x-koan" },
51751b0… drh 251 { "skm", 3, "application/x-koan" },
51751b0… drh 252 { "skp", 3, "application/x-koan" },
51751b0… drh 253 { "skt", 3, "application/x-koan" },
51751b0… drh 254 { "smi", 3, "application/smil" },
51751b0… drh 255 { "smil", 4, "application/smil" },
51751b0… drh 256 { "snd", 3, "audio/basic" },
51751b0… drh 257 { "sol", 3, "application/solids" },
51751b0… drh 258 { "spl", 3, "application/x-futuresplash" },
7c76c6a… stephan 259 { "sql", 3, "application/sql" },
51751b0… drh 260 { "src", 3, "application/x-wais-source" },
51751b0… drh 261 { "step", 4, "application/STEP" },
51751b0… drh 262 { "stl", 3, "application/SLA" },
51751b0… drh 263 { "stp", 3, "application/STEP" },
51751b0… drh 264 { "sv4cpio", 7, "application/x-sv4cpio" },
51751b0… drh 265 { "sv4crc", 6, "application/x-sv4crc" },
51751b0… drh 266 { "svg", 3, "image/svg+xml" },
51751b0… drh 267 { "swf", 3, "application/x-shockwave-flash" },
51751b0… drh 268 { "t", 1, "application/x-troff" },
51751b0… drh 269 { "tar", 3, "application/x-tar" },
51751b0… drh 270 { "tcl", 3, "application/x-tcl" },
51751b0… drh 271 { "tex", 3, "application/x-tex" },
51751b0… drh 272 { "texi", 4, "application/x-texinfo" },
51751b0… drh 273 { "texinfo", 7, "application/x-texinfo" },
51751b0… drh 274 { "tgz", 3, "application/x-tar-gz" },
51751b0… drh 275 { "th1", 3, "application/x-th1" },
51751b0… drh 276 { "tif", 3, "image/tiff" },
51751b0… drh 277 { "tiff", 4, "image/tiff" },
51751b0… drh 278 { "tr", 2, "application/x-troff" },
51751b0… drh 279 { "tsi", 3, "audio/TSP-audio" },
51751b0… drh 280 { "tsp", 3, "application/dsptype" },
51751b0… drh 281 { "tsv", 3, "text/tab-separated-values" },
51751b0… drh 282 { "txt", 3, "text/plain" },
51751b0… drh 283 { "unv", 3, "application/i-deas" },
51751b0… drh 284 { "ustar", 5, "application/x-ustar" },
5e81f4c… drh 285 { "vb", 2, "text/plain" },
51751b0… drh 286 { "vcd", 3, "application/x-cdlink" },
51751b0… drh 287 { "vda", 3, "application/vda" },
51751b0… drh 288 { "viv", 3, "video/vnd.vivo" },
51751b0… drh 289 { "vivo", 4, "video/vnd.vivo" },
51751b0… drh 290 { "vrml", 4, "model/vrml" },
5c47abd… stephan 291 { "wasm", 4, "application/wasm" },
51751b0… drh 292 { "wav", 3, "audio/x-wav" },
51751b0… drh 293 { "wax", 3, "audio/x-ms-wax" },
5590fb9… stephan 294 { "webp", 4, "image/webp" },
51751b0… drh 295 { "wiki", 4, "text/x-fossil-wiki" },
51751b0… drh 296 { "wma", 3, "audio/x-ms-wma" },
51751b0… drh 297 { "wmv", 3, "video/x-ms-wmv" },
51751b0… drh 298 { "wmx", 3, "video/x-ms-wmx" },
51751b0… drh 299 { "wrl", 3, "model/vrml" },
51751b0… drh 300 { "wvx", 3, "video/x-ms-wvx" },
51751b0… drh 301 { "xbm", 3, "image/x-xbitmap" },
51751b0… drh 302 { "xlc", 3, "application/vnd.ms-excel" },
51751b0… drh 303 { "xll", 3, "application/vnd.ms-excel" },
51751b0… drh 304 { "xlm", 3, "application/vnd.ms-excel" },
51751b0… drh 305 { "xls", 3, "application/vnd.ms-excel" },
51751b0… drh 306 { "xlsx", 4, "application/vnd.openxmlformats-"
51751b0… drh 307 "officedocument.spreadsheetml.sheet"},
51751b0… drh 308 { "xlw", 3, "application/vnd.ms-excel" },
51751b0… drh 309 { "xml", 3, "text/xml" },
51751b0… drh 310 { "xpm", 3, "image/x-xpixmap" },
c0f0e1d… stephan 311 { "xsl", 3, "text/xml" },
c0f0e1d… stephan 312 { "xslt", 4, "text/xml" },
51751b0… drh 313 { "xwd", 3, "image/x-xwindowdump" },
51751b0… drh 314 { "xyz", 3, "chemical/x-pdb" },
51751b0… drh 315 { "zip", 3, "application/zip" },
51751b0… drh 316 };
51751b0… drh 317
51751b0… drh 318 /*
e4c420b… drh 319 ** Verify that all entries in the aMime[] table are in sorted order.
e4c420b… drh 320 ** Abort with a fatal error if any is out-of-order.
e4c420b… drh 321 */
e4c420b… drh 322 static void mimetype_verify(void){
e4c420b… drh 323 int i;
3cb9ba4… andygoth 324 for(i=1; i<count(aMime); i++){
e4c420b… drh 325 if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){
3f5ab71… drh 326 fossil_panic("mimetypes out of sequence: %s before %s",
e4c420b… drh 327 aMime[i-1].zSuffix, aMime[i].zSuffix);
e4c420b… drh 328 }
e4c420b… drh 329 }
e4c420b… drh 330 }
e4c420b… drh 331
e4c420b… drh 332 /*
009a243… stephan 333 ** Looks in the contents of the "mimetypes" setting for a suffix
009a243… stephan 334 ** matching zSuffix. If found, it returns the configured value
009a243… stephan 335 ** in memory owned by the app (i.e. do not free() it), else it
009a243… stephan 336 ** returns 0.
009a243… stephan 337 **
009a243… stephan 338 ** The mimetypes setting is expected to be a list of file extensions
009a243… stephan 339 ** and mimetypes, with one such mapping per line. A leading '.' on
009a243… stephan 340 ** extensions is permitted for compatibility with lists imported from
009a243… stephan 341 ** other tools which require them.
009a243… stephan 342 */
009a243… stephan 343 static const char *mimetype_from_name_custom(const char *zSuffix){
009a243… stephan 344 static char * zList = 0;
009a243… stephan 345 static char const * zEnd = 0;
009a243… stephan 346 static int once = 0;
009a243… stephan 347 char * z;
009a243… stephan 348 int tokenizerState /* 0=expecting a key, 1=skip next token,
009a243… stephan 349 ** 2=accept next token */;
009a243… stephan 350 if(once==0){
275da70… danield 351 once = 1;
009a243… stephan 352 zList = db_get("mimetypes",0);
009a243… stephan 353 if(zList==0){
009a243… stephan 354 return 0;
009a243… stephan 355 }
009a243… stephan 356 /* Transform zList to simplify the main loop:
009a243… stephan 357 replace non-newline spaces with NUL bytes. */
009a243… stephan 358 zEnd = zList + strlen(zList);
009a243… stephan 359 for(z = zList; z<zEnd; ++z){
009a243… stephan 360 if('\n'==*z) continue;
009a243… stephan 361 else if(fossil_isspace(*z)){
009a243… stephan 362 *z = 0;
009a243… stephan 363 }
009a243… stephan 364 }
009a243… stephan 365 }else if(zList==0){
009a243… stephan 366 return 0;
009a243… stephan 367 }
009a243… stephan 368 tokenizerState = 0;
009a243… stephan 369 z = zList;
009a243… stephan 370 while( z<zEnd ){
009a243… stephan 371 if(*z==0){
009a243… stephan 372 ++z;
009a243… stephan 373 continue;
009a243… stephan 374 }
009a243… stephan 375 else if('\n'==*z){
009a243… stephan 376 if(2==tokenizerState){
009a243… stephan 377 /* We were expecting a value for a successful match
009a243… stephan 378 here, but got no value. Bail out. */
009a243… stephan 379 break;
009a243… stephan 380 }else{
009a243… stephan 381 /* May happen on malformed inputs. Skip this record. */
009a243… stephan 382 tokenizerState = 0;
009a243… stephan 383 ++z;
009a243… stephan 384 continue;
009a243… stephan 385 }
009a243… stephan 386 }
009a243… stephan 387 switch(tokenizerState){
009a243… stephan 388 case 0:{ /* This is a file extension */
009a243… stephan 389 static char * zCase = 0;
009a243… stephan 390 if('.'==*z){
009a243… stephan 391 /*ignore an optional leading dot, for compatibility
009a243… stephan 392 with some external mimetype lists*/;
009a243… stephan 393 if(++z==zEnd){
009a243… stephan 394 break;
009a243… stephan 395 }
009a243… stephan 396 }
009a243… stephan 397 if(zCase<z){
009a243… stephan 398 /*we have not yet case-folded this section: lower-case it*/
009a243… stephan 399 for(zCase = z; zCase<zEnd && *zCase!=0; ++zCase){
009a243… stephan 400 if(!(0x80 & *zCase)){
009a243… stephan 401 *zCase = (char)fossil_tolower(*zCase);
009a243… stephan 402 }
009a243… stephan 403 }
009a243… stephan 404 }
009a243… stephan 405 if(strcmp(z,zSuffix)==0){
009a243… stephan 406 tokenizerState = 2 /* Match: accept the next value. */;
009a243… stephan 407 }else{
009a243… stephan 408 tokenizerState = 1 /* No match: skip the next value. */;
009a243… stephan 409 }
009a243… stephan 410 z += strlen(z);
009a243… stephan 411 break;
009a243… stephan 412 }
009a243… stephan 413 case 1: /* This is a value, but not a match. Skip it. */
009a243… stephan 414 z += strlen(z);
009a243… stephan 415 break;
009a243… stephan 416 case 2: /* This is the value which matched the previous key. */;
009a243… stephan 417 return z;
009a243… stephan 418 default:
009a243… stephan 419 assert(!"cannot happen - invalid tokenizerState value.");
009a243… stephan 420 }
009a243… stephan 421 }
009a243… stephan 422 return 0;
009a243… stephan 423 }
009a243… stephan 424
009a243… stephan 425 /*
83f03e9… stephan 426 ** Emit Javascript which applies (or optionally can apply) to both the
83f03e9… stephan 427 ** /doc and /wiki pages. None of this implements required
9b2b6f5… stephan 428 ** functionality, just nice-to-haves. Any calls after the first are
9b2b6f5… stephan 429 ** no-ops.
83f03e9… stephan 430 */
83f03e9… stephan 431 void document_emit_js(void){
9b2b6f5… stephan 432 static int once = 0;
9b2b6f5… stephan 433 if(0==once++){
815b4fc… wyoung 434 builtin_fossil_js_bundle_or("pikchr", NULL);
9b2b6f5… stephan 435 style_script_begin(__FILE__,__LINE__);
9b2b6f5… stephan 436 CX("window.addEventListener('load', "
9b2b6f5… stephan 437 "()=>window.fossil.pikchr.addSrcView(), "
9b2b6f5… stephan 438 "false);\n");
9b2b6f5… stephan 439 style_script_end();
9b2b6f5… stephan 440 }
83f03e9… stephan 441 }
83f03e9… stephan 442
83f03e9… stephan 443 /*
4cb50c4… stephan 444 ** Guess the mimetype of a document based on its name.
5cf1206… drh 445 */
5cf1206… drh 446 const char *mimetype_from_name(const char *zName){
5cf1206… drh 447 const char *z;
5cf1206… drh 448 int i;
8e66784… drh 449 int first, last;
8e66784… drh 450 int len;
5cf1206… drh 451 char zSuffix[20];
8e66784… drh 452
ea2698e… drh 453
ea2698e… drh 454 #ifdef FOSSIL_DEBUG
ea2698e… drh 455 /* This is test code to make sure the table above is in the correct
ea2698e… drh 456 ** order
8e66784… drh 457 */
ea2698e… drh 458 if( fossil_strcmp(zName, "mimetype-test")==0 ){
e4c420b… drh 459 mimetype_verify();
ea2698e… drh 460 return "ok";
ea2698e… drh 461 }
ea2698e… drh 462 #endif
5cf1206… drh 463
5cf1206… drh 464 z = zName;
5cf1206… drh 465 for(i=0; zName[i]; i++){
5cf1206… drh 466 if( zName[i]=='.' ) z = &zName[i+1];
5cf1206… drh 467 }
8e66784… drh 468 len = strlen(z);
53db40e… drh 469 if( len<(int)sizeof(zSuffix)-1 ){
bbbb35a… drh 470 sqlite3_snprintf(sizeof(zSuffix), zSuffix, "%s", z);
2fac809… drh 471 for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]);
009a243… stephan 472 z = mimetype_from_name_custom(zSuffix);
009a243… stephan 473 if(z!=0){
009a243… stephan 474 return z;
009a243… stephan 475 }
8e66784… drh 476 first = 0;
3cb9ba4… andygoth 477 last = count(aMime) - 1;
8e66784… drh 478 while( first<=last ){
8e66784… drh 479 int c;
8e66784… drh 480 i = (first+last)/2;
31c52c7… drh 481 c = fossil_strcmp(zSuffix, aMime[i].zSuffix);
8e66784… drh 482 if( c==0 ) return aMime[i].zMimetype;
8e66784… drh 483 if( c<0 ){
8e66784… drh 484 last = i-1;
8e66784… drh 485 }else{
8e66784… drh 486 first = i+1;
5cf1206… drh 487 }
5cf1206… drh 488 }
5cf1206… drh 489 }
5cf1206… drh 490 return "application/x-fossil-artifact";
5cf1206… drh 491 }
5cf1206… drh 492
5cf1206… drh 493 /*
26eef7f… rberteig 494 ** COMMAND: test-mimetype
ea2698e… drh 495 **
ea2698e… drh 496 ** Usage: %fossil test-mimetype FILENAME...
ea2698e… drh 497 **
ea2698e… drh 498 ** Return the deduced mimetype for each file listed.
ea2698e… drh 499 **
ea2698e… drh 500 ** If Fossil is compiled with -DFOSSIL_DEBUG then the "mimetype-test"
ea2698e… drh 501 ** filename is special and verifies the integrity of the mimetype table.
ea2698e… drh 502 ** It should return "ok".
ea2698e… drh 503 */
ea2698e… drh 504 void mimetype_test_cmd(void){
ea2698e… drh 505 int i;
e4c420b… drh 506 mimetype_verify();
009a243… stephan 507 db_find_and_open_repository(0, 0);
ea2698e… drh 508 for(i=2; i<g.argc; i++){
ea2698e… drh 509 fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i]));
ea2698e… drh 510 }
ea2698e… drh 511 }
ea2698e… drh 512
51751b0… drh 513 /*
51751b0… drh 514 ** WEBPAGE: mimetype_list
51751b0… drh 515 **
51751b0… drh 516 ** Show the built-in table used to guess embedded document mimetypes
51751b0… drh 517 ** from file suffixes.
51751b0… drh 518 */
51751b0… drh 519 void mimetype_list_page(void){
51751b0… drh 520 int i;
009a243… stephan 521 char *zCustomList = 0; /* value of the mimetypes setting */
009a243… stephan 522 int nCustomEntries = 0; /* number of entries in the mimetypes
009a243… stephan 523 ** setting */
e4c420b… drh 524 mimetype_verify();
51751b0… drh 525 style_header("Mimetype List");
c64f28d… drh 526 @ <p>The Fossil <a href="%R/help/www/doc">/doc</a> page uses filename
009a243… stephan 527 @ suffixes and the following tables to guess at the appropriate mimetype
009a243… stephan 528 @ for each document. Mimetypes may be customized and overridden using
c64f28d… drh 529 @ <a href="%R/help/mimetypes">the mimetypes config setting</a>.</p>
009a243… stephan 530 zCustomList = db_get("mimetypes",0);
009a243… stephan 531 if( zCustomList!=0 ){
009a243… stephan 532 Blob list, entry, key, val;
009a243… stephan 533 @ <h1>Repository-specific mimetypes</h1>
009a243… stephan 534 @ <p>The following extension-to-mimetype mappings are defined via
c64f28d… drh 535 @ the <a href="%R/help/mimetypes">mimetypes setting</a>.</p>
009a243… stephan 536 @ <table class='sortable mimetypetable' border=1 cellpadding=0 \
009a243… stephan 537 @ data-column-types='tt' data-init-sort='0'>
009a243… stephan 538 @ <thead>
009a243… stephan 539 @ <tr><th>Suffix<th>Mimetype
009a243… stephan 540 @ </thead>
009a243… stephan 541 @ <tbody>
009a243… stephan 542 blob_set(&list, zCustomList);
009a243… stephan 543 while( blob_line(&list, &entry)>0 ){
009a243… stephan 544 const char *zKey;
009a243… stephan 545 if( blob_token(&entry, &key)==0 ) continue;
009a243… stephan 546 if( blob_token(&entry, &val)==0 ) continue;
009a243… stephan 547 zKey = blob_str(&key);
009a243… stephan 548 if( zKey[0]=='.' ) zKey++;
009a243… stephan 549 @ <tr><td>%h(zKey)<td>%h(blob_str(&val))</tr>
009a243… stephan 550 nCustomEntries++;
009a243… stephan 551 }
009a243… stephan 552 fossil_free(zCustomList);
009a243… stephan 553 if( nCustomEntries==0 ){
009a243… stephan 554 /* This can happen if the option is set to an empty/space-only
009a243… stephan 555 ** value. */
009a243… stephan 556 @ <tr><td colspan="2"><em>none</em></tr>
009a243… stephan 557 }
009a243… stephan 558 @ </tbody></table>
009a243… stephan 559 }
009a243… stephan 560 @ <h1>Default built-in mimetypes</h1>
009a243… stephan 561 if(nCustomEntries>0){
009a243… stephan 562 @ <p>Entries starting with an exclamation mark <em><strong>!</strong></em>
009a243… stephan 563 @ are overwritten by repository-specific settings.</p>
009a243… stephan 564 }
6b645d6… drh 565 @ <table class='sortable mimetypetable' border=1 cellpadding=0 \
6b645d6… drh 566 @ data-column-types='tt' data-init-sort='1'>
51751b0… drh 567 @ <thead>
51751b0… drh 568 @ <tr><th>Suffix<th>Mimetype
51751b0… drh 569 @ </thead>
51751b0… drh 570 @ <tbody>
3cb9ba4… andygoth 571 for(i=0; i<count(aMime); i++){
009a243… stephan 572 const char *zFlag = "";
009a243… stephan 573 if(nCustomEntries>0 &&
009a243… stephan 574 mimetype_from_name_custom(aMime[i].zSuffix)!=0){
009a243… stephan 575 zFlag = "<em><strong>!</strong></em> ";
009a243… stephan 576 }
009a243… stephan 577 @ <tr><td>%s(zFlag)%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
51751b0… drh 578 }
51751b0… drh 579 @ </tbody></table>
6b645d6… drh 580 style_table_sorter();
112c713… drh 581 style_finish_page();
51751b0… drh 582 }
ace8016… drh 583
ace8016… drh 584 /*
ace8016… drh 585 ** Check to see if the file in the pContent blob is "embedded HTML". Return
ace8016… drh 586 ** true if it is, and fill pTitle with the document title.
ace8016… drh 587 **
ace8016… drh 588 ** An "embedded HTML" file is HTML that lacks a header and a footer. The
ace8016… drh 589 ** standard Fossil header is prepended and the standard Fossil footer is
ace8016… drh 590 ** appended. Otherwise, the file is displayed without change.
ace8016… drh 591 **
ace8016… drh 592 ** Embedded HTML must be contained in a <div class='fossil-doc'> element.
ace8016… drh 593 ** If that <div> also contains a data-title attribute, then the
ace8016… drh 594 ** value of that attribute is extracted into pTitle and becomes the title
ace8016… drh 595 ** of the document.
ace8016… drh 596 */
ace8016… drh 597 int doc_is_embedded_html(Blob *pContent, Blob *pTitle){
ace8016… drh 598 const char *zIn = blob_str(pContent);
ace8016… drh 599 const char *zAttr;
ace8016… drh 600 const char *zValue;
ace8016… drh 601 int nAttr, nValue;
ace8016… drh 602 int seenClass = 0;
ace8016… drh 603 int seenTitle = 0;
5260fbf… jan.nijtmans 604
ace8016… drh 605 while( fossil_isspace(zIn[0]) ) zIn++;
ace8016… drh 606 if( fossil_strnicmp(zIn,"<div",4)!=0 ) return 0;
ace8016… drh 607 zIn += 4;
ace8016… drh 608 while( zIn[0] ){
ace8016… drh 609 if( fossil_isspace(zIn[0]) ) zIn++;
3d6a4fd… mistachkin 610 if( zIn[0]=='>' ) break;
ace8016… drh 611 zAttr = zIn;
ace8016… drh 612 while( fossil_isalnum(zIn[0]) || zIn[0]=='-' ) zIn++;
ace8016… drh 613 nAttr = (int)(zIn - zAttr);
ace8016… drh 614 while( fossil_isspace(zIn[0]) ) zIn++;
ace8016… drh 615 if( zIn[0]!='=' ) continue;
ace8016… drh 616 zIn++;
ace8016… drh 617 while( fossil_isspace(zIn[0]) ) zIn++;
ace8016… drh 618 if( zIn[0]=='"' || zIn[0]=='\'' ){
ace8016… drh 619 char cDelim = zIn[0];
ace8016… drh 620 zIn++;
ace8016… drh 621 zValue = zIn;
ace8016… drh 622 while( zIn[0] && zIn[0]!=cDelim ) zIn++;
ace8016… drh 623 if( zIn[0]==0 ) return 0;
ace8016… drh 624 nValue = (int)(zIn - zValue);
ace8016… drh 625 zIn++;
ace8016… drh 626 }else{
ace8016… drh 627 zValue = zIn;
ace8016… drh 628 while( zIn[0]!=0 && zIn[0]!='>' && zIn[0]!='/'
ace8016… drh 629 && !fossil_isspace(zIn[0]) ) zIn++;
ace8016… drh 630 if( zIn[0]==0 ) return 0;
ace8016… drh 631 nValue = (int)(zIn - zValue);
ace8016… drh 632 }
ace8016… drh 633 if( nAttr==5 && fossil_strnicmp(zAttr,"class",5)==0 ){
ace8016… drh 634 if( nValue!=10 || fossil_strnicmp(zValue,"fossil-doc",10)!=0 ) return 0;
ace8016… drh 635 seenClass = 1;
ace8016… drh 636 if( seenTitle ) return 1;
ace8016… drh 637 }
ace8016… drh 638 if( nAttr==10 && fossil_strnicmp(zAttr,"data-title",10)==0 ){
54e01c6… drh 639 /* The text argument to data-title="" will have had any characters that
54e01c6… drh 640 ** are special to HTML encoded. We need to decode these before turning
54e01c6… drh 641 ** the text into a title, as the title text will be reencoded later */
54e01c6… drh 642 char *zTitle = mprintf("%.*s", nValue, zValue);
54e01c6… drh 643 int i;
54e01c6… drh 644 for(i=0; fossil_isspace(zTitle[i]); i++){}
fd59eb1… drh 645 html_to_plaintext(zTitle+i, pTitle, 0);
54e01c6… drh 646 fossil_free(zTitle);
ace8016… drh 647 seenTitle = 1;
ace8016… drh 648 if( seenClass ) return 1;
ace8016… drh 649 }
ace8016… drh 650 }
ace8016… drh 651 return seenClass;
5260fbf… jan.nijtmans 652 }
ace8016… drh 653
ace8016… drh 654 /*
c49030f… drh 655 ** Look for a file named zName in the check-in with RID=vid. Load the content
48e1e18… drh 656 ** of that file into pContent and return the RID for the file. Or return 0
48e1e18… drh 657 ** if the file is not found or could not be loaded.
48e1e18… drh 658 */
48e1e18… drh 659 int doc_load_content(int vid, const char *zName, Blob *pContent){
f8363db… drh 660 int writable;
48e1e18… drh 661 int rid; /* The RID of the file being loaded */
f8363db… drh 662 if( db_is_protected(PROTECT_READONLY)
f8363db… drh 663 || !db_is_writeable("repository")
f8363db… drh 664 ){
f8363db… drh 665 writable = 0;
f8363db… drh 666 }else{
f8363db… drh 667 writable = 1;
f8363db… drh 668 }
0397982… drh 669 if( writable ){
0397982… drh 670 db_end_transaction(0);
0397982… drh 671 db_begin_write();
0397982… drh 672 }
b20f6cb… andygoth 673 if( !db_table_exists("repository", "vcache") || !writable ){
48e1e18… drh 674 db_multi_exec(
95edba6… andygoth 675 "CREATE %s TABLE IF NOT EXISTS vcache(\n"
c49030f… drh 676 " vid INTEGER, -- check-in ID\n"
48e1e18… drh 677 " fname TEXT, -- filename\n"
48e1e18… drh 678 " rid INTEGER, -- artifact ID\n"
48e1e18… drh 679 " PRIMARY KEY(vid,fname)\n"
b20f6cb… andygoth 680 ") WITHOUT ROWID", writable ? "" : "TEMPORARY"
48e1e18… drh 681 );
48e1e18… drh 682 }
48e1e18… drh 683 if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){
48e1e18… drh 684 db_multi_exec(
48e1e18… drh 685 "DELETE FROM vcache;\n"
48e1e18… drh 686 "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;\n"
48e1e18… drh 687 "INSERT INTO vcache(vid,fname,rid)"
48e1e18… drh 688 " SELECT checkinID, filename, blob.rid FROM foci, blob"
48e1e18… drh 689 " WHERE blob.uuid=foci.uuid"
48e1e18… drh 690 " AND foci.checkinID=%d;",
48e1e18… drh 691 vid
48e1e18… drh 692 );
48e1e18… drh 693 }
48e1e18… drh 694 rid = db_int(0, "SELECT rid FROM vcache"
48e1e18… drh 695 " WHERE vid=%d AND fname=%Q", vid, zName);
48e1e18… drh 696 if( rid && content_get(rid, pContent)==0 ){
48e1e18… drh 697 rid = 0;
48e1e18… drh 698 }
48e1e18… drh 699 return rid;
48e1e18… drh 700 }
48e1e18… drh 701
48e1e18… drh 702 /*
703e62a… drh 703 ** Check to verify that z[i] is contained within HTML markup.
703e62a… drh 704 **
703e62a… drh 705 ** This works by looking backwards in the string for the most recent
703e62a… drh 706 ** '<' or '>' character. If a '<' is found first, then we assume that
703e62a… drh 707 ** z[i] is within markup. If a '>' is seen or neither character is seen,
703e62a… drh 708 ** then z[i] is not within markup.
703e62a… drh 709 */
703e62a… drh 710 static int isWithinHtmlMarkup(const char *z, int i){
703e62a… drh 711 while( i>=0 && z[i]!='>' && z[i]!='<' ){ i--; }
703e62a… drh 712 return z[i]=='<';
703e62a… drh 713 }
703e62a… drh 714
703e62a… drh 715 /*
703e62a… drh 716 ** Check to see if z[i] is contained within an href='...' of markup.
703e62a… drh 717 */
703e62a… drh 718 static int isWithinHref(const char *z, int i){
703e62a… drh 719 while( i>5
703e62a… drh 720 && !fossil_isspace(z[i])
703e62a… drh 721 && z[i]!='\'' && z[i]!='"'
703e62a… drh 722 && z[i]!='>'
703e62a… drh 723 ){ i--; }
703e62a… drh 724 if( i<=6 ) return 0;
703e62a… drh 725 if( z[i]!='\'' && z[i]!='\"' ) return 0;
703e62a… drh 726 if( strncmp(&z[i-5],"href=",5)!=0 ) return 0;
703e62a… drh 727 if( !fossil_isspace(z[i-6]) ) return 0;
703e62a… drh 728 return 1;
703e62a… drh 729 }
703e62a… drh 730
703e62a… drh 731 /*
e57ab29… drh 732 ** Transfer content to the output. During the transfer, when text of
95edba6… andygoth 733 ** the following form is seen:
95edba6… andygoth 734 **
c865e1a… drh 735 ** href="$ROOT/..."
c865e1a… drh 736 ** action="$ROOT/..."
45427ae… drh 737 ** href=".../doc/$CURRENT/..."
c865e1a… drh 738 **
275da70… danield 739 ** Convert $ROOT to the root URI of the repository, and $CURRENT to the
c865e1a… drh 740 ** version number of the /doc/ document currently being displayed (if any).
275da70… danield 741 ** Allow ' in place of " and any case for href or action.
e57ab29… drh 742 **
c865e1a… drh 743 ** Efforts are made to limit this translation to cases where the text is
c865e1a… drh 744 ** fully contained with an HTML markup element.
e57ab29… drh 745 */
5602385… drh 746 void convert_href_and_output(Blob *pIn){
e57ab29… drh 747 int i, base;
e57ab29… drh 748 int n = blob_size(pIn);
e57ab29… drh 749 char *z = blob_buffer(pIn);
e57ab29… drh 750 for(base=0, i=7; i<n; i++){
c3b5f1d… jan.nijtmans 751 if( z[i]=='$'
e57ab29… drh 752 && strncmp(&z[i],"$ROOT/", 6)==0
e57ab29… drh 753 && (z[i-1]=='\'' || z[i-1]=='"')
e57ab29… drh 754 && i-base>=9
3e183bf… drh 755 && ((fossil_strnicmp(&z[i-6],"href=",5)==0 && fossil_isspace(z[i-7])) ||
3e183bf… drh 756 (fossil_strnicmp(&z[i-8],"action=",7)==0 && fossil_isspace(z[i-9])) )
703e62a… drh 757 && isWithinHtmlMarkup(z, i-6)
e57ab29… drh 758 ){
e57ab29… drh 759 blob_append(cgi_output_blob(), &z[base], i-base);
e57ab29… drh 760 blob_appendf(cgi_output_blob(), "%R");
e57ab29… drh 761 base = i+5;
703e62a… drh 762 }else
703e62a… drh 763 if( z[i]=='$'
45427ae… drh 764 && strncmp(&z[i-5],"/doc/$CURRENT/", 11)==0
703e62a… drh 765 && isWithinHref(z,i-5)
703e62a… drh 766 && isWithinHtmlMarkup(z, i-5)
703e62a… drh 767 && strncmp(g.zPath, "doc/",4)==0
703e62a… drh 768 ){
703e62a… drh 769 int j;
45427ae… drh 770 for(j=7; g.zPath[j] && g.zPath[j]!='/'; j++){}
703e62a… drh 771 blob_append(cgi_output_blob(), &z[base], i-base);
703e62a… drh 772 blob_appendf(cgi_output_blob(), "%.*s", j-4, g.zPath+4);
45427ae… drh 773 base = i+8;
e57ab29… drh 774 }
e57ab29… drh 775 }
e57ab29… drh 776 blob_append(cgi_output_blob(), &z[base], i-base);
e57ab29… drh 777 }
ec56c69… drh 778
ec56c69… drh 779 /*
ec56c69… drh 780 ** Render a document as the reply to the HTTP request. The body
ec56c69… drh 781 ** of the document is contained in pBody. The body might be binary.
ec56c69… drh 782 ** The mimetype is in zMimetype.
ec56c69… drh 783 */
ec56c69… drh 784 void document_render(
ec56c69… drh 785 Blob *pBody, /* Document content */
ec56c69… drh 786 const char *zMime, /* MIME-type */
ec56c69… drh 787 const char *zDefaultTitle, /* Default title */
ec56c69… drh 788 const char *zFilename /* Name of the file being rendered */
ec56c69… drh 789 ){
ec56c69… drh 790 Blob title;
36a17f3… drh 791 int isPopup = P("popup")!=0;
ec56c69… drh 792 blob_init(&title,0,0);
ec56c69… drh 793 if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
37ae94b… drh 794 Blob tail = BLOB_INITIALIZER;
ec56c69… drh 795 style_adunit_config(ADUNIT_RIGHT_OK);
ec56c69… drh 796 if( wiki_find_title(pBody, &title, &tail) ){
36a17f3… drh 797 if( !isPopup ) style_header("%s", blob_str(&title));
ec56c69… drh 798 wiki_convert(&tail, 0, WIKI_BUTTONS);
ec56c69… drh 799 }else{
36a17f3… drh 800 if( !isPopup ) style_header("%s", zDefaultTitle);
ec56c69… drh 801 wiki_convert(pBody, 0, WIKI_BUTTONS);
ec56c69… drh 802 }
36a17f3… drh 803 if( !isPopup ){
36a17f3… drh 804 document_emit_js();
36a17f3… drh 805 style_finish_page();
36a17f3… drh 806 }
37ae94b… drh 807 blob_reset(&tail);
ec56c69… drh 808 }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
ec56c69… drh 809 Blob tail = BLOB_INITIALIZER;
ec56c69… drh 810 markdown_to_html(pBody, &title, &tail);
36a17f3… drh 811 if( !isPopup ){
36a17f3… drh 812 if( blob_size(&title)>0 ){
0b24a45… drh 813 markdown_dehtmlize_blob(&title);
36a17f3… drh 814 style_header("%s", blob_str(&title));
36a17f3… drh 815 }else{
36a17f3… drh 816 style_header("%s", zDefaultTitle);
36a17f3… drh 817 }
ec56c69… drh 818 }
ec56c69… drh 819 convert_href_and_output(&tail);
36a17f3… drh 820 if( !isPopup ){
36a17f3… drh 821 document_emit_js();
36a17f3… drh 822 style_finish_page();
36a17f3… drh 823 }
37ae94b… drh 824 blob_reset(&tail);
ec56c69… drh 825 }else if( fossil_strcmp(zMime, "text/plain")==0 ){
ec56c69… drh 826 style_header("%s", zDefaultTitle);
ec56c69… drh 827 @ <blockquote><pre>
ec56c69… drh 828 @ %h(blob_str(pBody))
ec56c69… drh 829 @ </pre></blockquote>
83f03e9… stephan 830 document_emit_js();
112c713… drh 831 style_finish_page();
ec56c69… drh 832 }else if( fossil_strcmp(zMime, "text/html")==0
ec56c69… drh 833 && doc_is_embedded_html(pBody, &title) ){
ec56c69… drh 834 if( blob_size(&title)==0 ) blob_append(&title,zFilename,-1);
36a17f3… drh 835 if( !isPopup ) style_header("%s", blob_str(&title));
ec56c69… drh 836 convert_href_and_output(pBody);
36a17f3… drh 837 if( !isPopup ){
36a17f3… drh 838 document_emit_js();
36a17f3… drh 839 style_finish_page();
36a17f3… drh 840 }
e32214a… drh 841 }else if( fossil_strcmp(zMime, "text/x-pikchr")==0 ){
e32214a… drh 842 style_adunit_config(ADUNIT_RIGHT_OK);
363f01a… drh 843 if( !isPopup ) style_header("%s", zDefaultTitle);
e32214a… drh 844 wiki_render_by_mimetype(pBody, zMime);
363f01a… drh 845 if( !isPopup ) style_finish_page();
ec56c69… drh 846 #ifdef FOSSIL_ENABLE_TH1_DOCS
ec56c69… drh 847 }else if( Th_AreDocsEnabled() &&
ec56c69… drh 848 fossil_strcmp(zMime, "application/x-th1")==0 ){
ec56c69… drh 849 int raw = P("raw")!=0;
ec56c69… drh 850 if( !raw ){
ec56c69… drh 851 Blob tail;
ec56c69… drh 852 blob_zero(&tail);
ec56c69… drh 853 if( wiki_find_title(pBody, &title, &tail) ){
ec56c69… drh 854 style_header("%s", blob_str(&title));
ec56c69… drh 855 Th_Render(blob_str(&tail));
ec56c69… drh 856 blob_reset(&tail);
ec56c69… drh 857 }else{
19bbd2c… drh 858 style_header("%h", zFilename);
ec56c69… drh 859 Th_Render(blob_str(pBody));
ec56c69… drh 860 }
ec56c69… drh 861 }else{
ec56c69… drh 862 Th_Render(blob_str(pBody));
ec56c69… drh 863 }
ec56c69… drh 864 if( !raw ){
83f03e9… stephan 865 document_emit_js();
112c713… drh 866 style_finish_page();
ec56c69… drh 867 }
ec56c69… drh 868 #endif
ec56c69… drh 869 }else{
14c81d9… drh 870 fossil_free(style_csp(1));
ec56c69… drh 871 cgi_set_content_type(zMime);
ec56c69… drh 872 cgi_set_content(pBody);
ec56c69… drh 873 }
ec56c69… drh 874 }
ec56c69… drh 875
27d743e… drh 876
27d743e… drh 877 /*
27d743e… drh 878 ** WEBPAGE: uv
5cf1206… drh 879 ** WEBPAGE: doc
27d743e… drh 880 ** URL: /uv/FILE
cfcd9b8… drh 881 ** URL: /doc/CHECKIN/FILE
cfcd9b8… drh 882 **
fd9b7bd… drh 883 ** CHECKIN can be either tag or hash prefix or timestamp identifying a
7dd07b2… drh 884 ** particular check-in, or the name of a branch (meaning the most recent
cfcd9b8… drh 885 ** check-in on that branch) or one of various magic words:
cfcd9b8… drh 886 **
cfcd9b8… drh 887 ** "tip" means the most recent check-in
cfcd9b8… drh 888 **
cfcd9b8… drh 889 ** "ckout" means the current check-out, if the server is run from
cfcd9b8… drh 890 ** within a check-out, otherwise it is the same as "tip"
cfcd9b8… drh 891 **
d08bc9e… drh 892 ** "latest" means use the most recent check-in for the document
d08bc9e… drh 893 ** regardless of what branch it occurs on.
d08bc9e… drh 894 **
cfcd9b8… drh 895 ** FILE is the name of a file to delivered up as a webpage. FILE is relative
cfcd9b8… drh 896 ** to the root of the source tree of the repository. The FILE must
cfcd9b8… drh 897 ** be a part of CHECKIN, except when CHECKIN=="ckout" when FILE is read
b5ab1eb… drh 898 ** directly from disk and need not be a managed file. For /uv, FILE
b5ab1eb… drh 899 ** can also be the hash of the unversioned file.
cfcd9b8… drh 900 **
cfcd9b8… drh 901 ** The "ckout" CHECKIN is intended for development - to provide a mechanism
cfcd9b8… drh 902 ** for looking at what a file will look like using the /doc webpage after
dc1121f… drh 903 ** it gets checked in. Some commands like "fossil ui", "fossil server",
dc1121f… drh 904 ** and "fossil http" accept an argument "--ckout-alias NAME" when allows
dc1121f… drh 905 ** NAME to be understood as an alias for "ckout". On a site with many
dc1121f… drh 906 ** embedded hyperlinks to /doc/trunk/... one can run with "--ckout-alias trunk"
dc1121f… drh 907 ** to simulate what the pending changes will look like after they are
dc1121f… drh 908 ** checked in. The NAME alias is stored in g.zCkoutAlias.
cfcd9b8… drh 909 **
cfcd9b8… drh 910 ** The file extension is used to decide how to render the file.
bdfbbdd… drh 911 **
f4ceace… mistachkin 912 ** If FILE ends in "/" then the names "FILE/index.html", "FILE/index.wiki",
f4ceace… mistachkin 913 ** and "FILE/index.md" are tried in that order. If the binary was compiled
f4ceace… mistachkin 914 ** with TH1 embedded documentation support and the "th1-docs" setting is
f4ceace… mistachkin 915 ** enabled, the name "FILE/index.th1" is also tried. If none of those are
f4ceace… mistachkin 916 ** found, then FILE is completely replaced by "404.md" and tried. If that
f4ceace… mistachkin 917 ** is not found, then a default 404 screen is generated.
e57ab29… drh 918 **
26eef7f… rberteig 919 ** If the file's mimetype is "text/x-fossil-wiki" or "text/x-markdown"
26eef7f… rberteig 920 ** then headers and footers are added. If the document has mimetype
26eef7f… rberteig 921 ** text/html then headers and footers are usually not added. However,
26eef7f… rberteig 922 ** if a "text/html" document begins with the following div:
e57ab29… drh 923 **
e57ab29… drh 924 ** <div class='fossil-doc' data-title='TEXT'>
e57ab29… drh 925 **
e57ab29… drh 926 ** then headers and footers are supplied. The optional data-title field
e57ab29… drh 927 ** specifies the title of the document in that case.
e57ab29… drh 928 **
e57ab29… drh 929 ** For fossil-doc documents and for markdown documents, text of the
e57ab29… drh 930 ** form: "href='$ROOT/" or "action='$ROOT" has the $ROOT name expanded
e57ab29… drh 931 ** to the top-level of the repository.
5cf1206… drh 932 */
5cf1206… drh 933 void doc_page(void){
5aa3785… mistachkin 934 const char *zName = 0; /* Argument to the /doc page */
ef10899… drh 935 const char *zOrigName = "?"; /* Original document name */
5cf1206… drh 936 const char *zMime; /* Document MIME type */
c49030f… drh 937 char *zCheckin = "tip"; /* The check-in holding the document */
956d490… andygoth 938 char *zPathSuffix = ""; /* Text to append to g.zPath */
c49030f… drh 939 int vid = 0; /* Artifact of check-in */
5cf1206… drh 940 int rid = 0; /* Artifact of file */
5cf1206… drh 941 int i; /* Loop counter */
5cf1206… drh 942 Blob filebody; /* Content of the documentation file */
ace8016… drh 943 Blob title; /* Document title */
8b4b424… drh 944 int nMiss = (-1); /* Failed attempts to find the document */
27d743e… drh 945 int isUV = g.zPath[0]=='u'; /* True for /uv. False for /doc */
27d743e… drh 946 const char *zDfltTitle;
9586ac1… jan.nijtmans 947 static const char *const azSuffix[] = {
bdfbbdd… drh 948 "index.html", "index.wiki", "index.md"
f4ceace… mistachkin 949 #ifdef FOSSIL_ENABLE_TH1_DOCS
f4ceace… mistachkin 950 , "index.th1"
a6fd491… jan.nijtmans 951 #endif
bdfbbdd… drh 952 };
cfcd9b8… drh 953
cfcd9b8… drh 954 login_check_credentials();
653dd40… drh 955 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
112c713… drh 956 style_set_current_feature("doc");
ace8016… drh 957 blob_init(&title, 0, 0);
37ae94b… drh 958 blob_init(&filebody, 0, 0);
27d743e… drh 959 zDfltTitle = isUV ? "" : "Documentation";
48e1e18… drh 960 db_begin_transaction();
3cb9ba4… andygoth 961 while( rid==0 && (++nMiss)<=count(azSuffix) ){
c99b4e3… drh 962 zName = P("name");
27d743e… drh 963 if( isUV ){
4a83785… drh 964 if( zName==0 ) zName = "index.wiki";
27d743e… drh 965 i = 0;
bdfbbdd… drh 966 }else{
27d743e… drh 967 if( zName==0 || zName[0]==0 ) zName = "tip/index.wiki";
27d743e… drh 968 for(i=0; zName[i] && zName[i]!='/'; i++){}
27d743e… drh 969 zCheckin = mprintf("%.*s", i, zName);
27d743e… drh 970 if( fossil_strcmp(zCheckin,"ckout")==0 && g.localOpen==0 ){
27d743e… drh 971 zCheckin = "tip";
d08bc9e… drh 972 }else if( fossil_strcmp(zCheckin,"latest")==0 ){
d08bc9e… drh 973 char *zNewCkin = db_text(0,
d08bc9e… drh 974 "SELECT uuid FROM blob, mlink, event, filename"
d08bc9e… drh 975 " WHERE filename.name=%Q"
d08bc9e… drh 976 " AND mlink.fnid=filename.fnid"
d08bc9e… drh 977 " AND blob.rid=mlink.mid"
d08bc9e… drh 978 " AND event.objid=mlink.mid"
d08bc9e… drh 979 " ORDER BY event.mtime DESC LIMIT 1",
d08bc9e… drh 980 zName + i + 1);
d08bc9e… drh 981 if( zNewCkin ) zCheckin = zNewCkin;
27d743e… drh 982 }
27d743e… drh 983 }
3cb9ba4… andygoth 984 if( nMiss==count(azSuffix) ){
27d743e… drh 985 zName = "404.md";
ec56c69… drh 986 zDfltTitle = "Not Found";
27d743e… drh 987 }else if( zName[i]==0 ){
3cb9ba4… andygoth 988 assert( nMiss>=0 && nMiss<count(azSuffix) );
27d743e… drh 989 zName = azSuffix[nMiss];
27d743e… drh 990 }else if( !isUV ){
bdfbbdd… drh 991 zName += i;
bdfbbdd… drh 992 }
bdfbbdd… drh 993 while( zName[0]=='/' ){ zName++; }
27d743e… drh 994 if( isUV ){
956d490… andygoth 995 zPathSuffix = fossil_strdup(zName);
27d743e… drh 996 }else{
956d490… andygoth 997 zPathSuffix = mprintf("%s/%s", zCheckin, zName);
27d743e… drh 998 }
bdfbbdd… drh 999 if( nMiss==0 ) zOrigName = zName;
bdfbbdd… drh 1000 if( !file_is_simple_pathname(zName, 1) ){
bdfbbdd… drh 1001 if( sqlite3_strglob("*/", zName)==0 ){
3cb9ba4… andygoth 1002 assert( nMiss>=0 && nMiss<count(azSuffix) );
bdfbbdd… drh 1003 zName = mprintf("%s%s", zName, azSuffix[nMiss]);
bdfbbdd… drh 1004 if( !file_is_simple_pathname(zName, 1) ){
bdfbbdd… drh 1005 goto doc_not_found;
bdfbbdd… drh 1006 }
bdfbbdd… drh 1007 }else{
bdfbbdd… drh 1008 goto doc_not_found;
bdfbbdd… drh 1009 }
bdfbbdd… drh 1010 }
27d743e… drh 1011 if( isUV ){
7383450… drh 1012 if( db_table_exists("repository","unversioned") ){
b5ab1eb… drh 1013 rid = unversioned_content(zName, &filebody);
b5ab1eb… drh 1014 if( rid==1 ){
b5ab1eb… drh 1015 Stmt q;
b5ab1eb… drh 1016 db_prepare(&q, "SELECT hash, mtime FROM unversioned"
b5ab1eb… drh 1017 " WHERE name=%Q", zName);
b5ab1eb… drh 1018 if( db_step(&q)==SQLITE_ROW ){
b5ab1eb… drh 1019 etag_check(ETAG_HASH, db_column_text(&q,0));
b5ab1eb… drh 1020 etag_last_modified(db_column_int64(&q,1));
b5ab1eb… drh 1021 }
b5ab1eb… drh 1022 db_finalize(&q);
b5ab1eb… drh 1023 }else if( rid==2 ){
b5ab1eb… drh 1024 zName = db_text(zName,
b5ab1eb… drh 1025 "SELECT name FROM unversioned WHERE hash=%Q", zName);
b5ab1eb… drh 1026 g.isConst = 1;
b5ab1eb… drh 1027 }
b5ab1eb… drh 1028 zDfltTitle = zName;
b5ab1eb… drh 1029 }
dc1121f… drh 1030 }else if( fossil_strcmp(zCheckin,"ckout")==0
dc1121f… drh 1031 || fossil_strcmp(zCheckin,g.zCkoutAlias)==0
dc1121f… drh 1032 ){
bc36fdc… danield 1033 /* Read from the local check-out */
bdfbbdd… drh 1034 char *zFullpath;
bdfbbdd… drh 1035 db_must_be_within_tree();
bdfbbdd… drh 1036 zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
1772357… drh 1037 if( file_isfile(zFullpath, RepoFILE)
1772357… drh 1038 && blob_read_from_file(&filebody, zFullpath, RepoFILE)>0 ){
cfcd9b8… drh 1039 rid = 1; /* Fake RID just to get the loop to end */
cfcd9b8… drh 1040 }
cfcd9b8… drh 1041 fossil_free(zFullpath);
bdfbbdd… drh 1042 }else{
29cb826… drh 1043 vid = symbolic_name_to_rid(zCheckin, "ci");
29cb826… drh 1044 rid = vid>0 ? doc_load_content(vid, zName, &filebody) : 0;
48e1e18… drh 1045 }
48e1e18… drh 1046 }
956d490… andygoth 1047 g.zPath = mprintf("%s/%s", g.zPath, zPathSuffix);
bdfbbdd… drh 1048 if( rid==0 ) goto doc_not_found;
1c5b51e… drh 1049 blob_to_utf8_no_bom(&filebody, 0);
1c5b51e… drh 1050
1c5b51e… drh 1051 /* The file is now contained in the filebody blob. Deliver the
20d02ab… jan.nijtmans 1052 ** file to the user
1c5b51e… drh 1053 */
cfcd9b8… drh 1054 zMime = nMiss==0 ? P("mimetype") : 0;
f969b6c… drh 1055 if( zMime==0 ){
f969b6c… drh 1056 zMime = mimetype_from_name(zName);
f969b6c… drh 1057 }
2116238… drh 1058 Th_StoreUnsafe("doc_name", zName);
27d743e… drh 1059 if( vid ){
27d743e… drh 1060 Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
27d743e… drh 1061 " FROM blob WHERE rid=%d", vid));
27d743e… drh 1062 Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
27d743e… drh 1063 " WHERE objid=%d AND type='ci'", vid));
27d743e… drh 1064 }
57f1e87… drh 1065 cgi_check_for_malice();
ec56c69… drh 1066 document_render(&filebody, zMime, zDfltTitle, zName);
3cb9ba4… andygoth 1067 if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found");
27d743e… drh 1068 db_end_transaction(0);
37ae94b… drh 1069 blob_reset(&title);
37ae94b… drh 1070 blob_reset(&filebody);
20d02ab… jan.nijtmans 1071 return;
20d02ab… jan.nijtmans 1072
20d02ab… jan.nijtmans 1073 /* Jump here when unable to locate the document */
cfcd9b8… drh 1074 doc_not_found:
20d02ab… jan.nijtmans 1075 db_end_transaction(0);
2537956… drh 1076 if( isUV && P("name")==0 ){
7d12ba5… drh 1077 uvlist_page();
0cd96ed… drh 1078 return;
2537956… drh 1079 }
cfcd9b8… drh 1080 cgi_set_status(404, "Not Found");
cfcd9b8… drh 1081 style_header("Not Found");
cfcd9b8… drh 1082 @ <p>Document %h(zOrigName) not found
cfcd9b8… drh 1083 if( fossil_strcmp(zCheckin,"ckout")!=0 ){
cfcd9b8… drh 1084 @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
cfcd9b8… drh 1085 }
112c713… drh 1086 style_finish_page();
37ae94b… drh 1087 blob_reset(&title);
37ae94b… drh 1088 blob_reset(&filebody);
20d02ab… jan.nijtmans 1089 return;
4348111… drh 1090 }
4348111… drh 1091
4348111… drh 1092 /*
4348111… drh 1093 ** The default logo.
4348111… drh 1094 */
4348111… drh 1095 static const unsigned char aLogo[] = {
20d02ab… jan.nijtmans 1096 71, 73, 70, 56, 55, 97, 62, 0, 71, 0, 244, 0, 0, 85,
20d02ab… jan.nijtmans 1097 129, 149, 95, 136, 155, 99, 139, 157, 106, 144, 162, 113, 150, 166,
20d02ab… jan.nijtmans 1098 116, 152, 168, 127, 160, 175, 138, 168, 182, 148, 176, 188, 159, 184,
20d02ab… jan.nijtmans 1099 195, 170, 192, 202, 180, 199, 208, 184, 202, 210, 191, 207, 215, 201,
20d02ab… jan.nijtmans 1100 215, 221, 212, 223, 228, 223, 231, 235, 226, 227, 226, 226, 234, 237,
20d02ab… jan.nijtmans 1101 233, 239, 241, 240, 244, 246, 244, 247, 248, 255, 255, 255, 0, 0,
20d02ab… jan.nijtmans 1102 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
20d02ab… jan.nijtmans 1103 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0,
20d02ab… jan.nijtmans 1104 0, 0, 62, 0, 71, 0, 0, 5, 255, 96, 100, 141, 100, 105,
20d02ab… jan.nijtmans 1105 158, 168, 37, 41, 132, 192, 164, 112, 44, 207, 102, 99, 0, 56,
20d02ab… jan.nijtmans 1106 16, 84, 116, 239, 199, 141, 65, 110, 232, 248, 25, 141, 193, 161,
20d02ab… jan.nijtmans 1107 82, 113, 108, 202, 32, 55, 229, 210, 73, 61, 41, 164, 88, 102,
20d02ab… jan.nijtmans 1108 181, 10, 41, 96, 179, 91, 106, 35, 240, 5, 135, 143, 137, 242,
20d02ab… jan.nijtmans 1109 87, 123, 246, 33, 190, 81, 108, 163, 237, 198, 14, 30, 113, 233,
20d02ab… jan.nijtmans 1110 131, 78, 115, 72, 11, 115, 87, 101, 19, 124, 51, 66, 74, 8,
20d02ab… jan.nijtmans 1111 19, 16, 67, 100, 74, 133, 50, 15, 101, 135, 56, 11, 74, 6,
20d02ab… jan.nijtmans 1112 143, 49, 126, 106, 56, 8, 145, 67, 9, 152, 48, 139, 155, 5,
20d02ab… jan.nijtmans 1113 22, 13, 74, 115, 161, 41, 147, 101, 13, 130, 57, 132, 170, 40,
20d02ab… jan.nijtmans 1114 167, 155, 0, 94, 57, 3, 178, 48, 183, 181, 57, 160, 186, 40,
20d02ab… jan.nijtmans 1115 19, 141, 189, 0, 69, 192, 40, 16, 195, 155, 185, 199, 41, 201,
20d02ab… jan.nijtmans 1116 189, 191, 205, 193, 188, 131, 210, 49, 175, 88, 209, 214, 38, 19,
20d02ab… jan.nijtmans 1117 3, 11, 19, 111, 127, 60, 219, 39, 55, 204, 19, 11, 6, 100,
20d02ab… jan.nijtmans 1118 5, 10, 227, 228, 37, 163, 0, 239, 117, 56, 238, 243, 49, 195,
20d02ab… jan.nijtmans 1119 177, 247, 48, 158, 56, 251, 50, 216, 254, 197, 56, 128, 107, 158,
20d02ab… jan.nijtmans 1120 2, 125, 171, 114, 92, 218, 246, 96, 66, 3, 4, 50, 134, 176,
20d02ab… jan.nijtmans 1121 145, 6, 97, 64, 144, 24, 19, 136, 108, 91, 177, 160, 0, 194,
20d02ab… jan.nijtmans 1122 19, 253, 0, 216, 107, 214, 224, 192, 129, 5, 16, 83, 255, 244,
20d02ab… jan.nijtmans 1123 43, 213, 195, 24, 159, 27, 169, 64, 230, 88, 208, 227, 129, 182,
20d02ab… jan.nijtmans 1124 54, 4, 89, 158, 24, 181, 163, 199, 1, 155, 52, 233, 8, 130,
20d02ab… jan.nijtmans 1125 176, 83, 24, 128, 137, 50, 18, 32, 48, 48, 114, 11, 173, 137,
20d02ab… jan.nijtmans 1126 19, 110, 4, 64, 105, 1, 194, 30, 140, 68, 15, 24, 24, 224,
20d02ab… jan.nijtmans 1127 50, 76, 70, 0, 11, 171, 54, 26, 160, 181, 194, 149, 148, 40,
20d02ab… jan.nijtmans 1128 174, 148, 122, 64, 180, 208, 161, 17, 207, 112, 164, 1, 128, 96,
20d02ab… jan.nijtmans 1129 148, 78, 18, 21, 194, 33, 229, 51, 247, 65, 133, 97, 5, 250,
20d02ab… jan.nijtmans 1130 69, 229, 100, 34, 220, 128, 166, 116, 190, 62, 8, 167, 195, 170,
20d02ab… jan.nijtmans 1131 47, 163, 0, 130, 90, 152, 11, 160, 173, 170, 27, 154, 26, 91,
20d02ab… jan.nijtmans 1132 232, 151, 171, 18, 14, 162, 253, 98, 170, 18, 70, 171, 64, 219,
20d02ab… jan.nijtmans 1133 10, 67, 136, 134, 187, 116, 75, 180, 46, 179, 174, 135, 4, 189,
20d02ab… jan.nijtmans 1134 229, 231, 78, 40, 10, 62, 226, 164, 172, 64, 240, 167, 170, 10,
20d02ab… jan.nijtmans 1135 18, 124, 188, 10, 107, 65, 193, 94, 11, 93, 171, 28, 248, 17,
20d02ab… jan.nijtmans 1136 239, 46, 140, 78, 97, 34, 25, 153, 36, 99, 65, 130, 7, 203,
20d02ab… jan.nijtmans 1137 183, 168, 51, 34, 136, 25, 140, 10, 6, 16, 28, 255, 145, 241,
20d02ab… jan.nijtmans 1138 230, 140, 10, 66, 178, 167, 112, 48, 192, 128, 129, 9, 31, 141,
20d02ab… jan.nijtmans 1139 84, 138, 63, 163, 162, 2, 203, 206, 240, 56, 55, 98, 192, 188,
20d02ab… jan.nijtmans 1140 15, 185, 50, 160, 6, 0, 125, 62, 33, 214, 195, 33, 5, 24,
20d02ab… jan.nijtmans 1141 184, 25, 231, 14, 201, 245, 144, 23, 126, 104, 228, 0, 145, 2,
20d02ab… jan.nijtmans 1142 13, 140, 244, 212, 17, 21, 20, 176, 159, 17, 95, 225, 160, 128,
20d02ab… jan.nijtmans 1143 16, 1, 32, 224, 142, 32, 227, 125, 87, 64, 0, 16, 54, 129,
20d02ab… jan.nijtmans 1144 205, 2, 141, 76, 53, 130, 103, 37, 166, 64, 144, 107, 78, 196,
20d02ab… jan.nijtmans 1145 5, 192, 0, 54, 50, 229, 9, 141, 49, 84, 194, 35, 12, 196,
20d02ab… jan.nijtmans 1146 153, 48, 192, 137, 57, 84, 24, 7, 87, 159, 249, 240, 215, 143,
20d02ab… jan.nijtmans 1147 105, 241, 118, 149, 9, 139, 4, 64, 203, 141, 35, 140, 129, 131,
20d02ab… jan.nijtmans 1148 16, 222, 125, 231, 128, 2, 238, 17, 152, 66, 3, 5, 56, 224,
20d02ab… jan.nijtmans 1149 159, 103, 16, 76, 25, 75, 5, 11, 164, 215, 96, 9, 14, 16,
20d02ab… jan.nijtmans 1150 36, 225, 15, 11, 40, 144, 192, 156, 41, 10, 178, 199, 3, 66,
20d02ab… jan.nijtmans 1151 64, 80, 193, 3, 124, 90, 48, 129, 129, 102, 177, 18, 192, 154,
20d02ab… jan.nijtmans 1152 49, 84, 240, 208, 92, 22, 149, 96, 39, 9, 31, 74, 17, 94,
20d02ab… jan.nijtmans 1153 3, 8, 177, 199, 72, 59, 85, 76, 25, 216, 8, 139, 194, 197,
20d02ab… jan.nijtmans 1154 138, 163, 69, 96, 115, 0, 147, 72, 72, 84, 28, 14, 79, 86,
20d02ab… jan.nijtmans 1155 233, 230, 23, 113, 26, 160, 128, 3, 10, 58, 129, 103, 14, 159,
20d02ab… jan.nijtmans 1156 214, 163, 146, 117, 238, 213, 154, 128, 151, 109, 84, 64, 217, 13,
20d02ab… jan.nijtmans 1157 27, 10, 228, 39, 2, 235, 164, 168, 74, 8, 0, 59,
4348111… drh 1158 };
4348111… drh 1159
4348111… drh 1160 /*
4348111… drh 1161 ** WEBPAGE: logo
4348111… drh 1162 **
4348111… drh 1163 ** Return the logo image. This image is available to anybody who can see
4348111… drh 1164 ** the login page. It is designed for use in the upper left-hand corner
4348111… drh 1165 ** of the header.
4348111… drh 1166 */
4348111… drh 1167 void logo_page(void){
4348111… drh 1168 Blob logo;
4348111… drh 1169 char *zMime;
4348111… drh 1170
7383450… drh 1171 etag_check(ETAG_CONFIG, 0);
4348111… drh 1172 zMime = db_get("logo-mimetype", "image/gif");
4348111… drh 1173 blob_zero(&logo);
4348111… drh 1174 db_blob(&logo, "SELECT value FROM config WHERE name='logo-image'");
4348111… drh 1175 if( blob_size(&logo)==0 ){
4348111… drh 1176 blob_init(&logo, (char*)aLogo, sizeof(aLogo));
4348111… drh 1177 }
4348111… drh 1178 cgi_set_content_type(zMime);
4348111… drh 1179 cgi_set_content(&logo);
6239845… drh 1180 }
6239845… drh 1181
6239845… drh 1182 /*
6239845… drh 1183 ** The default background image: a 16x16 white GIF
6239845… drh 1184 */
6239845… drh 1185 static const unsigned char aBackground[] = {
6239845… drh 1186 71, 73, 70, 56, 57, 97, 16, 0, 16, 0,
6239845… drh 1187 240, 0, 0, 255, 255, 255, 0, 0, 0, 33,
6239845… drh 1188 254, 4, 119, 105, 115, 104, 0, 44, 0, 0,
6239845… drh 1189 0, 0, 16, 0, 16, 0, 0, 2, 14, 132,
6239845… drh 1190 143, 169, 203, 237, 15, 163, 156, 180, 218, 139,
6239845… drh 1191 179, 62, 5, 0, 59,
6239845… drh 1192 };
6239845… drh 1193
6239845… drh 1194
6239845… drh 1195 /*
6239845… drh 1196 ** WEBPAGE: background
6239845… drh 1197 **
7ab0328… drh 1198 ** Return the background image. If no background image is defined, a
7ab0328… drh 1199 ** built-in 16x16 pixel white GIF is returned.
6239845… drh 1200 */
6239845… drh 1201 void background_page(void){
6239845… drh 1202 Blob bgimg;
6239845… drh 1203 char *zMime;
6239845… drh 1204
7383450… drh 1205 etag_check(ETAG_CONFIG, 0);
6239845… drh 1206 zMime = db_get("background-mimetype", "image/gif");
6239845… drh 1207 blob_zero(&bgimg);
6239845… drh 1208 db_blob(&bgimg, "SELECT value FROM config WHERE name='background-image'");
6239845… drh 1209 if( blob_size(&bgimg)==0 ){
6239845… drh 1210 blob_init(&bgimg, (char*)aBackground, sizeof(aBackground));
6239845… drh 1211 }
6239845… drh 1212 cgi_set_content_type(zMime);
6239845… drh 1213 cgi_set_content(&bgimg);
15f0dbd… drh 1214 }
15f0dbd… drh 1215
7383450… drh 1216
7383450… drh 1217 /*
81b3ce3… drh 1218 ** WEBPAGE: favicon.ico
81b3ce3… drh 1219 **
37262b8… mistachkin 1220 ** Return the configured "favicon.ico" image. If no "favicon.ico" image
37262b8… mistachkin 1221 ** is defined, the returned image is for the Fossil lizard icon.
81b3ce3… drh 1222 **
37262b8… mistachkin 1223 ** The intended use case here is to supply an icon for the "fossil ui"
81b3ce3… drh 1224 ** command. For a permanent website, the recommended process is for
37262b8… mistachkin 1225 ** the admin to set up a project-specific icon and reference that icon
37262b8… mistachkin 1226 ** in the HTML header using a line like:
81b3ce3… drh 1227 **
81b3ce3… drh 1228 ** <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/>
275da70… danield 1229 **
81b3ce3… drh 1230 */
81b3ce3… drh 1231 void favicon_page(void){
37262b8… mistachkin 1232 Blob icon;
37262b8… mistachkin 1233 char *zMime;
81b3ce3… drh 1234
81b3ce3… drh 1235 etag_check(ETAG_CONFIG, 0);
37262b8… mistachkin 1236 zMime = db_get("icon-mimetype", "image/gif");
37262b8… mistachkin 1237 blob_zero(&icon);
37262b8… mistachkin 1238 db_blob(&icon, "SELECT value FROM config WHERE name='icon-image'");
37262b8… mistachkin 1239 if( blob_size(&icon)==0 ){
37262b8… mistachkin 1240 blob_init(&icon, (char*)aLogo, sizeof(aLogo));
37262b8… mistachkin 1241 }
37262b8… mistachkin 1242 cgi_set_content_type(zMime);
37262b8… mistachkin 1243 cgi_set_content(&icon);
81b3ce3… drh 1244 }
81b3ce3… drh 1245
81b3ce3… drh 1246 /*
7ab0328… drh 1247 ** WEBPAGE: docsrch
7ab0328… drh 1248 **
7ab0328… drh 1249 ** Search for documents that match a user-supplied full-text search pattern.
7ab0328… drh 1250 ** If no pattern is specified (by the s= query parameter) then the user
7ab0328… drh 1251 ** is prompted to enter a search string.
7ab0328… drh 1252 **
7ab0328… drh 1253 ** Query parameters:
15f0dbd… drh 1254 **
7ab0328… drh 1255 ** s=PATTERN Search for PATTERN
15f0dbd… drh 1256 */
15f0dbd… drh 1257 void doc_search_page(void){
809c010… stephan 1258 const int isSearch = P("s")!=0;
15f0dbd… drh 1259 login_check_credentials();
809c010… stephan 1260 style_header("Document Search%s", isSearch ? " Results" : "");
57f1e87… drh 1261 cgi_check_for_malice();
c0c0bae… drh 1262 search_screen(SRCH_DOC, 0);
112c713… drh 1263 style_finish_page();
5cf1206… drh 1264 }

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button