#!/usr/bin/perl # # # By Stefan 'tommie' Tomanek, stefan@kann-nix.org # # # This script works fine on DFN (german universities) and T-Oline sites # # 01.03.2002 # *Changed to GPL # # 15.03.2002 # *Now works on QUERIES as well # # 24.04.2002 # *the nick does not have to be on the channel # *switched to /WHO # # 27.04.2002 # *localization of hosts (/localize @hostname) # # 29.04.2002 # *tweaked Design # *added channel statistics # # 04.05.2002 # *added alternate database (IP Atlas) # # 05.05.2002 # *the script is now able to use both databases simultaniously # */set localize_use_ to enable or disable them # # 10.05.2002 # *non-blocking IO via fork() # # 13.05.2002 # *finally improved forking and background localizing # *now using XML # # 26.05.2002 # *Implemented auto-localize # # 28.05.2002 # *major updates # *fixed race conditions # # 30.05.2002 # *finally rendered traceroute support usefull # # 31.05.2002 # *moved database to this file # # 03.07.2002 # *switched to Data::Dumper # # 25.11.2014 # Added utrace.de as a localizer # http://www.utrace.de/ use strict; use vars qw($VERSION %IRSSI); $VERSION = "2017040101"; %IRSSI = ( authors => "Stefan 'tommie' Tomanek", contact => "stefan\@pico.ruhr.de", name => "localize", description => "Localizes users using traceroute, the localizer database or IP-Atlas", license => "GPLv2", url => "", changed => "$VERSION", modules => "Data::Dumper LWP::UserAgent HTML::Entities", commands => "localize" ); use Irssi 20020324; use LWP::UserAgent; use HTML::Entities; use Data::Dumper; use POSIX; use Socket; use vars qw(%queries %cache %ipdb $procs @tracer $debug); $debug = 0; $procs = 0; # host regexps for trace_host # @tracer = ( { provider => 'tonline', regexp => [ '.*?-.*?\.(.*?)(\.DE|)\.net\.dtag\.de', ] }, { provider => 'uunet', regexp => [ '.*?-\d+-\d+\..*?\.(.*?)\d?\.uunet\.de', ] }, { provider => 'kpnqwest', regexp => [ '.*?-.*?\.(\w?)\.de\.kpnqwest\.net', ] }, { provider => 'ewetel', regexp => [ '.*?-.*?-.*?\.rt8\.(.*?)\.ewetel\.net', '(.*?)[0-9]*-.*?\.ewetel\.net', 'so\d+-\d+-\d+-bbrt\d+\.(.*?)\.ewe-ip-backbone\.de'] }, { provider => 'arcor', regexp => [ '((?!dsl)\w+)-\d+-\d+-\d+-\d+\.arcor-ip\.net', '.*?-(.*?)-.*?\d*\.arcor-online\.net'] }, { provider => 'mediaways', regexp => ['.*-(.*)-de.*-.*-.*-.*\..*\.mediaways.net', ] }, { provider => 'mobilcom', regexp => ['.*\.(.*?)[0-9]+-.\.mcbone\.net',] }, { provider => 'vianetworks', regexp => ['\w+\.(.*?)\.revmap\.vianetworks\.de', 'rt\d{3}(.*?)\.de\.vianw\.net',] }, { provider => 'mfnx', regexp => ['.+-\d+-\d+-\d+\..+\.(.*?)[0-9]+\.de\.mfnx\.net',] }, { provider => 'colt', regexp => ['.+-.*\..+\.(.*?)\.DE.COLT-ISC.NET', '.+\.((?!dsl)(?!host)\w+)\.de\.colt\.net', '..\d\.(\w+)\.de\.colt\.net',] }, { provider => 'telia', regexp => ['(.*?)-.+-.+-.+\.telia.net',] }, { provider => 'hansanet', regexp => ['.*\.(.*?)-[0-9]+\.hansenet\.net',] }, { provider => 'isis', regexp => ['isis-gw-(.*?)[0-9]\.de\.cw\.net', ] }, { provider => 'cable & wireless', regexp => ['.*-\d+-\d+-\d+-.*?-(.*?)\d+\.de\.cw\.net', '.*?-.*?-(.*?)\d+\.de\.cw\.net'] }, { provider => 'NEFkom', regexp => ['nefkom-gw-(.*?)\.de\.cw\.net',] }, { provider => 'eastlink', regexp => ['.*?-.*?-.*?-.*?-(.*?)\.eastlink.de',] }, { provider => 'alternet', regexp => ['.*\.(.*?)\d?\.de\.alter\.net',] }, { provider => 'CompleTel', regexp => ['.+-.+-.+-.+\.(.*)\.ipcenta\.de',] }, { provider => 'mediascape', regexp => ['.+\..+\.(.*?)\.mediascape\.net',] }, { provider => 'schlund', regexp => ['gw-prtr-[0-9]+-.+\.(.+)[0-9]+\.schlund.net',] }, { provider => 'bisping', regexp => ['(.*?)-gw-pmx[0-9]*\.bisping\.net',] }, { provider => 'gatel', regexp => ['ser[0-3]+-[0-3]+\.(.*?)[0-3]+\.de\.gatel\.net',] }, { provider => 'qsc', regexp => ['rqsc-(.*?)-de[0-9]+-.+[0-9]+-[0-9]+-[0-9]+\.nw\.mediaways\.net', 'bsn\d+\.(.*?)\.qdsl-home\.de', 'bsn\d+\.(.*?)\.qsc\.de', 'core1\.(.*?)\.qsc\.de'] }, { provider => 'dfn', regexp => ['.r-(.*?)[0-9]+\.g-win.dfn.de', '.*\.uni-(.*?)\.de', '.*\.fh-(.*?)\.de', '.*\.tu-(.*?)\.de', '.*\.fu-(.*?)\.de',] }, { provider => 'mops.net', regexp => ['.*?\.core\d\.(.*?)\.mops\.net',] }, { provider => 'schule.de', regexp => ['.*\.(.*?)\..*?\.Schule\.DE',] }, { provider => 'belwue', regexp => ['(?:.*?-)?(.*?)\d+\.BelWue\.DE',] }, { provider => 'lambdanet', regexp => ['.*?\.(.*?)\.de\.lambdanet\.net',] } ); %ipdb = ( # For utrace.de API documentation, see http://en.utrace.de/api.php d1utrace=>{ name=>'utrace', active=>1, url=>'http://xml.utrace.de/?query=', city=>'(.*?)<\/region>', province=>'(.*?)<\/org>', country=>'(.*?)<\/countrycode>', provider=>'(.*?)<\/isp>', failure=>'request-limit-exceeded|Host not found'}, d2ipatlas=> { name=>'IP-Atlas', active=>0, url=>'http://www.xpenguin.com/plot.php?address=', city=>'is located in (.*?),', province=>'is located in.*, (.*?) \(state\),', country=>'is located in.*, (.*?)\. ', failure=>'cannot be located|does not resolve' }, d3netgeo => { name=>'NetGeo', active=>0, url=>'http://netgeo.caida.org/perl/netgeo.cgi?target=', city=>'CITY:\ *(\w+)
', province=>'STATE:.*?, (.*?) \(state\)
', country=>'COUNTRY:\ *(\w+)
', failure=> "SHOULD NOT"}, ); sub draw_box ($$$) { my ($title, $text, $footer) = @_; my $box = ''; $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n"; foreach (split(/\n/, $text)) { $box .= '%R|%n '.$_."\n"; } $box .= '%R`--<%n'.$footer.'%R>->%n'; return $box; } sub show_help() { my $help="Localize $VERSION /localize Try to localize the user 'nickname' /localize @ Try to localize the host /localize <#channel> Create a tree of the people inside the channel /localize -s Save the localize cache and settings /localize -r Reload the localize cache from file /localize -c Clear the cache /localize -sc Shows the current content of the cache /localize -h Display this help "; my $text = ""; foreach (split(/\n/, $help)) { $_ =~ s/^\/(.*)$/%9\/$1%9/; $text .= $_."\n"; } print CLIENTCRAP draw_box("Localize", $text, "Help"); } sub get ($) { my ($url) = @_; my $ua = LWP::UserAgent->new(env_proxy => 1,keep_alive => 1,timeout => 30); $ua->agent('Irssi'); my $request = HTTP::Request->new('GET', $url); my $response = $ua->request($request); if ($response->is_success()) { return $response->content(); } else { return undef; } } sub parse_page ($$) { my ($page, $item) = @_; my %empty; my (%location); $_ = $page; my $regexp = $item->{failure}; return(%location) if /$regexp/; foreach my $key ('city', 'province', 'country') { $location{$key} = ''; my $regexp = $item->{$key}; if (/$regexp/) { $location{$key} = $1; } else { return(%empty); } } if (defined $item->{provider}) { if (/$item->{provider}/) { $location{provider} = $1; } } $location{map} = $item->{name} if (%location); return (%location); } sub trace_host ($) { my ($host) = @_; my $cmd = Irssi::settings_get_str('localize_trace_cmd'); local *F; my $pid = open(F, '-|', $cmd.' '.$host.' 2>/dev/null'); my $loc_host; my $provider; my $hops = 0; my $maxhops = Irssi::settings_get_int('localize_trace_distance'); $_ = $host; while (defined $_) { print $_ if $debug; $hops++; if (/\*/) { kill 15, $pid; close(F); return([$loc_host, $provider]) if ($hops < $maxhops && $hops >= 0); return([undef, undef]); } else { foreach my $traced (@tracer) { foreach my $regexp (@{$traced->{regexp}}) { if (/[0-9]+ $regexp /i) { $loc_host = $1; $provider = $traced->{provider}; print $regexp if $debug; print "$loc_host <-> $provider" if $debug; $hops = 0; last; } } } } $_ = ; } close(F); if ( ($hops < $maxhops) && ($hops >= 0)) { if ($debug) { print $loc_host."-".$provider foreach (1..10); } return([$loc_host, $provider]); } else { print $hops." -> ".$maxhops if $debug; } return([undef, undef]); } sub localize($$) { my ($nicks, $query) = @_; if (Irssi::settings_get_bool('localize_background')) { bg_fetch($nicks, $query); } else { fg_fetch($nicks, $query); } } sub fg_fetch ($$) { my ($nicks, $query) = @_; my $data = create_output(@{$nicks}); my $auto = $queries{$query}->[0]{auto}; remove_request($query); process_input($query, $auto, $data); } sub bg_fetch ($$) { my ($nicks, $query) = @_; my ($rh, $wh); pipe($rh, $wh); my $pid = fork(); $procs++; if ($pid > 0) { close $wh; my $size = scalar(@{$nicks}); my $auto = $queries{$query}->[0]{auto}; remove_request($query); unless ($auto ne '') { print CLIENTCRAP '%R>>%n Localizing '.$size.' host(s) in background [pid '.$pid.']...' if Irssi::settings_get_bool('localize_show_message'); } Irssi::pidwait_add($pid); my $pipetag; my @args = ($rh, $query, $auto, \$pipetag); $pipetag = Irssi::input_add(fileno($rh), INPUT_READ, \&pipe_input, \@args); } else { my $data = create_output(@{$nicks}); eval { print($wh $data); close($wh) }; POSIX::_exit(1); } } sub create_output(@) { my (@nicks) = @_; my @new_db; my @stuff; my @data; my $i = 0; foreach (@nicks) { my $nick = $$_[0]; my $host = $$_[1]; my (%location); if (defined $cache{$host}) { %location = %{$cache{$host}}; $location{$_} = $location{$_} foreach (keys %location); $location{'map'} .= " (cached)"; } else { if (Irssi::settings_get_bool('localize_use_traceroute')) { unless (%location) { my ($sign, $provider) = @{ trace_host($host) }; print "\n\n>>>>".$sign if $debug; %location = kfz2location($sign) if $sign; $location{map} = 'traceroute' if (%location); $location{provider} = $provider if (%location); } } if (Irssi::settings_get_bool('localize_use_databases')) { unless (%location) { foreach (sort keys(%ipdb)) { my $item = $ipdb{$_}; next unless $item->{active}; #my $ip = gethostbyname($host); #next unless $ip; my $url = $item->{url}.$host; #inet_ntoa($ip); my $text = get($url); %location = parse_page($text, $item); } } } if (Irssi::settings_get_bool('localize_get_coordinates')) { if (%location) { my $city = $location{city}; my $ua = LWP::UserAgent->new(env_proxy=>1, keep_alive=>1, timeout=>30); my $data = $ua->get('http://www.astro.com/atlas/horoscope/?expr='.$city)->content(); foreach (split /\n/, $data) { decode_entities($_); if (/^
  • (.*?)<\/a>, (?:.*?): (\d+)n(\d+)<\/b>, (\d+)e(\d+)<\/b>/i) { $location{latitude} = "$2.$3"; $location{longitude} = "$4.$5"; last; } } } } } $location{'nick'} = $nick if (%location); $location{'host'} = $host if (%location); #$location{$_} = $location{$_} foreach (keys %location); push @stuff, \%location; $i++; } my %foo = ("nicks" => \@stuff); my $dumper = Data::Dumper->new([\%foo]); $dumper->Purity(1)->Deepcopy(1); my $data = $dumper->Dump; return($data); } sub pipe_input ($$$$) { my ($rh, $query, $auto, $pipetag) = @{$_[0]}; my @lines = <$rh>; close($rh); Irssi::input_remove($$pipetag); my $text = join("", @lines); process_input($query, $auto, $text); } sub process_input($$$) { my ($query, $auto, $text) = @_; my $channel_prefix = '^(\#|\+|\!)'; my %stuff; $procs--; no strict; %stuff = %{ eval "$text" }; return(0) unless (%stuff); my @items = @{$stuff{nicks}}; my %channel; foreach (@items) { my %location = %{$_}; if (not %location) { unless ($query =~ /$channel_prefix/) { print CLIENTCRAP '%R>>%n Unable to localize '.$query if ($auto eq ''); } } else { my $nocache = Irssi::settings_get_str('localize_ipatlas_nocache'); add_to_cache(%location) unless ($location{'map'} eq 'IP-Atlas' && $location{'host'} =~ /$nocache/); $location{$_} = $location{$_} foreach (keys %location); my $nick = $location{'nick'}; if ($query =~ /$channel_prefix/) { push @{ $channel{$location{"country"}}{$location{"province"}}{$location{"city"}} }, [$nick, $location{"map"}]; } else { if ($auto eq '') { show_location(%location); } else { auto_localize($auto, %location); } } #remove_request($query); } } if ($query =~ /$channel_prefix/) { show_cities($query, %channel); } } sub add_to_cache (%) { my (%location) = @_; my $host = $location{'host'}; return if defined $cache{$host}; foreach (keys %location) { next if ($_ eq 'nick' || $_ eq 'host'); $cache{$host}{$_} = $location{$_}; } } sub save_cache { my $filename = Irssi::settings_get_str('localize_cache_filename'); my $data = Dumper(\%cache); local *F; open(F, '>',$filename); print(F $data); close(F); print CLIENTCRAP "%R>>%n localize cache (".scalar(keys(%cache))." entries/".length($data)." bytes) saved to ".$filename; } sub load_cache { no strict; my $filename = Irssi::settings_get_str('localize_cache_filename'); my (%new_cache, $text); local *F; open F, "<",$filename || return; $text .= $_ foreach (); close(F); eval { %new_cache = %{ eval "$text" }; }; foreach (keys %new_cache) { $cache{$_} = $new_cache{$_} unless defined $cache{$_}; } print CLIENTCRAP "%R>>%n localize cache (".scalar(keys %new_cache)." hosts) loaded"; } sub clear_cache { foreach (keys(%cache)) { delete $cache{$_}; } print CLIENTCRAP "%R>>%n localize cache cleared"; } sub show_location (%) { my (%location) = @_; my $query = Irssi::query_find($location{"nick"}); my $output = \&Irssi::print; $output = sub { $query->print(@_); } if ($query); my $text = ""; my $headline = '%R,--[%n%9%ULocation of '.$location{"nick"}." (".$location{"host"}.")%U%9%R]%n"; foreach ('Country', 'Province', 'City', 'Provider') { my $fill = ' 'x(9-length($_)); $text .= '%B'.$fill.$_.':%n '.$location{lc $_}."\n" if defined $location{lc $_}; } #$text .= $location{latitude}."/".$location{longitude}; &$output(draw_box('Location of '.$location{nick}.' ('.$location{host}.')', $text, $location{map}), MSGLEVEL_CLIENTCRAP); show_map($location{latitude}, $location{longitude}, $location{nick}) if Irssi::settings_get_bool('localize_xplanet_show_map'); } sub show_map ($$$) { my ($lat, $long, $nick) = @_; return unless defined $lat && defined $long; my $cmd = Irssi::settings_get_str('localize_xplanet_cmd'); my $file = Irssi::settings_get_str('localize_xplanet_temp_file'); local *F; open F, '>',$file; print F $lat.' '.$long.' "'.$nick.'"'; close F; system("$cmd -markerf $file &"); } sub show_cities ($%) { my ($channel, %cities) = @_; print CLIENTCRAP "%R,---[%n%9%U".$channel."%U%9%R]%n"; foreach (sort keys %cities) { print CLIENTCRAP "%R+-+[%n".$_."%R]%n"; print CLIENTCRAP "%R| | %n"; my $n_provs = scalar( keys %{$cities{$_}}); foreach my $province (sort keys %{$cities{$_}}) { my $cp = '|'; $cp = ' ' if ($n_provs == 1); print CLIENTCRAP "%R| +-+%n"."%R[%n".$province."%R]%n"; my $n_cities = scalar(keys %{$cities{$_}{$province}}); foreach my $city (sort keys %{$cities{$_}{$province}}) { my $cc = '|'; $cc = ' ' if ($n_cities == 1); print CLIENTCRAP "%R| $cp +-+%n"."%R[%n".$city."%R]%n"; my $n_nicks = scalar(@{$cities{$_}{$province}{$city}}); foreach my $nick (sort @{$cities{$_}{$province}{$city}}) { my $cn = '|`'; $cn = '`-' if ($n_nicks == 1); print CLIENTCRAP "%R| $cp $cc $cn-----%n%B[%n".$nick->[0]."%B]%n"; $n_nicks--; } $n_cities--; } print CLIENTCRAP "%R| $cp "; $n_provs--; } #print CLIENTCRAP "%R| "; } print CLIENTCRAP "%R`----->%n"; } sub cmd_localize ($$$) { my ($args, $server, $witem) = @_; my @names = split(/ /, $args); foreach (@names) { if ( substr($_, 0, 1) eq '@' ) { my $ip = substr(lc($_), 1); new_request($server, $ip, 2, ''); localize([[$ip, $ip]], $ip); } elsif ($_ eq '-h') { show_help(); } elsif ($_ eq '-c') { clear_cache(); } elsif ($_ eq '-s') { save_cache(); } elsif ($_ eq '-r') { load_cache(); } elsif ($_ eq '-sc') { show_cache(@names); return(); } else { new_request($server, lc($_), 0, ''); } } } sub show_cache (@) { my (@params) = @_; unless (defined $params[1] && $params[1] eq '-i_am_insane') { my $entries = scalar(keys(%cache)); print CLIENTCRAP '%R>>%n There are '.$entries.' saved locations in the cache. If you really want to display them all, type /localize -sc -i_am_insane'; } else { my $text = ""; foreach my $key (sort keys %cache) { my %item = %{$cache{$key}}; $item{$_} = $item{$_} foreach (keys %item); my $string .= $key; foreach ('country', 'province', 'city', 'map') { $string .= ' | '.$item{$_}; } $text .= $string."\n"; } print CLIENTCRAP draw_box("Localize Cache", $text, "cache listing"); } } sub process_reply ($$$$) { my ($server, $args, $sender, $address) = @_; if ($args =~ /^(.*?) (.*?) (.*?) (.*?) (.*?) (.*?) (.*?)/) { if (defined $queries{lc $6} && scalar(@{$queries{lc $6}}) > 0) { foreach (@{$queries{lc $6}}) { my %query = %{$_}; next unless ($query{status} <2); Irssi::signal_stop(); push @{${$_}{buffer}},[$6, $4]; ${$_}{status} = 1; } } elsif (defined $queries{lc $2} && scalar(@{$queries{lc $2}}) > 0) { foreach (@{$queries{lc $2}}) { my %query = %{$_}; next unless ($query{status} <2); Irssi::signal_stop(); push @{${$_}{buffer}},[$6, $4]; ${$_}{status} = 1; } } } elsif ($args =~ /^(.*?) (.*?) :End of (|\/)WHO list\./) { my ($self, $target) = ($1, $2); return unless (defined $queries{lc $target} && scalar(@{$queries{lc $target}}) > 0); my $needed = 0; foreach (@{$queries{lc $target}}) { my %query = %{$_}; $needed = 1 if $query{status} < 2; next unless ($query{status} == 1); if ($query{status} == 1) { Irssi::signal_stop; $query{status} = 2; localize \@{$query{buffer}}, $target; delete $query{buffer}; return(); } } if ($needed) { Irssi::signal_stop; unless ($queries{lc $target}[0]{auto} ne '') { print CLIENTCRAP '%R>>%n No such nick '.$target; } remove_request($target); } } } sub event_message_join ($$$$) { my ($server, $channel, $nick, $address) = @_; return() unless Irssi::settings_get_bool('localize_auto_localize_on_join'); my $maxreq = Irssi::settings_get_int('localize_auto_localize_maxrequests'); my $channels = Irssi::settings_get_str('localize_auto_localize_channel_list'); if ($channel =~ /$channels/i) { $address =~ /(.*)@(.*)/; my $host = $2; if ($procs < $maxreq) { new_request($server, $nick, 2, lc($channel)); localize([[lc($nick), $host]], lc($nick)); } else { #Irssi::print "%R>>%n Too many processes running"; } } } sub event_query_created($$) { my ($query, $auto) = @_; my $nick = $query->{name}; my $server = $query->{server}; my $maxreq = Irssi::settings_get_int('localize_auto_localize_maxrequests'); return(0) unless (scalar(keys %queries) < $maxreq && Irssi::settings_get_bool('localize_auto_localize_on_query')); $nick = substr($nick, 1) if (substr($nick, 0, 1) eq '='); new_request($server, $nick, 0, lc($query->{name})); } sub auto_localize ($%) { my ($auto, %location) = @_; my $nick = lc($location{'nick'}); my $channel = Irssi::window_item_find($auto); $channel->printformat(MSGLEVEL_CLIENTCRAP, 'auto_localize', $nick, $location{host}, $location{'city'}, $location{'province'}, $location{'country'}, $location{'map'}) if defined $channel; } sub new_request ($$$$) { my ($server, $nick, $status, $auto) = @_; return unless ref $server; # 0 nothing done # 1 started to fetch hosts # 2 all hosts fetched push(@{$queries{lc $nick}}, {status => $status, auto=>$auto}); $server->command('who '.lc($nick)) if $status == 0; } sub remove_request ($) { my ($nick) = @_; shift @{$queries{$nick}}; delete $queries{$nick} if scalar(@{$queries{$nick}}) == 0; } # Yes, I know tat this i huge sub kfz2location($) { my %trans = ( "rklh"=> "RE", "wstk"=> "Wk", "essn"=> "E", "stgt"=> "S", "ffm" => "F", "mnz" => "MZ", "fra" => "F", "esn" => "E", "dtm" => "DO", "kln" => "K", "dus" => "D", "mue" => "M", "mnch"=> "M", "brln"=> "B", "hmb" => "HH", "brmn"=> "HB", "hmbg"=> "HH", "han" => "H", "kiel"=> "KI", "lpz" => "L", "bln" => "B", "ber" => "B", "mch" => "M", "erf" => "EF", "mdb" => "MD", "nbg" => "N", "hnv" => "H", "dui" => "DU", "mnhm" => "MA", "mhm" => "MA", "flf" => "FL", "lwhf" => "LU", "wue" => "WÜ", "frnk" => "F", "dsdf" => "D", "sgt" => "S", "aug" => "A", "mch" => "M", "ddn" => "DD", "drs" => "DD", "jen" => "J", "che" => "C", "nuremberg" => "N", "weingarten" => "RV", "munich" => "M", "muc" => "M", "goe" => "GÖ", "obhs" => "OB", "dus" => "D", ); my %province = ( 1=>'Baden-Württemberg', 2=>'Bayern', 3=>'Berlin', 4=>'Brandenburg', 5=>'Bremen', 6=>'Hamburg', 7=>'Hessen', 8=>'Mecklenburg-Vorpommern', 9=>'Niedersachsen', 10=>'Nordrhein-Westfalen', 11=>'Rheinland-Pfalz', 12=>'Saarland', 13=>'Sachsen', 14=>'Sachsen-Anhalt', 15=>'Thüringen', 16=>'Schleswig-Holstein' ); my %added = ( "PLA"=>{city=>"Plattling", province=>2}, ); my %de_kfz = ( "A"=>{city=>"Augsburg", province=>2}, "AA"=>{city=>"Ostalbkreis", province=>1}, "AB"=>{city=>"Aschaffenburg", province=>2}, "ABG"=>{city=>"Altenburger Land", province=>15}, "AC"=>{city=>"Aachen", province=>10}, "AE"=>{city=>"Auerbach", province=>13}, "AH"=>{city=>"Ahaus ", province=>10}, "AIB"=>{city=>"Bad Aibling", province=>2}, "AIC"=>{city=>"Aichach-Friedberg", province=>2}, "AK"=>{city=>"Altenkirchen", province=>11}, "AL"=>{city=>"Altena", province=>10}, "ALF"=>{city=>"Alfeld (Leine)", province=>9}, "ALS"=>{city=>"Alsfeld", province=>7}, "ALZ"=>{city=>"Alzenau", province=>2}, "AM"=>{city=>"Amberg", province=>2}, "AN"=>{city=>"Ansbach", province=>2}, "ANA"=>{city=>"Annaberg", province=>13}, "ANG"=>{city=>"Angermünde", province=>4}, "ANK"=>{city=>"Ostvorpommern, Anklam", province=>8}, "AP"=>{city=>"Weimarer-Land", province=>15}, "APD"=>{city=>"Weimarer Land, Apolda", province=>15}, "AR"=>{city=>"Arnsberg", province=>10}, "ARN"=>{city=>"Ilm-Kreis", province=>15}, "ART"=>{city=>"Artern", province=>15}, "AS"=>{city=>"Amberg-Sulzbach", province=>2}, "ASD"=>{city=>"Aschendorf-Hümmling", province=>9}, "ASL"=>{city=>"Aschersleben", province=>14}, "ASZ"=>{city=>"Aue-Schwarzenberg", province=>13}, "AT"=>{city=>"Altentreptow", province=>8}, "AU"=>{city=>"Aue", province=>13}, "AUR"=>{city=>"Aurich", province=>9}, "AW"=>{city=>"Ahrweiler", province=>11}, "AZ"=>{city=>"Alzey", province=>11}, "AZE"=>{city=>"Anhalt-Zerbst", province=>14}, "AÖ"=>{city=>"Altötting", province=>2}, "B"=>{city=>"Berlin", province=>"3"}, "BA"=>{city=>"Bamberg", province=>2}, "BAD"=>{city=>"Baden-Baden", province=>1}, "BAR"=>{city=>"Barnim", province=>4}, "BB"=>{city=>"Böblingen", province=>1}, "BBG"=>{city=>"Bernburg", province=>14}, "BC"=>{city=>"Biberach", province=>1}, "BCH"=>{city=>"Buchen", province=>1}, "BE"=>{city=>"Beckum", province=>10}, "BED"=>{city=>"Brand-Erbisdorf", province=>13}, "BEI"=>{city=>"Beilngries", province=>2}, "BEL"=>{city=>"Belzig", province=>4}, "BER"=>{city=>"Bernau", province=>4}, "BF"=>{city=>"Burgsteinfurt", province=>10}, "BGD"=>{city=>"Berchtesgaden", province=>2}, "BGL"=>{city=>"Berchtesgadener Land", province=>2}, "BH"=>{city=>"Bühl", province=>1}, "BI"=>{city=>"Bielefeld", province=>10}, "BID"=>{city=>"Biedenkopf", province=>7}, "BIN"=>{city=>"Bingen", province=>11}, "BIR"=>{city=>"Birkenfeld", province=>11}, "BIT"=>{city=>"Bitburg", province=>11}, "BIW"=>{city=>"Bischofswerda", province=>13}, "BK"=>{city=>"Backnang", province=>1}, "BKS"=>{city=>"Bernkastel", province=>11}, "BL"=>{city=>"Zollernalbkreis", province=>1}, "BLB"=>{city=>"Bad Berleburg", province=>10}, "BLK"=>{city=>"Burgenlandkreis", province=>14}, "BM"=>{city=>"Erftkreis", province=>10}, "BN"=>{city=>"Bonn", province=>10}, "BNA"=>{city=>"Borna", province=>13}, "BO"=>{city=>"Bochum", province=>10}, "BOG"=>{city=>"Bogen", province=>2}, "BOH"=>{city=>"Bocholt", province=>10}, "BOR"=>{city=>"Borken", province=>10}, "BOT"=>{city=>"Bottrop", province=>10}, "BR"=>{city=>"Bruchsal", province=>1}, "BRA"=>{city=>"Wesermarsch", province=>9}, "BRB"=>{city=>"Brandenburg", province=>4}, "BRG"=>{city=>"Burg", province=>14}, "BRI"=>{city=>"Brilon", province=>10}, "BRK"=>{city=>"Bad Brückenau", province=>2}, "BRL"=>{city=>"Braunlage", province=>9}, "BRV"=>{city=>"Bremervörde", province=>9}, "BS"=>{city=>"Braunschweig", province=>9}, "BSB"=>{city=>"Bersenbrück", province=>9}, "BSK"=>{city=>"Beeskow", province=>4}, "BT"=>{city=>"Bayreuth", province=>2}, "BTF"=>{city=>"Bitterfeld", province=>14}, "BU"=>{city=>"Burgdorf", province=>9}, "BUL"=>{city=>"Burglengenfeld", province=>2}, "BZ"=>{city=>"Bautzen", province=>13}, "BZA"=>{city=>"Bergzabern", province=>11}, "BÖ"=>{city=>"Bördekreis", province=>14}, "BÜD"=>{city=>"Büdingen", province=>7}, "BÜR"=>{city=>"Büren", province=>10}, "BÜS"=>{city=>"Büsingen", province=>1}, "BÜZ"=>{city=>"Bützow", province=>8}, "C"=>{city=>"Chemnitz", province=>13}, "CA"=>{city=>"Calau", province=>4}, "CAS"=>{city=>"Castrop-Rauxel", province=>10}, "CB"=>{city=>"Cottbus", province=>4}, "CE"=>{city=>"Celle", province=>9}, "CHA"=>{city=>"Cham", province=>2}, "CLP"=>{city=>"Cloppenburg", province=>9}, "CLZ"=>{city=>"Clausthal-Zellerfeld", province=>9}, "CO"=>{city=>"Coburg", province=>2}, "COC"=>{city=>"Cochem-Zell", province=>11}, "COE"=>{city=>"Coesfeld", province=>10}, "CR"=>{city=>"Crailsheim", province=>1}, "CUX"=>{city=>"Cuxhaven", province=>9}, "CW"=>{city=>"Calw", province=>1}, "D"=>{city=>"Düsseldorf", province=>10}, "DA"=>{city=>"Darmstadt", province=>7}, "DAH"=>{city=>"Dachau ", province=>2}, "DAN"=>{city=>"Lüchow-Dannenberg", province=>9}, "DAU"=>{city=>"Daun", province=>11}, "DBR"=>{city=>"Bad Doberan", province=>8}, "DD"=>{city=>"Dresden", province=>13}, "DE"=>{city=>"Dessau", province=>14}, "DEG"=>{city=>"Deggendorf", province=>2}, "DEL"=>{city=>"Delmenhorst", province=>9}, "DGF"=>{city=>"Dingolfing-Landau", province=>2}, "DH"=>{city=>"Diepholz", province=>9}, "DI"=>{city=>"Dieburg", province=>7}, "DIL"=>{city=>"Dillenburg", province=>7}, "DIN"=>{city=>"Dinslaken", province=>10}, "DIZ"=>{city=>"Diez", province=>11}, "DKB"=>{city=>"Dinkelsbühl", province=>2}, "DL"=>{city=>"Döbeln", province=>13}, "DLG"=>{city=>"Dillingen a. d. Donau", province=>2}, "DM"=>{city=>"Demmin", province=>8}, "DN"=>{city=>"Düren", province=>10}, "DO"=>{city=>"Dortmund", province=>10}, "DON"=>{city=>"Donau-Ries", province=>2}, "DS"=>{city=>"Donaueschingen", province=>1}, "DT"=>{city=>"Detmold", province=>10}, "DU"=>{city=>"Duisburg", province=>10}, "DUD"=>{city=>"Duderstadt", province=>9}, "DW"=>{city=>"Weißeritzkreis", province=>13}, "DZ"=>{city=>"Delitzsch", province=>13}, "DÜW"=>{city=>"Bad Dürkheim", province=>11}, "E"=>{city=>"Essen", province=>10}, "EA"=>{city=>"Eisenach, Stadt", province=>15}, "EB"=>{city=>"Eilenburg", province=>13}, "EBE"=>{city=>"Ebersberg", province=>2}, "EBN"=>{city=>"Ebern", province=>2}, "EBS"=>{city=>"Ebermannstadt", province=>2}, "ECK"=>{city=>"Eckernförde", province=>16}, "ED"=>{city=>"Erding", province=>2}, "EE"=>{city=>"Elbe-Elster", province=>4}, "EF"=>{city=>"Erfurt", province=>15}, "EG"=>{city=>"Eggenfelden", province=>2}, "EH"=>{city=>"Eisenhüttenstadt", province=>4}, "EHI"=>{city=>"Ehingen", province=>1}, "EI"=>{city=>"Eichstätt", province=>2}, "EIC"=>{city=>"Eichsfeld", province=>15}, "EIH"=>{city=>"Eichstätt-Kreis", province=>2}, "EIL"=>{city=>"Eisleben", province=>14}, "EIN"=>{city=>"Einbeck", province=>9}, "EIS"=>{city=>"Saale-Holzlandkreis, Eisenberg", province=>15}, "EL"=>{city=>"Emsland", province=>9}, "EM"=>{city=>"Emmendingen", province=>1}, "EMD"=>{city=>"Emden", province=>9}, "EMS"=>{city=>"Rhein-Lahn-Kreis", province=>11}, "EN"=>{city=>"Ennepe-Ruhr-Kreis", province=>10}, "ER"=>{city=>"Erlangen", province=>2}, "ERB"=>{city=>"Odenwaldkreis", province=>7}, "ERH"=>{city=>"Erlangen-Höchstadt", province=>2}, "ERK"=>{city=>"Erkelenz", province=>10}, "ES"=>{city=>"Esslingen", province=>1}, "ESA"=>{city=>"Eisenach", province=>15}, "ESB"=>{city=>"Eschenbach i.d.Oberpfalz", province=>2}, "ESW"=>{city=>"Werra-Meißner-Kreis", province=>7}, "EU"=>{city=>"Euskirchen", province=>10}, "EUT"=>{city=>"Eutin", province=>16}, "EW"=>{city=>"Eberswalde", province=>4}, "F"=>{city=>"Frankfurt am Main", province=>7}, "FAL"=>{city=>"Fallingbostel", province=>9}, "FB"=>{city=>"Wetteraukreis", province=>7}, "FD"=>{city=>"Fulda", province=>7}, "FDB"=>{city=>"Friedberg", province=>2}, "FDS"=>{city=>"Freudenstadt", province=>1}, "FEU"=>{city=>"Feuchtwangen", province=>2}, "FF"=>{city=>"Frankfurt / Oder", province=>4}, "FFB"=>{city=>"Fürstenfeldbruck", province=>2}, "FG"=>{city=>"Freiberg", province=>13}, "FH"=>{city=>"Frankfurt / Main-Höchst", province=>7}, "FI"=>{city=>"Finsterwalde", province=>4}, "FKB"=>{city=>"Frankenberg", province=>7}, "FL"=>{city=>"Flensburg", province=>16}, "FLÖ"=>{city=>"Flöha", province=>13}, "FN"=>{city=>"Bodenseekreis", province=>1}, "FO"=>{city=>"Forchheim", province=>2}, "FOR"=>{city=>"Forst", province=>4}, "FR"=>{city=>"Freiburg", province=>1}, "FRG"=>{city=>"Freyung-Grafenau", province=>2}, "FRI"=>{city=>"Friesland", province=>9}, "FRW"=>{city=>"Bad Freienwalde", province=>4}, "FS"=>{city=>"Freising", province=>2}, "FT"=>{city=>"Frankenthal", province=>11}, "FTL"=>{city=>"Freital", province=>13}, "FW"=>{city=>"Fürstenwalde", province=>4}, "FZ"=>{city=>"Fritzlar", province=>7}, "FÜ"=>{city=>"Fürth", province=>2}, "FÜS"=>{city=>"Füssen", province=>2}, "G"=>{city=>"Gera", province=>15}, "GA"=>{city=>"Gardelegen", province=>14}, "GAN"=>{city=>"Bad Gandersheim", province=>9}, "GAP"=>{city=>"Garmisch-Partenkirchen", province=>2}, "GC"=>{city=>"Chemnitzer Land", province=>13}, "GD"=>{city=>"Schwäbisch Gmünd", province=>1}, "GDB"=>{city=>"Gadebusch", province=>8}, "GE"=>{city=>"Gelsenkirchen", province=>10}, "GEL"=>{city=>"Geldern", province=>10}, "GEM"=>{city=>"Gemünden a.Main", province=>2}, "GEO"=>{city=>"Gerolzhofen", province=>2}, "GER"=>{city=>"Germersheim", province=>11}, "GF"=>{city=>"Gifhorn", province=>9}, "GG"=>{city=>"Groß-Gerau", province=>7}, "GHA"=>{city=>"Geithain", province=>13}, "GHC"=>{city=>"Gräfenhainichen", province=>14}, "GI"=>{city=>"Gießen", province=>7}, "GK"=>{city=>"Geilenkirchen-Heinsberg", province=>10}, "GL"=>{city=>"Rheinisch-Bergischer Kreis", province=>10}, "GLA"=>{city=>"Gladbeck", province=>10}, "GM"=>{city=>"Oberbergischer Kreis", province=>10}, "GMN"=>{city=>"Grimmen", province=>8}, "GN"=>{city=>"Gelnhausen", province=>7}, "GNT"=>{city=>"Genthin", province=>14}, "GOA"=>{city=>"St. Goar", province=>11}, "GOH"=>{city=>"St. Goarshausen", province=>11}, "GP"=>{city=>"Göppingen", province=>1}, "GR"=>{city=>"Görlitz", province=>13}, "GRA"=>{city=>"Grafenau", province=>2}, "GRH"=>{city=>"Großenhain", province=>13}, "GRI"=>{city=>"Griesbach i. Rottal", province=>2}, "GRM"=>{city=>"Grimma", province=>13}, "GRS"=>{city=>"Gransee", province=>4}, "GRZ"=>{city=>"Greiz", province=>15}, "GS"=>{city=>"Goslar", province=>9}, "GT"=>{city=>"Gütersloh", province=>10}, "GTH"=>{city=>"Gotha", province=>15}, "GUB"=>{city=>"Guben", province=>4}, "GUN"=>{city=>"Gunzenhausen", province=>2}, "GV"=>{city=>"Grevenbroich", province=>10}, "GVM"=>{city=>"Grevesmühlen", province=>8}, "GW"=>{city=>"Greifswald Land", province=>8}, "GZ"=>{city=>"Günzburg", province=>2}, "GÖ"=>{city=>"Göttingen", province=>9}, "GÜ"=>{city=>"Güstrow", province=>8}, "H"=>{city=>"Hannover", province=>9}, "HA"=>{city=>"Hagen", province=>10}, "HAB"=>{city=>"Hammelburg", province=>2}, "HAL"=>{city=>"Halle", province=>14}, "HAM"=>{city=>"Hamm", province=>10}, "HAS"=>{city=>"Haßberge", province=>2}, "HB"=>{city=>"Bremen", province=>5}, "HBN"=>{city=>"Hildburghausen", province=>15}, "HBS"=>{city=>"Halberstadt", province=>14}, "HC"=>{city=>"Hainichen", province=>13}, "HCH"=>{city=>"Hechingen", province=>1}, "HD"=>{city=>"Rhein-Neckar-Kreis", province=>1}, "HDH"=>{city=>"Heidenheim (Brenz)", province=>1}, "HDL"=>{city=>"Haldensleben", province=>14}, "HE"=>{city=>"Helmstedt", province=>9}, "HEB"=>{city=>"Hersbruck", province=>2}, "HEF"=>{city=>"Hersfeld-Rotenburg", province=>7}, "HEI"=>{city=>"Dithmarschen", province=>16}, "HER"=>{city=>"Herne", province=>10}, "HET"=>{city=>"Hettstedt", province=>14}, "HF"=>{city=>"Herford", province=>10}, "HG"=>{city=>"Hochtaunus-Kreis", province=>7}, "HGN"=>{city=>"Hagenow", province=>8}, "HGW"=>{city=>"Greifswald", province=>8}, "HH"=>{city=>"Hamburg", province=>6}, "HHM"=>{city=>"Hohenmölsen", province=>14}, "HI"=>{city=>"Hildesheim", province=>9}, "HIG"=>{city=>"Eichsfeld, Heiligenstadt", province=>15}, "HIP"=>{city=>"Hilpoltstein", province=>2}, "HL"=>{city=>"Lübeck", province=>16}, "HM"=>{city=>"Hameln-Pyrmont", province=>9}, "HMÜ"=>{city=>"Hann. Münden", province=>9}, "HN"=>{city=>"Heilbronn", province=>1}, "HO"=>{city=>"Hof", province=>2}, "HOG"=>{city=>"Hofgeismar", province=>7}, "HOH"=>{city=>"Hofheim i. Ufr.", province=>2}, "HOL"=>{city=>"Holzminden", province=>9}, "HOM"=>{city=>"Saarpfalz-Kreis", province=>12}, "HOR"=>{city=>"Horb", province=>1}, "HOT"=>{city=>"Hohenstein-Ernstthal", province=>13}, "HP"=>{city=>"Bergstraße", province=>7}, "HR"=>{city=>"Schwalm-Eder-Kreis", province=>7}, "HRO"=>{city=>"Rostock", province=>8}, "HS"=>{city=>"Heinsberg", province=>10}, "HSK"=>{city=>"Hochsauerland-Kreis", province=>10}, "HST"=>{city=>"Stralsund", province=>8}, "HU"=>{city=>"Main-Kinzig-Kreis", province=>7}, "HUS"=>{city=>"Husum", province=>16}, "HV"=>{city=>"Havelberg", province=>14}, "HVL"=>{city=>"Havelland", province=>4}, "HW"=>{city=>"Halle/Westfalen", province=>10}, "HWI"=>{city=>"Wismar", province=>8}, "HX"=>{city=>"Höxter", province=>10}, "HY"=>{city=>"Hoyerswerda", province=>13}, "HZ"=>{city=>"Herzberg", province=>4}, "HÖS"=>{city=>"Höchstadt a. d. Aisch", province=>2}, "HÜN"=>{city=>"Hünfeld", province=>7}, "IGB"=>{city=>"St. Ingbert", province=>12}, "IK"=>{city=>"Ilm-Kreis", province=>15}, "IL"=>{city=>"Ilmenau", province=>15}, "ILL"=>{city=>"Illertissen", province=>2}, "IN"=>{city=>"Ingolstadt", province=>2}, "IS"=>{city=>"Iserlohn", province=>10}, "IZ"=>{city=>"Steinburg", province=>16}, "J"=>{city=>"Jena", province=>15}, "JB"=>{city=>"Jüterbog", province=>4}, "JE"=>{city=>"Jessen", province=>14}, "JEV"=>{city=>"Jever", province=>9}, "JL"=>{city=>"Jerichower Land", province=>14}, "JÜL"=>{city=>"Jülich", province=>10}, "K"=>{city=>"Köln", province=>10}, "KA"=>{city=>"Karlsruhe", province=>1}, "KAR"=>{city=>"Karlstadt", province=>2}, "KB"=>{city=>"Waldeck-Frankenberg", province=>7}, "KC"=>{city=>"Kronach", province=>2}, "KE"=>{city=>"Kempten", province=>2}, "KEH"=>{city=>"Kelheim", province=>2}, "KEL"=>{city=>"Kehl", province=>1}, "KEM"=>{city=>"Kemnath", province=>2}, "KF"=>{city=>"Kaufbeuren", province=>2}, "KG"=>{city=>"Bad Kissingen", province=>2}, "KH"=>{city=>"Bad Kreuznach", province=>11}, "KI"=>{city=>"Kiel", province=>16}, "KIB"=>{city=>"Donnersberg-Kreis", province=>11}, "KK"=>{city=>"Kempen-Krefeld", province=>10}, "KL"=>{city=>"Kaiserslautern", province=>11}, "KLE"=>{city=>"Kleve", province=>10}, "KLZ"=>{city=>"Klötze", province=>14}, "KM"=>{city=>"Kamenz", province=>13}, "KN"=>{city=>"Konstanz", province=>1}, "KO"=>{city=>"Koblenz", province=>11}, "KR"=>{city=>"Krefeld", province=>10}, "KRU"=>{city=>"Krumbach", province=>2}, "KS"=>{city=>"Kassel", province=>7}, "KT"=>{city=>"Kitzingen", province=>2}, "KU"=>{city=>"Kulmbach", province=>2}, "KUS"=>{city=>"Kusel", province=>11}, "KW"=>{city=>"Königs-Wusterhausen", province=>4}, "KY"=>{city=>"Kyritz", province=>4}, "KYF"=>{city=>"Kyffhäuserkreis", province=>15}, "KÖN"=>{city=>"Bad Königshofen i. Grabfeld", province=>2}, "KÖT"=>{city=>"Köthen", province=>14}, "KÖZ"=>{city=>"Kötzting", province=>2}, "KÜN"=>{city=>"Hohenlohekreis", province=>1}, "L"=>{city=>"Leipzig / Leipziger Land", province=>13}, "LA"=>{city=>"Landshut", province=>2}, "LAN"=>{city=>"Landau a.d.Isar", province=>2}, "LAT"=>{city=>"Lauterbach", province=>7}, "LAU"=>{city=>"Nürnberger Land", province=>2}, "LB"=>{city=>"Ludwigsburg", province=>1}, "LBS"=>{city=>"Lobenstein", province=>15}, "LBZ"=>{city=>"Lübz", province=>8}, "LC"=>{city=>"Luckau", province=>4}, "LD"=>{city=>"Landau i. d. Pfalz", province=>11}, "LDK"=>{city=>"Lahn-Dill-Kreis", province=>7}, "LDS"=>{city=>"Dahme-Spreewald", province=>4}, "LE"=>{city=>"Lemgo", province=>10}, "LEO"=>{city=>"Leonberg", province=>1}, "LER"=>{city=>"Leer", province=>9}, "LEV"=>{city=>"Leverkusen", province=>10}, "LF"=>{city=>"Laufen", province=>2}, "LG"=>{city=>"Lüneburg", province=>9}, "LH"=>{city=>"Lüdinghausen", province=>10}, "LI"=>{city=>"Lindau", province=>2}, "LIB"=>{city=>"Bad Liebenwerda", province=>4}, "LIF"=>{city=>"Lichtenfels", province=>2}, "LIN"=>{city=>"Lingen", province=>9}, "LIP"=>{city=>"Lippe", province=>10}, "LK"=>{city=>"Lübbecke", province=>10}, "LL"=>{city=>"Landsberg am Lech", province=>2}, "LM"=>{city=>"Limburg-Weilburg", province=>7}, "LN"=>{city=>"Lübben", province=>4}, "LOH"=>{city=>"Lohr a.Main", province=>2}, "LOS"=>{city=>"Oder-Spree", province=>4}, "LP"=>{city=>"Lippstadt", province=>10}, "LR"=>{city=>"Lahr", province=>1}, "LSZ"=>{city=>"Bad Langensalza", province=>15}, "LU"=>{city=>"Ludwigshafen", province=>11}, "LUK"=>{city=>"Luckenwalde", province=>4}, "LWL"=>{city=>"Ludwigslust", province=>8}, "LÖ"=>{city=>"Lörrach", province=>1}, "LÖB"=>{city=>"Löbau", province=>13}, "LÜD"=>{city=>"Lüdenscheid, Stadt", province=>10}, "LÜN"=>{city=>"Lünen", province=>10}, "M"=>{city=>"München", province=>2}, "MA"=>{city=>"Mannheim", province=>1}, "MAB"=>{city=>"Marienberg", province=>13}, "MAI"=>{city=>"Mainburg", province=>2}, "MAK"=>{city=>"Marktredwitz", province=>2}, "MAL"=>{city=>"Mallersdorf", province=>2}, "MAR"=>{city=>"Marktheidenfeld", province=>2}, "MB"=>{city=>"Miesbach", province=>2}, "MC"=>{city=>"Malchin", province=>8}, "MD"=>{city=>"Magdeburg", province=>14}, "ME"=>{city=>"Mettmann", province=>10}, "MED"=>{city=>"Meldorf /Suderdithmarschen", province=>16}, "MEG"=>{city=>"Melsungen", province=>7}, "MEI"=>{city=>"Meißen", province=>13}, "MEK"=>{city=>"Mittlerer Erzgebirgskreis", province=>13}, "MEL"=>{city=>"Melle", province=>9}, "MEP"=>{city=>"Meppen", province=>9}, "MER"=>{city=>"Merseburg", province=>14}, "MES"=>{city=>"Meschede", province=>10}, "MET"=>{city=>"Mellrichstadt", province=>2}, "MG"=>{city=>"Mönchengladbach", province=>10}, "MGH"=>{city=>"Bad Mergentheim", province=>1}, "MGN"=>{city=>"Meiningen", province=>15}, "MH"=>{city=>"Mülheim an der Ruhr", province=>"Nordrhein-Westfalen."}, "MHL"=>{city=>"Unstrut-Hainich-Kreis, Mühlhausen", province=>15}, "MI"=>{city=>"Minden", province=>10}, "MIL"=>{city=>"Miltenberg", province=>2}, "MK"=>{city=>"Märkischer Kreis", province=>10}, "ML"=>{city=>"Mansfelder Land", province=>14}, "MM"=>{city=>"Memmingen", province=>2}, "MN"=>{city=>"Unterallgäu", province=>2}, "MO"=>{city=>"Moers", province=>10}, "MOD"=>{city=>"Marktoberdorf", province=>2}, "MOL"=>{city=>"Märkisch-Oderland", province=>4}, "MON"=>{city=>"Monschau", province=>10}, "MOS"=>{city=>"Neckar-Odenwald-Kreis", province=>1}, "MQ"=>{city=>"Merseburg-Querfurt", province=>14}, "MR"=>{city=>"Marburg-Biedenkopf", province=>7}, "MS"=>{city=>"Münster", province=>10}, "MSP"=>{city=>"Main-Spessart", province=>2}, "MST"=>{city=>"Mecklenburg-Strelitz", province=>8}, "MT"=>{city=>"Montabaur", province=>11}, "MTK"=>{city=>"Main-Taunus-Kreis", province=>7}, "MTL"=>{city=>"Muldentalkreis", province=>13}, "MW"=>{city=>"Mittweida", province=>13}, "MY"=>{city=>"Mayen", province=>11}, "MYK"=>{city=>"Mayen-Koblenz", province=>11}, "MZ"=>{city=>"Mainz (-Bingen)", province=>11}, "MZG"=>{city=>"Merzig-Saar", province=>12}, "MÜ"=>{city=>"Mühldorf am Inn", province=>2}, "MÜB"=>{city=>"Münchberg", province=>2}, "MÜL"=>{city=>"Müllheim", province=>1}, "MÜN"=>{city=>"Münsingen", province=>1}, "MÜR"=>{city=>"Müritz", province=>8}, "N"=>{city=>"Nürnberg", province=>2}, "NAB"=>{city=>"Nabburg", province=>2}, "NAI"=>{city=>"Naila", province=>2}, "NAU"=>{city=>"Nauen", province=>4}, "NB"=>{city=>"Neubrandenburg", province=>8}, "ND"=>{city=>"Neuburg-Schrobenhausen", province=>2}, "NDH"=>{city=>"Nordhausen", province=>15}, "NE"=>{city=>"Neuss", province=>10}, "NEA"=>{city=>"Neustadt a. d. Aisch", province=>2}, "NEB"=>{city=>"Nebra", province=>14}, "NEC"=>{city=>"Neustadt b.Coburg", province=>2}, "NEN"=>{city=>"Neunburg vorm Wald", province=>2}, "NES"=>{city=>"Rhön-Grabfeld", province=>2}, "NEU"=>{city=>"Titisee-Neustadt im Schwarzwald", province=>1}, "NEW"=>{city=>"Neustadt an der Waldnaab", province=>2}, "NF"=>{city=>"Nordfriesland", province=>16}, "NH"=>{city=>"Neuhaus am Rennweg", province=>15}, "NI"=>{city=>"Nienburg", province=>9}, "NIB"=>{city=>"Niebüll", province=>16}, "NK"=>{city=>"Neunkirchen", province=>12}, "NM"=>{city=>"Neumarkt", province=>2}, "NMB"=>{city=>"Naumburg", province=>14}, "NMS"=>{city=>"Neumünster", province=>16}, "NOH"=>{city=>"Bentheim", province=>9}, "NOL"=>{city=>"Niederschlesische Oberlausitz", province=>13}, "NOM"=>{city=>"Northeim", province=>9}, "NOR"=>{city=>"Norden", province=>9}, "NP"=>{city=>"Neuruppin", province=>4}, "NR"=>{city=>"Neuwied", province=>11}, "NRÜ"=>{city=>"Neustadt a.Rübenberge", province=>9}, "NT"=>{city=>"Nürtingen", province=>1}, "NU"=>{city=>"Neu-Ulm", province=>2}, "NVP"=>{city=>"Nordvorpommern", province=>8}, "NW"=>{city=>"Neustadt a. d. Weinstraße", province=>11}, "NWM"=>{city=>"Nordwestmecklenburg", province=>8}, "NY"=>{city=>"Niesky", province=>13}, "NZ"=>{city=>"Neustrelitz", province=>8}, "NÖ"=>{city=>"Nördlingen", province=>2}, "OA"=>{city=>"Oberallgäu", province=>2}, "OAL"=>{city=>"Ostallgäu", province=>2}, "OB"=>{city=>"Oberhausen", province=>10}, "OBB"=>{city=>"Obernburg a. Main", province=>2}, "OBG"=>{city=>"Osterburg", province=>14}, "OC"=>{city=>"Oschersleben", province=>14}, "OCH"=>{city=>"Ochsenfurt", province=>2}, "OD"=>{city=>"Stormarn", province=>16}, "OE"=>{city=>"Olpe", province=>10}, "OF"=>{city=>"Offenbach", province=>7}, "OG"=>{city=>"Ortenaukreis", province=>1}, "OH"=>{city=>"Ostholstein", province=>16}, "OHA"=>{city=>"Osterode am Harz", province=>9}, "OHV"=>{city=>"Oberhavel", province=>4}, "OHZ"=>{city=>"Osterholz-Scharmbeck", province=>9}, "OK"=>{city=>"Ohre-Kreis", province=>14}, "OL"=>{city=>"Oldenburg", province=>9}, "OLD"=>{city=>"Oldenburg/Holstein", province=>16}, "OP"=>{city=>"Opladen", province=>10}, "OPR"=>{city=>"Ostprignitz-Ruppin", province=>4}, "OR"=>{city=>"Oranienburg", province=>4}, "OS"=>{city=>"Osnabrück", province=>9}, "OSL"=>{city=>"Oberspreewald-Lausitz", province=>4}, "OTT"=>{city=>"Otterndorf", province=>9}, "OTW"=>{city=>"Ottweiler", province=>12}, "OVI"=>{city=>"Oberviechtach", province=>2}, "OVL"=>{city=>"Obervogtland", province=>13}, "OVP"=>{city=>"Ostvorpommern", province=>8}, "OZ"=>{city=>"Oschatz", province=>13}, "ÖHR"=>{city=>"Öhringen", province=>1}, "P"=>{city=>"Potsdam", province=>4}, "PA"=>{city=>"Passau", province=>2}, "PAF"=>{city=>"Pfaffenhofen", province=>2}, "PAN"=>{city=>"Rottal-Inn", province=>2}, "PAR"=>{city=>"Parsberg", province=>2}, "PB"=>{city=>"Paderborn", province=>10}, "PCH"=>{city=>"Parchim", province=>8}, "PE"=>{city=>"Peine", province=>9}, "PEG"=>{city=>"Pegnitz", province=>2}, "PER"=>{city=>"Perleberg", province=>4}, "PF"=>{city=>"Pforzheim / Enzkreis", province=>1}, "PI"=>{city=>"Pinneberg", province=>16}, "PIR"=>{city=>"Sächsische Schweiz", province=>13}, "PK"=>{city=>"Pritzwalk", province=>4}, "PL"=>{city=>"Plauen", province=>13}, "PLÖ"=>{city=>"Plön", province=>16}, "PM"=>{city=>"Potsdam-Mittelmark", province=>4}, "PN"=>{city=>"Pößneck", province=>15}, "PR"=>{city=>"Prignitz", province=>4}, "PRÜ"=>{city=>"Prüm", province=>11}, "PS"=>{city=>"Pirmasens / Südwestpfalz", province=>11}, "PW"=>{city=>"Pasewalk", province=>8}, "PZ"=>{city=>"Prenzlau", province=>4}, "QFT"=>{city=>"Querfurt", province=>14}, "QLB"=>{city=>"Quedlinburg", province=>14}, "R"=>{city=>"Regensburg", province=>2}, "RA"=>{city=>"Rastatt", province=>1}, "RC"=>{city=>"Reichenbach", province=>13}, "RD"=>{city=>"Rendsburg-Eckernförde", province=>16}, "RDG"=>{city=>"Ribnitz-Damgarten", province=>8}, "RE"=>{city=>"Recklinghausen", province=>10}, "REG"=>{city=>"Regen", province=>2}, "REH"=>{city=>"Rehau", province=>2}, "REI"=>{city=>"Bad Reichenhall", province=>2}, "RG"=>{city=>"Großenhain", province=>13}, "RH"=>{city=>"Roth", province=>2}, "RI"=>{city=>"Rinteln", province=>9}, "RID"=>{city=>"Riedenburg", province=>2}, "RIE"=>{city=>"Riesa", province=>13}, "RL"=>{city=>"Rochlitz", province=>13}, "RM"=>{city=>"Röbel", province=>8}, "RN"=>{city=>"Rathenow", province=>4}, "RO"=>{city=>"Rosenheim", province=>2}, "ROD"=>{city=>"Roding", province=>2}, "ROF"=>{city=>"Rotenburg/Fulda", province=>7}, "ROK"=>{city=>"Rockenhausen", province=>11}, "ROL"=>{city=>"Rottenburg a. d. Laaber", province=>2}, "ROS"=>{city=>"Rostock-Kreis", province=>8}, "ROT"=>{city=>"Rothenburg o.d.Tauber", province=>2}, "ROW"=>{city=>"Rotenburg (Wümme)", province=>9}, "RS"=>{city=>"Remscheid", province=>10}, "RSL"=>{city=>"Roßlau", province=>14}, "RT"=>{city=>"Reutlingen", province=>1}, "RU"=>{city=>"Rudolstadt", province=>15}, "RV"=>{city=>"Ravensburg", province=>1}, "RW"=>{city=>"Rottweil", province=>1}, "RY"=>{city=>"Rheydt", province=>10}, "RZ"=>{city=>"Herzogtum Lauenburg", province=>16}, "RÜD"=>{city=>"Rheingau-Taunus-Kreis", province=>7}, "RÜG"=>{city=>"Rügen", province=>8}, "S"=>{city=>"Stuttgart", province=>1}, "SAB"=>{city=>"Saarburg", province=>11}, "SAD"=>{city=>"Schwandorf in Bayern", province=>2}, "SAN"=>{city=>"Stadtsteinach", province=>2}, "SAW"=>{city=>"Altmarkkreis Salzwedel", province=>14}, "SB"=>{city=>"Saarbrücken", province=>12}, "SBG"=>{city=>"Strasburg", province=>8}, "SBK"=>{city=>"Schönebeck", province=>"Sachsen Anhalt"}, "SC"=>{city=>"Schwabach", province=>2}, "SCZ"=>{city=>"Schleiz", province=>15}, "SDH"=>{city=>"Sondershausen", province=>15}, "SDL"=>{city=>"Stendal", province=>14}, "SDT"=>{city=>"Schwedt", province=>4}, "SE"=>{city=>"Bad Segeberg", province=>16}, "SEB"=>{city=>"Sebnitz", province=>13}, "SEE"=>{city=>"Seelow", province=>4}, "SEF"=>{city=>"Scheinfeld", province=>2}, "SEL"=>{city=>"Selb", province=>2}, "SF"=>{city=>"Sonthofen", province=>2}, "SFA"=>{city=>"Soltau-Fallingbostel", province=>9}, "SFB"=>{city=>"Senftenberg", province=>4}, "SFT"=>{city=>"Staßfurt", province=>14}, "SG"=>{city=>"Solingen", province=>10}, "SGH"=>{city=>"Sangerhausen", province=>14}, "SHA"=>{city=>"Schwäbisch Hall", province=>1}, "SHG"=>{city=>"Schaumburg", province=>9}, "SHK"=>{city=>"Saale-Holzland-Kreis", province=>15}, "SHL"=>{city=>"Suhl", province=>15}, "SI"=>{city=>"Siegen", province=>10}, "SIG"=>{city=>"Sigmaringen", province=>1}, "SIM"=>{city=>"Rhein-Hunsrück-Kreis", province=>11}, "SK"=>{city=>"Saalkreis", province=>14}, "SL"=>{city=>"Schleswig-Flensburg", province=>16}, "SLE"=>{city=>"Schleiden", province=>10}, "SLF"=>{city=>"Saalfeld-Rudolstadt", province=>15}, "SLG"=>{city=>"Saulgau", province=>1}, "SLN"=>{city=>"Schmölln", province=>15}, "SLS"=>{city=>"Saarlouis", province=>12}, "SLZ"=>{city=>"Bad Salzungen", province=>15}, "SLÜ"=>{city=>"Schlüchtern", province=>7}, "SM"=>{city=>"Schmalkalden-Meiningen", province=>15}, "SMÜ"=>{city=>"Schwabmünchen", province=>2}, "SN"=>{city=>"Schwerin", province=>8}, "SNH"=>{city=>"Sinsheim Elsenz", province=>1}, "SO"=>{city=>"Soest", province=>10}, "SOB"=>{city=>"Schrobenhausen", province=>2}, "SOG"=>{city=>"Schongau", province=>2}, "SOK"=>{city=>"Saale-Orla-Kreis", province=>15}, "SOL"=>{city=>"Soltau", province=>9}, "SON"=>{city=>"Sonneberg", province=>15}, "SP"=>{city=>"Speyer", province=>11}, "SPB"=>{city=>"Spremberg", province=>4}, "SPN"=>{city=>"Spree-Neiße", province=>4}, "SPR"=>{city=>"Springe", province=>9}, "SR"=>{city=>"Straubing (-Bogen)", province=>2}, "SRB"=>{city=>"Strausberg", province=>4}, "SRO"=>{city=>"Stadtroda", province=>15}, "ST"=>{city=>"Steinfurt", province=>10}, "STA"=>{city=>"Starnberg", province=>2}, "STB"=>{city=>"Sternberg", province=>8}, "STD"=>{city=>"Stade", province=>9}, "STE"=>{city=>"Staffelstein", province=>2}, "STH"=>{city=>"Schaumburg-Lippe", province=>9}, "STL"=>{city=>"Stollberg", province=>13}, "STO"=>{city=>"Stockach", province=>1}, "SU"=>{city=>"Rhein-Sieg-Kreis", province=>10}, "SUL"=>{city=>"Sulzbach-Rosenberg", province=>2}, "SW"=>{city=>"Schweinfurt", province=>2}, "SWA"=>{city=>"Bad Schwalbach", province=>7}, "SY"=>{city=>"Syke", province=>9}, "SZ"=>{city=>"Salzgitter", province=>9}, "SZB"=>{city=>"Schwarzenberg", province=>13}, "SÄK"=>{city=>"Säckingen", province=>1}, "SÖM"=>{city=>"Sömmerda", province=>15}, "SÜW"=>{city=>"Südliche Weinstraße", province=>11}, "TBB"=>{city=>"Main-Tauber-Kreis", province=>1}, "TE"=>{city=>"Tecklenburg", province=>10}, "TET"=>{city=>"Teterow", province=>8}, "TF"=>{city=>"Teltow-Fläming", province=>4}, "TG"=>{city=>"Torgau", province=>13}, "TIR"=>{city=>"Tirschenreuth", province=>2}, "TO"=>{city=>"Torgau-Oschatz", province=>13}, "TP"=>{city=>"Templin", province=>4}, "TR"=>{city=>"Trier", province=>11}, "TS"=>{city=>"Traunstein", province=>2}, "TT"=>{city=>"Tettnang", province=>1}, "TUT"=>{city=>"Tuttlingen", province=>1}, "TÖL"=>{city=>"Bad Tölz-Wolfratshausen", province=>2}, "TÖN"=>{city=>"Tönning", province=>16}, "TÜ"=>{city=>"Tübingen", province=>1}, "UE"=>{city=>"Uelzen", province=>9}, "UEM"=>{city=>"Ueckermünde", province=>8}, "UER"=>{city=>"Uecker-Randow", province=>8}, "UFF"=>{city=>"Uffenheim", province=>2}, "UH"=>{city=>"Unstrut-Hainich-Kreis", province=>15}, "UL"=>{city=>"Ulm / Alb-Donau-Kreis", province=>1}, "UM"=>{city=>"Uckermark", province=>4}, "UN"=>{city=>"Unna", province=>10}, "USI"=>{city=>"Usingen", province=>7}, "ÜB"=>{city=>"Überlingen", province=>1}, "V"=>{city=>"Vogtlandkreis", province=>13}, "VAI"=>{city=>"Vaihingen", province=>1}, "VB"=>{city=>"Vogelsbergkreis", province=>7}, "VEC"=>{city=>"Vechta", province=>9}, "VER"=>{city=>"Verden", province=>9}, "VIB"=>{city=>"Vilsbiburg", province=>2}, "VIE"=>{city=>"Viersen", province=>10}, "VIT"=>{city=>"Viechtach", province=>2}, "VK"=>{city=>"Völklingen", province=>12}, "VL"=>{city=>"Villingen", province=>1}, "VOF"=>{city=>"Vilshofen", province=>2}, "VOH"=>{city=>"Vohenstrauß", province=>2}, "VS"=>{city=>"Schwarzwald-Baar-Kreis", province=>1}, "W"=>{city=>"Wuppertal", province=>10}, "WA"=>{city=>"Waldeck", province=>7}, "WAF"=>{city=>"Warendorf", province=>10}, "WAK"=>{city=>"Wartburgkreis", province=>15}, "WAM"=>{city=>"Westlicher Altmark-Kreis", province=>14}, "WAN"=>{city=>"Wanne-Eickel", province=>10}, "WAR"=>{city=>"Warburg", province=>10}, "WAT"=>{city=>"Wattenscheid", province=>10}, "WB"=>{city=>"Wittenberg", province=>14}, "WBS"=>{city=>"Worbis", province=>15}, "WD"=>{city=>"Wiedenbrück", province=>10}, "WDA"=>{city=>"Werdau", province=>13}, "WE"=>{city=>"Weimar", province=>15}, "WEB"=>{city=>"Westerburg-Westerwald", province=>11}, "WEG"=>{city=>"Wegscheid", province=>2}, "WEL"=>{city=>"Weilburg", province=>7}, "WEM"=>{city=>"Wesermünde", province=>9}, "WEN"=>{city=>"Weiden", province=>2}, "WER"=>{city=>"Wertingen", province=>2}, "WES"=>{city=>"Wesel", province=>10}, "WF"=>{city=>"Wolfenbüttel", province=>9}, "WG"=>{city=>"Wangen", province=>1}, "WHV"=>{city=>"Wilhelmshaven", province=>9}, "WI"=>{city=>"Wiesbaden", province=>7}, "WIL"=>{city=>"Wittlich", province=>11}, "WIS"=>{city=>"Wismar, Kreis", province=>8}, "WIT"=>{city=>"Witten", province=>10}, "WIZ"=>{city=>"Witzenhausen", province=>7}, "WK"=>{city=>"Wittstock", province=>4}, "WL"=>{city=>"Harburg", province=>9}, "WLG"=>{city=>"Wolgast", province=>8}, "WM"=>{city=>"Weilheim-Schongau", province=>2}, "WMS"=>{city=>"Wolmirstedt", province=>14}, "WN"=>{city=>"Rems-Murr-Kreis", province=>1}, "WND"=>{city=>"St. Wendel", province=>12}, "WO"=>{city=>"Worms", province=>11}, "WOB"=>{city=>"Wolfsburg", province=>9}, "WOH"=>{city=>"Wolfhagen", province=>7}, "WOL"=>{city=>"Wolfach", province=>1}, "WOR"=>{city=>"Wolfratshausen", province=>2}, "WOS"=>{city=>"Wolfstein", province=>2}, "WR"=>{city=>"Wernigerode", province=>14}, "WRN"=>{city=>"Waren", province=>8}, "WS"=>{city=>"Wasserburg a. Inn", province=>2}, "WSF"=>{city=>"Weißenfels", province=>14}, "WST"=>{city=>"Ammerland", province=>9}, "WSW"=>{city=>"Weißwasser", province=>13}, "WT"=>{city=>"Waldshut", province=>1}, "WTL"=>{city=>"Wittlage", province=>9}, "WTM"=>{city=>"Wittmund", province=>9}, "WUG"=>{city=>"Weißenburg-Gunzenhausen", province=>2}, "WUN"=>{city=>"Wunsiedel", province=>2}, "WUR"=>{city=>"Wurzen", province=>13}, "WW"=>{city=>"Westerwald-Kreis", province=>11}, "WZ"=>{city=>"Wetzlar", province=>7}, "WZL"=>{city=>"Wanzleben", province=>14}, "WÜ"=>{city=>"Würzburg", province=>2}, "WÜM"=>{city=>"Waldmünchen", province=>2}, "Z"=>{city=>"Zwickau (-Land)", province=>13}, "ZE"=>{city=>"Zerbst", province=>14}, "ZEL"=>{city=>"Zell / Mosel", province=>11}, "ZI"=>{city=>"Löbau-Zittau", province=>13}, "ZIG"=>{city=>"Ziegenhain", province=>7}, "ZP"=>{city=>"Zschopau", province=>13}, "ZR"=>{city=>"Zeulenroda", province=>15}, "ZS"=>{city=>"Zossen", province=>4}, "ZW"=>{city=>"Zweibrücken", province=>11}, "ZZ"=>{city=>"Zeitz", province=>14} ); my ($key) = @_; $key = $trans{lc $key} if defined $trans{lc $key}; my %location; $key = uc($key); my %base = %de_kfz; if (defined $base{$key}) { $location{country} = 'Germany'; $location{city} = $base{$key}{'city'}; $location{province} = $province{$base{$key}->{province}}; } else { #Irssi::print $key; foreach (keys %base) { my $city = $base{$_}{city}; #$city = lc($city); #$city =~ s/ä/ae/g; #$city =~ s/ü/ue/g; #$city =~ s/ö/oe/g; #$city = uc($city); if ($city =~ /(^| |-)$key( |-|$)/i) { $location{country} = 'Germany'; $location{city} = $base{$_}{city}; $location{province} = $province{$base{$_}{province}}; } } } return %location; } foreach ((352, 315)) { Irssi::signal_add_first('event '.$_, 'process_reply'); } sub pre_unload { save_cache(); } Irssi::signal_add('message join', 'event_message_join'); Irssi::signal_add('query created', 'event_query_created'); Irssi::signal_add('setup saved', 'save_cache'); Irssi::settings_add_bool($IRSSI{'name'}, 'localize_background', 1); Irssi::settings_add_str($IRSSI{'name'}, 'localize_cache_filename', Irssi::get_irssi_dir()."/localize_cache"); Irssi::settings_add_str($IRSSI{'name'}, 'localize_trace_cmd', "/usr/sbin/traceroute -q 1 -w 2 -I"); Irssi::settings_add_int($IRSSI{'name'}, 'localize_trace_distance', 3); Irssi::settings_add_str($IRSSI{'name'}, 'localize_auto_localize_channel_list', '.*'); Irssi::settings_add_bool($IRSSI{'name'}, 'localize_auto_localize_on_join', 0); Irssi::settings_add_bool($IRSSI{'name'}, 'localize_auto_localize_on_query', 1); Irssi::settings_add_int($IRSSI{'name'}, 'localize_auto_localize_maxrequests', 5); Irssi::settings_add_bool($IRSSI{'name'}, 'localize_get_coordinates', 0); Irssi::settings_add_bool($IRSSI{'name'}, 'localize_use_databases', 1); Irssi::settings_add_bool($IRSSI{'name'}, 'localize_use_traceroute', 1); Irssi::settings_add_bool($IRSSI{'name'}, 'localize_show_message', 1); Irssi::settings_add_str($IRSSI{'name'}, 'localize_ipatlas_nocache', '.*\.dip\.t-dialin\.net'); Irssi::settings_add_bool($IRSSI{'name'}, 'localize_xplanet_show_map', 0); Irssi::settings_add_str($IRSSI{'name'}, 'localize_xplanet_temp_file', Irssi::get_irssi_dir()."/localize_xplanet_temp"); Irssi::settings_add_str($IRSSI{'name'}, 'localize_xplanet_cmd', "xplanet -w"); Irssi::theme_register([ auto_localize => '%B`->%n $0 ($1) has been localized in $2, $3, $4 %B[%n$5%B]%n', ]); Irssi::command_bind('localize', 'cmd_localize'); load_cache(); print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded: /localize -h for help';