/*
* 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"
bool mhttp_lets_debug; /* global debugging flag */
bool mhttp_body_set_flag; /* global body set flag */
int mhttp_hcnt,
mhttp_bcnt,
mhttp_rcode,
mhttp_response_length;
char *mhttp_body,
*mhttp_response,
*mhttp_reason,
mhttp_resp_headers[MAX_STR],
*mhttp_headers[MAX_HEADERS],
*mhttp_buffers[MAX_BUFFERS];
void mhttp_switch_debug(int set)
{
if (set > 0)
{
mhttp_lets_debug = true;
mhttp_debug("%s", "switched on debugging...");
} else {
mhttp_lets_debug = false;
}
}
int mhttp_get_status_code(void)
{
return mhttp_rcode;
}
int mhttp_get_response_length(void)
{
return mhttp_response_length;
}
char *mhttp_get_reason(void)
{
return strdup(mhttp_reason);
}
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;
for (i = 0; i < mhttp_bcnt; i++)
{
free(mhttp_buffers[i]);
mhttp_debug("freeing buffer");
mhttp_buffers[i] = NULL;
}
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_bcnt = 0;
mhttp_rcode = 0;
mhttp_debug("finished reset");
}
void mhttp_init(void)
{
int i;
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_reset();
mhttp_debug("finished init");
}
void mhttp_add_header(char *hdr)
{
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)
{
mhttp_body = strdup(bdy);
mhttp_debug("setting body: %s", mhttp_body);
mhttp_body_set_flag = true;
}
int mhttp_call(char *paction, char *purl)
{
bool found_hdrs; /* found the end of headers flag */
bool rcode_flag; /* found return code flag */
char *action, *url, *host, *ptr, *eomsg, *clptr;
int port,
socket_descriptor,
returnval,
result,
bcnt,
len,
curr_len,
i;
char str[MAX_STR],
surl[MAX_STR];
char *resplines[MAX_RESPONSE];
int resplines_lens[MAX_RESPONSE];
memset(mhttp_resp_headers, 0, MAX_STR);
if(strlen(paction) == 0)
{
mhttp_debug("must supply an action");
return -2;
}
action = strdup(paction);
if(strlen(purl) == 0)
{
mhttp_debug("must supply a url");
return -3;
}
url = strdup(purl);
mhttp_debug("action: %s url: %s", action, url);
if (strcmp(action, "GET") != 0 &&
strcmp(action, "POST") != 0 &&
strcmp(action, "PUT") != 0 &&
strcmp(action, "DELETE") != 0 &&
strcmp(action, "HEAD") != 0)
{
mhttp_debug("must supply an action of GET, PUT, POST, DELETE, or HEAD");
return -1;
}
mhttp_debug("action is: %s", action);
mhttp_debug("url is: %s", url);
// make sure that the url starts with http://
if (strncmp(url, "http://", 7) != 0){
mhttp_debug("url must start with http:// - yep we dont support https\n");
return -4;
}
host = url+7;
memset(surl,0,sizeof(surl));
// hunt for the beginning of the uri
ptr = strstr(host, "/");
if (ptr != NULL){
*ptr = '\0';
ptr++;
sprintf(surl, "/%s", ptr);
} else {
sprintf(surl, "/");
}
mhttp_debug("hostname is: %s", host);
mhttp_debug("uri is: %s\n", surl);
// hunt for the beginning of the port
ptr = strstr(host, ":");
if (ptr != NULL){
*ptr = '\0';
ptr++;
port = atoi(ptr);
mhttp_debug("detected port: %d", port);
} else {
port = 80;
}
mhttp_debug("port number is: %d", port);
socket_descriptor = mhttp_connect_inet_addr(host, port);
len = 0;
// construct the query string
memset(str, 0, sizeof(str));
strcpy(str, action);
strcpy(str+strlen(str), " ");
strcpy(str+strlen(str), surl);
strcpy(str+strlen(str), " HTTP/1.0\r\n");
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);
returnval = write(socket_descriptor, str, strlen(str));
if(returnval < 0)
{
/* write returns -1 on error */
perror("write(query string) error");
return -5;
}
if(returnval < strlen(str))
{
/* I'm not dealing with this error, regular programs should. */
perror("the query string write was short\n");
return -6;
}
// 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_descriptor, 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_descriptor, "\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;
}
}
/* read off the response and split out headers and content */
mhttp_debug("starting output:");
found_hdrs = false;
rcode_flag = false;
len = 0;
bcnt = 0;
curr_len = 0;
while((returnval = read(socket_descriptor, str, 80)) > 0)
{
*(str+returnval) = '\0';
// make sure that we dont overflow
if (bcnt > MAX_RESPONSE)
break;
/* detect the end of the headers */
if (!found_hdrs){
sprintf(mhttp_resp_headers+strlen(mhttp_resp_headers), "%s", str);
if ((ptr = strstr(mhttp_resp_headers, "\r\n\r\n")) ||
(ptr = strstr(mhttp_resp_headers, "\n\n"))){
found_hdrs = true;
*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;
}
// determine the Content-Length header
if ((clptr = strstr(mhttp_resp_headers, "Content-Length:")) ||
(clptr = strstr(mhttp_resp_headers, "Content-length:"))){
mhttp_debug("found content-length");
clptr += 16;
mhttp_response_length = atoi(clptr);
mhttp_debug("content length: %d", mhttp_response_length);
mhttp_response = malloc(mhttp_response_length + 2);
memset(mhttp_response, 0 , mhttp_response_length + 2);
if (mhttp_response_length >= (returnval - curr_len)){
mhttp_debug("copying the initial part of the body");
memcpy(mhttp_response, ptr, returnval - curr_len);
len = returnval - curr_len;
} else {
// serious error - cant determine length properly
mhttp_debug("serious error - cant determine length properly");
len = 0;
return -8;
}
} else {
mhttp_debug("didnt find content-length - must use buffers");
mhttp_response_length = 0;
resplines[bcnt++] = malloc(returnval - curr_len);
memcpy(resplines[bcnt - 1], ptr, returnval - curr_len);
resplines_lens[bcnt - 1] = returnval - curr_len;
len += returnval - curr_len;
}
} else {
curr_len += 80;
}
} else {
if (mhttp_response_length > 0){
// make sure that it does not overflow the buffer
if (mhttp_response_length >= (len + returnval)){
memcpy(mhttp_response+len, str, returnval);
}
} else {
resplines[bcnt++] = malloc(returnval);
memcpy(resplines[bcnt - 1], str, returnval);
resplines_lens[bcnt - 1] = returnval;
}
len += returnval;
}
/* 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 */
//eomsg = NULL;
if ((eomsg = strstr(ptr, "\r\n")) || (eomsg = strstr(ptr, "\n"))){
mhttp_debug("found end of reason line");
*eomsg = '\0';
mhttp_reason = strdup(ptr);
}
mhttp_debug("detected return code: %d - %s", mhttp_rcode, mhttp_reason);
}
// lets get out of here if we have read enough
if (mhttp_response_length > 0 && len >= mhttp_response_length )
break;
}
if (mhttp_response_length > 0){
mhttp_debug("content length actually copied: %d", len);
} else {
mhttp_debug("transfering buffers as no content-length header");
resplines[bcnt] = NULL;
mhttp_response = malloc(len + 2);
memset(mhttp_response, 0 , len + 2);
curr_len = 0;
for (i = 0; i < bcnt; i++){
memcpy(mhttp_response+curr_len, resplines[i], resplines_lens[i]);
free(resplines[i]);
curr_len += resplines_lens[i];
}
}
mhttp_response_length = len;
/* it will be closed anyway when we exit */
close(socket_descriptor);
mhttp_debug("all done");
/* just to be pedantic... */
return 1;
}
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);
/* 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");
//exit(5);
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");
//exit(6);
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");
//exit(7);
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);
}