<commit_before>package cloudflare

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"strings"
	"time"

	"github.com/StackExchange/dnscontrol/models"
	"github.com/StackExchange/dnscontrol/providers"
	"github.com/StackExchange/dnscontrol/providers/diff"
	"github.com/StackExchange/dnscontrol/transform"
	"github.com/miekg/dns/dnsutil"
)

/*

Cloudflare API DNS provider:

Info required in `creds.json`:
   - apikey
   - apiuser

Record level metadata available:
   - cloudflare_proxy ("on", "off", or "full")

Domain level metadata available:
   - cloudflare_proxy_default ("on", "off", or "full")

 Provider level metadata available:
   - ip_conversions
*/

type CloudflareApi struct {
	ApiKey        string `json:"apikey"`
	ApiUser       string `json:"apiuser"`
	domainIndex   map[string]string
	nameservers   map[string][]string
	ipConversions []transform.IpConversion
	ignoredLabels []string
}

func labelMatches(label string, matches []string) bool {
	//log.Printf("DEBUG: labelMatches(%#v, %#v)\n", label, matches)
	for _, tst := range matches {
		if label == tst {
			return true
		}
	}
	return false
}
func (c *CloudflareApi) GetNameservers(domain string) ([]*models.Nameserver, error) {
	if c.domainIndex == nil {
		if err := c.fetchDomainList(); err != nil {
			return nil, err
		}
	}
	ns, ok := c.nameservers[domain]
	if !ok {
		return nil, fmt.Errorf("Nameservers for %s not found in cloudflare account", domain)
	}
	return models.StringsToNameservers(ns), nil
}

func (c *CloudflareApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
	if c.domainIndex == nil {
		if err := c.fetchDomainList(); err != nil {
			return nil, err
		}
	}
	id, ok := c.domainIndex[dc.Name]
	if !ok {
		return nil, fmt.Errorf("%s not listed in zones for cloudflare account", dc.Name)
	}
	if err := c.preprocessConfig(dc); err != nil {
		return nil, err
	}
	records, err := c.getRecordsForDomain(id, dc.Name)
	if err != nil {
		return nil, err
	}
	for i := len(records) - 1; i >= 0; i-- {
		rec := records[i]
		// Delete ignore labels
		if labelMatches(dnsutil.TrimDomainName(rec.Original.(*cfRecord).Name, dc.Name), c.ignoredLabels) {
			fmt.Printf("ignored_label: %s\n", rec.Original.(*cfRecord).Name)
			records = append(records[:i], records[i+1:]...)
		}
	}
	for _, rec := range dc.Records {
		if rec.Type == "ALIAS" {
			rec.Type = "CNAME"
		}
		if labelMatches(rec.Name, c.ignoredLabels) {
			log.Fatalf("FATAL: dnsconfig contains label that matches ignored_labels: %#v is in %v)\n", rec.Name, c.ignoredLabels)
		}
	}
	checkNSModifications(dc)
	differ := diff.New(dc, getProxyMetadata)
	_, create, del, mod := differ.IncrementalDiff(records)
	corrections := []*models.Correction{}

	for _, d := range del {
		corrections = append(corrections, c.deleteRec(d.Existing.Original.(*cfRecord), id))
	}
	for _, d := range create {
		corrections = append(corrections, c.createRec(d.Desired, id)...)
	}

	for _, d := range mod {
		e, rec := d.Existing.Original.(*cfRecord), d.Desired
		proxy := e.Proxiable && rec.Metadata[metaProxy] != "off"
		corrections = append(corrections, &models.Correction{
			Msg: d.String(),
			F:   func() error { return c.modifyRecord(id, e.ID, proxy, rec) },
		})
	}
	return corrections, nil
}

func checkNSModifications(dc *models.DomainConfig) {
	newList := make([]*models.RecordConfig, 0, len(dc.Records))
	for _, rec := range dc.Records {
		if rec.Type == "NS" && rec.NameFQDN == dc.Name {<commit_msg>adding no_purge to cloudflare (#110)<commit_after>package cloudflare

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"strings"
	"time"

	"github.com/StackExchange/dnscontrol/models"
	"github.com/StackExchange/dnscontrol/providers"
	"github.com/StackExchange/dnscontrol/providers/diff"
	"github.com/StackExchange/dnscontrol/transform"
	"github.com/miekg/dns/dnsutil"
)

/*

Cloudflare API DNS provider:

Info required in `creds.json`:
   - apikey
   - apiuser

Record level metadata available:
   - cloudflare_proxy ("on", "off", or "full")

Domain level metadata available:
   - cloudflare_proxy_default ("on", "off", or "full")

 Provider level metadata available:
   - ip_conversions
*/

type CloudflareApi struct {
	ApiKey        string `json:"apikey"`
	ApiUser       string `json:"apiuser"`
	domainIndex   map[string]string
	nameservers   map[string][]string
	ipConversions []transform.IpConversion
	ignoredLabels []string
}

func labelMatches(label string, matches []string) bool {
	//log.Printf("DEBUG: labelMatches(%#v, %#v)\n", label, matches)
	for _, tst := range matches {
		if label == tst {
			return true
		}
	}
	return false
}
func (c *CloudflareApi) GetNameservers(domain string) ([]*models.Nameserver, error) {
	if c.domainIndex == nil {
		if err := c.fetchDomainList(); err != nil {
			return nil, err
		}
	}
	ns, ok := c.nameservers[domain]
	if !ok {
		return nil, fmt.Errorf("Nameservers for %s not found in cloudflare account", domain)
	}
	return models.StringsToNameservers(ns), nil
}

func (c *CloudflareApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
	if c.domainIndex == nil {
		if err := c.fetchDomainList(); err != nil {
			return nil, err
		}
	}
	id, ok := c.domainIndex[dc.Name]
	if !ok {
		return nil, fmt.Errorf("%s not listed in zones for cloudflare account", dc.Name)
	}
	if err := c.preprocessConfig(dc); err != nil {
		return nil, err
	}
	records, err := c.getRecordsForDomain(id, dc.Name)
	if err != nil {
		return nil, err
	}
	for i := len(records) - 1; i >= 0; i-- {
		rec := records[i]
		// Delete ignore labels
		if labelMatches(dnsutil.TrimDomainName(rec.Original.(*cfRecord).Name, dc.Name), c.ignoredLabels) {
			fmt.Printf("ignored_label: %s\n", rec.Original.(*cfRecord).Name)
			records = append(records[:i], records[i+1:]...)
		}
	}
	for _, rec := range dc.Records {
		if rec.Type == "ALIAS" {
			rec.Type = "CNAME"
		}
		if labelMatches(rec.Name, c.ignoredLabels) {
			log.Fatalf("FATAL: dnsconfig contains label that matches ignored_labels: %#v is in %v)\n", rec.Name, c.ignoredLabels)
		}
	}
	checkNSModifications(dc)
	differ := diff.New(dc, getProxyMetadata)
	_, create, del, mod := differ.IncrementalDiff(records)
	corrections := []*models.Correction{}

	for _, d := range del {
		if dc.KeepUnknown {
			continue
		}
		corrections = append(corrections, c.deleteRec(d.Existing.Original.(*cfRecord), id))
	}
	for _, d := range create {
		corrections = append(corrections, c.createRec(d.Desired, id)...)
	}

	for _, d := range mod {
		e, rec := d.Existing.Original.(*cfRecord), d.Desired
		proxy := e.Proxiable && rec.Metadata[metaProxy] != "off"
		corrections = append(corrections, &models.Correction{
			Msg: d.String(),
			F:   func() error { return c.modifyRecord(id, e.ID, proxy, rec) },
		})
	}
	return corrections, nil
}

func checkNSModifications(dc *models.DomainConfig) {
	newList := make([]*models.RecordConfig, 0, len(dc.Records))
	for _, rec := range dc.Records {
		if rec.Type == "NS" && rec.NameFQDN == dc.Name {
{'fraction_non_alphanumeric': 0.15150208423170908, 'fraction_numerical': 0.0015811412965358632, 'mean_word_length': 3.9985632183908044, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6427933', 'n_tokens_mistral': 2633, 'n_tokens_neox': 2471, 'n_words': 591}