2007-07-03

Embed images to your emails with CDOSYS

Based on some great samples that I found on the web, specially those from Mike Gagnon, I've created my own class to send emails. I chose to use cdosys (Microsoft collaboration data objects for windows 2000) because it comes with Win2000 / XP / Vista and 2003, and it allows to send messages in various ways, specially embedding images that are used in the html content of the message.

MAPI was my first choice, but unfortunately it does not allow to embed pictures, only attachments are allowed. MS Outlook automation would work too, but I don`t have it too.

Below is a simple code that generates an html page that contains some images that is to be sent. The images are embedded to the message, not as common attachments! This allows us to show the pictures in the place and characteristics we desire.

Apart from the most common features, it also allows you to embed pictures, add attachments, set priority, ask for a read receipt and send HTML.

The "body" property of the class accepts 3 different kinds of parameters: 1- an html code, 2- an URL (CDOSYS will embed the entire page in the message) or 3- a .HTM file that will be embedded. for this last option, my tests failed when I used spaces in either the directory name of the filename, so avoid spaces in filenames!

The pictures below show some screenshots of some samples that I generated and received using the code below, opened with Outlook Express. this was tested in some webmails too, eg. hotmail, and the result is similar.



Save the code below as a PRG, not forgetting to replace the information in red for your own, and run it.

It will ask you to choose some pictures. When finished, click "cancel"at the GETPICT() dialog, then choose any 2 other files that will be attached. wait for some seconds, enough time for sending the email, and check your inbox.

LOCAL lcHeader, lcBody, lcClose, lcMsgBody, lnIndex, lcIndex, lcPictFile, lcJustFNamePictFile, lcHTML

TEXT TO m.lcHeader
[html][head]
[body bgcolor=#ffffff]
[div][font face=verdana size=4][strong]embed pictures sample with cdosys[/strong][/font][/div]
[p][/p][p][/p]
ENDTEXT

TEXT TO m.lcbody
[div][img src="[[lcpictfile]]"][/div]
[div][font face=verdana size=2]image [[lcindex]] - [[lcjustfnamepictfile]][/font][/div]
[p][/p][p][/p][div][/div][div][/div]
ENDTEXT

TEXT to m.lcclose
[/body][/html]
ENDTEXT

m.lcMsgBody = ""
m.lnIndex = 1
DO WHILE .T.
    m.lcPictFile = GETPICT()
    IF EMPTY(m.lcPictFile)
        EXIT
    ENDIF
    m.lcIndex = TRANSFORM(m.lnIndex)
    m.lcJustFNamePictFile = JUSTFNAME(m.lcPictFile)
    m.lcMsgBody = m.lcMsgBody + TEXTMERGE(m.lcBody)
    m.lnIndex = m.lnIndex + 1
ENDDO
m.lcHTML = m.lcHeader + m.lcMsgBody + m.lcClose
* strtofile(lchtml,"c:\myhtmfile.htm")

LOCAL loMail AS CDOSYSMAIL
m.loMail = CREATEOBJECT("cdosysmail")
WITH m.loMail
    .SmtpServer = "smtp.yourdomain.com" && place your smtp here
    .UserName = "yourusername" && username
    .PASSWORD = "yourpassword" && password
    .FROM = "your name [you@yourserver.com]"
    .TO = "destination@server.com"
    .Subject = "testing cdosysmail"
    .HtmlFormat = .T. && default = .t.
    .Priority = 1 && default = 0 -1=low, 0=normal, 1=high
    .ReadReceipt = .T. && default = .f.
    .ReceiptTo = "you@yourserver.com"
    .Body = m.lcHTML
    * .Body = "http://www.microsoft.com"
    * .Body = "c:/myhtmfile.htm"
    .AddAttachment(GETFILE()) && fullpath of attachment
    .AddAttachment(GETFILE())
    .SEND()
ENDWITH
RETURN




DEFINE CLASS CDOSYSMAIL AS CUSTOM
    Smtpserver = ""
    UserName = ""
    PASSWORD = ""
    FROM = ""
    TO = ""
    Body = ""
    Subject = ""
    CC = ""
    BCC = ""
    HtmlFormat = .T.
    Priority = 0
    ReadReceipt = .F.
    ReceiptTo = ""

    DIMENSION aFiles(1)

    PROCEDURE AddAttachment(tcFile)
        LOCAL lnFiles
        IF VARTYPE(m.tcFile) = "c" AND FILE(m.tcFile)
            m.lnFiles = ALEN(THIS.aFiles)
            DIMENSION THIS.aFiles(m.lnFiles + 1)
            THIS.aFiles(m.lnFiles + 1) = m.tcFile
        ENDIF
    ENDPROC

    PROCEDURE SEND
        #DEFINE CDOREFERENCETYPENAME 1
        #DEFINE CDOREFERENCETYPEID   0

        LOCAL lcSchema, loConfig AS "cdo.configuration", loMsg AS "cdo.message"
        m.lcSchema = "http://schemas.microsoft.com/cdo/configuration/"
        m.loConfig = CREATEOBJECT("cdo.configuration")
        WITH m.loConfig.FIELDS
            .ITEM(m.lcSchema + "smtpserver") = THIS.Smtpserver && host of smtp server
            .ITEM(m.lcSchema + "smtpserverport") = "25" && smtp port
            .ITEM(m.lcSchema + "sendusing") = 2 && send it using port
            .ITEM(m.lcSchema + "smtpauthenticate") = .T. && authenticate
            .ITEM(m.lcSchema + "smtpusessl") = .F.
            .ITEM(m.lcSchema + "sendusername") = THIS.UserName && login
            .ITEM(m.lcSchema + "sendpassword") = THIS.PASSWORD && your password
            .ITEM(m.lcSchema + "smtpconnectiontimeout") = 10 && assign timeout in seconds
            .UPDATE()
        ENDWITH

        m.loMsg = CREATEOBJECT ("cdo.message")
        WITH m.loMsg
            .configuration = m.loConfig
            .Subject = THIS.Subject
            .FROM = THIS.FROM
            .TO = THIS.TO
            IF VARTYPE(THIS.CC) = "c"
                .CC = THIS.CC
            ENDIF

            IF VARTYPE(THIS.BCC) = "c"
                .BCC = THIS.BCC
            ENDIF

            DO CASE

                CASE FILE(THIS.Body)
                    m.loMsg.createmhtmlbody("file://" + THIS.Body)

                CASE LOWER(LEFT(THIS.Body,7)) = "http://"
                    m.loMsg.createmhtmlbody(THIS.Body) && this.body
                CASE THIS.HtmlFormat = .T.

                    LOCAL N, lcFile, lcPictRef, lcHtml, lcImgTag
                    m.lcHtml = THIS.Body
                    m.N = 1

                    DO WHILE .T.
                        m.lcImgTag = STREXTRACT(m.lcHtml, "<img", "]", m.N, 1)
                        m.lcFile = STREXTRACT(m.lcImgTag, ["], ["])

                        IF EMPTY(m.lcImgTag)
                            EXIT
                        ENDIF
                        IF "http:" $ LOWER(m.lcFile) && reference to a picture stored on the web, so skip !
                            m.N = m.N + 1
                            LOOP
                        ENDIF

                        m.lcPictRef = "cid:" + JUSTFNAME(m.lcFile)

                        m.lcHtml = STRTRAN(m.lcHtml, m.lcFile, m.lcPictRef, 1, 1)
                        m.loMsg.addrelatedbodypart(m.lcFile, JUSTFNAME(m.lcFile), CDOREFERENCETYPEID)
                        m.loMsg.FIELDS.ITEM("urn:schemas:mailheader:content-id") = "<" + (JUSTFNAME(m.lcFile)) + "]"
                        m.loMsg.FIELDS.UPDATE()
                        m.N = m.N + 1
                    ENDDO
                    .htmlbody = m.lcHtml
                OTHERWISE
                    .textbody = THIS.Body
            ENDCASE

            IF ALEN(THIS.aFiles) ] 1
                LOCAL lnCount
                FOR m.lnCount = 2 TO ALEN(THIS.aFiles)
                    .AddAttachment(THIS.aFiles(m.lnCount))
                    .FIELDS.UPDATE()
                ENDFOR
            ENDIF

            * set priority if needed
            IF THIS.Priority < ] 0
                .FIELDS.ITEM("urn:schemas:mailheader:x-priority") = THIS.Priority && -1=low, 0=normal, 1=high
                .FIELDS.ITEM("urn:schemas:httpmail:importance") = THIS.Priority
                .FIELDS.UPDATE()
            ENDIF
            * ask for reading receipt if needed
            IF THIS.ReadReceipt = .T.
                IF EMPTY(THIS.ReceiptTo)
                    THIS.ReceiptTo = THIS.TO
                ENDIF
                .FIELDS("urn:schemas:mailheader:disposition-notification-to") = THIS.ReceiptTo
                .FIELDS("urn:schemas:mailheader:return-receipt-to") = THIS.ReceiptTo
                .FIELDS.UPDATE()
            ENDIF

            * set dsn options. this still needs some checking. for some smtp servers this makes cdosys not to send the msg.
            * name value description
            * cdodsndefault 0 no dsn commands are issued.
            * cdodsnnever 1 no dsn commands are issued.
            * cdodsnfailure 2 return a dsn if delivery fails.
            * cdodsnsuccess 4 return a dsn if delivery succeeds.
            * cdodsndelay 8 return a dsn if delivery is delayed.
            * cdodsnsuccessfailordelay 14 return a dsn if delivery succeeds, fails, or is delayed.
            .MdnRequested = .T.
            * .dsnoptions = 2
            .FIELDS.UPDATE()
            .SEND()

        ENDWITH

    ENDPROC

ENDDEFINE




 Related links:

Mike Gagnon Étude sur l'utilisation de cdo et mapi avec Visual FoxPro
http://fox.wikis.com/wc.dll?wiki~cdoemail~vfp
Craig Boyd article
How do I alter the priority / importance of an e-mail message? 
Email, POP3, SMTP scripts, components and articles
Lots of samples here
How to use the CDOSYS.DLL library to embed a message in a new message by using Visual C#





6 comments:

  1. Este articulo esta traducido al español en PortalFox:


    Incruste imágenes a sus correos electrónicos con CDOSYS

    http://www.portalfox.com/article.php?sid=2476

    ReplyDelete
  2. He probado y funciona bien pero tengo algunos problemas

    1 Solo puedo mandar mail a cuentasde correo  del mismo SMTP

    2 no funciona "cc" y "bcc"

    3 solo puedo enviar a 1 destino, y deber ser de una cuenta del mismo SMTP


    Espero me puedas indicar que estoy haciendomal

    Felicidades muy buen trabajo

    ReplyDelete
  3. No funciona el cc y bcc cuando intento enviar a mas de una cuenta de correo da error


    he intentado todo y no me funciona ?????


    que hago ????

    ReplyDelete
  4. Hola Cesar, gracias por compartir este codigo, lo ejecute y al parecer algo esta fallando ya que no llega en mi caso enviar si alguien pudiera colocar las modificaciones para poder utilizar este codigo.

    ReplyDelete
  5. Hi,

    I used your code as above, but sometimes I am getting the below error. Any suggestions to avoid this error

    Method: send
    Error: OLE IDispatch exception code 0 from ?: The message could not be sent to the SMTP server. The transport error code was 0x80040217. The server response was not available
    ..
    Code: SELECT ZT90SVI1A

    Any help will be appreciated

    ReplyDelete
  6. Dear Cesar,

    Too bad CDO does not support TLS over 587 (office365).

    Any advise?


    Thanks and regards,

    ReplyDelete