/***************************************************************************
 *                                                                         *
 *   MULTPART.CPP                                                          *
 *                                                                         *
 *   Copyright (c) 1997      Galacticomm, Inc.    All rights reserved.     *
 *                                                                         *
 *   Multipart partitioner.                                                *
 *                                                                         *
 *                                                - RNStein  1/8/97        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include <stdcomp.h>
#define RWSTD_NO_BOOL
#include <vector>
#include "iterand.h"
#include <string>
using namespace std;
#include "multpart.h"

#ifndef _RWSTDDLL
#error Use of <string> requires compiler command line option -D_RWSTDDLL
#endif                             // avoids exceptions in string::string()
#pragma warn -par                  // supresses warning from memory.h line 208
#pragma warn -lvc                  // supresses warnings about temporaries
#pragma warn -aus                  // supresses warning fm algorith.h line 275

const string
multiPart::hdrCRLF("\r\n");        // terminator of inner header fields (lines)

multiPart::multiPart(              // construct a multiPart inquisition
racsit& mpbegin,                   //   \ outer body, eg from HTTP request
racsit& mpend,                     //   / as a stream (file or memory buffer)
string& boundary) :                //   boundary from outer Content-Type header
     _nparts(0),
     _status(MPOK)
{
     string bnd,bndlast;
     bnd.assign("--").append(boundary).append("\r\n");
     bndlast.assign("--").append(boundary).append("--\r\n");
     pieces.push_back(mpbegin);
     pieces.push_back(mpend);
     vector<racsit>::iterator drit=
          pieces.insert(pieces.end()-1,racsit(search(pieces.front(),
                                                     pieces.back(),
                                                     bndlast.begin(),
                                                     bndlast.end())));
     if (*drit == pieces.back()) {
          _status=MPNOCLOSE;
          return;
     }
     drit=pieces.insert(drit+1,racsit(*drit+bndlast.length()))-1;
     racsit newbnd(mpbegin);
     while ((newbnd=search(*(drit-1),*drit,bnd.begin(),bnd.end())) != *drit) {
          drit=pieces.insert(drit,newbnd)+1;
          drit=pieces.insert(drit,racsit(newbnd+bnd.length()))+1;
     }
     for (drit=pieces.begin()+2 ; drit != pieces.end()-2 ; drit+=2) {
          racsit ohdrchit(*drit);
          racsit hdrchit(mpbegin);
          headers.push_back(vector<racsRange>());
          while ((hdrchit=search(ohdrchit,
                                 *(drit+1),hdrCRLF.begin(),
                                           hdrCRLF.end())) != *(drit+1)) {
               if (hdrchit == ohdrchit) {
                    ohdrchit=hdrchit+hdrCRLF.length();
                    break;
               }
               headers.back().push_back(racsRange(ohdrchit,hdrchit));
               ohdrchit=hdrchit+hdrCRLF.length();
          }
          bodies.push_back(racsRange(ohdrchit,*(drit+1)-hdrCRLF.length()));
                                             // (ref RFC 1521, line 1815)
          _nparts++;
     }
}

MPSTATUS
multiPart::status() const          // status of partition effort
{
     return(_status);
}

INT
multiPart::nparts() const          // Number of parts
{
     return(_nparts);
}

string *                           //   ""=none, CALLER MUST DELETE!
multiPart::partHeaderLine(         // Get a header line from a part
int part,                          //   part index, 0 to nparts()-1
int line) const                    //   line within the part, 0 to #lines-1
{
     string *hdrline=new string("");
     if (0 <= part && part < nparts()
      && 0 <= line && line < headers[part].size()) {
          racsit hdrit(headers[part][line].begin);
          racsit hdret(headers[part][line].end);
//        hdrline->reserve(hdret-hdrit);               // why won't this
//        copy(hdrit,hdret,hdrline->begin());          // work?????
          for ( ; hdrit != hdret ; hdrit++) {
               hdrline->append(*hdrit);      // hdrline <-- line between CRLFs
          }
     }
     return(hdrline);
}

racsRange                          //   body part (within original outer range)
multiPart::partBody(               // get iterators for an inner body part
int part) const                    //   part index, 0 to nparts()-1
{
     if (0 <= part && part < nparts()) {
          return(bodies[part]);
     }
     else {
          racsit mpeof(pieces.back());
          return(racsRange(mpeof,mpeof));
     }
}

racsRange
multiPart::preamble() const        // stuff before 1st boundary
{                                  //   (RFC 1521 page 31)
     if (status() == MPOK) {
          return(racsRange(pieces.front(),*(pieces.begin()+1)));
     }
     else {
          return(racsRange(pieces.front(),pieces.back()));
     }
}

racsRange
multiPart::epilogue() const        // stuff after last boundary
{                                  //   (RFC 1521 page 31)
     if (status() == MPOK) {
          return(racsRange(*(pieces.end()-2),pieces.back()));
     }
     else {
          return(racsRange(pieces.back(),pieces.back()));
     }
}

ostream& EXPORT
operator<<(                        // output partition report for debugging
ostream& out,                      //   sequential text stream
const multiPart& mp)               //   partitioned multiPart stream
{
     if (mp.status() == MPNOCLOSE) {
          out << "Request has no closing boundary" << endl;
     }
     else {
          out << "<P> All boundaries but last are " << mp.pieces[2]-mp.pieces[1]
              << " bytes long:  <TT>"
              << racsRange(mp.pieces[1],mp.pieces[2]) << "</TT>" << endl;
          out << "<UL>";
          if (mp.preamble().length() != 0) {
               out << "<LI> Preamble is " << mp.preamble().length()
                   << " bytes long" << endl;
          }
          for (INT partno=0 ; partno < mp.nparts() ; partno++) {
               out << "<LI> Part " << partno+1
                   << " at offset " << mp.pieces[2+partno*2]-mp.pieces.front()
                   << " is " << mp.pieces[2+partno*2+1]-mp.pieces[2+partno*2]
                   << " bytes long" << endl;
               out << "<UL>";
               string *hdrlinp;
               for (INT line=0 ; (hdrlinp=mp.partHeaderLine(partno,line))->length() > 0 ; line++) {
                    out << "<LI> Header: <TT>" << *hdrlinp << "</TT>" << endl;
                    delete hdrlinp;
               }
               delete hdrlinp;
               if (mp.partBody(partno).length() != 0) {
                    out << "<LI> Body is " << mp.partBody(partno).length()
                        << " bytes long" << endl;
               }
               out << "</UL>";
          }
          if (mp.epilogue().length() != 0) {
               out << "<LI> Epilogue is " << mp.epilogue().length()
                   << " bytes long" << endl;
          }
          out << "</UL>";
     }
     return(out);
}

