// This may look like C code, but it is really -*- C++ -*-
// 
// <copyright> 
//  
//  Copyright (c) 1995
//  Institute for Information Processing and Computer Supported New Media (IICM), 
//  Graz University of Technology, Austria. 
//  
// </copyright> 
// 
// 
// <file> 
// 
// Name:        httpresponse.C
// 
// Purpose:     
// 
// Created:     22 Feb 96   Joerg Faschingbauer
// 
// Modified:    
// 
// Description: 
// 
// $Id: httpresponse.C,v 1.9 1997/02/03 14:26:16 jfasch Exp $
// 
// $Log: httpresponse.C,v $
// Revision 1.9  1997/02/03 14:26:16  jfasch
// shortcut for finished entry
//
// Revision 1.8  1996/10/24 09:18:19  jfasch
// verbose.h and assert.h and new.h, ERROR was a macro under NT
//
// Revision 1.7  1996/05/23 14:54:23  tvollmer
// removed old tmpfield
//
// Revision 1.6  1996/05/03 07:59:01  jfasch
// added some debug output
//
// Revision 1.5  1996/04/11 10:12:48  jfasch
// added version control
//
// Revision 1.4  1996/03/04 08:41:33  tvollmer
// *** empty log message ***
//
// Revision 1.3  1996/02/28 12:31:17  jfasch
// intermediary
//
// Revision 1.2  1996/02/27 10:32:11  jfasch
// moved to common/http library
//
// Revision 1.1  1996/02/22 17:12:05  jfasch
// Initial revision
//
// 
// </file> 
#include "httpresponse.h"

#include "http.h"

#include <hyperg/utils/assert.h>
#include <hyperg/utils/new.h>

#include <strstream.h>



#define VERBOSE
#include <hyperg/utils/verbose.h>



// --------------------------------------------------------------------
const char* HTTPResponse :: version2 = "$Id: httpresponse.C,v 1.9 1997/02/03 14:26:16 jfasch Exp $" ;

HTTPResponse :: HTTPResponse() 
: state_(INIT),
  tmp_(nil) {}

HTTPResponse :: HTTPResponse(const RString& version,const RString& code, const RString& reason) 
: version_(version),code_(code),reason_(reason),state_(INIT),
  tmp_(nil) {}

HTTPResponse :: HTTPResponse (const HTTPResponse& r) 
: state_(INIT),
  tmp_(nil) {
     operator = (r) ;
  }

HTTPResponse :: ~HTTPResponse() {
   HGDELETE (tmp_) ;
}

HTTPResponse& HTTPResponse :: operator = (const HTTPResponse& r) {
   hgassert (!tmp_, "HTTPResponse::operator=(const HTTPResponse&): caught me during parsing") ;
   version_ = r.version_ ;
   code_ = r.code_ ;
   reason_ = r.reason_ ;
   hdr_ = r.hdr_ ;
   state_ = r.state_ ;
   return *this ;
}



int HTTPResponse :: add (const char* s, int l) {
   int accepted = 0 ;
   while (ok() && !complete() && l--) {
      accepted += add(*s++)? 1: 0 ;
   }
   return accepted ;
}

bool HTTPResponse :: add (char c) {
   switch (state_) {
     case INIT:
        if (tolower(c) == 'h') {
           state_ = H ;
           if (! tmp_)
              tmp_ = HGNEW (TmpField) ;
           *tmp_ << 'H' ;
        }
        else 
           state_ = HTTPERROR ;
        break ;
     case H:
        if (tolower(c) == 't') {
           state_ = T1 ;
           *tmp_ << 'T' ;
        }
        else 
           state_ = HTTPERROR ;
        break ;
     case T1:
        if (tolower(c) == 't') {
           state_ = T2 ;
           *tmp_ << 'T' ;
        }
        else 
           state_ = HTTPERROR ;
        break ;
     case T2:
        if (tolower(c) == 'p') {
           state_ = P ;
           *tmp_ << 'P' ;
        }
        else 
           state_ = HTTPERROR ;
        break ;
     case P:
        if (c == '/') {
           state_ = SLASH ;
           *tmp_ << c ;
        }
        else 
           state_ = HTTPERROR ;
        break ;
     case SLASH:
        if (! (HTTP::isCR(c) || HTTP::isLF(c) || HTTP::isSPorHT(c))) {
           state_ = VER ;
           *tmp_ << c ;
        }
        else 
           state_ = HTTPERROR ;
        break ;
     case VER:
        if (! (HTTP::isCR(c) || HTTP::isLF(c) || HTTP::isSPorHT(c)))
           *tmp_ << c ;
        else if (HTTP::isSPorHT(c)) {
           if (tmp_->end (version_))
              state_ = WS1 ;
           else
              state_ = HTTPERROR ;
        }
        else
           state_ = HTTPERROR ;
        break ;
     case WS1:
        if (HTTP::isDIGIT(c)) {
           state_ = C1 ;
           *tmp_ << c ;
        }
        else if (! HTTP::isSPorHT(c))
           state_ = HTTPERROR ;
        break ;
     case C1:
        if (HTTP::isDIGIT(c)) {
           state_ = C2 ;
           *tmp_ << c ;
        }
        else 
           state_ = HTTPERROR ;
        break ;
     case C2:
        if (HTTP::isDIGIT(c)) {
           state_ = C3 ;
           *tmp_ << c ;
        }
        else 
           state_ = HTTPERROR ;
        break ;
     case C3:
        if (HTTP::isSPorHT(c)) {
           if (tmp_->end (code_))
              state_ = WS2 ;
           else 
              state_ = HTTPERROR ;
        }
        else if (HTTP::isCR(c)) {
           if (tmp_->end (code_))
              state_ = CR ;
           else
              state_ = HTTPERROR ;
        }
        else if (HTTP::isLF(c)) {
           if (tmp_->end (code_))
              state_ = HDR ;
           else 
              state_ = HTTPERROR ;
        }
        else 
           state_ = HTTPERROR ;
        break ;
     case WS2:
        if (! (HTTP::isCR(c) || HTTP::isLF(c) || HTTP::isSPorHT(c))) {
           state_ = REASON ;
           *tmp_ << c ;
        }
        else if (HTTP::isCR(c))
           state_ = CR ;
        else if (HTTP::isLF(c))
           state_ = HDR ;
        else
           state_ = HTTPERROR ;
        break ;
     case REASON:
        if (! (HTTP::isCR(c) || HTTP::isLF(c)))
           *tmp_ << c ;
        else if (HTTP::isCR(c)) {
           if (tmp_->end (reason_))
              state_ = CR ;
           else 
              state_ = HTTPERROR ;
        }
        else if (HTTP::isLF(c)) {
           if (tmp_->end (reason_))
              state_ = HDR ;
           else
              state_ = HTTPERROR ;
        }
        else
           state_ = HTTPERROR ;
        break ;
     case CR:
        if (HTTP::isLF(c))
           state_ = HDR ;
        else
           state_ = HTTPERROR ;
        break ;
     case HDR:
        hdr_.add (c) ;
        if (hdr_.complete()) {
           DEBUGNL ("HTTPResponse::add(char) (HDR): header complete") ;
           state_ = TERM ;
        }
        else if (! hdr_.ok()) {
           DEBUGNL ("HTTPResponse::add(char) (HDR): header not ok") ;
           state_ = HTTPERROR ;
        }
        // else keep this state and wait for more data
        break ;
     default:
        hgassert (false, "HTTPResponse::add(char): invalid state: "<<state_) ;
   }
   return state_ != HTTPERROR ;
}


int HTTPResponse::length(const RString& sep1, const RString&  sep2, const RString& ret1,
               const RString& sep3,const RString& ret2, const RString& end) const
{
    return hdr().lines().length(sep3,ret2,end)+sep1.length()+sep2.length()+ret1.length()+
        version_.length()+code_.length()+reason_.length();
        

}
int HTTPResponse::write(char* p,const RString& sep1, const RString&  sep2, const RString& ret1,
               const RString& sep3,const RString& ret2, const RString& end) const
{
    const char* x=p;
    int len;
    len=version_.length();
    memcpy(p,version_.string(),len);
    p+=len;
    len=sep1.length();
    memcpy(p,sep1.string(),len);
    p+=len;

    len=code_.length();
    memcpy(p,code_.string(),len);
    p+=len;
    len=sep2.length();
    memcpy(p,sep2.string(),len);
    p+=len;

    len=reason_.length();
    memcpy(p,reason_.string(),len);
    p+=len;
    len=ret1.length();
    memcpy(p,ret1.string(),len);
    p+=len;

    p+=hdr().lines().write(p,sep3,ret2,end);
    return p-x;
}

RString HTTPResponse::all(const RString& sep1, const RString&  sep2, const RString& ret1,
               const RString& sep3,const RString& ret2, const RString& end) const
{
    int len=length(sep1,sep2,ret1,sep3,ret2,end);
    char* tmp = HGNEW (char[len]) ;
    int x=write(tmp,sep1,sep2,ret1,sep3,ret2,end);
    hgassert(x==len,"Shit happens");
    RString rstr (tmp, len) ;
    HGDELETE_ARR (tmp) ;
    return rstr ;
}








