2
* Copyright (C) 2010, 2011 Tuomo Penttinen, all rights reserved.
4
* Author: Tuomo Penttinen <tp@herqq.org>
6
* This file is part of Herqq UPnP (HUPnP) library.
8
* Herqq UPnP is free software: you can redistribute it and/or modify
9
* it under the terms of the GNU Lesser General Public License as published by
10
* the Free Software Foundation, either version 3 of the License, or
11
* (at your option) any later version.
13
* Herqq UPnP is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU Lesser General Public License for more details.
18
* You should have received a copy of the GNU Lesser General Public License
19
* along with Herqq UPnP. If not, see <http://www.gnu.org/licenses/>.
22
#include "hproduct_tokens.h"
24
#include "../general/hlogger_p.h"
26
#include <QtCore/QRegExp>
27
#include <QtCore/QVector>
35
/*******************************************************************************
37
******************************************************************************/
38
HProductToken::HProductToken() :
39
m_token(), m_productVersion()
43
HProductToken::HProductToken(const QString& token, const QString& productVersion) :
44
m_token(), m_productVersion()
48
QString tokenTmp(token.simplified());
49
QString productVersionTmp(productVersion.simplified());
50
if (tokenTmp.isEmpty() || productVersionTmp.isEmpty())
53
"Invalid product token. Token: [%1], Product Version: [%2]").arg(
54
tokenTmp, productVersionTmp));
60
m_productVersion = productVersionTmp;
63
HProductToken::~HProductToken()
67
bool HProductToken::isValid(HValidityCheckLevel checkLevel) const
69
bool looselyValid = !m_token.isEmpty() && !m_productVersion.isEmpty();
75
else if (checkLevel == LooseChecks)
81
qint32 separatorIndex = m_productVersion.indexOf('.');
82
if (separatorIndex < 0)
84
m_productVersion.toInt(&ok);
88
m_productVersion.left(separatorIndex).toInt(&ok);
92
separatorIndex+1, m_productVersion.indexOf('.', separatorIndex+1)).toInt(&ok);
98
bool HProductToken::isValidUpnpToken() const
100
if (!isValid(StrictChecks))
105
QString vrs = version();
107
return (m_token.compare("upnp", Qt::CaseInsensitive) == 0) &&
111
(vrs[2] == '0' || vrs[2] == '1'));
114
QString HProductToken::toString() const
116
if (!isValid(LooseChecks))
121
return QString("%1/%2").arg(m_token, m_productVersion);
124
qint32 HProductToken::minorVersion()
126
if (!isValid(LooseChecks))
131
QString tokenVersion = version();
133
qint32 separatorIndex = tokenVersion.indexOf('.');
134
if (separatorIndex < 0)
141
qint32 minTmp = tokenVersion.mid(
142
separatorIndex+1, tokenVersion.indexOf('.', separatorIndex+1)).toInt(&ok);
144
return ok ? minTmp : -1;
147
qint32 HProductToken::majorVersion()
149
if (!isValid(LooseChecks))
154
QString tokenVersion = version();
158
qint32 separatorIndex = tokenVersion.indexOf('.');
159
if (separatorIndex < 0)
161
majTmp = tokenVersion.toInt(&ok);
162
return ok ? majTmp : -1;
165
majTmp = tokenVersion.left(separatorIndex).toInt(&ok);
166
return ok ? majTmp : -1;
169
bool operator==(const HProductToken& obj1, const HProductToken& obj2)
171
return obj1.toString() == obj2.toString();
174
bool operator!=(const HProductToken& obj1, const HProductToken& obj2)
176
return !(obj1 == obj2);
179
/*******************************************************************************
180
* HProductTokensPrivate
181
******************************************************************************/
182
class HProductTokensPrivate :
185
H_DISABLE_COPY(HProductTokensPrivate)
189
// tries to parse the string into "token/version" pairs
190
// the pairs have to be delimited with white-space or commas
191
// a pair can contain "trailing" data until the last delimiter after which
192
// the token of a new pair is started. for instance, this is valid:
193
// token/version (some data; some more data) otherToken/otherVersion finalToken/finalVersion (data)
194
bool parse(const QString& tokens)
198
QVector<HProductToken> productTokens;
202
qint32 i = tokens.indexOf('/'), j = 0, lastDelim = 0;
208
token = tokens.left(i);
209
// the first special case "token/version token/version token/version"
212
for(i = i + 1; i < tokens.size(); ++i, ++j)
214
if (tokens[i] == '/')
218
// there must have been at least one space between the previous '/'
219
// and this one. it is an error otherwise.
223
HProductToken newToken(token, buf.left(lastDelim));
224
if (newToken.isValid(LooseChecks))
226
productTokens.append(newToken);
233
token = buf.mid(lastDelim+1);
238
else if (tokens[i] == ' ')
243
buf.append(tokens[i]);
246
HProductToken newToken(token, buf);
247
if (newToken.isValid(LooseChecks))
249
productTokens.append(newToken);
256
// at this point the provided token string is parsed into
257
// valid token/version pairs, but it is not known if the tokens string
258
// contained the UPnP token + we should inform the user if
259
// non-std input was given.
261
if (productTokens.size() < 3 || !productTokens[1].isValidUpnpToken())
263
HLOG_WARN_NONSTD(QString(
264
"The specified token string [%1] is not formed according "
265
"to the UDA specification").arg(tokens));
269
m_productTokens = productTokens;
275
QString m_originalTokenString;
276
QVector<HProductToken> m_productTokens;
280
HProductTokensPrivate() :
281
m_originalTokenString(), m_productTokens()
285
HProductTokensPrivate(const QString& tokens) :
286
m_originalTokenString(tokens.simplified()), m_productTokens()
290
bool ok = parse(m_originalTokenString);
293
// the string followed the UDA closely (rare, unfortunately)
297
if (m_originalTokenString.contains(','))
299
// some sloppy UPnP implementations uses the comma as the delimiter.
300
// technically, comma could be part of the "version" part of the token,
301
// but in practice, it if is present it is used as the delimiter.
303
ok = parse(QString(m_originalTokenString).remove(','));
306
HLOG_WARN_NONSTD(QString(
307
"Comma should not be used as a delimiter in "
308
"product tokens: [%1]").arg(tokens));
316
// tokenization failed.
317
// fall back for scanning the UPnP/version only
318
QRegExp rexp("(\\b|\\s+)UPnP/");
319
qint32 index = m_originalTokenString.indexOf(
320
rexp, Qt::CaseInsensitive);
324
qint32 matchedLength = rexp.matchedLength();
325
qint32 slash = index + matchedLength;
327
m_originalTokenString.indexOf(QRegExp("\\s|,"), slash);
330
m_originalTokenString.mid(index, matchedLength-1),
331
m_originalTokenString.mid(slash,
332
nextDelim < 0 ? -1 : nextDelim-slash));
334
if (token.isValidUpnpToken())
336
m_productTokens.push_back(token);
340
HLOG_WARN_NONSTD(QString(
341
"Missing the mandatory UPnP token: [%1]").arg(
342
m_originalTokenString));
347
HLOG_WARN_NONSTD(QString(
348
"Missing the mandatory UPnP token: [%1]").arg(
349
m_originalTokenString));
356
/*******************************************************************************
358
******************************************************************************/
359
HProductTokens::HProductTokens() :
360
h_ptr(new HProductTokensPrivate())
364
HProductTokens::HProductTokens(const QString& tokens) :
365
h_ptr(new HProductTokensPrivate(tokens))
369
HProductTokens::HProductTokens(const HProductTokens& other) :
372
Q_ASSERT(&other != this);
375
HProductTokens& HProductTokens::operator=(const HProductTokens& other)
377
Q_ASSERT(&other != this);
382
HProductTokens::~HProductTokens()
386
bool HProductTokens::isValid() const
388
return h_ptr->m_productTokens.size() > 0;
391
bool HProductTokens::isEmpty() const
393
return h_ptr->m_originalTokenString.isEmpty();
396
HProductToken HProductTokens::osToken() const
398
if (h_ptr->m_productTokens.size() < 3)
400
return HProductToken();
403
return h_ptr->m_productTokens[0];
406
HProductToken HProductTokens::upnpToken() const
408
qint32 size = h_ptr->m_productTokens.size();
411
return HProductToken();
415
return h_ptr->m_productTokens[0];
418
return h_ptr->m_productTokens[1];
421
HProductToken HProductTokens::productToken() const
423
if (h_ptr->m_productTokens.size() < 3)
425
return HProductToken();
428
return h_ptr->m_productTokens[2];
431
QVector<HProductToken> HProductTokens::extraTokens() const
433
return h_ptr->m_productTokens.size() > 3 ?
434
h_ptr->m_productTokens.mid(3) : QVector<HProductToken>();
437
bool HProductTokens::hasExtraTokens() const
439
return h_ptr->m_productTokens.size() > 3;
442
QVector<HProductToken> HProductTokens::tokens() const
444
return h_ptr->m_productTokens;
447
QString HProductTokens::toString() const
449
return h_ptr->m_originalTokenString;
452
bool operator==(const HProductTokens& ht1, const HProductTokens& ht2)
454
return ht1.toString() == ht2.toString();