Bug 353430: nsHTTPConn::Request never frees usrPsd, patch by Ryan Jones <sciguyryan+bugzilla@gmail.com>, r+sr=dveditz

This commit is contained in:
gavin%gavinsharp.com 2006-10-10 14:04:16 +00:00
Родитель ac0b198889
Коммит 3ac6ccbe76
1 изменённых файлов: 0 добавлений и 765 удалений

Просмотреть файл

@ -1,765 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla 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://www.mozilla.org/MPL/
*
* 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 Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Samir Gehani <sgehani@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/stat.h>
#include "nsHTTPConn.h"
#include "nsSocket.h"
const char kHTTPProto[8] = "http://";
const char kFTPProto[8] = "ftp://";
const int kHTTPPort = 80;
const int kFTPPort = 21;
const int kRespBufSize = 1024;
const int kReqBufSize = 4096;
const int kHdrBufSize = 4096;
const char kCRLF[3] = "\r\n";
const char kHdrBodyDelim[5] = "\r\n\r\n";
const char kDefaultDestFile[11] = "index.html";
nsHTTPConn::nsHTTPConn(char *aHost, int aPort, char *aPath, int (*aEventPumpCB)(void)):
mEventPumpCB(aEventPumpCB),
mHost(aHost),
mPath(aPath),
mProxiedURL(NULL),
mProxyUser(NULL),
mProxyPswd(NULL),
mDestFile(NULL),
mHostPathAllocd(FALSE),
mSocket(NULL)
{
if (aPort <= 0)
mPort = kHTTPPort;
else
mPort = aPort;
DUMP(("mHost = %s\n", mHost));
DUMP(("mPort = %d\n", mPort));
DUMP(("mPath = %s\n", mPath));
}
nsHTTPConn::nsHTTPConn(char *aHost, int aPort, char *aPath) :
mEventPumpCB(NULL),
mHost(aHost),
mPath(aPath),
mProxiedURL(NULL),
mProxyUser(NULL),
mProxyPswd(NULL),
mDestFile(NULL),
mHostPathAllocd(FALSE),
mSocket(NULL)
{
if (aPort <= 0)
mPort = kHTTPPort;
else
mPort = aPort;
DUMP(("mHost = %s\n", mHost));
DUMP(("mPort = %d\n", mPort));
DUMP(("mPath = %s\n", mPath));
}
nsHTTPConn::nsHTTPConn(char *aURL, int (*aEventPumpCB)(void)) :
mEventPumpCB(aEventPumpCB),
mPort(kHTTPPort),
mProxiedURL(NULL),
mProxyUser(NULL),
mProxyPswd(NULL),
mDestFile(NULL),
mHostPathAllocd(FALSE),
mSocket(NULL),
mResponseCode(0)
{
// parse URL
if (ParseURL(kHTTPProto, aURL, &mHost, &mPort, &mPath) == OK)
mHostPathAllocd = TRUE;
else
{
mHost = NULL;
mPath = NULL;
}
DUMP(("mHost = %s\n", mHost));
DUMP(("mPort = %d\n", mPort));
DUMP(("mPath = %s\n", mPath));
}
nsHTTPConn::nsHTTPConn(char *aURL) :
mEventPumpCB(NULL),
mPort(kHTTPPort),
mProxiedURL(NULL),
mProxyUser(NULL),
mProxyPswd(NULL),
mDestFile(NULL),
mHostPathAllocd(FALSE),
mSocket(NULL),
mResponseCode(0)
{
// parse URL
if (ParseURL(kHTTPProto, aURL, &mHost, &mPort, &mPath) == OK)
mHostPathAllocd = TRUE;
else
{
mHost = NULL;
mPath = NULL;
}
DUMP(("mHost = %s\n", mHost));
DUMP(("mPort = %d\n", mPort));
DUMP(("mPath = %s\n", mPath));
}
nsHTTPConn::~nsHTTPConn()
{
if (mHostPathAllocd)
{
if (mHost)
free(mHost);
if (mPath)
free(mPath);
}
}
int
nsHTTPConn::Open()
{
// verify host && path
if (!mHost || !mPath)
return E_MALFORMED_URL;
// create socket
mSocket = new nsSocket(mHost, mPort, mEventPumpCB);
if (!mSocket)
return E_MEM;
// open socket
return mSocket->Open();
}
int
nsHTTPConn::ResumeOrGet(HTTPGetCB aCallback, char *aDestFile)
{
struct stat stbuf;
int rv = 0;
int resPos = 0;
if (!aDestFile)
return E_PARAM;
/* stat local file */
rv = stat(aDestFile, &stbuf);
if (rv == 0)
resPos = stbuf.st_size;
return Get(aCallback, aDestFile, resPos);
// XXX TO DO:
// XXX handle proxies
}
int
nsHTTPConn::Get(HTTPGetCB aCallback, char *aDestFile)
{
// deprecated API; wrapper for backwards compatibility
return ResumeOrGet(aCallback, aDestFile);
}
int
nsHTTPConn::Get(HTTPGetCB aCallback, char *aDestFile, int aResumePos)
{
int rv;
char *pathToUse;
// verify host && path
if (!mHost || !mPath)
return E_MALFORMED_URL;
if (!aDestFile)
{
if (mProxiedURL)
pathToUse = mProxiedURL;
else
pathToUse = mPath;
// no leaf: assume default file 'index.html'
if (*(pathToUse + strlen(pathToUse) - 1) == '/')
aDestFile = (char *) kDefaultDestFile;
else
aDestFile = strrchr(pathToUse, '/') + 1;
}
// issue request
rv = Request(aResumePos);
// recv response
if (rv == OK)
rv = Response(aCallback, aDestFile, aResumePos);
return rv;
}
int
nsHTTPConn::Close()
{
int rv;
// close socket
rv = mSocket->Close();
// destroy socket
delete mSocket;
return rv;
}
void
nsHTTPConn::SetProxyInfo(char *aProxiedURL, char *aProxyUser,
char *aProxyPswd)
{
mProxiedURL = aProxiedURL;
mProxyUser = aProxyUser;
mProxyPswd = aProxyPswd;
}
int
nsHTTPConn::Request(int aResumePos)
{
char req[kReqBufSize];
char hdr[kHdrBufSize];
int rv;
memset(req, 0, kReqBufSize);
// format header buf:
// request line
memset(hdr, 0, kHdrBufSize);
if (mProxiedURL)
{
char *host = NULL, *path = NULL;
char proto[8];
int port;
#ifdef DEBUG
assert(sizeof hdr > (strlen(mProxiedURL) + 15 ));
#endif
sprintf(hdr, "GET %s HTTP/1.0%s", mProxiedURL, kCRLF);
strcpy(req, hdr);
if (strncmp(mProxiedURL, kFTPProto, strlen(kFTPProto)) == 0)
{
strcpy(proto,kFTPProto);
port = kFTPPort;
}
else
{
strcpy(proto,kHTTPProto);
port = kHTTPPort;
}
rv = ParseURL(proto, mProxiedURL,
&host, &port, &path);
if (rv == OK) {
memset(hdr, 0, kHdrBufSize);
sprintf(hdr, "Host: %s:%d%s", host, port, kCRLF);
strcat(req, hdr);
}
if (host)
free(host);
if (path)
free(path);
}
else
{
sprintf(hdr, "GET %s HTTP/1.0%s", mPath, kCRLF);
strcpy(req, hdr);
memset(hdr, 0, kHdrBufSize);
sprintf(hdr, "Host: %s%s", mHost, kCRLF);
strcat(req, hdr);
}
// if proxy set and proxy user/pswd set
if (mProxyUser && mProxyPswd)
{
char *usrPsd = (char *) malloc(strlen(mProxyUser) +
strlen(":") +
strlen(mProxyPswd) + 1);
if (!usrPsd)
return E_MEM;
sprintf(usrPsd, "%s:%s", mProxyUser, mProxyPswd);
// base 64 encode proxy header
char usrPsdEncoded[128]; // pray that 128 is long enough
memset(usrPsdEncoded, 0, 128);
DUMP(("Unencoded string: %s\n", usrPsd));
rv = Base64Encode((const unsigned char *)usrPsd, strlen(usrPsd),
usrPsdEncoded, 128);
DUMP(("Encoded string: %s\n", usrPsdEncoded));
DUMP(("Base64Encode returned: %d\n", rv));
if (rv <= 0)
{
return E_B64_ENCODE;
}
// append proxy header to header buf
memset(hdr, 0, kHdrBufSize);
sprintf(hdr, "Proxy-authorization: Basic %s%s", usrPsdEncoded, kCRLF);
strcat(req, hdr);
// XXX append host with port 21 if ftp
}
// byte range support
if (aResumePos > 0)
{
sprintf(hdr, "Range: bytes=%d-%s", aResumePos, kCRLF);
strcat(req, hdr);
}
// headers all done so indicate
strcat(req, kCRLF);
// send header buf over socket
int bufSize = strlen(req);
rv = mSocket->Send((unsigned char *) req, &bufSize);
DUMP(("\n\n%s", req));
if (bufSize != (int) strlen(req))
rv = E_REQ_INCOMPLETE;
return rv;
}
int
nsHTTPConn::Response(HTTPGetCB aCallback, char *aDestFile, int aResumePos)
{
// NOTE: overwrites dest file if it already exists
int rv = OK;
char resp[kRespBufSize];
int bufSize, total = 0, fwriteLen, fwrote, bytesWritten = 0, expectedSize = 0;
FILE *destFd;
char *fwritePos;
int bFirstIter = TRUE;
if (!aDestFile)
return E_PARAM;
// open dest file
if (aResumePos > 0)
{
destFd = fopen(aDestFile, "r+b");
if (!destFd)
return E_OPEN_FILE;
if (fseek(destFd, aResumePos, SEEK_SET) != 0)
{
fclose(destFd);
return E_SEEK_FILE;
}
}
else
{
destFd = fopen(aDestFile, "w+b");
if (!destFd)
return E_OPEN_FILE;
}
// iteratively recv response
do
{
memset(resp, 0, kRespBufSize);
bufSize = kRespBufSize;
rv = mSocket->Recv((unsigned char *) resp, &bufSize);
DUMP(("nsSocket::Recv returned: %d\t and recd: %d\n", rv, bufSize));
if(rv == nsSocket::E_EOF_FOUND || (rv != nsSocket::E_READ_MORE && rv != nsSocket::OK) ) {
break;
}
if (bFirstIter)
{
fwritePos = strstr(resp, kHdrBodyDelim);
if (fwritePos == NULL)
{
// XXX no header! should we handle?
fwritePos = resp;
fwriteLen = bufSize;
}
else
{
ParseResponseCode((const char *)resp, &mResponseCode);
if ( mResponseCode < 200 || mResponseCode >=300 )
{
// if we don't get a response code in the 200 range then fail
// TODO: handle the response codes in the 300 range
rv = nsHTTPConn::E_HTTP_RESPONSE;
break;
}
ParseContentLength((const char *)resp, &expectedSize);
// move past hdr-body delimiter
fwritePos += strlen(kHdrBodyDelim);
fwriteLen = bufSize - (fwritePos - resp);
total = expectedSize + aResumePos;
}
bFirstIter = FALSE;
}
else
{
fwritePos = resp;
fwriteLen = bufSize;
}
fwrote = fwrite(fwritePos, sizeof(char), fwriteLen, destFd);
assert(fwrote == fwriteLen);
if (fwriteLen > 0)
bytesWritten += fwriteLen;
if (aCallback &&
(aCallback(bytesWritten, total) == E_USER_CANCEL))
rv = E_USER_CANCEL; // we want to ignore all errors returned
// from aCallback() except E_USER_CANCEL
if ( mEventPumpCB )
mEventPumpCB();
} while ( rv == nsSocket::E_READ_MORE || rv == nsSocket::OK);
if ( bytesWritten == expectedSize && rv != nsHTTPConn::E_HTTP_RESPONSE)
rv = nsSocket::E_EOF_FOUND;
if (rv == nsSocket::E_EOF_FOUND)
{
DUMP(("EOF detected\n"));
rv = OK;
}
fclose(destFd);
return rv;
}
int
nsHTTPConn::ParseURL(const char *aProto, char *aURL, char **aHost,
int *aPort, char **aPath)
{
if (!aURL || !aHost || !aPort || !aPath || !aProto)
return E_PARAM;
char *pos, *nextSlash, *nextColon, *end, *hostEnd;
int protoLen = strlen(aProto);
if ((strncmp(aURL, aProto, protoLen) != 0) ||
(strlen(aURL) < 9))
return E_MALFORMED_URL;
pos = aURL + protoLen;
nextColon = strchr(pos, ':');
nextSlash = strchr(pos, '/');
// optional port specification
if (nextColon && ((nextSlash && nextColon < nextSlash) ||
!nextSlash))
{
int portStrLen;
if (nextSlash)
portStrLen = nextSlash - nextColon;
else
portStrLen = strlen(nextColon);
char *portStr = (char *) malloc(portStrLen + 1);
if (!portStr)
return E_MEM;
memset(portStr, 0, portStrLen + 1);
strncpy(portStr, nextColon+1, portStrLen);
*aPort = atoi(portStr);
free(portStr);
}
if ( (!nextColon || (nextSlash && (nextColon > nextSlash)))
&& *aPort <= 0) // don't override port if already set
*aPort = -1;
// only host in URL, assume '/' for path
if (!nextSlash)
{
int copyLen;
if (nextColon)
copyLen = nextColon - pos;
else
copyLen = strlen(pos);
*aHost = (char *) malloc(copyLen + 1); // to NULL terminate
if (!aHost)
return E_MEM;
memset(*aHost, 0, copyLen + 1);
strncpy(*aHost, pos, copyLen);
*aPath = (char *) malloc(2);
strcpy(*aPath, "/");
return OK;
}
// normal parsing: both host and path exist
if (nextColon)
hostEnd = nextColon;
else
hostEnd = nextSlash;
*aHost = (char *) malloc(hostEnd - pos + 1); // to NULL terminate
if (!*aHost)
return E_MEM;
memset(*aHost, 0, hostEnd - pos + 1);
strncpy(*aHost, pos, hostEnd - pos);
*(*aHost + (hostEnd - pos)) = 0; // NULL terminate
pos = nextSlash;
end = aURL + strlen(aURL);
*aPath = (char *) malloc(end - pos + 1);
if (!*aPath)
{
if (*aHost)
free(*aHost);
return E_MEM;
}
memset(*aPath, 0, end - pos + 1);
strncpy(*aPath, pos, end - pos);
return OK;
}
void
nsHTTPConn::ParseResponseCode(const char *aBuf, int *aCode)
{
char codeStr[4];
const char *pos;
if (!aBuf || !aCode)
return;
// make sure the beginning of the buffer is the HTTP status code
if (strncmp(aBuf,"HTTP/",5) == 0)
{
pos = strstr(aBuf," "); // find the space before the code
++pos; // move to the beginning of the code
strncpy((char *)codeStr,pos, 3);
codeStr[3] = '\0';
*aCode = atoi(codeStr);
}
}
void
nsHTTPConn::ParseContentLength(const char *aBuf, int *aLength)
{
const char *clHdr; // Content-length header line start
const char *eol, *pos;
char clNameStr1[16] = "Content-length:";
char clNameStr2[16] = "Content-Length:";
char *clNameStr = clNameStr1;
if (!aBuf || !aLength)
return; // non fatal so no error codes returned
*aLength = 0;
// XXX strcasestr() needs to be ported for Solaris (and Win32 and Mac?)
clHdr = strstr(aBuf, (char *)clNameStr1);
if (!clHdr)
{
clHdr = strstr(aBuf, (char *)clNameStr2);
clNameStr = clNameStr2;
}
if (clHdr)
{
eol = strstr(clHdr, kCRLF); // end of line
pos = clHdr + strlen(clNameStr);
while ((pos < eol) && (*pos == ' ' || *pos == '\t'))
pos++;
if (pos < eol)
{
int clValStrLen = eol - pos + 1; // extra byte to NULL terminate
char *clValStr = (char *) malloc(clValStrLen);
if (!clValStr)
return; // imminent doom!
memset(clValStr, 0, clValStrLen);
strncpy(clValStr, pos, eol - pos);
*aLength = atoi(clValStr);
}
}
}
int
nsHTTPConn::Base64Encode(const unsigned char *in_str, int in_len,
char *out_str, int out_len)
{
// NOTE: shamelessly copied from nsAbSyncPostEngine.cpp
static unsigned char base64[] =
{
/* 0 1 2 3 4 5 6 7 */
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', /* 0 */
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 1 */
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', /* 2 */
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', /* 3 */
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 4 */
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', /* 5 */
'w', 'x', 'y', 'z', '0', '1', '2', '3', /* 6 */
'4', '5', '6', '7', '8', '9', '+', '/' /* 7 */
};
int curr_out_len = 0;
int i = 0;
unsigned char a, b, c;
out_str[0] = '\0';
if (in_len > 0)
{
while (i < in_len)
{
a = in_str[i];
b = (i + 1 >= in_len) ? 0 : in_str[i + 1];
c = (i + 2 >= in_len) ? 0 : in_str[i + 2];
if (i + 2 < in_len)
{
out_str[curr_out_len++] = (base64[(a >> 2) & 0x3F]);
out_str[curr_out_len++] = (base64[((a << 4) & 0x30)
+ ((b >> 4) & 0xf)]);
out_str[curr_out_len++] = (base64[((b << 2) & 0x3c)
+ ((c >> 6) & 0x3)]);
out_str[curr_out_len++] = (base64[c & 0x3F]);
}
else if (i + 1 < in_len)
{
out_str[curr_out_len++] = (base64[(a >> 2) & 0x3F]);
out_str[curr_out_len++] = (base64[((a << 4) & 0x30)
+ ((b >> 4) & 0xf)]);
out_str[curr_out_len++] = (base64[((b << 2) & 0x3c)
+ ((c >> 6) & 0x3)]);
out_str[curr_out_len++] = '=';
}
else
{
out_str[curr_out_len++] = (base64[(a >> 2) & 0x3F]);
out_str[curr_out_len++] = (base64[((a << 4) & 0x30)
+ ((b >> 4) & 0xf)]);
out_str[curr_out_len++] = '=';
out_str[curr_out_len++] = '=';
}
i += 3;
if ((curr_out_len + 4) > out_len)
{
return(-1);
}
}
out_str[curr_out_len] = '\0';
}
return curr_out_len;
}
#ifdef TEST_NSHTTPCONN
int
TestHTTPCB(int aBytesRd, int aTotal)
{
DUMP(("Bytes rd: %d\tTotal: %d\n", aBytesRd, aTotal));
return 0;
}
int
main(int argc, char **argv)
{
nsHTTPConn *conn;
int rv = nsHTTPConn::OK;
char *proxiedURL = NULL;
char *proxyUser = NULL;
char *proxyPswd = NULL;
DUMP(("*** %s: A self-test for the nsHTTPConn class.\n", argv[0]));
if (argc < 2)
{
printf("usage: %s <http_url> [<proxied_url> [<proxy_user> ", argv[0]);
printf("<proxy_pswd>]]\n");
exit(1);
}
conn = new nsHTTPConn(argv[1]);
if (argc >= 3)
{
proxiedURL = argv[2];
}
if (argc >= 5)
{
proxyUser = argv[3];
proxyPswd = argv[4];
}
conn->SetProxyInfo(proxiedURL, proxyUser, proxyPswd);
rv = conn->Open();
DUMP(("nsHTTPConn::Open returned: %d\n", rv));
rv = conn->Get(TestHTTPCB, NULL); // NULL: local file name = URL leaf
DUMP(("nsHTTPConn::Get returned: %d\n", rv));
rv = conn->Close();
DUMP(("nsHTTPConn::Close returned: %d\n", rv));
return 0;
}
#endif /* TEST_NSHTTPCONN */