#!/usr/bin/perl 

# $Id: dns_srv_imap,v 1.5 2016/08/15 01:24:20 gilles Exp gilles $

use strict;
use warnings;
use English;
use Test::More;
use Net::DNS;

# _imaps._tcp.gmail.com.  86183   IN      SRV     5 0 993 imap.gmail.com.
lookup_srv_string('_imaps._tcp.gmail.com');

#lookup_srv( '_imap._tcp.gmail.com' ) ;
#lookup_srv( '_imaps._tcp.lamiral.info' ) ;
#lookup_srv( '_imap._tcp.lamiral.info' ) ;

foreach my $email (@ARGV) {
    my $domain = domain_name_of($email);
    print "Domain for email $email: $domain\n";
    my ( $host, $port ) = host_port_from_lookup_srv( '_imaps._tcp.' . $domain );
    $host ||= q{};
    $port ||= q{};
    print "IMAPS server name and port for $email: $host $port\n";

    ( $host, $port ) = host_port_from_lookup_srv( '_imap._tcp.' . $domain );
    $host ||= q{};
    $port ||= q{};
    print "IMAP  server name and port for $email: $host $port\n";

}

tests_server_name_from_srv_string();
tests_server_port_from_srv_string();
tests_domain_name_of();
tests_host_port_ssl_from_user();
done_testing();

my $debug = 1;

sub host_port_ssl_from_user {
    my $user = shift @ARG;

    if ( !$user ) {
        return;
    }

    my $domain = domain_name_of($user);
    if ( !$domain ) {
        return;
    }

    my ( $host, $port ) = host_port_from_lookup_srv(qq{_imaps._tcp.$domain});
    my $ssl = 1;
    if ( $host and $port ) {
        return ( $host, $port, $ssl );
    }

    # fallback to imap in clear
    $ssl = 0;
    ( $host, $port ) = host_port_from_lookup_srv(qq{_imap._tcp.$domain});

    if ( $host and $port ) {
        return ( $host, $port, $ssl );
    }

    return;

}

sub tests_host_port_ssl_from_user {
    is( undef, host_port_ssl_from_user(),
        'host_port_ssl_from_user: no args => undef' );
    is_deeply(
        [qw( imap.gmail.com. 993 1 )],
        [ host_port_ssl_from_user('gilles.lamiral@gmail.com') ],
'host_port_ssl_from_user: gilles.lamiral@gmail.com => imap.gmail.com. 993 1 (ssl)'
    );

}

sub host_port_from_lookup_srv {
    my $request = shift @ARG;

    my $lookup = lookup_srv_string($request);
    if ( !$lookup ) {
        return;
    }

    my $host = server_name_from_srv_string($lookup);
    my $port = server_port_from_srv_string($lookup);

    if ( $host and $port ) {
        return ( $host, $port );
    }
    else {
        return;
    }
}

sub domain_name_of_email {
    my $email = shift;

    return (undef) if ( not $email );

    my $domain;

    if ( $email =~ /^.*@([^@]+)$/ ) {
        $domain = $1;
        $debug and print "domain: $domain\n";
        return ($domain);
    }
    return;
}

sub domain_name_of {
    my $email = shift;

    return (undef) if ( not $email );

    my $domain = domain_name_of_email($email);

    if ( !$domain ) {
        $domain = $email;
    }

    return ($domain);
}

sub tests_domain_name_of {
    ok( not( domain_name_of('') ), 'domain_name_of: void => undef' );
    ok( not( domain_name_of() ),   'domain_name_of: undef => undef' );
    ok( 'foo' eq domain_name_of('foo'), 'domain_name_of: foo => foo' );

    #ok( 'foo' eq domain_name_of( 'foo ' ),  'domain_name_of: foo  => foo' ) ;
    #ok( 'foo' eq domain_name_of( 'foo  ' ), 'domain_name_of: foo   => foo' ) ;
    ok(
        'example.com' eq domain_name_of('foo@example.com'),
        'domain_name_of: foo@example.com => example.com'
    );
    ok(
        'example.com' eq domain_name_of('@foo@example.com'),
        'domain_name_of: @foo@example.com => example.com'
    );
    ok(
        'example.com' eq domain_name_of('bar@foo@example.com'),
        'domain_name_of: bar@foo@example.com => example.com'
    );
}

sub lookup_srv_string {
    my $name = shift;

    my $resolver = new Net::DNS::Resolver();
    my $reply = $resolver->query( $name, 'SRV' );

    my $string;
    if ($reply) {

        #($reply->answer)[0]->print;
        foreach my $rr ( $reply->answer ) {
            $debug and print 'name:   ' . $rr->name . "\n";
            $debug and print 'class:  ' . $rr->class . "\n";
            $debug and print 'type:   ' . $rr->type . "\n";
            $debug and print 'ttl:    ' . $rr->ttl . "\n";
            $debug and print 'string: ' . $rr->string . "\n";
            next if ( 'SRV' ne $rr->type );
            next if ( not( $rr->string ) );
            $string = $rr->string;
            return ($string);
        }
    }
    else {
        print "Query failed SRV for domain $name: ", $resolver->errorstring,
          "\n";
        return (undef);
    }
    return ($string);
}

sub server_name_from_srv_string {
    my $srv_string = shift;

    return (undef) if ( not $srv_string );
    my $server_name = ( split( /\s+/, $srv_string ) )[7];
    return (undef) if ( '.' eq $server_name );
    return ($server_name);
}

sub tests_server_name_from_srv_string {
    ok(
        not( server_name_from_srv_string('') ),
        'server_name_from_srv_string: void'
    );
    ok(
        not( server_name_from_srv_string() ),
        'server_name_from_srv_string: undef'
    );
    ok(
        'imap.gmail.com.' eq server_name_from_srv_string(
'_imaps._tcp.gmail.com.  82466   IN      SRV     5 0 993 imap.gmail.com.'
        ),
        'server_name_from_srv_string: _imaps._tcp.gmail.com. => imap.gmail.com.'
    );
    ok(
        not(
            server_name_from_srv_string(
                '_imap._tcp.gmail.com.   81999   IN      SRV     0 0 0 .')
        ),
        'server_name_from_srv_string: _imap._tcp.gmail.com. => undef'
    );

    return ();
}

sub server_port_from_srv_string {
    my $srv_string = shift;

    return (undef) if ( not $srv_string );
    my $server_port = ( split( /\s+/, $srv_string ) )[6];
    return (undef) if ( 0 == $server_port );
    return ($server_port);
}

sub tests_server_port_from_srv_string {
    ok(
        not( server_port_from_srv_string('') ),
        'server_port_from_srv_string: void'
    );
    ok(
        not( server_port_from_srv_string() ),
        'server_port_from_srv_string: undef'
    );
    ok(
        '993' eq server_port_from_srv_string(
'_imaps._tcp.gmail.com.  82466   IN      SRV     5 0 993 imap.gmail.com.'
        ),
        'server_port_from_srv_string: _imaps._tcp.gmail.com. => 993'
    );
    ok(
        not(
            server_port_from_srv_string(
                '_imap._tcp.gmail.com.   81999   IN      SRV     0 0 0 .')
        ),
        'server_port_from_srv_string: _imap._tcp.gmail.com. => undef'
    );

    return ();
}
