Fossil SCM

fossil-scm / src / th_main.c
Source Blame History 3323 lines
588bb7c… aku 1 /*
c19f34c… drh 2 ** Copyright (c) 2008 D. Richard Hipp
588bb7c… aku 3 **
588bb7c… aku 4 ** This program is free software; you can redistribute it and/or
c06edd2… drh 5 ** modify it under the terms of the Simplified BSD License (also
c06edd2… drh 6 ** known as the "2-Clause License" or "FreeBSD License".)
c06edd2… drh 7
588bb7c… aku 8 ** This program is distributed in the hope that it will be useful,
c06edd2… drh 9 ** but without any warranty; without even the implied warranty of
c06edd2… drh 10 ** merchantability or fitness for a particular purpose.
588bb7c… aku 11 **
588bb7c… aku 12 ** Author contact information:
588bb7c… aku 13 ** [email protected]
588bb7c… aku 14 ** http://www.hwaci.com/drh/
588bb7c… aku 15 **
588bb7c… aku 16 *******************************************************************************
588bb7c… aku 17 **
588bb7c… aku 18 ** This file contains an interface between the TH scripting language
588bb7c… aku 19 ** (an independent project) and fossil.
588bb7c… aku 20 */
588bb7c… aku 21 #include "config.h"
588bb7c… aku 22 #include "th_main.h"
4f8c897… drh 23 #include "sqlite3.h"
588bb7c… aku 24
0e68620… mistachkin 25 #if INTERFACE
0e68620… mistachkin 26 /*
0e68620… mistachkin 27 ** Flag parameters to the Th_FossilInit() routine used to control the
0e68620… mistachkin 28 ** interpreter creation and initialization process.
0e68620… mistachkin 29 */
0e68620… mistachkin 30 #define TH_INIT_NONE ((u32)0x00000000) /* No flags. */
0e68620… mistachkin 31 #define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */
0e68620… mistachkin 32 #define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */
a17919a… mistachkin 33 #define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */
0e68620… mistachkin 34 #define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */
57d8a71… mistachkin 35 #define TH_INIT_NO_REPO ((u32)0x00000010) /* Skip opening repository. */
9164a5d… drh 36 #define TH_INIT_MASK ((u32)0x0000001F) /* All possible init flags. */
3355835… mistachkin 37
3355835… mistachkin 38 /*
3355835… mistachkin 39 ** Useful and/or "well-known" combinations of flag values.
3355835… mistachkin 40 */
3355835… mistachkin 41 #define TH_INIT_DEFAULT (TH_INIT_NONE) /* Default flags. */
1c528d3… mistachkin 42 #define TH_INIT_HOOK (TH_INIT_NEED_CONFIG | TH_INIT_FORCE_SETUP)
3355835… mistachkin 43 #define TH_INIT_FORBID_MASK (TH_INIT_FORCE_TCL) /* Illegal from a script. */
1c528d3… mistachkin 44 #endif
142200b… mistachkin 45
142200b… mistachkin 46 /*
142200b… mistachkin 47 ** Flags set by functions in this file to keep track of integration state
142200b… mistachkin 48 ** information. These flags should not be used outside of this file.
142200b… mistachkin 49 */
a5f00e0… stephan 50 #define TH_STATE_CONFIG ((u32)0x00000200) /* We opened the config. */
a5f00e0… stephan 51 #define TH_STATE_REPOSITORY ((u32)0x00000400) /* We opened the repository. */
a5f00e0… stephan 52 #define TH_STATE_MASK ((u32)0x00000600) /* All possible state flags. */
1c528d3… mistachkin 53
1c528d3… mistachkin 54 #ifdef FOSSIL_ENABLE_TH1_HOOKS
1c528d3… mistachkin 55 /*
1c528d3… mistachkin 56 ** These are the "well-known" TH1 error messages that occur when no hook is
1c528d3… mistachkin 57 ** registered to be called prior to executing a command or processing a web
1c528d3… mistachkin 58 ** page, respectively. If one of these errors is seen, it will not be sent
1c528d3… mistachkin 59 ** or displayed to the remote user or local interactive user, respectively.
1c528d3… mistachkin 60 */
1c528d3… mistachkin 61 #define NO_COMMAND_HOOK_ERROR "no such command: command_hook"
1c528d3… mistachkin 62 #define NO_WEBPAGE_HOOK_ERROR "no such command: webpage_hook"
0e68620… mistachkin 63 #endif
0e68620… mistachkin 64
1c528d3… mistachkin 65 /*
b727218… drh 66 ** These macros are used within this file to detect if the repository and
b727218… drh 67 ** configuration ("user") database are currently open.
b727218… drh 68 */
b727218… drh 69 #define Th_IsRepositoryOpen() (g.repositoryOpen)
b727218… drh 70 #define Th_IsConfigOpen() (g.zConfigDbName!=0)
b727218… drh 71
999e33c… mistachkin 72 /*
999e33c… mistachkin 73 ** When memory debugging is enabled, use our custom memory allocator.
999e33c… mistachkin 74 */
999e33c… mistachkin 75 #if defined(TH_MEMDEBUG)
999e33c… mistachkin 76 /*
999e33c… mistachkin 77 ** Global variable counting the number of outstanding calls to malloc()
999e33c… mistachkin 78 ** made by the th1 implementation. This is used to catch memory leaks
e2bdc10… danield 79 ** in the interpreter. Obviously, it also means th1 is not thread-safe.
999e33c… mistachkin 80 */
999e33c… mistachkin 81 static int nOutstandingMalloc = 0;
999e33c… mistachkin 82
999e33c… mistachkin 83 /*
999e33c… mistachkin 84 ** Implementations of malloc() and free() to pass to the interpreter.
999e33c… mistachkin 85 */
999e33c… mistachkin 86 static void *xMalloc(unsigned int n){
999e33c… mistachkin 87 void *p = fossil_malloc(n);
999e33c… mistachkin 88 if( p ){
999e33c… mistachkin 89 nOutstandingMalloc++;
999e33c… mistachkin 90 }
999e33c… mistachkin 91 return p;
999e33c… mistachkin 92 }
999e33c… mistachkin 93 static void xFree(void *p){
999e33c… mistachkin 94 if( p ){
999e33c… mistachkin 95 nOutstandingMalloc--;
999e33c… mistachkin 96 }
999e33c… mistachkin 97 free(p);
999e33c… mistachkin 98 }
999e33c… mistachkin 99 static Th_Vtab vtab = { xMalloc, xFree };
999e33c… mistachkin 100
999e33c… mistachkin 101 /*
999e33c… mistachkin 102 ** Returns the number of outstanding TH1 memory allocations.
999e33c… mistachkin 103 */
999e33c… mistachkin 104 int Th_GetOutstandingMalloc(){
999e33c… mistachkin 105 return nOutstandingMalloc;
999e33c… mistachkin 106 }
999e33c… mistachkin 107 #endif
60acb31… mistachkin 108
60acb31… mistachkin 109 /*
f55c6a1… drh 110 ** Generate a TH1 trace message if debugging is enabled.
f55c6a1… drh 111 */
f55c6a1… drh 112 void Th_Trace(const char *zFormat, ...){
f55c6a1… drh 113 va_list ap;
f55c6a1… drh 114 va_start(ap, zFormat);
f55c6a1… drh 115 blob_vappendf(&g.thLog, zFormat, ap);
f55c6a1… drh 116 va_end(ap);
f55c6a1… drh 117 }
f55c6a1… drh 118
0264475… mistachkin 119 /*
c1915c3… mistachkin 120 ** Forces input and output to be done via the CGI subsystem.
c1915c3… mistachkin 121 */
c1915c3… mistachkin 122 void Th_ForceCgi(int fullHttpReply){
c1915c3… mistachkin 123 g.httpOut = stdout;
c1915c3… mistachkin 124 g.httpIn = stdin;
c1915c3… mistachkin 125 fossil_binary_mode(g.httpOut);
c1915c3… mistachkin 126 fossil_binary_mode(g.httpIn);
c1915c3… mistachkin 127 g.cgiOutput = 1;
c1915c3… mistachkin 128 g.fullHttpReply = fullHttpReply;
c1915c3… mistachkin 129 }
c1915c3… mistachkin 130
c1915c3… mistachkin 131 /*
0264475… mistachkin 132 ** Checks if the TH1 trace log needs to be enabled. If so, prepares
0264475… mistachkin 133 ** it for use.
0264475… mistachkin 134 */
0264475… mistachkin 135 void Th_InitTraceLog(){
0264475… mistachkin 136 g.thTrace = find_option("th-trace", 0, 0)!=0;
0264475… mistachkin 137 if( g.thTrace ){
a588e55… mistachkin 138 g.fAnyTrace = 1;
0264475… mistachkin 139 blob_zero(&g.thLog);
0264475… mistachkin 140 }
0264475… mistachkin 141 }
0264475… mistachkin 142
0264475… mistachkin 143 /*
0264475… mistachkin 144 ** Prints the entire contents of the TH1 trace log to the standard
0264475… mistachkin 145 ** output channel.
0264475… mistachkin 146 */
0264475… mistachkin 147 void Th_PrintTraceLog(){
0264475… mistachkin 148 if( g.thTrace ){
0264475… mistachkin 149 fossil_print("\n------------------ BEGIN TRACE LOG ------------------\n");
0264475… mistachkin 150 fossil_print("%s", blob_str(&g.thLog));
0264475… mistachkin 151 fossil_print("\n------------------- END TRACE LOG -------------------\n");
f4ceace… mistachkin 152 }
f4ceace… mistachkin 153 }
f4ceace… mistachkin 154
f4ceace… mistachkin 155 /*
f677d53… danield 156 ** - adapted from ls_cmd_rev in checkin.c
f677d53… danield 157 ** - adapted commands/error handling for usage within th1
f677d53… danield 158 ** - interface adapted to allow result creation as TH1 List
f4ceace… mistachkin 159 **
bc36fdc… danield 160 ** Takes a check-in identifier in zRev and an optiona glob pattern in zGLOB
f4ceace… mistachkin 161 ** as parameter returns a TH list in pzList,pnList with filenames matching
f4ceace… mistachkin 162 ** glob pattern with the checking
f4ceace… mistachkin 163 */
f4ceace… mistachkin 164 static void dir_cmd_rev(
f4ceace… mistachkin 165 Th_Interp *interp,
f4ceace… mistachkin 166 char **pzList,
f4ceace… mistachkin 167 int *pnList,
f4ceace… mistachkin 168 const char *zRev, /* Revision string given */
f4ceace… mistachkin 169 const char *zGlob, /* Glob pattern given */
f4ceace… mistachkin 170 int bDetails
f4ceace… mistachkin 171 ){
f4ceace… mistachkin 172 Stmt q;
f4ceace… mistachkin 173 char *zOrderBy = "pathname COLLATE nocase";
f4ceace… mistachkin 174 int rid;
f4ceace… mistachkin 175
f4ceace… mistachkin 176 rid = th1_name_to_typed_rid(interp, zRev, "ci");
f4ceace… mistachkin 177 compute_fileage(rid, zGlob);
f4ceace… mistachkin 178 db_prepare(&q,
ea63a2d… drh 179 "SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n"
f4ceace… mistachkin 180 " blob.size\n"
f4ceace… mistachkin 181 " FROM fileage, blob\n"
f4ceace… mistachkin 182 " WHERE blob.rid=fileage.fid \n"
f4ceace… mistachkin 183 " ORDER BY %s;", zOrderBy /*safe-for-%s*/
f4ceace… mistachkin 184 );
f4ceace… mistachkin 185 while( db_step(&q)==SQLITE_ROW ){
f4ceace… mistachkin 186 const char *zFile = db_column_text(&q, 1);
f4ceace… mistachkin 187 if( bDetails ){
f4ceace… mistachkin 188 const char *zTime = db_column_text(&q, 0);
f4ceace… mistachkin 189 int size = db_column_int(&q, 2);
f4ceace… mistachkin 190 char zSize[50];
f4ceace… mistachkin 191 char *zSubList = 0;
f4ceace… mistachkin 192 int nSubList = 0;
f4ceace… mistachkin 193 sqlite3_snprintf(sizeof(zSize), zSize, "%d", size);
f4ceace… mistachkin 194 Th_ListAppend(interp, &zSubList, &nSubList, zFile, -1);
f4ceace… mistachkin 195 Th_ListAppend(interp, &zSubList, &nSubList, zSize, -1);
f4ceace… mistachkin 196 Th_ListAppend(interp, &zSubList, &nSubList, zTime, -1);
f4ceace… mistachkin 197 Th_ListAppend(interp, pzList, pnList, zSubList, -1);
f4ceace… mistachkin 198 Th_Free(interp, zSubList);
f4ceace… mistachkin 199 }else{
f4ceace… mistachkin 200 Th_ListAppend(interp, pzList, pnList, zFile, -1);
f4ceace… mistachkin 201 }
f4ceace… mistachkin 202 }
f4ceace… mistachkin 203 db_finalize(&q);
f4ceace… mistachkin 204 }
f4ceace… mistachkin 205
f4ceace… mistachkin 206 /*
f4ceace… mistachkin 207 ** TH1 command: dir CHECKIN ?GLOB? ?DETAILS?
f4ceace… mistachkin 208 **
f4ceace… mistachkin 209 ** Returns a list containing all files in CHECKIN. If GLOB is given only
f4ceace… mistachkin 210 ** the files matching the pattern GLOB within CHECKIN will be returned.
f4ceace… mistachkin 211 ** If DETAILS is non-zero, the result will be a list-of-lists, with each
f4ceace… mistachkin 212 ** element containing at least three elements: the file name, the file
f4ceace… mistachkin 213 ** size (in bytes), and the file last modification time (relative to the
f4ceace… mistachkin 214 ** time zone configured for the repository).
f4ceace… mistachkin 215 */
f4ceace… mistachkin 216 static int dirCmd(
f4ceace… mistachkin 217 Th_Interp *interp,
f4ceace… mistachkin 218 void *ctx,
f4ceace… mistachkin 219 int argc,
f4ceace… mistachkin 220 const char **argv,
f4ceace… mistachkin 221 int *argl
f4ceace… mistachkin 222 ){
f4ceace… mistachkin 223 const char *zGlob = 0;
f4ceace… mistachkin 224 int bDetails = 0;
f4ceace… mistachkin 225
f4ceace… mistachkin 226 if( argc<2 || argc>4 ){
f4ceace… mistachkin 227 return Th_WrongNumArgs(interp, "dir CHECKIN ?GLOB? ?DETAILS?");
f4ceace… mistachkin 228 }
f4ceace… mistachkin 229 if( argc>=3 ){
f4ceace… mistachkin 230 zGlob = argv[2];
f4ceace… mistachkin 231 }
f4ceace… mistachkin 232 if( argc>=4 && Th_ToInt(interp, argv[3], argl[3], &bDetails) ){
f4ceace… mistachkin 233 return TH_ERROR;
f4ceace… mistachkin 234 }
f4ceace… mistachkin 235 if( Th_IsRepositoryOpen() ){
f4ceace… mistachkin 236 char *zList = 0;
f4ceace… mistachkin 237 int nList = 0;
f4ceace… mistachkin 238 dir_cmd_rev(interp, &zList, &nList, argv[1], zGlob, bDetails);
f4ceace… mistachkin 239 Th_SetResult(interp, zList, nList);
f4ceace… mistachkin 240 Th_Free(interp, zList);
f4ceace… mistachkin 241 return TH_OK;
f4ceace… mistachkin 242 }else{
f4ceace… mistachkin 243 Th_SetResult(interp, "repository unavailable", -1);
f4ceace… mistachkin 244 return TH_ERROR;
b000db4… mistachkin 245 }
b000db4… mistachkin 246 }
b000db4… mistachkin 247
b000db4… mistachkin 248 /*
109d8f5… mistachkin 249 ** TH1 command: httpize STRING
fc6bb93… jan.nijtmans 250 **
fc6bb93… jan.nijtmans 251 ** Escape all characters of STRING which have special meaning in URI
fc6bb93… jan.nijtmans 252 ** components. Return a new string result.
fc6bb93… jan.nijtmans 253 */
fc6bb93… jan.nijtmans 254 static int httpizeCmd(
b000db4… mistachkin 255 Th_Interp *interp,
b000db4… mistachkin 256 void *p,
b000db4… mistachkin 257 int argc,
b000db4… mistachkin 258 const char **argv,
fc6bb93… jan.nijtmans 259 int *argl
fc6bb93… jan.nijtmans 260 ){
fc6bb93… jan.nijtmans 261 char *zOut;
fc6bb93… jan.nijtmans 262 if( argc!=2 ){
fc6bb93… jan.nijtmans 263 return Th_WrongNumArgs(interp, "httpize STRING");
fc6bb93… jan.nijtmans 264 }
2116238… drh 265 zOut = httpize((char*)argv[1], TH1_LEN(argl[1]));
fc6bb93… jan.nijtmans 266 Th_SetResult(interp, zOut, -1);
fc6bb93… jan.nijtmans 267 free(zOut);
fc6bb93… jan.nijtmans 268 return TH_OK;
fc6bb93… jan.nijtmans 269 }
fc6bb93… jan.nijtmans 270
fc6bb93… jan.nijtmans 271 /*
588bb7c… aku 272 ** True if output is enabled. False if disabled.
588bb7c… aku 273 */
588bb7c… aku 274 static int enableOutput = 1;
588bb7c… aku 275
588bb7c… aku 276 /*
109d8f5… mistachkin 277 ** TH1 command: enable_output BOOLEAN
588bb7c… aku 278 **
6908832… drh 279 ** Enable or disable the puts, wiki, combobox and copybtn commands.
588bb7c… aku 280 */
588bb7c… aku 281 static int enableOutputCmd(
b000db4… mistachkin 282 Th_Interp *interp,
b000db4… mistachkin 283 void *p,
b000db4… mistachkin 284 int argc,
b000db4… mistachkin 285 const char **argv,
588bb7c… aku 286 int *argl
588bb7c… aku 287 ){
4f8c897… drh 288 int rc;
4f8c897… drh 289 if( argc<2 || argc>3 ){
4f8c897… drh 290 return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN");
588bb7c… aku 291 }
4f8c897… drh 292 rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput);
4f8c897… drh 293 if( g.thTrace ){
2116238… drh 294 Th_Trace("enable_output {%.*s} -> %d<br>\n",
2116238… drh 295 TH1_LEN(argl[1]),argv[1],enableOutput);
4f8c897… drh 296 }
4f8c897… drh 297 return rc;
588bb7c… aku 298 }
588bb7c… aku 299
588bb7c… aku 300 /*
2212ac4… mistachkin 301 ** Returns a name for a TH1 return code.
588bb7c… aku 302 */
6c47a16… mistachkin 303 const char *Th_ReturnCodeName(int rc, int nullIfOk){
b058c8a… mistachkin 304 static char zRc[32];
2212ac4… mistachkin 305
b058c8a… mistachkin 306 switch( rc ){
6c47a16… mistachkin 307 case TH_OK: return nullIfOk ? 0 : "TH_OK";
b058c8a… mistachkin 308 case TH_ERROR: return "TH_ERROR";
b058c8a… mistachkin 309 case TH_BREAK: return "TH_BREAK";
b058c8a… mistachkin 310 case TH_RETURN: return "TH_RETURN";
b058c8a… mistachkin 311 case TH_CONTINUE: return "TH_CONTINUE";
36f0e47… mistachkin 312 case TH_RETURN2: return "TH_RETURN2";
b058c8a… mistachkin 313 default: {
2212ac4… mistachkin 314 sqlite3_snprintf(sizeof(zRc), zRc, "TH1 return code %d", rc);
b058c8a… mistachkin 315 }
b058c8a… mistachkin 316 }
b058c8a… mistachkin 317 return zRc;
b058c8a… mistachkin 318 }
b058c8a… mistachkin 319
5173701… stephan 320 /* See Th_SetOutputBlob() */
5173701… stephan 321 static Blob * pThOut = 0;
b058c8a… mistachkin 322 /*
5173701… stephan 323 ** Sets the th1-internal output-redirection blob and returns the
5173701… stephan 324 ** previous value. That blob is used by certain output-generation
5173701… stephan 325 ** routines to emit its output. It returns the previous value so that
a5f00e0… stephan 326 ** a routine can temporarily replace the buffer with its own and
5173701… stephan 327 ** restore it when it's done.
b058c8a… mistachkin 328 */
5173701… stephan 329 Blob * Th_SetOutputBlob(Blob * pOut){
5173701… stephan 330 Blob * tmp = pThOut;
5173701… stephan 331 pThOut = pOut;
5173701… stephan 332 return tmp;
5173701… stephan 333 }
5173701… stephan 334
5173701… stephan 335 /*
5173701… stephan 336 ** Send text to the appropriate output: If pOut is not NULL, it is
5173701… stephan 337 ** appended there, else to the console or to the CGI reply buffer.
5173701… stephan 338 ** Escape all characters with special meaning to HTML if the encode
9164a5d… drh 339 ** parameter is true.
5173701… stephan 340 **
5173701… stephan 341 ** If pOut is NULL and the global pThOut is not then that blob
5173701… stephan 342 ** is used for output.
5173701… stephan 343 */
2116238… drh 344 static void sendText(Blob *pOut, const char *z, int n, int encode){
5173701… stephan 345 if(0==pOut && pThOut!=0){
5173701… stephan 346 pOut = pThOut;
5173701… stephan 347 }
588bb7c… aku 348 if( enableOutput && n ){
2116238… drh 349 if( n<0 ){
2116238… drh 350 n = strlen(z);
2116238… drh 351 }else{
2116238… drh 352 n = TH1_LEN(n);
2116238… drh 353 }
588bb7c… aku 354 if( encode ){
588bb7c… aku 355 z = htmlize(z, n);
588bb7c… aku 356 n = strlen(z);
588bb7c… aku 357 }
5173701… stephan 358 if(pOut!=0){
5173701… stephan 359 blob_append(pOut, z, n);
5173701… stephan 360 }else if( g.cgiOutput ){
588bb7c… aku 361 cgi_append_content(z, n);
588bb7c… aku 362 }else{
588bb7c… aku 363 fwrite(z, 1, n, stdout);
d8ed5a0… drh 364 fflush(stdout);
588bb7c… aku 365 }
588bb7c… aku 366 if( encode ) free((char*)z);
588bb7c… aku 367 }
588bb7c… aku 368 }
588bb7c… aku 369
5173701… stephan 370 /*
5173701… stephan 371 ** error-reporting counterpart of sendText().
5173701… stephan 372 */
5173701… stephan 373 static void sendError(Blob * pOut, const char *z, int n, int forceCgi){
4f8c897… drh 374 int savedEnable = enableOutput;
4f8c897… drh 375 enableOutput = 1;
b058c8a… mistachkin 376 if( forceCgi || g.cgiOutput ){
f5482a0… wyoung 377 sendText(pOut, "<hr><p class=\"thmainError\">", -1, 0);
b058c8a… mistachkin 378 }
5173701… stephan 379 sendText(pOut,"ERROR: ", -1, 0);
5173701… stephan 380 sendText(pOut,(char*)z, n, 1);
5173701… stephan 381 sendText(pOut,forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0);
4f8c897… drh 382 enableOutput = savedEnable;
d44207f… mistachkin 383 }
d44207f… mistachkin 384
d44207f… mistachkin 385 /*
d44207f… mistachkin 386 ** Convert name to an rid. This function was copied from name_to_typed_rid()
d44207f… mistachkin 387 ** in name.c; however, it has been modified to report TH1 script errors instead
d44207f… mistachkin 388 ** of "fatal errors".
d44207f… mistachkin 389 */
d44207f… mistachkin 390 int th1_name_to_typed_rid(
d44207f… mistachkin 391 Th_Interp *interp,
d44207f… mistachkin 392 const char *zName,
d44207f… mistachkin 393 const char *zType
d44207f… mistachkin 394 ){
d44207f… mistachkin 395 int rid;
d44207f… mistachkin 396
d44207f… mistachkin 397 if( zName==0 || zName[0]==0 ) return 0;
d44207f… mistachkin 398 rid = symbolic_name_to_rid(zName, zType);
d44207f… mistachkin 399 if( rid<0 ){
d44207f… mistachkin 400 Th_SetResult(interp, "ambiguous name", -1);
d44207f… mistachkin 401 }else if( rid==0 ){
d44207f… mistachkin 402 Th_SetResult(interp, "name not found", -1);
d44207f… mistachkin 403 }
d44207f… mistachkin 404 return rid;
d44207f… mistachkin 405 }
d44207f… mistachkin 406
d44207f… mistachkin 407 /*
c49030f… drh 408 ** Attempt to lookup the specified check-in and file name into an rid.
d44207f… mistachkin 409 ** This function was copied from artifact_from_ci_and_filename() in
d44207f… mistachkin 410 ** info.c; however, it has been modified to report TH1 script errors
d44207f… mistachkin 411 ** instead of "fatal errors".
d44207f… mistachkin 412 */
d44207f… mistachkin 413 int th1_artifact_from_ci_and_filename(
d44207f… mistachkin 414 Th_Interp *interp,
d44207f… mistachkin 415 const char *zCI,
d44207f… mistachkin 416 const char *zFilename
d44207f… mistachkin 417 ){
d44207f… mistachkin 418 int cirid;
d44207f… mistachkin 419 Blob err;
d44207f… mistachkin 420 Manifest *pManifest;
d44207f… mistachkin 421 ManifestFile *pFile;
d44207f… mistachkin 422
d44207f… mistachkin 423 if( zCI==0 ){
d44207f… mistachkin 424 Th_SetResult(interp, "invalid check-in", -1);
d44207f… mistachkin 425 return 0;
d44207f… mistachkin 426 }
d44207f… mistachkin 427 if( zFilename==0 ){
d44207f… mistachkin 428 Th_SetResult(interp, "invalid file name", -1);
d44207f… mistachkin 429 return 0;
d44207f… mistachkin 430 }
d44207f… mistachkin 431 cirid = th1_name_to_typed_rid(interp, zCI, "*");
d44207f… mistachkin 432 blob_zero(&err);
d44207f… mistachkin 433 pManifest = manifest_get(cirid, CFTYPE_MANIFEST, &err);
d44207f… mistachkin 434 if( pManifest==0 ){
d44207f… mistachkin 435 if( blob_size(&err)>0 ){
d44207f… mistachkin 436 Th_SetResult(interp, blob_str(&err), blob_size(&err));
d44207f… mistachkin 437 }else{
d44207f… mistachkin 438 Th_SetResult(interp, "manifest not found", -1);
d44207f… mistachkin 439 }
d44207f… mistachkin 440 blob_reset(&err);
d44207f… mistachkin 441 return 0;
d44207f… mistachkin 442 }
d44207f… mistachkin 443 blob_reset(&err);
d44207f… mistachkin 444 manifest_file_rewind(pManifest);
d44207f… mistachkin 445 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
d44207f… mistachkin 446 if( fossil_strcmp(zFilename, pFile->zName)==0 ){
d44207f… mistachkin 447 int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
d44207f… mistachkin 448 manifest_destroy(pManifest);
d44207f… mistachkin 449 return rid;
d44207f… mistachkin 450 }
d44207f… mistachkin 451 }
d44207f… mistachkin 452 Th_SetResult(interp, "file name not found in manifest", -1);
d44207f… mistachkin 453 return 0;
d44207f… mistachkin 454 }
d44207f… mistachkin 455
d44207f… mistachkin 456 /*
8a65cd1… mistachkin 457 ** TH1 command: nonce
8a65cd1… mistachkin 458 **
8a65cd1… mistachkin 459 ** Returns the value of the cryptographic nonce for the request being
8a65cd1… mistachkin 460 ** processed.
8a65cd1… mistachkin 461 */
8a65cd1… mistachkin 462 static int nonceCmd(
8a65cd1… mistachkin 463 Th_Interp *interp,
8a65cd1… mistachkin 464 void *pConvert,
8a65cd1… mistachkin 465 int argc,
8a65cd1… mistachkin 466 const char **argv,
8a65cd1… mistachkin 467 int *argl
8a65cd1… mistachkin 468 ){
8a65cd1… mistachkin 469 if( argc!=1 ){
8a65cd1… mistachkin 470 return Th_WrongNumArgs(interp, "nonce");
8a65cd1… mistachkin 471 }
8a65cd1… mistachkin 472 Th_SetResult(interp, style_nonce(), -1);
8a65cd1… mistachkin 473 return TH_OK;
8a65cd1… mistachkin 474 }
8a65cd1… mistachkin 475
8a65cd1… mistachkin 476 /*
109d8f5… mistachkin 477 ** TH1 command: puts STRING
109d8f5… mistachkin 478 ** TH1 command: html STRING
109d8f5… mistachkin 479 **
35563f3… drh 480 ** Output STRING escaped for HTML (puts) or unchanged (html).
588bb7c… aku 481 */
588bb7c… aku 482 static int putsCmd(
b000db4… mistachkin 483 Th_Interp *interp,
b000db4… mistachkin 484 void *pConvert,
b000db4… mistachkin 485 int argc,
b000db4… mistachkin 486 const char **argv,
588bb7c… aku 487 int *argl
588bb7c… aku 488 ){
2116238… drh 489 int encode = *(unsigned int*)pConvert;
2116238… drh 490 int n;
588bb7c… aku 491 if( argc!=2 ){
588bb7c… aku 492 return Th_WrongNumArgs(interp, "puts STRING");
588bb7c… aku 493 }
2116238… drh 494 n = argl[1];
2116238… drh 495 if( encode==0 && n>0 && TH1_TAINTED(n) ){
2116238… drh 496 if( Th_ReportTaint(interp, "output string", argv[1], n) ){
2116238… drh 497 return TH_ERROR;
2116238… drh 498 }
2116238… drh 499 }
ded2126… drh 500 sendText(0,(char*)argv[1], TH1_LEN(n), encode);
71caba5… mistachkin 501 return TH_OK;
71caba5… mistachkin 502 }
71caba5… mistachkin 503
71caba5… mistachkin 504 /*
bee6dbd… mistachkin 505 ** TH1 command: redirect URL ?withMethod?
71caba5… mistachkin 506 **
bee6dbd… mistachkin 507 ** Issues an HTTP redirect to the specified URL and then exits the process.
bee6dbd… mistachkin 508 ** By default, an HTTP status code of 302 is used. If the optional withMethod
bee6dbd… mistachkin 509 ** argument is present and non-zero, an HTTP status code of 307 is used, which
bee6dbd… mistachkin 510 ** should force the user agent to preserve the original method for the request
bee6dbd… mistachkin 511 ** (e.g. GET, POST) instead of (possibly) forcing the user agent to change the
bee6dbd… mistachkin 512 ** method to GET.
71caba5… mistachkin 513 */
71caba5… mistachkin 514 static int redirectCmd(
71caba5… mistachkin 515 Th_Interp *interp,
71caba5… mistachkin 516 void *p,
71caba5… mistachkin 517 int argc,
71caba5… mistachkin 518 const char **argv,
71caba5… mistachkin 519 int *argl
71caba5… mistachkin 520 ){
bee6dbd… mistachkin 521 int withMethod = 0;
bee6dbd… mistachkin 522 if( argc!=2 && argc!=3 ){
bee6dbd… mistachkin 523 return Th_WrongNumArgs(interp, "redirect URL ?withMethod?");
71caba5… mistachkin 524 }
bee6dbd… mistachkin 525 if( argc==3 ){
bee6dbd… mistachkin 526 if( Th_ToInt(interp, argv[2], argl[2], &withMethod) ){
bee6dbd… mistachkin 527 return TH_ERROR;
bee6dbd… mistachkin 528 }
2116238… drh 529 }
2116238… drh 530 if( TH1_TAINTED(argl[1])
2116238… drh 531 && Th_ReportTaint(interp,"redirect URL",argv[1],argl[1])
2116238… drh 532 ){
2116238… drh 533 return TH_ERROR;
bee6dbd… mistachkin 534 }
bee6dbd… mistachkin 535 if( withMethod ){
bee6dbd… mistachkin 536 cgi_redirect_with_method(argv[1]);
bee6dbd… mistachkin 537 }else{
bee6dbd… mistachkin 538 cgi_redirect(argv[1]);
bee6dbd… mistachkin 539 }
71caba5… mistachkin 540 Th_SetResult(interp, argv[1], argl[1]); /* NOT REACHED */
f8820ef… mistachkin 541 return TH_OK;
f8820ef… mistachkin 542 }
f8820ef… mistachkin 543
f8820ef… mistachkin 544 /*
f8820ef… mistachkin 545 ** TH1 command: insertCsrf
f8820ef… mistachkin 546 **
f8820ef… mistachkin 547 ** While rendering a form, call this command to add the Anti-CSRF token
f8820ef… mistachkin 548 ** as a hidden element of the form.
f8820ef… mistachkin 549 */
f8820ef… mistachkin 550 static int insertCsrfCmd(
f8820ef… mistachkin 551 Th_Interp *interp,
f8820ef… mistachkin 552 void *p,
f8820ef… mistachkin 553 int argc,
f8820ef… mistachkin 554 const char **argv,
f8820ef… mistachkin 555 int *argl
f8820ef… mistachkin 556 ){
f8820ef… mistachkin 557 if( argc!=1 ){
f8820ef… mistachkin 558 return Th_WrongNumArgs(interp, "insertCsrf");
f8820ef… mistachkin 559 }
f8820ef… mistachkin 560 login_insert_csrf_secret();
f8820ef… mistachkin 561 return TH_OK;
f8820ef… mistachkin 562 }
f8820ef… mistachkin 563
f8820ef… mistachkin 564 /*
f8820ef… mistachkin 565 ** TH1 command: verifyCsrf
f8820ef… mistachkin 566 **
f8820ef… mistachkin 567 ** Before using the results of a form, first call this command to verify
f8820ef… mistachkin 568 ** that this Anti-CSRF token is present and is valid. If the Anti-CSRF token
f8820ef… mistachkin 569 ** is missing or is incorrect, that indicates a cross-site scripting attack.
f8820ef… mistachkin 570 ** If the event of an attack is detected, an error message is generated and
f8820ef… mistachkin 571 ** all further processing is aborted.
f8820ef… mistachkin 572 */
f8820ef… mistachkin 573 static int verifyCsrfCmd(
f8820ef… mistachkin 574 Th_Interp *interp,
f8820ef… mistachkin 575 void *p,
f8820ef… mistachkin 576 int argc,
f8820ef… mistachkin 577 const char **argv,
f8820ef… mistachkin 578 int *argl
f8820ef… mistachkin 579 ){
f8820ef… mistachkin 580 if( argc!=1 ){
f8820ef… mistachkin 581 return Th_WrongNumArgs(interp, "verifyCsrf");
f8820ef… mistachkin 582 }
920ace1… drh 583 if( !cgi_csrf_safe(2) ){
920ace1… drh 584 fossil_fatal("possible CSRF attack");
920ace1… drh 585 }
a470d60… mistachkin 586 return TH_OK;
a470d60… mistachkin 587 }
a470d60… mistachkin 588
a470d60… mistachkin 589 /*
a470d60… mistachkin 590 ** TH1 command: verifyLogin
a470d60… mistachkin 591 **
a470d60… mistachkin 592 ** Returns non-zero if the specified user name and password represent a
a470d60… mistachkin 593 ** valid login for the repository.
a470d60… mistachkin 594 */
a470d60… mistachkin 595 static int verifyLoginCmd(
a470d60… mistachkin 596 Th_Interp *interp,
a470d60… mistachkin 597 void *p,
a470d60… mistachkin 598 int argc,
a470d60… mistachkin 599 const char **argv,
a470d60… mistachkin 600 int *argl
a470d60… mistachkin 601 ){
a470d60… mistachkin 602 const char *zUser;
a470d60… mistachkin 603 const char *zPass;
a470d60… mistachkin 604 int uid;
a470d60… mistachkin 605 if( argc!=3 ){
a470d60… mistachkin 606 return Th_WrongNumArgs(interp, "verifyLogin userName password");
a470d60… mistachkin 607 }
a470d60… mistachkin 608 zUser = argv[1];
a470d60… mistachkin 609 zPass = argv[2];
a470d60… mistachkin 610 uid = login_search_uid(&zUser, zPass);
a470d60… mistachkin 611 Th_SetResultInt(interp, uid!=0);
a470d60… mistachkin 612 if( uid==0 ) sqlite3_sleep(100);
f6d29e9… mistachkin 613 return TH_OK;
f6d29e9… mistachkin 614 }
f6d29e9… mistachkin 615
f6d29e9… mistachkin 616 /*
f6d29e9… mistachkin 617 ** TH1 command: markdown STRING
f6d29e9… mistachkin 618 **
f6d29e9… mistachkin 619 ** Renders the input string as markdown. The result is a two-element list.
f6d29e9… mistachkin 620 ** The first element is the text-only title string. The second element
f6d29e9… mistachkin 621 ** contains the body, rendered as HTML.
f6d29e9… mistachkin 622 */
f6d29e9… mistachkin 623 static int markdownCmd(
f6d29e9… mistachkin 624 Th_Interp *interp,
f6d29e9… mistachkin 625 void *p,
f6d29e9… mistachkin 626 int argc,
f6d29e9… mistachkin 627 const char **argv,
f6d29e9… mistachkin 628 int *argl
f6d29e9… mistachkin 629 ){
f6d29e9… mistachkin 630 Blob src, title, body;
f6d29e9… mistachkin 631 char *zValue = 0;
f6d29e9… mistachkin 632 int nValue = 0;
f6d29e9… mistachkin 633 if( argc!=2 ){
f6d29e9… mistachkin 634 return Th_WrongNumArgs(interp, "markdown STRING");
f6d29e9… mistachkin 635 }
f6d29e9… mistachkin 636 blob_zero(&src);
2116238… drh 637 blob_init(&src, (char*)argv[1], TH1_LEN(argl[1]));
f6d29e9… mistachkin 638 blob_zero(&title); blob_zero(&body);
f6d29e9… mistachkin 639 markdown_to_html(&src, &title, &body);
f6d29e9… mistachkin 640 Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title));
f6d29e9… mistachkin 641 Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body));
f6d29e9… mistachkin 642 Th_SetResult(interp, zValue, nValue);
999e33c… mistachkin 643 Th_Free(interp, zValue);
588bb7c… aku 644 return TH_OK;
588bb7c… aku 645 }
588bb7c… aku 646
588bb7c… aku 647 /*
1e5548a… mistachkin 648 ** TH1 command: decorate STRING
109d8f5… mistachkin 649 ** TH1 command: wiki STRING
588bb7c… aku 650 **
1e5548a… mistachkin 651 ** Render the input string as wiki. For the decorate command, only links
1e5548a… mistachkin 652 ** are handled.
588bb7c… aku 653 */
588bb7c… aku 654 static int wikiCmd(
b000db4… mistachkin 655 Th_Interp *interp,
b000db4… mistachkin 656 void *p,
b000db4… mistachkin 657 int argc,
b000db4… mistachkin 658 const char **argv,
588bb7c… aku 659 int *argl
588bb7c… aku 660 ){
74e3f90… drh 661 int flags = WIKI_INLINE | WIKI_NOBADLINKS | *(unsigned int*)p;
588bb7c… aku 662 if( argc!=2 ){
588bb7c… aku 663 return Th_WrongNumArgs(interp, "wiki STRING");
588bb7c… aku 664 }
588bb7c… aku 665 if( enableOutput ){
588bb7c… aku 666 Blob src;
2116238… drh 667 blob_init(&src, (char*)argv[1], TH1_LEN(argl[1]));
74e3f90… drh 668 wiki_convert(&src, 0, flags);
588bb7c… aku 669 blob_reset(&src);
588bb7c… aku 670 }
25f43cc… stephan 671 return TH_OK;
25f43cc… stephan 672 }
25f43cc… stephan 673
25f43cc… stephan 674 /*
25f43cc… stephan 675 ** TH1 command: wiki_assoc STRING STRING
25f43cc… stephan 676 **
25f43cc… stephan 677 ** Render an associated wiki page. The first string is the namespace
25f43cc… stephan 678 ** (e.g. "checkin", "branch", "ticket"). The second is the ID of the
25f43cc… stephan 679 ** associated object. See wiki_render_associated().
25f43cc… stephan 680 */
25f43cc… stephan 681 static int wikiAssocCmd(
25f43cc… stephan 682 Th_Interp *interp,
25f43cc… stephan 683 void *p,
25f43cc… stephan 684 int argc,
25f43cc… stephan 685 const char **argv,
25f43cc… stephan 686 int *argl
25f43cc… stephan 687 ){
25f43cc… stephan 688 if( argc!=3 ){
25f43cc… stephan 689 return Th_WrongNumArgs(interp, "wiki_assoc STRING STRING");
25f43cc… stephan 690 }
25f43cc… stephan 691 wiki_render_associated((char*)argv[1], (char*)argv[2], WIKIASSOC_FULL_TITLE);
109d8f5… mistachkin 692 return TH_OK;
109d8f5… mistachkin 693 }
109d8f5… mistachkin 694
109d8f5… mistachkin 695 /*
109d8f5… mistachkin 696 ** TH1 command: htmlize STRING
46e6a03… drh 697 **
46e6a03… drh 698 ** Escape all characters of STRING which have special meaning in HTML.
46e6a03… drh 699 ** Return a new string result.
46e6a03… drh 700 */
46e6a03… drh 701 static int htmlizeCmd(
b000db4… mistachkin 702 Th_Interp *interp,
b000db4… mistachkin 703 void *p,
b000db4… mistachkin 704 int argc,
b000db4… mistachkin 705 const char **argv,
46e6a03… drh 706 int *argl
46e6a03… drh 707 ){
46e6a03… drh 708 char *zOut;
46e6a03… drh 709 if( argc!=2 ){
46e6a03… drh 710 return Th_WrongNumArgs(interp, "htmlize STRING");
46e6a03… drh 711 }
2116238… drh 712 zOut = htmlize((char*)argv[1], TH1_LEN(argl[1]));
3f21421… mistachkin 713 Th_SetResult(interp, zOut, -1);
3f21421… mistachkin 714 free(zOut);
3f21421… mistachkin 715 return TH_OK;
3f21421… mistachkin 716 }
3f21421… mistachkin 717
3f21421… mistachkin 718 /*
3f21421… mistachkin 719 ** TH1 command: encode64 STRING
3f21421… mistachkin 720 **
3f21421… mistachkin 721 ** Encode the specified string using Base64 and return the result.
3f21421… mistachkin 722 */
3f21421… mistachkin 723 static int encode64Cmd(
3f21421… mistachkin 724 Th_Interp *interp,
3f21421… mistachkin 725 void *p,
3f21421… mistachkin 726 int argc,
3f21421… mistachkin 727 const char **argv,
3f21421… mistachkin 728 int *argl
3f21421… mistachkin 729 ){
3f21421… mistachkin 730 char *zOut;
3f21421… mistachkin 731 if( argc!=2 ){
3f21421… mistachkin 732 return Th_WrongNumArgs(interp, "encode64 STRING");
3f21421… mistachkin 733 }
2116238… drh 734 zOut = encode64((char*)argv[1], TH1_LEN(argl[1]));
0c99a15… drh 735 Th_SetResult(interp, zOut, -1);
0c99a15… drh 736 free(zOut);
0c99a15… drh 737 return TH_OK;
0c99a15… drh 738 }
0c99a15… drh 739
0c99a15… drh 740 /*
109d8f5… mistachkin 741 ** TH1 command: date
0c99a15… drh 742 **
e076252… drh 743 ** Return a string which is the current time and date. If the
e076252… drh 744 ** -local option is used, the date appears using localtime instead
e076252… drh 745 ** of UTC.
0c99a15… drh 746 */
0c99a15… drh 747 static int dateCmd(
b000db4… mistachkin 748 Th_Interp *interp,
b000db4… mistachkin 749 void *p,
b000db4… mistachkin 750 int argc,
b000db4… mistachkin 751 const char **argv,
0c99a15… drh 752 int *argl
0c99a15… drh 753 ){
e076252… drh 754 char *zOut;
2116238… drh 755 if( argc>=2 && TH1_LEN(argl[1])==6 && memcmp(argv[1],"-local",6)==0 ){
ea63a2d… drh 756 zOut = db_text("??", "SELECT datetime('now',toLocal())");
e076252… drh 757 }else{
e076252… drh 758 zOut = db_text("??", "SELECT datetime('now')");
e076252… drh 759 }
0c99a15… drh 760 Th_SetResult(interp, zOut, -1);
0c99a15… drh 761 free(zOut);
0c99a15… drh 762 return TH_OK;
0c99a15… drh 763 }
0c99a15… drh 764
0c99a15… drh 765 /*
109d8f5… mistachkin 766 ** TH1 command: hascap STRING...
653dd40… drh 767 ** TH1 command: anoncap STRING...
0c99a15… drh 768 **
653dd40… drh 769 ** Return true if the current user (hascap) or if the anonymous user
653dd40… drh 770 ** (anoncap) has all of the capabilities listed in STRING.
0c99a15… drh 771 */
0c99a15… drh 772 static int hascapCmd(
b000db4… mistachkin 773 Th_Interp *interp,
b000db4… mistachkin 774 void *p,
b000db4… mistachkin 775 int argc,
b000db4… mistachkin 776 const char **argv,
b000db4… mistachkin 777 int *argl
b000db4… mistachkin 778 ){
1c0b1a5… mistachkin 779 int rc = 1, i;
901f183… mistachkin 780 char *zCapList = 0;
901f183… mistachkin 781 int nCapList = 0;
c0c0bae… drh 782 if( argc<2 ){
c0c0bae… drh 783 return Th_WrongNumArgs(interp, "hascap STRING ...");
c0c0bae… drh 784 }
1c0b1a5… mistachkin 785 for(i=1; rc==1 && i<argc; i++){
901f183… mistachkin 786 if( g.thTrace ){
2116238… drh 787 Th_ListAppend(interp, &zCapList, &nCapList, argv[i], TH1_LEN(argl[i]));
901f183… mistachkin 788 }
2116238… drh 789 rc = login_has_capability((char*)argv[i],TH1_LEN(argl[i]),*(int*)p);
c0c0bae… drh 790 }
c0c0bae… drh 791 if( g.thTrace ){
f5482a0… wyoung 792 Th_Trace("[%s %#h] => %d<br>\n", argv[0], nCapList, zCapList, rc);
901f183… mistachkin 793 Th_Free(interp, zCapList);
c0c0bae… drh 794 }
901f183… mistachkin 795 Th_SetResultInt(interp, rc);
901f183… mistachkin 796 return TH_OK;
901f183… mistachkin 797 }
f96de5a… drh 798
f96de5a… drh 799 /*
f96de5a… drh 800 ** TH1 command: capexpr CAPABILITY-EXPR
f96de5a… drh 801 **
f96de5a… drh 802 ** Nmemonic: "CAPability EXPRression"
f96de5a… drh 803 **
f96de5a… drh 804 ** The capability expression is a list. Each term of the list is a cluster
f96de5a… drh 805 ** of capability letters. The overall expression is true if any one term
f96de5a… drh 806 ** is true. A single term is true if all letters within that term are true.
f96de5a… drh 807 ** Or, if the term begins with "!", then the term is true if none of the
f96de5a… drh 808 ** terms or true. Or, if the term begins with "@" then the term is true
f96de5a… drh 809 ** if all of the capability letters in that term are available to the
f96de5a… drh 810 ** "anonymous" user. Or, if the term is "*" then it is always true.
f96de5a… drh 811 **
f96de5a… drh 812 ** Examples:
f96de5a… drh 813 **
f96de5a… drh 814 ** capexpr {j o r} True if any one of j, o, or r are available
f96de5a… drh 815 ** capexpr {oh} True if both o and h are available
f96de5a… drh 816 ** capexpr {@2 @3 4 5 6} 2 or 3 available for anonymous or one of
f96de5a… drh 817 ** 4, 5 or 6 is available for the user
f96de5a… drh 818 */
aa5beb8… drh 819 int capexprCmd(
f96de5a… drh 820 Th_Interp *interp,
f96de5a… drh 821 void *p,
f96de5a… drh 822 int argc,
f96de5a… drh 823 const char **argv,
f96de5a… drh 824 int *argl
f96de5a… drh 825 ){
f96de5a… drh 826 char **azCap;
f96de5a… drh 827 int *anCap;
f96de5a… drh 828 int nCap;
f96de5a… drh 829 int rc;
f96de5a… drh 830 int i;
f96de5a… drh 831
f96de5a… drh 832 if( argc!=2 ){
f96de5a… drh 833 return Th_WrongNumArgs(interp, "capexpr EXPR");
f96de5a… drh 834 }
2116238… drh 835 rc = Th_SplitList(interp, argv[1], TH1_LEN(argl[1]), &azCap, &anCap, &nCap);
f96de5a… drh 836 if( rc ) return rc;
f96de5a… drh 837 rc = 0;
f96de5a… drh 838 for(i=0; i<nCap; i++){
f96de5a… drh 839 if( azCap[i][0]=='!' ){
f96de5a… drh 840 rc = !login_has_capability(azCap[i]+1, anCap[i]-1, 0);
f96de5a… drh 841 }else if( azCap[i][0]=='@' ){
f96de5a… drh 842 rc = login_has_capability(azCap[i]+1, anCap[i]-1, LOGIN_ANON);
f96de5a… drh 843 }else if( azCap[i][0]=='*' ){
f96de5a… drh 844 rc = 1;
f96de5a… drh 845 }else{
f96de5a… drh 846 rc = login_has_capability(azCap[i], anCap[i], 0);
f96de5a… drh 847 }
8188ef9… drh 848 if( rc ) break;
f96de5a… drh 849 }
f96de5a… drh 850 Th_Free(interp, azCap);
f96de5a… drh 851 Th_SetResultInt(interp, rc);
999e33c… mistachkin 852 return TH_OK;
f96de5a… drh 853 }
f96de5a… drh 854
c0c0bae… drh 855
c0c0bae… drh 856 /*
c0c0bae… drh 857 ** TH1 command: searchable STRING...
c0c0bae… drh 858 **
c0c0bae… drh 859 ** Return true if searching in any of the document classes identified
c0c0bae… drh 860 ** by STRING is enabled for the repository and user has the necessary
c0c0bae… drh 861 ** capabilities to perform the search.
c0c0bae… drh 862 **
c0c0bae… drh 863 ** Document classes:
c0c0bae… drh 864 **
c0c0bae… drh 865 ** c Check-in comments
c0c0bae… drh 866 ** d Embedded documentation
c0c0bae… drh 867 ** t Tickets
c0c0bae… drh 868 ** w Wiki
c0c0bae… drh 869 **
c0c0bae… drh 870 ** To be clear, only one of the document classes identified by each STRING
c0c0bae… drh 871 ** needs to be searchable in order for that argument to be true. But
c0c0bae… drh 872 ** all arguments must be true for this routine to return true. Hence, to
c0c0bae… drh 873 ** see if ALL document classes are searchable:
b8e3dc1… jan.nijtmans 874 **
c0c0bae… drh 875 ** if {[searchable c d t w]} {...}
c0c0bae… drh 876 **
c0c0bae… drh 877 ** But to see if ANY document class is searchable:
c0c0bae… drh 878 **
c0c0bae… drh 879 ** if {[searchable cdtw]} {...}
c0c0bae… drh 880 **
c0c0bae… drh 881 ** This command is useful for enabling or disabling a "Search" entry
c0c0bae… drh 882 ** on the menu bar.
c0c0bae… drh 883 */
c0c0bae… drh 884 static int searchableCmd(
c0c0bae… drh 885 Th_Interp *interp,
c0c0bae… drh 886 void *p,
c0c0bae… drh 887 int argc,
c0c0bae… drh 888 const char **argv,
c0c0bae… drh 889 int *argl
c0c0bae… drh 890 ){
c0c0bae… drh 891 int rc = 1, i, j;
2e9e369… drh 892 unsigned int searchCap = search_restrict(SRCH_ALL);
ba418ee… drh 893 if( argc<2 ){
ba418ee… drh 894 return Th_WrongNumArgs(interp, "hascap STRING ...");
ba418ee… drh 895 }
c0c0bae… drh 896 for(i=1; i<argc && rc; i++){
c0c0bae… drh 897 int match = 0;
2116238… drh 898 int nn = TH1_LEN(argl[i]);
2116238… drh 899 for(j=0; j<nn; j++){
c0c0bae… drh 900 switch( argv[i][j] ){
c0c0bae… drh 901 case 'c': match |= searchCap & SRCH_CKIN; break;
c0c0bae… drh 902 case 'd': match |= searchCap & SRCH_DOC; break;
c0c0bae… drh 903 case 't': match |= searchCap & SRCH_TKT; break;
c0c0bae… drh 904 case 'w': match |= searchCap & SRCH_WIKI; break;
c0c0bae… drh 905 }
c0c0bae… drh 906 }
c0c0bae… drh 907 if( !match ) rc = 0;
a87eaae… mistachkin 908 }
a87eaae… mistachkin 909 if( g.thTrace ){
2116238… drh 910 Th_Trace("[searchable %#h] => %d<br>\n", TH1_LEN(argl[1]), argv[1], rc);
a87eaae… mistachkin 911 }
a87eaae… mistachkin 912 Th_SetResultInt(interp, rc);
a87eaae… mistachkin 913 return TH_OK;
a87eaae… mistachkin 914 }
a87eaae… mistachkin 915
a87eaae… mistachkin 916 /*
109d8f5… mistachkin 917 ** TH1 command: hasfeature STRING
a87eaae… mistachkin 918 **
a87eaae… mistachkin 919 ** Return true if the fossil binary has the given compile-time feature
a87eaae… mistachkin 920 ** enabled. The set of features includes:
a87eaae… mistachkin 921 **
e10d117… mistachkin 922 ** "ssl" = FOSSIL_ENABLE_SSL
4a880a1… mistachkin 923 ** "legacyMvRm" = FOSSIL_ENABLE_LEGACY_MV_RM
825d78b… mistachkin 924 ** "execRelPaths" = FOSSIL_ENABLE_EXEC_REL_PATHS
c91bafc… mistachkin 925 ** "th1Docs" = FOSSIL_ENABLE_TH1_DOCS
1c528d3… mistachkin 926 ** "th1Hooks" = FOSSIL_ENABLE_TH1_HOOKS
e10d117… mistachkin 927 ** "tcl" = FOSSIL_ENABLE_TCL
6b58c67… mistachkin 928 ** "useTclStubs" = USE_TCL_STUBS
e10d117… mistachkin 929 ** "tclStubs" = FOSSIL_ENABLE_TCL_STUBS
e10d117… mistachkin 930 ** "tclPrivateStubs" = FOSSIL_ENABLE_TCL_PRIVATE_STUBS
e10d117… mistachkin 931 ** "json" = FOSSIL_ENABLE_JSON
e10d117… mistachkin 932 ** "markdown" = FOSSIL_ENABLE_MARKDOWN
edb8237… mistachkin 933 ** "unicodeCmdLine" = !BROKEN_MINGW_CMDLINE
7932cb3… mistachkin 934 ** "dynamicBuild" = FOSSIL_DYNAMIC_BUILD
d5ca538… mistachkin 935 ** "mman" = USE_MMAN_H
9129f6f… mistachkin 936 ** "see" = USE_SEE
a87eaae… mistachkin 937 **
c1c8d72… jan.nijtmans 938 ** Specifying an unknown feature will return a value of false, it will not
c1c8d72… jan.nijtmans 939 ** raise a script error.
a87eaae… mistachkin 940 */
a87eaae… mistachkin 941 static int hasfeatureCmd(
b000db4… mistachkin 942 Th_Interp *interp,
b000db4… mistachkin 943 void *p,
b000db4… mistachkin 944 int argc,
b000db4… mistachkin 945 const char **argv,
a87eaae… mistachkin 946 int *argl
a87eaae… mistachkin 947 ){
a87eaae… mistachkin 948 int rc = 0;
5330d10… jan.nijtmans 949 const char *zArg;
a87eaae… mistachkin 950 if( argc!=2 ){
a87eaae… mistachkin 951 return Th_WrongNumArgs(interp, "hasfeature STRING");
a87eaae… mistachkin 952 }
5330d10… jan.nijtmans 953 zArg = (const char *)argv[1];
a87eaae… mistachkin 954 if(NULL==zArg){
a87eaae… mistachkin 955 /* placeholder for following ifdefs... */
a87eaae… mistachkin 956 }
a87eaae… mistachkin 957 #if defined(FOSSIL_ENABLE_SSL)
e10d117… mistachkin 958 else if( 0 == fossil_strnicmp( zArg, "ssl\0", 4 ) ){
3c941dd… drh 959 rc = 1;
3c941dd… drh 960 }
3c941dd… drh 961 #endif
4a880a1… mistachkin 962 else if( 0 == fossil_strnicmp( zArg, "legacyMvRm\0", 11 ) ){
4a880a1… mistachkin 963 rc = 1;
4a880a1… mistachkin 964 }
825d78b… mistachkin 965 #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS)
825d78b… mistachkin 966 else if( 0 == fossil_strnicmp( zArg, "execRelPaths\0", 13 ) ){
c91bafc… mistachkin 967 rc = 1;
c91bafc… mistachkin 968 }
c91bafc… mistachkin 969 #endif
c91bafc… mistachkin 970 #if defined(FOSSIL_ENABLE_TH1_DOCS)
c91bafc… mistachkin 971 else if( 0 == fossil_strnicmp( zArg, "th1Docs\0", 8 ) ){
1c528d3… mistachkin 972 rc = 1;
1c528d3… mistachkin 973 }
1c528d3… mistachkin 974 #endif
1c528d3… mistachkin 975 #if defined(FOSSIL_ENABLE_TH1_HOOKS)
1c528d3… mistachkin 976 else if( 0 == fossil_strnicmp( zArg, "th1Hooks\0", 9 ) ){
6b58c67… mistachkin 977 rc = 1;
6b58c67… mistachkin 978 }
6b58c67… mistachkin 979 #endif
a87eaae… mistachkin 980 #if defined(FOSSIL_ENABLE_TCL)
e10d117… mistachkin 981 else if( 0 == fossil_strnicmp( zArg, "tcl\0", 4 ) ){
6b58c67… mistachkin 982 rc = 1;
6b58c67… mistachkin 983 }
6b58c67… mistachkin 984 #endif
6b58c67… mistachkin 985 #if defined(USE_TCL_STUBS)
6b58c67… mistachkin 986 else if( 0 == fossil_strnicmp( zArg, "useTclStubs\0", 12 ) ){
a87eaae… mistachkin 987 rc = 1;
a87eaae… mistachkin 988 }
a87eaae… mistachkin 989 #endif
a87eaae… mistachkin 990 #if defined(FOSSIL_ENABLE_TCL_STUBS)
e10d117… mistachkin 991 else if( 0 == fossil_strnicmp( zArg, "tclStubs\0", 9 ) ){
e10d117… mistachkin 992 rc = 1;
e10d117… mistachkin 993 }
e10d117… mistachkin 994 #endif
e10d117… mistachkin 995 #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS)
e10d117… mistachkin 996 else if( 0 == fossil_strnicmp( zArg, "tclPrivateStubs\0", 16 ) ){
a87eaae… mistachkin 997 rc = 1;
a87eaae… mistachkin 998 }
a87eaae… mistachkin 999 #endif
a87eaae… mistachkin 1000 #if defined(FOSSIL_ENABLE_JSON)
e10d117… mistachkin 1001 else if( 0 == fossil_strnicmp( zArg, "json\0", 5 ) ){
e10d117… mistachkin 1002 rc = 1;
e10d117… mistachkin 1003 }
e10d117… mistachkin 1004 #endif
edb8237… mistachkin 1005 #if !defined(BROKEN_MINGW_CMDLINE)
edb8237… mistachkin 1006 else if( 0 == fossil_strnicmp( zArg, "unicodeCmdLine\0", 15 ) ){
edb8237… mistachkin 1007 rc = 1;
edb8237… mistachkin 1008 }
edb8237… mistachkin 1009 #endif
7932cb3… mistachkin 1010 #if defined(FOSSIL_DYNAMIC_BUILD)
7932cb3… mistachkin 1011 else if( 0 == fossil_strnicmp( zArg, "dynamicBuild\0", 13 ) ){
7932cb3… mistachkin 1012 rc = 1;
7932cb3… mistachkin 1013 }
7932cb3… mistachkin 1014 #endif
d5ca538… mistachkin 1015 #if defined(USE_MMAN_H)
d5ca538… mistachkin 1016 else if( 0 == fossil_strnicmp( zArg, "mman\0", 5 ) ){
d5ca538… mistachkin 1017 rc = 1;
d5ca538… mistachkin 1018 }
d5ca538… mistachkin 1019 #endif
9129f6f… mistachkin 1020 #if defined(USE_SEE)
9129f6f… mistachkin 1021 else if( 0 == fossil_strnicmp( zArg, "see\0", 4 ) ){
9129f6f… mistachkin 1022 rc = 1;
9129f6f… mistachkin 1023 }
9129f6f… mistachkin 1024 #endif
e10d117… mistachkin 1025 else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){
a87eaae… mistachkin 1026 rc = 1;
ba418ee… drh 1027 }
2be3a87… stephan 1028 if( g.thTrace ){
2116238… drh 1029 Th_Trace("[hasfeature %#h] => %d<br>\n", TH1_LEN(argl[1]), zArg, rc);
2be3a87… stephan 1030 }
2be3a87… stephan 1031 Th_SetResultInt(interp, rc);
2be3a87… stephan 1032 return TH_OK;
2be3a87… stephan 1033 }
2be3a87… stephan 1034
a87eaae… mistachkin 1035
2be3a87… stephan 1036 /*
109d8f5… mistachkin 1037 ** TH1 command: tclReady
a87eaae… mistachkin 1038 **
a87eaae… mistachkin 1039 ** Return true if the fossil binary has the Tcl integration feature
a87eaae… mistachkin 1040 ** enabled and it is currently available for use by TH1 scripts.
5df13a0… stephan 1041 **
2be3a87… stephan 1042 */
a87eaae… mistachkin 1043 static int tclReadyCmd(
a87eaae… mistachkin 1044 Th_Interp *interp,
a87eaae… mistachkin 1045 void *p,
a87eaae… mistachkin 1046 int argc,
a87eaae… mistachkin 1047 const char **argv,
2be3a87… stephan 1048 int *argl
2be3a87… stephan 1049 ){
2be3a87… stephan 1050 int rc = 0;
a87eaae… mistachkin 1051 if( argc!=1 ){
a87eaae… mistachkin 1052 return Th_WrongNumArgs(interp, "tclReady");
a87eaae… mistachkin 1053 }
a87eaae… mistachkin 1054 #if defined(FOSSIL_ENABLE_TCL)
a87eaae… mistachkin 1055 if( g.tcl.interp ){
a87eaae… mistachkin 1056 rc = 1;
a87eaae… mistachkin 1057 }
a87eaae… mistachkin 1058 #endif
a87eaae… mistachkin 1059 if( g.thTrace ){
f5482a0… wyoung 1060 Th_Trace("[tclReady] => %d<br>\n", rc);
9b70675… drh 1061 }
9b70675… drh 1062 Th_SetResultInt(interp, rc);
9b70675… drh 1063 return TH_OK;
9b70675… drh 1064 }
9b70675… drh 1065
2be3a87… stephan 1066
9b70675… drh 1067 /*
109d8f5… mistachkin 1068 ** TH1 command: anycap STRING
9b70675… drh 1069 **
d83638e… danield 1070 ** Return true if the current user
653dd40… drh 1071 ** has any one of the capabilities listed in STRING.
9b70675… drh 1072 */
9b70675… drh 1073 static int anycapCmd(
b000db4… mistachkin 1074 Th_Interp *interp,
b000db4… mistachkin 1075 void *p,
b000db4… mistachkin 1076 int argc,
b000db4… mistachkin 1077 const char **argv,
9b70675… drh 1078 int *argl
9b70675… drh 1079 ){
9b70675… drh 1080 int rc = 0;
9b70675… drh 1081 int i;
2116238… drh 1082 int nn;
0c99a15… drh 1083 if( argc!=2 ){
9b70675… drh 1084 return Th_WrongNumArgs(interp, "anycap STRING");
0c99a15… drh 1085 }
2116238… drh 1086 nn = TH1_LEN(argl[1]);
2116238… drh 1087 for(i=0; rc==0 && i<nn; i++){
653dd40… drh 1088 rc = login_has_capability((char*)&argv[1][i],1,0);
9b70675… drh 1089 }
f55c6a1… drh 1090 if( g.thTrace ){
2116238… drh 1091 Th_Trace("[anycap %#h] => %d<br>\n", TH1_LEN(argl[1]), argv[1], rc);
f55c6a1… drh 1092 }
9b70675… drh 1093 Th_SetResultInt(interp, rc);
0c99a15… drh 1094 return TH_OK;
0c99a15… drh 1095 }
0c99a15… drh 1096
0c99a15… drh 1097 /*
109d8f5… mistachkin 1098 ** TH1 command: combobox NAME TEXT-LIST NUMLINES
588bb7c… aku 1099 **
588bb7c… aku 1100 ** Generate an HTML combobox. NAME is both the name of the
588bb7c… aku 1101 ** CGI parameter and the name of a variable that contains the
588bb7c… aku 1102 ** currently selected value. TEXT-LIST is a list of possible
588bb7c… aku 1103 ** values for the combobox. NUMLINES is 1 for a true combobox.
588bb7c… aku 1104 ** If NUMLINES is greater than one then the display is a listbox
588bb7c… aku 1105 ** with the number of lines given.
588bb7c… aku 1106 */
588bb7c… aku 1107 static int comboboxCmd(
588bb7c… aku 1108 Th_Interp *interp,
b000db4… mistachkin 1109 void *p,
b000db4… mistachkin 1110 int argc,
b000db4… mistachkin 1111 const char **argv,
588bb7c… aku 1112 int *argl
588bb7c… aku 1113 ){
588bb7c… aku 1114 if( argc!=4 ){
588bb7c… aku 1115 return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES");
588bb7c… aku 1116 }
588bb7c… aku 1117 if( enableOutput ){
588bb7c… aku 1118 int height;
0c99a15… drh 1119 Blob name;
2116238… drh 1120 int nValue = 0;
588bb7c… aku 1121 const char *zValue;
588bb7c… aku 1122 char *z, *zH;
0c99a15… drh 1123 int nElem;
0c99a15… drh 1124 int *aszElem;
0c99a15… drh 1125 char **azElem;
0c99a15… drh 1126 int i;
588bb7c… aku 1127
588bb7c… aku 1128 if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR;
2116238… drh 1129 Th_SplitList(interp, argv[2], TH1_LEN(argl[2]), &azElem, &aszElem, &nElem);
2116238… drh 1130 blob_init(&name, (char*)argv[1], TH1_LEN(argl[1]));
588bb7c… aku 1131 zValue = Th_Fetch(blob_str(&name), &nValue);
2116238… drh 1132 nValue = TH1_LEN(nValue);
73fdb8a… joel 1133 zH = htmlize(blob_buffer(&name), blob_size(&name));
73fdb8a… joel 1134 z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height);
73fdb8a… joel 1135 free(zH);
5173701… stephan 1136 sendText(0,z, -1, 0);
588bb7c… aku 1137 free(z);
588bb7c… aku 1138 blob_reset(&name);
0c99a15… drh 1139 for(i=0; i<nElem; i++){
0c99a15… drh 1140 zH = htmlize((char*)azElem[i], aszElem[i]);
b000db4… mistachkin 1141 if( zValue && aszElem[i]==nValue
0c99a15… drh 1142 && memcmp(zValue, azElem[i], nValue)==0 ){
3243e63… drh 1143 z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
3243e63… drh 1144 zH, zH);
588bb7c… aku 1145 }else{
588bb7c… aku 1146 z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
588bb7c… aku 1147 }
588bb7c… aku 1148 free(zH);
5173701… stephan 1149 sendText(0,z, -1, 0);
588bb7c… aku 1150 free(z);
588bb7c… aku 1151 }
5173701… stephan 1152 sendText(0,"</select>", -1, 0);
0c99a15… drh 1153 Th_Free(interp, azElem);
6908832… drh 1154 }
6908832… drh 1155 return TH_OK;
6908832… drh 1156 }
6908832… drh 1157
6908832… drh 1158 /*
8ee5e55… drh 1159 ** TH1 command: copybtn TARGETID FLIPPED TEXT ?COPYLENGTH?
6908832… drh 1160 **
6908832… drh 1161 ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
6908832… drh 1162 ** Javascript module, and generates HTML elements with the following IDs:
6908832… drh 1163 **
6908832… drh 1164 ** TARGETID: The <span> wrapper around TEXT.
6908832… drh 1165 ** copy-TARGETID: The <span> for the copy button.
8ee5e55… drh 1166 **
8ee5e55… drh 1167 ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT.
6908832… drh 1168 **
6908832… drh 1169 ** The optional COPYLENGTH argument defines the length of the substring of TEXT
6908832… drh 1170 ** copied to clipboard:
6908832… drh 1171 **
6908832… drh 1172 ** <= 0: No limit (default if the argument is omitted).
6908832… drh 1173 ** >= 3: Truncate TEXT after COPYLENGTH (single-byte) characters.
6908832… drh 1174 ** 1: Use the "hash-digits" setting as the limit.
6908832… drh 1175 ** 2: Use the length appropriate for URLs as the limit (defined at
6908832… drh 1176 ** compile-time by FOSSIL_HASH_DIGITS_URL, defaults to 16).
6908832… drh 1177 */
6908832… drh 1178 static int copybtnCmd(
6908832… drh 1179 Th_Interp *interp,
6908832… drh 1180 void *p,
6908832… drh 1181 int argc,
6908832… drh 1182 const char **argv,
6908832… drh 1183 int *argl
6908832… drh 1184 ){
8ee5e55… drh 1185 if( argc!=4 && argc!=5 ){
8ee5e55… drh 1186 return Th_WrongNumArgs(interp,
8ee5e55… drh 1187 "copybtn TARGETID FLIPPED TEXT ?COPYLENGTH?");
6908832… drh 1188 }
6908832… drh 1189 if( enableOutput ){
8ee5e55… drh 1190 int flipped = 0;
6908832… drh 1191 int copylength = 0;
759fbda… drh 1192 char *zResult;
8ee5e55… drh 1193 if( Th_ToInt(interp, argv[2], argl[2], &flipped) ) return TH_ERROR;
8ee5e55… drh 1194 if( argc==5 ){
8ee5e55… drh 1195 if( Th_ToInt(interp, argv[4], argl[4], &copylength) ) return TH_ERROR;
8ee5e55… drh 1196 }
759fbda… drh 1197 zResult = style_copy_button(
759fbda… drh 1198 /*bOutputCGI==*/0, /*TARGETID==*/(char*)argv[1],
759fbda… drh 1199 flipped, copylength, "%h", /*TEXT==*/(char*)argv[3]);
5173701… stephan 1200 sendText(0,zResult, -1, 0);
6908832… drh 1201 free(zResult);
109d8f5… mistachkin 1202 }
109d8f5… mistachkin 1203 return TH_OK;
109d8f5… mistachkin 1204 }
109d8f5… mistachkin 1205
109d8f5… mistachkin 1206 /*
109d8f5… mistachkin 1207 ** TH1 command: linecount STRING MAX MIN
588bb7c… aku 1208 **
588bb7c… aku 1209 ** Return one more than the number of \n characters in STRING. But
588bb7c… aku 1210 ** never return less than MIN or more than MAX.
588bb7c… aku 1211 */
588bb7c… aku 1212 static int linecntCmd(
588bb7c… aku 1213 Th_Interp *interp,
b000db4… mistachkin 1214 void *p,
b000db4… mistachkin 1215 int argc,
b000db4… mistachkin 1216 const char **argv,
588bb7c… aku 1217 int *argl
588bb7c… aku 1218 ){
0c99a15… drh 1219 const char *z;
588bb7c… aku 1220 int size, n, i;
588bb7c… aku 1221 int iMin, iMax;
588bb7c… aku 1222 if( argc!=4 ){
588bb7c… aku 1223 return Th_WrongNumArgs(interp, "linecount STRING MAX MIN");
588bb7c… aku 1224 }
588bb7c… aku 1225 if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR;
588bb7c… aku 1226 if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR;
588bb7c… aku 1227 z = argv[1];
2116238… drh 1228 size = TH1_LEN(argl[1]);
588bb7c… aku 1229 for(n=1, i=0; i<size; i++){
588bb7c… aku 1230 if( z[i]=='\n' ){
588bb7c… aku 1231 n++;
588bb7c… aku 1232 if( n>=iMax ) break;
588bb7c… aku 1233 }
588bb7c… aku 1234 }
588bb7c… aku 1235 if( n<iMin ) n = iMin;
588bb7c… aku 1236 if( n>iMax ) n = iMax;
588bb7c… aku 1237 Th_SetResultInt(interp, n);
588bb7c… aku 1238 return TH_OK;
588bb7c… aku 1239 }
588bb7c… aku 1240
588bb7c… aku 1241 /*
109d8f5… mistachkin 1242 ** TH1 command: repository ?BOOLEAN?
d8ed5a0… drh 1243 **
d8ed5a0… drh 1244 ** Return the fully qualified file name of the open repository or an empty
d8ed5a0… drh 1245 ** string if one is not currently open. Optionally, it will attempt to open
d8ed5a0… drh 1246 ** the repository if the boolean argument is non-zero.
d8ed5a0… drh 1247 */
d8ed5a0… drh 1248 static int repositoryCmd(
d8ed5a0… drh 1249 Th_Interp *interp,
b000db4… mistachkin 1250 void *p,
b000db4… mistachkin 1251 int argc,
b000db4… mistachkin 1252 const char **argv,
d8ed5a0… drh 1253 int *argl
d8ed5a0… drh 1254 ){
d8ed5a0… drh 1255 if( argc!=1 && argc!=2 ){
d8ed5a0… drh 1256 return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
d8ed5a0… drh 1257 }
d8ed5a0… drh 1258 if( argc==2 ){
24273fd… mistachkin 1259 int openRepository = 0;
d8ed5a0… drh 1260 if( Th_ToInt(interp, argv[1], argl[1], &openRepository) ){
d8ed5a0… drh 1261 return TH_ERROR;
d8ed5a0… drh 1262 }
d8ed5a0… drh 1263 if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0);
d8ed5a0… drh 1264 }
d8ed5a0… drh 1265 Th_SetResult(interp, g.zRepositoryName, -1);
d8ed5a0… drh 1266 return TH_OK;
d8ed5a0… drh 1267 }
d8ed5a0… drh 1268
57be4d5… mistachkin 1269 /*
109d8f5… mistachkin 1270 ** TH1 command: checkout ?BOOLEAN?
57be4d5… mistachkin 1271 **
bc36fdc… danield 1272 ** Return the fully qualified directory name of the current check-out or an
589b902… mistachkin 1273 ** empty string if it is not available. Optionally, it will attempt to find
bc36fdc… danield 1274 ** the current check-out, opening the configuration ("user") database and the
589b902… mistachkin 1275 ** repository as necessary, if the boolean argument is non-zero.
57be4d5… mistachkin 1276 */
57be4d5… mistachkin 1277 static int checkoutCmd(
57be4d5… mistachkin 1278 Th_Interp *interp,
57be4d5… mistachkin 1279 void *p,
57be4d5… mistachkin 1280 int argc,
57be4d5… mistachkin 1281 const char **argv,
57be4d5… mistachkin 1282 int *argl
57be4d5… mistachkin 1283 ){
57be4d5… mistachkin 1284 if( argc!=1 && argc!=2 ){
57be4d5… mistachkin 1285 return Th_WrongNumArgs(interp, "checkout ?BOOLEAN?");
57be4d5… mistachkin 1286 }
57be4d5… mistachkin 1287 if( argc==2 ){
589b902… mistachkin 1288 int openCheckout = 0;
589b902… mistachkin 1289 if( Th_ToInt(interp, argv[1], argl[1], &openCheckout) ){
57be4d5… mistachkin 1290 return TH_ERROR;
57be4d5… mistachkin 1291 }
589b902… mistachkin 1292 if( openCheckout ) db_open_local(0);
57be4d5… mistachkin 1293 }
57be4d5… mistachkin 1294 Th_SetResult(interp, g.zLocalRoot, -1);
57be4d5… mistachkin 1295 return TH_OK;
57be4d5… mistachkin 1296 }
57be4d5… mistachkin 1297
57be4d5… mistachkin 1298 /*
109d8f5… mistachkin 1299 ** TH1 command: trace STRING
57be4d5… mistachkin 1300 **
57be4d5… mistachkin 1301 ** Generate a TH1 trace message if debugging is enabled.
57be4d5… mistachkin 1302 */
57be4d5… mistachkin 1303 static int traceCmd(
57be4d5… mistachkin 1304 Th_Interp *interp,
57be4d5… mistachkin 1305 void *p,
57be4d5… mistachkin 1306 int argc,
57be4d5… mistachkin 1307 const char **argv,
57be4d5… mistachkin 1308 int *argl
57be4d5… mistachkin 1309 ){
57be4d5… mistachkin 1310 if( argc!=2 ){
57be4d5… mistachkin 1311 return Th_WrongNumArgs(interp, "trace STRING");
57be4d5… mistachkin 1312 }
57be4d5… mistachkin 1313 if( g.thTrace ){
57be4d5… mistachkin 1314 Th_Trace("%s", argv[1]);
57be4d5… mistachkin 1315 }
57be4d5… mistachkin 1316 Th_SetResult(interp, 0, 0);
57be4d5… mistachkin 1317 return TH_OK;
57be4d5… mistachkin 1318 }
57be4d5… mistachkin 1319
57be4d5… mistachkin 1320 /*
66ae70a… drh 1321 ** TH1 command: globalState NAME ?DEFAULT?
66ae70a… drh 1322 **
66ae70a… drh 1323 ** Returns a string containing the value of the specified global state
66ae70a… drh 1324 ** variable -OR- the specified default value. Currently, the supported
66ae70a… drh 1325 ** items are:
66ae70a… drh 1326 **
bc36fdc… danield 1327 ** "checkout" = The active local check-out directory, if any.
66ae70a… drh 1328 ** "configuration" = The active configuration database file name,
66ae70a… drh 1329 ** if any.
66ae70a… drh 1330 ** "executable" = The fully qualified executable file name.
3355835… mistachkin 1331 ** "flags" = The TH1 initialization flags.
66ae70a… drh 1332 ** "log" = The error log file name, if any.
66ae70a… drh 1333 ** "repository" = The active local repository file name, if
66ae70a… drh 1334 ** any.
66ae70a… drh 1335 ** "top" = The base path for the active server instance,
66ae70a… drh 1336 ** if applicable.
66ae70a… drh 1337 ** "user" = The active user name, if any.
66ae70a… drh 1338 ** "vfs" = The SQLite VFS in use, if overridden.
66ae70a… drh 1339 **
66ae70a… drh 1340 ** Attempts to query for unsupported global state variables will result
66ae70a… drh 1341 ** in a script error. Additional global state variables may be exposed
66ae70a… drh 1342 ** in the future.
66ae70a… drh 1343 **
66ae70a… drh 1344 ** See also: checkout, repository, setting
66ae70a… drh 1345 */
66ae70a… drh 1346 static int globalStateCmd(
66ae70a… drh 1347 Th_Interp *interp,
66ae70a… drh 1348 void *p,
66ae70a… drh 1349 int argc,
66ae70a… drh 1350 const char **argv,
66ae70a… drh 1351 int *argl
66ae70a… drh 1352 ){
66ae70a… drh 1353 const char *zDefault = 0;
66ae70a… drh 1354 if( argc!=2 && argc!=3 ){
66ae70a… drh 1355 return Th_WrongNumArgs(interp, "globalState NAME ?DEFAULT?");
66ae70a… drh 1356 }
66ae70a… drh 1357 if( argc==3 ){
66ae70a… drh 1358 zDefault = argv[2];
66ae70a… drh 1359 }
66ae70a… drh 1360 if( fossil_strnicmp(argv[1], "checkout\0", 9)==0 ){
66ae70a… drh 1361 Th_SetResult(interp, g.zLocalRoot ? g.zLocalRoot : zDefault, -1);
66ae70a… drh 1362 return TH_OK;
66ae70a… drh 1363 }else if( fossil_strnicmp(argv[1], "configuration\0", 14)==0 ){
66ae70a… drh 1364 Th_SetResult(interp, g.zConfigDbName ? g.zConfigDbName : zDefault, -1);
66ae70a… drh 1365 return TH_OK;
66ae70a… drh 1366 }else if( fossil_strnicmp(argv[1], "executable\0", 11)==0 ){
66ae70a… drh 1367 Th_SetResult(interp, g.nameOfExe ? g.nameOfExe : zDefault, -1);
66ae70a… drh 1368 return TH_OK;
3355835… mistachkin 1369 }else if( fossil_strnicmp(argv[1], "flags\0", 6)==0 ){
3355835… mistachkin 1370 Th_SetResultInt(interp, g.th1Flags);
3355835… mistachkin 1371 return TH_OK;
66ae70a… drh 1372 }else if( fossil_strnicmp(argv[1], "log\0", 4)==0 ){
66ae70a… drh 1373 Th_SetResult(interp, g.zErrlog ? g.zErrlog : zDefault, -1);
66ae70a… drh 1374 return TH_OK;
66ae70a… drh 1375 }else if( fossil_strnicmp(argv[1], "repository\0", 11)==0 ){
66ae70a… drh 1376 Th_SetResult(interp, g.zRepositoryName ? g.zRepositoryName : zDefault, -1);
66ae70a… drh 1377 return TH_OK;
66ae70a… drh 1378 }else if( fossil_strnicmp(argv[1], "top\0", 4)==0 ){
66ae70a… drh 1379 Th_SetResult(interp, g.zTop ? g.zTop : zDefault, -1);
66ae70a… drh 1380 return TH_OK;
66ae70a… drh 1381 }else if( fossil_strnicmp(argv[1], "user\0", 5)==0 ){
66ae70a… drh 1382 Th_SetResult(interp, g.zLogin ? g.zLogin : zDefault, -1);
66ae70a… drh 1383 return TH_OK;
66ae70a… drh 1384 }else if( fossil_strnicmp(argv[1], "vfs\0", 4)==0 ){
66ae70a… drh 1385 Th_SetResult(interp, g.zVfsName ? g.zVfsName : zDefault, -1);
66ae70a… drh 1386 return TH_OK;
66ae70a… drh 1387 }else{
2116238… drh 1388 Th_ErrorMessage(interp, "unsupported global state:",
2116238… drh 1389 argv[1], TH1_LEN(argl[1]));
66ae70a… drh 1390 return TH_ERROR;
66ae70a… drh 1391 }
66ae70a… drh 1392 }
66ae70a… drh 1393
66ae70a… drh 1394 /*
109d8f5… mistachkin 1395 ** TH1 command: getParameter NAME ?DEFAULT?
57be4d5… mistachkin 1396 **
57be4d5… mistachkin 1397 ** Return the value of the specified query parameter or the specified default
57be4d5… mistachkin 1398 ** value when there is no matching query parameter.
57be4d5… mistachkin 1399 */
57be4d5… mistachkin 1400 static int getParameterCmd(
57be4d5… mistachkin 1401 Th_Interp *interp,
57be4d5… mistachkin 1402 void *p,
57be4d5… mistachkin 1403 int argc,
57be4d5… mistachkin 1404 const char **argv,
57be4d5… mistachkin 1405 int *argl
57be4d5… mistachkin 1406 ){
57be4d5… mistachkin 1407 const char *zDefault = 0;
6a6b854… drh 1408 const char *zVal;
6a6b854… drh 1409 int sz;
57be4d5… mistachkin 1410 if( argc!=2 && argc!=3 ){
57be4d5… mistachkin 1411 return Th_WrongNumArgs(interp, "getParameter NAME ?DEFAULT?");
57be4d5… mistachkin 1412 }
57be4d5… mistachkin 1413 if( argc==3 ){
57be4d5… mistachkin 1414 zDefault = argv[2];
57be4d5… mistachkin 1415 }
6a6b854… drh 1416 zVal = cgi_parameter(argv[1], zDefault);
6a6b854… drh 1417 sz = th_strlen(zVal);
6a6b854… drh 1418 Th_SetResult(interp, zVal, TH1_ADD_TAINT(sz));
57be4d5… mistachkin 1419 return TH_OK;
57be4d5… mistachkin 1420 }
57be4d5… mistachkin 1421
57be4d5… mistachkin 1422 /*
109d8f5… mistachkin 1423 ** TH1 command: setParameter NAME VALUE
57be4d5… mistachkin 1424 **
57be4d5… mistachkin 1425 ** Sets the value of the specified query parameter.
57be4d5… mistachkin 1426 */
57be4d5… mistachkin 1427 static int setParameterCmd(
57be4d5… mistachkin 1428 Th_Interp *interp,
57be4d5… mistachkin 1429 void *p,
57be4d5… mistachkin 1430 int argc,
57be4d5… mistachkin 1431 const char **argv,
57be4d5… mistachkin 1432 int *argl
57be4d5… mistachkin 1433 ){
57be4d5… mistachkin 1434 if( argc!=3 ){
57be4d5… mistachkin 1435 return Th_WrongNumArgs(interp, "setParameter NAME VALUE");
57be4d5… mistachkin 1436 }
4c3e172… danield 1437 cgi_replace_parameter(fossil_strdup(argv[1]), fossil_strdup(argv[2]));
3355835… mistachkin 1438 return TH_OK;
3355835… mistachkin 1439 }
3355835… mistachkin 1440
3355835… mistachkin 1441 /*
3355835… mistachkin 1442 ** TH1 command: reinitialize ?FLAGS?
3355835… mistachkin 1443 **
3355835… mistachkin 1444 ** Reinitializes the TH1 interpreter using the specified flags.
3355835… mistachkin 1445 */
3355835… mistachkin 1446 static int reinitializeCmd(
3355835… mistachkin 1447 Th_Interp *interp,
3355835… mistachkin 1448 void *p,
3355835… mistachkin 1449 int argc,
3355835… mistachkin 1450 const char **argv,
3355835… mistachkin 1451 int *argl
3355835… mistachkin 1452 ){
3355835… mistachkin 1453 u32 flags = TH_INIT_DEFAULT;
3355835… mistachkin 1454 if( argc!=1 && argc!=2 ){
3355835… mistachkin 1455 return Th_WrongNumArgs(interp, "reinitialize ?FLAGS?");
3355835… mistachkin 1456 }
3355835… mistachkin 1457 if( argc==2 ){
3355835… mistachkin 1458 int iFlags;
3355835… mistachkin 1459 if( Th_ToInt(interp, argv[1], argl[1], &iFlags) ){
3355835… mistachkin 1460 return TH_ERROR;
3355835… mistachkin 1461 }else{
3355835… mistachkin 1462 flags = (u32)iFlags;
3355835… mistachkin 1463 }
3355835… mistachkin 1464 }
3355835… mistachkin 1465 Th_FossilInit(flags & ~TH_INIT_FORBID_MASK);
3355835… mistachkin 1466 Th_SetResult(interp, 0, 0);
57be4d5… mistachkin 1467 return TH_OK;
57be4d5… mistachkin 1468 }
57be4d5… mistachkin 1469
57be4d5… mistachkin 1470 /*
109d8f5… mistachkin 1471 ** TH1 command: render STRING
57be4d5… mistachkin 1472 **
57be4d5… mistachkin 1473 ** Renders the template and writes the results.
57be4d5… mistachkin 1474 */
57be4d5… mistachkin 1475 static int renderCmd(
57be4d5… mistachkin 1476 Th_Interp *interp,
57be4d5… mistachkin 1477 void *p,
57be4d5… mistachkin 1478 int argc,
57be4d5… mistachkin 1479 const char **argv,
57be4d5… mistachkin 1480 int *argl
57be4d5… mistachkin 1481 ){
57be4d5… mistachkin 1482 int rc;
57be4d5… mistachkin 1483 if( argc!=2 ){
57be4d5… mistachkin 1484 return Th_WrongNumArgs(interp, "render STRING");
57be4d5… mistachkin 1485 }
57be4d5… mistachkin 1486 rc = Th_Render(argv[1]);
57be4d5… mistachkin 1487 Th_SetResult(interp, 0, 0);
57be4d5… mistachkin 1488 return rc;
57be4d5… mistachkin 1489 }
57be4d5… mistachkin 1490
57be4d5… mistachkin 1491 /*
c1cb688… mistachkin 1492 ** TH1 command: defHeader TITLE
c1cb688… mistachkin 1493 **
c1cb688… mistachkin 1494 ** Returns the default page header.
c1cb688… mistachkin 1495 */
c1cb688… mistachkin 1496 static int defHeaderCmd(
c1cb688… mistachkin 1497 Th_Interp *interp,
c1cb688… mistachkin 1498 void *p,
c1cb688… mistachkin 1499 int argc,
c1cb688… mistachkin 1500 const char **argv,
c1cb688… mistachkin 1501 int *argl
c1cb688… mistachkin 1502 ){
c1cb688… mistachkin 1503 if( argc!=1 ){
c1cb688… mistachkin 1504 return Th_WrongNumArgs(interp, "defHeader");
c1cb688… mistachkin 1505 }
c1cb688… mistachkin 1506 Th_SetResult(interp, get_default_header(), -1);
c1cb688… mistachkin 1507 return TH_OK;
c1cb688… mistachkin 1508 }
c1cb688… mistachkin 1509
c1cb688… mistachkin 1510 /*
109d8f5… mistachkin 1511 ** TH1 command: styleHeader TITLE
57be4d5… mistachkin 1512 **
874a4da… mistachkin 1513 ** Render the configured style header for the selected skin.
57be4d5… mistachkin 1514 */
57be4d5… mistachkin 1515 static int styleHeaderCmd(
57be4d5… mistachkin 1516 Th_Interp *interp,
57be4d5… mistachkin 1517 void *p,
57be4d5… mistachkin 1518 int argc,
57be4d5… mistachkin 1519 const char **argv,
57be4d5… mistachkin 1520 int *argl
57be4d5… mistachkin 1521 ){
57be4d5… mistachkin 1522 if( argc!=2 ){
57be4d5… mistachkin 1523 return Th_WrongNumArgs(interp, "styleHeader TITLE");
57be4d5… mistachkin 1524 }
142200b… mistachkin 1525 if( Th_IsRepositoryOpen() ){
57be4d5… mistachkin 1526 style_header("%s", argv[1]);
57be4d5… mistachkin 1527 Th_SetResult(interp, 0, 0);
57be4d5… mistachkin 1528 return TH_OK;
57be4d5… mistachkin 1529 }else{
142200b… mistachkin 1530 Th_SetResult(interp, "repository unavailable", -1);
57be4d5… mistachkin 1531 return TH_ERROR;
57be4d5… mistachkin 1532 }
57be4d5… mistachkin 1533 }
57be4d5… mistachkin 1534
57be4d5… mistachkin 1535 /*
109d8f5… mistachkin 1536 ** TH1 command: styleFooter
57be4d5… mistachkin 1537 **
874a4da… mistachkin 1538 ** Render the configured style footer for the selected skin.
57be4d5… mistachkin 1539 */
57be4d5… mistachkin 1540 static int styleFooterCmd(
57be4d5… mistachkin 1541 Th_Interp *interp,
57be4d5… mistachkin 1542 void *p,
57be4d5… mistachkin 1543 int argc,
57be4d5… mistachkin 1544 const char **argv,
57be4d5… mistachkin 1545 int *argl
57be4d5… mistachkin 1546 ){
57be4d5… mistachkin 1547 if( argc!=1 ){
57be4d5… mistachkin 1548 return Th_WrongNumArgs(interp, "styleFooter");
57be4d5… mistachkin 1549 }
142200b… mistachkin 1550 if( Th_IsRepositoryOpen() ){
112c713… drh 1551 style_finish_page();
9c88799… drh 1552 Th_SetResult(interp, 0, 0);
9c88799… drh 1553 return TH_OK;
9c88799… drh 1554 }else{
9c88799… drh 1555 Th_SetResult(interp, "repository unavailable", -1);
9c88799… drh 1556 return TH_ERROR;
9c88799… drh 1557 }
9c88799… drh 1558 }
9c88799… drh 1559
9c88799… drh 1560 /*
fa8be1a… drh 1561 ** TH1 command: styleScript ?BUILTIN-FILENAME?
fa8be1a… drh 1562 **
fa8be1a… drh 1563 ** Render the js.txt file from the current skin. Or, if an argument
fa8be1a… drh 1564 ** is supplied, render the built-in filename given.
9cd7428… drh 1565 **
9cd7428… drh 1566 ** By "rendering" we mean that the script is loaded and run through
9cd7428… drh 1567 ** TH1 to expand variables and process <th1>...</th1> script. Contrast
9cd7428… drh 1568 ** with the "builtin_request_js BUILTIN-FILENAME" command which just
9cd7428… drh 1569 ** loads the file as-is without interpretation.
9c88799… drh 1570 */
9c88799… drh 1571 static int styleScriptCmd(
9c88799… drh 1572 Th_Interp *interp,
9c88799… drh 1573 void *p,
9c88799… drh 1574 int argc,
9c88799… drh 1575 const char **argv,
9c88799… drh 1576 int *argl
9c88799… drh 1577 ){
fa8be1a… drh 1578 if( argc!=1 && argc!=2 ){
fa8be1a… drh 1579 return Th_WrongNumArgs(interp, "styleScript ?BUILTIN_NAME?");
9c88799… drh 1580 }
9c88799… drh 1581 if( Th_IsRepositoryOpen() ){
fa8be1a… drh 1582 const char *zScript;
fa8be1a… drh 1583 if( argc==2 ){
fa8be1a… drh 1584 zScript = (const char*)builtin_file(argv[1], 0);
fa8be1a… drh 1585 }else{
fa8be1a… drh 1586 zScript = skin_get("js");
fa8be1a… drh 1587 }
9c88799… drh 1588 if( zScript==0 ) zScript = "";
9c88799… drh 1589 Th_Render(zScript);
57be4d5… mistachkin 1590 Th_SetResult(interp, 0, 0);
57be4d5… mistachkin 1591 return TH_OK;
57be4d5… mistachkin 1592 }else{
142200b… mistachkin 1593 Th_SetResult(interp, "repository unavailable", -1);
142200b… mistachkin 1594 return TH_ERROR;
142200b… mistachkin 1595 }
71b2216… george 1596 }
71b2216… george 1597
71b2216… george 1598 /*
71b2216… george 1599 ** TH1 command: submenu link LABEL URL
71b2216… george 1600 **
71b2216… george 1601 ** Add a hyperlink to the submenu.
71b2216… george 1602 */
71b2216… george 1603 static int submenuCmd(
71b2216… george 1604 Th_Interp *interp,
71b2216… george 1605 void *p,
71b2216… george 1606 int argc,
71b2216… george 1607 const char **argv,
71b2216… george 1608 int *argl
71b2216… george 1609 ){
71b2216… george 1610 if( argc!=4 || memcmp(argv[1],"link",5)!=0 ){
71b2216… george 1611 return Th_WrongNumArgs(interp, "submenu link LABEL URL");
71b2216… george 1612 }
71b2216… george 1613 if( argl[2]==0 ){
71b2216… george 1614 Th_SetResult(interp, "link's LABEL is empty", -1);
71b2216… george 1615 return TH_ERROR;
71b2216… george 1616 }
71b2216… george 1617 if( argl[3]==0 ){
71b2216… george 1618 Th_SetResult(interp, "link's URL is empty", -1);
71b2216… george 1619 return TH_ERROR;
71b2216… george 1620 }
71b2216… george 1621 /*
71b2216… george 1622 ** Label and URL are unescaped because it is expected that
71b2216… george 1623 ** style_finish_page() provides propper escaping via %h format.
71b2216… george 1624 */
71b2216… george 1625 style_submenu_element( fossil_strdup(argv[2]), "%s", argv[3] );
71b2216… george 1626 Th_SetResult(interp, 0, 0);
71b2216… george 1627 return TH_OK;
9cd7428… drh 1628 }
9cd7428… drh 1629
9cd7428… drh 1630 /*
9cd7428… drh 1631 ** TH1 command: builtin_request_js NAME
9cd7428… drh 1632 **
9cd7428… drh 1633 ** Request that the built-in javascript file called NAME be added to the
9cd7428… drh 1634 ** end of the generated page.
9cd7428… drh 1635 **
9cd7428… drh 1636 ** See also: styleScript
9cd7428… drh 1637 */
9cd7428… drh 1638 static int builtinRequestJsCmd(
9cd7428… drh 1639 Th_Interp *interp,
9cd7428… drh 1640 void *p,
9cd7428… drh 1641 int argc,
9cd7428… drh 1642 const char **argv,
9cd7428… drh 1643 int *argl
9cd7428… drh 1644 ){
9cd7428… drh 1645 if( argc!=2 ){
9cd7428… drh 1646 return Th_WrongNumArgs(interp, "builtin_request_js NAME");
9cd7428… drh 1647 }
9cd7428… drh 1648 builtin_request_js(argv[1]);
9cd7428… drh 1649 return TH_OK;
9cd7428… drh 1650 }
00e4fed… mistachkin 1651
00e4fed… mistachkin 1652 /*
00e4fed… mistachkin 1653 ** TH1 command: artifact ID ?FILENAME?
109d8f5… mistachkin 1654 **
109d8f5… mistachkin 1655 ** Attempts to locate the specified artifact and return its contents. An
109d8f5… mistachkin 1656 ** error is generated if the repository is not open or the artifact cannot
109d8f5… mistachkin 1657 ** be found.
109d8f5… mistachkin 1658 */
109d8f5… mistachkin 1659 static int artifactCmd(
109d8f5… mistachkin 1660 Th_Interp *interp,
109d8f5… mistachkin 1661 void *p,
109d8f5… mistachkin 1662 int argc,
109d8f5… mistachkin 1663 const char **argv,
109d8f5… mistachkin 1664 int *argl
109d8f5… mistachkin 1665 ){
00e4fed… mistachkin 1666 if( argc!=2 && argc!=3 ){
00e4fed… mistachkin 1667 return Th_WrongNumArgs(interp, "artifact ID ?FILENAME?");
109d8f5… mistachkin 1668 }
109d8f5… mistachkin 1669 if( Th_IsRepositoryOpen() ){
109d8f5… mistachkin 1670 int rid;
109d8f5… mistachkin 1671 Blob content;
00e4fed… mistachkin 1672 if( argc==3 ){
d44207f… mistachkin 1673 rid = th1_artifact_from_ci_and_filename(interp, argv[1], argv[2]);
00e4fed… mistachkin 1674 }else{
d44207f… mistachkin 1675 rid = th1_name_to_typed_rid(interp, argv[1], "*");
00e4fed… mistachkin 1676 }
109d8f5… mistachkin 1677 if( rid!=0 && content_get(rid, &content) ){
109d8f5… mistachkin 1678 Th_SetResult(interp, blob_str(&content), blob_size(&content));
74099a5… mistachkin 1679 blob_reset(&content);
74099a5… mistachkin 1680 return TH_OK;
74099a5… mistachkin 1681 }else{
d44207f… mistachkin 1682 return TH_ERROR;
d44207f… mistachkin 1683 }
d44207f… mistachkin 1684 }else{
d44207f… mistachkin 1685 Th_SetResult(interp, "repository unavailable", -1);
d44207f… mistachkin 1686 return TH_ERROR;
d44207f… mistachkin 1687 }
d44207f… mistachkin 1688 }
d44207f… mistachkin 1689
1b5b69f… mistachkin 1690 /*
32c4880… mistachkin 1691 ** TH1 command: cgiHeaderLine line
32c4880… mistachkin 1692 **
32c4880… mistachkin 1693 ** Adds the specified line to the CGI header.
32c4880… mistachkin 1694 */
32c4880… mistachkin 1695 static int cgiHeaderLineCmd(
32c4880… mistachkin 1696 Th_Interp *interp,
32c4880… mistachkin 1697 void *p,
32c4880… mistachkin 1698 int argc,
32c4880… mistachkin 1699 const char **argv,
32c4880… mistachkin 1700 int *argl
32c4880… mistachkin 1701 ){
32c4880… mistachkin 1702 if( argc!=2 ){
32c4880… mistachkin 1703 return Th_WrongNumArgs(interp, "cgiHeaderLine line");
32c4880… mistachkin 1704 }
32c4880… mistachkin 1705 cgi_append_header(argv[1]);
32c4880… mistachkin 1706 return TH_OK;
32c4880… mistachkin 1707 }
32c4880… mistachkin 1708
32c4880… mistachkin 1709 /*
1b5b69f… mistachkin 1710 ** TH1 command: unversioned content FILENAME
1b5b69f… mistachkin 1711 **
1b5b69f… mistachkin 1712 ** Attempts to locate the specified unversioned file and return its contents.
1b5b69f… mistachkin 1713 ** An error is generated if the repository is not open or the unversioned file
1b5b69f… mistachkin 1714 ** cannot be found.
1b5b69f… mistachkin 1715 */
1b5b69f… mistachkin 1716 static int unversionedContentCmd(
1b5b69f… mistachkin 1717 Th_Interp *interp,
1b5b69f… mistachkin 1718 void *p,
1b5b69f… mistachkin 1719 int argc,
1b5b69f… mistachkin 1720 const char **argv,
1b5b69f… mistachkin 1721 int *argl
1b5b69f… mistachkin 1722 ){
1b5b69f… mistachkin 1723 if( argc!=3 ){
1b5b69f… mistachkin 1724 return Th_WrongNumArgs(interp, "unversioned content FILENAME");
1b5b69f… mistachkin 1725 }
1b5b69f… mistachkin 1726 if( Th_IsRepositoryOpen() ){
1b5b69f… mistachkin 1727 Blob content;
b5ab1eb… drh 1728 if( unversioned_content(argv[2], &content)!=0 ){
1b5b69f… mistachkin 1729 Th_SetResult(interp, blob_str(&content), blob_size(&content));
1b5b69f… mistachkin 1730 blob_reset(&content);
1b5b69f… mistachkin 1731 return TH_OK;
1b5b69f… mistachkin 1732 }else{
1b5b69f… mistachkin 1733 return TH_ERROR;
1b5b69f… mistachkin 1734 }
1b5b69f… mistachkin 1735 }else{
1b5b69f… mistachkin 1736 Th_SetResult(interp, "repository unavailable", -1);
1b5b69f… mistachkin 1737 return TH_ERROR;
1b5b69f… mistachkin 1738 }
1b5b69f… mistachkin 1739 }
d44207f… mistachkin 1740
d44207f… mistachkin 1741 /*
1b5b69f… mistachkin 1742 ** TH1 command: unversioned list
1b5b69f… mistachkin 1743 **
1b5b69f… mistachkin 1744 ** Returns a list of the names of all unversioned files held in the local
1b5b69f… mistachkin 1745 ** repository. An error is generated if the repository is not open.
d44207f… mistachkin 1746 */
1b5b69f… mistachkin 1747 static int unversionedListCmd(
1b5b69f… mistachkin 1748 Th_Interp *interp,
1b5b69f… mistachkin 1749 void *p,
1b5b69f… mistachkin 1750 int argc,
1b5b69f… mistachkin 1751 const char **argv,
1b5b69f… mistachkin 1752 int *argl
1b5b69f… mistachkin 1753 ){
1b5b69f… mistachkin 1754 if( argc!=2 ){
1b5b69f… mistachkin 1755 return Th_WrongNumArgs(interp, "unversioned list");
1b5b69f… mistachkin 1756 }
1b5b69f… mistachkin 1757 if( Th_IsRepositoryOpen() ){
1b5b69f… mistachkin 1758 Stmt q;
1b5b69f… mistachkin 1759 char *zList = 0;
1b5b69f… mistachkin 1760 int nList = 0;
1b5b69f… mistachkin 1761 db_prepare(&q, "SELECT name FROM unversioned WHERE hash IS NOT NULL"
1b5b69f… mistachkin 1762 " ORDER BY name");
1b5b69f… mistachkin 1763 while( db_step(&q)==SQLITE_ROW ){
1b5b69f… mistachkin 1764 Th_ListAppend(interp, &zList, &nList, db_column_text(&q,0), -1);
1b5b69f… mistachkin 1765 }
1b5b69f… mistachkin 1766 db_finalize(&q);
1b5b69f… mistachkin 1767 Th_SetResult(interp, zList, nList);
1b5b69f… mistachkin 1768 Th_Free(interp, zList);
1b5b69f… mistachkin 1769 return TH_OK;
1b5b69f… mistachkin 1770 }else{
1b5b69f… mistachkin 1771 Th_SetResult(interp, "repository unavailable", -1);
1b5b69f… mistachkin 1772 return TH_ERROR;
1b5b69f… mistachkin 1773 }
1b5b69f… mistachkin 1774 }
1b5b69f… mistachkin 1775
1b5b69f… mistachkin 1776 static int unversionedCmd(
1b5b69f… mistachkin 1777 Th_Interp *interp,
1b5b69f… mistachkin 1778 void *p,
1b5b69f… mistachkin 1779 int argc,
1b5b69f… mistachkin 1780 const char **argv,
1b5b69f… mistachkin 1781 int *argl
1b5b69f… mistachkin 1782 ){
1b5b69f… mistachkin 1783 static const Th_SubCommand aSub[] = {
1b5b69f… mistachkin 1784 { "content", unversionedContentCmd },
1b5b69f… mistachkin 1785 { "list", unversionedListCmd },
1b5b69f… mistachkin 1786 { 0, 0 }
1b5b69f… mistachkin 1787 };
1b5b69f… mistachkin 1788 return Th_CallSubCommand(interp, p, argc, argv, argl, aSub);
1b5b69f… mistachkin 1789 }
1b5b69f… mistachkin 1790
109d8f5… mistachkin 1791
109d8f5… mistachkin 1792 /*
109d8f5… mistachkin 1793 ** TH1 command: utime
3d50bdc… drh 1794 **
3d50bdc… drh 1795 ** Return the number of microseconds of CPU time consumed by the current
3d50bdc… drh 1796 ** process in user space.
3d50bdc… drh 1797 */
3d50bdc… drh 1798 static int utimeCmd(
3d50bdc… drh 1799 Th_Interp *interp,
b000db4… mistachkin 1800 void *p,
b000db4… mistachkin 1801 int argc,
b000db4… mistachkin 1802 const char **argv,
db0c512… drh 1803 int *argl
db0c512… drh 1804 ){
db0c512… drh 1805 sqlite3_uint64 x;
db0c512… drh 1806 char zUTime[50];
6c3d370… drh 1807 fossil_cpu_times(&x, 0);
db0c512… drh 1808 sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
db0c512… drh 1809 Th_SetResult(interp, zUTime, -1);
db0c512… drh 1810 return TH_OK;
db0c512… drh 1811 }
db0c512… drh 1812
db0c512… drh 1813 /*
109d8f5… mistachkin 1814 ** TH1 command: stime
db0c512… drh 1815 **
db0c512… drh 1816 ** Return the number of microseconds of CPU time consumed by the current
db0c512… drh 1817 ** process in system space.
db0c512… drh 1818 */
db0c512… drh 1819 static int stimeCmd(
db0c512… drh 1820 Th_Interp *interp,
b000db4… mistachkin 1821 void *p,
b000db4… mistachkin 1822 int argc,
b000db4… mistachkin 1823 const char **argv,
3d50bdc… drh 1824 int *argl
3d50bdc… drh 1825 ){
3d50bdc… drh 1826 sqlite3_uint64 x;
3d50bdc… drh 1827 char zUTime[50];
6c3d370… drh 1828 fossil_cpu_times(0, &x);
3d50bdc… drh 1829 sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
3d50bdc… drh 1830 Th_SetResult(interp, zUTime, -1);
3d50bdc… drh 1831 return TH_OK;
3d50bdc… drh 1832 }
3d50bdc… drh 1833
2116238… drh 1834 /*
2116238… drh 1835 ** TH1 command: taint STRING
2116238… drh 1836 **
2116238… drh 1837 ** Return a copy of STRING that is marked as tainted.
2116238… drh 1838 */
2116238… drh 1839 static int taintCmd(
2116238… drh 1840 Th_Interp *interp,
2116238… drh 1841 void *p,
2116238… drh 1842 int argc,
2116238… drh 1843 const char **argv,
2116238… drh 1844 int *argl
2116238… drh 1845 ){
2116238… drh 1846 if( argc!=2 ){
2116238… drh 1847 return Th_WrongNumArgs(interp, "STRING");
2116238… drh 1848 }
2116238… drh 1849 Th_SetResult(interp, argv[1], TH1_ADD_TAINT(argl[1]));
2116238… drh 1850 return TH_OK;
2116238… drh 1851 }
2116238… drh 1852
2116238… drh 1853 /*
2116238… drh 1854 ** TH1 command: untaint STRING
2116238… drh 1855 **
2116238… drh 1856 ** Return a copy of STRING that is marked as untainted.
2116238… drh 1857 */
2116238… drh 1858 static int untaintCmd(
2116238… drh 1859 Th_Interp *interp,
2116238… drh 1860 void *p,
2116238… drh 1861 int argc,
2116238… drh 1862 const char **argv,
2116238… drh 1863 int *argl
2116238… drh 1864 ){
2116238… drh 1865 if( argc!=2 ){
2116238… drh 1866 return Th_WrongNumArgs(interp, "STRING");
2116238… drh 1867 }
2116238… drh 1868 Th_SetResult(interp, argv[1], TH1_LEN(argl[1]));
2116238… drh 1869 return TH_OK;
2116238… drh 1870 }
74e3f90… drh 1871
74e3f90… drh 1872 /*
109d8f5… mistachkin 1873 ** TH1 command: randhex N
74e3f90… drh 1874 **
b000db4… mistachkin 1875 ** Return N*2 random hexadecimal digits with N<50. If N is omitted,
74e3f90… drh 1876 ** use a value of 10.
74e3f90… drh 1877 */
74e3f90… drh 1878 static int randhexCmd(
74e3f90… drh 1879 Th_Interp *interp,
b000db4… mistachkin 1880 void *p,
b000db4… mistachkin 1881 int argc,
b000db4… mistachkin 1882 const char **argv,
74e3f90… drh 1883 int *argl
74e3f90… drh 1884 ){
74e3f90… drh 1885 int n;
fc5a217… mistachkin 1886 unsigned char aRand[50];
fc5a217… mistachkin 1887 unsigned char zOut[100];
74e3f90… drh 1888 if( argc!=1 && argc!=2 ){
74e3f90… drh 1889 return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
74e3f90… drh 1890 }
74e3f90… drh 1891 if( argc==2 ){
74e3f90… drh 1892 if( Th_ToInt(interp, argv[1], argl[1], &n) ){
74e3f90… drh 1893 return TH_ERROR;
74e3f90… drh 1894 }
74e3f90… drh 1895 if( n<1 ) n = 1;
53db40e… drh 1896 if( n>(int)sizeof(aRand) ) n = sizeof(aRand);
74e3f90… drh 1897 }else{
74e3f90… drh 1898 n = 10;
74e3f90… drh 1899 }
74e3f90… drh 1900 sqlite3_randomness(n, aRand);
74e3f90… drh 1901 encode16(aRand, zOut, n);
fc5a217… mistachkin 1902 Th_SetResult(interp, (const char *)zOut, -1);
74e3f90… drh 1903 return TH_OK;
801eca6… drh 1904 }
801eca6… drh 1905
801eca6… drh 1906 /*
801eca6… drh 1907 ** Run sqlite3_step() while suppressing error messages sent to the
801eca6… drh 1908 ** rendered webpage or to the console.
801eca6… drh 1909 */
801eca6… drh 1910 static int ignore_errors_step(sqlite3_stmt *pStmt){
801eca6… drh 1911 int rc;
801eca6… drh 1912 g.dbIgnoreErrors++;
801eca6… drh 1913 rc = sqlite3_step(pStmt);
801eca6… drh 1914 g.dbIgnoreErrors--;
801eca6… drh 1915 return rc;
c8289bd… drh 1916 }
c8289bd… drh 1917
c8289bd… drh 1918 /*
c8289bd… drh 1919 ** TH1 command: query [-nocomplain] SQL CODE
4f8c897… drh 1920 **
4f8c897… drh 1921 ** Run the SQL query given by the SQL argument. For each row in the result
4f8c897… drh 1922 ** set, run CODE.
4f8c897… drh 1923 **
4f8c897… drh 1924 ** In SQL, parameters such as $var are filled in using the value of variable
4f8c897… drh 1925 ** "var". Result values are stored in variables with the column name prior
4f8c897… drh 1926 ** to each invocation of CODE.
4f8c897… drh 1927 */
4f8c897… drh 1928 static int queryCmd(
4f8c897… drh 1929 Th_Interp *interp,
b000db4… mistachkin 1930 void *p,
b000db4… mistachkin 1931 int argc,
b000db4… mistachkin 1932 const char **argv,
4f8c897… drh 1933 int *argl
4f8c897… drh 1934 ){
4f8c897… drh 1935 sqlite3_stmt *pStmt;
4f8c897… drh 1936 int rc;
4f8c897… drh 1937 const char *zSql;
4f8c897… drh 1938 int nSql;
4f8c897… drh 1939 const char *zTail;
4f8c897… drh 1940 int n, i;
4f8c897… drh 1941 int res = TH_OK;
4f8c897… drh 1942 int nVar;
2056f62… drh 1943 char *zErr = 0;
c8289bd… drh 1944 int noComplain = 0;
4f8c897… drh 1945
2116238… drh 1946 if( argc>3 && TH1_LEN(argl[1])==11
2116238… drh 1947 && strncmp(argv[1], "-nocomplain", 11)==0
2116238… drh 1948 ){
c8289bd… drh 1949 argc--;
c8289bd… drh 1950 argv++;
c8289bd… drh 1951 argl++;
c8289bd… drh 1952 noComplain = 1;
c8289bd… drh 1953 }
4f8c897… drh 1954 if( argc!=3 ){
4f8c897… drh 1955 return Th_WrongNumArgs(interp, "query SQL CODE");
4f8c897… drh 1956 }
4f8c897… drh 1957 if( g.db==0 ){
c8289bd… drh 1958 if( noComplain ) return TH_OK;
4f8c897… drh 1959 Th_ErrorMessage(interp, "database is not open", 0, 0);
4f8c897… drh 1960 return TH_ERROR;
4f8c897… drh 1961 }
4f8c897… drh 1962 zSql = argv[1];
4f8c897… drh 1963 nSql = argl[1];
2116238… drh 1964 if( TH1_TAINTED(nSql) ){
2116238… drh 1965 if( Th_ReportTaint(interp,"query SQL",zSql,nSql) ){
2116238… drh 1966 return TH_ERROR;
2116238… drh 1967 }
2116238… drh 1968 nSql = TH1_LEN(nSql);
2116238… drh 1969 }
2116238… drh 1970
4f8c897… drh 1971 while( res==TH_OK && nSql>0 ){
2056f62… drh 1972 zErr = 0;
3071475… drh 1973 report_restrict_sql(&zErr);
801eca6… drh 1974 g.dbIgnoreErrors++;
2116238… drh 1975 rc = sqlite3_prepare_v2(g.db, argv[1], TH1_LEN(argl[1]), &pStmt, &zTail);
801eca6… drh 1976 g.dbIgnoreErrors--;
3071475… drh 1977 report_unrestrict_sql();
2056f62… drh 1978 if( rc!=0 || zErr!=0 ){
c8289bd… drh 1979 if( noComplain ) return TH_OK;
2056f62… drh 1980 Th_ErrorMessage(interp, "SQL error: ",
2056f62… drh 1981 zErr ? zErr : sqlite3_errmsg(g.db), -1);
4f8c897… drh 1982 return TH_ERROR;
4f8c897… drh 1983 }
4f8c897… drh 1984 n = (int)(zTail - zSql);
4f8c897… drh 1985 zSql += n;
4f8c897… drh 1986 nSql -= n;
4f8c897… drh 1987 if( pStmt==0 ) continue;
4f8c897… drh 1988 nVar = sqlite3_bind_parameter_count(pStmt);
4f8c897… drh 1989 for(i=1; i<=nVar; i++){
4f8c897… drh 1990 const char *zVar = sqlite3_bind_parameter_name(pStmt, i);
4f8c897… drh 1991 int szVar = zVar ? th_strlen(zVar) : 0;
4f8c897… drh 1992 if( szVar>1 && zVar[0]=='$'
4f8c897… drh 1993 && Th_GetVar(interp, zVar+1, szVar-1)==TH_OK ){
4f8c897… drh 1994 int nVal;
4f8c897… drh 1995 const char *zVal = Th_GetResult(interp, &nVal);
2116238… drh 1996 sqlite3_bind_text(pStmt, i, zVal, TH1_LEN(nVal), SQLITE_TRANSIENT);
4f8c897… drh 1997 }
4f8c897… drh 1998 }
801eca6… drh 1999 while( res==TH_OK && ignore_errors_step(pStmt)==SQLITE_ROW ){
4f8c897… drh 2000 int nCol = sqlite3_column_count(pStmt);
4f8c897… drh 2001 for(i=0; i<nCol; i++){
4f8c897… drh 2002 const char *zCol = sqlite3_column_name(pStmt, i);
4f8c897… drh 2003 int szCol = th_strlen(zCol);
4f8c897… drh 2004 const char *zVal = (const char*)sqlite3_column_text(pStmt, i);
4f8c897… drh 2005 int szVal = sqlite3_column_bytes(pStmt, i);
2116238… drh 2006 Th_SetVar(interp, zCol, szCol, zVal, TH1_ADD_TAINT(szVal));
fee9ede… mistachkin 2007 }
fee9ede… mistachkin 2008 if( g.thTrace ){
2116238… drh 2009 Th_Trace("query_eval {<pre>%#h</pre>}<br>\n",TH1_LEN(argl[2]),argv[2]);
4f8c897… drh 2010 }
2116238… drh 2011 res = Th_Eval(interp, 0, argv[2], TH1_LEN(argl[2]));
fee9ede… mistachkin 2012 if( g.thTrace ){
fee9ede… mistachkin 2013 int nTrRes;
fee9ede… mistachkin 2014 char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes);
f5482a0… wyoung 2015 Th_Trace("[query_eval] => %h {%#h}<br>\n",
2116238… drh 2016 Th_ReturnCodeName(res, 0), TH1_LEN(nTrRes), zTrRes);
fee9ede… mistachkin 2017 }
4f8c897… drh 2018 if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK;
4f8c897… drh 2019 }
4f8c897… drh 2020 rc = sqlite3_finalize(pStmt);
4f8c897… drh 2021 if( rc!=SQLITE_OK ){
c8289bd… drh 2022 if( noComplain ) return TH_OK;
4f8c897… drh 2023 Th_ErrorMessage(interp, "SQL error: ", sqlite3_errmsg(g.db), -1);
4f8c897… drh 2024 return TH_ERROR;
4f8c897… drh 2025 }
b000db4… mistachkin 2026 }
4f8c897… drh 2027 return res;
d772ff7… mistachkin 2028 }
d772ff7… mistachkin 2029
d772ff7… mistachkin 2030 /*
109d8f5… mistachkin 2031 ** TH1 command: setting name
0264475… mistachkin 2032 **
0264475… mistachkin 2033 ** Gets and returns the value of the specified Fossil setting.
0264475… mistachkin 2034 */
0264475… mistachkin 2035 #define SETTING_WRONGNUMARGS "setting ?-strict? ?--? name"
0264475… mistachkin 2036 static int settingCmd(
0264475… mistachkin 2037 Th_Interp *interp,
0264475… mistachkin 2038 void *p,
0264475… mistachkin 2039 int argc,
0264475… mistachkin 2040 const char **argv,
0264475… mistachkin 2041 int *argl
0264475… mistachkin 2042 ){
0264475… mistachkin 2043 int rc;
0264475… mistachkin 2044 int strict = 0;
0264475… mistachkin 2045 int nArg = 1;
0264475… mistachkin 2046 char *zValue;
0264475… mistachkin 2047 if( argc<2 || argc>4 ){
0264475… mistachkin 2048 return Th_WrongNumArgs(interp, SETTING_WRONGNUMARGS);
0264475… mistachkin 2049 }
0264475… mistachkin 2050 if( fossil_strcmp(argv[nArg], "-strict")==0 ){
0264475… mistachkin 2051 strict = 1; nArg++;
0264475… mistachkin 2052 }
0264475… mistachkin 2053 if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
0264475… mistachkin 2054 if( nArg+1!=argc ){
0264475… mistachkin 2055 return Th_WrongNumArgs(interp, SETTING_WRONGNUMARGS);
0264475… mistachkin 2056 }
0264475… mistachkin 2057 zValue = db_get(argv[nArg], 0);
0264475… mistachkin 2058 if( zValue!=0 ){
0264475… mistachkin 2059 Th_SetResult(interp, zValue, -1);
0264475… mistachkin 2060 rc = TH_OK;
0264475… mistachkin 2061 }else if( strict ){
0264475… mistachkin 2062 Th_ErrorMessage(interp, "no value for setting \"", argv[nArg], -1);
0264475… mistachkin 2063 rc = TH_ERROR;
0264475… mistachkin 2064 }else{
0264475… mistachkin 2065 Th_SetResult(interp, 0, 0);
0264475… mistachkin 2066 rc = TH_OK;
0264475… mistachkin 2067 }
0264475… mistachkin 2068 if( g.thTrace ){
f5482a0… wyoung 2069 Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "",
2116238… drh 2070 TH1_LEN(argl[nArg]), argv[nArg], rc);
62f1f48… mistachkin 2071 }
62f1f48… mistachkin 2072 return rc;
62f1f48… mistachkin 2073 }
62f1f48… mistachkin 2074
62f1f48… mistachkin 2075 /*
62f1f48… mistachkin 2076 ** TH1 command: glob_match ?-one? ?--? patternList string
62f1f48… mistachkin 2077 **
62f1f48… mistachkin 2078 ** Checks the string against the specified glob pattern -OR- list of glob
62f1f48… mistachkin 2079 ** patterns and returns non-zero if there is a match.
62f1f48… mistachkin 2080 */
62f1f48… mistachkin 2081 #define GLOB_MATCH_WRONGNUMARGS "glob_match ?-one? ?--? patternList string"
62f1f48… mistachkin 2082 static int globMatchCmd(
62f1f48… mistachkin 2083 Th_Interp *interp,
62f1f48… mistachkin 2084 void *p,
62f1f48… mistachkin 2085 int argc,
62f1f48… mistachkin 2086 const char **argv,
62f1f48… mistachkin 2087 int *argl
62f1f48… mistachkin 2088 ){
62f1f48… mistachkin 2089 int rc;
62f1f48… mistachkin 2090 int one = 0;
62f1f48… mistachkin 2091 int nArg = 1;
62f1f48… mistachkin 2092 Glob *pGlob = 0;
62f1f48… mistachkin 2093 if( argc<3 || argc>5 ){
62f1f48… mistachkin 2094 return Th_WrongNumArgs(interp, GLOB_MATCH_WRONGNUMARGS);
62f1f48… mistachkin 2095 }
62f1f48… mistachkin 2096 if( fossil_strcmp(argv[nArg], "-one")==0 ){
62f1f48… mistachkin 2097 one = 1; nArg++;
62f1f48… mistachkin 2098 }
62f1f48… mistachkin 2099 if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
62f1f48… mistachkin 2100 if( nArg+2!=argc ){
62f1f48… mistachkin 2101 return Th_WrongNumArgs(interp, GLOB_MATCH_WRONGNUMARGS);
62f1f48… mistachkin 2102 }
62f1f48… mistachkin 2103 if( one ){
62f1f48… mistachkin 2104 Th_SetResultInt(interp, sqlite3_strglob(argv[nArg], argv[nArg+1])==0);
62f1f48… mistachkin 2105 rc = TH_OK;
62f1f48… mistachkin 2106 }else{
62f1f48… mistachkin 2107 pGlob = glob_create(argv[nArg]);
62f1f48… mistachkin 2108 if( pGlob ){
62f1f48… mistachkin 2109 Th_SetResultInt(interp, glob_match(pGlob, argv[nArg+1]));
62f1f48… mistachkin 2110 rc = TH_OK;
62f1f48… mistachkin 2111 }else{
62f1f48… mistachkin 2112 Th_SetResult(interp, "unable to create glob from pattern list", -1);
62f1f48… mistachkin 2113 rc = TH_ERROR;
62f1f48… mistachkin 2114 }
62f1f48… mistachkin 2115 glob_free(pGlob);
0264475… mistachkin 2116 }
0264475… mistachkin 2117 return rc;
0264475… mistachkin 2118 }
0264475… mistachkin 2119
0264475… mistachkin 2120 /*
109d8f5… mistachkin 2121 ** TH1 command: regexp ?-nocase? ?--? exp string
d772ff7… mistachkin 2122 **
d772ff7… mistachkin 2123 ** Checks the string against the specified regular expression and returns
d772ff7… mistachkin 2124 ** non-zero if it matches. If the regular expression is invalid or cannot
d772ff7… mistachkin 2125 ** be compiled, an error will be generated.
d772ff7… mistachkin 2126 */
d772ff7… mistachkin 2127 #define REGEXP_WRONGNUMARGS "regexp ?-nocase? ?--? exp string"
d772ff7… mistachkin 2128 static int regexpCmd(
d772ff7… mistachkin 2129 Th_Interp *interp,
d772ff7… mistachkin 2130 void *p,
d772ff7… mistachkin 2131 int argc,
d772ff7… mistachkin 2132 const char **argv,
d772ff7… mistachkin 2133 int *argl
d772ff7… mistachkin 2134 ){
d772ff7… mistachkin 2135 int rc;
d772ff7… mistachkin 2136 int noCase = 0;
d772ff7… mistachkin 2137 int nArg = 1;
d772ff7… mistachkin 2138 ReCompiled *pRe = 0;
d772ff7… mistachkin 2139 const char *zErr;
d772ff7… mistachkin 2140 if( argc<3 || argc>5 ){
d772ff7… mistachkin 2141 return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
d772ff7… mistachkin 2142 }
d772ff7… mistachkin 2143 if( fossil_strcmp(argv[nArg], "-nocase")==0 ){
d772ff7… mistachkin 2144 noCase = 1; nArg++;
d772ff7… mistachkin 2145 }
d772ff7… mistachkin 2146 if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
d772ff7… mistachkin 2147 if( nArg+2!=argc ){
d772ff7… mistachkin 2148 return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
d772ff7… mistachkin 2149 }
82888a0… drh 2150 zErr = fossil_re_compile(&pRe, argv[nArg], noCase);
d772ff7… mistachkin 2151 if( !zErr ){
d772ff7… mistachkin 2152 Th_SetResultInt(interp, re_match(pRe,
2116238… drh 2153 (const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1])));
d772ff7… mistachkin 2154 rc = TH_OK;
d772ff7… mistachkin 2155 }else{
d772ff7… mistachkin 2156 Th_SetResult(interp, zErr, -1);
d772ff7… mistachkin 2157 rc = TH_ERROR;
d772ff7… mistachkin 2158 }
d772ff7… mistachkin 2159 re_free(pRe);
d772ff7… mistachkin 2160 return rc;
1311841… jan.nijtmans 2161 }
1311841… jan.nijtmans 2162
1311841… jan.nijtmans 2163 /*
109d8f5… mistachkin 2164 ** TH1 command: http ?-asynchronous? ?--? url ?payload?
1311841… jan.nijtmans 2165 **
1311841… jan.nijtmans 2166 ** Perform an HTTP or HTTPS request for the specified URL. If a
1311841… jan.nijtmans 2167 ** payload is present, it will be interpreted as text/plain and
1311841… jan.nijtmans 2168 ** the POST method will be used; otherwise, the GET method will
1311841… jan.nijtmans 2169 ** be used. Upon success, if the -asynchronous option is used, an
1311841… jan.nijtmans 2170 ** empty string is returned as the result; otherwise, the response
1311841… jan.nijtmans 2171 ** from the server is returned as the result. Synchronous requests
1311841… jan.nijtmans 2172 ** are not currently implemented.
1311841… jan.nijtmans 2173 */
1311841… jan.nijtmans 2174 #define HTTP_WRONGNUMARGS "http ?-asynchronous? ?--? url ?payload?"
1311841… jan.nijtmans 2175 static int httpCmd(
1311841… jan.nijtmans 2176 Th_Interp *interp,
1311841… jan.nijtmans 2177 void *p,
1311841… jan.nijtmans 2178 int argc,
1311841… jan.nijtmans 2179 const char **argv,
1311841… jan.nijtmans 2180 int *argl
1311841… jan.nijtmans 2181 ){
1311841… jan.nijtmans 2182 int nArg = 1;
1311841… jan.nijtmans 2183 int fAsynchronous = 0;
1311841… jan.nijtmans 2184 const char *zType, *zRegexp;
1311841… jan.nijtmans 2185 Blob payload;
1311841… jan.nijtmans 2186 ReCompiled *pRe = 0;
1311841… jan.nijtmans 2187 UrlData urlData;
1311841… jan.nijtmans 2188
1311841… jan.nijtmans 2189 if( argc<2 || argc>5 ){
1311841… jan.nijtmans 2190 return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS);
1311841… jan.nijtmans 2191 }
2116238… drh 2192 if( fossil_strnicmp(argv[nArg], "-asynchronous", TH1_LEN(argl[nArg]))==0 ){
1311841… jan.nijtmans 2193 fAsynchronous = 1; nArg++;
1311841… jan.nijtmans 2194 }
1311841… jan.nijtmans 2195 if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
1311841… jan.nijtmans 2196 if( nArg+1!=argc && nArg+2!=argc ){
1311841… jan.nijtmans 2197 return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
1311841… jan.nijtmans 2198 }
1311841… jan.nijtmans 2199 memset(&urlData, '\0', sizeof(urlData));
1311841… jan.nijtmans 2200 url_parse_local(argv[nArg], 0, &urlData);
1311841… jan.nijtmans 2201 if( urlData.isSsh || urlData.isFile ){
1311841… jan.nijtmans 2202 Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0);
1311841… jan.nijtmans 2203 return TH_ERROR;
1311841… jan.nijtmans 2204 }
1311841… jan.nijtmans 2205 zRegexp = db_get("th1-uri-regexp", 0);
1311841… jan.nijtmans 2206 if( zRegexp && zRegexp[0] ){
82888a0… drh 2207 const char *zErr = fossil_re_compile(&pRe, zRegexp, 0);
1311841… jan.nijtmans 2208 if( zErr ){
1311841… jan.nijtmans 2209 Th_SetResult(interp, zErr, -1);
1311841… jan.nijtmans 2210 return TH_ERROR;
1311841… jan.nijtmans 2211 }
1311841… jan.nijtmans 2212 }
1311841… jan.nijtmans 2213 if( !pRe || !re_match(pRe, (const unsigned char *)urlData.canonical, -1) ){
1311841… jan.nijtmans 2214 Th_SetResult(interp, "url not allowed", -1);
1311841… jan.nijtmans 2215 re_free(pRe);
1311841… jan.nijtmans 2216 return TH_ERROR;
1311841… jan.nijtmans 2217 }
1311841… jan.nijtmans 2218 re_free(pRe);
1311841… jan.nijtmans 2219 blob_zero(&payload);
1311841… jan.nijtmans 2220 if( nArg+2==argc ){
2116238… drh 2221 blob_append(&payload, argv[nArg+1], TH1_LEN(argl[nArg+1]));
1311841… jan.nijtmans 2222 zType = "POST";
1311841… jan.nijtmans 2223 }else{
1311841… jan.nijtmans 2224 zType = "GET";
1311841… jan.nijtmans 2225 }
1311841… jan.nijtmans 2226 if( fAsynchronous ){
1311841… jan.nijtmans 2227 const char *zSep, *zParams;
1311841… jan.nijtmans 2228 Blob hdr;
1311841… jan.nijtmans 2229 zParams = strrchr(argv[nArg], '?');
1311841… jan.nijtmans 2230 if( strlen(urlData.path)>0 && zParams!=argv[nArg] ){
1311841… jan.nijtmans 2231 zSep = "";
1311841… jan.nijtmans 2232 }else{
1311841… jan.nijtmans 2233 zSep = "/";
1311841… jan.nijtmans 2234 }
1311841… jan.nijtmans 2235 blob_zero(&hdr);
1311841… jan.nijtmans 2236 blob_appendf(&hdr, "%s %s%s%s HTTP/1.0\r\n",
1311841… jan.nijtmans 2237 zType, zSep, urlData.path, zParams ? zParams : "");
1311841… jan.nijtmans 2238 if( urlData.proxyAuth ){
1311841… jan.nijtmans 2239 blob_appendf(&hdr, "Proxy-Authorization: %s\r\n", urlData.proxyAuth);
1311841… jan.nijtmans 2240 }
1311841… jan.nijtmans 2241 if( urlData.passwd && urlData.user && urlData.passwd[0]=='#' ){
1311841… jan.nijtmans 2242 char *zCredentials = mprintf("%s:%s", urlData.user, &urlData.passwd[1]);
1311841… jan.nijtmans 2243 char *zEncoded = encode64(zCredentials, -1);
1311841… jan.nijtmans 2244 blob_appendf(&hdr, "Authorization: Basic %s\r\n", zEncoded);
1311841… jan.nijtmans 2245 fossil_free(zEncoded);
1311841… jan.nijtmans 2246 fossil_free(zCredentials);
1311841… jan.nijtmans 2247 }
1311841… jan.nijtmans 2248 blob_appendf(&hdr, "Host: %s\r\n"
1311841… jan.nijtmans 2249 "User-Agent: %s\r\n", urlData.hostname, get_user_agent());
1311841… jan.nijtmans 2250 if( zType[0]=='P' ){
1311841… jan.nijtmans 2251 blob_appendf(&hdr, "Content-Type: application/x-www-form-urlencoded\r\n"
1311841… jan.nijtmans 2252 "Content-Length: %d\r\n\r\n", blob_size(&payload));
1311841… jan.nijtmans 2253 }else{
1311841… jan.nijtmans 2254 blob_appendf(&hdr, "\r\n");
1311841… jan.nijtmans 2255 }
1311841… jan.nijtmans 2256 if( transport_open(&urlData) ){
1311841… jan.nijtmans 2257 Th_ErrorMessage(interp, transport_errmsg(&urlData), 0, 0);
1311841… jan.nijtmans 2258 blob_reset(&hdr);
1311841… jan.nijtmans 2259 blob_reset(&payload);
1311841… jan.nijtmans 2260 return TH_ERROR;
1311841… jan.nijtmans 2261 }
1311841… jan.nijtmans 2262 transport_send(&urlData, &hdr);
1311841… jan.nijtmans 2263 transport_send(&urlData, &payload);
1311841… jan.nijtmans 2264 blob_reset(&hdr);
1311841… jan.nijtmans 2265 blob_reset(&payload);
1311841… jan.nijtmans 2266 transport_close(&urlData);
1311841… jan.nijtmans 2267 Th_SetResult(interp, 0, 0); /* NOTE: Asynchronous, no results. */
1311841… jan.nijtmans 2268 return TH_OK;
1311841… jan.nijtmans 2269 }else{
1311841… jan.nijtmans 2270 Th_ErrorMessage(interp,
1311841… jan.nijtmans 2271 "synchronous requests are not yet implemented", 0, 0);
1311841… jan.nijtmans 2272 blob_reset(&payload);
1311841… jan.nijtmans 2273 return TH_ERROR;
1311841… jan.nijtmans 2274 }
1311841… jan.nijtmans 2275 }
5173701… stephan 2276
5173701… stephan 2277 /*
5173701… stephan 2278 ** TH1 command: captureTh1 STRING
5173701… stephan 2279 **
5173701… stephan 2280 ** Evaluates the given string as TH1 code and captures any of its
5173701… stephan 2281 ** TH1-generated output as a string (instead of it being output),
5173701… stephan 2282 ** which becomes the result of the function.
5173701… stephan 2283 */
5173701… stephan 2284 static int captureTh1Cmd(
5173701… stephan 2285 Th_Interp *interp,
5173701… stephan 2286 void *pConvert,
5173701… stephan 2287 int argc,
5173701… stephan 2288 const char **argv,
5173701… stephan 2289 int *argl
5173701… stephan 2290 ){
5173701… stephan 2291 Blob out = empty_blob;
5173701… stephan 2292 Blob * pOrig;
5173701… stephan 2293 const char * zStr;
5173701… stephan 2294 int nStr, rc;
5173701… stephan 2295 if( argc!=2 ){
5173701… stephan 2296 return Th_WrongNumArgs(interp, "captureTh1 STRING");
5173701… stephan 2297 }
5173701… stephan 2298 pOrig = Th_SetOutputBlob(&out);
5173701… stephan 2299 zStr = argv[1];
2116238… drh 2300 nStr = TH1_LEN(argl[1]);
5173701… stephan 2301 rc = Th_Eval(g.interp, 0, zStr, nStr);
5173701… stephan 2302 Th_SetOutputBlob(pOrig);
5173701… stephan 2303 if(0==rc){
5173701… stephan 2304 Th_SetResult(g.interp, blob_str(&out), blob_size(&out));
5173701… stephan 2305 }
5173701… stephan 2306 blob_reset(&out);
5173701… stephan 2307 return rc;
5173701… stephan 2308 }
5173701… stephan 2309
57d8a71… mistachkin 2310
57d8a71… mistachkin 2311 /*
9773eba… mistachkin 2312 ** Attempts to open the configuration ("user") database. Optionally, also
9773eba… mistachkin 2313 ** attempts to try to find the repository and open it.
9773eba… mistachkin 2314 */
9773eba… mistachkin 2315 void Th_OpenConfig(
9773eba… mistachkin 2316 int openRepository
9773eba… mistachkin 2317 ){
142200b… mistachkin 2318 if( openRepository && !Th_IsRepositoryOpen() ){
9773eba… mistachkin 2319 db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0);
142200b… mistachkin 2320 if( Th_IsRepositoryOpen() ){
142200b… mistachkin 2321 g.th1Flags |= TH_STATE_REPOSITORY;
142200b… mistachkin 2322 }else{
142200b… mistachkin 2323 g.th1Flags &= ~TH_STATE_REPOSITORY;
142200b… mistachkin 2324 }
1c528d3… mistachkin 2325 }
142200b… mistachkin 2326 if( !Th_IsConfigOpen() ){
b06cd63… mistachkin 2327 db_open_config(0, 1);
142200b… mistachkin 2328 if( Th_IsConfigOpen() ){
142200b… mistachkin 2329 g.th1Flags |= TH_STATE_CONFIG;
142200b… mistachkin 2330 }else{
142200b… mistachkin 2331 g.th1Flags &= ~TH_STATE_CONFIG;
142200b… mistachkin 2332 }
142200b… mistachkin 2333 }
1c528d3… mistachkin 2334 }
1c528d3… mistachkin 2335
1c528d3… mistachkin 2336 /*
1c528d3… mistachkin 2337 ** Attempts to close the configuration ("user") database. Optionally, also
1c528d3… mistachkin 2338 ** attempts to close the repository.
1c528d3… mistachkin 2339 */
1c528d3… mistachkin 2340 void Th_CloseConfig(
1c528d3… mistachkin 2341 int closeRepository
1c528d3… mistachkin 2342 ){
142200b… mistachkin 2343 if( g.th1Flags & TH_STATE_CONFIG ){
142200b… mistachkin 2344 db_close_config();
142200b… mistachkin 2345 g.th1Flags &= ~TH_STATE_CONFIG;
142200b… mistachkin 2346 }
142200b… mistachkin 2347 if( closeRepository && (g.th1Flags & TH_STATE_REPOSITORY) ){
1c528d3… mistachkin 2348 db_close(1);
142200b… mistachkin 2349 g.th1Flags &= ~TH_STATE_REPOSITORY;
1c528d3… mistachkin 2350 }
9773eba… mistachkin 2351 }
9773eba… mistachkin 2352
9773eba… mistachkin 2353 /*
f55c6a1… drh 2354 ** Make sure the interpreter has been initialized. Initialize it if
f55c6a1… drh 2355 ** it has not been already.
f55c6a1… drh 2356 **
f55c6a1… drh 2357 ** The interpreter is stored in the g.interp global variable.
588bb7c… aku 2358 */
0e68620… mistachkin 2359 void Th_FossilInit(u32 flags){
b058c8a… mistachkin 2360 int wasInit = 0;
0e68620… mistachkin 2361 int needConfig = flags & TH_INIT_NEED_CONFIG;
0e68620… mistachkin 2362 int forceReset = flags & TH_INIT_FORCE_RESET;
0e68620… mistachkin 2363 int forceTcl = flags & TH_INIT_FORCE_TCL;
0e68620… mistachkin 2364 int forceSetup = flags & TH_INIT_FORCE_SETUP;
57d8a71… mistachkin 2365 int noRepo = flags & TH_INIT_NO_REPO;
5173701… stephan 2366 static unsigned int aFlags[] = {0, 1, WIKI_LINKSONLY};
653dd40… drh 2367 static int anonFlag = LOGIN_ANON;
653dd40… drh 2368 static int zeroInt = 0;
588bb7c… aku 2369 static struct _Command {
588bb7c… aku 2370 const char *zName;
588bb7c… aku 2371 Th_CommandProc xProc;
588bb7c… aku 2372 void *pContext;
588bb7c… aku 2373 } aCommand[] = {
653dd40… drh 2374 {"anoncap", hascapCmd, (void*)&anonFlag},
9b70675… drh 2375 {"anycap", anycapCmd, 0},
109d8f5… mistachkin 2376 {"artifact", artifactCmd, 0},
9cd7428… drh 2377 {"builtin_request_js", builtinRequestJsCmd, 0},
f96de5a… drh 2378 {"capexpr", capexprCmd, 0},
5173701… stephan 2379 {"captureTh1", captureTh1Cmd, 0},
32c4880… mistachkin 2380 {"cgiHeaderLine", cgiHeaderLineCmd, 0},
57be4d5… mistachkin 2381 {"checkout", checkoutCmd, 0},
588bb7c… aku 2382 {"combobox", comboboxCmd, 0},
6908832… drh 2383 {"copybtn", copybtnCmd, 0},
46e6a03… drh 2384 {"date", dateCmd, 0},
74e3f90… drh 2385 {"decorate", wikiCmd, (void*)&aFlags[2]},
c1cb688… mistachkin 2386 {"defHeader", defHeaderCmd, 0},
f4ceace… mistachkin 2387 {"dir", dirCmd, 0},
74e3f90… drh 2388 {"enable_output", enableOutputCmd, 0},
3f21421… mistachkin 2389 {"encode64", encode64Cmd, 0},
57be4d5… mistachkin 2390 {"getParameter", getParameterCmd, 0},
62f1f48… mistachkin 2391 {"glob_match", globMatchCmd, 0},
66ae70a… drh 2392 {"globalState", globalStateCmd, 0},
fc6bb93… jan.nijtmans 2393 {"httpize", httpizeCmd, 0},
653dd40… drh 2394 {"hascap", hascapCmd, (void*)&zeroInt},
74e3f90… drh 2395 {"hasfeature", hasfeatureCmd, 0},
74e3f90… drh 2396 {"html", putsCmd, (void*)&aFlags[0]},
74e3f90… drh 2397 {"htmlize", htmlizeCmd, 0},
1311841… jan.nijtmans 2398 {"http", httpCmd, 0},
f8820ef… mistachkin 2399 {"insertCsrf", insertCsrfCmd, 0},
74e3f90… drh 2400 {"linecount", linecntCmd, 0},
f6d29e9… mistachkin 2401 {"markdown", markdownCmd, 0},
8a65cd1… mistachkin 2402 {"nonce", nonceCmd, 0},
74e3f90… drh 2403 {"puts", putsCmd, (void*)&aFlags[1]},
4f8c897… drh 2404 {"query", queryCmd, 0},
74e3f90… drh 2405 {"randhex", randhexCmd, 0},
71caba5… mistachkin 2406 {"redirect", redirectCmd, 0},
d772ff7… mistachkin 2407 {"regexp", regexpCmd, 0},
3355835… mistachkin 2408 {"reinitialize", reinitializeCmd, 0},
57be4d5… mistachkin 2409 {"render", renderCmd, 0},
d8ed5a0… drh 2410 {"repository", repositoryCmd, 0},
c0c0bae… drh 2411 {"searchable", searchableCmd, 0},
57be4d5… mistachkin 2412 {"setParameter", setParameterCmd, 0},
0264475… mistachkin 2413 {"setting", settingCmd, 0},
57be4d5… mistachkin 2414 {"styleFooter", styleFooterCmd, 0},
9c88799… drh 2415 {"styleHeader", styleHeaderCmd, 0},
9c88799… drh 2416 {"styleScript", styleScriptCmd, 0},
71b2216… george 2417 {"submenu", submenuCmd, 0},
2116238… drh 2418 {"taint", taintCmd, 0},
a87eaae… mistachkin 2419 {"tclReady", tclReadyCmd, 0},
57be4d5… mistachkin 2420 {"trace", traceCmd, 0},
3d50bdc… drh 2421 {"stime", stimeCmd, 0},
2116238… drh 2422 {"untaint", untaintCmd, 0},
1b5b69f… mistachkin 2423 {"unversioned", unversionedCmd, 0},
74e3f90… drh 2424 {"utime", utimeCmd, 0},
f8820ef… mistachkin 2425 {"verifyCsrf", verifyCsrfCmd, 0},
a470d60… mistachkin 2426 {"verifyLogin", verifyLoginCmd, 0},
74e3f90… drh 2427 {"wiki", wikiCmd, (void*)&aFlags[0]},
25f43cc… stephan 2428 {"wiki_assoc", wikiAssocCmd, 0},
1b1fd23… mistachkin 2429 {0, 0, 0}
588bb7c… aku 2430 };
3355835… mistachkin 2431 if( g.thTrace ){
f5482a0… wyoung 2432 Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags);
3355835… mistachkin 2433 }
b058c8a… mistachkin 2434 if( needConfig ){
b058c8a… mistachkin 2435 /*
b058c8a… mistachkin 2436 ** This function uses several settings which may be defined in the
b058c8a… mistachkin 2437 ** repository and/or the global configuration. Since the caller
b058c8a… mistachkin 2438 ** passed a non-zero value for the needConfig parameter, make sure
b058c8a… mistachkin 2439 ** the necessary database connections are open prior to continuing.
b058c8a… mistachkin 2440 */
57d8a71… mistachkin 2441 Th_OpenConfig(!noRepo);
b058c8a… mistachkin 2442 }
0e68620… mistachkin 2443 if( forceReset || forceTcl || g.interp==0 ){
5d20065… mistachkin 2444 int created = 0;
588bb7c… aku 2445 int i;
5d20065… mistachkin 2446 if( g.interp==0 ){
999e33c… mistachkin 2447 Th_Vtab *pVtab = 0;
999e33c… mistachkin 2448 #if defined(TH_MEMDEBUG)
999e33c… mistachkin 2449 if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
999e33c… mistachkin 2450 pVtab = &vtab;
999e33c… mistachkin 2451 if( g.thTrace ){
f5482a0… wyoung 2452 Th_Trace("th1-init MEMDEBUG ENABLED<br>\n");
999e33c… mistachkin 2453 }
999e33c… mistachkin 2454 }
999e33c… mistachkin 2455 #endif
999e33c… mistachkin 2456 g.interp = Th_CreateInterp(pVtab);
5d20065… mistachkin 2457 created = 1;
5d20065… mistachkin 2458 }
5d20065… mistachkin 2459 if( forceReset || created ){
5d20065… mistachkin 2460 th_register_language(g.interp); /* Basic scripting commands. */
0e68620… mistachkin 2461 }
d8ed5a0… drh 2462 #ifdef FOSSIL_ENABLE_TCL
e0f22dd… mistachkin 2463 if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 ||
e0f22dd… mistachkin 2464 db_get_boolean("tcl", 0) ){
b058c8a… mistachkin 2465 if( !g.tcl.setup ){
b058c8a… mistachkin 2466 g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */
b058c8a… mistachkin 2467 }
d8ed5a0… drh 2468 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
d8ed5a0… drh 2469 }
d8ed5a0… drh 2470 #endif
3cb9ba4… andygoth 2471 for(i=0; i<count(aCommand); i++){
1b1fd23… mistachkin 2472 if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
588bb7c… aku 2473 Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
588bb7c… aku 2474 aCommand[i].pContext, 0);
588bb7c… aku 2475 }
b058c8a… mistachkin 2476 }else{
b058c8a… mistachkin 2477 wasInit = 1;
b058c8a… mistachkin 2478 }
b058c8a… mistachkin 2479 if( forceSetup || !wasInit ){
b058c8a… mistachkin 2480 int rc = TH_OK;
b058c8a… mistachkin 2481 if( !g.th1Setup ){
b058c8a… mistachkin 2482 g.th1Setup = db_get("th1-setup", 0); /* Grab TH1 setup script. */
b058c8a… mistachkin 2483 }
b058c8a… mistachkin 2484 if( g.th1Setup ){
b058c8a… mistachkin 2485 rc = Th_Eval(g.interp, 0, g.th1Setup, -1);
b058c8a… mistachkin 2486 if( rc==TH_ERROR ){
b058c8a… mistachkin 2487 int nResult = 0;
b058c8a… mistachkin 2488 char *zResult = (char*)Th_GetResult(g.interp, &nResult);
5173701… stephan 2489 sendError(0,zResult, nResult, 0);
b058c8a… mistachkin 2490 }
b058c8a… mistachkin 2491 }
b058c8a… mistachkin 2492 if( g.thTrace ){
f5482a0… wyoung 2493 Th_Trace("th1-setup {%h} => %h<br>\n", g.th1Setup,
6c47a16… mistachkin 2494 Th_ReturnCodeName(rc, 0));
6c47a16… mistachkin 2495 }
142200b… mistachkin 2496 }
142200b… mistachkin 2497 g.th1Flags &= ~TH_INIT_MASK;
142200b… mistachkin 2498 g.th1Flags |= (flags & TH_INIT_MASK);
8a65cd1… mistachkin 2499 }
8a65cd1… mistachkin 2500
8a65cd1… mistachkin 2501 /*
8a65cd1… mistachkin 2502 ** Store a string value in a variable in the interpreter if the variable
8a65cd1… mistachkin 2503 ** does not already exist.
8a65cd1… mistachkin 2504 */
8a65cd1… mistachkin 2505 void Th_MaybeStore(const char *zName, const char *zValue){
8a65cd1… mistachkin 2506 Th_FossilInit(TH_INIT_DEFAULT);
8a65cd1… mistachkin 2507 if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){
8a65cd1… mistachkin 2508 if( g.thTrace ){
f5482a0… wyoung 2509 Th_Trace("maybe_set %h {%h}<br>\n", zName, zValue);
8a65cd1… mistachkin 2510 }
8a65cd1… mistachkin 2511 Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
8a65cd1… mistachkin 2512 }
588bb7c… aku 2513 }
588bb7c… aku 2514
588bb7c… aku 2515 /*
588bb7c… aku 2516 ** Store a string value in a variable in the interpreter.
588bb7c… aku 2517 */
588bb7c… aku 2518 void Th_Store(const char *zName, const char *zValue){
0e68620… mistachkin 2519 Th_FossilInit(TH_INIT_DEFAULT);
f55c6a1… drh 2520 if( zValue ){
f55c6a1… drh 2521 if( g.thTrace ){
f5482a0… wyoung 2522 Th_Trace("set %h {%h}<br>\n", zName, zValue);
f55c6a1… drh 2523 }
f55c6a1… drh 2524 Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
2116238… drh 2525 }
2116238… drh 2526 }
2116238… drh 2527
2116238… drh 2528 /*
2116238… drh 2529 ** Store a string value in a variable in the interpreter
2116238… drh 2530 ** with the "taint" marking, so that TH1 knows that this
2116238… drh 2531 ** variable contains content under the control of the remote
2116238… drh 2532 ** user and presents a risk of XSS or SQL-injection attacks.
2116238… drh 2533 */
2116238… drh 2534 void Th_StoreUnsafe(const char *zName, const char *zValue){
2116238… drh 2535 Th_FossilInit(TH_INIT_DEFAULT);
2116238… drh 2536 if( zValue ){
2116238… drh 2537 if( g.thTrace ){
2116238… drh 2538 Th_Trace("set %h [taint {%h}]<br>\n", zName, zValue);
2116238… drh 2539 }
2116238… drh 2540 Th_SetVar(g.interp, zName, -1, zValue, TH1_ADD_TAINT(strlen(zValue)));
fc32fae… mistachkin 2541 }
fc32fae… mistachkin 2542 }
fc32fae… mistachkin 2543
fc32fae… mistachkin 2544 /*
fc32fae… mistachkin 2545 ** Appends an element to a TH1 list value. This function is called by the
fc32fae… mistachkin 2546 ** transfer subsystem; therefore, it must be very careful to avoid doing
fc32fae… mistachkin 2547 ** any unnecessary work. To that end, the TH1 subsystem will not be called
fc32fae… mistachkin 2548 ** or initialized if the list pointer is zero (i.e. which will be the case
fc32fae… mistachkin 2549 ** when TH1 transfer hooks are disabled).
fc32fae… mistachkin 2550 */
fc32fae… mistachkin 2551 void Th_AppendToList(
fc32fae… mistachkin 2552 char **pzList,
fc32fae… mistachkin 2553 int *pnList,
fc32fae… mistachkin 2554 const char *zElem,
fc32fae… mistachkin 2555 int nElem
fc32fae… mistachkin 2556 ){
fc32fae… mistachkin 2557 if( pzList && zElem ){
fc32fae… mistachkin 2558 Th_FossilInit(TH_INIT_DEFAULT);
fc32fae… mistachkin 2559 Th_ListAppend(g.interp, pzList, pnList, zElem, nElem);
fc32fae… mistachkin 2560 }
fc32fae… mistachkin 2561 }
fc32fae… mistachkin 2562
fc32fae… mistachkin 2563 /*
fc32fae… mistachkin 2564 ** Stores a list value in the specified TH1 variable using the specified
fc32fae… mistachkin 2565 ** array of strings as the source of the element values.
1c528d3… mistachkin 2566 */
1c528d3… mistachkin 2567 void Th_StoreList(
1c528d3… mistachkin 2568 const char *zName,
1c528d3… mistachkin 2569 char **pzList,
1c528d3… mistachkin 2570 int nList
1c528d3… mistachkin 2571 ){
1c528d3… mistachkin 2572 Th_FossilInit(TH_INIT_DEFAULT);
1c528d3… mistachkin 2573 if( pzList ){
1c528d3… mistachkin 2574 char *zValue = 0;
1c528d3… mistachkin 2575 int nValue = 0;
1c528d3… mistachkin 2576 int i;
1c528d3… mistachkin 2577 for(i=0; i<nList; i++){
1c528d3… mistachkin 2578 Th_ListAppend(g.interp, &zValue, &nValue, pzList[i], -1);
1c528d3… mistachkin 2579 }
1c528d3… mistachkin 2580 if( g.thTrace ){
f5482a0… wyoung 2581 Th_Trace("set %h {%h}<br>\n", zName, zValue);
1c528d3… mistachkin 2582 }
1c528d3… mistachkin 2583 Th_SetVar(g.interp, zName, -1, zValue, nValue);
1c528d3… mistachkin 2584 Th_Free(g.interp, zValue);
b058c8a… mistachkin 2585 }
b058c8a… mistachkin 2586 }
b058c8a… mistachkin 2587
b058c8a… mistachkin 2588 /*
b058c8a… mistachkin 2589 ** Store an integer value in a variable in the interpreter.
b058c8a… mistachkin 2590 */
b058c8a… mistachkin 2591 void Th_StoreInt(const char *zName, int iValue){
b058c8a… mistachkin 2592 Blob value;
b058c8a… mistachkin 2593 char *zValue;
0e68620… mistachkin 2594 Th_FossilInit(TH_INIT_DEFAULT);
b058c8a… mistachkin 2595 blob_zero(&value);
b058c8a… mistachkin 2596 blob_appendf(&value, "%d", iValue);
b058c8a… mistachkin 2597 zValue = blob_str(&value);
b058c8a… mistachkin 2598 if( g.thTrace ){
f5482a0… wyoung 2599 Th_Trace("set %h {%h}<br>\n", zName, zValue);
b058c8a… mistachkin 2600 }
b058c8a… mistachkin 2601 Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
b058c8a… mistachkin 2602 blob_reset(&value);
68c24b1… drh 2603 }
68c24b1… drh 2604
68c24b1… drh 2605 /*
68c24b1… drh 2606 ** Unset a variable.
68c24b1… drh 2607 */
68c24b1… drh 2608 void Th_Unstore(const char *zName){
68c24b1… drh 2609 if( g.interp ){
0c99a15… drh 2610 Th_UnsetVar(g.interp, (char*)zName, -1);
68c24b1… drh 2611 }
588bb7c… aku 2612 }
588bb7c… aku 2613
588bb7c… aku 2614 /*
588bb7c… aku 2615 ** Retrieve a string value from the interpreter. If no such
588bb7c… aku 2616 ** variable exists, return NULL.
588bb7c… aku 2617 */
588bb7c… aku 2618 char *Th_Fetch(const char *zName, int *pSize){
588bb7c… aku 2619 int rc;
0e68620… mistachkin 2620 Th_FossilInit(TH_INIT_DEFAULT);
0c99a15… drh 2621 rc = Th_GetVar(g.interp, (char*)zName, -1);
588bb7c… aku 2622 if( rc==TH_OK ){
588bb7c… aku 2623 return (char*)Th_GetResult(g.interp, pSize);
588bb7c… aku 2624 }else{
588bb7c… aku 2625 return 0;
588bb7c… aku 2626 }
588bb7c… aku 2627 }
588bb7c… aku 2628
588bb7c… aku 2629 /*
588bb7c… aku 2630 ** Return true if the string begins with the TH1 begin-script
588bb7c… aku 2631 ** tag: <th1>.
588bb7c… aku 2632 */
588bb7c… aku 2633 static int isBeginScriptTag(const char *z){
588bb7c… aku 2634 return z[0]=='<'
588bb7c… aku 2635 && (z[1]=='t' || z[1]=='T')
588bb7c… aku 2636 && (z[2]=='h' || z[2]=='H')
588bb7c… aku 2637 && z[3]=='1'
588bb7c… aku 2638 && z[4]=='>';
588bb7c… aku 2639 }
588bb7c… aku 2640
588bb7c… aku 2641 /*
588bb7c… aku 2642 ** Return true if the string begins with the TH1 end-script
588bb7c… aku 2643 ** tag: </th1>.
588bb7c… aku 2644 */
588bb7c… aku 2645 static int isEndScriptTag(const char *z){
588bb7c… aku 2646 return z[0]=='<'
588bb7c… aku 2647 && z[1]=='/'
588bb7c… aku 2648 && (z[2]=='t' || z[2]=='T')
588bb7c… aku 2649 && (z[3]=='h' || z[3]=='H')
588bb7c… aku 2650 && z[4]=='1'
588bb7c… aku 2651 && z[5]=='>';
588bb7c… aku 2652 }
588bb7c… aku 2653
588bb7c… aku 2654 /*
588bb7c… aku 2655 ** If string z[0...] contains a valid variable name, return
588bb7c… aku 2656 ** the number of characters in that name. Otherwise, return 0.
588bb7c… aku 2657 */
588bb7c… aku 2658 static int validVarName(const char *z){
588bb7c… aku 2659 int i = 0;
588bb7c… aku 2660 int inBracket = 0;
588bb7c… aku 2661 if( z[0]=='<' ){
588bb7c… aku 2662 inBracket = 1;
588bb7c… aku 2663 z++;
588bb7c… aku 2664 }
2fac809… drh 2665 if( z[0]==':' && z[1]==':' && fossil_isalpha(z[2]) ){
588bb7c… aku 2666 z += 3;
588bb7c… aku 2667 i += 3;
2fac809… drh 2668 }else if( fossil_isalpha(z[0]) ){
588bb7c… aku 2669 z ++;
588bb7c… aku 2670 i += 1;
588bb7c… aku 2671 }else{
588bb7c… aku 2672 return 0;
588bb7c… aku 2673 }
2fac809… drh 2674 while( fossil_isalnum(z[0]) || z[0]=='_' ){
588bb7c… aku 2675 z++;
588bb7c… aku 2676 i++;
588bb7c… aku 2677 }
588bb7c… aku 2678 if( inBracket ){
588bb7c… aku 2679 if( z[0]!='>' ) return 0;
588bb7c… aku 2680 i += 2;
588bb7c… aku 2681 }
588bb7c… aku 2682 return i;
588bb7c… aku 2683 }
588bb7c… aku 2684
1c528d3… mistachkin 2685 #ifdef FOSSIL_ENABLE_TH1_HOOKS
1c528d3… mistachkin 2686 /*
e52682e… mistachkin 2687 ** This function determines if TH1 hooks are enabled for the repository. It
e52682e… mistachkin 2688 ** may be necessary to open the repository and/or the configuration ("user")
e52682e… mistachkin 2689 ** database from within this function. Before this function returns, any
e52682e… mistachkin 2690 ** database opened will be closed again. This is very important because some
e52682e… mistachkin 2691 ** commands do not expect the repository and/or the configuration ("user")
e52682e… mistachkin 2692 ** database to be open prior to their own code doing so.
e52682e… mistachkin 2693 */
e52682e… mistachkin 2694 int Th_AreHooksEnabled(void){
e52682e… mistachkin 2695 int rc;
e52682e… mistachkin 2696 if( fossil_getenv("TH1_ENABLE_HOOKS")!=0 ){
e52682e… mistachkin 2697 return 1;
e52682e… mistachkin 2698 }
e52682e… mistachkin 2699 Th_OpenConfig(1);
e52682e… mistachkin 2700 rc = db_get_boolean("th1-hooks", 0);
e52682e… mistachkin 2701 Th_CloseConfig(1);
e52682e… mistachkin 2702 return rc;
e52682e… mistachkin 2703 }
e52682e… mistachkin 2704
e52682e… mistachkin 2705 /*
1c528d3… mistachkin 2706 ** This function is called by Fossil just prior to dispatching a command.
1c528d3… mistachkin 2707 ** Returning a value other than TH_OK from this function (i.e. via an
1c528d3… mistachkin 2708 ** evaluated script raising an error or calling [break]/[continue]) will
1c528d3… mistachkin 2709 ** cause the actual command execution to be skipped.
1c528d3… mistachkin 2710 */
1c528d3… mistachkin 2711 int Th_CommandHook(
1c528d3… mistachkin 2712 const char *zName,
e6aab1a… drh 2713 unsigned int cmdFlags
1c528d3… mistachkin 2714 ){
1c528d3… mistachkin 2715 int rc = TH_OK;
e52682e… mistachkin 2716 if( !Th_AreHooksEnabled() ) return rc;
1c528d3… mistachkin 2717 Th_FossilInit(TH_INIT_HOOK);
1c528d3… mistachkin 2718 Th_Store("cmd_name", zName);
1c528d3… mistachkin 2719 Th_StoreList("cmd_args", g.argv, g.argc);
1c528d3… mistachkin 2720 Th_StoreInt("cmd_flags", cmdFlags);
1c528d3… mistachkin 2721 rc = Th_Eval(g.interp, 0, "command_hook", -1);
1c528d3… mistachkin 2722 if( rc==TH_ERROR ){
1c528d3… mistachkin 2723 int nResult = 0;
1c528d3… mistachkin 2724 char *zResult = (char*)Th_GetResult(g.interp, &nResult);
1c528d3… mistachkin 2725 /*
1c528d3… mistachkin 2726 ** Make sure that the TH1 script error was not caused by a "missing"
1c528d3… mistachkin 2727 ** command hook handler as that is not actually an error condition.
1c528d3… mistachkin 2728 */
2116238… drh 2729 nResult = TH1_LEN(nResult);
1c528d3… mistachkin 2730 if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){
5173701… stephan 2731 sendError(0,zResult, nResult, 0);
a313f03… mistachkin 2732 }else{
a313f03… mistachkin 2733 /*
a313f03… mistachkin 2734 ** There is no command hook handler "installed". This situation
a313f03… mistachkin 2735 ** is NOT actually an error.
a313f03… mistachkin 2736 */
a313f03… mistachkin 2737 rc = TH_OK;
1c528d3… mistachkin 2738 }
1c528d3… mistachkin 2739 }
1c528d3… mistachkin 2740 /*
1c528d3… mistachkin 2741 ** If the script returned TH_ERROR (e.g. the "command_hook" TH1 command does
1c528d3… mistachkin 2742 ** not exist because commands are not being hooked), return TH_OK because we
1c528d3… mistachkin 2743 ** do not want to skip executing essential commands unless the called command
1c528d3… mistachkin 2744 ** (i.e. "command_hook") explicitly forbids this by successfully returning
1c528d3… mistachkin 2745 ** TH_BREAK or TH_CONTINUE.
1c528d3… mistachkin 2746 */
1c528d3… mistachkin 2747 if( g.thTrace ){
f5482a0… wyoung 2748 Th_Trace("[command_hook {%h}] => %h<br>\n", zName,
1c528d3… mistachkin 2749 Th_ReturnCodeName(rc, 0));
1c528d3… mistachkin 2750 }
e52682e… mistachkin 2751 /*
e52682e… mistachkin 2752 ** Does our call to Th_FossilInit() result in opening a database? If so,
e52682e… mistachkin 2753 ** clean it up now. This is very important because some commands do not
e52682e… mistachkin 2754 ** expect the repository and/or the configuration ("user") database to be
e52682e… mistachkin 2755 ** open prior to their own code doing so.
e52682e… mistachkin 2756 */
e52682e… mistachkin 2757 if( TH_INIT_HOOK & TH_INIT_NEED_CONFIG ) Th_CloseConfig(1);
a313f03… mistachkin 2758 return rc;
1c528d3… mistachkin 2759 }
1c528d3… mistachkin 2760
1c528d3… mistachkin 2761 /*
1c528d3… mistachkin 2762 ** This function is called by Fossil just after dispatching a command.
1c528d3… mistachkin 2763 ** Returning a value other than TH_OK from this function (i.e. via an
1c528d3… mistachkin 2764 ** evaluated script raising an error or calling [break]/[continue]) may
1c528d3… mistachkin 2765 ** cause an error message to be displayed to the local interactive user.
1c528d3… mistachkin 2766 ** Currently, TH1 error messages generated by this function are ignored.
1c528d3… mistachkin 2767 */
1c528d3… mistachkin 2768 int Th_CommandNotify(
1c528d3… mistachkin 2769 const char *zName,
e6aab1a… drh 2770 unsigned int cmdFlags
1c528d3… mistachkin 2771 ){
1c528d3… mistachkin 2772 int rc = TH_OK;
e52682e… mistachkin 2773 if( !Th_AreHooksEnabled() ) return rc;
1c528d3… mistachkin 2774 Th_FossilInit(TH_INIT_HOOK);
1c528d3… mistachkin 2775 Th_Store("cmd_name", zName);
1c528d3… mistachkin 2776 Th_StoreList("cmd_args", g.argv, g.argc);
1c528d3… mistachkin 2777 Th_StoreInt("cmd_flags", cmdFlags);
1c528d3… mistachkin 2778 rc = Th_Eval(g.interp, 0, "command_notify", -1);
1c528d3… mistachkin 2779 if( g.thTrace ){
f5482a0… wyoung 2780 Th_Trace("[command_notify {%h}] => %h<br>\n", zName,
1c528d3… mistachkin 2781 Th_ReturnCodeName(rc, 0));
1c528d3… mistachkin 2782 }
e52682e… mistachkin 2783 /*
e52682e… mistachkin 2784 ** Does our call to Th_FossilInit() result in opening a database? If so,
e52682e… mistachkin 2785 ** clean it up now. This is very important because some commands do not
e52682e… mistachkin 2786 ** expect the repository and/or the configuration ("user") database to be
e52682e… mistachkin 2787 ** open prior to their own code doing so.
e52682e… mistachkin 2788 */
e52682e… mistachkin 2789 if( TH_INIT_HOOK & TH_INIT_NEED_CONFIG ) Th_CloseConfig(1);
1c528d3… mistachkin 2790 return rc;
1c528d3… mistachkin 2791 }
1c528d3… mistachkin 2792
1c528d3… mistachkin 2793 /*
1c528d3… mistachkin 2794 ** This function is called by Fossil just prior to processing a web page.
1c528d3… mistachkin 2795 ** Returning a value other than TH_OK from this function (i.e. via an
1c528d3… mistachkin 2796 ** evaluated script raising an error or calling [break]/[continue]) will
1c528d3… mistachkin 2797 ** cause the actual web page processing to be skipped.
1c528d3… mistachkin 2798 */
1c528d3… mistachkin 2799 int Th_WebpageHook(
1c528d3… mistachkin 2800 const char *zName,
3ca2bc1… mistachkin 2801 unsigned int cmdFlags
1c528d3… mistachkin 2802 ){
1c528d3… mistachkin 2803 int rc = TH_OK;
e52682e… mistachkin 2804 if( !Th_AreHooksEnabled() ) return rc;
1c528d3… mistachkin 2805 Th_FossilInit(TH_INIT_HOOK);
1c528d3… mistachkin 2806 Th_Store("web_name", zName);
1c528d3… mistachkin 2807 Th_StoreList("web_args", g.argv, g.argc);
1c528d3… mistachkin 2808 Th_StoreInt("web_flags", cmdFlags);
1c528d3… mistachkin 2809 rc = Th_Eval(g.interp, 0, "webpage_hook", -1);
1c528d3… mistachkin 2810 if( rc==TH_ERROR ){
1c528d3… mistachkin 2811 int nResult = 0;
1c528d3… mistachkin 2812 char *zResult = (char*)Th_GetResult(g.interp, &nResult);
1c528d3… mistachkin 2813 /*
1c528d3… mistachkin 2814 ** Make sure that the TH1 script error was not caused by a "missing"
1c528d3… mistachkin 2815 ** webpage hook handler as that is not actually an error condition.
1c528d3… mistachkin 2816 */
2116238… drh 2817 nResult = TH1_LEN(nResult);
1c528d3… mistachkin 2818 if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){
5173701… stephan 2819 sendError(0,zResult, nResult, 1);
a313f03… mistachkin 2820 }else{
a313f03… mistachkin 2821 /*
a313f03… mistachkin 2822 ** There is no webpage hook handler "installed". This situation
a313f03… mistachkin 2823 ** is NOT actually an error.
a313f03… mistachkin 2824 */
a313f03… mistachkin 2825 rc = TH_OK;
1c528d3… mistachkin 2826 }
1c528d3… mistachkin 2827 }
1c528d3… mistachkin 2828 /*
1c528d3… mistachkin 2829 ** If the script returned TH_ERROR (e.g. the "webpage_hook" TH1 command does
1c528d3… mistachkin 2830 ** not exist because commands are not being hooked), return TH_OK because we
1c528d3… mistachkin 2831 ** do not want to skip processing essential web pages unless the called
1c528d3… mistachkin 2832 ** command (i.e. "webpage_hook") explicitly forbids this by successfully
1c528d3… mistachkin 2833 ** returning TH_BREAK or TH_CONTINUE.
1c528d3… mistachkin 2834 */
1c528d3… mistachkin 2835 if( g.thTrace ){
f5482a0… wyoung 2836 Th_Trace("[webpage_hook {%h}] => %h<br>\n", zName,
1c528d3… mistachkin 2837 Th_ReturnCodeName(rc, 0));
1c528d3… mistachkin 2838 }
e52682e… mistachkin 2839 /*
e52682e… mistachkin 2840 ** Does our call to Th_FossilInit() result in opening a database? If so,
e52682e… mistachkin 2841 ** clean it up now. This is very important because some commands do not
e52682e… mistachkin 2842 ** expect the repository and/or the configuration ("user") database to be
e52682e… mistachkin 2843 ** open prior to their own code doing so.
e52682e… mistachkin 2844 */
e52682e… mistachkin 2845 if( TH_INIT_HOOK & TH_INIT_NEED_CONFIG ) Th_CloseConfig(1);
a313f03… mistachkin 2846 return rc;
1c528d3… mistachkin 2847 }
1c528d3… mistachkin 2848
1c528d3… mistachkin 2849 /*
1c528d3… mistachkin 2850 ** This function is called by Fossil just after processing a web page.
1c528d3… mistachkin 2851 ** Returning a value other than TH_OK from this function (i.e. via an
1c528d3… mistachkin 2852 ** evaluated script raising an error or calling [break]/[continue]) may
1c528d3… mistachkin 2853 ** cause an error message to be displayed to the remote user.
1c528d3… mistachkin 2854 ** Currently, TH1 error messages generated by this function are ignored.
1c528d3… mistachkin 2855 */
1c528d3… mistachkin 2856 int Th_WebpageNotify(
1c528d3… mistachkin 2857 const char *zName,
3ca2bc1… mistachkin 2858 unsigned int cmdFlags
1c528d3… mistachkin 2859 ){
1c528d3… mistachkin 2860 int rc = TH_OK;
e52682e… mistachkin 2861 if( !Th_AreHooksEnabled() ) return rc;
1c528d3… mistachkin 2862 Th_FossilInit(TH_INIT_HOOK);
1c528d3… mistachkin 2863 Th_Store("web_name", zName);
1c528d3… mistachkin 2864 Th_StoreList("web_args", g.argv, g.argc);
1c528d3… mistachkin 2865 Th_StoreInt("web_flags", cmdFlags);
1c528d3… mistachkin 2866 rc = Th_Eval(g.interp, 0, "webpage_notify", -1);
1c528d3… mistachkin 2867 if( g.thTrace ){
f5482a0… wyoung 2868 Th_Trace("[webpage_notify {%h}] => %h<br>\n", zName,
1c528d3… mistachkin 2869 Th_ReturnCodeName(rc, 0));
1c528d3… mistachkin 2870 }
e52682e… mistachkin 2871 /*
e52682e… mistachkin 2872 ** Does our call to Th_FossilInit() result in opening a database? If so,
e52682e… mistachkin 2873 ** clean it up now. This is very important because some commands do not
e52682e… mistachkin 2874 ** expect the repository and/or the configuration ("user") database to be
e52682e… mistachkin 2875 ** open prior to their own code doing so.
e52682e… mistachkin 2876 */
e52682e… mistachkin 2877 if( TH_INIT_HOOK & TH_INIT_NEED_CONFIG ) Th_CloseConfig(1);
1c528d3… mistachkin 2878 return rc;
1c528d3… mistachkin 2879 }
1c528d3… mistachkin 2880 #endif
1c528d3… mistachkin 2881
71536a2… mistachkin 2882
71536a2… mistachkin 2883 #ifdef FOSSIL_ENABLE_TH1_DOCS
71536a2… mistachkin 2884 /*
71536a2… mistachkin 2885 ** This function determines if TH1 docs are enabled for the repository.
71536a2… mistachkin 2886 */
71536a2… mistachkin 2887 int Th_AreDocsEnabled(void){
71536a2… mistachkin 2888 if( fossil_getenv("TH1_ENABLE_DOCS")!=0 ){
71536a2… mistachkin 2889 return 1;
71536a2… mistachkin 2890 }
71536a2… mistachkin 2891 return db_get_boolean("th1-docs", 0);
71536a2… mistachkin 2892 }
71536a2… mistachkin 2893 #endif
71536a2… mistachkin 2894
71536a2… mistachkin 2895
5173701… stephan 2896 #if INTERFACE
5173701… stephan 2897 /*
5173701… stephan 2898 ** Flags for use with Th_RenderToBlob. These must not overlap with
5173701… stephan 2899 ** TH_INIT_MASK.
5173701… stephan 2900 */
5173701… stephan 2901 #define TH_R2B_MASK ((u32)0x0f000)
5173701… stephan 2902 #define TH_R2B_NO_VARS ((u32)0x01000) /* Disables eval of $vars and $<vars> */
5173701… stephan 2903 #endif
5173701… stephan 2904
588bb7c… aku 2905 /*
a5f00e0… stephan 2906 ** If pOut is NULL, this works identically to Th_Render() and sends
a5f00e0… stephan 2907 ** any TH1-generated output to stdin (in CLI mode) or the CGI buffer
a5f00e0… stephan 2908 ** (in CGI mode), else it works just like that function but appends
a5f00e0… stephan 2909 ** any TH1-generated output to the given blob. A bitmask of TH_R2B_xxx
a5f00e0… stephan 2910 ** and/or TH_INIT_xxx flags may be passed as the 3rd argument, or 0
a5f00e0… stephan 2911 ** for default options. Note that this function necessarily calls
a5f00e0… stephan 2912 ** Th_FossilInit(), which may unset flags used on previous calls
a5f00e0… stephan 2913 ** unless mFlags is explicitly passed in.
588bb7c… aku 2914 */
5173701… stephan 2915 int Th_RenderToBlob(const char *z, Blob * pOut, u32 mFlags){
588bb7c… aku 2916 int i = 0;
588bb7c… aku 2917 int n;
588bb7c… aku 2918 int rc = TH_OK;
0c99a15… drh 2919 char *zResult;
5173701… stephan 2920 Blob * const origOut = Th_SetOutputBlob(pOut);
5173701… stephan 2921
5173701… stephan 2922 assert(0==(TH_R2B_MASK & TH_INIT_MASK) && "init/r2b mask conflict");
5173701… stephan 2923 Th_FossilInit(mFlags & TH_INIT_MASK);
588bb7c… aku 2924 while( z[i] ){
5173701… stephan 2925 if( 0==(TH_R2B_NO_VARS & mFlags)
5173701… stephan 2926 && z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
588bb7c… aku 2927 const char *zVar;
588bb7c… aku 2928 int nVar;
aeec10b… drh 2929 int encode = 1;
5173701… stephan 2930 sendText(pOut,z, i, 0);
588bb7c… aku 2931 if( z[i+1]=='<' ){
aeec10b… drh 2932 /* Variables of the form $<aaa> are html escaped */
588bb7c… aku 2933 zVar = &z[i+2];
588bb7c… aku 2934 nVar = n-2;
588bb7c… aku 2935 }else{
aeec10b… drh 2936 /* Variables of the form $aaa are output raw */
588bb7c… aku 2937 zVar = &z[i+1];
588bb7c… aku 2938 nVar = n;
aeec10b… drh 2939 encode = 0;
588bb7c… aku 2940 }
0c99a15… drh 2941 rc = Th_GetVar(g.interp, (char*)zVar, nVar);
588bb7c… aku 2942 z += i+1+n;
588bb7c… aku 2943 i = 0;
0c99a15… drh 2944 zResult = (char*)Th_GetResult(g.interp, &n);
d259be4… drh 2945 if( !TH1_TAINTED(n)
d259be4… drh 2946 || encode
d259be4… drh 2947 || Th_ReportTaint(g.interp, "inline variable", zVar, nVar)==TH_OK
d259be4… drh 2948 ){
d259be4… drh 2949 sendText(pOut,(char*)zResult, n, encode);
d259be4… drh 2950 }
588bb7c… aku 2951 }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
5173701… stephan 2952 sendText(pOut,z, i, 0);
588bb7c… aku 2953 z += i+5;
588bb7c… aku 2954 for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
4f8c897… drh 2955 if( g.thTrace ){
f5482a0… wyoung 2956 Th_Trace("render_eval {<pre>%#h</pre>}<br>\n", i, z);
4f8c897… drh 2957 }
0c99a15… drh 2958 rc = Th_Eval(g.interp, 0, (const char*)z, i);
fee9ede… mistachkin 2959 if( g.thTrace ){
fee9ede… mistachkin 2960 int nTrRes;
fee9ede… mistachkin 2961 char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes);
f5482a0… wyoung 2962 Th_Trace("[render_eval] => %h {%#h}<br>\n",
2116238… drh 2963 Th_ReturnCodeName(rc, 0), TH1_LEN(nTrRes), zTrRes);
fee9ede… mistachkin 2964 }
588bb7c… aku 2965 if( rc!=TH_OK ) break;
588bb7c… aku 2966 z += i;
588bb7c… aku 2967 if( z[0] ){ z += 6; }
588bb7c… aku 2968 i = 0;
588bb7c… aku 2969 }else{
b853b5d… drh 2970 i += strcspn(&z[i+1], "<$") + 1;
588bb7c… aku 2971 }
588bb7c… aku 2972 }
588bb7c… aku 2973 if( rc==TH_ERROR ){
0c99a15… drh 2974 zResult = (char*)Th_GetResult(g.interp, &n);
5173701… stephan 2975 sendError(pOut,zResult, n, 1);
588bb7c… aku 2976 }else{
5173701… stephan 2977 sendText(pOut,z, i, 0);
588bb7c… aku 2978 }
5173701… stephan 2979 Th_SetOutputBlob(origOut);
588bb7c… aku 2980 return rc;
5173701… stephan 2981 }
5173701… stephan 2982
5173701… stephan 2983 /*
5173701… stephan 2984 ** The z[] input contains text mixed with TH1 scripts.
5173701… stephan 2985 ** The TH1 scripts are contained within <th1>...</th1>.
5173701… stephan 2986 ** TH1 variables are $aaa or $<aaa>. The first form of
5173701… stephan 2987 ** variable is literal. The second is run through htmlize
5173701… stephan 2988 ** before being inserted.
5173701… stephan 2989 **
5173701… stephan 2990 ** This routine processes the template and writes the results to one
5173701… stephan 2991 ** of stdout, CGI, or an internal blob which was set up via a prior
5173701… stephan 2992 ** call to Th_SetOutputBlob().
5173701… stephan 2993 */
5173701… stephan 2994 int Th_Render(const char *z){
a5f00e0… stephan 2995 return Th_RenderToBlob(z, pThOut, g.th1Flags)
a5f00e0… stephan 2996 /* Maintenance reminder: on most calls to Th_Render(), e.g. for
a5f00e0… stephan 2997 ** outputing the site skin, pThOut will be 0, which means that
a5f00e0… stephan 2998 ** Th_RenderToBlob() will output directly to the CGI buffer (in
a5f00e0… stephan 2999 ** CGI mode) or stdout (in CLI mode). Recursive calls, however,
a5f00e0… stephan 3000 ** e.g. via the "render" script function binding, need to use the
a5f00e0… stephan 3001 ** pThOut blob in order to avoid out-of-order output if
a5f00e0… stephan 3002 ** Th_SetOutputBlob() has been called. If it has not been called,
a5f00e0… stephan 3003 ** pThOut will be 0, which will redirect the output to CGI/stdout,
a5f00e0… stephan 3004 ** as appropriate. We need to pass on g.th1Flags for the case of
9164a5d… drh 3005 ** recursive calls.
a5f00e0… stephan 3006 */;
2116238… drh 3007 }
2116238… drh 3008
2116238… drh 3009 /*
2116238… drh 3010 ** SETTING: vuln-report width=8 default=log
2116238… drh 3011 **
2116238… drh 3012 ** This setting controls Fossil's behavior when it encounters a potential
2116238… drh 3013 ** XSS or SQL-injection vulnerability due to misuse of TH1 configuration
2116238… drh 3014 ** scripts. Choices are:
2116238… drh 3015 **
2116238… drh 3016 ** off Do nothing. Ignore the vulnerability.
2116238… drh 3017 **
2116238… drh 3018 ** log Write a report of the problem into the error log.
2116238… drh 3019 **
2116238… drh 3020 ** block Like "log" but also prevent the offending TH1 command
2116238… drh 3021 ** from running.
2116238… drh 3022 **
2116238… drh 3023 ** fatal Render an error message page instead of the requested
2116238… drh 3024 ** page.
2116238… drh 3025 */
2116238… drh 3026
2116238… drh 3027 /*
2116238… drh 3028 ** Report misuse of a tainted string in TH1.
2116238… drh 3029 **
2116238… drh 3030 ** The behavior depends on the vuln-report setting. If "off", this routine
2116238… drh 3031 ** is a no-op. Otherwise, right a message into the error log. If
2116238… drh 3032 ** vuln-report is "log", that is all that happens. But for any other
2116238… drh 3033 ** value of vuln-report, a fatal error is raised.
2116238… drh 3034 */
2116238… drh 3035 int Th_ReportTaint(
2116238… drh 3036 Th_Interp *interp, /* Report error here, if an error is reported */
2116238… drh 3037 const char *zWhere, /* Where the tainted string appears */
2116238… drh 3038 const char *zStr, /* The tainted string */
2116238… drh 3039 int nStr /* Length of the tainted string */
2116238… drh 3040 ){
76f1ddb… drh 3041 static const char *zDisp = 0; /* Dispensation; what to do with the error */
76f1ddb… drh 3042 const char *zVulnType; /* Type of vulnerability */
2116238… drh 3043
76f1ddb… drh 3044 if( zDisp==0 ) zDisp = db_get("vuln-report","log");
2116238… drh 3045 if( is_false(zDisp) ) return 0;
2116238… drh 3046 if( strstr(zWhere,"SQL")!=0 ){
2116238… drh 3047 zVulnType = "SQL-injection";
2116238… drh 3048 }else{
2116238… drh 3049 zVulnType = "XSS";
2116238… drh 3050 }
2116238… drh 3051 nStr = TH1_LEN(nStr);
d259be4… drh 3052 fossil_errorlog("possible TH1 %s vulnerability due to tainted %s: \"%.*s\"",
2116238… drh 3053 zVulnType, zWhere, nStr, zStr);
2116238… drh 3054 if( strcmp(zDisp,"log")==0 ){
2116238… drh 3055 return 0;
2116238… drh 3056 }
2116238… drh 3057 if( strcmp(zDisp,"block")==0 ){
2116238… drh 3058 char *z = mprintf("tainted %s: \"", zWhere);
2116238… drh 3059 Th_ErrorMessage(interp, z, zStr, nStr);
2116238… drh 3060 fossil_free(z);
2116238… drh 3061 }else{
2116238… drh 3062 char *z = mprintf("%#h", nStr, zStr);
76f1ddb… drh 3063 zDisp = "off";
2116238… drh 3064 cgi_reset_content();
2116238… drh 3065 style_submenu_enable(0);
2116238… drh 3066 style_set_current_feature("error");
2116238… drh 3067 style_header("Configuration Error");
2116238… drh 3068 @ <p>Error in a TH1 configuration script:
2116238… drh 3069 @ tainted %h(zWhere): "%z(z)"
2116238… drh 3070 style_finish_page();
2116238… drh 3071 cgi_reply();
2116238… drh 3072 fossil_exit(1);
2116238… drh 3073 }
2116238… drh 3074 return 1;
924315b… drh 3075 }
924315b… drh 3076
924315b… drh 3077 /*
588bb7c… aku 3078 ** COMMAND: test-th-render
924315b… drh 3079 **
924315b… drh 3080 ** Usage: %fossil test-th-render FILE
924315b… drh 3081 **
924315b… drh 3082 ** Read the content of the file named "FILE" as if it were a header or
924315b… drh 3083 ** footer or ticket rendering script, evaluate it, and show the results
924315b… drh 3084 ** on standard output.
924315b… drh 3085 **
924315b… drh 3086 ** Options:
924315b… drh 3087 ** --cgi Include a CGI response header in the output
924315b… drh 3088 ** --http Include an HTTP response header in the output
924315b… drh 3089 ** --open-config Open the configuration database
5ea2a4f… mistachkin 3090 ** --set-anon-caps Set anonymous login capabilities
5ea2a4f… mistachkin 3091 ** --set-user-caps Set user login capabilities
ddd4cb5… mistachkin 3092 ** --th-trace Trace TH1 execution (for debugging purposes)
588bb7c… aku 3093 */
588bb7c… aku 3094 void test_th_render(void){
f141fbe… mistachkin 3095 int forceCgi, fullHttpReply;
588bb7c… aku 3096 Blob in;
0264475… mistachkin 3097 Th_InitTraceLog();
924315b… drh 3098 forceCgi = find_option("cgi", 0, 0)!=0;
924315b… drh 3099 fullHttpReply = find_option("http", 0, 0)!=0;
924315b… drh 3100 if( fullHttpReply ) forceCgi = 1;
c1915c3… mistachkin 3101 if( forceCgi ) Th_ForceCgi(fullHttpReply);
924315b… drh 3102 if( find_option("open-config", 0, 0)!=0 ){
9773eba… mistachkin 3103 Th_OpenConfig(1);
9773eba… mistachkin 3104 }
5ea2a4f… mistachkin 3105 if( find_option("set-anon-caps", 0, 0)!=0 ){
5915e6f… mistachkin 3106 const char *zCap = fossil_getenv("TH1_TEST_ANON_CAPS");
5915e6f… mistachkin 3107 login_set_capabilities(zCap ? zCap : "sx", LOGIN_ANON);
5ea2a4f… mistachkin 3108 g.useLocalauth = 1;
5ea2a4f… mistachkin 3109 }
5ea2a4f… mistachkin 3110 if( find_option("set-user-caps", 0, 0)!=0 ){
5915e6f… mistachkin 3111 const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS");
5915e6f… mistachkin 3112 login_set_capabilities(zCap ? zCap : "sx", 0);
5ea2a4f… mistachkin 3113 g.useLocalauth = 1;
5ea2a4f… mistachkin 3114 }
2116238… drh 3115 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
924315b… drh 3116 verify_all_options();
588bb7c… aku 3117 if( g.argc<3 ){
588bb7c… aku 3118 usage("FILE");
588bb7c… aku 3119 }
588bb7c… aku 3120 blob_zero(&in);
1772357… drh 3121 blob_read_from_file(&in, g.argv[2], ExtFILE);
588bb7c… aku 3122 Th_Render(blob_str(&in));
0264475… mistachkin 3123 Th_PrintTraceLog();
c1915c3… mistachkin 3124 if( forceCgi ) cgi_reply();
6c47a16… mistachkin 3125 }
6c47a16… mistachkin 3126
6c47a16… mistachkin 3127 /*
6c47a16… mistachkin 3128 ** COMMAND: test-th-eval
924315b… drh 3129 **
924315b… drh 3130 ** Usage: %fossil test-th-eval SCRIPT
924315b… drh 3131 **
924315b… drh 3132 ** Evaluate SCRIPT as if it were a header or footer or ticket rendering
6456fab… stephan 3133 ** script and show the results on standard output. SCRIPT may be either
6456fab… stephan 3134 ** a filename or a string of th1 script code.
924315b… drh 3135 **
924315b… drh 3136 ** Options:
924315b… drh 3137 ** --cgi Include a CGI response header in the output
924315b… drh 3138 ** --http Include an HTTP response header in the output
924315b… drh 3139 ** --open-config Open the configuration database
5ea2a4f… mistachkin 3140 ** --set-anon-caps Set anonymous login capabilities
5ea2a4f… mistachkin 3141 ** --set-user-caps Set user login capabilities
ddd4cb5… mistachkin 3142 ** --th-trace Trace TH1 execution (for debugging purposes)
6c47a16… mistachkin 3143 */
6c47a16… mistachkin 3144 void test_th_eval(void){
6c47a16… mistachkin 3145 int rc;
6c47a16… mistachkin 3146 const char *zRc;
6456fab… stephan 3147 const char *zCode = 0;
c1915c3… mistachkin 3148 int forceCgi, fullHttpReply;
6456fab… stephan 3149 Blob code = empty_blob;
f141fbe… mistachkin 3150 Th_InitTraceLog();
f141fbe… mistachkin 3151 forceCgi = find_option("cgi", 0, 0)!=0;
f141fbe… mistachkin 3152 fullHttpReply = find_option("http", 0, 0)!=0;
f141fbe… mistachkin 3153 if( fullHttpReply ) forceCgi = 1;
f141fbe… mistachkin 3154 if( forceCgi ) Th_ForceCgi(fullHttpReply);
f141fbe… mistachkin 3155 if( find_option("open-config", 0, 0)!=0 ){
f141fbe… mistachkin 3156 Th_OpenConfig(1);
f141fbe… mistachkin 3157 }
5ea2a4f… mistachkin 3158 if( find_option("set-anon-caps", 0, 0)!=0 ){
5915e6f… mistachkin 3159 const char *zCap = fossil_getenv("TH1_TEST_ANON_CAPS");
5915e6f… mistachkin 3160 login_set_capabilities(zCap ? zCap : "sx", LOGIN_ANON);
5ea2a4f… mistachkin 3161 g.useLocalauth = 1;
5ea2a4f… mistachkin 3162 }
5ea2a4f… mistachkin 3163 if( find_option("set-user-caps", 0, 0)!=0 ){
5915e6f… mistachkin 3164 const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS");
5915e6f… mistachkin 3165 login_set_capabilities(zCap ? zCap : "sx", 0);
5ea2a4f… mistachkin 3166 g.useLocalauth = 1;
5ea2a4f… mistachkin 3167 }
2116238… drh 3168 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
f141fbe… mistachkin 3169 verify_all_options();
f141fbe… mistachkin 3170 if( g.argc!=3 ){
f141fbe… mistachkin 3171 usage("script");
f141fbe… mistachkin 3172 }
6456fab… stephan 3173 if(file_isfile(g.argv[2], ExtFILE)){
6456fab… stephan 3174 blob_read_from_file(&code, g.argv[2], ExtFILE);
6456fab… stephan 3175 zCode = blob_str(&code);
6456fab… stephan 3176 }else{
6456fab… stephan 3177 zCode = g.argv[2];
6456fab… stephan 3178 }
f141fbe… mistachkin 3179 Th_FossilInit(TH_INIT_DEFAULT);
6456fab… stephan 3180 rc = Th_Eval(g.interp, 0, zCode, -1);
f141fbe… mistachkin 3181 zRc = Th_ReturnCodeName(rc, 1);
f141fbe… mistachkin 3182 fossil_print("%s%s%s\n", zRc, zRc ? ": " : "", Th_GetResult(g.interp, 0));
f141fbe… mistachkin 3183 Th_PrintTraceLog();
6456fab… stephan 3184 blob_reset(&code);
f141fbe… mistachkin 3185 if( forceCgi ) cgi_reply();
f141fbe… mistachkin 3186 }
f141fbe… mistachkin 3187
f141fbe… mistachkin 3188 /*
f141fbe… mistachkin 3189 ** COMMAND: test-th-source
f141fbe… mistachkin 3190 **
f141fbe… mistachkin 3191 ** Usage: %fossil test-th-source FILE
f141fbe… mistachkin 3192 **
f141fbe… mistachkin 3193 ** Evaluate the contents of the file named "FILE" as if it were a header
f141fbe… mistachkin 3194 ** or footer or ticket rendering script and show the results on standard
f141fbe… mistachkin 3195 ** output.
f141fbe… mistachkin 3196 **
f141fbe… mistachkin 3197 ** Options:
f141fbe… mistachkin 3198 ** --cgi Include a CGI response header in the output
f141fbe… mistachkin 3199 ** --http Include an HTTP response header in the output
f141fbe… mistachkin 3200 ** --open-config Open the configuration database
5ea2a4f… mistachkin 3201 ** --set-anon-caps Set anonymous login capabilities
5ea2a4f… mistachkin 3202 ** --set-user-caps Set user login capabilities
f141fbe… mistachkin 3203 ** --th-trace Trace TH1 execution (for debugging purposes)
817c643… stephan 3204 ** --no-print-result Do not output the final result. Use if it
817c643… stephan 3205 ** interferes with script output.
f141fbe… mistachkin 3206 */
f141fbe… mistachkin 3207 void test_th_source(void){
f141fbe… mistachkin 3208 int rc;
f141fbe… mistachkin 3209 const char *zRc;
817c643… stephan 3210 int forceCgi, fullHttpReply, fNoPrintRc;
f141fbe… mistachkin 3211 Blob in;
924315b… drh 3212 Th_InitTraceLog();
924315b… drh 3213 forceCgi = find_option("cgi", 0, 0)!=0;
924315b… drh 3214 fullHttpReply = find_option("http", 0, 0)!=0;
817c643… stephan 3215 fNoPrintRc = find_option("no-print-result",0,0)!=0;
924315b… drh 3216 if( fullHttpReply ) forceCgi = 1;
924315b… drh 3217 if( forceCgi ) Th_ForceCgi(fullHttpReply);
924315b… drh 3218 if( find_option("open-config", 0, 0)!=0 ){
924315b… drh 3219 Th_OpenConfig(1);
5ea2a4f… mistachkin 3220 }
5ea2a4f… mistachkin 3221 if( find_option("set-anon-caps", 0, 0)!=0 ){
5915e6f… mistachkin 3222 const char *zCap = fossil_getenv("TH1_TEST_ANON_CAPS");
5915e6f… mistachkin 3223 login_set_capabilities(zCap ? zCap : "sx", LOGIN_ANON);
5ea2a4f… mistachkin 3224 g.useLocalauth = 1;
5ea2a4f… mistachkin 3225 }
5ea2a4f… mistachkin 3226 if( find_option("set-user-caps", 0, 0)!=0 ){
5915e6f… mistachkin 3227 const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS");
5915e6f… mistachkin 3228 login_set_capabilities(zCap ? zCap : "sx", 0);
5ea2a4f… mistachkin 3229 g.useLocalauth = 1;
f141fbe… mistachkin 3230 }
f141fbe… mistachkin 3231 verify_all_options();
924315b… drh 3232 if( g.argc!=3 ){
f141fbe… mistachkin 3233 usage("file");
924315b… drh 3234 }
f141fbe… mistachkin 3235 blob_zero(&in);
1772357… drh 3236 blob_read_from_file(&in, g.argv[2], ExtFILE);
924315b… drh 3237 Th_FossilInit(TH_INIT_DEFAULT);
f141fbe… mistachkin 3238 rc = Th_Eval(g.interp, 0, blob_str(&in), -1);
924315b… drh 3239 zRc = Th_ReturnCodeName(rc, 1);
817c643… stephan 3240 if(0==fNoPrintRc){
817c643… stephan 3241 fossil_print("%s%s%s\n", zRc, zRc ? ": " : "",
817c643… stephan 3242 Th_GetResult(g.interp, 0));
817c643… stephan 3243 }
c1915c3… mistachkin 3244 Th_PrintTraceLog();
c1915c3… mistachkin 3245 if( forceCgi ) cgi_reply();
1c528d3… mistachkin 3246 }
1c528d3… mistachkin 3247
1c528d3… mistachkin 3248 #ifdef FOSSIL_ENABLE_TH1_HOOKS
1c528d3… mistachkin 3249 /*
1c528d3… mistachkin 3250 ** COMMAND: test-th-hook
ddd4cb5… mistachkin 3251 **
ddd4cb5… mistachkin 3252 ** Usage: %fossil test-th-hook TYPE NAME FLAGS
ddd4cb5… mistachkin 3253 **
f141fbe… mistachkin 3254 ** Evaluates the TH1 script configured for the pre-operation (i.e. a command
ddd4cb5… mistachkin 3255 ** or web page) "hook" or post-operation "notification". The results of the
ddd4cb5… mistachkin 3256 ** script evaluation, if any, will be printed to the standard output channel.
ddd4cb5… mistachkin 3257 ** The NAME argument must be the name of a command or web page; however, it
ddd4cb5… mistachkin 3258 ** does not necessarily have to be a command or web page that is normally
ddd4cb5… mistachkin 3259 ** recognized by Fossil. The FLAGS argument will be used to set the value
ddd4cb5… mistachkin 3260 ** of the "cmd_flags" and/or "web_flags" TH1 variables, if applicable. The
ddd4cb5… mistachkin 3261 ** TYPE argument must be one of the following:
ddd4cb5… mistachkin 3262 **
ddd4cb5… mistachkin 3263 ** cmdhook Executes the TH1 procedure [command_hook], after
ddd4cb5… mistachkin 3264 ** setting the TH1 variables "cmd_name", "cmd_args",
ddd4cb5… mistachkin 3265 ** and "cmd_flags" to appropriate values.
ddd4cb5… mistachkin 3266 **
ddd4cb5… mistachkin 3267 ** cmdnotify Executes the TH1 procedure [command_notify], after
ddd4cb5… mistachkin 3268 ** setting the TH1 variables "cmd_name", "cmd_args",
ddd4cb5… mistachkin 3269 ** and "cmd_flags" to appropriate values.
ddd4cb5… mistachkin 3270 **
ddd4cb5… mistachkin 3271 ** webhook Executes the TH1 procedure [webpage_hook], after
ddd4cb5… mistachkin 3272 ** setting the TH1 variables "web_name", "web_args",
ddd4cb5… mistachkin 3273 ** and "web_flags" to appropriate values.
ddd4cb5… mistachkin 3274 **
ddd4cb5… mistachkin 3275 ** webnotify Executes the TH1 procedure [webpage_notify], after
ddd4cb5… mistachkin 3276 ** setting the TH1 variables "web_name", "web_args",
ddd4cb5… mistachkin 3277 ** and "web_flags" to appropriate values.
ddd4cb5… mistachkin 3278 **
ddd4cb5… mistachkin 3279 ** Options:
ddd4cb5… mistachkin 3280 ** --cgi Include a CGI response header in the output
ddd4cb5… mistachkin 3281 ** --http Include an HTTP response header in the output
ddd4cb5… mistachkin 3282 ** --th-trace Trace TH1 execution (for debugging purposes)
1c528d3… mistachkin 3283 */
1c528d3… mistachkin 3284 void test_th_hook(void){
1c528d3… mistachkin 3285 int rc = TH_OK;
1c528d3… mistachkin 3286 int nResult = 0;
ddd4cb5… mistachkin 3287 char *zResult = 0;
c1915c3… mistachkin 3288 int forceCgi, fullHttpReply;
142200b… mistachkin 3289 Th_InitTraceLog();
0afcc93… mistachkin 3290 forceCgi = find_option("cgi", 0, 0)!=0;
0afcc93… mistachkin 3291 fullHttpReply = find_option("http", 0, 0)!=0;
0afcc93… mistachkin 3292 if( fullHttpReply ) forceCgi = 1;
c1915c3… mistachkin 3293 if( forceCgi ) Th_ForceCgi(fullHttpReply);
f141fbe… mistachkin 3294 verify_all_options();
1c528d3… mistachkin 3295 if( g.argc<5 ){
1c528d3… mistachkin 3296 usage("TYPE NAME FLAGS");
1c528d3… mistachkin 3297 }
1c528d3… mistachkin 3298 if( fossil_stricmp(g.argv[2], "cmdhook")==0 ){
e6aab1a… drh 3299 rc = Th_CommandHook(g.argv[3], (unsigned int)atoi(g.argv[4]));
1c528d3… mistachkin 3300 }else if( fossil_stricmp(g.argv[2], "cmdnotify")==0 ){
e6aab1a… drh 3301 rc = Th_CommandNotify(g.argv[3], (unsigned int)atoi(g.argv[4]));
1c528d3… mistachkin 3302 }else if( fossil_stricmp(g.argv[2], "webhook")==0 ){
e6aab1a… drh 3303 rc = Th_WebpageHook(g.argv[3], (unsigned int)atoi(g.argv[4]));
1c528d3… mistachkin 3304 }else if( fossil_stricmp(g.argv[2], "webnotify")==0 ){
e6aab1a… drh 3305 rc = Th_WebpageNotify(g.argv[3], (unsigned int)atoi(g.argv[4]));
1c528d3… mistachkin 3306 }else{
2c42b21… andygoth 3307 fossil_fatal("Unknown TH1 hook %s", g.argv[2]);
ddd4cb5… mistachkin 3308 }
ddd4cb5… mistachkin 3309 if( g.interp ){
ddd4cb5… mistachkin 3310 zResult = (char*)Th_GetResult(g.interp, &nResult);
ddd4cb5… mistachkin 3311 }
5173701… stephan 3312 sendText(0,"RESULT (", -1, 0);
5173701… stephan 3313 sendText(0,Th_ReturnCodeName(rc, 0), -1, 0);
5173701… stephan 3314 sendText(0,")", -1, 0);
ddd4cb5… mistachkin 3315 if( zResult && nResult>0 ){
5173701… stephan 3316 sendText(0,": ", -1, 0);
5173701… stephan 3317 sendText(0,zResult, nResult, 0);
ddd4cb5… mistachkin 3318 }
5173701… stephan 3319 sendText(0,"\n", -1, 0);
142200b… mistachkin 3320 Th_PrintTraceLog();
c1915c3… mistachkin 3321 if( forceCgi ) cgi_reply();
588bb7c… aku 3322 }
1c528d3… mistachkin 3323 #endif

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button