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( "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 while( argstream.rdstate() == ios::goodbit &&
00334 fileBoundary != testdata &&
00335 argstream.get(c) )
00336 {
00337 if( haveFile ) ofile.put(testdata[0]);
00338 memmove( testdata, testdata + 1, fileBoundaryLen );
00339 testdata[fileBoundaryLen-1]=c;
00340 writelog2( "file data: ", testdata );
00341 }
00342 writelog2( "Closing file: ", boundary );
00343 if( haveFile ) ofile.close();
00344 return true;
00345 }
00346
00347
00348 bool fileDump( istream & argstream )
00349 {
00350 char c = '\0';
00351 size_t pos = 0;
00352 ofstream ofile;
00353 filename = "cgiDump.log";
00354 writelog2( "Dumping file: ", filename );
00355 ofile.open( filename.c_str(), ios::out | ios::trunc | ios::binary );
00356 while( argstream.rdstate() == ios::goodbit &&
00357 argstream.get(c) )
00358 {
00359 ofile.put(c);
00360 }
00361 ofile.close();
00362 return true;
00363 }
00364 };
00365
00366
00367
00368
00369
00370 cgiInput::cgiInput()
00371 {
00372 ;
00373 }
00374
00375 cgiInput::~cgiInput()
00376 {;}
00377 void cgiInput::setMultipart( aString boundary )
00378 {
00379 string path;
00380 if( uploadPath.length() )
00381 {
00382 path = uploadPath.str();
00383 int pathlength = path.length();
00384 if( pathlength && path[pathlength-1] != '/' )
00385 {
00386 path += "/";
00387 }
00388 }
00389 multipart mp(boundary.str(),theMap,fileMap,path);
00390 mp.consume( cin );
00391
00392
00393 writelog2("cgiInput::setMultipart mp.consume( cin ) called",boundary);
00394 }
00395
00396 void cgiInput::set( const char * queryString, size_t size )
00397 {
00398 if( queryString && strlen(queryString) > 0 )
00399 {
00400 if( size )
00401 {
00402 safe.setSize( size+1 );
00403 char * buf = (char*) safe;
00404 if( buf )
00405 {
00406 memcpy( buf, queryString, size );
00407 buf[size] = '\0';
00408 }
00409 }
00410 else
00411 {
00412 safe = queryString;
00413 }
00414
00415 const char * pchTok = safe.token( "&" );
00416
00417 while ( pchTok && strlen( pchTok ) )
00418 {
00419
00420 aString subToken = pchTok;
00421 subToken.deHexify('%');
00422 subToken.replaceFoundWith("+"," ");
00423 subToken.replaceFoundWith("+"," ");
00424
00425
00426 string tmpName = subToken.token( "=" );
00427
00428
00429 aString tmpVal = subToken.remainder();
00430
00431
00432 queryStringMap::iterator pos = theMap.find(tmpName);
00433
00434 if( pos == theMap.end() )
00435 {
00436 theMap.insert(make_pair(tmpName,tmpVal));
00437 }
00438 else
00439 {
00440 theMap[tmpName] += "|";
00441 theMap[tmpName] += tmpVal;
00442 }
00443 pchTok = safe.token( "&" );
00444 }
00445 }
00446 }
00447
00448 aString & cgiInput::Safe(void)
00449 {
00450 return safe;
00451 }
00452 queryStringMap & cgiInput::TheMap(void)
00453 {
00454 return theMap;
00455 }
00456 ocFiles & cgiInput::FileMap(void)
00457 {
00458 return fileMap;
00459 }
00460 int cgiInput::count( const char * key )
00461 {
00462 int retVal = 0;
00463 queryStringMap::iterator pos;
00464 pos = theMap.find(key);
00465 if( pos != theMap.end() )
00466 {
00467 aString & whole = theMap[key];
00468 while( whole.token("|") )
00469 {
00470 retVal++;
00471 }
00472 }
00473 return retVal;
00474 }
00475 aString & cgiInput::operator [] ( const char * key )
00476 {
00477 aString & returnValue = theMap[key];
00478 return returnValue;
00479 }
00480
00481
00482
00483
00484
00485 cgiEnvironment::cgiEnvironment(const char * uploadPath) : contentSize(0)
00486 {
00487 contentLength = getenv("CONTENT_LENGTH");
00488 contentType = getenv("CONTENT_TYPE");
00489 if( uploadPath ) clientArguments.uploadPath = uploadPath;
00490 aString aTemp = contentType.token(";");
00491 if( contentType.remainderPosition() != -1 )
00492 {
00493 contentType.token("=");
00494
00495
00496
00497
00498 contentBoundary = "--";
00499 contentBoundary += contentType.remainder();
00500 contentType = aTemp;
00501 contentType = contentType.lower();
00502 }
00503
00504 gatewayInterface = getenv("GATEWAY_INTERFACE");
00505 httpAccept = getenv("HTTP_ACCEPT");
00506 httpUserAgent = getenv("HTTP_USER_AGENT");
00507 pathInfo = getenv("PATH_INFO");
00508 pathTranslated = getenv("PATH_TRANSLATED");
00509 queryString = getenv("QUERY_STRING");
00510 remoteAddr = getenv("REMOTE_ADDR");
00511 remoteHost = getenv("REMOTE_HOST");
00512 remoteIdent = getenv("REMOTE_IDENT");
00513 requestMethod = getenv("REQUEST_METHOD");
00514 remoteUser = getenv("REMOTE_USER");
00515 scriptName = getenv("SCRIPT_NAME");
00516 serverName = getenv("SERVER_NAME");
00517 serverPort = getenv("SERVER_PORT");
00518 serverProtocol = getenv("SERVER_PROTOCOL");
00519 serverSoftware = getenv("SERVER_SOFTWARE");
00520
00521 contentSize = atoi( contentLength.str() );
00522
00523 if( requestMethod.match("GET") )
00524 {
00525 clientArguments.set( queryString.str() );
00526 }
00527 else if( requestMethod.match("POST") )
00528 {
00529 if( contentType == "multipart/form-data" )
00530 {
00531
00532 clientArguments.setMultipart( contentBoundary );
00533 writelog("cgiEnvironment::cgiEnvironment back from clientArguments.setMultipart()");
00534 }
00535 else
00536 {
00537 char * tempBuf = new char[ contentSize + 1 ];
00538 cin.read( tempBuf, contentSize );
00539 tempBuf[contentSize]='\0';
00540 clientArguments.set( tempBuf, contentSize );
00541 delete [] tempBuf;
00542
00543 }
00544
00545
00546 if( queryString.length() )
00547 {
00548 writelog2("cgiEnvironment::cgiEnvironment found additional data in query string: ", queryString );
00549 clientArguments.set( queryString.str() );
00550 }
00551 }
00552 writelog("cgiEnvironment::cgiEnvironment contructor done" );
00553 }
00554 cgiEnvironment::~cgiEnvironment()
00555 {
00556 ;
00557 }
00558
00559 aString & cgiEnvironment::ContentLength(void)
00560 {
00561 return contentLength;
00562 }
00563 aString & cgiEnvironment::ContentType(void)
00564 {
00565 return contentType;
00566 }
00567 aString & cgiEnvironment::GatewayInterface(void)
00568 {
00569 return gatewayInterface;
00570 }
00571 aString & cgiEnvironment::HttpAccept(void)
00572 {
00573 return httpAccept;
00574 }
00575 aString & cgiEnvironment::HttpUserAgent(void)
00576 {
00577 return httpUserAgent;
00578 }
00579 aString & cgiEnvironment::PathInfo(void)
00580 {
00581 return pathInfo;
00582 }
00583 aString & cgiEnvironment::PathTranslated(void)
00584 {
00585 return pathTranslated;
00586 }
00587 aString & cgiEnvironment::QueryString(void)
00588 {
00589 return queryString;
00590 }
00591 aString & cgiEnvironment::RemoteAddr(void)
00592 {
00593 return remoteAddr;
00594 }
00595 aString & cgiEnvironment::RemoteHost(void)
00596 {
00597 return remoteHost;
00598 }
00599 aString & cgiEnvironment::RemoteIdent(void)
00600 {
00601 return remoteIdent;
00602 }
00603 aString & cgiEnvironment::RequestMethod(void)
00604 {
00605 return requestMethod;
00606 }
00607 aString & cgiEnvironment::RemoteUser(void)
00608 {
00609 return remoteUser;
00610 }
00611 aString & cgiEnvironment::ScriptName(void)
00612 {
00613 return scriptName;
00614 }
00615 aString & cgiEnvironment::ServerName(void)
00616 {
00617 return serverName;
00618 }
00619 aString & cgiEnvironment::ServerPort(void)
00620 {
00621 return serverPort;
00622 }
00623 aString & cgiEnvironment::ServerProtocol(void)
00624 {
00625 return serverProtocol;
00626 }
00627 aString & cgiEnvironment::ServerSoftware(void)
00628 {
00629 return serverSoftware;
00630 }
00631 cgiInput & cgiEnvironment::ClientArguments(void)
00632 {
00633 return clientArguments;
00634 }
00635 aString & cgiEnvironment::ContentBoundary(void)
00636 {
00637 return contentBoundary;
00638 }
00639
00640 cgiBase::cgiBase():ostream(cout.rdbuf()),endLine("\r\n"),id(cgi)
00641 {
00642 ;
00643 }
00644 cgiBase::cgiBase(cgiBase& input):ostream(cout.rdbuf()),endLine("\r\n"),id(input.id)
00645 {
00646 ;
00647 }
00648 const char * cgiBase::tag( void )
00649 {
00650 return opening.str();
00651 }
00652 cgiBase::~cgiBase()
00653 {
00654 }
00655
00656
00657 cgiScript & cgiScript::DebugString( void )
00658 {
00659 *this << "<pre>" << endl;
00660 *this << "mime type: " << mimeType << endl;
00661 if( RequestMethod().length() > 0 )
00662 {
00663 *this << "ContentLength: [[" << ContentLength() << "]]"<< endl;
00664 *this << "ContentType: [[" << ContentType() << "]]"<< endl;
00665 *this << "ContentBoundary: [[" << ContentBoundary() << "]]"<< endl;
00666 *this << "GatewayInterface: [[" << GatewayInterface() << "]]"<< endl;
00667 *this << "HttpAccept: [[" << HttpAccept() << "]]"<< endl;
00668 *this << "HttpUserAgent: [[" << HttpUserAgent() << "]]"<< endl;
00669 *this << "PathInfo: " << PathInfo() << "]]"<< endl;
00670 *this << "PathTranslated: [[" << PathTranslated() << "]]"<< endl;
00671 *this << "QueryString: [[" << QueryString() << "]]"<< endl;
00672 *this << "RemoteAddr: [[" << RemoteAddr() << "]]"<< endl;
00673 *this << "RemoteHost: [[" << RemoteHost() << "]]"<< endl;
00674 *this << "RemoteIdent: [[" << RemoteIdent() << "]]"<< endl;
00675 *this << "RequestMethod: [[" << RequestMethod() << "]]"<< endl;
00676 *this << "RemoteUse: [[" << RemoteUser() << "]]"<< endl;
00677 *this << "ScriptName: [[" << ScriptName() << "]]"<< endl;
00678 *this << "ServerName: [[" << ServerName() << "]]"<< endl;
00679 *this << "ServerPort: [[" << ServerPort() << "]]"<< endl;
00680 *this << "ServerProtocol: [[" << ServerProtocol() << "]]"<< endl;
00681 *this << "ServerSoftware: [[" << ServerSoftware() << "]]"<< endl;
00682 *this << "Saved Query String: [[" << ClientArguments().Safe().str() << "]]"<< endl;
00683 }
00684 else
00685 {
00686 *this << "Invalid call to cgi program - invalid environment variables." << endl;
00687 }
00688 *this << endl <<"Client Arguments:" << endl;
00689 cgiInput & cgiInput = ClientArguments();
00690 queryStringMap::iterator pos;
00691
00692 for( pos = cgiInput.TheMap().begin();
00693 pos != cgiInput.TheMap().end();
00694 ++pos )
00695 {
00696 *this << pos->first.c_str() << ": [[";
00697 *this << pos->second << "]]" << endl;
00698 }
00699
00700
00701 *this << "</pre>" << endl;
00702 *this << ends;
00703 return *this;
00704 }
00705
00706
00707 cgiScriptLite::cgiScriptLite( const char * mimeString, bool bCloseHeader )
00708 {
00709 id = cgi;
00710 mimeType=mimeString;
00711 opening="Content-type: ";
00712 opening+=mimeType;
00713 opening+=endLine;
00714 close=endLine;
00715 *this << opening;
00716 if (bCloseHeader ) closeHeader();
00717
00718 }
00719
00720 void cgiScriptLite::closeHeader( void )
00721 {
00722 *this << endLine;
00723 }
00724
00725 cgiScriptLite::~cgiScriptLite()
00726 {
00727 *this << close << endLine;
00728 }
00729
00730 void cgiScriptLite::Redirect( const char * location )
00731 {
00732 *this << "Location: " << location << endLine << endLine;
00733 }
00734
00735
00736
00737
00738
00739
00740 cgiScript::cgiScript( const char * mimeString,
00741 bool bCloseHeader, const char * uploadPath)
00742 :cgiScriptLite(mimeString,bCloseHeader),cgiEnvironment(uploadPath)
00743 {
00744 ;
00745 }
00746
00747 cgiScript::~cgiScript()
00748 {
00749 ;
00750 }
00751
00752
00753
00754
00755 cgiHtml::cgiHtml( char * attr)
00756 {
00757 id = html;
00758 opening="<html";
00759 opening += attr;
00760 opening += ">";
00761 close="</html>";
00762 *this << opening << endl;
00763 }
00764 cgiHtml::~cgiHtml()
00765 {
00766 *this << close << endl;
00767 }
00768
00769
00770
00771
00772
00773
00774 cgiHead::cgiHead( char * attr)
00775 {
00776 id = head;
00777 opening="<head";
00778 opening += attr;
00779 opening += ">";
00780 close="</head>";
00781 *this << opening << endl;
00782 }
00783 cgiHead::~cgiHead()
00784 {
00785 *this << close << endl;
00786 }
00787
00788
00789
00790
00791 cgiBody::cgiBody(char * attr)
00792 {
00793 id = body;
00794 opening="<body";
00795 opening += attr;
00796 opening += ">";
00797 close="</body>";
00798 *this << opening << endl;
00799 }
00800 cgiBody::~cgiBody()
00801 {
00802 *this << close << endl;
00803 }
00804
00805
00806