Fossil

Check-in [b312f5ff5b]
Login

Check-in [b312f5ff5b]

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

Overview
Comment:Merge in some changes to the CGI reply generator that we made back in early December but got lost on an abandoned branch. Distributed version control is nice, but it also leaves open the real danger of losing changes this way. We need to work on interface features to minimize the risk of losing changes like this, and to identify lost changes quickly.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: b312f5ff5b0c4ef57eaf21ecb274928a87817ce6
User & Date: drh 2008-02-03 02:41:50.000
Context
2008-02-03
16:32
Refactored to use a shared query-rendering routine. ... (check-in: 02a7c850b4 user: stephan tags: trunk)
02:41
Merge in some changes to the CGI reply generator that we made back in early December but got lost on an abandoned branch. Distributed version control is nice, but it also leaves open the real danger of losing changes this way. We need to work on interface features to minimize the risk of losing changes like this, and to identify lost changes quickly. ... (check-in: b312f5ff5b user: drh tags: trunk)
02:38
Documentation updates. ... (check-in: c403f9dc40 user: drh tags: trunk)
2007-12-04
13:05
Generate CGI replies as separate header and body so that the header can be extended during the construction of the body. ... (check-in: 6af8fdc230 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cgi.c.
55
56
57
58
59
60
61







62
63
64
65
66
67
68
69
70
71
72







73

74



















75
76
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
** does the same except "y" is returned in place of NULL if there is not match.
*/
#define P(x)        cgi_parameter((x),0)
#define PD(x,y)     cgi_parameter((x),(y))
#define QP(x)       quotable_string(cgi_parameter((x),0))
#define QPD(x,y)    quotable_string(cgi_parameter((x),(y)))








#endif /* INTERFACE */

/*
** Provide a reliable implementation of a caseless string comparison
** function.
*/
#define stricmp sqlite3StrICmp
extern int sqlite3StrICmp(const char*, const char*);

/*
** The body of the HTTP reply text is stored here.







*/

static Blob cgiContent = BLOB_INITIALIZER;




















/*
** Append reply content to what already exists.
*/
void cgi_append_content(const char *zData, int nAmt){
  blob_append(&cgiContent, zData, nAmt);
}

/*
** Reset the HTTP reply text to be an empty string.
*/
void cgi_reset_content(void){
  blob_reset(&cgiContent);

}

/*
** Return a pointer to the CGI output blob.
*/
Blob *cgi_output_blob(void){










  return &cgiContent;

}

/*
** Return a pointer to the HTTP reply text.
*/
char *cgi_extract_content(int *pnAmt){

  return blob_buffer(&cgiContent);
}

/*
** Additional information used to form the HTTP reply
*/
static char *zContentType = "text/html";     /* Content type of the reply */
static char *zReplyStatus = "OK";            /* Reply status description */







>
>
>
>
>
>
>










|
>
>
>
>
>
>
>

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





|






|
>






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






>
|







55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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
** does the same except "y" is returned in place of NULL if there is not match.
*/
#define P(x)        cgi_parameter((x),0)
#define PD(x,y)     cgi_parameter((x),(y))
#define QP(x)       quotable_string(cgi_parameter((x),0))
#define QPD(x,y)    quotable_string(cgi_parameter((x),(y)))


/*
** Destinations for output text.
*/
#define CGI_HEADER   0
#define CGI_BODY     1

#endif /* INTERFACE */

/*
** Provide a reliable implementation of a caseless string comparison
** function.
*/
#define stricmp sqlite3StrICmp
extern int sqlite3StrICmp(const char*, const char*);

/*
** The HTTP reply is generated in two pieces: the header and the body.
** These pieces are generated separately because they are not necessary
** produced in order.  Parts of the header might be built after all or
** part of the body.  The header and body are accumulated in separate
** Blob structures then output sequentially once everything has been
** built.
**
** The cgi_destination() interface switch between the buffers.
*/
static Blob *pContent;
static Blob cgiContent[2] = { BLOB_INITIALIZER, BLOB_INITIALIZER };

/*
** Set the destination buffer into which to accumulate CGI content.
*/
void cgi_destination(int dest){
  switch( dest ){
    case CGI_HEADER: {
      pContent = &cgiContent[0];
      break;
    }
    case CGI_BODY: {
      pContent = &cgiContent[1];
      break;
    }
    default: {
      cgi_panic("bad destination");
    }
  }
}

/*
** Append reply content to what already exists.
*/
void cgi_append_content(const char *zData, int nAmt){
  blob_append(pContent, zData, nAmt);
}

/*
** Reset the HTTP reply text to be an empty string.
*/
void cgi_reset_content(void){
  blob_reset(&cgiContent[0]);
  blob_reset(&cgiContent[1]);
}

/*
** Return a pointer to the CGI output blob.
*/
Blob *cgi_output_blob(void){
  return pContent;
}

/*
** Combine the header and body of the CGI into a single string.
*/
static void cgi_combine_header_and_body(void){
  int size = blob_size(&cgiContent[1]);
  if( size>0 ){
    blob_append(&cgiContent[0], blob_buffer(&cgiContent[1]), size);
    blob_reset(&cgiContent[1]);
  }
}

/*
** Return a pointer to the HTTP reply text.
*/
char *cgi_extract_content(int *pnAmt){
  cgi_combine_header_and_body();
  return blob_buffer(&cgiContent[0]);
}

/*
** Additional information used to form the HTTP reply
*/
static char *zContentType = "text/html";     /* Content type of the reply */
static char *zReplyStatus = "OK";            /* Reply status description */
117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
132
  zContentType = mprintf("%s", zType);
}

/*
** Set the reply content to the specified BLOB.
*/
void cgi_set_content(Blob *pNewContent){
  blob_reset(&cgiContent);

  cgiContent = *pNewContent;
  blob_zero(pNewContent);
}

/*
** Set the reply status code
*/
void cgi_set_status(int iStat, const char *zStat){







|
>
|







164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  zContentType = mprintf("%s", zType);
}

/*
** Set the reply content to the specified BLOB.
*/
void cgi_set_content(Blob *pNewContent){
  cgi_reset_content();
  cgi_destination(CGI_HEADER);
  cgiContent[0] = *pNewContent;
  blob_zero(pNewContent);
}

/*
** Set the reply status code
*/
void cgi_set_status(int iStat, const char *zStat){
218
219
220
221
222
223
224

225
226
227
228
229
230
231
}
#endif

/*
** Do a normal HTTP reply
*/
void cgi_reply(void){

  if( iReplyStatus<=0 ){
    iReplyStatus = 200;
    zReplyStatus = "OK";
  }

#if 0
  if( iReplyStatus==200 && check_cache_control() ) {







>







266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
}
#endif

/*
** Do a normal HTTP reply
*/
void cgi_reply(void){
  int total_size;
  if( iReplyStatus<=0 ){
    iReplyStatus = 200;
    zReplyStatus = "OK";
  }

#if 0
  if( iReplyStatus==200 && check_cache_control() ) {
271
272
273
274
275
276
277

278
279
280
281

282
283
284
285




286


287
288
289
290
291
292
293

#if FOSSIL_I18N
  printf( "Content-Type: %s; charset=%s\r\n", zContentType, nl_langinfo(CODESET));
#else
  printf( "Content-Type: %s; charset=ISO-8859-1\r\n", zContentType);
#endif
  if( strcmp(zContentType,"application/x-fossil")==0 ){

    blob_compress(&cgiContent, &cgiContent);
  }

  if( iReplyStatus != 304 ) {

    printf( "Content-Length: %d\r\n", blob_size(&cgiContent) );
  }
  printf("\r\n");
  if( blob_size(&cgiContent)>0 && iReplyStatus != 304 ){




    fwrite(blob_buffer(&cgiContent), 1, blob_size(&cgiContent), stdout);


  }
  CGIDEBUG(("DONE\n"));
}

/*
** Do a redirect request to the URL given in the argument.
**







>
|



>
|


|
>
>
>
>
|
>
>







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

#if FOSSIL_I18N
  printf( "Content-Type: %s; charset=%s\r\n", zContentType, nl_langinfo(CODESET));
#else
  printf( "Content-Type: %s; charset=ISO-8859-1\r\n", zContentType);
#endif
  if( strcmp(zContentType,"application/x-fossil")==0 ){
    cgi_combine_header_and_body();
    blob_compress(&cgiContent[0], &cgiContent[0]);
  }

  if( iReplyStatus != 304 ) {
    total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]);
    printf( "Content-Length: %d\r\n", total_size);
  }
  printf("\r\n");
  if( total_size>0 && iReplyStatus != 304 ){
    int i, size;
    for(i=0; i<2; i++){
      size = blob_size(&cgiContent[i]);
      if( size>0 ){
        fwrite(blob_buffer(&cgiContent[i]), 1, size, stdout);
      }
    }
  }
  CGIDEBUG(("DONE\n"));
}

/*
** Do a redirect request to the URL given in the argument.
**
609
610
611
612
613
614
615

616
617
618
619
620
621
622
** the QUERY_STRING environment variable (if it exists), from standard
** input if there is POST data, and from HTTP_COOKIE.
*/
void cgi_init(void){
  char *z;
  const char *zType;
  int len;

  z = (char*)P("QUERY_STRING");
  if( z ){
    z = mprintf("%s",z);
    add_param_list(z, '&');
  }

  len = atoi(PD("CONTENT_LENGTH", "0"));







>







666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
** the QUERY_STRING environment variable (if it exists), from standard
** input if there is POST data, and from HTTP_COOKIE.
*/
void cgi_init(void){
  char *z;
  const char *zType;
  int len;
  cgi_destination(CGI_BODY);
  z = (char*)P("QUERY_STRING");
  if( z ){
    z = mprintf("%s",z);
    add_param_list(z, '&');
  }

  len = atoi(PD("CONTENT_LENGTH", "0"));
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
/*
** This routine works like "printf" except that it has the
** extra formatting capabilities such as %h and %t.
*/
void cgi_printf(const char *zFormat, ...){
  va_list ap;
  va_start(ap,zFormat);
  vxprintf(&cgiContent,zFormat,ap);
  va_end(ap);
}

/*
** This routine works like "vprintf" except that it has the
** extra formatting capabilities such as %h and %t.
*/
void cgi_vprintf(const char *zFormat, va_list ap){
  vxprintf(&cgiContent,zFormat,ap);
}


/*
** Send a reply indicating that the HTTP request was malformed
*/
static void malformed_request(void){







|








|







995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
/*
** This routine works like "printf" except that it has the
** extra formatting capabilities such as %h and %t.
*/
void cgi_printf(const char *zFormat, ...){
  va_list ap;
  va_start(ap,zFormat);
  vxprintf(pContent,zFormat,ap);
  va_end(ap);
}

/*
** This routine works like "vprintf" except that it has the
** extra formatting capabilities such as %h and %t.
*/
void cgi_vprintf(const char *zFormat, va_list ap){
  vxprintf(pContent,zFormat,ap);
}


/*
** Send a reply indicating that the HTTP request was malformed
*/
static void malformed_request(void){
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
  cgi_reset_content();
  cgi_set_status(500, "Internal Server Error");
  cgi_printf(
    "<html><body><h1>Internal Server Error</h1>\n"
    "<plaintext>"
  );
  va_start(ap, zFormat);
  vxprintf(&cgiContent,zFormat,ap);
  va_end(ap);
  cgi_reply();
  exit(1);
}

/*
** Remove the first space-delimited token from a string and return







|







1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
  cgi_reset_content();
  cgi_set_status(500, "Internal Server Error");
  cgi_printf(
    "<html><body><h1>Internal Server Error</h1>\n"
    "<plaintext>"
  );
  va_start(ap, zFormat);
  vxprintf(pContent,zFormat,ap);
  va_end(ap);
  cgi_reply();
  exit(1);
}

/*
** Remove the first space-delimited token from a string and return
Changes to src/style.c.
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
*/
void style_header(const char *zTitle){
  const char *zLogInOut = "Login";
  const char *zHeader = db_get("header", (char*)zDefaultHeader);  
  login_check_credentials();
  
  if( pInterp ) return;


  /* Generate the header up through the main menu */
  pInterp = SbS_Create();
  SbS_Store(pInterp, "project_name",
                     db_get("project-name","Unnamed Fossil Project"), 0);
  SbS_Store(pInterp, "title", zTitle, 0);
  SbS_Store(pInterp, "baseurl", g.zBaseURL, 0);
  SbS_Store(pInterp, "manifest_version", MANIFEST_VERSION, 0);
  SbS_Store(pInterp, "manifest_date", MANIFEST_DATE, 0);
  if( g.zLogin ){
    SbS_Store(pInterp, "login", g.zLogin, 0);
    zLogInOut = "Logout";
  }
  SbS_Render(pInterp, zHeader);

  /* Generate the main menu and the submenu (if any) */
  @ <div class="mainmenu">
  @ <a href="%s(g.zBaseURL)/home">Home</a>
  if( g.okRead ){
    @ <a href="%s(g.zBaseURL)/leaves">Leaves</a>
    @ <a href="%s(g.zBaseURL)/timeline">Timeline</a>
    @ <a href="%s(g.zBaseURL)/tagview">Tags</a>
  }







>















|







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
*/
void style_header(const char *zTitle){
  const char *zLogInOut = "Login";
  const char *zHeader = db_get("header", (char*)zDefaultHeader);  
  login_check_credentials();
  
  if( pInterp ) return;
  cgi_destination(CGI_HEADER);

  /* Generate the header up through the main menu */
  pInterp = SbS_Create();
  SbS_Store(pInterp, "project_name",
                     db_get("project-name","Unnamed Fossil Project"), 0);
  SbS_Store(pInterp, "title", zTitle, 0);
  SbS_Store(pInterp, "baseurl", g.zBaseURL, 0);
  SbS_Store(pInterp, "manifest_version", MANIFEST_VERSION, 0);
  SbS_Store(pInterp, "manifest_date", MANIFEST_DATE, 0);
  if( g.zLogin ){
    SbS_Store(pInterp, "login", g.zLogin, 0);
    zLogInOut = "Logout";
  }
  SbS_Render(pInterp, zHeader);

  /* Generate the main menu */
  @ <div class="mainmenu">
  @ <a href="%s(g.zBaseURL)/home">Home</a>
  if( g.okRead ){
    @ <a href="%s(g.zBaseURL)/leaves">Leaves</a>
    @ <a href="%s(g.zBaseURL)/timeline">Timeline</a>
    @ <a href="%s(g.zBaseURL)/tagview">Tags</a>
  }
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
  if( g.okSetup ){
    @ <a href="%s(g.zBaseURL)/setup">Setup</a>
  }
  if( !g.noPswd ){
    @ <a href="%s(g.zBaseURL)/login">%s(zLogInOut)</a>
  }
  @ </div>
















  if( nSubmenu>0 ){
    int i;

    @ <div class="submenu">
    qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
    for(i=0; i<nSubmenu; i++){
      struct Submenu *p = &aSubmenu[i];
      if( p->zLink==0 ){
        @ <span class="label">%h(p->zLabel)</span>
      }else{
        @ <a class="label" href="%s(p->zLink)">%h(p->zLabel)</a>
      }
    }
    @ </div>
  }
  @ <div class="content">
  g.cgiPanic = 1;
}

/*
** Draw the footer at the bottom of the page.
*/
void style_footer(void){
  const char *zFooter;
  
  if( pInterp==0 ) return;
  zFooter = db_get("footer", (char*)zDefaultFooter);
  @ </div>
  SbS_Render(pInterp, zFooter);
  SbS_Destroy(pInterp);
  pInterp = 0;
}








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


>











<
<
|
|

<
|
|
<
<
|
<







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
  if( g.okSetup ){
    @ <a href="%s(g.zBaseURL)/setup">Setup</a>
  }
  if( !g.noPswd ){
    @ <a href="%s(g.zBaseURL)/login">%s(zLogInOut)</a>
  }
  @ </div>
  cgi_destination(CGI_BODY);
  g.cgiPanic = 1;
}

/*
** Draw the footer at the bottom of the page.
*/
void style_footer(void){
  const char *zFooter;
  
  if( pInterp==0 ) return;

  /* Go back and put the submenu at the top of the page.  We delay the
  ** creation of the submenu until the end so that we can add elements
  ** to the submenu while generating page text.
  */
  if( nSubmenu>0 ){
    int i;
    cgi_destination(CGI_HEADER);
    @ <div class="submenu">
    qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
    for(i=0; i<nSubmenu; i++){
      struct Submenu *p = &aSubmenu[i];
      if( p->zLink==0 ){
        @ <span class="label">%h(p->zLabel)</span>
      }else{
        @ <a class="label" href="%s(p->zLink)">%h(p->zLabel)</a>
      }
    }
    @ </div>


    cgi_destination(CGI_BODY);
  }


  /* Put the footer at the bottom of the page.
  */


  @ <div class="content">

  zFooter = db_get("footer", (char*)zDefaultFooter);
  @ </div>
  SbS_Render(pInterp, zFooter);
  SbS_Destroy(pInterp);
  pInterp = 0;
}