qtcontactsmobility/src/versit/versitutils.cpp
changeset 24 0ba2181d7c28
child 25 76a2435edfd4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qtcontactsmobility/src/versit/versitutils.cpp	Fri Mar 19 09:27:18 2010 +0200
@@ -0,0 +1,459 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "versitutils_p.h"
+#include "qmobilityglobal.h"
+
+#include <QMap>
+#include <QRegExp>
+
+QTM_BEGIN_NAMESPACE
+
+/*!
+ * Folds \a text by making all lines \a maxChars long.
+ */
+QByteArray VersitUtils::fold(QByteArray& text, int maxChars)
+{
+    char previous = 0;
+    int charsSinceLastLineBreak = 0;
+    for (int i=0; i<text.length(); i++) {
+         char current = text.at(i);
+         if (previous == '\r' && current == '\n') {
+             charsSinceLastLineBreak = 0;
+             previous = 0;
+         } else {
+             char next = 0;
+             if (i != text.length()-1)
+                 next = text.at(i+1);
+             if (charsSinceLastLineBreak == maxChars &&
+                 (current != '\r' && next != '\n')) {
+                 text.insert(i,"\r\n ");
+                 charsSinceLastLineBreak = 1; // space
+                 // Skip the added CRLF, for-loop increment i++ skips the space:
+                 i += 2;
+                 previous = 0;
+             } else {
+                 charsSinceLastLineBreak++;
+                 previous = current;
+             }
+         }
+    }
+
+    return text;
+}
+
+/*!
+ * Unfolds \a text by removing all the CRLFs
+ *followed immediately by a linear whitespace (SPACE or TAB).
+ */
+QByteArray VersitUtils::unfold(QByteArray& text)
+{
+    char previous = 0;
+    char previousOfTheprevious = 0;
+    for (int i=0; i<text.length(); i++) {
+        char current = text.at(i);
+        if ((current == ' ' || current == '\t') &&
+             previous == '\n' &&
+             previousOfTheprevious == '\r') {
+            text.replace(i-2,3,QByteArray());
+            previous = 0;
+            previousOfTheprevious = 0;
+            i--;
+        } else {
+            previousOfTheprevious = previous;
+            previous = current;
+        }
+    }
+
+    return text;
+}
+
+/*!
+ * Returns the count of leading whitespaces in /a text
+ * starting from /a pos.
+ */
+int VersitUtils::countLeadingWhiteSpaces(const QByteArray& text, int pos)
+{
+    int whiteSpaceCount = 0;
+    bool nonWhiteSpaceFound = false;
+    for (int i=pos; i<text.length() && !nonWhiteSpaceFound; i++) {
+        char current = text.at(i);
+        if (current == ' ' ||
+            current == '\t' ||
+            current == '\r' ||
+            current == '\n') {
+            whiteSpaceCount++;
+        } else {
+            nonWhiteSpaceFound = true;
+        }
+    }
+
+    return whiteSpaceCount;
+}
+
+/*!
+ * Encodes special characters in /a text 
+ * using Quoted-Printable encoding (RFC 1521).
+ * Returns true if at least one character was encoded.
+ */
+bool VersitUtils::quotedPrintableEncode(QByteArray& text)
+{    
+    bool encoded = false;
+    for (int i=0; i<text.length(); i++) {
+        char current = text.at(i);
+        if (shouldBeQuotedPrintableEncoded(current)) {
+            QString encodedStr;
+            encodedStr.sprintf("=%02X",current);
+            text.replace(i,1,encodedStr.toAscii());
+            i += 2;
+            encoded = true;
+        }
+    }
+    return encoded;
+}
+
+/*!
+ * Decodes Quoted-Printable encoded (RFC 1521) characters in /a text.
+ */
+void VersitUtils::decodeQuotedPrintable(QByteArray& text)
+{
+    for (int i=0; i < text.length(); i++) {
+        char current = text.at(i);
+        if (current == '=' && i+2 < text.length()) {
+            char next = text.at(i+1);
+            char nextAfterNext = text.at(i+2);
+            if (next == '\r' && nextAfterNext == '\n') {
+                text.remove(i,3);
+            }
+            if (((next >= 'a' && next <= 'f') ||
+                 (next >= 'A' && next <= 'F') ||
+                 (next >= '0' && next <= '9')) &&
+                ((nextAfterNext >= 'a' && nextAfterNext <= 'f') ||
+                 (nextAfterNext >= 'A' && nextAfterNext <= 'F') ||
+                 (nextAfterNext >= '0' && nextAfterNext <= '9'))) {
+                QByteArray hexEncodedChar(text.mid(i+1,2));
+                bool decoded = false; 
+                char decodedChar = hexEncodedChar.toInt(&decoded,16);
+                QByteArray decodedCharAsByteArray;
+                decodedCharAsByteArray.append(decodedChar);
+                if (decoded) {
+                    text.replace(i,3,decodedCharAsByteArray);
+                }
+            }
+        }
+    }
+}
+
+/*!
+ * Performs backslash escaping for line breaks (CRLFs),
+ * semicolons, backslashes and commas according to RFC 2426.
+ */
+bool VersitUtils::backSlashEscape(QByteArray& text)
+{
+    bool escaped = false;
+    bool withinQuotes = false;
+    char previous = 0;
+    for (int i=0; i < text.length(); i++) {
+        char current = text.at(i);
+        if (previous != '\\' && !withinQuotes) {
+            char next = 0;
+            if (i != text.length()-1)
+                 next = text.at(i+1);
+            if (current == ';' || current == ',' ||
+                (current == '\\' &&
+                 next != '\\' && next != ';' && next != ',' && next != 'n')) {
+                text.insert(i,'\\');
+                i++;
+                escaped = true;
+            } else if (previous == '\r' && current == '\n') {
+                text.replace(i-1,2,"\\n");
+                escaped = true;
+            } else {
+                // NOP
+            }
+        }
+        if (current == '"')
+            withinQuotes = !withinQuotes;
+        previous = current;
+    }
+    return escaped;
+}
+
+/*!
+ * Removes backslash escaping for line breaks (CRLFs),
+ * semicolons, backslashes and commas according to RFC 2426.
+ */
+void VersitUtils::removeBackSlashEscaping(QByteArray& text)
+{
+    char previous = 0;
+    bool withinQuotes = false;
+    for (int i=0; i < text.length(); i++) {
+        char current = text.at(i);
+        if (previous == '\\' && !withinQuotes) {
+            if (current == ';' || current == ',' || current == '\\') {
+                text.remove(i-1,1);
+            } else if (current == 'n' || current == 'N') {
+                text.replace(i-1,2,"\r\n");
+            } else {
+                // NOP
+            }
+        }
+        if (current == '"')
+            withinQuotes = !withinQuotes;
+        previous = current;
+    }
+}
+
+/*!
+ * Finds the position of the first non-soft line break 
+ * in a Quoted-Printable encoded string.
+ */
+int VersitUtils::findHardLineBreakInQuotedPrintable(const QByteArray& encoded)
+{
+    int crlfIndex = encoded.indexOf("\r\n");
+    if (crlfIndex <= 0)
+        return -1;
+    while (crlfIndex > 0 && encoded.at(crlfIndex-1) == '=') {
+        crlfIndex = encoded.indexOf("\r\n",crlfIndex+2);
+    }
+
+    return crlfIndex;
+}
+
+/*!
+ * Extracts the groups and the name of the property.
+ */
+QPair<QStringList,QString> VersitUtils::extractPropertyGroupsAndName(
+    const QByteArray& property)
+{
+    QPair<QStringList,QString> groupsAndName;
+    int length = 0;
+    char previous = 0;
+    for (int i=0; i < property.length(); i++) {
+        char current = property.at(i);
+        if ((current == ';' && previous != '\\') ||
+            current == ':') {
+            length = i;
+            break;
+        }
+        previous = current;
+    }
+    if (length > 0) {
+        QString trimmedGroupsAndName =
+            QString::fromAscii(property.left(length).trimmed());
+        QStringList parts = trimmedGroupsAndName.split(QString::fromAscii("."));
+        if (parts.count() > 1) {
+            groupsAndName.second = parts.takeLast();
+            groupsAndName.first = parts;
+        } else {
+            groupsAndName.second = trimmedGroupsAndName;
+        }
+    }
+
+    return groupsAndName;
+}
+
+/*!
+ * Extracts the value of the property.
+ * Returns an empty string if the value was not found
+ */
+QByteArray VersitUtils::extractPropertyValue(const QByteArray& property)
+{
+    QByteArray value;
+    int index = property.indexOf(':') + 1;
+    if (index > 0 && property.length() > index)
+        value = property.mid(index);
+    return value;
+}
+
+/*!
+ * Extracts the property parameters as a QMultiHash.
+ * The parameters without names are added as "TYPE" parameters.
+ */
+QMultiHash<QString,QString> VersitUtils::extractVCard21PropertyParams(
+    const QByteArray& property)
+{
+    QMultiHash<QString,QString> result;
+    QList<QByteArray> paramList = extractParams(property);
+    while (!paramList.isEmpty()) {
+        QByteArray param = paramList.takeLast();
+        QString name = QString::fromAscii(paramName(param));
+        QString value = QString::fromAscii(paramValue(param));
+        result.insert(name,value);
+    }
+
+    return result;
+}
+
+/*!
+ * Extracts the property parameters as a QMultiHash.
+ * The parameters without names are added as "TYPE" parameters.
+ */
+QMultiHash<QString,QString> VersitUtils::extractVCard30PropertyParams(
+    const QByteArray& property)
+{
+    QMultiHash<QString,QString> result;
+    QList<QByteArray> paramList = extractParams(property);
+    while (!paramList.isEmpty()) {
+        QByteArray param = paramList.takeLast();
+        QByteArray name = paramName(param);
+        removeBackSlashEscaping(name);
+        QByteArray values = paramValue(param);
+        QList<QByteArray> valueList = extractParts(values,',');
+        while (!valueList.isEmpty()) {
+            QByteArray value(valueList.takeLast());
+            removeBackSlashEscaping(value);
+            result.insert(QString::fromAscii(name),QString::fromAscii(value));
+        }
+    }
+
+    return result;
+}
+
+/*!
+ * Extracts the parameters as delimited by semicolons.
+ */
+QList<QByteArray> VersitUtils::extractParams(const QByteArray& property)
+{
+    QList<QByteArray> params;
+    int colonIndex = property.indexOf(':');
+    if (colonIndex > 0) {
+        QByteArray nameAndParamsString = property.left(colonIndex);
+        params = extractParts(nameAndParamsString,';');
+        if (!params.isEmpty())
+            params.removeFirst(); // Remove property name
+    }
+
+    return params;
+}
+
+/*!
+ * Extracts the parts separated by separator
+ * discarding the separators escaped with a backslash
+ */
+QList<QByteArray> VersitUtils::extractParts(
+    const QByteArray& text,
+    char separator)
+{
+    QList<QByteArray> parts;
+    int partStartIndex = 0;
+    char previous = 0;
+    for (int i=0; i<text.length(); i++) {
+        char current = text.at(i);
+        if (current == separator && previous != '\\') {
+            int length = i-partStartIndex;
+            QByteArray part = extractPart(text,partStartIndex,length);
+            if (part.length() > 0)
+                parts.append(part);
+            partStartIndex = i+1;
+        }
+        previous = current;
+    }
+
+    // Add the last or only part
+    QByteArray part = extractPart(text,partStartIndex);
+    if (part.length() > 0)
+        parts.append(part);
+    return parts;
+}
+
+/*!
+ * Extracts a substring limited by /a startPosition and /a length.
+ */
+QByteArray VersitUtils::extractPart(
+    const QByteArray& text,
+    int startPosition, 
+    int length)
+{
+    QByteArray part;
+    if (startPosition >= 0)
+        part = text.mid(startPosition,length).trimmed();
+    return part;
+}
+
+/*!
+ * Extracts the name of the parameter.
+ * No name is interpreted as an implicit "TYPE".
+ */
+QByteArray VersitUtils::paramName(const QByteArray& parameter)
+{
+     if (parameter.trimmed().length() == 0)
+         return QByteArray();
+     int equalsIndex = parameter.indexOf('=');
+     if (equalsIndex > 0) {
+         return parameter.left(equalsIndex).trimmed();
+     }
+
+     return QByteArray("TYPE");
+}
+
+/*!
+ * Extracts the value of the parameter
+ */
+QByteArray VersitUtils::paramValue(const QByteArray& parameter)
+{
+    QByteArray value(parameter);
+    int equalsIndex = parameter.indexOf('=');
+    if (equalsIndex > 0) {
+        if (equalsIndex == parameter.length()-1) {
+            value = QByteArray();
+        } else {
+            int valueLength = parameter.length() - (equalsIndex + 1);
+            value = parameter.right(valueLength).trimmed();
+        }    
+    }
+
+    return value;
+}
+
+/*!
+ * Checks whether the \a chr should be Quoted-Printable encoded (RFC 1521). 
+ */
+bool VersitUtils::shouldBeQuotedPrintableEncoded(char chr)
+{
+    return (chr < 32 || 
+            chr == '!' || chr == '"' || chr == '#' || chr == '$' || 
+            chr == '=' || chr == '@' || chr == '[' || chr == '\\' || 
+            chr == ']' || chr == '^' || chr == '`' ||
+            chr > 122 ); 
+}
+
+QTM_END_NAMESPACE