dip/vendor/github.com/likexian/whois-parser/prepare.go
Paul Lecuq fa74f7ad4c
All checks were successful
continuous-integration/drone/push Build is passing
updated dependencies
2024-04-28 13:08:41 +02:00

1439 lines
31 KiB
Go

/*
* Copyright 2014-2024 Li Kexian
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Go module for domain whois information parsing
* https://www.likexian.com/
*/
package whoisparser
import (
"fmt"
"regexp"
"strings"
"github.com/likexian/gokit/assert"
"github.com/likexian/gokit/xslice"
)
// Prepare do prepare the whois info for parsing
func Prepare(text, ext string) (string, bool) { //nolint:cyclop
text = strings.Replace(text, "\r", "", -1)
text = strings.Replace(text, "\t", " ", -1)
text = strings.TrimSpace(text)
switch ext {
case "":
return prepareTLD(text), true
case "edu":
return prepareEDU(text), true
case "int":
return prepareINT(text), true
case "mo":
return prepareMO(text), true
case "hk":
return prepareHK(text), true
case "tw":
return prepareTW(text), true
case "ch":
return prepareCH(text), true
case "it":
return prepareIT(text), true
case "fr", "re", "tf", "yt", "pm", "wf":
return prepareFR(text), true
case "ru", "su", "xn--p1ai":
return prepareRU(text), true
case "fi":
return prepareFI(text), true
case "jp":
return prepareJP(text), true
case "uk":
return prepareUK(text), true
case "kr":
return prepareKR(text), true
case "nz":
return prepareNZ(text), true
case "tk":
return prepareTK(text), true
case "nl":
return prepareNL(text), true
case "eu":
return prepareEU(text), true
case "br":
return prepareBR(text), true
case "ir", "xn--mgba3a4f16a":
return prepareIR(text), true
case "rs":
return prepareRS(text), true
case "kz":
return prepareKZ(text), true
case "ee":
return prepareEE(text), true
case "cn", "xn--fiqs8s", "xn--fiqz9s":
return prepareCN(text), true
case "pl":
return preparePL(text), true
case "dk":
return prepareDK(text), true
case "by":
return prepareBY(text), true
case "ua":
return prepareUA(text), true
case "at":
return prepareAT(text), true
default:
return text, false
}
}
// prepareTLD do prepare the tld domain
func prepareTLD(text string) string {
token := ""
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
token = ""
continue
}
if strings.Contains(v, ":") {
vs := strings.Split(v, ":")
if strings.TrimSpace(vs[0]) == "organisation" {
if token == "" {
token = "registrant"
}
}
if strings.TrimSpace(vs[0]) == "contact" {
token = strings.TrimSpace(vs[1])
} else {
if token != "" {
v = fmt.Sprintf("%s %s", token, v)
}
}
}
result += "\n" + v
}
return result
}
// prepareEDU do prepare the .edu domain
func prepareEDU(text string) string {
tokens := map[string][]string{
"Registrant:": {
"Organization",
"Address",
"Phone",
"Email",
},
"Administrative Contact:": {
"Name",
"Organization",
"Address",
"Phone",
"Email",
},
"Technical Contact:": {
"Name",
"Organization",
"Address",
"Phone",
"Email",
},
}
token := ""
index := 0
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
if strings.HasSuffix(v, ":") {
token = ""
index = 0
}
if _, ok := tokens[v]; ok {
token = v
} else {
if token == "" {
result += "\n" + v
} else {
// address ending now jump to phone
if tokens[token][index] == "Address" && strings.HasPrefix(v, "+") {
found := xslice.Index(tokens[token], "Phone")
if found != -1 {
index = found
}
}
result += fmt.Sprintf("\n%s %s: %s", token[:len(token)-1], tokens[token][index], v)
if tokens[token][index] != "Address" {
index++
}
}
}
}
return result
}
// prepareINT do prepare the .int domain
func prepareINT(text string) string {
token := ""
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
token = ""
continue
}
if strings.Contains(v, ":") {
vs := strings.Split(v, ":")
if strings.TrimSpace(vs[0]) == "organisation" {
if token == "" {
token = "registrant"
}
}
if strings.TrimSpace(vs[0]) == "contact" {
token = strings.TrimSpace(vs[1])
} else {
if token != "" {
v = fmt.Sprintf("%s %s", token, v)
}
}
}
result += "\n" + v
}
return result
}
// prepareKZ do prepare the .kz domain
func prepareKZ(text string) string {
groupTokens := map[string]string{
"Organization Using Domain Name": "Registrant ",
"Administrative Contact/Agent": "Administrative ",
}
topTokens := map[string]string{
"Domain status": "Domain status : ",
}
tokens := map[string]string{
"Primary server": "name server",
"Secondary server": "name server",
"Current Registar": "Registrar Name",
}
groupToken := ""
topToken := ""
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
groupToken = ""
continue
}
if token, ok := groupTokens[v]; ok {
groupToken = token
continue
}
if !strings.Contains(v, ":") {
if topToken != "" {
v = fmt.Sprintf("%s%s", topToken, v)
} else {
continue
}
}
vs := strings.SplitN(v, ":", 2)
key := strings.TrimSpace(strings.Replace(vs[0], ".", "", -1))
key = fmt.Sprintf("%s%s", groupToken, key)
if token, ok := tokens[key]; ok {
key = token
}
value := vs[1]
if token, ok := topTokens[key]; ok {
topToken = token
} else {
topToken = ""
}
v = fmt.Sprintf("%s: %s", key, value)
result += "\n" + v
}
return result
}
// prepareMO do prepare the .mo domain
func prepareMO(text string) string {
tokens := map[string]string{
"Registrant:": "Registrant",
"Admin Contact(s):": "Admin",
"Billing Contact(s):": "Billing",
"Technical Contact(s):": "Technical",
}
token := ""
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
token = ""
continue
}
if v[0] == '-' {
continue
}
for _, s := range []string{"Record created on", "Record expires on"} {
if strings.HasPrefix(v, s) {
v = strings.Replace(v, s, s+":", 1)
}
}
if _, ok := tokens[v]; ok {
token = tokens[v]
} else {
if token != "" {
v = fmt.Sprintf("%s %s", token, v)
}
}
result += "\n" + v
}
return result
}
var prepareHKEmailRx = regexp.MustCompile(`Email\:\s+([^\s]+)(\s+Hotline\:(.*))?`)
// prepareHK do prepare the .hk domain
func prepareHK(text string) string {
tokens := map[string]string{
"Registrant Contact Information:": "Registrant",
"Administrative Contact Information:": "Admin",
"Technical Contact Information:": "Technical",
"Name Servers Information:": "Name Servers:",
}
dateTokens := []string{
"Domain Name Commencement Date",
"Expiry Date",
}
token := ""
addressToken := false
text = strings.Replace(text, "\n\n", "\n", -1)
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
token = ""
continue
}
field := ""
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
field = strings.TrimSpace(vs[0])
if strings.Contains(field, "(") {
field = strings.Split(field, "(")[0]
v = fmt.Sprintf("%s: %s", field, vs[1])
}
addressToken = field == "Address"
if field == "Registrar Contact Information" {
m := prepareHKEmailRx.FindStringSubmatch(vs[1])
if len(m) == 4 {
v = ""
if m[1] != "" {
v += "Registrar Contact Email: " + m[1] + "\n"
}
if m[3] != "" {
v += "Registrar Contact Phone: " + m[3] + "\n"
}
v = strings.TrimSpace(v)
}
}
if field == "Family name" {
vv := strings.TrimSpace(vs[1])
if vv != "" && vv != "." {
result += " " + vv
}
continue
}
} else {
if addressToken {
result += ", " + v
continue
}
}
if _, ok := tokens[v]; ok {
token = tokens[v]
} else {
if token != "" && !assert.IsContains(dateTokens, field) {
v = fmt.Sprintf("%s %s", token, v)
}
}
result += "\n" + v
}
return result
}
var prepareTWEmailRx = regexp.MustCompile(`(.*)\s+([^\s]+@[^\s]+)`)
// prepareTW do prepare the .tw domain
func prepareTW(text string) string { //nolint:cyclop
tokens := map[string][]string{
"Registrant:": {
"Organization",
"Organization",
"Name,Email",
"Phone",
"Fax",
"Address",
"Address",
"Address",
},
"Administrative Contact:": {
"Name,Email",
"Phone",
"Fax",
},
"Technical Contact:": {
"Name,Email",
"Phone",
"Fax",
},
"Contact:": {
"Name",
"Email",
},
}
token := ""
index := -1
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if token == "" && v == "" {
continue
}
for _, s := range []string{"Record created on", "Record expires on"} {
if strings.HasPrefix(v, s) {
v = strings.Replace(v, s, s+":", 1)
}
}
if strings.Contains(v, ":") {
token = ""
index = -1
}
if _, ok := tokens[v]; ok {
token = v
} else {
if token == "" {
result += "\n" + v
} else {
index++
if index > len(tokens[token])-1 {
continue
}
tokenName := token[:len(token)-1]
// Organization may be one line or two lines
if tokenName == "Registrant" && tokens[token][index] == "Organization" {
if strings.Contains(v, "@") {
// Organization one line, jump to next
index++
} else if index == 1 {
// Organization two line, join it
result = strings.TrimSpace(result)
if !strings.HasSuffix(result, ":") {
result += ", " + v
} else {
result += " " + v
}
continue
}
}
// See testdata/noterror/tw_git.tw
if tokenName == "Registrant" && tokens[token][index] != "Address" {
if len(v) == 2 && strings.ToLower(v) != v {
index = xslice.Index(tokens[token], "Address")
}
}
indexName := tokens[token][index]
if tokenName == "Contact" {
tokenName = "Registrant Contact"
}
if strings.Contains(indexName, ",") {
ins := strings.Split(indexName, ",")
m := prepareTWEmailRx.FindStringSubmatch(v)
if len(m) == 3 {
result += fmt.Sprintf("\n%s %s: %s", tokenName, ins[0], strings.TrimSpace(m[1]))
result += fmt.Sprintf("\n%s %s: %s", tokenName, ins[1], strings.TrimSpace(m[2]))
} else {
result += fmt.Sprintf("\n%s %s: %s", tokenName, ins[0], strings.TrimSpace(v))
}
continue
}
result += fmt.Sprintf("\n%s %s: %s", tokenName, indexName, v)
}
}
}
return result
}
// prepareCH do prepare the .ch domain
func prepareCH(text string) string {
phoneMark := "Phone +"
tokens := map[string][]string{
"Domain name": {},
"Registrar": {"Registrar name", "Registrar street", "Registrar Phone", "Registrar Email"},
"DNSSEC": {},
"Name servers": {},
"First registration date": {},
}
var result string
var lastToken string
var lastTokenIndex int
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
for t := range tokens {
if strings.HasPrefix(strings.ToLower(v)+" ", strings.ToLower(t+" ")) {
lastToken = t
lastTokenIndex = 0
v = strings.TrimSpace(v[len(t):])
break
}
}
if v == "" {
continue
}
if len(tokens[lastToken]) > 0 {
if strings.HasPrefix(v, phoneMark) {
lastTokenIndex++
v = strings.TrimSpace(v[len(phoneMark)-1:])
}
result += fmt.Sprintf("\n%s: %s", tokens[lastToken][lastTokenIndex], v)
if tokens[lastToken][lastTokenIndex] != "Registrar street" {
lastTokenIndex++
}
} else {
result += fmt.Sprintf("\n%s: %s", lastToken, v)
}
}
return result
}
// prepareIT do prepare the .it domain
func prepareIT(text string) string {
topTokens := []string{
"Registrant",
"Admin Contact",
"Technical Contacts",
"Registrar",
"Nameservers",
}
topToken := ""
subToken := ""
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
if assert.IsContains(topTokens, v) {
topToken = v + " "
subToken = ""
} else {
if v[0] != '*' && strings.Contains(v, ":") {
vs := strings.Split(v, ":")
subToken = vs[0]
} else {
if subToken != "" {
result += ", " + v
continue
}
}
if topToken != "" && !strings.Contains(v, ":") {
result += fmt.Sprintf("\n%s: %s", topToken, v)
} else {
result += fmt.Sprintf("\n%s%s", topToken, v)
}
}
}
return result
}
// prepareFR do prepare the .fr domain
func prepareFR(text string) string {
dsToken := "dsl-id"
hdlToken := "nic-hdl"
regToken := "registrar"
tokens := map[string]string{
"holder-c": "holder",
"admin-c": "admin",
"tech-c": "tech",
}
token := ""
newBlock := false
hdls := map[string]string{}
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
newBlock = true
continue
}
vs := strings.Split(v, ":")
if newBlock && strings.TrimSpace(vs[0]) == regToken {
token = regToken + " "
v = fmt.Sprintf("name: %s", strings.TrimSpace(vs[1]))
}
newBlock = false
if t, ok := tokens[strings.TrimSpace(vs[0])]; ok {
hdls[t] = strings.TrimSpace(vs[1])
}
if strings.TrimSpace(vs[0]) == dsToken && strings.TrimSpace(vs[1]) != "" {
v += "\nDNSSEC: signed"
}
if strings.TrimSpace(vs[0]) == hdlToken {
for _, kk := range keys(hdls) {
if strings.TrimSpace(vs[1]) == hdls[kk] {
token = kk + " "
delete(hdls, kk)
break
}
}
}
result += fmt.Sprintf("\n%s%s", token, v)
}
return result
}
// prepareRU do prepare the .ru domain
func prepareRU(text string) string {
tokens := map[string]string{
"person": "Registrant Name",
"e-mail": "Registrant Email",
"org": "Registrant Organization",
}
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
if !strings.Contains(v, ":") {
continue
}
vs := strings.Split(v, ":")
if vv, ok := tokens[strings.TrimSpace(vs[0])]; ok {
v = fmt.Sprintf("%s: %s", vv, vs[1])
} else if vs[0] == "nserver" {
v = strings.Replace(v, ",", " ", -1)
}
result += v + "\n"
}
return result
}
var prepareJPreplacerRx = regexp.MustCompile(`\n(?:\w+\.\s)?\[(.+?)\][\ ]*(.+?)?`)
// prepareJP do prepare the .jp domain
func prepareJP(text string) string {
text = prepareJPreplacerRx.ReplaceAllString(text, "\n$1: $2")
adminToken := "Contact Information"
addressToken := "Postal Address"
token := ""
prefixToken := ""
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
token = strings.TrimSpace(vs[0])
if token == adminToken {
prefixToken = "admin "
}
if strings.ToLower(token) == "registrant" {
v = fmt.Sprintf("registrant name: %s", vs[1])
}
v = prepareSecondLevelJP(v, token, vs[1])
} else {
if token == addressToken {
result += ", " + v
continue
}
}
result += "\n" + prefixToken + v
}
return result
}
// prepareJP prepares specific mappings for second level .jp domains
// examples include:
// - co.jp
// - ac.jp
// - go.jp
// - or.jp
// - ad.jp
// - ne.jp
// - gr.jp
// - ed.jp
func prepareSecondLevelJP(original string, token string, value string) string {
if strings.ToLower(token) == "administrative contact" {
return fmt.Sprintf("Administrative Contact ID: %s", strings.TrimSpace(value))
}
if strings.ToLower(token) == "technical contact" {
return fmt.Sprintf("Technical Contact ID: %s", strings.TrimSpace(value))
}
if strings.ToLower(token) == "organization" || strings.ToLower(token) == "network service name" {
return fmt.Sprintf("Registrant Organization: %s", strings.TrimSpace(value))
}
return original
}
// prepareUK do prepare the .uk domain
func prepareUK(text string) string {
tokens := map[string]string{
"URL": "Registrar URL",
}
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
if vv, ok := tokens[strings.TrimSpace(vs[0])]; ok {
v = fmt.Sprintf("%s: %s", vv, vs[1])
}
}
result += "\n" + v
}
return result
}
// prepareKR do prepare the .kr domain
func prepareKR(text string) string {
english := "# ENGLISH"
tokens := map[string]string{
"Administrative Contact(AC)": "Administrative Contact Name",
"AC E-Mail": "Administrative Contact E-Mail",
"AC Phone Number": "Administrative Contact Phone Number",
"Authorized Agency": "Registrar Name",
"Registrant": "Registrant Name",
}
pos := strings.Index(text, english)
if pos != -1 {
text = text[pos+len(english):]
}
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
if v[0] == '\'' || v[0] == '-' {
continue
}
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
if vv, ok := tokens[strings.TrimSpace(vs[0])]; ok {
v = fmt.Sprintf("%s: %s", vv, vs[1])
}
}
result += "\n" + v
}
return result
}
// prepareNZ do prepare the .nz domain
func prepareNZ(text string) string {
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
if strings.HasPrefix(strings.TrimSpace(vs[0]), "ns_name_") {
v = fmt.Sprintf("name server: %s", vs[1])
}
}
result += "\n" + v
}
return result
}
// prepareTK do prepare the .tk domain
func prepareTK(text string) string {
tokens := map[string]string{
"Domain name:": "Domain",
"Domain Nameservers:": "Nameservers",
"Owner contact:": "Registrant",
"Admin contact:": "Admin",
"Billing contact:": "Billing",
"Tech contact:": "Technical",
"Organisation:": "Registrant",
}
fields := map[string][]string{
"Registrant": {
"Organization",
"Name",
"Address",
"Address",
"Address",
"Country",
},
}
token := ""
result := ""
index := 0
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
token = ""
continue
}
if _, ok := tokens[v]; ok {
token = tokens[v]
continue
}
if token == "Domain" && strings.Contains(v, " is ") {
vv := strings.Split(v, " is ")
v = fmt.Sprintf("Name: %s\nStatus: %s", vv[0], vv[1])
} else if token == "Registrant" && !strings.Contains(v, ":") {
v = fmt.Sprintf("%s: %s", fields[token][index], v)
index++
}
if token != "" {
if !strings.Contains(v, ":") {
v = fmt.Sprintf("%s: %s", token, v)
} else {
v = fmt.Sprintf("%s %s", token, v)
}
}
result += "\n" + strings.TrimSpace(v)
}
return result
}
// prepareNL do prepare the .nl domain
func prepareNL(text string) string {
tokens := map[string][]string{
"Reseller:": {
"Name",
"Address",
"Address",
"Address",
"Address",
},
"Registrar:": {
"Name",
"Address",
"Address",
"Address",
"Address",
},
}
token := ""
index := 0
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
if strings.HasSuffix(v, ":") {
token = ""
index = 0
}
if _, ok := tokens[v]; ok {
token = v
} else {
if token == "" {
result += "\n" + v
} else {
result += fmt.Sprintf("\n%s %s: %s", token[:len(token)-1], tokens[token][index], v)
index++
}
}
}
return result
}
// prepareEU do prepare the .eu domain
func prepareEU(text string) string {
tokens := map[string]string{
"Registrant:": "Registrant",
"Technical:": "Technical",
"Registrar:": "Registrar",
"Onsite(s):": "Onsite",
"Name servers:": "Name servers",
}
token := ""
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
token = ""
continue
}
if _, ok := tokens[v]; ok {
token = tokens[v]
continue
}
if token != "" {
if strings.Contains(v, ":") {
v = fmt.Sprintf("%s %s", token, v)
} else {
if strings.HasPrefix(v, "Visit www.eurid.eu") {
continue
}
v = fmt.Sprintf("%s: %s", token, v)
}
}
result += "\n" + v
}
return result
}
// prepareBR do prepare the .br domain
func prepareBR(text string) string {
hdlToken := "nic-hdl-br"
tokens := map[string]string{
"owner-c": "registrant",
"admin-c": "admin",
"tech-c": "tech",
"billing-c": "billing",
}
token := ""
hdlMap := map[string]string{}
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
token = ""
continue
}
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
if strings.TrimSpace(vs[0]) == hdlToken {
token = strings.TrimSpace(vs[1])
hdlMap[token] = ""
}
}
if token != "" {
hdlMap[token] += "\n" + v
}
}
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
if strings.TrimSpace(vs[0]) == "owner" {
v = fmt.Sprintf("registrant organization: %s", vs[1])
}
if vv, ok := tokens[strings.TrimSpace(vs[0])]; ok {
for _, tt := range strings.Split(hdlMap[strings.TrimSpace(vs[1])], "\n") {
if strings.TrimSpace(tt) == "" {
continue
}
result += fmt.Sprintf("\n%s %s", vv, tt)
}
continue
}
}
result += "\n" + v
}
return result
}
// prepareIR do prepare the .ir domain
func prepareIR(text string) string {
hdlToken := "nic-hdl"
tokens := map[string]string{
"holder-c": "registrant",
"admin-c": "admin",
"tech-c": "tech",
"bill-c": "billing",
}
token := ""
hdlMap := map[string]string{}
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
token = ""
continue
}
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
if strings.TrimSpace(vs[0]) == hdlToken {
token = strings.TrimSpace(vs[1])
hdlMap[token] = ""
}
}
if token != "" {
hdlMap[token] += "\n" + v
}
}
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
if vv, ok := tokens[strings.TrimSpace(vs[0])]; ok {
for _, tt := range strings.Split(hdlMap[strings.TrimSpace(vs[1])], "\n") {
if strings.TrimSpace(tt) == "" {
continue
}
result += fmt.Sprintf("\n%s %s", vv, tt)
}
continue
}
}
result += "\n" + v
}
return result
}
// prepareFI do prepare the .fi domain
func prepareFI(text string) string {
tokens := map[string]string{
"Holder": "Registrant",
"Registrar": "Registrar",
"Tech": "Technical",
}
token := ""
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if len(v) == 0 {
continue
}
if v[0] == '>' {
token = ""
}
if _, ok := tokens[v]; ok {
token = tokens[v]
} else {
if strings.Contains(v, ":") {
vv := strings.SplitN(v, ":", 2)
vv[0] = strings.Trim(vv[0], ".")
if token == "Registrar" && vv[0] == "registrar" {
vv[0] = "name"
}
v = fmt.Sprintf("%s: %s", vv[0], vv[1])
}
if token != "" {
v = fmt.Sprintf("%s %s", token, v)
}
}
result += "\n" + v
}
return result
}
// prepareRS do prepare the .rs domain
func prepareRS(text string) string {
tokens := map[string]string{
"Registrant": "Registrant",
"Administrative contact": "Administrative",
"Technical contact": "Technical",
}
token := ""
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if len(v) == 0 {
token = ""
continue
}
if strings.Contains(v, ":") {
vv := strings.SplitN(v, ":", 2)
vv[0] = strings.TrimSpace(vv[0])
if t, ok := tokens[vv[0]]; ok {
token = t
} else if token != "" {
v = fmt.Sprintf("%s %s", token, v)
}
}
result += "\n" + v
}
return result
}
// prepareEE do prepare the .ee domain
func prepareEE(text string) string {
tokens := map[string]string{
"Domain:": "Domain",
"Registrar:": "Registrar",
"Registrant:": "Registrant",
"Administrative contact:": "Administrative",
"Technical contact:": "Technical",
"Name servers:": "",
}
token := ""
result := ""
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if len(v) == 0 {
token = ""
continue
}
if t, ok := tokens[v]; ok {
token = t
continue
}
v = fmt.Sprintf("%s %s", token, v)
result += "\n" + strings.TrimSpace(v)
}
return result
}
// prepareCN do prepare the .cn domain
func prepareCN(text string) string {
var result string
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
if strings.ToLower(strings.TrimSpace(vs[0])) == "registrant" {
vs[0] = "registrant name"
}
v = fmt.Sprintf("%s: %s", vs[0], vs[1])
}
result += "\n" + v
}
return result
}
// preparePL prepares the .pl domain
func preparePL(text string) string {
result := ""
special := ""
registrarLine := 0
for _, v := range strings.Split(text, "\n") {
if special == "nameservers" {
if strings.HasPrefix(v, " ") {
ns := strings.SplitN(v, "[", 2)
result += fmt.Sprintf("\nnameservers: %s", strings.TrimSpace(ns[0]))
continue
}
special = ""
} else if special == "REGISTRAR" {
if strings.TrimSpace(v) == "" {
special = ""
} else {
switch registrarLine {
case 0:
// always name
result += fmt.Sprintf("\nregistrar name: %s", strings.TrimSpace(v))
case 1:
// always street address
result += fmt.Sprintf("\nregistrar street: %s", strings.TrimSpace(v))
case 2:
// postal code, city, state, sometimes country in an undefined format
// there's no way we can reliably unpack that
registrarLine++
continue
default:
// usually country unless it was on previous line, then phones/emails/www
if strings.Contains(v, "@") {
// email may have an "e-mail" prefix, but mostly does not
result += fmt.Sprintf("\nregistrar email: %s", strings.TrimSpace(strings.TrimLeft(v, "e-mail:")))
} else if strings.HasPrefix(v, "+") {
// phone numbers helpfully always start with +
result += fmt.Sprintf("\nregistrar phone: %s", strings.TrimSpace(v))
} else if strings.Contains(v, ".") {
// WWW addresses sometimes include http/https, sometimes do not
result += fmt.Sprintf("\nregistrar www: %s", strings.TrimSpace(v))
} else {
result += fmt.Sprintf("\nregistrar country: %s", strings.TrimSpace(v))
}
}
registrarLine++
continue
}
}
v = strings.TrimSpace(v)
if v == "" {
continue
}
if strings.HasPrefix(v, "nameservers: ") {
special = "nameservers"
ns := strings.SplitN(v, "[", 2)
result += fmt.Sprintf("\n%s", strings.TrimSpace(ns[0]))
continue
}
if strings.HasPrefix(v, "REGISTRAR:") {
special = "REGISTRAR"
continue
}
result += fmt.Sprintf("\n%s", strings.ReplaceAll(v, "WHOIS database responses:", "whois:"))
}
return result
}
// prepareDK do prepare the .dk domain
func prepareDK(text string) string {
var result string
for _, v := range strings.Split(text, "\n") {
if strings.HasPrefix(v, "DNS:") {
continue
}
result += v + "\n"
}
return result
}
// prepareBY do prepare the .by domain
func prepareBY(text string) string {
var result string
tokens := map[string]string{
"Person": "Registrant Person",
"Org": "Registrant Org",
"Country": "Registrant Country",
"Address": "Registrant Address",
"Phone": "Registrant Phone",
"Email": "Registrant Email",
}
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if strings.Contains(v, ":") {
vs := strings.SplitN(v, ":", 2)
if t, ok := tokens[strings.TrimSpace(vs[0])]; ok {
v = fmt.Sprintf("%s: %s", t, vs[1])
}
}
result += v + "\n"
}
return result
}
// prepareUA do prepare the .ua domain
func prepareUA(text string) string {
var result string
tokens := map[string]string{
"% Registrar:": "Registrar",
"% Registrant:": "Registrant",
"% Administrative Contacts:": "Administrative",
"% Technical Contacts:": "Technical",
}
var token string
uniqueLine := map[string]bool{}
for _, v := range strings.Split(text, "\n") {
v = strings.TrimSpace(v)
if v, ok := tokens[v]; ok {
token = v
continue
}
if token != "" && v != "" && !strings.HasPrefix(v, "%") {
vs := strings.SplitN(v, ":", 2)
vs[0] = strings.TrimSuffix(strings.TrimSpace(vs[0]), "-loc")
if vs[0] == "registrar" {
vs[0] = "name"
}
vs[1] = strings.TrimSpace(vs[1])
if vs[1] == "n/a" {
continue
}
v = fmt.Sprintf("%s %s", token, strings.Join(vs, ":"))
}
if v != "" && uniqueLine[v] {
continue
}
uniqueLine[v] = true
result += v + "\n"
}
return result
}
// prepareAT prepares the .at domain
func prepareAT(text string) string {
result := ""
registrantID := ""
techID := ""
tokens := map[string]string{
"street address": "address",
"postal code": "address",
"city": "address",
"country": "address",
"e-mail": "email",
"nic-hdl": "id",
"personname": "name",
}
formatLine := func(line, token string) string {
before, after, _ := strings.Cut(line, ":")
key := strings.TrimSpace(before)
if t, ok := tokens[key]; ok {
key = t
}
val := strings.TrimSpace(after)
return fmt.Sprintf("%s %s: %s", token, key, val)
}
for _, v := range strings.Split(text, "\n\n") {
v = strings.TrimSpace(v)
if strings.HasPrefix(v, "%") {
continue
}
if strings.Contains(v, ":") {
b := strings.Split(v, "\n")
if strings.HasPrefix(b[0], "domain") {
for _, l := range b {
w := ""
if before, after, ok := strings.Cut(l, ":"); ok {
key := strings.TrimSpace(before)
val := strings.TrimSpace(after)
switch key {
case "domain":
w = fmt.Sprintf("%s: %s", "domain name", val)
case "registrant":
registrantID = val
case "tech-c":
techID = val
case "changed":
w = fmt.Sprintf("%s: %s", "updated_date", val)
case "nserver":
w = fmt.Sprintf("%s: %s", "name_servers", val)
default:
w = fmt.Sprintf("domain %s: %s", key, val)
}
if w != "" {
result += w + "\n"
}
}
}
} else if strings.HasPrefix(b[0], "personname") {
token := ""
if strings.Contains(v, registrantID) {
token = "registrant"
} else if strings.Contains(v, techID) {
token = "technical contact"
}
for _, l := range strings.Split(v, "\n") {
result += formatLine(l, token) + "\n"
}
}
}
}
return result
}