Uri.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2006-2007 by Peter Dimov.
00004 
00005 This file is part of Calitko (http://www.calitko.org).
00006 
00007 Calitko is free software; you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation; either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 Calitko is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Calitko; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 */
00022 
00023 #include "Qt.h"
00024 #include "Uri.h"
00025 #include "Imports.cpp"
00026 
00028 Uri::Uri()
00029     : d (new PrivateData)
00030 {
00031 }
00032 
00034 
00039 Uri::Uri (const Uri &other)
00040     : d (other.d)
00041 {
00042 }
00043 
00045 
00049 Uri & Uri::operator= (const Uri &other)
00050 {
00051     d = other.d;
00052     return *this;
00053 }
00054 
00056 
00062 QByteArray Uri::authority() const
00063 {
00064     QByteArray res;
00065     if (!d->userInfo.isEmpty())
00066         res += d->userInfo + '@';
00067     res += d->host;
00068     if (!d->port.isEmpty())
00069         res += ':' + d->port;
00070     return res;
00071 }
00072 
00074 QByteArray Uri::query() const
00075 {
00076     QByteArray query (d->query);
00077     bool decoded = doDecode (query, true);
00078     Q_ASSERT (decoded); // Must have been encoded correctly.
00079     return query;
00080 }
00081 
00083 
00094 QByteArray Uri::queryItemValue (const QByteArray &key) const
00095 {
00096     foreach (const QueryItem item, queryItems()) {
00097         if (item.first == key)
00098             return item.second;
00099     }
00100     return QByteArray();
00101 }
00102 
00104 
00109 Uri::QueryItemList Uri::queryItems() const
00110 {
00111     if (d->query.isEmpty())
00112         return QueryItemList();
00113 
00114     QueryItemList list;
00115     QList <QByteArray> items = d->query.split ('&');
00116     foreach (QByteArray item, items) {
00117         QList <QByteArray> subitems = item.split ('=');
00118         QByteArray key = subitems.value (0);
00119         QByteArray value = subitems.value (1);
00120         bool decoded = doDecode (key, true);
00121         Q_ASSERT (decoded); // Must have been verified when parsing.
00122         decoded = doDecode (value, true);
00123         Q_ASSERT (decoded); // Must have been verified when parsing.
00124         list += QueryItem (key, value);
00125     }
00126     return list;
00127 }
00128 
00130 
00138 QByteArray Uri::toQByteArray (Fields fields, bool encode) const
00139 {
00140     QByteArray res;
00141     // scheme ":"
00142     if (!d->scheme.isNull() && fields & Scheme)
00143         res += d->scheme + ':'; // \todo encode?
00144     // "//" [userInfo "@"] host [":" port] (only hierarchical schemes)
00145     if (!d->host.isNull() && fields & Authority) {
00146         res += "//";
00147         if (!d->userInfo.isNull() && fields & UserInfo)
00148             res += doEncode (d->userInfo, encode, UserInfoEncodeExcludeChars)
00149                 += '@';
00150         if (fields & Host)
00151             res += doEncode (d->host, encode, HostEncodeExcludeChars);
00152         if (!d->port.isNull() && fields & Port)
00153             res += ':' + d->port; // \todo encode?
00154     }
00155     if (fields & Path) // path
00156         res += doEncode (d->path, encode, PathEncodeExcludeChars);
00157     // "?" query (optional)
00158     if (!d->query.isNull() && fields & Query)
00159         if (encode)
00160             res += '?' + d->query; // which is already stored in encoded form.
00161         else {
00162             QByteArray query = d->query;
00163             bool decoded = doDecode (query, true);
00164             Q_ASSERT (decoded); // Must have been encoded correctly!
00165             res += '?' + query;
00166         }
00167     // "#" fragment (optional)
00168     if (!d->fragment.isNull() && fields & Fragment)
00169         res += '#' + doEncode (d->fragment, encode, FragmentEncodeExcludeChars);
00170     // Done!
00171     return res;
00172 }
00173 
00175 
00185 QByteArray Uri::doEncode (const QByteArray &bytes, bool encode,
00186                           const QByteArray &charsToExclude)
00187 {
00188     if (encode) {
00189         QByteArray res;
00190         // Each octet will be encoded in at most 3 unreserved octets:
00191         res.reserve (bytes.length() * 3);
00192         foreach (char ch, bytes)
00193             if (shouldEncode (ch, charsToExclude))
00194                 res += quoted (ch);
00195             else
00196                 res += ch;
00197         return res;
00198     } else
00199         return bytes;
00200 }
00201 
00203 
00212 bool Uri::shouldEncode (char ch, const QByteArray &charsToExclude)
00213 {
00214     if (ch >= 'a' && ch <= 'z'
00215         || ch >= 'A' && ch <= 'Z'
00216         || ch >= '0' && ch <= '9'
00217         || ch == '-'
00218         || ch == '.'
00219         || ch == '_'
00220         || ch == '~'
00221         || charsToExclude.indexOf(ch) != -1) // other excluded from encoding
00222         return false;
00223     else
00224         return true;
00225 }
00226 
00228 
00233 char Uri::hex (char ch)
00234 {
00235     static char hexmap[] = "0123456789ABCDEF";
00236     return hexmap [ch & 0x0F];
00237 }
00238 
00240 
00246 QByteArray Uri::quoted (char ch)
00247 {
00248     return QByteArray ("%") + hex (ch >> 4) + hex (ch);
00249 }
00250 
00252 
00262 QByteArray Uri::unencoded (Fields fields) const
00263 {
00264     return toQByteArray (fields, false);
00265 }
00266 
00268 
00299 QByteArray Uri::encoded (Fields fields) const
00300 {
00301     return toQByteArray (fields, true);
00302 }
00303 
00324 bool Uri::parse (const QByteArray &bytes, bool decode)
00325 {
00326     QRegExp re ("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?",
00327                  Qt::CaseSensitive, QRegExp::RegExp2);
00328     if (re.indexIn (QString::fromLatin1 (bytes)) > -1) {
00329         if (!re.cap (1).isEmpty()) { // scheme ":"
00330             d->scheme = re.cap (2).toLatin1();
00331             if (!doDecode (d->scheme, decode))
00332                 return false;
00333         }
00334         if (!re.cap (3).isEmpty()) { // "//" authority
00335             if (!parseAuthority (re.cap (4).toLatin1(), decode))
00336                 return false;
00337         }
00338         if (!re.cap (5).isEmpty()) { // path
00339             d->path = re.cap (5).toLatin1();
00340             if (!doDecode (d->path, decode))
00341                 return false;
00342         }
00343         if (!re.cap (6).isEmpty()) { // "?" query
00344             d->query = re.cap (7).toLatin1();
00345             // We do not want to store query in decoded form but we want to
00346             // check whether it could be parsed:
00347             QByteArray query = d->query;
00348             if (!doDecode (query, decode))
00349                 return false;
00350             ensureNotNull (d->query);
00351         }
00352         if (!re.cap (8).isEmpty()) { // "#" fragment
00353             d->fragment = re.cap (9).toLatin1();
00354             if (!doDecode (d->fragment, decode))
00355                 return false;
00356         }
00357         return true;
00358     } else
00359         return false;
00360 }
00361 
00363 
00381 bool Uri::doDecode (QByteArray &bytes, bool decode)
00382 {
00383     if (decode) {
00384         QByteArray res;
00385         // Decoded string should be no longer than the undecoded one:
00386         res.reserve (bytes.length());
00387         const char *pos = bytes.constData();
00388         const char *end = bytes.constData() + bytes.length();
00389         for (; pos != end; ++pos) {
00390             if (*pos != '%')
00391                 res += *pos;
00392             else if (pos + 2 < end) {
00393                 char hex1 = *++pos;
00394                 char hex2 = *++pos;
00395                 if (fromHex (hex1) && fromHex (hex2))
00396                     res += unquoted (hex1, hex2);
00397                 else
00398                     return false;
00399             } else
00400                 return false;
00401         }
00402         bytes = res;
00403     }
00404     ensureNotNull (bytes);
00405     return true;
00406 }
00407 
00409 
00418 bool Uri::fromHex (char &hex)
00419 {
00420     if (hex >= 'a' && hex <= 'f')
00421         hex -= 'a' - 10;
00422     else if (hex >= 'A' && hex <= 'F')
00423         hex -= 'A' - 10;
00424     else if (hex >= '0' && hex <= '9')
00425         hex -= '0';
00426     else
00427         return false;
00428     return true;
00429 }
00430 
00432 
00438 char Uri::unquoted (char hex1, char hex2)
00439 {
00440     return hex1 << 4 | hex2;
00441 }
00442 
00444 
00453 void Uri::setAuthority (const QByteArray &authority)
00454 {
00455     Uri tempUri = *this;
00456     if (tempUri.parseAuthority (authority, false))
00457         *this = tempUri;
00458     else
00459         Q_ASSERT (false); // Invalid authority component was set!
00460 }
00461 
00463 
00472 void Uri::setAuthority (const QByteArray &userInfo,
00473                         const QByteArray &host,
00474                         const QByteArray &port)
00475 {
00476     Q_ASSERT (!host.isEmpty());
00477     d->userInfo = userInfo;
00478     d->host = host;
00479     d->port = port;
00480 }
00481 
00483 
00491 bool Uri::parseAuthority (const QByteArray &authority, bool decode)
00492 {
00493     int userInfoEnd = authority.indexOf ('@');
00494     if (userInfoEnd >= 0) {
00495         d->userInfo = authority.left (userInfoEnd++);
00496         if (!doDecode (d->userInfo, decode))
00497             return false;
00498     } else
00499         userInfoEnd = 0;
00500     int hostEnd = authority.indexOf (':', userInfoEnd);
00501     if (hostEnd >= 0) {
00502         d->port = authority.mid (hostEnd +1);
00503         if (!doDecode (d->port, decode))
00504             return false;
00505     } else
00506         hostEnd = authority.length();
00507 
00508     d->host = authority.mid (userInfoEnd, hostEnd - userInfoEnd);
00509     if (!doDecode (d->host, decode))
00510         return false;
00511     // The authority component is only valid if host is not empty when
00512     // userInfo and/or port are set.
00513     if (d->host.isEmpty()
00514         && (!d->port.isEmpty() || !d->userInfo.isEmpty()))
00515         return false;
00516     else
00517         return true;
00518 }
00519 
00521 void Uri::setQuery (const QByteArray &query)
00522 {
00523     d->query = doEncode (query, true, QueryEncodeExcludeChars);
00524 }
00525 
00527 
00536 void Uri::ensureNotNull (QByteArray &bytes)
00537 {
00538     if (bytes.isNull())
00539         bytes = QByteArray (""); // forces bytes.isNull() to return false.
00540 }
00541 
00543 
00547 void Uri::appendQuery (const QByteArray &bytes)
00548 {
00549     d->query += doEncode (bytes, true, QueryEncodeExcludeChars);
00550 }
00551 
00553 
00565 void Uri::appendQueryItem (const QByteArray &key, const QByteArray &value)
00566 {
00567     QByteArray query = d->query;
00568     if (!query.isEmpty())
00569         query += '&';
00570     query += doEncode (key, true, QueryItemEncodeExcludeChars);
00571     if (!value.isEmpty())
00572         query += '=' + doEncode (value, true, QueryItemEncodeExcludeChars);
00573     d->query = query;
00574 }
00575 
00579 void Uri::appendQueryItem (const QueryItem &item)
00580 {
00581     appendQueryItem (item.first, item.second);
00582 }
00583 
00585 
00589 void Uri::appendQueryItems (const QueryItemList &itemList)
00590 {
00591     foreach (QueryItem item, itemList)
00592         appendQueryItem (item);
00593 }
00594 
00596 
00601 bool Uri::operator== (const Uri &other) const
00602 {
00603     return unencoded() == other.unencoded();
00604 }
00605 
00607 
00612 bool Uri::operator!= (const Uri &other) const
00613 {
00614     return !(*this == other);
00615 }
00616 
00618 
00624 Uri Uri::fromUnencoded (const QByteArray &bytes)
00625 {
00626     Uri temp;
00627     if (temp.parse (bytes, false))
00628         return temp;
00629     else
00630         return Uri();
00631 }
00632 
00634 
00640 Uri Uri::fromEncoded (const QByteArray &bytes)
00641 {
00642     Uri temp;
00643     if (temp.parse (bytes, true))
00644         return temp;
00645     else
00646         return Uri();
00647 }
00648 
00649 uint Utils::qHash (const Uri &uri)
00650 {
00651     return qHash (uri.unencoded());
00652 }
00653 
00654 const QByteArray Uri::UserInfoEncodeExcludeChars    ("!$&'()*+,;=:");
00655 const QByteArray Uri::HostEncodeExcludeChars        ("[]:");
00656 const QByteArray Uri::PathEncodeExcludeChars        ("!$&'()*+,;=:@/");
00657 const QByteArray Uri::QueryEncodeExcludeChars       ("!$&'()*+,;=:@/?");
00658 const QByteArray Uri::QueryItemEncodeExcludeChars   ("!$'()*+,;:@/?");
00659 const QByteArray Uri::FragmentEncodeExcludeChars    ("!$&'()*+,;=:@/?");