2021-02-25 09:04:39 +01:00
|
|
|
package fbx
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2021-02-28 11:44:31 +01:00
|
|
|
"strconv"
|
2021-02-25 09:04:39 +01:00
|
|
|
"strings"
|
|
|
|
|
2021-02-28 11:44:31 +01:00
|
|
|
"github.com/hashicorp/mdns"
|
2021-02-25 09:04:39 +01:00
|
|
|
"github.com/trazfr/freebox-exporter/log"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FreeboxAPIVersion struct {
|
|
|
|
APIDomain string `json:"api_domain"`
|
|
|
|
UID string `json:"uid"`
|
|
|
|
HTTPSAvailable bool `json:"https_available"`
|
|
|
|
HTTPSPort uint16 `json:"https_port"`
|
|
|
|
DeviceName string `json:"device_name"`
|
|
|
|
APIVersion string `json:"api_version"`
|
|
|
|
APIBaseURL string `json:"api_base_url"`
|
|
|
|
DeviceType string `json:"device_type"`
|
2022-09-30 22:08:14 +02:00
|
|
|
|
|
|
|
QueryApiVersion int `json:"-"`
|
2021-02-25 09:04:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
apiVersionURL = "http://mafreebox.freebox.fr/api_version"
|
2021-02-28 11:44:31 +01:00
|
|
|
mdnsService = "_fbx-api._tcp"
|
2021-02-25 09:04:39 +01:00
|
|
|
)
|
|
|
|
|
2021-02-28 11:44:31 +01:00
|
|
|
type FreeboxDiscovery int
|
2021-02-25 09:04:39 +01:00
|
|
|
|
2021-02-28 11:44:31 +01:00
|
|
|
const (
|
|
|
|
// FreeboxDiscoveryHTTP Freebox discovery by call to http://mafreebox.freebox.fr/api_version
|
|
|
|
FreeboxDiscoveryHTTP FreeboxDiscovery = iota
|
|
|
|
// FreeboxDiscoveryMDNS Freebox discovery by mDNS on service _fbx-api._tcp
|
|
|
|
FreeboxDiscoveryMDNS
|
|
|
|
)
|
2021-02-25 09:04:39 +01:00
|
|
|
|
2022-09-30 22:08:14 +02:00
|
|
|
func NewFreeboxAPIVersion(client *FreeboxHttpClient, discovery FreeboxDiscovery, forceApiVersion int) (*FreeboxAPIVersion, error) {
|
2021-02-25 09:04:39 +01:00
|
|
|
result := &FreeboxAPIVersion{}
|
2021-02-28 11:44:31 +01:00
|
|
|
|
|
|
|
if err := result.getDiscovery(discovery)(client); err != nil {
|
2021-02-25 09:04:39 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
2021-02-28 11:44:31 +01:00
|
|
|
|
2022-09-30 22:08:14 +02:00
|
|
|
if err := result.setQueryApiVersion(forceApiVersion); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-09-30 21:11:12 +02:00
|
|
|
if !result.IsValid() {
|
|
|
|
return nil, errors.New("could not get the API version")
|
2021-02-25 09:04:39 +01:00
|
|
|
}
|
2021-02-28 13:28:12 +01:00
|
|
|
log.Debug.Println("APIVersion", result)
|
2021-02-25 09:04:39 +01:00
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FreeboxAPIVersion) IsValid() bool {
|
|
|
|
if f == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return f.APIDomain != "" &&
|
|
|
|
f.UID != "" &&
|
2022-09-30 21:11:12 +02:00
|
|
|
f.HTTPSAvailable &&
|
2021-02-25 09:04:39 +01:00
|
|
|
f.HTTPSPort != 0 &&
|
|
|
|
f.DeviceName != "" &&
|
|
|
|
f.APIVersion != "" &&
|
|
|
|
f.APIBaseURL != "" &&
|
2022-09-30 22:08:14 +02:00
|
|
|
f.DeviceType != "" &&
|
|
|
|
f.QueryApiVersion > 0
|
2021-02-25 09:04:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FreeboxAPIVersion) GetURL(path string, miscPath ...interface{}) (string, error) {
|
2022-09-30 21:11:12 +02:00
|
|
|
if !f.IsValid() {
|
|
|
|
return "", errors.New("invalid FreeboxAPIVersion")
|
2021-02-25 09:04:39 +01:00
|
|
|
}
|
|
|
|
args := make([]interface{}, len(miscPath)+4)
|
|
|
|
args[0] = f.APIDomain
|
|
|
|
args[1] = f.HTTPSPort
|
|
|
|
args[2] = f.APIBaseURL
|
2022-09-30 22:08:14 +02:00
|
|
|
args[3] = f.QueryApiVersion
|
2021-02-25 09:04:39 +01:00
|
|
|
if len(miscPath) > 0 {
|
|
|
|
copy(args[4:], miscPath)
|
|
|
|
}
|
2022-09-30 22:08:14 +02:00
|
|
|
return fmt.Sprintf("https://%s:%d%sv%d/"+path, args...), nil
|
2021-02-25 09:04:39 +01:00
|
|
|
}
|
2021-02-28 11:44:31 +01:00
|
|
|
|
|
|
|
func (f *FreeboxAPIVersion) getDiscovery(discovery FreeboxDiscovery) func(client *FreeboxHttpClient) error {
|
|
|
|
function := func(*FreeboxHttpClient) error {
|
2022-09-30 21:11:12 +02:00
|
|
|
return errors.New("wrong discovery argument")
|
2021-02-28 11:44:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
switch discovery {
|
|
|
|
case FreeboxDiscoveryHTTP:
|
|
|
|
function = f.newFreeboxAPIVersionHTTP
|
|
|
|
case FreeboxDiscoveryMDNS:
|
|
|
|
function = f.newFreeboxAPIVersionMDNS
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
return function
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FreeboxAPIVersion) newFreeboxAPIVersionHTTP(client *FreeboxHttpClient) error {
|
|
|
|
log.Info.Println("Freebox discovery: GET", apiVersionURL)
|
|
|
|
|
|
|
|
// HTTP GET api version
|
|
|
|
r, err := client.client.Get(apiVersionURL)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer r.Body.Close()
|
|
|
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(f); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FreeboxAPIVersion) newFreeboxAPIVersionMDNS(*FreeboxHttpClient) error {
|
|
|
|
log.Info.Println("Freebox discovery: mDNS")
|
|
|
|
entries := make(chan *mdns.ServiceEntry, 4)
|
|
|
|
|
|
|
|
// mDNS lookup
|
|
|
|
go func() {
|
|
|
|
defer close(entries)
|
|
|
|
if err := mdns.Lookup(mdnsService, entries); err != nil {
|
|
|
|
log.Error.Println("mDNS lookup failed:", err)
|
|
|
|
}
|
|
|
|
log.Debug.Println("End of mDNS lookup")
|
|
|
|
}()
|
|
|
|
|
|
|
|
for entry := range entries {
|
|
|
|
deviceName := entry.Name
|
|
|
|
idx := strings.Index(deviceName, ".")
|
|
|
|
if idx >= 0 {
|
|
|
|
deviceName = deviceName[0:idx]
|
|
|
|
}
|
|
|
|
deviceName = strings.ReplaceAll(deviceName, "\\", "")
|
|
|
|
|
|
|
|
*f = FreeboxAPIVersion{
|
|
|
|
DeviceName: deviceName,
|
|
|
|
}
|
|
|
|
for i := range entry.InfoFields {
|
|
|
|
kv := strings.SplitN(entry.InfoFields[i], "=", 2)
|
|
|
|
if len(kv) != 2 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
switch kv[0] {
|
|
|
|
case "api_domain":
|
|
|
|
f.APIDomain = kv[1]
|
|
|
|
case "uid":
|
|
|
|
f.UID = kv[1]
|
|
|
|
case "https_available":
|
|
|
|
f.HTTPSAvailable = (kv[1] == "1")
|
|
|
|
case "https_port":
|
|
|
|
port, _ := strconv.ParseUint(kv[1], 10, 16)
|
|
|
|
f.HTTPSPort = uint16(port)
|
|
|
|
case "api_version":
|
|
|
|
f.APIVersion = kv[1]
|
|
|
|
case "api_base_url":
|
|
|
|
f.APIBaseURL = kv[1]
|
|
|
|
case "device_type":
|
|
|
|
f.DeviceType = kv[1]
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if f.IsValid() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.New("MDNS timeout")
|
|
|
|
}
|
2022-09-30 22:08:14 +02:00
|
|
|
|
|
|
|
func (f *FreeboxAPIVersion) setQueryApiVersion(forceApiVersion int) error {
|
|
|
|
versionSplit := strings.Split(f.APIVersion, ".")
|
|
|
|
if len(versionSplit) != 2 {
|
|
|
|
return fmt.Errorf("could not decode the api version \"%s\"", f.APIVersion)
|
|
|
|
}
|
|
|
|
if apiVersionFromDiscovery, err := strconv.Atoi(versionSplit[0]); err != nil {
|
|
|
|
return err
|
|
|
|
} else if forceApiVersion > apiVersionFromDiscovery {
|
|
|
|
return fmt.Errorf("could use the api version %d which is higher than %d", forceApiVersion, apiVersionFromDiscovery)
|
|
|
|
} else if forceApiVersion > 0 {
|
|
|
|
f.QueryApiVersion = forceApiVersion
|
|
|
|
} else {
|
|
|
|
f.QueryApiVersion = apiVersionFromDiscovery
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|