Skip to content

Instantly share code, notes, and snippets.

@NTICompass
Last active October 31, 2023 16:05
Show Gist options
  • Save NTICompass/06eb0e60f004a073833d to your computer and use it in GitHub Desktop.
Save NTICompass/06eb0e60f004a073833d to your computer and use it in GitHub Desktop.
Bulk-remove Mandrill sending domains
<?php
// Quick and dirty script to remove spam domains from our mandrill account
define('API_KEY', '');
// Their API doesn't offer a "delete" method, so I'm using their website
define('USERNAME', '');
define('PASSWORD', '');
echo 'Downloading domain list...';
// Step 1, get all "sending domains" via their API
$jsonKey = json_encode(['key' => API_KEY]);
$api = curl_init('https://mandrillapp.com/api/1.0/senders/domains.json');
curl_setopt_array($api, [
CURLOPT_POST => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_POSTFIELDS => $jsonKey,
CURLOPT_HTTPHEADER => [
'Content-type: application/json',
'Content-length: '.strlen($jsonKey)
]
]);
if(($jsonDomains = curl_exec($api)) === FALSE){
die("cURL error\n".curl_error($api)."\n");
}
echo "done.\n";
$domains = json_decode($jsonDomains, TRUE);
curl_close($api);
echo count($domains)." domains found\n";
// Step 2: Filter out *valid* domains!
echo "\n";
$badDomains = [];
foreach($domains as $domain){
if($domain['spf']['valid'] || $domain['dkim']['valid'] || $domain['valid_signing']){
echo "Keeping {$domain['domain']}\n";
}
else{
$badDomains[] = $domain['domain'];
}
}
echo "\nRemoving ".count($badDomains)." domains\n";
// Step 3: Login to MailChimp's website
$siteCookies = tempnam(sys_get_temp_dir(), 'mandrill');
// To do this, we need to scrape the '__csrf_token' from the form
$login = curl_init('https://login.mailchimp.com/');
curl_setopt_array($login, [
CURLOPT_HTTPGET => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_FOLLOWLOCATION => TRUE,
CURLOPT_AUTOREFERER => TRUE,
CURLOPT_COOKIEFILE => $siteCookies,
CURLOPT_COOKIEJAR => $siteCookies,
CURLOPT_HTTPHEADER => [
'Accept-Encoding: gzip, deflate',
]
]);
if(($html = curl_exec($login)) === FALSE){
die("cURL error\n".curl_error($login)."\n");
}
curl_close($login);
$web = new DOMDocument;
libxml_use_internal_errors(TRUE);
$web->loadHTML(gzdecode($html));
$xpath = new DOMXpath($web);
$csrf_el = $xpath->query("//input[@name='__csrf_token']/@value");
$csrf = count($csrf_el) > 0 ? $csrf_el->item(0)->nodeValue : '';
echo "Captured __csrf_token={$csrf}\n";
$website = curl_init('https://login.mailchimp.com/login/post');
curl_setopt_array($website, [
#CURLOPT_VERBOSE => TRUE,
CURLOPT_POST => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_POSTFIELDS => http_build_query([
'referrer' => '/transactional/launch',
'username' => USERNAME,
'password' => PASSWORD,
'__csrf_token' => $csrf,
'from' => '',
'auth_token' => '',
'auth_system' => '',
]),
CURLOPT_FOLLOWLOCATION => TRUE,
CURLOPT_AUTOREFERER => TRUE,
CURLOPT_COOKIEFILE => $siteCookies,
CURLOPT_COOKIEJAR => $siteCookies,
CURLOPT_REFERER => 'https://login.mailchimp.com/',
CURLOPT_HTTPHEADER => [
'Origin: https://login.mailchimp.com',
'Accept-Encoding: gzip, deflate',
]
]);
if(($x = curl_exec($website)) === FALSE){
echo "Cannot login to Mandrill\n";
die("cURL error\n".curl_error($website)."\n");
}
curl_close($website);
// MailChimp is doing something "fun",
// it's returning a form you need to submit... after posting the login form
$loginForm = new DOMDocument;
$loginForm->loadHTML(gzdecode($x));
$formData = $loginForm->getElementsByTagName('form');
if (count($formData)) {
$loginUrl = $formData->item(0)->getAttribute('action');
$loginFormData = [];
foreach ($formData->item(0)->getElementsByTagName('input') as $field) {
$loginFormData[$field->getAttribute('name')] = $field->getAttribute('value');
}
$website = curl_init($loginUrl);
curl_setopt_array($website, [
#CURLOPT_VERBOSE => TRUE,
CURLOPT_POST => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_POSTFIELDS => http_build_query($loginFormData),
CURLOPT_FOLLOWLOCATION => TRUE,
CURLOPT_AUTOREFERER => TRUE,
CURLOPT_COOKIEFILE => $siteCookies,
CURLOPT_COOKIEJAR => $siteCookies,
CURLOPT_REFERER => 'https://login.mailchimp.com/login/post/',
CURLOPT_HTTPHEADER => [
'Origin: https://login.mailchimp.com',
'Accept-Encoding: gzip, deflate',
]
]);
if(($x = curl_exec($website)) === FALSE){
echo "Cannot login to Mandrill\n";
die("cURL error\n".curl_error($website)."\n");
}
curl_close($website);
// After posting this login form, we redirect to Mandrill
// And guess what, another form to post...
$mandrillForm = new DOMDocument;
$mandrillForm->loadHTML(gzdecode($x));
$formData = $mandrillForm->getElementsByTagName('form');
if (count($formData)) {
$adminDomain = parse_url($loginUrl, PHP_URL_HOST);
$loginFormData = [];
foreach ($formData->item(0)->getElementsByTagName('input') as $field) {
$loginFormData[$field->getAttribute('name')] = $field->getAttribute('value');
}
$website = curl_init($formData->item(0)->getAttribute('action'));
curl_setopt_array($website, [
#CURLOPT_VERBOSE => TRUE,
CURLOPT_POST => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_POSTFIELDS => http_build_query($loginFormData),
CURLOPT_FOLLOWLOCATION => TRUE,
CURLOPT_AUTOREFERER => TRUE,
CURLOPT_COOKIEFILE => $siteCookies,
CURLOPT_COOKIEJAR => $siteCookies,
CURLOPT_REFERER => "https://{$adminDomain}/transactional/launch",
CURLOPT_HTTPHEADER => [
"Origin: https://{$adminDomain}",
'Accept-Encoding: gzip, deflate',
]
]);
if(($x = curl_exec($website)) === FALSE){
echo "Cannot login to Mandrill\n";
die("cURL error\n".curl_error($website)."\n");
}
curl_close($website);
}
}
echo "Logged into Mandrill website\n";
echo "Removing domains via website...\n";
foreach($badDomains as $domain){
echo "\t{$domain}...";
$query = http_build_query([
'domain' => $domain
]);
$website = curl_init("https://mandrillapp.com/settings/delete-domain?{$query}");
curl_setopt_array($website, [
CURLOPT_HTTPGET => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_FOLLOWLOCATION => TRUE,
CURLOPT_AUTOREFERER => TRUE,
CURLOPT_COOKIEFILE => $siteCookies,
CURLOPT_COOKIEJAR => $siteCookies,
CURLOPT_REFERER => 'https://mandrillapp.com/settings/sending-domains',
]);
if(($x = curl_exec($website)) === FALSE){
echo "Cannot remove {$domain}\n";
die("cURL error\n".curl_error($website)."\n");
}
curl_close($website);
echo "done\n";
}
echo "\ndone\n";
@NTICompass
Copy link
Author

@vdepagter Since they changed Mandrill to being a part of MailChimp, the login page is different now. So, I'll need to update that to get the __csrf_token.

@NTICompass
Copy link
Author

@vdepagter I updated the program to login via MailChimp, but I am not 100% sure that it works. I have not tested this...

@vdepagter
Copy link

Marvelous, that did the trick! Byeee 142 domains I wouldn't have deleted by hand. ;-) Thanks!

@NTICompass
Copy link
Author

You're welcome! Glad I could help :-)

@vdepagter
Copy link

I'm sorry, I did not check the result afterwards. Although the output showed done after every domain, it didn't actually delete them..

@NTICompass
Copy link
Author

Ok, so the entire login process was changed here. It's not as easy as it was before. I tried to update the code, but it's kind of a mess... it also doesn't work with 2-factor auth.

Let me know if this works for you now. It won't break anything if it doesn't work. The worst it'll do is ask for a CAPTCHA when you login in the web browser (which the script cannot handle).

@vdepagter
Copy link

Finally got around to trying it again. Doesn't work yet unfortunately. No worries if you don't feel like updating this. ;-)

Downloading domain list...done.
146 domains found

Keeping <domain>
Keeping <domain>
Keeping <domain>
Keeping <domain>

Removing 142 domains
Captured __csrf_token=<token>
Cannot login to Mandrill
cURL error
<url> malformed

@cdmckay
Copy link

cdmckay commented Aug 7, 2023

Since logging in is difficult since they combined it with Mailchimp, the easiest way is to login, and extract the session cookie and then set it using define('COOKIE', 'PHPSESSID=XXX').

Then, replace the CURLOPT_COOKIE* arguments with:

CURLOPT_HTTPHEADER => array("Cookie: ".COOKIE, 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'),

You need to set the User-Agent header or it doesn't work.

@cdmckay
Copy link

cdmckay commented Aug 7, 2023

Here's the updated script:

<?php
// Quick and dirty script to remove spam domains from our mandrill account
define('API_KEY', '');
define('COOKIE', '');

echo 'Downloading domain list...';
// Step 1, get all "sending domains" via their API
$jsonKey = json_encode(['key' => API_KEY]);
$api = curl_init('https://mandrillapp.com/api/1.0/senders/domains.json');
curl_setopt_array($api, [
	CURLOPT_POST => TRUE,
	CURLOPT_RETURNTRANSFER => TRUE,
	CURLOPT_POSTFIELDS => $jsonKey,
	CURLOPT_HTTPHEADER => [
		'Content-type: application/json',
		'Content-length: '.strlen($jsonKey)
	]
]);

if(($jsonDomains = curl_exec($api)) === FALSE){
	die("cURL error\n".curl_error($api)."\n");
}
echo "done.\n";
$domains = json_decode($jsonDomains, TRUE);
curl_close($api);
echo count($domains)." domains found\n";

// Step 2: Filter out *valid* domains!
echo "\n";
$badDomains = [];
foreach($domains as $domain){
	if($domain['verified_at']){
		echo "Keeping {$domain['domain']}\n";
	}
	else{
		$badDomains[] = $domain['domain'];
	}
}
echo "\nRemoving ".count($badDomains)." domains\n";

echo "Logged into Mandrill website\n";

echo "Removing domains via website...\n";
foreach($badDomains as $domain){
	echo "\t{$domain}...";
	$query = http_build_query([
		'domain' => $domain
	]);
	
	$website = curl_init("https://mandrillapp.com/settings/delete-domain?{$query}");
	curl_setopt_array($website, [
		CURLOPT_HTTPGET => TRUE,
		CURLOPT_RETURNTRANSFER => TRUE,
		CURLOPT_FOLLOWLOCATION => TRUE,
		CURLOPT_AUTOREFERER => TRUE,
		CURLOPT_HTTPHEADER => array("Cookie: ".COOKIE, 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'),
		CURLOPT_REFERER => 'https://mandrillapp.com/settings/sending-domains',
	]);
	
	if(($x = curl_exec($website)) === FALSE){
		echo "Cannot remove {$domain}\n";
		die("cURL error\n".curl_error($website)."\n");
	}
	curl_close($website);
	echo "done\n";
}
echo "\ndone\n";

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment