code:
/*
** JNetLib
** Copyright (C) 2000-2001 Nullsoft, Inc.
** Author: Justin Frankel
** File: httpget.h - JNL interface for doing HTTP GETs.
** License: see jnetlib.h
**
** Usage:
**   1. Create a JNL_HTTPGet object, optionally specifying a JNL_AsyncDNS
**      object to use (or NULL for none, or JNL_CONNECTION_AUTODNS for auto),
**      and the receive buffer size, and a string specifying proxy (or NULL
**      for none). See note on proxy string below.
**   2. call addheader() to add whatever headers you want. It is recommended to
**      add at least the following two:
**        addheader("User-Agent:MyApp (Mozilla)");
*///      addheader("Accept:*/*");
/*         ( the comment weirdness is there so I Can do the star-slash :)
**   3. Call connect() with the URL you wish to GET (see URL string note below)
**   4. Call run() once in a while, checking to see if it returns -1
**      (if it does return -1, call geterrorstr() to see what the error is).
**      (if it returns 1, no big deal, the connection has closed).
**   5. While you're at it, you can call bytes_available() to see if any data
**      from the http stream is available, or getheader() to see if any headers
**      are available, or getreply() to see the HTTP reply, or getallheaders()
**      to get a double null terminated, null delimited list of headers returned.
**   6. If you want to read from the stream, call get_bytes (which returns how much
**      was actually read).
**   7. content_length() is a helper function that uses getheader() to check the
**      content-length header.
**   8. Delete ye' ol' object when done.
**
** Proxy String:
**   should be in the format of host:port, or user@host:port, or
**   user:password@host:port. if port is not specified, 80 is assumed.
** URL String:
**   should be in the format of http://user:pass@host:port/requestwhatever
**   note that user, pass, port, and /requestwhatever are all optional :)
**   note that also, http:// is really not important. if you do poo://
**   or even leave out the http:// altogether, it will still work.
*/

#ifndef _HTTPGET_H_
#define _HTTPGET_H_

#include "connection.h"

class JNL_HTTPGet
{
  public:
    JNL_HTTPGet(JNL_AsyncDNS *dns=JNL_CONNECTION_AUTODNS, int recvbufsize=16384, char *proxy=NULL);
    ~JNL_HTTPGet();

    void addheader(const char *header);

    void connect(const char *url, int ver=0, char *requestmethod="GET");

    int run(); // returns: 0 if all is OK. -1 if error (call geterrorstr()). 1 if connection closed.

    int   get_status(); // returns 0 if connecting, 1 if reading headers,
                        // 2 if reading content, -1 if error.

    char *getallheaders(); // double null terminated, null delimited list
    char *getheader(char *headername);
    char *getreply() { return m_reply; }
    int   getreplycode(); // returns 0 if none yet, otherwise returns http reply code.

    char *geterrorstr() { return m_errstr;}

    int bytes_available();
    int get_bytes(char *buf, int len);
    int peek_bytes(char *buf, int len);

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
    int content_length() { char *p=getheader("content-length"); if (p) return atoi(p); return 0; }
#pragma GCC diagnostic pop

    JNL_Connection *get_con() { return m_con; }

  protected:
    void reinit();
    void deinit();
    void seterrstr(char *str) { if (m_errstr) free(m_errstr); m_errstr=(char*)malloc(strlen(str)+1); strcpy(m_errstr,str); }

    void do_parse_url(char *url, char **host, int *port, char **req, char **lp);
    void do_encode_mimestr(char *in, char *out);

    JNL_AsyncDNS *m_dns;
    JNL_Connection *m_con;
    int m_recvbufsize;

    int m_http_state;

    int m_http_port;
    char *m_http_url;
    char *m_http_host;
    char *m_http_lpinfo;
    char *m_http_request;

    char *m_http_proxylpinfo;
    char *m_http_proxyhost;
    int   m_http_proxyport;

    char *m_sendheaders;
    char *m_recvheaders;
    int m_recvheaders_size;
    char *m_reply;

    char *m_errstr;
};

#endif // _HTTPGET_H_


code:
/*
** JNetLib
** Copyright (C) 2001 Nullsoft, Inc.
** Author: Justin Frankel
** File: httpserv.cpp - JNL HTTP GET/POST serving implementation
** License: see jnetlib.h
**
** This class just manages the http reply/sending, not where the data
** comes from, etc.
*/

#include "netinc.h"
#include "util.h"

#include "httpserv.h"

/*
  States for m_state:
    -1 error (connection closed, etc)
    0 not read request yet.
    1 reading headers
    2 headers read, have not sent reply
    3 sent reply
    4 closed
*/

JNL_HTTPServ::JNL_HTTPServ(JNL_Connection *con)
{
  m_con=con;
  m_state=0;
  m_reply_headers=0;
  m_reply_string=0;
  m_recvheaders=0;
  m_recv_request=0;
  m_recvheaders_size=0;
  m_errstr=0;
  m_reply_ready=0;
}

JNL_HTTPServ::~JNL_HTTPServ()
{
  free(m_recv_request);
  free(m_recvheaders);
  free(m_reply_string);
  free(m_reply_headers);
  free(m_errstr);
  delete m_con;
}

int JNL_HTTPServ::run()
{ // returns: < 0 on error, 0 on connection close, 1 if reading request, 2 if reply not sent, 3 if reply sent, sending data.
  int cnt=0;
run_again:
  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 4;

  if (m_state == 0)
  {
    if (m_con->recv_lines_available()>0)
    {
      char *buf=(char*)malloc(m_con->recv_bytes_available()-1);
      m_con->recv_line(buf,m_con->recv_bytes_available()-1);
      free(m_recv_request);
      m_recv_request=(char*)malloc(strlen(buf)+2);
      strcpy(m_recv_request,buf);
      m_recv_request[strlen(m_recv_request)+1]=0;
      free(buf);
      buf=m_recv_request;
      while (*buf) buf++;
      while (buf >= m_recv_request && *buf != ' ') buf--;
      if (strncmp(buf+1,"HTTP",4) || strncmp(m_recv_request,"GET ",3))
      {
        seterrstr("malformed HTTP request");
        m_state=-1;
      }
      else
      {
        m_state=1;
        cnt=0;
        if (buf >= m_recv_request) buf[0]=buf[1]=0;

        buf=strstr(m_recv_request,"?");
        if (buf)
        {
          *buf++=0; // change &'s into 0s now.
          char *t=buf;
          int stat=1;
          while (*t)
          {
            if (*t == '&' && !stat) { stat=1; *t=0; }
            else stat=0;
            t++;
          }
        }
      }
    }
    else if (!cnt++) goto run_again;
  }
  if (m_state == 1)
  {
    if (!cnt++ && m_con->recv_lines_available()<1) goto run_again;
    while (m_con->recv_lines_available()>0)
    {
      char buf[4096];
      buf[0]=0;
      m_con->recv_line(buf,4096);
      if (!buf[0]) { m_state=2; 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_state == 2)
  {
    if (m_reply_ready)
    {
      // send reply
      m_con->send_string((char*)(m_reply_string?m_reply_string:"HTTP/1.1 200 OK"));
      m_con->send_string("\r\n");
      if (m_reply_headers) m_con->send_string(m_reply_headers);
      m_con->send_string("\r\n");
      m_state=3;
    }
  }
  if (m_state == 3)
  {
    // nothing.
  }

  return m_state;
}

char *JNL_HTTPServ::get_request_file()
{
  // file portion of http request
  if (!m_recv_request) return NULL;
  char *t=m_recv_request;
  while (*t != ' ' && *t) t++;
  if (!*t) return NULL;
  while (*t == ' ') t++;
  return t;
}

char *JNL_HTTPServ::get_request_parm(char *parmname) // parameter portion (after ?)
{
  char *t=m_recv_request;
  while (*t) t++;
  t++;
  while (*t)
  {
    while (*t == '&') t++;
    if (!strnicmp(t,parmname,strlen(parmname)) && t[strlen(parmname)] == '=')
    {
      return t+strlen(parmname)+1;
    }
    t+=strlen(t)+1;
  }
  return NULL;
}

char *JNL_HTTPServ::getheader(char *headername)
{
  char *ret=NULL;
  if (strlen(headername)<1||!m_recvheaders) return NULL;
  char *buf=(char*)malloc(strlen(headername)+2);
  strcpy(buf,headername);
  if (buf[strlen(buf)-1]!=':') strcat(buf,":");
  char *p=m_recvheaders;
  while (*p)
  {
    if (!strnicmp(buf,p,strlen(buf)))
    {
      ret=p+strlen(buf);
      while (*ret == ' ') ret++;
      break;
    }
    p+=strlen(p)+1;
  }
  free(buf);
  return ret;
}

void JNL_HTTPServ::set_reply_string(char *reply_string) // should be HTTP/1.1 OK or the like
{
  free(m_reply_string);
  m_reply_string=(char*)malloc(strlen(reply_string)+1);
  strcpy(m_reply_string,reply_string);
}

void JNL_HTTPServ::set_reply_header(char *header) // "Connection: close" for example
{
  if (m_reply_headers)
  {
    char *tmp=(char*)malloc(strlen(m_reply_headers)+strlen(header)+3);
    strcpy(tmp,m_reply_headers);
    strcat(tmp,header);
    strcat(tmp,"\r\n");
    free(m_reply_headers);
    m_reply_headers=tmp;
  }
  else
  {
    m_reply_headers=(char*)malloc(strlen(header)+3);
    strcpy(m_reply_headers,header);
    strcat(m_reply_headers,"\r\n");
  }
}

repo_name: ninjam/libninjam

license: mit


code:
/*
** JNetLib
** Copyright (C) 2001 Nullsoft, Inc.
** Author: Justin Frankel
** File: httpserv.h - JNL interface for doing HTTP GET/POST serving.
** License: see jnetlib.h
** This class just manages the http reply/sending, not where the data
** comes from, etc.
** for a mini-web server see webserver.h
*/

#ifndef _HTTPSERV_H_
#define _HTTPSERV_H_

#include "connection.h"

class JNL_HTTPServ
{
  public:
    JNL_HTTPServ(JNL_Connection *con);
    ~JNL_HTTPServ();

    int run(); // returns: < 0 on error, 0 on request not read yet, 1 if reading headers, 2 if reply not sent, 3 if reply sent, sending data. 4 on connection closed.

    char *geterrorstr() { return m_errstr;}

    // use these when state returned by run() is 2
    char *get_request_file(); // file portion of http request
    char *get_request_parm(char *parmname); // parameter portion (after ?)
    char *getallheaders() { return m_recvheaders; } // double null terminated, null delimited list
    char *getheader(char *headername);

    void set_reply_string(char *reply_string); // should be HTTP/1.1 OK or the like
    void set_reply_header(char *header); // i.e. "content-size: 12345"

    void send_reply() { m_reply_ready=1; } // send reply, state will advance to 3.

    ////////// sending data ///////////////
    int bytes_inqueue() { if (m_state == 3 || m_state == -1 || m_state ==4) return m_con->send_bytes_in_queue(); else return 0; }
    int bytes_cansend() { if (m_state == 3) return m_con->send_bytes_available(); else return 0; }
    void write_bytes(char *bytes, int length) { m_con->send(bytes,length); }

    void close(int quick) { m_con->close(quick); m_state=4; }

    JNL_Connection *get_con() { return m_con; }

  protected:
    void seterrstr(char *str) { if (m_errstr) free(m_errstr); m_errstr=(char*)malloc(strlen(str)+1); strcpy(m_errstr,str); }

    int m_reply_ready;
    int m_state;

    char *m_errstr;
    char *m_reply_headers;
    char *m_reply_string;
    char *m_recvheaders;
    int   m_recvheaders_size;
    char *m_recv_request; // either double-null terminated, or may contain parameters after first null.
    JNL_Connection *m_con;
};

#endif // _HTTPSERV_H_


code:
/*
** JNetLib
** Copyright (C) 2000-2003 Nullsoft, Inc.
** Author: Justin Frankel
** File: jnetlib.h - JNL main include file (not really necessary).
**
** For documentation, look at the following files:
**  Generic network initialization: netinc.h
**  DNS: asyncdns.h
**  TCP connections: connection.h
**  HTTP GET connections: httpget.h
**  TCP listen: listen.h
**
**  license:
**
**  This software is provided 'as-is', without any express or implied
**  warranty.  In no event will the authors be held liable for any damages
**  arising from the use of this software.
**
**  Permission is granted to anyone to use this software for any purpose,
**  including commercial applications, and to alter it and redistribute it
**  freely, subject to the following restrictions:
**
**  1. The origin of this software must not be misrepresented; you must not
**     claim that you wrote the original software. If you use this software
**     in a product, an acknowledgment in the product documentation would be
**     appreciated but is not required.
**  2. Altered source versions must be plainly marked as such, and must not be
**     misrepresented as being the original software.
**  3. This notice may not be removed or altered from any source distribution.
**
*/

#ifndef _JNETLIB_H_
#define _JNETLIB_H_

#include "netinc.h"
#include "util.h"

#include "asyncdns.h"
#include "connection.h"
#include "httpget.h"
#include "httpserv.h"
#include "listen.h"

#endif//_JNETLIB_H_


code:
/*
** JNetLib
** Copyright (C) 2000-2001 Nullsoft, Inc.
** Author: Justin Frankel
** File: listen.cpp - JNL TCP listen implementation
** License: see jnetlib.h
*/

#include "netinc.h"
#include "util.h"
#include "listen.h"

JNL_Listen::JNL_Listen(short port, unsigned long which_interface)
{
  m_port=port;
  m_socket = ::socket(AF_INET,SOCK_STREAM,0);
  if (m_socket < 0)
  {
  }
  else
  {
    struct sockaddr_in sin;
    SET_SOCK_BLOCK(m_socket,0);
#ifndef _WIN32
    int bflag = 1;
    setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &bflag, sizeof(bflag));
#endif
    memset((char *) &sin, 0,sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons( (short) port );
    sin.sin_addr.s_addr = which_interface?which_interface:INADDR_ANY;
    if (::bind(m_socket,(struct sockaddr *)&sin,sizeof(sin)))
    {
      closesocket(m_socket);
      m_socket=-1;
    }
    else
    {
      if (::listen(m_socket,8)==-1)
      {
        closesocket(m_socket);
        m_socket=-1;
      }
    }
  }
}

JNL_Listen::~JNL_Listen()
{
  if (m_socket>=0)
  {
    closesocket(m_socket);
  }
}

JNL_Connection *JNL_Listen::get_connect(int sendbufsize, int recvbufsize)
{
  if (m_socket < 0)
  {
    return NULL;
  }
	struct sockaddr_in saddr;
	socklen_t length = sizeof(struct sockaddr_in);
	int s = accept(m_socket, (struct sockaddr *) &saddr, &length);
  if (s != -1)
  {
    JNL_Connection *c=new JNL_Connection(NULL,sendbufsize, recvbufsize);
    c->connect(s,&saddr);
    return c;
  }
  return NULL;
}


code:
/*
** JNetLib
** Copyright (C) 2000-2001 Nullsoft, Inc.
** Author: Justin Frankel
** File: listen.h - JNL interface for opening a TCP listen
** License: see jnetlib.h
**
** Usage:
**   1. create a JNL_Listen object with the port and (optionally) the interface
**      to listen on.
**   2. call get_connect() to get any new connections (optionally specifying what
**      buffer sizes the connection should be created with)
**   3. check is_error() to see if an error has occured
**   4. call port() if you forget what port the listener is on.
**
*/

#ifndef _LISTEN_H_
#define _LISTEN_H_
#include "connection.h"

class JNL_Listen
{
  public:
    JNL_Listen(short port, unsigned long which_interface=0);
    ~JNL_Listen();

    JNL_Connection *get_connect(int sendbufsize=8192, int recvbufsize=8192);
    short port(void) { return m_port; }
    int is_error(void) { return (m_socket<0); }

  protected:
    int m_socket;
    short m_port;
};

#endif //_LISTEN_H_


code:
/*
** JNetLib
** Copyright (C) 2000-2001 Nullsoft, Inc.
** Author: Justin Frankel
** File: netinc.h - network includes and portability defines (used internally)
** License: see jnetlib.h
*/

#ifndef _NETINC_H_
#define _NETINC_H_

#ifdef _WIN32

#include <windows.h>
#include <stdio.h>
#include <time.h>
#define strcasecmp(x,y) stricmp(x,y)
#define ERRNO (WSAGetLastError())
#define SET_SOCK_BLOCK(s,block) { unsigned long __i=block?0:1; ioctlsocket(s,FIONBIO,&__i); }
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEWOULDBLOCK
typedef int socklen_t;

#else

#ifndef THREAD_SAFE
#define THREAD_SAFE
#endif
#ifndef _REENTRANT
#define _REENTRANT
#endif
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define ERRNO errno
#define closesocket(s) close(s)
#define SET_SOCK_BLOCK(s,block) { int __flags; if ((__flags = fcntl(s, F_GETFL, 0)) != -1) { if (!block) __flags |= O_NONBLOCK; else __flags &= ~O_NONBLOCK; fcntl(s, F_SETFL, __flags);  } }

#define stricmp(x,y) strcasecmp(x,y)
#define strnicmp(x,y,z) strncasecmp(x,y,z)
#define wsprintf sprintf

#ifdef MACOSX
typedef int socklen_t;
#endif

#endif // !_WIN32

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif

#ifndef INADDR_ANY
#define INADDR_ANY 0
#endif

#ifndef SHUT_RDWR
#define SHUT_RDWR 2
#endif

#endif //_NETINC_H_


code:
/*
** JNetLib
** Copyright (C) 2000-2001 Nullsoft, Inc.
** Author: Justin Frankel
** File: test.cpp - JNL test code
** License: see jnetlib.h
*/

#ifdef _WIN32
#include <windows.h>
#else
#define Sleep(x) usleep((x)*1000)
#endif
#include <stdio.h>
#include "jnetlib.h"

#define TEST_ASYNCDNS 0
#define TEST_CONNECTION 0
#define TEST_LISTEN 0
#define TEST_TELNET_GATEWAY 0
#define TEST_HTTPGET 0
#define TEST_WEBSERVER 1

#define TEST_UDP 0 //udp is not done yet tho :)

#if (TEST_WEBSERVER)
#include "webserver.h"

class MemPageGenerator : public IPageGenerator
{
  public:
    virtual ~MemPageGenerator() { free(m_buf); }
    MemPageGenerator(char *buf, int buf_len=-1) { m_buf=buf; if (buf_len >= 0) m_buf_size=buf_len; else m_buf_size=strlen(buf); m_buf_pos=0; }
    virtual int GetData(char *buf, int size) // return 0 when done
    {
      int a=m_buf_size-m_buf_pos;
      if (a < size) size=a;
      memcpy(buf,m_buf+m_buf_pos,size);
      m_buf_pos+=size;
      return size;
    }

  private:
    char *m_buf;
    int m_buf_size;
    int m_buf_pos;
};

class wwwServer : public WebServerBaseClass
{
public:
  wwwServer() { }
  virtual IPageGenerator *onConnection(JNL_HTTPServ *serv, int port)
  {
    serv->set_reply_header("Server:jnetlib_test/0.0");
    if (!strcmp(serv->get_request_file(),"/"))
    {
      serv->set_reply_string("HTTP/1.1 200 OK");
      serv->set_reply_header("Content-Type:text/html");
      serv->send_reply();

      return new MemPageGenerator(strdup("Test Web Server v0.0"));
    }
    else
    {
      serv->set_reply_string("HTTP/1.1 404 NOT FOUND");
      serv->send_reply();
      return 0; // no data
    }
  }
};

int main(int argc, char **argv)
{
  JNL::open_socketlib();
  {
    wwwServer foo;
    foo.addListenPort(8080);
    while (1)
    {
      foo.run();
      Sleep(10);
    }
  }
  JNL::close_socketlib();
  return 0;
}

#endif

#if (TEST_HTTPGET)
int main(int argc, char **argv)
{

  if (argc != 3)
  {
    printf("usage: httpget <url> <outfile>\n");
    exit(0);
  }

  JNL_HTTPGet get;
  JNL::open_socketlib();

  get.addheader("User-Agent:PooHead (Mozilla)");
  get.addheader("Accept:*/*");
  get.connect(argv[1]);

  FILE *fp=fopen(argv[2],"wb");
  int headerstate=0;
  int has_printed_headers=0;
  int has_printed_reply=0;
  while (1)
  {
    int st=get.run();
    if (st<0)
    {
      printf("HTTPGet error: %s\n",get.geterrorstr());
      break;
    }
    if (get.get_status()>0)
    {
      if (!has_printed_reply)
      {
        has_printed_reply=1;
        printf("reply: %s (code:%d)\n",get.getreply(),get.getreplycode());
      }
      if (get.get_status()==2)
      {
        int len;
        if (!has_printed_headers)
        {
          has_printed_headers=1;
          printf("headers:\n");
          char *p=get.getallheaders();
          while (p&&*p)
          {
            printf("%s\n",p);
            p+=strlen(p)+1;
          }
        }
        while ((len=get.bytes_available()) > 0)
        {
          char buf[4096];
          if (len > 4096) len=4096;
          len=get.get_bytes(buf,len);
          if (len>0)fwrite(buf,len,1,fp);
        }
      }
    }
    if (st==1) // 1 means connection closed
    {
      printf("HTTPGet done!\n");
      break;
    }
  }
  if (fp) fclose(fp);
  JNL::close_socketlib();
  return 0;
}

#endif

#if (TEST_TELNET_GATEWAY)

int main()
{
  JNL_Connection *cons[32]={0,};
  JNL_Connection *outcons[32]={0,};
  char textpos[32][256];
  int n_cons=0;
  int states[32]={0,};

  JNL::open_socketlib();
  JNL_AsyncDNS dns;
  JNL_Listen l(23);
  while (!l.is_error())
  {
    Sleep(30);
    if (n_cons<32)
    {
      JNL_Connection *con=l.get_connect();
      if (con)
      {
        int x;
        for (x = 0; x < 32; x ++)
        {
          if (!cons[x])
          {
            cons[x]=con;
            outcons[x]=0;
            states[x]=0;
            n_cons++;
            break;
          }
        }
      }
    }
    int x;
    for (x = 0; x < 32; x ++)
    {
      if (cons[x])
      {
        cons[x]->run();
        if (outcons[x]) outcons[x]->run();

        if (cons[x]->get_state() == JNL_Connection::STATE_ERROR || cons[x]->get_state()==JNL_Connection::STATE_CLOSED ||
            (outcons[x] && (cons[x]->get_state() == JNL_Connection::STATE_ERROR || cons[x]->get_state()==JNL_Connection::STATE_CLOSED)))
        {
          delete cons[x];
          if (outcons[x]) delete outcons[x];
          outcons[x]=0;
          cons[x]=0;
          states[x]=0;
          n_cons--;
        }
        else
        {
          if (states[x]==0)
          {
            cons[x]->send_string("\r\nwelcome ");
            states[x]++;
          }
          if (states[x]==1)
          {
            char hoststr[256];
            int ret=dns.reverse(cons[x]->get_remote(),hoststr);
            if (ret==0)
            {
              cons[x]->send_string(hoststr);
              cons[x]->send_string(". host: ");
              states[x]++;
              textpos[x][0]=0;
            }
            if (ret==-1)
            {
              JNL::addr_to_ipstr(cons[x]->get_remote(),hoststr,256);
              cons[x]->send_string(hoststr);
              cons[x]->send_string(". host: ");
              states[x]++;
              textpos[x][0]=0;
            }
          }
          if (states[x]==2)
          {
            char b;
            while (cons[x]->recv_bytes(&b,1) && states[x]==2)
            {
              if (b == '\r' || b == '\n')
              {
                if (strlen(textpos[x]))
                {
                  char *p=strstr(textpos[x],":");
                  int port=23;
                  if (p)
                  {
                    *p++=0;
                    if (atoi(p)) port=atoi(p);
                  }
                  outcons[x]=new JNL_Connection(&dns);
                  outcons[x]->connect(textpos[x],port);

                  char str[512];
                  sprintf(str,"\r\nconnecting to port %d of %s\r\n",port,textpos[x]);
                  cons[x]->send_string(str);
                  states[x]++;
                }
                else states[x]=0;
              }
              else if (b == '\b')
              {
                if (textpos[x][0])
                {
                  textpos[x][strlen(textpos[x])-1]=0;
                  cons[x]->send_string("\b \b");
              }
              }
              else
              {
                textpos[x][strlen(textpos[x])+1]=0;
                textpos[x][strlen(textpos[x])]=b;
                cons[x]->send(&b,1);
              }
            }
          }
          if (states[x]==3)
          {
            char buf[1024];
            outcons[x]->run();
            int l=cons[x]->recv_bytes(buf,1024);
            if (l) outcons[x]->send(buf,l);
            l=outcons[x]->recv_bytes(buf,1024);
            if (l) cons[x]->send(buf,l);
          }
        }
      }
    }
  }
  JNL::close_socketlib();
  return 0;
}

#endif

#if (TEST_LISTEN)

int main()
{
  JNL_HTTPServ *cons[32]={0,};
  char *contents[32]={0,};
  int n_cons=0;

  JNL::open_socketlib();
  JNL_AsyncDNS dns;
  JNL_Listen l(8000);
  while (!l.is_error())
  {
    Sleep(100);
    if (n_cons<32)
    {
      JNL_Connection *con=l.get_connect();
      if (con)
      {
        int x;
        for (x = 0; x < 32; x ++)
        {
          if (!cons[x])
          {
            cons[x]=new JNL_HTTPServ(con);
            n_cons++;
            break;
          }
        }
      }
    }
    int x;
    for (x = 0; x < 32; x ++)
    {
      if (cons[x])
      {
        int r=cons[x]->run();
        if (r == -1 || r == 4)
        {
          if (r == -1) printf("error:%s\n",cons[x]->geterrorstr());
          delete cons[x];
          cons[x]=0;
          free(contents[x]);
          contents[x]=0;
          n_cons--;
        }
        if (r == 2)
        {
          cons[x]->set_reply_string("HTTP/1.1 200 OK");
          cons[x]->set_reply_header("Content-type:text/plain");
          cons[x]->set_reply_header("Server:JNLTest");
          contents[x]=(char*)malloc(32768);
          char *poop=cons[x]->get_request_parm("poop");
          sprintf(contents[x],"test, sucka\r\n%s\r\n\r\n",poop?poop:"no poop");
          cons[x]->send_reply();
        }
        if (r == 3)
        {
          if (contents[x] && cons[x]->bytes_cansend()>strlen(contents[x]))
          {
            cons[x]->write_bytes(contents[x],strlen(contents[x]));
            cons[x]->close(0);
            free(contents[x]);
            contents[x]=0;
          }
        }
      }
    }
  }
  JNL::close_socketlib();
  return 0;
}

#endif

#if (TEST_CONNECTION)
int main()
{
  JNL::open_socketlib();
  {
    JNL_AsyncDNS dns;
    JNL_Connection con(&dns);
    con.connect("localhost",80);
    FILE *fp=fopen("c:\\hi.raw","wb");
    while (1)
    {
      con.run();
      if (con.get_state()==JNL_Connection::STATE_ERROR)
      {
        printf("error %s\n",con.get_errstr());
      }
      if (con.get_state()==JNL_Connection::STATE_CLOSED)
      {
      }
      while (con.recv_bytes_available()>0)
      {
        char buf[1024];
        int a=con.recv_bytes_available();
        if (a > 1024) a=1024;
        con.recv_bytes(buf,a);
        fwrite(buf,a,1,fp);
      }
    }
    if (fp) fclose(fp);
  }
  JNL::close_socketlib();
  return 0;
}
#endif

#if (TEST_ASYNCDNS)
int main()
{
  JNL_AsyncDNS dns;
  char *hosts[]=
  {
    "www.firehose.net",
    "gnutella.com",
    "207.48.52.200",
    "www.slashdot.org",
    "www.google.com",
    "www.winamp.com",
    "www.genekan.com",
  };
  char *reverses[]=
  {
    "64.0.160.98",
    "205.188.245.120",
    "207.48.52.222",
    "207.48.52.200",
  };
  int n=0;
  int pass=0;
  while (n<sizeof(hosts)/sizeof(hosts[0])+sizeof(reverses)/sizeof(reverses[0]))
  {
    int x;
    n=0;
    printf("pass %d\n",pass++);
    for (x = 0; x < sizeof(hosts)/sizeof(hosts[0]); x ++)
    {
      unsigned long addr;
      printf("%-30s",hosts[x]);
      switch (dns.resolve(hosts[x],&addr))
      {
        case 0:
          {
            char str[256];
            JNL::addr_to_ipstr(addr,str,256);
            printf("%s\n",str);
            n++;
          }
          break;
        case 1: printf("looking up\n"); break;
        case -1: printf("error\n"); n++; break;
      }
    }
    for (x = 0; x < sizeof(reverses)/sizeof(reverses[0]); x ++)
    {
      printf("reverse: %-21s",reverses[x]);
      char hn[512];
      switch (dns.reverse(JNL::ipstr_to_addr(reverses[x]),hn))
      {
        case 0: printf("%s\n",hn); n++; break;
        case 1: printf("looking up\n"); break;
        case -1: printf("error\n"); n++; break;
      }
    }
    Sleep(100);
  }
  return 0;
};
#endif

#if (TEST_UDP)
int main(int argc, char **argv)
{
  if (argc != 2)
  {
    printf("usage: udptest <mode>\n");
    printf("mode: 0 for client, 1 for server\n");
    exit(0);
  }
  int mode=atoi(argv[1]);

  JNL::open_socketlib();

  switch(mode)
  {
  case 0:   // client mode
    {
      JNL_AsyncDNS dns;
      JNL_UDPConnection con(0,&dns); // 0 chooses a random port
      con.setpeer("localhost",80);
      printf("Sending message...\n");
      con.send("blah",5);
      while (1)
      {
        con.run();
        if (con.get_state()==JNL_UDPConnection::STATE_ERROR)
        {
          printf("error %s\n",con.get_errstr());
        }
        while (con.recv_bytes_available()>0)
        {
          char buf[1024];
          int s=min(con.recv_bytes_available(), sizeof(buf));
          con.recv_bytes(buf,s);
          printf("received message: %s\n", buf);
        }
      }
    }
    break;
  case 1:   // server (listening) mode
    {
      JNL_UDPConnection con(80);
      printf("Waiting for messages...\n");
      while(1)
      {
        con.run();
        if (con.get_state()==JNL_UDPConnection::STATE_ERROR)
        {
          printf("error %s\n",con.get_errstr());
        }
        while (con.recv_bytes_available()>0)
        {
          char buf[1024];
          int s=min(con.recv_bytes_available(), sizeof(buf));
          con.recv_bytes(buf,s);
          printf("message received: %s. Replying...\n", buf);
          // reply on the addr:port from sender
          struct sockaddr from;
          con.get_last_recv_msg_addr(&from);
          con.setpeer(&from);
          con.send("blorp",6);
        }
      }
    }
    break;
  }

  JNL::close_socketlib();
  return 0;
}
#endif


code:
/*
** JNetLib
** Copyright (C) 2000-2001 Nullsoft, Inc.
** Author: Justin Frankel
** File: testbnc.cpp - JNL network bounce test code
** License: see jnetlib.h
*/

#ifdef _WIN32
#include <windows.h>
#else
#define Sleep(x) usleep((x)*1000)
#endif
#include <stdio.h>
#include "jnetlib.h"

int main(int argc, char *argv[])
{
  JNL_Connection *cons[32]={0,};
  JNL_Connection *outcons[32]={0,};
  int n_cons=0;

  if (argc != 4 || !atoi(argv[1]) || !atoi(argv[3]) || !argv[2][0])
  {
    printf("usage: redir localport host remoteport\n");
    exit(1);
  }

  JNL::open_socketlib();
  JNL_AsyncDNS dns;
  JNL_Listen l((short)atoi(argv[1]));
  printf("running...\n");
  while (!l.is_error())
  {
    Sleep(10);
    if (n_cons<32)
    {
      JNL_Connection *con=l.get_connect();
      if (con)
      {
        int x;
        for (x = 0; x < 32; x ++)
        {
          if (!cons[x])
          {
            outcons[x]=new JNL_Connection();
            outcons[x]->connect(argv[2],atoi(argv[3]));
            cons[x]=con;
            char host[256];
            JNL::addr_to_ipstr(cons[x]->get_remote(),host,sizeof(host));
            n_cons++;
            printf("Connection %d (%s) opened (%d).\n",x,host,n_cons);
            break;
          }
        }
      }
    }
    int x;
    for (x = 0; x < 32; x ++)
    {
      if (cons[x])
      {
        cons[x]->run();
        outcons[x]->run();

        int cerr=(cons[x]->get_state() == JNL_Connection::STATE_ERROR || cons[x]->get_state()==JNL_Connection::STATE_CLOSED);
        int oerr=(outcons[x]->get_state() == JNL_Connection::STATE_ERROR || outcons[x]->get_state()==JNL_Connection::STATE_CLOSED);

        if ((!outcons[x]->send_bytes_in_queue() && !cons[x]->recv_bytes_available() && cerr) ||
            (!cons[x]->send_bytes_in_queue() && !outcons[x]->recv_bytes_available() && oerr) ||
            (cerr && oerr))
        {
          char host[256];
          JNL::addr_to_ipstr(cons[x]->get_remote(),host,sizeof(host));
          delete cons[x];
          delete outcons[x];
          outcons[x]=0;
          cons[x]=0;
          n_cons--;
          printf("Connection %d (%s) closed (%d)\n",x,host,n_cons);
        }
        else
        {
          char buf[4096];
          int l;
          l=outcons[x]->send_bytes_available();
          if (l > 4096) l=4096;
          if (l) l=cons[x]->recv_bytes(buf,l);
          if (l) outcons[x]->send(buf,l);

          l=cons[x]->send_bytes_available();
          if (l > 4096) l=4096;
          if (l) l=outcons[x]->recv_bytes(buf,l);
          if (l) cons[x]->send(buf,l);
        }
      }
    }
  }
  JNL::close_socketlib();
  return 0;
}


code:
/*
** JNetLib
** Copyright (C) 2000-2001 Nullsoft, Inc.
** Author: Justin Frankel
** File: util.cpp - JNL implementation of basic network utilities
** License: see jnetlib.h
*/

#include "netinc.h"

#include "util.h"

int JNL::open_socketlib()
{
#ifdef _WIN32
  WSADATA wsaData;
  if (WSAStartup(MAKEWORD(1, 1), &wsaData)) return 1;
#endif
  return 0;
}
void JNL::close_socketlib()
{
#ifdef _WIN32
  WSACleanup();
#endif
}
unsigned long JNL::ipstr_to_addr(const char *cp)
{
  return ::inet_addr(cp);
}

void JNL::addr_to_ipstr(unsigned long addr, char *host, int maxhostlen)
{
  struct in_addr a; a.s_addr=addr;
  char *p=::inet_ntoa(a); strncpy(host,p?p:"",maxhostlen);
}


code:
/*
** JNetLib
** Copyright (C) 2000-2001 Nullsoft, Inc.
** Author: Justin Frankel
** File: util.h - JNL interface for basic network utilities
** License: see jnetlib.h
**
** routines you may be interested in:
**   JNL::open_socketlib();
**    opens the socket library. Call this once before using any network
**    code. If you create a new thread, call this again. Only really an
**    issue for Win32 support, but use it anyway for portability/
**
**   JNL::close_Socketlib();
**    closes the socketlib. Call this when you're done with the network,
**    after all your JNetLib objects have been destroyed.
**
**   unsigned long JNL::ipstr_to_addr(const char *cp);
**    gives you the integer representation of a ip address in dotted
**    decimal form.
**
**  JNL::addr_to_ipstr(unsigned long addr, char *host, int maxhostlen);
**    gives you the dotted decimal notation of an integer ip address.
**
*/

#ifndef _UTIL_H_
#define _UTIL_H_

class JNL
{
  public:
    static int open_socketlib();
    static void close_socketlib();
    static unsigned long ipstr_to_addr(const char *cp);
    static void addr_to_ipstr(unsigned long addr, char *host, int maxhostlen);
};

#endif //_UTIL_H_


code:
/*
** 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 <windows.h>
#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;
}


code:
/*
** JNetLib
** Copyright (C) 2003 Nullsoft, Inc.
** Author: Justin Frankel
** File: webserver.h - Generic simple webserver baseclass
** License: see jnetlib.h
**
** You can derive your object from WebServerBaseClass to do simple web serving. Example:

  class wwwServer : public WebServerBaseClass
  {
  public:
    wwwServer() { }
    virtual IPageGenerator *onConnection(JNL_HTTPServ *serv, int port)
    {
      serv->set_reply_header("Server:jnetlib_test/0.0");
      if (!strcmp(serv->get_request_file(),"/"))
      {
        serv->set_reply_string("HTTP/1.1 200 OK");
        serv->set_reply_header("Content-Type:text/html");
        serv->send_reply();

        return new MemPageGenerator(strdup("Test Web Server v0.0"));
      }
      else
      {
        serv->set_reply_string("HTTP/1.1 404 NOT FOUND");
        serv->send_reply();
        return 0; // no data
      }
    }
  };

    wwwServer foo;
    foo.addListenPort(8080);
    while (1)
    {
      foo.run();
      Sleep(10);
    }

  You will also need to derive from the IPageGenerator interface to provide a data stream, here is an
  example of MemPageGenerator:

    class MemPageGenerator : public IPageGenerator
    {
      public:
        virtual ~MemPageGenerator() { free(m_buf); }
        MemPageGenerator(char *buf, int buf_len=-1) { m_buf=buf; if (buf_len >= 0) m_buf_size=buf_len; else m_buf_size=strlen(buf); m_buf_pos=0; }
        virtual int GetData(char *buf, int size) // return 0 when done
        {
          int a=m_buf_size-m_buf_pos;
          if (a < size) size=a;
          memcpy(buf,m_buf+m_buf_pos,size);
          m_buf_pos+=size;
          return size;
        }

      private:
        char *m_buf;
        int m_buf_size;
        int m_buf_pos;
    };

**
*/

#ifndef _JNL_WEBSERVER_H_
#define _JNL_WEBSERVER_H_

#include "httpserv.h"

class IPageGenerator
{
public:
  virtual ~IPageGenerator() { };
  virtual int IsNonBlocking() { return 0; } // override this and return 1 if GetData should be allowed to return 0
  virtual int GetData(char *buf, int size)=0; // return < 0 when done (or 0 if IsNonBlocking() is 1)
};

class WS_ItemList;
class WS_conInst;

class WebServerBaseClass
{
protected: // never create one of these directly, always derive
  WebServerBaseClass();

public:
  virtual ~WebServerBaseClass();

  // stuff for setting limits/timeouts
  void setMaxConnections(int max_con);
  void setRequestTimeout(int timeout_s);

  // stuff for setting listener port
  int addListenPort(int port, unsigned long which_interface=0);
  int getListenPort(int idx, int *err=0);
  void removeListenPort(int port);
  void removeListenIdx(int idx);

  // call this a lot :)
  void run(void);

  // if you want to manually attach a connection, use this:
  // you need to specify the port it came in on so the web server can build
  // links
  void attachConnection(JNL_Connection *con, int port);

  // derived classes need to override this one =)
  virtual IPageGenerator *onConnection(JNL_HTTPServ *serv, int port)=0;

  // stats getting functions

  // these can be used externally, as well as are used by the web server
  static void url_encode(char *in, char *out, int max_out);
  static void url_decode(char *in, char *out, int maxlen);
  static void base64decode(char *src, char *dest, int destsize);
  static void base64encode(char *in, char *out);

  static int parseAuth(char *auth_header, char *out, int out_len);//returns 0 on unknown auth, 1 on basic

private:
  int run_connection(WS_conInst *con);

  int m_timeout_s;
  int m_max_con;

  JNL_AsyncDNS m_dns;

  WS_ItemList *m_listeners;
  int m_listener_rot;
  WS_ItemList *m_connections;
};

#endif//_JNL_WEBSERVER_H_


code:
/*
    WDL - lineparse.h
    Copyright (C) 2005 Cockos Incorporated
    Copyright (C) 1999-2004 Nullsoft, Inc.

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file provides a simple line parsing class. This class is also used in NSIS,
  http://nsis.sf.net. In particular, it allows for multiple space delimited tokens
  on a line, with a choice of three quotes (`bla`, 'bla', or "bla") to contain any
  items that may have spaces.

  For a bigger reference on the format, you can refer to NSIS's documentation.

*/

#ifndef WDL_LINEPARSE_H_
#define WDL_LINEPARSE_H_

#ifndef WDL_LINEPARSE_IMPL_ONLY
class LineParser {
  public:

    LineParser(bool bCommentBlock)
    {
      m_bCommentBlock=bCommentBlock;
      m_nt=m_eat=0;
      m_tokens=0;
    }

    ~LineParser()
    {
      freetokens();
    }

    bool InCommentBlock()
    {
      return m_bCommentBlock;
    }
    int getnumtokens() { return m_nt-m_eat; }

    void eattoken() { m_eat++; }

#define WDL_LINEPARSE_PREFIX
#else
#define WDL_LINEPARSE_PREFIX LineParser::
#endif

    int WDL_LINEPARSE_PREFIX parse(const char *line, int ignore_escaping
#ifdef WDL_LINEPARSE_INTF_ONLY
      =1); // returns -1 on error
#else
#ifdef WDL_LINEPARSE_IMPL_ONLY
    )
#else
    =1)
#endif
    {
      freetokens();
      bool bPrevCB=m_bCommentBlock;
      int n=doline(line, ignore_escaping);
      if (n) return n;
      if (m_nt)
      {
        m_bCommentBlock=bPrevCB;
        m_tokens=(char**)malloc(sizeof(char*)*m_nt);
        n=doline(line, ignore_escaping);
        if (n)
        {
          freetokens();
          return -1;
        }
      }
      return 0;
    }
#endif

    double WDL_LINEPARSE_PREFIX gettoken_float(int token, int *success
#ifdef WDL_LINEPARSE_INTF_ONLY
      =0);
#else

#ifdef WDL_LINEPARSE_IMPL_ONLY
    )
#else
    =0)
#endif
    {
      token+=m_eat;
      if (token < 0 || token >= m_nt)
      {
        if (success) *success=0;
        return 0.0;
      }
      if (success)
      {
        char *t=m_tokens[token];
        *success=*t?1:0;
        while (*t)
        {
          if ((*t < '0' || *t > '9')&&*t != '.') *success=0;
          t++;
        }
      }
      return atof(m_tokens[token]);
    }
#endif

    int WDL_LINEPARSE_PREFIX gettoken_int(int token, int *success
#ifdef WDL_LINEPARSE_INTF_ONLY
      =0) ;
#else
#ifdef WDL_LINEPARSE_IMPL_ONLY
    )
#else
    =0)
#endif
    {
      token+=m_eat;
      if (token < 0 || token >= m_nt || !m_tokens[token][0])
      {
        if (success) *success=0;
        return 0;
      }
      char *tmp;
      int l;
      if (m_tokens[token][0] == '-') l=strtol(m_tokens[token],&tmp,0);
      else l=(int)strtoul(m_tokens[token],&tmp,0);
      if (success) *success=! (int)(*tmp);
      return l;
    }
#endif
    char * WDL_LINEPARSE_PREFIX gettoken_str(int token)
#ifdef WDL_LINEPARSE_INTF_ONLY
      ;
#else
    {
      token+=m_eat;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
      if (token < 0 || token >= m_nt) return "";
#pragma GCC diagnostic pop
      return m_tokens[token];
    }
#endif
    int WDL_LINEPARSE_PREFIX gettoken_enum(int token, const char *strlist) // null seperated list
#ifdef WDL_LINEPARSE_INTF_ONLY
      ;
#else
    {
      int x=0;
      char *tt=gettoken_str(token);
      if (tt && *tt) while (*strlist)
      {
#ifdef _WIN32
        if (!stricmp(tt,strlist)) return x;
#else
        if (!strcasecmp(tt,strlist)) return x;
#endif
        strlist+=strlen(strlist)+1;
        x++;
      }
      return -1;
    }
#endif

#ifndef WDL_LINEPARSE_IMPL_ONLY
  private:
#endif//!WDL_LINEPARSE_IMPL_ONLY
    void WDL_LINEPARSE_PREFIX freetokens()
#ifdef WDL_LINEPARSE_INTF_ONLY
      ;
#else
    {
      if (m_tokens)
      {
        int x;
        for (x = 0; x < m_nt; x ++)
          free(m_tokens[x]);
        free(m_tokens);
      }
      m_tokens=0;
      m_nt=0;
    }
#endif

    int WDL_LINEPARSE_PREFIX doline(const char *line, int ignore_escaping)
#ifdef WDL_LINEPARSE_INTF_ONLY
      ;
#else
    {
      m_nt=0;
      if ( m_bCommentBlock )
      {
        while ( *line )
        {
          if ( *line == '*' && *(line+1) == '/' )
          {
            m_bCommentBlock=false; // Found end of comment block
            line+=2;
            break;
          }
          line++;
        }
      }
      while (*line == ' ' || *line == '\t') line++;
      while (*line)
      {
        int lstate=0; // 1=", 2=`, 4='
        if (*line == ';' || *line == '#') break;
        if (*line == '/' && *(line+1) == '*')
        {
          m_bCommentBlock = true;
          line+=2;
          return doline(line, ignore_escaping);
        }
        if (*line == '\"') lstate=1;
        else if (*line == '\'') lstate=2;
        else if (*line == '`') lstate=4;
        if (lstate) line++;
        int nc=0;
        const char *p = line;
        while (*line)
        {
          if (line[0] == '$' && line[1] == '\\') {
            switch (line[2]) {
              case '"':
              case '\'':
              case '`':
                nc += ignore_escaping ? 3 : 1;
                line += 3;
                continue;
            }
          }
          if (lstate==1 && *line =='\"') break;
          if (lstate==2 && *line =='\'') break;
          if (lstate==4 && *line =='`') break;
          if (!lstate && (*line == ' ' || *line == '\t')) break;
          line++;
          nc++;
        }
        if (m_tokens)
        {
          int i;
          m_tokens[m_nt]=(char*)malloc(nc+1);
          for (i = 0; p < line; i++, p++) {
            if (!ignore_escaping && p[0] == '$' && p[1] == '\\') {
              switch (p[2]) {
                case '"':
                case '\'':
                case '`':
                  p += 2;
              }
            }
            m_tokens[m_nt][i] = *p;
          }
          m_tokens[m_nt][nc]=0;
        }
        m_nt++;
        if (lstate)
        {
          if (*line) line++;
          else return -2;
        }
        while (*line == ' ' || *line == '\t') line++;
      }
      return 0;
    }
#endif

#ifndef WDL_LINEPARSE_IMPL_ONLY
    int m_eat;
    int m_nt;
    bool m_bCommentBlock;
    char **m_tokens;
};
#endif//!WDL_LINEPARSE_IMPL_ONLY
#endif//WDL_LINEPARSE_H_


code:
/*
    WDL - mutex.h
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file provides a simple class that abstracts a mutex or critical section object.
  On Windows it uses CRITICAL_SECTION, on everything else it uses pthread's mutex library.

  NOTE: the behavior of the two supported modes is very different, when it comes to code
  where a single thread locks the mutex twice.
  Take the code below:

    WDL_Mutex m;
    m.Enter();
    m.Enter();
    m.Leave();
    m.Leave();

  On Windows, this code will finish. On non-windows systems, the above code will hang, as the second
  Enter() will wait infinitely.
*/

#ifndef _WDL_MUTEX_H_
#define _WDL_MUTEX_H_

#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif

class WDL_Mutex {
  public:
    WDL_Mutex()
    {
#ifdef _WIN32
      InitializeCriticalSection(&m_cs);
#else
      pthread_mutex_init(&m_mutex,NULL);
#endif
    }
    ~WDL_Mutex()
    {
#ifdef _WIN32
      DeleteCriticalSection(&m_cs);
#else
      pthread_mutex_destroy(&m_mutex);
#endif
    }

    void Enter()
    {
#ifdef _WIN32
      EnterCriticalSection(&m_cs);
#else
      pthread_mutex_lock(&m_mutex);
#endif
    }

    void Leave()
    {
#ifdef _WIN32
      LeaveCriticalSection(&m_cs);
#else
      pthread_mutex_unlock(&m_mutex);
#endif
    }

  private:
#ifdef _WIN32
  CRITICAL_SECTION m_cs;
#else
  pthread_mutex_t m_mutex;
#endif

};

#endif


code:
/*
    WDL - pcmfmtcvt.h
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file provides some simple functions for dealing with PCM audio.
  Specifically:
    + convert between 16/24/32 bit integer samples and flaots (only really tested on little-endian (i.e. x86) systems)
    + mix (and optionally resample, using low quality linear interpolation) a block of floats to another.

*/

#ifndef _PCMFMTCVT_H_
#define _PCMFMTCVT_H_

static inline int float2int(double d)
{
  return (int) d;
//	  int tmp;
  //  __asm__ __volatile__ ("fistpl %0" : "=m" (tmp) : "t" (d) : "st") ;
  //  return tmp;
}

#define float_TO_INT16(out,in) \
		if ((in)<0.0) { if ((in) <= -1.0) (out) = -32768; else (out) = (short) (float2int(((in) * 32768.0)-0.5)); } \
		else { if ((in) >= 1.0) (out) = 32767; else (out) = (short) float2int((in) * 32767.0 + 0.5); }

#define INT16_TO_float(out,in) { if ((in) < 0) (out) = (float)(((double)in)/32768.0); else (out) = (float)(((double)in)/32767.0); }

static inline void i32_to_float(int i32, float *p)
{
  *p = (float) ((((double) i32)) * (1.0 / (2147483648.0)));
}

static inline void float_to_i32(float *vv, int *i32)
{
  float v = *vv;
  if (v < 0.0)
  {
	  if (v < -1.0)
	  {
		*i32 = 0x80000000;
	  }
	  else
	  {
    		*i32=float2int(v*2147483648.0-0.5);
	  }
  }
  else
  {
	  if (v >= 1.0)
	  {
		*i32 = 0x7FFFFFFF;
	  }
	  else
	  {

    		*i32=float2int(v*2147483648.0+0.5);
	  }
  }
}

static inline void i24_to_float(unsigned char *i24, float *p)
{
  int val=(i24[0]) | (i24[1]<<8) | (i24[2]<<16);
  if (val&0x800000)
  {
	  val|=0xFF000000;
  	  *p = (float) ((((double) val)) * (1.0 / (8388608.0)));
  }
  else
  {
	  val&=0xFFFFFF;
  	  *p = (float) ((((double) val)) * (1.0 / (8388607.0)));
  }

}

static inline void float_to_i24(float *vv, unsigned char *i24)
{
  float v = *vv;
  if (v < 0.0)
  {
	  if (v < -1.0)
	  {
    		i24[0]=i24[1]=0x00;
    		i24[2]=0x80;
	  }
	  else
	  {
    		int i=float2int(v*8388608.0-0.5);
    		i24[0]=(i)&0xff;
    		i24[1]=(i>>8)&0xff;
    		i24[2]=(i>>16)&0xff;
	  }
  }
  else
  {
	  if (v >= 1.0)
	  {
    		i24[0]=i24[1]=0xff;
    		i24[2]=0x7f;
	  }
	  else
	  {

    		int i=float2int(v*8388607+0.5);
    		i24[0]=(i)&0xff;
    		i24[1]=(i>>8)&0xff;
    		i24[2]=(i>>16)&0xff;
	  }
  }
}

static void pcmToFloats(void *src, int items, int bps, int src_spacing, float *dest, int dest_spacing)
{
  if (bps == 32)
  {
    int *i1=(int *)src;
    while (items--)
    {
      i32_to_float(*i1,dest);
      i1+=src_spacing;
      dest+=dest_spacing;
    }
  }
  else if (bps == 24)
  {
    unsigned char *i1=(unsigned char *)src;
    int adv=3*src_spacing;
    while (items--)
    {
      i24_to_float(i1,dest);
      dest+=dest_spacing;
      i1+=adv;
    }
  }
  else if (bps == 16)
  {
    short *i1=(short *)src;
    while (items--)
    {
      INT16_TO_float(*dest,*i1);
      i1+=src_spacing;
      dest+=dest_spacing;
    }
  }
}

static void floatsToPcm(float *src, int src_spacing, int items, void *dest, int bps, int dest_spacing)
{
  if (bps==32)
  {
    int *o1=(int*)dest;
    while (items--)
    {
      float_to_i32(src,o1);
      src+=src_spacing;
      o1+=dest_spacing;
    }
  }
  else if (bps == 24)
  {
    unsigned char *o1=(unsigned char*)dest;
    int adv=dest_spacing*3;
    while (items--)
    {
      float_to_i24(src,o1);
      src+=src_spacing;
      o1+=adv;
    }
  }
  else if (bps==16)
  {
    short *o1=(short*)dest;
    while (items--)
    {
      float_TO_INT16(*o1,*src);
      src+=src_spacing;
      o1+=dest_spacing;
    }
  }
}

static int resampleLengthNeeded(int src_srate, int dest_srate, int dest_len, double *state)
{
  // safety
  if (!src_srate) src_srate=48000;
  if (!dest_srate) dest_srate=48000;
  if (src_srate == dest_srate) return dest_len;
  return (int) (((double)src_srate*(double)dest_len/(double)dest_srate)+*state);

}

/* unused function (originally called from win32 client channelmix.cpp
       - perhaps may still be useful someday?)
static void mixFloats(float *src, int src_srate, int src_nch,  // lengths are sample pairs
                            float *dest, int dest_srate, int dest_nch,
                            int dest_len, float vol, float pan, double *state)
{
  // fucko: better resampling, this is shite
  int x;
  if (pan < -1.0f) pan=-1.0f;
  else if (pan > 1.0f) pan=1.0f;
  if (vol > 4.0f) vol=4.0f;
  if (vol < 0.0f) vol=0.0f;

  if (!src_srate) src_srate=48000;
  if (!dest_srate) dest_srate=48000;

  double vol1=vol,vol2=vol;
  if (dest_nch == 2)
  {
    if (pan < 0.0f)  vol2 *= 1.0f+pan;
    else if (pan > 0.0f) vol1 *= 1.0f-pan;
  }

  double rspos=*state;
  double drspos = (double)src_srate/(double)dest_srate;

  for (x = 0; x < dest_len; x ++)
  {

    double ls;
    double rs;
    if (src_srate != dest_srate)
    {
      int ipos = (int)rspos;
      double fracpos=rspos-ipos;
      if (src_nch == 2)
      {
        ipos+=ipos;
        ls=src[ipos]*(1.0-fracpos) + src[ipos+2]*fracpos;
        rs=src[ipos+1]*(1.0-fracpos) + src[ipos+3]*fracpos;
      }
      else
      {
        rs=ls=src[ipos]*(1.0-fracpos) + src[ipos+1]*fracpos;
      }
    }
    else
    {
      if (src_nch == 2)
      {
        int t=x+x;
        ls=src[t];
        rs=src[t+1];
      }
      else
      {
        rs=ls=src[x];
      }
    }

    rspos+=drspos;

    ls *= vol1;
    if (ls > 1.0) ls=1.0;
    else if (ls<-1.0) ls=-1.0;

    if (dest_nch == 2)
    {
      rs *= vol2;
      if (rs > 1.0) rs=1.0;
      else if (rs<-1.0) rs=-1.0;

      dest[0]+=(float) ls;
      dest[1]+=(float) rs;
      dest+=2;
    }
    else
    {
      dest[0]+=(float) ls;
      dest++;
    }
  }
  *state = rspos - (int)rspos;
}
*/

static void mixFloatsNIOutput(float *src, int src_srate, int src_nch,  // lengths are sample pairs. input is interleaved samples, output not
                            float **dest, int dest_srate, int dest_nch,
                            int dest_len, float vol, float pan, double *state)
{
  // fucko: better resampling, this is shite
  int x;
  if (pan < -1.0f) pan=-1.0f;
  else if (pan > 1.0f) pan=1.0f;
  if (vol > 4.0f) vol=4.0f;
  if (vol < 0.0f) vol=0.0f;

  if (!src_srate) src_srate=48000;
  if (!dest_srate) dest_srate=48000;

  double vol1=vol,vol2=vol;
  float *dest1=dest[0];
  float *dest2=NULL;
  if (dest_nch > 1)
  {
    dest2=dest[1];
    if (pan < 0.0f)  vol2 *= 1.0f+pan;
    else if (pan > 0.0f) vol1 *= 1.0f-pan;
  }

  double rspos=*state;
  double drspos = 1.0;
  if (src_srate != dest_srate) drspos=(double)src_srate/(double)dest_srate;

  for (x = 0; x < dest_len; x ++)
  {
    double ls,rs;
    if (src_srate != dest_srate)
    {
      int ipos = (int)rspos;
      double fracpos=rspos-ipos;
      if (src_nch == 2)
      {
        ipos+=ipos;
        ls=src[ipos]*(1.0-fracpos) + src[ipos+2]*fracpos;
        rs=src[ipos+1]*(1.0-fracpos) + src[ipos+3]*fracpos;
      }
      else
      {
        rs=ls=src[ipos]*(1.0-fracpos) + src[ipos+1]*fracpos;
      }
      rspos+=drspos;

    }
    else
    {
      if (src_nch == 2)
      {
        int t=x+x;
        ls=src[t];
        rs=src[t+1];
      }
      else
      {
        rs=ls=src[x];
      }
    }

    ls *= vol1;
    if (ls > 1.0) ls=1.0;
    else if (ls<-1.0) ls=-1.0;

    *dest1++ +=(float) ls;

    if (dest_nch > 1)
    {
      rs *= vol2;
      if (rs > 1.0) rs=1.0;
      else if (rs<-1.0) rs=-1.0;

      *dest2++ += (float) rs;
    }
  }
  *state = rspos - (int)rspos;
}

#endif //_PCMFMTCVT_H_


code:
/*
    WDL - ptrlist.h
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file provides a simple templated class for a list of pointers. Nothing fancy, and it
  utilizes an internal WDL_HeapBuf for storage.

*/

#ifndef _WDL_PTRLIST_H_
#define _WDL_PTRLIST_H_

#include "heapbuf.h"

template<class PTRTYPE> class WDL_PtrList
{
  public:
    WDL_PtrList()
    {
    }
    ~WDL_PtrList()
    {
    }

    PTRTYPE **GetList() { return (PTRTYPE**)m_hb.Get(); }
    PTRTYPE *Get(int index) { if (GetList() && index >= 0 && index < GetSize()) return GetList()[index]; return NULL; }
    int GetSize(void) { return m_hb.GetSize()/sizeof(PTRTYPE *); }

    int Find(PTRTYPE *p)
    {
      int x;
      if (p) for (x = 0; x < GetSize(); x ++) if (Get(x) == p) return x;
      return -1;
    }

    PTRTYPE *Add(PTRTYPE *item)
    {
      int s=GetSize();
      m_hb.Resize((s+1)*sizeof(PTRTYPE*));
      return Set(s,item);
    }

    PTRTYPE *Set(int index, PTRTYPE *item)
    {
      if (index >= 0 && index < GetSize() && GetList()) return GetList()[index]=item;
      return NULL;
    }

    PTRTYPE *Insert(int index, PTRTYPE *item)
    {
      int s=GetSize();
      m_hb.Resize((s+1)*sizeof(PTRTYPE*));

      if (index<0) index=0;
      else if (index > s) index=s;

      int x;
      for (x = s; x > index; x --)
      {
        GetList()[x]=GetList()[x-1];
      }
      return (GetList()[x] = item);
    }

    void Delete(int index, bool wantDelete=false, void (*delfunc)(void *)=NULL)
    {
      PTRTYPE **list=GetList();
      int size=GetSize();
      if (list && index >= 0 && index < size)
      {
        if (wantDelete)
        {
          if (delfunc) delfunc(Get(index));
          else delete Get(index);
        }
        if (index < --size) memcpy(list+index,list+index+1,sizeof(PTRTYPE *)*(size-index));
        m_hb.Resize(size * sizeof(PTRTYPE*));
      }
    }
    void Empty(bool wantDelete=false, void (*delfunc)(void *)=NULL)
    {
      if (wantDelete)
      {
        int x;
        for (x = GetSize()-1; x >= 0; x --)
        {
          if (delfunc) delfunc(Get(x));
          else delete Get(x);
          m_hb.Resize(x*sizeof(PTRTYPE *),false);
        }
      }
      m_hb.Resize(0);
    }

  private:
    WDL_HeapBuf m_hb;
};

#endif


code:
/*
    WDL - queue.h
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file provides a simple class for a FIFO queue of bytes. It uses a simple buffer,
  so should not generally be used for large quantities of data (it can advance the queue
  pointer, but Compact() needs to be called regularly to keep memory usage down, and when
  it is called, there's a memcpy() penalty for the remaining data. oh well, is what it is).

*/

#ifndef _WDL_QUEUE_H_
#define _WDL_QUEUE_H_

#include "heapbuf.h"

class WDL_Queue
{
public:
  WDL_Queue() : m_pos(0)
  {
  }
  ~WDL_Queue()
  {
  }

  void *Add(const void *buf, int len)
  {
    int olen=m_hb.GetSize();
    void *obuf=m_hb.Resize(olen+len,false);
    if (!obuf) return 0;
    if (buf) memcpy((char*)obuf+olen,buf,len);
    return (char*)obuf+olen;
  }

  int GetSize()
  {
    return m_hb.GetSize()-m_pos;
  }

  void *Get()
  {
    void *buf=m_hb.Get();
    if (buf && m_pos >= 0 && m_pos < m_hb.GetSize()) return (char *)buf+m_pos;
    return NULL;
  }

  int Available()
  {
    return m_hb.GetSize() - m_pos;
  }

  void Clear()
  {
    m_pos=0;
    m_hb.Resize(0,false);
  }

  void Advance(int bytecnt)
  {
    m_pos+=bytecnt;
  }

  void Compact(bool allocdown=false)
  {
    if (m_pos > 0)
    {
      int olen=m_hb.GetSize();
      if (m_pos < olen)
      {
        void *a=m_hb.Get();
        if (a) memmove(a,(char*)a+m_pos,olen-m_pos);
        m_hb.Resize(olen-m_pos,allocdown);
      }
      else m_hb.Resize(0,allocdown);
      m_pos=0;
    }
  }

  void SetGranul(int granul)
  {
    m_hb.SetGranul(granul);
  }

private:
  WDL_HeapBuf m_hb;
  int m_pos;
};

#endif


code:
/*
    WDL - rng.cpp
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file provides the implementation of a decent random number generator,
  that internally uses a 256-bit state, and SHA-1 to iterate.

*/

#ifdef _WIN32
#include <windows.h>
#else
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#ifndef min
#define min(x,y) ((x)<(y)?(x):(y))
#endif
#endif

#include "rng.h"
#include "sha.h"

static unsigned char state[32];

void WDL_RNG_addentropy(void *buf, int buflen)
{
  WDL_SHA1 tmp;
  tmp.add(state,sizeof(state));
  tmp.result(state);
  tmp.reset();
  tmp.add((unsigned char *)buf,buflen);
  tmp.result(state+sizeof(state) - WDL_SHA1SIZE);
}

static void rngcycle()
{
  int i;
  for (i = 0; i < (int)sizeof(state) && state[i]++; i++);
}

int WDL_RNG_int32()
{
  WDL_SHA1 tmp;
  tmp.add(state,sizeof(state));
  rngcycle();
  char buf[WDL_SHA1SIZE];
  tmp.result(buf);
  return *(int *)buf;

}

void WDL_RNG_bytes(void *buf, int buflen)
{
  char *b=(char *)buf;
  while (buflen > 0)
  {
    char tb[WDL_SHA1SIZE];
    WDL_SHA1 tmp;
    tmp.add(state,sizeof(state));
    rngcycle();

    tmp.result(tb);
    int l=min(buflen,WDL_SHA1SIZE);
    memcpy(b,tb,l);
    buflen-=l;
    b+=l;
  }
}


code:
/*
    WDL - rng.h
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This header provides the interface to a decent random number generator,
  that internally uses a 256-bit state, and SHA-1 to iterate. We wouldn't consider
  this RNG to be cryptographically secure, but it may be decent.

*/

#ifndef _WDL_RNG_H_
#define _WDL_RNG_H_

void WDL_RNG_addentropy(void *buf, int buflen); // add entropy to the RNG

int WDL_RNG_int32(); // get a random integer
void WDL_RNG_bytes(void *buf, int buflen); // get a random string of bytes

#endif


code:
/*
    WDL - sha.cpp
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file provides the implementation to the WDL_SHA1 object, which performs SHA-1 hashes
  on data.

*/

#include "sha.h"

/// sha

WDL_SHA1::WDL_SHA1()
{
  reset();
}

void WDL_SHA1::reset()
{
  lenW = 0;
  size[0] = size[1] = 0;
  H[0] = 0x67452301L;
  H[1] = 0xefcdab89L;
  H[2] = 0x98badcfeL;
  H[3] = 0x10325476L;
  H[4] = 0xc3d2e1f0L;
  int x;
  for (x = 0; x < sizeof(W)/sizeof(W[0]); x ++) W[x]=0;
}

#define SHA_ROTL(X,n) ((((X)&0xffffffffL) << (n)) | (((X)&0xffffffffL) >> (32-(n))))
#define SHUFFLE() E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP

void WDL_SHA1::add(const void *data, int datalen)
{
  int i;
  for (i = 0; i < datalen; i++)
  {
    W[lenW / 4] <<= 8;
    W[lenW / 4] |= (unsigned long)((const unsigned char *)data)[i];
    if (!(++lenW & 63))
    {
      int t;

      unsigned long A = H[0];
      unsigned long B = H[1];
      unsigned long C = H[2];
      unsigned long D = H[3];
      unsigned long E = H[4];

      for (t = 16; t < 80; t++) W[t] = SHA_ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);

      for (t = 0; t < 20; t++)
      {
        unsigned long TEMP = SHA_ROTL(A,5) + E + W[t] + 0x5a827999L + (((C^D)&B)^D);
        SHUFFLE();
      }
      for (; t < 40; t++)
      {
        unsigned long TEMP = SHA_ROTL(A,5) + E + W[t] + 0x6ed9eba1L + (B^C^D);
        SHUFFLE();
      }
      for (; t < 60; t++)
      {
        unsigned long TEMP = SHA_ROTL(A,5) + E + W[t] + 0x8f1bbcdcL + ((B&C)|(D&(B|C)));
        SHUFFLE();
      }
      for (; t < 80; t++)
      {
        unsigned long TEMP = SHA_ROTL(A,5) + E + W[t] + 0xca62c1d6L + (B^C^D);
        SHUFFLE();
      }

      H[0] += A;
      H[1] += B;
      H[2] += C;
      H[3] += D;
      H[4] += E;

      lenW = 0;
    }
    size[0] += 8;
    if (size[0] < 8) size[1]++;
  }
}

void WDL_SHA1::result(void *out)
{
  unsigned char pad0x80 = 0x80;
  unsigned char pad0x00 = 0x00;
  unsigned char padlen[8];
  int i;
  padlen[0] = (unsigned char)((size[1] >> 24) & 0xff);
  padlen[1] = (unsigned char)((size[1] >> 16) & 0xff);
  padlen[2] = (unsigned char)((size[1] >> 8) & 0xff);
  padlen[3] = (unsigned char)((size[1]) & 0xff);
  padlen[4] = (unsigned char)((size[0] >> 24) & 0xff);
  padlen[5] = (unsigned char)((size[0] >> 16) & 0xff);
  padlen[6] = (unsigned char)((size[0] >> 8) & 0xff);
  padlen[7] = (unsigned char)((size[0]) & 0xff);
  add(&pad0x80, 1);
  while (lenW != 56) add(&pad0x00, 1);
  add(padlen, 8);
  for (i = 0; i < 20; i++)
  {
    ((unsigned char *)out)[i] = (unsigned char)(H[i / 4] >> 24);
    H[i / 4] <<= 8;
  }
  reset();
}


code:
/*
    WDL - sha.h
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This header provides an interface to the WDL_SHA1 object, which performs SHA-1 hashes
  on data.
*/

#ifndef _WDL_SHA_H_
#define _WDL_SHA_H_

#define WDL_SHA1SIZE 20

// sha

class WDL_SHA1 {

public:
  WDL_SHA1();
  void add(const void *data, int datalen);
  void result(void *out);
  void reset();

private:

  unsigned long H[5];
  unsigned long W[80];
  int lenW;
  unsigned long size[2];
};

#endif


code:
/*
    WDL - string.h
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file provides a simple class for variable-length string manipulation.
  It provides only the simplest features, and does not do anything confusing like
  operator overloading. It uses a WDL_HeapBuf for internal storage.

*/

#ifndef _WDL_STRING_H_
#define _WDL_STRING_H_

#include "heapbuf.h"

#ifndef WDL_STRING_IMPL_ONLY
class WDL_String
{
public:
  WDL_String(const char *initial=NULL, int initial_len=0)
  {
    if (initial) Set(initial,initial_len);
  }
  WDL_String(WDL_String &s)
  {
    Set(s.Get());
  }
  WDL_String(WDL_String *s)
  {
    if (s && s != this) Set(s->Get());
  }
  ~WDL_String()
  {
  }
#define WDL_STRING_PREFIX
#else
#define WDL_STRING_PREFIX WDL_String::
#endif

  void WDL_STRING_PREFIX Set(const char *str, int maxlen
#ifdef WDL_STRING_INTF_ONLY
      =0);
#else
#ifdef WDL_STRING_IMPL_ONLY
    )
#else
    =0)
#endif
  {
    int s=strlen(str);
    if (maxlen && s > maxlen) s=maxlen;

    char *newbuf=(char*)m_hb.Resize(s+1);
    if (newbuf)
    {
      memcpy(newbuf,str,s);
      newbuf[s]=0;
    }
  }
#endif

  void WDL_STRING_PREFIX Append(const char *str, int maxlen
#ifdef WDL_STRING_INTF_ONLY
      =0);
#else
#ifdef WDL_STRING_IMPL_ONLY
    )
#else
    =0)
#endif
  {
    int s=strlen(str);
    if (maxlen && s > maxlen) s=maxlen;

    int olds=strlen(Get());

    char *newbuf=(char*)m_hb.Resize(olds + s + 1);
    if (newbuf)
    {
      memcpy(newbuf + olds, str, s);
      newbuf[olds+s]=0;
    }
  }
#endif

  void WDL_STRING_PREFIX DeleteSub(int position, int len)
#ifdef WDL_STRING_INTF_ONLY
    ;
#else
    {
	  char *p=Get();
	  if (!p || !*p) return;

	  int l=strlen(p);
	  if (position < 0 || position >= l) return;
	  if (position+len > l) len=l-position;
	  memmove(p+position,p+position+len,l-position-len+1); // +1 for null
  }
#endif

  void WDL_STRING_PREFIX Insert(const char *str, int position, int maxlen
#ifdef WDL_STRING_INTF_ONLY
      =0);
#else
#ifdef WDL_STRING_IMPL_ONLY
    )
#else
    =0)
#endif
  {
	  int sl=strlen(Get());
	  if (position > sl) position=sl;

	  int ilen=strlen(str);
	  if (maxlen > 0 && maxlen < ilen) ilen=maxlen;

	  Append(str);
	  char *cur=Get();

      	  memmove(cur+position+ilen,cur+position,sl-position);
	  memcpy(cur+position,str,ilen);
	  cur[sl+ilen]=0;
  }
#endif

  void WDL_STRING_PREFIX SetLen(int length) // sets number of chars allocated for string, not including null terminator
#ifdef WDL_STRING_INTF_ONLY
    ;
#else
  {                       // can use to resize down, too, or resize up for a sprintf() etc
    char *b=(char*)m_hb.Resize(length+1);
    if (b) b[length]=0;
  }
#endif

#ifndef WDL_STRING_IMPL_ONLY
  char *Get()
  {
    if (m_hb.Get()) return (char *)m_hb.Get();
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
    return "";
#pragma GCC diagnostic pop
  }

  private:
    WDL_HeapBuf m_hb;
};
#endif

#endif


code:
/*
    WDL - vorbisencdec.h
    Copyright (C) 2005 Cockos Incorporated

    WDL is dual-licensed. You may modify and/or distribute WDL under either of
    the following  licenses:

      This software is provided 'as-is', without any express or implied
      warranty.  In no event will the authors be held liable for any damages
      arising from the use of this software.

      Permission is granted to anyone to use this software for any purpose,
      including commercial applications, and to alter it and redistribute it
      freely, subject to the following restrictions:

      1. The origin of this software must not be misrepresented; you must not
         claim that you wrote the original software. If you use this software
         in a product, an acknowledgment in the product documentation would be
         appreciated but is not required.
      2. Altered source versions must be plainly marked as such, and must not be
         misrepresented as being the original software.
      3. This notice may not be removed or altered from any source distribution.

    or:

      WDL is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      WDL is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with WDL; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file provides simple interfaces for encoding and decoding of OGG Vorbis data.
  It is a wrapper around what their SDKs expose, which is not too easy to use.

  This stuff is pretty limited and simple, but works, usually.

*/

#ifndef _VORBISENCDEC_H_
#define _VORBISENCDEC_H_

#include "vorbis/vorbisenc.h"
#include "vorbis/codec.h"
#include "queue.h"

class VorbisDecoder
{
  public:
    VorbisDecoder()
    {
      m_samples_used=0;
    	packets=0;
	    memset(&oy,0,sizeof(oy));
	    memset(&os,0,sizeof(os));
	    memset(&og,0,sizeof(og));
	    memset(&op,0,sizeof(op));
	    memset(&vi,0,sizeof(vi));
	    memset(&vc,0,sizeof(vc));
	    memset(&vd,0,sizeof(vd));
	    memset(&vb,0,sizeof(vb));

      ogg_sync_init(&oy); /* Now we can read pages */
      m_err=0;
    }
    ~VorbisDecoder()
    {
      ogg_stream_clear(&os);
      vorbis_block_clear(&vb);
      vorbis_dsp_clear(&vd);
	    vorbis_comment_clear(&vc);
      vorbis_info_clear(&vi);

  	  ogg_sync_clear(&oy);
    }

    int GetSampleRate() { return vi.rate; }
    int GetNumChannels() { return vi.channels?vi.channels:1; }

    WDL_HeapBuf m_samples; // we let the size get as big as it needs to, so we don't worry about tons of mallocs/etc
    int m_samples_used;

    void *DecodeGetSrcBuffer(int srclen)
    {
		  return ogg_sync_buffer(&oy,srclen);
    }

    void DecodeWrote(int srclen)
    {
      ogg_sync_wrote(&oy,srclen);

		  while(ogg_sync_pageout(&oy,&og)>0)
		  {
			  int serial=ogg_page_serialno(&og);
			  if (!packets) ogg_stream_init(&os,serial);
			  else if (serial!=os.serialno)
			  {
				  vorbis_block_clear(&vb);
				  vorbis_dsp_clear(&vd);
				  vorbis_comment_clear(&vc);
				  vorbis_info_clear(&vi);

				  ogg_stream_clear(&os);
				  ogg_stream_init(&os,serial);
				  packets=0;
			  }
			  if (!packets)
			  {
				  vorbis_info_init(&vi);
				  vorbis_comment_init(&vc);
			  }
			  ogg_stream_pagein(&os,&og);
			  while(ogg_stream_packetout(&os,&op)>0)
			  {
				  if (packets<3)
				  {
					  if(vorbis_synthesis_headerin(&vi,&vc,&op)<0) return;
				  }
				  else
				  {
					  float ** pcm;
					  int samples;
					  if(vorbis_synthesis(&vb,&op)==0) vorbis_synthesis_blockin(&vd,&vb);
					  while((samples=vorbis_synthesis_pcmout(&vd,&pcm))>0)
					  {
						  int n,c;

              int newsize=(m_samples_used+(samples+4096)*vi.channels)*sizeof(float);

              if (m_samples.GetSize() < newsize) m_samples.Resize(newsize+32768);

              float *bufmem = (float *)m_samples.Get();

						  for(n=0;n<samples;n++)
						  {
							  for(c=0;c<vi.channels;c++)
							  {
								  bufmem[m_samples_used++]=pcm[c][n];
							  }
						  }
						  vorbis_synthesis_read(&vd,samples);
					  }
				  }
				  packets++;
				  if (packets==3)
				  {
					  vorbis_synthesis_init(&vd,&vi);
					  vorbis_block_init(&vd,&vb);
				  }
			  }
		  }
    }

    void Reset()
    {
      m_samples_used=0;

			vorbis_block_clear(&vb);
			vorbis_dsp_clear(&vd);
			vorbis_comment_clear(&vc);
			vorbis_info_clear(&vi);

			ogg_stream_clear(&os);
			packets=0;
    }

  private:

    int m_err;
    int packets;

    ogg_sync_state   oy; /* sync and verify incoming physical bitstream */
    ogg_stream_state os; /* take physical pages, weld into a logical
			    stream of packets */
    ogg_page         og; /* one Ogg bitstream page.  Vorbis packets are inside */
    ogg_packet       op; /* one raw packet of data for decode */

    vorbis_info      vi; /* struct that stores all the static vorbis bitstream
			    settings */
    vorbis_comment   vc; /* struct that stores all the bitstream user comments */
    vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
    vorbis_block     vb; /* local working space for packet->PCM decode */

};

class VorbisEncoder
{
public:
  VorbisEncoder(int srate, int nch, int bitrate, int serno)
  {
    m_ds=0;

    memset(&vi,0,sizeof(vi));
    memset(&vc,0,sizeof(vc));
    memset(&vd,0,sizeof(vd));
    memset(&vb,0,sizeof(vb));

    m_nch=nch;
    vorbis_info_init(&vi);

    float qv=0.0;
    if (nch == 2) bitrate=  (bitrate*5)/8;
    // at least for mono 44khz
    //-0.1 = ~40kbps
    //0.0 == ~64kbps
    //0.1 == 75
    //0.3 == 95
    //0.5 == 110
    //0.75== 140
    //1.0 == 240
    if (bitrate <= 32)
    {
      m_ds=1;
      bitrate*=2;
    }

    if (bitrate < 40) qv=-0.1f;
    else if (bitrate < 64) qv=-0.10f + (bitrate-40)*(0.10f/24.0f);
    else if (bitrate < 75) qv=(bitrate-64)*(0.1f/9.0f);
    else if (bitrate < 95) qv=0.1f+(bitrate-75)*(0.2f/20.0f);
    else if (bitrate < 110) qv=0.3f+(bitrate-95)*(0.2f/15.0f);
    else if (bitrate < 140) qv=0.5f+(bitrate-110)*(0.25f/30.0f);
    else qv=0.75f+(bitrate-140)*(0.25f/100.0f);

    if (qv<-0.10f)qv=-0.10f;
    if (qv>1.0f)qv=1.0f;

    m_err=vorbis_encode_init_vbr(&vi,nch,srate>>m_ds,qv);

    vorbis_comment_init(&vc);
    vorbis_analysis_init(&vd,&vi);
    vorbis_block_init(&vd,&vb);
    ogg_stream_init(&os,m_ser=serno);

    if (m_err) return;

    reinit(1);
  }

  void reinit(int bla=0)
  {
    if (!bla)
    {
      ogg_stream_clear(&os);
      vorbis_block_clear(&vb);
      vorbis_dsp_clear(&vd);

      vorbis_analysis_init(&vd,&vi);
      vorbis_block_init(&vd,&vb);
      ogg_stream_init(&os,m_ser++); //++?

      outqueue.Advance(outqueue.Available());
      outqueue.Compact();
    }

    ogg_packet header;
    ogg_packet header_comm;
    ogg_packet header_code;
    vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
    ogg_stream_packetin(&os,&header); /* automatically placed in its own page */
    ogg_stream_packetin(&os,&header_comm);
    ogg_stream_packetin(&os,&header_code);

	  for (;;)
    {
      ogg_page og;
		  int result=ogg_stream_flush(&os,&og);
		  if(result==0)break;
      outqueue.Add(og.header,og.header_len);
		  outqueue.Add(og.body,og.body_len);
	  }
  }

  void Encode(float *in, int inlen, int advance=1, int spacing=1) // length in sample (PAIRS)
  {
    if (m_err) return;

    if (inlen == 0)
    {
      // disable this for now, it fucks us sometimes
      // maybe we should throw some silence in instead?
        vorbis_analysis_wrote(&vd,0);
    }
    else
    {
      inlen >>= m_ds;
      float **buffer=vorbis_analysis_buffer(&vd,inlen);
      int i,i2=0;
      for (i = 0; i < inlen; i ++)
      {
        buffer[0][i]=in[i2];
        if (m_nch==2) buffer[1][i]=in[i2+spacing];
        i2+=advance<<m_ds;
      }
      vorbis_analysis_wrote(&vd,i);
    }

    int eos=0;
    while(vorbis_analysis_blockout(&vd,&vb)==1)
    {
      vorbis_analysis(&vb,NULL);
      vorbis_bitrate_addblock(&vb);
      ogg_packet       op;

      while(vorbis_bitrate_flushpacket(&vd,&op))
      {

      	ogg_stream_packetin(&os,&op);

	      while (!eos)
        {
          ogg_page og;
		      int result=ogg_stream_flush(&os,&og);
		      if(result==0)break;
          outqueue.Add(og.header,og.header_len);
		      outqueue.Add(og.body,og.body_len);
          if(ogg_page_eos(&og)) eos=1;
	      }
      }
    }
  }

  int isError() { return m_err; }

  ~VorbisEncoder()
  {
    ogg_stream_clear(&os);
    vorbis_block_clear(&vb);
    vorbis_dsp_clear(&vd);
    vorbis_comment_clear(&vc);
    if (!m_err) vorbis_info_clear(&vi);
  }

  WDL_Queue outqueue;

private:
  int m_err,m_nch;

  ogg_stream_state os;
  vorbis_info      vi;
  vorbis_comment   vc;
  vorbis_dsp_state vd;
  vorbis_block     vb;
  int m_ser;
  int m_ds;

};

#endif//_VORBISENCDEC_H_