...

Source file src/codeberg.org/tslocum/gmitohtml/main.go

Documentation: codeberg.org/tslocum/gmitohtml

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"flag"
     6  	"fmt"
     7  	"io"
     8  	"log"
     9  	"net/url"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  	"path/filepath"
    14  	"runtime"
    15  
    16  	"codeberg.org/tslocum/gmitohtml/pkg/gmitohtml"
    17  	"gopkg.in/yaml.v2"
    18  )
    19  
    20  func defaultConfigPath() (string, error) {
    21  	homedir, err := os.UserHomeDir()
    22  	if err != nil {
    23  		return "", err
    24  	} else if homedir == "" {
    25  		return "", errors.New("a blank path to user homedir was returned")
    26  	}
    27  
    28  	dir := path.Join(homedir, ".config", "gmitohtml")
    29  	_, err = os.Stat(path.Join(dir, "config.yaml"))
    30  	if err == nil {
    31  		return path.Join(dir, "config.yaml"), nil
    32  	}
    33  
    34  	_, err = os.Stat(path.Join(dir, "config.yml"))
    35  	if err == nil {
    36  		return path.Join(dir, "config.yml"), nil
    37  	}
    38  
    39  	return path.Join(dir, "config.yaml"), nil
    40  }
    41  
    42  func serializeConfig(path string) error {
    43  	if path == "" {
    44  		return errors.New("failed to serialize: no path specified")
    45  
    46  	}
    47  
    48  	out, err := yaml.Marshal(gmitohtml.Config)
    49  	if err != nil {
    50  		return fmt.Errorf("failed to marshal configuration: %s", err)
    51  	}
    52  
    53  	os.MkdirAll(filepath.Dir(path), 0755)
    54  	err = os.WriteFile(path, out, 0644)
    55  	if err != nil {
    56  		return fmt.Errorf("failed to write to %s: %s", path, err)
    57  	}
    58  
    59  	return nil
    60  }
    61  
    62  func deserializeConfig(path string) error {
    63  	if path == "" {
    64  		return errors.New("failed to deserialize: no path specified")
    65  	}
    66  
    67  	_, err := os.Stat(path)
    68  	if os.IsNotExist(err) {
    69  		return nil
    70  	}
    71  
    72  	configData, err := os.ReadFile(path)
    73  	if err != nil {
    74  		return fmt.Errorf("failed to read file: %s", err)
    75  	}
    76  
    77  	err = yaml.Unmarshal(configData, gmitohtml.Config)
    78  	if err != nil {
    79  		return fmt.Errorf("failed to parse file: %s", err)
    80  	}
    81  
    82  	return nil
    83  }
    84  
    85  func openBrowser(url string) {
    86  	var err error
    87  	switch runtime.GOOS {
    88  	case "linux":
    89  		err = exec.Command("xdg-open", url).Start()
    90  	case "windows":
    91  		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
    92  	case "darwin":
    93  		err = exec.Command("open", url).Start()
    94  	default:
    95  		err = fmt.Errorf("unsupported platform")
    96  	}
    97  	if err != nil {
    98  		log.Fatal(err)
    99  	}
   100  }
   101  
   102  func main() {
   103  	var (
   104  		view       bool
   105  		allowFile  bool
   106  		daemon     string
   107  		hostname   string
   108  		configFile string
   109  	)
   110  	flag.BoolVar(&view, "view", false, "open web browser")
   111  	flag.BoolVar(&allowFile, "allow-file", false, "allow local file access via file://")
   112  	flag.StringVar(&daemon, "daemon", "", "start daemon on specified address")
   113  	flag.StringVar(&hostname, "hostname", "", "server hostname (e.g. rocket9labs.com) (defaults to daemon address)")
   114  	flag.StringVar(&configFile, "config", "", "path to configuration file")
   115  	// TODO option to include response header in page
   116  	flag.Parse()
   117  
   118  	defaultConfig, err := defaultConfigPath()
   119  	if err != nil {
   120  		log.Fatal(err)
   121  	} else if configFile == "" {
   122  		configFile = defaultConfig
   123  	}
   124  
   125  	if configFile != "" {
   126  		var configExists bool
   127  		if _, err := os.Stat(defaultConfig); !os.IsNotExist(err) {
   128  			configExists = true
   129  		}
   130  
   131  		if configExists || configFile != defaultConfig {
   132  			err := deserializeConfig(configFile)
   133  			if err != nil {
   134  				log.Fatalf("failed to read configuration file at %s: %v\nSee CONFIGURATION.md for information on configuring gmitohtml", configFile, err)
   135  			}
   136  
   137  			for u, label := range gmitohtml.Config.Bookmarks {
   138  				gmitohtml.AddBookmark(u, label)
   139  			}
   140  		}
   141  	}
   142  
   143  	for domain, cc := range gmitohtml.Config.Certs {
   144  		certData, err := os.ReadFile(cc.Cert)
   145  		if err != nil {
   146  			log.Fatalf("failed to load client certificate for domain %s: %s", domain, err)
   147  		}
   148  
   149  		keyData, err := os.ReadFile(cc.Key)
   150  		if err != nil {
   151  			log.Fatalf("failed to load client certificate for domain %s: %s", domain, err)
   152  		}
   153  
   154  		err = gmitohtml.SetClientCertificate(domain, certData, keyData)
   155  		if err != nil {
   156  			log.Fatalf("failed to load client certificate for domain %s", domain)
   157  		}
   158  	}
   159  
   160  	if daemon != "" {
   161  		gmitohtml.SetOnBookmarksChanged(func() {
   162  			gmitohtml.Config.Bookmarks = gmitohtml.GetBookmarks()
   163  
   164  			err := serializeConfig(configFile)
   165  			if err != nil {
   166  				log.Fatal(err)
   167  			}
   168  		})
   169  
   170  		err := gmitohtml.StartDaemon(daemon, hostname, allowFile)
   171  		if err != nil {
   172  			log.Fatal(err)
   173  		}
   174  
   175  		if view {
   176  			openBrowser("http://" + daemon)
   177  		}
   178  
   179  		select {}
   180  	}
   181  
   182  	data, err := io.ReadAll(os.Stdin)
   183  	if err != nil {
   184  		log.Fatal(err)
   185  	}
   186  
   187  	data = gmitohtml.Convert(data, "")
   188  
   189  	if view {
   190  		openBrowser(string(append([]byte("data:text/html,"), []byte(url.PathEscape(string(data)))...)))
   191  		return
   192  	}
   193  	fmt.Print(gmitohtml.Convert(data, ""))
   194  }
   195  

View as plain text