//<copyright>
//
// Copyright (c) 1996
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
//
//</copyright>

//<file>
//
// Name:    http.C
//
// Purpose: http specs tools
//
// Created: 7 Mar 1996 Till Vollmer
//
// $Id: http.C,v 1.6 1996/10/24 10:41:07 jfasch Exp $
//
// Description:
//   
//</file>
//
// $Log: http.C,v $
// Revision 1.6  1996/10/24 10:41:07  jfasch
// WIN32 shit
//
// Revision 1.5  1996/10/24 09:18:19  jfasch
// verbose.h and assert.h and new.h, ERROR was a macro under NT
//
// Revision 1.4  1996/07/31 14:32:24  jfasch
// removed some unused parameters
//
// Revision 1.3  1996/07/24 06:24:58  jfasch
// added HTTP::Date class
//
// Revision 1.2  1996/04/12 12:22:58  jfasch
// *** empty log message ***
//
// Revision 1.1  1996/04/09 15:24:07  tvollmer
// Initial revision
//
//

#include "http.h"

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

#include <stdio.h>
#include <time.h>
#include <string.h>

#ifdef WIN32
#define strncasecmp _strnicmp
#endif



RString HTTP::httpDate(time_t secs) 
{
  static char* weekday[] = 
  {
    "Sun",
    "Mon",
    "Tue",
    "Wed",
    "Thu",
    "Fri",
    "Sat"
       };
  static char* month[] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

  RString result;
  static char timestr[50];
  
  struct tm* tm = ::gmtime(&secs);
  sprintf(timestr, "%s, %02d %s %04d %02d:%02d:%02d GMT", weekday[tm->tm_wday],
	    tm->tm_mday, month[tm->tm_mon], tm->tm_year+1900, tm->tm_hour, tm->tm_min,
	    tm->tm_sec);
  
  return RString(timestr);
}


int HTTP :: rfc1123 (const char* s, int l, time_t&) {
   // rfc1123-date = wkday "," SP date1 SP time SP "GMT"

   int tot_cons = 0 ;
   int cur_cons ;

   if (! (cur_cons = Date::wkday (s, l)))
      return 0 ;

   tot_cons += cur_cons ;
   if (*(s+tot_cons++) != ',')
      return 0 ;

   while (tot_cons < l  &&  isSP (*(s+tot_cons++))) ;
   if (tot_cons == l)
      return 0 ;

   if (! (cur_cons = Date::date1 (s+tot_cons, l-tot_cons)))
      return 0 ;

   tot_cons += cur_cons ;
   while (tot_cons < l  &&  isSP (*(s+tot_cons++))) ;
   if (tot_cons == l)
      return 0 ;
   
   if (! (cur_cons = Date::time (s+tot_cons, l-tot_cons)))
      return 0 ;

   tot_cons += cur_cons ;
   while (tot_cons < l  &&  isSP (*(s+tot_cons++))) ;
   if (tot_cons == l)
      return 0 ;

   if (! (tot_cons < l  &&  tolower (*(s+tot_cons++)) == 'g'))
      return 0 ;
   if (! (tot_cons < l  &&  tolower (*(s+tot_cons++)) == 'm'))
      return 0 ;
   if (! (tot_cons < l  &&  tolower (*(s+tot_cons++)) == 't'))
      return 0 ;
   
   return tot_cons ;
}

int HTTP::Date::wkday (const char* s, int l) {
   WkDay d ;
   if (l < 3)
      return 0 ;
   if (! strncasecmp (s, "mon", 3)) d = Mon ;
   else if (! strncasecmp (s, "tue", 3)) d = Tue ;
   else if (! strncasecmp (s, "wed", 3)) d = Wed ;
   else if (! strncasecmp (s, "thu", 3)) d = Thu ;
   else if (! strncasecmp (s, "fri", 3)) d = Fri ;
   else if (! strncasecmp (s, "sat", 3)) d = Sat ;
   else if (! strncasecmp (s, "sun", 3)) d = Sun ;
   else 
      return 0 ;
   return 3 ;
}

int HTTP::Date::date1 (const char* s, int l) {
   int tot_cons = 0 ;
   int cur_cons = 0 ;

   while (tot_cons < l  &&  isDIGIT (*(s+tot_cons++))) ;
   if (tot_cons == 0  ||  tot_cons == l)
      return 0 ;

   while (tot_cons < l  &&  isSP (*(s+tot_cons++))) ;
   if (tot_cons == l)
      return 0 ;

   if (! (cur_cons = Date::month (s+tot_cons, l-tot_cons)))
      return 0 ;

   tot_cons += cur_cons ;
   
   while (tot_cons < l  &&  isSP (*(s+tot_cons++))) ;
   if (tot_cons == l)
      return 0 ;

   if (l-tot_cons < 4)
      return 0 ;
   else
      return tot_cons + 4 ;
}

int HTTP::Date::time (const char* s, int l) {
   int tot_cons = 0 ;
   int cur_cons = 0 ;

   while (tot_cons < l  &&   isDIGIT(*(s+tot_cons++))) ;
   if (tot_cons == 0  ||  tot_cons==l  ||  tot_cons > 2)
      return 0 ;
   
   if (*(s+tot_cons++) != ':')
      return 0 ;

   if (tot_cons == l)
      return 0 ;

   cur_cons = tot_cons ;
   while (tot_cons < l  &&  isDIGIT(*(s+tot_cons++))) ;
   if (tot_cons == cur_cons  ||  tot_cons == l  ||  tot_cons > cur_cons+2)
      return 0 ;

   if (*(s+tot_cons++) != ':')
      return 0 ;

   if (tot_cons == l) 
      return 0 ;

   cur_cons = tot_cons ;
   while (tot_cons < l  &&  isDIGIT (*(s+tot_cons++))) ;
   if (tot_cons == cur_cons  ||  tot_cons == l  || tot_cons > cur_cons+2)
      return 0 ;

   return tot_cons ;
}

int HTTP::Date::month (const char* s, int l) {
   Month m ;
   if (l < 3)
      return 0 ;
   if (! strncasecmp (s, "jan", 3)) m = Jan ;
   else if (! strncasecmp (s, "feb", 3)) m = Feb ;
   else if (! strncasecmp (s, "mar", 3)) m = Mar ;
   else if (! strncasecmp (s, "apr", 3)) m = Apr ;
   else if (! strncasecmp (s, "may", 3)) m = May ;
   else if (! strncasecmp (s, "jun", 3)) m = Jun ;
   else if (! strncasecmp (s, "jul", 3)) m = Jul ;
   else if (! strncasecmp (s, "aug", 3)) m = Aug ;
   else if (! strncasecmp (s, "sep", 3)) m = Sep ;
   else if (! strncasecmp (s, "oct", 3)) m = Oct ;
   else if (! strncasecmp (s, "nov", 3)) m = Nov ;
   else if (! strncasecmp (s, "dec", 3)) m = Dec ;
   else 
      return 0 ;
   return 3 ;
}

int HTTP :: crlf (const char* s, int l) {
   if (! l)
      return -1 ;
   if (*s == '\n')
      return 1 ;
   if (*s == '\r') {
      if (! --l)
         return -1 ;
      s++ ;
      if (*s == '\n')
         return 2 ;
      else 
         return -1 ;
   }
   else 
      return -1 ;
}

int HTTP :: lws (const char* s, int l) {
   int n = crlf (s, l) ;
   if (n < 0)
      n = 0 ;

   int lorig = l ;
   s += n ;
   l -= n ;

   if (l && isSPorHT(*s)) {
      s++ ;
      l-- ;
   }
   else
      return -1 ;

   while (l && isSPorHT(*s)) {
      s++ ;
      l-- ;
   }
   return lorig - l ;
}

int HTTP :: token (const char* s, int l) {
   if (! (l && isTokenChar(*s)))
      return -1 ;
   int lorig = l ;
   s++ ;
   l-- ;
   while (l && isTokenChar(*s)) {
      s++ ;
      l-- ;
   }
   return lorig - l ;
}

int HTTP :: qdtext (const char* s, int l) {
   if (! l) 
      return -1 ;
   if (isCHAR(*s)) {
      if (*s == '"'  ||  isCTL(*s))
         return -1 ;
      else 
         return lws (s, l) ;
   }
   else
      return -1 ;
}

int HTTP :: quoted_string (const char* s, int l) {
   if (! (l  &&  *s == '"'))
      return -1 ;
   int lorig = l ;
   s++ ;
   l-- ;
   
   int n ;
   while (l > 0  &&  (n = qdtext (s, l)) > 0) {
      s += n ;
      l -= n ;
   }

   if (l <= 0  ||  *s != '"')
      return -1 ;
   
   l-- ;
   
   return lorig - l ;
}

int HTTP :: word (const char* s, int l) {
   int n = token (s, l) ;
   if (n >= 0)
      return n ;
   else 
      return quoted_string (s, l) ;
}

int HTTP :: list (int (*elem)(const char*, int),
                  int min, int max, const char* s, int l, RStringField* f) {
   hgassert (min<=max, "HTTP::list(): min > max") ;
   int lorig = l ;
   int nelems = 0 ;
   int n ;

   while (l  &&  (n = lws (s, l)) > 0) {
      s += n ;
      l -= n ;
   }

   if (l) {
      if ((n = (*elem) (s, l)) > 0) {
         if (f)
            f->append (RString(s, n)) ;
         s += n ;
         l -= n ;
         nelems++ ;

         while (l  &&  (n = lws (s, l)) > 0) {
            s += n ;
            l -= n ;
         }

         while (l) {
            if (*s == ',') {
               s++ ;
               l-- ;
               while (l  &&  (n = lws (s, l)) > 0) {
                  s += n ;
                  l -= n ;
               }
               if (l) {
                  if ((n = (*elem) (s, l)) > 0) {
                     if (f)
                        f->append (RString(s,n)) ;
                     s += n ;
                     l -= n ;
                     nelems++ ;
                  }
                  else {
                     // ignore that case since empty elements are allowed
                  }
                  while (l  &&  (n = lws (s, l)) > 0) {
                     s += n ;
                     l -= n ;
                  }
               }
               else
                  return -1 ;
            }
            else 
               break ;
         }
      }
   }

   if (nelems >= min  &&  nelems <= max)
      return lorig - l ;
   else {
      if (f)
         f->free() ;
      return -1 ;
   }
}
