Check-in [7303bfeb12]
Not logged in
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
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