Fossil

Check-in [929d28e358]
Login

Check-in [929d28e358]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
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.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 929d28e358248b19de0186fe0e82519ad124d26f
User & Date: drh 2007-11-05 02:42:58.000
Context
2007-11-07
22:22
Improvements to the merge algorithm so that it works better for common changes. Still more work needed. ... (check-in: ac6bb3ce06 user: drh tags: trunk)
2007-11-05
02:42
Added the "e" capability for viewing ticket submitter email addresses. Additional tinkering toward the design of tickets. This check-in is only thinly tested. ... (check-in: 929d28e358 user: drh tags: trunk)
2007-11-03
04:39
Add support for built-in variables in subscript. ... (check-in: a67fbd784d user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/login.c.
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

311
312
313
314
315
316
317
318



































319
320
321
322
323
324
325
326
327
328
329
  int i;
  for(i=0; zCap[i]; i++){
    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;
      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;
      case 'g':   g.okClone = 1;                                break;
      case 'p':   g.okPassword = 1;                             break;
      case 'q':   g.okQuery = 1;                                break;

      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 '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;
    }
  }
}




































/*
** Call this routine when the credential check fails.  It causes
** a redirect to the "login" page.
*/
void login_needed(void){
  const char *zUrl = PD("REQUEST_URI", "index");
  cgi_redirect(mprintf("login?g=%T", zUrl));
  /* NOTREACHED */
  assert(0);
}







|














>








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
  int i;
  for(i=0; zCap[i]; i++){
    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 = 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;
      case 'g':   g.okClone = 1;                                break;
      case 'p':   g.okPassword = 1;                             break;
      case 'q':   g.okQuery = 1;                                break;

      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.
*/
void login_needed(void){
  const char *zUrl = PD("REQUEST_URI", "index");
  cgi_redirect(mprintf("login?g=%T", zUrl));
  /* NOTREACHED */
  assert(0);
}
Changes to src/main.c.
101
102
103
104
105
106
107

108
109
110
111
112
113
114
  int okNewWiki;          /* f: create new wiki via web */
  int okApndWiki;         /* m: append to wiki via web */
  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 */


  FILE *fDebug;           /* Write debug information here, if the file exists */
};

/*
** Macro for debugging:
*/







>







101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  int okNewWiki;          /* f: create new wiki via web */
  int okApndWiki;         /* m: append to wiki via web */
  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 */
};

/*
** Macro for debugging:
*/
Changes to src/sample-config1.txt.
77
78
79
80
81
82
83
84
85
86






































87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  [submitbutton]
  </td>
  <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
######################################################################
{






































  <table cellpadding="5">
  <tr><td align="right">Title:</td><td>
  [/title 60 textedit]
  </td></tr>
  <tr><td align="right">Status:</td><td>
  [/status status_choices 20 combobox]
  </td></tr>
  <tr><td align="right">Type:</td><td>
  [/type type_choices 20 combobox]
  </td></tr>
  <tr><td align="right">Severity:</td><td>
  [/severity {High Medium Low} 10 combobox]
  </td></tr>
  <tr><td align="right">Priority:</td><td>
  [/priority {High Medium Low} 10 combobox]
  </td></tr>
  <tr><td align="right">Resolution:</td><td>
  [/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]
    <tr><td align="right">Contact:</td><td>
    [/contact 40 textedit]
    </td></tr>
  [1 enable_output]
  <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
  [/foundin 50 textedit]
  </td></tr>
  <tr><td colspan="2">
  [ok_wrtkt /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]
  [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]
  </td></tr>
  <tr><td align="right"></td><td>
  [{Submit Changes} submitbutton]
  </td></tr>
  </table>
} /edit_template set
######################################################################
{
  <table cellpadding="5">
  <tr><td align="right">Title:</td><td>
  [/title textview]
  </td></tr>
  <tr><td align="right">Status:</td><td>
  [/status textview]
  </td></tr>
  <tr><td align="right">Type:</td><td>
  [/type textview]
  </td></tr>
  <tr><td align="right">Severity:</td><td>
  [/severity textview]
  </td></tr>
  <tr><td align="right">Priority:</td><td>
  [/priority textview]
  </td></tr>
  <tr><td align="right">Resolution:</td><td>
  [/priority textview]
  </td></tr>
  <tr><td align="right">Subsystem:</td><td>
  [/subsystem textview]
  </td></tr>
  [is_anon not enable_output]
    <tr><td align="right">Contact:</td><td>
    [/contact textview]
    </td></tr>
  [1 enable_output]
  <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
  [/foundin textview]
  </td></tr>
  <tr><td colspan="2">
  Description And Comments:<br>
  [/comment wikiview]
  </td></tr>
  </table>
} /view_template set

##############
# Verb list:
#
# CNEV
# ****  VALUE NAME set
# *---  LIST setfields







|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|


|


|


|


|


|


|

|

|



|


|


|
>
|










|




|



















|












|







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
  [submitbutton]
  </td>
  <td>After filling in the information above, press this button to create
  the new ticket</td>
  </tr>
  </table>
  [/status /Open default_value]
} /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>
  <input type="text" name="title" value="[vtitle htmlize puts] size=60">
  </td></tr>
  <tr><td align="right">Status:</td><td>
  [vstatus /status status_choices 20 combobox]
  </td></tr>
  <tr><td align="right">Type:</td><td>
  [vtype /type type_choices 20 combobox]
  </td></tr>
  <tr><td align="right">Severity:</td><td>
  [vseverity /severity {High Medium Low} 10 combobox]
  </td></tr>
  <tr><td align="right">Priority:</td><td>
  [vpriority /priority {High Medium Low} 10 combobox]
  </td></tr>
  <tr><td align="right">Resolution:</td><td>
  [vresolution /resolution resolution_choices 20 combobox]
  </td></tr>
  <tr><td align="right">Subsystem:</td><td>
  [vsubsystem /subsystem subsystem_choices 30 combobox]
  </td></tr>
  [{e} hascap enable_output]
    <tr><td align="right">Contact:</td><td>
    <input type="text" name="contact" size="40" value="[vcontact htmlize puts]">
    </td></tr>
  [1 enable_output]
  <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
  <input type="text" name="foundin" size="50" value="[foundin htmlize puts]">
  </td></tr>
  <tr><td colspan="2">
  [w hascap /eall 0 paramget and /eall store]
  [eall enable_output]
    Description And Comments:<br>
    <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]
  </td></tr>
  <tr><td align="right"></td><td>
  [{Submit Changes} submitbutton]
  </td></tr>
  </table>
} /edit setpage
######################################################################
{
  <table cellpadding="5">
  <tr><td align="right">Title:</td><td>
  [/title htmlize]
  </td></tr>
  <tr><td align="right">Status:</td><td>
  [/status textview]
  </td></tr>
  <tr><td align="right">Type:</td><td>
  [/type textview]
  </td></tr>
  <tr><td align="right">Severity:</td><td>
  [/severity textview]
  </td></tr>
  <tr><td align="right">Priority:</td><td>
  [/priority textview]
  </td></tr>
  <tr><td align="right">Resolution:</td><td>
  [/priority textview]
  </td></tr>
  <tr><td align="right">Subsystem:</td><td>
  [/subsystem textview]
  </td></tr>
  [{e} hascap enable_output]
    <tr><td align="right">Contact:</td><td>
    [/contact textview]
    </td></tr>
  [1 enable_output]
  <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
  [/foundin textview]
  </td></tr>
  <tr><td colspan="2">
  Description And Comments:<br>
  [/comment wikiview]
  </td></tr>
  </table>
} /view setpage

##############
# Verb list:
#
# CNEV
# ****  VALUE NAME set
# *---  LIST setfields
Changes to src/setup.c.
124
125
126
127
128
129
130

131
132
133
134
135
136
137
  @ <b>Notes:</b>
  @ <ol>
  @ <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="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>
  @ <li value="11"><b>Write-Wiki</b>: Edit wiki pages</li>
  @ <li value="13"><b>Append-Wiki</b>: Append to wiki pages</li>







>







124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  @ <b>Notes:</b>
  @ <ol>
  @ <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>
  @ <li value="11"><b>Write-Wiki</b>: Edit wiki pages</li>
  @ <li value="13"><b>Append-Wiki</b>: Append to wiki pages</li>
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
}

/*
** 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;
  int doWrite;
  int uid;
  int higherUser = 0;  /* True if user being edited is SETUP and the */
                       /* user doing the editing is ADMIN.  Disallow editing */

  /* Must have ADMIN privleges to access this page
  */







|
|







159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
}

/*
** 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, *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 */

  /* Must have ADMIN privleges to access this page
  */
198
199
200
201
202
203
204

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

223
224
225
226
227
228
229
  if( doWrite ){
    const char *zPw;
    const char *zLogin;
    char zCap[30];
    int i = 0;
    int aa = P("aa")!=0;
    int ad = P("ad")!=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;
    int ap = P("ap")!=0;
    int aq = P("aq")!=0;
    int ar = P("ar")!=0;
    int as = g.okSetup && P("as")!=0;
    int aw = P("aw")!=0;
    int ac = P("ac")!=0;
    int af = P("af")!=0;
    int am = P("am")!=0;
    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( af ){ zCap[i++] = 'f'; }
    if( ah ){ zCap[i++] = 'h'; }
    if( ag ){ zCap[i++] = 'g'; }
    if( ai ){ zCap[i++] = 'i'; }
    if( aj ){ zCap[i++] = 'j'; }
    if( ak ){ zCap[i++] = 'k'; }
    if( am ){ zCap[i++] = 'm'; }







>


















>







199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  if( doWrite ){
    const char *zPw;
    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;
    int ap = P("ap")!=0;
    int aq = P("aq")!=0;
    int ar = P("ar")!=0;
    int as = g.okSetup && P("as")!=0;
    int aw = P("aw")!=0;
    int ac = P("ac")!=0;
    int af = P("af")!=0;
    int am = P("am")!=0;
    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'; }
    if( ak ){ zCap[i++] = 'k'; }
    if( am ){ zCap[i++] = 'm'; }
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

278
279
280
281
282
283
284
  }

  /* Load the existing information about the user, if any
  */
  zLogin = "";
  zInfo = "";
  zCap = "";
  oaa = oac = oad = 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, '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";
    if( strchr(zCap, 'k') ) oak = " checked";
    if( strchr(zCap, 'm') ) oam = " checked";







|








>







265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
  }

  /* Load the existing information about the user, if any
  */
  zLogin = "";
  zInfo = "";
  zCap = "";
  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";
    if( strchr(zCap, 'k') ) oak = " checked";
    if( strchr(zCap, 'm') ) oam = " checked";
322
323
324
325
326
327
328

329
330
331
332
333
334
335
  @   <td align="right" valign="top">Capabilities:</td>
  @   <td>
  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="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>
  @     <input type="checkbox" name="ag"%s(oag)>Clone</input><br>
  @     <input type="checkbox" name="aj"%s(oaj)>Read Wiki</input><br>







>







326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
  @   <td align="right" valign="top">Capabilities:</td>
  @   <td>
  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>
  @     <input type="checkbox" name="ag"%s(oag)>Clone</input><br>
  @     <input type="checkbox" name="aj"%s(oaj)>Read Wiki</input><br>
Changes to src/subscript.c.
78
79
80
81
82
83
84





















































85
86
87
88
89
90
91
#define SBSTT_NAME        2    /* ex:   /abcde  */
#define SBSTT_VERB        3    /* ex:   abcde   */
#define SBSTT_STRING      4    /* ex:   {...}   */
#define SBSTT_INTEGER     5    /* Integer including option sign */
#define SBSTT_INCOMPLETE  6    /* Unterminated string token */
#define SBSTT_UNKNOWN     7    /* Unknown token */
#define SBSTT_EOF         8    /* End of input */






















































/*
** 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){







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#define SBSTT_NAME        2    /* ex:   /abcde  */
#define SBSTT_VERB        3    /* ex:   abcde   */
#define SBSTT_STRING      4    /* ex:   {...}   */
#define SBSTT_INTEGER     5    /* Integer including option sign */
#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){
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
    return i;
  }
  *pTokenType = SBSTT_UNKNOWN;
  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);
    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){
  int h = 0;
  int i;
  for(i=0; i<n; i++){







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







200
201
202
203
204
205
206























207
208
209
210
211
212
213
214
215
216
217
218




















219
220
221
222
223
224
225
    return i;
  }
  *pTokenType = SBSTT_UNKNOWN;
  return 1;
}

























/*
** Release any memory allocated by a value.
*/
static void sbs_value_reset(SbSValue *p){
  if( p->flags & SBSVAL_DYN ){
    free(p->u.str.z);
    p->flags = SBSVAL_STR;
    p->u.str.z = "";
    p->u.str.size = 0;
  }
}





















/*
** Compute a hash on a string.
*/
static int sbs_hash(const char *z, int n){
  int h = 0;
  int i;
  for(i=0; i<n; i++){
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317













318
319
320
321
322
323
324
      sbs_value_reset(&p->val);
      free(p);
    }
  }
  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;
}














/*
** Destroy an subscript interpreter
*/
void SbS_Destroy(struct Subscript *p){
  int i;
  sbs_hash_reset(&p->symTab);







<
<
<
<
<
<
<
<
<
<











>
>
>
>
>
>
>
>
>
>
>
>
>







300
301
302
303
304
305
306










307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
      sbs_value_reset(&p->val);
      free(p);
    }
  }
  memset(pHash, 0, sizeof(*pHash));
}











/*
** 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
*/
void SbS_Destroy(struct Subscript *p){
  int i;
  sbs_hash_reset(&p->symTab);
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582















583
584
585
586
587
588
589
590
591





592

593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609

610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636


637
638
639
640
641
642
643

644
645
646
647
648
649
650
651
652
  b = SbS_StackValueInt(p, 1);
  switch( (int)pOp ){
    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_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 puts
*/
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);

  SbS_Pop(p, 1);
  return 0;
}


/*
** 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 },
};
  


/*
** Create a new subscript interpreter


*/
struct Subscript *SbS_Create(void){
  Subscript *p;

  p = malloc( sizeof(*p) );
  if( p ){
    memset(p, 0, sizeof(*p));

  }
  return p;
}

/*
** Evaluate the script given by the first nScript bytes of zScript[].
** Return 0 on success and non-zero for an error.
*/
int SbS_Eval(struct Subscript *p, const char *zScript, int nScript){







|







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









>
>
>
>
>
|
>










<



|
|
|
>
|
|
|
|
|
|
|
|

|
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<

<
>
>

<
<
|
|
|
<
>

|







581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636

637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653












654


655

656
657
658


659
660
661

662
663
664
665
666
667
668
669
670
671
  b = SbS_StackValueInt(p, 1);
  switch( (int)pOp ){
    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_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
*/
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);
  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;
}


/*
** A table of built-in commands
*/
static const struct {
  const char *zCmd;

  int (*xCmd)(Subscript*,void*);
  void *pArg;
} aBuiltin[] = {
  { "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    },
};
  















/*

** Compare a zero-terminated string zPattern against
** an unterminated string zStr of length nStr.
*/


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 c;
}

/*
** Evaluate the script given by the first nScript bytes of zScript[].
** Return 0 on success and non-zero for an error.
*/
int SbS_Eval(struct Subscript *p, const char *zScript, int nScript){
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
          /* If the verb is not in the hash table, look for a 
          ** built-in command */
          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);
            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 ){
          rc = SbS_Eval(p, pVal->u.str.z, pVal->u.str.size);
        }else{
          rc = SbS_Push(p, pVal->u.str.z, pVal->u.str.size, 0);
        }







|









<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724























725
726
727
728
729
730
731
          /* If the verb is not in the hash table, look for a 
          ** built-in command */
          int upr = sizeof(aBuiltin)/sizeof(aBuiltin[0]) - 1;
          int lwr = 0;
          rc = SBS_ERROR;
          while( upr>=lwr ){
            int i = (upr+lwr)/2;
            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;
            }
          }























        }else if( pVal->flags & SBSVAL_VERB ){
          rc = pVal->u.verb.xVerb(p, pVal->u.verb.pArg);
        }else if( pVal->flags & SBSVAL_EXEC ){
          rc = SbS_Eval(p, pVal->u.str.z, pVal->u.str.size);
        }else{
          rc = SbS_Push(p, pVal->u.str.z, pVal->u.str.size, 0);
        }