go-sendxmpp/parseconfig.go
2023-09-05 20:39:54 +02:00

163 lines
4.1 KiB
Go

// Copyright Martin Dosch.
// Use of this source code is governed by the BSD-2-clause
// license that can be found in the LICENSE file.
package main
import (
"bufio"
"errors"
"fmt"
"log"
"os"
"os/exec"
"os/user"
"runtime"
"strconv"
"strings"
)
func findConfig() (string, error) {
// Get the current user.
curUser, err := user.Current()
if err != nil {
return "", fmt.Errorf("findConfig: failed to get current user: %w", err)
}
// Get home directory.
home := curUser.HomeDir
if home == "" {
return "", errors.New("no home directory found")
}
osConfigDir := os.Getenv("$XDG_CONFIG_HOME")
if osConfigDir == "" {
osConfigDir = home + "/.config"
}
configFiles := [3]string{
osConfigDir + "/go-sendxmpp/config",
osConfigDir + "/go-sendxmpp/sendxmpprc", home +
"/.sendxmpprc",
}
for _, r := range configFiles {
// Check that the config file is existing.
_, err := os.Stat(r)
if err == nil {
return r, nil
}
}
return "", errors.New("no configuration file found")
}
// Opens the config file and returns the specified values
// for username, server and port.
func parseConfig(configPath string) (configuration, error) {
var (
output configuration
err error
)
// Use $XDG_CONFIG_HOME/.config/go-sendxmpp/config,
// $XDG_CONFIG_HOME/.config/go-sendxmpp/sendxmpprc or
// ~/.sendxmpprc if no config path is specified.
// Get systems user config path.
if configPath == "" {
configPath, err = findConfig()
if err != nil {
log.Fatal(err)
}
}
// Only check file permissions if we are not running on windows.
if runtime.GOOS != "windows" {
info, err := os.Stat(configPath)
if err != nil {
log.Fatal(err)
}
// Check for file permissions. Must be 600, 640, 440 or 400.
perm := info.Mode().Perm()
permissions := strconv.FormatInt(int64(perm), 8)
if permissions != "600" && permissions != "640" && permissions != "440" && permissions != "400" {
return output, errors.New("Wrong permissions for " + configPath + ": " +
permissions + " instead of 400, 440, 600 or 640.")
}
}
// Open config file.
file, err := os.Open(configPath)
if err != nil {
return output, fmt.Errorf("parseConfig: failed to open config file: %w", err)
}
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
// Read config file per line.
for scanner.Scan() {
if strings.HasPrefix(scanner.Text(), "#") {
continue
}
row := strings.SplitN(scanner.Text(), " ", defaultConfigRowSep)
switch row[0] {
case "username:":
output.username = row[1]
case "jserver:":
output.jserver = row[1]
case "password:":
output.password = row[1]
case "eval_password:":
shell := os.Getenv("SHELL")
if shell == "" {
shell = "/bin/sh"
}
out, err := exec.Command(shell, "-c", row[1]).Output()
if err != nil {
log.Fatal(err)
}
output.password = string(out)
if output.password[len(output.password)-1] == '\n' {
output.password = output.password[:len(output.password)-1]
}
case "port:":
output.port = row[1]
case "alias:":
output.alias = row[1]
default:
if len(row) >= defaultConfigRowSep {
if strings.Contains(scanner.Text(), ";") {
output.username = strings.Split(row[0], ";")[0]
output.jserver = strings.Split(row[0], ";")[1]
output.password = row[1]
} else {
output.username = strings.Split(row[0], ":")[0]
jserver := strings.Split(row[0], "@")
if len(jserver) < defaultLenServerConf {
log.Fatal("Couldn't parse config: ", row[0])
}
output.jserver = jserver[0]
output.password = row[1]
}
}
}
}
err = file.Close()
if err != nil {
fmt.Println("error closing file:", err)
}
// Check if the username is a valid JID
output.username, err = MarshalJID(output.username)
if err != nil {
// Check whether only the local part was used by appending an @ and the
// server part.
output.username = output.username + "@" + output.jserver
// Check if the username is a valid JID now
output.username, err = MarshalJID(output.username)
if err != nil {
return output, errors.New("invalid username/JID: " + output.username)
}
}
return output, err
}