Cgicc.cpp

00001 /* -*-mode:c++; c-file-style: "gnu";-*- */
00002 /*
00003  *  $Id: Cgicc_8cpp-source.html,v 1.1 2007/07/03 19:20:07 sebdiaz Exp $
00004  *
00005  *  Copyright (C) 1996 - 2004 Stephen F. Booth <sbooth@gnu.org>
00006  *                       2007 Sebastien DIAZ <sebastien.diaz@gmail.com>
00007  *  Part of the GNU cgicc library, http://www.gnu.org/software/cgicc
00008  *
00009  *  This library is free software; you can redistribute it and/or
00010  *  modify it under the terms of the GNU Lesser General Public
00011  *  License as published by the Free Software Foundation; either
00012  *  version 3 of the License, or (at your option) any later version.
00013  *
00014  *  This library is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  *  Lesser General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU Lesser General Public
00020  *  License along with this library; if not, write to the Free Software
00021  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA 
00022  */
00023 
00024 #ifdef __GNUG__
00025 #  pragma implementation
00026 #endif
00027 
00028 #include <new>
00029 #include <algorithm>
00030 #include <functional>
00031 #include <iterator>
00032 #include <stdexcept>
00033 
00034 #include "cgicc/CgiUtils.h"
00035 #include "cgicc/Cgicc.h"
00036 
00037 
00038 namespace cgicc {
00039 
00040   // ============================================================
00041   // Class FE_nameCompare
00042   // ============================================================
00043   class FE_nameCompare : public std::unary_function<FormEntry, bool>
00044   {
00045   public:
00046     
00047     inline explicit FE_nameCompare(const std::string& name)
00048       : fName(name) {}
00049     
00050     inline bool operator() (const FormEntry& entry)     const
00051     { return stringsAreEqual(fName, entry.getName()); }
00052     
00053   private:
00054     std::string fName;
00055   };
00056   
00057   // ============================================================
00058   // Class FE_valueCompare
00059   // ============================================================
00060   class FE_valueCompare : public std::unary_function<FormEntry, bool>
00061   {
00062   public:
00063     
00064     inline explicit FE_valueCompare(const std::string& value)
00065       : fValue(value) {}
00066     
00067     inline bool operator() (const FormEntry& entry)     const
00068     { return stringsAreEqual(fValue, entry.getValue()); }
00069     
00070   private:
00071     std::string fValue;
00072   };
00073   
00074   
00075   // ============================================================
00076   // Class FF_compare
00077   // ============================================================
00078   class FF_compare : public std::unary_function<FormFile, bool>
00079   {
00080   public:
00081     
00082     inline explicit FF_compare(const std::string& name)
00083       : fName(name) {}
00084     
00085     inline bool operator() (const FormFile& entry)      const
00086     { return stringsAreEqual(fName, entry.getName()); }
00087     
00088   private:
00089     std::string fName;
00090   };
00091   
00092   // ============================================================
00093   // Function copy_if (handy, missing from STL)
00094   // ============================================================
00095   // This code taken directly from 
00096   // "The C++ Programming Language, Third Edition" by Bjarne Stroustrup
00097   template<class In, class Out, class Pred>
00098   Out 
00099   copy_if(In first, 
00100           In last, 
00101           Out res, 
00102           Pred p)
00103   {
00104     while(first != last) {
00105       if(p(*first))
00106         *res++ = *first;
00107       ++first;
00108     }
00109     return res;
00110   }
00111   
00112 } // namespace cgicc
00113 
00114 // ============================================================
00115 // Class MultipartHeader
00116 // ============================================================
00117 class cgicc::MultipartHeader 
00118 {
00119 public:
00120   
00121   MultipartHeader(const std::string& disposition,
00122                   const std::string& name,
00123                   const std::string& filename,
00124                   const std::string& cType);
00125   
00126   inline
00127   MultipartHeader(const MultipartHeader& head)
00128   { operator=(head); }
00129   ~MultipartHeader();
00130 
00131   MultipartHeader&
00132   operator= (const MultipartHeader& head);
00133   
00134   inline std::string 
00135   getContentDisposition()                               const
00136   { return fContentDisposition; }
00137   
00138   inline std::string
00139   getName()                                             const
00140   { return fName; }
00141 
00142   inline std::string 
00143   getFilename()                                         const
00144   { return fFilename; }
00145 
00146   inline std::string 
00147   getContentType()                                      const
00148   { return fContentType; }
00149 
00150 private:
00151   std::string fContentDisposition;
00152   std::string fName;
00153   std::string fFilename;
00154   std::string fContentType;
00155 };
00156 
00157 cgicc::MultipartHeader::MultipartHeader(const std::string& disposition,
00158                                         const std::string& name,
00159                                         const std::string& filename,
00160                                         const std::string& cType)
00161   : fContentDisposition(disposition),
00162     fName(name),
00163     fFilename(filename),
00164     fContentType(cType)
00165 {}
00166 
00167 cgicc::MultipartHeader::~MultipartHeader()
00168 {}
00169 
00170 cgicc::MultipartHeader&
00171 cgicc::MultipartHeader::operator= (const MultipartHeader& head)
00172 {
00173   fContentDisposition   = head.fContentDisposition;
00174   fName                 = head.fName;
00175   fFilename             = head.fFilename;
00176   fContentType          = head.fContentType;
00177 
00178   return *this;
00179 }
00180 
00181 // ============================================================
00182 // Class Cgicc
00183 // ============================================================
00184 cgicc::Cgicc::Cgicc(CgiInput *input)
00185   : fEnvironment(input)
00186 { 
00187   // this can be tweaked for performance
00188   fFormData.reserve(20);
00189   fFormFiles.reserve(2);
00190 
00191   parseFormInput(fEnvironment.getPostData());
00192   parseFormInput(fEnvironment.getQueryString());
00193 }
00194 
00195 cgicc::Cgicc::~Cgicc()
00196 {}
00197 
00198 cgicc::Cgicc& 
00199 cgicc::Cgicc::operator= (const Cgicc& cgi)
00200 {
00201   this->fEnvironment = cgi.fEnvironment;
00202 
00203   fFormData.clear();
00204   fFormFiles.clear();
00205 
00206   parseFormInput(fEnvironment.getPostData());
00207   parseFormInput(fEnvironment.getQueryString());
00208   
00209   return *this;
00210 }
00211 
00212 const char*
00213 cgicc::Cgicc::getCompileDate()                                  const
00214 { return __DATE__; }
00215 
00216 const char*
00217 cgicc::Cgicc::getCompileTime()                                  const
00218 { return __TIME__; }
00219 
00220 const char*
00221 cgicc::Cgicc::getVersion()                                      const
00222 { return VERSION; }
00223 
00224 const char*
00225 cgicc::Cgicc::getHost()                                         const
00226 { return HOST; }
00227 
00228 void
00229 cgicc::Cgicc::save(const std::string& filename)                 const
00230 {
00231   fEnvironment.save(filename);
00232 }
00233 
00234 void
00235 cgicc::Cgicc::restore(const std::string& filename)
00236 {
00237   fEnvironment.restore(filename);
00238 
00239   // clear the current data and re-parse the enviroment
00240   fFormData.clear();
00241   fFormFiles.clear();
00242 
00243   parseFormInput(fEnvironment.getPostData());
00244   parseFormInput(fEnvironment.getQueryString());
00245 }
00246 
00247 bool 
00248 cgicc::Cgicc::queryCheckbox(const std::string& elementName)     const
00249 {
00250   const_form_iterator iter = getElement(elementName);
00251   return (iter != fFormData.end() && stringsAreEqual(iter->getValue(), "on"));
00252 }
00253 
00254 std::string
00255 cgicc::Cgicc::operator() (const std::string& name)              const
00256 {
00257   std::string result;
00258   const_form_iterator iter = getElement(name);
00259   if(iter != fFormData.end() && false == iter->isEmpty())
00260     result = iter->getValue();
00261   return result;
00262 }
00263 
00264 cgicc::form_iterator 
00265 cgicc::Cgicc::getElement(const std::string& name)
00266 {
00267   return std::find_if(fFormData.begin(), fFormData.end(),FE_nameCompare(name));
00268 }
00269 
00270 cgicc::const_form_iterator 
00271 cgicc::Cgicc::getElement(const std::string& name)               const
00272 {
00273   return std::find_if(fFormData.begin(), fFormData.end(),FE_nameCompare(name));
00274 }
00275 
00276 bool 
00277 cgicc::Cgicc::getElement(const std::string& name, 
00278                          std::vector<FormEntry>& result)        const
00279 { 
00280   return findEntries(name, true, result); 
00281 }
00282 
00283 cgicc::form_iterator 
00284 cgicc::Cgicc::getElementByValue(const std::string& value)
00285 {
00286   return std::find_if(fFormData.begin(), fFormData.end(),
00287                       FE_valueCompare(value));
00288 }
00289 
00290 cgicc::const_form_iterator 
00291 cgicc::Cgicc::getElementByValue(const std::string& value)       const
00292 {
00293   return std::find_if(fFormData.begin(), fFormData.end(), 
00294                       FE_valueCompare(value));
00295 }
00296 
00297 bool 
00298 cgicc::Cgicc::getElementByValue(const std::string& value, 
00299                                 std::vector<FormEntry>& result) const
00300 { 
00301   return findEntries(value, false, result); 
00302 }
00303 
00304 cgicc::file_iterator 
00305 cgicc::Cgicc::getFile(const std::string& name)
00306 {
00307   return std::find_if(fFormFiles.begin(), fFormFiles.end(), FF_compare(name));
00308 }
00309 
00310 cgicc::const_file_iterator 
00311 cgicc::Cgicc::getFile(const std::string& name)                  const
00312 {
00313   return std::find_if(fFormFiles.begin(), fFormFiles.end(), FF_compare(name));
00314 }
00315 
00316 
00317 // implementation method
00318 bool
00319 cgicc::Cgicc::findEntries(const std::string& param, 
00320                           bool byName,
00321                           std::vector<FormEntry>& result)       const
00322 {
00323   // empty the target vector
00324   result.clear();
00325 
00326   if(byName) {
00327     copy_if(fFormData.begin(), fFormData.end(), 
00328             std::back_inserter(result),FE_nameCompare(param));
00329   }
00330   else {
00331     copy_if(fFormData.begin(), fFormData.end(), 
00332             std::back_inserter(result), FE_valueCompare(param));
00333   }
00334 
00335   return false == result.empty();
00336 }
00337 
00338 void
00339 cgicc::Cgicc::parseFormInput(const std::string& data)
00340 {
00341   std::string content_type      = fEnvironment.getContentType();
00342   std::string standard_type     = "application/x-www-form-urlencoded";
00343   std::string multipart_type    = "multipart/form-data";
00344 
00345   // Don't waste time on empty input
00346   if(true == data.empty())
00347     return;
00348 
00349   // Standard content type = application/x-www-form-urlencoded
00350   // It may not be explicitly specified
00351   if(true == content_type.empty() 
00352      || stringsAreEqual(content_type, standard_type)) {
00353     std::string name, value;
00354     std::string::size_type pos;
00355     std::string::size_type oldPos = 0;
00356 
00357     // Parse the data in one fell swoop for efficiency
00358     while(true) {
00359       // Find the '=' separating the name from its value
00360       pos = data.find_first_of('=', oldPos);
00361       
00362       // If no '=', we're finished
00363       if(std::string::npos == pos)
00364         break;
00365       
00366       // Decode the name
00367       name = form_urldecode(data.substr(oldPos, pos - oldPos));
00368       oldPos = ++pos;
00369       
00370       // Find the '&' separating subsequent name/value pairs
00371       pos = data.find_first_of('&', oldPos);
00372       
00373       // Even if an '&' wasn't found the rest of the string is a value
00374       value = form_urldecode(data.substr(oldPos, pos - oldPos));
00375 
00376       // Store the pair
00377       fFormData.push_back(FormEntry(name, value));
00378       
00379       if(std::string::npos == pos)
00380         break;
00381 
00382       // Update parse position
00383       oldPos = ++pos;
00384     }
00385   }
00386   // File upload type = multipart/form-data
00387   else if(stringsAreEqual(multipart_type, content_type,
00388                           multipart_type.length())){
00389 
00390     // Find out what the separator is
00391     std::string                 bType   = "boundary=";
00392     std::string::size_type      pos     = content_type.find(bType);
00393 
00394     // generate the separators
00395     std::string sep1 = content_type.substr(pos + bType.length());
00396     sep1.append("\r\n");
00397     sep1.insert(0, "--");
00398 
00399     std::string sep2 = content_type.substr(pos + bType.length());
00400     sep2.append("--\r\n");
00401     sep2.insert(0, "--");
00402 
00403     // Find the data between the separators
00404     std::string::size_type start  = data.find(sep1);
00405     std::string::size_type sepLen = sep1.length();
00406     std::string::size_type oldPos = start + sepLen;
00407 
00408     while(true) {
00409       pos = data.find(sep1, oldPos);
00410 
00411       // If sep1 wasn't found, the rest of the data is an item
00412       if(std::string::npos == pos)
00413         break;
00414 
00415       // parse the data
00416       parseMIME(data.substr(oldPos, pos - oldPos));
00417 
00418       // update position
00419       oldPos = pos + sepLen;
00420     }
00421 
00422     // The data is terminated by sep2
00423     pos = data.find(sep2, oldPos);
00424     // parse the data, if found
00425     if(std::string::npos != pos) {
00426       parseMIME(data.substr(oldPos, pos - oldPos));
00427     }
00428   }
00429 }
00430 
00431 cgicc::MultipartHeader
00432 cgicc::Cgicc::parseHeader(const std::string& data)
00433 {
00434   std::string disposition;
00435   disposition = extractBetween(data, "Content-Disposition: ", ";");
00436   
00437   std::string name;
00438   name = extractBetween(data, "name=\"", "\"");
00439   
00440   std::string filename;
00441   filename = extractBetween(data, "filename=\"", "\"");
00442 
00443   std::string cType;
00444   cType = extractBetween(data, "Content-Type: ", "\r\n\r\n");
00445 
00446   // This is hairy: Netscape and IE don't encode the filenames
00447   // The RFC says they should be encoded, so I will assume they are.
00448   filename = form_urldecode(filename);
00449 
00450   return MultipartHeader(disposition, name, filename, cType);
00451 }
00452 
00453 void
00454 cgicc::Cgicc::parseMIME(const std::string& data)
00455 {
00456   // Find the header
00457   std::string end = "\r\n\r\n";
00458   std::string::size_type headLimit = data.find(end, 0);
00459   
00460   // Detect error
00461   if(std::string::npos == headLimit)
00462     throw std::runtime_error("Malformed input");
00463 
00464   // Extract the value - there is still a trailing CR/LF to be subtracted off
00465   std::string::size_type valueStart = headLimit + end.length();
00466   std::string value = data.substr(valueStart, data.length() - valueStart - 2);
00467 
00468   // Parse the header - pass trailing CR/LF x 2 to parseHeader
00469   MultipartHeader head = parseHeader(data.substr(0, valueStart));
00470 
00471   if(head.getFilename().empty())
00472     fFormData.push_back(FormEntry(head.getName(), value));
00473   else
00474     fFormFiles.push_back(FormFile(head.getName(), 
00475                                   head.getFilename(), 
00476                                   head.getContentType(), 
00477                                   value));
00478 }

Generated on Tue Jul 3 15:44:28 2007 for GNUCgicc by  doxygen 1.5.1