fastcgi++
http.cpp
Go to the documentation of this file.
00001 
00002 /***************************************************************************
00003 * Copyright (C) 2007 Eddie Carle [eddie@erctech.org]                       *
00004 *                                                                          *
00005 * This file is part of fastcgi++.                                          *
00006 *                                                                          *
00007 * fastcgi++ is free software: you can redistribute it and/or modify it     *
00008 * under the terms of the GNU Lesser General Public License as  published   *
00009 * by the Free Software Foundation, either version 3 of the License, or (at *
00010 * your option) any later version.                                          *
00011 *                                                                          *
00012 * fastcgi++ is distributed in the hope that it will be useful, but WITHOUT *
00013 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    *
00014 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public     *
00015 * License for more details.                                                *
00016 *                                                                          *
00017 * You should have received a copy of the GNU Lesser General Public License *
00018 * along with fastcgi++.  If not, see <http://www.gnu.org/licenses/>.       *
00019 ****************************************************************************/
00020 
00021 
00022 #include <boost/date_time/posix_time/posix_time.hpp>
00023 
00024 #include <fastcgi++/http.hpp>
00025 #include <fastcgi++/protocol.hpp>
00026 
00027 #include "utf8_codecvt.hpp"
00028 
00029 void Fastcgipp::Http::charToString(const char* data, size_t size, std::wstring& string)
00030 {
00031    const size_t bufferSize=512;
00032    wchar_t buffer[bufferSize];
00033    using namespace std;
00034 
00035    if(size)
00036    {
00037       codecvt_base::result cr=codecvt_base::partial;
00038       while(cr==codecvt_base::partial)
00039       {{
00040          wchar_t* it;
00041          const char* tmpData;
00042          mbstate_t conversionState = mbstate_t();
00043          cr=use_facet<codecvt<wchar_t, char, mbstate_t> >(locale(locale::classic(), new utf8CodeCvt::utf8_codecvt_facet)).in(conversionState, data, data+size, tmpData, buffer, buffer+bufferSize, it);
00044          string.append(buffer, it);
00045          size-=tmpData-data;
00046          data=tmpData;
00047       }}
00048       if(cr==codecvt_base::error) throw Exceptions::CodeCvt();
00049    }
00050 }
00051 
00052 int Fastcgipp::Http::atoi(const char* start, const char* end)
00053 {
00054    bool neg=false;
00055    if(*start=='-')
00056    {
00057       neg=true;
00058       ++start;
00059    }
00060    int result=0;
00061    for(; 0x30 <= *start && *start <= 0x39 && start<end; ++start)
00062       result=result*10+(*start&0x0f);
00063 
00064    return neg?-result:result;
00065 }
00066 
00067 size_t Fastcgipp::Http::percentEscapedToRealBytes(const char* source, char* destination, size_t size)
00068 {
00069    if (size < 1) return 0;
00070 
00071    unsigned int i=0;
00072    char* start=destination;
00073    while(1)
00074    {
00075       if(*source=='%')
00076       {
00077          *destination=0;
00078          for(int shift=4; shift>=0; shift-=4)
00079          {
00080             if(++i==size) break;
00081             ++source;
00082             if((*source|0x20) >= 'a' && (*source|0x20) <= 'f')
00083                *destination|=((*source|0x20)-0x57)<<shift;
00084             else if(*source >= '0' && *source <= '9')
00085                *destination|=(*source&0x0f)<<shift;
00086          }
00087          ++source;
00088          ++destination;
00089       }
00090       else if(*source=='+')
00091       {
00092          *destination++=' ';
00093          ++source;
00094       }
00095       else
00096          *destination++=*source++;
00097 
00098       if(++i==size) break;
00099    }
00100    return destination-start;
00101 }
00102 
00103 template void Fastcgipp::Http::Environment<char>::fill(const char* data, size_t size);
00104 template void Fastcgipp::Http::Environment<wchar_t>::fill(const char* data, size_t size);
00105 template<class charT> void Fastcgipp::Http::Environment<charT>::fill(const char* data, size_t size)
00106 {
00107    using namespace std;
00108    using namespace boost;
00109 
00110    while(size)
00111    {{
00112       size_t nameSize;
00113       size_t valueSize;
00114       const char* name;
00115       const char* value;
00116       Protocol::processParamHeader(data, size, name, nameSize, value, valueSize);
00117       size-=value-data+valueSize;
00118       data=value+valueSize;
00119 
00120       switch(nameSize)
00121       {
00122       case 9:
00123          if(!memcmp(name, "HTTP_HOST", 9))
00124             charToString(value, valueSize, host);
00125          else if(!memcmp(name, "PATH_INFO", 9))
00126          {
00127             boost::scoped_array<char> buffer(new char[valueSize]);
00128             const char* source=value;
00129             int size=-1;
00130             for(; source<value+valueSize+1; ++source, ++size)
00131             {
00132                if(*source == '/' || source == value+valueSize)
00133                {
00134                   if(size > 0)
00135                   {
00136                      percentEscapedToRealBytes(source-size, buffer.get(), size);
00137                      pathInfo.push_back(std::basic_string<charT>());
00138                      charToString(buffer.get(), size, pathInfo.back());
00139                   }
00140                   size=-1;                
00141                }
00142             }
00143          }
00144          break;
00145       case 11:
00146          if(!memcmp(name, "HTTP_ACCEPT", 11))
00147             charToString(value, valueSize, acceptContentTypes);
00148          else if(!memcmp(name, "HTTP_COOKIE", 11))
00149             decodeUrlEncoded(value, valueSize, cookies, ';');
00150          else if(!memcmp(name, "SERVER_ADDR", 11))
00151             serverAddress.assign(value, value+valueSize);
00152          else if(!memcmp(name, "REMOTE_ADDR", 11))
00153             remoteAddress.assign(value, value+valueSize);
00154          else if(!memcmp(name, "SERVER_PORT", 11))
00155             serverPort=atoi(value, value+valueSize);
00156          else if(!memcmp(name, "REMOTE_PORT", 11))
00157             remotePort=atoi(value, value+valueSize);
00158          else if(!memcmp(name, "SCRIPT_NAME", 11))
00159             charToString(value, valueSize, scriptName);
00160          else if(!memcmp(name, "REQUEST_URI", 11))
00161             charToString(value, valueSize, requestUri);
00162          break;
00163       case 12:
00164          if(!memcmp(name, "HTTP_REFERER", 12) && valueSize)
00165             charToString(value, valueSize, referer);
00166          else if(!memcmp(name, "CONTENT_TYPE", 12))
00167          {
00168             const char* end=(char*)memchr(value, ';', valueSize);
00169             charToString(value, end?end-value:valueSize, contentType);
00170             if(end)
00171             {
00172                const char* start=(char*)memchr(end, '=', valueSize-(end-data));
00173                if(start)
00174                {
00175                   boundarySize=valueSize-(++start-value);
00176                   boundary.reset(new char[boundarySize]);
00177                   memcpy(boundary.get(), start, boundarySize);
00178                }
00179             }
00180          }
00181          else if(!memcmp(name, "QUERY_STRING", 12) && valueSize)
00182             decodeUrlEncoded(value, valueSize, gets);
00183          break;
00184       case 13:
00185          if(!memcmp(name, "DOCUMENT_ROOT", 13))
00186             charToString(value, valueSize, root);
00187          break;
00188       case 14:
00189          if(!memcmp(name, "REQUEST_METHOD", 14))
00190          {
00191             requestMethod = HTTP_METHOD_ERROR;
00192             switch(valueSize)
00193             {
00194             case 3:
00195                if(!memcmp(value, requestMethodLabels[HTTP_METHOD_GET], 3)) requestMethod = HTTP_METHOD_GET;
00196                else if(!memcmp(value, requestMethodLabels[HTTP_METHOD_PUT], 3)) requestMethod = HTTP_METHOD_PUT;
00197                break;
00198             case 4:
00199                if(!memcmp(value, requestMethodLabels[HTTP_METHOD_HEAD], 4)) requestMethod = HTTP_METHOD_HEAD;
00200                else if(!memcmp(value, requestMethodLabels[HTTP_METHOD_POST], 4)) requestMethod = HTTP_METHOD_POST;
00201                break;
00202             case 5:
00203                if(!memcmp(value, requestMethodLabels[HTTP_METHOD_TRACE], 5)) requestMethod = HTTP_METHOD_TRACE;
00204                break;
00205             case 6:
00206                if(!memcmp(value, requestMethodLabels[HTTP_METHOD_DELETE], 6)) requestMethod = HTTP_METHOD_DELETE;
00207                break;
00208             case 7:
00209                if(!memcmp(value, requestMethodLabels[HTTP_METHOD_OPTIONS], 7)) requestMethod = HTTP_METHOD_OPTIONS;
00210                else if(!memcmp(value, requestMethodLabels[HTTP_METHOD_OPTIONS], 7)) requestMethod = HTTP_METHOD_CONNECT;
00211                break;
00212             }
00213          }
00214          else if(!memcmp(name, "CONTENT_LENGTH", 14))
00215             contentLength=atoi(value, value+valueSize);
00216          break;
00217       case 15:
00218          if(!memcmp(name, "HTTP_USER_AGENT", 15))
00219             charToString(value, valueSize, userAgent);
00220          else if(!memcmp(name, "HTTP_KEEP_ALIVE", 15))
00221             keepAlive=atoi(value, value+valueSize);
00222          break;
00223       case 18:
00224          if(!memcmp(name, "HTTP_IF_NONE_MATCH", 18))
00225             etag=atoi(value, value+valueSize);
00226          break;
00227       case 19:
00228          if(!memcmp(name, "HTTP_ACCEPT_CHARSET", 19))
00229             charToString(value, valueSize, acceptCharsets);
00230          break;
00231       case 20:
00232          if(!memcmp(name, "HTTP_ACCEPT_LANGUAGE", 20))
00233             charToString(value, valueSize, acceptLanguages);
00234          break;
00235       case 22:
00236          if(!memcmp(name, "HTTP_IF_MODIFIED_SINCE", 22))
00237          {
00238             stringstream dateStream;
00239             dateStream.write(value, valueSize);
00240             dateStream.imbue(locale(locale::classic(), new posix_time::time_input_facet("%a, %d %b %Y %H:%M:%S GMT")));
00241             dateStream >> ifModifiedSince;
00242          }
00243          break;
00244       }
00245    }}
00246 }
00247 
00248 template bool Fastcgipp::Http::Environment<char>::fillPostBuffer(const char* data, size_t size);
00249 template bool Fastcgipp::Http::Environment<wchar_t>::fillPostBuffer(const char* data, size_t size);
00250 template<class charT> bool Fastcgipp::Http::Environment<charT>::fillPostBuffer(const char* data, size_t size)
00251 {
00252    if(!postBuffer)
00253    {
00254       postBuffer.reset(new char[contentLength]);
00255       pPostBuffer=postBuffer.get();
00256    }
00257 
00258    size_t trueSize=minPostBufferSize(size);
00259    if(trueSize)
00260    {
00261       std::memcpy(pPostBuffer, data, trueSize);
00262       pPostBuffer+=trueSize;
00263       return true;
00264    }
00265    else
00266       return false;
00267 }
00268 
00269 template void Fastcgipp::Http::Environment<char>::parsePostsMultipart();
00270 template void Fastcgipp::Http::Environment<wchar_t>::parsePostsMultipart();
00271 template<class charT> void Fastcgipp::Http::Environment<charT>::parsePostsMultipart()
00272 {
00273    using namespace std;
00274 
00275    const char cName[] = "name=\"";
00276    const char cFilename[] = "filename=\"";
00277    const char cContentType[] = "Content-Type: ";
00278    const char cBodyStart[] = "\r\n\r\n";
00279 
00280    pPostBuffer=postBuffer.get()+boundarySize+1;
00281    const char* contentTypeStart=0;
00282    ssize_t contentTypeSize=-1;
00283    const char* nameStart=0;
00284    ssize_t nameSize=-1;
00285    const char* filenameStart=0;
00286    ssize_t filenameSize=-1;
00287    const char* bodyStart=0;
00288    ssize_t bodySize=-1;
00289    enum ParseState { HEADER, NAME, FILENAME, CONTENT_TYPE, BODY } parseState=HEADER;
00290    for(pPostBuffer=postBuffer.get()+boundarySize+2; pPostBuffer<postBuffer.get()+contentLength; ++pPostBuffer)
00291    {
00292       switch(parseState)
00293       {
00294          case HEADER:
00295          {
00296             if(nameSize == -1)
00297             {
00298                const size_t size=minPostBufferSize(sizeof(cName)-1);
00299                if(!memcmp(pPostBuffer, cName, size))
00300                {
00301                   pPostBuffer+=size-1;
00302                   nameStart=pPostBuffer+1;
00303                   parseState=NAME;
00304                   continue;
00305                }
00306             }
00307             if(filenameSize == -1)
00308             {
00309                const size_t size=minPostBufferSize(sizeof(cFilename)-1);
00310                if(!memcmp(pPostBuffer, cFilename, size))
00311                {
00312                   pPostBuffer+=size-1;
00313                   filenameStart=pPostBuffer+1;
00314                   parseState=FILENAME;
00315                   continue;
00316                }
00317             }
00318             if(contentTypeSize == -1)
00319             {
00320                const size_t size=minPostBufferSize(sizeof(cContentType)-1);
00321                if(!memcmp(pPostBuffer, cContentType, size))
00322                {
00323                   pPostBuffer+=size-1;
00324                   contentTypeStart=pPostBuffer+1;
00325                   parseState=CONTENT_TYPE;
00326                   continue;
00327                }
00328             }
00329             if(bodySize == -1)
00330             {
00331                const size_t size=minPostBufferSize(sizeof(cBodyStart)-1);
00332                if(!memcmp(pPostBuffer, cBodyStart, size))
00333                {
00334                   pPostBuffer+=size-1;
00335                   bodyStart=pPostBuffer+1;
00336                   parseState=BODY;
00337                   continue;
00338                }
00339             }
00340             continue;
00341          }
00342 
00343          case NAME:
00344          {
00345             if(*pPostBuffer == '"')
00346             {
00347                nameSize=pPostBuffer-nameStart;
00348                parseState=HEADER;
00349             }
00350             continue;
00351          }
00352 
00353          case FILENAME:
00354          {
00355             if(*pPostBuffer == '"')
00356             {
00357                filenameSize=pPostBuffer-filenameStart;
00358                parseState=HEADER;
00359             }
00360             continue;
00361          }
00362 
00363          case CONTENT_TYPE:
00364          {
00365             if(*pPostBuffer == '\r' || *pPostBuffer == '\n')
00366             {
00367                contentTypeSize=pPostBuffer-contentTypeStart;
00368                --pPostBuffer;
00369                parseState=HEADER;
00370             }
00371             continue;
00372          }
00373 
00374          case BODY:
00375          {
00376             const size_t size=minPostBufferSize(sizeof(boundarySize)-1);
00377             if(!memcmp(pPostBuffer, boundary.get(), size))
00378             {
00379                bodySize=pPostBuffer-bodyStart-2;
00380                if(bodySize<0) bodySize=0;
00381                else if(bodySize>=2 && *(bodyStart+bodySize-1)=='\n' && *(bodyStart+bodySize-2)=='\r')
00382                   bodySize -= 2;
00383 
00384                if(nameSize != -1)
00385                {
00386                   basic_string<charT> name;
00387                   charToString(nameStart, nameSize, name);
00388 
00389                   Post<charT>& thePost=posts[name];
00390                   if(contentTypeSize != -1)
00391                   {
00392                      thePost.type=Post<charT>::file;
00393                      charToString(contentTypeStart, contentTypeSize, thePost.contentType);
00394                      if(filenameSize != -1) charToString(filenameStart, filenameSize, thePost.filename);
00395                      thePost.m_size=bodySize;
00396                      if(bodySize)
00397                      {
00398                         thePost.m_data = new char[bodySize];
00399                         memcpy(thePost.m_data, bodyStart, bodySize);
00400                      }
00401                   }
00402                   else
00403                   {
00404                      thePost.type=Post<charT>::form;
00405                      charToString(bodyStart, bodySize, thePost.value);
00406                   }
00407                }
00408 
00409                pPostBuffer+=size;
00410                parseState=HEADER;
00411                contentTypeStart=0;
00412                contentTypeSize=-1;
00413                nameStart=0;
00414                nameSize=-1;
00415                filenameStart=0;
00416                filenameSize=-1;
00417                bodyStart=0;
00418                bodySize=-1;
00419             }
00420             continue;
00421          }
00422       }
00423    }
00424 }
00425 
00426 template void Fastcgipp::Http::Environment<char>::parsePostsUrlEncoded();
00427 template void Fastcgipp::Http::Environment<wchar_t>::parsePostsUrlEncoded();
00428 template<class charT> void Fastcgipp::Http::Environment<charT>::parsePostsUrlEncoded()
00429 {
00430    char* nameStart=postBuffer.get();
00431    size_t nameSize;
00432    char* valueStart=0;
00433    size_t valueSize;
00434 
00435    for(char* i=postBuffer.get(); i<=postBuffer.get()+contentLength; ++i)
00436    {
00437       if(*i == '=' && nameStart && !valueStart)
00438       {
00439          nameSize=percentEscapedToRealBytes(nameStart, nameStart, i-nameStart);
00440          valueStart=i+1;
00441       }
00442       else if( (i==postBuffer.get()+contentLength || *i == '&') && nameStart && valueStart)
00443       {
00444          valueSize=percentEscapedToRealBytes(valueStart, valueStart, i-valueStart);
00445 
00446          std::basic_string<charT> name;
00447          charToString(nameStart, nameSize, name);
00448          nameStart=i+1;
00449          Post<charT>& thePost=posts[name];
00450          thePost.type=Post<charT>::form;
00451          charToString(valueStart, valueSize, thePost.value);
00452          valueStart=0;
00453       }
00454    }
00455 }
00456 
00457 bool Fastcgipp::Http::SessionId::seeded=false;
00458 
00459 Fastcgipp::Http::SessionId::SessionId()
00460 {
00461    if(!seeded)
00462    {
00463       std::srand(boost::posix_time::microsec_clock::universal_time().time_of_day().fractional_seconds());
00464       seeded=true;
00465    }
00466 
00467    for(char* i=data; i<data+size; ++i)
00468       *i=char(rand()%256);
00469    timestamp = boost::posix_time::second_clock::universal_time();
00470 }
00471 
00472 template const Fastcgipp::Http::SessionId& Fastcgipp::Http::SessionId::operator=<const char>(const char* data_);
00473 template const Fastcgipp::Http::SessionId& Fastcgipp::Http::SessionId::operator=<const wchar_t>(const wchar_t* data_);
00474 template<class charT> const Fastcgipp::Http::SessionId& Fastcgipp::Http::SessionId::operator=(charT* data_)
00475 {
00476    std::memset(data, 0, size);
00477    base64Decode(data_, data_+size*4/3, data);
00478    timestamp = boost::posix_time::second_clock::universal_time();
00479    return *this;
00480 }
00481 
00482 template void Fastcgipp::Http::decodeUrlEncoded<char>(const char* data, size_t size, std::map<std::basic_string<char>, std::basic_string<char> >& output, const char fieldSeperator);
00483 template void Fastcgipp::Http::decodeUrlEncoded<wchar_t>(const char* data, size_t size, std::map<std::basic_string<wchar_t>, std::basic_string<wchar_t> >& output, const char fieldSeperator);
00484 template<class charT> void Fastcgipp::Http::decodeUrlEncoded(const char* data, size_t size, std::map<std::basic_string<charT>, std::basic_string<charT> >& output, const char fieldSeperator)
00485 {
00486    using namespace std;
00487 
00488    boost::scoped_array<char> buffer(new char[size]);
00489    memcpy(buffer.get(), data, size);
00490 
00491    char* nameStart=buffer.get();
00492    size_t nameSize;
00493    char* valueStart=0;
00494    size_t valueSize;
00495    for(char* i=buffer.get(); i<=buffer.get()+size; ++i)
00496    {
00497       if(*i == ' ' && nameStart && !valueStart)
00498          ++nameStart;
00499 
00500       else if(*i == '=' && nameStart && !valueStart)
00501       {
00502          nameSize=percentEscapedToRealBytes(nameStart, nameStart, i-nameStart);
00503          valueStart=i+1;
00504       }
00505       else if( (i==buffer.get()+size || *i == fieldSeperator) && nameStart && valueStart)
00506       {
00507          valueSize=percentEscapedToRealBytes(valueStart, valueStart, i-valueStart);
00508 
00509          basic_string<charT> name;
00510          charToString(nameStart, nameSize, name);
00511          nameStart=i+1;
00512          basic_string<charT>& value=output[name];
00513          charToString(valueStart, valueSize, value);
00514          valueStart=0;
00515       }
00516    }
00517 }
00518 
00519 const char Fastcgipp::Http::base64Characters[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00520 const char* Fastcgipp::Http::requestMethodLabels[]= {
00521    "ERROR",
00522    "HEAD",
00523    "GET",
00524    "POST",
00525    "PUT",
00526    "DELETE",
00527    "TRACE",
00528    "OPTIONS",
00529    "CONNECT"
00530 };
00531 
00532 template const std::basic_string<char>& Fastcgipp::Http::Environment<char>::findCookie(const char* key) const;
00533 template const std::basic_string<wchar_t>& Fastcgipp::Http::Environment<wchar_t>::findCookie(const wchar_t* key) const;
00534 template<class charT> const std::basic_string<charT>& Fastcgipp::Http::Environment<charT>::findCookie(const charT* key) const
00535 {
00536    static const std::basic_string<charT> emptyString;
00537    typename Cookies::const_iterator it=cookies.find(key);
00538    if(it==cookies.end())
00539       return emptyString;
00540    else
00541       return it->second;
00542 }
00543 
00544 template const std::basic_string<char>& Fastcgipp::Http::Environment<char>::findGet(const char* key) const;
00545 template const std::basic_string<wchar_t>& Fastcgipp::Http::Environment<wchar_t>::findGet(const wchar_t* key) const;
00546 template<class charT> const std::basic_string<charT>& Fastcgipp::Http::Environment<charT>::findGet(const charT* key) const
00547 {
00548    static const std::basic_string<charT> emptyString;
00549    typename Gets::const_iterator it=gets.find(key);
00550    if(it==gets.end())
00551       return emptyString;
00552    else
00553       return it->second;
00554 }
00555 
00556 template const Fastcgipp::Http::Post<char>& Fastcgipp::Http::Environment<char>::findPost(const char* key) const;
00557 template const Fastcgipp::Http::Post<wchar_t>& Fastcgipp::Http::Environment<wchar_t>::findPost(const wchar_t* key) const;
00558 template<class charT> const Fastcgipp::Http::Post<charT>& Fastcgipp::Http::Environment<charT>::findPost(const charT* key) const
00559 {
00560    static const Post<charT> emptyPost;
00561    typename Posts::const_iterator it=posts.find(key);
00562    if(it==posts.end())
00563       return emptyPost;
00564    else
00565       return it->second;
00566 }
00567 
00568 template bool Fastcgipp::Http::Environment<char>::checkForGet(const char* key) const;
00569 template bool Fastcgipp::Http::Environment<wchar_t>::checkForGet(const wchar_t* key) const;
00570 template<class charT> bool Fastcgipp::Http::Environment<charT>::checkForGet(const charT* key) const
00571 {
00572    typename Gets::const_iterator it=gets.find(key);
00573    if(it==gets.end())
00574       return false;
00575    else
00576       return true;
00577 }
00578 
00579 template bool Fastcgipp::Http::Environment<char>::checkForPost(const char* key) const;
00580 template bool Fastcgipp::Http::Environment<wchar_t>::checkForPost(const wchar_t* key) const;
00581 template<class charT> bool Fastcgipp::Http::Environment<charT>::checkForPost(const charT* key) const
00582 {
00583    typename Posts::const_iterator it=posts.find(key);
00584    if(it==posts.end())
00585       return false;
00586    else
00587       return true;
00588 }
00589 
00590 Fastcgipp::Http::Address& Fastcgipp::Http::Address::operator&=(const Address& x)
00591 {
00592    *(uint64_t*)m_data &= *(const uint64_t*)x.m_data;
00593    *(uint64_t*)(m_data+size/2) &= *(const uint64_t*)(x.m_data+size/2);
00594 
00595    return *this;
00596 }
00597 
00598 Fastcgipp::Http::Address Fastcgipp::Http::Address::operator&(const Address& x) const
00599 {
00600    Address address(*this);
00601    address &= x;
00602 
00603    return address;
00604 }
00605 
00606 void Fastcgipp::Http::Address::assign(const char* start, const char* end)
00607 {
00608    const char* read=start-1;
00609    unsigned char* write=m_data;
00610    unsigned char* pad=0;
00611    unsigned char offset;
00612    uint16_t chunk=0;
00613    bool error=false;
00614 
00615    while(1)
00616    {
00617       ++read;
00618       if(read >= end || *read == ':')
00619       {
00620          if(read == start || *(read-1) == ':')
00621          {
00622             if(pad && pad != write)
00623             {
00624                error=true;
00625                break;
00626             }
00627             else
00628                pad = write;
00629          }
00630          else
00631          {
00632             *write = (chunk&0xff00)>>8;
00633             *(write+1) = chunk&0x00ff;
00634             chunk = 0;
00635             write += 2;
00636             if(write>=m_data+size || read >= end)
00637                break;
00638          }
00639          continue;
00640       }
00641       else if('0' <= *read && *read <= '9')
00642          offset = '0';
00643       else if('A' <= *read && *read <= 'Z')
00644          offset = 'A'-10;
00645       else if('a' <= *read && *read <= 'z')
00646          offset = 'a'-10;
00647       else if(*read == '.')
00648       {
00649          if(write == m_data)
00650          {
00651             // We must be getting a pure ipv4 formatted address. Not an ::ffff:xxx.xxx.xxx.xxx style ipv4 address.
00652             *(uint16_t*)write = 0xffff;
00653             pad = m_data;
00654             write+=2;
00655          }
00656          else if(write - m_data > 12)
00657          {
00658             // We don't have enought space for an ipv4 address
00659             error=true;
00660             break;
00661          }
00662 
00663          // First convert the value stored in chunk to the first part of the ipv4 address
00664          *write = 0;
00665          for(int i=0; i<3; ++i)
00666          {
00667             *write = *write * 10 + ((chunk&0x0f00)>>8);
00668             chunk <<= 4;
00669          }
00670          ++write;
00671 
00672          // Now we'll get the remaining pieces
00673          for(int i=0; i<3 && read<end; ++i)
00674          {
00675             const char* point=(const char*)memchr(read, '.', end-read);
00676             if(point && point<end-1)
00677                read=point;
00678             else
00679             {
00680                error=true;
00681                break;
00682             }
00683             *write++ = atoi(++read, end);
00684          }
00685          break;
00686       }
00687       else
00688       {
00689          error=true;
00690          break;
00691       }
00692       chunk <<= 4;
00693       chunk |= *read-offset;
00694    }
00695 
00696    if(error)
00697       std::memset(m_data, 0, size);
00698    else if(pad)
00699    {
00700       if(pad==write)
00701          std::memset(write, 0, size-(write-m_data));
00702       else
00703       {
00704          const size_t padSize=m_data+size-write;
00705          std::memmove(pad+padSize, pad, write-pad);
00706          std::memset(pad, 0, padSize);
00707       }
00708    }
00709 }
00710 
00711 template std::basic_ostream<char, std::char_traits<char> >& Fastcgipp::Http::operator<< <char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >& os, const Address& address);
00712 template std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& Fastcgipp::Http::operator<< <wchar_t, std::char_traits<wchar_t> >(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const Address& address);
00713 template<class charT, class Traits> std::basic_ostream<charT, Traits>& Fastcgipp::Http::operator<<(std::basic_ostream<charT, Traits>& os, const Address& address)
00714 {
00715    using namespace std;
00716    if(!os.good()) return os;
00717    
00718    try
00719    {
00720       typename basic_ostream<charT, Traits>::sentry opfx(os);
00721       if(opfx)
00722       {
00723          streamsize fieldWidth=os.width(0);
00724          charT buffer[40];
00725          charT* bufPtr=buffer;
00726          locale loc(os.getloc(), new num_put<charT, charT*>);
00727 
00728          const uint16_t* subStart=0;
00729          const uint16_t* subEnd=0;
00730          {
00731             const uint16_t* subStartCandidate;
00732             const uint16_t* subEndCandidate;
00733             bool inZero = false;
00734 
00735             for(const uint16_t* it = (const uint16_t*)address.data(); it < (const uint16_t*)(address.data()+Address::size); ++it)
00736             {
00737                if(*it == 0)
00738                {
00739                   if(!inZero)
00740                   {
00741                      subStartCandidate = it;
00742                      subEndCandidate = it;
00743                      inZero=true;
00744                   }
00745                   ++subEndCandidate;
00746                }
00747                else if(inZero)
00748                {
00749                   if(subEndCandidate-subStartCandidate > subEnd-subStart)
00750                   {
00751                      subStart=subStartCandidate;
00752                      subEnd=subEndCandidate-1;
00753                   }
00754                   inZero=false;
00755                }
00756             }
00757             if(inZero)
00758             {
00759                if(subEndCandidate-subStartCandidate > subEnd-subStart)
00760                {
00761                   subStart=subStartCandidate;
00762                   subEnd=subEndCandidate-1;
00763                }
00764                inZero=false;
00765             }
00766          }
00767 
00768          ios_base::fmtflags oldFlags = os.flags();
00769          os.setf(ios::hex, ios::basefield);
00770 
00771          if(subStart==(const uint16_t*)address.data() && subEnd==(const uint16_t*)address.data()+4 && *((const uint16_t*)address.data()+5) == 0xffff)
00772          {
00773             // It is an ipv4 address
00774             *bufPtr++=os.widen(':');
00775             *bufPtr++=os.widen(':');
00776             bufPtr=use_facet<num_put<charT, charT*> >(loc).put(bufPtr, os, os.fill(), static_cast<unsigned long int>(0xffff));
00777             *bufPtr++=os.widen(':');
00778             os.setf(ios::dec, ios::basefield);
00779 
00780             for(const unsigned char* it = address.data()+12; it < address.data()+Address::size; ++it)
00781             {
00782                bufPtr=use_facet<num_put<charT, charT*> >(loc).put(bufPtr, os, os.fill(), static_cast<unsigned long int>(*it));
00783                *bufPtr++=os.widen('.');
00784             }
00785             --bufPtr;
00786          }
00787          else
00788          {
00789             // It is an ipv6 address
00790             for(const uint16_t* it = (const uint16_t*)address.data(); it < (const uint16_t*)(address.data()+Address::size); ++it)
00791             {
00792                if(subStart <= it && it <= subEnd)
00793                {
00794                   if(it == subStart && it == (const uint16_t*)address.data())
00795                      *bufPtr++=os.widen(':');
00796                   if(it == subEnd)
00797                      *bufPtr++=os.widen(':');
00798                }
00799                else
00800                {
00801                   bufPtr=use_facet<num_put<charT, charT*> >(loc).put(bufPtr, os, os.fill(), static_cast<unsigned long int>(Protocol::readBigEndian(*it)));
00802 
00803                   if(it < (const uint16_t*)(address.data()+Address::size)-1)
00804                      *bufPtr++=os.widen(':');
00805                }
00806             }
00807          }
00808 
00809          os.flags(oldFlags);
00810 
00811          charT* ptr=buffer;
00812          ostreambuf_iterator<charT,Traits> sink(os);
00813          if(os.flags() & ios_base::left)
00814             for(int i=max(fieldWidth, bufPtr-buffer); i>0; i--)
00815             {
00816                if(ptr!=bufPtr) *sink++=*ptr++;
00817                else *sink++=os.fill();
00818             }
00819          else
00820             for(int i=fieldWidth-(bufPtr-buffer); ptr!=bufPtr;)
00821             {
00822                if(i>0) { *sink++=os.fill(); --i; }
00823                else *sink++=*ptr++;
00824             }
00825 
00826          if(sink.failed()) os.setstate(ios_base::failbit);
00827       }
00828    }
00829    catch(bad_alloc&)
00830    {
00831       ios_base::iostate exception_mask = os.exceptions();
00832       os.exceptions(ios_base::goodbit);
00833       os.setstate(ios_base::badbit);
00834       os.exceptions(exception_mask);
00835       if(exception_mask & ios_base::badbit) throw;
00836    }
00837    catch(...)
00838    {
00839       ios_base::iostate exception_mask = os.exceptions();
00840       os.exceptions(ios_base::goodbit);
00841       os.setstate(ios_base::failbit);
00842       os.exceptions(exception_mask);
00843       if(exception_mask & ios_base::failbit) throw;
00844    }
00845    return os;
00846 }
00847 
00848 template std::basic_istream<char, std::char_traits<char> >& Fastcgipp::Http::operator>> <char, std::char_traits<char> >(std::basic_istream<char, std::char_traits<char> >& is, Address& address);
00849 template std::basic_istream<wchar_t, std::char_traits<wchar_t> >& Fastcgipp::Http::operator>> <wchar_t, std::char_traits<wchar_t> >(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& is, Address& address);
00850 template<class charT, class Traits> std::basic_istream<charT, Traits>& Fastcgipp::Http::operator>>(std::basic_istream<charT, Traits>& is, Address& address)
00851 {
00852    using namespace std;
00853    if(!is.good()) return is;
00854 
00855    ios_base::iostate err = ios::goodbit;
00856    try
00857    {
00858       typename basic_istream<charT, Traits>::sentry ipfx(is);
00859       if(ipfx)
00860       {
00861          istreambuf_iterator<charT, Traits> read(is);
00862          unsigned char buffer[Address::size];
00863          unsigned char* write=buffer;
00864          unsigned char* pad=0;
00865          unsigned char offset;
00866          unsigned char count=0;
00867          uint16_t chunk=0;
00868          charT lastChar=0;
00869 
00870          for(;;++read)
00871          {
00872             if(++count>40)
00873             {
00874                err = ios::failbit;
00875                break;
00876             }
00877             else if('0' <= *read && *read <= '9')
00878                offset = '0';
00879             else if('A' <= *read && *read <= 'Z')
00880                offset = 'A'-10;
00881             else if('a' <= *read && *read <= 'z')
00882                offset = 'a'-10;
00883             else if(*read == '.')
00884             {
00885                if(write == buffer)
00886                {
00887                   // We must be getting a pure ipv4 formatted address. Not an ::ffff:xxx.xxx.xxx.xxx style ipv4 address.
00888                   *(uint16_t*)write = 0xffff;
00889                   pad = buffer;
00890                   write+=2;
00891                }
00892                else if(write - buffer > 12)
00893                {
00894                   // We don't have enought space for an ipv4 address
00895                   err = ios::failbit;
00896                   break;
00897                }
00898 
00899                // First convert the value stored in chunk to the first part of the ipv4 address
00900                *write = 0;
00901                for(int i=0; i<3; ++i)
00902                {
00903                   *write = *write * 10 + ((chunk&0x0f00)>>8);
00904                   chunk <<= 4;
00905                }
00906                ++write;
00907 
00908                // Now we'll get the remaining pieces
00909                for(int i=0; i<3; ++i)
00910                {
00911                   if(*read != is.widen('.'))
00912                   {
00913                      err = ios::failbit;
00914                      break;
00915                   }
00916                   unsigned int value;
00917                   use_facet<num_get<charT, istreambuf_iterator<charT, Traits> > >(is.getloc()).get(++read, istreambuf_iterator<charT, Traits>(), is, err, value);
00918                   *write++ = value;
00919                }
00920                break;
00921             }
00922             else
00923             {
00924                if(*read == ':' && (!lastChar || lastChar == ':'))
00925                {
00926                   if(pad && pad != write)
00927                   {
00928                      err = ios::failbit;
00929                      break;
00930                   }
00931                   else
00932                      pad = write;
00933                }
00934                else
00935                {
00936                   *write = (chunk&0xff00)>>8;
00937                   *(write+1) = chunk&0x00ff;
00938                   chunk = 0;
00939                   write += 2;
00940                   if(write>=buffer+Address::size)
00941                      break;
00942                   if(*read!=':')
00943                   {
00944                      if(!pad)
00945                         err = ios::failbit;
00946                      break;
00947                   }
00948                }
00949                lastChar=':';
00950                continue;
00951             }
00952             chunk <<= 4;
00953             chunk |= *read-offset;
00954             lastChar=*read;
00955             
00956          }
00957 
00958          if(err == ios::goodbit)
00959          {
00960             if(pad)
00961             {
00962                if(pad==write)
00963                   std::memset(write, 0, Address::size-(write-buffer));
00964                else
00965                {
00966                   const size_t padSize=buffer+Address::size-write;
00967                   std::memmove(pad+padSize, pad, write-pad);
00968                   std::memset(pad, 0, padSize);
00969                }
00970             }
00971             address=buffer;
00972          }
00973          else
00974             is.setstate(err);
00975       }
00976    }
00977    catch(bad_alloc&)
00978    {
00979       ios_base::iostate exception_mask = is.exceptions();
00980       is.exceptions(ios_base::goodbit);
00981       is.setstate(ios_base::badbit);
00982       is.exceptions(exception_mask);
00983       if(exception_mask & ios_base::badbit) throw;
00984    }
00985    catch(...)
00986    {
00987       ios_base::iostate exception_mask = is.exceptions();
00988       is.exceptions(ios_base::goodbit);
00989       is.setstate(ios_base::failbit);
00990       is.exceptions(exception_mask);
00991       if(exception_mask & ios_base::failbit) throw;
00992    }
00993 
00994    return is;
00995 }
00996 
00997 Fastcgipp::Http::Address::operator bool() const
00998 {
00999    static const unsigned char nullString[size] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 
01000    if(std::memcmp(m_data, nullString, size) == 0)
01001       return false;
01002    return true;
01003 }