📄 webs.c
字号:
time_t tip = 0;
if ((cp = gstrchr(value, ';')) != NULL) {
*cp = '\0';
}
fmtAlloc(&cmd, 64, T("%s"), value);
if ((wp->since = dateParse(tip, cmd)) != 0) {
wp->flags |= WEBS_IF_MODIFIED;
}
bfreeSafe(B_L, cmd);
#endif /* WEBS_IF_MODIFIED_SUPPORT */
}
}
}
/******************************************************************************/
/*
* Set the variable (CGI) environment for this request. Create variables
* for all standard CGI variables. Also decode the query string and create
* a variable for each name=value pair.
*/
void websSetEnv(webs_t wp)
{
char_t portBuf[8];
char_t *keyword, *value, *valCheck, *valNew;
a_assert(websValid(wp));
websSetVar(wp, T("QUERY_STRING"), wp->query);
websSetVar(wp, T("GATEWAY_INTERFACE"), T("CGI/1.1"));
websSetVar(wp, T("SERVER_HOST"), websHost);
websSetVar(wp, T("SERVER_NAME"), websHost);
websSetVar(wp, T("SERVER_URL"), websHostUrl);
websSetVar(wp, T("REMOTE_HOST"), wp->ipaddr);
websSetVar(wp, T("REMOTE_ADDR"), wp->ipaddr);
websSetVar(wp, T("PATH_INFO"), wp->path);
stritoa(websPort, portBuf, sizeof(portBuf));
websSetVar(wp, T("SERVER_PORT"), portBuf);
websSetVar(wp, T("SERVER_ADDR"), websIpaddr);
fmtAlloc(&value, FNAMESIZE, T("%s/%s"), WEBS_NAME, WEBS_VERSION);
websSetVar(wp, T("SERVER_SOFTWARE"), value);
bfreeSafe(B_L, value);
websSetVar(wp, T("SERVER_PROTOCOL"), wp->protoVersion);
/*
* Decode and create an environment query variable for each query keyword.
* We split into pairs at each '&', then split pairs at the '='.
* Note: we rely on wp->decodedQuery preserving the decoded values in the
* symbol table.
*/
wp->decodedQuery = bstrdup(B_L, wp->query);
keyword = gstrtok(wp->decodedQuery, T("&"));
while (keyword != NULL) {
if ((value = gstrchr(keyword, '=')) != NULL) {
*value++ = '\0';
websDecodeUrl(keyword, keyword, gstrlen(keyword));
websDecodeUrl(value, value, gstrlen(value));
} else {
value = T("");
}
if (*keyword) {
/*
* If keyword has already been set, append the new value to what has
* been stored.
*/
if ((valCheck = websGetVar(wp, keyword, NULL)) != 0) {
fmtAlloc(&valNew, 256, T("%s %s"), valCheck, value);
websSetVar(wp, keyword, valNew);
bfreeSafe(B_L, valNew);
} else {
websSetVar(wp, keyword, value);
}
}
keyword = gstrtok(NULL, T("&"));
}
#ifdef EMF
/*
* Add GoAhead Embedded Management Framework defines
*/
websSetEmfEnvironment(wp);
#endif
}
/******************************************************************************/
/*
* Define a webs (CGI) variable for this connection. Also create in relevant
* scripting engines. Note: the incoming value may be volatile.
*/
void websSetVar(webs_t wp, char_t *var, char_t *value)
{
value_t v;
a_assert(websValid(wp));
/*
* value_instring will allocate the string if required.
*/
if (value) {
v = valueString(value, VALUE_ALLOCATE);
} else {
v = valueString(T(""), VALUE_ALLOCATE);
}
symEnter(wp->cgiVars, var, v, 0);
}
/******************************************************************************/
/*
* Return TRUE if a webs variable exists for this connection.
*/
int websTestVar(webs_t wp, char_t *var)
{
sym_t *sp;
a_assert(websValid(wp));
if (var == NULL || *var == '\0') {
return 0;
}
if ((sp = symLookup(wp->cgiVars, var)) == NULL) {
return 0;
}
return 1;
}
/******************************************************************************/
/*
* Get a webs variable but return a default value if string not found.
* Note, defaultGetValue can be NULL to permit testing existence.
*/
char_t *websGetVar(webs_t wp, char_t *var, char_t *defaultGetValue)
{
sym_t *sp;
a_assert(websValid(wp));
a_assert(var && *var);
if ((sp = symLookup(wp->cgiVars, var)) != NULL) {
a_assert(sp->content.type == string);
if (sp->content.value.string) {
return sp->content.value.string;
} else {
return T("");
}
}
return defaultGetValue;
}
/******************************************************************************/
/*
* Return TRUE if a webs variable is set to a given value
*/
int websCompareVar(webs_t wp, char_t *var, char_t *value)
{
a_assert(websValid(wp));
a_assert(var && *var);
if (gstrcmp(value, websGetVar(wp, var, T(" __UNDEF__ "))) == 0) {
return 1;
}
return 0;
}
/******************************************************************************/
/*
* Cancel the request timeout. Note may be called multiple times.
*/
void websTimeoutCancel(webs_t wp)
{
a_assert(websValid(wp));
if (wp->timeout >= 0) {
emfUnschedCallback(wp->timeout);
wp->timeout = -1;
}
}
/******************************************************************************/
/*
* Output a HTTP response back to the browser. If redirect is set to a
* URL, the browser will be sent to this location.
*/
void websResponse(webs_t wp, int code, char_t *message, char_t *redirect)
{
char_t *date;
a_assert(websValid(wp));
/*
* IE3.0 needs no Keep Alive for some return codes.
*/
wp->flags &= ~WEBS_KEEP_ALIVE;
/*
* Only output the header if a header has not already been output.
*/
if ( !(wp->flags & WEBS_HEADER_DONE)) {
wp->flags |= WEBS_HEADER_DONE;
/*
* Redirect behaves much better when sent with HTTP/1.0
*/
if (redirect != NULL) {
websWrite(wp, T("HTTP/1.0 %d %s\r\n"), code, websErrorMsg(code));
} else {
websWrite(wp, T("HTTP/1.1 %d %s\r\n"), code, websErrorMsg(code));
}
/*
* By license terms the following line of code must not be modified.
*/
websWrite(wp, T("Server: %s\r\n"), WEBS_NAME);
/*
* Timestamp/Date is usually the next to go
*/
if ((date = websGetDateString(NULL)) != NULL) {
websWrite(wp, T("Date: %s\r\n"), date);
bfree(B_L, date);
}
/*
* If authentication is required, send the auth header info
*/
if (code == 401) {
if (!(wp->flags & WEBS_AUTH_DIGEST)) {
websWrite(wp, T("WWW-Authenticate: Basic realm=\"%s\"\r\n"),
websGetRealm());
#ifdef DIGEST_ACCESS_SUPPORT
} else {
char_t *nonce, *opaque;
/* $$$ before... (note commas instead of semicolons...)
nonce = websCalcNonce(wp),
opaque = websCalcOpaque(wp),
$$$ after */
nonce = websCalcNonce(wp);
opaque = websCalcOpaque(wp);
/* ...$$$ end */
websWrite(wp,
T("WWW-Authenticate: Digest realm=\"%s\", domain=\"%s\",")
T("qop=\"%s\", nonce=\"%s\", opaque=\"%s\",")
T("algorithm=\"%s\", stale=\"%s\"\r\n"),
websGetRealm(),
websGetHostUrl(),
T("auth"),
nonce,
opaque, T("MD5"), T("FALSE"));
bfree(B_L, nonce);
bfree(B_L, opaque);
#endif
}
}
if (wp->flags & WEBS_KEEP_ALIVE) {
websWrite(wp, T("Connection: keep-alive\r\n"));
}
websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n"));
websWrite(wp, T("Content-Type: text/html\r\n"));
/*
* We don't do a string length here as the message may be multi-line.
* Ie. <CR><LF> will count as only one and we will have a content-length
* that is too short.
*
* websWrite(wp, T("Content-Length: %s\r\n"), message);
*/
if (redirect) {
websWrite(wp, T("Location: %s\r\n"), redirect);
}
websWrite(wp, T("\r\n"));
}
/*
* If the browser didn't do a HEAD only request, send the message as well.
*/
if ((wp->flags & WEBS_HEAD_REQUEST) == 0 && message && *message) {
websWrite(wp, T("%s\r\n"), message);
}
websDone(wp, code);
}
/******************************************************************************/
/*
* Redirect the user to another webs page
*/
void websRedirect(webs_t wp, char_t *url)
{
char_t *msgbuf, *urlbuf, *redirectFmt;
a_assert(websValid(wp));
a_assert(url);
websStats.redirects++;
msgbuf = urlbuf = NULL;
/*
* Some browsers require a http://host qualified URL for redirection
*/
if (gstrstr(url, T("http://")) == NULL) {
if (*url == '/') {
url++;
}
redirectFmt = T("http://%s/%s");
#ifdef WEBS_SSL_SUPPORT
if (wp->flags & WEBS_SECURE) {
redirectFmt = T("https://%s/%s");
}
#endif
fmtAlloc(&urlbuf, WEBS_MAX_URL + 80, redirectFmt,
websGetVar(wp, T("HTTP_HOST"), websHostUrl), url);
url = urlbuf;
}
/*
* Add human readable message for completeness. Should not be required.
*/
fmtAlloc(&msgbuf, WEBS_MAX_URL + 80,
T("<html><head></head><body>\r\n\
This document has moved to a new <a href=\"%s\">location</a>.\r\n\
Please update your documents to reflect the new location.\r\n\
</body></html>\r\n"), url);
websResponse(wp, 302, msgbuf, url);
bfreeSafe(B_L, msgbuf);
bfreeSafe(B_L, urlbuf);
}
/******************************************************************************/
/*
* Output an error message and cleanup
*/
#ifdef qRichErrorPage
extern int dmfRichError(webs_t wp, int code, char_t* userMsg);
#endif
void websError(webs_t wp, int code, char_t *fmt, ...)
{
va_list args;
char_t *msg, *userMsg, *buf;
#ifdef qRichErrorPage
static int reEntry = 0;
int errorOk;
#endif
a_assert(websValid(wp));
a_assert(fmt);
websStats.errors++;
va_start(args, fmt);
userMsg = NULL;
fmtValloc(&userMsg, WEBS_BUFSIZE, fmt, args);
va_end(args);
#ifdef qRichErrorPage
if (!reEntry)
{
/*
* The dmfRichError function that we're about to call may very well call
* websError() as part of its work. If that happens, we do NOT want to
* get into a never-ending recursive call chain. When we get back here
* in a call from inside dmfRichError(), we check to see if we're
* already trying to call dmfRichError. If we are, we just revert to the
* old non-rich behavior and display a black on white error page.
*/
reEntry = 1;
errorOk = dmfRichError(wp, code, userMsg);
reEntry = 0;
if (errorOk)
{
return;
}
/* ...else we need to fall through and execute the simple error page. */
}
/* implicit else... */
#endif
msg = T("<html><head><title>Document Error: %s</title></head>\r\n\
<body><h2>Access Error: %s</h2>\r\n\
when trying to obtain <b>%s</b><br><p>%s</p></body></html>\r\n");
/*
* Ensure we have plenty of room
*/
buf = NULL;
fmtAlloc(&buf, WEBS_BUFSIZE, msg, websErrorMsg(code),
websErrorMsg(code), wp->url, userMsg);
websResponse(wp, code, buf, NULL);
bfreeSafe(B_L, buf);
bfreeSafe(B_L, userMsg);
}
/******************************************************************************/
/*
* Return the error message for a given code
*/
/*static char_t *websErrorMsg(int code)*/
char_t *websErrorMsg(int code)
{
websErrorType *ep;
for (ep = websErrors; ep->code; ep++) {
if (code == ep->code) {
return ep->msg;
}
}
a_assert(0);
return T("");
}
/******************************************************************************/
/*
* Do formatted output to the browser. This is the public ASP and form
* write procedure.
*/
int websWrite(webs_t wp, char_t *fmt, ...)
{
va_list vargs;
char_t *buf;
int rc;
a_assert(websValid(wp));
va_start(vargs, fmt);
buf = NULL;
rc = 0;
if (fmtValloc(&buf, WEBS_BUFSIZE, fmt, vargs) >= WEBS_BUFSIZE) {
trace(0, T("webs: websWrite lost data, buffer overflow\n"));
}
va_end(vargs);
a_assert(buf);
if (buf) {
rc = websWriteBlock(wp, buf, gstrlen(buf));
bfree(B_L, buf);
}
return rc;
}
/******************************************************************************/
/*
* Write a block of data of length "nChars" to the user's browser. Public
* write block procedure. If unicode is turned on this function expects
* buf to be a unicode string and it converts it to ASCII before writing.
* See websWriteDataNonBlock to always write binary or ASCII data with no
* unicode conversion. This returns the number of char_t's processed.
* It spins until nChars are flushed to the socket. For non-blocking
* behavior, use websWriteDataNonBlock.
*/
int websWriteBlock(webs_t wp, char_t *buf, int nChars)
{
int len, done;
char *asciiBuf, *pBuf;
a_assert(wp);
a_assert(websValid(wp));
a_assert(buf);
a_assert(nChars >= 0);
done = len = 0;
/*
* ballocUniToAsc will convert Unicode to strings to Ascii. If Unicode is
* not turned on then ballocUniToAsc will not do the conversion.
*/
pBuf = asciiBuf = ballocUniToAsc(buf, nChars);
while (nChars > 0) {
#ifdef WEBS_SSL_SUPPORT
if (wp->flags & WEBS_SECURE) {
if ((len = websSSLWrite(wp->wsp, pBuf, nChars)) < 0) {
bfree(B_L, asciiBuf);
return -1;
}
websSSLFlush(wp->wsp);
} else {
if ((len = socketWrite(wp->sid, pBuf, nChars)) < 0) {
bfree(B_L, asciiBuf);
return -1;
}
socketFlush(wp->sid);
}
#else /* ! WEBS_SSL_SUPPORT */
if ((len = socketWrite(wp->sid, pBuf, nChars)) < 0) {
bfree(B_L, asciiBuf);
return -1;
}
socketFlush(wp->sid);
#endif /* WEBS_SSL_SUPPORT */
nChars -= len;
pBuf += len;
done += len;
}
bfree(B_L, asciiBuf);
return done;
}
/******************************************************************************/
/*
* Write a block of data of length "nChars" to the user's browser. Same as
* websWriteBlock except that it expects straight ASCII or binary and does no
* unicode conversion before writing the data. If the socket cannot hold all
* the data, it will return the number of bytes flushed to the socket before
* it would have blocked. This returns the number of chars processed or -1
* if socketWrite fails.
*/
int websWriteDataNonBlock(webs_t wp, char *buf, int nChars)
{
int r;
a_assert(wp);
a_assert(websValid(wp));
a_assert(buf);
a_assert(nChars >= 0);
#ifdef WEBS_SSL_SUPPORT
if (wp->flags & WEBS_SECURE) {
r = websSSLWrite(wp->wsp, buf, nChars);
websSSLFlush(wp->wsp);
} else {
r = socketWrite(wp->sid, buf, nChars);
socketFlush(wp->sid);
}
#else
r = socketWrite(wp->sid, buf, nChars);
socketFlush(wp->sid);
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -