Guidelines for the Generation of Message IDs and Similar Unique Identifiers
メールに振られる Message-ID のガイドライン。どれだけ普及してるかは調べなかった。
Guidelines for the Generation of Message IDs and Similar Unique Identifiers
素振りのつもりで実装してみたけど、途中で挫折。
理由はなんかどう動くかよく分かんなくなっちゃったから。
そんなときこそ TDD 重要。
sub test { print message_id_based_on_hosts_fqdn(); print message_id_based_on_email_address("yuji-okazawa@teraintl.co.jp", ["crypt", "md5", "sha1"]); print message_id_based_on_login_name_and_fqdn(["crypt", "md5", "sha1"]); } my $OBSOLETE_MODE = "1"; sub message_id_based_on_hosts_fqdn { my $result = ""; my $fqdn = _fqdn(); if ($fqdn) { $result = sprintf("%s@%s", _unique(), $fqdn); } return $result; } sub message_id_based_on_email_address { my ($addr_spec, $array_ref) = @_; my $result = ""; my ($local_part) = ($addr_spec =~ m/^([^@]+)@.*$/); my ($hash_name, $hash_value) = _hash($array_ref, $local_part); if ($hash_name && $hash_value) { $result = sprintf("%s%%%s%%%s", _unique(), $hash_name, $addr_spec); } else { $result = sprintf("%s%%%s", _unique(), $addr_spec); } return $result; } sub message_id_based_on_login_name_and_fqdn { my ($array_ref) = @_; my $login = (getpwuid($<))[0] || $<; my $fqdn = _fqdn(); my $result = ""; if ($fqdn) { my ($hash_name, $hash_value) = _hash($array_ref, $login); if ($hash_name && $hash_value) { $result = sprintf("%s%%%s%%%s@%s", _unique(), $hash_name, $login, $fqdn); } else { $result = sprintf("%s%%%s@%s", _unique(), $login, $fqdn); } } return $result; } sub _unique { my $seconds = eval { use Time::Hires qw(time); return time(); }; if ($@) { $seconds = time(); } return join( map {_base_n_encode(80, $_)} (time(), $$), "."); } sub _base_n_encode { my ($n, $v) = @_; my @unique_chars = ("A".."Z", "a".."z", "1".."9" , "\!", "\#", "\$", "\&", "\'", "*", "+", "-" , "/", "=", "?", "^", "_", "`", "{", "|" , "}", "~"); my @bits = split(//, unpack("B*", pack("H*", $v))); my $bit_width = 0; for (my $i = 0; ($i < 32) && ($bit_width == 0); $i++) { if ($v <= (2**$i)) { $bit_width = ($i - 1); } } my $result = ""; for (my $pos = 0; $pos < scalar(@{$ref}); $pos += $bit_width) { $result .= $unique_chars[int(join("", @{$ref}[$pos..($pos+$bit_width)]))] } return $result; } sub _fqdn { my $result = eval { use Sys::Hostname::FQDN qw(fqdn); return fqdn(); }; if ($@) { warn $@; if ($OBSOLETE_MODE) { $result = eval { use IO::Interface::Simple; for my $iface (IO::Interface::Simple->interfaces) { if ($iface->address !~ m/^(?:10\.|172.16\.|192.168.).*/) { return sprintf "[%s]", $iface->address; } } }; warn $@ if $@; } } return $result; } sub _hash { my ($ref, $v) = @_; for my $hname (@{$ref}) { if ($hname == "crypt") { my $salt = join("", ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]); return crypt($v, $salt); } elsif ($hname == "md5") { use Digest::MD5 qw(md5_base64); return md5_base64($v); } elsif ($hname == "sha1") { use Digest::SHA1 qw(sha1_base64); return sha1_base64($v); } else { warn "unknown hash function: $hname"; } } } 1;