Compare commits
No commits in common. "ed4bbeb6083eb68c5c592c753e1f2758ddc11295" and "9a7834c330574bee8c173282e0c4a19bcf2a0066" have entirely different histories.
ed4bbeb608
...
9a7834c330
8 changed files with 23 additions and 201 deletions
34
args/args.go
34
args/args.go
|
@ -3,21 +3,13 @@ package args
|
||||||
/* This file manages declaration of, parsing of, and returning copies of
|
/* This file manages declaration of, parsing of, and returning copies of
|
||||||
command line arguments. */
|
command line arguments. */
|
||||||
|
|
||||||
import (
|
import "flag"
|
||||||
"flag"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
/* Method to trigger flag parsing. Can be called multiple times safely. */
|
/* Method to trigger flag parsing. Can be called multiple times safely. */
|
||||||
func Parse() {
|
func Parse() {
|
||||||
if (flagsParsed) {return}
|
if (flagsParsed) {return}
|
||||||
flag.Parse();
|
flag.Parse();
|
||||||
flagsParsed = true;
|
flagsParsed = true;
|
||||||
|
|
||||||
// Process DBUS socket location
|
|
||||||
if socketLocation == nil || *socketLocation == "" {
|
|
||||||
*socketLocation = os.Getenv("DBUS_SYSTEM_BUS_ADDRESS");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/* module-specific variable to avoid re-parsing flags */
|
/* module-specific variable to avoid re-parsing flags */
|
||||||
var flagsParsed bool = false;
|
var flagsParsed bool = false;
|
||||||
|
@ -28,28 +20,4 @@ var confLocation = flag.String("conf", "./config.txt", "Config file to read from
|
||||||
func GetConfLocation() (location string, set bool) {
|
func GetConfLocation() (location string, set bool) {
|
||||||
if confLocation == nil {return "", false}
|
if confLocation == nil {return "", false}
|
||||||
return *confLocation, true;
|
return *confLocation, true;
|
||||||
}
|
|
||||||
|
|
||||||
/* TCP port to bind to */
|
|
||||||
var httpPort = flag.Int("port", 11938, "Port number to bind to")
|
|
||||||
/* @return set boolean will be true if argument is not nil */
|
|
||||||
func GetHTTPPort() (port int, set bool) {
|
|
||||||
if httpPort == nil {return -1, false}
|
|
||||||
return *httpPort, true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Listen on a UNIX socket */
|
|
||||||
var socketLocation = flag.String("socket", "", "Location of UNIX socket to listen on. Setting will disable TCP.")
|
|
||||||
/* @return set boolean will be true if argument is not nil */
|
|
||||||
func GetSocketLocation() (port string, set bool) {
|
|
||||||
if socketLocation == nil {return "", false}
|
|
||||||
return *socketLocation, true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Where the signal-cli binary is */
|
|
||||||
var binaryLocation = flag.String("binary", "/usr/local/bin/signal-cli", "Location of the signal-cli binary.")
|
|
||||||
/* @return set boolean will be true if argument is not nil */
|
|
||||||
func GetBinaryLocation() (port string, set bool) {
|
|
||||||
if binaryLocation == nil {return "", false}
|
|
||||||
return *binaryLocation, true;
|
|
||||||
}
|
}
|
27
conf/conf.go
27
conf/conf.go
|
@ -10,14 +10,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// import "fmt"
|
||||||
|
|
||||||
/* Object to handle what is in a JSON config */
|
/* Object to handle what is in a JSON config */
|
||||||
type Config struct {
|
type Config struct {
|
||||||
configData map[string][]string;
|
configData map[string][]string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default Config object */
|
|
||||||
var GlobalConfig * Config;
|
|
||||||
|
|
||||||
/* Opens and reads a file at the path */
|
/* Opens and reads a file at the path */
|
||||||
func NewConfig(filePath string) (newConfig *Config, err error) {
|
func NewConfig(filePath string) (newConfig *Config, err error) {
|
||||||
// Open file
|
// Open file
|
||||||
|
@ -31,7 +30,8 @@ func NewConfig(filePath string) (newConfig *Config, err error) {
|
||||||
// Read lines into newConfigData
|
// Read lines into newConfigData
|
||||||
scanner := bufio.NewScanner(file)
|
scanner := bufio.NewScanner(file)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
parts := strings.SplitN(scanner.Text(), " ", 2);
|
line := scanner.Text()
|
||||||
|
parts := strings.SplitN(line, " ", 2);
|
||||||
if len(parts) != 2 {err = errors.New("Bad config file!"); return;}
|
if len(parts) != 2 {err = errors.New("Bad config file!"); return;}
|
||||||
newConfigData[parts[0]] = append(newConfigData[parts[0]], parts[1]);
|
newConfigData[parts[0]] = append(newConfigData[parts[0]], parts[1]);
|
||||||
}
|
}
|
||||||
|
@ -41,21 +41,4 @@ func NewConfig(filePath string) (newConfig *Config, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Gets a reference copy to the config data */
|
/* Gets a reference copy to the config data */
|
||||||
func (config * Config) GetConfigData() map[string][]string {
|
func (config Config) GetConfigData() map[string][]string {return config.configData;}
|
||||||
return config.configData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns if a bearer key is authorized for the path in this Config object
|
|
||||||
@return false for any situation that isn't a valid match */
|
|
||||||
func (config * Config) ValidateBearerKey(bearerKey string, request string) bool {
|
|
||||||
paths, exists := config.configData[bearerKey];
|
|
||||||
if !exists {return false}
|
|
||||||
|
|
||||||
for _, matchTo := range paths {
|
|
||||||
if match(request, matchTo) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
|
@ -13,8 +13,16 @@ func splitPath(path string) []string {
|
||||||
|
|
||||||
/* Attempts to match a request path to a set of whitelisted paths
|
/* Attempts to match a request path to a set of whitelisted paths
|
||||||
@return false for anything other than a valid match */
|
@return false for anything other than a valid match */
|
||||||
func match(request string, matchTo string) bool {
|
func match(request string, matchTo []string) bool {
|
||||||
return matchSegments(splitPath(request), splitPath(matchTo));
|
requestSplit := splitPath(request)
|
||||||
|
for _, matchToAttempt := range matchTo {
|
||||||
|
matchToAttemptSplit := splitPath(matchToAttempt);
|
||||||
|
if matchSegments(requestSplit, matchToAttemptSplit) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns false for anything other than a valid match */
|
/* Returns false for anything other than a valid match */
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
||||||
/* This file handles listening to HTTP requests */
|
|
||||||
|
|
||||||
import (
|
|
||||||
"signal-cli-http/conf"
|
|
||||||
"signal-cli-http/subprocess"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func StartWebserver(port int) {
|
|
||||||
http.HandleFunc("/", getRoot)
|
|
||||||
|
|
||||||
err := http.ListenAndServe(":"+fmt.Sprint(port), nil)
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRoot(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Check that Authentication header exists
|
|
||||||
authArr, ok := r.Header["Authentication"]
|
|
||||||
if (!ok) || (len(authArr) == 0) {
|
|
||||||
w.WriteHeader(400);
|
|
||||||
w.Write([]byte("Authentication header missing\n"))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bearer := authArr[0];
|
|
||||||
|
|
||||||
// Check that the request is allowed for the path
|
|
||||||
if !conf.GlobalConfig.ValidateBearerKey(bearer, r.URL.Path) {
|
|
||||||
w.WriteHeader(403);
|
|
||||||
w.Write([]byte("Bearer key not whitelisted for this path\n"))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK authentication wise
|
|
||||||
|
|
||||||
body, err := io.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500);
|
|
||||||
w.Write([]byte("Error reading body\n"))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call subprocess
|
|
||||||
status, bodyContent, err := subprocess.Run(r.URL.Path, body)
|
|
||||||
|
|
||||||
// Error
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500);
|
|
||||||
w.Write([]byte("Internal server error: " + err.Error() + "\n"));
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Respond to client with status
|
|
||||||
w.WriteHeader(status);
|
|
||||||
w.Write(bodyContent);
|
|
||||||
|
|
||||||
// Log the request
|
|
||||||
log.Default().Print("HTTP Request: ", bearer, " " , r.URL.Path, " ", status)
|
|
||||||
}
|
|
15
main.go
15
main.go
|
@ -5,8 +5,8 @@ package main
|
||||||
import (
|
import (
|
||||||
"signal-cli-http/args"
|
"signal-cli-http/args"
|
||||||
"signal-cli-http/conf"
|
"signal-cli-http/conf"
|
||||||
"signal-cli-http/http"
|
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,14 +14,11 @@ func main() {
|
||||||
// Read arguments
|
// Read arguments
|
||||||
args.Parse();
|
args.Parse();
|
||||||
configLocation, confLocationSet := args.GetConfLocation();
|
configLocation, confLocationSet := args.GetConfLocation();
|
||||||
if !confLocationSet {log.Default().Print("No config value!"); return}
|
if !confLocationSet {log.Default().Print("No config value!"); return;}
|
||||||
log.Default().Print("Reading config value from ", configLocation);
|
log.Default().Print("Reading config value from ", configLocation);
|
||||||
|
|
||||||
// Set up config
|
// Set up config
|
||||||
conf.GlobalConfig, _ = conf.NewConfig(configLocation);
|
config, err := conf.NewConfig(configLocation);
|
||||||
if conf.GlobalConfig == nil {log.Default().Print("Error reading config"); return}
|
if err != nil {log.Default().Print("Error reading config: ", err); return;}
|
||||||
|
fmt.Println(config.GetConfigData())
|
||||||
port, portSet := args.GetHTTPPort();
|
|
||||||
if !portSet {log.Default().Print("No port value!"); return;}
|
|
||||||
http.StartWebserver(port)
|
|
||||||
}
|
}
|
|
@ -7,9 +7,4 @@ Very simple HTTP frontend to [signal-cli](https://github.com/AsamK/signal-cli).
|
||||||
Please also read the following README files for the individual modules:
|
Please also read the following README files for the individual modules:
|
||||||
|
|
||||||
* [args](args/readme.md) - handles command line arguments.
|
* [args](args/readme.md) - handles command line arguments.
|
||||||
* [conf](conf/readme.md) - handles the config file.
|
* [conf](conf/readme.md) - handles the config file.
|
||||||
|
|
||||||
Too be implemented:
|
|
||||||
|
|
||||||
* subprocess - handles running the binaries which communicate with the daemon.
|
|
||||||
* web - handles the incoming http requests.
|
|
|
@ -1,21 +0,0 @@
|
||||||
package subprocess
|
|
||||||
|
|
||||||
/* This file manages creating the command line arguments to the subprocess */
|
|
||||||
|
|
||||||
/* Method which module http calls to create the subprocess */
|
|
||||||
func Run(path string, body []byte) (status int, bodyContents []byte, err error) {
|
|
||||||
arguments := getArguments(path, body);
|
|
||||||
|
|
||||||
// Don't know what to do with this request
|
|
||||||
if arguments == nil {return 404, []byte("Unknown request\n"), nil;}
|
|
||||||
|
|
||||||
// Call subprocess
|
|
||||||
status, bodyContents, err = runCommand(arguments);
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Converts a request into the correct binary arguments */
|
|
||||||
func getArguments(path string, body []byte) []string {
|
|
||||||
return nil // For now
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package subprocess
|
|
||||||
|
|
||||||
import (
|
|
||||||
"signal-cli-http/args"
|
|
||||||
|
|
||||||
"errors"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
/* This file manages calling signal-cli */
|
|
||||||
|
|
||||||
/* Runs the command */
|
|
||||||
func runCommand(arguments []string) (returnStatus int, bodyContent []byte, err error) {
|
|
||||||
// Get binary location
|
|
||||||
binary, ok := args.GetBinaryLocation();
|
|
||||||
if !ok {
|
|
||||||
err = errors.New("Binary cannot be found!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create command using binary location and arguments
|
|
||||||
command := exec.Command(binary, strings.Join(arguments, " "));
|
|
||||||
|
|
||||||
// Duplicate pointer into command.Stdout
|
|
||||||
//command.Stdout = &bodyContent;
|
|
||||||
|
|
||||||
// Run the command
|
|
||||||
err = command.Run();
|
|
||||||
if err != nil {return}
|
|
||||||
|
|
||||||
// Extract exit code if possible
|
|
||||||
if exitError, ok := err.(*exec.ExitError); ok {
|
|
||||||
returnStatus = exitError.ExitCode();
|
|
||||||
} else {
|
|
||||||
err = errors.New("Cannot get exit code!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get output
|
|
||||||
bodyContent, err = command.Output();
|
|
||||||
|
|
||||||
// Named return values allow this
|
|
||||||
return;
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue