47 #include <QtCore/QString>
48 #include <QtCore/QFile>
49 #include <QtCore/QDir>
50 #include <QtCore/QRegExp>
51 #include <QtCore/QTextStream>
59 #undef MAX_COOKIE_LIMIT
61 #define MAX_COOKIES_PER_HOST 25
62 #define READ_BUFFER_SIZE 8192
63 #define IP_ADDRESS_EXPRESSION "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
68 #define QL1S(x) QLatin1String(x)
69 #define QL1C(x) QLatin1Char(x)
74 const int index = value.indexOf(
QL1C(
' '));
77 const QString weekday = value.left(index);
78 for (
int i = 1; i < 8; ++i) {
79 if (weekday.startsWith(QDate::shortDayName(i), Qt::CaseInsensitive) ||
80 weekday.startsWith(QDate::longDayName(i), Qt::CaseInsensitive)) {
86 return value.mid(pos);
104 static const char* date_formats[] = {
105 "%:B%t%d%t%H:%M:%S%t%Y%t%Z",
106 "%:B%t%d%t%Y%t%H:%M:%S%t%Z",
110 for (
int i = 0; date_formats[i]; ++i) {
123 return (dt.toMSecsSinceEpoch()/1000);
128 return toEpochSecs(QDateTime::currentDateTimeUtc());
138 default:
return QL1S(
"Dunno");
147 QString advice = _str.toLower();
149 if (advice ==
QL1S(
"accept"))
151 else if (advice ==
QL1S(
"reject"))
153 else if (advice ==
QL1S(
"ask"))
171 int _protocolVersion,
174 bool _explicitPath) :
177 mPath(_path.isEmpty() ?
QString() : _path),
180 mExpireDate(_expireDate),
181 mProtocolVersion(_protocolVersion),
183 mHttpOnly(_httpOnly),
184 mExplicitPath(_explicitPath)
193 if (currentDate == -1)
194 currentDate =
epoch();
207 if (
mName.isEmpty() )
219 result +=
QL1S(
"; $Port");
222 Q_FOREACH(
int port,
mPorts)
223 portNums += QString::number(port) +
QL1C(
' ');
224 result +=
QL1S(
"; $Port=\"") + portNums.trimmed() +
QL1C(
'"');
234 const QString &path,
int port)
const
242 else if (!domains.contains(
mDomain))
249 if ( !domains.contains( domain ) )
267 if( path.startsWith(
mPath) &&
269 (path.length() ==
mPath.length() ) ||
313 if (domain1.isEmpty())
314 domain1 = cookiePtr.
host();
316 QMutableListIterator<KHttpCookie> cookieIterator(*list);
317 while (cookieIterator.hasNext()) {
320 if (domain2.isEmpty())
321 domain2 = cookie.
host();
323 if (cookiePtr.
name() == cookie.
name() &&
324 (nameMatchOnly || (domain1 == domain2 && cookiePtr.
path() == cookie.
path())))
326 if (updateWindowId) {
327 Q_FOREACH(
long windowId, cookie.
windowIds()) {
328 if (windowId && (!cookiePtr.
windowIds().contains(windowId))) {
333 cookieIterator.remove();
352 if (!
parseUrl(_url, fqdn, path, &port))
355 const bool secureRequest = (_url.startsWith(
QL1S(
"https://"), Qt::CaseInsensitive) ||
356 _url.startsWith(
QL1S(
"webdavs://"), Qt::CaseInsensitive));
358 port = (secureRequest ? 443 : 80);
363 for (QStringList::ConstIterator it = domains.constBegin(), itEnd = domains.constEnd();;++it)
368 cookieList = pendingCookies;
387 QMutableListIterator<KHttpCookie> cookieIt (*cookieList);
388 while (cookieIt.hasNext())
399 if (!cookie.
match(fqdn, domains, path, port))
402 if( cookie.
isSecure() && !secureRequest )
419 if (windowId && (cookie.
windowIds().indexOf(windowId) == -1))
425 allCookies.append(cookie);
438 if (!allCookies.isEmpty())
441 cookieStr =
QL1S(
"Cookie: ");
444 cookieStr = cookieStr +
QL1S(
"$Version=") + QString::number(protVersion) +
QL1S(
"; ");
447 cookieStr = cookieStr + cookie.
cookieStr(useDOMFormat) +
QL1S(
"; ");
449 cookieStr.truncate(cookieStr.length() - 2);
468 bool keepQuotes=
false,
469 bool rfcQuotes=
false)
471 const char *s = header;
473 for(; (*s !=
'='); s++)
475 if ((*s==
'\0') || (*s==
';') || (*s==
'\n'))
480 Value =
QL1S(header);
481 Value.truncate( s - header );
482 Value = Value.trimmed();
488 Name.truncate( s - header );
489 Name = Name.trimmed();
495 for(; (*s ==
' ') || (*s ==
'\t'); s++)
497 if ((*s==
'\0') || (*s==
';') || (*s==
'\n'))
505 if ((rfcQuotes || !keepQuotes) && (*s ==
'\"'))
512 for(;(*s !=
'\"');s++)
514 if ((*s==
'\0') || (*s==
'\n'))
517 Value =
QL1S(header);
518 Value.truncate(s - header);
522 Value =
QL1S(header);
525 Value.truncate( ++s - header );
527 Value.truncate( s++ - header );
532 if ((*s==
'\0') || (*s==
';') || (*s==
'\n'))
540 while ((*s !=
'\0') && (*s !=
';') && (*s !=
'\n'))
543 Value =
QL1S(header);
544 Value.truncate( s - header );
545 Value = Value.trimmed();
555 if (domains.count() > 3)
556 _domain = domains[3];
557 else if ( domains.count() > 0 )
558 _domain = domains[0];
566 if (cookie.
domain().isEmpty())
579 if (!kurl.isValid() || kurl.
protocol().isEmpty())
582 _fqdn = kurl.host().toLower();
586 if (_fqdn.contains(
QL1C(
'/')) || _fqdn.contains(
QL1C(
'%')))
604 if (_fqdn.isEmpty()) {
605 _domains.append(
QL1S(
"localhost") );
612 _domains.append( _fqdn );
618 _domains.append( _fqdn );
624 _domains.append(_fqdn);
625 _domains.append(
QL1C(
'.') + _fqdn);
627 QStringList partList = _fqdn.split(
QL1C(
'.'), QString::SkipEmptyParts);
629 if (partList.count())
630 partList.erase(partList.begin());
632 while(partList.count())
635 if (partList.count() == 1)
638 if ((partList.count() == 2) &&
m_twoLevelTLD.contains(partList[1].toLower()))
644 if ((partList.count() == 2) && (partList[1].length() == 2))
648 if (partList[0].length() <= 2)
653 if (
m_gTLDs.contains(partList[0].toLower()))
658 _domains.append(domain);
659 _domains.append(
QL1C(
'.') + domain);
660 partList.erase(partList.begin());
673 const QByteArray &cookie_headers,
684 bool isRFC2965 =
false;
685 bool crossDomain =
false;
686 const char *cookieStr = cookie_headers.constData();
689 const int index = path.lastIndexOf(
QL1C(
'/'));
691 defaultPath = path.left(index);
694 if (qstrncmp(cookieStr,
"Cross-Domain\n", 13) == 0)
704 if (qstrnicmp(cookieStr,
"Set-Cookie:", 11) == 0)
719 cookieList.append(cookie);
721 else if (qstrnicmp(cookieStr,
"Set-Cookie2:", 12) == 0)
725 cookieStr =
parseNameValue(cookieStr+12, Name, Value,
true,
true);
737 cookieList2.append(cookie);
742 while (*cookieStr && *cookieStr !=
'\n')
745 if (*cookieStr ==
'\n')
754 while ((*cookieStr ==
';') || (*cookieStr ==
' '))
760 KHttpCookie& lastCookie = (isRFC2965 ? cookieList2.last() : cookieList.last());
762 if (Name.compare(
QL1S(
"domain"), Qt::CaseInsensitive) == 0)
767 if(dom.length() && dom[0] !=
'.')
770 if(dom.length() > 2 && dom[dom.length()-1] ==
'.')
771 dom = dom.left(dom.length()-1);
773 if(dom.count(
QL1C(
'.')) > 1 || dom ==
".local")
776 else if (Name.compare(
QL1S(
"max-age"), Qt::CaseInsensitive) == 0)
778 int max_age = Value.toInt();
784 else if (Name.compare(
QL1S(
"expires"), Qt::CaseInsensitive) == 0)
794 else if (Name.compare(
QL1S(
"path"), Qt::CaseInsensitive) == 0)
797 lastCookie.
mPath.clear();
799 lastCookie.
mPath = QUrl::fromPercentEncoding(Value.toLatin1());
802 else if (Name.compare(
QL1S(
"version"), Qt::CaseInsensitive) == 0)
806 else if (Name.compare(
QL1S(
"secure"), Qt::CaseInsensitive) == 0 ||
807 (Name.isEmpty() && Value.compare(
QL1S(
"secure"), Qt::CaseInsensitive) == 0))
811 else if (Name.compare(
QL1S(
"httponly"), Qt::CaseInsensitive) == 0 ||
812 (Name.isEmpty() && Value.compare(
QL1S(
"httponly"), Qt::CaseInsensitive) == 0))
816 else if (isRFC2965 && (Name.compare(
QL1S(
"port"), Qt::CaseInsensitive) == 0 ||
817 (Name.isEmpty() && Value.compare(
QL1S(
"port"), Qt::CaseInsensitive) == 0)))
824 lastCookie.
mPorts.append(-1);
825 const bool secureRequest = (_url.startsWith(
QL1S(
"https://"), Qt::CaseInsensitive) ||
826 _url.startsWith(
QL1S(
"webdavs://"), Qt::CaseInsensitive));
828 lastCookie.
mPorts.append(443);
830 lastCookie.
mPorts.append(80);
835 const QStringList portNums = Value.split(
QL1C(
' '), QString::SkipEmptyParts);
836 Q_FOREACH(
const QString& portNum, portNums)
838 const int port = portNum.toInt(&ok);
840 lastCookie.
mPorts.append(port);
846 if (*cookieStr ==
'\0')
854 while(!cookieList2.isEmpty()) {
857 cookieList.append(lastCookie);
858 cookieList2.removeFirst();
871 const QByteArray &cookie_domstring,
877 const char *cookieStr = cookie_domstring.data();
902 cookieList.append(cookie);
904 if (*cookieStr !=
'\0')
917 return item1.
path().length() > item2.
path().length();
921 #ifdef MAX_COOKIE_LIMIT
926 for(
KHttpCookiePtr cookie = cookieList->first(); cookie; cookie = cookieList->next())
933 lastCookie = cookieList->first();
934 cookieList->removeRef(lastCookie);
949 QStringListIterator it (domains);
952 const QString& key = it.next();
991 #ifdef MAX_COOKIE_LIMIT
993 makeRoom(cookieList, cookie);
995 cookieList->push_back(cookie);
998 qStableSort(cookieList->begin(), cookieList->end(),
compareCookies);
1019 if (!cookie.
domain().isEmpty()) {
1020 if (!domains.contains(cookie.
domain()) &&
1030 QStringListIterator it (domains);
1032 const QString& domain = it.next();
1033 if (domain.startsWith(
QL1C(
'.')) || cookie.
host() == domain) {
1073 if (cookieList->
getAdvice() != _advice) {
1079 if ((cookieList->isEmpty()) && (_advice ==
KCookieDunno)) {
1136 if (_domain.isEmpty())
1156 cookieList->erase(cookieIterator);
1158 if ((cookieList->isEmpty()) &&
1171 if (!cookieList || cookieList->isEmpty())
return;
1173 cookieList->clear();
1211 QMutableListIterator<KHttpCookie> cookieIterator(*cookieList);
1212 while (cookieIterator.hasNext()) {
1221 if (ids.contains(windowId)) {
1222 if (ids.count() > 1)
1223 kDebug(7104) <<
"removing window id" << windowId <<
"from session cookie";
1225 kDebug(7104) <<
"deleting session cookie";
1228 if (!ids.removeAll(windowId) || !ids.isEmpty()) {
1231 cookieIterator.remove();
1240 if (ports.isEmpty())
1241 return cookie->
host();
1244 Q_FOREACH(
int port, ports)
1245 portList << QString::number(port);
1247 return (cookie->
host() +
QL1C(
':') + portList.join(
QL1S(
",")));
1258 if (!cookieFile.
open())
1260 cookieFile.setPermissions(QFile::ReadUser|QFile::WriteUser);
1262 QTextStream ts(&cookieFile);
1264 ts <<
"# KDE Cookie File v2\n#\n";
1267 s.sprintf(
"%-20s %-20s %-12s %-10s %-4s %-20s %-4s %s\n",
1268 "# Host",
"Domain",
"Path",
"Exp.date",
"Prot",
1269 "Name",
"Sec",
"Value");
1270 ts << s.toLatin1().constData();
1273 while (it.hasNext())
1275 const QString& domain = it.next();
1276 bool domainPrinted =
false;
1282 QMutableListIterator<KHttpCookie> cookieIterator(*cookieList);
1283 while (cookieIterator.hasNext()) {
1284 const KHttpCookie& cookie = cookieIterator.next();
1287 cookieIterator.remove();
1292 if (!domainPrinted) {
1293 domainPrinted =
true;
1294 ts <<
'[' << domain.toLocal8Bit().data() <<
"]\n";
1302 s.sprintf(
"%-20s %-20s %-12s %10lld %3d %-20s %-4i %s\n",
1303 host.toLatin1().constData(), domain.toLatin1().constData(),
1304 path.toLatin1().constData(), cookie.
expireDate(),
1306 cookie.
name().isEmpty() ? cookie.
value().toLatin1().constData() : cookie.
name().toLatin1().constData(),
1309 cookie.
value().toLatin1().constData());
1310 ts << s.toLatin1().constData();
1318 static const char *
parseField(
char* &buffer,
bool keepQuotes=
false)
1321 if (!keepQuotes && (*buffer ==
'\"'))
1326 while((*buffer !=
'\"') && (*buffer))
1333 while((*buffer !=
' ') && (*buffer !=
'\t') && (*buffer !=
'\n') && (*buffer))
1342 while((*buffer ==
' ') || (*buffer ==
'\t') || (*buffer ==
'\n'))
1354 const int index = str.indexOf(
QL1C(
':'));
1358 const QString host = str.left(index);
1362 Q_FOREACH(
const QString& portStr, portList) {
1363 const int portNum = portStr.toInt(&ok);
1365 ports->append(portNum);
1378 QFile cookieFile (_filename);
1380 if (!cookieFile.open(QIODevice::ReadOnly))
1384 bool success =
false;
1390 if (qstrcmp(buffer,
"# KDE Cookie File\n") == 0)
1394 else if(qstrcmp(buffer,
"# KDE Cookie File v") > 0)
1397 const int verNum = QByteArray(buffer+19, len-19).trimmed().toInt(&ok);
1413 char *line = buffer;
1415 if ((line[0] ==
'#') || (line[0] ==
'['))
1420 if (host.isEmpty() && domain.isEmpty())
1424 if (expStr.isEmpty())
continue;
1425 const qint64 expDate = expStr.toLongLong();
1427 if (verStr.isEmpty())
continue;
1428 int protVer = verStr.toInt();
1430 bool keepQuotes =
false;
1431 bool secure =
false;
1432 bool httpOnly =
false;
1433 bool explicitPath =
false;
1434 const char *value = 0;
1435 if ((version == 2) || (protVer >= 200))
1442 explicitPath = i & 4;
1445 line[strlen(line)-1] =
'\0';
1456 secure = QByteArray(
parseField(line)).toShort();
1460 if (!value || expDate == 0 || expDate < currentTime)
1463 KHttpCookie cookie(host, domain, path, name, value, expDate,
1464 protVer, secure, httpOnly, explicitPath);
1493 while (it.hasNext())
1495 const QString& domain = it.next();
1500 domainSettings.append(value);
1503 policyGroup.
writeEntry(
"CookieDomainAdvice", domainSettings);
1535 for (QStringList::ConstIterator it = domainSettings.constBegin(), itEnd = domainSettings.constEnd();
1539 const int sepPos = value.lastIndexOf(
QL1C(
':'));
1543 const QString domain(value.left(sepPos));
1551 dbg.nospace() << cookie.
cookieStr(
false);