fastcgi++
|
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 }