Overview
SHA1 Hash: | 7303bfeb129c55fe9ad55a48050f6b4c7bd8ab9f |
---|---|
Date: | 2008-11-17 19:11:32 |
User: | drh |
Comment: | Modify the artifact viewer to base the mimetype off of artifact content, not the artifact name. |
Timelines: | ancestors | descendants | both | trunk |
Other Links: | files | ZIP archive | manifest |
Tags And Properties
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
Changes
[hide diffs]Modified src/doc.c from [2b833a11d7] to [a23503395b].
@@ -25,10 +25,62 @@ ** pages. */ #include "config.h" #include "doc.h" #include <assert.h> + +/* +** Try to guess the mimetype from content. +** +** If the content is pure text, return NULL. +** +** For image types, attempt to return an appropriate mimetype +** name like "image/gif" or "image/jpeg". +** +** For any other binary type, return "unknown/unknown". +*/ +const char *mimetype_from_content(Blob *pBlob){ + int i; + int n; + const unsigned char *x; + + static const char isBinary[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + }; + + /* A table of mimetypes based on file content prefixes + */ + static const struct { + const char *zPrefix; /* The file prefix */ + int size; /* Length of the prefix */ + const char *zMimetype; /* The corresponding mimetype */ + } aMime[] = { + { "GIF87a", 6, "image/gif" }, + { "GIF89a", 6, "image/gif" }, + { "\211PNG\r\n\032\r", 8, "image/png" }, + { "\377\332\377", 3, "image/jpeg" }, + }; + + x = (const unsigned char*)blob_buffer(pBlob); + n = blob_size(pBlob); + for(i=0; i<n; i++){ + unsigned char c = x[i]; + if( c<=0x1f && isBinary[c] ){ + break; + } + } + if( i>=n ){ + return 0; /* Plain text */ + } + for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){ + if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){ + return aMime[i].zMimetype; + } + } + return "unknown/unknown"; +} /* ** Guess the mime-type of a document based on its name. */ const char *mimetype_from_name(const char *zName){
Modified src/info.c from [17472b8889] to [48386916c4].
@@ -686,18 +686,17 @@ ** ** * It's uuid ** * date of check-in ** * Comment & user */ -static const char *object_description( +static void object_description( int rid, /* The artifact ID */ int linkToView /* Add viewer link if true */ ){ Stmt q; int cnt = 0; int nWiki = 0; - const char *zMime = 0; db_prepare(&q, "SELECT filename.name, datetime(event.mtime), substr(a.uuid,1,10)," " coalesce(event.ecomment,event.comment)," " coalesce(event.euser,event.user)," " b.uuid" @@ -718,12 +717,12 @@ const char *zVers = db_column_text(&q, 5); @ File <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a> @ uuid %s(zFuuid) part of check-in hyperlink_to_uuid(zVers); @ %w(zCom) by %h(zUser) on %s(zDate) - zMime = mimetype_from_name(zName); cnt++; + break; } db_finalize(&q); db_prepare(&q, "SELECT substr(tagname, 6, 10000), datetime(event.mtime)," " coalesce(event.euser, event.user), uuid" @@ -743,11 +742,10 @@ @ Wiki page @ [<a href="%s(g.zBaseURL)/wiki?name=%t(zPagename)">%h(zPagename)</a>] @ uuid %s(zUuid) by %h(zUser) on %s(zDate) nWiki++; cnt++; - zMime = 0; } db_finalize(&q); if( nWiki==0 ){ db_prepare(&q, "SELECT datetime(mtime), user, comment, uuid, type" @@ -781,11 +779,10 @@ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); @ Control file %s(zUuid). }else if( linkToView ){ @ <a href="%s(g.zBaseURL)/artifact/%d(rid)">[view]</a> } - return zMime; } /* ** WEBPAGE: fdiff ** @@ -843,10 +840,99 @@ cgi_set_content_type(zMime); cgi_set_content(&content); } /* +** Render a hex dump of a file. +*/ +static void hexdump(Blob *pBlob){ + const unsigned char *x; + int n, i, j, k; + char zLine[100]; + static const char zHex[] = "0123456789abcdef"; + + x = (const unsigned char*)blob_buffer(pBlob); + n = blob_size(pBlob); + for(i=0; i<n; i+=16){ + j = 0; + zLine[0] = zHex[(i>>24)&0xf]; + zLine[1] = zHex[(i>>16)&0xf]; + zLine[2] = zHex[(i>>8)&0xf]; + zLine[3] = zHex[i&0xf]; + zLine[4] = ':'; + sprintf(zLine, "%04x: ", i); + for(j=0; j<16; j++){ + k = 5+j*3; + zLine[k] = ' '; + if( i+j<n ){ + unsigned char c = x[i+j]; + zLine[k+1] = zHex[c>>4]; + zLine[k+2] = zHex[c&0xf]; + }else{ + zLine[k+1] = ' '; + zLine[k+2] = ' '; + } + } + zLine[53] = ' '; + zLine[54] = ' '; + for(j=0; j<16; j++){ + k = j+55; + if( i+j<n ){ + unsigned char c = x[i+j]; + if( c>=0x20 && c<=0x7e ){ + zLine[k] = c; + }else{ + zLine[k] = '.'; + } + }else{ + zLine[k] = 0; + } + } + zLine[71] = 0; + @ %h(zLine) + } +} + +/* +** WEBPAGE: hexdump +** URL: /hexdump?name=ARTIFACTID +** +** Show the complete content of a file identified by ARTIFACTID +** as preformatted text. +*/ +void hexdump_page(void){ + int rid; + Blob content; + + rid = name_to_rid(PD("name","0")); + login_check_credentials(); + if( !g.okRead ){ login_needed(); return; } + if( rid==0 ){ cgi_redirect("/home"); } + if( g.okAdmin ){ + const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); + if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){ + style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1", + g.zTop, zUuid); + }else{ + style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", + g.zTop, zUuid); + } + } + style_header("Hex Artifact Content"); + @ <h2>Hexadecimal Content Of:</h2> + @ <blockquote> + object_description(rid, 0); + @ </blockquote> + @ <hr> + content_get(rid, &content); + @ <blockquote><pre> + hexdump(&content); + @ </pre></blockquote> + style_footer(); +} + +/* ** WEBPAGE: artifact ** URL: /artifact?name=ARTIFACTID ** ** Show the complete content of a file identified by ARTIFACTID ** as preformatted text. @@ -871,22 +957,30 @@ } } style_header("Artifact Content"); @ <h2>Content Of:</h2> @ <blockquote> - zMime = object_description(rid, 0); + object_description(rid, 0); @ </blockquote> @ <hr> - if( zMime && strncmp(zMime, "image/", 6)==0 ){ - @ <img src="%s(g.zBaseURL)/raw?name=%d(rid)&m=%s(zMime)"></img> - }else{ - @ <blockquote><pre> - content_get(rid, &content); + @ <blockquote> + content_get(rid, &content); + zMime = mimetype_from_content(&content); + if( zMime==0 ){ + @ <pre> @ %h(blob_str(&content)) - @ </pre></blockquote> - blob_reset(&content); + @ </pre> + style_submenu_element("Hex","Hex", "%s/hexdump?name=%d", g.zTop, rid); + }else if( strncmp(zMime, "image/", 6)==0 ){ + @ <img src="%s(g.zBaseURL)/raw?name=%d(rid)&m=%s(zMime)"></img> + style_submenu_element("Hex","Hex", "%s/hexdump?name=%d", g.zTop, rid); + }else{ + @ <pre> + hexdump(&content); + @ </pre> } + @ </blockquote> style_footer(); } /* ** WEBPAGE: tinfo