/*
* copied and amended from a simple http socket program:
* "Simple Internet client program - by Dan Drown <abob@linux.com>"
* available (last I looked) at:
* http://linux.omnipotent.net/article.php?article_id=5424
*/
#include "mhttp.h"
#ifdef DOHERROR
void herror(char *str){
fprintf(stderr, "herror: %s\n", str);
}
#endif
bool mhttp_lets_debug; /* global debugging flag */
bool mhttp_body_set_flag; /* global body set flag */
bool mhttp_host_hdr; /* I got a Host header */
int mhttp_protocol = 0;
bool mhttp_first_init = false;
int mhttp_hcnt,
mhttp_rcode,
mhttp_response_length;
char *mhttp_body,
*mhttp_response,
*mhttp_reason,
mhttp_resp_headers[MAX_HDR_STR],
*mhttp_headers[MAX_HEADERS],
*mhttp_buffers[MAX_BUFFERS];
mhttp_conn_t mhttp_connection = NULL;
mhttp_conn_t mhttp_last_connection = NULL;
int mhttp_call(char *paction, char *purl)
{
bool found_hdrs; /* found the end of headers flag */
bool rcode_flag; /* found return code flag */
bool newcon_flag; /* new connection flag */
bool chunked; /* transfer encoding is chunked */
char *action, *url, *host, *ptr, *eomsg, *clptr;
char *req;
char *surl;
char *cert_str;
int port,
returnval,
len,
curr_len,
buffer_size,
this_chunk,
i,
rem,
pos;
char str[MAX_STR];
newcon_flag = false;
if (!mhttp_first_init)
mhttp_init();
memset(mhttp_resp_headers, 0, MAX_HDR_STR);
mhttp_connection = mhttp_new_conn();
if ((returnval = check_action(paction, &action)) < 0)
return returnval;
if ((returnval = check_url(purl, &url, &host)) < 0)
return returnval;
if ((port = get_port_and_uri(purl, host, &surl)) < 0)
return port;
mhttp_connection->host = strdup(host);
mhttp_connection->port = port;
mhttp_debug("action: %s host: %s port: %d url: %s", action, host, port, url);
mhttp_debug("purl is: #%s#", purl);
if (strncmp(purl, "https", 5) == 0)
mhttp_connection->is_ssl = true;
mhttp_debug("This connection: %s / %d / %d",
mhttp_connection->host,
mhttp_connection->port,
mhttp_connection->is_ssl);
if (mhttp_protocol == 1 && ! mhttp_host_hdr){
mhttp_debug("This is HTTP/1.1 and we don't have a Host header");
return -19;
}
if (mhttp_last_connection != NULL){
mhttp_debug("OLD connection: %s / %d / %d",
mhttp_last_connection->host,
mhttp_last_connection->port,
mhttp_last_connection->is_ssl);
}
#ifdef GOTSSL
if (mhttp_last_connection != NULL &&
strcmp(mhttp_connection->host, mhttp_last_connection->host) == 0 &&
mhttp_last_connection->port == mhttp_connection->port &&
mhttp_last_connection->is_ssl == mhttp_connection->is_ssl){
mhttp_connection->fd = mhttp_last_connection->fd;
mhttp_connection->ctx = mhttp_last_connection->ctx;
mhttp_connection->ssl = mhttp_last_connection->ssl;
mhttp_connection->meth = mhttp_last_connection->meth;
mhttp_connection->server_cert = mhttp_last_connection->server_cert;
#else
if (mhttp_last_connection != NULL &&
strcmp(mhttp_connection->host, mhttp_last_connection->host) == 0 &&
mhttp_last_connection->port == mhttp_connection->port){
mhttp_connection->fd = mhttp_last_connection->fd;
#endif
mhttp_debug("using the cached connection");
} else {
if (mhttp_last_connection != NULL){
#ifdef GOTSSL
if (mhttp_last_connection->is_ssl)
SSL_shutdown (mhttp_last_connection->ssl); /* send SSL/TLS close_notify */
#endif
close(mhttp_last_connection->fd);
#ifdef GOTSSL
if (mhttp_last_connection->is_ssl){
mhttp_debug("shuting down the ssl engine");
SSL_free (mhttp_last_connection->ssl);
SSL_CTX_free (mhttp_last_connection->ctx);
}
#endif
mhttp_end_conn(mhttp_last_connection);
mhttp_last_connection = NULL;
mhttp_debug("Closed the last connection");
}
newcon_flag = true;
mhttp_connection->fd = -1;
mhttp_debug("didnt find connection - creating a new one: %s/%d - %d ", mhttp_connection->host, mhttp_connection->port, mhttp_connection->fd);
#ifdef GOTSSL
if (mhttp_connection->is_ssl){
mhttp_debug("creating an SSL connection");
//SSLeay_add_ssl_algorithms();
OpenSSL_add_ssl_algorithms();
//mhttp_connection->meth = SSLv2_client_method();
mhttp_connection->meth = SSLv3_client_method();
SSL_load_error_strings();
mhttp_connection->ctx = SSL_CTX_new (mhttp_connection->meth);
if (mhttp_connection->ctx == NULL){
mhttp_debug("SSL_CTX_new failed - abort everything");
return -11;
}
if( (mhttp_connection->fd = mhttp_connect_inet_addr(mhttp_connection->host, mhttp_connection->port)) < 0) {
mhttp_debug("could not create a new socket: %d", mhttp_connection->fd);
return mhttp_connection->fd;
}
// XXX set SSL here
/* ----------------------------------------------- */
/* Now we have TCP conncetion. Start SSL negotiation. */
mhttp_debug("SSL craeting the ctx");
mhttp_connection->ssl = SSL_new (mhttp_connection->ctx);
if (mhttp_connection->ssl == NULL){
mhttp_debug("SSL_new failed - abort everything");
return -12;
}
mhttp_debug("SSL set_verify");
SSL_CTX_set_default_verify_paths(mhttp_connection->ctx);
//SSL_CTX_load_verify_locations(mhttp_connection->ctx, "/etc/httpd/conf/ssl.crt/ca-bundle.crt",
// NULL);
SSL_CTX_set_verify(mhttp_connection->ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, mhttp_verify_callback);
//SSL_CTX_set_verify(mhttp_connection->ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, NULL);
mhttp_debug("SSL set_fd");
SSL_set_fd (mhttp_connection->ssl, mhttp_connection->fd);
returnval = SSL_connect (mhttp_connection->ssl);
if (returnval == -1){
mhttp_debug("SSL_connect failed - abort everything");
ERR_print_errors_fp(stderr);
return -13;
}
/* Get the cipher - opt */
mhttp_debug ("SSL connection using %s\n", SSL_get_cipher (mhttp_connection->ssl));
/* Get server's certificate (note: beware of dynamic allocation) - opt */
mhttp_connection->server_cert = SSL_get_peer_certificate (mhttp_connection->ssl);
if (mhttp_connection->server_cert == NULL){
mhttp_debug("SSL_get_peer_certificate failed - abort everything");
return -14;
}
cert_str = X509_NAME_oneline (X509_get_subject_name (mhttp_connection->server_cert),0,0);
if (cert_str == NULL){
mhttp_debug("X509_get_subject_name failed - abort everything");
return -15;
}
mhttp_debug ("Certificate subject: %s\n", cert_str);
free (cert_str);
cert_str = X509_NAME_oneline (X509_get_issuer_name (mhttp_connection->server_cert),0,0);
if (cert_str == NULL){
mhttp_debug("X509_get_issuer_name failed - abort everything");
return -16;
}
mhttp_debug ("Certificate issuer: %s\n", cert_str);
free (cert_str);
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
if (SSL_get_verify_result(mhttp_connection->ssl) == X509_V_OK){
/* The client sent a certificate which verified OK */
mhttp_debug("certificate OK");
} else {
mhttp_debug("Certificate error: %s", X509_verify_cert_error_string(SSL_get_verify_result(mhttp_connection->ssl)));
}
X509_free (mhttp_connection->server_cert);
} else {
if( (mhttp_connection->fd = mhttp_connect_inet_addr(mhttp_connection->host, mhttp_connection->port)) < 0) {
mhttp_debug("could not create a new socket: %d", mhttp_connection->fd);
return mhttp_connection->fd;
}
}
#else
if( (mhttp_connection->fd = mhttp_connect_inet_addr(mhttp_connection->host, mhttp_connection->port)) < 0) {
mhttp_debug("could not create a new socket: %d", mhttp_connection->fd);
return mhttp_connection->fd;
}
#endif
}
mhttp_debug("socket descriptor is: %d ", mhttp_connection->fd);
len = 0;
// construct the query string
memset(str, 0, sizeof(str));
if ((req = construct_request(action, surl)) == NULL)
return -2;
returnval = write_socket(mhttp_connection, req, strlen(req));
if(returnval < 0)
{
/* write returns -1 on error */
perror("write(query string) error");
return -5;
}
if(returnval < strlen(req))
{
/* I'm not dealing with this error, regular programs should. */
perror("the query string write was short\n");
return -6;
}
free(req);
free(surl);
free(url);
free(host);
// if this is a PUT or POST - write the body
if (mhttp_body_set_flag)
{
// XXX we probably have a problem where 1024 is exceeded and need to rewrite
mhttp_debug("this is a %s.... writing the body...", action);
mhttp_debug("writing data: %s", mhttp_body);
returnval = write_socket(mhttp_connection, mhttp_body, strlen(mhttp_body));
if(returnval < strlen(mhttp_body))
{
/* I'm not dealing with this error, regular programs should. */
fprintf(stderr, "the write of %s data was short\n", action);
return -6;
}
returnval = write_socket(mhttp_connection, "\r\n", 2);
if(returnval < 2)
{
/* I'm not dealing with this error, regular programs should. */
fprintf(stderr, "the write of %s data - last line failed\n", action);
return -7;
}
}
free(action);
/* read off the response and split out headers and content */
mhttp_debug("starting output:");
found_hdrs = false;
rcode_flag = false;
len = 0;
curr_len = 0;
chunked = false;
if ((len = read_headers(mhttp_connection, str)) < 0){
// we have no headers
mhttp_debug("we have no headers ");
exit(1);
}
if (mhttp_response_length > 0){
buffer_size = mhttp_response_length;
} else {
buffer_size = MAX_STR;
}
mhttp_debug("initial len is: %d", len);
while((returnval = read_socket(mhttp_connection, str)) > 0)
{
*(str+returnval) = '\0';
// we have the headers - this is the body
if (mhttp_response_length > 0){
// we dont know how big it should be so compensate
if (mhttp_connection->is_chunked){
if (len + returnval >= buffer_size - 2){
memcpy(mhttp_response+len, str, (buffer_size - 2) - len);
pos = (buffer_size - 2) - len;
len += pos;
mhttp_debug("len at end of chunk is: %d", len);
// find the next chunk
mhttp_debug("looking for the next chunk");
ptr = str+pos;
rem = returnval - pos;
if ((this_chunk = find_chunk(mhttp_connection, &ptr, &rem)) > 0){
// resize and paste on remainder
mhttp_debug("resize and paste on remainder for next chunk processing");
mhttp_response = realloc(mhttp_response, buffer_size + this_chunk);
buffer_size += this_chunk;
memcpy(mhttp_response+len, ptr, rem);
returnval = rem;
} else if (this_chunk == 0){
// no more to come
mhttp_debug("we found the final chunk");
break;
}
} else {
// not end of chunk yet
memcpy(mhttp_response+len, str, returnval);
}
} else {
// make sure that it does not overflow the buffer
if (mhttp_response_length >= (len + returnval)){
memcpy(mhttp_response+len, str, returnval);
}
}
} else {
// we dont know how big it should be so compensate
// else this is not a chunked read - so realloc when necessary
if (len + returnval > buffer_size){
mhttp_response = realloc(mhttp_response, buffer_size + MAX_STR);
buffer_size += MAX_STR;
}
memcpy(mhttp_response+len, str, returnval);
}
len += returnval;
// lets get out of here if we have read enough
if (mhttp_response_length > 0 && len >= mhttp_response_length )
break;
}
mhttp_debug("content length actually copied: %d (may include \\r\\n)", len);
mhttp_response_length = len;
/* it will be closed anyway when we exit */
if (mhttp_protocol == 0 ||
(clptr = strstr(mhttp_resp_headers, "Connection: close")) ||
(clptr = strstr(mhttp_resp_headers, "Connection: Close")) ){
#ifdef GOTSSL
if (mhttp_connection->is_ssl)
SSL_shutdown (mhttp_connection->ssl); /* send SSL/TLS close_notify */
#endif
/* Clean up. */
close(mhttp_connection->fd);
#ifdef GOTSSL
if (mhttp_connection->is_ssl){
mhttp_debug("shuting down the ssl engine");
SSL_free (mhttp_connection->ssl);
SSL_CTX_free (mhttp_connection->ctx);
}
#endif
if (mhttp_last_connection != NULL){
mhttp_end_conn(mhttp_last_connection);
mhttp_last_connection = NULL;
mhttp_debug("removed socket name");
}
mhttp_debug("Closed the connection");
} else {
if (newcon_flag){
if (mhttp_last_connection != NULL){
mhttp_end_conn(mhttp_last_connection);
mhttp_last_connection = NULL;
}
mhttp_last_connection = mhttp_new_conn();
mhttp_last_connection->host = strdup(mhttp_connection->host);
mhttp_last_connection->port = mhttp_connection->port;
mhttp_last_connection->fd = mhttp_connection->fd;
#ifdef GOTSSL
if (mhttp_connection->is_ssl){
mhttp_debug("saving SSL stuff");
mhttp_last_connection->is_ssl = mhttp_connection->is_ssl;
mhttp_last_connection->ssl = mhttp_connection->ssl;
mhttp_last_connection->ctx = mhttp_connection->ctx;
}
#endif
mhttp_debug("Caching the connection");
} else {
mhttp_debug("connection allready cached");
}
}
mhttp_end_conn(mhttp_connection);
mhttp_connection = NULL;
mhttp_debug("all done");
return 1;
}
int find_content_length(void){
char *ptr;
int rem = 0;
// determine the Content-Length header
if ((ptr = strstr(mhttp_resp_headers, "Content-Length:")) ||
(ptr = strstr(mhttp_resp_headers, "Content-length:"))){
mhttp_debug("found content-length");
ptr += 16;
mhttp_response_length = atoi(ptr);
mhttp_debug("content length: %d", mhttp_response_length);
mhttp_response = malloc(mhttp_response_length + 2);
return mhttp_response_length;
}
return 0;
}
bool find_transfer_encoding(void){
char *clptr;
int this_chunk;
/* look for Transfer-Encoding: chunked */
// clptr the chunk pointer
// ptr the beginning of the body
if ((clptr = strstr(mhttp_resp_headers, "Transfer-Encoding:")) ||
(clptr = strstr(mhttp_resp_headers, "Transfer-encoding:"))){
clptr += 19;
if (strncmp(clptr, "chunked",7) == 0){
mhttp_debug("found Transfer-Encoding: chunked");
return true;
}
}
return false;
}
int find_chunk(mhttp_conn_t conn, char **ptr, int *rem){
char *clptr;
char *myptr;
int myrem;
int diff;
int this_chunk;
int returnval;
/* look for Transfer-Encoding: chunked */
// clptr the chunk pointer
// ptr the beginning of the body
myptr = *ptr;
myrem = *rem;
// make sure that there is enuf of ptr left
mhttp_debug("remainder is: %d", myrem);
if (myrem <= 2 || !(clptr = strstr(myptr, "\r\n"))){
mhttp_debug("getting another line");
if ((returnval = read_socket(conn, myptr+myrem)) > 0){
myrem += returnval;
*(myptr+myrem) = '\0';
mhttp_debug("got another line: %d - #%s#", returnval, myptr);
} else {
mhttp_debug("cant get another line - aborting");
return -17;
}
}
// must find the end of the chunked length line
// in the remainding buffer
if (clptr = strstr(myptr, "\r\n")){
mhttp_debug("looking for chunk in: %s#", myptr);
if (sscanf(myptr, "%x\r\n", &this_chunk) != 1){
mhttp_debug("count not the chunked amount - something ify");
if ((returnval = read_socket(conn, myptr+myrem)) > 0){
myrem += returnval;
*(myptr+myrem) = '\0';
mhttp_debug("got another line: %d - #%s#", returnval, myptr);
if (strncmp(myptr, "\r\n", 2) == 0){
myptr+=2;
myrem-=2;
}
mhttp_debug("looking for chunk in: #%s#", myptr);
if (sscanf(myptr, "%x\r\n", &this_chunk) != 1){
mhttp_debug("count not the chunked amount - something broken");
return -17;
}
}
return -17;
}
// shift past the chunk size
*clptr = '\0';
myrem -= strlen(myptr) + 2;
clptr += 2;
mhttp_debug("Transfer-Encoding: chunked buffer is %d - %d bytes left: %s", this_chunk, myrem, clptr+myrem);
*ptr = clptr;
*rem = myrem;
return this_chunk;
}
return -17;
}
int read_headers(mhttp_conn_t conn, char *str){
int returnval;
int curr_len;
int rem;
int this_chunk;
char *ptr, *eomsg;
bool rcode_flag;
rcode_flag = false;
curr_len = 0;
while((returnval = read_socket(conn, str)) > 0)
{
*(str+returnval) = '\0';
mhttp_debug("Header line %d: %s", returnval, str);
if (strlen(mhttp_resp_headers) + returnval > MAX_HDR_STR){
mhttp_debug("have not found the headers within MAX_HDR_STR: %d", MAX_HDR_STR);
return -18;
}
/* detect the end of the headers */
sprintf(mhttp_resp_headers+strlen(mhttp_resp_headers), "%s", str);
/* find the return code */
if (!rcode_flag &&
strncmp(str, "HTTP/",5) == 0 &&
(strncmp(str+5, "0.9 ",4) == 0 ||
strncmp(str+5, "1.0 ",4) == 0 ||
strncmp(str+5, "1.1 ",4) == 0 ) ){
ptr = str+9;
*(ptr+3) = '\0';
mhttp_rcode = atoi(ptr);
rcode_flag = true;
ptr+=4;
/* find the status reason */
if ((eomsg = strstr(ptr, "\r\n")) || (eomsg = strstr(ptr, "\n"))){
*eomsg = '\0';
mhttp_reason = strdup(ptr);
}
mhttp_debug("detected return code: %d - %s", mhttp_rcode, mhttp_reason);
}
if ((ptr = strstr(mhttp_resp_headers, "\r\n\r\n")) ||
(ptr = strstr(mhttp_resp_headers, "\n\n"))){
*ptr = '\0';
mhttp_debug("found end of headers at: %d", strlen(mhttp_resp_headers));
mhttp_debug("headers are: %s", mhttp_resp_headers);
if (strncmp(ptr,"\0\n\r\n",4) == 0){
/* how far along the current buffer is the eoh marker */
curr_len = (strlen(mhttp_resp_headers) + 4) - curr_len;
ptr+=4;
} else {
curr_len = (strlen(mhttp_resp_headers) + 2) - curr_len;
ptr+=2;
}
/* tidy up the first bit of the body XXX */
mhttp_debug("returnval: %d - curr_len: %d", returnval, curr_len);
rem = returnval - curr_len;
mhttp_debug("the remainder is: %d", rem);
// find the Content-Length header
if ( find_content_length() > 0 ){
if (mhttp_response_length >= rem){
mhttp_debug("copying the initial part of the body: %s", ptr);
memcpy(mhttp_response, ptr, rem);
return rem;
} else {
// serious error - cant determine length properly
mhttp_debug("serious error - cant determine length properly");
return -8;
}
// or find the Tranfer-Enconding: chunked header
} else if (find_transfer_encoding()){
// find the chunk value
conn->is_chunked = true;
if ((this_chunk = find_chunk(conn, &ptr, &rem)) > 0){
mhttp_response = malloc(this_chunk + 2);
memcpy(mhttp_response, ptr, rem);
mhttp_response_length = this_chunk + 2;
return rem;
} else if (this_chunk == 0){
// an empty body
return 0;
} else {
// failed to find the next chunk value
mhttp_debug("cannot find \\r\\n after first chunked marker - time to give up");
return -17;
}
} else {
mhttp_debug("didnt find content-length - must use realloc: %d", rem);
mhttp_response_length = 0;
mhttp_response = malloc(MAX_STR);
memcpy(mhttp_response, ptr, rem);
return rem;
}
// or determine that it is HTTP/1.0
// or find the Connection: close
return curr_len;
}
curr_len += returnval;
}
/* must have hit an error */
return returnval;
}
int check_url(char *purl, char **url, char **host){
char *ptrhost;
if(strlen(purl) == 0)
{
mhttp_debug("must supply a url");
return -3;
}
if (strncmp(purl, "http://", 7) == 0){
ptrhost = purl+7;
#ifdef GOTSSL
} else if (strncmp(purl, "https://", 8) == 0){
ptrhost = purl+8;
mhttp_debug("setting the ssl flag");
mhttp_connection->is_ssl = true;
#endif
} else {
mhttp_debug("url must start with http:// - and yep we dont support https\n");
return -4;
}
*url = strdup(purl);
*host = strdup(ptrhost);
mhttp_debug("begin of host is: %s", *host);
return 0;
}
int get_port_and_uri(char *url, char *host, char **surl){
char *ptr;
int port;
// hunt for the beginning of the uri
mhttp_debug("begin looking for host at: %s", host);
*surl = malloc(MAX_STR);
ptr = strstr(host, "/");
if (ptr != NULL){
*ptr = '\0';
ptr++;
sprintf(*surl, "/%s", ptr);
} else {
sprintf(*surl, "/");
}
// hunt for the beginning of the port
ptr = strstr(host, ":");
if (ptr != NULL){
*ptr = '\0';
ptr++;
port = atoi(ptr);
} else {
#ifdef GOTSSL
if (strncmp(url ,"https", 5) == 0){
port = 443;
} else {
port = 80;
}
#else
port = 80;
#endif
}
return port;
}
#ifdef GOTSSL
static int mhttp_verify_callback(int ok, X509_STORE_CTX* ctx)
{
return 1;
}
#endif
mhttp_conn_t mhttp_new_conn(void){
mhttp_conn_t new_conn;
new_conn = (mhttp_conn_t) malloc(sizeof(struct mhttp_conn_st));
memset(new_conn, 0, sizeof(struct mhttp_conn_st));
new_conn->host = NULL;
new_conn->port = 0;
new_conn->is_ssl = false;
new_conn->is_chunked = false;
return new_conn;
}
void mhttp_end_conn(mhttp_conn_t conn){
mhttp_debug("resetting conn");
free(conn->host);
free(conn);
}
char *construct_request(char *action, char *url){
char *str;
int i;
str = malloc(MAX_HDR_STR);
strcpy(str, action);
strcpy(str+strlen(str), " ");
strcpy(str+strlen(str), url);
sprintf(str+strlen(str), " HTTP/1.%d\r\n", mhttp_protocol);
mhttp_debug("adding on the headers: %d", mhttp_hcnt);
for(i = 0; i < mhttp_hcnt; i++)
{
// make sure that we dont exceed the buffer
if ((strlen(str) + strlen(mhttp_headers[i])) > MAX_BUFFERS - 1)
break;
mhttp_debug("adding header: %s", mhttp_headers[i]);
sprintf(str+strlen(str), "%s\r\n", mhttp_headers[i]);
}
// if this is a post - add the Content-Length header
if (mhttp_body_set_flag)
{
sprintf(str+strlen(str),
"Content-Length: %d\r\n\r\n", strlen(mhttp_body));
} else {
strcpy(str+strlen(str), "\r\n\r\n");
}
mhttp_debug("query string + headers are: %s", str);
return str;
}
int check_action(char *paction, char **action){
if(strlen(paction) == 0)
{
mhttp_debug("must supply an action");
return -2;
}
if (strcmp(paction, "GET") != 0 &&
strcmp(paction, "POST") != 0 &&
strcmp(paction, "PUT") != 0 &&
strcmp(paction, "DELETE") != 0 &&
strcmp(paction, "HEAD") != 0)
{
mhttp_debug("must supply an action of GET, PUT, POST, DELETE, or HEAD");
return -1;
}
*action = strdup(paction);
mhttp_debug("The action is: %s", *action);
return 0;
}
void mhttp_switch_debug(int set)
{
if (!mhttp_first_init)
mhttp_init();
if (set > 0)
{
mhttp_lets_debug = true;
#ifdef GOTSSL
mhttp_debug("%s", "switched on debugging(SSL Support running)...");
#else
mhttp_debug("%s", "switched on debugging...");
#endif
} else {
mhttp_lets_debug = false;
}
}
void mhttp_set_protocol(int proto)
{
if (!mhttp_first_init)
mhttp_init();
mhttp_protocol = proto;
}
int mhttp_get_status_code(void)
{
return mhttp_rcode;
}
int mhttp_get_response_length(void)
{
return mhttp_response_length;
}
char *mhttp_get_reason(void)
{
if (mhttp_reason != NULL){
mhttp_debug("the reason is: %s", mhttp_reason);
return strdup(mhttp_reason);
} else {
return NULL;
}
}
char *mhttp_get_response(void)
{
return mhttp_response;
}
char *mhttp_get_response_headers(void)
{
return strdup(mhttp_resp_headers);
}
void mhttp_reset(void)
{
int i;
if (!mhttp_first_init)
mhttp_init();
if (mhttp_response != NULL){
free(mhttp_response);
mhttp_response = NULL;
mhttp_debug("reset the response");
}
mhttp_response_length = 0;
if (mhttp_reason != NULL){
free(mhttp_reason);
mhttp_reason = NULL;
mhttp_debug("reset the reason");
}
if (mhttp_body_set_flag)
free(mhttp_body);
mhttp_body_set_flag = false;
mhttp_rcode = 0;
mhttp_debug("finished reset");
}
void mhttp_init(void)
{
int i;
mhttp_first_init = true;
for (i = 0; i < mhttp_hcnt; i++)
{
free(mhttp_headers[i]);
mhttp_debug("freeing header");
mhttp_headers[i] = NULL;
}
mhttp_hcnt = 0;
mhttp_lets_debug = false;
mhttp_protocol = 0;
mhttp_host_hdr = false;
mhttp_reset();
mhttp_debug("finished init");
}
void mhttp_add_header(char *hdr)
{
if (!mhttp_first_init)
mhttp_init();
/* Do we have a Host Header? */
if (! mhttp_host_hdr && strncmp("Host:", hdr, 5) == 0)
mhttp_host_hdr = true;
mhttp_headers[mhttp_hcnt++] = strdup(hdr);
mhttp_debug("request header %s", mhttp_headers[mhttp_hcnt - 1]);
mhttp_headers[mhttp_hcnt] = NULL;
}
void mhttp_set_body(char *bdy)
{
// assumes body is a string XXX
if (!mhttp_first_init)
mhttp_init();
mhttp_body = strdup(bdy);
mhttp_debug("setting body: %s", mhttp_body);
mhttp_body_set_flag = true;
}
int read_socket(mhttp_conn_t conn, void *buf){
int returnval;
#ifdef GOTSSL
if (conn->is_ssl){
returnval = SSL_read (conn->ssl, buf, READ_BUF);
if (returnval == -1){
mhttp_debug("SSL_read failed - abort everything");
ERR_print_errors_fp(stderr);
return -16;
}
} else {
returnval = read(conn->fd, buf, READ_BUF);
}
#else
returnval = read(conn->fd, buf, READ_BUF);
#endif
return returnval;
}
int write_socket(mhttp_conn_t conn, const void *buf, size_t count){
int returnval;
#ifdef GOTSSL
if (conn->is_ssl){
//mhttp_debug("writing to ssl connection");
returnval = SSL_write (conn->ssl, buf, count);
if (returnval == -1){
mhttp_debug("SSL_write failed - abort everything");
ERR_print_errors_fp(stderr);
return -17;
}
} else {
returnval = write(conn->fd, buf, count);
}
#else
returnval = write(conn->fd, buf, count);
#endif
return returnval;
}
int mhttp_connect_inet_addr(const char *hostname, unsigned short int port)
{
int inet_socket; /* socket descriptor */
struct sockaddr_in inet_address; /* IP/port of the remote host to connect to */
if ( mhttp_build_inet_addr(&inet_address, hostname, port) < 0 )
return -1;
/* socket(domain, type, protocol) */
inet_socket = socket(PF_INET, SOCK_STREAM, 0);
mhttp_debug("socket no: %d", inet_socket);
/* domain is PF_INET(internet/IPv4 domain) *
* type is SOCK_STREAM(tcp) *
* protocol is 0(only one SOCK_STREAM type in the PF_INET domain
*/
if (inet_socket < 0)
{
/* socket returns -1 on error */
perror("socket(PF_INET, SOCK_STREAM, 0) error");
mhttp_debug("socket(PF_INET, SOCK_STREAM, 0) error");
return -2;
}
/* connect(sockfd, serv_addr, addrlen) */
if(connect(inet_socket, (struct sockaddr *)&inet_address, sizeof(struct sockaddr_in)) < 0)
{
/* connect returns -1 on error */
perror("connect(...) error");
mhttp_debug("connect(...) error");
return -3;
}
return inet_socket;
}
int mhttp_build_inet_addr(struct sockaddr_in *addr, const char *hostname, unsigned short int port)
{
struct hostent *host_entry;
/* gethostbyname(name) */
host_entry = gethostbyname(hostname);
if(host_entry == NULL)
{
/* gethostbyname returns NULL on error */
herror("gethostbyname failed");
mhttp_debug("gethostbyname failed");
return -1;
}
/* memcpy(dest, src, length) */
memcpy(&addr->sin_addr.s_addr, host_entry->h_addr_list[0], host_entry->h_length);
/* copy the address to the sockaddr_in struct. */
/* set the family type (PF_INET) */
addr->sin_family = host_entry->h_addrtype;
/* addr->sin_port = port won't work because they are different byte
* orders
*/
addr->sin_port = htons(port);
/* just to be pedantic... */
return 1;
}
/* debug logging */
void mhttp_debug(const char *msgfmt, ...)
{
va_list ap;
char *pos, message[MAX_STR];
int sz;
time_t t;
if (!mhttp_lets_debug)
return;
/* timestamp */
t = time(NULL);
pos = ctime(&t);
sz = strlen(pos);
/* chop off the \n */
pos[sz-1]='\0';
/* insert the header */
snprintf(message, MAX_STR, "mhttp debug:%s: ", pos);
/* find the end and attach the rest of the msg */
for (pos = message; *pos != '\0'; pos++); //empty statement
sz = pos - message;
va_start(ap, msgfmt);
vsnprintf(pos, MAX_STR - sz, msgfmt, ap);
fprintf(stderr,"%s", message);
fprintf(stderr, "\n");
fflush(stderr);
}