469 } else { |
469 } else { |
470 property.setValue(QVariant::fromValue(subDocument)); |
470 property.setValue(QVariant::fromValue(subDocument)); |
471 } |
471 } |
472 } else { |
472 } else { |
473 QTextCodec* codec; |
473 QTextCodec* codec; |
474 QVariant valueVariant(decodeCharset(value, property, lineReader.codec(), &codec)); |
474 bool isBinary = unencode(value, cursor, property, lineReader); |
475 bool isBinary = unencode(valueVariant, cursor, property, codec, lineReader); |
475 if (isBinary) { |
476 property.setValue(valueVariant); |
476 property.setValue(value); |
477 if (isBinary) |
|
478 property.setValueType(QVersitProperty::BinaryType); |
477 property.setValueType(QVersitProperty::BinaryType); |
479 else |
478 } |
|
479 else { |
|
480 property.setValue(decodeCharset(value, property, lineReader.codec(), &codec)); |
480 splitStructuredValue(property, false); |
481 splitStructuredValue(property, false); |
|
482 } |
481 } |
483 } |
482 } |
484 } |
483 |
485 |
484 /*! |
486 /*! |
485 * Parses the property according to vCard 3.0 syntax. |
487 * Parses the property according to vCard 3.0 syntax. |
490 property.setParameters(extractVCard30PropertyParams(cursor, lineReader.codec())); |
492 property.setParameters(extractVCard30PropertyParams(cursor, lineReader.codec())); |
491 |
493 |
492 QByteArray value = extractPropertyValue(cursor); |
494 QByteArray value = extractPropertyValue(cursor); |
493 |
495 |
494 QTextCodec* codec; |
496 QTextCodec* codec; |
495 QString valueString(decodeCharset(value, property, lineReader.codec(), &codec)); |
|
496 |
497 |
497 if (property.valueType() == QVersitProperty::VersitDocumentType) { |
498 if (property.valueType() == QVersitProperty::VersitDocumentType) { |
|
499 QString valueString(decodeCharset(value, property, lineReader.codec(), &codec)); |
498 removeBackSlashEscaping(valueString); |
500 removeBackSlashEscaping(valueString); |
499 // Make a line reader from the value of the property. |
501 // Make a line reader from the value of the property. |
500 QByteArray subDocumentValue(codec->fromUnicode(valueString)); |
502 QByteArray subDocumentValue(codec->fromUnicode(valueString)); |
501 QBuffer subDocumentData(&subDocumentValue); |
503 QBuffer subDocumentData(&subDocumentValue); |
502 subDocumentData.open(QIODevice::ReadOnly); |
504 subDocumentData.open(QIODevice::ReadOnly); |
508 property = QVersitProperty(); |
510 property = QVersitProperty(); |
509 } else { |
511 } else { |
510 property.setValue(QVariant::fromValue(subDocument)); |
512 property.setValue(QVariant::fromValue(subDocument)); |
511 } |
513 } |
512 } else { |
514 } else { |
513 QVariant valueVariant(valueString); |
515 bool isBinary = unencode(value, cursor, property, lineReader); |
514 bool isBinary = unencode(valueVariant, cursor, property, codec, lineReader); |
|
515 property.setValue(valueVariant); |
|
516 if (isBinary) { |
516 if (isBinary) { |
|
517 property.setValue(value); |
517 property.setValueType(QVersitProperty::BinaryType); |
518 property.setValueType(QVersitProperty::BinaryType); |
518 } else { |
519 } else { |
|
520 property.setValue(decodeCharset(value, property, lineReader.codec(), &codec)); |
519 bool isList = splitStructuredValue(property, true); |
521 bool isList = splitStructuredValue(property, true); |
520 // Do backslash unescaping |
522 // Do backslash unescaping |
521 if (isList) { |
523 if (isList) { |
522 QStringList list = property.value<QStringList>(); |
524 QStringList list = property.value<QStringList>(); |
523 for (int i = 0; i < list.length(); i++) { |
525 for (int i = 0; i < list.length(); i++) { |
539 bool QVersitReaderPrivate::setVersionFromProperty(QVersitDocument& document, const QVersitProperty& property) const |
541 bool QVersitReaderPrivate::setVersionFromProperty(QVersitDocument& document, const QVersitProperty& property) const |
540 { |
542 { |
541 bool valid = true; |
543 bool valid = true; |
542 if (property.name() == QLatin1String("VERSION")) { |
544 if (property.name() == QLatin1String("VERSION")) { |
543 QString value = property.value().trimmed(); |
545 QString value = property.value().trimmed(); |
544 if (property.parameters().contains(QLatin1String("ENCODING"),QLatin1String("BASE64")) |
546 QStringList encodingParameters = property.parameters().values(QLatin1String("ENCODING")); |
545 || property.parameters().contains(QLatin1String("TYPE"),QLatin1String("BASE64"))) |
547 QStringList typeParameters = property.parameters().values(QLatin1String("TYPE")); |
|
548 if (encodingParameters.contains(QLatin1String("BASE64"), Qt::CaseInsensitive) |
|
549 || typeParameters.contains(QLatin1String("BASE64"), Qt::CaseInsensitive)) |
546 value = QLatin1String(QByteArray::fromBase64(value.toAscii())); |
550 value = QLatin1String(QByteArray::fromBase64(value.toAscii())); |
547 if (value == QLatin1String("2.1")) { |
551 if (value == QLatin1String("2.1")) { |
548 document.setType(QVersitDocument::VCard21Type); |
552 document.setType(QVersitDocument::VCard21Type); |
549 } else if (value == QLatin1String("3.0")) { |
553 } else if (value == QLatin1String("3.0")) { |
550 document.setType(QVersitDocument::VCard30Type); |
554 document.setType(QVersitDocument::VCard30Type); |
554 } |
558 } |
555 return valid; |
559 return valid; |
556 } |
560 } |
557 |
561 |
558 /*! |
562 /*! |
559 * On entry, \a value should hold a QString. On exit, it may be either a QString or a QByteArray. |
563 * On entry, \a value should be the byte array to unencode. It is modified to be the unencoded |
560 * Returns true if and only if the property value is turned into a QByteArray. |
564 * version. Returns true if and only if the value was base-64 encoded. \a cursor and |
561 */ |
565 * \a lineReader are supplied in case more lines need to be read (for quoted-printable). The |
562 bool QVersitReaderPrivate::unencode(QVariant& value, VersitCursor& cursor, |
566 * \a property is supplied so we know what kind of encoding was used. |
563 QVersitProperty& property, QTextCodec* codec, |
567 */ |
|
568 bool QVersitReaderPrivate::unencode(QByteArray& value, VersitCursor& cursor, |
|
569 QVersitProperty& property, |
564 LineReader& lineReader) const |
570 LineReader& lineReader) const |
565 { |
571 { |
566 Q_ASSERT(value.type() == QVariant::String); |
572 QStringList encodingParameters = property.parameters().values(QLatin1String("ENCODING")); |
567 |
573 QStringList typeParameters = property.parameters().values(QLatin1String("TYPE")); |
568 QString valueString = value.toString(); |
574 if (encodingParameters.contains(QLatin1String("QUOTED-PRINTABLE"), Qt::CaseInsensitive)) { |
569 |
|
570 if (property.parameters().contains(QLatin1String("ENCODING"), QLatin1String("QUOTED-PRINTABLE"))) { |
|
571 // At this point, we need to accumulate bytes until we hit a real line break (no = before |
575 // At this point, we need to accumulate bytes until we hit a real line break (no = before |
572 // it) value already contains everything up to the character before the newline |
576 // it) value already contains everything up to the character before the newline |
573 while (valueString.endsWith(QLatin1Char('='))) { |
577 while (value.endsWith('=')) { |
574 valueString.chop(1); // Get rid of '=' |
578 value.chop(1); // Get rid of '=' |
575 // We add each line (minus the escaped = and newline chars) |
579 // We add each line (minus the escaped = and newline chars) |
576 cursor = lineReader.readLine(); |
580 cursor = lineReader.readLine(); |
577 QString line = codec->toUnicode( |
581 QByteArray line = cursor.data.mid(cursor.position, cursor.selection-cursor.position); |
578 cursor.data.mid(cursor.position, cursor.selection-cursor.position)); |
582 value.append(line); |
579 valueString.append(line); |
583 } |
580 } |
584 decodeQuotedPrintable(value); |
581 decodeQuotedPrintable(valueString); |
|
582 // Remove the encoding parameter as the value is now decoded |
585 // Remove the encoding parameter as the value is now decoded |
583 property.removeParameters(QLatin1String("ENCODING")); |
586 property.removeParameters(QLatin1String("ENCODING")); |
584 value.setValue(valueString); |
|
585 return false; |
587 return false; |
586 } else if (property.parameters().contains(QLatin1String("ENCODING"), QLatin1String("BASE64")) |
588 } else if (encodingParameters.contains(QLatin1String("BASE64"), Qt::CaseInsensitive) |
587 || property.parameters().contains(QLatin1String("ENCODING"), QLatin1String("B")) |
589 || encodingParameters.contains(QLatin1String("B"), Qt::CaseInsensitive) |
588 || property.parameters().contains(QLatin1String("TYPE"), QLatin1String("BASE64")) |
590 || typeParameters.contains(QLatin1String("BASE64"), Qt::CaseInsensitive) |
589 || property.parameters().contains(QLatin1String("TYPE"), QLatin1String("B"))) { |
591 || typeParameters.contains(QLatin1String("B"), Qt::CaseInsensitive)) { |
590 value.setValue(QByteArray::fromBase64(valueString.toAscii())); |
592 value = QByteArray::fromBase64(value); |
591 // Remove the encoding parameter as the value is now decoded |
593 // Remove the encoding parameter as the value is now decoded |
592 property.removeParameters(QLatin1String("ENCODING")); |
594 property.removeParameters(QLatin1String("ENCODING")); |
593 // Hack: add the charset parameter back in (even if there wasn't one to start with and |
|
594 // the default codec was used). This will help later on if someone calls valueString() |
|
595 // on the property. |
|
596 property.insertParameter(QLatin1String("CHARSET"), QLatin1String(codec->name())); |
|
597 return true; |
595 return true; |
598 } |
596 } |
599 return false; |
597 return false; |
600 } |
598 } |
601 |
599 |
625 } |
623 } |
626 |
624 |
627 /*! |
625 /*! |
628 * Decodes Quoted-Printable encoded (RFC 1521) characters in /a text. |
626 * Decodes Quoted-Printable encoded (RFC 1521) characters in /a text. |
629 */ |
627 */ |
630 void QVersitReaderPrivate::decodeQuotedPrintable(QString& text) const |
628 void QVersitReaderPrivate::decodeQuotedPrintable(QByteArray& text) const |
631 { |
629 { |
632 for (int i=0; i < text.length(); i++) { |
630 for (int i=0; i < text.length(); i++) { |
633 QChar current = text.at(i); |
631 char current = text.at(i); |
634 if (current == QLatin1Char('=') && i+2 < text.length()) { |
632 if (current == '=' && i+2 < text.length()) { |
635 int next = text.at(i+1).unicode(); |
633 char next = text.at(i+1); |
636 int nextAfterNext = text.at(i+2).unicode(); |
634 char nextAfterNext = text.at(i+2); |
637 if (((next >= 'a' && next <= 'f') || |
635 if (((next >= 'a' && next <= 'f') || |
638 (next >= 'A' && next <= 'F') || |
636 (next >= 'A' && next <= 'F') || |
639 (next >= '0' && next <= '9')) && |
637 (next >= '0' && next <= '9')) && |
640 ((nextAfterNext >= 'a' && nextAfterNext <= 'f') || |
638 ((nextAfterNext >= 'a' && nextAfterNext <= 'f') || |
641 (nextAfterNext >= 'A' && nextAfterNext <= 'F') || |
639 (nextAfterNext >= 'A' && nextAfterNext <= 'F') || |
642 (nextAfterNext >= '0' && nextAfterNext <= '9'))) { |
640 (nextAfterNext >= '0' && nextAfterNext <= '9'))) { |
643 bool ok; |
641 bool ok; |
644 QChar decodedChar(text.mid(i+1, 2).toInt(&ok,16)); |
642 char decodedChar(text.mid(i+1, 2).toInt(&ok,16)); |
645 if (ok) |
643 if (ok) { |
646 text.replace(i, 3, decodedChar); |
644 text[i] = decodedChar; |
|
645 text.remove(i+1, 2); |
|
646 } |
647 } else if (next == '\r' && nextAfterNext == '\n') { |
647 } else if (next == '\r' && nextAfterNext == '\n') { |
648 // Newlines can still be found here if they are encoded in a non-default charset. |
648 // Newlines can still be found here if they are encoded in a non-default charset. |
649 text.remove(i, 3); |
649 text.remove(i, 3); |
650 } |
650 } |
651 } |
651 } |