Created
December 17, 2012 07:47
-
-
Save Elemecca/4316489 to your computer and use it in GitHub Desktop.
Exports mailbox configuration from the Webmasters.com control panel.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
use strict; | |
use warnings; | |
use POSIX qw(strftime); | |
use LWP::UserAgent (); | |
use LWP::ConnCache (); | |
use HTML::TreeBuilder (); | |
use XML::Writer (); | |
$global::VERSION = "0.1"; | |
our ($agent, $host, $user, $pass, $out); | |
######################################################################## | |
# Subroutines # | |
######################################################################## | |
sub logmsg { | |
print STDERR strftime( '%Y-%m-%d %H:%M:%S', localtime ) | |
. " " . join( " ", @_ ) . "\n"; | |
} | |
sub login () { | |
our ($agent, $host, $user, $pass); | |
my $res = $agent->post( "https://$host/4admin/", [ | |
'Action' => 'None', | |
'mode' => 'Login', | |
'Type' => 'Hosted', | |
'encrypt' => 'Y', | |
'User' => $user, | |
'Pass' => $pass, | |
't_stamp' => time() | |
]); | |
if (!$res->is_success) { | |
logmsg( "login failed:", $res->status_line ); | |
return 0; | |
} | |
return 1; | |
} | |
sub fetch ($%) { | |
my ($page, %post) = @_; | |
our ($agent, $host); | |
my $url = "https://$host/$page"; | |
my $res; | |
if (%post) { | |
$res = $agent->post( $url, \%post ); | |
} else { | |
$res = $agent->get( $url ); | |
} | |
if ($res->is_redirect | |
&& '/simpleHTML/logout.php' eq $res->header( 'Location' )) { | |
logmsg( "redirected to login page, logging in" ); | |
if (!login()) { | |
return undef; | |
} | |
if (%post) { | |
$res = $agent->post( $url, \%post ); | |
} else { | |
$res = $agent->get( $url ); | |
} | |
} | |
if (!$res->is_success) { | |
logmsg( "request for '$page' failed:", $res->status_line ); | |
return undef; | |
} | |
return $res->content; | |
} | |
sub treeify ($) { | |
my ($content) = @_; | |
my $root = HTML::TreeBuilder->new_from_content( $content ); | |
$root->elementify(); | |
return $root; | |
} | |
sub read_mailbox ($$) { | |
my ($mailbox, $domain) = @_; | |
logmsg( "fetching mailbox $mailbox" ); | |
my $edit_page; | |
if (defined $domain) { | |
$edit_page = fetch( '/simpleHTML/fw_mail.php', | |
'Domain' => $domain, | |
'Mailbox' => $mailbox, | |
'mode' => 'Edit', | |
) or return; | |
} else { | |
$edit_page = fetch( '/simpleHTML/mail_edit.php', | |
'Mailbox' => $mailbox | |
) or return; | |
} | |
my $edit_tree = treeify( $edit_page ); | |
$out->startTag( 'mailbox', 'name' => $mailbox ); | |
# optional text description | |
my $desc_elem = $edit_tree->look_down( | |
'_tag' => 'input', 'name' => 'Label' ); | |
my $desc = $desc_elem->attr( 'value' ); | |
$out->dataElement( 'description', $desc ) if ($desc); | |
# whether local delivery is enabled | |
my $local_elem = $edit_tree->look_down( | |
'_tag' => 'input', 'name' => 'Store', 'value' => 'Yes' ); | |
if (defined $local_elem->attr( 'checked' )) { | |
my $config_elem = $edit_tree->look_down( | |
'_tag' => 'input', 'name' => 'Control', 'value' => 'Yes' ); | |
my $config = defined $config_elem->attr( 'checked' ); | |
$out->emptyTag( 'local', | |
'configurable' => ($config ? 'true' : 'false') ); | |
} | |
# BCC outgoing mail | |
my $bcc_elem = $edit_tree->look_down( | |
'_tag' => 'input', 'name' => 'Monitor' ); | |
if (defined $bcc_elem) { | |
my $bcc = $bcc_elem->attr( 'value' ); | |
$out->emptyTag( 'bcc', 'to' => $bcc ) if ($bcc); | |
} | |
# forwarding addresses | |
my $forwards_elem = $edit_tree->look_down( | |
'_tag' => 'textarea', 'name' => 'Faddress' ); | |
my @forwards = split /\s/, $forwards_elem->as_trimmed_text; | |
my $forward_elem = $edit_tree->look_down( | |
'_tag' => 'input', 'name' => 'Forward', 'value' => 'Yes' ); | |
my $forward = defined $forward_elem->attr( 'checked' ); | |
if ($forward || @forwards) { | |
$out->startTag( 'forwarding', | |
'enabled' => ($forward ? 'true' : 'false') ); | |
for my $addr (@forwards) { | |
$out->emptyTag( 'forward', 'to' => $addr ); | |
} | |
$out->endTag( 'forwarding' ); | |
} | |
# autoresponder | |
my $responder_elem = $edit_tree->look_down( | |
'_tag' => 'input', 'name' => 'Responder', 'value' => 'Yes' ); | |
my $responder = defined $responder_elem->attr( 'checked' ); | |
my $message_elem = $edit_tree->look_down( | |
'_tag' => 'textarea', 'name' => 'Rtext' ); | |
my $message = $message_elem->as_text; | |
if ($responder || $message) { | |
my $format_elem = $edit_tree->look_down( | |
'_tag' => 'input', 'name' => 'type', | |
'value' => 'text/plain' ); | |
my $plain = defined $format_elem->attr( 'checked' ); | |
$out->dataElement( 'responder', $message, | |
'enabled' => ($responder ? 'true' : 'false'), | |
'format' => ($plain ? 'plain' : 'html'), | |
); | |
} | |
$out->endTag( 'mailbox' ); | |
} | |
sub read_domain ($) { | |
my ($domain) = @_; | |
logmsg( "fetching list of mailboxen for $domain" ); | |
my $list_page = fetch( '/simpleHTML/fw_mail.php', | |
'Domain' => $domain, | |
) or return; | |
my $list_tree = treeify( $list_page ); | |
if (defined $list_tree->look_down( '_tag' => 'input', | |
'class' => 'sbt', 'value' => 'Enable' )) { | |
logmsg( "email is disabled on $domain" ); | |
return; | |
} | |
$out->startTag( 'domain', 'name' => $domain ); | |
my $list_elem = $list_tree->look_down( | |
'_tag' => 'select', 'name' => 'Mailbox' ); | |
unless (defined $list_elem) { | |
logmsg( "unable to find account list" ); | |
return; | |
} | |
my @options = $list_elem->find_by_tag_name( 'option' ); | |
for my $option (@options) { | |
my $mailbox = $option->attr( 'value' ); | |
read_mailbox( $mailbox, $domain ); | |
} | |
$out->endTag( 'domain' ); | |
} | |
######################################################################## | |
# Executable Body # | |
######################################################################## | |
# TODO: parse command-line arguments | |
$host = "48.webmasters.com"; | |
$user = "science_sam"; | |
$pass = "SomethingSecrety"; | |
my $out_path = "addresses.xml"; | |
open( my $out_file, ">", $out_path ) | |
or die "unable to open output file '$out_path': $!"; | |
$out = XML::Writer->new( | |
OUTPUT => $out_file, | |
DATA_MODE => 1, | |
DATA_INDENT => 2, | |
ENCODING => 'utf-8', | |
); | |
$out->xmlDecl(); | |
$out->comment( "created by 4admin-emaildump $global::VERSION" ); | |
$out->startTag( 'account' ); | |
$agent = LWP::UserAgent->new( | |
'agent' => "4admin-emaildump/$global::VERSION ", | |
'cookie_jar' => {}, # creates an empty cookie jar | |
'conn_cache' => LWP::ConnCache->new, | |
'protocols_allowed' => [ 'https' ], | |
'ssl_opts' => { | |
'verify_hostname' => 0, | |
'SSL_verify_mode' => 0 | |
}, | |
'requests_redirectable' => [] | |
); | |
logmsg( 'fetching list of mailboxen for master account' ); | |
my $list_page = fetch( '/simpleHTML/mail_menu.php' ) or die; | |
my $list_tree = treeify( $list_page ); | |
# the domain of the master account | |
# Augh, why can't they use IDs? This is horrible. | |
my $domain_parent_elem = $list_tree->look_down( | |
'_tag' => 'b', sub ($) { | |
for my $node ($_[ 0 ]->content_list) { | |
return 1 if (!ref $node and $node eq 'Account: '); | |
} | |
return 0; | |
} | |
); | |
my $domain = $domain_parent_elem->look_down( | |
'_tag' => 'font' )->as_trimmed_text; | |
$out->startTag( 'domain', 'name' => $domain ); | |
my $list_elem = $list_tree->look_down( | |
'_tag' => 'select', 'name' => 'Mailbox' ); | |
die "unable to find account list" unless (defined $list_elem); | |
my @options = $list_elem->find_by_tag_name( 'option' ); | |
for my $option (@options) { | |
my $mailbox = $option->attr( 'value' ); | |
read_mailbox( $mailbox, undef ); | |
} | |
$out->endTag( 'domain' ); | |
logmsg( "fetching list of addon domains" ); | |
my $addon_page = fetch( "/simpleHTML/fw_top.php?Master=$domain" ); | |
my $addon_tree = treeify( $addon_page ); | |
my $addon_elem = $addon_tree->look_down( | |
'_tag' => 'select', 'name' => 'dom' ); | |
for my $addon ($addon_elem->find_by_tag_name( 'option' )) { | |
read_domain( $addon->attr( 'value' ) ); | |
} | |
$out->endTag( 'account' ); | |
$out->end(); | |
close( $out_file ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment