/* ** JNetLib ** Copyright (C) 2000-2001 Nullsoft, Inc. ** Author: Justin Frankel ** File: httpget.cpp - JNL HTTP GET implementation ** License: zlib ** ** Reviewed for Unicode Support by Jim Park -- 08/17/2007 ** Note: The functions that work on char's should be explicitely set to use the ** ANSI versions. Some of the functions like wprintf() are #defined to be ** the wide-char versions when _UNICODE is defined. So these must be explictly ** set to use the ANSI versions. */ #include "netinc.h" #include "util.h" #include "httpget.h" void *operator new( unsigned int num_bytes ){return GlobalAlloc(GPTR,num_bytes);} void operator delete( void *p ) { if (p) GlobalFree(p); } JNL_HTTPGet::JNL_HTTPGet(JNL_AsyncDNS *dns, int recvbufsize, char *proxy) { m_recvbufsize=recvbufsize; m_dns=dns; m_con=NULL; m_http_proxylpinfo=0; m_http_proxyhost=0; m_http_proxyport=0; if (proxy && *proxy) { char *p=(char*)malloc(strlen(proxy)+1); if (p) { char *r=NULL; strcpy(p,proxy); do_parse_url(p,&m_http_proxyhost,&m_http_proxyport,&r,&m_http_proxylpinfo); free(r); free(p); } } m_sendheaders=NULL; reinit(); } void JNL_HTTPGet::reinit() { m_errstr=0; m_recvheaders=NULL; m_recvheaders_size=0; m_http_state=0; m_http_port=0; m_http_url=0; m_reply=0; m_http_host=m_http_lpinfo=m_http_request=NULL; } void JNL_HTTPGet::deinit() { delete m_con; free(m_recvheaders); free(m_http_url); free(m_http_host); free(m_http_lpinfo); free(m_http_request); free(m_errstr); free(m_reply); reinit(); } JNL_HTTPGet::~JNL_HTTPGet() { deinit(); free(m_sendheaders); free(m_http_proxylpinfo); free(m_http_proxyhost); } void JNL_HTTPGet::addheader(char *header) { //if (strstr(header,"\r") || strstr(header,"\n")) return; if (!m_sendheaders) { m_sendheaders=(char*)malloc(strlen(header)+3); if (m_sendheaders) { strcpy(m_sendheaders,header); strcat(m_sendheaders,"\r\n"); } } else { char *t=(char*)malloc(strlen(header)+strlen(m_sendheaders)+1+2); if (t) { strcpy(t,m_sendheaders); strcat(t,header); strcat(t,"\r\n"); free(m_sendheaders); m_sendheaders=t; } } } void JNL_HTTPGet::do_encode_mimestr(char *in, char *out) { char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int shift = 0; int accum = 0; while (*in) { if (*in) { accum <<= 8; shift += 8; accum |= *in++; } while ( shift >= 6 ) { shift -= 6; *out++ = alphabet[(accum >> shift) & 0x3F]; } } if (shift == 4) { *out++ = alphabet[(accum & 0xF)<<2]; *out++='='; } else if (shift == 2) { *out++ = alphabet[(accum & 0x3)<<4]; *out++='='; *out++='='; } *out++=0; } void JNL_HTTPGet::connect(char *url) { deinit(); m_http_url=(char*)malloc(strlen(url)+1); strcpy(m_http_url,url); do_parse_url(m_http_url,&m_http_host,&m_http_port,&m_http_request, &m_http_lpinfo); strcpy(m_http_url,url); if (!m_http_host || !m_http_host[0] || !m_http_port) { m_http_state=-1; seterrstr("invalid URL"); return; } int sendbufferlen=0; if (!m_http_proxyhost || !m_http_proxyhost[0]) { sendbufferlen += 4 /* GET */ + strlen(m_http_request) + 9 /* HTTP/1.0 */ + 2; } else { sendbufferlen += 4 /* GET */ + strlen(m_http_url) + 9 /* HTTP/1.0 */ + 2; if (m_http_proxylpinfo&&m_http_proxylpinfo[0]) { sendbufferlen+=58+strlen(m_http_proxylpinfo)*2; // being safe here } } sendbufferlen += 5 /* Host: */ + strlen(m_http_host) + 2; if (m_http_lpinfo&&m_http_lpinfo[0]) { sendbufferlen+=46+strlen(m_http_lpinfo)*2; // being safe here } if (m_sendheaders) sendbufferlen+=strlen(m_sendheaders); char *str=(char*)malloc(sendbufferlen+1024); if (!str) { seterrstr("error allocating memory"); m_http_state=-1; } if (!m_http_proxyhost || !m_http_proxyhost[0]) { wsprintfA(str,"GET %s HTTP/1.0\r\n",m_http_request); } else { wsprintfA(str,"GET %s HTTP/1.0\r\n",m_http_url); } wsprintfA(str+strlen(str),"Host: %s\r\n",m_http_host); if (m_http_lpinfo&&m_http_lpinfo[0]) { strcat(str,"Authorization: Basic "); do_encode_mimestr(m_http_lpinfo,str+strlen(str)); strcat(str,"\r\n"); } if (m_http_proxylpinfo&&m_http_proxylpinfo[0]) { strcat(str,"Proxy-Authorization: Basic "); do_encode_mimestr(m_http_proxylpinfo,str+strlen(str)); strcat(str,"\r\n"); } if (m_sendheaders) strcat(str,m_sendheaders); strcat(str,"\r\n"); int a=m_recvbufsize; if (a < 4096) a=4096; m_con=new JNL_Connection(m_dns,strlen(str)+4,a); if (m_con) { if (!m_http_proxyhost || !m_http_proxyhost[0]) { m_con->connect(m_http_host,m_http_port); } else { m_con->connect(m_http_proxyhost,m_http_proxyport); } m_con->send_string(str); } else { m_http_state=-1; seterrstr("could not create connection object"); } free(str); } static int my_strnicmp(char *b1, char *b2, int l) { while (l-- && *b1 && *b2) { char bb1=*b1++; char bb2=*b2++; if (bb1>='a' && bb1 <= 'z') bb1+='A'-'a'; if (bb2>='a' && bb2 <= 'z') bb2+='A'-'a'; if (bb1 != bb2) return bb1-bb2; } return 0; } char *_strstr(char *i, char *s) { if (strlen(i)>=strlen(s)) while (i[strlen(s)-1]) { int l=strlen(s)+1; char *ii=i; char *is=s; while (--l>0) { if (*ii != *is) break; ii++; is++; } if (l==0) return i; i++; } return NULL; } #define strstr _strstr void JNL_HTTPGet::do_parse_url(char *url, char **host, int *port, char **req, char **lp) { char *p,*np; free(*host); *host=0; free(*req); *req=0; free(*lp); *lp=0; if (strstr(url,"://")) np=p=strstr(url,"://")+3; else np=p=url; while (*np != '/' && *np) np++; if (*np) { *req=(char*)malloc(strlen(np)+1); if (*req) strcpy(*req,np); *np++=0; } else { *req=(char*)malloc(2); if (*req) strcpy(*req,"/"); } np=p; while (*np != '@' && *np) np++; if (*np) { *np++=0; *lp=(char*)malloc(strlen(p)+1); if (*lp) strcpy(*lp,p); p=np; } else { *lp=(char*)malloc(1); if (*lp) strcpy(*lp,""); } np=p; while (*np != ':' && *np) np++; if (*np) { *np++=0; *port=my_atoi(np); } else *port=80; *host=(char*)malloc(strlen(p)+1); if (*host) strcpy(*host,p); } char *JNL_HTTPGet::getallheaders() { // double null terminated, null delimited list if (m_recvheaders) return m_recvheaders; else return "\0\0"; } char *JNL_HTTPGet::getheader(char *headername) { char *ret=NULL; if (strlen(headername)<1||!m_recvheaders) return NULL; char *p=m_recvheaders; while (*p) { if (!my_strnicmp(headername,p,strlen(headername))) { ret=p+strlen(headername); while (*ret == ' ') ret++; break; } p+=strlen(p)+1; } return ret; } int JNL_HTTPGet::run() { int cnt=0; if (m_http_state==-1||!m_con) return -1; // error run_again: static char main_buf[4096]; char *buf = main_buf; m_con->run(); if (m_con->get_state()==JNL_Connection::STATE_ERROR) { seterrstr(m_con->get_errstr()); return -1; } if (m_con->get_state()==JNL_Connection::STATE_CLOSED) return 1; if (m_http_state==0) // connected, waiting for reply { if (m_con->recv_lines_available()>0) { m_con->recv_line(buf,4095); buf[4095]=0; m_reply=(char*)malloc(strlen(buf)+1); strcpy(m_reply,buf); if (strstr(buf,"200")) m_http_state=2; // proceed to read headers normally else if (strstr(buf,"301") || strstr(buf,"302")) { m_http_state=1; // redirect city } else { seterrstr(buf); m_http_state=-1; return -1; } cnt=0; } else if (!cnt++) goto run_again; } if (m_http_state == 1) // redirect { while (m_con->recv_lines_available() > 0) { m_con->recv_line(buf,4096); if (!buf[0]) { m_http_state=-1; return -1; } if (!my_strnicmp(buf,"Location:",9)) { char *p=buf+9; while (*p== ' ') p++; if (*p) { connect(p); return 0; } } } } if (m_http_state==2) { if (!cnt++ && m_con->recv_lines_available() < 1) goto run_again; while (m_con->recv_lines_available() > 0) { m_con->recv_line(buf,4096); if (!buf[0]) { m_http_state=3; break; } if (!m_recvheaders) { m_recvheaders_size=strlen(buf)+1; m_recvheaders=(char*)malloc(m_recvheaders_size+1); if (m_recvheaders) { strcpy(m_recvheaders,buf); m_recvheaders[m_recvheaders_size]=0; } } else { int oldsize=m_recvheaders_size; m_recvheaders_size+=strlen(buf)+1; char *n=(char*)malloc(m_recvheaders_size+1); if (n) { memcpy(n,m_recvheaders,oldsize); strcpy(n+oldsize,buf); n[m_recvheaders_size]=0; free(m_recvheaders); m_recvheaders=n; } } } } if (m_http_state==3) { } return 0; } int JNL_HTTPGet::get_status() // returns 0 if connecting, 1 if reading headers, // 2 if reading content, -1 if error. { if (m_http_state < 0) return -1; if (m_http_state < 2) return 0; if (m_http_state == 2) return 1; if (m_http_state == 3) return 2; return -1; } int JNL_HTTPGet::getreplycode()// returns 0 if none yet, otherwise returns http reply code. { if (!m_reply) return 0; char *p=m_reply; while (*p && *p != ' ') p++; // skip over HTTP/x.x if (!*p) return 0; return my_atoi(++p); } int JNL_HTTPGet::bytes_available() { if (m_con && m_http_state==3) return m_con->recv_bytes_available(); return 0; } int JNL_HTTPGet::get_bytes(char *buf, int len) { if (m_con && m_http_state==3) return m_con->recv_bytes(buf,len); return 0; } int JNL_HTTPGet::peek_bytes(char *buf, int len) { if (m_con && m_http_state==3) return m_con->peek_bytes(buf,len); return 0; }