Curl Smtp : Asking for help regarding very basic stuff again,sorry

New to FreeBASIC? Post your questions here.
Post Reply
PeterHu
Posts: 159
Joined: Jul 24, 2022 4:57

Curl Smtp : Asking for help regarding very basic stuff again,sorry

Post by PeterHu »

I am learning sending email via curl api,in the orignal c version provided by curl,it works.When I tried to write in FreeBasic,the program compiled and runs,email can send out,but the income email of the receiver shows blank in both subject and the content.I do know there is something wrong with basic string/pointer/array/callbacks in my code,but I just could not figure them out.

Appreciate the help again.

My freebasic code sendmail.bas

Code: Select all

#include "crt.bi"
#include "curl.bi"

const SmtpType="smtp://real.smtp.com:25"

const UserName="real_sender@addr.com"
const Password="real_pass_word"
const MailFrom="real_sender@addr.com"
const ToName="receiver@addr1.org"
const ToAddress="receiver@addr1.org"
const CcAddress="cc_receiver@addr2.io"
const CcName="cc_receiver@addr2.io"
 
   
static shared payload_text as  zstring ptr= @( _
   !"To: " & ToAddress & !"\r\n" _
    !"From: " & MailFrom & !"\r\n" _
    !"Cc: " & CcAddress & !"\r\n" _
    !"\r\n" _
    !"Subject: SMTP example message" & !"\r\n" _
    !"\r\n" _ 
    !"The body of the message starts here." & !"\r\n" _
    !"\r\n" _
    !"It could be a lot of lines, could be MIME encoded, whatever.\r\n\r\nGood Luck!\r\nGood Luck!" & !"\r\n" _
)  
type upload_status
   as ubyte bytes_read
end type
function payload_source(byref values as zstring ptr,byval size as ubyte,byval nmemb as ubyte,byref userp as any ptr) as ubyte
   dim upload_ctx as upload_status ptr=cptr(upload_status ptr,userp)
   dim datas as zstring ptr
   dim room as ubyte=size*nmemb
    if((size=0) or (nmemb=0) or ((size*nmemb)<1) ) then
      return 0
    end if
   datas=@payload_text[upload_ctx->bytes_read]
   if(datas<>0) then
      dim length as ubyte = strlen(datas)
      if(room<length) then
        length=room
       end if
      memcpy(values,datas,length)
      upload_ctx->bytes_read+=length
      return length
    end if
    return 0
end function
function main_procedure as integer
   dim curl as CURL ptr
   dim as CURLcode res=CURLE_OK
   dim as curl_slist ptr recipients=null
   dim as upload_status upload_ctx
   
   curl=curl_easy_init()
   if(curl) then
      curl_easy_setopt(curl,CURLOPT_URL,SmtpType)
      curl_easy_setopt(curl,CURLOPT_USERNAME,UserName)
      curl_easy_setopt(curl,CURLOPT_MAIL_FROM,MailFrom)
      curl_easy_setopt(curl,CURLOPT_PASSWORD,Password)
      
      recipients=curl_slist_append(recipients,ToAddress)
      recipients=curl_slist_append(recipients,CcAddress)
      curl_easy_setopt(curl,CURLOPT_MAIL_RCPT,recipients)
      
      curl_easy_setopt(curl,CURLOPT_READFUNCTION,@payload_source)
      curl_easy_setopt(curl,CURLOPT_READDATA,@upload_ctx)
      curl_easy_setopt(curl,CURLOPT_VERBOSE,1)
      curl_easy_setopt(curl,CURLOPT_UPLOAD,1)
      curl_easy_setopt(curl,CURLOPT_USE_SSL,CURLUSESSL_ALL)
      
      curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0)
      
      res=curl_easy_perform(curl)
      
      if(res <>CURLE_OK) then
         fprintf(stderr,!"curl_easy_perform() failed:%s\n",curl_easy_strerror(res))
       end if
       curl_slist_free_all(recipients)
       
       curl_easy_cleanup(curl)
   end if
   
   return res
end function

main_procedure

sleep


Orignal c version : sendmail.c < gcc -o sendmail sendmail.c libcurl.a -lcurl.dll>

Code: Select all

/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at https://curl.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 * SPDX-License-Identifier: curl
 *
 ***************************************************************************/
 
/* <DESC>
 * Send email with SMTP
 * </DESC>
 */
 
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
 
/*
 * For an SMTP example using the multi interface please see smtp-multi.c.
 */
 
/* The libcurl options want plain addresses, the viewable headers in the mail
 * can get a full name as well.
 */


#define SMTP_TYPE "smtp://real.smtp.com:25"

#define FROM_ADDR "real_sender@addr.com"
#define PASSWORD "real_pass_word" 
#define TO_ADDR "receiver@addr1.org"
#define CC_ADDR "cc_receiver@addr2.io"
#define FROM_MAIL "name_or_email_address_from_sender"
#define TO_MAIL   "name_or_email_address_of_receiver"
#define CC_MAIL   "name_or_email_address_of_cc_receiver"

 //"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd@"
  //"rfcpedant.example.org>\r\n"
  //  "Date: Wed, 13 Dec 2023 10:51:29 +1100\r\n"
static const char *payload_text =
  "To: " TO_MAIL "\r\n"
  "From: " FROM_MAIL "\r\n"
  "Cc: " CC_MAIL "\r\n"
  "Subject: SMTP example message\r\n"
  "\r\n" /* empty line to divide headers from body, see RFC 5322 */
  "The body of the message starts here.\r\n"
  "\r\n"
  "It could be a lot of lines, could be MIME encoded, whatever.\r\nGood Luck!\r\nGood Luck!\r\n"
  "Check RFC 5322.\r\n";
 
struct upload_status {
  size_t bytes_read;
};
 
static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp)
{
  struct upload_status *upload_ctx = (struct upload_status *)userp;
  const char *data;
  size_t room = size * nmemb;
 
  if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
    return 0;
  }
 
  data = &payload_text[upload_ctx->bytes_read];
 
  if(data) {
    size_t len = strlen(data);
    if(room < len)
      len = room;
    memcpy(ptr, data, len);
    upload_ctx->bytes_read += len;
 
    return len;
  }
 
  return 0;
}
 
int main(void)
{
  CURL *curl;
  CURLcode res = CURLE_OK;
  struct curl_slist *recipients = NULL;
  struct upload_status upload_ctx = { 0 };
 
  curl = curl_easy_init();
  if(curl) {
    /* This is the URL for your mailserver */
    curl_easy_setopt(curl, CURLOPT_URL,SMTP_TYPE );
 
    /* Note that this option is not 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,CURLOPT_USERNAME,FROM_MAIL);
    curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM_ADDR);
    curl_easy_setopt(curl, CURLOPT_PASSWORD, PASSWORD);
    /* 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_ADDR);
    recipients = curl_slist_append(recipients, CC_ADDR);
    curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
 
    /* We are 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, CURLOPT_READFUNCTION, payload_source);
    curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
    curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(curl,CURLOPT_USE_SSL,CURLUSESSL_ALL);
    
    curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0);
    
    /* Send the message */
    res = curl_easy_perform(curl);
 
    /* 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 will not send the QUIT command until you call cleanup, so you
     * should be able to reuse 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 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);
  }

   getchar();
  return (int)res;
}
Last edited by PeterHu on Dec 26, 2023 23:47, edited 1 time in total.
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Curl Smtp : Asking for help regarding very basic stuff again,sorry

Post by coderJeff »

Hi Peter

Careful on translating data types; UBYTE is not the same type as size_t
See results of this:

Code: Select all

#include "crt.bi"
print "sizeof size_t ="; sizeof( size_t )
print "sizeof ubyte  ="; sizeof( ubyte )
sleep
Because you have #include "crt.bi", you should be able to use the original C types if you want, for example:

Code: Select all

type upload_status
   as size_t bytes_read
end type
Check the parameters of 'payload_source()' too

Hope this helps, I didn't test the program itself
PeterHu
Posts: 159
Joined: Jul 24, 2022 4:57

Re: Curl Smtp : Asking for help regarding very basic stuff again,sorry

Post by PeterHu »

coderJeff wrote: Dec 26, 2023 13:33 Hi Peter

Careful on translating data types; UBYTE is not the same type as size_t
See results of this:

Code: Select all

#include "crt.bi"
print "sizeof size_t ="; sizeof( size_t )
print "sizeof ubyte  ="; sizeof( ubyte )
sleep
Because you have #include "crt.bi", you should be able to use the original C types if you want, for example:

Code: Select all

type upload_status
   as size_t bytes_read
end type
Check the parameters of 'payload_source()' too

Hope this helps, I didn't test the program itself
Thank you for the help.I have never realised size_t been introduced into freebasic as well when I include crt.

I modified the bas source file accordingly but still the subject and content of the incoming mail are blank.

I guess curl api doesn't never have a chance to read the content of payload_text,but I just don't know what's wrong with my translation on the multiline const string payload_text and those assigment/memory copy in payload_source.

Thanks and best regards,
Peter
adeyblue
Posts: 300
Joined: Nov 07, 2019 20:08

Re: Curl Smtp : Asking for help regarding very basic stuff again,sorry

Post by adeyblue »

There's one pretty big difference between the C code formation of the message headers, which is 5 lines:

Code: Select all

  "To: " TO_MAIL "\r\n"
  "From: " FROM_MAIL "\r\n"
  "Cc: " CC_MAIL "\r\n"
  "Subject: SMTP example message\r\n"
  "\r\n" /* empty line to divide headers from body, see RFC 5322 */
And your translation, which is 6:

Code: Select all

    !"To: " & ToAddress & !"\r\n" _
    !"From: " & MailFrom & !"\r\n" _
    !"Cc: " & CcAddress & !"\r\n" _
    !"\r\n" _
    !"Subject: SMTP example message" & !"\r\n" _
    !"\r\n" _ 
The comment in the last line of the C code there explains why yours does what it does.
PeterHu
Posts: 159
Joined: Jul 24, 2022 4:57

Re: Curl Smtp : Asking for help regarding very basic stuff again,sorry

Post by PeterHu »

adeyblue wrote: Dec 28, 2023 2:08 There's one pretty big difference between the C code formation of the message headers, which is 5 lines:

Code: Select all

  "To: " TO_MAIL "\r\n"
  "From: " FROM_MAIL "\r\n"
  "Cc: " CC_MAIL "\r\n"
  "Subject: SMTP example message\r\n"
  "\r\n" /* empty line to divide headers from body, see RFC 5322 */
And your translation, which is 6:

Code: Select all

    !"To: " & ToAddress & !"\r\n" _
    !"From: " & MailFrom & !"\r\n" _
    !"Cc: " & CcAddress & !"\r\n" _
    !"\r\n" _
    !"Subject: SMTP example message" & !"\r\n" _
    !"\r\n" _ 
The comment in the last line of the C code there explains why yours does what it does.
Thank you very much for the help.Now I made the fb version as same as c version on the above string translations,but the result is still a failure.
As it is running in verbose mode,I can see the console just printed about 11 lines of blank when the api tried to read the data from payload_text(maybe).

Code: Select all

    !"To: " & ToAddress & !"\r\n" _
    !"From: " & MailFrom & !"\r\n" _
    !"Cc: " & CcAddress & !"\r\n" _
    !"Subject: SMTP example message" & !"\r\n" _
    !"\r\n" _ 
Post Reply