00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00036 #include "OW_config.h"
00037 #include "OW_HTTPUtils.hpp"
00038 #include "OW_DateTime.hpp"
00039 #include "OW_HTTPStatusCodes.hpp"
00040 #include "OW_HTTPCounter.hpp"
00041 #include "OW_HTTPException.hpp"
00042 #include "OW_StringBuffer.hpp"
00043 #include "OW_AuthenticationException.hpp"
00044 #include "OW_MD5.hpp"
00045 #include "OW_AutoPtr.hpp"
00046 #include "OW_Format.hpp"
00047 #include "OW_ExceptionIds.hpp"
00048
00049 #include <algorithm>
00050 #include <cctype>
00051 #include <cstring>
00052 #include <cstdio>
00053 #ifdef OW_HAVE_ISTREAM
00054 #include <istream>
00055 #else
00056 #include <iostream>
00057 #endif
00058
00059 namespace OW_NAMESPACE
00060 {
00061
00062 OW_DEFINE_EXCEPTION_WITH_ID(Base64Format);
00063
00064 namespace HTTPUtils
00065 {
00066 using std::istream;
00067
00069 const char* const Header_BypassLocker = "OW_BypassLocker";
00070 const char* const HeaderValue_true = "true";
00071 const char* const HeaderValue_false = "false";
00073 bool
00074 parseHeader(HTTPHeaderMap& map, Array<String>& array, istream& istr)
00075 {
00076 String line;
00077 do
00078 {
00079 line = String::getLine(istr);
00080 } while ( line.isSpaces() && istr );
00081
00082 if ( !istr )
00083 {
00084 return false;
00085 }
00086
00087 array = line.tokenize();
00088 return buildMap(map, istr);
00089 }
00091
00092 bool
00093 parseHeader(HTTPHeaderMap& map, istream& istr)
00094 {
00095 return buildMap(map, istr);
00096 }
00098 bool
00099 buildMap(HTTPHeaderMap& map, istream& istr)
00100 {
00101 String line;
00102 String key;
00103 while ( istr )
00104 {
00105 line = String::getLine(istr);
00106 if ( line.isSpaces() )
00107 {
00108 break;
00109 }
00110 size_t len = line.length();
00111 if ( len > line.ltrim().length() )
00112 {
00113 if ( key.length() > 1 )
00114 {
00115 map[key].concat(" ");
00116 map[key].concat(line.rtrim());
00117 continue;
00118 }
00119 else
00120 {
00121 return false;
00122
00123
00124 }
00125 }
00126 size_t idx = line.indexOf(':');
00127 if ( idx == String::npos )
00128 {
00129 return false;
00130 }
00131 key = line.substring(0, idx).toLowerCase();
00132 if ( map.count(key) == 0 )
00133 {
00134 map[key] = line.substring(idx + 1).trim();
00135 }
00136 else
00137 {
00138
00139
00140
00141 map[key].concat(", ");
00142 map[key].concat(line.substring(idx + 1).trim());
00143 }
00144 }
00145 return true;
00146 }
00148 String date( void )
00149 {
00150 DateTime DateTime;
00151 DateTime.setToCurrent();
00152 Array< String > DateTimeArray = DateTime.toString(DateTime::E_UTC_TIME).tokenize();
00153 if ( DateTimeArray.size() < 5 )
00154 {
00155 OW_THROW(HTTPException, "DateTimeArray has less than 5 elements.");
00156 }
00157 String HTTPDateTime = DateTimeArray[ 0 ] + ", " + DateTimeArray[ 2 ] + " " +
00158 DateTimeArray[ 1 ] + " " + DateTimeArray[ 4 ] + " " + DateTimeArray[ 3 ] + " GMT";
00159 return HTTPDateTime;
00160 }
00162 String
00163 status2String(int code)
00164 {
00165 switch ( code )
00166 {
00167 case SC_CONTINUE:
00168 return String("Continue");
00169 case SC_SWITCHING_PROTOCOLS:
00170 return String("Switching protocols");
00171 case SC_OK:
00172 return String("Ok");
00173 case SC_CREATED:
00174 return String("Created");
00175 case SC_ACCEPTED:
00176 return String("Accepted");
00177 case SC_NON_AUTHORITATIVE_INFORMATION:
00178 return String("Non-authoritative");
00179 case SC_NO_CONTENT:
00180 return String("No content");
00181 case SC_RESET_CONTENT:
00182 return String("Reset content");
00183 case SC_PARTIAL_CONTENT:
00184 return String("Partial content");
00185 case SC_MULTIPLE_CHOICES:
00186 return String("Multiple choices");
00187 case SC_MOVED_PERMANENTLY:
00188 return String("Moved permanentently");
00189 case SC_MOVED_TEMPORARILY:
00190 return String("Moved temporarily");
00191 case SC_SEE_OTHER:
00192 return String("See other");
00193 case SC_NOT_MODIFIED:
00194 return String("Not modified");
00195 case SC_USE_PROXY:
00196 return String("Use proxy");
00197 case SC_BAD_REQUEST:
00198 return String("Bad request");
00199 case SC_UNAUTHORIZED:
00200 return String("Unauthorized");
00201 case SC_PAYMENT_REQUIRED:
00202 return String("Payment required");
00203 case SC_FORBIDDEN:
00204 return String("Forbidden");
00205 case SC_NOT_FOUND:
00206 return String("Not found");
00207 case SC_METHOD_NOT_ALLOWED:
00208 return String("Method not allowed");
00209 case SC_NOT_ACCEPTABLE:
00210 return String("Not acceptable");
00211 case SC_PROXY_AUTHENTICATION_REQUIRED:
00212 return String("Proxy auth required");
00213 case SC_REQUEST_TIMEOUT:
00214 return String("Request timeout");
00215 case SC_CONFLICT:
00216 return String("Conflict");
00217 case SC_GONE:
00218 return String("Gone");
00219 case SC_LENGTH_REQUIRED:
00220 return String("Length required");
00221 case SC_PRECONDITION_FAILED:
00222 return String("Precondition failed");
00223 case SC_REQUEST_ENTITY_TOO_LARGE:
00224 return String("Request entity too large");
00225 case SC_REQUEST_URI_TOO_LONG:
00226 return String("Request URI too large");
00227 case SC_UNSUPPORTED_MEDIA_TYPE:
00228 return String("Unsupported media type");
00229 case SC_INTERNAL_SERVER_ERROR:
00230 return String("Internal server error");
00231 case SC_NOT_IMPLEMENTED:
00232 return String("Not implemented");
00233 case SC_BAD_GATEWAY:
00234 return String("Bad gateway");
00235 case SC_SERVICE_UNAVAILABLE:
00236 return String("Service unavailable");
00237 case SC_GATEWAY_TIMEOUT:
00238 return String("Gateway timeout");
00239 case SC_HTTP_VERSION_NOT_SUPPORTED:
00240 return String("HTTP version not supported");
00241 case SC_NOT_EXTENDED:
00242 return String("Not Extended");
00243 default:
00244 return String() ;
00245 }
00246 }
00248 static HTTPCounter theCounter;
00250 String
00251 getCounterStr()
00252 {
00253 int count = theCounter.getNextCounter();
00254
00255 if ( count < 10 )
00256 {
00257 return("0" + String(count));
00258 }
00259 else
00260 {
00261 return String(count);
00262 }
00263 }
00264
00266 static const char* const Base64 =
00267 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00268 static const char Pad64 = '=';
00270 String base64Decode(const String& arg)
00271 {
00272
00273 return String(&base64Decode(arg.c_str())[0]);
00274 }
00275 static int char2val(char c)
00276 {
00277 switch (c)
00278 {
00279 case 'A': return 0;
00280 case 'B': return 1;
00281 case 'C': return 2;
00282 case 'D': return 3;
00283 case 'E': return 4;
00284 case 'F': return 5;
00285 case 'G': return 6;
00286 case 'H': return 7;
00287 case 'I': return 8;
00288 case 'J': return 9;
00289 case 'K': return 10;
00290 case 'L': return 11;
00291 case 'M': return 12;
00292 case 'N': return 13;
00293 case 'O': return 14;
00294 case 'P': return 15;
00295 case 'Q': return 16;
00296 case 'R': return 17;
00297 case 'S': return 18;
00298 case 'T': return 19;
00299 case 'U': return 20;
00300 case 'V': return 21;
00301 case 'W': return 22;
00302 case 'X': return 23;
00303 case 'Y': return 24;
00304 case 'Z': return 25;
00305 case 'a': return 26;
00306 case 'b': return 27;
00307 case 'c': return 28;
00308 case 'd': return 29;
00309 case 'e': return 30;
00310 case 'f': return 31;
00311 case 'g': return 32;
00312 case 'h': return 33;
00313 case 'i': return 34;
00314 case 'j': return 35;
00315 case 'k': return 36;
00316 case 'l': return 37;
00317 case 'm': return 38;
00318 case 'n': return 39;
00319 case 'o': return 40;
00320 case 'p': return 41;
00321 case 'q': return 42;
00322 case 'r': return 43;
00323 case 's': return 44;
00324 case 't': return 45;
00325 case 'u': return 46;
00326 case 'v': return 47;
00327 case 'w': return 48;
00328 case 'x': return 49;
00329 case 'y': return 50;
00330 case 'z': return 51;
00331 case '0': return 52;
00332 case '1': return 53;
00333 case '2': return 54;
00334 case '3': return 55;
00335 case '4': return 56;
00336 case '5': return 57;
00337 case '6': return 58;
00338 case '7': return 59;
00339 case '8': return 60;
00340 case '9': return 61;
00341 case '+': return 62;
00342 case '/': return 63;
00343 default: return -1;
00344 }
00345 }
00347 Array<char> base64Decode(const char* src)
00348 {
00349 int szdest = strlen(src) * 2;
00350
00351 AutoPtrVec<char> dest(new char[szdest]);
00352 memset(dest.get(), '\0', szdest);
00353 int destidx, state, ch;
00354 int b64val;
00355 state = 0;
00356 destidx = 0;
00357 while ( (ch = *src) != '\0' )
00358 {
00359 ++src;
00360 if ( isspace(ch) )
00361 {
00362 continue;
00363 }
00364 if ( ch == Pad64 )
00365 {
00366 break;
00367 }
00368 b64val = char2val(ch);
00369 if ( b64val == -1 )
00370 {
00371 OW_THROW(Base64FormatException, "non-base64 char");
00372 }
00373 switch ( state )
00374 {
00375 case 0:
00376 if ( destidx >= szdest )
00377 {
00378 OW_THROW(Base64FormatException, "non-base64 char");
00379 }
00380 dest[destidx] = b64val << 2;
00381 state = 1;
00382 break;
00383 case 1:
00384 if ( destidx + 1 >= szdest )
00385 {
00386 OW_THROW(Base64FormatException, "non-base64 char");
00387 }
00388 dest[destidx] |= b64val >> 4;
00389 dest[destidx+1] = (b64val & 0x0f) << 4 ;
00390 destidx++;
00391 state = 2;
00392 break;
00393 case 2:
00394 if ( destidx + 1 >= szdest )
00395 {
00396 OW_THROW(Base64FormatException, "non-base64 char");
00397 }
00398 dest[destidx] |= b64val >> 2;
00399 dest[destidx+1] = (b64val & 0x03) << 6;
00400 destidx++;
00401 state = 3;
00402 break;
00403 case 3:
00404 if ( destidx >= szdest )
00405 {
00406 OW_THROW(Base64FormatException, "non-base64 char");
00407 }
00408 dest[destidx] |= b64val;
00409 destidx++;
00410 state = 0;
00411 break;
00412 }
00413 }
00414
00415
00416 if ( ch == Pad64 )
00417 {
00418 ch = *src++;
00419 switch ( state )
00420 {
00421 case 0:
00422 case 1:
00423 OW_THROW(Base64FormatException, "non-base64 char");
00424 case 2:
00425
00426 for ( ; ch != '\0'; ch = *src++ )
00427 {
00428 if ( !isspace(ch) )
00429 {
00430 break;
00431 }
00432 }
00433
00434 if ( ch != Pad64 )
00435 {
00436 OW_THROW(Base64FormatException, "non-base64 char");
00437 }
00438 ch = *src++;
00439
00440
00441 case 3:
00442
00443
00444 for ( ; ch != '\0'; ch = *src++ )
00445 {
00446 if ( !isspace(ch) )
00447 {
00448 OW_THROW(Base64FormatException, "non-base64 char");
00449 }
00450 }
00451
00452
00453
00454
00455 if ( dest[destidx] != 0 )
00456 {
00457 OW_THROW(Base64FormatException, "non-base64 char");
00458 }
00459 }
00460 }
00461 else
00462 {
00463
00464
00465 if ( state != 0 )
00466 {
00467 OW_THROW(Base64FormatException, "non-base64 char");
00468 }
00469 }
00470 Array<char> rval(dest.get(), dest.get()+destidx+1);
00471 return rval;
00472 }
00474 String base64Encode(const String& arg)
00475 {
00476 return base64Encode(reinterpret_cast<const UInt8*>(arg.c_str()), arg.length());
00477 }
00479 String base64Encode(const char* src)
00480 {
00481 return base64Encode(reinterpret_cast<const UInt8*>(src), ::strlen(src));
00482 }
00483
00485 String base64Encode(const UInt8* src, size_t len)
00486 {
00487 int szdest = len * 3 + 4;
00488
00489 AutoPtrVec<char> dest(new char[szdest]);
00490 dest[0] = '\0';
00491 char a, b, c, d;
00492 char *dst(0);
00493 const UInt8* cp(0);
00494 int i, srclen, enclen, remlen;
00495 cp = src;
00496 dst = dest.get();
00497 srclen = len;
00498 enclen = srclen / 3;
00499 remlen = srclen - 3 * enclen;
00500 for ( i = 0; i < enclen; i++ )
00501 {
00502 a = (cp[0] >> 2);
00503 b = (cp[0] << 4) & 0x30;
00504 b |= (cp[1] >> 4);
00505 c = (cp[1] << 2) & 0x3c;
00506 c |= (cp[2] >> 6);
00507 d = cp[2] & 0x3f;
00508 cp +=3;
00509 if ( dst + 6 - dest.get() > szdest )
00510 {
00511 OW_THROW(Base64FormatException, "buffer too small");
00512 }
00513 sprintf(dst, "%c%c%c%c",Base64[a],Base64[b],Base64[c],Base64[d]);
00514 dst+=4;
00515 }
00516 if ( remlen == 1 )
00517 {
00518 a = (cp[0] >> 2);
00519 b = (cp[0] << 4) & 0x30;
00520 if ( dst + 6 - dest.get() > szdest )
00521 {
00522 OW_THROW(Base64FormatException, "buffer too small");
00523 }
00524 sprintf(dst, "%c%c==",Base64[a],Base64[b]);
00525 dst+=4;
00526 }
00527 else if ( remlen == 2 )
00528 {
00529 a = (cp[0] >> 2);
00530 b = (cp[0] << 4) & 0x30 ;
00531 b |= (cp[1] >> 4);
00532 c = (cp[1] << 2) & 0x3c;
00533 if ( dst + 6 - dest.get() > szdest )
00534 {
00535 OW_THROW(Base64FormatException, "non-base64 char");
00536 }
00537 sprintf(dst, "%c%c%c=",Base64[a],Base64[b],Base64[c]);
00538 dst+=4;
00539 }
00540 String rval(String::E_TAKE_OWNERSHIP, dest.get(), dst-dest.get());
00541 dest.release();
00542 return rval;
00543 }
00545 #ifndef OW_DISABLE_DIGEST
00546
00547 void DigestCalcHA1(
00548 const String &sAlg,
00549 const String &sUserName,
00550 const String &sRealm,
00551 const String &sPassword,
00552 const String &sNonce,
00553 const String &sCNonce,
00554 String &sSessionKey
00555 )
00556 {
00557 MD5 md5;
00558 md5.update(sUserName);
00559 md5.update(":");
00560 md5.update(sRealm);
00561 md5.update(":");
00562 md5.update(sPassword);
00563 sSessionKey = md5.toString();
00564 if ( sAlg.equalsIgnoreCase( "md5-sess" ))
00565 {
00566 unsigned char sHA1[MD5HASHLEN];
00567 memcpy(sHA1, md5.getDigest(), MD5HASHLEN);
00568 MD5 md5_2;
00569 md5_2.update(reinterpret_cast<const char*>(sHA1));
00570 md5_2.update(":");
00571 md5_2.update(sNonce);
00572 md5_2.update(":");
00573 md5_2.update(sCNonce);
00574 sSessionKey = md5_2.toString();
00575 };
00576 };
00577
00578 void DigestCalcResponse(
00579 const String& sHA1,
00580 const String& sNonce,
00581 const String& sNonceCount,
00582 const String& sCNonce,
00583 const String& sQop,
00584 const String& sMethod,
00585 const String& sDigestUri,
00586 const String& sHEntity,
00587 String& sResponse
00588 )
00589 {
00590 String sHA2Hex;
00591
00592 MD5 md5;
00593 md5.update(sMethod);
00594 md5.update(":");
00595 md5.update(sDigestUri);
00596 if ( sQop.equalsIgnoreCase( "auth-int" ))
00597 {
00598 md5.update(":");
00599 md5.update(sHEntity);
00600 };
00601 sHA2Hex = md5.toString();
00602
00603 MD5 md5new;
00604 md5new.update(sHA1);
00605 md5new.update(":");
00606 md5new.update(sNonce);
00607 md5new.update(":");
00608 if ( !sQop.empty())
00609 {
00610 md5new.update(sNonceCount);
00611 md5new.update(":");
00612 md5new.update(sCNonce);
00613 md5new.update(":");
00614 md5new.update(sQop);
00615 md5new.update(":");
00616 };
00617 md5new.update(sHA2Hex);
00618 sResponse = md5new.toString();
00619 };
00620 #endif
00621
00622
00623 bool
00624 headerHasKey(const HTTPHeaderMap& headers, const String& key)
00625 {
00626 HTTPHeaderMap::const_iterator i =
00627 headers.find(key.toString().toLowerCase());
00628 if ( i != headers.end() )
00629 {
00630 return true;
00631 }
00632 else
00633 {
00634 return false;
00635 }
00636 }
00638
00639 String
00640 getHeaderValue(const HTTPHeaderMap& headers, const String& key)
00641 {
00642 HTTPHeaderMap::const_iterator i =
00643 headers.find(key.toString().toLowerCase());
00644 if ( i != headers.end() )
00645 {
00646 return(*i).second;
00647 }
00648 else
00649 {
00650 return String();
00651 }
00652 }
00654
00655 void
00656 addHeader(Array<String>& headers, const String& key, const String& value)
00657 {
00658 String tmpKey = key;
00659 tmpKey.trim();
00660 if ( !tmpKey.empty())
00661 {
00662 String newHeader = key + ": " + value;
00663 if (std::find(headers.begin(), headers.end(), newHeader) == headers.end())
00664 {
00665 headers.push_back(newHeader);
00666 }
00667 }
00668 else
00669 {
00670 headers.push_back(" " + value);
00671 }
00672 }
00674
00675 void
00676 eatEntity(istream& istr)
00677 {
00678 while ( istr )
00679 {
00680 istr.get();
00681 }
00682 }
00683 void
00684 decodeBasicCreds(const String& info, String& name, String& password)
00685 {
00686 String decoded = info;
00687 size_t idx = decoded.indexOf("Basic");
00688 if (idx == String::npos)
00689 {
00690 OW_THROW(AuthenticationException, "Authentication info is not type "
00691 "\"Basic\"");
00692 }
00693 decoded = decoded.substring(idx + 6);
00694 try
00695 {
00696 decoded = base64Decode(decoded);
00697 }
00698 catch (Base64FormatException &e)
00699 {
00700 OW_THROW(AuthenticationException, "invalid BASE64 encoding of "
00701 "credentials");
00702 }
00703 size_t icolon = decoded.indexOf(':');
00704 if (icolon == String::npos)
00705 {
00706 OW_THROW(AuthenticationException, "invalid credentials syntax");
00707 }
00708 else
00709 {
00710 name = decoded.substring(0,icolon);
00711 password = decoded.substring(icolon + 1);
00712 }
00713 }
00714
00716 String escapeCharForURL(char c)
00717 {
00718 char rval[4];
00719 rval[0] = '%';
00720 UInt8 hi = UInt8(c) >> 4;
00721 rval[1] = hi >= 10 ? hi - 10 + 'A' : hi + '0';
00722 UInt8 low = UInt8(c) & 0xF;
00723 rval[2] = low >= 10 ? low - 10 + 'A' : low + '0';
00724 rval[3] = '\0';
00725 return String(rval);
00726 }
00727
00728 OW_DEFINE_EXCEPTION_WITH_ID(UnescapeCharForURL);
00729
00730 namespace {
00731 inline char digitToVal(char c)
00732 {
00733 return isdigit(c) ? c - '0' : toupper(c) - 'A' + 10;
00734 }
00735 }
00736
00738 char unescapeCharForURL(const char* str)
00739 {
00740 if (strlen(str) < 3 || str[0] != '%' || !isxdigit(str[1]) || !isxdigit(str[2]))
00741 {
00742 OW_THROW(UnescapeCharForURLException, Format("Invalid escape: %1", str).c_str());
00743 }
00744 return (digitToVal(str[1]) << 4 ) | digitToVal(str[2]);
00745 }
00746
00748 String escapeForURL(const String& input)
00749 {
00750 StringBuffer rval;
00751 for (size_t i = 0; i < input.length(); ++i)
00752 {
00753 switch (input[i])
00754 {
00755
00756 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
00757 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
00758 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
00759 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
00760 case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':
00761 case 'e': case 'f': case 'g': case 'h': case 'i': case 'j':
00762 case 'k': case 'l': case 'm': case 'n': case 'o': case 'p':
00763 case 'q': case 'r': case 's': case 't': case 'u': case 'v':
00764 case 'w': case 'x': case 'y': case 'z':
00765
00766 case '0': case '1': case '2': case '3': case '4': case '5':
00767 case '6': case '7': case '8': case '9':
00768
00769 case '-': case '_': case '.': case '!': case '~':
00770 case '*': case '\'': case '(': case ')':
00771 rval += input[i];
00772 break;
00773 default:
00774 rval += escapeCharForURL(input[i]);
00775 break;
00776 }
00777 }
00778 return rval.releaseString();
00779 }
00780
00782 String unescapeForURL(const String& input)
00783 {
00784 StringBuffer rval(input.length());
00785 const char* pos = input.c_str();
00786 while (*pos != '\0')
00787 {
00788 if (*pos == '%')
00789 {
00790 rval += unescapeCharForURL(pos);
00791 pos += 3;
00792 }
00793 else
00794 {
00795 rval += *pos;
00796 ++pos;
00797 }
00798 }
00799 return rval.releaseString();
00800 }
00801
00802
00803 }
00804 }
00805