Fixed bug #844809.
/*
* The contents of this file are subject to the AOLserver Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://aolserver.com/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is AOLserver Code and related documentation
* distributed by AOL.
*
* The Initial Developer of the Original Code is America Online,
* Inc. Portions created by AOL are Copyright (C) 1999 America Online,
* Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU General Public License (the "GPL"), in which case the
* provisions of GPL are applicable instead of those above. If you wish
* to allow use of your version of this file only under the terms of the
* GPL and not to allow others to use your version of this file under the
* License, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the GPL.
* If you do not delete the provisions above, a recipient may use your
* version of this file under either the License or the GPL.
*/
/*
* urlopen.c --
*
* Make outgoing HTTP requests.
*/
static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsd/urlopen.c,v 1.17 2003/12/28 00:22:06 scottg Exp $, compiled: " __DATE__ " " __TIME__;
#include "nsd.h"
#define BUFSIZE 2048
typedef struct Stream {
SOCKET sock;
int error;
int cnt;
char *ptr;
char buf[BUFSIZE+1];
} Stream;
/*
* Local functions defined in this file
*/
static int GetLine(Stream *sPtr, Ns_DString *dsPtr);
static int FillBuf(Stream *sPtr);
/*
*----------------------------------------------------------------------
*
* Ns_FetchPage --
*
* Fetch a page off of this very server. Url must reference a
* file in the filesystem.
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* The file contents will be put into the passed-in dstring.
*
*----------------------------------------------------------------------
*/
int
Ns_FetchPage(Ns_DString *dsPtr, char *url, char *server)
{
Ns_DString path;
int fd;
int nread;
char buf[1024];
Ns_DStringInit(&path);
Ns_UrlToFile(&path, server, url);
fd = open(path.string, O_RDONLY|O_BINARY);
Ns_DStringFree(&path);
if (fd < 0) {
return NS_ERROR;
}
while ((nread = read(fd, buf, sizeof(buf))) > 0) {
Ns_DStringNAppend(dsPtr, buf, nread);
}
close(fd);
return NS_OK;
}
/*
*----------------------------------------------------------------------
*
* Ns_FetchURL --
*
* Open up an HTTP connection to an arbitrary URL.
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* Page contents will be appended to the passed-in dstring.
* Headers returned to us will be put into the passed-in Ns_Set.
* The set name will be changed to a copy of the HTTP status line.
*
*----------------------------------------------------------------------
*/
int
Ns_FetchURL(Ns_DString *dsPtr, char *url, Ns_Set *headers)
{
SOCKET sock;
char *p;
Ns_DString ds;
Stream stream;
Ns_Request *request;
int status, tosend, n;
status = NS_ERROR;
sock = INVALID_SOCKET;
Ns_DStringInit(&ds);
/*
* Parse the URL and open a connection.
*/
Ns_DStringVarAppend(&ds, "GET ", url, " HTTP/1.0", NULL);
request = Ns_ParseRequest(ds.string);
if (request == NULL || request->protocol == NULL ||
!STREQ(request->protocol, "http") || request->host == NULL) {
Ns_Log(Notice, "urlopen: invalid url '%s'", url);
goto done;
}
if (request->port == 0) {
request->port = 80;
}
sock = Ns_SockConnect(request->host, request->port);
if (sock == INVALID_SOCKET) {
Ns_Log(Error, "urlopen: failed to connect to '%s': '%s'",
url, ns_sockstrerror(ns_sockerrno));
goto done;
}
/*
* Send a simple HTTP GET request.
*/
Ns_DStringTrunc(&ds, 0);
Ns_DStringVarAppend(&ds, "GET ", request->url, NULL);
if (request->query != NULL) {
Ns_DStringVarAppend(&ds, "?", request->query, NULL);
}
Ns_DStringAppend(&ds, " HTTP/1.0\r\nAccept: */*\r\n\r\n");
p = ds.string;
tosend = ds.length;
while (tosend > 0) {
n = send(sock, p, tosend, 0);
if (n == SOCKET_ERROR) {
Ns_Log(Error, "urlopen: failed to send data to '%s': '%s'",
url, ns_sockstrerror(ns_sockerrno));
goto done;
}
tosend -= n;
p += n;
}
/*
* Buffer the socket and read the response line and then
* consume the headers, parsing them into any given header set.
*/
stream.cnt = 0;
stream.error = 0;
stream.ptr = stream.buf;
stream.sock = sock;
if (!GetLine(&stream, &ds)) {
goto done;
}
if (headers != NULL && strncmp(ds.string, "HTTP", 4) == 0) {
if (headers->name != NULL) {
ns_free(headers->name);
}
headers->name = Ns_DStringExport(&ds);
}
do {
if (!GetLine(&stream, &ds)) {
goto done;
}
if (ds.length > 0
&& headers != NULL
&& Ns_ParseHeader(headers, ds.string, Preserve) != NS_OK) {
goto done;
}
} while (ds.length > 0);
/*
* Without any check on limit or total size, foolishly read
* the remaining content into the dstring.
*/
do {
Ns_DStringNAppend(dsPtr, stream.ptr, stream.cnt);
} while (FillBuf(&stream));
if (!stream.error) {
status = NS_OK;
}
done:
if (request != NULL) {
Ns_FreeRequest(request);
}
if (sock != INVALID_SOCKET) {
ns_sockclose(sock);
}
Ns_DStringFree(&ds);
return status;
}
/*
*----------------------------------------------------------------------
*
* NsTclGetUrlObjCmd --
*
* Implements ns_geturl.
*
* Results:
* Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclGetUrlObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
NsInterp *itPtr = arg;
Ns_DString ds;
Ns_Set *headers;
int status, code;
char *url;
if ((objc != 3) && (objc != 2)) {
Tcl_WrongNumArgs(interp, 1, objv, "url ?headersSetIdVar?");
return TCL_ERROR;
}
code = TCL_ERROR;
if (objc == 2) {
headers = NULL;
} else {
headers = Ns_SetCreate(NULL);
}
Ns_DStringInit(&ds);
url = Tcl_GetString(objv[1]);
if (url[0] == '/') {
status = Ns_FetchPage(&ds, Tcl_GetString(objv[1]), itPtr->servPtr->server);
} else {
status = Ns_FetchURL(&ds, Tcl_GetString(objv[1]), headers);
}
if (status != NS_OK) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "could not fetch: ",
Tcl_GetString(objv[1]), NULL);
if (headers != NULL) {
Ns_SetFree(headers);
}
goto done;
}
if (objc == 3) {
Ns_TclEnterSet(interp, headers, NS_TCL_SET_DYNAMIC);
if (Tcl_ObjSetVar2(interp, objv[2], NULL, Tcl_GetObjResult(interp),
TCL_LEAVE_ERR_MSG) == NULL) {
goto done;
}
}
Tcl_SetResult(interp, ds.string, TCL_VOLATILE);
code = TCL_OK;
done:
Ns_DStringFree(&ds);
return code;
}
/*
*----------------------------------------------------------------------
*
* FillBuf --
*
* Fill the socket stream buffer.
*
* Results:
* 1 if fill ok, 0 otherwise.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
FillBuf(Stream *sPtr)
{
int n;
n = recv(sPtr->sock, sPtr->buf, BUFSIZE, 0);
if (n <= 0) {
if (n < 0) {
Ns_Log(Error, "urlopen: "
"failed to fill socket stream buffer: '%s'", strerror(errno));
sPtr->error = 1;
}
return 0;
}
sPtr->buf[n] = '\0';
sPtr->ptr = sPtr->buf;
sPtr->cnt = n;
return 1;
}
/*
*----------------------------------------------------------------------
*
* GetLine --
*
* Copy the next line from the stream to a dstring, trimming
* the \n and \r.
*
* Results:
* 1 or 0.
*
* Side effects:
* The dstring is truncated on entry.
*
*----------------------------------------------------------------------
*/
static int
GetLine(Stream *sPtr, Ns_DString *dsPtr)
{
char *eol;
int n;
Ns_DStringTrunc(dsPtr, 0);
do {
if (sPtr->cnt > 0) {
eol = strchr(sPtr->ptr, '\n');
if (eol == NULL) {
n = sPtr->cnt;
} else {
*eol++ = '\0';
n = eol - sPtr->ptr;
}
Ns_DStringNAppend(dsPtr, sPtr->ptr, n - 1);
sPtr->ptr += n;
sPtr->cnt -= n;
if (eol != NULL) {
n = dsPtr->length;
if (n > 0 && dsPtr->string[n-1] == '\r') {
Ns_DStringTrunc(dsPtr, n-1);
}
return 1;
}
}
} while (FillBuf(sPtr));
return 0;
}
|
Back to SourceForge.net Powered by ViewCVS 1.0-dev |