OW_HTTPUtils.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2001-2004 Vintela, Inc. All rights reserved.
00003 *
00004 * Redistribution and use in source and binary forms, with or without
00005 * modification, are permitted provided that the following conditions are met:
00006 *
00007 *  - Redistributions of source code must retain the above copyright notice,
00008 *    this list of conditions and the following disclaimer.
00009 *
00010 *  - Redistributions in binary form must reproduce the above copyright notice,
00011 *    this list of conditions and the following disclaimer in the documentation
00012 *    and/or other materials provided with the distribution.
00013 *
00014 *  - Neither the name of Vintela, Inc. nor the names of its
00015 *    contributors may be used to endorse or promote products derived from this
00016 *    software without specific prior written permission.
00017 *
00018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc. OR THE CONTRIBUTORS
00022 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00028 * POSSIBILITY OF SUCH DAMAGE.
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> // for std::find
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    { // leading empty lines should be ignored.
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 // static
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; // blank line; end of header.
00109       }
00110       size_t len = line.length();
00111       if ( len > line.ltrim().length() ) // continuation "folded" line.
00112       {
00113          if ( key.length() > 1 ) // make sure there is a previous key.
00114          {
00115             map[key].concat(" ");
00116             map[key].concat(line.rtrim());
00117             continue;  // "folding" is probably rare, so this should be sufficient
00118          }
00119          else
00120          {
00121             return false; // continuation expected, but no previous key.
00122             // TODO maybe we should silently ignore bad header
00123             // data instead
00124          }
00125       }
00126       size_t idx = line.indexOf(':');
00127       if ( idx == String::npos ) // no ':' found, and not a continuation line.
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          // multiple headers with the same name (key) MAY be
00139          // present if subsequent headers represent the continuation
00140          // of a comma-seperated list of values.
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    // string should always be two digits.
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    // only up to the first '\0' will be returned.
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    // TODO this is likely too big, but safe.  figure out correct minimal size.
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) ) // Skip whitespace
00361       {
00362          continue;
00363       }
00364       if ( ch == Pad64 )
00365       {
00366          break;
00367       }
00368       b64val = char2val(ch);
00369       if ( b64val == -1 ) // A non-base64 character
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    // We are done decoding Base-64 chars.  Let's see if we ended
00415    // on a byte boundary, and/or with erroneous trailing characters.
00416    if ( ch == Pad64 )     // We got a pad char
00417    {
00418       ch = *src++;      // Skip it, get next
00419       switch ( state )
00420       {
00421       case 0:     // Invalid = in first position
00422       case 1:     // Invalid = in second position
00423          OW_THROW(Base64FormatException, "non-base64 char");
00424       case 2:     // Valid, means one byte of info
00425          // Skip any number of spaces
00426          for ( ; ch != '\0'; ch = *src++ )
00427          {
00428             if ( !isspace(ch) )
00429             {
00430                break;
00431             }
00432          }
00433             // Make sure there is another trailing = sign
00434          if ( ch != Pad64 )
00435          {
00436             OW_THROW(Base64FormatException, "non-base64 char");
00437          }
00438          ch = *src++;      // Skip the =
00439          // Fall through to "single trailing =" case
00440          // FALLTHROUGH
00441       case 3:     // Valid, means two bytes of info
00442          // We know this char is an =.  Is there anything but
00443          // whitespace after it?
00444          for ( ; ch != '\0'; ch = *src++ )
00445          {
00446             if ( !isspace(ch) )
00447             {
00448                OW_THROW(Base64FormatException, "non-base64 char");
00449             }
00450          }
00451             // Now make sure for cases 2 and 3 that the "extra"
00452             // bits that slopped past the last full byte were
00453             // zeros.  If we don't check them, they become a
00454             // subliminal channel.
00455          if ( dest[destidx] != 0 )
00456          {
00457             OW_THROW(Base64FormatException, "non-base64 char");
00458          }
00459       }
00460    }
00461    else
00462    {
00463       // We ended by seeing the end of the string.  Make sure we
00464       // have no partial bytes lying around.
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    // TODO this is likely too big, but safe.  figure out correct minimal size.
00489    AutoPtrVec<char> dest(new char[szdest]);
00490    dest[0] = '\0'; // null terminate in case input is empty
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;        // length of source
00498    enclen = srclen / 3;         // number of 4 byte encodings (source DIV 3)
00499    remlen = srclen - 3 * enclen; // remainder if srclen not divisible by 3 (source MOD 3)
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 /* calculate H(A1) as per spec */
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 /* calculate request-digest/response-digest as per HTTP Digest spec */
00578 void DigestCalcResponse(
00579     const String& sHA1,        /* H(A1) */
00580     const String& sNonce,      /* nonce from server */
00581     const String& sNonceCount, /* 8 hex digits */
00582     const String& sCNonce,     /* client nonce */
00583     const String& sQop,        /* qop-value: "", "auth", "auth-int" */
00584     const String& sMethod,     /* method from the request */
00585     const String& sDigestUri,  /* requested URL */
00586     const String& sHEntity,    /* H(entity body) if qop="auth-int" */
00587     String& sResponse
00588     )
00589 {
00590    String sHA2Hex;
00591    // calculate H(A2)
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    // calculate response
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 // STATIC
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 // STATIC
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 //STATIC
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    { // a "folded" continuation line
00670       headers.push_back(" " + value);
00671    }
00672 }
00674 // STATIC
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       // see rfc 2396 section 2
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 } // end namespace HTTPUtils
00804 } // end namespace OW_NAMESPACE
00805 

Generated on Thu Feb 9 08:48:00 2006 for openwbem by  doxygen 1.4.6