File Annotation
Not logged in
5cf1206dfa 2008-05-15       drh: /*
5cf1206dfa 2008-05-15       drh: ** Copyright (c) 2007 D. Richard Hipp
5cf1206dfa 2008-05-15       drh: **
5cf1206dfa 2008-05-15       drh: ** This program is free software; you can redistribute it and/or
5cf1206dfa 2008-05-15       drh: ** modify it under the terms of the GNU General Public
5cf1206dfa 2008-05-15       drh: ** License version 2 as published by the Free Software Foundation.
5cf1206dfa 2008-05-15       drh: **
5cf1206dfa 2008-05-15       drh: ** This program is distributed in the hope that it will be useful,
5cf1206dfa 2008-05-15       drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of
5cf1206dfa 2008-05-15       drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
5cf1206dfa 2008-05-15       drh: ** General Public License for more details.
5cf1206dfa 2008-05-15       drh: **
5cf1206dfa 2008-05-15       drh: ** You should have received a copy of the GNU General Public
5cf1206dfa 2008-05-15       drh: ** License along with this library; if not, write to the
5cf1206dfa 2008-05-15       drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
5cf1206dfa 2008-05-15       drh: ** Boston, MA  02111-1307, USA.
5cf1206dfa 2008-05-15       drh: **
5cf1206dfa 2008-05-15       drh: ** Author contact information:
5cf1206dfa 2008-05-15       drh: **   drh@hwaci.com
5cf1206dfa 2008-05-15       drh: **   http://www.hwaci.com/drh/
5cf1206dfa 2008-05-15       drh: **
5cf1206dfa 2008-05-15       drh: *******************************************************************************
5cf1206dfa 2008-05-15       drh: **
5cf1206dfa 2008-05-15       drh: ** This file contains code to implement the "/doc" web page and related
5cf1206dfa 2008-05-15       drh: ** pages.
5cf1206dfa 2008-05-15       drh: */
5cf1206dfa 2008-05-15       drh: #include "config.h"
5cf1206dfa 2008-05-15       drh: #include "doc.h"
5cf1206dfa 2008-05-15       drh: #include <assert.h>
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh: /*
5cf1206dfa 2008-05-15       drh: ** Guess the mime-type of a document based on its name.
5cf1206dfa 2008-05-15       drh: */
5cf1206dfa 2008-05-15       drh: const char *mimetype_from_name(const char *zName){
5cf1206dfa 2008-05-15       drh:   const char *z;
5cf1206dfa 2008-05-15       drh:   int i;
5cf1206dfa 2008-05-15       drh:   char zSuffix[20];
5cf1206dfa 2008-05-15       drh:   static const struct {
5cf1206dfa 2008-05-15       drh:     const char *zSuffix;
5cf1206dfa 2008-05-15       drh:     const char *zMimetype;
5cf1206dfa 2008-05-15       drh:   } aMime[] = {
5cf1206dfa 2008-05-15       drh:     { "html",     "text/html"                 },
5cf1206dfa 2008-05-15       drh:     { "htm",      "text/html"                 },
5cf1206dfa 2008-05-15       drh:     { "wiki",     "application/x-fossil-wiki" },
5cf1206dfa 2008-05-15       drh:     { "txt",      "text/plain"                },
5cf1206dfa 2008-05-15       drh:     { "jpg",      "image/jpeg"                },
5cf1206dfa 2008-05-15       drh:     { "jpeg",     "image/jpeg"                },
5cf1206dfa 2008-05-15       drh:     { "gif",      "image/gif"                 },
5cf1206dfa 2008-05-15       drh:     { "png",      "image/png"                 },
5cf1206dfa 2008-05-15       drh:     { "css",      "text/css"                  },
5cf1206dfa 2008-05-15       drh:   };
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh:   z = zName;
5cf1206dfa 2008-05-15       drh:   for(i=0; zName[i]; i++){
5cf1206dfa 2008-05-15       drh:     if( zName[i]=='.' ) z = &zName[i+1];
5cf1206dfa 2008-05-15       drh:   }
5cf1206dfa 2008-05-15       drh:   i = strlen(z);
5cf1206dfa 2008-05-15       drh:   if( i<sizeof(zSuffix)-1 ){
5cf1206dfa 2008-05-15       drh:     strcpy(zSuffix, z);
5cf1206dfa 2008-05-15       drh:     for(i=0; zSuffix[i]; i++) zSuffix[i] = tolower(zSuffix[i]);
5cf1206dfa 2008-05-15       drh:     for(i=0; i<sizeof(aMime)/sizeof(aMime[0]); i++){
5cf1206dfa 2008-05-15       drh:       if( strcmp(zSuffix, aMime[i].zSuffix)==0 ){
5cf1206dfa 2008-05-15       drh:         return aMime[i].zMimetype;
5cf1206dfa 2008-05-15       drh:       }
5cf1206dfa 2008-05-15       drh:     }
5cf1206dfa 2008-05-15       drh:   }
5cf1206dfa 2008-05-15       drh:   return "application/x-fossil-artifact";
5cf1206dfa 2008-05-15       drh: }
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh: /*
5cf1206dfa 2008-05-15       drh: ** WEBPAGE: doc
5cf1206dfa 2008-05-15       drh: ** URL: /doc?name=BASELINE/PATH
5cf1206dfa 2008-05-15       drh: **
5cf1206dfa 2008-05-15       drh: ** BASELINE can be either a baseline uuid prefix or magic words "tip"
5cf1206dfa 2008-05-15       drh: ** to me the most recently checked in baseline or "ckout" to mean the
5cf1206dfa 2008-05-15       drh: ** content of the local checkout, if any.  PATH is the relative pathname
5cf1206dfa 2008-05-15       drh: ** of some file.  This method returns the file content.
5cf1206dfa 2008-05-15       drh: **
5cf1206dfa 2008-05-15       drh: ** If PATH matches the patterns *.wiki or *.txt then formatting content
5cf1206dfa 2008-05-15       drh: ** is added before returning the file.  For all other names, the content
5cf1206dfa 2008-05-15       drh: ** is returned straight without any interpretation or processing.
5cf1206dfa 2008-05-15       drh: */
5cf1206dfa 2008-05-15       drh: void doc_page(void){
5cf1206dfa 2008-05-15       drh:   const char *zName;                /* Argument to the /doc page */
5cf1206dfa 2008-05-15       drh:   const char *zMime;                /* Document MIME type */
5cf1206dfa 2008-05-15       drh:   int vid = 0;                      /* Artifact of baseline */
5cf1206dfa 2008-05-15       drh:   int rid = 0;                      /* Artifact of file */
5cf1206dfa 2008-05-15       drh:   int i;                            /* Loop counter */
5cf1206dfa 2008-05-15       drh:   Blob filebody;                    /* Content of the documentation file */
5cf1206dfa 2008-05-15       drh:   char zBaseline[UUID_SIZE+1];      /* Baseline UUID */
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh:   login_check_credentials();
5cf1206dfa 2008-05-15       drh:   if( !g.okRead ){ login_needed(); return; }
5cf1206dfa 2008-05-15       drh:   zName = PD("name", "tip/index.wiki");
5cf1206dfa 2008-05-15       drh:   for(i=0; zName[i] && zName[i]!='/'; i++){}
5cf1206dfa 2008-05-15       drh:   if( zName[i]==0 || i>UUID_SIZE ){
5cf1206dfa 2008-05-15       drh:     goto doc_not_found;
5cf1206dfa 2008-05-15       drh:   }
5cf1206dfa 2008-05-15       drh:   memcpy(zBaseline, zName, i);
5cf1206dfa 2008-05-15       drh:   zBaseline[i] = 0;
5cf1206dfa 2008-05-15       drh:   zName += i;
5cf1206dfa 2008-05-15       drh:   while( zName[0]=='/' ){ zName++; }
5cf1206dfa 2008-05-15       drh:   if( !file_is_simple_pathname(zName) ){
5cf1206dfa 2008-05-15       drh:     goto doc_not_found;
5cf1206dfa 2008-05-15       drh:   }
5cf1206dfa 2008-05-15       drh:   if( strcmp(zBaseline,"ckout")==0 ){
5cf1206dfa 2008-05-15       drh:     /* Read from the local checkout */
5cf1206dfa 2008-05-15       drh:     char *zFullpath;
5cf1206dfa 2008-05-15       drh:     db_must_be_within_tree();
5cf1206dfa 2008-05-15       drh:     zFullpath = mprintf("%s/%s", g.zLocalRoot, zName);
5cf1206dfa 2008-05-15       drh:     if( !file_isfile(zFullpath) ){
5cf1206dfa 2008-05-15       drh:       goto doc_not_found;
5cf1206dfa 2008-05-15       drh:     }
5cf1206dfa 2008-05-15       drh:     if( blob_read_from_file(&filebody, zFullpath)<0 ){
5cf1206dfa 2008-05-15       drh:       goto doc_not_found;
5cf1206dfa 2008-05-15       drh:     }
5cf1206dfa 2008-05-15       drh:   }else{
5cf1206dfa 2008-05-15       drh:     db_begin_transaction();
5cf1206dfa 2008-05-15       drh:     if( strcmp(zBaseline,"tip")==0 ){
5cf1206dfa 2008-05-15       drh:       vid = db_int(0, "SELECT objid FROM event WHERE type='ci'"
5cf1206dfa 2008-05-15       drh:                       " ORDER BY mtime DESC LIMIT 1");
5cf1206dfa 2008-05-15       drh:     }else{
5cf1206dfa 2008-05-15       drh:       vid = name_to_rid(zBaseline);
5cf1206dfa 2008-05-15       drh:     }
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh:     /* Create the baseline cache if it does not already exist */
5cf1206dfa 2008-05-15       drh:     db_multi_exec(
5cf1206dfa 2008-05-15       drh:       "CREATE TABLE IF NOT EXISTS vcache(\n"
5cf1206dfa 2008-05-15       drh:       "  vid INTEGER,         -- baseline ID\n"
5cf1206dfa 2008-05-15       drh:       "  fname TEXT,          -- filename\n"
5cf1206dfa 2008-05-15       drh:       "  rid INTEGER,         -- artifact ID\n"
5cf1206dfa 2008-05-15       drh:       "  UNIQUE(vid,fname,rid)\n"
5cf1206dfa 2008-05-15       drh:       ")"
5cf1206dfa 2008-05-15       drh:     );
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh:     /* Check to see if the documentation file artifact ID is contained
5cf1206dfa 2008-05-15       drh:     ** in the baseline cache */
5cf1206dfa 2008-05-15       drh:     rid = db_int(0, "SELECT rid FROM vcache"
5cf1206dfa 2008-05-15       drh:                     " WHERE vid=%d AND fname=%Q", vid, zName);
5cf1206dfa 2008-05-15       drh:     if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){
5cf1206dfa 2008-05-15       drh:       goto doc_not_found;
5cf1206dfa 2008-05-15       drh:     }
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh:     if( rid==0 ){
5cf1206dfa 2008-05-15       drh:       Stmt s;
5cf1206dfa 2008-05-15       drh:       Blob baseline;
5cf1206dfa 2008-05-15       drh:       Manifest m;
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh:       /* Add the vid baseline to the cache */
5cf1206dfa 2008-05-15       drh:       if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){
5cf1206dfa 2008-05-15       drh:         db_multi_exec("DELETE FROM vcache");
5cf1206dfa 2008-05-15       drh:       }
5cf1206dfa 2008-05-15       drh:       if( content_get(vid, &baseline)==0 ){
5cf1206dfa 2008-05-15       drh:         goto doc_not_found;
5cf1206dfa 2008-05-15       drh:       }
5cf1206dfa 2008-05-15       drh:       if( manifest_parse(&m, &baseline)==0 || m.type!=CFTYPE_MANIFEST ){
5cf1206dfa 2008-05-15       drh:         goto doc_not_found;
5cf1206dfa 2008-05-15       drh:       }
5cf1206dfa 2008-05-15       drh:       db_prepare(&s,
5cf1206dfa 2008-05-15       drh:         "INSERT INTO vcache(vid,fname,rid)"
5cf1206dfa 2008-05-15       drh:         " SELECT %d, :fname, rid FROM blob"
5cf1206dfa 2008-05-15       drh:         "  WHERE uuid=:uuid",
5cf1206dfa 2008-05-15       drh:         vid
5cf1206dfa 2008-05-15       drh:       );
5cf1206dfa 2008-05-15       drh:       for(i=0; i<m.nFile; i++){
5cf1206dfa 2008-05-15       drh:         db_bind_text(&s, ":fname", m.aFile[i].zName);
5cf1206dfa 2008-05-15       drh:         db_bind_text(&s, ":uuid", m.aFile[i].zUuid);
5cf1206dfa 2008-05-15       drh:         db_step(&s);
5cf1206dfa 2008-05-15       drh:         db_reset(&s);
5cf1206dfa 2008-05-15       drh:       }
5cf1206dfa 2008-05-15       drh:       db_finalize(&s);
5cf1206dfa 2008-05-15       drh:       manifest_clear(&m);
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh:       /* Try again to find the file */
5cf1206dfa 2008-05-15       drh:       rid = db_int(0, "SELECT rid FROM vcache"
5cf1206dfa 2008-05-15       drh:                       " WHERE vid=%d AND fname=%Q", vid, zName);
5cf1206dfa 2008-05-15       drh:     }
5cf1206dfa 2008-05-15       drh:     if( rid==0 ){
5cf1206dfa 2008-05-15       drh:       goto doc_not_found;
5cf1206dfa 2008-05-15       drh:     }
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh:     /* Get the file content */
5cf1206dfa 2008-05-15       drh:     if( content_get(rid, &filebody)==0 ){
5cf1206dfa 2008-05-15       drh:       goto doc_not_found;
5cf1206dfa 2008-05-15       drh:     }
5cf1206dfa 2008-05-15       drh:     db_end_transaction(0);
5cf1206dfa 2008-05-15       drh:   }
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh:   /* The file is now contained in the filebody blob.  Deliver the
5cf1206dfa 2008-05-15       drh:   ** file to the user
5cf1206dfa 2008-05-15       drh:   */
5cf1206dfa 2008-05-15       drh:   zMime = mimetype_from_name(zName);
5cf1206dfa 2008-05-15       drh:   if( strcmp(zMime, "application/x-fossil-wiki")==0 ){
5cf1206dfa 2008-05-15       drh:     style_header("Documentation");
5cf1206dfa 2008-05-15       drh:     wiki_convert(&filebody, 0, 0);
5cf1206dfa 2008-05-15       drh:     style_footer();
5cf1206dfa 2008-05-15       drh:   }else if( strcmp(zMime, "text/plain")==0 ){
5cf1206dfa 2008-05-15       drh:     style_header("Documentation");
5cf1206dfa 2008-05-15       drh:     @ <blockquote><pre>
5cf1206dfa 2008-05-15       drh:     @ %h(blob_str(&filebody))
5cf1206dfa 2008-05-15       drh:     @ </pre></blockquote>
5cf1206dfa 2008-05-15       drh:     style_footer();
5cf1206dfa 2008-05-15       drh:   }else{
5cf1206dfa 2008-05-15       drh:     cgi_set_content_type(zMime);
5cf1206dfa 2008-05-15       drh:     cgi_set_content(&filebody);
5cf1206dfa 2008-05-15       drh:   }
5cf1206dfa 2008-05-15       drh:   return;
5cf1206dfa 2008-05-15       drh: 
5cf1206dfa 2008-05-15       drh: doc_not_found:
5cf1206dfa 2008-05-15       drh:   /* Jump here when unable to locate the document */
5cf1206dfa 2008-05-15       drh:   db_end_transaction(0);
5cf1206dfa 2008-05-15       drh:   style_header("Document Not Found");
5cf1206dfa 2008-05-15       drh:   @ <p>No such document: %h(PD("name","tip/index.wiki"))</p>
5cf1206dfa 2008-05-15       drh:   style_footer();
5cf1206dfa 2008-05-15       drh:   return;
5cf1206dfa 2008-05-15       drh: }