qtcontacts-extensions_impl.h 7.85 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/*
 * Copyright (C) 2013 Jolla Ltd. <matthew.vogt@jollamobile.com>
 *
 * You may use this file under the terms of the BSD license as follows:
 *
 * "Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Nemo Mobile nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 */

#ifndef QTCONTACTS_EXTENSIONS_IMPL_H
#define QTCONTACTS_EXTENSIONS_IMPL_H

35
#include <qtcontacts-extensions.h>
36

37 38 39 40 41
namespace {

QString normalize(const QString &input, int flags, int maxCharacters)
{
    // Allow '[' and ']' even though RFC3966 doesn't
42 43
    static const QString allowedSeparators(QString::fromLatin1(" -()[]"));
    static const QString dtmfChars(QString::fromLatin1("pP,.wWxX#*"));
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
    static const QString sipScheme(QString::fromLatin1("sips:"));
    static const QString hashControl(QString::fromLatin1("#31#"));
    static const QString starControl(QString::fromLatin1("*31#"));

    static const QChar plus(QChar::fromLatin1('+'));
    static const QChar colon(QChar::fromLatin1(':'));
    static const QChar at(QChar::fromLatin1('@'));

    // If this is a SIP URI (empty scheme means 'sips'), validate the identifier part
    QString number(input);
    if (number.startsWith(sipScheme) || number.startsWith(colon)) {
        int colonIndex = number.indexOf(colon);
        int atIndex = number.indexOf(at, colonIndex + 1);
        if (atIndex != -1) {
            number = number.mid(colonIndex + 1, (atIndex - colonIndex - 1));
        }
    }

    QString subset;
    subset.reserve(number.length());

65 66
    QChar initialChar;
    bool numericComponent = false;
67 68 69 70 71 72 73 74
    int firstDtmfIndex = -1;

    QString::const_iterator it = number.constBegin(), end = number.constEnd();
    for ( ; it != end; ++it) {
        if ((*it).isDigit()) {
            // Convert to ASCII, capturing unicode digit values
            const QChar digit(QChar::fromLatin1('0' + (*it).digitValue()));
            subset.append(digit);
75 76 77
            numericComponent = true;
            if (initialChar.isNull()) {
                initialChar = digit;
78 79
            }
        } else if (*it == plus) {
80
            if (initialChar.isNull()) {
81 82
                // This is the start of the diallable number
                subset.append(*it);
83
                initialChar = *it;
84 85 86
            } else if (firstDtmfIndex != -1) {
                // Allow inside the DMTF section
                subset.append(*it);
87 88 89 90 91 92 93 94 95
            } else if (flags & QtContactsSqliteExtensions::ValidatePhoneNumber) {
                // Not valid in this location
                return QString();
            }
        } else if (allowedSeparators.contains(*it)) {
            if (flags & QtContactsSqliteExtensions::KeepPhoneNumberPunctuation) {
                subset.append(*it);
            }
        } else if (dtmfChars.contains(*it)) {
96 97
            if ((*it).isLetter() && !numericComponent) {
                // Alphabetic DTMF chars can only occur after some numeric component
98
                if (flags & QtContactsSqliteExtensions::ValidatePhoneNumber) {
99 100 101 102 103 104 105 106 107 108 109 110 111 112
                    return QString();
                } else {
                    // Skip this character, not valid in this position
                }
            } else {
                if ((flags & QtContactsSqliteExtensions::KeepPhoneNumberDialString) == 0) {
                    // No need to continue accumulating
                    if (flags & QtContactsSqliteExtensions::ValidatePhoneNumber) {
                        // Ensure the remaining characters are permissible
                        while (++it != end) {
                            if ((!(*it).isDigit()) && !allowedSeparators.contains(*it) && !dtmfChars.contains(*it)) {
                                // Invalid character
                                return QString();
                            }
113 114
                        }
                    }
115
                    break;
116 117 118 119
                }

                // Otherwise, continue with processing
                if (firstDtmfIndex == -1) {
120
                    firstDtmfIndex = subset.length();
121
                }
122 123 124 125 126 127 128

                // Accept 'x' and 'X', but convert them to 'p' in the normalized form
                if ((*it).toLower() == QChar::fromLatin1('x')) {
                    subset.append(QChar::fromLatin1('p'));
                } else {
                    subset.append(*it);
                }
129 130 131 132 133 134 135 136
            }
        } else if (flags & QtContactsSqliteExtensions::ValidatePhoneNumber) {
            // Invalid character
            return QString();
        }
    }

    if ((flags & QtContactsSqliteExtensions::ValidatePhoneNumber) &&
137
        (initialChar == plus) && (firstDtmfIndex != -1)) {
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
        // If this number starts with '+', it mustn't contain control codes
        if ((subset.indexOf(hashControl, firstDtmfIndex) != -1) ||
            (subset.indexOf(starControl, firstDtmfIndex) != -1)) {
            return QString();
        }
    }

    if (maxCharacters != -1) {
        int characters = 0;
        int index = (firstDtmfIndex == -1) ? (subset.length() - 1) : (firstDtmfIndex - 1);
        for ( ; index > 0; --index) {
            const QChar &c(subset.at(index));
            if (c.isDigit() || c == plus) {
                if (++characters == maxCharacters) {
                    // Only include the digits from here
                    subset = subset.mid(index);
                    break;
                }
            }
        }
    }

    return subset.trimmed();
}

}

165 166
namespace QtContactsSqliteExtensions {

167
QContactId apiContactId(quint32 iid)
168 169 170 171 172 173 174 175 176 177 178 179
{
    QContactId contactId;
    if (iid != 0) {
        static const QString idStr(QString::fromLatin1("qtcontacts:org.nemomobile.contacts.sqlite::sql-%1"));
        contactId = QContactId::fromString(idStr.arg(iid));
        if (contactId.isNull()) {
            qWarning() << "Unable to formulate valid ID from:" << iid;
        }
    }
    return contactId;
}

180
quint32 internalContactId(const QContactId &id)
181 182 183 184 185 186 187 188 189 190 191
{
    if (!id.isNull()) {
        QStringList components = id.toString().split(QChar::fromLatin1(':'));
        const QString &idComponent = components.isEmpty() ? QString() : components.last();
        if (idComponent.startsWith(QString::fromLatin1("sql-"))) {
            return idComponent.mid(4).toUInt();
        }
    }
    return 0;
}

192 193 194 195 196 197 198 199 200 201 202
QString normalizePhoneNumber(const QString &input, NormalizePhoneNumberFlags flags)
{
    return normalize(input, flags, -1);
}

QString minimizePhoneNumber(const QString &input, int maxCharacters)
{
    // Minimal form number should preserve DTMF dial string to differentiate PABX extensions
    return normalize(input, KeepPhoneNumberDialString, maxCharacters);
}

203 204 205
}

#endif