Check-in [d14adf1032]
Not logged in
Overview

SHA1 Hash:d14adf1032924428f6eda72570fa81b68fe69621
Date: 2008-11-19 16:55:14
User: kejoki
Comment:Merge src & doc leaves back.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/cgi.c from [40d3404d8c] to [e524bac009].

@@ -336,11 +336,27 @@
     blob_compress(&cgiContent[0], &cgiContent[0]);
   }
 
   if( iReplyStatus != 304 ) {
     total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]);
+#ifdef __MINGW32__
+    /* In windows versions of Apache, extra \r characters get added to the
+    ** response, which mess up the Content-Length.  So let apache figure
+    ** out the content length for itself if we are using CGI.  If this
+    ** is a complete stand-alone webserver, on the other hand, we still
+    ** need the Content-Length.
+    */
+    if( g.fullHttpReply ){
+      fprintf(g.httpOut, "Content-Length: %d\r\n", total_size);
+    }
+#else
+    /* On unix, \n to \r\n translation is never a problem.  We know the
+    ** content length, so we might as well go ahead and tell the webserver
+    ** what it is in all cases.
+    */
     fprintf(g.httpOut, "Content-Length: %d\r\n", total_size);
+#endif
   }
   fprintf(g.httpOut, "\r\n");
   if( total_size>0 && iReplyStatus != 304 ){
     int i, size;
     for(i=0; i<2; i++){

Modified src/clearsign.c from [3868e827c1] to [3e61358e2b].

@@ -37,10 +37,13 @@
   char *zIn;
   char *zOut;
   char *zBase = db_get("pgp-command", "gpg --clearsign -o ");
   char *zCmd;
   int rc;
+  if( is_false(zBase) ){
+    return 0;
+  }
   zRand = db_text(0, "SELECT hex(randomblob(10))");
   zOut = mprintf("out-%s", zRand);
   zIn = mprintf("in-%z", zRand);
   blob_write_to_file(pIn, zOut);
   zCmd = mprintf("%s %s %s", zBase, zIn, zOut);

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 [97b0bae401] to [594a3689d1].

@@ -686,11 +686,14 @@
 **
 **     * It's uuid
 **     * date of check-in
 **     * Comment & user
 */
-static void object_description(int rid, int linkToView){
+static void object_description(
+  int rid,                 /* The artifact ID */
+  int linkToView           /* Add viewer link if true */
+){
   Stmt q;
   int cnt = 0;
   int nWiki = 0;
   db_prepare(&q,
     "SELECT filename.name, datetime(event.mtime), substr(a.uuid,1,10),"
@@ -710,14 +713,19 @@
     const char *zDate = db_column_text(&q, 1);
     const char *zFuuid = db_column_text(&q, 2);
     const char *zCom = db_column_text(&q, 3);
     const char *zUser = db_column_text(&q, 4);
     const char *zVers = db_column_text(&q, 5);
-    @ File <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a>
+    if( cnt>0 ){
+      @ Also file
+    }else{
+      @ 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)
+    @ %w(zCom) by %h(zUser) on %s(zDate).
     cnt++;
   }
   db_finalize(&q);
   db_prepare(&q,
     "SELECT substr(tagname, 6, 10000), datetime(event.mtime),"
@@ -733,13 +741,17 @@
   while( db_step(&q)==SQLITE_ROW ){
     const char *zPagename = db_column_text(&q, 0);
     const char *zDate = db_column_text(&q, 1);
     const char *zUser = db_column_text(&q, 2);
     const char *zUuid = db_column_text(&q, 3);
-    @ Wiki page
+    if( cnt>0 ){
+      @ Also wiki page
+    }else{
+      @ Wiki page
+    }
     @ [<a href="%s(g.zBaseURL)/wiki?name=%t(zPagename)">%h(zPagename)</a>]
-    @ uuid %s(zUuid) by %h(zUser) on %s(zDate)
+    @ uuid %s(zUuid) by %h(zUser) on %s(zDate).
     nWiki++;
     cnt++;
   }
   db_finalize(&q);
   if( nWiki==0 ){
@@ -754,10 +766,13 @@
       const char *zDate = db_column_text(&q, 0);
       const char *zUuid = db_column_text(&q, 3);
       const char *zUser = db_column_text(&q, 1);
       const char *zCom = db_column_text(&q, 2);
       const char *zType = db_column_text(&q, 4);
+      if( cnt>0 ){
+        @ Also
+      }
       if( zType[0]=='w' ){
         @ Wiki edit
       }else if( zType[0]=='t' ){
         @ Ticket change
       }else if( zType[0]=='c' ){
@@ -764,11 +779,11 @@
         @ Manifest of baseline
       }else{
         @ Control file referencing
       }
       hyperlink_to_uuid(zUuid);
-      @ %w(zCom) by %h(zUser) on %s(zDate)
+      @ %w(zCom) by %h(zUser) on %s(zDate).
       cnt++;
     }
     db_finalize(&q);
   }
   if( cnt==0 ){
@@ -814,19 +829,131 @@
   blob_reset(&diff);
   style_footer();
 }
 
 /*
+** WEBPAGE: raw
+** URL: /raw?name=ARTIFACTID&m=TYPE
+**
+** Return the uninterpreted content of an artifact.  Used primarily
+** to view artifacts that are images.
+*/
+void rawartifact_page(void){
+  int rid;
+  const char *zMime;
+  Blob content;
+
+  rid = name_to_rid(PD("name","0"));
+  zMime = PD("m","application/x-fossil-artifact");
+  login_check_credentials();
+  if( !g.okRead ){ login_needed(); return; }
+  if( rid==0 ){ cgi_redirect("/home"); }
+  content_get(rid, &content);
+  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.
 */
 void artifact_page(void){
   int rid;
   Blob content;
+  const char *zMime;
 
   rid = name_to_rid(PD("name","0"));
   login_check_credentials();
   if( !g.okRead ){ login_needed(); return; }
   if( rid==0 ){ cgi_redirect("/home"); }
@@ -844,15 +971,27 @@
   @ <h2>Content Of:</h2>
   @ <blockquote>
   object_description(rid, 0);
   @ </blockquote>
   @ <hr>
-  @ <blockquote><pre>
+  @ <blockquote>
   content_get(rid, &content);
-  @ %h(blob_str(&content))
-  @ </pre></blockquote>
-  blob_reset(&content);
+  zMime = mimetype_from_content(&content);
+  if( zMime==0 ){
+    @ <pre>
+    @ %h(blob_str(&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

Modified src/wiki.c from [45c7efaf6d] to [8673214ab7].

@@ -83,13 +83,11 @@
   if( zPageName ){
     login_check_credentials();
     g.zExtra = zPageName;
     cgi_set_parameter_nocopy("name", g.zExtra);
     g.okRdWiki = 1;
-    g.okApndWiki = 0;
-    g.okWrWiki = 0;
-    g.okHistory = 0;
+    g.argv[1] = "home";
     wiki_page();
     return;
   }
   login_check_credentials();
   style_header("Home");
@@ -121,10 +119,11 @@
   Blob wiki;
   Manifest m;
   const char *zPageName;
   char *zHtmlPageName;
   char *zBody = mprintf("%s","<i>Empty Page</i>");
+  int isHome = g.argv[1][0]=='h';
 
   login_check_credentials();
   if( !g.okRdWiki ){ login_needed(); return; }
   zPageName = P("name");
   if( zPageName==0 ){
@@ -169,21 +168,23 @@
       if( m.type==CFTYPE_WIKI ){
         zBody = m.zWiki;
       }
     }
   }
-  if( isSandbox || (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
-    style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
-         g.zTop, zPageName);
-  }
-  if( isSandbox || (rid && g.okApndWiki) ){
-    style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
-         g.zTop, zPageName);
-  }
-  if( !isSandbox && g.okHistory ){
-    style_submenu_element("History", "History", "%s/whistory?name=%T",
-         g.zTop, zPageName);
+  if( !isHome ){
+    if( isSandbox || (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
+      style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
+           g.zTop, zPageName);
+    }
+    if( isSandbox || (rid && g.okApndWiki) ){
+      style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
+           g.zTop, zPageName);
+    }
+    if( !isSandbox && g.okHistory ){
+      style_submenu_element("History", "History", "%s/whistory?name=%T",
+           g.zTop, zPageName);
+    }
   }
   zHtmlPageName = mprintf("%h", zPageName);
   style_header(zHtmlPageName);
   blob_init(&wiki, zBody, -1);
   wiki_convert(&wiki, 0, 0);

Modified src/wikiformat.c from [ad6f4b4c31] to [51759e15ab].

@@ -936,10 +936,16 @@
   ){
     blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
   }else if( zTarget[0]=='/' ){
     if( g.okHistory ){
       blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zBaseURL, zTarget);
+    }else{
+      zTerm = "";
+    }
+  }else if( zTarget[0]=='.' ){
+    if( g.okHistory ){
+      blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
     }else{
       zTerm = "";
     }
   }else if( is_valid_uuid(zTarget) ){
     int isClosed;