1package cmd
2
3import (
4	"encoding/json"
5	"fmt"
6	"net/url"
7	"os"
8	"path/filepath"
9	"strings"
10
11	"github.com/go-acme/lego/v4/certcrypto"
12	"github.com/urfave/cli"
13)
14
15func createList() cli.Command {
16	return cli.Command{
17		Name:   "list",
18		Usage:  "Display certificates and accounts information.",
19		Action: list,
20		Flags: []cli.Flag{
21			cli.BoolFlag{
22				Name:  "accounts, a",
23				Usage: "Display accounts.",
24			},
25			cli.BoolFlag{
26				Name:  "names, n",
27				Usage: "Display certificate common names only.",
28			},
29		},
30	}
31}
32
33func list(ctx *cli.Context) error {
34	if ctx.Bool("accounts") && !ctx.Bool("names") {
35		if err := listAccount(ctx); err != nil {
36			return err
37		}
38	}
39
40	return listCertificates(ctx)
41}
42
43func listCertificates(ctx *cli.Context) error {
44	certsStorage := NewCertificatesStorage(ctx)
45
46	matches, err := filepath.Glob(filepath.Join(certsStorage.GetRootPath(), "*.crt"))
47	if err != nil {
48		return err
49	}
50
51	names := ctx.Bool("names")
52
53	if len(matches) == 0 {
54		if !names {
55			fmt.Println("No certificates found.")
56		}
57		return nil
58	}
59
60	if !names {
61		fmt.Println("Found the following certs:")
62	}
63
64	for _, filename := range matches {
65		if strings.HasSuffix(filename, ".issuer.crt") {
66			continue
67		}
68
69		data, err := os.ReadFile(filename)
70		if err != nil {
71			return err
72		}
73
74		pCert, err := certcrypto.ParsePEMCertificate(data)
75		if err != nil {
76			return err
77		}
78
79		if names {
80			fmt.Println(pCert.Subject.CommonName)
81		} else {
82			fmt.Println("  Certificate Name:", pCert.Subject.CommonName)
83			fmt.Println("    Domains:", strings.Join(pCert.DNSNames, ", "))
84			fmt.Println("    Expiry Date:", pCert.NotAfter)
85			fmt.Println("    Certificate Path:", filename)
86			fmt.Println()
87		}
88	}
89
90	return nil
91}
92
93func listAccount(ctx *cli.Context) error {
94	// fake email, needed by NewAccountsStorage
95	if err := ctx.GlobalSet("email", "unknown"); err != nil {
96		return err
97	}
98
99	accountsStorage := NewAccountsStorage(ctx)
100
101	matches, err := filepath.Glob(filepath.Join(accountsStorage.GetRootPath(), "*", "*", "*.json"))
102	if err != nil {
103		return err
104	}
105
106	if len(matches) == 0 {
107		fmt.Println("No accounts found.")
108		return nil
109	}
110
111	fmt.Println("Found the following accounts:")
112	for _, filename := range matches {
113		data, err := os.ReadFile(filename)
114		if err != nil {
115			return err
116		}
117
118		var account Account
119		err = json.Unmarshal(data, &account)
120		if err != nil {
121			return err
122		}
123
124		uri, err := url.Parse(account.Registration.URI)
125		if err != nil {
126			return err
127		}
128
129		fmt.Println("  Email:", account.Email)
130		fmt.Println("  Server:", uri.Host)
131		fmt.Println("  Path:", filepath.Dir(filename))
132		fmt.Println()
133	}
134
135	return nil
136}
137