mirror of
https://github.com/bbernhard/signal-cli-rest-api.git
synced 2026-09-20 06:09:28 +00:00
Compare commits
6 Commits
4a53e693e5
...
fb0f6b26f8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb0f6b26f8 | ||
|
|
fe9df012f1 | ||
|
|
ede08b64d8 | ||
|
|
aa8709359f | ||
|
|
6c774054fd | ||
|
|
0651bca94b |
@ -304,7 +304,10 @@ func (a *Api) RegisterNumber(c *gin.Context) {
|
||||
var req RegisterNumberRequest
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(c.Request.Body)
|
||||
if _, err := buf.ReadFrom(c.Request.Body); err != nil {
|
||||
c.JSON(400, Error{Msg: "Couldn't process request - failed to read body."})
|
||||
return
|
||||
}
|
||||
if buf.String() != "" {
|
||||
err := json.Unmarshal(buf.Bytes(), &req)
|
||||
if err != nil {
|
||||
@ -356,7 +359,10 @@ func (a *Api) UnregisterNumber(c *gin.Context) {
|
||||
deleteAccount := false
|
||||
deleteLocalData := false
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(c.Request.Body)
|
||||
if _, err := buf.ReadFrom(c.Request.Body); err != nil {
|
||||
c.JSON(400, Error{Msg: "Couldn't process request - failed to read body."})
|
||||
return
|
||||
}
|
||||
if buf.String() != "" {
|
||||
var req UnregisterNumberRequest
|
||||
err := json.Unmarshal(buf.Bytes(), &req)
|
||||
@ -436,7 +442,10 @@ func (a *Api) VerifyRegisteredNumber(c *gin.Context) {
|
||||
pin := ""
|
||||
var req VerifyNumberSettings
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(c.Request.Body)
|
||||
if _, err := buf.ReadFrom(c.Request.Body); err != nil {
|
||||
c.JSON(400, Error{Msg: "Couldn't process request - failed to read body."})
|
||||
return
|
||||
}
|
||||
if buf.String() != "" {
|
||||
err := json.Unmarshal(buf.Bytes(), &req)
|
||||
if err != nil {
|
||||
@ -588,7 +597,9 @@ func (a *Api) handleSignalReceive(ws *websocket.Conn, number string, stop chan s
|
||||
select {
|
||||
case <-stop:
|
||||
a.signalClient.RemoveReceiveChannel(channelUuid)
|
||||
ws.Close()
|
||||
if err := ws.Close(); err != nil {
|
||||
log.Debug("Error closing websocket: ", err.Error())
|
||||
}
|
||||
return
|
||||
case msg := <-receiveChannel:
|
||||
var data string = string(msg.Params)
|
||||
@ -645,7 +656,9 @@ func (a *Api) handleSignalReceive(ws *websocket.Conn, number string, stop chan s
|
||||
func wsPong(ws *websocket.Conn, stop chan struct{}) {
|
||||
defer func() {
|
||||
close(stop)
|
||||
ws.Close()
|
||||
if err := ws.Close(); err != nil {
|
||||
log.Debug("Error closing websocket: ", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
ws.SetReadLimit(512)
|
||||
@ -663,7 +676,9 @@ func (a *Api) wsPing(ws *websocket.Conn, stop chan struct{}) {
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
ws.Close()
|
||||
if err := ws.Close(); err != nil {
|
||||
log.Debug("Error closing websocket: ", err.Error())
|
||||
}
|
||||
return
|
||||
case <-pingTicker.C:
|
||||
a.wsMutex.Lock()
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
uuid "github.com/gofrs/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type AttachmentEntry struct {
|
||||
@ -93,7 +94,7 @@ func (attachmentEntry *AttachmentEntry) storeBase64AsTemporaryFile() error {
|
||||
|
||||
attachmentEntry.DirName = dirNameUuid.String()
|
||||
dirPath := attachmentEntry.attachmentTmpDir + attachmentEntry.DirName
|
||||
if err := os.Mkdir(dirPath, os.ModePerm); err != nil {
|
||||
if err := os.Mkdir(dirPath, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -103,29 +104,41 @@ func (attachmentEntry *AttachmentEntry) storeBase64AsTemporaryFile() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.Write(dec); err != nil {
|
||||
if closeErr := f.Close(); closeErr != nil {
|
||||
log.Error("Couldn't close attachment tmp file: ", closeErr.Error())
|
||||
}
|
||||
attachmentEntry.cleanUp()
|
||||
return err
|
||||
}
|
||||
if err := f.Sync(); err != nil {
|
||||
if closeErr := f.Close(); closeErr != nil {
|
||||
log.Error("Couldn't close attachment tmp file: ", closeErr.Error())
|
||||
}
|
||||
attachmentEntry.cleanUp()
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
attachmentEntry.cleanUp()
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (attachmentEntry *AttachmentEntry) cleanUp() {
|
||||
if strings.Compare(attachmentEntry.FilePath, "") != 0 {
|
||||
os.Remove(attachmentEntry.FilePath)
|
||||
if err := os.Remove(attachmentEntry.FilePath); err != nil {
|
||||
log.Error("Couldn't remove file ", attachmentEntry.FilePath, ": ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Compare(attachmentEntry.DirName, "") != 0 {
|
||||
dirPath := attachmentEntry.attachmentTmpDir + attachmentEntry.DirName
|
||||
os.Remove(dirPath)
|
||||
if err := os.Remove(dirPath); err != nil {
|
||||
log.Error("Couldn't remove directory ", dirPath, ": ", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,11 +4,13 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
utils "github.com/bbernhard/signal-cli-rest-api/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
utils "github.com/bbernhard/signal-cli-rest-api/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type CliClient struct {
|
||||
@ -104,7 +106,12 @@ func (s *CliClient) Execute(wait bool, args []string, stdin string) (string, err
|
||||
cmdTimeout = 120
|
||||
}
|
||||
|
||||
cmd := exec.Command(signalCliBinary, args...)
|
||||
resolvedBinary, err := exec.LookPath(signalCliBinary)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("signal-cli binary '%s' not found in PATH: %w", signalCliBinary, err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(resolvedBinary, args...) // #nosec G204 -- resolvedBinary is the LookPath-resolved path of a hardcoded binary name ("signal-cli" or "signal-cli-native"), not user-controlled
|
||||
if stdin != "" {
|
||||
cmd.Stdin = strings.NewReader(stdin)
|
||||
}
|
||||
@ -161,7 +168,9 @@ func (s *CliClient) Execute(wait bool, args []string, stdin string) (string, err
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cmd.Start()
|
||||
if err := cmd.Start(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf := bufio.NewReader(stdout) // Notice that this is not in a loop
|
||||
line, _, _ := buf.ReadLine()
|
||||
return string(line), nil
|
||||
|
||||
@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@ -119,6 +118,7 @@ type GroupEntry struct {
|
||||
InternalId string `json:"internal_id"`
|
||||
Members []string `json:"members"`
|
||||
Blocked bool `json:"blocked"`
|
||||
Member bool `json:"member"`
|
||||
PendingInvites []string `json:"pending_invites"`
|
||||
PendingRequests []string `json:"pending_requests"`
|
||||
InviteLink string `json:"invite_link"`
|
||||
@ -143,6 +143,7 @@ type ExpandedGroupEntry struct {
|
||||
InternalId string `json:"internal_id"`
|
||||
Members []GroupMember `json:"members"`
|
||||
Blocked bool `json:"blocked"`
|
||||
Member bool `json:"member"`
|
||||
PendingInvites []GroupMember `json:"pending_invites"`
|
||||
PendingRequests []GroupMember `json:"pending_requests"`
|
||||
InviteLink string `json:"invite_link"`
|
||||
@ -258,7 +259,9 @@ type ListDevicesResponse struct {
|
||||
|
||||
func cleanupTmpFiles(paths []string) {
|
||||
for _, path := range paths {
|
||||
os.Remove(path)
|
||||
if err := os.Remove(path); err != nil {
|
||||
log.Error("Couldn't remove tmp file ", path, ": ", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -743,7 +746,7 @@ func (s *SignalClient) About() About {
|
||||
BuildNr: 2,
|
||||
Mode: getSignalCliModeString(s.signalCliMode),
|
||||
Version: utils.GetEnv("BUILD_VERSION", "unset"),
|
||||
Capabilities: map[string][]string{"v2/send": []string{"quotes", "mentions"}},
|
||||
Capabilities: map[string][]string{"v2/send": {"quotes", "mentions"}},
|
||||
}
|
||||
return about
|
||||
}
|
||||
@ -1137,8 +1140,7 @@ func (s *SignalClient) CreateGroup(number string, name string, members []string,
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
var resp Response
|
||||
json.Unmarshal([]byte(rawData), &resp)
|
||||
if err != nil {
|
||||
if err := json.Unmarshal([]byte(rawData), &resp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
internalGroupId = resp.GroupId
|
||||
@ -1330,6 +1332,25 @@ func (s *SignalClient) RemoveAdminsFromGroup(number string, groupId string, admi
|
||||
return s.updateGroupAdmins(number, groupId, admins, false)
|
||||
}
|
||||
|
||||
func signalCliGroupEntryToExpandedGroupEntry(signalCliGroupEntry SignalCliGroupEntry) ExpandedGroupEntry {
|
||||
var groupEntry ExpandedGroupEntry
|
||||
groupEntry.InternalId = signalCliGroupEntry.Id
|
||||
groupEntry.Name = signalCliGroupEntry.Name
|
||||
groupEntry.Id = convertInternalGroupIdToGroupId(signalCliGroupEntry.Id)
|
||||
groupEntry.Blocked = signalCliGroupEntry.IsBlocked
|
||||
groupEntry.Member = signalCliGroupEntry.IsMember
|
||||
groupEntry.Description = signalCliGroupEntry.Description
|
||||
groupEntry.Permissions.SendMessages = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionSendMessage)
|
||||
groupEntry.Permissions.EditGroup = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionSendMessage)
|
||||
groupEntry.Permissions.AddMembers = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionAddMember)
|
||||
groupEntry.Members = signalCliGroupEntry.Members
|
||||
groupEntry.PendingInvites = signalCliGroupEntry.PendingMembers
|
||||
groupEntry.PendingRequests = signalCliGroupEntry.RequestingMembers
|
||||
groupEntry.Admins = signalCliGroupEntry.Admins
|
||||
groupEntry.InviteLink = signalCliGroupEntry.GroupInviteLink
|
||||
return groupEntry
|
||||
}
|
||||
|
||||
func (s *SignalClient) GetGroupsExpanded(number string) ([]ExpandedGroupEntry, error) {
|
||||
groupEntries := []ExpandedGroupEntry{}
|
||||
|
||||
@ -1360,22 +1381,7 @@ func (s *SignalClient) GetGroupsExpanded(number string) ([]ExpandedGroupEntry, e
|
||||
}
|
||||
|
||||
for _, signalCliGroupEntry := range signalCliGroupEntries {
|
||||
var groupEntry ExpandedGroupEntry
|
||||
groupEntry.InternalId = signalCliGroupEntry.Id
|
||||
groupEntry.Name = signalCliGroupEntry.Name
|
||||
groupEntry.Id = convertInternalGroupIdToGroupId(signalCliGroupEntry.Id)
|
||||
groupEntry.Blocked = signalCliGroupEntry.IsBlocked
|
||||
groupEntry.Description = signalCliGroupEntry.Description
|
||||
groupEntry.Permissions.SendMessages = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionSendMessage)
|
||||
groupEntry.Permissions.EditGroup = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionSendMessage)
|
||||
groupEntry.Permissions.AddMembers = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionAddMember)
|
||||
groupEntry.Members = signalCliGroupEntry.Members
|
||||
groupEntry.PendingInvites = signalCliGroupEntry.PendingMembers
|
||||
groupEntry.PendingRequests = signalCliGroupEntry.RequestingMembers
|
||||
groupEntry.Admins = signalCliGroupEntry.Admins
|
||||
groupEntry.InviteLink = signalCliGroupEntry.GroupInviteLink
|
||||
|
||||
groupEntries = append(groupEntries, groupEntry)
|
||||
groupEntries = append(groupEntries, signalCliGroupEntryToExpandedGroupEntry(signalCliGroupEntry))
|
||||
}
|
||||
|
||||
return groupEntries, nil
|
||||
@ -1390,7 +1396,7 @@ func (s *SignalClient) GetGroups(number string) ([]GroupEntry, error) {
|
||||
groupEntries := []GroupEntry{}
|
||||
for _, expandedGroupEntry := range expandedGroupEntries {
|
||||
groupEntry := GroupEntry{InternalId: expandedGroupEntry.InternalId, Name: expandedGroupEntry.Name,
|
||||
Id: expandedGroupEntry.Id, Blocked: expandedGroupEntry.Blocked, Description: expandedGroupEntry.Description,
|
||||
Id: expandedGroupEntry.Id, Blocked: expandedGroupEntry.Blocked, Member: expandedGroupEntry.Member, Description: expandedGroupEntry.Description,
|
||||
Permissions: expandedGroupEntry.Permissions, InviteLink: expandedGroupEntry.InviteLink}
|
||||
|
||||
members := []string{}
|
||||
@ -1737,7 +1743,9 @@ func (s *SignalClient) finishLinkAsync(jsonRpc2Client *JsonRpc2Client, deviceNam
|
||||
return
|
||||
}
|
||||
log.Debug("Linking device result: ", result)
|
||||
s.signalCliApiConfig.Load(s.signalCliApiConfigPath)
|
||||
if err := s.signalCliApiConfig.Load(s.signalCliApiConfigPath); err != nil {
|
||||
log.Error("Couldn't reload signal-cli API config after linking device: ", err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@ -1830,7 +1838,7 @@ func (s *SignalClient) GetAttachment(attachment string) ([]byte, error) {
|
||||
return []byte{}, &NotFoundError{Description: "No attachment with that name found"}
|
||||
}
|
||||
|
||||
attachmentBytes, err := ioutil.ReadFile(path)
|
||||
attachmentBytes, err := os.ReadFile(path) // #nosec G304 -- path is secured by securejoin.SecureJoin above
|
||||
if err != nil {
|
||||
return []byte{}, &InternalError{Description: "Couldn't read attachment - please try again later"}
|
||||
}
|
||||
@ -1857,23 +1865,38 @@ func (s *SignalClient) UpdateProfile(number string, profileName string, base64Av
|
||||
return err
|
||||
}
|
||||
|
||||
avatarTmpPath = s.avatarTmpDir + u.String() + "." + fType.Extension
|
||||
avatarFilename := u.String() + "." + fType.Extension
|
||||
avatarTmpPath = filepath.Join(s.avatarTmpDir, avatarFilename)
|
||||
|
||||
f, err := os.Create(avatarTmpPath)
|
||||
avatarRoot, err := os.OpenRoot(s.avatarTmpDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer avatarRoot.Close()
|
||||
|
||||
f, err := avatarRoot.Create(avatarFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.Write(avatarBytes); err != nil {
|
||||
if closeErr := f.Close(); closeErr != nil {
|
||||
log.Error("Couldn't close avatar tmp file: ", closeErr.Error())
|
||||
}
|
||||
cleanupTmpFiles([]string{avatarTmpPath})
|
||||
return err
|
||||
}
|
||||
if err := f.Sync(); err != nil {
|
||||
if closeErr := f.Close(); closeErr != nil {
|
||||
log.Error("Couldn't close avatar tmp file: ", closeErr.Error())
|
||||
}
|
||||
cleanupTmpFiles([]string{avatarTmpPath})
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
cleanupTmpFiles([]string{avatarTmpPath})
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
|
||||
if s.signalCliMode == JsonRpc {
|
||||
@ -2072,23 +2095,38 @@ func (s *SignalClient) UpdateGroup(number string, groupId string, base64Avatar *
|
||||
return err
|
||||
}
|
||||
|
||||
avatarTmpPath = s.avatarTmpDir + u.String() + "." + fType.Extension
|
||||
avatarFilename := u.String() + "." + fType.Extension
|
||||
avatarTmpPath = filepath.Join(s.avatarTmpDir, avatarFilename)
|
||||
|
||||
f, err := os.Create(avatarTmpPath)
|
||||
avatarRoot, err := os.OpenRoot(s.avatarTmpDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer avatarRoot.Close()
|
||||
|
||||
f, err := avatarRoot.Create(avatarFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.Write(avatarBytes); err != nil {
|
||||
if closeErr := f.Close(); closeErr != nil {
|
||||
log.Error("Couldn't close avatar tmp file: ", closeErr.Error())
|
||||
}
|
||||
cleanupTmpFiles([]string{avatarTmpPath})
|
||||
return err
|
||||
}
|
||||
if err := f.Sync(); err != nil {
|
||||
if closeErr := f.Close(); closeErr != nil {
|
||||
log.Error("Couldn't close avatar tmp file: ", closeErr.Error())
|
||||
}
|
||||
cleanupTmpFiles([]string{avatarTmpPath})
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
cleanupTmpFiles([]string{avatarTmpPath})
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
|
||||
if s.signalCliMode == JsonRpc {
|
||||
|
||||
93
src/client/groups_test.go
Normal file
93
src/client/groups_test.go
Normal file
@ -0,0 +1,93 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// sampleListGroupsJSON mirrors the shape of signal-cli's `listGroups` JSON output
|
||||
// (see signal-cli's ListGroupsCommand / the jsonrpc man page). It contains a group
|
||||
// the account is still in (isMember=true), a group it has left or been removed from
|
||||
// (isMember=false) — the "ghost" group that the REST API previously could not
|
||||
// distinguish — and a blocked-but-still-member group to verify the two flags are
|
||||
// mapped independently.
|
||||
const sampleListGroupsJSON = `[
|
||||
{
|
||||
"id": "Pmpi+EfPWmsxiomLe9Nx2XF9HOE483p6iKiFj65iMwI=",
|
||||
"name": "Current Group",
|
||||
"description": "still a member",
|
||||
"isMember": true,
|
||||
"isBlocked": false,
|
||||
"members": [{"number": "+15551230001", "uuid": "11111111-1111-1111-1111-111111111111"}],
|
||||
"pendingMembers": [],
|
||||
"requestingMembers": [],
|
||||
"admins": [{"number": "+15551230001", "uuid": "11111111-1111-1111-1111-111111111111"}],
|
||||
"groupInviteLink": "",
|
||||
"permissionAddMember": "EVERY_MEMBER",
|
||||
"permissionSendMessage": "EVERY_MEMBER"
|
||||
},
|
||||
{
|
||||
"id": "Zm9vYmFyYmF6cXV4MTIzNDU2Nzg5MGFiY2RlZmdoaWo=",
|
||||
"name": "Left Group",
|
||||
"description": "removed or left",
|
||||
"isMember": false,
|
||||
"isBlocked": false,
|
||||
"members": [],
|
||||
"pendingMembers": [],
|
||||
"requestingMembers": [],
|
||||
"admins": [],
|
||||
"groupInviteLink": ""
|
||||
},
|
||||
{
|
||||
"id": "YmxvY2tlZGdyb3VwaWQwMDAwMDAwMDAwMDAwMDAwMDA=",
|
||||
"name": "Blocked But Member",
|
||||
"description": "blocked yet still a member",
|
||||
"isMember": true,
|
||||
"isBlocked": true,
|
||||
"members": [],
|
||||
"pendingMembers": [],
|
||||
"requestingMembers": [],
|
||||
"admins": [],
|
||||
"groupInviteLink": ""
|
||||
}
|
||||
]`
|
||||
|
||||
// TestSignalCliGroupEntryToExpandedGroupEntry verifies that the signal-cli isMember
|
||||
// flag is carried through to the REST ExpandedGroupEntry.Member field, independently
|
||||
// of the isBlocked -> Blocked mapping.
|
||||
func TestSignalCliGroupEntryToExpandedGroupEntry(t *testing.T) {
|
||||
var entries []SignalCliGroupEntry
|
||||
if err := json.Unmarshal([]byte(sampleListGroupsJSON), &entries); err != nil {
|
||||
t.Fatalf("failed to unmarshal sample listGroups JSON: %v", err)
|
||||
}
|
||||
if len(entries) != 3 {
|
||||
t.Fatalf("expected 3 group entries, got %d", len(entries))
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
wantMember bool
|
||||
wantBlocked bool
|
||||
}{
|
||||
{"Current Group", true, false},
|
||||
{"Left Group", false, false},
|
||||
{"Blocked But Member", true, true},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
got := signalCliGroupEntryToExpandedGroupEntry(entries[i])
|
||||
|
||||
if got.Name != c.name {
|
||||
t.Errorf("entry %d: Name = %q, want %q", i, got.Name, c.name)
|
||||
}
|
||||
if got.Member != c.wantMember {
|
||||
t.Errorf("%s: Member = %v, want %v (must reflect signal-cli isMember)", c.name, got.Member, c.wantMember)
|
||||
}
|
||||
if got.Blocked != c.wantBlocked {
|
||||
t.Errorf("%s: Blocked = %v, want %v", c.name, got.Blocked, c.wantBlocked)
|
||||
}
|
||||
if got.InternalId != entries[i].Id {
|
||||
t.Errorf("%s: InternalId = %q, want %q", c.name, got.InternalId, entries[i].Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -222,7 +222,9 @@ func (r *JsonRpc2Client) ReceiveData(number string, receiveWebhookUrl string) {
|
||||
str, err := connbuf.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Error("Lost connection to signal-cli...attempting to reconnect (", err.Error(), ")")
|
||||
r.conn.Close()
|
||||
if err := r.conn.Close(); err != nil {
|
||||
log.Warn("Error closing connection to signal-cli: ", err.Error())
|
||||
}
|
||||
err = r.Dial(r.address, 15)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to reconnect to signal-cli: ", err.Error(), "...aborting")
|
||||
@ -234,7 +236,10 @@ func (r *JsonRpc2Client) ReceiveData(number string, receiveWebhookUrl string) {
|
||||
log.Debug("json-rpc received data: ", str)
|
||||
|
||||
var resp1 JsonRpc2ReceivedMessage
|
||||
json.Unmarshal([]byte(str), &resp1)
|
||||
if err := json.Unmarshal([]byte(str), &resp1); err != nil {
|
||||
log.Warn("Couldn't parse received message: ", err.Error())
|
||||
continue
|
||||
}
|
||||
if resp1.Method == "receive" {
|
||||
r.receivedMessagesMutex.Lock()
|
||||
for _, c := range r.receivedMessagesChannels {
|
||||
|
||||
@ -806,6 +806,9 @@ const docTemplate = `{
|
||||
"invite_link": {
|
||||
"type": "string"
|
||||
},
|
||||
"member": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"members": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
@ -838,6 +841,7 @@ const docTemplate = `{
|
||||
"id",
|
||||
"internal_id",
|
||||
"invite_link",
|
||||
"member",
|
||||
"members",
|
||||
"name",
|
||||
"pending_invites",
|
||||
|
||||
@ -801,6 +801,9 @@
|
||||
"invite_link": {
|
||||
"type": "string"
|
||||
},
|
||||
"member": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"members": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
@ -833,6 +836,7 @@
|
||||
"id",
|
||||
"internal_id",
|
||||
"invite_link",
|
||||
"member",
|
||||
"members",
|
||||
"name",
|
||||
"pending_invites",
|
||||
|
||||
21
src/main.go
21
src/main.go
@ -3,9 +3,10 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"plugin"
|
||||
"strconv"
|
||||
|
||||
@ -75,7 +76,9 @@ func main() {
|
||||
err := utils.SetLogLevel(logLevel)
|
||||
if err != nil {
|
||||
log.Error("Couldn't set log level to '", logLevel, "'. Falling back to the info log level")
|
||||
utils.SetLogLevel("info")
|
||||
if err := utils.SetLogLevel("info"); err != nil {
|
||||
log.Error("Couldn't set fallback log level to 'info': ", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -416,9 +419,9 @@ func main() {
|
||||
|
||||
c := cron.New()
|
||||
c.Schedule(schedule, cron.FuncJob(func() {
|
||||
accountsJsonPath := *signalCliConfig + "/data/accounts.json"
|
||||
accountsJsonPath := filepath.Clean(*signalCliConfig + "/data/accounts.json")
|
||||
if _, err := os.Stat(accountsJsonPath); err == nil {
|
||||
signalCliConfigJsonData, err := ioutil.ReadFile(accountsJsonPath)
|
||||
signalCliConfigJsonData, err := os.ReadFile(accountsJsonPath)
|
||||
if err != nil {
|
||||
log.Fatal("AUTO_RECEIVE_SCHEDULE: Couldn't read accounts.json: ", err.Error())
|
||||
}
|
||||
@ -452,8 +455,10 @@ func main() {
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
jsonResp, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
jsonResp, err := io.ReadAll(resp.Body)
|
||||
if closeErr := resp.Body.Close(); closeErr != nil {
|
||||
log.Error("AUTO_RECEIVE_SCHEDULE: Couldn't close response body: ", closeErr.Error())
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("AUTO_RECEIVE_SCHEDULE: Couldn't read json response: ", err.Error())
|
||||
continue
|
||||
@ -479,5 +484,7 @@ func main() {
|
||||
c.Start()
|
||||
}
|
||||
|
||||
router.Run()
|
||||
if err := router.Run(); err != nil {
|
||||
log.Fatal("Couldn't start HTTP router: ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,4 +226,4 @@ func (p plugHandler) InitPlugin(pluginConfig utils.PluginConfig) error {
|
||||
}
|
||||
|
||||
// exported
|
||||
var PluginHandler plugHandler
|
||||
var PluginHandler plugHandler
|
||||
@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
@ -104,7 +104,7 @@ func main() {
|
||||
signalCliIgnoreAvatars, signalCliIgnoreStickers, tcpPort,
|
||||
supervisorctlProgramName, supervisorctlProgramName)
|
||||
|
||||
err = ioutil.WriteFile(supervisorctlConfigFilename, []byte(supervisorctlConfig), 0644)
|
||||
err = os.WriteFile(supervisorctlConfigFilename, []byte(supervisorctlConfig), 0600)
|
||||
if err != nil {
|
||||
log.Fatal("Couldn't write ", supervisorctlConfigFilename, ": ", err.Error())
|
||||
}
|
||||
|
||||
@ -2,9 +2,10 @@ package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type SignalCliTrustMode int
|
||||
@ -55,9 +56,10 @@ func NewSignalCliApiConfig() *SignalCliApiConfig {
|
||||
}
|
||||
|
||||
func (c *SignalCliApiConfig) Load(path string) error {
|
||||
c.path = path
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
cleanPath := filepath.Clean(path)
|
||||
c.path = cleanPath
|
||||
if _, err := os.Stat(cleanPath); err == nil {
|
||||
data, err := os.ReadFile(cleanPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -92,5 +94,5 @@ func (c *SignalCliApiConfig) Persist() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(c.path, out, 0644)
|
||||
return os.WriteFile(c.path, out, 0600)
|
||||
}
|
||||
|
||||
@ -2,7 +2,8 @@ package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
@ -26,7 +27,7 @@ func NewJsonRpc2ClientConfig() *JsonRpc2ClientConfig {
|
||||
}
|
||||
|
||||
func (c *JsonRpc2ClientConfig) Load(path string) error {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
data, err := os.ReadFile(filepath.Clean(path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -69,5 +70,5 @@ func (c *JsonRpc2ClientConfig) Persist(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(path, out, 0644)
|
||||
return os.WriteFile(filepath.Clean(path), out, 0600)
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -25,33 +26,48 @@ type PluginConfigs struct {
|
||||
}
|
||||
|
||||
func (c *PluginConfigs) Load(baseDirectory string) error {
|
||||
baseDirectory = filepath.Clean(baseDirectory)
|
||||
root, err := os.OpenRoot(baseDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer root.Close()
|
||||
|
||||
err := filepath.Walk(baseDirectory, func(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
err = filepath.WalkDir(baseDirectory, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if filepath.Ext(path) != ".def" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pluginConfig PluginConfig
|
||||
pluginConfig.Version = 1
|
||||
err = yaml.Unmarshal(data, &pluginConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pluginConfig.ScriptPath = strings.TrimSuffix(path, filepath.Ext(path)) + ".lua"
|
||||
c.Configs = append(c.Configs, pluginConfig)
|
||||
relPath, err := filepath.Rel(baseDirectory, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := root.Open(relPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pluginConfig PluginConfig
|
||||
pluginConfig.Version = 1 // default; overridden by yaml if present
|
||||
if err = yaml.Unmarshal(data, &pluginConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
pluginConfig.ScriptPath = strings.TrimSuffix(path, filepath.Ext(path)) + ".lua"
|
||||
c.Configs = append(c.Configs, pluginConfig)
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func GetEnv(key string, defaultVal string) string {
|
||||
|
||||
@ -11,32 +11,31 @@ func expectEqual(t *testing.T, in1 bool, in2 bool) {
|
||||
}
|
||||
|
||||
func TestIsPhoneNumber(t *testing.T) {
|
||||
res := IsPhoneNumber("+12345678")
|
||||
res := IsPhoneNumber("+12345678")
|
||||
expectEqual(t, res, true)
|
||||
}
|
||||
|
||||
|
||||
func TestIsPhoneNumberWithSpaces(t *testing.T) {
|
||||
res := IsPhoneNumber("+ 12345678")
|
||||
res := IsPhoneNumber("+ 12345678")
|
||||
expectEqual(t, res, true)
|
||||
}
|
||||
|
||||
func TestIsPhoneNumberWithSpaces1(t *testing.T) {
|
||||
res := IsPhoneNumber("+ 1234 5678")
|
||||
res := IsPhoneNumber("+ 1234 5678")
|
||||
expectEqual(t, res, true)
|
||||
}
|
||||
|
||||
func TestIsPhoneNumberWithInvalidCharacters(t *testing.T) {
|
||||
res := IsPhoneNumber("+123456x")
|
||||
res := IsPhoneNumber("+123456x")
|
||||
expectEqual(t, res, false)
|
||||
}
|
||||
|
||||
func TestIsPhoneNumberWithMissingPrefix(t *testing.T) {
|
||||
res := IsPhoneNumber("123456x")
|
||||
res := IsPhoneNumber("123456x")
|
||||
expectEqual(t, res, false)
|
||||
}
|
||||
|
||||
func TestIsPhoneNumberWithInvalidCharactersAndSpaces(t *testing.T) {
|
||||
res := IsPhoneNumber("+12345 6x")
|
||||
res := IsPhoneNumber("+12345 6x")
|
||||
expectEqual(t, res, false)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user