Attachment Support

Overview

A calendar entry has any number of attachments associated with it. These attachments are URI or file attachments. See the CCalAttachment in-source documentation for more details about attachments.

The CCalAttachment class allows access to the attachment content itself and associated metadata. This can be either a URI (RFC 3986) or a file attachment, which must be specified on creation. A URI attachment requires a descriptor containing the URI.

A file attachment can be specified as either a descriptor containing the binary data or as a file handle. Attachment data (URI and binary data) cannot be changed once an attachment has been created. This does not include metadata properties that are modifiable through the CCalAttachment APIs.

File attachments may also have only a content ID on creation. This is true in cases when a vCalendar or iCalendar (RFC 2445) is imported as part of a message. The content ID refers to the attachment file located elsewhere in the message. The attachment data must be set using CCalAttachment::SetResourceL() before the attachment can be stored.

Add an attachment to a calendar entry from a file handle

This use case is intended for a Calendar UI application if there is an option to attach a file to a calendar entry. To add a file attachment to a calendar entry, using a file handle:

  1. Fetch a calendar entry.

  2. Create a file of any type and obtain its file handle.

  3. Add the file to the calendar entry (see example code below).

  4. Store the calendar entry. When the entry is stored the file is moved to the calendar store.

Example code:

Preconditions:

  • iFs is an open session to the file server (RFs).

  • attachmentFileName is the file name of the attachment to be added (a descriptor).

  • label is a label name to be given to the attachment (a descriptor).

  • calEntry is a calendar entry (CCalEntry) to which the attachment is added, on the cleanup stack.


User::LeaveIfError(iFs.ShareProtected());
RFile fileHandle;
fileHandle.Open(iFs, attachmentFileName, EFileWrite);
CleanupClosePushL(fileHandle);
// create attachment and add to calendar entry
CCalAttachment* attachment = CCalAttachment::NewFileL(fileHandle);
CleanupStack::PopAndDestroy(&fileHandle);
CleanupStack::PushL(attachment);
// set attachment properties
attachment->SetLabelL(label);
...
calEntry->AddAttachmentL(*attachment); // calEntry takes ownership
CleanupStack::Pop(attachment);

Note that the file selected is moved to the Calendar store and no longer exists in its current location. In effect the Calendar takes ownership of it.

Add an attachment to a calendar entry from binary data

This use case is intended for a messaging or sync application when importing a calendar entry that has an attachment as binary data.

To add a file attachment to a calendar entry from binary data:

  1. Fetch a calendar entry.

  2. Fetch the binary data of the attachment.

  3. Add the binary data to the calendar entry (see example code below).

  4. Store the calendar entry. When the entry is stored the attachment is added to the calendar store.

Example code:

Preconditions:

  • binaryData is the binary data of the attachment to be added (an 8-bit descriptor).

  • label is a label name to be given to the attachment (a descriptor).

  • calEntry is a calendar entry (CCalEntry) to which the attachment is added, on the cleanup stack.

CCalAttachment* attachment = CCalAttachment::NewFileL(binaryData);
CleanupStack::PushL(attachment); 
// set attachment properties
attachment->SetLabelL(label);
...
calEntry->AddAttachmentL(*attachment); // calEntry takes ownership
CleanupStack::Pop(attachment);

Add a URI attachment to calendar entry

This use case could happen in a sync application when importing a calendar entry, or in a Calendar UI application if a user selects a URI to attach to a calendar entry.

To add a URI attachment to a calendar entry:

  1. Fetch a calendar entry.

  2. Get the URI as an 8-bit descriptor.

  3. Add the URI to the calendar entry (see example code below).

  4. Store the calendar entry.

Example code:

Preconditions:

  • uriAttachment is the URI link of the attachment to be added (an 8-bit descriptor).

  • label is a label name to be given to the attachment (a descriptor).

  • calEntry is a calendar entry (CCalEntry) to which the attachment is added, on the cleanup stack.

CCalAttachment* attachment = CCalAttachment::NewUriL(uriAttachment); 
CleanupStack::PushL(attachment);
// set attachment properties
attachment->SetLabelL(label);
...
calEntry->AddAttachmentL(*attachment); // calEntry takes ownership
CleanupStack::Pop(attachment);

Create an email message with a calendar entry and attachment

This use case is intended for all messaging applications when exporting a calendar entry that contains an attachment.

In this case, the message must contain a vCalendar or iCalendar, and the attachment data itself. The location of the attachment data within the message is referenced by a content ID within the vCalendar or iCalendar. This is described in the MIME specification (RFC 2045).

To create the email message:

  1. Fetch an entry from calendar store.

  2. Fetch an attachment from the entry.

  3. Generate a content ID for attachment.

  4. Set the content ID on the calendar attachment object (see example code below).

  5. Export the entry to a vCalendar or iCalendar.

  6. Add a vCalendar or iCalendar to the email message.

  7. Extract the attachment as binary data from the calendar attachment object (see example code in Fetch a calendar attachment as binary data).

  8. Add the attachment to email message.

Example code:

Preconditions:

  • calEntry is a calendar entry (CCalEntry) containing an attachment to be exported, on the cleanup stack.

  • attachIndex is the index of the attachment in calEntry to be exported (zero if the entry has only one attachment).


CCalAttachment* attachment = calEntry->AttachmentL(attachIndex);
if (attachment->FileAttachment() != NULL)
     {
     // generate a content ID for the attachment
     ...
     // set the content ID on this attachment
     attachment->FileAttachment()->SetContentIdL(contentId);
     }
// export calEntry to create vCalendar / iCalendar
...

Receive an email message with a calendar entry and attachment

This use case is intended for all messaging applications when importing a calendar entry that contains an attachment.

The message contains a vCalendar or iCalendar, and the attachment data. The location of the attachment data within the message is referenced by a content ID within the vCalendar or iCalendar. This is described in the MIME specification (RFC 2045).

To extract a calendar entry and a file attachment from an email message:

  1. Import the calendar entry from a vCalendar or iCalendar.

  2. Fetch the content ID of the entry’s attachment (see example code).

  3. Resolve this content ID and fetch the attachment as a file handle.

  4. Add the file handle to the calendar entry (see example code).

  5. Store the calendar entry.

Example code:

Preconditions:

  • calEntry is a calendar entry which has been imported from a vCalendar or iCalendar. It is on the cleanup stack and has a file attachment.

  • attachIndex is the index of a file attachment in the calendar entry.


CCalAttachment* attachment = calEntry->AttachmentL(attachIndex);
// check this is a file attachment with a content ID
if (attachment->FileAttachment() != NULL && attachment-> FileAttachment()->ContentId().Length() > 0)
        { 
     RFile fileHandle;
     // resolve the content ID to find the attachment file handle 
     ...
     // set the file handle on this attachment
     attachment->FileAttachment()->SetResourceL(fileHandle);
        }

Fetch a calendar attachment as binary data

This use case is intended for a messaging or sync application when exporting a file attachment as binary data.

To fetch an attachment from a calendar entry and extract the content as binary data:

  1. Fetch a calendar entry from the database.

  2. Fetch the attachment’s binary data (see example code).

Example code:

Preconditions:

  • calEntry is a calendar entry which has been fetched from the calendar store, and placed on the cleanup stack.

  • attachIndex is a valid index of an attachment on this entry.


CCalAttachment* attachment = calEntry->AttachmentL(attachIndex);
if (attachment->FileAttachment() != NULL)
     {
     attachment->FileAttachment()->LoadBinaryDataL(); // fetches data
     const TDesC8& KBinaryData = attachment->Value();
     // KBinaryData contains the binary data of this attachment
     ...
     }
else
     {
     const TDesC8& KUri = attachment->Value();
     // KUri contains the URI of this attachment
     ...
        }

Fetch a file handle of a calendar attachment

This use case is intended for a calendar application to open an attachment file from a calendar entry.

To fetch a file attachment from a calendar entry and open it:

  1. Fetch the calendar entry from the database.

  2. Fetch the file handle (see example code). Note that the file handle is read-only.

  3. Open the file from its file handle.

Example code:

Preconditions:

  • calEntry is a calendar entry which has been fetched from the calendar store, and placed on the cleanup stack.

  • attachIndex is a valid index of a file attachment on this entry.


CCalAttachment* attachment = calEntry->AttachmentL(attachIndex);
if (attachment->FileAttachment() != NULL)
     {
     RFile fileHandle;
     attachment->FileAttachment()->FetchFileHandleL(fileHandle);
     // fileHandle is a read-only file handle to this attachment
     ...
        }

Remove an attachment from a calendar entry

To remove an attachment from a calendar entry:

  1. Fetch a calendar entry with an attachment from the calendar store.

  2. Remove an attachment (see example code).

  3. Store the entry. The attachment is removed from the calendar store when the entry is stored.

Example code:

Preconditions:

  • calEntry is a calendar entry which has been fetched from the calendar store, on the cleanup stack.

  • attachIndex is the index of the file attachment to be deleted on this entry.


CCalAttachment* attachment = calEntry->AttachmentL(attachIndex);
calEntry->DeleteAttachmentL(*attachment);

List all file attachments in order of size

To find all file attachments in order of size to be displayed on a user dialog:

  1. Create an attachment iterator.

  2. Fetch each attachment held by the iterator (sorted in order of size).

  3. Fetch details about all file attachments.

  4. Display details in UI.

Example code:


CCalAttachmentIterator* attachmentIterator = iCalAttachmentManager-> FetchAttachmentsL(CCalAttachmentManager::ESortBySizeLargestFirst);
CleanupStack::PushL(attachmentIterator);
while (attachmentIterator->HasMore())
  {
  CCalAttachment* attachment = attachmentIterator->NextL();
  CleanupStack::PushL(attachment); 
  // display attachment details
  ...
  CleanupStack::PopAndDestroy(attachment);
  }

Note that it may be useful to take ownership of the CCalAttachment objects returned from the iterator if it is possible to carry out further actions. For example, if there is a delete option on the attachment dialog, the attachment metadata object is required to fetch the associated entry and remove the attachment.

Do not export file attachments within vCalendar and iCalendar if they are above a certain size

This use case is intended for sync applications.

When file attachments are exported in a vCalendar by AgnVersit, they are always exported as binary data unless the ‘do not export inline’ flag is set. This flag may be set on any attachment.

AgnVersit does not make any checks to see if a file is too big to be exported. However, if an out of memory error occurs while exporting binary data, the binary data is ignored as if the ‘do not export inline’ flag had been set.

To find all file attachments above a certain size and flag these attachments as ‘do not export inline’:

  1. Fetch details about all file attachments, sorted in order of size.

  2. If the file attachment is greater than a certain size, modify its attributes so it is not exported inline.

  3. Update the calendar entry with this modified attachment metadata.

  4. Store the entry.

Note that it is possible to set the ‘do not export inline’ flag for one synchronisation only. In this case, the attachment’s flag can be set on the entry immediately before exporting, without storing the entry in the database. This ensures that other synchronisation clients are unaffected. This approach does not require the CCalAttachmentManager, instead the client needs to check all entries for their attachments over a certain size.

Example code:

Preconditions:

  • KSizeThreshold is the size in bytes above which an attachment must not be exported.

  • iCalAttachmentManager is the open calendar attachment manager.


// fetch file attachments in an iterator in size order, largest first
CCalAttachmentIterator* attachmentIterator = iCalAttachmentManager-> FetchAttachmentsL(CCalAttachmentManager::ESortBySizeLargestFirst);
CleanupStack::PushL(attachmentIterator); 
while (attachmentIterator->HasMore())
  {
  // fetch next attachment from iterator
  CCalAttachment* attachment = attachmentIterator->NextL();
  CleanupStack::PushL(attachment);
  if (attachment->FileAttachment() != NULL)
       {
    // get the attachment size
       const TInt KAttachSize = attachment->FileAttachment()->Size();
       if (KAttachSize < KSizeThreshold)
         {
         // if an attachment whose size is smaller than the threshold is reached, stop iterating – attachments are in size order so there are no more attachments which are greater than the threshold
         break;
         }
       else
         {
         // this attachment has a size greater than the threshold
         RArray<TCalLocalUid> localUids;
         CleanupClosePushL(localUids);
         // get the IDs of entries associated with this attachment
         iCalAttachmentManager->EntriesReferencingFileAttachmentL (localUids, *attachment);
            for (TInt I = 0; I < localUids.Count(); ++I)
              {
              // fetch each entry associated with the attachment (usually there is only one)
              CCalEntry* calEntry = iEntryView->FetchL(localUids[i]); 
              CleanupStack::PushL(calEntry); 
        // modify the attachment’s metadata in order not to export inline
              calEntry->DeleteAttachmentL(*attachment);
              attachment->ClearAttribute(CCalAttachment::EExportInline);
              calEntry->AddAttachmentL(*attachment);
              // store the entry with the modified attachment
              ...
              CleanupStack::PopAndDestroy(calEntry);
              }
            CleanupStack::PopAndDestroy(&localUids);
         }
       }
  CleanupStack::PopAndDestroy(attachment);
  }
CleanupStack::PopAndDestroy(attachmentIterator);

Implementation Considerations

Error condition handling: See in-source documentation for expected error codes.

Performance: Creating large attachments results in a lot of memory being taken up. However, storing entries containing these attachments is only slightly slower than storing entries without attachments because the entire attachment is not passed over IPC. File handles are passed over IPC instead.

Security: The use of file handle transfer ensures that no user can modify an attachment in the calendar store. Users need ReadUserData capability to read attachment data, which is in a read-only format.