/* ** JNetLib ** Copyright (C) 2000-2003 Nullsoft, Inc. ** Author: Justin Frankel ** File: webserver.cpp - Generic simple webserver baseclass ** License: see jnetlib.h ** see test.cpp for an example of how to use this class */ #include #include "jnetlib.h" #include "webserver.h" class WS_ItemList { public: WS_ItemList() { m_size=0; m_list=0; } ~WS_ItemList() { ::free(m_list); } void *Add(void *i); void *Get(int w); void Del(int idx); int GetSize(void) { return m_size; } protected: void **m_list; int m_size; }; class WS_conInst { public: WS_conInst(JNL_Connection *c, int which_port); ~WS_conInst(); // these will be used by WebServerBaseClass::onConnection yay JNL_HTTPServ m_serv; IPageGenerator *m_pagegen; int m_port; // port this came in on time_t m_connect_time; }; void *WS_ItemList::Add(void *i) { if (!m_list || !(m_size&31)) { m_list=(void**)::realloc(m_list,sizeof(void*)*(m_size+32)); } m_list[m_size++]=i; return i; } void *WS_ItemList::Get(int w) { if (w >= 0 && w < m_size) return m_list[w]; return NULL; } void WS_ItemList::Del(int idx) { if (m_list && idx >= 0 && idx < m_size) { m_size--; if (idx != m_size) ::memcpy(m_list+idx,m_list+idx+1,sizeof(void *)*(m_size-idx)); if (!(m_size&31)&&m_size) // resize down { m_list=(void**)::realloc(m_list,sizeof(void*)*m_size); } } } WS_conInst::WS_conInst(JNL_Connection *c, int which_port) : m_serv(c), m_port(which_port) { m_pagegen=0; time(&m_connect_time); } WS_conInst::~WS_conInst() { delete m_pagegen; } WebServerBaseClass::~WebServerBaseClass() { int x; for (x = 0; x < m_connections->GetSize(); x ++) { delete (WS_conInst *)m_connections->Get(x); } delete m_connections; for (x = 0; x < m_listeners->GetSize(); x ++) { delete (JNL_Listen *)m_listeners->Get(x); } delete m_listeners; } WebServerBaseClass::WebServerBaseClass() { m_connections=new WS_ItemList; m_listeners=new WS_ItemList; m_listener_rot=0; m_timeout_s=15; m_max_con=100; } void WebServerBaseClass::setMaxConnections(int max_con) { m_max_con=max_con; } void WebServerBaseClass::setRequestTimeout(int timeout_s) { m_timeout_s=timeout_s; } int WebServerBaseClass::addListenPort(int port, unsigned long which_interface) { int ffree=-1; removeListenPort(port); JNL_Listen *p=new JNL_Listen(port,which_interface); m_listeners->Add((void *)p); if (p->is_error()) return -1; return 0; } void WebServerBaseClass::removeListenPort(int port) { int x; for (x = 0; x < m_listeners->GetSize(); x ++) { JNL_Listen *p=(JNL_Listen *)m_listeners->Get(x); if (p->port()==port) { delete p; m_listeners->Del(x); break; } } } void WebServerBaseClass::removeListenIdx(int idx) { if (idx >= 0 && idx < m_listeners->GetSize()) { JNL_Listen *p=(JNL_Listen *)m_listeners->Get(idx); delete p; m_listeners->Del(idx); } } int WebServerBaseClass::getListenPort(int idx, int *err) { JNL_Listen *p=(JNL_Listen *)m_listeners->Get(idx); if (p) { if (err) *err=p->is_error(); return p->port(); } return 0; } void WebServerBaseClass::attachConnection(JNL_Connection *con, int port) { m_connections->Add((void*)new WS_conInst(con,port)); } void WebServerBaseClass::run(void) { int nl; if (m_connections->GetSize() < m_max_con && (nl=m_listeners->GetSize())) { JNL_Listen *l=(JNL_Listen *)m_listeners->Get(m_listener_rot++ % nl); JNL_Connection *c=l->get_connect(); if (c) { attachConnection(c,l->port()); } } int x; for (x = 0; x < m_connections->GetSize(); x ++) { if (run_connection((WS_conInst *)m_connections->Get(x))) { delete ((WS_conInst *)m_connections->Get(x)); m_connections->Del(x--); } } } int WebServerBaseClass::run_connection(WS_conInst *con) { int s=con->m_serv.run(); if (s < 0) { // m_serv.geterrorstr() return 1; } if (s < 2) { // return 1 if we timed out return time(NULL)-con->m_connect_time > m_timeout_s; } if (s < 3) { con->m_pagegen=onConnection(&con->m_serv,con->m_port); return 0; } if (s < 4) { if (!con->m_pagegen) { return !con->m_serv.bytes_inqueue(); } char buf[16384]; int l=con->m_serv.bytes_cansend(); if (l > 0) { if (l > sizeof(buf))l=sizeof(buf); l=con->m_pagegen->GetData(buf,l); if (l < (con->m_pagegen->IsNonBlocking() ? 0 : 1)) // if nonblocking, this is l < 0, otherwise it's l<1 { return !con->m_serv.bytes_inqueue(); } if (l>0) con->m_serv.write_bytes(buf,l); } return 0; } return 1; // we're done by this point } void WebServerBaseClass::url_encode(char *in, char *out, int max_out) { while (*in && max_out > 4) { if ((*in >= 'A' && *in <= 'Z')|| (*in >= 'a' && *in <= 'z')|| (*in >= '0' && *in <= '9')|| *in == '.' || *in == '_' || *in == '-') { *out++=*in++; max_out--; } else { int i=*in++; *out++ = '%'; int b=(i>>4)&15; if (b < 10) *out++='0'+b; else *out++='A'+b-10; b=i&15; if (b < 10) *out++='0'+b; else *out++='A'+b-10; max_out-=3; } } *out=0; } void WebServerBaseClass::url_decode(char *in, char *out, int maxlen) { while (*in && maxlen>1) { if (*in == '+') { in++; *out++=' '; } else if (*in == '%' && in[1] != '%' && in[1]) { int a=0; int b=0; for ( b = 0; b < 2; b ++) { int r=in[1+b]; if (r>='0'&&r<='9') r-='0'; else if (r>='a'&&r<='z') r-='a'-10; else if (r>='A'&&r<='Z') r-='A'-10; else break; a*=16; a+=r; } if (b < 2) *out++=*in++; else { *out++=a; in += 3;} } else *out++=*in++; maxlen--; } *out=0; } void WebServerBaseClass::base64decode(char *src, char *dest, int destsize) { int accum=0; int nbits=0; while (*src) { int x=0; char c=*src++; if (c >= 'A' && c <= 'Z') x=c-'A'; else if (c >= 'a' && c <= 'z') x=c-'a' + 26; else if (c >= '0' && c <= '9') x=c-'0' + 52; else if (c == '+') x=62; else if (c == '/') x=63; else break; accum <<= 6; accum |= x; nbits += 6; while (nbits >= 8) { if (--destsize<=0) break; nbits-=8; *dest++ = (char)((accum>>nbits)&0xff); } } *dest=0; } void WebServerBaseClass::base64encode(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; } int WebServerBaseClass::parseAuth(char *auth_header, char *out, int out_len)//returns 0 on unknown auth, 1 on basic { char *authstr=auth_header; *out=0; if (!auth_header || !*auth_header) return 0; while (*authstr == ' ') authstr++; if (strnicmp(authstr,"basic ",6)) return 0; authstr+=6; while (*authstr == ' ') authstr++; base64decode(authstr,out,out_len); return 1; }