Embedded attachments in email - url in email opens attachment - how is it done?

Posted on 2008-11-20
Last Modified: 2013-12-18
I am a Lotus Notes user and was passed an email which contained links (ie urls) in the body of text which, when clicked, prompted Notes to open a specific document.

The attached files can be found in the $FILE fields in the doc properties, but they are not visible in the email. The actual URLs are hotspots which contain URL values similar to:


I know this came from an Exchange server, but I can't contact the sender but would like to emulate this behaviour in some apps that I develop.

Does anyone know how this is done?

Question by:chuckalicious
    LVL 22

    Accepted Solution

    The 'cid' tag is a MIME tag that stands for 'Content ID'.  As you have already learned, it points to a resource that is stored in a hidden $FILE item.  In the example above, you should have a $FILE item where the 'File Name' property is 'df5277f9-70e8-495c-9dd2-a0aee336f9d8'.

    >> "would like to emulate this behavior in some apps that I develop."

    To emulate this, you need to understand how MIME messages are composed and how file attachments are represented in MIME.  In LotusScript, you will need to become familiar with the following classes: NotesMimeEntity, NotesMimeHeader, and NotesStream.

    I've never created MIME messages with download links to embedded files, but I have created MIME messages that include inline images.  I have included a function from my script library below.  This function creates a MIME entity and supports inline images (gif and jpeg only).  Since this function is part of my library, it will not run in your environment without a lot of work.  This code is for example purposes only.

    Here are some of the main points in the function:
    Line #30: Disables automatic MIME conversion.  Important since Notes wants to convert everything to rich text.
    Line #42: Removes any MIME already stored in the specified item.  This is not required if you are composing a new memo.  If you need this code, just let me know.
    Line #43: Gets or creates a MIME entity for the specified item.  I've added this function below.
    Line #45-49: Loads HTML into a stream which is then fed to an HTML MIME part (a child of the main MIME entity).
    Line #51-73: Creates the embedded image files as child entities to the main MIME entity.  The content id is set in each file's header in lines 58 & 58.  Line 59 loads the file into a stream, and line 70 loads the stream into the image entity.  To convert this function for your own needs, you will need to know the content types of the files you are embedding (text/html, image/gif, application/pdf, etc).  A list of content types can be found here:  You may also need to experiment with different encoding types.  ENC_IDENTITY_BINARY works for GIF anf JPEG files.

    Here is an example of how I would use this function to display two images in a table.

    Dim html As String
    Dim imageFilePaths As Variant, imageContentIds As Variant
    html = "<table><tr><td><img src="cid:img1"></td></tr><tr><td><img src="cid:img2"></td></tr></table>"
    imageFilePaths = Split("c:\temp\product_image1.gif,c:\temp\product_image2.jpg", ",")
    imageFilePaths = Split("img1,img2", ",")
    Call DocSetMimeEntityHtml(doc, "Body", html, imageFilePaths, imageContentIds)
    Call doc.Send(False)

    If I'm correct, all you need to do is to fix the content and encoding types for your files and wrap the cid in an anchor tag rather than an image tag in the HTML.  Example: <a scr="cid:your_file">click-me</a>.
    Public Sub DocSetMimeEntityHtml(doc As NotesDocument, Byval itemName As String, Byval html As String, imageFilePaths As Variant, imageContentIds As Variant)
    	' * Sets the html part of a mime entity.
    	' * Note: In-line images must be specified in the HTML code using an IMG tag.
    	' * For example, if the image's file path is "c:\temp\test.gif", the tag might be:  <img src="cid:test.gif">, 
    	' * the imageFilePaths array would contain ["c:\temp\test.gif"], and the imageContentIds array would contain ["test.gif"].
    	' * @param doc The NotesDocument that contains the mime entity.
    	' * @parma itemName The name of the item used to store the mime entity.
    	' * @param html A string containing the HTML code.
    	' * @param imageFilePaths An array of strings that contains the full path to all image files referenced by the HTML code.
    	' * @param imageContentIds An array of strings that contains the content id (cid) of all image files contained in imageFilePaths.
    	' */
    	If (TRAP_ERRORS) Then On Error Goto CATCH
    	Dim sess As New NotesSession
    	Dim stream As NotesStream
    	Dim mimeBody As NotesMIMEEntity
    	Dim mimeHeader As NotesMimeHeader
    	Dim mimeHtml As NotesMIMEEntity
    	Dim mimeImageHeader As NotesMimeHeader
    	Dim mimeImage As NotesMIMEEntity
    	Dim item As NotesItem
    	Dim strImagePath As String
    	Dim strImageExt As String
    	Dim strImageCid As String
    	Dim strImageType As String
    	Dim iImageIndex As Integer
    	Dim bSetImages As Boolean
    	Dim bConvertMime As Boolean
    	bConvertMime = sess.ConvertMime
    	sess.ConvertMime = False
    	itemName = Trim(itemName)
    	If (itemName = "") Then itemName = "Body"
    	bSetImages = False
    	If (Isarray(imageFilePaths) Or Isarray(imageContentIds)) Then
    		If (Ubound(imageFilePaths) <> Ubound(imageContentIds)) Then
    			Error ERR_INVALID_PARAM, MSG_ERR_INVALID_PARAM & "  The image file array does not match the image cid array."
    			bSetImages = True
    		End If
    	End If
    	' Create or replace the mime entity.
    	Call DocRemoveMimeEntity(doc, itemName)
    	Set mimeBody = DocGetMimeEntity(doc, itemName, True)
    	' Add the HTML part.
    	Set stream = sess.CreateStream()
    	Call stream.WriteText(html)
    	Set mimeHtml = mimeBody.CreateChildEntity()
    	Call mimeHtml.SetContentFromText(stream, {text/html; charset="iso-8859-1"}, ENC_QUOTED_PRINTABLE)
    	Call stream.Close()
    	' Add the image content.
    	If (bSetImages) Then
    		For iImageIndex = 0 To Ubound(imageFilePaths)
    			' Get the image file path and content id (cid).
    			strImagePath = Trim(imageFilePaths(iImageIndex))
    			If (strImagePath = "") Then Exit Sub
    			strImageCid = Trim(imageContentIds(iImageIndex))
    			If (strImageCid = "") Then Exit Sub
    			' Get the image context type.
    			If (StrContains(strImagePath, ".", True)) Then strImageExt = Strrightback(strImagePath, ".") Else strImageExt = ""
    			Select Case Lcase(strImageExt)
    			Case "gif":	strImageType = "image/gif"
    			Case "jpg":	strImageType = "image/jpg"
    			Case Else:	strImageType = "image/gif"
    			End Select
    			' Add the image part.
    			Set mimeImage = mimeBody.CreateChildEntity()
    			Set mimeImageHeader = mimeImage.CreateHeader({Content-ID})
    			Call mimeImageHeader.SetHeaderVal("<" & strImageCid & ">")
    			Call stream.Open(strImagePath)
    			Call mimeImage.SetContentFromBytes(stream, strImageType & {;name="} + strImageCid + {"}, ENC_IDENTITY_BINARY)
    			Call stream.Close()
    	End If
    	' Clean up
    	sess.ConvertMime = bConvertMime
    	Call doc.CloseMIMEEntities(True, itemName)
    	Call doc.ReplaceItemValue(itemName & "MimeUpdated", Now)
    	Exit Sub
    	On Error Goto THROW
    	sess.ConvertMime = bConvertMime
    	Call doc.CloseMIMEEntities(False, itemName)	
    	Call ThrowError("")
    End Sub
    Public Function DocGetMimeEntity(doc As NotesDocument, Byval itemName As String, Byval create As Boolean) As NotesMimeEntity
    	' * Retrieves a MIME Entity from a document.
    	' * @param doc The NotesDocument that contains the mime entity.
    	' * @parma itemName The name of the item used to store the mime entity.
    	' * @param create Specify True to create the entity if it does not already exist.
    	' * @return The specified mime entity.
    	' */
    	If (TRAP_ERRORS) Then On Error Goto CATCH
    	Dim sess As New NotesSession()
    	Dim entity As NotesMimeEntity
    	Dim bConvertMime As Boolean
    	bConvertMime = sess.ConvertMime
    	sess.ConvertMime = False
    	' Get existing mime entity.
    	itemName = Trim(itemName)
    	If (itemName = "") Then itemName = "Body"
    	Set entity = doc.GetMIMEEntity(itemName)
    	' Create new mime entity.
    	If (entity Is Nothing) Then
    		If (create) Then
    			If (doc.HasItem(itemName)) Then
    				Call DocRemoveMimeEntity(doc, itemName)
    			End If
    			Set entity = doc.CreateMIMEEntity(itemName)
    		End If
    	End If
    	' Return mime entity.
    	Set DocGetMimeEntity = entity
    	sess.ConvertMime = bConvertMime
    	Exit Function
    	sess.ConvertMime = bConvertMime
    	Call ThrowError("")
    End Function

    Open in new window

    LVL 4

    Author Closing Comment

    I have not had a chance to fully test this, but the response does answer my question, so thanks

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    Find Ransomware Secrets With All-Source Analysis

    Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

    Notes Document Link used by IBM Notes is a link file which aids in the sharing of links to documents in email and webpages. The posts describe the importance and steps to create a Lotus Notes NDL file in brief.
    This article covers general Notes 8.5 troubleshooting information including recreating the Notes\Data folder.
    Hi everyone! This is Experts Exchange customer support.  This quick video will show you how to change your primary email address.  If you have any questions, then please Write a Comment below!
    Here's a very brief overview of the methods PRTG Network Monitor ( offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

    759 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    11 Experts available now in Live!

    Get 1:1 Help Now