1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
// Copyright Martin Dosch.
// Use of this source code is governed by the BSD-2-clause
// license that can be found in the LICENSE file.
// TODO: Add logging
package main
import (
"bufio"
"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("find config: failed to get current user: %w", err)
}
// Get home directory.
home := curUser.HomeDir
if home == "" {
return "", fmt.Errorf("find config: 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 "", fmt.Errorf("find config: no configuration file found")
}
// Opens the config file and returns the specified values
// for username, server and port.
func parseConfig(configPath string) (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, fmt.Errorf("parse config: wrong permissions for %s: %s instead of 400 (recommended), 440, 600 or 640", configPath, permissions)
}
}
// Open config file.
file, err := os.Open(configPath)
if err != nil {
return output, fmt.Errorf("parse config: 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
}
column := strings.SplitN(scanner.Text(), " ", defaultConfigColumnSep)
switch column[0] {
case "username:":
output.username = column[1]
case "jserver:":
output.jserver = column[1]
case "password:":
output.password = column[1]
case "eval_password:":
shell := os.Getenv("SHELL")
if shell == "" {
shell = "/bin/sh"
}
out, err := exec.Command(shell, "-c", column[1]).Output()
if err != nil {
file.Close()
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 = column[1]
case "alias:":
output.alias = column[1]
default:
// Try to parse legacy sendxmpp config files.
if len(column) >= defaultConfigColumnSep {
if strings.Contains(scanner.Text(), ";") {
output.username = strings.Split(column[0], ";")[0]
output.jserver = strings.Split(column[0], ";")[1]
output.password = column[1]
} else {
output.username = strings.Split(column[0], ":")[0]
if strings.Contains(output.username, "@") {
jserver := strings.SplitAfter(output.username, "@")[1]
if len(jserver) < defaultLenServerConf {
log.Fatal("Couldn't parse config: ", column[0])
}
output.jserver = jserver
}
output.password = column[1]
}
}
}
}
// 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, fmt.Errorf("parse config: invalid username/JID: %s", output.username)
}
}
file.Close()
return output, err
}
|