00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "cgiClass.h"
00016 #include <cstdlib>
00017 #include "ocString.h"
00018
00019
00020 #include "openLogger.h"
00021
00022
00023 enum multipart_state
00024 {
00025 init = 0,
00026 newData,
00027 dataSep,
00028 fileType,
00029 fileSep,
00030 readData,
00031 readFile,
00032 fileRead,
00033 finished,
00034 eof
00035 };
00036 class multipart
00037 {
00038 string boundary;
00039 size_t boundaryLen;
00040 string fileBoundary;
00041 string endboundary;
00042 size_t fileBoundaryLen;
00043 string clrf;
00044 ocString testline;
00045 string name;
00046 string filename;
00047 string value;
00048 string type;
00049 char * testdata;
00050 multipart_state state;
00051 queryStringMap & rMap;
00052 ocFiles & rFileMap;
00053 string path;
00054 public:
00055 multipart( string inBoundary, queryStringMap & inVars, ocFiles & fileMap, string iPath )
00056 :testdata(NULL),boundaryLen(0),state(init),rMap(inVars),rFileMap(fileMap),path(iPath)
00057 {
00058 clrf = "\r\n";
00059 fileBoundary = clrf;
00060 boundary = inBoundary;
00061 writelog2( "boundary: ", boundary );
00062 fileBoundary += inBoundary;
00063 endboundary = boundary;
00064 endboundary += "--";
00065 writelog2( "endboundary: ", endboundary );
00066 boundaryLen = boundary.length();
00067 fileBoundaryLen = fileBoundary.length();
00068 testdata = new char[fileBoundaryLen+1];
00069 memset(testdata, 0, fileBoundaryLen+1);
00070 }
00071 ~multipart()
00072 {
00073 delete testdata;
00074 }
00075 bool getline( istream & argstream )
00076 {
00077 char term;
00078 if( argstream.rdstate() == ios::goodbit &&
00079 state != readFile )
00080 {
00081
00082 std::getline(argstream, testline);
00083
00084
00085 if( testline.length() ) testline.resize( testline.length() - 1 );
00086
00087 testline.parseInit();
00088
00089 writelog2( "multipart::getline got: ", testline );
00090 }
00091 return true;
00092 }
00093 void addFile( void )
00094 {
00095 ocFile tempFile;
00096 tempFile.name=value;
00097 tempFile.path=filename;
00098 tempFile.type=type;
00099 rFileMap[value] = tempFile;
00100 }
00101 void addDataItem( void )
00102 {
00103
00104 queryStringMap::iterator pos = rMap.find(name);
00105 aString tmpVal(value);
00106 if( pos == rMap.end() )
00107 {
00108 rMap.insert(make_pair(name,tmpVal));
00109 }
00110 else
00111 {
00112 rMap[name] += "|";
00113 rMap[name] += tmpVal;
00114 }
00115 }
00116 void fixupFilename( string temp )
00117 {
00118 value = "";
00119 filename = "";
00120 writelog2("fixupFilename: checking length of",temp);
00121 if(temp.length())
00122 {
00123
00124 string::size_type idx = temp.find_last_of("\\");
00125 if( idx != string::npos )
00126 {
00127 temp = temp.substr(idx+1);
00128 }
00129
00130 writelog("fixupFilename adding path");
00131 filename = path;
00132 writelog("fixupFilename adding filename")
00133 filename += temp;
00134 value = temp;
00135 }
00136 writelog2("fixupFilename Done!", value);
00137 }
00138
00139 bool consume( istream & argstream )
00140 {
00141 bool bret = true;
00142 ocString test;
00143 while( argstream.rdstate() == ios::goodbit &&
00144 state != eof &&
00145 getline( argstream ) )
00146 {
00147 writelog2("Consumed",testline);
00148 switch( state )
00149 {
00150 case init:
00151
00152 if( boundary != testline )
00153 {
00154 state = eof;
00155 }
00156 else
00157 {
00158 state = newData;
00159 }
00160 break;
00161 case newData:
00162
00163 test = testline.parse(": ");
00164 transform(test.begin(),test.end(),test.begin(),::tolower);
00165 writelog2("Testing",test);
00166 if( test == "content-disposition" )
00167 {
00168
00169 test = testline.parse("; ");
00170
00171 transform(test.begin(),test.end(),test.begin(),::tolower);
00172
00173 writelog2("Content Testing",test);
00174 if( test == "form-data" )
00175 {
00176
00177 while( testline.length() && !testline.endOfParse() )
00178 {
00179 ocString test = testline.parse("; ");
00180 writelog2("Param Testing",test);
00181 if( test.length() )
00182 {
00183 string paramname=test.parse("=\"");
00184
00185 transform(paramname.begin(),paramname.end(),paramname.begin(),::tolower);
00186
00187 if( paramname == "name" )
00188 {
00189
00190 name = test.parse("\"");
00191 state = dataSep;
00192 }
00193 else if( paramname == "filename" )
00194 {
00195 writelog2("Fix Filename",test);
00196 fixupFilename(test.parse("\""));
00197 writelog("Filename Fixed");
00198 state = fileType;
00199 }
00200 }
00201 }
00202 }
00203 }
00204 else
00205 {
00206
00207 state = eof;
00208 }
00209 break;
00210 case fileType:
00211
00212 test = testline.parse(": ");
00213
00214 transform(test.begin(),test.end(),test.begin(),::tolower);
00215 if( test == "content-type" )
00216 {
00217 type = testline.remainder();
00218 state = fileSep;
00219 }
00220 else
00221 {
00222 state = eof;
00223 }
00224 break;
00225 case fileSep:
00226 if(testline.length() == 0)
00227 {
00228 state = readFile;
00229 }
00230 else
00231 {
00232 state = eof;
00233 }
00234 break;
00235 case dataSep:
00236
00237 if(testline.length() == 0)
00238 {
00239 state = readData;
00240 }
00241 else
00242 {
00243 state = eof;
00244 }
00245 break;
00246 case readData:
00247
00248 dataConsume(argstream);
00249 break;
00250 case readFile:
00251 fileConsume(argstream);
00252 addFile();
00253 addDataItem();
00254
00255 state = fileRead;
00256 break;
00257 case fileRead:
00258 if( testline == "--" )
00259 {
00260 state = eof;
00261 }
00262 state = newData;
00263 break;
00264 case finished:
00265
00266 if( boundary == testline )
00267 {
00268 state = newData;
00269 }
00270 else
00271 {
00272 state = eof;
00273 }
00274 break;
00275 default:
00276 state = eof;
00277 break;
00278 }
00279 }
00280 return bret;
00281 }
00282
00283 bool dataConsume( istream & argstream )
00284 {
00285 bool bRet = false;
00286 if( argstream.rdstate() == ios::goodbit )
00287 {
00288 value = testline;
00289 while( argstream.rdstate() == ios::goodbit &&
00290 getline( argstream ) &&
00291 testline != boundary &&
00292 testline != endboundary )
00293 {
00294 value +="\n";
00295 value += testline;
00296 }
00297
00298
00299 if(testline == boundary) state = newData;
00300 else if(testline == endboundary) state = eof;
00301
00302 addDataItem();
00303 }
00304 else
00305 {
00306 state = eof;
00307 }
00308 return bRet;
00309 }
00310
00311
00312 bool fileConsume( istream & argstream )
00313 {
00314 char c = '\0';
00315 size_t pos = 0;
00316 ofstream ofile;
00317 bool haveFile = filename.length() > 0;
00318 writelog2( "Consuming and saving file: ", filename );
00319 if( haveFile )
00320 {
00321 ofile.open( filename.c_str(), ios::out | ios::trunc | ios::binary );
00322 }
00323 writelog2( "fileBoundaryLen: " , fileBoundaryLen );
00324
00325 while( argstream.rdstate() == ios::goodbit &&
00326 pos < fileBoundaryLen &&
00327 argstream.get(c) )
00328 {
00329 testdata[pos++] = c;
00330 }
00331 writelog2( "final file testdata pos: ", pos-1 )
00332
00333
00334 while( argstream.rdstate() == ios::goodbit &&
00335 fileBoundary != testdata &&
00336 argstream.get(c) )
00337 {
00338 if( haveFile ) ofile.put(testdata[0]);
00339 memmove( testdata, testdata + 1, fileBoundaryLen );
00340 testdata[fileBoundaryLen-1]=c;
00341 }
00342 writelog2( "Closing file: ", boundary );
00343 if( haveFile ) ofile.close();
00344
00345 return true;
00346 }
00347
00348
00349 bool fileDump( istream & argstream )
00350 {
00351 char c = '\0';
00352 size_t pos = 0;
00353 ofstream ofile;
00354 filename = "cgiDump.log";
00355 writelog2( "Dumping file: ", filename );
00356 ofile.open( filename.c_str(), ios::out | ios::trunc | ios::binary );
00357 while( argstream.rdstate() == ios::goodbit &&
00358 argstream.get(c) )
00359 {
00360 ofile.put(c);
00361 }
00362 ofile.close();
00363
00364 return true;
00365 }
00366 };
00367
00368
00369
00370
00371
00372 cgiInput::cgiInput()
00373 {
00374 ;
00375 }
00376
00377 cgiInput::~cgiInput()
00378 {;}
00379 void cgiInput::setMultipart( aString boundary )
00380 {
00381 string path;
00382 if( uploadPath.length() )
00383 {
00384 path = uploadPath.str();
00385 int pathlength = path.length();
00386 if( pathlength && path[pathlength-1] != '/' )
00387 {
00388 path += "/";
00389 }
00390 }
00391 multipart mp(boundary.str(),theMap,fileMap,path);
00392 mp.consume( cin );
00393
00394
00395 writelog2("cgiInput::setMultipart mp.consume( cin ) called",boundary);
00396 }
00397
00398 void cgiInput::set( const char * queryString, size_t size )
00399 {
00400 if( queryString && strlen(queryString) > 0 )
00401 {
00402 if( size )
00403 {
00404 safe.setSize( size+1 );
00405 char * buf = (char*) safe;
00406 if( buf )
00407 {
00408 memcpy( buf, queryString, size );
00409 buf[size] = '\0';
00410 }
00411 }
00412 else
00413 {
00414 safe = queryString;
00415 }
00416
00417 const char * pchTok = safe.token( "&" );
00418
00419 while ( pchTok && strlen( pchTok ) )
00420 {
00421
00422 aString subToken = pchTok;
00423 subToken.deHexify('%');
00424 subToken.replaceFoundWith("+"," ");
00425 subToken.replaceFoundWith("+"," ");
00426 writelog2("cgiInput::set() subToken",subToken);
00427
00428 string tmpName = subToken.token( "=" );
00429
00430
00431 aString tmpVal = subToken.remainder();
00432
00433
00434 queryStringMap::iterator pos = theMap.find(tmpName);
00435
00436 if( pos == theMap.end() )
00437 {
00438 theMap.insert(make_pair(tmpName,tmpVal));
00439 }
00440 else
00441 {
00442 theMap[tmpName] += "|";
00443 theMap[tmpName] += tmpVal;
00444 }
00445 pchTok = safe.token( "&" );
00446 }
00447 }
00448 }
00449
00450 aString & cgiInput::Safe(void)
00451 {
00452 return safe;
00453 }
00454 queryStringMap & cgiInput::TheMap(void)
00455 {
00456 return theMap;
00457 }
00458 ocFiles & cgiInput::FileMap(void)
00459 {
00460 return fileMap;
00461 }
00462 int cgiInput::count( const char * key )
00463 {
00464 int retVal = 0;
00465 queryStringMap::iterator pos;
00466 pos = theMap.find(key);
00467 if( pos != theMap.end() )
00468 {
00469 aString & whole = theMap[key];
00470 while( whole.token("|") )
00471 {
00472 retVal++;
00473 }
00474 }
00475 return retVal;
00476 }
00477 aString & cgiInput::operator [] ( const char * key )
00478 {
00479 aString & returnValue = theMap[key];
00480 return returnValue;
00481 }
00482
00483 bool cgiEnvironment::readOnCreateMode = true;
00484 void cgiEnvironment::ReadOnCreateMode( bool doRead )
00485 {
00486 readOnCreateMode = doRead;
00487 }
00488
00489
00490 bool cgiEnvironment::readInput( void )
00491 {
00492 if( requestMethod.match("GET") )
00493 {
00494 clientArguments.set( queryString.str() );
00495 }
00496 else if( requestMethod.match("POST") )
00497 {
00498 if( contentType == "multipart/form-data" )
00499 {
00500
00501 clientArguments.setMultipart( contentBoundary );
00502 writelog("cgiEnvironment::cgiEnvironment back from clientArguments.setMultipart()");
00503 }
00504 else
00505 {
00506 writelog2( "cgiEnvironment::cgiEnvironment(post buf size: ", contentSize );
00507
00508 char * tempBuf = new char[ contentSize + 1 ];
00509 memset(tempBuf,0,contentSize + 1);
00510 cin.read( tempBuf, contentSize );
00511 writelog2( "cgiEnvironment::cgiEnvironment(post buffer content: ",tempBuf);
00512 tempBuf[contentSize]='\0';
00513 clientArguments.set( tempBuf, contentSize );
00514 delete [] tempBuf;
00515
00516 }
00517
00518 if( queryString.length() )
00519 {
00520 writelog2("cgiEnvironment::cgiEnvironment found additional data in query string: ", queryString );
00521 clientArguments.set( queryString.str() );
00522 }
00523 }
00524 return true;
00525 }
00526
00527
00528
00529
00530 cgiEnvironment::cgiEnvironment(const char * uploadPath) : contentSize(0)
00531 {
00532 contentLength = getenv("CONTENT_LENGTH");
00533 contentType = getenv("CONTENT_TYPE");
00534 if( uploadPath ) clientArguments.uploadPath = uploadPath;
00535 aString aTemp = contentType.token(";");
00536 if( contentType.remainderPosition() != -1 )
00537 {
00538 contentType.token("=");
00539
00540
00541
00542
00543 contentBoundary = "--";
00544 contentBoundary += contentType.remainder();
00545 contentType = aTemp;
00546 contentType = contentType.lower();
00547 }
00548
00549 gatewayInterface = getenv("GATEWAY_INTERFACE");
00550 httpAccept = getenv("HTTP_ACCEPT");
00551 httpUserAgent = getenv("HTTP_USER_AGENT");
00552 httpReferer = getenv("HTTP_REFERER");
00553 pathInfo = getenv("PATH_INFO");
00554 pathTranslated = getenv("PATH_TRANSLATED");
00555 queryString = getenv("QUERY_STRING");
00556 remoteAddr = getenv("REMOTE_ADDR");
00557 remoteHost = getenv("REMOTE_HOST");
00558 remoteIdent = getenv("REMOTE_IDENT");
00559 requestMethod = getenv("REQUEST_METHOD");
00560 remoteUser = getenv("REMOTE_USER");
00561 scriptName = getenv("SCRIPT_NAME");
00562 serverName = getenv("SERVER_NAME");
00563 serverPort = getenv("SERVER_PORT");
00564 serverProtocol = getenv("SERVER_PROTOCOL");
00565 serverSoftware = getenv("SERVER_SOFTWARE");
00566
00567 contentSize = atoi( contentLength.str() );
00568
00569 if( readOnCreateMode ) readInput();
00570
00571 writelog("cgiEnvironment::cgiEnvironment contructor done" );
00572 }
00573 cgiEnvironment::~cgiEnvironment()
00574 {
00575 ;
00576 }
00577
00578 aString & cgiEnvironment::ContentLength(void)
00579 {
00580 return contentLength;
00581 }
00582 aString & cgiEnvironment::ContentType(void)
00583 {
00584 return contentType;
00585 }
00586 aString & cgiEnvironment::GatewayInterface(void)
00587 {
00588 return gatewayInterface;
00589 }
00590 aString & cgiEnvironment::HttpAccept(void)
00591 {
00592 return httpAccept;
00593 }
00594 aString & cgiEnvironment::HttpUserAgent(void)
00595 {
00596 return httpUserAgent;
00597 }
00598 aString & cgiEnvironment::HttpReferer(void)
00599 {
00600 return httpReferer;
00601 }
00602 aString & cgiEnvironment::PathInfo(void)
00603 {
00604 return pathInfo;
00605 }
00606 aString & cgiEnvironment::PathTranslated(void)
00607 {
00608 return pathTranslated;
00609 }
00610 aString & cgiEnvironment::QueryString(void)
00611 {
00612 return queryString;
00613 }
00614 aString & cgiEnvironment::RemoteAddr(void)
00615 {
00616 return remoteAddr;
00617 }
00618 aString & cgiEnvironment::RemoteHost(void)
00619 {
00620 return remoteHost;
00621 }
00622 aString & cgiEnvironment::RemoteIdent(void)
00623 {
00624 return remoteIdent;
00625 }
00626 aString & cgiEnvironment::RequestMethod(void)
00627 {
00628 return requestMethod;
00629 }
00630 aString & cgiEnvironment::RemoteUser(void)
00631 {
00632 return remoteUser;
00633 }
00634 aString & cgiEnvironment::ScriptName(void)
00635 {
00636 return scriptName;
00637 }
00638 aString & cgiEnvironment::ServerName(void)
00639 {
00640 return serverName;
00641 }
00642 aString & cgiEnvironment::ServerPort(void)
00643 {
00644 return serverPort;
00645 }
00646 aString & cgiEnvironment::ServerProtocol(void)
00647 {
00648 return serverProtocol;
00649 }
00650 aString & cgiEnvironment::ServerSoftware(void)
00651 {
00652 return serverSoftware;
00653 }
00654 cgiInput & cgiEnvironment::ClientArguments(void)
00655 {
00656 return clientArguments;
00657 }
00658 aString & cgiEnvironment::ContentBoundary(void)
00659 {
00660 return contentBoundary;
00661 }
00662
00663 cgiBase::cgiBase():ostream(cout.rdbuf()),endLine("\r\n"),id(cgi)
00664 {
00665 ;
00666 }
00667 cgiBase::cgiBase(cgiBase& input):ostream(cout.rdbuf()),endLine("\r\n"),id(input.id)
00668 {
00669 ;
00670 }
00671 const char * cgiBase::tag( void )
00672 {
00673 return opening.str();
00674 }
00675 cgiBase::~cgiBase()
00676 {
00677 }
00678
00679
00680 cgiScript & cgiScript::DebugString( void )
00681 {
00682 *this << "<pre>" << endl;
00683 *this << "mime type: " << mimeType << endl;
00684 if( RequestMethod().length() > 0 )
00685 {
00686 *this << "ContentLength: [[" << ContentLength() << "]]"<< endl;
00687 *this << "ContentType: [[" << ContentType() << "]]"<< endl;
00688 *this << "ContentBoundary: [[" << ContentBoundary() << "]]"<< endl;
00689 *this << "GatewayInterface: [[" << GatewayInterface() << "]]"<< endl;
00690 *this << "HttpAccept: [[" << HttpAccept() << "]]"<< endl;
00691 *this << "HttpUserAgent: [[" << HttpUserAgent() << "]]"<< endl;
00692 *this << "PathInfo: " << PathInfo() << "]]"<< endl;
00693 *this << "PathTranslated: [[" << PathTranslated() << "]]"<< endl;
00694 *this << "QueryString: [[" << QueryString() << "]]"<< endl;
00695 *this << "RemoteAddr: [[" << RemoteAddr() << "]]"<< endl;
00696 *this << "RemoteHost: [[" << RemoteHost() << "]]"<< endl;
00697 *this << "RemoteIdent: [[" << RemoteIdent() << "]]"<< endl;
00698 *this << "RequestMethod: [[" << RequestMethod() << "]]"<< endl;
00699 *this << "RemoteUse: [[" << RemoteUser() << "]]"<< endl;
00700 *this << "ScriptName: [[" << ScriptName() << "]]"<< endl;
00701 *this << "ServerName: [[" << ServerName() << "]]"<< endl;
00702 *this << "ServerPort: [[" << ServerPort() << "]]"<< endl;
00703 *this << "ServerProtocol: [[" << ServerProtocol() << "]]"<< endl;
00704 *this << "ServerSoftware: [[" << ServerSoftware() << "]]"<< endl;
00705 *this << "Saved Query String: [[" << ClientArguments().Safe().str() << "]]"<< endl;
00706 }
00707 else
00708 {
00709 *this << "Invalid call to cgi program - invalid environment variables." << endl;
00710 }
00711 *this << endl <<"Client Arguments:" << endl;
00712 cgiInput & cgiInput = ClientArguments();
00713 queryStringMap::iterator pos;
00714
00715 for( pos = cgiInput.TheMap().begin();
00716 pos != cgiInput.TheMap().end();
00717 ++pos )
00718 {
00719 *this << pos->first.c_str() << ": [[";
00720 *this << pos->second << "]]" << endl;
00721 }
00722
00723
00724 *this << "</pre>" << endl;
00725 *this << ends;
00726 return *this;
00727 }
00728
00729
00730 cgiScriptLite::cgiScriptLite( const char * mimeString, bool bCloseHeader, bool cache )
00731 {
00732 id = cgi;
00733 mimeType=mimeString;
00734 opening="Content-type: ";
00735 opening+=mimeType;
00736 opening+=endLine;
00737 close=endLine;
00738 if( !cache )
00739 {
00740 openHeader();
00741 }
00742 this->flush();
00743 if (bCloseHeader ) closeHeader();
00744 }
00745 void cgiScriptLite::openHeader( const char * mimeString )
00746 {
00747 if( mimeString )
00748 {
00749 mimeType=mimeString;
00750 opening="Content-type: ";
00751 opening+=mimeType;
00752 opening+=endLine;
00753 }
00754 *this << opening;
00755 this->flush();
00756 }
00757 void cgiScriptLite::closeHeader( void )
00758 {
00759 *this << endLine;
00760 this->flush();
00761 }
00762
00763 cgiScriptLite::~cgiScriptLite()
00764 {
00765 *this << close << endLine;
00766 }
00767
00768 void cgiScriptLite::Redirect( const char * location )
00769 {
00770 *this << "Location: " << location << endLine << endLine;
00771 }
00772
00773
00774
00775
00776
00777
00778 cgiScript::cgiScript( const char * mimeString,
00779 bool bCloseHeader, const char * uploadPath, bool bCache)
00780 :cgiScriptLite(mimeString,bCloseHeader,bCache),cgiEnvironment(uploadPath)
00781 {
00782 ;
00783 }
00784
00785 cgiScript::~cgiScript()
00786 {
00787 ;
00788 }
00789
00790
00791
00792
00793 cgiHtml::cgiHtml( char * attr)
00794 {
00795 id = html;
00796 opening="<html";
00797 opening += attr;
00798 opening += ">";
00799 close="</html>";
00800 *this << opening << endl;
00801 }
00802 cgiHtml::~cgiHtml()
00803 {
00804 *this << close << endl;
00805 }
00806
00807
00808
00809
00810
00811
00812 cgiHead::cgiHead( char * attr)
00813 {
00814 id = head;
00815 opening="<head";
00816 opening += attr;
00817 opening += ">";
00818 close="</head>";
00819 *this << opening << endl;
00820 }
00821 cgiHead::~cgiHead()
00822 {
00823 *this << close << endl;
00824 }
00825
00826
00827
00828
00829 cgiBody::cgiBody(char * attr)
00830 {
00831 id = body;
00832 opening="<body";
00833 opening += attr;
00834 opening += ">";
00835 close="</body>";
00836 *this << opening << endl;
00837 }
00838 cgiBody::~cgiBody()
00839 {
00840 *this << close << endl;
00841 }
00842
00843
00844