/* ** Query the database for all TICKET fields for the specific ** ticket whose name is given by the "name" CGI parameter. ** Load the values for all fields into the interpreter. ** ** Only load those fields which do not already exist as ** variables. ** ** Fields of the TICKET table that begin with "private_" are ** expanded using the db_reveal() function. If g.perm.RdAddr is ** true, then the db_reveal() function will decode the content ** using the CONCEALED table so that the content legible. ** Otherwise, db_reveal() is a no-op and the content remains ** obscured. */ static void initializeVariablesFromDb(void){ const char *zName; Stmt q; int i, n, size, j; zName = PD("name","-none-"); db_prepare(&q, "SELECT datetime(tkt_mtime%s) AS tkt_datetime, *" " FROM ticket WHERE tkt_uuid GLOB '%q*'", timeline_utc(), zName); if( db_step(&q)==SQLITE_ROW ){ n = db_column_count(&q); for(i=0; i<n; i++){ const char *zVal = db_column_text(&q, i); const char *zName = db_column_name(&q, i); char *zRevealed = 0; if( zVal==0 ){ zVal = ""; }else if( strncmp(zName, "private_", 8)==0 ){ zVal = zRevealed = db_reveal(zVal); } if( (j = fieldId(zName))>=0 ){ aField[j].zValue = mprintf("%s", zVal); }else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){ Th_Store(zName, zVal); } free(zRevealed); } } db_finalize(&q); for(i=0; i<nField; i++){ if( Th_Fetch(aField[i].zName, &size)==0 ){ Th_Store(aField[i].zName, aField[i].zValue); } } }
/* ** WEBPAGE: tktedit ** WEBPAGE: debug_tktedit ** ** Edit a ticket. The ticket is identified by the name CGI parameter. ** /tktedit is the official page. The /debug_tktedit page does the same ** thing except that it does not save the ticket change record when you ** press submit - it instead prints the ticket change record at the top ** of the page. The /debug_tktedit page is intended to be used when ** debugging ticket configurations. */ void tktedit_page(void){ const char *zScript; int nName; const char *zName; int nRec; login_check_credentials(); if( !g.perm.ApndTkt && !g.perm.WrTkt ){ login_needed(); return; } zName = P("name"); if( P("cancel") ){ cgi_redirectf("tktview?name=%T", zName); } style_header("Edit Ticket"); if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE || !validate16(zName,nName) ){ cgi_printf("<span class=\"tktError\">Not a valid ticket id: \\\"%h\\\"</span>\n",(zName)); style_footer(); return; } nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'", zName); if( nRec==0 ){ cgi_printf("<span class=\"tktError\">No such ticket: \\\"%h\\\"</span>\n",(zName)); style_footer(); return; } if( nRec>1 ){ cgi_printf("<span class=\"tktError\">%d tickets begin with:\n" "\\\"%h\\\"</span>\n",(nRec),(zName)); style_footer(); return; } if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); ticket_init(); getAllTicketFields(); initializeVariablesFromCGI(); initializeVariablesFromDb(); if( g.zPath[0]=='d' ) showAllFields(); form_begin(0, "%R/%s", g.zPath); cgi_printf("<input type=\"hidden\" name=\"name\" value=\"%s\" />\n",(zName)); login_insert_csrf_secret(); zScript = ticket_editpage_code(); Th_Store("login", g.zLogin ? g.zLogin : "******"); Th_Store("date", db_text(0, "SELECT datetime('now')")); Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1); if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName)); return; } captcha_generate(0); cgi_printf("</form>\n"); if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); style_footer(); }
/* ** Query the database for all TICKET fields for the specific ** ticket whose name is given by the "name" CGI parameter. ** Load the values for all fields into the interpreter. ** ** Only load those fields which do not already exist as ** variables. ** ** Fields of the TICKET table that begin with "private_" are ** expanded using the db_reveal() function. If g.perm.RdAddr is ** true, then the db_reveal() function will decode the content ** using the CONCEALED table so that the content legable. ** Otherwise, db_reveal() is a no-op and the content remains ** obscured. */ static void initializeVariablesFromDb(void){ const char *zName; Stmt q; int i, n, size, j; zName = PD("name","-none-"); db_prepare(&q, "SELECT datetime(tkt_mtime,'localtime') AS tkt_datetime, *" " FROM ticket WHERE tkt_uuid GLOB '%q*'", zName); if( db_step(&q)==SQLITE_ROW ){ n = db_column_count(&q); for(i=0; i<n; i++){ const char *zVal = db_column_text(&q, i); const char *zName = db_column_name(&q, i); char *zRevealed = 0; if( zVal==0 ){ zVal = ""; }else if( strncmp(zName, "private_", 8)==0 ){ zVal = zRevealed = db_reveal(zVal); } for(j=0; j<nField; j++){ if( fossil_strcmp(azField[j],zName)==0 ){ azValue[j] = mprintf("%s", zVal); break; } } if( Th_Fetch(zName, &size)==0 ){ Th_Store(zName, zVal); } free(zRevealed); } }else{ db_finalize(&q); db_prepare(&q, "PRAGMA table_info(ticket)"); if( Th_Fetch("tkt_uuid",&size)==0 ){ Th_Store("tkt_uuid",zName); } while( db_step(&q)==SQLITE_ROW ){ const char *zField = db_column_text(&q, 1); if( Th_Fetch(zField, &size)==0 ){ Th_Store(zField, ""); } } if( Th_Fetch("tkt_datetime",&size)==0 ){ Th_Store("tkt_datetime",""); } } db_finalize(&q); }
/* ** Transfer all CGI parameters to variables in the interpreter. */ static void initializeVariablesFromCGI(void){ int i; const char *z; for(i=0; (z = cgi_parameter_name(i))!=0; i++){ Th_Store(z, P(z)); } }
/* ** WEBPAGE: tktnew ** WEBPAGE: debug_tktnew ** ** Enter a new ticket. The tktnew_template script in the ticket ** configuration is used. The /tktnew page is the official ticket ** entry page. The /debug_tktnew page is used for debugging the ** tktnew_template in the ticket configuration. /debug_tktnew works ** just like /tktnew except that it does not really save the new ticket ** when you press submit - it just prints the ticket artifact at the ** top of the screen. */ void tktnew_page(void){ const char *zScript; char *zNewUuid = 0; login_check_credentials(); if( !g.perm.NewTkt ){ login_needed(); return; } if( P("cancel") ){ cgi_redirect("home"); } style_header("New Ticket"); if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1); ticket_init(); initializeVariablesFromCGI(); getAllTicketFields(); initializeVariablesFromDb(); if( g.zPath[0]=='d' ) showAllFields(); form_begin(0, "%R/%s", g.zPath); login_insert_csrf_secret(); if( P("date_override") && g.perm.Setup ){ cgi_printf("<input type=\"hidden\" name=\"date_override\" value=\"%h\">\n",(P("date_override"))); } zScript = ticket_newpage_code(); Th_Store("login", g.zLogin ? g.zLogin : "******"); Th_Store("date", db_text(0, "SELECT datetime('now')")); Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zNewUuid, 0); if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1); if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid)); return; } captcha_generate(0); cgi_printf("</form>\n"); if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); style_footer(); }
/* ** Draw the header. */ void style_header(const char *zTitleFormat, ...){ va_list ap; char *zTitle; const char *zHeader = db_get("header", (char*)zDefaultHeader); login_check_credentials(); va_start(ap, zTitleFormat); zTitle = vmprintf(zTitleFormat, ap); va_end(ap); cgi_destination(CGI_HEADER); cgi_printf("%s","<!DOCTYPE html>"); if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1); /* Generate the header up through the main menu */ Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); Th_Store("title", zTitle); Th_Store("baseurl", g.zBaseURL); Th_Store("home", g.zTop); Th_Store("index_page", db_get("index-page","/home")); Th_Store("current_page", g.zPath); Th_Store("release_version", RELEASE_VERSION); Th_Store("manifest_version", MANIFEST_VERSION); Th_Store("manifest_date", MANIFEST_DATE); Th_Store("compiler_name", COMPILER_NAME); if( g.zLogin ){ Th_Store("login", g.zLogin); } if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1); Th_Render(zHeader); if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1); Th_Unstore("title"); /* Avoid collisions with ticket field names */ cgi_destination(CGI_BODY); g.cgiOutput = 1; headerHasBeenGenerated = 1; sideboxUsed = 0; }
/* ** WEBPAGE: doc ** URL: /doc?name=BASELINE/PATH ** URL: /doc/BASELINE/PATH ** ** BASELINE can be either a baseline uuid prefix or magic words "tip" ** to mean the most recently checked in baseline or "ckout" to mean the ** content of the local checkout, if any. PATH is the relative pathname ** of some file. This method returns the file content. ** ** If PATH matches the patterns *.wiki or *.txt then formatting content ** is added before returning the file. For all other names, the content ** is returned straight without any interpretation or processing. */ void doc_page(void){ const char *zName; /* Argument to the /doc page */ const char *zMime; /* Document MIME type */ int vid = 0; /* Artifact of baseline */ int rid = 0; /* Artifact of file */ int i; /* Loop counter */ Blob filebody; /* Content of the documentation file */ char zBaseline[UUID_SIZE+1]; /* Baseline UUID */ login_check_credentials(); if( !g.perm.Read ){ login_needed(); return; } zName = PD("name", "tip/index.wiki"); for(i=0; zName[i] && zName[i]!='/'; i++){} if( zName[i]==0 || i>UUID_SIZE ){ zName = "index.html"; goto doc_not_found; } memcpy(zBaseline, zName, i); zBaseline[i] = 0; zName += i; while( zName[0]=='/' ){ zName++; } if( !file_is_simple_pathname(zName) ){ int n = strlen(zName); if( n>0 && zName[n-1]=='/' ){ zName = mprintf("%sindex.html", zName); if( !file_is_simple_pathname(zName) ){ goto doc_not_found; } }else{ goto doc_not_found; } } if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){ sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); } if( fossil_strcmp(zBaseline,"ckout")==0 ){ /* Read from the local checkout */ char *zFullpath; db_must_be_within_tree(); zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); if( !file_isfile(zFullpath) ){ goto doc_not_found; } if( blob_read_from_file(&filebody, zFullpath)<0 ){ goto doc_not_found; } }else{ db_begin_transaction(); if( fossil_strcmp(zBaseline,"tip")==0 ){ vid = db_int(0, "SELECT objid FROM event WHERE type='ci'" " ORDER BY mtime DESC LIMIT 1"); }else{ vid = name_to_typed_rid(zBaseline, "ci"); } /* Create the baseline cache if it does not already exist */ db_multi_exec( "CREATE TABLE IF NOT EXISTS vcache(\n" " vid INTEGER, -- baseline ID\n" " fname TEXT, -- filename\n" " rid INTEGER, -- artifact ID\n" " UNIQUE(vid,fname,rid)\n" ")" ); /* Check to see if the documentation file artifact ID is contained ** in the baseline cache */ rid = db_int(0, "SELECT rid FROM vcache" " WHERE vid=%d AND fname=%Q", vid, zName); if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ goto doc_not_found; } if( rid==0 ){ Stmt s; Manifest *pM; ManifestFile *pFile; /* Add the vid baseline to the cache */ if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){ db_multi_exec("DELETE FROM vcache"); } pM = manifest_get(vid, CFTYPE_MANIFEST); if( pM==0 ){ goto doc_not_found; } db_prepare(&s, "INSERT INTO vcache(vid,fname,rid)" " SELECT %d, :fname, rid FROM blob" " WHERE uuid=:uuid", vid ); manifest_file_rewind(pM); while( (pFile = manifest_file_next(pM,0))!=0 ){ db_bind_text(&s, ":fname", pFile->zName); db_bind_text(&s, ":uuid", pFile->zUuid); db_step(&s); db_reset(&s); } db_finalize(&s); manifest_destroy(pM); /* Try again to find the file */ rid = db_int(0, "SELECT rid FROM vcache" " WHERE vid=%d AND fname=%Q", vid, zName); } if( rid==0 ){ goto doc_not_found; } /* Get the file content */ if( content_get(rid, &filebody)==0 ){ goto doc_not_found; } db_end_transaction(0); } /* The file is now contained in the filebody blob. Deliver the ** file to the user */ zMime = P("mimetype"); if( zMime==0 ){ zMime = mimetype_from_name(zName); } Th_Store("doc_name", zName); Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" " FROM blob WHERE rid=%d", vid)); Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" " WHERE objid=%d AND type='ci'", vid)); if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){ Blob title, tail; if( wiki_find_title(&filebody, &title, &tail) ){ style_header(blob_str(&title)); wiki_convert(&tail, 0, 0); }else{ style_header("Documentation"); wiki_convert(&filebody, 0, 0); } style_footer(); }else if( fossil_strcmp(zMime, "text/plain")==0 ){ style_header("Documentation"); @ <blockquote><pre>