/* ** Return an appropriate set of flags for wiki_convert() for displaying ** comments on a timeline. These flag settings are determined by ** configuration parameters. ** ** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2 ** flags) and is false for plain "%W". The ! indicates that the text is ** to be rendered on a form rather than the timeline and that block markup ** is acceptable even if the "timeline-block-markup" setting is false. */ static int wiki_convert_flags(int altForm2){ static int wikiFlags = 0; if( wikiFlags==0 ){ if( altForm2 || db_get_boolean("timeline-block-markup", 0) ){ wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS; }else{ wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS; } if( db_get_boolean("timeline-plaintext", 0) ){ wikiFlags |= WIKI_LINKSONLY; } } return wikiFlags; }
/* ** Make sure the interpreter has been initialized. Initialize it if ** it has not been already. ** ** The interpreter is stored in the g.interp global variable. */ void Th_FossilInit(void){ static struct _Command { const char *zName; Th_CommandProc xProc; void *pContext; } aCommand[] = { {"anycap", anycapCmd, 0}, {"combobox", comboboxCmd, 0}, {"enable_output", enableOutputCmd, 0}, {"linecount", linecntCmd, 0}, {"hascap", hascapCmd, 0}, {"htmlize", htmlizeCmd, 0}, {"date", dateCmd, 0}, {"html", putsCmd, 0}, {"puts", putsCmd, (void*)1}, {"wiki", wikiCmd, 0}, {"repository", repositoryCmd, 0}, {0, 0, 0} }; if( g.interp==0 ){ int i; g.interp = Th_CreateInterp(&vtab); th_register_language(g.interp); /* Basic scripting commands. */ #ifdef FOSSIL_ENABLE_TCL if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){ th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */ } #endif for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){ if ( !aCommand[i].zName || !aCommand[i].xProc ) continue; Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc, aCommand[i].pContext, 0); } } }
/* ** This routine returns the names of files in a working checkout that ** are created by Fossil itself, and hence should not be added, deleted, ** or merge, and should be omitted from "clean" and "extra" lists. ** ** Return the N-th name. The first name has N==0. When all names have ** been used, return 0. */ const char *fossil_reserved_name(int N){ /* Possible names of the local per-checkout database file and ** its associated journals */ static const char *const azName[] = { "_FOSSIL_", "_FOSSIL_-journal", "_FOSSIL_-wal", "_FOSSIL_-shm", ".fslckout", ".fslckout-journal", ".fslckout-wal", ".fslckout-shm", /* The use of ".fos" as the name of the checkout database is ** deprecated. Use ".fslckout" instead. At some point, the following ** entries should be removed. 2012-02-04 */ ".fos", ".fos-journal", ".fos-wal", ".fos-shm", }; /* Names of auxiliary files generated by SQLite when the "manifest" ** properity is enabled */ static const char *const azManifest[] = { "manifest", "manifest.uuid", }; /* Cached setting "manifest" */ static int cachedManifest = -1; if( cachedManifest == -1 ){ cachedManifest = db_get_boolean("manifest",0); } if( N>=0 && N<count(azName) ) return azName[N]; if( N>=count(azName) && N<count(azName)+count(azManifest) && cachedManifest ){ return azManifest[N-count(azName)]; } return 0; }
/* ** This routine determines if files should be case-sensitive or not. ** In other words, this routine determines if two filenames that ** differ only in case should be considered the same name or not. ** ** The case-sensitive setting determines the default value. If ** the case-sensitive setting is undefined, then case sensitivity ** defaults on for Mac and Windows and off for all other unix. ** ** The --case-sensitive BOOLEAN command-line option overrides any ** setting. */ int filenames_are_case_sensitive(void){ static int caseSensitive; static int once = 1; if( once ){ once = 0; if( zCaseSensitive ){ caseSensitive = is_truth(zCaseSensitive); }else{ #if !defined(_WIN32) && !defined(__DARWIN__) && !defined(__APPLE__) caseSensitive = 1; /* Unix */ #else caseSensitive = 0; /* Windows and Mac */ #endif caseSensitive = db_get_boolean("case-sensitive",caseSensitive); } } return caseSensitive; }
/* ** Look at every VFILE entry with the given vid and set update ** VFILE.CHNGED field on every file according to whether or not ** the file has changes. 0 means no change. 1 means edited. 2 means ** the file has changed due to a merge. 3 means the file was added ** by a merge. ** ** If VFILE.DELETED is true or if VFILE.RID is zero, then the file was ** either removed from managemented via "vcs rm" or added via ** "vcs add", respectively, and in both cases we always know that ** the file has changed without having the check the size, mtime, ** or on-disk content. ** ** If the size of the file has changed, then we always know that the file ** changed without having to look at the mtime or on-disk content. ** ** The mtime of the file is only a factor if the mtime-changes setting ** is false and the useSha1sum flag is false. If the mtime-changes ** setting is true (or undefined - it defaults to true) or if useSha1sum ** is true, then we do not trust the mtime and will examine the on-disk ** content to determine if a file really is the same. ** ** If the mtime is used, it is used only to determine if files are the same. ** If the mtime of a file has changed, we still examine the on-disk content ** to see whether or not the edit was a null-edit. */ void vfile_check_signature(int vid, int notFileIsFatal, int useSha1sum){ int nErr = 0; Stmt q; Blob fileCksum, origCksum; int useMtime = useSha1sum==0 && db_get_boolean("mtime-changes", 1); db_begin_transaction(); db_prepare(&q, "SELECT id, %Q || pathname," " vfile.mrid, deleted, chnged, uuid, size, mtime" " FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid" " WHERE vid=%d ", g.zLocalRoot, vid); while( db_step(&q)==SQLITE_ROW ){ int id, rid, isDeleted; const char *zName; int chnged = 0; int oldChnged; i64 oldMtime; i64 currentMtime; i64 origSize; i64 currentSize; id = db_column_int(&q, 0); zName = db_column_text(&q, 1); rid = db_column_int(&q, 2); isDeleted = db_column_int(&q, 3); oldChnged = chnged = db_column_int(&q, 4); oldMtime = db_column_int64(&q, 7); currentSize = file_wd_size(zName); origSize = db_column_int64(&q, 6); currentMtime = file_wd_mtime(0); if( chnged==0 && (isDeleted || rid==0) ){ /* "vcs rm" or "vcs add" always change the file */ chnged = 1; }else if( !file_wd_isfile_or_link(0) && currentSize>=0 ){ if( notFileIsFatal ){ vcs_warning("not an ordinary file: %s", zName); nErr++; } chnged = 1; } if( origSize!=currentSize ){ if( chnged!=1 ){ /* A file size change is definitive - the file has changed. No ** need to check the mtime or sha1sum */ chnged = 1; } }else if( chnged==1 && rid!=0 && !isDeleted ){ /* File is believed to have changed but it is the same size. ** Double check that it really has changed by looking at content. */ assert( origSize==currentSize ); db_ephemeral_blob(&q, 5, &origCksum); if( sha1sum_file(zName, &fileCksum) ){ blob_zero(&fileCksum); } if( blob_compare(&fileCksum, &origCksum)==0 ) chnged = 0; blob_reset(&origCksum); blob_reset(&fileCksum); }else if( chnged==0 && (useMtime==0 || currentMtime!=oldMtime) ){ /* For files that were formerly believed to be unchanged, if their ** mtime changes, or unconditionally if --sha1sum is used, check ** to see if they have been edited by looking at their SHA1 sum */ assert( origSize==currentSize ); db_ephemeral_blob(&q, 5, &origCksum); if( sha1sum_file(zName, &fileCksum) ){ blob_zero(&fileCksum); } if( blob_compare(&fileCksum, &origCksum) ){ chnged = 1; } blob_reset(&origCksum); blob_reset(&fileCksum); } if( currentMtime!=oldMtime || chnged!=oldChnged ){ db_multi_exec("UPDATE vfile SET mtime=%lld, chnged=%d WHERE id=%d", currentMtime, chnged, id); } } db_finalize(&q); if( nErr ) vcs_fatal("abort due to prior errors"); db_end_transaction(0); }
/* ** Main sub-menu for configuring the transfer system. ** WEBPAGE: xfersetup */ void xfersetup_page(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_header("Transfer Setup"); cgi_printf("<table border=\"0\" cellspacing=\"20\">\n"); setup_menu_entry("Common", "xfersetup_com", "Common TH1 code run before all transfer request processing."); setup_menu_entry("Push", "xfersetup_push", "Specific TH1 code to run after \"push\" transfer requests."); setup_menu_entry("Commit", "xfersetup_commit", "Specific TH1 code to run after processing a commit."); setup_menu_entry("Ticket", "xfersetup_ticket", "Specific TH1 code to run after processing a ticket change."); cgi_printf("</table>\n"); url_parse(0, 0); if( g.url.protocol ){ unsigned syncFlags; const char *zButton; char *zWarning; if( db_get_boolean("dont-push", 0) ){ syncFlags = SYNC_PULL; zButton = "Pull"; zWarning = 0; }else{ syncFlags = SYNC_PUSH | SYNC_PULL; zButton = "Synchronize"; zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.", g.url.canonical); } if( P("sync") ){ user_select(); url_enable_proxy(0); client_sync(syncFlags, 0, 0); } cgi_printf("<p>Press the %h button below to synchronize with the\n" "\"%h\" repository now. This may be useful when\n" "testing the various transfer scripts.</p>\n" "<p>You can use the \"http -async\" command in your scripts, but\n" "make sure the \"th1-uri-regexp\" setting is set first.</p>\n",(zButton),(g.url.canonical)); if( zWarning ){ cgi_printf("\n" "<big><b>%h</b></big>\n",(zWarning)); free(zWarning); } cgi_printf("\n" "<blockquote>\n" "<form method=\"post\" action=\"%s/%s\"><div>\n",(g.zTop),(g.zPath)); login_insert_csrf_secret(); cgi_printf("<input type=\"submit\" name=\"sync\" value=\"%h\" />\n" "</div></form>\n" "</blockquote>\n" "\n",(zButton)); } style_footer(); }
/* ** Given the RID for a checkin, construct a tarball containing ** all files in that checkin ** ** If RID is for an object that is not a real manifest, then the ** resulting tarball contains a single file which is the RID ** object. ** ** If the RID object does not exist in the repository, then ** pTar is zeroed. ** ** zDir is a "synthetic" subdirectory which all files get ** added to as part of the tarball. It may be 0 or an empty string, in ** which case it is ignored. The intention is to create a tarball which ** politely expands into a subdir instead of filling your current dir ** with source files. For example, pass a UUID or "ProjectName". ** */ void tarball_of_checkin(int rid, Blob *pTar, const char *zDir){ Blob mfile, hash, file; Manifest *pManifest; ManifestFile *pFile; Blob filename; int nPrefix; char *zName; unsigned int mTime; content_get(rid, &mfile); if( blob_size(&mfile)==0 ){ blob_zero(pTar); return; } blob_zero(&hash); blob_zero(&filename); if( zDir && zDir[0] ){ blob_appendf(&filename, "%s/", zDir); } nPrefix = blob_size(&filename); pManifest = manifest_get(rid, CFTYPE_MANIFEST); if( pManifest ){ mTime = (pManifest->rDate - 2440587.5)*86400.0; tar_begin(mTime); if( db_get_boolean("manifest", 0) ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); tar_add_file(zName, &mfile, 0, mTime); sha1sum_blob(&mfile, &hash); blob_reset(&mfile); blob_append(&hash, "\n", 1); blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.uuid", -1); zName = blob_str(&filename); tar_add_file(zName, &hash, 0, mTime); blob_reset(&hash); } manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ int fid = uuid_to_rid(pFile->zUuid, 0); if( fid ){ content_get(fid, &file); blob_resize(&filename, nPrefix); blob_append(&filename, pFile->zName, -1); zName = blob_str(&filename); tar_add_file(zName, &file, manifest_file_mperm(pFile), mTime); blob_reset(&file); } } }else{ sha1sum_blob(&mfile, &hash); blob_append(&filename, blob_str(&hash), 16); zName = blob_str(&filename); mTime = db_int64(0, "SELECT (julianday('now') - 2440587.5)*86400.0;"); tar_begin(mTime); tar_add_file(zName, &mfile, 0, mTime); } manifest_destroy(pManifest); blob_reset(&mfile); blob_reset(&filename); tar_finish(pTar); }
/* ** Subscript command: submit_ticket ** ** Construct and submit a new ticket artifact. The fields of the artifact ** are the names of the columns in the TICKET table. The content is ** taken from TH variables. If the content is unchanged, the field is ** omitted from the artifact. Fields whose names begin with "private_" ** are concealed using the db_conceal() function. */ static int submitTicketCmd( Th_Interp *interp, void *pUuid, int argc, const char **argv, int *argl ){ char *zDate; const char *zUuid; int i; int nJ = 0; Blob tktchng, cksum; login_verify_csrf_secret(); if( !captcha_is_correct() ){ cgi_printf("<p class=\"generalError\">Error: Incorrect security code.</p>\n"); return TH_OK; } zUuid = (const char *)pUuid; blob_zero(&tktchng); zDate = date_in_standard_format("now"); blob_appendf(&tktchng, "D %s\n", zDate); free(zDate); for(i=0; i<nField; i++){ if( aField[i].zAppend ){ blob_appendf(&tktchng, "J +%s %z\n", aField[i].zName, fossilize(aField[i].zAppend, -1)); ++nJ; } } for(i=0; i<nField; i++){ const char *zValue; int nValue; if( aField[i].zAppend ) continue; zValue = Th_Fetch(aField[i].zName, &nValue); if( zValue ){ while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0) || memcmp(zValue, aField[i].zValue, nValue)!=0 || strlen(aField[i].zValue)!=nValue ){ if( memcmp(aField[i].zName, "private_", 8)==0 ){ zValue = db_conceal(zValue, nValue); blob_appendf(&tktchng, "J %s %s\n", aField[i].zName, zValue); }else{ blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue); } nJ++; } } } if( *(char**)pUuid ){ zUuid = db_text(0, "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%q*'", P("name") ); }else{ zUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); } *(const char**)pUuid = zUuid; blob_appendf(&tktchng, "K %s\n", zUuid); blob_appendf(&tktchng, "U %F\n", g.zLogin ? g.zLogin : ""); md5sum_blob(&tktchng, &cksum); blob_appendf(&tktchng, "Z %b\n", &cksum); if( nJ==0 ){ blob_reset(&tktchng); return TH_OK; } if( g.zPath[0]=='d' ){ /* If called from /debug_tktnew or /debug_tktedit... */ cgi_printf("<font color=\"blue\">\n" "<p>Ticket artifact that would have been submitted:</p>\n" "<blockquote><pre>%h</pre></blockquote>\n" "<hr /></font>\n",(blob_str(&tktchng))); return TH_OK; }else{ if( g.thTrace ){ Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" "}<br />\n", blob_str(&tktchng)); } ticket_put(&tktchng, zUuid, (g.perm.ModTkt==0 && db_get_boolean("modreq-tkt",0)==1)); } return ticket_change(); }