[Kde-pim] Review Request: Fix/improve handling of Headers and Contents in KMime (long)

Constantin Berzan exit3219 at gmail.com
Tue Jul 28 14:07:54 BST 2009


-----------------------------------------------------------
This is an automatically generated e-mail. To reply, visit:
http://reviewboard.kde.org/r/1151/
-----------------------------------------------------------

Review request for KDE PIM, Thomas McGuire, Andras Mantia, and Tom Albers.


Summary
-------

This is a rather huge patch attempting to fix a number of problems in KMime.  (You will also find small unrelated changes, some refactoring, and some TODO questions.)

(1) The problem with headers:
--------------------------------
Before: Headers were handled in an inconsistent and broken manner.
Examples:
* Content-ID and a bunch of others were not supported at all (assemble() didn't know about them).
* Headers were not parsed from the head in parse(), but only when a particular header was asked for. This meant that after removeHeader(some_header), headerByType(type_of_header_removed) still returned the header which was supposed to be removed.
* There was a lot of duplication and unnecessary complexity with separate handling of headers in KMime::Content, KMime::Message and KMime::NewsArticle.
* There wasn't any way of having more than one header of a particular type (e.g. Resent-From:), other than manually playing with the head QByteArray.

After: Headers are handled uniformly in KMime::Content, and no special handling is necessary in Message or NewsArticle. MIME headers (Content-*), RFC5322 headers (From: etc.), and custom headers (X-*) all work. Each header class has a virtual clone() method for making a copy of the given header. A HeaderFactory is used when parsing, so that headers of appropriate type are created. appendHeader() and prependHeader() Content methods allow setting multiple headers of a given type.

(2) The problem with singlepart<->multipart Content conversion:
------------------------------------------------------------------
Before: Consequences of this automatic conversion led to bugs.
Examples:
* Say c is single-part. Headers::Base *h = c->contentType(); c->addContent(bla); Now the h pointer no longer belongs to c, but to c->contents().first(), because c was converted to multipart.
* Say c is multipart/mixed, with 0 sub-contents. c->addContent(alpha); c->addContent(beta); c->removeContent(beta); alpha has just been deleted, because c has been converted to singple-part.

After: In all cases that I can think of, the developer can create a single-part or a multipart content, depending on what he or she wants. The automatic conversion is a source of bugs and confusion, with very little benefit / convenience. But we cannot change this behaviour and stay backwards-compatible, right? :-(
All I did was put big warnings in the API docs.

(3) The problem with signatures and corrupted content:
---------------------------------------------------------
Before: Calling assemble() on a Message changed it in slight ways (header order, where header folding occurs, Content-[Tt]ype capitalization, etc). This broke signature checking. AndrĂ¡s has added a fix for this, where assembleHeaders() would not change the head of the Content unless some header has actually been modified. IMHO, this is not the best possible solution. Problems with this are:
* It's slow: Every header has to be checked in assembleHeaders().
* It's easy to break: For example, calling c->contentDescription() will silently create an empty Content-Description header in c. It is difficult to handle such empty headers correctly in assembleHeaders(), getHeaderByType(), setHeader(), as well as when the headers have to be moved around in addContent()/removeContent().
* Ideally, assemble() should not bother with message integrity. Assembling a message means creating it from scratch.

After: I started with this clear picture of KMime::Content: There is a string representation (a big QByteArray holding the head and body of the content), and there is a broken-down representation (a list of headers and a list of subcontents). parse() updates the broken-down representation from the string representation. assemble() updates the string representation from the broken-down representation. This distinction has been inconsistent before (true for subcontents but not true for headers). Making parse() and assemble() do what they are supposed to do may actually break some applications, but I think fixing this will make everything clearer. In particular, before one could do:
Content *c = new Content; c->setContent(something); c->getSomeHeader();
Whereas now the header will not be available until parse() is called.
For signature checking, I have added setFrozen() / isFrozen() methods to KMime::Content. Freezing a content guarantees that parse() and assemble() will never change the string representation returned by encodedContent().

---
Thanks for reading. I appreciate any comments or suggestions.


Diffs
-----

  /trunk/KDE/kdepimlibs/kmime/CMakeLists.txt 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_codecs.cpp 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_content.h 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_content.cpp 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_content_p.h 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_header_parsing.h 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_header_parsing.cpp 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_headerfactory.h PRE-CREATION 
  /trunk/KDE/kdepimlibs/kmime/kmime_headerfactory.cpp PRE-CREATION 
  /trunk/KDE/kdepimlibs/kmime/kmime_headers.h 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_headers.cpp 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_message.cpp 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_message_p.h 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_newsarticle.cpp 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_util.h 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_util.cpp 1003373 
  /trunk/KDE/kdepimlibs/kmime/kmime_util_p.h 1003373 
  /trunk/KDE/kdepimlibs/kmime/tests/CMakeLists.txt 1003373 
  /trunk/KDE/kdepimlibs/kmime/tests/headertest.h 1003373 
  /trunk/KDE/kdepimlibs/kmime/tests/headertest.cpp 1003373 
  /trunk/KDE/kdepimlibs/kmime/tests/kmime_content_test.h 1003373 
  /trunk/KDE/kdepimlibs/kmime/tests/kmime_content_test.cpp 1003373 
  /trunk/KDE/kdepimlibs/kmime/tests/kmime_headerfactorytest.h PRE-CREATION 
  /trunk/KDE/kdepimlibs/kmime/tests/kmime_headerfactorytest.cpp PRE-CREATION 

Diff: http://reviewboard.kde.org/r/1151/diff


Testing
-------

I added some unit tests, and the message composer in kdepimlibs seems to have no problems.

It would be great if big users of KMime such as the mailreader and Mailody could try this and point out any potential problems. That is the reason I left plenty of kDebug()s in the code.


Thanks,

Constantin

_______________________________________________
KDE PIM mailing list kde-pim at kde.org
https://mail.kde.org/mailman/listinfo/kde-pim
KDE PIM home page at http://pim.kde.org/


More information about the kde-pim mailing list