KMail VCard Support

KMail currently supports: For KDE 2.3, support is planned for ;-)

Components

How it works

Parsing vCards

The main class is `VCard´. Every VCard object represents one single vcard. VCards can be constructed directly (via the VCard() constructor) but im KMail code they are always constructed by the static VCard::parseVCard method.
      #include "vcard.h"
      ...
      VCard *vc = VCard::parseVCard(filename);

Displaying vCards

To display a vcard, one has to construct a KMDisplayVCard-Dialog, passing a constructed VCard object as an argument.
      vcdlg = new KMDisplayVCard(vc);
      kernel->kbp()->idle();
      vcdlg->show();
Because the dialog is non-modal, the vcard is destructed on destruction of the dialog.

VCards in the Message Reader

VCards are ordinary attachments to e-mails of MIME type "text/x-vcard". Therefore - from the user's point of view - it is easy to SEND vcards to others by attaching a .vcf file.

The message reader deals with vcard attachments in the following manner

  1. the KMail-Reader checks the MIME type of ALL attachments. If one of them has the MIME type "text/x-vcard" it adds an anchor with the text "[vcard]" to the sender´s address, cf. KMReaderWin::writeMsgHeader().

    The URL of the anchor begins with `part:´ and refers to the vcard attachment

    If further checks (by parsing the vcard attachment) if the attachment contains a valid vcard

  2. If the user clicks onto that `part:' anchor, this anchor emits a signal which is connected to the slot `slotAtmOpen()'. This slot checks if the current msgpart is a vcard (compares its MIME type to "text/x-vcard" again) and, if so, opens a KMDisplayVCard dialog.

    The same happens if the Icon which is representing the VCard attachment is clicked on.

  3. Similar code is executed if the user decides to `view...' the vcard (atmView())

VCard Parser

KMail has its own, native C++ parser for VCards; it does not make use of the versit VCard/VCalender classes, which are used elsewhere (by KOrganizer, for example).

The VCard Class

The main vcard class is `VCard'. Every VCard object represents a visiting card with a lot of attributes to a person.

The usual way to construct a VCard object is by parsing it from a file (or from a message part):

      #include "vcard.h"
      ...
      VCard *vc = VCard::parseVCard(filename);
On success, vc is different from NULL and refers to a valid VCard object.

It should be possible to construct a VCard from scratch (using the primitives VCard::addLine() and VCard::addQualifiedLine(). However, vcards are not created this way in KMail.

Querying Data from a VCard

Generally spoken, VCard data is organized by keywords and, optionally, qualifiers. For example, a telephone number can be just `the' telephone number of the person, or it can be a special telephone number, let's say, that of the cellular.

The methods getValue(...) and getValues(...) can be used to query data from a VCard object, either by querying by keyword, e.g.

      QString s = vc->getValue(VCARD_TEL);
or by keyword and qualifier, e.g.
      QString s = vc->getValue(VCARD_TEL, VCARD_TEL_CELL);
There are also entrys (like name, address of the person) which do not consist of one single text string, but rather consist of a number of strings. These entries are then queried by the corresponding getValues(...) methods.

Again, querying by keyword

      QStringList l = vc->getValues(VCARD_ADR);
or querying by keyword and qualifier, e.g.
      QStringList l = vc->getValues(VCARD_ADR, VCARD_ADR_HOME);
is possible.

Unfortunately, one has to know which entry has to be queried by getValue() and which one by getValues(). Have a look into kmdisplayvcard.cpp for concrete examples.

Code fragments

kmreaderwin.cpp

Context menu for attachments
void KMReaderWin::slotUrlPopup(const QString &aUrl, const QPoint& aPos)
...
    menu->insertItem(i18n("Open..."), this, SLOT(slotAtmOpen()));
    menu->insertItem(i18n("Open with..."), this, SLOT(slotAtmOpenWith()));
    menu->insertItem(i18n("View..."), this, SLOT(slotAtmView()));
    menu->insertItem(i18n("Save as..."), this, SLOT(slotAtmSave()));
...
Is there any vcard attached?
void KMReaderWin::parseMsg(KMMessage* aMsg)
  ...
  for (int j = 0; j < aMsg->numBodyParts(); j++) {
    ...
    if (!qstricmp(msgPart.typeStr(), "text")
       && !qstricmp(msgPart.subtypeStr(), "x-vcard")) {
        ...
        vc = VCard::parseVCard(msgPart.body(), &vcerr);

        if (vc) {
          ...
          vcnum = j;
          break;
        }
    }
  }

  writeMsgHeader(vcnum);
  ...
Add "[vCard]" to the from-address
void KMReaderWin::writeMsgHeader(int vcpartnum)
    ...
    if (vcpartnum >= 0) {
      mViewer->write("<a href=\""+vcname+"\">"+i18n("[vCard]")+"</a>");
    }
    ...
Opening a vCard...
void KMReaderWin::slotAtmOpen()
{
  ...
  if (qstricmp(msgPart.typeStr(), "text") == 0) {
    if (qstricmp(msgPart.subtypeStr(), "x-vcard") == 0) {
      ...
      VCard *vc = VCard::parseVCard(msgPart.body(), &vcerr);
      ...
      vcdlg = new KMDisplayVCard(vc);
      vcdlg->show();
      return;
    }
  }
...or viewing it (part 1)
void KMReaderWin::slotAtmView()
{
  KMMessagePart msgPart;
  mMsg->bodyPart(mAtmCurrent, &msgPart);
  QString pname = msgPart.fileName();
  if (pname.isEmpty()) pname=msgPart.name();
  if (pname.isEmpty()) pname=msgPart.contentDescription();
  if (pname.isEmpty()) pname="unnamed";
  // image Attachment is saved already
  atmView(this, &msgPart, htmlMail(), QString("%1part%2/%3").arg(mAttachDir).
    arg(mAtmCurrent+1).arg(pname), pname, mCodec);
}
(part 2)
void KMReaderWin::atmView(KMReaderWin* aReaderWin, KMMessagePart* aMsgPart,
    bool aHTML, const QString& aFileName, const QString& pname, QTextCodec *codec)
    ...
    if (qstricmp(aMsgPart->typeStr(), "text")==0) {
      if (qstricmp(aMsgPart->subtypeStr(), "x-vcard") == 0) {
        ...
        VCard *vc = VCard::parseVCard(aMsgPart->body(), &vcerr);
        ...
        vcdlg = new KMDisplayVCard(vc);
        kernel->kbp()->idle();
        vcdlg->show();
        ...