Highlighted
Respected Contributor.
Respected Contributor.
2198 views

smtp mail with cURL (was in wrong forum)

Jump to solution

Hello,

I have been trying to send mail using cURL from Cobol. I took an example from this forum but I am stuck with the mail body:

Here is the link to the example from cURL http://curl.haxx.se/libcurl/c/smtp-mail.html.

The following code (partly taken from a previous discussion on the forum) seems to work partially :

       *> Demo showing how to make an http request from a COBOL program
       *>    using libcurl.
       *> libcurl can be found at http://sourceforge.net/projects/curl/
       *> libcurl documentation can be found at http://curl.haxx.se/libcurl/
       *>
       *> compile as follows:
       *>    cob -x urltest.cbl -L/usr/lib -lcurl -L/usr/ssl/lib -lssl -lcrypto -ldl
       *>

       Program-ID. urltest.
       Working-Storage Section.
       01  Curl-Handle                     pointer.
       01  Bad-Handle                      pointer.
       01  Recipients                      pointer.
       78  CURLOPT-NOTHING                 value     0.
       78  CURLOPT-FILE                    value 10001.
       78  CURLOPT-URL                     value 10002.
       78  CURLOPT-PORT                    value     3.
       78  CURLOPT-ERRORBUFFER             value 10010.
       78  CURLOPT-WRITEFUNCTION           value 20011.
       78  CURLOPT-CRLF                    value    27.
       78  CURLOPT-VERBOSE                 value    41.
       78  CURLOPT-HEADER                  value    42.
       78  CURLOPT-NOPROGRESS              value    43.
       78  CURLOPT-NOBODY                  value    44.
       78  CURLOPT-HEADER-YES              value     1.
       78  CURLOPT-HEADER-NO               value     0.
       
       78  CURLOPT-MAIL-FROM               value 10186.
       78  CURLOPT-MAIL-RCPT               value 10187.
       78  CURLOPT-READFUNCTION            value 20012.
       78  CURLOPT-READDATA                value 10009.
       78  CURLOPT-UPLOAD                  value 46   .
       78  CURLOPT-VERBOSE                 value 41   .
       78  CURLE-OK                        value 0    .

       78  78True  value 1.
       78  78FROM  value z"<sender@example.org>".
       78  78TO    value z"<addressee@example.net>".
       78  78CC    value z"<info@example.org>".
       
       01  Curl-URL                        pic x(256).
       01  Writefunction-Ptr               procedure-pointer.
       01  Curl-Error-Text                 pic x(256).
       01  Ws-Error                        pic x(40).
       01  Web-Data                        pic x(65536).
       01  Web-Data-Len                    pic x(04) comp-5 value 0.
       01  Web-Data-Max                    pic x(04) comp-5.
       01  Total-Size                      pic x(04) comp-5.
       01  Bytes-Read                      pic x(04) comp-5.

       Linkage Section.
       01  Data-In                         pic x(04) comp-5.
       01  Data-Elem-Size                  pic x(04) comp-5.
       01  Data-Elem-Num                   pic x(04) comp-5.
       01  Unused-Ptr                      pointer.

       Procedure Division.
           *>Curl-Handle = curl-easy-init();
           set Curl-Handle to null
           set Bad-Handle  to null
           call "curl_easy_init" returning Curl-Handle

           if Curl-Handle = Bad-Handle then
               move "Easy Init Error" to Ws-Error
               perform 9000-Error
               *> quit the program
           end-if
           
           display "Init OK"
           
           
           call "curl_easy_setopt"
               using by value     Curl-Handle
                     by value     CURLOPT-VERBOSE size 4
                     by value     78True size 4
           end-call
           if Return-Code not = 0
               move "Verbose Error" to Ws-Error
               perform 9000-Error
           else
               display "CURLOPT-VERBOSE OK"
           end-if
           
           *> This is the URL for your mailserver */
           move z"smtp://mail.example.com"   to Curl-URL
           call "curl_easy_setopt"
               using by value     Curl-Handle
                     by value     CURLOPT-URL size 4
                     by reference Curl-URL
           end-call
           if Return-Code not = CURLE-OK then
               move "Smtp Error" to Ws-Error
               perform 9000-Error
           else
               display "smtp ok"
           end-if
                     
           *> Note that this option isn't strictly required, omitting it will result in
           *> libcurl sending the MAIL FROM command with empty sender data. All
           *> autoresponses should have an empty reverse-path, and should be directed
           *> to the address in the reverse-path which triggered them. Otherwise, they
           *> could cause an endless loop. See RFC 5321 Section 4.5.5 for more details.
            
           *>curl-easy-setopt(Curl-Handle, CURLOPT-MAIL-FROM, FROM);
           move 78From to Curl-URL
           call "curl_easy_setopt"
               using by value     Curl-Handle
                     by value     CURLOPT-MAIL-FROM size 4
                     by reference Curl-URL
           end-call
           if Return-Code not = CURLE-OK then
               move "FROM Error" to Ws-Error
               perform 9000-Error
           else
               display "FROM ok"
           end-if
          
           set recipients to null
           *> Add two recipients, in this particular case they correspond to the
           *> To: and Cc: addressees in the header, but they could be any kind of
           *> recipient. */
           *>recipients = curl-slist-append(recipients, TO);
           *>recipients = curl-slist-append(recipients, CC);
           call "curl_slist_append"
               using by value     recipients
                     by reference 78TO
               returning recipients
           if Return-Code not = CURLE-OK then
               move "recipients Error" to Ws-Error
               perform 9000-Error
           else
               display "recipients ok"
           end-if

           *>curl-easy-setopt(Curl-Handle, CURLOPT_MAIL_RCPT, recipients);
           call "curl_easy_setopt"
               using by value     Curl-Handle
                     by value     CURLOPT-MAIL-RCPT size 4
                     by value     recipients
           end-call
           if Return-Code not = CURLE-OK then
               move "TO Error" to Ws-Error
               perform 9000-Error
           else
               display "TO ok"
           end-if
                     
           *> callback function
           call "curl_easy_setopt"
               using by value     Curl-Handle
                     by value     CURLOPT-ERRORBUFFER size 4
                     by reference Curl-Error-Text
           end-call
           if Return-Code not = 0
               move "Set Option ErrorBuffer Error" to Ws-Error
               perform 9000-Error
           else
               display "CURLOPT-ERRORBUFFER OK"
           end-if
           
           call "curl_easy_setopt"
               using by value     Curl-Handle
                     by value     CURLOPT-HEADER    size 4
                     by value     CURLOPT-HEADER-NO size 4
           end-call
           
           if Return-Code not = 0 then
               move "Set Option Header Error" to Ws-Error
               perform 9000-Error
           else
               display "CURLOPT-HEADER OK"
           end-if
           
           set Writefunction-Ptr to entry "curl-callback"
           
           call "curl_easy_setopt"
               using by value     Curl-Handle
                     by value     CURLOPT-WRITEFUNCTION size 4
                     by value     Writefunction-Ptr
           end-call
           
           if Return-Code not = 0 then
               move "Set Option Writefunction Error" to Ws-Error
               perform 9000-Error
           else
               display "CURLOPT-WRITEFUNCTION OK"
           end-if
           
           *> We're using a callback function to specify the payload (the headers and
           *> body of the message). You could just use the CURLOPT-READDATA option to
           *> specify a FILE pointer to read from. */
           *>curl_easy_setopt(Curl-Handle, CURLOPT-READFUNCTION, payload-source);
           *>curl_easy_setopt(Curl-Handle, CURLOPT-READDATA, &upload-ctx);
           *>curl_easy_setopt(Curl-Handle, CURLOPT-UPLOAD, 1L);
          
           *> Here I should have the body of the message
           
           *> Send the message */
           *> res = curl-easy-perform(Curl-Handle);
           call "curl_easy_perform"
               using by value     Curl-Handle
           end-call
           if Return-Code not = CURLE-OK then
               move "Perform Error" to Ws-Error
               perform 9000-Error
           else
               display "Send ok"
           end-if
          
           *> Check for errors
           *> if (res != CURLE-OK)
           *>   fprintf(stderr, "curl-easy-perform() failed: %s\n",
           *>           curl-easy-strerror(res));
           *>
           *> /* Free the list of recipients */
           *>curl-slist-free-all(recipients);
          
           *> curl won't send the QUIT command until you call cleanup, so you should be
           *> able to re-use this connection for additional messages (setting
           *> CURLOPT-MAIL-FROM and CURLOPT-MAIL-RCPT as required, and calling
           *> curl-easy-perform() again. It may not be a good idea to keep the
           *> connection open for a very long time though (more than a few minutes may
           *> result in the server timing out the connection), and you do want to clean
           *> up in the end.
           *>
           *>curl-easy-cleanup(Curl-Handle);
           call "curl_easy_cleanup" using by value Curl-Handle end-call
           if Return-Code not = CURLE-OK then
               move "Cleanup Error" to Ws-Error
               perform 9000-Error
           else
               display "cleanup ok"
           end-if

           continue
           goback.


       9000-Error.
           display Ws-Error
           display "Return-Code: " Return-Code
           display "Error Text: " Curl-Error-Text
           call "curl_easy_cleanup" using by value Curl-Handle
           goback.

       Program-Termination.
       entry "curl-callback"
           using by reference Data-In
                 by value     Data-Elem-Size
                 by value     Data-Elem-Num
                 by value     Unused-Ptr.
      
       E1000-Buffer-Data.
           compute Total-Size =   (Data-Elem-Size * Data-Elem-Num)
           compute Web-Data-Max = (length of Web-Data - Web-Data-Len)
           if Total-Size > Web-Data-Max then
               move Web-Data-Max to Total-Size
           end-if
       
           move Data-In(1:Total-Size)
                to Web-Data(Web-Data-Len + 1:Total-Size)
           add Total-Size to Web-Data-Len
           move Total-Size to Bytes-Read
           goback returning Bytes-Read.
       
       Entry-Termination.
      

When it comes to modifiying this section :

           *>curl_easy_setopt(Curl-Handle, CURLOPT-READFUNCTION, payload-source);
           *>curl_easy_setopt(Curl-Handle, CURLOPT-READDATA, &upload-ctx);
           *>curl_easy_setopt(Curl-Handle, CURLOPT-UPLOAD, 1L);

i must confess that I don't kno how to handle it in cobol


Has someone an idea on how to proceed ?


Regards,

0 Likes
1 Solution

Accepted Solutions
Highlighted
Respected Contributor.
Respected Contributor.

RE: smtp mail with cURL (was in wrong forum)

Jump to solution

Thank you.

I solved the problem using sendgrid (www.sendgrid.com) which has a very easy way to send emails. We use cURL although.

Regards.

View solution in original post

0 Likes
2 Replies
Highlighted
Micro Focus Expert
Micro Focus Expert

RE: smtp mail with cURL (was in wrong forum)

Jump to solution

Michael Wojcik posted the following reply in the general COBOL forum to this same question:

There's no simple answer, I'm afraid.

The cURL example uses a callback function. You can implement that in COBOL, with a separate PROGRAM or ENTRY in the same process. Then in your callback you could implement something like what the C code does, which is to deliver the next line of header or body text each time the callback is invoked. (The "upload_ctx" stuff is how the C example keeps track of how many lines it's processed. If your program isn't reentrant, you can just do this with global data, i.e. a working-storage item.)

Obviously this isn't a common COBOL idiom, but the COBOL example you're working from has a data-sending callback already - it's everything after "Program-Termination". I haven't looked at the cURL API closely, so I don't know how suitable this callback is when using cURL for SMTP.

The alternative mentioned in the cURL example is using a FILE pointer, which is a C runtime library construct. To do that with COBOL, you could OPEN a line-sequential file, write all of your header and body lines to it, CLOSE it, then call the C library function fopen to get a FILE pointer to it. Something like this:

OPEN temp-file

... *> write header and body

CLOSE temp-file

CALL "fopen" USING

  BY REFERENCE z"/path/to/tempfile"

  BY REFERENCE z"rb"

  RETURNING some-pointer-item

Then you pass some-pointer-item to cURL using CURLOPT_READDATA, and don't worry about a callback. Per the cURL documentation, this does not work on Windows if cURL is a DLL (due to the broken design of the Microsoft C runtime) - you didn't say what platform you're running on. And it's really not much easier than the first option.

There are other difficulties:

• Lines need to be terminated with CRLF, which means adding "& x'0d' & x'0a'" to the end of each string literal you write for a header or body text.

• You need to set To, From, and possibly other headers using the same values you're using for 78TO, etc. The cURL example interpolates those constants directly into the header lines. You can't do the same in your COBOL example, because you've declared 78TO and the others as nul-terminated strings (with z"..."). That makes it more complicated to construct those header lines correctly.

• You need to set headers like Date and Message-ID with values computed at runtime.

There may be other issues; those are just the ones that spring to mind.

Why are you doing SMTP directly? Most platforms have scriptable MUAs (email clients) or MTAs (email servers) that will do most of the work for you.

0 Likes
Highlighted
Respected Contributor.
Respected Contributor.

RE: smtp mail with cURL (was in wrong forum)

Jump to solution

Thank you.

I solved the problem using sendgrid (www.sendgrid.com) which has a very easy way to send emails. We use cURL although.

Regards.

View solution in original post

0 Likes
The opinions expressed above are the personal opinions of the authors, not of Micro Focus. By using this site, you accept the Terms of Use and Rules of Participation. Certain versions of content ("Material") accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company. As of September 1, 2017, the Material is now offered by Micro Focus, a separately owned and operated company. Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.