webForumDet fria alternativet

E-mail kontroll

10 svar · 750 visningar · startad av Mr T

Mr TMedlem sedan maj 20071 147 inlägg
#1

Hur kollar man att en e-mail adress verkligen finns på ett bra sätt ?

Har ett enkelt skicka-text-formulär, ej någon inloggnings förfrågan eller så, där användaren uppger sin e-mail, och denna e-mailadress vill jag kolla "äktheten" på innan jag skikar iväg det.

Har hört att det ska gå att "fråga" mail servrarna om en e-mail adress är okej. Är det någon som vet hur man gör ?

Ett litet script är ju annars lösningen där man kollar att det finns med ett @ och minst en punkt och att e-mailadressen är minst 5 char. Är det någon som har en sådan färdig koll, tas den tacksamt emot.

voigtann1Medlem sedan juni 20018 810 inlägg
#2
if (preg_match('/^[a-z0-9._%-]+@[a-z0-9.-]+\\.[a-z]{2,4}$/', $subject)) {
	
} else {
	
}
Mr TMedlem sedan maj 20071 147 inlägg
#3

Tack voigtann1.

Ska testa denna enkla och snygg lösning på att alla nödvändiga tecken finns med i en e-mail adress.

Jag hoppas dock att någon har lösningen på hur man kan kolla om domänen och e-mailardessen verkligen finns.

itpastornMedlem sedan feb. 2007280 inlägg
#4

I stort sett en fungerande lösning

Det finns ingen till 100 % fungerande lösning. Följande utkast funkar till 90 %.

<?php
/**
* Kontroll av mejladress
*
* @author  Lars Gunther, Hans Eriksson
* @copyright (c) Lars Gunther, Hans Eriksson
* @license Creative Commons Attribution-Noncommercial-Share Alike 3.0
*          http://creativecommons.org/licenses/by-nc-sa/3.0/
* @package  Keryx_Globala_funktioner
* @subpackage KeryxMejl
*/
/**
* Klass med metoder för att kolla om en email-adress är riktig
*
* Varje metod anropar den föregående om det behövs, så att de bildar en trestegsraket.
* Man kan välja vilken ambitionsnivå man skall hålla sig på.
*
* @version 0.98 Beta.
* @todo    Fixa problem med baklänges DNS
*/
class keryxMejl_validAddress {

    /**
    * @var string @mejl Adressen som undersöks
    */
    private static $mejl     = '';
    /**
    * @var string @mejl Adressens alias-del
    */
    private static $alias    = '';
    /**
    * @var string @mejl Adressens domän-del
    */
    private static $domain   = '';

    /*
    * Felmeddelanden
    */
    const FEL_KORT   = 'Adressen är för kort.';
    const FEL_SNABEL = 'Adressen har inte exakt ett snabel-a.';
    const FEL_ALIAS  = 'Otillåtna tecken i mejlets alias del.';
    const FEL_DOMÄN  = 'Otillåtna tecken i domänangivelsen eller felaktig toppdomän.';
    const FEL_INGENDOMÄN = 'Ingen domän registrerad med namnet %s.'; // Används med sprintf()
    const FEL_SMTPKONTAKT =  'Kunde ej kontakta %s [%s (%u)].'; // Används: sprintf(FEL_SMTPKONTAKT,$mailserver,$errstr,$errno);
    const FEL_SMTPEJSVAR  =  'Mejlserver för domänen %s kunde inte kontaktas.'; // Används: sprintf(FEL_SMTPEJSVAR,self::$domain);
    const FEL_SMTPFALSE   =  'Användaren %s bekräftas inte av mejlservern för domänen %s.'; // Används: sprintf(FEL_SMTPFALSE,self::$mejl,self::$domain);

    /**
    * Funktion som kollar att adressen är korrekt enligt RFC
    *
    * @param string $mejl Den adress som skall testas
    * @param mixed $kod   Felkod från funktionen (skickas "by reference") NULL = inget fel
    * @param array  $fel  Eventuella felmeddelanden (skickas "by reference")
    * @return bool Om adressen uppfyller villkoren eller inte
    * @uses keryxMailAndUriChars
    */
    public static function rfcCheck($mejl='',&$kod=null,&$fel=array())
    {
        self::$mejl = & $mejl;
        // $fel och $kod skall alltid tömmas här så att felen inte ackumuleras om man testar flera adresser
        $fel = array();
        $kod = null;
        // Variabler att använda i regexp-tester
        $MAILATOMCHAR = keryxMailAndUriChars::getString('MAILATOMCHAR');
        $ADRCHAR      = keryxMailAndUriChars::getString('ADRCHAR');
        $TOPS         = keryxMailAndUriChars::getString('TOPS');

        // Förutsätt att adressen är ok
        $ok = true; // Denna variabel håller reda på om villkoren uppfyllts
        // Är adressen tillräckligt lång?
        if ( strlen($mejl) < 5 ) {
            $ok = false;
            $fel[] = self::FEL_KORT;
            $kod = 'rfc';
        }
        // Exakt ett @?
        if ( preg_match_all('/@/',$mejl, $out) !== 1 ) {
            $ok = false;
            $fel[] = self::FEL_SNABEL;
            $kod = 'rfc';
        } else {
            @list($alias,$domain) = explode('@',$mejl);
            // Skapa self::$alias och self::$domain
            // för senare bruk
            self::$alias  = & $alias;
            self::$domain = & $domain;
            // Första delen av adressen
            if ( !preg_match( "/^{$MAILATOMCHAR}+(\.|{$MAILATOMCHAR}+)*\$/" , $alias ) ) {
                $ok = false;
                $fel[] = self::FEL_ALIAS;
                $kod = 'rfc';
            }
            // Andra delen av adressen
            if ( !preg_match( "/^{$ADRCHAR}+\.({$ADRCHAR}+\.)*({$TOPS})\$/", $domain ) ) {
                $ok = false;
                $fel[] = self::FEL_DOMÄN;
                $kod = 'rfc';
            }
        }
        return $ok;
    }

    /**
    * Kontrollera DNS
    *
    * Denna funktion fungerar inte på Windows-servrar, då den använder checkdnsrr
    *
    * Denna funktion anropar rfcCheck om den inte redan körts
    * @param string $mejl Den adress som skall testas
    * @param mixed $kod   Felkod från funktionen (skickas "by reference") NULL = inget fel
    * @param array  $fel  Eventuella felmeddelanden (skickas "by reference")
    * @return bool Om adressen går till en faktiskt registrerad domän
    * @todo Fixa så att den kan köras på Windows också
    */
    public static function dnsCheck($mejl,&$kod=null,&$fel=array())
    {
        // Kolla alltid RFC först, som dessutom tömmer $fel
        if ( false === self::rfcCheck($mejl,$kod,$fel) ) {
            return false;
        }
        if ( checkdnsrr(self::$domain,'MX') ) {
            return true;
        }
        if ( checkdnsrr(self::$domain,'A') ) {
            return true;
        }
        // Kommer man hit är det fel
        $fel[] = sprintf(self::FEL_INGENDOMÄN,self::$domain);
        $kod = 'dns';
        return false;
    }
    /**
    * Kontrollera att mejladressen verkligen finns
    *
    * Denna funktion anropar dnsCheck om den inte redan körts.
    * Vi vet alltså att det finns minst ett A-record för domänen när testerna körs.
    * De enda adresser som inte är äkta, men som slinker igenom, återfinns på "catch-all" system.
    * I händelse av "Reverse DNS lookup failed" från den svarande SMTP-servern, så bekräftas inte
    * korrekta adresser, vilket den anropande kodens logik måste kunna hantera.
    *
    * @param string $mejl Den adress som skall testas
    * @param mixed  $kod  Svarskod från servern (skickas "by reference")
    * @param array  $fel  Eventuella felmeddelanden (skickas "by reference")
    * @param string $serv_str  Svar från servern (skickas "by reference")
    * @return bool Om adressen känns igen på en svarande mailserver eller inte
    * @todo Denna funktion fungerar inte på Windows-servrar, då den använder mx_records
    */
    public static function smtpCheck($mejl,&$kod=null,&$fel=array(),&$serv_str=null,
                                     $server_name='keryx.se', $server_admin='gunther@keryx.se')
    {
        // Nollställ $serv_str så att den inte ackumuleras vid flera körningar
        $serv_str = null;

        // Om inte någon DNS är registrerad, så bryt nu
        if ( !self::dnsCheck($mejl,$kod,$fel) ) {
            return false;
        }
        // starta med obekräftat = false
        $mejlok = false;

        // self::$domain skapades i dnsCheck
        getmxrr(self::$domain,$mx_records,$mx_weight);

        // om MX-record finns använd det (ex "swipnet.se" har inget MX-record)
        // annars om MX-record inte finns använd A-record
        // Vi vet sedan tidigare self::dnsCheck() att det finns minst ett A-record
        if ( count($mx_records) < 1 ) {
            $mx_records[0] = self::$domain;
        } else {
            // sortera MX-records efter prioritet
            array_multisort($mx_weight,$mx_records);
            reset($mx_records);
        }

        // kontrollera om SMTP-server svarar
        $mx_checked = false;
        while ( !$mx_checked AND ( list($key,$mailserver) = each($mx_records) ) ) {
            //försök att öppna anslutning till SMTP-server
            $fp = @fsockopen("$mailserver", 25, $errno, $errstr, 5);
            if (!$fp) {
                $serv_str = sprintf(self::FEL_SMTPKONTAKT,$mailserver,$errstr,$errno);
                $kod = 'smtpdown';
            } else {
                $mx_checked = true;
                //simulara en SMTP-session för att verifiera användare
                //se RFC 821 för mer detaljer i SMTP-protokollet (http://www.faqs.org/rfcs/rfc821.html)
                $res  = fread($fp,1024);
                // Somliga mejlservrar svarar med mer än en rad när de kontaktas, därav fread
                fputs($fp, "HELO $server_name\r\n");
                $res .= fgets($fp,1024);
                fputs($fp, 'MAIL FROM:<'.$server_admin.">\r\n");
                // Catch reverse DNS-lookup failure
                $prel_res = fgets($fp,1024);
                if ( (int)$prel_res != 250 ) {
                    $kod = 'reverse_dns_lookup_failed';
                    $serv_str = substr($prel_res,4);
                    $mx_checked = false;
                } else {
                    $res .= $prel_res;
                    fputs($fp, 'RCPT TO:<'.self::$mejl.">\r\n");
                    $svar = fread($fp,1024); // kan sträcka sig över flera rader
                    // $kod blir här svarskoden från smtp-servern
                    $kod  = (int)$svar;
                    $serv_str = substr($svar,4);
                    $res .= $svar;
                }
                //avbryt SMTP-session (skicka inget mail)
                fputs($fp, "RSET\r\n");
                $res .= fgets($fp,1024);
                fputs($fp, "QUIT\r\n");
                $res .= fgets($fp,1024);
                //stäng anslutning till SMTP-server
                fclose($fp);
            }
        }
        // Om ingen server kunde kontaktas returnera false
        if ( !$mx_checked ) {
            $mejlok = false;
            $fel[] = sprintf(self::FEL_SMTPEJSVAR,self::$domain);
        }
        //om svarskod från mailserver är ok (250) eller mail forward (251) returnera true
        // Svarskod mellan 450 och 460 är greylistade adresser
        // Se http://projects.puremagic.com/greylisting/
        else {
            $mejlok = ( ($kod == 250) OR ($kod == 251) OR ( ($kod >= 450) AND ($kod < 460) ) );
            if ( !$mejlok ) {
                $fel[] = sprintf(self::FEL_SMTPFALSE,self::$mejl,self::$domain);
            }
        }
        return $mejlok;
    }
}
itpastornMedlem sedan feb. 2007280 inlägg
#5

Hjälpklass

Denna hjälpklass behövs till förra:

<?php
/**
* Denna fil rymmer klassen keryxMailAndUriChars
*
* @author  Lars Gunther <gunther@keryx.se>
* @license Creative Commons Attribution-Noncommercial-Share Alike 3.0
*          http://creativecommons.org/licenses/by-nc-sa/3.0/
* @package Keryx_Globala_funktioner
* @subpackage Allmänt
* @copyright (c) Lars Gunther, keryx
*/
/**
* Återanvändningsbara strängar för PCRE-regexp
*
* Dessa strängar används för att validera
* och bearbeta URI:er och mejladresser
* Alla variabler ges värden i konstruktorn, och hämtas via funktionen getString
*
* Användbara adresser:
* {@link [url]http://www.w3.org/Addressing/URL/uri-spec.html[/url] W3C:s regler för URI:er} och
* {@link [url]http://www.iana.org/[/url] Ianas hemsida (domäner)}
* RFC:er:
* - {@link [url]http://www.ietf.org/rfc/rfc821.txt[/url] RFC 821} och speciellt
*   {@link [url]http://www.ietf.org/rfc/rfc822.txt[/url] RFC 822} för SMTP och plain-text
* - 2045, 2046, 2047, 2048, 2049 för MIME
* - 1896 för "text/enriched"
* - 2557 Content-IDs för bifogade bilder
* - 2554 SASL
* {@link [url]http://www.imc.org/[/url] Internet mail Consortium} och
* {@link [url]http://www.imc.org/terms.html[/url] terminologi för mejl}
*
* @version 0.96 beta
* @todo Testa att hämta strängar via overloading (__get) i stället för funktionen getString
* @todo Whitespace är tillåtet i mejladresser (kollapsas automatiskt av MTA:erna)
*/
class keryxMailAndUriChars {
    /**
    * @var bool $instance Håller reda på om klassen är instansierad
    */
    private static $instance = false; //
    /**
    * @var $ADRCHAR       Tecken som är OK som domännamn, etc.
    */
    protected  static $ADRCHAR = '[-_+0-9a-zA-Z]';
    /**
    * @var $HEX           Hexadecimala tecken, för url-encode
    */
    protected  static $HEX     = '[0-9A-Fa-f]';
    /**
    * @var $URICHAR       Alla tecken som kan finnas i en URI
    */
    protected  static $URICHAR = '';
    /**
    * @var $MAILATOMCHAR  Lagliga tecken i ett mejls första del
    * @todo Fyll på med fler tecken inkl. tilde. Se {@link http://www.ascii.cl/htmlcodes.htm}
    */
    protected  static $MAILATOMCHAR = '';
    /**
    * @var array $TOPDOMAINS All världens toppdomäner grupperade
    */
    protected  static $TOPDOMAINS = array();
    /**
    * @var string $TOPS All världens toppdomäner som PCRE-sträng
    */
    protected static $TOPS = '';
    /**
    * @var string $VALIDURI      En komplett http(s) - URI
    * @todo Fixa så att $VALIDURL matchar annat än http(s)
    */
    protected  static $VALIDURI = '';

    /**
    * Konstruktorn. Fyller variabler med innehåll
    *
    * @todo Sortera alla toppdomäner (för skojs skull)
    */
    private function __construct() {
        $urichar = self::$URICHAR = '('.self::$ADRCHAR.'|(%'.self::$HEX.'{2}))';         // Alla tecken som kan finnas i en URI
        self::$MAILATOMCHAR = "(".self::$ADRCHAR."|".'[\'\\$\\/§½!#¤%&=*?\\^|`{}åäöÅÄÖ\\x7E])';
        // Lagliga tecken (faktiskt) i ett mejls första del
        // \x7E är tilde
        self::$TOPDOMAINS['usa']    = 'com|net|org|mil|gov|edu|us|um';
        self::$TOPDOMAINS['norden'] = 'se|dk|fi|fo|gl|is|no|sj';
        self::$TOPDOMAINS['fusk']   = 'nu|to';
        self::$TOPDOMAINS['europa'] = 'uk|de|ad|al|at|ba|be|bg|by|ch|cs|cy|cz|ee|es|fr|gb|gg|gi|gr|hr|hu|ie';
        self::$TOPDOMAINS['europa'].= '|im|it|je|li|lt|lu|lv|mc|md|mk|mt|nl|pl|pt|ro|ru|si|sk|sm|tr|ua|yu';
        self::$TOPDOMAINS['afrika'] = 'ao|bd|bf|bi|bj|bw|cd|cg|ci|cm|cv|dj|dz|eg|eh|er|et|ga|gh|gm|gn|gg|gw';
        self::$TOPDOMAINS['afrika'].= '|gy|ke|lr|ls|ly|ma|mg|ml|mr|mw|mz|na|ne|ng|rw|sd|sl|sn|so|sz|td|tg';
        self::$TOPDOMAINS['afrika'].= '|tn|tz|ug|za|zm|zw';
        self::$TOPDOMAINS['asien']  = 'af|cn|hk|in|jp|kg|kh|kp|kr|kz|la|lk|mm|mn|mo|mv|my|np|pk|sg|th|tj|tm|tw|uz|vn';
        self::$TOPDOMAINS['m_öst']  = 'ae|am|az|ge|il|iq|ir|jo|kw|lb|om|ps|qa|sa|sy|ye';
        self::$TOPDOMAINS['amerika'] = 'ag|ai|ar|bb|bm|bo|br|bs|ca|cl|co|cr|cu|dm|do|ec|fk|gd|gf|gt|ht|jm';
        self::$TOPDOMAINS['amerika'].= '|kn|ky|mx|ni|pa|pe|pr|py|sr|st|sv|tc|tt|uy|vc|ve';
        self::$TOPDOMAINS['havet']  = 'ac|as|au|bn|ck|cx|fj|fm|gs|gu|id|io|ki|mh|mp|nc|nr|nz|pf|pg|ph|pn';
        self::$TOPDOMAINS['havet'] .= '|pw|re|sb|sh|tl|tp|tv|vg|vi|vu|ws';
        self::$TOPDOMAINS['andra']  = 'aq|va|aero|biz|coop|info|museum|name|pro|int';
        self::$TOPDOMAINS['osäker'] = 'an|aw|ax|bt|bv|bz|cc|gp|hm|hn|km|lc|mq|ms|mu|nf|pm|sc|tf|tk|wf|yt';
        $tops = self::$TOPS = join('|',self::$TOPDOMAINS);
        $ports = '[1-9][0-9]{0,4}'; // Matchar alla tal mellan 1 och 99999 - borde bara gå till 65535
        $adrchar = self::$ADRCHAR;
        self::$VALIDURI = <<<EOF
            (http(s)?://)
            ($adrchar+\\.
              ($adrchar+\\.)*
              ($tops)
              (:$ports)?
              (/
                ($urichar+/)*
                (
                  (
                    $urichar+
                    (
                      (\.$urichar{2,5})?
                      ((\\?)($urichar+=$urichar+)(&amp;$urichar+=$urichar+)*)?
                      (#$urichar+)?
                    )
                  )
                )?
              )?
            )
EOF;
        self::$VALIDURI = preg_replace ( '/\\s/','',self::$VALIDURI); // Bort med whitespace
    }

    /**
    * Returnera en av klassens strängar. (Enda sättet att komma åt dem.)
    *
    * Denna metod instansiererar klassen enligt singleton-pattern.
    *
    * @param string $string_name Namnet på den sträng som skall hämtas.
    * @return string Patterns för matchning med PCRE-funktioner.
    */
    public static function getString($string_name) {
        // Factory
        if ( self::$instance === false ) {
            self::$instance = new keryxMailAndURIChars;
        }
        // Dubbeldollarknepet används för att kunna returnera alla tänkbara strängar
        if ( isset(self::$$string_name) ) {
            return self::$$string_name;
        }
        else {
            return false;
        }
    }
}
Mr TMedlem sedan maj 20071 147 inlägg
#6

Wow !

Bockar så djupt av vördnad för dessa coder att det knakar i ryggkotorna.
Ska bli verkligt spännande att prova denna coden.

Tack.

Mr TMedlem sedan maj 20071 147 inlägg
#7

Wowet satte sig lite i halsen när jag igår skulle använda coden jag fått och då först insåg att det inte var en function, utan en class. Då jag tidigare aldrig jobbat med classer och inte kan lista ut hur man ska använda den frågar jag enkelt:

Hur ska man anropa denna code?
För att få det riktigt korrekt tar vi och säger att jag har en variabel med en sträng från ett formulär med namnet $email. Hur gör jag för att kolla om den är okej ?

voigtann1Medlem sedan juni 20018 810 inlägg
GunnarDMedlem sedan juni 20014 250 inlägg
#9

En liten nackdel med klassen är att den försöker kontakta mailservrar och har man en ISP som spärrar port 25 (smtp porten) så kommer den inte att fungera. Vet inte hur man skulle kunna gå runt den begränsningen.

Har man inte en sådan ISP så är det ett relativt bra sätt att testa en mailadress.

itpastornMedlem sedan feb. 2007280 inlägg
#10

Hur min klass funkar

Mr T skrev:

Hur ska man anropa denna code?
För att få det riktigt korrekt tar vi och säger att jag har en variabel med en sträng från ett formulär med namnet $email. Hur gör jag för att kolla om den är okej ?

Klassen innehåller bara statiska metoder, så de kan anropas som vore de enkla funktioner. Stoppar din ISP anrop på port 25 kan man bara använda de två första metoderna. Om inte din ISP är helt inkompetent finns det ingen väg runt detta.

$mej = "do-not-exist@keryx.se"; // OK enligt RFC och DNS men finns inte!
// OBS! Catch-all system kan ge konstiga svar.

$kod = ""; // Lagrar felkod, frivillig parameter
$fel = ""; // Lagrar felmeddelande, frivillig parameter

if ( keryxMejl_validAddress::dnsCheck($mejl, $kod, $fel) ) {
    // Med mitt exempel borde du komma hit
    echo "Adressen $mejl ser OK ut och går till en existerande domän.";
} else {
    // snabbtitt på felmeddelandena
    var_dump($fel);
}

if ( keryxMejl_validAddress::smtpCheck($mejl, $kod, $fel) ) {
    // Med mitt exempel borde du däremot INTE komma hit
    echo "Adressen $mejl finns med minst 90 % sannolikhet på riktigt.";
} else {
    // snabbtitt på felmeddelandena
    var_dump($fel);
}

Om man anropar smtpCheck() så anropar den i sin tur dnsCheck().

Anropar man dnsCheck(), så anropar den i sin tur först rfcCheck().

Kontrollen sker alltså automatiskt som en trestegsraket, där andra och tredje steget dock är frivilliga.

smtpCheck ger inte 100 % pålitliga svar. Somliga SMTP-servrar ljuger - åt båda håll. Somliga SMTP-servrar kräver att du kan identifiera dig med reverse DNS. Kan du inte det (vilket du inte kan hemifrån eller på en delad värd), så vägrar de också ge rätt svar - eller svarar inte alls.

Mr TMedlem sedan maj 20071 147 inlägg
#11

Inser plötsligt varför det strular och inte funkar. Servern jag ligger på kör PHP 4.4.4
Om jag förstår det hela rätt så är det PHP 5 som gäller för att få detta att funka !?

Finns det kanske någon som har en version för PHP 4.4.4, eller lösning på varför det inte funkar (ovanligt luddig fråga :) )

136 ms totalt · 3 externa anrop · cache AV · v20260731051352-full.56cc9887
0 ms — hämta statistik (cache)
0 ms — hämta forumlista (cache)
133 ms — hämta tråd, inlägg och bilagor (db)