` Printed Icetips Article

Icetips Article



Email, FTP etc.: Checking for valid email address
2003-08-12 -- Jim Kane
 
Newsgroups: TopSpeed.Topic.Third_Party

This is what I use.  I just wrote it not long ago so if anyone who uses it
has comments please send them.
I decide to use these two test and then set up a bounce box where all emails
sent out that bounce come back to a special email address.  Anything comming
back to that address needs to be investigated.  I usually set up an outlook
client and use ole to control outlook to find all the bounces and strip bad
emails.
jim kane

"Geoff Bomford"  wrote in message
news:3f38caeb$1@news.softvelocity.com...
> Is anyone aware of a Clarion tool that can check the MX Record of an Email
> address for a valid entry?
>
> It is the sort of thing that I would expect Nettalk to be capable of - is
> it?  If so, anyone care to share some code?

Editors Note:  Jim attached two files with his message, dnscl.inc and dnscl.clw.  
These contain an ABC compliant class and should be placed in the Clarion LibSrc
directory:


dnscl.inc:

!ABCIncludeFile

OMIT('_EndOfInclude_',_dnsclPresent_)
_dnsclPresent_ EQUATE(1)

!Other Classes
!  Include('OtherClass.INC')

!Equates - will be seen by using program

dnsclType
Class,type,module('dnscl.CLW'),LINK('dnscl.CLW',_ABCLinkMode_),DLL(_ABCDllMode_)

!Member Data

!dnsresult values:
! -1 : dns not tried - email syntax error
!  0 : dns tried and no error
! -2 : dns tried, no error reading records but no MX records were returned - domain does
not support email
!Anything else: win32 error 9000 -10000  are dns specific errors


!Methods - all return 0 for success if they return anything
SyntaxCheck            Procedure(string pEmail,*string pErrorStr, *cstring
domain),byte,private
ValidateEmailAddr      Procedure(string pEMailAddr, *String pErrorstr, *long
DnsResult),byte
FormatErrMsg           Procedure(*string errmsg,long EC),private

    end
 !_EndOfInclude_


dnscl.clw:

 Member
_ABCDllMode_  EQUATE(0)
_ABCLinkMode_ EQUATE(1)

 Map
   !Local Procedures, if any, go here
   !API Prototypes
   Module('api')
      DnsQuery(*cstring pDomainName, ushort pWType=15, long fOptions=0, |
               long plpServerList=0, *long plpResult, long Reserved=0),|
                 long,pascal,proc,raw,name('DnsQuery_A')
      DnsRecordListFree(long plpResult, long pfreeType=1),pascal,raw,proc
      getlasterror(),long,pascal
      memcpy(long,long,signed),long,proc,name('_Memcpy')
      lstrlen(long),SIGNED,PASCAL,NAME('lstrLenA')
     
FormatMessage(ulong,long,ulong,ulong,long,ulong,long),ulong,PASCAL,RAW,Name('FormatMessageA')
      LocalFree(long),long,PASCAL,proc
   End
 end

!Includes this class and others
 include('dnscl.inc')

!global data for this module
Return:Benign equate(0)
Return:Fatal  equate(3)
Return:Notify equate(5)

maxemaillength  equate(100)
!-------------------------------------------------------------
!---Start of dnscltype
!-------------------------------------------------------------

dnscltype.SyntaxCheck                          procedure(string pEmail,*string pErrorStr,
*cstring pdomain)
!purpose: do a syntax check on the email address
!input: email address,
!output: place to write an error description and the domain portion
!return value: beign-good email; Notify - bad email; Fatal - error - cant tell
AtCharIdx  long,auto
L          long,auto
Mailbox      string(maxemaillength)
DomainPrefix string(maxemaillength)
DomainSuffix string(maxemaillength)
LastPeriodIdx long
thechar      string(1)
I           long
partlen     long
!Match:Simple       EQUATE(0)
!Match:Wild         EQUATE(1)
!Match:Regular      EQUATE(2)
!Match:Soundex      EQUATE(3)
!Match:NoCase       EQUATE(10H)   ! May be added to Simple,Wild and Regular

  code
  L=len(clip(pEmail))
  if L >MaxEmailLength then
    pErrorStr='Email address too long!'
    return return:notify
  end

  if L<3 then
    pErrorstr='Email address too short.'
    return return:notify
  end

  if pemail[1]='<<' or pEmail[L]='>' then
    pErrorstr='Angle brackets arround the address must be removed.'
    return return:notify
  end

  !find the 1st @ char
  AtCharIdx=instring('@',pemail,1,1)
  if ~AtCharidx or AtCharidx>=L then
    pErrorstr='@ character is not in a legal place.'
    return return:notify
  end

  Mailbox=pEmail[1 : atCharIdx-1]
  pdomain=pEmail[atCharIdx + 1 : L]

  !Message('email parts: ' & clip(mailbox) & ' ' & clip(DomainPrefix) & '  ' &
clip(DomainSuffix))
  !see http://www.netusi.com/regExp.htm
  !not going to support the @[10.1.23.1] format which is legal for the
domainprefix&'.'&domainsuffix

  !format for the mail box name
  !   ^([A-Za-z0-9_]\-\.]+)
  partlen=len(clip(mailbox))
  loop I= 1 to partlen
    thechar=upper(mailbox[i])
    if (thechar>='A' and theChar<='Z') or |
       (thechar>='0' and theChar<='9') or |
       thechar='_' or |
       theChar='-' or |
       thechar='.' then
         cycle !good char
    else
      pErrorstr='The mail box name may only contain letters, numbers, dash, underline and
periods.'
      Return return:notify
    end
  end

  !find the last period in the email - must be before the @ sign
  LastPeriodIdx=0
  loop I= L to AtCharIdx+1 by -1
    if pEmail[I]='.' then
      LastPeriodidx=I
      break
    end
  end
  if ~LastPeriodIdx or L - LastPeriodIdx < 2 or L - LastPeriodIdx>4 then
    pErrorstr='There must be 2 to 4 characters after the final period at the end of the
email.'
    return return:notify
  end

  !break the domain into 2 parts
  DomainPrefix=pEmail[atCharIdx+1 : LastPeriodIDx-1]
  DomainSuffix=pEmail[LastPeriodIdx+1 : L]


  !look for the [nn.nn.nn.n]
  partlen=len(clip(pDomain))
  if pDomain[1]='[' and pDomain[PartLen]=']' then
    !special [nn.nn.nn.n] syntax
    if pDomain[2]='.' or pdomain[PartLen-1]='.' or PartLen<9 or PartLen>17 then
      pErrorstr='To use an ip address for the domain the syntax is [nnn.nnn.nnn.nnn].'
      return return:notify
    end
    Loop I=1 to partlen
      thechar=pdomain[I]
      if thechar='.' or  (thechar>='0' and thechar<='9') then
        !good char
        cycle
      end
      pErrorstr='To use an ip address for the domain the syntax is [nnn.nnn.nnn.nnn].'
      return return:notify
    end
    Return return:benign  !no further domain check is needed
  end


  !format for the DomainPrefix
  !   (([[A-Za-z0-9_]\-]+\.)+)

  L=len(clip(DomainPrefix))
  loop I=1 to L
    thechar=upper(domainPrefix[i])
    if (thechar>='A' and theChar<='Z') or |
       (thechar>='0' and theChar<='9') or |
       thechar='_' or |
       theChar='-' or |
       thechar='.' then
         cycle !good char
    else
      pErrorstr='The domain name before the period may only contain letters, ' &|
                'numbers, dash, underline and periods.'
      Return return:notify
    end
  end

  !format for the DomainSuffix
  !   ([a-zA-Z]{2,4})
  L=len(clip(DomainSuffix))
  loop I=1 to L
    thechar=upper(domainSuffix[i])
    if (thechar>='A' and theChar<='Z') then
         cycle !good char
    else
      pErrorstr='The domain name after the final period may only contain letters.'
      Return return:notify
    end
  end


  return return:benign


dnsclType.ValidateEmailAddr                  Procedure(string pEMailAddr, *String
pErrorstr, *long DnsResult)
DomainName      cstring(maxemaillength)
!DnsResult       long(-1)   !return value from dnsQuery. 0=no error
lpResult        long   !pointer to the array of dns_recordMX's

lpRecord        long    !pointer to the current record being read
recordcount     long    !counts the number of records read
MXRecordCount   long    !the number of mx records read (mx = mail records)
cstringRef      &cstring  !cstring reference variable to make displaying the cstrings
easier


dns_RecordMXType group,Type
lpNextRecord    long
lpName          long
wType           ushort
wDataLength     ushort
flags           long   !section:2,delete:1,charset:2,unused:3,Reserved:24
dwTtl           long
dwReserved      long
lpNameExchange  long
wPreference     ushort
pad             Ushort
  end
dns_RecordMx    &Dns_RecordMXType
DNS_Type_MX     equate(15)
res             byte(return:fatal)
  code
  !initialize output
  clear(pErrorStr)
  dnsresult=-1 !not attempted
  res=SELF.SyntaxCheck(pEmailAddr,pErrorStr, domainname)
  if res=return:notify then
    pErrorstr=clip(pErrorstr) & '  Bad email address syntax!'
  end
  if res then
    return res
  end

  clear(lpResult)
  clear(RecordCount)
  Clear(MXRecordCount)
  dnsResult = DnsQuery(domainname,DNS_Type_MX,0,0,lpResult,0)
  !9002 dns server failure - tried to resolve myown domain name
  !9003 means no match - name is bad
  !9502 no records found for the query
  !9503 means match on name but no mail server
  !Message('dnsResult='&dnsresult & '  lpResult='&lpResult)

  !init lpRecord to the address of the 1st record
  lpRecord=lpResult 
  if dnsResult=0 then
   loop
    dns_RecordMX&=(lpResult)
    if dns_recordmx.wtype=15 then
      MXRecordCount+=1
      !cstringRef&=(dns_RecordMX.lpName)
      !Message('Name='& cstringRef)
      !cstringRef&=(dns_RecordMX.lpNameExchange)
      !message('Mail server='&cStringRef)
      !message('timetolive='&dns_recordmx.dwttl)
      !message('preference='&dns_recordmx.wPreference)
      !message('DataLength='&dns_Recordmx.wDataLength)
    !else
    !  message('wtype='&dns_recordmx.wtype)
    end
    recordcount+=1
    if ~dns_RecordMX.lpNextRecord or dns_RecordMX.lpNextRecord=lpRecord then
       !Message('done with records  count='&Recordcount)
       break
    else
      !Message('thisrec='&lprecord & ' NextRecord='&dns_RecordMX.lpNextRecord)
      lpRecord=dns_RecordMX.lpNextRecord
    end
   end
   if ~MXRecordCount then .
  else
    !make a message string
    SeLF.formatErrMsg(pErrorStr,DnsResult)
    !Message(clip(errorstr),'DNS Error')
  end
  !free list if any
  if lpResult then
    DnsRecordListFree(lpResult)
  end

  !show success or failure
  if DNSResult=0 then
    if MxRecordCount>0 then
      pErrorStr='Email address is okay!'
      res=return:benign !every thing good
    else
      !mxrecordcount<=0 and dnsresult=0
      dnsresult=-2 !code for tried dns but no mx records
      pErrorstr='The domain exists but does not support email.'
      res=return:notify
    end
  elsif DNSResult=9852 or DNSResult<9000 or dnsresult>10000 then
    !9852 = no network or no dns server
    res=return:Fatal !wierd error like out of memory or somthing
    pErrorstr='Error determining if the domain is valid. ' & clip(pErrorstr)
  else
    !9xxx errorcode in dns land
    Res=Return:Notify !probably not a good domain
    pErrorstr='Email domain bad! '& clip(pErrorstr)
  end
  return res





dnsclType.FormatErrMsg  procedure(*string errmsg,long EC)
lpMsgBuf  long,auto  !Address of message string formated by FormatMessage Call
lenMsgBuf long,auto  !char in cstring return by formatMessage
res       byte(3)
  code
  If FormatMessage(1100H, | !Format_Message_Allocate_Buffer + Format_message_From_System
                   0, EC,0,Address(lpMsgBuf),0,0)
    lenMsgBuf = lstrlen(lpmsgBuf)
    If lenMsgBuf then
      lenMsgBuf = choose(lenMsgBuf>size(errMsg), size(errMsg), lenmsgbuf)
      Clear(errMsg)
      memcpy(Address(errMsg), lpMsgBuf, lenMsgBuf)
      res=0
    end
    If lpMsgBuf then  LocalFree(lpMsgBuf). !clean up allocated memory
  end
  if res then errmsg='Unknown error - '&ec .
  Return



Printed May 4, 2024, 8:01 am
This article has been viewed/printed 35125 times.
Google search has resulted in 546 hits on this article since January 25, 2004.