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: |
b312f5ff5b0c4ef57eaf21ecb274928a |
| 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
Changes to src/cgi.c.
| ︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | ** 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*); /* | > > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > | > > | | 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 |
zContentType = mprintf("%s", zType);
}
/*
** Set the reply content to the specified BLOB.
*/
void cgi_set_content(Blob *pNewContent){
| | > | | 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 |
#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 ){
| > | > | | > > > > | > > | 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 |
/*
** 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);
| | | | 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 |
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);
| | | 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 |
*/
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);
| > | | 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 |
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>
| > > > > > > > > > > > > > > > > > < < | | < | | < < | < | 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;
}
|
| ︙ | ︙ |