// 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 "", 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, 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(), " ", 2) 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 "resource:": output.resource = row[1] fmt.Println(configPath+":", "Deprecated option: resource.") case "alias:": output.alias = row[1] default: if len(row) >= 2 { 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) < 2 { 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 }