Overview
SHA1 Hash: | 929d28e358248b19de0186fe0e82519ad124d26f |
---|---|
Date: | 2007-11-05 02:42:58 |
User: | drh |
Comment: | Added the "e" capability for viewing ticket submitter email addresses. Additional tinkering toward the design of tickets. This check-in is only thinly tested. |
Timelines: | ancestors | descendants | both | trunk |
Other Links: | files | ZIP archive | manifest |
Tags And Properties
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
Changes
[hide diffs]Modified src/login.c from [f15d2f5703] to [457f0114fe].
@@ -291,11 +291,11 @@ switch( zCap[i] ){ case 's': g.okSetup = 1; case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okQuery = g.okRdWiki = g.okWrWiki = g.okNewWiki = g.okApndWiki = g.okHistory = g.okClone = - g.okNewTkt = g.okPassword = 1; + g.okNewTkt = g.okPassword = g.okRdAddr = 1; case 'i': g.okRead = g.okWrite = 1; break; case 'o': g.okRead = 1; break; case 'd': g.okDelete = 1; break; case 'h': g.okHistory = 1; break; @@ -306,17 +306,53 @@ case 'j': g.okRdWiki = 1; break; case 'k': g.okWrWiki = g.okRdWiki = g.okApndWiki =1; break; case 'm': g.okApndWiki = 1; break; case 'f': g.okNewWiki = 1; break; + case 'e': g.okRdAddr = 1; break; case 'r': g.okRdTkt = 1; break; case 'n': g.okNewTkt = 1; break; case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt = g.okApndTkt = 1; break; case 'c': g.okApndTkt = 1; break; } } +} + +/* +** If the current login lacks any of the capabilities listed in +** the input, then return 0. If all capabilities are present, then +** return 1. +*/ +int login_has_capability(const char *zCap, int nCap){ + int i; + int rc = 1; + if( nCap<0 ) nCap = strlen(zCap); + for(i=0; i<nCap && rc && zCap[i]; i++){ + switch( zCap[i] ){ + case 'a': rc = g.okAdmin; break; + case 'c': rc = g.okApndTkt; break; + case 'd': rc = g.okDelete; break; + case 'e': rc = g.okRdAddr; break; + case 'f': rc = g.okNewWiki; break; + case 'g': rc = g.okClone; break; + case 'h': rc = g.okHistory; break; + case 'i': rc = g.okWrite; break; + case 'j': rc = g.okRdWiki; break; + case 'k': rc = g.okWrWiki; break; + case 'm': rc = g.okApndWiki; break; + case 'n': rc = g.okNewTkt; break; + case 'o': rc = g.okRead; break; + case 'p': rc = g.okPassword; break; + case 'q': rc = g.okQuery; break; + case 'r': rc = g.okRdTkt; break; + case 's': rc = g.okSetup; break; + case 'w': rc = g.okWrTkt; break; + default: rc = 0; break; + } + } + return rc; } /* ** Call this routine when the credential check fails. It causes ** a redirect to the "login" page.
Modified src/main.c from [5992525fce] to [cfe3d68152].
@@ -103,10 +103,11 @@ int okWrWiki; /* k: edit wiki via web */ int okRdTkt; /* r: view tickets via web */ int okNewTkt; /* n: create new tickets */ int okApndTkt; /* c: append to tickets via the web */ int okWrTkt; /* w: make changes to tickets via web */ + int okRdAddr; /* e: read email addresses on tickets */ FILE *fDebug; /* Write debug information here, if the file exists */ }; /*
Modified src/sample-config1.txt from [17dcdcf216] to [54c5c82c83].
@@ -79,49 +79,88 @@ <td>After filling in the information above, press this button to create the new ticket</td> </tr> </table> [/status /Open default_value] -} /new_template set +} /new setpage ###################################################################### { + [ + # Extract the current information from the ticket table + {SELECT type, status, subsystem, priority, + severity, contact, title, comment + FROM ticket + WHERE tktid=:name} db_prepare + /name {} cgi_parameter {:name} db_bind + db_exec + if /title exists not { + {<i>No such ticket: } puts + /name {} cgi_parameter htmlize puts + {</i>} puts + return + } endif + /title title cgiparam /vtitle store + /status status cgiparam /vstatus store + /type type cgiparam /vtype store + + if /submit cgiexists { + /name {} cgi_parameter login ticketchng_begin + if /apndcom cgiexists { + {+comment} + {<hr><i>Added by } + login htmlize concat + { on } concat + datetime concat + {:</i><br} concat + /apndcom {} cgi_parameter htmlize concat + ticketchng_field + } else { + if vcomment comment eq not {/comment vcomment ticketchng_field} endif + } endif + if vtitle title eq not {/title vtitle ticketchng_field} endif + ticketchng_submit + baseurl /tktview?name= concat /name {} cgi_parameter concat redirect + } endif + ] + <form method="POST" action="[baseurl]/tktedit"> <table cellpadding="5"> <tr><td align="right">Title:</td><td> - [/title 60 textedit] + <input type="text" name="title" value="[vtitle htmlize puts] size=60"> </td></tr> <tr><td align="right">Status:</td><td> - [/status status_choices 20 combobox] + [vstatus /status status_choices 20 combobox] </td></tr> <tr><td align="right">Type:</td><td> - [/type type_choices 20 combobox] + [vtype /type type_choices 20 combobox] </td></tr> <tr><td align="right">Severity:</td><td> - [/severity {High Medium Low} 10 combobox] + [vseverity /severity {High Medium Low} 10 combobox] </td></tr> <tr><td align="right">Priority:</td><td> - [/priority {High Medium Low} 10 combobox] + [vpriority /priority {High Medium Low} 10 combobox] </td></tr> <tr><td align="right">Resolution:</td><td> - [/resolution resolution_choices 20 combobox] + [vresolution /resolution resolution_choices 20 combobox] </td></tr> <tr><td align="right">Subsystem:</td><td> - [/subsystem subsystem_choices 30 combobox] - </td></tr> - [is_anon not enable_output] + [vsubsystem /subsystem subsystem_choices 30 combobox] + </td></tr> + [{e} hascap enable_output] <tr><td align="right">Contact:</td><td> - [/contact 40 textedit] + <input type="text" name="contact" size="40" value="[vcontact htmlize puts]"> </td></tr> [1 enable_output] <tr><td align="right">Version Found In:</td><td> - [/foundin 50 textedit] + <input type="text" name="foundin" size="50" value="[foundin htmlize puts]"> </td></tr> <tr><td colspan="2"> - [ok_wrtkt /eall 0 paramget and /eall store] + [w hascap /eall 0 paramget and /eall store] [eall enable_output] Description And Comments:<br> - [/comment 70 /comment linecount 15 max multilineedit]<br> - [/aonly {Append Remark} auxbutton] + <textarea name="comment" cols="80" rows="[comment linecount 15 max 10 min]" + wrap="virtual" class="wikiedit">[comment htmlize puts]</textarea><br> + <input type="submit" name="aonly" value="Append Remark"> [eall not enable_output] Append Remark:<br> [/comment /cmappnd 70 /cmappnd linecount 10 max multilineappend]<br> [ok_wrtkt enable_output /eall {Edit All} auxbutton] [1 enable_output] @@ -128,16 +167,16 @@ </td></tr> <tr><td align="right"></td><td> [{Submit Changes} submitbutton] </td></tr> </table> -} /edit_template set +} /edit setpage ###################################################################### { <table cellpadding="5"> <tr><td align="right">Title:</td><td> - [/title textview] + [/title htmlize] </td></tr> <tr><td align="right">Status:</td><td> [/status textview] </td></tr> <tr><td align="right">Type:</td><td> @@ -153,11 +192,11 @@ [/priority textview] </td></tr> <tr><td align="right">Subsystem:</td><td> [/subsystem textview] </td></tr> - [is_anon not enable_output] + [{e} hascap enable_output] <tr><td align="right">Contact:</td><td> [/contact textview] </td></tr> [1 enable_output] <tr><td align="right">Version Found In:</td><td> @@ -166,11 +205,11 @@ <tr><td colspan="2"> Description And Comments:<br> [/comment wikiview] </td></tr> </table> -} /view_template set +} /view setpage ############## # Verb list: # # CNEV
Modified src/setup.c from [44eafbc596] to [24c8846279].
@@ -126,10 +126,11 @@ @ <li><p>The permission flags are as follows:</p> @ <ol type="a"> @ <li value="1"><b>Admin</b>: Create and delete users</li> @ <li value="3"><b>Append-Tkt</b>: Append to tickets</li> @ <li value="4"><b>Delete</b>: Delete wiki and tickets</li> + @ <li value="5"><b>Email</b>: View EMail addresses on tickets</li> @ <li value="6"><b>New-Wiki</b>: Create new wiki pages</li> @ <li value="7"><b>Clone</b>: Clone the repository</li> @ <li value="8"><b>History</b>: View detail repository history</li> @ <li value="9"><b>Check-In</b>: Commit new versions in the repository</li> @ <li value="10"><b>Read-Wiki</b>: View wiki pages</li> @@ -160,12 +161,12 @@ /* ** WEBPAGE: /setup_uedit */ void user_edit(void){ const char *zId, *zLogin, *zInfo, *zCap; - char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap ; - char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag; + char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap; + char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag, *oae; int doWrite; int uid; int higherUser = 0; /* True if user being edited is SETUP and the */ /* user doing the editing is ADMIN. Disallow editing */ @@ -200,10 +201,11 @@ const char *zLogin; char zCap[30]; int i = 0; int aa = P("aa")!=0; int ad = P("ad")!=0; + int ae = P("ae")!=0; int ai = P("ai")!=0; int aj = P("aj")!=0; int ak = P("ak")!=0; int an = P("an")!=0; int ao = P("ao")!=0; @@ -218,10 +220,11 @@ int ah = P("ah")!=0; int ag = P("ag")!=0; if( aa ){ zCap[i++] = 'a'; } if( ac ){ zCap[i++] = 'c'; } if( ad ){ zCap[i++] = 'd'; } + if( ae ){ zCap[i++] = 'e'; } if( af ){ zCap[i++] = 'f'; } if( ah ){ zCap[i++] = 'h'; } if( ag ){ zCap[i++] = 'g'; } if( ai ){ zCap[i++] = 'i'; } if( aj ){ zCap[i++] = 'j'; } @@ -264,19 +267,20 @@ /* Load the existing information about the user, if any */ zLogin = ""; zInfo = ""; zCap = ""; - oaa = oac = oad = oaf = oag = oah = oai = oaj = oak = oam = + oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam = oan = oao = oap = oaq = oar = oas = oaw = ""; if( uid ){ zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); if( strchr(zCap, 'a') ) oaa = " checked"; if( strchr(zCap, 'c') ) oac = " checked"; if( strchr(zCap, 'd') ) oad = " checked"; + if( strchr(zCap, 'e') ) oae = " checked"; if( strchr(zCap, 'f') ) oaf = " checked"; if( strchr(zCap, 'g') ) oag = " checked"; if( strchr(zCap, 'h') ) oah = " checked"; if( strchr(zCap, 'i') ) oai = " checked"; if( strchr(zCap, 'j') ) oaj = " checked"; @@ -324,10 +328,11 @@ if( g.okSetup ){ @ <input type="checkbox" name="as"%s(oas)>Setup</input><br> } @ <input type="checkbox" name="aa"%s(oaa)>Admin</input><br> @ <input type="checkbox" name="ad"%s(oad)>Delete</input><br> + @ <input type="checkbox" name="ae"%s(oad)>Email</input><br> @ <input type="checkbox" name="ap"%s(oap)>Password</input><br> @ <input type="checkbox" name="aq"%s(oaq)>Query</input><br> @ <input type="checkbox" name="ai"%s(oai)>Check-In</input><br> @ <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br> @ <input type="checkbox" name="ah"%s(oah)>History</input><br>
Modified src/subscript.c from [a19913b6c5] to [3df34dd953].
@@ -82,10 +82,63 @@ #define SBSTT_INCOMPLETE 6 /* Unterminated string token */ #define SBSTT_UNKNOWN 7 /* Unknown token */ #define SBSTT_EOF 8 /* End of input */ /* +** Values are stored in the hash table as instances of the following +** structure. +*/ +typedef struct SbSValue SbSValue; +struct SbSValue { + int flags; /* Bitmask of SBSVAL_* values */ + union { + struct { + int size; /* Number of bytes in string, not counting final zero */ + char *z; /* Pointer to string content */ + } str; /* Value if SBSVAL_STR */ + struct { + int (*xVerb)(Subscript*, void*); /* Function to do the work */ + void *pArg; /* 2nd parameter to xVerb */ + } verb; /* Value if SBSVAL_VERB */ + } u; +}; +#define SBSVAL_VERB 0x0001 /* Value stored in u.verb */ +#define SBSVAL_STR 0x0002 /* Value stored in u.str */ +#define SBSVAL_DYN 0x0004 /* u.str.z is dynamically allocated */ +#define SBSVAL_EXEC 0x0008 /* u.str.z is a script */ + +/* +** An entry in the hash table is an instance of this structure. +*/ +typedef struct SbsHashEntry SbsHashEntry; +struct SbsHashEntry { + SbsHashEntry *pNext; /* Next entry with the same hash on zKey */ + SbSValue val; /* The payload */ + int nKey; /* Length of the key */ + char zKey[0]; /* The key */ +}; + +/* +** A hash table is an instance of the following structure. +*/ +typedef struct SbsHashTab SbsHashTab; +struct SbsHashTab { + SbsHashEntry *aHash[SBSCONFIG_NHASH]; /* The hash table */ +}; + +/* +** An instance of the Subscript interpreter +*/ +struct Subscript { + int nStack; /* Number of entries on stack */ + SbsHashTab symTab; /* The symbol table */ + char zErrMsg[SBSCONFIG_ERRSIZE]; /* Space to write an error message */ + SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */ +}; + + +/* ** Given an input string z of length n, identify the token that ** starts at z[0]. Write the token type into *pTokenType and ** return the length of the token. */ static int sbs_next_token(const char *z, int n, int *pTokenType){ @@ -150,33 +203,10 @@ return 1; } /* -** Values are stored in the hash table as instances of the following -** structure. -*/ -typedef struct SbSValue SbSValue; -struct SbSValue { - int flags; /* Bitmask of SBSVAL_* values */ - union { - struct { - int size; /* Number of bytes in string, not counting final zero */ - char *z; /* Pointer to string content */ - } str; /* Value if SBSVAL_STR */ - struct { - int (*xVerb)(Subscript*, void*); /* Function to do the work */ - void *pArg; /* 2nd parameter to xVerb */ - } verb; /* Value if SBSVAL_VERB */ - } u; -}; -#define SBSVAL_VERB 0x0001 /* Value stored in u.verb */ -#define SBSVAL_STR 0x0002 /* Value stored in u.str */ -#define SBSVAL_DYN 0x0004 /* u.str.z is dynamically allocated */ -#define SBSVAL_EXEC 0x0008 /* u.str.z is a script */ - -/* ** Release any memory allocated by a value. */ static void sbs_value_reset(SbSValue *p){ if( p->flags & SBSVAL_DYN ){ free(p->u.str.z); @@ -183,30 +213,10 @@ p->flags = SBSVAL_STR; p->u.str.z = ""; p->u.str.size = 0; } } - - -/* -** An entry in the hash table is an instance of this structure. -*/ -typedef struct SbsHashEntry SbsHashEntry; -struct SbsHashEntry { - SbsHashEntry *pNext; /* Next entry with the same hash on zKey */ - SbSValue val; /* The payload */ - int nKey; /* Length of the key */ - char zKey[0]; /* The key */ -}; - -/* -** A hash table is an instance of the following structure. -*/ -typedef struct SbsHashTab SbsHashTab; -struct SbsHashTab { - SbsHashEntry *aHash[SBSCONFIG_NHASH]; /* The hash table */ -}; /* ** Compute a hash on a string. */ static int sbs_hash(const char *z, int n){ @@ -293,29 +303,32 @@ } memset(pHash, 0, sizeof(*pHash)); } /* -** An instance of the Subscript interpreter -*/ -struct Subscript { - int nStack; /* Number of entries on stack */ - SbsHashTab symTab; /* The symbol table */ - char zErrMsg[SBSCONFIG_ERRSIZE]; /* Space to write an error message */ - SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */ -}; - -/* ** Push a value onto the stack of an interpreter */ static int sbs_push(Subscript *p, SbSValue *pVal){ if( p->nStack>=SBSCONFIG_NSTACK ){ sqlite3_snprintf(SBSCONFIG_ERRSIZE, p->zErrMsg, "stack overflow"); return SBS_ERROR; } p->aStack[p->nStack++] = *pVal; return SBS_OK; +} + +/* +** Create a new subscript interpreter. Return a pointer to the +** new interpreter, or return NULL if malloc fails. +*/ +struct Subscript *SbS_Create(void){ + Subscript *p; + p = malloc( sizeof(*p) ); + if( p ){ + memset(p, 0, sizeof(*p)); + } + return p; } /* ** Destroy an subscript interpreter */ @@ -570,17 +583,32 @@ case SBSOP_ADD: c = a+b; break; case SBSOP_SUB: c = a-b; break; case SBSOP_MUL: c = a*b; break; case SBSOP_DIV: c = b!=0 ? a/b : 0; break; case SBSOP_AND: c = a && b; break; - case SBSOP_OR: c = a || b; break; + case SBSOP_OR: c = a || b; break; case SBSOP_MIN: c = a<b ? a : b; break; case SBSOP_MAX: c = a<b ? b : a; break; } SbS_Pop(p, 2); SbS_PushInt(p, c); return 0; +} + +/* +** Subscript command: STRING hascap INTEGER +** +** Return true if the user has all of the capabilities listed. +*/ +static int hascapCmd(struct Subscript *p, void *pNotUsed){ + const char *z; + int i, n, a; + if( SbS_RequireStack(p, 1, "hascap") ) return 1; + z = SbS_StackValue(p, 0, &n); + a = login_has_capability(z, n); + SbS_Pop(p, 1); + SbS_PushInt(p, a); } /* ** Subscript command: STRING puts */ @@ -587,11 +615,17 @@ static int putsCmd(struct Subscript *p, void *pNotUsed){ int size; const char *z; if( SbS_RequireStack(p, 1, "puts") ) return 1; z = SbS_StackValue(p, 0, &size); - printf("%.*s\n", size, z); + if( g.cgiPanic ){ + char *zCopy = mprintf("%.*s", size, z); + cgi_printf("%h", zCopy); + free(zCopy); + }else{ + printf("%.*s\n", size, z); + } SbS_Pop(p, 1); return 0; } @@ -598,53 +632,38 @@ /* ** A table of built-in commands */ static const struct { const char *zCmd; - int nCmd; int (*xCmd)(Subscript*,void*); void *pArg; } aBuiltin[] = { - { "add", 3, bopCmd, (void*)SBSOP_AND }, - { "and", 3, bopCmd, (void*)SBSOP_AND }, - { "div", 3, bopCmd, (void*)SBSOP_DIV }, - { "max", 3, bopCmd, (void*)SBSOP_MAX }, - { "min", 3, bopCmd, (void*)SBSOP_MIN }, - { "mul", 3, bopCmd, (void*)SBSOP_MUL }, - { "not", 3, notCmd, 0 }, - { "or", 2, bopCmd, (void*)SBSOP_OR }, - { "puts", 4, putsCmd, 0 }, - { "set", 3, setCmd, 0 }, - { "sub", 3, bopCmd, (void*)SBSOP_SUB }, -}; - -/* -** A table of built-in string and integer values -*/ -static const struct { - const char *zVar; - int nVar; - int *pI; - char *z; -} aVars[] = { - { "okAdmin", 7, &g.okAdmin, 0 }, - { "okSetup", 7, &g.okSetup, 0 }, + { "add", bopCmd, (void*)SBSOP_AND }, + { "and", bopCmd, (void*)SBSOP_AND }, + { "div", bopCmd, (void*)SBSOP_DIV }, + { "hascap", hascapCmd, 0 }, + { "max", bopCmd, (void*)SBSOP_MAX }, + { "min", bopCmd, (void*)SBSOP_MIN }, + { "mul", bopCmd, (void*)SBSOP_MUL }, + { "not", notCmd, 0 }, + { "or", bopCmd, (void*)SBSOP_OR }, + { "puts", putsCmd, 0 }, + { "set", setCmd, 0 }, + { "sub", bopCmd, (void*)SBSOP_SUB }, }; - /* -** Create a new subscript interpreter +** Compare a zero-terminated string zPattern against +** an unterminated string zStr of length nStr. */ -struct Subscript *SbS_Create(void){ - Subscript *p; - - p = malloc( sizeof(*p) ); - if( p ){ - memset(p, 0, sizeof(*p)); +static int compare_cmd(const char *zPattern, const char *zStr, int nStr){ + int c = strncmp(zPattern, zStr, nStr); + if( c==0 && zPattern[nStr]!=0 ){ + c = -1; } - return p; + return c; } /* ** Evaluate the script given by the first nScript bytes of zScript[]. ** Return 0 on success and non-zero for an error. @@ -691,41 +710,18 @@ int upr = sizeof(aBuiltin)/sizeof(aBuiltin[0]) - 1; int lwr = 0; rc = SBS_ERROR; while( upr>=lwr ){ int i = (upr+lwr)/2; - int c = strncmp(zScript, aBuiltin[i].zCmd, n); + int c = compare_cmd(aBuiltin[i].zCmd, zScript, n); if( c==0 ){ rc = aBuiltin[i].xCmd(p, aBuiltin[i].pArg); break; }else if( c<0 ){ upr = i-1; }else{ lwr = i+1; - } - } - if( upr<lwr ){ - /* If it is not a built-in command, look for a built-in - ** variable */ - upr = sizeof(aVars)/sizeof(aVars[0]) - 1; - lwr = 0; - while( upr>=lwr ){ - int i = (upr+lwr)/2; - int c = strncmp(zScript, aVars[i].zVar, n); - if( c==0 ){ - if( aVars[i].pI ){ - SbS_PushInt(p, *aVars[i].pI); - }else{ - SbS_Push(p, aVars[i].z, -1, 0); - } - rc = SBS_OK; - break; - }else if( c<0 ){ - upr = i-1; - }else{ - lwr = i+1; - } } } }else if( pVal->flags & SBSVAL_VERB ){ rc = pVal->u.verb.xVerb(p, pVal->u.verb.pArg); }else if( pVal->flags & SBSVAL_EXEC ){