qtcontactsmobility/src/versit/versitutils.cpp
changeset 24 0ba2181d7c28
child 25 76a2435edfd4
equal deleted inserted replaced
0:e686773b3f54 24:0ba2181d7c28
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "versitutils_p.h"
       
    43 #include "qmobilityglobal.h"
       
    44 
       
    45 #include <QMap>
       
    46 #include <QRegExp>
       
    47 
       
    48 QTM_BEGIN_NAMESPACE
       
    49 
       
    50 /*!
       
    51  * Folds \a text by making all lines \a maxChars long.
       
    52  */
       
    53 QByteArray VersitUtils::fold(QByteArray& text, int maxChars)
       
    54 {
       
    55     char previous = 0;
       
    56     int charsSinceLastLineBreak = 0;
       
    57     for (int i=0; i<text.length(); i++) {
       
    58          char current = text.at(i);
       
    59          if (previous == '\r' && current == '\n') {
       
    60              charsSinceLastLineBreak = 0;
       
    61              previous = 0;
       
    62          } else {
       
    63              char next = 0;
       
    64              if (i != text.length()-1)
       
    65                  next = text.at(i+1);
       
    66              if (charsSinceLastLineBreak == maxChars &&
       
    67                  (current != '\r' && next != '\n')) {
       
    68                  text.insert(i,"\r\n ");
       
    69                  charsSinceLastLineBreak = 1; // space
       
    70                  // Skip the added CRLF, for-loop increment i++ skips the space:
       
    71                  i += 2;
       
    72                  previous = 0;
       
    73              } else {
       
    74                  charsSinceLastLineBreak++;
       
    75                  previous = current;
       
    76              }
       
    77          }
       
    78     }
       
    79 
       
    80     return text;
       
    81 }
       
    82 
       
    83 /*!
       
    84  * Unfolds \a text by removing all the CRLFs
       
    85  *followed immediately by a linear whitespace (SPACE or TAB).
       
    86  */
       
    87 QByteArray VersitUtils::unfold(QByteArray& text)
       
    88 {
       
    89     char previous = 0;
       
    90     char previousOfTheprevious = 0;
       
    91     for (int i=0; i<text.length(); i++) {
       
    92         char current = text.at(i);
       
    93         if ((current == ' ' || current == '\t') &&
       
    94              previous == '\n' &&
       
    95              previousOfTheprevious == '\r') {
       
    96             text.replace(i-2,3,QByteArray());
       
    97             previous = 0;
       
    98             previousOfTheprevious = 0;
       
    99             i--;
       
   100         } else {
       
   101             previousOfTheprevious = previous;
       
   102             previous = current;
       
   103         }
       
   104     }
       
   105 
       
   106     return text;
       
   107 }
       
   108 
       
   109 /*!
       
   110  * Returns the count of leading whitespaces in /a text
       
   111  * starting from /a pos.
       
   112  */
       
   113 int VersitUtils::countLeadingWhiteSpaces(const QByteArray& text, int pos)
       
   114 {
       
   115     int whiteSpaceCount = 0;
       
   116     bool nonWhiteSpaceFound = false;
       
   117     for (int i=pos; i<text.length() && !nonWhiteSpaceFound; i++) {
       
   118         char current = text.at(i);
       
   119         if (current == ' ' ||
       
   120             current == '\t' ||
       
   121             current == '\r' ||
       
   122             current == '\n') {
       
   123             whiteSpaceCount++;
       
   124         } else {
       
   125             nonWhiteSpaceFound = true;
       
   126         }
       
   127     }
       
   128 
       
   129     return whiteSpaceCount;
       
   130 }
       
   131 
       
   132 /*!
       
   133  * Encodes special characters in /a text 
       
   134  * using Quoted-Printable encoding (RFC 1521).
       
   135  * Returns true if at least one character was encoded.
       
   136  */
       
   137 bool VersitUtils::quotedPrintableEncode(QByteArray& text)
       
   138 {    
       
   139     bool encoded = false;
       
   140     for (int i=0; i<text.length(); i++) {
       
   141         char current = text.at(i);
       
   142         if (shouldBeQuotedPrintableEncoded(current)) {
       
   143             QString encodedStr;
       
   144             encodedStr.sprintf("=%02X",current);
       
   145             text.replace(i,1,encodedStr.toAscii());
       
   146             i += 2;
       
   147             encoded = true;
       
   148         }
       
   149     }
       
   150     return encoded;
       
   151 }
       
   152 
       
   153 /*!
       
   154  * Decodes Quoted-Printable encoded (RFC 1521) characters in /a text.
       
   155  */
       
   156 void VersitUtils::decodeQuotedPrintable(QByteArray& text)
       
   157 {
       
   158     for (int i=0; i < text.length(); i++) {
       
   159         char current = text.at(i);
       
   160         if (current == '=' && i+2 < text.length()) {
       
   161             char next = text.at(i+1);
       
   162             char nextAfterNext = text.at(i+2);
       
   163             if (next == '\r' && nextAfterNext == '\n') {
       
   164                 text.remove(i,3);
       
   165             }
       
   166             if (((next >= 'a' && next <= 'f') ||
       
   167                  (next >= 'A' && next <= 'F') ||
       
   168                  (next >= '0' && next <= '9')) &&
       
   169                 ((nextAfterNext >= 'a' && nextAfterNext <= 'f') ||
       
   170                  (nextAfterNext >= 'A' && nextAfterNext <= 'F') ||
       
   171                  (nextAfterNext >= '0' && nextAfterNext <= '9'))) {
       
   172                 QByteArray hexEncodedChar(text.mid(i+1,2));
       
   173                 bool decoded = false; 
       
   174                 char decodedChar = hexEncodedChar.toInt(&decoded,16);
       
   175                 QByteArray decodedCharAsByteArray;
       
   176                 decodedCharAsByteArray.append(decodedChar);
       
   177                 if (decoded) {
       
   178                     text.replace(i,3,decodedCharAsByteArray);
       
   179                 }
       
   180             }
       
   181         }
       
   182     }
       
   183 }
       
   184 
       
   185 /*!
       
   186  * Performs backslash escaping for line breaks (CRLFs),
       
   187  * semicolons, backslashes and commas according to RFC 2426.
       
   188  */
       
   189 bool VersitUtils::backSlashEscape(QByteArray& text)
       
   190 {
       
   191     bool escaped = false;
       
   192     bool withinQuotes = false;
       
   193     char previous = 0;
       
   194     for (int i=0; i < text.length(); i++) {
       
   195         char current = text.at(i);
       
   196         if (previous != '\\' && !withinQuotes) {
       
   197             char next = 0;
       
   198             if (i != text.length()-1)
       
   199                  next = text.at(i+1);
       
   200             if (current == ';' || current == ',' ||
       
   201                 (current == '\\' &&
       
   202                  next != '\\' && next != ';' && next != ',' && next != 'n')) {
       
   203                 text.insert(i,'\\');
       
   204                 i++;
       
   205                 escaped = true;
       
   206             } else if (previous == '\r' && current == '\n') {
       
   207                 text.replace(i-1,2,"\\n");
       
   208                 escaped = true;
       
   209             } else {
       
   210                 // NOP
       
   211             }
       
   212         }
       
   213         if (current == '"')
       
   214             withinQuotes = !withinQuotes;
       
   215         previous = current;
       
   216     }
       
   217     return escaped;
       
   218 }
       
   219 
       
   220 /*!
       
   221  * Removes backslash escaping for line breaks (CRLFs),
       
   222  * semicolons, backslashes and commas according to RFC 2426.
       
   223  */
       
   224 void VersitUtils::removeBackSlashEscaping(QByteArray& text)
       
   225 {
       
   226     char previous = 0;
       
   227     bool withinQuotes = false;
       
   228     for (int i=0; i < text.length(); i++) {
       
   229         char current = text.at(i);
       
   230         if (previous == '\\' && !withinQuotes) {
       
   231             if (current == ';' || current == ',' || current == '\\') {
       
   232                 text.remove(i-1,1);
       
   233             } else if (current == 'n' || current == 'N') {
       
   234                 text.replace(i-1,2,"\r\n");
       
   235             } else {
       
   236                 // NOP
       
   237             }
       
   238         }
       
   239         if (current == '"')
       
   240             withinQuotes = !withinQuotes;
       
   241         previous = current;
       
   242     }
       
   243 }
       
   244 
       
   245 /*!
       
   246  * Finds the position of the first non-soft line break 
       
   247  * in a Quoted-Printable encoded string.
       
   248  */
       
   249 int VersitUtils::findHardLineBreakInQuotedPrintable(const QByteArray& encoded)
       
   250 {
       
   251     int crlfIndex = encoded.indexOf("\r\n");
       
   252     if (crlfIndex <= 0)
       
   253         return -1;
       
   254     while (crlfIndex > 0 && encoded.at(crlfIndex-1) == '=') {
       
   255         crlfIndex = encoded.indexOf("\r\n",crlfIndex+2);
       
   256     }
       
   257 
       
   258     return crlfIndex;
       
   259 }
       
   260 
       
   261 /*!
       
   262  * Extracts the groups and the name of the property.
       
   263  */
       
   264 QPair<QStringList,QString> VersitUtils::extractPropertyGroupsAndName(
       
   265     const QByteArray& property)
       
   266 {
       
   267     QPair<QStringList,QString> groupsAndName;
       
   268     int length = 0;
       
   269     char previous = 0;
       
   270     for (int i=0; i < property.length(); i++) {
       
   271         char current = property.at(i);
       
   272         if ((current == ';' && previous != '\\') ||
       
   273             current == ':') {
       
   274             length = i;
       
   275             break;
       
   276         }
       
   277         previous = current;
       
   278     }
       
   279     if (length > 0) {
       
   280         QString trimmedGroupsAndName =
       
   281             QString::fromAscii(property.left(length).trimmed());
       
   282         QStringList parts = trimmedGroupsAndName.split(QString::fromAscii("."));
       
   283         if (parts.count() > 1) {
       
   284             groupsAndName.second = parts.takeLast();
       
   285             groupsAndName.first = parts;
       
   286         } else {
       
   287             groupsAndName.second = trimmedGroupsAndName;
       
   288         }
       
   289     }
       
   290 
       
   291     return groupsAndName;
       
   292 }
       
   293 
       
   294 /*!
       
   295  * Extracts the value of the property.
       
   296  * Returns an empty string if the value was not found
       
   297  */
       
   298 QByteArray VersitUtils::extractPropertyValue(const QByteArray& property)
       
   299 {
       
   300     QByteArray value;
       
   301     int index = property.indexOf(':') + 1;
       
   302     if (index > 0 && property.length() > index)
       
   303         value = property.mid(index);
       
   304     return value;
       
   305 }
       
   306 
       
   307 /*!
       
   308  * Extracts the property parameters as a QMultiHash.
       
   309  * The parameters without names are added as "TYPE" parameters.
       
   310  */
       
   311 QMultiHash<QString,QString> VersitUtils::extractVCard21PropertyParams(
       
   312     const QByteArray& property)
       
   313 {
       
   314     QMultiHash<QString,QString> result;
       
   315     QList<QByteArray> paramList = extractParams(property);
       
   316     while (!paramList.isEmpty()) {
       
   317         QByteArray param = paramList.takeLast();
       
   318         QString name = QString::fromAscii(paramName(param));
       
   319         QString value = QString::fromAscii(paramValue(param));
       
   320         result.insert(name,value);
       
   321     }
       
   322 
       
   323     return result;
       
   324 }
       
   325 
       
   326 /*!
       
   327  * Extracts the property parameters as a QMultiHash.
       
   328  * The parameters without names are added as "TYPE" parameters.
       
   329  */
       
   330 QMultiHash<QString,QString> VersitUtils::extractVCard30PropertyParams(
       
   331     const QByteArray& property)
       
   332 {
       
   333     QMultiHash<QString,QString> result;
       
   334     QList<QByteArray> paramList = extractParams(property);
       
   335     while (!paramList.isEmpty()) {
       
   336         QByteArray param = paramList.takeLast();
       
   337         QByteArray name = paramName(param);
       
   338         removeBackSlashEscaping(name);
       
   339         QByteArray values = paramValue(param);
       
   340         QList<QByteArray> valueList = extractParts(values,',');
       
   341         while (!valueList.isEmpty()) {
       
   342             QByteArray value(valueList.takeLast());
       
   343             removeBackSlashEscaping(value);
       
   344             result.insert(QString::fromAscii(name),QString::fromAscii(value));
       
   345         }
       
   346     }
       
   347 
       
   348     return result;
       
   349 }
       
   350 
       
   351 /*!
       
   352  * Extracts the parameters as delimited by semicolons.
       
   353  */
       
   354 QList<QByteArray> VersitUtils::extractParams(const QByteArray& property)
       
   355 {
       
   356     QList<QByteArray> params;
       
   357     int colonIndex = property.indexOf(':');
       
   358     if (colonIndex > 0) {
       
   359         QByteArray nameAndParamsString = property.left(colonIndex);
       
   360         params = extractParts(nameAndParamsString,';');
       
   361         if (!params.isEmpty())
       
   362             params.removeFirst(); // Remove property name
       
   363     }
       
   364 
       
   365     return params;
       
   366 }
       
   367 
       
   368 /*!
       
   369  * Extracts the parts separated by separator
       
   370  * discarding the separators escaped with a backslash
       
   371  */
       
   372 QList<QByteArray> VersitUtils::extractParts(
       
   373     const QByteArray& text,
       
   374     char separator)
       
   375 {
       
   376     QList<QByteArray> parts;
       
   377     int partStartIndex = 0;
       
   378     char previous = 0;
       
   379     for (int i=0; i<text.length(); i++) {
       
   380         char current = text.at(i);
       
   381         if (current == separator && previous != '\\') {
       
   382             int length = i-partStartIndex;
       
   383             QByteArray part = extractPart(text,partStartIndex,length);
       
   384             if (part.length() > 0)
       
   385                 parts.append(part);
       
   386             partStartIndex = i+1;
       
   387         }
       
   388         previous = current;
       
   389     }
       
   390 
       
   391     // Add the last or only part
       
   392     QByteArray part = extractPart(text,partStartIndex);
       
   393     if (part.length() > 0)
       
   394         parts.append(part);
       
   395     return parts;
       
   396 }
       
   397 
       
   398 /*!
       
   399  * Extracts a substring limited by /a startPosition and /a length.
       
   400  */
       
   401 QByteArray VersitUtils::extractPart(
       
   402     const QByteArray& text,
       
   403     int startPosition, 
       
   404     int length)
       
   405 {
       
   406     QByteArray part;
       
   407     if (startPosition >= 0)
       
   408         part = text.mid(startPosition,length).trimmed();
       
   409     return part;
       
   410 }
       
   411 
       
   412 /*!
       
   413  * Extracts the name of the parameter.
       
   414  * No name is interpreted as an implicit "TYPE".
       
   415  */
       
   416 QByteArray VersitUtils::paramName(const QByteArray& parameter)
       
   417 {
       
   418      if (parameter.trimmed().length() == 0)
       
   419          return QByteArray();
       
   420      int equalsIndex = parameter.indexOf('=');
       
   421      if (equalsIndex > 0) {
       
   422          return parameter.left(equalsIndex).trimmed();
       
   423      }
       
   424 
       
   425      return QByteArray("TYPE");
       
   426 }
       
   427 
       
   428 /*!
       
   429  * Extracts the value of the parameter
       
   430  */
       
   431 QByteArray VersitUtils::paramValue(const QByteArray& parameter)
       
   432 {
       
   433     QByteArray value(parameter);
       
   434     int equalsIndex = parameter.indexOf('=');
       
   435     if (equalsIndex > 0) {
       
   436         if (equalsIndex == parameter.length()-1) {
       
   437             value = QByteArray();
       
   438         } else {
       
   439             int valueLength = parameter.length() - (equalsIndex + 1);
       
   440             value = parameter.right(valueLength).trimmed();
       
   441         }    
       
   442     }
       
   443 
       
   444     return value;
       
   445 }
       
   446 
       
   447 /*!
       
   448  * Checks whether the \a chr should be Quoted-Printable encoded (RFC 1521). 
       
   449  */
       
   450 bool VersitUtils::shouldBeQuotedPrintableEncoded(char chr)
       
   451 {
       
   452     return (chr < 32 || 
       
   453             chr == '!' || chr == '"' || chr == '#' || chr == '$' || 
       
   454             chr == '=' || chr == '@' || chr == '[' || chr == '\\' || 
       
   455             chr == ']' || chr == '^' || chr == '`' ||
       
   456             chr > 122 ); 
       
   457 }
       
   458 
       
   459 QTM_END_NAMESPACE