├── README.md ├── DecodeShortURLs.pm └── DecodeShortURLs.cf /README.md: -------------------------------------------------------------------------------- 1 | NOTE: this plugin is be merged into SpamAssassin, so it will no longer be updated here. 2 | ======================================================================================== 3 | 4 | DecodeShortURLs 5 | =============== 6 | 7 | This is a plugin for SpamAssassin. 8 | 9 | This plugin looks for URLs shortened by a list of URL shortening services and 10 | upon finding a matching URL will connect using to the shortening service and 11 | do an HTTP HEAD lookup and retrieve the location header which points to the 12 | actual shortened URL, it then adds this URL to the list of URIs extracted by 13 | SpamAssassin which can then be accessed by other plug-ins, such as URIDNSBL. 14 | 15 | This plugin also sets the rule HAS_SHORT_URL if any matching short URLs are 16 | found. 17 | 18 | Regular 'uri' rules can be used to detect and score links disabled by the 19 | shortening service for abuse and URL_BITLY_BLOCKED is supplied as an example. 20 | It should be safe to score this rule highly on a match as experience shows 21 | that bit.ly only blocks access to a URL if it has seen consistent abuse and 22 | problem reports. 23 | 24 | As of version 0.3 this plug-in will follow 'chained' shorteners e.g. 25 | 26 | short URL -> short URL -> short URL -> real URL 27 | 28 | If this form of chaining is found, then the rule 'SHORT_URL_CHAINED' will be 29 | fired. If a loop is detected then 'SHORT_URL_LOOP' will be fired. 30 | This plug-in limits the number of chained shorteners to a maximim of 10 at 31 | which point it will fire the rule 'SHORT_URL_MAXCHAIN' and go no further. 32 | 33 | If a shortener returns a '404 Not Found' result for the short URL then the 34 | rule 'SHORT_URL_404' will be fired. 35 | 36 | If a shortener does not return an HTTP redirect, then a dynamic rule will 37 | be fired: 'SHORT_\_\' where \ is the uppercase 38 | name of the shortener with dots converted to underscores. e.g.: 39 | 'SHORT_T_CO_200' This is to handle the case of t.co which now returns an 40 | HTTP 200 and an abuse page instead of redirecting to an abuse page like 41 | every other shortener does... 42 | 43 | NOTES 44 | ----- 45 | 46 | This plugin runs the parsed_metadata hook with a priority of -1 so that 47 | it may modify the parsed URI list prior to the URIDNSBL plugin which 48 | runs as priority 0. 49 | 50 | Currently the plugin queries a maximum of 10 distinct shortened URLs with 51 | a maximum timeout of 5 seconds per lookup. 52 | 53 | ACKNOWLEDGEMENTS 54 | ---------------- 55 | 56 | A lot of this plugin has been hacked together by using other plugins as 57 | examples. The author would particularly like to tip his hat to Karsten 58 | Bräckelmann for the _add_uri_detail_list() function that he stole from 59 | GUDO.pm for which this plugin would not be possible due to the SpamAssassin 60 | API making no provision for adding to the base list of extracted URIs and 61 | the author not knowing enough about Perl to be able to achieve this without 62 | a good example from someone that does ;-) 63 | -------------------------------------------------------------------------------- /DecodeShortURLs.pm: -------------------------------------------------------------------------------- 1 | # <@LICENSE> 2 | # Licensed to the Apache Software Foundation (ASF) under one or more 3 | # contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright ownership. 5 | # The ASF licenses this file to you under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with 7 | # the License. You may obtain a copy of the License at: 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | # Author: Steve Freegard 19 | 20 | =head1 NAME 21 | 22 | DecodeShortURLs - Expand shortened URLs 23 | 24 | =head1 SYNOPSIS 25 | 26 | loadplugin Mail::SpamAssassin::Plugin::DecodeShortURLs 27 | 28 | url_shortener bit.ly 29 | url_shortener go.to 30 | ... 31 | 32 | =head1 DESCRIPTION 33 | 34 | This plugin looks for URLs shortened by a list of URL shortening services and 35 | upon finding a matching URL will connect using to the shortening service and 36 | do an HTTP HEAD lookup and retrieve the location header which points to the 37 | actual shortened URL, it then adds this URL to the list of URIs extracted by 38 | SpamAssassin which can then be accessed by other plug-ins, such as URIDNSBL. 39 | 40 | This plugin also sets the rule HAS_SHORT_URL if any matching short URLs are 41 | found. 42 | 43 | Regular 'uri' rules can be used to detect and score links disabled by the 44 | shortening service for abuse and URL_BITLY_BLOCKED is supplied as an example. 45 | It should be safe to score this rule highly on a match as experience shows 46 | that bit.ly only blocks access to a URL if it has seen consistent abuse and 47 | problem reports. 48 | 49 | As of version 0.3 this plug-in will follow 'chained' shorteners e.g. 50 | 51 | 52 | short URL -> short URL -> short URL -> real URL 53 | 54 | 55 | If this form of chaining is found, then the rule 'SHORT_URL_CHAINED' will be 56 | fired. If a loop is detected then 'SHORT_URL_LOOP' will be fired. 57 | This plug-in limits the number of chained shorteners to a maximim of 10 at 58 | which point it will fire the rule 'SHORT_URL_MAXCHAIN' and go no further. 59 | 60 | If a shortener returns a '404 Not Found' result for the short URL then the 61 | rule 'SHORT_URL_404' will be fired. 62 | 63 | If a shortener does not return an HTTP redirect, then a dynamic rule will 64 | be fired: 'SHORT__' where is the uppercase 65 | name of the shortener with dots converted to underscores. e.g.: 66 | 'SHORT_T_CO_200' This is to handle the case of t.co which now returns an 67 | HTTP 200 and an abuse page instead of redirecting to an abuse page like 68 | every other shortener does... 69 | 70 | =head1 NOTES 71 | 72 | This plugin runs the parsed_metadata hook with a priority of -1 so that 73 | it may modify the parsed URI list prior to the URIDNSBL plugin which 74 | runs as priority 0. 75 | 76 | Currently the plugin queries a maximum of 10 distinct shortened URLs with 77 | a maximum timeout of 5 seconds per lookup. 78 | 79 | =head1 ACKNOWLEDGEMENTS 80 | 81 | A lot of this plugin has been hacked together by using other plugins as 82 | examples. The author would particularly like to tip his hat to Karsten 83 | Bräckelmann for the _add_uri_detail_list() function that he stole from 84 | GUDO.pm for which this plugin would not be possible due to the SpamAssassin 85 | API making no provision for adding to the base list of extracted URIs and 86 | the author not knowing enough about Perl to be able to achieve this without 87 | a good example from someone that does ;-) 88 | 89 | =cut 90 | 91 | package Mail::SpamAssassin::Plugin::DecodeShortURLs; 92 | 93 | my $VERSION = 0.11; 94 | 95 | use Mail::SpamAssassin::Plugin; 96 | use strict; 97 | use warnings; 98 | 99 | use vars qw(@ISA); 100 | @ISA = qw(Mail::SpamAssassin::Plugin); 101 | 102 | use constant HAS_LWP_USERAGENT => eval { local $SIG{'__DIE__'}; require LWP::UserAgent; }; 103 | use constant HAS_SQLITE => eval { local $SIG{'__DIE__'}; require DBD::SQLite; }; 104 | use Fcntl qw(:flock SEEK_END); 105 | use Sys::Syslog qw(:DEFAULT setlogsock); 106 | 107 | 108 | sub dbg { 109 | my $msg = shift; 110 | return Mail::SpamAssassin::Logger::dbg("DecodeShortURLs: $msg"); 111 | } 112 | 113 | sub new { 114 | my $class = shift; 115 | my $mailsaobject = shift; 116 | 117 | $class = ref($class) || $class; 118 | my $self = $class->SUPER::new($mailsaobject); 119 | bless ($self, $class); 120 | 121 | if ($mailsaobject->{local_tests_only} || !HAS_LWP_USERAGENT) { 122 | $self->{disabled} = 1; 123 | } else { 124 | $self->{disabled} = 0; 125 | } 126 | 127 | unless ($self->{disabled}) { 128 | $self->{ua} = new LWP::UserAgent; 129 | $self->{ua}->{max_redirect} = 0; 130 | $self->{ua}->{timeout} = 5; 131 | $self->{ua}->env_proxy; 132 | $self->{logging} = 0; 133 | $self->{caching} = 0; 134 | $self->{syslog} = 0; 135 | } 136 | 137 | $self->set_config($mailsaobject->{conf}); 138 | $self->register_method_priority ('parsed_metadata', -1); 139 | $self->register_eval_rule('short_url_tests'); 140 | 141 | return $self; 142 | } 143 | 144 | sub set_config { 145 | my($self, $conf) = @_; 146 | my @cmds = (); 147 | 148 | push (@cmds, { 149 | setting => 'url_shortener', 150 | default => {}, 151 | code => sub { 152 | my ($self, $key, $value, $line) = @_; 153 | if ($value =~ /^$/) { 154 | return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE; 155 | } 156 | foreach my $domain (split(/\s+/, $value)) { 157 | $self->{url_shorteners}->{lc $domain} = 1; 158 | } 159 | } 160 | }); 161 | 162 | =cut 163 | 164 | =head1 PRIVILEGED SETTINGS 165 | 166 | =over 4 167 | 168 | =item url_shortener_log (default: none) 169 | 170 | A path to a log file to be written to. The file will be created if it does 171 | not already exist and must be writable by the user running spamassassin. 172 | 173 | For each short URL found the following will be written to the log file: 174 | [unix_epoch_time] => 175 | 176 | =cut 177 | 178 | push (@cmds, { 179 | setting => 'url_shortener_log', 180 | default => '', 181 | is_priv => 1, 182 | type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING 183 | }); 184 | 185 | =item url_shortener_cache (default: none) 186 | 187 | The full path to a database file to write cache entries to. The database will 188 | be created automatically if is does not already exist but the supplied path 189 | and file must be read/writable by the user running spamassassin or spamd. 190 | 191 | 192 | NOTE: you will need the DBD::SQLite module installed to use this feature. 193 | 194 | Example: 195 | 196 | url_shortener_cache /tmp/DecodeShortURLs.sq3 197 | 198 | =cut 199 | 200 | 201 | push (@cmds, { 202 | setting => 'url_shortener_cache', 203 | default => '', 204 | is_priv => 1, 205 | type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING 206 | }); 207 | 208 | =item url_shortener_cache_ttl (default: 86400) 209 | 210 | The length of time a cache entry will be valid for in seconds. 211 | Default is 86400 (1 day). 212 | 213 | 214 | NOTE: you will also need to run the following via cron to actually remove the 215 | records from the database: 216 | 217 | echo "DELETE FROM short_url_cache WHERE modified < strftime('%s',now) - ; | sqlite3 /path/to/database" 218 | 219 | 220 | NOTE: replace above with the same value you use for this option 221 | 222 | =cut 223 | 224 | push (@cmds, { 225 | setting => 'url_shortener_cache_ttl', 226 | is_admin => 1, 227 | default => 86400, 228 | type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC 229 | }); 230 | 231 | =item url_shortener_syslog (default: 0 (off)) 232 | 233 | If this option is enabled (set to 1), then short URLs and the decoded URLs will be logged to syslog (mail.info). 234 | 235 | =cut 236 | 237 | 238 | push (@cmds, { 239 | setting => 'url_shortener_syslog', 240 | is_admin => 1, 241 | default => 0, 242 | type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL 243 | }); 244 | 245 | push (@cmds, { 246 | setting => 'log_target_only', 247 | is_admin => 1, 248 | default => 0, 249 | type => $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL 250 | }); 251 | 252 | $conf->{parser}->register_commands(\@cmds); 253 | } 254 | 255 | sub parsed_metadata { 256 | my ($self, $opts) = @_; 257 | my $pms = $opts->{permsgstatus}; 258 | my $msg = $opts->{msg}; 259 | 260 | return if $self->{disabled}; 261 | 262 | dbg ('warn: get_uri_detail_list() has been called already') 263 | if exists $pms->{uri_detail_list}; 264 | 265 | # don't keep dereferencing these 266 | $self->{url_shorteners} = $pms->{main}->{conf}->{url_shorteners}; 267 | ($self->{url_shortener_log}) = ($pms->{main}->{conf}->{url_shortener_log} =~ /^(.*)$/g); 268 | ($self->{url_shortener_cache}) = ($pms->{main}->{conf}->{url_shortener_cache} =~ /^(.*)$/g); 269 | $self->{url_shortener_cache_ttl} = $pms->{main}->{conf}->{url_shortener_cache_ttl}; 270 | $self->{url_shortener_syslog} = $pms->{main}->{conf}->{url_shortener_syslog}; 271 | $self->{log_target_only} = $pms->{main}->{conf}->{log_target_only}; 272 | 273 | # Sort short URLs into hash to de-dup them 274 | my %short_urls; 275 | my $uris = $pms->get_uri_detail_list(); 276 | while (my($uri, $info) = each %{$uris}) { 277 | next unless ($info->{domains}); 278 | foreach ( keys %{ $info->{domains} } ) { 279 | if (exists $self->{url_shorteners}->{lc $_}) { 280 | # NOTE: $info->{domains} appears to contain all the domains parsed 281 | # from the single input URI with no way to work out what the base 282 | # domain is. So to prevent someone from stuffing the URI with a 283 | # shortener to force this plug-in to follow a link that *isn't* on 284 | # the list of shorteners; we enforce that the shortener must be the 285 | # base URI and that a path must be present. 286 | if ($uri !~ /^https?:\/\/(?:www\.)?$_\/.+$/i) { 287 | dbg("Discarding URI: $uri"); 288 | next; 289 | } 290 | $short_urls{$uri} = 1; 291 | next; 292 | } 293 | } 294 | } 295 | 296 | # Make sure we have some work to do 297 | # Before we open any log files etc. 298 | my $count = scalar keys %short_urls; 299 | return undef unless $count gt 0; 300 | 301 | # Initialise logging if enabled 302 | if ($self->{url_shortener_log}) { 303 | eval { 304 | local $SIG{'__DIE__'}; 305 | open($self->{logfh}, '>>'.$self->{url_shortener_log}) or die $!; 306 | }; 307 | if ($@) { 308 | dbg("warn: $@"); 309 | } else { 310 | $self->{logging} = 1; 311 | } 312 | } 313 | 314 | # Initialise syslog if enabled 315 | if ($self->{url_shortener_syslog}) { 316 | eval { 317 | local $SIG{'__DIE__'}; 318 | openlog('DecodeShortURLs','ndelay,pid','mail'); 319 | }; 320 | if ($@) { 321 | dbg("warn: $@"); 322 | } else { 323 | $self->{syslog} = 1; 324 | } 325 | } 326 | 327 | # Initialise cache if enabled 328 | if ($self->{url_shortener_cache} && HAS_SQLITE) { 329 | eval { 330 | local $SIG{'__DIE__'}; 331 | $self->{dbh} = DBI->connect_cached("dbi:SQLite:dbname=".$self->{url_shortener_cache},"","", {RaiseError => 1, PrintError => 0, InactiveDestroy => 1}) or die $!; 332 | }; 333 | if ($@) { 334 | dbg("warn: $@"); 335 | } else { 336 | $self->{caching} = 1; 337 | 338 | # Create database if needed 339 | eval { 340 | local $SIG{'__DIE__'}; 341 | $self->{dbh}->do(" 342 | CREATE TABLE IF NOT EXISTS short_url_cache ( 343 | short_url TEXT PRIMARY KEY NOT NULL, 344 | decoded_url TEXT NOT NULL, 345 | hits INTEGER NOT NULL DEFAULT 1, 346 | created INTEGER NOT NULL DEFAULT (strftime('%s','now')), 347 | modified INTEGER NOT NULL DEFAULT (strftime('%s','now')) 348 | ) 349 | "); 350 | $self->{dbh}->do(" 351 | CREATE INDEX IF NOT EXISTS short_url_by_modified 352 | ON short_url_cache(short_url, modified) 353 | "); 354 | $self->{dbh}->do(" 355 | CREATE INDEX IF NOT EXISTS short_url_modified 356 | ON short_url_cache(modified) 357 | "); 358 | }; 359 | if ($@) { 360 | dbg("warn: $@"); 361 | $self->{caching} = 0; 362 | } 363 | } 364 | } 365 | 366 | my $max_short_urls = 10; 367 | foreach my $short_url (keys %short_urls) { 368 | next if ($max_short_urls le 0); 369 | my $location = $self->recursive_lookup($short_url, $pms); 370 | $max_short_urls--; 371 | } 372 | 373 | # Close log 374 | eval { 375 | local $SIG{'__DIE__'}; 376 | close($self->{logfh}) or die $!; 377 | } if $self->{logging}; 378 | 379 | # Close syslog 380 | eval { 381 | local $SIG{'__DIE__'}; 382 | closelog() or die $!; 383 | } if $self->{syslog}; 384 | 385 | # Don't disconnect cached database handle 386 | # eval { $self->{dbh}->disconnect() or die $!; } if $self->{caching}; 387 | } 388 | 389 | sub recursive_lookup { 390 | my ($self, $short_url, $pms, %been_here) = @_; 391 | 392 | my $count = scalar keys %been_here; 393 | dbg("Redirection count $count") if $count gt 0; 394 | if ($count >= 10) { 395 | dbg("Error: more than 10 shortener redirections"); 396 | # Fire test 397 | $pms->got_hit('SHORT_URL_MAXCHAIN'); 398 | return undef; 399 | } 400 | 401 | my $location; 402 | 403 | if ($self->{caching} && ($location = $self->cache_get($short_url))) { 404 | dbg("Found cached $short_url => $location"); 405 | eval { 406 | local $SIG{'__DIE__'}; 407 | if ($self->{log_target_only}) { 408 | $self->log_to_file($location); 409 | } else { 410 | $self->log_to_file("$short_url => $location"); 411 | } 412 | } if $self->{logging}; 413 | syslog('info',"Found cached $short_url => $location") if $self->{syslog}; 414 | } else { 415 | # Not cached; do lookup 416 | my $response = $self->{ua}->head($short_url); 417 | if (!$response->is_redirect) { 418 | dbg("URL is not redirect: $short_url = ".$response->status_line); 419 | if ((my ($domain) = ($short_url =~ /^https?:\/\/(\S+)\//))) { 420 | if (exists $self->{url_shorteners}->{$domain}) { 421 | $domain =~ s/\./_/g; 422 | $domain = uc($domain); 423 | my $h = 'SHORT_' . $domain . '_' . $response->code; 424 | dbg("hit rule: $h"); 425 | $pms->got_hit($h); 426 | } 427 | } 428 | $pms->got_hit('SHORT_URL_404') if($response->code == '404'); 429 | return undef; 430 | } 431 | $location = $response->headers->{location}; 432 | # Bail out if $short_url redirects to itself 433 | return undef if ($short_url eq $location); 434 | $self->cache_add($short_url, $location) if $self->{caching}; 435 | dbg("Found $short_url => $location"); 436 | eval { 437 | local $SIG{'__DIE__'}; 438 | if ($self->{log_target_only}) { 439 | $self->log_to_file($location); 440 | } else { 441 | $self->log_to_file("$short_url => $location"); 442 | } 443 | } if $self->{logging}; 444 | syslog('info',"Found $short_url => $location") if $self->{syslog}; 445 | } 446 | 447 | # At this point we have a new URL in $response 448 | $pms->got_hit('HAS_SHORT_URL'); 449 | $self->_add_uri_detail_list($pms, $location); 450 | 451 | # Set chained here otherwise we might mark a disabled page or 452 | # redirect back to the same host as chaining incorrectly. 453 | $pms->got_hit('SHORT_URL_CHAINED') if ($count gt 0); 454 | 455 | # Check if we are being redirected to a local page 456 | # Don't recurse in this case... 457 | if($location !~ /^https?:/) { 458 | my($host) = ($short_url =~ /^(https?:\/\/\S+)\//); 459 | $location = "$host/$location"; 460 | dbg("Looks like a local redirection: $short_url => $location"); 461 | $self->_add_uri_detail_list($pms, $location); 462 | return $location; 463 | } 464 | 465 | # Check for recursion 466 | if ((my ($domain) = ($location =~ /^https?:\/\/(\S+)\//))) { 467 | if (exists $been_here{$location}) { 468 | # Loop detected 469 | dbg("Error: loop detected"); 470 | $pms->got_hit('SHORT_URL_LOOP'); 471 | return $location; 472 | } else { 473 | if (exists $self->{url_shorteners}->{$domain}) { 474 | $been_here{$location} = 1; 475 | # Recurse... 476 | return $self->recursive_lookup($location, $pms, %been_here); 477 | } 478 | } 479 | } 480 | 481 | # No recursion; just return the final location... 482 | return $location; 483 | } 484 | 485 | sub short_url_tests { 486 | # Set by parsed_metadata 487 | return 0; 488 | } 489 | 490 | # Beware. Code copied from PerMsgStatus get_uri_detail_list(). 491 | # Stolen from GUDO.pm 492 | sub _add_uri_detail_list { 493 | my ($self, $pms, $uri) = @_; 494 | my $info; 495 | 496 | # Cache of text parsed URIs, as previously used by get_uri_detail_list(). 497 | push @{$pms->{parsed_uri_list}}, $uri; 498 | 499 | $info->{types}->{parsed} = 1; 500 | 501 | $info->{cleaned} = 502 | [Mail::SpamAssassin::Util::uri_list_canonify (undef, $uri)]; 503 | 504 | foreach (@{$info->{cleaned}}) { 505 | my ($dom, $host) = $self->{main}->{registryboundaries}->uri_to_domain($_); 506 | 507 | if ($dom && !$info->{domains}->{$dom}) { 508 | # 3.4 compatibility as per Marc Martinec 509 | if ($host) { 510 | $info->{hosts}->{$host} = $dom; 511 | } 512 | $info->{domains}->{$dom} = 1; 513 | $pms->{uri_domain_count}++; 514 | } 515 | } 516 | 517 | $pms->{uri_detail_list}->{$uri} = $info; 518 | 519 | # And of course, copied code from PerMsgStatus get_uri_list(). *sigh* 520 | dbg ('warn: PMS::get_uri_list() appears to have been harvested'), 521 | push @{$pms->{uri_list}}, @{$info->{cleaned}} 522 | if exists $pms->{uri_list}; 523 | } 524 | 525 | sub log_to_file { 526 | my ($self, $msg) = @_; 527 | return undef if not $self->{logging}; 528 | my $fh = $self->{logfh}; 529 | eval { 530 | flock($fh, LOCK_EX) or die $!; 531 | seek($fh, 0, SEEK_END) or die $!; 532 | if ($self->{log_target_only}) { 533 | print $fh "$msg\n"; 534 | } else { 535 | print $fh '['.time.'] '.$msg."\n"; 536 | } 537 | flock($fh, LOCK_UN) or die $!; 538 | }; 539 | } 540 | 541 | sub cache_add { 542 | my ($self, $short_url, $decoded_url) = @_; 543 | return undef if not $self->{caching}; 544 | 545 | eval { 546 | $self->{sth_insert} = $self->{dbh}->prepare_cached(" 547 | INSERT OR IGNORE INTO short_url_cache (short_url, decoded_url) 548 | VALUES (?,?) 549 | "); 550 | }; 551 | if ($@) { 552 | dbg("warn: $@"); 553 | return undef; 554 | }; 555 | 556 | $self->{sth_insert}->execute($short_url, $decoded_url); 557 | return undef; 558 | } 559 | 560 | sub cache_get { 561 | my ($self, $key) = @_; 562 | return undef if not $self->{caching}; 563 | 564 | eval { 565 | $self->{sth_select} = $self->{dbh}->prepare_cached(" 566 | SELECT decoded_url FROM short_url_cache 567 | WHERE short_url = ? AND modified > (strftime('%s','now') - ?) 568 | "); 569 | }; 570 | if ($@) { 571 | dbg("warn: $@"); 572 | return undef; 573 | } 574 | 575 | eval { 576 | $self->{sth_update} = $self->{dbh}->prepare_cached(" 577 | UPDATE short_url_cache 578 | SET modified=strftime('%s','now'), hits=hits+1 579 | WHERE short_url = ? 580 | "); 581 | }; 582 | if ($@) { 583 | dbg("warn: $@"); 584 | return undef; 585 | } 586 | 587 | $self->{sth_select}->execute($key, $self->{url_shortener_cache_ttl}); 588 | my $row = $self->{sth_select}->fetchrow_array(); 589 | if($row) { 590 | # Found cache entry; touch it to prevent expiry 591 | $self->{sth_update}->execute($key); 592 | $self->{sth_select}->finish(); 593 | $self->{sth_update}->finish(); 594 | return $row; 595 | } 596 | 597 | $self->{sth_select}->finish(); 598 | $self->{sth_update}->finish(); 599 | return undef; 600 | } 601 | 602 | 1; 603 | -------------------------------------------------------------------------------- /DecodeShortURLs.cf: -------------------------------------------------------------------------------- 1 | loadplugin Mail::SpamAssassin::Plugin::DecodeShortURLs DecodeShortURLs.pm 2 | 3 | ifplugin Mail::SpamAssassin::Plugin::DecodeShortURLs 4 | 5 | body HAS_SHORT_URL eval:short_url_tests() 6 | describe HAS_SHORT_URL Message contains one or more shortened URLs 7 | score HAS_SHORT_URL 0.01 8 | 9 | body SHORT_URL_CHAINED eval:short_url_tests() 10 | describe SHORT_URL_CHAINED Message has shortened URL chained to other shorteners 11 | score SHORT_URL_CHAINED 3.0 12 | 13 | body SHORT_URL_MAXCHAIN eval:short_url_tests() 14 | describe SHORT_URL_MAXCHAIN Message has shortened URL that causes more than 10 redirections 15 | score SHORT_URL_MAXCHAIN 5.0 16 | 17 | body SHORT_URL_LOOP eval:short_url_tests() 18 | describe SHORT_URL_LOOP Message has short URL that loops back to itself 19 | score SHORT_URL_LOOP 0.01 20 | 21 | body SHORT_URL_404 eval:short_url_tests() 22 | describe SHORT_URL_404 Message has short URL that returns 404 23 | score SHORT_URL_404 1.0 24 | 25 | body SHORT_T_CO_200 eval:short_url_tests() 26 | describe SHORT_T_CO_200 Message contains a t.co URL that has a warning due to abuse 27 | score SHORT_T_CO_200 10.0 28 | 29 | uri URI_BITLY_BLOCKED /^http:\/\/(?:bit\.ly|bitly\.com)\/a\/warning/i 30 | describe URI_BITLY_BLOCKED Message contains a bit.ly URL that has been disabled due to abuse 31 | score URI_BITLY_BLOCKED 10.0 32 | 33 | uri URI_SIMURL_BLOCKED /^http:\/\/simurl\.com\/redirect_black\.php/i 34 | describe URI_SIMURL_BLOCKED Message contains a simurl URL that has been disabled due to abuse 35 | score URI_SIMURL_BLOCKED 10.0 36 | 37 | uri URI_MIGRE_BLOCKED /^http:\/\/migre\.me\/bloqueado/i 38 | describe URI_MIGRE_BLOCKED Message contains a migre.me URL that has been disabled due to abuse 39 | score URI_MIGRE_BLOCKED 10.0 40 | 41 | uri URI_TINYURL_BLOCKED /^http:\/\/tinyurl\.com\/nospam\.php/i 42 | describe URI_TINYURL_BLOCKED Message contains a tinyurl that has been disabled due to abuse 43 | score URI_TINYURL_BLOCKED 10.0 44 | 45 | meta SHORT_URIBL HAS_SHORT_URL && (URIBL_BLACK || URIBL_AB_SURBL || URIBL_WS_SURBL || URIBL_JP_SURBL || URIBL_SC_SURBL || URIBL_RHS_DOB || URIBL_DBL_SPAM || URIBL_SBL) 46 | describe SHORT_URIBL Message contains shortened URL(s) and also hits a URIDNSBL 47 | score SHORT_URIBL 0.01 48 | 49 | # url_shortener_log /tmp/DecodeShortURLs.txt 50 | url_shortener_cache /tmp/DecodeShortURLs.sq3 51 | # url_shortener_syslog 1 52 | 53 | url_shortener 0rz.tw 54 | url_shortener 1l2.us 55 | url_shortener 1link.in 56 | url_shortener 1u.ro 57 | url_shortener 1url.com 58 | url_shortener 2.gp 59 | url_shortener 2.ly 60 | url_shortener 2big.at 61 | url_shortener 2chap.it 62 | url_shortener 2pl.us 63 | url_shortener 2su.de 64 | url_shortener 2tu.us 65 | url_shortener 2ze.us 66 | url_shortener 3.ly 67 | url_shortener 301.to 68 | url_shortener 301url.com 69 | url_shortener 307.to 70 | url_shortener 4ms.me 71 | url_shortener 4sq.com 72 | url_shortener 4url.cc 73 | url_shortener 6url.com 74 | url_shortener 7.ly 75 | url_shortener 9mp.com 76 | url_shortener a.gd 77 | url_shortener a.gg 78 | url_shortener a.nf 79 | url_shortener a2a.me 80 | url_shortener a2n.eu 81 | url_shortener aa.cx 82 | url_shortener abbr.com 83 | url_shortener abcurl.net 84 | url_shortener abe5.com 85 | url_shortener access.im 86 | url_shortener ad.vu 87 | url_shortener adf.ly 88 | url_shortener adjix.com 89 | url_shortener afx.cc 90 | url_shortener all.fuseurl.com 91 | url_shortener alturl.com 92 | url_shortener amzn.com 93 | url_shortener amzn.to 94 | url_shortener ar.gy 95 | url_shortener arm.in 96 | url_shortener arst.ch 97 | url_shortener asso.in 98 | url_shortener atu.ca 99 | url_shortener aurls.info 100 | url_shortener awe.sm 101 | url_shortener ayl.lv 102 | url_shortener azc.cc 103 | url_shortener azqq.com 104 | url_shortener b23.ru 105 | url_shortener b2l.me 106 | url_shortener b65.com 107 | url_shortener b65.us 108 | url_shortener bacn.me 109 | url_shortener bcool.bz 110 | url_shortener beam.to 111 | url_shortener bgl.me 112 | url_shortener binged.it 113 | url_shortener bit.do 114 | url_shortener bit.ly 115 | url_shortener bitly.com 116 | url_shortener bizj.us 117 | url_shortener bkite.com 118 | url_shortener blippr.com 119 | url_shortener bloat.me 120 | url_shortener blu.cc 121 | url_shortener bon.no 122 | url_shortener bravo.ly 123 | url_shortener bsa.ly 124 | url_shortener bt.io 125 | url_shortener budurl.com 126 | url_shortener buff.ly 127 | url_shortener buk.me 128 | url_shortener burnurl.com 129 | url_shortener c-o.in 130 | url_shortener c.shamekh.ws 131 | url_shortener canurl.com 132 | url_shortener cd4.me 133 | url_shortener chilp.it 134 | url_shortener chopd.it 135 | url_shortener chpt.me 136 | url_shortener chs.mx 137 | url_shortener chzb.gr 138 | url_shortener cl.lk 139 | url_shortener cl.ly 140 | url_shortener clck.ru 141 | url_shortener cli.gs 142 | url_shortener cliccami.info 143 | url_shortener clickthru.ca 144 | url_shortener clipurl.us 145 | url_shortener clk.my 146 | url_shortener cloaky.de 147 | url_shortener clop.in 148 | url_shortener clp.ly 149 | url_shortener coge.la 150 | url_shortener cokeurl.com 151 | url_shortener conta.cc 152 | url_shortener cort.as 153 | url_shortener cot.ag 154 | url_shortener crks.me 155 | url_shortener crum.pl 156 | url_shortener ctvr.us 157 | url_shortener curio.us 158 | url_shortener cuthut.com 159 | url_shortener cutt.us 160 | url_shortener cuturl.com 161 | url_shortener cuturls.com 162 | url_shortener dai.ly 163 | url_shortener db.tt 164 | url_shortener dealspl.us 165 | url_shortener decenturl.com 166 | url_shortener df9.net 167 | url_shortener dfl8.me 168 | url_shortener digbig.com 169 | url_shortener digg.com 170 | url_shortener digipills.com 171 | url_shortener digs.by 172 | url_shortener disq.us 173 | url_shortener dld.bz 174 | url_shortener dlvr.it 175 | url_shortener dn.vc 176 | url_shortener do.my 177 | url_shortener doi.org 178 | url_shortener doiop.com 179 | url_shortener dopen.us 180 | url_shortener dr.tl 181 | url_shortener drudge.tw 182 | url_shortener durl.me 183 | url_shortener durl.us 184 | url_shortener dvlr.it 185 | url_shortener dwarfurl.com 186 | url_shortener easyuri.com 187 | url_shortener easyurl.net 188 | url_shortener eca.sh 189 | url_shortener eclurl.com 190 | url_shortener eepurl.com 191 | url_shortener eezurl.com 192 | url_shortener eweri.com 193 | url_shortener ewerl.com 194 | url_shortener ezurl.eu 195 | url_shortener fa.by 196 | url_shortener faceto.us 197 | url_shortener fav.me 198 | url_shortener fb.me 199 | url_shortener fbshare.me 200 | url_shortener ff.im 201 | url_shortener fff.to 202 | url_shortener fhurl.com 203 | url_shortener fire.to 204 | url_shortener firsturl.de 205 | url_shortener firsturl.net 206 | url_shortener flic.kr 207 | url_shortener flingk.com 208 | url_shortener flq.us 209 | url_shortener fly2.ws 210 | url_shortener fon.gs 211 | url_shortener foxyurl.com 212 | url_shortener freak.to 213 | url_shortener fur.ly 214 | url_shortener fuseurl.com 215 | url_shortener fuzzy.to 216 | url_shortener fwd4.me 217 | url_shortener fwdurl.net 218 | url_shortener fwib.net 219 | url_shortener g.ro.lt 220 | url_shortener g8l.us 221 | url_shortener get-shorty.com 222 | url_shortener get-url.com 223 | url_shortener get.sh 224 | url_shortener geturl.us 225 | url_shortener gg.gg 226 | url_shortener gi.vc 227 | url_shortener gizmo.do 228 | url_shortener gkurl.us 229 | url_shortener gl.am 230 | url_shortener go.9nl.com 231 | url_shortener go.ign.com 232 | url_shortener go.to 233 | url_shortener go.usa.gov 234 | url_shortener go2.me 235 | url_shortener gog.li 236 | url_shortener golmao.com 237 | url_shortener goo.gl 238 | url_shortener good.ly 239 | url_shortener goshrink.com 240 | url_shortener gplus.to 241 | url_shortener gri.ms 242 | url_shortener gurl.es 243 | url_shortener hao.jp 244 | url_shortener hellotxt.com 245 | url_shortener hex.io 246 | url_shortener hiderefer.com 247 | url_shortener hmm.ph 248 | url_shortener hop.im 249 | url_shortener hopclicks.com 250 | url_shortener hotredirect.com 251 | url_shortener hotshorturl.com 252 | url_shortener href.in 253 | url_shortener hsblinks.com 254 | url_shortener ht.ly 255 | url_shortener htxt.it 256 | url_shortener hub.am 257 | url_shortener huff.to 258 | url_shortener hugeurl.com 259 | url_shortener hulu.com 260 | url_shortener hurl.it 261 | url_shortener hurl.me 262 | url_shortener hurl.no 263 | url_shortener hurl.ws 264 | url_shortener icanhaz.com 265 | url_shortener icio.us 266 | url_shortener idek.net 267 | url_shortener ikr.me 268 | url_shortener ilix.in 269 | url_shortener ir.pe 270 | url_shortener irt.me 271 | url_shortener is.gd 272 | url_shortener iscool.net 273 | url_shortener it2.in 274 | url_shortener ito.mx 275 | url_shortener its.my 276 | url_shortener itsy.it 277 | url_shortener ix.lt 278 | url_shortener j.mp 279 | url_shortener j2j.de 280 | url_shortener jdem.cz 281 | url_shortener jijr.com 282 | url_shortener just.as 283 | url_shortener k.vu 284 | url_shortener k6.kz 285 | url_shortener ketkp.in 286 | url_shortener kisa.ch 287 | url_shortener kissa.be 288 | url_shortener kl.am 289 | url_shortener klck.me 290 | url_shortener kore.us 291 | url_shortener korta.nu 292 | url_shortener kots.nu 293 | url_shortener krunchd.com 294 | url_shortener krz.ch 295 | url_shortener ktzr.us 296 | url_shortener kxk.me 297 | url_shortener l.hh.de 298 | url_shortener l.pr 299 | url_shortener l9k.net 300 | url_shortener lat.ms 301 | url_shortener liip.to 302 | url_shortener liltext.com 303 | url_shortener lin.cr 304 | url_shortener lin.io 305 | url_shortener linkbee.com 306 | url_shortener linkbun.ch 307 | url_shortener linkee.com 308 | url_shortener linkgap.com 309 | url_shortener linkslice.com 310 | url_shortener linxfix.de 311 | url_shortener liteurl.net 312 | url_shortener liurl.cn 313 | url_shortener livesi.de 314 | url_shortener lix.in 315 | url_shortener lk.ht 316 | url_shortener ln-s.net 317 | url_shortener ln-s.ru 318 | url_shortener lnk.by 319 | url_shortener lnk.gd 320 | url_shortener lnk.in 321 | url_shortener lnk.ly 322 | url_shortener lnk.ms 323 | url_shortener lnk.sk 324 | url_shortener lnkd.in 325 | url_shortener lnkurl.com 326 | url_shortener loopt.us 327 | url_shortener lost.in 328 | url_shortener lru.jp 329 | url_shortener lt.tl 330 | url_shortener lu.to 331 | url_shortener lurl.no 332 | url_shortener macte.ch 333 | url_shortener mash.to 334 | url_shortener mavrev.com 335 | url_shortener mcaf.ee 336 | url_shortener memurl.com 337 | url_shortener merky.de 338 | url_shortener metamark.net 339 | url_shortener migre.me 340 | url_shortener min2.me 341 | url_shortener minilien.com 342 | url_shortener minilink.org 343 | url_shortener miniurl.com 344 | url_shortener minurl.fr 345 | url_shortener mke.me 346 | url_shortener moby.to 347 | url_shortener moourl.com 348 | url_shortener mrte.ch 349 | url_shortener msg.sg 350 | url_shortener murl.kz 351 | url_shortener mv2.me 352 | url_shortener myloc.me 353 | url_shortener mysp.in 354 | url_shortener myurl.in 355 | url_shortener myurl.si 356 | url_shortener n.pr 357 | url_shortener nanoref.com 358 | url_shortener nanourl.se 359 | url_shortener nbc.co 360 | url_shortener nblo.gs 361 | url_shortener nbx.ch 362 | url_shortener ncane.com 363 | url_shortener ndurl.com 364 | url_shortener ne1.net 365 | url_shortener netnet.me 366 | url_shortener netshortcut.com 367 | url_shortener ni.to 368 | url_shortener nig.gr 369 | url_shortener nm.ly 370 | url_shortener nn.nf 371 | url_shortener not.my 372 | url_shortener notlong.com 373 | url_shortener nsfw.in 374 | url_shortener nutshellurl.com 375 | url_shortener nxy.in 376 | url_shortener nyti.ms 377 | url_shortener o-x.fr 378 | url_shortener o.ly 379 | url_shortener oboeyasui.com 380 | url_shortener oc1.us 381 | url_shortener offur.com 382 | url_shortener ofl.me 383 | url_shortener om.ly 384 | url_shortener omf.gd 385 | url_shortener omoikane.net 386 | url_shortener on.cnn.com 387 | url_shortener on.mktw.net 388 | url_shortener onecent.us 389 | url_shortener onforb.es 390 | url_shortener onion.com 391 | url_shortener onsaas.info 392 | url_shortener ooqx.com 393 | url_shortener oreil.ly 394 | url_shortener orz.se 395 | url_shortener ow.ly 396 | url_shortener oxyz.info 397 | url_shortener p.ly 398 | url_shortener p8g.tw 399 | url_shortener parv.us 400 | url_shortener paulding.net 401 | url_shortener pduda.mobi 402 | url_shortener peaurl.com 403 | url_shortener pendek.in 404 | url_shortener pep.si 405 | url_shortener pic.gd 406 | url_shortener piko.me 407 | url_shortener ping.fm 408 | url_shortener piurl.com 409 | url_shortener pli.gs 410 | url_shortener plumurl.com 411 | url_shortener plurl.me 412 | url_shortener pnt.me 413 | url_shortener politi.co 414 | url_shortener poll.fm 415 | url_shortener pop.ly 416 | url_shortener poprl.com 417 | url_shortener post.ly 418 | url_shortener posted.at 419 | url_shortener pp.gg 420 | url_shortener profile.to 421 | url_shortener pt2.me 422 | url_shortener ptiturl.com 423 | url_shortener pub.vitrue.com 424 | url_shortener puke.it 425 | url_shortener pysper.com 426 | url_shortener qik.li 427 | url_shortener qlnk.net 428 | url_shortener qoiob.com 429 | url_shortener qr.cx 430 | url_shortener qte.me 431 | url_shortener qu.tc 432 | url_shortener quickurl.co.uk 433 | url_shortener qurl.com 434 | url_shortener qurlyq.com 435 | url_shortener quu.nu 436 | url_shortener qux.in 437 | url_shortener qy.fi 438 | url_shortener r.im 439 | url_shortener rb6.me 440 | url_shortener rde.me 441 | url_shortener read.bi 442 | url_shortener readthis.ca 443 | url_shortener reallytinyurl.com 444 | url_shortener redir.ec 445 | url_shortener redirects.ca 446 | url_shortener redirx.com 447 | url_shortener relyt.us 448 | url_shortener retwt.me 449 | url_shortener ri.ms 450 | url_shortener rickroll.it 451 | url_shortener rivva.de 452 | url_shortener riz.gd 453 | url_shortener rly.cc 454 | url_shortener rnk.me 455 | url_shortener rsmonkey.com 456 | url_shortener rt.nu 457 | url_shortener ru.ly 458 | url_shortener rubyurl.com 459 | url_shortener rurl.org 460 | url_shortener rww.tw 461 | url_shortener s.gnoss.us 462 | url_shortener s3nt.com 463 | url_shortener s4c.in 464 | url_shortener s7y.us 465 | url_shortener safe.mn 466 | url_shortener safelinks.ru 467 | url_shortener sai.ly 468 | url_shortener sameurl.com 469 | url_shortener sdut.us 470 | url_shortener sed.cx 471 | url_shortener sfu.ca 472 | url_shortener shadyurl.com 473 | url_shortener shar.es 474 | url_shortener shim.net 475 | url_shortener shink.de 476 | url_shortener shorl.com 477 | url_shortener short.ie 478 | url_shortener short.to 479 | url_shortener shorten.ws 480 | url_shortener shortenurl.com 481 | url_shortener shorterlink.com 482 | url_shortener shortio.com 483 | url_shortener shortlinks.co.uk 484 | url_shortener shortly.nl 485 | url_shortener shortn.me 486 | url_shortener shortna.me 487 | url_shortener shortr.me 488 | url_shortener shorturl.com 489 | url_shortener shortz.me 490 | url_shortener shoturl.us 491 | url_shortener shout.to 492 | url_shortener show.my 493 | url_shortener shredu 494 | url_shortener shredurl.com 495 | url_shortener shrinkify.com 496 | url_shortener shrinkr.com 497 | url_shortener shrinkster.com 498 | url_shortener shrinkurl.us 499 | url_shortener shrt.fr 500 | url_shortener shrt.st 501 | url_shortener shrt.ws 502 | url_shortener shrten.com 503 | url_shortener shrtl.com 504 | url_shortener shrtn.com 505 | url_shortener shrtnd.com 506 | url_shortener shrunkin.com 507 | url_shortener shurl.net 508 | url_shortener shw.me 509 | url_shortener simurl.com 510 | url_shortener simurl.net 511 | url_shortener simurl.org 512 | url_shortener simurl.us 513 | url_shortener sitelutions.com 514 | url_shortener siteo.us 515 | url_shortener sl.ly 516 | url_shortener slate.me 517 | url_shortener slidesha.re 518 | url_shortener slki.ru 519 | url_shortener smallr.com 520 | url_shortener smallr.net 521 | url_shortener smarturl.it 522 | url_shortener smfu.in 523 | url_shortener smsh.me 524 | url_shortener smurl.com 525 | url_shortener smurl.name 526 | url_shortener sn.im 527 | url_shortener sn.vc 528 | url_shortener snadr.it 529 | url_shortener snipie.com 530 | url_shortener snipr.com 531 | url_shortener snipurl.com 532 | url_shortener snkr.me 533 | url_shortener snurl.com 534 | url_shortener soo.gd 535 | url_shortener song.ly 536 | url_shortener sp2.ro 537 | url_shortener spedr.com 538 | url_shortener sqze.it 539 | url_shortener srnk.net 540 | url_shortener srs.li 541 | url_shortener starturl.com 542 | url_shortener stickurl.com 543 | url_shortener stpmvt.com 544 | url_shortener sturly.com 545 | url_shortener su.pr 546 | url_shortener surl.co.uk 547 | url_shortener surl.hu 548 | url_shortener surl.it 549 | url_shortener t.cn 550 | url_shortener t.co 551 | url_shortener t.lh.com 552 | url_shortener ta.gd 553 | url_shortener takemyfile.com 554 | url_shortener tbd.ly 555 | url_shortener tcrn.ch 556 | url_shortener tgr.me 557 | url_shortener tgr.ph 558 | url_shortener th8.us 559 | url_shortener thecow.me 560 | url_shortener thrdl.es 561 | url_shortener tighturl.com 562 | url_shortener timesurl.at 563 | url_shortener tini.us 564 | url_shortener tiniuri.com 565 | url_shortener tiny.cc 566 | url_shortener tiny.ly 567 | url_shortener tiny.pl 568 | url_shortener tinyarro.ws 569 | url_shortener tinylink.com 570 | url_shortener tinylink.in 571 | url_shortener tinypl.us 572 | url_shortener tinysong.com 573 | url_shortener tinytw.it 574 | url_shortener tinyuri.ca 575 | url_shortener tinyurl.com 576 | url_shortener tk. 577 | url_shortener tl.gd 578 | url_shortener tllg.net 579 | url_shortener tmi.me 580 | url_shortener tncr.ws 581 | url_shortener tnij.org 582 | url_shortener tnw.to 583 | url_shortener tny.com 584 | url_shortener to. 585 | url_shortener to.je 586 | url_shortener to.ly 587 | url_shortener to.vg 588 | url_shortener togoto.us 589 | url_shortener totc.us 590 | url_shortener toysr.us 591 | url_shortener tpm.ly 592 | url_shortener tr.im 593 | url_shortener tr.my 594 | url_shortener tra.kz 595 | url_shortener traceurl.com 596 | url_shortener trackurl.it 597 | url_shortener trcb.me 598 | url_shortener trg.li 599 | url_shortener trib.al 600 | url_shortener trick.ly 601 | url_shortener trii.us 602 | url_shortener trim.li 603 | url_shortener trumpink.lt 604 | url_shortener trunc.it 605 | url_shortener truncurl.com 606 | url_shortener tsort.us 607 | url_shortener tubeurl.com 608 | url_shortener turo.us 609 | url_shortener tw0.us 610 | url_shortener tw1.us 611 | url_shortener tw2.us 612 | url_shortener tw5.us 613 | url_shortener tw6.us 614 | url_shortener tw8.us 615 | url_shortener tw9.us 616 | url_shortener twa.lk 617 | url_shortener tweet.me 618 | url_shortener tweetburner.com 619 | url_shortener tweetl.com 620 | url_shortener twhub.com 621 | url_shortener twi.gy 622 | url_shortener twip.us 623 | url_shortener twirl.at 624 | url_shortener twit.ac 625 | url_shortener twitclicks.com 626 | url_shortener twitterurl.net 627 | url_shortener twitterurl.org 628 | url_shortener twitthis.com 629 | url_shortener twittu.ms 630 | url_shortener twiturl.de 631 | url_shortener twitzap.com 632 | url_shortener twlv.net 633 | url_shortener twtr.us 634 | url_shortener twurl.cc 635 | url_shortener twurl.nl 636 | url_shortener u.mavrev.com 637 | url_shortener u.nu 638 | url_shortener u76.org 639 | url_shortener ub0.cc 640 | url_shortener uiop.me 641 | url_shortener ulimit.com 642 | url_shortener ulu.lu 643 | url_shortener unfaker.it 644 | url_shortener updating.me 645 | url_shortener ur.ly 646 | url_shortener ur1.ca 647 | url_shortener urizy.com 648 | url_shortener url.ag 649 | url_shortener url.az 650 | url_shortener url.co.uk 651 | url_shortener url.go.it 652 | url_shortener url.ie 653 | url_shortener url.inc-x.eu 654 | url_shortener url.lotpatrol.com 655 | url_shortener url360.me 656 | url_shortener url4.eu 657 | url_shortener urlao.com 658 | url_shortener urlbee.com 659 | url_shortener urlborg.com 660 | url_shortener urlbrief.com 661 | url_shortener urlcorta.es 662 | url_shortener urlcover.com 663 | url_shortener urlcut.com 664 | url_shortener urlcutter.com 665 | url_shortener urlenco.de 666 | url_shortener urlg.info 667 | url_shortener urlhawk.com 668 | url_shortener urli.nl 669 | url_shortener urlin.it 670 | url_shortener urlkiss.com 671 | url_shortener urloo.com 672 | url_shortener urlpire.com 673 | url_shortener urls.im 674 | url_shortener urlshorteningservicefortwitter.com 675 | url_shortener urltea.com 676 | url_shortener urlu.ms 677 | url_shortener urlvi.b 678 | url_shortener urlvi.be 679 | url_shortener urlx.ie 680 | url_shortener urlz.at 681 | url_shortener urlzen.com 682 | url_shortener usat.ly 683 | url_shortener use.my 684 | url_shortener uservoice.com 685 | url_shortener ustre.am 686 | url_shortener vado.it 687 | url_shortener vb.ly 688 | url_shortener vdirect.com 689 | url_shortener vgn.am 690 | url_shortener vi.ly 691 | url_shortener viigo.im 692 | url_shortener virl.com 693 | url_shortener vl.am 694 | url_shortener vm.lc 695 | url_shortener voizle.com 696 | url_shortener vtc.es 697 | url_shortener w0r.me 698 | url_shortener w33.us 699 | url_shortener w34.us 700 | url_shortener w3t.org 701 | url_shortener w55.de 702 | url_shortener wa9.la 703 | url_shortener wapo.st 704 | url_shortener wapurl.co.uk 705 | url_shortener webalias.com 706 | url_shortener welcome.to 707 | url_shortener wh.gov 708 | url_shortener widg.me 709 | url_shortener wipi.es 710 | url_shortener wkrg.com 711 | url_shortener woo.ly 712 | url_shortener wp.me 713 | url_shortener x.co 714 | url_shortener x.hypem.com 715 | url_shortener x.se 716 | url_shortener x.vu 717 | url_shortener xeeurl.com 718 | url_shortener xil.in 719 | url_shortener xlurl.de 720 | url_shortener xn--1ci.ws 721 | url_shortener xn--3fi.ws 722 | url_shortener xn--5gi.ws 723 | url_shortener xn--9gi.ws 724 | url_shortener xn--bih.ws 725 | url_shortener xn--cwg.ws 726 | url_shortener xn--egi.ws 727 | url_shortener xn--fwg.ws 728 | url_shortener xn--hgi.ws 729 | url_shortener xn--l3h.ws 730 | url_shortener xn--odi.ws 731 | url_shortener xn--ogi.ws 732 | url_shortener xn--rei.ws 733 | url_shortener xn--vgi.ws 734 | url_shortener xr.com 735 | url_shortener xrl.in 736 | url_shortener xrl.us 737 | url_shortener xrt.me 738 | url_shortener xurl.es 739 | url_shortener xurl.jp 740 | url_shortener xxsurl.de 741 | url_shortener xzb.cc 742 | url_shortener y.ahoo.it 743 | url_shortener yatuc.com 744 | url_shortener ye-s.com 745 | url_shortener ye.pe 746 | url_shortener yep.it 747 | url_shortener yfrog.com 748 | url_shortener yhoo.it 749 | url_shortener yiyd.com 750 | url_shortener youtu.be 751 | url_shortener yuarel.com 752 | url_shortener z.pe 753 | url_shortener z0p.de 754 | url_shortener zapt.in 755 | url_shortener zi.ma 756 | url_shortener zi.me 757 | url_shortener zi.mu 758 | url_shortener zi.pe 759 | url_shortener zip.li 760 | url_shortener zipmyurl.com 761 | url_shortener zite.to 762 | url_shortener zootit.com 763 | url_shortener zud.me 764 | url_shortener zurl.ws 765 | url_shortener zz.gd 766 | url_shortener zzang.kr 767 | 768 | endif 769 | --------------------------------------------------------------------------------