github.com/wtfutil/wtf@v0.43.0/modules/ipaddresses/ipinfo/widget.go (about)

     1  package ipinfo
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	log "github.com/wtfutil/wtf/logger"
     8  	"net"
     9  	"net/http"
    10  	"text/template"
    11  
    12  	"github.com/rivo/tview"
    13  	"github.com/wtfutil/wtf/view"
    14  )
    15  
    16  type Widget struct {
    17  	view.TextWidget
    18  
    19  	result   string
    20  	settings *Settings
    21  }
    22  
    23  type ipinfo struct {
    24  	Ip           string `json:"ip"`
    25  	Hostname     string `json:"hostname"`
    26  	City         string `json:"city"`
    27  	Region       string `json:"region"`
    28  	Country      string `json:"country"`
    29  	Coordinates  string `json:"loc"`
    30  	PostalCode   string `json:"postal"`
    31  	Organization string `json:"org"`
    32  }
    33  
    34  func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settings *Settings) *Widget {
    35  	widget := Widget{
    36  		TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common),
    37  
    38  		settings: settings,
    39  	}
    40  
    41  	widget.View.SetWrap(false)
    42  
    43  	return &widget
    44  }
    45  
    46  func (widget *Widget) Refresh() {
    47  	widget.ipinfo()
    48  
    49  	widget.Redraw(func() (string, string, bool) { return widget.CommonSettings().Title, widget.result, false })
    50  }
    51  
    52  // this method reads the config and calls ipinfo for ip information
    53  func (widget *Widget) ipinfo() {
    54  	client := &http.Client{}
    55  	var url string
    56  	ip, ipv6 := getMyIP(widget.settings.protocolVersion)
    57  	if ipv6 {
    58  		url = fmt.Sprintf("https://ipinfo.io/%s", ip.String())
    59  	} else {
    60  		url = "https://ipinfo.io/"
    61  	}
    62  
    63  	req, err := http.NewRequest("GET", url, http.NoBody)
    64  	if err != nil {
    65  		widget.result = err.Error()
    66  		return
    67  	}
    68  	req.Header.Set("User-Agent", "curl")
    69  	if widget.settings.apiToken != "" {
    70  		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", widget.settings.apiToken))
    71  	}
    72  
    73  	response, err := client.Do(req)
    74  	if err != nil {
    75  		widget.result = err.Error()
    76  		return
    77  	}
    78  	defer func() { _ = response.Body.Close() }()
    79  
    80  	var info ipinfo
    81  	err = json.NewDecoder(response.Body).Decode(&info)
    82  	if err != nil {
    83  		widget.result = err.Error()
    84  		return
    85  	}
    86  
    87  	widget.setResult(&info)
    88  }
    89  
    90  func (widget *Widget) setResult(info *ipinfo) {
    91  	resultTemplate, _ := template.New("ipinfo_result").Parse(
    92  		formatableText("IP", "Ip") +
    93  			formatableText("Hostname", "Hostname") +
    94  			formatableText("City", "City") +
    95  			formatableText("Region", "Region") +
    96  			formatableText("Country", "Country") +
    97  			formatableText("Loc", "Coordinates") +
    98  			formatableText("Org", "Organization"),
    99  	)
   100  
   101  	resultBuffer := new(bytes.Buffer)
   102  
   103  	err := resultTemplate.Execute(resultBuffer, map[string]string{
   104  		"subheadingColor": widget.settings.Colors.Subheading,
   105  		"valueColor":      widget.settings.Colors.Text,
   106  		"Ip":              info.Ip,
   107  		"Hostname":        info.Hostname,
   108  		"City":            info.City,
   109  		"Region":          info.Region,
   110  		"Country":         info.Country,
   111  		"Coordinates":     info.Coordinates,
   112  		"PostalCode":      info.PostalCode,
   113  		"Organization":    info.Organization,
   114  	})
   115  
   116  	if err != nil {
   117  		widget.result = err.Error()
   118  	}
   119  
   120  	widget.result = resultBuffer.String()
   121  }
   122  
   123  func formatableText(key, value string) string {
   124  	return fmt.Sprintf(" [{{.subheadingColor}}]%8s[-:-:-] [{{.valueColor}}]{{.%s}}\n", key, value)
   125  }
   126  
   127  // getMyIP provides this system's default IPv4 or IPv6 IP address for routing WAN requests.
   128  // It does so by dialing out to a site known to have both an A and AAAA DNS records (IPv6)
   129  // The 'net' package is allowed to decide how to connect, connecting to both IPv4 or IPv6 address
   130  // depending on the availbility of IP protocols.
   131  func getMyIP(version protocolVersion) (ip net.IP, v6 bool) {
   132  	log.Log(fmt.Sprintf("Protocol version: %s", version))
   133  	log.Log(fmt.Sprintf("Network: %s", version.toNetwork()))
   134  	//fmt.Println("Protocol version: ", version)
   135  	conn, err := net.Dial(version.toNetwork(), "fast.com:80")
   136  	if err != nil {
   137  		return
   138  	}
   139  	defer func() { _ = conn.Close() }()
   140  
   141  	addr := conn.LocalAddr().(*net.TCPAddr)
   142  	ip = addr.IP
   143  	v6 = ip.To4() == nil
   144  
   145  	return
   146  }
   147  
   148  func (pv protocolVersion) toNetwork() string {
   149  	switch pv {
   150  	case ipV4:
   151  		return "tcp4"
   152  	case ipV6:
   153  		return "tcp6"
   154  	default:
   155  		return "tcp"
   156  	}
   157  }