SendMail_Include.pbi (windows / linux / macos)

Share your advanced PureBasic knowledge/code with the community.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

SendMail_Include.pbi (windows / linux / macos)

Post by ts-soft »

This Code is based on some older Code!
Sending Mail with SMTP-AUTH + add multiple attachments on Linux / Windows
Supports ASCII and Unicode, supports 32- and 64-Bit


Here the Code:

Code: Select all

;- _____________________________________________________________________________
;- |                                                                           |
;- |                              SendEmail (New)                              |
;- |                              _______________                              |
;- |                                                                           |
;- |___________________________________________________________________________|
;{ SendEmail (New) (Start)                                       
; Author : clipper
; PureBasic 3.93
; Changed to Purebasic 4.61 x86 from Falko
; Sending Mail with SMTP-AUTH + add multiple attachments
; Don´t fill the Username if you don't want authentification

; changed by ts-soft:
; + variabledeclaration (for EnableExplicit)
; + Prefix SendMail_ for Functions and global Vars, __SendMail_ for private Functions
; + compatibility to linux
; + option to set the port in SendMail_SendEmail()
; + Unicode-Support
; ^ fixed many bugs
; + added Structure for SendMail_SendEMail() Parameter
; + added Callback for AttachedFile
; + added Protocol option
; + added timeout
; + cancel on more errors, thx to IdeasVacuum
; + mime-support for MacOS
; + supports multiple recipients
;}
EnableExplicit

Enumeration
  #eHlo
  #RequestAuthentication
  #Username
  #Password
  #MailFrom
  #RcptTo
  #Data
  #Quit
  #Complete
EndEnumeration

Structure SendMail_Parameter
  Name.s
  Sender.s
  Recipient.s
  UserName.s
  Password.s
  SMTPServer.s
  Subject.s
  Body.s
  Port.w
  ProgressAttachedFile.i
  EnableProtocol.b
EndStructure

Prototype SendMail_Callback(percent.i)

Global NewList SendMail_Attachments.s()
Global SendMail_ConnectionID

Procedure SendMail_AddAttachment(File.s)
  AddElement(SendMail_Attachments())
  SendMail_Attachments() =  File
EndProcedure

Procedure SendMail_NoAttachment()
  ClearList(SendMail_Attachments())
EndProcedure

Procedure.s __SendMail_GetMimeType(pExt.s)
  ; Cross-Platform
  ; Windows code originally by Kale
  ; Linux code by Straker
  ;
  ; returns as default "application/octet-stream" if Mime Type is not found.
  
  Protected lRetVal.s, lMimeFile.s, lContinue
  Protected hKey, lKeyValue.s, lDataSize.l, lLoop
  Protected lLof.q, *lMemoryID, lBytesRead, lFileContents.s
  Protected lPos1, lPos2, lMimeLen, lMyChar.s, lDefault.s
  Protected MimeFile
  
  Protected Dim lExt.s(7)
  
  lContinue = 1
  lDefault = "application/octet-stream"
  
  CompilerSelect #PB_Compiler_OS

    CompilerCase #PB_OS_MacOS
      Select LCase(pExt)
        Case "pdf" : lRetVal = "application/pdf"
        Case "ai", "eps", "ps" : lRetVal = "application/postscript"
        Case "rtf" : lRetVal = "application/rtf"
        Case "tar" : lRetVal = "application/x-tar"
        Case "zip" : lRetVal = "application/zip"
        Case "au", "snd" : lRetVal = "audio/basic"
        Case "aif", "aiff", "aifc" : lRetVal = "audio/x-aiff"
        Case "wav" : lRetVal = "audio/x-wav"
        Case "gif" : lRetVal = "image/gif"
        Case "jpg", "jpeg", "jpe" : lRetVal = "image/jpeg"
        Case "png" : lRetVal = "image/png"
        Case "tiff", "tif" : lRetVal = "image/tiff"
        Case "zip" : lRetVal = "multipart/x-zip"
        Case "gz", "gzip" : lRetVal = "multipart/x-gzip"
        Case "htm", "html" : lRetVal = "text/html"
        Case "txt", "g", "h", "c", "cc", "hh", "m", "f90" : lRetVal = "text/plain"
        Case "mpeg", "mpg", "mpe" : lRetVal = "video/mpeg"
        Case "qt", "mov" : lRetVal = "video/quicktime"
        Case "avi" : lRetVal = "video/msvideo "
        Case "movie" : lRetVal = "video/x-sgi-movie"
        Default
          lRetVal = lDefault
      EndSelect    

    CompilerCase #PB_OS_Windows
      pExt = ("." + pExt)
      lKeyValue = Space(255)
      lDataSize = 255
      If (RegOpenKeyEx_(#HKEY_CLASSES_ROOT, pExt, 0, #KEY_READ, @hKey))
        lKeyValue = lDefault
      Else
        If RegQueryValueEx_(hKey, "Content Type", 0, 0, @lKeyValue, @lDataSize)
          lKeyValue = lDefault
        Else
          lKeyValue = Left(lKeyValue, (lDataSize - 1))
        EndIf
        RegCloseKey_(hKey)
      EndIf
      lRetVal = lKeyValue
      
    CompilerCase #PB_OS_Linux
      
      pExt = LCase(pExt)
      lRetVal = lDefault
      lMimeFile = "/etc/mime.types"
      
      MimeFile = ReadFile(#PB_Any, lMimeFile)
      If MimeFile
        lLof = Lof(MimeFile)
        *lMemoryID = AllocateMemory(lLof)
        If (*lMemoryID)
          lBytesRead = ReadData(MimeFile, *lMemoryID, lLof)
          lFileContents = PeekS(*lMemoryID, lLof, #PB_UTF8)
        Else
          lContinue = 0
        EndIf
        CloseFile(MimeFile)
      Else
        lContinue = 0
      EndIf
      
      If (lContinue = 1)
        ; find the extension in the /etc/mime.types file
        
        lExt.s(0) = (Space(1) + pExt + Space(1))
        lExt.s(1) = (Chr(9) + pExt + Chr(10))
        lExt.s(2) = (Chr(9) + pExt + Space(1))
        lExt.s(3) = (Chr(9) + pExt + Chr(9))
        lExt.s(4) = (Chr(9) + pExt)
        lExt.s(5) = (Space(1) + pExt + Chr(10))
        lExt.s(6) = (Space(1) + pExt + Chr(9))
        lExt.s(7) = (Space(1) + pExt)
        
        lContinue = 0
        
        For lLoop = 0 To 7 Step 1
          lPos1 = FindString(lFileContents, lExt.s(lLoop), 1)
          If (lPos1 > 0)
            lContinue = 1
            Break
          EndIf
        Next
        
      EndIf
      
      If (lContinue = 1)
        ; found the line - parse the mime type...
        For lLoop = 1 To 80 Step 1
          If (Mid(lFileContents, (lPos1 - lLoop), 1) = Chr(10))
            lPos2 = (lPos1 - lLoop + 1)
            Break
          EndIf
        Next
      EndIf
      
      If (lPos2 > 0)
        For lLoop = 1 To 80 Step 1
          lMyChar = Mid(lFileContents, (lPos2 + lLoop), 1)
          If ((lMyChar = Chr(9)) Or (lMyChar = " "))
            lMimeLen = lLoop
            Break
          EndIf
        Next
      EndIf
      
      If (lMimeLen > 0)
        lRetVal = Trim(Mid(lFileContents, lPos2, lMimeLen))
        If (Left(lRetVal, 1) = "#")
          lRetVal = lDefault
        EndIf
      EndIf
      
      FreeMemory(*lMemoryID)
      
  CompilerEndSelect
  
  ProcedureReturn lRetVal
EndProcedure

Procedure.s __SendMail_Base64Encode(strText.s)
  Protected Result.s
  Protected *B64EncodeBufferA = AllocateMemory(Len(strText) + 1)
  Protected *B64EncodeBufferB = AllocateMemory((Len(strText) * 3) + 1)
  PokeS(*B64EncodeBufferA, strText, -1, #PB_Ascii)
  Base64Encoder(*B64EncodeBufferA, Len(strText), *B64EncodeBufferB, Len(strText) * 3)
  Result = PeekS(*B64EncodeBufferB, -1, #PB_Ascii)
  FreeMemory(*B64EncodeBufferA)
  FreeMemory(*B64EncodeBufferB)
  ProcedureReturn Result
EndProcedure

Procedure __SendMail_Send(msg.s)
  Protected *mem, length
  msg + #CRLF$
  length = StringByteLength(msg, #PB_UTF8)
  *mem = AllocateMemory(length + 1)
  If *mem
    PokeS(*mem, msg, -1, #PB_UTF8)
    SendNetworkData(SendMail_ConnectionID, *mem, length)
    FreeMemory(*mem)
  EndIf
EndProcedure

Procedure __SendMail_SendFiles(ProgressCB.SendMail_Callback = 0)
  Protected file.s, FF, InputBufferLength.i, OutputBufferLength.i
  Protected *memin, *memout, Boundry.s, temp.s, i
  
  ResetList(SendMail_Attachments())
  While(NextElement(SendMail_Attachments()))
    file = SendMail_Attachments()
    __SendMail_Send("")
    FF = ReadFile(#PB_Any, file)
    InputBufferLength = Lof(FF)
    OutputBufferLength = InputBufferLength * 1.4
    *memin = AllocateMemory(InputBufferLength)
    If *memin
      *memout = AllocateMemory(OutputBufferLength)
      If *memout
        Boundry = "--MyBoundary"
        __SendMail_Send(Boundry)
        __SendMail_Send("Content-Type: " + __SendMail_GetMIMEType(GetExtensionPart(file)) + "; name=" + Chr(34) + GetFilePart(file.s) + Chr(34))
        __SendMail_Send("Content-Transfer-Encoding: base64")
        __SendMail_Send("Content-Disposition: Attachment; filename=" + Chr(34) + GetFilePart(file) + Chr(34))
        __SendMail_Send("")
        ReadData(FF, *memin, InputBufferLength)
        Base64Encoder(*memin, 60, *memout, OutputBufferLength)
        __SendMail_Send(PeekS(*memout, 60, #PB_Ascii)) ; this must be done because For i=0 To OutputBufferLength/60 doesn't work
        If ProgressCB
          ProgressCB(60 / (OutputBufferLength / 100))
        EndIf
        Base64Encoder(*memin, InputBufferLength, *memout, OutputBufferLength)               
        For i = 1 To OutputBufferLength / 60
          temp = Trim(PeekS(*memout + i * 60, 60, #PB_Ascii))
          If Len(temp) > 0
            __SendMail_Send(temp)
            If ProgressCB
              ProgressCB((60 * i) / (OutputBufferLength / 100))
            EndIf
          EndIf
        Next
        FreeMemory(*memout)
      EndIf
      FreeMemory(*memin)
    EndIf
    CloseFile(FF)
  Wend
  ProcedureReturn
EndProcedure

Procedure.s __SendMail_SendEmail(*para.SendMail_Parameter, TimeOut.l, Number = 0)
  Protected loop250, ReceivedData.s, ct, cmdID.s, cmdText.s
  Protected State, quit, ProtocolTxt.s, Time.l = ElapsedMilliseconds()

  If *para\Port = 0 : *para\Port = 25 : EndIf
  
  SendMail_ConnectionID = OpenNetworkConnection(*para\SMTPServer, *para\Port)
  With *para
    If SendMail_ConnectionID
      Repeat   
        If NetworkClientEvent(SendMail_ConnectionID)
          ReceivedData = Space(9999)
          ct = ReceiveNetworkData(SendMail_ConnectionID, @ReceivedData, 9999)
          ReceivedData = PeekS(@ReceivedData, -1, #PB_Ascii)
          If ct
            cmdID = Left(ReceivedData, 3)
            cmdText = Mid(ReceivedData, 5, ct - 6)
            If \EnableProtocol = #True
              ProtocolTxt + "[" + FormatDate("%hh:%ii:%ss", Date()) + "] " + cmdText + #CRLF$
            EndIf
            Select cmdID
              Case "220"
                If Len(\Username) > 0
                  __SendMail_Send("Ehlo " + Hostname())
                  State = #eHlo
                Else
                  __SendMail_Send("HELO " + Hostname())
                  State = #MailFrom
                EndIf   
              Case "221"
                __SendMail_Send("[connection closed]")
                State = #Complete
                quit = 1     
              Case "235"
                __SendMail_Send("MAIL FROM: <" + \Sender + ">")
                State = #RcptTo
                
              Case "334"
                If State=#RequestAuthentication
                  __SendMail_Send(__SendMail_Base64Encode(\UserName))
                  State = #Username
                EndIf
                If State = #Username
                  __SendMail_Send(__SendMail_Base64Encode(\Password))
                  state = #Password
                EndIf
                
              Case "250"
                Select state
                  Case #eHlo
                    __SendMail_Send("AUTH LOGIN")
                    state = #RequestAuthentication     
                  Case #MailFrom   
                    __SendMail_Send("MAIL FROM: <" + \Sender + ">")
                    state = #RcptTo
                  Case #RcptTo
                    __SendMail_Send("RCPT TO: <" + StringField(\Recipient, Number + 1, ";") + ">")
                    state = #Data
                  Case #Data
                    __SendMail_Send("DATA")
                    state = #Quit
                  Case #Quit
                    __SendMail_Send("QUIT")
                EndSelect
                
              Case "251"
                __SendMail_Send("DATA")
                state = #Data
              Case "354"
                __SendMail_Send("X-Mailer: eSMTP 1.0")
                __SendMail_Send("To: " + \Recipient)
                __SendMail_Send("From: " + \Name + " <" + \Sender + ">")
                __SendMail_Send("Reply-To: " + \Sender)
                __SendMail_Send("Date:" + FormatDate("%dd/%mm/%yyyy @ %hh:%ii:%ss", Date()))
                __SendMail_Send("Subject: " + \Subject)
                __SendMail_Send("MIME-Version: 1.0")
                __SendMail_Send("Content-Type: multipart/mixed; boundary=" + Chr(34) + "MyBoundary" + Chr(34))
                __SendMail_Send("")
                __SendMail_Send("--MyBoundary")
                __SendMail_Send("Content-Type: text/plain; charset=utf-8")
                __SendMail_Send("Content-Transfer-Encoding: 8bit")
                __SendMail_Send("")                     
                __SendMail_Send(\Body)
                __SendMail_SendFiles(\ProgressAttachedFile)
                __SendMail_Send("--MyBoundary--")
                __SendMail_Send(".")
 
               Case "421", "450", "451", "452", "454", "500", "501", "502", "503", "535","550", "551", "552", "553", "554"
                ProtocolTxt + cmdID + " " + cmdText + #CRLF$
                quit = 1     
            EndSelect
          EndIf
        EndIf
        
      Until quit = 1 Or ElapsedMilliseconds() > (Time + TimeOut)
      CloseNetworkConnection(SendMail_ConnectionID)
      
    EndIf
  EndWith
  
  ProcedureReturn ProtocolTxt
EndProcedure

Procedure.s SendMail_SendEmail(*para.SendMail_Parameter, TimeOut.l = 15000)
  Protected Result.s
  Protected I, Count = CountString(*para\Recipient, ";")
  For I = 0 To Count
    Result + StringField(*para\Recipient, I + 1, ";") + ":" + #CRLF$
    Result + __SendMail_SendEmail(*para, TimeOut.l, I)
  Next
  ProcedureReturn Result
EndProcedure

InitNetwork()

Define mpara.SendMail_Parameter
With mpara
  \Name = "Mein Name bei email.de"  ; Hier ist egal was steht.
  \Sender = "******@alice.de"       ; E-Mail des Senders
  \Recipient = "******@web.de"      ; E-Mail des Empfängers
  \UserName = "******@alice.de"     ; Username
  \Password = "******"              ; hier dein Kennwort
  \SMTPServer = "smtp.alice.de"
  \Subject = "Diese Mail wurde von mir geschickt" ; Hier Betreffzeile
  \Body = "Das ist eine Mail, die ich mal testweise an mich selber über PB schicke" ; Hier der Text im Body
  \Port = 587
  \EnableProtocol = #True
EndWith

SendMail_AddAttachment("d:\Dateien\purebasic\Meine Projekte\SendMail\SendMail_Include.pbi")
Define.s ProtocolTxt.s = SendMail_SendEmail(mpara)

If ProtocolTxt
  OpenWindow(0, #PB_Ignore, #PB_Ignore, 640, 480, "SendMail - Protocol")
  EditorGadget(0, 5, 5, 630, 470, #PB_Editor_ReadOnly)
  SetGadgetText(0, ProtocolTxt)
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
Successful tested on: Evanzo, Alice.de, GMX,de, Web.de
Doesn't work on GMail.

Have Fun

// edit:
Added Protocol-Option!
Last edited by ts-soft on Sat Jul 21, 2012 1:04 pm, edited 6 times in total.
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: SendMail_Include.pbi (windows / linux)

Post by IdeasVacuum »

A beautiful piece of work and cross platform too! :mrgreen:

The server messages received might not be good news. Here is a list of the common bad news messages:

Case "421": sServerMsg = "Connection failed (unknown error)"
Case "450": sServerMsg = "The service is not available, connection timeout"
Case "451": sServerMsg = "Failed, User's mailbox unavailable"
Case "452": sServerMsg = "Aborted due to server error, try again later"
Case "500": sServerMsg = "Aborted: insufficient storage space on server"
Case "501": sServerMsg = "Send command syntax error (line too long)"
Case "502": sServerMsg = "Send command parameter syntax error (Invalid domain address)"
Case "503": sServerMsg = "Command not supported by server"
Case "550": sServerMsg = "Server failed: bad sequence of commands"
Case "551": sServerMsg = "Command failed: User mailbox not found"
Case "552": sServerMsg = "Receipient is not local to this server"
Case "553": sServerMsg = "Aborted: storage allocation exceeded"
Case "554": sServerMsg = "Aborted: Mail box name invalid"

At the moment, the code just closes the connection if a 550 is received but it could of course let the User know there was a problem and what that problem was.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: SendMail_Include.pbi (windows / linux)

Post by ts-soft »

added Timeout.

added abort on more cmdIDs, suggest by IdeasVacuum
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SendMail_Include.pbi (windows / linux)

Post by infratec »

Hi,

I would prefer:

Code: Select all

Case "421", "450", "451", "452", "500", "501", "502", "503", "550", "551", "552", "553", "554"
  ProtocolTxt = cmdID
  quit = 1
If the result does not start with '[' it is an error code.

Bernd
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: SendMail_Include.pbi (windows / linux)

Post by ts-soft »

Thx, i have changed to:

Code: Select all

               Case "421", "450", "451", "452", "454", "500", "501", "502", "503", "535","550", "551", "552", "553", "554"
                ProtocolTxt + cmdID + " " + cmdText + #CRLF$
                quit = 1     
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
kinglestat
Enthusiast
Enthusiast
Posts: 732
Joined: Fri Jul 14, 2006 8:53 pm
Location: Malta
Contact:

Re: SendMail_Include.pbi (windows / linux)

Post by kinglestat »

nice piece of code
I may not help with your coding
Just ask about mental issues!

http://www.lulu.com/spotlight/kingwolf
http://www.sen3.net
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: SendMail_Include.pbi (windows / linux / macos)

Post by ts-soft »

thx :D

Update:
added support for MimeType on MacOS
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
User avatar
captain_skank
Enthusiast
Enthusiast
Posts: 636
Joined: Fri Oct 06, 2006 3:57 pm
Location: England

Re: SendMail_Include.pbi (windows / linux / macos)

Post by captain_skank »

This is a really usefull piece of code, thanks :)

A quick question, I added the following to your .pbi for multiple recipients but it doesn't work - any idea why ?

The first person gets the mail and the second person is listed on the mail as a recipient, but doesn't receive it.

I had a squiz at the smtp protocol but it looks right.

Code: Select all


; \Recipient = "anyone@anywher.com;someone@somewhere.com"

Case #RcptTo
  name.s = ""
  For loopcount = 1 To Len(\Recipient)
    character.s = Mid(\Recipient, loopcount, 1)
     If character <> ";"
       name  + character
     Else
        __SendMail_Send("RCPT TO: <" + name + ">")
        name = ""
     EndIf
  Next
                     
  State = #Data
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: SendMail_Include.pbi (windows / linux / macos)

Post by ts-soft »

The code is not designed to support multiple recipients, this require a complete redesign of sequence.
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
User avatar
captain_skank
Enthusiast
Enthusiast
Posts: 636
Joined: Fri Oct 06, 2006 3:57 pm
Location: England

Re: SendMail_Include.pbi (windows / linux / macos)

Post by captain_skank »

Thanks for the info.
Falko
Enthusiast
Enthusiast
Posts: 269
Joined: Sat Oct 04, 2003 12:57 pm
Location: Germany
Contact:

Re: SendMail_Include.pbi (windows / linux / macos)

Post by Falko »

Multiple recipients ?
Herewith maybe this is possible.

viewtopic.php?p=99699&sid=524377f0c98fe ... 88c#p99699

Not tested.
www.falko-pure.de
Win8 Pro 64-Bit & Mediacenter, PB_5.1B7, GFA-WinDOS, Powerbasic9.05-Windows, NSBasic/CE, NSBasic/Desktop, NSBasic4APP, EmergenceBasic
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: SendMail_Include.pbi (windows / linux / macos)

Post by ts-soft »

thx Falko, but this doesn't work :wink:

Update:
Added support of multiple Recipients
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
Falko
Enthusiast
Enthusiast
Posts: 269
Joined: Sat Oct 04, 2003 12:57 pm
Location: Germany
Contact:

Re: SendMail_Include.pbi (windows / linux / macos)

Post by Falko »

Thx Thomas :wink:
www.falko-pure.de
Win8 Pro 64-Bit & Mediacenter, PB_5.1B7, GFA-WinDOS, Powerbasic9.05-Windows, NSBasic/CE, NSBasic/Desktop, NSBasic4APP, EmergenceBasic
User avatar
captain_skank
Enthusiast
Enthusiast
Posts: 636
Joined: Fri Oct 06, 2006 3:57 pm
Location: England

Re: SendMail_Include.pbi (windows / linux / macos)

Post by captain_skank »

Thx Thomas
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: SendMail_Include.pbi (windows / linux / macos)

Post by srod »

Works great Thomas.

Thanks.
I may look like a mule, but I'm not a complete ass.
Post Reply