├── log └── .keepme ├── auth ├── constants.go ├── claims.go └── middleware.go ├── .github ├── FUNDING.yml └── workflows │ ├── star.yml │ ├── codeql-analysis.yml │ └── build.yml ├── controller └── v1 │ ├── controller.go │ ├── websocket.go │ ├── hepsubsearch.go │ ├── profile.go │ └── remote.go ├── system ├── webcodes │ └── weberrorcodes.go └── webmessages │ └── webmessages.go ├── .gitignore ├── model ├── databasesMap.go ├── versions.go ├── profile.go ├── applications.go ├── rawData.go ├── system.go ├── remote.go ├── alias.go ├── hepsub.go ├── globalSettings.go ├── prometheus.go ├── dashboard.go ├── userSettings.go ├── agentLocation.go ├── statistic.go ├── mappingSchema.go └── authToken.go ├── utils ├── sipparser │ ├── fuzz.go │ ├── params_test.go │ ├── internal │ │ ├── machine.rl │ │ └── machine.go │ ├── params.go │ ├── warning.go │ ├── rack_test.go │ ├── diversion_test.go │ ├── warning_test.go │ ├── accept_test.go │ ├── contentdisposition.go │ ├── cseq.go │ ├── cseq_test.go │ ├── remotepartyid_test.go │ ├── rack.go │ ├── authorization_test.go │ ├── reason_test.go │ ├── accept.go │ ├── authorization.go │ ├── passertedid_test.go │ ├── reason.go │ ├── contentdisposition_test.go │ ├── uri_test.go │ ├── utils_test.go │ ├── utils.go │ ├── diversion.go │ ├── remotepartyid.go │ ├── startline_test.go │ ├── via_test.go │ ├── passertedid.go │ ├── from_test.go │ ├── from.go │ ├── via.go │ └── startline.go ├── logger │ ├── function │ │ ├── function.go │ │ └── utils.go │ ├── echologrus │ │ └── echologrus.go │ └── logger.go └── httpauth │ └── httpauth.go ├── docker ├── docker-entrypoint.sh ├── docker-entrypoint.d │ └── 9 └── webapp_config.json ├── scripts ├── swagger_build.sh ├── build_binary.sh ├── build_package.sh ├── build_frontend.sh ├── build_docker.sh ├── DEVELOPERS.md ├── build_local_frontend.sh └── github-release.sh ├── version.go ├── router └── v1 │ ├── websocket.go │ ├── hepsubsearch.go │ ├── alias.go │ ├── remote.go │ ├── userSettings.go │ ├── advanced.go │ ├── profile.go │ ├── prometheus.go │ ├── hepsub.go │ ├── authtoken.go │ ├── statistic.go │ ├── dashboard.go │ ├── mapping.go │ ├── grafana.go │ ├── agentsSub.go │ ├── search.go │ └── user.go ├── homer-app.service ├── Makefile ├── network └── response │ └── httpresponse.go ├── .goreleaser.yml ├── Dockerfile ├── data └── service │ ├── hepsubsearch.go │ ├── service.go │ ├── profile.go │ ├── advanced.go │ ├── agenttoken.go │ ├── hepsub.go │ └── alias.go ├── homer-app.yaml ├── sqlparser └── query │ └── query.go ├── go.mod ├── etc └── dashboard_home.json.example └── config └── config.go /log/.keepme: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /auth/constants.go: -------------------------------------------------------------------------------- 1 | package auth 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [qxip] 2 | custom: ["https://bunq.me/qxip"] 3 | -------------------------------------------------------------------------------- /controller/v1/controller.go: -------------------------------------------------------------------------------- 1 | package controllerv1 2 | 3 | type Controller struct { 4 | } 5 | -------------------------------------------------------------------------------- /system/webcodes/weberrorcodes.go: -------------------------------------------------------------------------------- 1 | package webcodes 2 | 3 | const ( 4 | NoError = 200 5 | ) 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | # direnv and nix-shell 3 | homer-app 4 | .envrc 5 | default.nix 6 | .direnv.* 7 | *.deb 8 | *.rpm 9 | -------------------------------------------------------------------------------- /model/databasesMap.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type DatabasesMap struct { 4 | Value string `json:"value"` 5 | Name string `json:"name"` 6 | } 7 | -------------------------------------------------------------------------------- /utils/sipparser/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package sipparser 4 | 5 | func Fuzz(data []byte) int { 6 | ParseMsg(string(data)) 7 | return 0 8 | } 9 | -------------------------------------------------------------------------------- /docker/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=/docker-entrypoint.d 4 | 5 | if [[ -d "$DIR" ]] 6 | then 7 | run-parts "$DIR" 8 | fi 9 | 10 | exec "$@" 11 | -------------------------------------------------------------------------------- /scripts/swagger_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # CHECK FOR Swagger 4 | if ! [ -x "$(command -v swagger)" ]; then 5 | echo 'Error: swagger is not installed. Exiting...' >&2 6 | exit 1 7 | fi 8 | 9 | swagger generate spec -m -o ./swagger.json 10 | #Docker 11 | cp swagger.json etc 12 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // VERSION 4 | var VERSION_APPLICATION = "1.5.14" 5 | 6 | // NAME 7 | var NAME_APPLICATION = "homer-app" 8 | 9 | func getVersion() string { 10 | return VERSION_APPLICATION 11 | } 12 | 13 | func getName() string { 14 | return NAME_APPLICATION 15 | } 16 | -------------------------------------------------------------------------------- /utils/logger/function/function.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | ) 7 | 8 | func StringToTime(s string) (time.Time, error) { 9 | sec, err := strconv.ParseInt(s, 10, 64) 10 | if err != nil { 11 | return time.Time{}, err 12 | } 13 | return time.Unix(sec, 0), nil 14 | } 15 | -------------------------------------------------------------------------------- /router/v1/websocket.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/labstack/echo/v4" 5 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 6 | ) 7 | 8 | // routesearch Apis 9 | func RouteWebSocketApis(acc *echo.Group, addr string) { 10 | // initialize service of user 11 | // create new user 12 | 13 | wc := controllerv1.WebSocketController{Addr: &addr} 14 | acc.GET("/ws", wc.RelayHepData) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /utils/logger/function/utils.go: -------------------------------------------------------------------------------- 1 | package function 2 | 3 | import "github.com/Jeffail/gabs/v2" 4 | 5 | func KeyExits(value interface{}, keyMap []interface{}) bool { 6 | for _, v := range keyMap { 7 | if value == v { 8 | return true 9 | } 10 | } 11 | return false 12 | } 13 | 14 | func ArrayKeyExits(key string, dataKeys *gabs.Container) bool { 15 | for _, v := range dataKeys.Children() { 16 | if key == v.Data().(string) { 17 | return true 18 | } 19 | } 20 | return false 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/star.yml: -------------------------------------------------------------------------------- 1 | name: Starring Partner 2 | on: 3 | issues: 4 | types: [opened, reopened] 5 | jobs: 6 | # This workflow checks if a user has starred a repository and takes actions 7 | starcheck: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Please Star First 11 | uses: qxip/please-star-light@v4 12 | with: 13 | token: ${{ secrets.GITHUB_TOKEN }} 14 | message: "Your report is appreciated. Please star this repository to motivate its developers! :star:" 15 | label: "stargazed" 16 | -------------------------------------------------------------------------------- /scripts/build_binary.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # CHECK FOR DOCKER 3 | if ! [ -x "$(command -v docker)" ]; then 4 | echo 'Error: docker is not installed. Exiting...' >&2 5 | exit 1 6 | fi 7 | 8 | GOOS=${GOOS:-linux} 9 | 10 | # BUILD GO BINARY 11 | read -p "This will drop and rebuild the homer-app binary from source for ${GOOS}. Continue (y/n)?" CONT 12 | if [ "$CONT" = "y" ]; then 13 | docker run --rm \ 14 | -v $PWD:/app \ 15 | -e GOOS=${GOOS} \ 16 | golang:1.24.5 \ 17 | bash -c "cd /app && make modules && make all" 18 | else 19 | echo "Exiting..." 20 | fi 21 | -------------------------------------------------------------------------------- /homer-app.service: -------------------------------------------------------------------------------- 1 | # cp homer-app.service /etc/systemd/system/ 2 | # systemctl daemon-reload 3 | # systemctl start homer-app 4 | # systemctl enable homer-app 5 | 6 | [Unit] 7 | Description=Homer API Server and UI Webapplication 8 | After=network.target 9 | #Uncomment it only if you have postgresql on the same host! 10 | #After=network.target postgresql.service 11 | 12 | [Service] 13 | WorkingDirectory=/usr/local/homer 14 | ExecStart=/usr/local/bin/homer-app 15 | ExecStop=/bin/kill ${MAINPID} 16 | Restart=on-failure 17 | RestartSec=10s 18 | Type=simple 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /model/versions.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func (TableVersions) TableName() string { 8 | return "versions" 9 | } 10 | 11 | type TableVersions struct { 12 | Id int `gorm:"column:id;primary_key;AUTO_INCREMENT" json:"-"` 13 | // required: true 14 | NameTable string `gorm:"column:table_name;type:varchar(50);not null" json:"table_name" validate:"required"` 15 | // example: 10 16 | VersionTable int `gorm:"column:table_version;type:int;default:10;not null" json:"table_version" validate:"required"` 17 | CreatedAt time.Time `gorm:"column:created_at;default:current_timestamp;not null" json:"-"` 18 | } 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME=homer-app 2 | GOOS ?= linux 3 | 4 | all: 5 | CGO_ENABLED=0 GOOS=$(GOOS) go build -ldflags "-s -w" -buildvcs=false -o $(NAME) 6 | #go build -a -ldflags '-extldflags "-static"' -o $(NAME) 7 | 8 | debug: 9 | go build -o $(NAME) 10 | 11 | modules: 12 | go get ./... 13 | 14 | docker: 15 | ./scripts/build_docker.sh 16 | 17 | package: 18 | ./scripts/build_package.sh 19 | 20 | frontend: 21 | ./scripts/build_frontend.sh 22 | 23 | localfe: 24 | ./scripts/build_local_frontend.sh 25 | 26 | binary: 27 | ./scripts/build_binary.sh 28 | 29 | bundle: 30 | ./scripts/build_binary.sh 31 | ./scripts/build_frontend.sh 32 | ./scripts/build_package.sh 33 | 34 | .PHONY: clean 35 | clean: 36 | rm -fr $(NAME) 37 | -------------------------------------------------------------------------------- /router/v1/hepsubsearch.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 7 | "github.com/sipcapture/homer-app/data/service" 8 | ) 9 | 10 | func RouteHepSubSearch(acc *echo.Group, session *gorm.DB) { 11 | // initialize service of user 12 | HepsubsearchService := service.HepsubsearchService{ServiceConfig: service.ServiceConfig{Session: session}} 13 | // initialize user controller 14 | hss := controllerv1.HepsubsearchController{ 15 | HepsubsearchService: &HepsubsearchService, 16 | } 17 | 18 | // create agent subscribe 19 | /************************************/ 20 | acc.POST("/hepsub/search", hss.DoHepsubsearch) 21 | 22 | } 23 | -------------------------------------------------------------------------------- /utils/sipparser/params_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestGetParam(t *testing.T) { 14 | s := "key=value" 15 | p := getParam(s) 16 | if p.Param != "key" { 17 | t.Errorf("[TestGetParam] Error with getParam parsing \"key=value\".") 18 | } 19 | if p.Val != "value" { 20 | t.Errorf("[TestGetParam] Error with getParam parsing \"key=value\". Bad value.") 21 | } 22 | s = "key" 23 | p = getParam(s) 24 | if p.Val != "" { 25 | t.Errorf("[TestGetParam] Error with getParam. Received value from \"key\".") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /model/profile.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | // swagger:model Node 4 | type Node struct { 5 | // example: false 6 | Arhive bool `json:"arhive"` 7 | // example: hepic_archive 8 | Db_archive string `json:"db_archive"` 9 | // example: hepic_data 10 | Db_name string `json:"db_name"` 11 | // example: 148.251.238.121 12 | Host string `json:"host"` 13 | // example: DE7 Node 14 | Name string `json:"name"` 15 | // example: "" 16 | Node string `json:"node"` 17 | // example: true 18 | Online bool `json:"online"` 19 | // example: false 20 | Primary bool `json:"primary"` 21 | Table_prefix string `json:"table_prefix"` 22 | // example: de7node 23 | Value string `json:"value"` 24 | } 25 | 26 | // swagger:model NodeList 27 | type NodeList struct { 28 | Data []Node `json:"data"` 29 | // example: 1 30 | Count int `json:"count"` 31 | } 32 | -------------------------------------------------------------------------------- /router/v1/alias.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | "github.com/sipcapture/homer-app/auth" 7 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 8 | "github.com/sipcapture/homer-app/data/service" 9 | ) 10 | 11 | func RouteAliasApis(acc *echo.Group, configSession *gorm.DB) { 12 | // initialize service of user 13 | aliasService := service.AliasService{ServiceConfig: service.ServiceConfig{Session: configSession}} 14 | // initialize user controller 15 | src := controllerv1.AliasController{ 16 | AliasService: &aliasService, 17 | } 18 | acc.GET("/alias", src.GetAllAlias) 19 | acc.POST("/alias", src.AddAlias, auth.IsAdmin) 20 | acc.DELETE("/alias/:guid", src.DeleteAlias, auth.IsAdmin) 21 | acc.PUT("/alias/:guid", src.UpdateAlias, auth.IsAdmin) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /utils/sipparser/internal/machine.rl: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // ragel -G2 -Z machine.rl 8 | 9 | %%{ 10 | machine metric; 11 | write data; 12 | }%% 13 | 14 | func ExtractSIPParam(param, data string) (s string) { 15 | if numPos := strings.Index(data, param); numPos >= 0 { 16 | numPos += len(param) 17 | data = data[numPos:] 18 | } else { 19 | return s 20 | } 21 | 22 | cs, p, pe, eof := 0, 0, len(data), len(data) 23 | mark := 0 24 | 25 | %%{ 26 | action mark { 27 | mark = p 28 | } 29 | 30 | action extract { 31 | s = data[mark:p] 32 | } 33 | 34 | LWS = (' ' | ';' | '\n' | '\r'| '\t' | '",'); 35 | 36 | num = any* >mark %extract; 37 | main := num :>LWS? @{ return s }; 38 | 39 | write init; 40 | write exec; 41 | }%% 42 | 43 | return s 44 | } -------------------------------------------------------------------------------- /scripts/build_package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION=$($PWD/homer-app -version | egrep -o '[0-9]+.[0-9]+.[0-9]+') 4 | 5 | PACKAGE="homer-app" 6 | RELEASE=${VERSION:-"0.0.0"} 7 | ARCH="amd64" 8 | 9 | # CHECK FOR DOCKER 10 | if ! [ -x "$(command -v docker)" ]; then 11 | echo 'Error: docker is not installed. Exiting...' >&2 12 | exit 1 13 | fi 14 | 15 | echo "Packaging release $RELEASE ..." 16 | # BUILD DEB PACKAGE 17 | EXT="deb" 18 | docker run --rm \ 19 | -v $PWD:/tmp/pkg \ 20 | -e VERSION="$RELEASE" \ 21 | goreleaser/nfpm pkg --config /tmp/pkg/$PACKAGE.yaml --target "/tmp/pkg/$PACKAGE-$RELEASE-$ARCH.$EXT" 22 | 23 | # BUILD RPM PACKAGE 24 | EXT="rpm" 25 | docker run --rm \ 26 | -v $PWD:/tmp/pkg \ 27 | -e VERSION="$RELEASE" \ 28 | goreleaser/nfpm pkg --config /tmp/pkg/$PACKAGE.yaml --target "/tmp/pkg/$PACKAGE-$RELEASE-$ARCH.$EXT" 29 | 30 | -------------------------------------------------------------------------------- /router/v1/remote.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/labstack/echo/v4" 5 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 6 | "github.com/sipcapture/homer-app/data/service" 7 | ) 8 | 9 | // RouteStatisticApis : here you tell us what RouteStatisticApis is 10 | func RouteLokiApis(acc *echo.Group, serviceLoki service.ServiceLoki) { 11 | 12 | // initialize service of user 13 | remoteService := service.RemoteService{ServiceLoki: serviceLoki} 14 | 15 | // initialize user controller 16 | src := controllerv1.RemoteController{ 17 | RemoteService: &remoteService, 18 | } 19 | 20 | // create new stats 21 | acc.GET("/search/remote/label", src.RemoteLabel) 22 | 23 | // create new stats 24 | acc.GET("/search/remote/values", src.RemoteValues) 25 | 26 | // create new stats 27 | acc.POST("/search/remote/data", src.RemoteData) 28 | 29 | } 30 | -------------------------------------------------------------------------------- /scripts/build_frontend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # CHECK FOR DOCKER 3 | if ! [ -x "$(command -v docker)" ]; then 4 | echo 'Error: docker is not installed. Exiting...' >&2 5 | exit 1 6 | fi 7 | # REBUILD FRONTEND 8 | read -p "This will drop and rebuild the Frontend from GIT. Continue (y/n)?" CONT 9 | if [ "$CONT" = "y" ]; then 10 | echo "Cleaning up..." 11 | mv dist dist.backup 12 | mkdir dist 13 | echo "Rebuilding Frontend..."; 14 | docker run --rm \ 15 | -v $PWD/dist:/dist \ 16 | node:16-alpine \ 17 | sh -c "apk --update add git && git clone https://github.com/sipcapture/homer-ui /app && cd /app && npm install && npm install -g @angular/cli && npm run build && cp -R /app/dist/homer-ui/* /dist/ && cat /app/src/VERSION.ts | egrep -o '[0-9].[0-9].[0-9]+' > /dist/VERSION && echo 'done!'" 18 | ls -alF ./dist 19 | rm -rf dist.backup 20 | else 21 | echo "Exiting..."; 22 | fi 23 | 24 | -------------------------------------------------------------------------------- /network/response/httpresponse.go: -------------------------------------------------------------------------------- 1 | package httpresponse 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/labstack/echo/v4" 7 | ) 8 | 9 | func CreateBadResponse(c *echo.Context, requestCode int, message string) error { 10 | localC := *c 11 | response := fmt.Sprintf("{\"data\":{},\"message\":%q}", message) 12 | return localC.JSONBlob(requestCode, []byte(response)) 13 | } 14 | 15 | func CreateSuccessResponse(c *echo.Context, requestCode int, message string) error { 16 | localC := *c 17 | return localC.JSONBlob(requestCode, []byte(message)) 18 | } 19 | 20 | func CreateBadResponseWithJson(c *echo.Context, requestCode int, message []byte) error { 21 | localC := *c 22 | return localC.JSONBlob(requestCode, message) 23 | } 24 | 25 | func CreateSuccessResponseWithJson(c *echo.Context, requestCode int, message []byte) error { 26 | localC := *c 27 | return localC.JSONBlob(requestCode, []byte(message)) 28 | } 29 | -------------------------------------------------------------------------------- /utils/sipparser/params.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from go standard library 9 | 10 | // Param is just a struct that holds a parameter and a value 11 | // As an example of this would be something like user=phone 12 | type Param struct { 13 | Param string 14 | Val string 15 | } 16 | 17 | // getParam is just a convenience function to pass a string 18 | // and get a *Param 19 | func getParam(s string) *Param { 20 | p := new(Param) 21 | for i := range s { 22 | if s[i] == '=' { 23 | p.Param = cleanWs(s[0:i]) 24 | if i+1 < len(s) { 25 | p.Val = cleanWs(s[i+1:]) 26 | return p 27 | } 28 | return p 29 | } 30 | } 31 | p.Param = cleanWs(s) 32 | return p 33 | } 34 | -------------------------------------------------------------------------------- /router/v1/userSettings.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 7 | "github.com/sipcapture/homer-app/data/service" 8 | ) 9 | 10 | func RouteUserSettingsApis(acc *echo.Group, session *gorm.DB) { 11 | // initialize service of user 12 | userSettingService := service.UserSettingsService{ServiceConfig: service.ServiceConfig{Session: session}} 13 | // initialize user controller 14 | urc := controllerv1.UserSettingsController{ 15 | UserSettingsService: &userSettingService, 16 | } 17 | // get user settings 18 | acc.GET("/user/settings", urc.GetAll) 19 | acc.GET("/user/settings/:category", urc.GetCategory) 20 | acc.POST("/user/settings", urc.AddUserSettings) 21 | acc.DELETE("/user/settings/:category", urc.DeleteUserSettings) 22 | acc.PUT("/user/settings/:category", urc.UpdateUserSettings) 23 | 24 | } 25 | -------------------------------------------------------------------------------- /utils/httpauth/httpauth.go: -------------------------------------------------------------------------------- 1 | package httpauth 2 | 3 | import ( 4 | "github.com/sipcapture/homer-app/model" 5 | "net/http" 6 | 7 | "github.com/go-resty/resty/v2" 8 | ) 9 | 10 | type Client struct { 11 | URL string 12 | InsecureSkipVerify bool 13 | } 14 | 15 | func (rs *Client) Authenticate(username, password string) (*model.HTTPAUTHResp, error) { 16 | client := resty.New() 17 | // POST JSON string 18 | // No need to set content type, if you have client level setting 19 | resp, err := client.R(). 20 | SetResult(model.HTTPAUTHResp{}). 21 | SetHeader("Content-Type", "application/json"). 22 | SetBody(map[string]string{"username": username, "password": password}). 23 | //SetResult(&AuthSuccess{}). // or SetResult(AuthSuccess{}). 24 | Post(rs.URL) 25 | 26 | if err != nil || resp.StatusCode() != http.StatusOK { 27 | return nil, err 28 | } 29 | 30 | return resp.Result().(*model.HTTPAUTHResp), nil 31 | } 32 | -------------------------------------------------------------------------------- /router/v1/advanced.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | "github.com/sipcapture/homer-app/auth" 7 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 8 | "github.com/sipcapture/homer-app/data/service" 9 | ) 10 | 11 | func RouteAdvancedApis(acc *echo.Group, configSession *gorm.DB) { 12 | // initialize service of user 13 | AdvancedService := service.AdvancedService{ServiceConfig: service.ServiceConfig{Session: configSession}} 14 | // initialize user controller 15 | ac := controllerv1.AdvancedController{ 16 | AdvancedService: &AdvancedService, 17 | } 18 | acc.GET("/advanced", ac.GetAll) 19 | acc.GET("/advanced/:guid", ac.GetAdvancedAgainstGUID) 20 | acc.POST("/advanced", ac.AddAdvanced, auth.IsAdmin) 21 | acc.PUT("/advanced/:guid", ac.UpdateAdvancedAgainstGUID, auth.IsAdmin) 22 | acc.DELETE("/advanced/:guid", ac.DeleteAdvancedAgainstGUID, auth.IsAdmin) 23 | } 24 | -------------------------------------------------------------------------------- /router/v1/profile.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 7 | "github.com/sipcapture/homer-app/data/service" 8 | "github.com/sipcapture/homer-app/model" 9 | ) 10 | 11 | // comments 12 | func RouteProfileApis(acc *echo.Group, session *gorm.DB, databaseNodeMap []model.DatabasesMap) { 13 | // initialize service of user 14 | ProfileService := service.ProfileService{ServiceConfig: service.ServiceConfig{Session: session}, DatabaseNodeMap: &databaseNodeMap} 15 | // initialize user controller 16 | hs := controllerv1.ProfileController{ 17 | ProfileService: &ProfileService, 18 | } 19 | // get all dashboards 20 | acc.GET("/admin/profiles", hs.GetDashboardList) 21 | acc.GET("/database/node/list", hs.GetDBNodeList) 22 | acc.GET("/modules/status", hs.GetModulesStatus) 23 | 24 | //acc.GET("/hepsub/protocol", hs.GetHepsub) 25 | } 26 | -------------------------------------------------------------------------------- /model/applications.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | //table name 8 | func (TableApplications) TableName() string { 9 | return "applications" 10 | } 11 | 12 | //table app 13 | type TableApplications struct { 14 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT" json:"-"` 15 | GUID string `gorm:"column:guid;type:uuid" json:"guid" validate:"required"` 16 | NameApplication string `gorm:"column:name;type:varchar(50);unique_index:idx_name_host;not null" json:"application_name" validate:"required"` 17 | HostApplication string `gorm:"column:host;type:varchar(100);unique_index:idx_name_host;not null" json:"application_host" validate:"required"` 18 | VersionApplication string `gorm:"column:version;type:varchar(100);not null" json:"application_version" validate:"required"` 19 | CreatedAt time.Time `gorm:"column:created_at;default:current_timestamp;not null" json:"-"` 20 | } 21 | -------------------------------------------------------------------------------- /model/rawData.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | func (TableRawData) TableName() string { 9 | 10 | return "hep_proto_200_default" 11 | } 12 | 13 | // swagger:model TableRtpStatsV2 14 | type TableRawData struct { 15 | Id int `gorm:"column:id;primary_key;AUTO_INCREMENT" json:"id"` 16 | // should be a unique value representing user 17 | // example: e71771a2-1ea0-498f-8d27-391713e10664 18 | // required: true 19 | SID string `gorm:"column:sid;type:varchar" json:"sid" validate:"required"` 20 | //Create data 21 | CreateDate time.Time `gorm:"column:create_date;default:current_timestamp;not null" json:"create_date"` 22 | //protocol_header 23 | ProtocolHeader json.RawMessage `gorm:"column:protocol_header;type:jsonb;not null" json:"protocol_header"` 24 | //data_header 25 | DataHeader json.RawMessage `gorm:"column:data_header;type:jsonb;not null" json:"data_header"` 26 | //raw 27 | Raw json.RawMessage `gorm:"column:raw;type:jsonb;not null" json:"raw"` 28 | } 29 | -------------------------------------------------------------------------------- /router/v1/prometheus.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/labstack/echo/v4" 5 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 6 | "github.com/sipcapture/homer-app/data/service" 7 | ) 8 | 9 | // RouteStatisticApis : here you tell us what RouteStatisticApis is 10 | func RoutePrometheusApis(acc *echo.Group, servicePrometheus service.ServicePrometheus) { 11 | 12 | // initialize service of user 13 | prometheusService := service.PrometheusService{ServicePrometheus: servicePrometheus} 14 | 15 | // initialize user controller 16 | src := controllerv1.PrometheusController{ 17 | PrometheusService: &prometheusService, 18 | } 19 | 20 | // create new stats 21 | acc.POST("/prometheus/data", src.PrometheusData) 22 | 23 | // get new labels 24 | acc.GET("/prometheus/label/:userlabel", src.PrometheusLabelData) 25 | 26 | // get new labels 27 | acc.GET("/prometheus/labels", src.PrometheusLabels) 28 | 29 | // get new values 30 | acc.POST("/prometheus/value", src.PrometheusValue) 31 | } 32 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example goreleaser.yaml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | project_name: homer-app 4 | before: 5 | hooks: 6 | # you may remove this if you don't use vgo 7 | - go mod tidy 8 | # you may remove this if you don't need go generate 9 | - go generate ./... 10 | 11 | dist: release 12 | scoop: 13 | commit_author: 14 | name: sipcapture_release 15 | email: alexandr.dubovikov@gmail.com 16 | builds: 17 | - env: 18 | - CGO_ENABLED=0 19 | ldflags: 20 | - -s -w 21 | goos: 22 | - linux 23 | goarch: 24 | - amd64 25 | # - arm64 26 | archives: 27 | - replacements: 28 | darwin: Darwin 29 | linux: Linux 30 | windows: Windows 31 | 386: i386 32 | amd64: x86_64 33 | checksum: 34 | name_template: 'checksums.txt' 35 | snapshot: 36 | name_template: "{{ .Tag }}-next" 37 | changelog: 38 | sort: asc 39 | filters: 40 | exclude: 41 | - '^docs:' 42 | - '^test:' 43 | -------------------------------------------------------------------------------- /router/v1/hepsub.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | "github.com/sipcapture/homer-app/auth" 7 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 8 | "github.com/sipcapture/homer-app/data/service" 9 | ) 10 | 11 | func RouteHepsubApis(acc *echo.Group, session *gorm.DB) { 12 | // initialize service of user 13 | HepsubService := service.HepsubService{ServiceConfig: service.ServiceConfig{Session: session}} 14 | // initialize user controller 15 | hs := controllerv1.HepsubController{ 16 | HepsubService: &HepsubService, 17 | } 18 | // get all dashboards 19 | 20 | acc.GET("/hepsub/protocol/:id/:transaction", hs.GetHepSubFields) 21 | acc.GET("/hepsub/protocol", hs.GetHepSub) 22 | acc.GET("/hepsub/protocol/:guid", hs.GetHepSubAgainstGUID) 23 | acc.POST("/hepsub/protocol", hs.AddHepSub, auth.IsAdmin) 24 | acc.PUT("/hepsub/protocol/:guid", hs.UpdateHepSubAgainstGUID, auth.IsAdmin) 25 | acc.DELETE("/hepsub/protocol/:guid", hs.DeleteHepSubAgainstGUID, auth.IsAdmin) 26 | } 27 | -------------------------------------------------------------------------------- /utils/sipparser/warning.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "fmt" 11 | "strconv" 12 | "strings" 13 | ) 14 | 15 | type Warning struct { 16 | Val string 17 | Code string 18 | CodeInt int 19 | Agent string 20 | Text string 21 | } 22 | 23 | func (w *Warning) parse() error { 24 | parts := strings.SplitN(w.Val, " ", 3) 25 | if got, want := len(parts), 3; got != want { 26 | return fmt.Errorf("Warning.parse err: split on LWS returned %d fields, want %d", got, want) 27 | } 28 | c, err := strconv.Atoi(parts[0]) 29 | if err != nil || c < 0 || c > 999 { 30 | return fmt.Errorf("Warning.parse err: got code %q, want 3-digit code", parts[0]) 31 | } 32 | w.Code = parts[0] 33 | w.CodeInt = c 34 | w.Agent = parts[1] 35 | w.Text = strings.Replace(parts[2], "\"", "", -1) 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /router/v1/authtoken.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | "github.com/sipcapture/homer-app/auth" 7 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 8 | "github.com/sipcapture/homer-app/data/service" 9 | ) 10 | 11 | func RouteAuthTokenApis(acc *echo.Group, session *gorm.DB) { 12 | // initialize service of user 13 | AuthtokenService := service.AuthtokenService{ServiceConfig: service.ServiceConfig{Session: session}} 14 | // initialize user controller 15 | ats := controllerv1.AuthtokenController{ 16 | AuthtokenService: &AuthtokenService, 17 | } 18 | 19 | // create agent subscribe 20 | /************************************/ 21 | acc.GET("/token/auth", ats.GetAuthtoken, auth.IsAdmin) 22 | acc.GET("/token/auth/:guid", ats.GetAuthtokenAgainstGUID, auth.IsAdmin) 23 | 24 | acc.POST("/token/auth", ats.AddAuthtoken, auth.IsAdmin) 25 | acc.PUT("/token/auth/:guid", ats.UpdateAuthtokenAgainstGUID, auth.IsAdmin) 26 | 27 | acc.DELETE("/token/auth/:guid", ats.DeleteAuthtokenAgainstGUID, auth.IsAdmin) 28 | 29 | } 30 | -------------------------------------------------------------------------------- /scripts/build_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SIPCAPTURE HOMER 7.7 Webapp 4 | # Docker Builder & Slimmer 5 | # 6 | # ENV OPTIONS: 7 | # PUSH = push images to dockerhub 8 | # SLIM = build slim docker image 9 | # REPO = default image respository/name 10 | # TAG = defailt image tag (server) 11 | # TAG_SLIM = default slim image tag (slim) 12 | 13 | # CHECK FOR DOCKER 14 | if ! [ -x "$(command -v docker)" ]; then 15 | echo 'Error: docker is not installed. Exiting...' >&2 16 | exit 1 17 | fi 18 | 19 | REPO=${REPO:-sipcapture/webapp} 20 | TAG=${TAG:-latest} 21 | TAG_SLIM=${TAG_SLIM:-slim} 22 | 23 | echo "Building HOMER docker ..." 24 | docker build --no-cache -t $REPO:$TAG . 25 | if [ ! -z "$PUSH" ]; then 26 | echo "Pushing $REPO:$TAG ..." 27 | docker push $REPO:$TAG 28 | fi 29 | if [ ! -z "$SLIM" ]; then 30 | echo "Building HOMER slim docker ..." 31 | docker-slim build \ 32 | --include-path /etc \ 33 | --tag $REPO:$TAG_SLIM \ 34 | $REPO:$TAG 35 | if [ ! -z "$PUSH" ]; then 36 | echo "Pushing $REPO:$TAG_SLIM ..." 37 | docker push $REPO:$TAG_SLIM 38 | fi 39 | fi 40 | -------------------------------------------------------------------------------- /utils/sipparser/rack_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | 10 | /* func TestRack(t *testing.T) { 11 | sm := &SipMsg{} 12 | s := "776656 1 INVITE" 13 | sm.parseRack(s) 14 | if sm.Error != nil { 15 | t.Errorf("[TestRack] Error parsing rack hdr: 776656 1 INVITE. Received err: %v", sm.Error) 16 | } 17 | if sm.Rack.RseqVal != "776656" { 18 | t.Errorf("[TestRack] Error parsing rack hdr: 776656 1 INVITE. RseqVal should be 776656 but received: %v", sm.Rack.RseqVal) 19 | } 20 | if sm.Rack.CseqVal != "1" { 21 | t.Errorf("[TestRack] Error parsing rack hdr: 776656 1 INVITE. CseqVal should be 1 but received: %v", sm.Rack.CseqVal) 22 | } 23 | if sm.Rack.CseqMethod != "INVITE" { 24 | t.Errorf("[TestRack] Error parsing rack hdr: 776656 1 INVITE. CseqMethod should be \"INVITE\" but received: \"%s\"", sm.Rack.CseqMethod) 25 | } 26 | } 27 | */ 28 | -------------------------------------------------------------------------------- /router/v1/statistic.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/labstack/echo/v4" 5 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 6 | "github.com/sipcapture/homer-app/data/service" 7 | ) 8 | 9 | // RouteStatisticApis : here you tell us what RouteStatisticApis is 10 | func RouteStatisticApis(acc *echo.Group, influxClient service.ServiceInfluxDB) { 11 | // initialize service of user 12 | statisticService := service.StatisticService{ServiceInfluxDB: influxClient} 13 | 14 | // initialize user controller 15 | src := controllerv1.StatisticController{ 16 | StatisticService: &statisticService, 17 | } 18 | 19 | // create new stats 20 | acc.POST("/statistic/data", src.StatisticData) 21 | acc.GET("/statistic/_db", src.GetStatisticDBList) 22 | acc.POST("/statistic/_retentions", src.GetStatisticRetentionsList) 23 | acc.GET("/statistic/_measurements/:dbid", src.GetStatisticMeasurementsList) 24 | acc.POST("/statistic/_metrics", src.GetStatisticMetricsList) 25 | acc.POST("/statistic/_tags", src.GetStatisticTagsList) 26 | 27 | //acc.POST("/api/call/report/log", src.HepSub) 28 | } 29 | -------------------------------------------------------------------------------- /model/system.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/labstack/echo/v4" 7 | ) 8 | 9 | type AppContext struct { 10 | echo.Context 11 | UserName string `json:"username"` 12 | Admin bool `json:"admin"` 13 | UserGroup string `json:"usergroup"` 14 | ExternalAuth bool `json:"externalauth"` 15 | } 16 | 17 | // swagger:model SuccessResponse 18 | type SuccessfulResponse struct { 19 | // count elements in data 20 | // 21 | // required: true 22 | Count int `json:"count"` 23 | // data in JSON format 24 | // type: object 25 | // required: true 26 | Data json.RawMessage `json:"data"` 27 | // the message for user 28 | // 29 | // required: true 30 | Message string `json:"message"` 31 | } 32 | 33 | // swagger:model KeyContext 34 | type KeyContext struct { 35 | echo.Context 36 | TokenObject TableAuthToken `json:"token"` 37 | UserName string `json:"username"` 38 | UserGroup string `json:"usergroup"` 39 | UserAdmin bool `json:"admin"` 40 | AuthKey string `json:"auth-key"` 41 | Auth bool `json:"auth"` 42 | } 43 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-alpine AS webapp 2 | ENV BUILD=20220211-001 3 | RUN apk add git && git clone https://github.com/sipcapture/homer-ui /app 4 | WORKDIR /app 5 | RUN npm install && npm install -g @angular/cli && npm run build 6 | 7 | FROM golang:alpine AS webapi 8 | ENV BUILD=20220211-001 9 | RUN apk --update add git make 10 | COPY . /homer-app 11 | WORKDIR /homer-app 12 | RUN make modules && make all 13 | 14 | FROM alpine 15 | WORKDIR / 16 | RUN apk --update add bash sed 17 | # Create default directories 18 | RUN mkdir -p /usr/local/homer 19 | COPY --from=webapi /homer-app/homer-app . 20 | COPY --from=webapi /homer-app/docker/webapp_config.json /usr/local/homer/etc/webapp_config.json 21 | COPY --from=webapi /homer-app/swagger.json /usr/local/homer/etc/swagger.json 22 | COPY --from=webapp /app/dist/homer-ui /usr/local/homer/dist 23 | # Configure entrypoint 24 | COPY ./docker/docker-entrypoint.sh / 25 | COPY ./docker/docker-entrypoint.d/* /docker-entrypoint.d/ 26 | RUN chmod +x /docker-entrypoint.d/* /docker-entrypoint.sh 27 | ENTRYPOINT ["/docker-entrypoint.sh"] 28 | CMD ["/homer-app", "-webapp-config-path=/usr/local/homer/etc"] 29 | -------------------------------------------------------------------------------- /router/v1/dashboard.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 7 | "github.com/sipcapture/homer-app/data/service" 8 | ) 9 | 10 | func RouteDashboardApis(acc *echo.Group, session *gorm.DB) { 11 | // initialize service of user 12 | dashBoardService := service.DashBoardService{ServiceConfig: service.ServiceConfig{Session: session}} 13 | // initialize user controller 14 | dbc := controllerv1.DashBoardController{ 15 | DashBoardService: &dashBoardService, 16 | } 17 | // get all dashboards 18 | acc.GET("/dashboard/info", dbc.GetDashBoardLists) 19 | // get all dashboards 20 | acc.GET("/dashboard/store/:dashboardId", dbc.GetDashBoard) 21 | // insert new dashboard 22 | acc.POST("/dashboard/store/:dashboardId", dbc.InsertDashboard) 23 | // insert new dashboard 24 | acc.PUT("/dashboard/store/:dashboardId", dbc.UpdateDashboard) 25 | // delete dashboard 26 | acc.DELETE("/dashboard/store/:dashboardId", dbc.DeleteDashboard) 27 | // dashboard reset 28 | acc.GET("/dashboard/reset", dbc.ResetUserDashboard) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /utils/sipparser/diversion_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestDiv(t *testing.T) { 14 | sm := &SipMsg{} 15 | s := "\"Unknown\" ;reason=unconditional;privacy=off;counter=1" 16 | sm.parseRemotePartyId(s) 17 | if sm.Error != nil { 18 | t.Errorf("[TestDiv] Error parsing div hdr: \"Unknown\" ;reason=unconditional;privacy=off;counter=1. Received err: %v", sm.Error) 19 | } 20 | if sm.RemotePartyId.Name != "Unknown" { 21 | t.Error("[TestDiv] Error parsing div hdr: \"Unknown\" ;reason=unconditional;privacy=off;counter=1. Name should be \"Unknown\".") 22 | } 23 | if sm.RemotePartyId.Privacy != "off" { 24 | t.Error("[TestDiv] Error parsing div hdr: \"Unknown\" ;reason=unconditional;privacy=off;counter=1. Privacy should be \"off\".") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /data/service/hepsubsearch.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "sort" 8 | 9 | "github.com/sipcapture/homer-app/model" 10 | ) 11 | 12 | type HepsubsearchService struct { 13 | ServiceConfig 14 | } 15 | 16 | // this method gets all users from database 17 | func (hss *HepsubsearchService) GetAgentsub() (string, error) { 18 | var AgentsubObject []model.TableAgentLocationSession 19 | var count int 20 | if err := hss.Session.Debug().Table("agent_location_session"). 21 | Find(&AgentsubObject).Count(&count).Error; err != nil { 22 | return "", err 23 | } 24 | sort.Slice(AgentsubObject[:], func(i, j int) bool { 25 | return AgentsubObject[i].GUID < AgentsubObject[j].GUID 26 | }) 27 | data, _ := json.Marshal(AgentsubObject) 28 | response := fmt.Sprintf("{\"count\":%d,\"data\":%s}", count, string(data)) 29 | return response, nil 30 | } 31 | 32 | // this method gets all users from database 33 | func (hss *HepsubsearchService) DoHepSubSearch(data model.SearchObject) (string, error) { 34 | response := fmt.Sprintf("{\"message\":\"successfully created DoHepSubSearch\",\"data\":\"empty\"}") 35 | return response, nil 36 | } 37 | -------------------------------------------------------------------------------- /scripts/DEVELOPERS.md: -------------------------------------------------------------------------------- 1 | # HOMER-APP 7.7 DEV 2 | 3 | 4 | ### Create Release w/ packages 5 | 6 | #### Requirements 7 | * Docker 8 | * Github API Token ($TOKEN) 9 | 10 | #### Create Release 11 | ``` 12 | make binary 13 | make frontend 14 | ``` 15 | #### Get Release Version 16 | ``` 17 | RELEASE=$(./homer-app -version | egrep -o '[0-9].[0-9].[0-9]+') 18 | ``` 19 | #### Build Package artifacts 20 | ``` 21 | VERSION=$RELEASE make package 22 | ``` 23 | 24 | #### Push to Github 25 | ``` 26 | PKGNAME="homer-app-$RELEASE-amd64" 27 | ./scripts/github-release.sh github_api_token=$TOKEN owner=sipcapture repo=homer-app tag=$RELEASE filename=./homer-app 28 | ./scripts/github-release.sh github_api_token=$TOKEN owner=sipcapture repo=homer-app tag=$RELEASE filename=./$PKGNAME.deb 29 | ./scripts/github-release.sh github_api_token=$TOKEN owner=sipcapture repo=homer-app tag=$RELEASE filename=./$PKGNAME.rpm 30 | ``` 31 | #### Push to Packagecloud 32 | ``` 33 | PKGNAME="homer-app-$RELEASE-amd64" 34 | package_cloud push qxip/sipcapture/debian/stretch $PKGNAME.deb 35 | package_cloud push qxip/sipcapture/debian/buster $PKGNAME.deb 36 | package_cloud push qxip/sipcapture/el/7 $PKGNAME.rpm 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /scripts/build_local_frontend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # CHECK FOR DOCKER 3 | if ! [ -x "$(command -v docker)" ]; then 4 | echo 'Error: docker is not installed. Exiting...' >&2 5 | exit 1 6 | fi 7 | 8 | # CHECK FOR SPECIFIC DOCKER IMAGE 9 | IMAGE_NAME="homer-ui" 10 | if ! docker image inspect $IMAGE_NAME > /dev/null 2>&1; then 11 | echo "Error: Docker image '$IMAGE_NAME' does not exist. Exiting..." >&2 12 | exit 1 13 | fi 14 | 15 | # PULL FRONTEND 16 | read -p "This will pull the Frontend from local ${IMAGE_NAME} docker image. Continue (y/n)?" CONT 17 | if [ "$CONT" = "y" ]; then 18 | echo "Cleaning up..." 19 | mv dist dist.backup 20 | mkdir dist 21 | echo "Pulling Frontend..."; 22 | 23 | if ! docker create --name homer-ui ${IMAGE_NAME} > /dev/null 2>&1; then 24 | echo "Error: unable to create homer-ui container. Exiting..." >&2 25 | exit 1 26 | fi 27 | 28 | docker cp homer-ui:/app/dist/homer-ui/. ./dist/ 29 | docker cp homer-ui:/app/src/VERSION.ts /tmp/VERSION.ts 30 | cat /tmp/VERSION.ts | egrep -o '[0-9].[0-9].[0-9]+' > ./dist/VERSION 31 | docker rm -f homer-ui 32 | 33 | ls -alF ./dist 34 | rm -rf dist.backup 35 | else 36 | echo "Exiting..."; 37 | fi 38 | 39 | -------------------------------------------------------------------------------- /utils/sipparser/warning_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | import ( 9 | "testing" 10 | ) 11 | 12 | func TestWarning(t *testing.T) { 13 | s := "301 isi.edu \"Incompatible network address type 'E.164'\"" 14 | w := &Warning{Val: s} 15 | err := w.parse() 16 | if err != nil { 17 | t.Errorf("[TestWarning] Error parsing warning. Got err: %v", err) 18 | } 19 | if w.Code != "301" { 20 | t.Errorf("[TestWarning] Error parsing warning. Code is not \"301\". Rcvd: \"%s\"", w.Code) 21 | } 22 | if w.CodeInt != 301 { 23 | t.Errorf("[TestWarning] Error parsing warning. CodeInt is not 301. Rcvd: %d", w.CodeInt) 24 | } 25 | if w.Agent != "isi.edu" { 26 | t.Errorf("[TestWarning] Error parsing warning. Agent is not \"isi.edu\". Rcvd: \"%s\"", w.Agent) 27 | } 28 | if w.Text != "Incompatible network address type 'E.164'" { 29 | t.Errorf("[TestWarning] Error parsing warning. Text should be \"Incompatible network address type 'E.164'\". Rcvd: \"%s\"", w.Text) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /utils/sipparser/accept_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | 10 | /* // TestAccept tests the accept header and parsing functions 11 | func TestAccept(t *testing.T) { 12 | sm := &SipMsg{} 13 | s := "application/sdp" 14 | sm.parseAccept(s) 15 | if sm.Accept.Val != "application/sdp" { 16 | t.Errorf("[TestAccept] Error parsing accept hdr: application/sdp. sm.Accept.Val should be application/sdp but received: " + sm.Accept.Val) 17 | } 18 | if len(sm.Accept.Params) != 1 { 19 | t.Errorf("[TestAccept] Error parsing accept hdr: application/sdp. sm.Accept.Params should have length of 1.") 20 | } 21 | if sm.Accept.Params[0].Type != "application" { 22 | t.Errorf("[TestAccept] Error parsing accept hdr: application/sdp. sm.Accept.Params[0].Type should be \"application\". Received: %q", sm.Accept.Params[0].Type) 23 | } 24 | if sm.Accept.Params[0].Val != "sdp" { 25 | t.Errorf("[TestAccept] Error parsing accept hdr: application/sdp. sm.Accept.Params[0].Val should be \"sdp\" but received: %q", sm.Accept.Params[0].Val) 26 | } 27 | } 28 | */ 29 | -------------------------------------------------------------------------------- /utils/sipparser/contentdisposition.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "strings" 11 | ) 12 | 13 | // ContentDisposition is a struct that holds a parsed 14 | // content-disposition hdr: 15 | // -- Val is the raw value 16 | // -- DispType is the display type 17 | // -- Params is slice of parameters 18 | type ContentDisposition struct { 19 | Val string 20 | DispType string 21 | Params []*Param 22 | } 23 | 24 | func (c *ContentDisposition) addParam(s string) { 25 | if s == "" { 26 | return 27 | } 28 | if c.Params == nil { 29 | c.Params = []*Param{getParam(s)} 30 | return 31 | } 32 | c.Params = append(c.Params, getParam(s)) 33 | } 34 | 35 | func (c *ContentDisposition) parse() { 36 | charPos := strings.IndexRune(c.Val, ';') 37 | if charPos == -1 { 38 | c.DispType = c.Val 39 | return 40 | } 41 | c.DispType = c.Val[0:charPos] 42 | if len(c.Val)-1 > charPos { 43 | params := strings.Split(c.Val[charPos+1:], ";") 44 | for i := range params { 45 | c.addParam(params[i]) 46 | } 47 | } 48 | return 49 | } 50 | -------------------------------------------------------------------------------- /router/v1/mapping.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | "github.com/sipcapture/homer-app/auth" 7 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 8 | "github.com/sipcapture/homer-app/data/service" 9 | ) 10 | 11 | func RouteMappingdApis(acc *echo.Group, session *gorm.DB) { 12 | // initialize service of user 13 | mappingService := service.MappingService{ServiceConfig: service.ServiceConfig{Session: session}} 14 | // initialize user controller 15 | mpc := controllerv1.MappingController{ 16 | MappingService: &mappingService, 17 | } 18 | // get all dashboards 19 | acc.GET("/mapping/protocol", mpc.GetMapping) 20 | acc.GET("/mapping/protocol/reset", mpc.ResetMapping) 21 | acc.GET("/mapping/protocol/reset/:uuid", mpc.ResetMappingAgainstUUID) 22 | acc.GET("/mapping/protocol/:id/:transaction", mpc.GetMappingFields) 23 | acc.GET("/mapping/protocol/:guid", mpc.GetMappingAgainstGUID) 24 | acc.POST("/mapping/protocol", mpc.AddMapping, auth.IsAdmin) 25 | acc.PUT("/mapping/protocol/:guid", mpc.UpdateMappingAgainstGUID, auth.IsAdmin) 26 | acc.DELETE("/mapping/protocol/:guid", mpc.DeleteMappingAgainstGUID, auth.IsAdmin) 27 | 28 | /* search smart */ 29 | acc.GET("/smart/search/tag/:hepid/:profile", mpc.GetSmartHepProfile) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /utils/sipparser/cseq.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "errors" 11 | "strings" 12 | ) 13 | 14 | // Cseq is a struct that holds the values for a cseq header: 15 | // -- Val is the raw string value of the cseq hdr 16 | // -- Method is the SIP method 17 | // -- Digit is the numeric indicator for the method 18 | type Cseq struct { 19 | Val string 20 | Method string 21 | Digit string 22 | } 23 | 24 | func (c *Cseq) parse() error { 25 | if len(c.Val) < 3 { 26 | return errors.New("Cseq.parse err: length of CSeq is < 3") 27 | } 28 | s := strings.IndexRune(c.Val, ' ') 29 | if s == -1 { 30 | return errors.New("Cseq.parse err: lws err with: " + c.Val) 31 | } 32 | if s == 0 { 33 | return errors.New("Cseq.parse err: lws at pos 0 in val: " + c.Val) 34 | } 35 | if len(c.Val)-1 < s+1 { 36 | return errors.New("Cseq.parse err: first lws is end of line in val: " + c.Val) 37 | } 38 | c.Digit = c.Val[0:s] 39 | if c.Val[s+1] != ' ' { 40 | c.Method = c.Val[s+1:] 41 | } else { 42 | c.Method = cleanWs(c.Val[s+1:]) 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /utils/sipparser/cseq_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestCseq(t *testing.T) { 14 | sm := &SipMsg{} 15 | sm.parseCseq("100 INVITE") 16 | if sm.Error != nil { 17 | t.Errorf("[TestCseq] Error parsing cseq: \"100 INVITE\". Received err: %v", sm.Error) 18 | } 19 | if sm.Cseq.Digit != "100" { 20 | t.Errorf("[TestCseq] Error parsing cseq: \"100 INVITE\". Digit should be 100.") 21 | } 22 | if sm.Cseq.Method != "INVITE" { 23 | t.Errorf("[TestCseq] Error parsing cseq: \"100 INVITE\". Method should be \"INVITE\".") 24 | } 25 | sm.parseCseq("1112423100 REGISTER ") 26 | if sm.Error != nil { 27 | t.Errorf("[TestCseq] Error parsing cseq: \"1112423100 REGISTER \". Received err: %v", sm.Error) 28 | } 29 | if sm.Cseq.Digit != "1112423100" { 30 | t.Errorf("[TestCseq] Error parsing cseq: \"1112423100 REGISTER \". Digit should be 1112423100.") 31 | } 32 | if sm.Cseq.Method != "REGISTER" { 33 | t.Errorf("[TestCseq] Error parsing cseq: \"1112423100 REGISTER \". Method should be \"REGISTER\".") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /utils/sipparser/remotepartyid_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestRpid(t *testing.T) { 14 | sm := &SipMsg{} 15 | s := "\"Unknown\" ;party=calling;screen=yes;privacy=off" 16 | sm.parseRemotePartyId(s) 17 | if sm.Error != nil { 18 | t.Errorf("[TestRpid] Error parsing rpid hdr: \"Unknown\" ;party=calling;screen=yes;privacy=off. Received err: %v", sm.Error) 19 | } 20 | if sm.RemotePartyId.Name != "Unknown" { 21 | t.Error("[TestRpid] Error parsing rpid hdr: \"Unknown\" ;party=calling;screen=yes;privacy=off. Name should be \"Unknown\".") 22 | } 23 | if sm.RemotePartyId.URI == nil { 24 | t.Error("[TestRpid] Error parsing rpid hdr: \"Unknown\" ;party=calling;screen=yes;privacy=off. sm.RemotePartyId.URI is nil.") 25 | } 26 | if sm.RemotePartyId.Privacy != "off" { 27 | t.Error("[TestRpid] Error parsing rpid hdr: \"Unknown\" ;party=calling;screen=yes;privacy=off. Privacy should be \"off\".") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /utils/sipparser/rack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "errors" 11 | ) 12 | 13 | // Rack is a struct that holds the parsed rack hdr 14 | // The fields are as follows: 15 | // -- Val is the raw value 16 | // -- RseqVal is the rseq value from the rack hdr 17 | // -- CseqVal is the cseq value from the rack hdr 18 | // -- CseqMethod is the method from the cseq hdr 19 | type Rack struct { 20 | Val string 21 | RseqVal string 22 | CseqVal string 23 | CseqMethod string 24 | } 25 | 26 | // parse parses the .Val of the Rack struct 27 | func (r *Rack) parse() error { 28 | r.Val = cleanWs(r.Val) 29 | pos := make([]int, 0) 30 | for i := range r.Val { 31 | if r.Val[i] == ' ' { 32 | pos = append(pos, i) 33 | } 34 | } 35 | if len(pos) != 2 { 36 | return errors.New("Rack.parse err: could not locate two LWS") 37 | } 38 | r.RseqVal = r.Val[0:pos[0]] 39 | r.CseqVal = r.Val[pos[0]+1 : pos[1]] 40 | if len(r.Val)-1 > pos[1] { 41 | r.CseqMethod = r.Val[pos[1]+1:] 42 | return nil 43 | } 44 | return errors.New("Rack.parse err: value of rack ends in LWS") 45 | } 46 | -------------------------------------------------------------------------------- /utils/sipparser/authorization_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from go standard library 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestAuthorization(t *testing.T) { 14 | val := "Digest username=\"foobaruser124\", realm=\"FOOBAR\", algorithm=MD5, uri=\"sip:foo.bar.com\", nonce=\"4f6d7a1d\", response=\"6a79a5c75572b0f6a18963ae04e971cf\", opaque=\"\"" 15 | a := &Authorization{Val: val} 16 | err := a.parse() 17 | if err != nil { 18 | t.Errorf("[TestAuthorization] Err parsing authorization hdr. Received: %v", err) 19 | } 20 | if a.Credentials != "Digest" { 21 | t.Errorf("[TestAuthorization] Err parsing authorization hdr. Credentials should be \"Digest\" but rcvd: %v", a.Credentials) 22 | } 23 | if a.Username != "foobaruser124" { 24 | t.Errorf("[TestAuthorization] Err parsing authorization hdr. Username should be \"foobaruser124\" but rcvd: %v", a.Username) 25 | } 26 | /* 27 | if a.GetParam("realm").Val != "FOOBAR" { 28 | t.Errorf("[TestAuthorization] Err parsing authorization hdr. Called a.GetParam(\"realm\") and did not get \"FOOBAR\". rcvd: %v", a.GetParam("realm").Val) 29 | } 30 | */ 31 | } 32 | -------------------------------------------------------------------------------- /homer-app.yaml: -------------------------------------------------------------------------------- 1 | # nfpm example config file 2 | name: "homer-app" 3 | arch: "amd64" 4 | platform: "linux" 5 | version: "v${VERSION}" 6 | section: "default" 7 | priority: "extra" 8 | replaces: 9 | - homer-app 10 | provides: 11 | - homer-app 12 | #depends: 13 | #- telegraf 14 | # recommends on rpm packages requires rpmbuild >= 4.13 15 | #recommends: 16 | #- whatever 17 | # suggests on rpm packages requires rpmbuild >= 4.13 18 | #suggests: 19 | #- something-else 20 | #conflicts: 21 | #- not-foo 22 | #- not-bar 23 | maintainer: "QXIP SIPCapture " 24 | description: | 25 | HOMER 7 WebAPP + API 26 | vendor: "QXIP" 27 | homepage: "http://qxip.net" 28 | license: "AGPLv3" 29 | contents: 30 | # Basic file that applies to all packagers 31 | - src: /tmp/pkg/homer-app 32 | dst: /usr/local/bin/homer-app 33 | - src: /tmp/pkg/log/.keepme 34 | dst: /usr/local/homer/log/.keepme 35 | - src: /tmp/pkg/dist/**/* 36 | dst: /usr/local/homer/dist 37 | - src: /tmp/pkg/dist/* 38 | dst: /usr/local/homer/dist 39 | # Simple config file 40 | - src: /tmp/pkg/homer-app.service 41 | dst: /lib/systemd/system/homer-app.service 42 | type: config 43 | - src: /tmp/pkg/swagger.json 44 | dst: /usr/local/homer/etc/swagger.json 45 | type: config 46 | - src: /tmp/pkg/etc/webapp_config.json 47 | dst: /usr/local/homer/etc/webapp_config.json 48 | type: config 49 | -------------------------------------------------------------------------------- /data/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "net/http" 5 | 6 | client "github.com/influxdata/influxdb1-client/v2" 7 | "github.com/jinzhu/gorm" 8 | ) 9 | 10 | // Service : here you tell us what Salutation is 11 | type ServiceData struct { 12 | Session map[string]*gorm.DB 13 | Decoder ExternalDecoder 14 | } 15 | 16 | //ServiceConfig 17 | type ServiceConfig struct { 18 | Session *gorm.DB 19 | } 20 | 21 | // ServiceInfluxDB : here you tell us what ServiceInfluxDB is 22 | type ServiceInfluxDB struct { 23 | InfluxClient client.Client 24 | Active bool 25 | } 26 | 27 | // ServicePrometheus : here you tell us what ServicePrometheus is 28 | type ServicePrometheus struct { 29 | HttpClient *http.Client 30 | User string 31 | Password string 32 | Host string 33 | Api string 34 | Active bool 35 | } 36 | 37 | // ServiceRemote : here you tell us what ServiceRemote is 38 | type ServiceLoki struct { 39 | HttpClient *http.Client 40 | User string 41 | Password string 42 | Host string 43 | Api string 44 | ParamQuery string 45 | Active bool 46 | } 47 | 48 | // ServiceGrafana : here you tell us what ServiceRemote is 49 | type ServiceGrafana struct { 50 | HttpClient *http.Client 51 | User string 52 | Token string 53 | Password string 54 | Host string 55 | Api string 56 | Active bool 57 | } 58 | -------------------------------------------------------------------------------- /utils/sipparser/reason_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | /* func TestReason(t *testing.T) { 9 | sm := &SipMsg{} 10 | s := "Q.850;cause=16;text=\"NORMAL_CLEARING\"" 11 | sm.parseReason(s) 12 | if sm.Reason.Proto != "Q.850" { 13 | t.Errorf("[TestReason] Error parsing reason hdr: Q.850;cause=16;text=\"NORMAL_CLEARING\". Proto should be \"Q.850\" but received: " + sm.Reason.Proto) 14 | } 15 | if sm.Reason.Cause != "16" { 16 | t.Errorf("[TestReason] Error parsing reason hdr: Q.850;cause=16;text=\"NORMAL_CLEARING\". Cause should be \"16\" but received: " + sm.Reason.Cause) 17 | } 18 | if sm.Reason.Text != "NORMAL_CLEARING" { 19 | t.Errorf("[TestReason] Error parsing reason hdr: Q.850;cause=16;text=\"NORMAL_CLEARING\". Text should be \"NORMAL_CLEARING\" but received: " + sm.Reason.Text) 20 | } 21 | s = "Q.850;cause=102" 22 | sm.parseReason(s) 23 | if sm.Reason.Proto != "Q.850" { 24 | t.Errorf("[TestReason] Error parsing reason hdr: Q.850;cause=102. Proto should be \"Q.850\" but received: " + sm.Reason.Proto) 25 | } 26 | if sm.Reason.Cause != "102" { 27 | t.Errorf("[TestReason] Error parsing reason hdr: Q.850;cause=102. Cause should be \"102\" but received: " + sm.Reason.Cause) 28 | } 29 | } 30 | */ 31 | -------------------------------------------------------------------------------- /router/v1/grafana.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 7 | "github.com/sipcapture/homer-app/data/service" 8 | ) 9 | 10 | // RouteStatisticApis : here you tell us what RouteStatisticApis is 11 | func RouteGrafanaApis(acc *echo.Group, session *gorm.DB, serviceGrafana service.ServiceGrafana) { 12 | 13 | // initialize service of user 14 | grafanaService := service.GrafanaService{ServiceConfig: service.ServiceConfig{Session: session}, ServiceGrafana: serviceGrafana} 15 | 16 | // initialize user controller 17 | src := controllerv1.GrafanaController{ 18 | GrafanaService: &grafanaService, 19 | } 20 | 21 | // create new stats 22 | acc.GET("/proxy/grafana/url", src.GrafanaURL) 23 | // 24 | acc.GET("/proxy/grafana/path", src.GrafanaPath) 25 | // create new stats 26 | acc.GET("/proxy/grafana/status", src.GrafanaStatus) 27 | 28 | // create new requests 29 | acc.GET("/proxy/grafana/request/d/:uid/:param", src.GrafanaGetDashboardRequest) 30 | 31 | // create new stats 32 | acc.GET("/proxy/grafana/org", src.GrafanaORG) 33 | 34 | // create new stats 35 | acc.GET("/proxy/grafana/folders", src.GrafanaFolders) 36 | 37 | //get new search by uuid 38 | acc.GET("/proxy/grafana/search/:uid", src.GrafanaGetFoldersAgainstUUID) 39 | 40 | // create new stats 41 | acc.GET("/proxy/grafana/dashboards/uid/:uid", src.GrafanaGetDashboardAgainstUUID) 42 | 43 | } 44 | -------------------------------------------------------------------------------- /utils/sipparser/accept.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | 10 | // AcceptParam is just a key:value pair of params for the accept 11 | // header 12 | type AcceptParam struct { 13 | Type string 14 | Val string 15 | } 16 | 17 | // Accept is a struct that holds the following: 18 | // -- the raw value 19 | // -- a slice of parced AcceptParam 20 | type Accept struct { 21 | Val string 22 | Params []*AcceptParam 23 | } 24 | 25 | // addParam is called when you want to add a parameter to the accept 26 | // struct 27 | func (a *Accept) addParam(s string) { 28 | for i := range s { 29 | if s[i] == '/' { 30 | if len(s)-1 > i { 31 | if a.Params == nil { 32 | a.Params = []*AcceptParam{&AcceptParam{Type: cleanWs(s[0:i]), Val: cleanWs(s[i+1:])}} 33 | return 34 | } 35 | a.Params = append(a.Params, &AcceptParam{Type: cleanWs(s[0:i]), Val: cleanWs(s[i+1:])}) 36 | return 37 | } 38 | return 39 | } 40 | } 41 | } 42 | 43 | // parse just gets a comma seperated list of the parameters from 44 | // the .Val and calls addparam on each of the parameters 45 | func (a *Accept) parse() { 46 | cs := getCommaSeperated(a.Val) 47 | if cs == nil { 48 | a.addParam(a.Val) 49 | return 50 | } 51 | for i := range cs { 52 | a.addParam(cs[i]) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /utils/sipparser/authorization.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from go standard library 9 | import ( 10 | "errors" 11 | "strings" 12 | 13 | "github.com/sipcapture/homer-app/utils/sipparser/internal" 14 | ) 15 | 16 | type Authorization struct { 17 | Val string 18 | Credentials string 19 | Username string 20 | //Params []*Param 21 | } 22 | 23 | /* 24 | func (a *Authorization) GetParam(param string) *Param { 25 | if a.Params == nil { 26 | return nil 27 | } 28 | for i := range a.Params { 29 | if a.Params[i].Param == param { 30 | return a.Params[i] 31 | } 32 | } 33 | return nil 34 | } 35 | */ 36 | func (a *Authorization) parse() error { 37 | pos := strings.IndexRune(a.Val, ' ') 38 | if pos == -1 { 39 | return errors.New("Authorization.parse err: no LWS found") 40 | } 41 | a.Credentials = a.Val[0:pos] 42 | if len(a.Val)-1 <= pos { 43 | return errors.New("Authorization.parse err: no digest-resp found") 44 | } 45 | a.Username = internal.ExtractSIPParam("username=\"", a.Val) 46 | /* 47 | a.Params = make([]*Param, 0) 48 | parts := strings.Split(a.Val[pos+1:], ",") 49 | for i := range parts { 50 | a.Params = append(a.Params, getParam(strings.Replace(parts[i], "\"", "", -1))) 51 | } 52 | if a.GetParam("username") != nil { 53 | a.Username = a.GetParam("username").Val 54 | } 55 | */ 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /data/service/profile.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/sipcapture/homer-app/model" 8 | ) 9 | 10 | type ProfileService struct { 11 | ServiceConfig 12 | DatabaseNodeMap *[]model.DatabasesMap 13 | } 14 | 15 | // this method gets all users from database 16 | func (ps *ProfileService) GetProfile() (string, error) { 17 | var hepsubObject []*model.TableHepsubSchema 18 | var count int 19 | if err := ps.Session.Debug().Table("hepsub_mapping_schema").Where("partid = ?", 10). 20 | Find(&hepsubObject).Count(&count).Error; err != nil { 21 | return "", err 22 | } 23 | data, _ := json.Marshal(hepsubObject) 24 | response := fmt.Sprintf("{\"count\":%d,\"data\":%s}", count, string(data)) 25 | return response, nil 26 | } 27 | 28 | // this method gets all users from database 29 | func (ps *ProfileService) GetDashboardList() (string, error) { 30 | var hepsubObject []*model.TableHepsubSchema 31 | var count int 32 | if err := ps.Session.Debug().Table("user_settings"). 33 | Find(&hepsubObject).Count(&count).Error; err != nil { 34 | return "", err 35 | } 36 | data, _ := json.Marshal(hepsubObject) 37 | response := fmt.Sprintf("{\"count\":%d,\"data\":%s}", count, string(data)) 38 | return response, nil 39 | } 40 | 41 | // this method gets all users from database 42 | func (ps *ProfileService) GetDBNodeList() (string, error) { 43 | var count = 0 44 | data, _ := json.Marshal(ps.DatabaseNodeMap) 45 | count = len(*ps.DatabaseNodeMap) 46 | response := fmt.Sprintf("{\"count\":%d,\"data\":%s}", count, string(data)) 47 | return response, nil 48 | } 49 | -------------------------------------------------------------------------------- /utils/sipparser/passertedid_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestPAssertedId(t *testing.T) { 14 | s := "\"VoIP Call\"" 15 | p := &PAssertedId{Val: s} 16 | p.parse() 17 | if p.Error != nil { 18 | t.Errorf("[TestPAssertedId] Error parsing p-asserted-id hdr: \"VoIP Call\". Received err: %v", p.Error) 19 | } 20 | if p.Name != "VoIP Call" { 21 | t.Errorf("[TestPAssertedId] Error parsing p-assertd-id hdr: \"VoIP Call\". Name should be \"VoIP Call\" but received: \"%s\"", p.Name) 22 | } 23 | if p.URI == nil { 24 | t.Error("[TestPAssertedId] Error parsing p-asserted-id hdr: \"VoIP Call\". No URI in parsed hdr.") 25 | } 26 | if p.Params != nil { 27 | t.Error("[TestPAssertedId] Error parsing p-asserted-id hdr: \"VoIP Call\". p.Params should be nil.") 28 | } 29 | s = "bad header" 30 | p = &PAssertedId{Val: s} 31 | p.parse() 32 | if p.Error == nil { 33 | t.Error("[TestPAssertedId] Should have received an err when parsing bad hdr: \"bad header\".") 34 | } 35 | s = "" 36 | p = &PAssertedId{Val: s} 37 | p.parse() 38 | if p.URI == nil { 39 | t.Error("[TestPAssertedId] No URI for parsing p-asserted-id hdr: ") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /router/v1/agentsSub.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 7 | "github.com/sipcapture/homer-app/data/service" 8 | ) 9 | 10 | func RouteAgentsubApis(acc *echo.Group, session *gorm.DB) { 11 | // initialize service of user 12 | AgentsubService := service.AgentsubService{ServiceConfig: service.ServiceConfig{Session: session}} 13 | // initialize user controller 14 | ass := controllerv1.AgentsubController{ 15 | AgentsubService: &AgentsubService, 16 | } 17 | 18 | // create agent subscribe 19 | /************************************/ 20 | acc.GET("/agent/subscribe", ass.GetAgentsub) 21 | /* search by all */ 22 | acc.GET("/agent/type", ass.GetAgentsub) 23 | /* search by type */ 24 | acc.GET("/agent/type/:type", ass.GetAgentsubByType) 25 | 26 | acc.GET("/agent/subscribe/:guid", ass.GetAgentsubAgainstGUID) 27 | acc.DELETE("/agent/subscribe/:guid", ass.DeleteAgentsubAgainstGUID) 28 | acc.PUT("/agent/subscribe/:guid", ass.UpdateAgentsubAgainstGUID) 29 | 30 | /* search */ 31 | acc.POST("/agent/search/:guid/:type", ass.GetAgentSearchByTypeAndGUID) 32 | 33 | } 34 | 35 | func RouteAgentsubAuthKeyApis(acc *echo.Group, session *gorm.DB) { 36 | // initialize service of user 37 | AgentsubService := service.AgentsubService{ServiceConfig: service.ServiceConfig{Session: session}} 38 | // initialize user controller 39 | ass := controllerv1.AgentsubController{ 40 | AgentsubService: &AgentsubService, 41 | } 42 | 43 | // create agent subscribe 44 | /************************************/ 45 | acc.POST("/agent/subscribe", ass.AddAgentsubWithKey) 46 | } 47 | -------------------------------------------------------------------------------- /router/v1/search.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 7 | "github.com/sipcapture/homer-app/data/service" 8 | ) 9 | 10 | // routesearch Apis 11 | func RouteSearchApis(acc *echo.Group, dataSession map[string]*gorm.DB, configSession *gorm.DB, externalDecoder service.ExternalDecoder) { 12 | // initialize service of user 13 | searchService := service.SearchService{ServiceData: service.ServiceData{Session: dataSession, Decoder: externalDecoder}} 14 | aliasService := service.AliasService{ServiceConfig: service.ServiceConfig{Session: configSession}} 15 | settingService := service.UserSettingsService{ServiceConfig: service.ServiceConfig{Session: configSession}} 16 | 17 | // initialize user controller 18 | src := controllerv1.SearchController{ 19 | SearchService: &searchService, 20 | SettingService: &settingService, 21 | AliasService: &aliasService, 22 | } 23 | 24 | // create new user 25 | acc.POST("/search/call/data", src.SearchData) 26 | acc.POST("/search/call/message", src.GetMessageById) 27 | 28 | acc.POST("/search/call/decode/message", src.GetDecodeMessageById) 29 | acc.POST("/call/transaction", src.GetTransaction) 30 | acc.POST("/call/report/qos", src.GetTransactionQos) 31 | acc.POST("/call/report/log", src.GetTransactionLog) 32 | acc.POST("/export/call/messages/pcap", src.GetMessagesAsPCap) 33 | acc.POST("/export/call/messages/text", src.GetMessagesAsText) 34 | 35 | /* import data */ 36 | acc.POST("/import/data/pcap", src.GetDataAsPCap) 37 | acc.POST("/import/data/pcap/now", src.GetDataAsPCapNow) 38 | 39 | //acc.POST("/api/call/report/log", src.HepSub) 40 | } 41 | -------------------------------------------------------------------------------- /auth/claims.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/golang-jwt/jwt/v5" 7 | "github.com/sipcapture/homer-app/config" 8 | "github.com/sipcapture/homer-app/model" 9 | "github.com/sipcapture/homer-app/utils/logger" 10 | ) 11 | 12 | // jwt token claims which contains info regarding user 13 | type JwtUserClaim struct { 14 | UserName string `json:"username"` 15 | UserAdmin bool `json:"useradmin"` 16 | UserGroup string `json:"usergroup"` 17 | ExternalAuth bool `json:"externalauth"` 18 | ExternalProfile string `json:"externaltype"` 19 | DisplayName string `json:"displayname"` 20 | Avatar string `json:"avatar"` 21 | jwt.RegisteredClaims 22 | } 23 | 24 | func Token(user model.TableUser) (string, error) { 25 | 26 | tNow := time.Now() 27 | tUTC := tNow 28 | 29 | newTUTC := tUTC.Add(time.Duration(config.Setting.AUTH_SETTINGS.AuthTokenExpire) * time.Minute) 30 | 31 | // Set custom claims 32 | claims := &JwtUserClaim{ 33 | user.UserName, 34 | user.IsAdmin, 35 | user.UserGroup, 36 | user.ExternalAuth, 37 | user.ExternalProfile, 38 | user.FirstName + " " + user.LastName, 39 | user.Avatar, 40 | jwt.RegisteredClaims{ 41 | ExpiresAt: jwt.NewNumericDate(newTUTC), 42 | }, 43 | } 44 | 45 | logger.Debug("Current time : ", tNow) 46 | logger.Debug("Local time : ", tUTC) 47 | logger.Debug("Expire Local time : ", newTUTC) 48 | 49 | // Create token with claims 50 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 51 | 52 | // Generate encoded token and send it as response. 53 | t, err := token.SignedString([]byte(config.Setting.AUTH_SETTINGS.JwtSecret)) 54 | if err != nil { 55 | return "", err 56 | } 57 | 58 | return t, nil 59 | } 60 | -------------------------------------------------------------------------------- /controller/v1/websocket.go: -------------------------------------------------------------------------------- 1 | package controllerv1 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/labstack/echo/v4" 8 | "github.com/sipcapture/homer-app/utils/logger" 9 | "golang.org/x/net/websocket" 10 | ) 11 | 12 | type WebSocketController struct { 13 | Controller 14 | Addr *string 15 | } 16 | 17 | // GET /ws WebSocket 18 | // 19 | // Returns data based upon filtered json 20 | // --- 21 | // consumes: 22 | // - application/json 23 | // produces: 24 | // - application/json 25 | // parameters: 26 | // - name: SearchObject 27 | // in: body 28 | // type: object 29 | // description: SearchObject parameters 30 | // schema: 31 | // "$ref": "#/definitions/SearchCallData" 32 | // required: true 33 | // SecurityDefinitions: 34 | // bearer: 35 | // type: apiKey 36 | // name: Authorization 37 | // in: header 38 | // responses: 39 | // '200': body:ListUsers 40 | // '400': body:UserLoginFailureResponse 41 | func (wb *WebSocketController) RelayHepData(c echo.Context) error { 42 | websocket.Handler(func(ws *websocket.Conn) { 43 | conn, err := net.Dial("udp", *wb.Addr) 44 | if err != nil { 45 | fmt.Printf("Some error %v", err) 46 | return 47 | } 48 | 49 | defer func() { 50 | ws.Close() 51 | conn.Close() 52 | }() 53 | for { 54 | // Read 55 | var msg []byte 56 | err = websocket.Message.Receive(ws, &msg) 57 | if err != nil { 58 | logger.Error(fmt.Sprintf("got error while reading data on websocket", err)) 59 | break 60 | } 61 | _, err = conn.Write(msg) 62 | if err != nil { 63 | logger.Error(fmt.Sprintf("got error while writing data on hep socket", err)) 64 | break 65 | } 66 | } 67 | }).ServeHTTP(c.Response(), c.Request()) 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /utils/sipparser/reason.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "strings" 11 | ) 12 | 13 | // Reason is a struct that holds a parsed reason hdr 14 | // Fields are as follows: 15 | // -- Val is the raw value 16 | // -- Proto is the protocol (i.e. SIP) 17 | // -- Cause is the cause code (i.e. 41) 18 | // -- Text is the actual text response 19 | type Reason struct { 20 | Val string 21 | Proto string 22 | Cause string 23 | Text string 24 | } 25 | 26 | // addParam is a method for the Reason type that looks at the 27 | // parameter passed into it 28 | func (r *Reason) addParam(s string) { 29 | np := getParam(s) 30 | if np.Param == "cause" { 31 | r.Cause = np.Val 32 | } 33 | if np.Param == "text" { 34 | r.Text = np.Val 35 | } 36 | } 37 | 38 | // parse is the method that actual parses the .Val of the Reason type 39 | func (r *Reason) parse() { 40 | pos := make([]int, 0) 41 | for i := range r.Val { 42 | if r.Val[i] == ';' { 43 | pos = append(pos, i) 44 | } 45 | } 46 | if len(pos) == 0 { 47 | return 48 | } 49 | if len(pos) == 1 { 50 | r.Proto = cleanWs(r.Val[0:pos[0]]) 51 | if len(r.Val)-1 > pos[0] { 52 | r.addParam(strings.Replace(r.Val[pos[0]+1:], "\"", "", -1)) 53 | } 54 | return 55 | } 56 | if len(pos) > 1 { 57 | r.Proto = cleanWs(r.Val[0:pos[0]]) 58 | for i := range pos { 59 | if len(pos)-1 == i { 60 | if len(r.Val)-1 > pos[i] { 61 | r.addParam(strings.Replace(r.Val[pos[i]+1:], "\"", "", -1)) 62 | } 63 | return 64 | } 65 | r.addParam(strings.Replace(r.Val[pos[i]+1:pos[i+1]], "\"", "", -1)) 66 | } 67 | return 68 | } 69 | return 70 | } 71 | -------------------------------------------------------------------------------- /model/remote.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // swagger:model RemoteObject 8 | type RemoteObject struct { 9 | Param struct { 10 | // example: 100 11 | Limit int `json:"limit"` 12 | // example: {type="call"} 13 | Search string `json:"search"` 14 | // example: http://localhost:3100 15 | Server string `json:"server"` 16 | Timezone json.RawMessage `json:"timezone"` 17 | } `json:"param"` 18 | Timestamp struct { 19 | From int64 `json:"from"` 20 | To int64 `json:"to"` 21 | } `json:"timestamp"` 22 | } 23 | 24 | type RemoteResponse struct { 25 | Total int `json:"total"` 26 | D json.RawMessage `json:"data"` 27 | } 28 | 29 | // Salutation : here you tell us what Salutation is 30 | // Printer : what is this? 31 | // Greet : describe what this function does 32 | // CreateMessage : describe what this function does 33 | type RemotePoint struct { 34 | Attemps int `json:"attemps"` 35 | Partid int `json:"partid"` 36 | Group int `json:"group"` 37 | Id int `json:"id"` 38 | Reporttime int64 `json:"reporttime"` 39 | Table string `json:"table"` 40 | Tag1 string `json:"tag1"` 41 | Transaction string `json:"transaction"` 42 | Countername string `json:"countername"` 43 | Value float64 `json:"value"` 44 | } 45 | 46 | // swagger:model RemoteLabels 47 | type RemoteLabels []string 48 | 49 | // swagger:model RemoteValues 50 | type RemoteValues []string 51 | 52 | //swagger:model RemoteResponseData 53 | type RemoteResponseData struct { 54 | Data []struct { 55 | // example: 56 | Custom1 string `json:"custom_1"` 57 | // example: {"duration":"0","from_user":"1201","ruri_user":"102110112384797001",status":"8","type":"call"} 58 | Custom2 string `json:"custom_2"` 59 | // example: 1 60 | ID int `json:"id"` 61 | // example: 1634081796154 62 | MicroTs int `json:"micro_ts"` 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /utils/sipparser/contentdisposition_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | 10 | /* func TestContentDisposition(t *testing.T) { 11 | sm := &SipMsg{} 12 | s := "session; handling=required" 13 | sm.parseContentDisposition(s) 14 | if sm.ContentDisposition.Val != "session; handling=required" { 15 | t.Errorf("[TestContentDisposition] Error parsing content-disposition hdr: session; handling=required. sm.ContentDisposition.Val should be \"session; handling=required\" but received: \"%s\"", sm.ContentDisposition.Val) 16 | } 17 | if sm.ContentDisposition.DispType != "session" { 18 | t.Errorf("[TestContentDisposition] Error parsing content-disposition hdr: session; handling=required. sm.ContentDisposition.DispType should be \"session\" but received: \"%s\"", sm.ContentDisposition.DispType) 19 | } 20 | if len(sm.ContentDisposition.Params) != 1 { 21 | t.Errorf("[TestContentDisposition] Error parsing content-disposition hdr: session; handling=required. Length of sm.ContentDisposition.Params should be 1 but instead is: %d", len(sm.ContentDisposition.Params)) 22 | } 23 | if sm.ContentDisposition.Params[0].Param != "handling" { 24 | t.Errorf("[TestContentDisposition] Error parsing content-disposition hdr: session; handling=required. sm.ContentDisposition.Params[0].Param should be \"handling\" but received: \"%s\"", sm.ContentDisposition.Params[0].Param) 25 | } 26 | if sm.ContentDisposition.Params[0].Val != "required" { 27 | t.Errorf("[TestContentDisposition] Error parsing content-disposition hdr: session; handling=required. sm.ContentDisposition.Params[0].Val should be \"required\" but received: \"%s\"", sm.ContentDisposition.Params[0].Val) 28 | } 29 | } 30 | */ 31 | -------------------------------------------------------------------------------- /controller/v1/hepsubsearch.go: -------------------------------------------------------------------------------- 1 | package controllerv1 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/labstack/echo/v4" 7 | "github.com/sipcapture/homer-app/data/service" 8 | "github.com/sipcapture/homer-app/model" 9 | httpresponse "github.com/sipcapture/homer-app/network/response" 10 | "github.com/sipcapture/homer-app/utils/logger" 11 | ) 12 | 13 | type HepsubsearchController struct { 14 | Controller 15 | HepsubsearchService *service.HepsubsearchService 16 | } 17 | 18 | // swagger:route POST /hepsub/search hep hepSubSearchDoHepsubsearch 19 | // 20 | // Add hepsubsearch item 21 | // --- 22 | // consumes: 23 | // - application/json 24 | // produces: 25 | // - application/json 26 | // parameters: 27 | // + name: SearchObject 28 | // in: body 29 | // type: object 30 | // description: SearchObject parameters 31 | // schema: 32 | // type: SearchObject 33 | // required: true 34 | // Security: 35 | // - bearer: [] 36 | // 37 | // SecurityDefinitions: 38 | // bearer: 39 | // type: apiKey 40 | // name: Authorization 41 | // in: header 42 | // responses: 43 | // 201: body:HepsubCreateSuccessResponse 44 | // 400: body:FailureResponse 45 | func (hss *HepsubsearchController) DoHepsubsearch(c echo.Context) error { 46 | // Stub an user to be populated from the body 47 | searchObject := model.SearchObject{} 48 | err := c.Bind(&searchObject) 49 | if err != nil { 50 | return httpresponse.CreateBadResponse(&c, http.StatusBadRequest, err.Error()) 51 | } 52 | // validate input request body 53 | if err := c.Validate(searchObject); err != nil { 54 | logger.Error(err.Error()) 55 | return httpresponse.CreateBadResponse(&c, http.StatusBadRequest, err.Error()) 56 | } 57 | 58 | reply, err := hss.HepsubsearchService.DoHepSubSearch(searchObject) 59 | if err != nil { 60 | return httpresponse.CreateBadResponse(&c, http.StatusBadRequest, err.Error()) 61 | } 62 | return httpresponse.CreateSuccessResponseWithJson(&c, http.StatusCreated, []byte(reply)) 63 | } 64 | -------------------------------------------------------------------------------- /router/v1/user.go: -------------------------------------------------------------------------------- 1 | package apirouterv1 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "github.com/labstack/echo/v4" 6 | "github.com/sipcapture/homer-app/auth" 7 | controllerv1 "github.com/sipcapture/homer-app/controller/v1" 8 | "github.com/sipcapture/homer-app/data/service" 9 | "github.com/sipcapture/homer-app/utils/httpauth" 10 | "github.com/sipcapture/homer-app/utils/ldap" 11 | ) 12 | 13 | func RouteUserDetailsApis(acc *echo.Group, session *gorm.DB) { 14 | // initialize service of user 15 | userService := service.UserService{ServiceConfig: service.ServiceConfig{Session: session}, LdapClient: nil} 16 | // initialize user controller 17 | urc := controllerv1.UserController{ 18 | UserService: &userService, 19 | } 20 | // get all user 21 | acc.GET("/users", urc.GetUser) 22 | // 23 | acc.GET("/users/groups", urc.GetGroups) 24 | // create new user 25 | acc.POST("/users", urc.CreateUser, auth.IsAdmin) 26 | // update user 27 | acc.PUT("/users/:userGuid", urc.UpdateUser) 28 | 29 | // delete user 30 | acc.DELETE("/users/:userGuid", urc.DeleteUser, auth.IsAdmin) 31 | 32 | //get user 33 | acc.GET("/users/:userGuid", urc.GetUserByGUID) 34 | 35 | //return current user 36 | acc.GET("/users/profile", urc.GetCurrentUserProfile) 37 | 38 | } 39 | 40 | func RouteUserApis(acc *echo.Group, session *gorm.DB, ldapClient *ldap.LDAPClient, httpAuth *httpauth.Client) { 41 | // initialize service of user 42 | userService := service.UserService{ServiceConfig: service.ServiceConfig{Session: session}, LdapClient: ldapClient, HttpAuth: httpAuth} 43 | // initialize user controller 44 | urc := controllerv1.UserController{ 45 | UserService: &userService, 46 | } 47 | // user login 48 | acc.POST("/auth", urc.LoginUser) 49 | 50 | //list of auths 51 | acc.GET("/auth/type/list", urc.GetAuthTypeList) 52 | 53 | //Oauth2 Request 54 | acc.GET("/oauth2/redirect/:provider", urc.RedirecToSericeAuth) 55 | 56 | //Oauth2 Request 57 | acc.GET("/oauth2/auth/:provider", urc.AuthSericeRequest) 58 | 59 | //Oauth2 Token Ex-Change 60 | acc.POST("/oauth2/token", urc.Oauth2TokenExchange) 61 | } 62 | -------------------------------------------------------------------------------- /model/alias.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func (TableAlias) TableName() string { 8 | 9 | return "alias" 10 | } 11 | 12 | // swagger:model AliasStruct 13 | type TableAlias struct { 14 | Id int `gorm:"column:id;primary_key;AUTO_INCREMENT" json:"id"` 15 | GUID string `gorm:"column:guid;type:uuid" json:"guid"` 16 | // alias name 17 | // example: verizon 18 | // required: true 19 | Alias string `gorm:"column:alias;type:varchar(250)" json:"alias" validate:"required"` 20 | // example: 192.168.10.20 21 | // required: true 22 | IP string `gorm:"column:ip;type:varchar(60)" json:"ip" validate:"required,ip"` 23 | // example: 5060 24 | // required: true 25 | Port *int `gorm:"column:port;type:int;default:0" json:"port" validate:"required,numeric"` 26 | // example: 32 27 | // required: true 28 | Mask *int `gorm:"column:mask;type:int" json:"mask" validate:"required,numeric"` 29 | // example: 0 30 | // required: true 31 | CaptureID string `gorm:"column:captureID;type:varchar(20)" json:"captureID" validate:"required"` 32 | Status *bool `gorm:"column:status;type:bool" json:"status" validate:"required"` 33 | CreateDate time.Time `gorm:"column:create_date;default:current_timestamp;not null" json:"-"` 34 | } 35 | 36 | // swagger:model AliasStructList 37 | type TableAliasList struct { 38 | Data []TableAlias `json:"data"` 39 | } 40 | 41 | // swagger:model AliasCreationSuccessResponse 42 | type AliasCreationSuccessResponse struct { 43 | // example: f2d0a540-bf21-4c0d-ac73-8696ea10855a 44 | Data string `json:"data"` 45 | // example: successfully created alias 46 | Message string `json:"message"` 47 | } 48 | 49 | // swagger:model AliasUpdateSuccessResponse 50 | type AliasUpdateSuccessResponse struct { 51 | // example: f2d0a540-bf21-4c0d-ac73-8696ea10855a 52 | Data string `json:"data"` 53 | // example: successfully updated alias 54 | Message string `json:"message"` 55 | } 56 | 57 | // swagger:model AliasDeleteSuccessResponse 58 | type AliasDeleteSuccessResponse struct { 59 | // example: f2d0a540-bf21-4c0d-ac73-8696ea10855a 60 | Data string `json:"data"` 61 | // example: successfully deleted alias 62 | Message string `json:"message"` 63 | } 64 | -------------------------------------------------------------------------------- /model/hepsub.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | func (TableHepsubSchema) TableName() string { 9 | return "hepsub_mapping_schema" 10 | } 11 | 12 | // swagger:model HepsubSchema 13 | type TableHepsubSchema struct { 14 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT" json:"-"` 15 | // example: 4b855914-ca3d-4562-8563-f2b660fe2636 16 | GUID string `gorm:"column:guid;type:uuid" json:"guid" validate:"required"` 17 | // example: call 18 | Profile string `gorm:"column:profile;type:varchar(100)" json:"profile" validate:"required"` 19 | // example: 1 20 | Hepid int `gorm:"column:hepid;type:varchar(250)" json:"hepid" validate:"required"` 21 | // example: SIP 22 | HepAlias string `gorm:"column:hep_alias;type:varchar(250)" json:"hep_alias" validate:"required"` 23 | // example: 1603221345489 24 | Version int `gorm:"column:version;type:varchar(50)" json:"version" validate:"required"` 25 | Mapping json.RawMessage `gorm:"column:mapping" json:"mapping" validate:"required"` 26 | // example: 2020-10-20T21:15:45+02:00 27 | CreateDate time.Time `gorm:"column:create_date;default:current_timestamp;not null" json:"-"` 28 | } 29 | 30 | // swagger:model HepsubSchemaList 31 | type HepsubSchemaList struct { 32 | Data []TableHepsubSchema `json:"data"` 33 | // example: 34 | Count int `json:"count"` 35 | } 36 | 37 | // swagger:model HepsubUpdateSuccessResponse 38 | type HepsubUpdateSuccessResponse struct { 39 | // example: 4b855914-ca3d-4562-8563-f2b660fe2636 40 | Data string `json:"data"` 41 | // example: successfully updated hepsub settings 42 | Message string `json:"message"` 43 | } 44 | 45 | // swagger:model HepsubCreateSuccessResponse 46 | type HepsubCreateSuccessResponse struct { 47 | // example: 4b855914-ca3d-4562-8563-f2b660fe2636 48 | Data string `json:"data"` 49 | // example: successfully created hepsub settings 50 | Message string `json:"message"` 51 | } 52 | 53 | // swagger:model HepsubDeleteSuccessResponse 54 | type HepsubDeleteSuccessResponse struct { 55 | // example: 4b855914-ca3d-4562-8563-f2b660fe2636 56 | Data string `json:"data"` 57 | // example: successfully deleted hepsub settings 58 | Message string `json:"message"` 59 | } 60 | -------------------------------------------------------------------------------- /sqlparser/query/query.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | // Query represents a parsed query 4 | type Query struct { 5 | Type Type 6 | TableName string 7 | Conditions []Condition 8 | Updates map[string]string 9 | Inserts [][]string 10 | Fields []string // Used for SELECT (i.e. SELECTed field names) and INSERT (INSERTEDed field names) 11 | Aliases map[string]string 12 | } 13 | 14 | // Type is the type of SQL query, e.g. SELECT/UPDATE 15 | type Type int 16 | 17 | const ( 18 | // UnknownType is the zero value for a Type 19 | UnknownType Type = iota 20 | // Select represents a SELECT query 21 | Select 22 | // Update represents an UPDATE query 23 | Update 24 | // Insert represents an INSERT query 25 | Insert 26 | // Delete represents a DELETE query 27 | Delete 28 | ) 29 | 30 | // TypeString is a string slice with the names of all types in order 31 | var TypeString = []string{ 32 | "UnknownType", 33 | "Select", 34 | "Update", 35 | "Insert", 36 | "Delete", 37 | } 38 | 39 | // Operator is between operands in a condition 40 | type Operator int 41 | 42 | const ( 43 | // UnknownOperator is the zero value for an Operator 44 | UnknownOperator Operator = iota 45 | // Eq -> "=" 46 | Eq 47 | // Ne -> "!=" 48 | Ne 49 | // Gt -> ">" 50 | Gt 51 | // Lt -> "<" 52 | Lt 53 | // Gte -> ">=" 54 | Gte 55 | // Lte -> "<=" 56 | Lte 57 | // Lte -> "<=" 58 | Like 59 | ) 60 | 61 | // OperatorString is a string slice with the names of all operators in order 62 | var OperatorString = []string{ 63 | "UnknownOperator", 64 | "=", 65 | "!=", 66 | ">", 67 | "<", 68 | ">=", 69 | "<=", 70 | "LIKE", 71 | } 72 | 73 | // Condition is a single boolean condition in a WHERE clause 74 | type Condition struct { 75 | // Operand1 is the left hand side operand 76 | Operand1 string 77 | // Operand1IsField determines if Operand1 is a literal or a field name 78 | Operand1IsField bool 79 | // Operator is e.g. "=", ">" 80 | Operator Operator 81 | // Operand1 is the right hand side operand 82 | Operand2 string 83 | // Operand2IsField determines if Operand2 is a literal or a field name 84 | Operand2IsField bool 85 | // Logical determines if Operand2 is a literal or a field name 86 | Logical string 87 | } 88 | -------------------------------------------------------------------------------- /system/webmessages/webmessages.go: -------------------------------------------------------------------------------- 1 | package webmessages 2 | 3 | // Messages Codes System 4 | const ( 5 | PleaseTryAgain = "Please Try Again" 6 | OperationFailed = "Operation Failed" 7 | ) 8 | 9 | // Messages Codes for Users 10 | const ( 11 | UserRequestFailed = "failed to get Users" 12 | UserSettingsFailed = "failed to get user settings" 13 | UserProfileFailed = "failed to get user profile" 14 | MappingHepSubFailed = "failed to get hepsub mapping schema" 15 | InsertDashboardFailed = "failed to create a new dashboard" 16 | GetDashboardFailed = "failed to get dashboards" 17 | GetDashboardListFailed = "failed to get of list of dashboards" 18 | GetDBNodeListFailed = "failed to get list of db nodes" 19 | DeleteDashboardFailed = "failed to delete the dashboard" 20 | MappingSchemaFailed = "failed to get mapping schema" 21 | HepSubRequestFailed = "failed to get hep sub schema" 22 | MappingRecreateFailed = "failed to recreated all mappings" 23 | GetAgentSubFailed = "failed to get agent sub" 24 | GetAuthTokenFailed = "failed to get auth token" 25 | DeleteAdvancedAgainstFailed = "failed to delete advanced setting" 26 | GetAdvancedAgainstFailed = "failed to get advanced setting" 27 | MappingSchemaByUUIDFailed = "failed to get mapping schema by uuid" 28 | DeleteMappingSchemaFailed = "failed to delete mapping schema" 29 | SmartHepProfileFailed = "failed to get smart hep profile" 30 | DashboardNotExists = "dashboard for the user doesn't exist" 31 | HomeDashboardNotExists = "home dashboard for the user doesn't exist" 32 | Unauthorized = "Unauthorized" 33 | IncorrectPassword = "incorrect password" 34 | UserCreationFailed = "failed to Create User" 35 | SuccessfullyCreatedUser = "successfully created user" 36 | UserRequestFormatIncorrect = "request format is not correct" 37 | UIVersionFileNotExistsError = "error version file couldn't be found" 38 | SwaggerFileNotExistsError = "swagger file couldn't be found" 39 | BadPCAPData = "bad pcap data" 40 | BadDatabaseRetrieve = "db data retrieve error" 41 | GrafanaProcessingError = "grafana returned" 42 | ) 43 | -------------------------------------------------------------------------------- /model/globalSettings.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | func (TableGlobalSettings) TableName() string { 9 | return "global_settings" 10 | } 11 | 12 | // swagger:model GlobalSettingsStruct 13 | type TableGlobalSettings struct { 14 | Id int `gorm:"column:id;primary_key;AUTO_INCREMENT" json:"-"` 15 | // should be a unique value representing GlobalSettings 16 | // example: e71771a2-1ea0-498f-8d27-391713e10664 17 | // required: true 18 | GUID string `gorm:"column:guid;type:uuid" json:"guid" validate:"required"` 19 | // example: 1 20 | // required: true 21 | PartId int `gorm:"column:partid;type:int;not null" json:"partid" validate:"required"` 22 | // example: profile 23 | // required: true 24 | Category string `gorm:"column:category;type:varchar(100);not null" json:"category" validate:"required"` 25 | 26 | CreateDate time.Time `gorm:"column:create_date;default:current_timestamp;not null" json:"-"` 27 | // example: homer 28 | // required: true 29 | Param string `gorm:"column:param;type:varchar(100);not null" json:"param" validate:"required"` 30 | 31 | Data json.RawMessage `gorm:"column:data;type:json" json:"data"` 32 | } 33 | 34 | // swagger:model GlobalSettingsStructList 35 | type GlobalSettingsStructList struct { 36 | Count int `json:"count"` 37 | Data []TableGlobalSettings `json:"data"` 38 | } 39 | 40 | // swagger:model GlobalSettingsCreateSuccessfulResponse 41 | type GlobalSettingsCreateSuccessfulResponse struct { 42 | // example: af72057b-2745-0a1b-b674-56586aadec57 43 | Data string `json:"data"` 44 | // example: successfully created GlobalSetting 45 | Message string `json:"message"` 46 | } 47 | 48 | // swagger:model GlobalSettingsUpdateSuccessfulResponse 49 | type GlobalSettingsUpdateSuccessfulResponse struct { 50 | // example: af72057b-2745-0a1b-b674-56586aadec57 51 | Data string `json:"data"` 52 | // example: successfully updated GlobalSetting 53 | Message string `json:"message"` 54 | } 55 | 56 | // swagger:model GlobalSettingsDeleteSuccessfulResponse 57 | type GlobalSettingsDeleteSuccessfulResponse struct { 58 | // example: af72057b-2745-0a1b-b674-56586aadec57 59 | Data string `json:"data"` 60 | // example: successfully deleted GlobalSetting 61 | Message string `json:"message"` 62 | } 63 | -------------------------------------------------------------------------------- /model/prometheus.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // swagger:model PrometheusObject 8 | type PrometheusObject struct { 9 | Param struct { 10 | // example:500 11 | Limit int `json:"limit"` 12 | // example: 3600 13 | Precision int `json:"precision"` 14 | // example: ["go_goroutines"] 15 | Metrics []string `json:"metrics"` 16 | // false 17 | Total bool `json:"total"` 18 | } `json:"param"` 19 | Timestamp struct { 20 | From int64 `json:"from"` 21 | To int64 `json:"to"` 22 | } `json:"timestamp"` 23 | } 24 | 25 | type PrometheusResponse struct { 26 | Total int `json:"total"` 27 | D json.RawMessage `json:"data"` 28 | } 29 | 30 | // Salutation : here you tell us what Salutation is 31 | // Printer : what is this? 32 | // Greet : describe what this function does 33 | // CreateMessage : describe what this function does 34 | type PrometheusPoint struct { 35 | Attemps int `json:"attemps"` 36 | Partid int `json:"partid"` 37 | Group int `json:"group"` 38 | Id int `json:"id"` 39 | Reporttime int64 `json:"reporttime"` 40 | Table string `json:"table"` 41 | Tag1 string `json:"tag1"` 42 | Transaction string `json:"transaction"` 43 | Countername string `json:"countername"` 44 | Value float64 `json:"value"` 45 | } 46 | 47 | // swagger:model ListLabels 48 | type ListLabels []string 49 | 50 | // swagger:model Label 51 | type Label []struct { 52 | // example: localhost:9090 53 | Instance string `json:"instance"` 54 | // example: prometheus 55 | Job string `json:"job"` 56 | Version string `json:"version"` 57 | // example: go_routines 58 | Name string `json:"__name__"` 59 | } 60 | 61 | // swagger:model PrometheusResponse 62 | type PrometheusResponseValue struct { 63 | Success string `json:"success"` 64 | Data struct { 65 | // example: matrix 66 | ResultType string `json:"resultType"` 67 | Result []struct { 68 | Metric struct { 69 | // example: go_routines 70 | Name string `json:"__name__"` 71 | // example: localhost:9090 72 | Instance string `json:"instance"` 73 | // example: prometheus 74 | Job string `json:"job"` 75 | } `json:"metric"` 76 | Values [][]interface{} `json:"values"` 77 | } `json:"result"` 78 | } `json:"data"` 79 | } 80 | -------------------------------------------------------------------------------- /model/dashboard.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | func (TableUserSettings) TableName() string { 9 | return "user_settings" 10 | } 11 | 12 | // swagger:model UserSettings 13 | type TableUserSettings struct { 14 | Id int `gorm:"column:id;primary_key;AUTO_INCREMENT" json:"id"` 15 | // should be a unique value representing user 16 | // example: e71771a2-1ea0-498f-8d27-391713e10664 17 | // required: true 18 | GUID string `gorm:"column:guid;type:uuid" json:"guid" validate:"required"` 19 | // required: true 20 | UserName string `gorm:"column:username;type:varchar(100);not null" json:"username" validate:"required"` 21 | // example: 10 22 | // required: true 23 | PartId int `gorm:"column:partid;type:int;not null" json:"partid" validate:"required"` 24 | // example: profile 25 | // required: true 26 | Category string `gorm:"column:category;type:varchar(100);not null" json:"category" validate:"required"` 27 | 28 | CreateDate time.Time `gorm:"column:create_date;default:current_timestamp;not null" json:"-"` 29 | // example: homer 30 | // required: true 31 | Param string `gorm:"column:param;type:varchar(100);not null" json:"param" validate:"required"` 32 | 33 | Data json.RawMessage `gorm:"column:data;type:json" json:"data"` 34 | 35 | // external 36 | IsExternal bool `gorm:"-" json:"external"` 37 | 38 | //Data map[string]interface{} `gorm:"column:data;type:json" json:"data"` 39 | } 40 | 41 | // swagger:model DashboardElement 42 | type DashBoardElement struct { 43 | // example: fa 44 | CssClass string `json:"cssclass"` 45 | // example: _1631547759877 46 | Href string `json:"href"` 47 | // example: _1631547759877 48 | Id string `json:"id"` 49 | // example: admin 50 | Owner string `json:"owner"` 51 | // example: Search 52 | Name string `json:"name"` 53 | // example: search 54 | Param string `json:"param"` 55 | // example: false 56 | Shared bool `json:"shared"` 57 | // example: 1 58 | Type int `json:"type"` 59 | // example: 10 60 | Weight float64 `json:"weight"` 61 | } 62 | 63 | // swagger:model DashboardElementList 64 | type DashboardElementList struct { 65 | // example: ok 66 | Auth string `json:"auth"` 67 | // example: ok 68 | Status string `json:"status"` 69 | Data []DashBoardElement `json:"data"` 70 | // example: 1 71 | Total int `json:"total"` 72 | } 73 | -------------------------------------------------------------------------------- /model/userSettings.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type CorrelationMap struct { 4 | SourceField string `json:"source_field"` 5 | LookupID int `json:"lookup_id"` 6 | LookupProfile string `json:"lookup_profile"` 7 | LookupField string `json:"lookup_field"` 8 | LookupRange []int `json:"lookup_range"` 9 | } 10 | 11 | // swagger:model UserSetting 12 | type UserSetting struct { 13 | // example: dashboard 14 | Category string `json:"category"` 15 | Data DashBoardElement `json:"data,omitempty"` 16 | // example: 1868f318-5f16-4c40-ab1e-bfa4f417ff61 17 | GUID string `json:"guid"` 18 | // example: 59 19 | ID int `json:"id"` 20 | // example:_1633964934444 21 | Param string `json:"param"` 22 | // example: 10 23 | Partid int `json:"partid"` 24 | // example: admin 25 | Username string `json:"username"` 26 | } 27 | 28 | // swagger:model UserSettingList 29 | type UserSettingList struct { 30 | Data []UserSetting `json:"data"` 31 | // example: 7 32 | Count int `json:"count"` 33 | } 34 | 35 | // swagger:model UserSettingCreateSuccessfulResponse 36 | type UserSettingCreateSuccessfulResponse struct { 37 | // example: f4e2953e-ab42-40df-a7de-9ceb7faca396 38 | Data string `json:"data"` 39 | // example: successfully created userObject 40 | Message string `json:"message"` 41 | } 42 | 43 | // swagger:model UserSettingDeleteSuccessfulResponse 44 | type UserSettingDeleteSuccessfulResponse struct { 45 | // example: f4e2953e-ab42-40df-a7de-9ceb7faca396 46 | Data string `json:"data"` 47 | // example: successfully deleted userObject 48 | Message string `json:"message"` 49 | } 50 | 51 | // swagger:model UserSettingUpdateSuccessfulResponse 52 | type UserSettingUpdateSuccessfulResponse struct { 53 | // example: f4e2953e-ab42-40df-a7de-9ceb7faca396 54 | Data string `json:"data"` 55 | // example: successfully updated userObject" 56 | Message string `json:"message"` 57 | } 58 | 59 | // swagger:model UserProfile 60 | type UserProfile struct { 61 | // example: dashboard 62 | GUID string `json:"guid"` 63 | UserName string `json:"username"` 64 | DisplayName string `json:"displayname"` 65 | Avatar string `json:"avatar"` 66 | UserGroup string `json:"group"` 67 | UserAdmin bool `json:"admin"` 68 | ExternalAuth bool `json:"external_auth"` 69 | ExternalProfile string `json:"external_profile"` 70 | } 71 | -------------------------------------------------------------------------------- /docker/docker-entrypoint.d/9: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DB_HOST=${DB_HOST:-db} 4 | DB_PORT=${DB_PORT:-5432} 5 | DB_USER=${DB_USER:-root} 6 | DB_PASS=${DB_PASS:-homerSeven} 7 | DB_ROOT_USER=${DB_ROOT_USER:-$DB_USER} 8 | DB_ROOT_PASS=${DB_ROOT_PASS:-$DB_PASS} 9 | DB_SSL_MODE=${DB_SSL_MODE:-disable} 10 | 11 | if [ -f /usr/local/homer/etc/webapp_config.json ]; then 12 | 13 | ###Create user 14 | #/homer-app -create-homer-user -database-root-user=$DB_ROOT_USER -database-host=$DB_HOST -database-root-password=$DB_ROOT_PASS -database-ssl-mode=$DB_SSL_MODE 15 | ###Show user 16 | #/homer-app -show-db-user -database-root-user=$DB_ROOT_USER -database-host=$DB_HOST -database-root-password=$DB_ROOT_PASS -database-ssl-mode=$DB_SSL_MODE 17 | ###Create Role 18 | #/homer-app -create-homer-role -database-root-user=$DB_ROOT_USER -database-host=localhost -database-root-password=$DB_ROOT_PASS -database-homer-data=homer_data -database-homer-config=homer_config -database-ssl-mode=$DB_SSL_MODE 19 | 20 | ###Create DB 21 | /homer-app -create-config-db -database-root-user=$DB_ROOT_USER -database-host=$DB_HOST -database-port=$DB_PORT -database-root-password=$DB_ROOT_PASS -database-homer-user=$DB_USER -database-ssl-mode=$DB_SSL_MODE 22 | /homer-app -create-data-db -database-root-user=$DB_ROOT_USER -database-host=$DB_HOST -database-port=$DB_PORT -database-root-password=$DB_ROOT_PASS -database-homer-user=$DB_USER -database-ssl-mode=$DB_SSL_MODE 23 | 24 | #Save it or edit the webapp_config.json manualy 25 | #/homer-app -save-homer-db-config-settings -database-host=localhost -database-homer-config=homer_config -database-homer-user=homer_user -database-homer-password=homer_password -database-ssl-mode=$DB_SSL_MODE 26 | #/homer-app -save-homer-db-data-settings -database-host=localhost -database-homer-data=homer_data -database-homer-user=homer_user -database-homer-password=homer_password -database-ssl-mode=$DB_SSL_MODE 27 | 28 | #Create Table / Migration - connection data will be read from the webapp_config.json 29 | /homer-app -create-table-db-config -webapp-config-path=/usr/local/homer/etc -database-ssl-mode=$DB_SSL_MODE 30 | 31 | #Populate DB 32 | /homer-app -populate-table-db-config -webapp-config-path=/usr/local/homer/etc -database-ssl-mode=$DB_SSL_MODE 33 | 34 | echo "Database provisioning completed!" 35 | 36 | else 37 | 38 | echo "DB fail! Configuration file not found!" 39 | 40 | fi 41 | 42 | exec "$@" 43 | -------------------------------------------------------------------------------- /model/agentLocation.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func (TableAgentLocationSession) TableName() string { 8 | return "agent_location_session" 9 | } 10 | 11 | // swagger:model AgentsLocation 12 | type TableAgentLocationSession struct { 13 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT" json:"-"` 14 | GUID string `gorm:"column:guid;type:uuid" json:"uuid" validate:"required"` 15 | Gid int `gorm:"column:gid;type:int" json:"gid" validate:"required"` 16 | Host string `gorm:"column:host;type:varchar(250);default:'127.0.0.1'" json:"host" validate:"required"` 17 | Port int `gorm:"column:port;type:int;default:8080" json:"port"` 18 | Protocol string `gorm:"column:protocol;type:varchar(50);default:'log'" json:"protocol"` 19 | Path string `gorm:"column:path;type:varchar(250);default:'/api/search'" json:"path"` 20 | Node string `gorm:"column:node;type:varchar(100);default:'testnode'" json:"node"` 21 | Type string `gorm:"column:type;type:varchar(200);default:'type'" json:"type"` 22 | TTL int `gorm:"-" json:"ttl"` 23 | CreateDate time.Time `gorm:"column:create_date;default:current_timestamp;not null" json:"create_date"` 24 | ExpireDate time.Time `gorm:"column:expire_date;not null" json:"expire_date"` 25 | Active int `gorm:"column:active;type:int;default:1" json:"active"` 26 | } 27 | 28 | // swagger:model AgentsLocationList 29 | type TableAgentLocationSessionList struct { 30 | Data []TableAgentLocationSession `json:"data"` 31 | } 32 | 33 | // swagger:model AgentLocationUpdateSuccessResponse 34 | type AgentLocationUpdateSuccessResponse struct { 35 | // example: 4b855914-ca3d-4562-8563-f2b660fe2636 36 | Data string `json:"data"` 37 | // example: successfully updated AgentLocation settings 38 | Message string `json:"message"` 39 | } 40 | 41 | // swagger:model AgentLocationCreateSuccessResponse 42 | type AgentLocationCreateSuccessResponse struct { 43 | // example: 4b855914-ca3d-4562-8563-f2b660fe2636 44 | Data string `json:"data"` 45 | // example: successfully created AgentLocation settings 46 | Message string `json:"message"` 47 | } 48 | 49 | // swagger:model AgentLocationDeleteSuccessResponse 50 | type AgentLocationDeleteSuccessResponse struct { 51 | // example: 4b855914-ca3d-4562-8563-f2b660fe2636 52 | Data string `json:"data"` 53 | // example: successfully deleted AgentLocation settings 54 | Message string `json:"message"` 55 | } 56 | -------------------------------------------------------------------------------- /scripts/github-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Author: Stefan Buck 4 | # License: MIT 5 | # https://gist.github.com/stefanbuck/ce788fee19ab6eb0b4447a85fc99f447 6 | # 7 | # 8 | # This script accepts the following parameters: 9 | # 10 | # * owner 11 | # * repo 12 | # * tag 13 | # * filename 14 | # * github_api_token 15 | # 16 | # Script to upload a release asset using the GitHub API v3. 17 | # 18 | # Example: 19 | # 20 | # upload-github-release-asset.sh github_api_token=TOKEN owner=stefanbuck repo=playground tag=v0.1.0 filename=./build.zip 21 | # 22 | 23 | # Check dependencies. 24 | set -e 25 | xargs=$(which gxargs || which xargs) 26 | 27 | # Validate settings. 28 | [ "$TRACE" ] && set -x 29 | 30 | CONFIG=$@ 31 | 32 | for line in $CONFIG; do 33 | eval "$line" 34 | done 35 | 36 | # Define variables. 37 | GH_API="https://api.github.com" 38 | GH_REPO="$GH_API/repos/$owner/$repo" 39 | GH_TAGS="$GH_REPO/releases/tags/$tag" 40 | VERSION="${tag}" 41 | TEXT="${text:-tag}" 42 | BRANCH="${branch:-master}" 43 | AUTH="Authorization: token $github_api_token" 44 | WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie" 45 | CURL_ARGS="-LJO#" 46 | 47 | if [[ "$tag" == 'LATEST' ]]; then 48 | GH_TAGS="$GH_REPO/releases/latest" 49 | fi 50 | 51 | # Validate token. 52 | curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!"; exit 1; } 53 | 54 | # Create Release 55 | generate_post_data() 56 | { 57 | cat <&2; exit 1; } 78 | 79 | # Upload asset 80 | echo "Uploading asset... " 81 | 82 | # Construct url 83 | GH_ASSET="https://uploads.github.com/repos/$owner/$repo/releases/$id/assets?name=$(basename $filename)" 84 | curl "$GITHUB_OAUTH_BASIC" --data-binary @"$filename" -H "Authorization: token $github_api_token" -H "Content-Type: application/octet-stream" $GH_ASSET 85 | -------------------------------------------------------------------------------- /utils/sipparser/uri_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestUri(t *testing.T) { 14 | s := "sip:15555551000;npdi=yes;rn=15555551999@0.0.0.0:5060;user=phone;key" 15 | u := ParseURI(s) 16 | if u.Scheme != SIP_SCHEME { 17 | t.Errorf("[TestUri] Error parsing URI \"sip:15555551000;npdi=yes;rn=15555551999@0.0.0.0:5060;user=phone;key\". Scheme should be sip not received val: " + u.Scheme) 18 | } 19 | if u.User != "15555551000" { 20 | t.Errorf("[TestUri] Error parsing URI \"sip:15555551000@0.0.0.0:5060;user=phone\". User value is not \"15555551000\".") 21 | } 22 | if u.Host != "0.0.0.0" { 23 | t.Errorf("[TestUri] Error parsing URI \"sip:15555551000@0.0.0.0:5060;user=phone\". Host value should be \"0.0.0.0\" but received: " + u.Host) 24 | } 25 | if u.Port != "5060" { 26 | t.Errorf("[TestUri] Error parsing URI \"sip:15555551000@0.0.0.0:5060;user=phone\". Port value should be '5060' ... but it is not.") 27 | } 28 | if u.PortInt != 5060 { 29 | t.Errorf("[TestUri] Error parsing URI \"sip:15555551000@0.0.0.0:5060;user=phone\". Port value should be 5060 ... but it is not.") 30 | } 31 | s = "tel:5554448000@myfoo.com" 32 | u = ParseURI(s) 33 | if u.User != "5554448000" { 34 | t.Errorf("[TestUri] Error parsing URI \"tel:5554448000@myfoo.com\". Should have received \"5554448000\" as the user. Received: " + u.User) 35 | } 36 | if u.Host != "myfoo.com" { 37 | t.Errorf("[TestUri] Error parsing URI \"tel:5554448000@myfoo.com\". Host should be \"myfoo.com\" but received: " + u.Host) 38 | } 39 | s = "tel:+5554448000" 40 | u = ParseURI(s) 41 | if u.User != "+5554448000" { 42 | t.Errorf("[TestUri] Error parsing URI \"tel:+5554448000\". Should have received \"+5554448000\" as the user. Received: " + u.User) 43 | } 44 | s = "sip:myfoo.com" 45 | u = ParseURI(s) 46 | if u.Raw != "myfoo.com" { 47 | t.Errorf("[TestUri] Error parsing URI \"sip:myfoo.com\". Should have received \"myfoo.com\" as Raw. Received: " + u.Raw) 48 | } 49 | if u.User != "" { 50 | t.Errorf("[TestUri] Error parsing URI \"sip:myfoo.com\". User should be \"\" but received: " + u.User) 51 | } 52 | if u.Host != "myfoo.com" { 53 | t.Errorf("[TestUri] Error parsing URI \"sip:myfoo.com\". Host should be \"myfoo.com\" but received: " + u.Host) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /utils/sipparser/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | import ( 9 | "testing" 10 | ) 11 | 12 | func TestCleanWs(t *testing.T) { 13 | s := " white space " 14 | if cleanWs(s) != "white space" { 15 | t.Errorf("[TestCleanWs] Error from cleanWs. Got unexpected result.") 16 | } 17 | s = " " // tab 18 | if cleanWs(s) != "" { 19 | t.Errorf("[TestCleanWs] Error from cleanWs. Got unexpected result.") 20 | } 21 | } 22 | 23 | func TestCleanBrack(t *testing.T) { 24 | s := "" 25 | if cleanBrack(s) != "sip:foo@bar.com" { 26 | t.Errorf("[TestCleanBrack] Error cleaning bracks from \"\".") 27 | } 28 | s = ";param=foo?header=boo" 29 | if cleanBrack(s) != "sip:foo@bar.com;param=foo?header=boo" { 30 | t.Errorf("[TestCleanBrack] Error cleaning bracks from \";param=foo?header=boo\".") 31 | } 32 | } 33 | 34 | func TestGetName(t *testing.T) { 35 | s := "\"name\" " 36 | name, _ := getName(s) 37 | if name != "name" { 38 | t.Errorf("[TestGetName] Error getting name from getName for: \"name\" ") 39 | } 40 | s = "" 41 | name, _ = getName(s) 42 | if name != "" { 43 | t.Errorf("[TestGetName] Received a name for a string without one from getName.") 44 | } 45 | s = "Anonymous ;tag=hyh8" 46 | name, _ = getName(s) 47 | if name != "Anonymous" { 48 | t.Errorf("[TestGetName] Error parsing string: Anonymous ;tag=hyh8. Should have gotten name: \"Anonymous\" but received: \"%s\".", name) 49 | } 50 | } 51 | 52 | func TestGetCommaSeperated(t *testing.T) { 53 | s := "foo " 54 | cs := getCommaSeperated(s) 55 | if cs != nil { 56 | t.Errorf("[TestGetCommaSeperated] Error with string: \"foo\". getCommaSeperated should have returned nil.") 57 | } 58 | s = "foo , bar" 59 | cs = getCommaSeperated(s) 60 | if cs == nil { 61 | t.Errorf("[TestGetCommaSeperated] Error with string: \"foo , bar\". getCommaSeperated should have returned a list of strings. Returned nil.") 62 | } 63 | if len(cs) != 2 { 64 | t.Errorf("[TestGetCommaSeperated] Error with string: \"foo , bar\". Returned list but length should be 2.") 65 | } 66 | if cs[0] != "foo" { 67 | t.Errorf("[TestGetCommaSeperated] Error with string: \"foo , bar\". Returned list pos[0] should be \"foo\" but received: " + cs[0]) 68 | } 69 | if cs[1] != "bar" { 70 | t.Errorf("[TestGetCommaSeperated] Error with string: \"foo , bar\". Returned list pos[1] should be \"bar\" but received: " + cs[1]) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /utils/sipparser/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from go standard library 9 | import ( 10 | "strings" 11 | ) 12 | 13 | func cleanWs(s string) string { 14 | if s == "" { 15 | return s 16 | } 17 | PREFIXWS: 18 | if len(s) > 0 { 19 | if s[0] == ' ' || s[0] == '\t' { 20 | s = s[1:] 21 | goto PREFIXWS 22 | } 23 | } 24 | 25 | SUFFIXWS: 26 | if len(s) > 0 { 27 | if s[len(s)-1] == ' ' || s[len(s)-1] == '\t' { 28 | s = s[0 : len(s)-1] 29 | goto SUFFIXWS 30 | } 31 | } 32 | return s 33 | } 34 | 35 | func cleanBrack(s string) string { 36 | if s == "" { 37 | return "" 38 | } 39 | sLen := len(s) 40 | var n string 41 | switch { 42 | case sLen > 0 && s[0] == '<': 43 | n = s[1:] 44 | default: 45 | n = s 46 | } 47 | for i := range n { 48 | if n[i] == '>' { 49 | if len(n)-1 > i+1 { 50 | if n[i+1] == ';' { 51 | n = n[0:i] + n[i+1:] 52 | return n 53 | } 54 | } 55 | if i == len(n)-1 { 56 | n = n[0:i] 57 | return n 58 | } 59 | } 60 | } 61 | return n 62 | } 63 | 64 | func getQuoteChars(s string) (one int, two int, chk bool) { 65 | ct := 0 66 | for i := range s { 67 | if s[i] == '"' { 68 | switch { 69 | case ct == 0: 70 | one = i 71 | ct = 1 72 | case ct == 1: 73 | two = i 74 | return one, two, true 75 | default: 76 | return one, two, false 77 | } 78 | } 79 | } 80 | return 0, 0, false 81 | } 82 | 83 | func getBracks(s string) (one int, two int, chk bool) { 84 | one = strings.IndexRune(s, '<') 85 | if one == -1 { 86 | return 0, 0, false 87 | } 88 | two = strings.IndexRune(s, '>') 89 | if two == -1 { 90 | return 0, 0, false 91 | } 92 | if two < one { 93 | return 0, 0, false 94 | } 95 | return one, two, true 96 | } 97 | 98 | func getName(s string) (name string, end int) { 99 | if s == "" { 100 | return "", 0 101 | } 102 | posOne, posTwo, chk := getQuoteChars(s) 103 | if chk == true { 104 | if len(s)-1 > posTwo { 105 | return cleanWs(s[posOne+1 : posTwo]), posTwo 106 | } 107 | return "", 0 108 | } 109 | posOne = strings.IndexRune(s, '<') 110 | if posOne == -1 { 111 | return "", 0 112 | } 113 | if posOne == 0 { 114 | return "", 0 115 | } 116 | return cleanWs(s[0:posOne]), posOne 117 | } 118 | 119 | func getCommaSeperated(str string) []string { 120 | s := strings.Split(str, ",") 121 | if len(s) == 1 { 122 | return nil 123 | } 124 | for i := range s { 125 | s[i] = cleanWs(s[i]) 126 | } 127 | return s 128 | } 129 | -------------------------------------------------------------------------------- /utils/sipparser/diversion.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "errors" 11 | "fmt" 12 | ) 13 | 14 | type parseDivStateFn func(d *Diversion) parseDivStateFn 15 | 16 | type Diversion struct { 17 | Error error 18 | Val string 19 | Name string 20 | URI *URI 21 | Counter string 22 | Reason string 23 | Privacy string 24 | Params []*Param 25 | } 26 | 27 | func (r *Diversion) addParam(s string) { 28 | p := getParam(s) 29 | switch { 30 | case p.Param == "reason": 31 | r.Reason = p.Val 32 | case p.Param == "privacy": 33 | r.Privacy = p.Val 34 | case p.Param == "counter": 35 | r.Counter = p.Val 36 | default: 37 | switch { 38 | case r.Params == nil: 39 | r.Params = []*Param{p} 40 | default: 41 | r.Params = append(r.Params, p) 42 | } 43 | } 44 | } 45 | 46 | func (d *Diversion) parse() { 47 | for state := parseDiv; state != nil; { 48 | state = state(d) 49 | } 50 | } 51 | 52 | func parseDiv(d *Diversion) parseDivStateFn { 53 | if d.Error != nil { 54 | return nil 55 | } 56 | d.Name, _ = getName(d.Val) 57 | return parseDivGetUri 58 | } 59 | 60 | func parseDivGetUri(d *Diversion) parseDivStateFn { 61 | left := 0 62 | right := 0 63 | for i := range d.Val { 64 | if d.Val[i] == '<' && left == 0 { 65 | left = i 66 | } 67 | if d.Val[i] == '>' && right == 0 { 68 | right = i 69 | } 70 | } 71 | if left < right { 72 | d.URI = ParseURI(d.Val[left+1 : right]) 73 | if d.URI.Error != nil { 74 | d.Error = fmt.Errorf("parseDivGetUri err: received err getting uri: %v", d.URI.Error) 75 | return nil 76 | } 77 | return parseDivGetParams 78 | } 79 | d.Error = errors.New("parseDivGetUri err: could not locate bracks in uri") 80 | return nil 81 | } 82 | 83 | func parseDivGetParams(d *Diversion) parseDivStateFn { 84 | var pos []int 85 | right := 0 86 | for i := range d.Val { 87 | if d.Val[i] == '>' && right == 0 { 88 | right = i 89 | } 90 | } 91 | if len(d.Val) > right+1 { 92 | pos = make([]int, 0) 93 | for i := range d.Val[right+1:] { 94 | if d.Val[right+1:][i] == ';' { 95 | pos = append(pos, i) 96 | } 97 | } 98 | } 99 | if pos == nil { 100 | return nil 101 | } 102 | for i := range pos { 103 | if len(pos)-1 == i { 104 | if len(d.Val[right+1:])-1 > pos[i]+1 { 105 | d.addParam(d.Val[right+1:][pos[i]+1:]) 106 | } 107 | } 108 | if len(pos)-1 > i { 109 | d.addParam(d.Val[right+1:][pos[i]+1 : pos[i+1]]) 110 | } 111 | } 112 | return nil 113 | } 114 | -------------------------------------------------------------------------------- /utils/sipparser/remotepartyid.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "errors" 11 | "fmt" 12 | ) 13 | 14 | type parseRpidStateFn func(r *RemotePartyId) parseRpidStateFn 15 | 16 | type RemotePartyId struct { 17 | Error error 18 | Val string 19 | Name string 20 | URI *URI 21 | Party string 22 | Screen string 23 | Privacy string 24 | Params []*Param 25 | } 26 | 27 | func (r *RemotePartyId) addParam(s string) { 28 | p := getParam(s) 29 | switch { 30 | case p.Param == "screen": 31 | r.Screen = p.Val 32 | case p.Param == "party": 33 | r.Party = p.Val 34 | case p.Param == "privacy": 35 | r.Privacy = p.Val 36 | default: 37 | switch { 38 | case r.Params == nil: 39 | r.Params = []*Param{p} 40 | default: 41 | r.Params = append(r.Params, p) 42 | } 43 | } 44 | } 45 | 46 | func (r *RemotePartyId) parse() { 47 | for state := parseRpid; state != nil; { 48 | state = state(r) 49 | } 50 | } 51 | 52 | func parseRpid(r *RemotePartyId) parseRpidStateFn { 53 | if r.Error != nil { 54 | return nil 55 | } 56 | r.Name, _ = getName(r.Val) 57 | return parseRpidGetUri 58 | } 59 | 60 | func parseRpidGetUri(r *RemotePartyId) parseRpidStateFn { 61 | left := 0 62 | right := 0 63 | for i := range r.Val { 64 | if r.Val[i] == '<' && left == 0 { 65 | left = i 66 | } 67 | if r.Val[i] == '>' && right == 0 { 68 | right = i 69 | } 70 | } 71 | if left < right { 72 | r.URI = ParseURI(r.Val[left+1 : right]) 73 | if r.URI.Error != nil { 74 | r.Error = fmt.Errorf("parseRpidGetUri err: received err getting uri: %v", r.URI.Error) 75 | return nil 76 | } 77 | return parseRpidGetParams 78 | } 79 | r.Error = errors.New("parseRpidGetUri err: could not locate bracks in uri") 80 | return nil 81 | } 82 | 83 | func parseRpidGetParams(r *RemotePartyId) parseRpidStateFn { 84 | var pos []int 85 | right := 0 86 | for i := range r.Val { 87 | if r.Val[i] == '>' && right == 0 { 88 | right = i 89 | } 90 | } 91 | if len(r.Val) > right+1 { 92 | pos = make([]int, 0) 93 | for i := range r.Val[right+1:] { 94 | if r.Val[right+1:][i] == ';' { 95 | pos = append(pos, i) 96 | } 97 | } 98 | } 99 | if pos == nil { 100 | return nil 101 | } 102 | for i := range pos { 103 | if len(pos)-1 == i { 104 | if len(r.Val[right+1:])-1 > pos[i]+1 { 105 | r.addParam(r.Val[right+1:][pos[i]+1:]) 106 | } 107 | } 108 | if len(pos)-1 > i { 109 | r.addParam(r.Val[right+1:][pos[i]+1 : pos[i+1]]) 110 | } 111 | } 112 | return nil 113 | } 114 | -------------------------------------------------------------------------------- /utils/sipparser/startline_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestStartLine(t *testing.T) { 14 | str := "SIP/2.0 487 Request Cancelled" 15 | s := &StartLine{Val: str} 16 | s.run() 17 | if s.Type != SIP_RESPONSE { 18 | t.Error("[TestStartLine] Error parsing startline: SIP/2.0 487 Request Cancelled. s.Type should be \"RESPONSE\".") 19 | } 20 | if s.Resp != "487" { 21 | t.Error("[TestStartLine] Error parsing startline: SIP/2.0 487 Request Cancelled. s.Resp should be \"487\".") 22 | } 23 | if s.RespText != "Request Cancelled" { 24 | t.Error("[TestStartLine] Error parsing startline: SIP/2.0 487 Request Cancelled. s.RespText should be \"Request Cancelled\".") 25 | } 26 | str = "1412@34922@336312786@1.2.3.4:5061;transport=tcp;user=phone@home1.2.3.4 111111111" 27 | s = ParseStartLine(str) 28 | if s.Error == nil { 29 | t.Error("[TestStartLine] Error parsing startline. s.Error should not be nil.") 30 | } 31 | str = "dlskmgkfmdg ldf,l," 32 | s = ParseStartLine(str) 33 | if s.Error == nil { 34 | t.Error("[TestStartLine] Error parsing startline. s.Error should not be nil for string: \"dlskmgkfmdg ldf,l,\".") 35 | } 36 | str = "INVITE sip:+15554440000@0.0.0.0;user=phone SIP/2.0" 37 | s = ParseStartLine(str) 38 | if s.Error != nil { 39 | t.Errorf("[TestStartLine] Got error when parsing startline: \"INVITE sip:+15554440000@0.0.0.0;user=phone SIP/2.0\". Received err: %v", s.Error) 40 | } 41 | if s.Type != SIP_REQUEST { 42 | t.Error("[TestStartLine] Got error when parsing startline: \"INVITE sip:+15554440000@0.0.0.0;user=phone SIP/2.0\". s.Type should be \"Request\".") 43 | } 44 | if s.Method != SIP_METHOD_INVITE { 45 | t.Error("[TestStartLine] Got error when parsing startline: \"INVITE sip:+15554440000@0.0.0.0;user=phone SIP/2.0\". s.Method should be \"INVITE\".") 46 | } 47 | if s.Proto != "SIP" { 48 | t.Errorf("[TestStartLine] Got error when startline: \"INVITE sip:+15554440000@0.0.0.0;user=phone SIP/2.0\". s.Proto should be \"SIP\". Received: \"%s\"", s.Proto) 49 | } 50 | if s.Version != "2.0" { 51 | t.Errorf("[TestStartLine] Got error when parsing startline: \"INVITE sip:+15554440000@0.0.0.0;user=phone SIP/2.0\". s.Version should be \"2.0\". Received: \"%s\"", s.Version) 52 | } 53 | // throwing this in to make sure we don't toss an index error 54 | str = "INVITE foo@bar.com SIP/" 55 | s = ParseStartLine(str) 56 | if s.Error == nil { 57 | t.Error("[TestStartLine] Should have a no version err when parsing request line: \"INVITE foo@bar.com SIP/\".") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /utils/sipparser/via_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestVia(t *testing.T) { 14 | sm := &SipMsg{} 15 | s := "SIP/2.0/UDP 0.0.0.0:5060;branch=z9hG4bK05B1a4c756d527cb513" 16 | sm.parseVia(s) 17 | if sm.Error != nil { 18 | t.Errorf("[TestVia] Error parsing via. Received: %v", sm.Error) 19 | } 20 | /* 21 | if sm.Via[0].Proto != "SIP" { 22 | t.Errorf("[TestVia] Error parsing via \"SIP/2.0/UDP 0.0.0.0:5060;branch=z9hG4bK05B1a4c756d527cb513\". sm.Via[0].Proto should be \"SIP\" but received: \"%s\"", sm.Via[0].Proto) 23 | } 24 | if sm.Via[0].Version != "2.0" { 25 | t.Errorf("[TestVia] Error parsing via \"SIP/2.0/UDP 0.0.0.0:5060;branch=z9hG4bK05B1a4c756d527cb513\". sm.Via[0].Version should be \"2.0\" but received: \"%s\"", sm.Via[0].Version) 26 | } 27 | if sm.Via[0].Transport != "UDP" { 28 | t.Errorf("[TestVia] Error parsing via \"SIP/2.0/UDP 0.0.0.0:5060;branch=z9hG4bK05B1a4c756d527cb513\". sm.Via[0].Transport should be \"UDP\" but received: \"%s\"", sm.Via[0].Transport) 29 | } 30 | if sm.Via[0].SentBy != "0.0.0.0:5060" { 31 | t.Errorf("[TestVia] Error parsing via \"SIP/2.0/UDP 0.0.0.0:5060;branch=z9hG4bK05B1a4c756d527cb513\". Sent by should be \"0.0.0.0:5060\" but received: \"%s\".", sm.Via[0].SentBy) 32 | } 33 | */ 34 | if sm.ViaOne != s { 35 | t.Errorf("[TestVia] Error parsing via. Received: %v", sm.Error) 36 | } 37 | if sm.ViaOneBranch != "z9hG4bK05B1a4c756d527cb513" { 38 | t.Errorf("[TestMultipleVias] Error parsing via %q. sm.ViaOneBranch should be \"z9hG4bK05B1a4c756d527cb513\" but received %q", s, sm.ViaOneBranch) 39 | } 40 | } 41 | 42 | func TestMultipleVias(t *testing.T) { 43 | sm := &SipMsg{} 44 | //s := "SIP/2.0/UDP 0.0.0.0:5060;branch=z9hG4bKea28eb32f60dc,SIP/2.0/UDP 1.1.1.1:5060;branch=z9hG4bK1750901461" 45 | s := "SIP/2.0/UDP 0.0.0.0:5060;branch=z9hG4bKea28eb32f60dc;rport=5080" 46 | sm.parseVia(s) 47 | if sm.ViaOneBranch != "z9hG4bKea28eb32f60dc" { 48 | t.Errorf("[TestMultipleVias] Error parsing via %q. sm.ViaOneBranch should be \"z9hG4bKea28eb32f60dc\" but received %q", s, sm.ViaOneBranch) 49 | } 50 | /* if len(sm.Via) != 2 { 51 | t.Fatalf("[TestMultipleVias] Error parsing via %q. len(sm.Via) should be 2 but received %d", s, len(sm.Via)) 52 | } 53 | if sm.Via[0].Branch != "z9hG4bKea28eb32f60dc" { 54 | t.Errorf("[TestMultipleVias] Error parsing via %q. sm.Via[0].Branch should be \"z9hG4bKea28eb32f60dc\" but received %q", s, sm.Via[0].Branch) 55 | } 56 | if sm.Via[1].Branch != "z9hG4bK1750901461" { 57 | t.Errorf("[TestMultipleVias] Error parsing via %q. sm.Via[1].Branch should be \"z9hG4bK1750901461\" but received %q", s, sm.Via[1].Branch) 58 | } */ 59 | } 60 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '23 23 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'go' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /data/service/advanced.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | 8 | "github.com/Jeffail/gabs/v2" 9 | "github.com/sipcapture/homer-app/model" 10 | ) 11 | 12 | type AdvancedService struct { 13 | ServiceConfig 14 | } 15 | 16 | // this method create new user in the database 17 | // it doesn't check internally whether all the validation are applied or not 18 | func (as *AdvancedService) GetAll() (string, error) { 19 | 20 | var userGlobalSettings = []model.TableGlobalSettings{} 21 | if err := as.Session.Debug(). 22 | Table("global_settings"). 23 | Find(&userGlobalSettings).Error; err != nil { 24 | return "", errors.New("no users settings found") 25 | } 26 | 27 | data, _ := json.Marshal(userGlobalSettings) 28 | rows, _ := gabs.ParseJSON(data) 29 | count, _ := rows.ArrayCount() 30 | reply := gabs.New() 31 | reply.Set(count, "count") 32 | reply.Set(rows.Data(), "data") 33 | return reply.String(), nil 34 | } 35 | 36 | // this method gets all the mapping from database 37 | func (as *AdvancedService) GetAdvancedAgainstGUID(guid string) (string, error) { 38 | var userGlobalSettings = []model.TableGlobalSettings{} 39 | var count int 40 | if err := as.Session.Debug().Table("global_settings"). 41 | Where("guid = ?", guid). 42 | Find(&userGlobalSettings).Count(&count).Error; err != nil { 43 | return "", err 44 | } 45 | if len(userGlobalSettings) == 0 { 46 | return "", fmt.Errorf("data was not found") 47 | } 48 | data, _ := json.Marshal(userGlobalSettings) 49 | response := fmt.Sprintf("{\"count\":%d,\"data\":%s}", count, string(data)) 50 | return response, nil 51 | } 52 | 53 | // this method gets all users from database 54 | func (as *AdvancedService) AddAdvanced(data model.TableGlobalSettings) (string, error) { 55 | if err := as.Session.Debug().Table("global_settings"). 56 | Create(&data).Error; err != nil { 57 | return "", err 58 | } 59 | response := fmt.Sprintf("{\"message\":\"successfully created advanced settings\",\"data\":\"%s\"}", data.GUID) 60 | return response, nil 61 | } 62 | 63 | // this method gets all users from database 64 | func (as *AdvancedService) UpdateAdvancedAgainstGUID(guid string, data model.TableGlobalSettings) (string, error) { 65 | if err := as.Session.Debug().Table("global_settings"). 66 | Where("guid = ?", guid). 67 | Update(&data).Error; err != nil { 68 | return "", err 69 | } 70 | response := fmt.Sprintf("{\"message\":\"successfully updated global_settings settings\",\"data\":\"%s\"}", guid) 71 | return response, nil 72 | } 73 | 74 | // this method delete the mapping from database 75 | func (as *AdvancedService) DeleteAdvancedAgainstGUID(guid string) (string, error) { 76 | var advancedObject []*model.TableGlobalSettings 77 | if err := as.Session.Debug().Table("global_settings"). 78 | Where("guid = ?", guid). 79 | Delete(&advancedObject).Error; err != nil { 80 | return "", err 81 | } 82 | response := fmt.Sprintf("{\"message\":\"successfully deleted global_settings settings\",\"data\":\"%s\"}", guid) 83 | return response, nil 84 | } 85 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sipcapture/homer-app 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.7 6 | 7 | require ( 8 | github.com/Jeffail/gabs v1.4.0 9 | github.com/Jeffail/gabs/v2 v2.7.0 10 | github.com/davecgh/go-spew v1.1.1 11 | github.com/dop251/goja v0.0.0-20250630131328-58d95d85e994 12 | github.com/fsnotify/fsnotify v1.9.0 13 | github.com/gaissmai/cidrtree v0.5.0 14 | github.com/go-resty/resty/v2 v2.16.5 15 | github.com/golang-jwt/jwt/v5 v5.2.2 16 | github.com/google/gopacket v1.1.19 17 | github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c 18 | github.com/jinzhu/gorm v1.9.16 19 | github.com/labstack/echo-jwt/v4 v4.3.1 20 | github.com/labstack/echo/v4 v4.13.4 21 | github.com/labstack/gommon v0.4.2 22 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible 23 | github.com/lib/pq v1.10.9 24 | github.com/manifoldco/promptui v0.9.0 25 | github.com/mcuadros/go-defaults v1.2.0 26 | github.com/satori/go.uuid v1.2.1-0.20180404165556-75cca531ea76 27 | github.com/shomali11/util v0.0.0-20220717175126-f0771b70947f 28 | github.com/sirupsen/logrus v1.9.3 29 | github.com/spf13/viper v1.20.1 30 | golang.org/x/crypto v0.40.0 31 | golang.org/x/net v0.42.0 32 | golang.org/x/oauth2 v0.30.0 33 | gopkg.in/go-playground/validator.v9 v9.31.0 34 | gopkg.in/ldap.v3 v3.1.0 35 | ) 36 | 37 | require ( 38 | github.com/chzyer/readline v1.5.1 // indirect 39 | github.com/dlclark/regexp2 v1.11.5 // indirect 40 | github.com/gaissmai/extnetip v1.2.0 // indirect 41 | github.com/go-playground/locales v0.14.1 // indirect 42 | github.com/go-playground/universal-translator v0.18.1 // indirect 43 | github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect 44 | github.com/go-viper/mapstructure/v2 v2.4.0 // indirect 45 | github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect 46 | github.com/jinzhu/inflection v1.0.0 // indirect 47 | github.com/jonboulle/clockwork v0.1.0 // indirect 48 | github.com/leodido/go-urn v1.4.0 // indirect 49 | github.com/lestrrat-go/strftime v1.1.1 // indirect 50 | github.com/mattn/go-colorable v0.1.14 // indirect 51 | github.com/mattn/go-isatty v0.0.20 // indirect 52 | github.com/pelletier/go-toml/v2 v2.2.4 // indirect 53 | github.com/pkg/errors v0.9.1 // indirect 54 | github.com/sagikazarmark/locafero v0.10.0 // indirect 55 | github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect 56 | github.com/spf13/afero v1.14.0 // indirect 57 | github.com/spf13/cast v1.9.2 // indirect 58 | github.com/spf13/pflag v1.0.7 // indirect 59 | github.com/subosito/gotenv v1.6.0 // indirect 60 | github.com/valyala/bytebufferpool v1.0.0 // indirect 61 | github.com/valyala/fasttemplate v1.2.2 // indirect 62 | golang.org/x/sys v0.34.0 // indirect 63 | golang.org/x/text v0.27.0 // indirect 64 | golang.org/x/time v0.12.0 // indirect 65 | gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect 66 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 67 | gopkg.in/go-playground/assert.v1 v1.2.1 // indirect 68 | gopkg.in/yaml.v3 v3.0.1 // indirect 69 | ) 70 | -------------------------------------------------------------------------------- /utils/sipparser/passertedid.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "errors" 11 | "fmt" 12 | ) 13 | 14 | // pAssertedIdStateFn is just a fn type 15 | type pAssertedIdStateFn func(p *PAssertedId) pAssertedIdStateFn 16 | 17 | // PAssertedId is a struct that holds: 18 | // -- Error is just an os.Error 19 | // -- Val is the raw value 20 | // -- Name is the name value from the p-asserted-id hdr 21 | // -- URI is the parsed uri from the p-asserted-id hdr 22 | // -- Params is a slice of the *Params from the p-asserted-id hdr 23 | type PAssertedId struct { 24 | Error error 25 | Val string 26 | Name string 27 | URI *URI 28 | Params []*Param 29 | nameInt int 30 | } 31 | 32 | // addParam adds the *Param to the Params field 33 | func (p *PAssertedId) addParam(s string) { 34 | np := getParam(s) 35 | if p.Params == nil { 36 | p.Params = []*Param{np} 37 | return 38 | } 39 | p.Params = append(p.Params, np) 40 | } 41 | 42 | // parse actually parsed the .Val field of the PAssertedId struct 43 | func (p *PAssertedId) parse() { 44 | for state := parsePAssertedId; state != nil; { 45 | state = state(p) 46 | } 47 | } 48 | 49 | func parsePAssertedId(p *PAssertedId) pAssertedIdStateFn { 50 | if p.Error != nil { 51 | return nil 52 | } 53 | p.Name, p.nameInt = getName(p.Val) 54 | return parsePAssertedIdGetUri 55 | } 56 | 57 | func parsePAssertedIdGetUri(p *PAssertedId) pAssertedIdStateFn { 58 | left := 0 59 | right := 0 60 | for i := range p.Val { 61 | if p.Val[i] == '<' && left == 0 { 62 | left = i 63 | } 64 | if p.Val[i] == '>' && right == 0 { 65 | right = i 66 | } 67 | } 68 | if left < right { 69 | p.URI = ParseURI(p.Val[left+1 : right]) 70 | if p.URI.Error != nil { 71 | p.Error = fmt.Errorf("parsePAssertedIdGetUri err: received err getting uri: %v", p.URI.Error) 72 | return nil 73 | } 74 | return parsePAssertedIdGetParams 75 | } 76 | p.Error = errors.New("parsePAssertedIdGetUri err: could not locate bracks in uri") 77 | return nil 78 | } 79 | 80 | func parsePAssertedIdGetParams(p *PAssertedId) pAssertedIdStateFn { 81 | var pos []int 82 | right := 0 83 | for i := range p.Val { 84 | if p.Val[i] == '>' && right == 0 { 85 | right = i 86 | } 87 | } 88 | if len(p.Val) > right+1 { 89 | pos = make([]int, 0) 90 | for i := range p.Val[right+1:] { 91 | if p.Val[right+1:][i] == ';' { 92 | pos = append(pos, i) 93 | } 94 | } 95 | } 96 | if pos == nil { 97 | return nil 98 | } 99 | for i := range pos { 100 | if len(pos)-1 == i { 101 | if len(p.Val[right+1:])-1 > pos[i]+1 { 102 | p.addParam(p.Val[right+1:][pos[i]+1:]) 103 | } 104 | } 105 | if len(pos)-1 > i { 106 | p.addParam(p.Val[right+1:][pos[i]+1 : pos[i+1]]) 107 | } 108 | } 109 | return nil 110 | } 111 | -------------------------------------------------------------------------------- /utils/sipparser/from_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestFrom(t *testing.T) { 14 | sm := &SipMsg{} 15 | str := "\"Unknown\" ;tag=dd737a8-co7387-INS002" 16 | sm.parseFrom(str) 17 | if sm.Error != nil { 18 | t.Errorf("[TestFrom] Error parsing from hdr: \"Unknown\" ;tag=dd737a8-co7387-INS002. Received err: %v", sm.Error) 19 | } 20 | if sm.From.Name != "Unknown" { 21 | t.Errorf("[TestFrom] Error parsing from hdr: \"Unknown\" ;tag=dd737a8-co7387-INS002. Name field should be \"Unknown\".") 22 | } 23 | if sm.From.URI.User != "5554441000" { 24 | t.Errorf("[TestFrom] Error parsing from hdr: \"Unknown\" ;tag=dd737a8-co7387-INS002. URI.User field should be \"5554441000\".") 25 | } 26 | if sm.From.Tag != "dd737a8-co7387-INS002" { 27 | t.Errorf("[TestFrom] Error parsing from hdr: \"Unknown\" ;tag=dd737a8-co7387-INS002. sm.From.Tag should be \"dd737a8-co7387-INS002\".") 28 | } 29 | str = ";tag=dd737a8-co7387-INS002" 30 | sm.parseFrom(str) 31 | if sm.Error != nil { 32 | t.Errorf("[TestFrom] Error parsing from hdr: \";tag=dd737a8-co7387-INS002\". Received err: %v", sm.Error) 33 | } 34 | if sm.From.Name != "" { 35 | t.Errorf("[TestFrom] Error parsing from hdr: \";tag=dd737a8-co7387-INS002\". Name should be \"\" but received: \"%s\"", sm.From.Name) 36 | } 37 | if sm.From.URI.User != "5554441000" { 38 | t.Errorf("[TestFrom] Error parsing from hdr: \";tag=dd737a8-co7387-INS002\". URI.User should be \"5554441000\" but received: \"%s\"", sm.From.URI.User) 39 | } 40 | str = "sip:+12125551212@phone2net.com;tag=887s" 41 | sm.parseFrom(str) 42 | if sm.Error != nil { 43 | t.Errorf("[TestFrom] Error parsing from hdr: sip:+12125551212@phone2net.com;tag=887s. Received err: %v", sm.Error) 44 | } 45 | if sm.From.Tag != "887s" { 46 | t.Errorf("[TestFrom] Error parsing from hdr: sip:+12125551212@phone2net.com;tag=887s. Tag should be \"887s\" but received: \"%s\".", sm.From.Tag) 47 | } 48 | sm.parseFrom("tel:+4512345678;tag=752520ac91292bae839ce09f3fa382aa") 49 | if sm.From.URI.User != "+4512345678" { 50 | t.Errorf("[TestFrom] Error parsing from hdr: tel:+4512345678;tag=752520ac91292bae839ce09f3fa382aa. User should be \"+4512345678\" but received: \"%s\".", sm.From.URI.User) 51 | } 52 | sm.parseFrom(";tag=sbc09033drebier-CC-3") 53 | if sm.From.URI.User != "180012345678" { 54 | t.Errorf("[TestFrom] Error parsing from hdr: ;tag=sbc09033drebier-CC-3. User should be \"180012345678\" but received: \"%s\".", sm.From.URI.User) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /utils/sipparser/from.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "fmt" 11 | 12 | "github.com/sipcapture/homer-app/utils/sipparser/internal" 13 | ) 14 | 15 | type parseFromStateFn func(f *From) parseFromStateFn 16 | 17 | // From holds a parsed header that has a format like: 18 | // "NAME" ;param=val 19 | // and is used for the parsing of the from, to, and 20 | // contact header in the parser program 21 | // From holds the following public fields: 22 | // -- Error is an error 23 | // -- Val is the raw value 24 | // -- Name is the name value 25 | // -- Tag is the tag value 26 | // -- URI is a parsed uri 27 | // -- Params are for any generic params that are part of 28 | // the header 29 | type From struct { 30 | Error error 31 | Val string 32 | Name string 33 | Tag string 34 | URI *URI 35 | //Params []*Param 36 | endName int 37 | rightBrack int 38 | leftBrack int 39 | brackChk bool 40 | } 41 | 42 | func (f *From) parse() { 43 | for state := parseFromState; state != nil; { 44 | state = state(f) 45 | } 46 | } 47 | 48 | /* 49 | func (f *From) addParam(s string) { 50 | p := getParam(s) 51 | switch { 52 | case p.Param == "tag": 53 | f.Tag = p.Val 54 | default: 55 | if f.Params == nil { 56 | f.Params = make([]*Param, 0) 57 | } 58 | f.Params = append(f.Params, p) 59 | } 60 | } 61 | */ 62 | 63 | func parseFromState(f *From) parseFromStateFn { 64 | if f.Error != nil { 65 | return nil 66 | } 67 | f.Name, f.endName = getName(f.Val) 68 | return parseFromGetURI 69 | } 70 | 71 | func parseFromGetURI(f *From) parseFromStateFn { 72 | f.leftBrack, f.rightBrack, f.brackChk = getBracks(f.Val) 73 | if f.brackChk == false { 74 | f.URI = ParseURI(f.Val) 75 | if f.URI.Error != nil { 76 | f.Error = fmt.Errorf("parseFromGetURI err: rcvd err parsing uri: %v", f.URI.Error) 77 | return nil 78 | } 79 | /* 80 | if f.URI.UriParams != nil { 81 | for i := range f.URI.UriParams { 82 | if f.URI.UriParams[i].Param == "tag" { 83 | f.Tag = f.URI.UriParams[i].Val 84 | } 85 | } 86 | } 87 | */ 88 | return nil 89 | } 90 | if f.brackChk == true { 91 | f.URI = ParseURI(f.Val[f.leftBrack+1 : f.rightBrack]) 92 | if f.URI.Error != nil { 93 | f.Error = fmt.Errorf("parseFromGetURI err: rcvd err parsing uri: %v", f.URI.Error) 94 | return nil 95 | } 96 | return parseFromGetParams 97 | } 98 | return nil 99 | } 100 | 101 | func parseFromGetParams(f *From) parseFromStateFn { 102 | /* 103 | if f.brackChk == true && len(f.Val) > f.rightBrack+1 { 104 | pms := strings.Split(f.Val[f.rightBrack+1:], ";") 105 | for i := range pms { 106 | f.addParam(pms[i]) 107 | } 108 | return nil 109 | } 110 | */ 111 | return nil 112 | } 113 | 114 | func getFrom(s string) *From { 115 | f := &From{Val: s} 116 | f.Tag = internal.ExtractSIPParam("tag=", s) 117 | f.parse() 118 | return f 119 | } 120 | -------------------------------------------------------------------------------- /data/service/agenttoken.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "sort" 8 | 9 | "github.com/Jeffail/gabs/v2" 10 | "github.com/sipcapture/homer-app/model" 11 | "github.com/sipcapture/homer-app/utils/logger" 12 | ) 13 | 14 | type AuthtokenService struct { 15 | ServiceConfig 16 | } 17 | 18 | // this method gets all users from database 19 | func (hs *AuthtokenService) GetAuthtokenAgainstGUID(guid string) (string, error) { 20 | var AuthtokenObject []model.TableAuthToken 21 | var count int 22 | if err := hs.Session.Debug().Table("auth_token"). 23 | Where("guid = ?", guid). 24 | Find(&AuthtokenObject).Count(&count).Error; err != nil { 25 | return "", err 26 | } 27 | if len(AuthtokenObject) == 0 { 28 | return "", fmt.Errorf("token has been not found for guid [%s]", guid) 29 | } 30 | sort.Slice(AuthtokenObject[:], func(i, j int) bool { 31 | return AuthtokenObject[i].GUID < AuthtokenObject[j].GUID 32 | }) 33 | 34 | data, _ := json.Marshal(AuthtokenObject) 35 | response := fmt.Sprintf("{\"count\":%d,\"data\":\"%s\"}", count, string(data)) 36 | return response, nil 37 | } 38 | 39 | // this method gets all users from database 40 | func (hs *AuthtokenService) GetAuthtoken() (string, error) { 41 | var AuthtokenObject []model.TableAuthToken 42 | var count int 43 | if err := hs.Session.Debug().Table("auth_token"). 44 | Find(&AuthtokenObject).Count(&count).Error; err != nil { 45 | return "", err 46 | } 47 | sort.Slice(AuthtokenObject[:], func(i, j int) bool { 48 | return AuthtokenObject[i].GUID < AuthtokenObject[j].GUID 49 | }) 50 | 51 | data, _ := json.Marshal(AuthtokenObject) 52 | response := fmt.Sprintf("{\"count\":%d,\"data\":%s}", count, string(data)) 53 | return response, nil 54 | } 55 | 56 | // this method gets all users from database 57 | func (hs *AuthtokenService) AddAuthtoken(data model.TableAuthToken) (string, error) { 58 | if err := hs.Session.Debug().Table("auth_token"). 59 | Create(&data).Error; err != nil { 60 | return "", err 61 | } 62 | 63 | dataReply := gabs.New() 64 | dataReply.Set(data.Token, "token") 65 | reply := gabs.New() 66 | reply.Set("successfully created auth token", "message") 67 | reply.Set(dataReply.Data(), "data") 68 | 69 | return reply.String(), nil 70 | } 71 | 72 | // this method gets all users from database 73 | func (hs *AuthtokenService) UpdateAuthtokenAgainstGUID(guid string, data model.TableAuthToken) (string, error) { 74 | if err := hs.Session.Debug().Table("auth_token"). 75 | Where("guid = ?", guid). 76 | Update(&data).Error; err != nil { 77 | return "", err 78 | } 79 | response := fmt.Sprintf("{\"message\":\"successfully updated auth token settings\",\"data\":\"%s\"}", guid) 80 | return response, nil 81 | } 82 | 83 | // this method gets all users from database 84 | func (hs *AuthtokenService) DeleteAuthtokenAgainstGUID(guid string) (string, error) { 85 | var AuthtokenObject []model.TableAuthToken 86 | if err := hs.Session.Debug().Table("auth_token"). 87 | Where("guid = ?", guid). 88 | Delete(&AuthtokenObject).Error; err != nil { 89 | logger.Debug(err.Error()) 90 | return "", err 91 | } 92 | response := fmt.Sprintf("{\"message\":\"successfully deleted authtoken\",\"data\":\"%s\"}", guid) 93 | return response, nil 94 | } 95 | -------------------------------------------------------------------------------- /docker/webapp_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "database_data": { 3 | "local": { 4 | "node": "LocalNode", 5 | "user": "homer_db_user", 6 | "pass": "homer_password", 7 | "name": "homer_data", 8 | "host": "homer_db_host", 9 | "port": "homer_db_port" 10 | } 11 | }, 12 | "database_config": { 13 | "user": "homer_db_user", 14 | "pass": "homer_password", 15 | "name": "homer_config", 16 | "host": "homer_db_host", 17 | "port": "homer_db_port", 18 | "keepalive": homer_db_keepalive 19 | }, 20 | "influxdb_config": { 21 | "user": "influx_user", 22 | "pass": "influx_pass", 23 | "name": "homer_config", 24 | "host": "http://influx_host:8086", 25 | "database": "homer", 26 | "policy": "autogen" 27 | }, 28 | "prometheus_config": { 29 | "user": "prom_user", 30 | "pass": "prom_pass", 31 | "host": "http://prom_host:9090", 32 | "api": "api/v1" 33 | }, 34 | "grafana_config": { 35 | "host": "http://grafana_host:3000", 36 | "path": "grafana_path", 37 | "token": "grafana_token" 38 | }, 39 | "loki_config": { 40 | "user": "loki_user", 41 | "pass": "loki_pass", 42 | "host": "http://loki_host:3100", 43 | "api": "api/prom" 44 | }, 45 | "api_settings": { 46 | "enable_token_auth": homer_enable_api 47 | }, 48 | "http_settings": { 49 | "host": "0.0.0.0", 50 | "port": homer_web_port, 51 | "root": "/usr/local/homer/dist", 52 | "path": "homer_serve_path", 53 | "gzip": true, 54 | "debug": false 55 | }, 56 | "system_settings": { 57 | "logpath": "/usr/local/homer", 58 | "logname": "homer-app.log", 59 | "_comment": "loglevel can be: fatal, error, warn, info, debug, trace", 60 | "loglevel": "homer_loglevel", 61 | "logstdout": false 62 | }, 63 | "auth_settings": { 64 | "_comment": "The type param can be internal, ldap", 65 | "type": "internal" 66 | }, 67 | "ldap_config": { 68 | "base": "dc=example,dc=com", 69 | "host": "ldap.example.com", 70 | "port": 389, 71 | "usessl": false, 72 | "skiptls": true, 73 | "binddn": "uid=readonlysuer,ou=People,dc=example,dc=com", 74 | "bindpassword": "readonlypassword", 75 | "userfilter": "(uid=%s)", 76 | "groupfilter": "(memberUid=%s)", 77 | "group_attributes": [ 78 | "cn", 79 | "memberOf", 80 | "GroupAttribute", 81 | "distinguishedName", 82 | "dn", 83 | "member" 84 | ], 85 | "admingroup": "HOMER_admin", 86 | "adminmode": true, 87 | "usergroup": "HOMER_user", 88 | "usermode": true, 89 | "attributes": ["dn", "givenName", "sn", "mail", "uid"], 90 | "skipverify": true, 91 | "anonymous": false, 92 | "userdn": "uid=%s,ou=People,dc=example,dc=com" 93 | }, 94 | "decoder_shark": { 95 | "_comment": "Here you can do packet decoding using tshark application. Please define uid, gid if you run the app under root", 96 | "active": tshark_active, 97 | "uid": tshark_uid, 98 | "gid": tshark_gid, 99 | "bin": "tshark_bin", 100 | "protocols": [ 101 | "1_call", 102 | "1_registration", 103 | "1_default" 104 | ] 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /utils/sipparser/via.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "errors" 11 | "strings" 12 | ) 13 | 14 | type viaStateFn func(v *Via) viaStateFn 15 | 16 | type vias struct { 17 | via string 18 | vias []*Via 19 | err error 20 | } 21 | 22 | func (vs *vias) parse() { 23 | parts := strings.Split(vs.via, ",") 24 | for _, p := range parts { 25 | v := &Via{Via: p} 26 | v.parse() 27 | if v.Error != nil { 28 | vs.err = v.Error 29 | return 30 | } 31 | vs.vias = append(vs.vias, v) 32 | } 33 | } 34 | 35 | type Via struct { 36 | State string 37 | Error error 38 | Via string 39 | Proto string 40 | Version string 41 | Transport string 42 | SentBy string 43 | Branch string 44 | Received string 45 | RPort string 46 | Params []*Param 47 | protoEnd int 48 | paramStart int 49 | } 50 | 51 | func (v *Via) parse() { 52 | for state := parseViaState; state != nil; { 53 | state = state(v) 54 | } 55 | } 56 | 57 | func (v *Via) addParam(s string) { 58 | if v.Params == nil { 59 | v.Params = make([]*Param, 0) 60 | } 61 | p := getParam(s) 62 | switch { 63 | case p.Param == "branch": 64 | v.Branch = p.Val 65 | case p.Param == "rport": 66 | v.RPort = p.Val 67 | case p.Param == "received": 68 | v.Received = p.Val 69 | default: 70 | v.Params = append(v.Params, p) 71 | } 72 | } 73 | 74 | func (v *Via) AddReceived(s string) { 75 | v.Received = s 76 | } 77 | 78 | func parseViaState(v *Via) viaStateFn { 79 | if v.Error != nil { 80 | return nil 81 | } 82 | return parseViaGetProto 83 | } 84 | 85 | func parseViaGetProto(v *Via) viaStateFn { 86 | v.protoEnd = strings.Index(v.Via, " ") 87 | if v.protoEnd == -1 { 88 | v.Error = errors.New("parseViaGetProto err: could not get LWS char") 89 | return nil 90 | } 91 | protoParts := strings.SplitN(v.Via[0:v.protoEnd], "/", 3) 92 | if len(protoParts) != 3 { 93 | v.Error = errors.New("parseViaGetProto err: split err on proto char") 94 | return nil 95 | } 96 | v.Proto = protoParts[0] 97 | v.Version = protoParts[1] 98 | v.Transport = protoParts[2] 99 | return parseViaGetParams 100 | } 101 | 102 | func parseViaGetParams(v *Via) viaStateFn { 103 | pChar := strings.IndexRune(v.Via, ';') 104 | if pChar == -1 { 105 | return parseViaGetHostPort 106 | } 107 | if v.paramStart == 0 { 108 | v.paramStart = pChar 109 | } 110 | if len(v.Via)-1 > pChar+1 { 111 | parts := strings.Split(v.Via[pChar+1:], ";") 112 | if len(parts) == 1 { 113 | v.addParam(parts[0]) 114 | return parseViaGetHostPort 115 | } 116 | for i := 0; i < len(parts); i++ { 117 | v.addParam(parts[i]) 118 | } 119 | } 120 | return parseViaGetHostPort 121 | } 122 | 123 | func parseViaGetHostPort(v *Via) viaStateFn { 124 | if v.protoEnd == 0 { 125 | v.Error = errors.New("parseViaGetHostPort err: protoEnd is 0") 126 | return nil 127 | } 128 | if v.protoEnd < v.paramStart { 129 | v.SentBy = v.Via[v.protoEnd+1 : v.paramStart] 130 | } 131 | return nil 132 | } 133 | -------------------------------------------------------------------------------- /model/statistic.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // swagger:model StatisticObject 8 | type StatisticObject struct { 9 | Param struct { 10 | Limit int `json:"limit"` 11 | Precision int `json:"precision"` 12 | Query []struct { 13 | Main string `json:"main"` 14 | Database string `json:"database"` 15 | Retention string `json:"retention"` 16 | Rawquery string `json:"rawquery"` 17 | Type []string `json:"type"` 18 | Tag []string `json:"tag"` 19 | } `json:"query"` 20 | Bfrom int `json:"bfrom"` 21 | Total bool `json:"total"` 22 | } `json:"param"` 23 | Timestamp struct { 24 | From int64 `json:"from"` 25 | To int64 `json:"to"` 26 | } `json:"timestamp"` 27 | } 28 | 29 | // swagger:model StatisticSearchObject 30 | type StatisticSearchObject struct { 31 | Param struct { 32 | Limit int `json:"limit"` 33 | Precision int `json:"precision"` 34 | Search struct { 35 | Database string `json:"database"` 36 | } `json:"search"` 37 | Bfrom int `json:"bfrom"` 38 | Total bool `json:"total"` 39 | } `json:"param"` 40 | Timestamp struct { 41 | From int64 `json:"from"` 42 | To int64 `json:"to"` 43 | } `json:"timestamp"` 44 | } 45 | 46 | type InfluxDatabasesPolices struct { 47 | Name string `json:"name"` 48 | } 49 | 50 | type InfluxDatabasesMeasurements struct { 51 | Name string `json:"name"` 52 | } 53 | 54 | type StatisticResponse struct { 55 | Total int `json:"total"` 56 | D json.RawMessage `json:"data"` 57 | } 58 | 59 | type StatisticPoint struct { 60 | Attemps int `json:"attemps"` 61 | Partid int `json:"partid"` 62 | Group int `json:"group"` 63 | Id int `json:"id"` 64 | Reporttime int64 `json:"reporttime"` 65 | Table string `json:"table"` 66 | Tag1 string `json:"tag1"` 67 | Transaction string `json:"transaction"` 68 | Countername string `json:"countername"` 69 | Value float64 `json:"value"` 70 | } 71 | 72 | // swagger:model StatisticDb 73 | type StatisticDb struct { 74 | Data struct { 75 | Results []struct { 76 | Series []struct { 77 | // example: databases 78 | Name string `json:"name"` 79 | // example: ["name"] 80 | Columns []string `json:"columns"` 81 | // example: [["telegraf"], ["_internal"], ["homer"]] 82 | Values [][]string `json:"values"` 83 | } `json:"Series"` 84 | Messages interface{} `json:"Messages"` 85 | } `json:"Results"` 86 | } `json:"data"` 87 | // example: ok 88 | Status string `json:"status"` 89 | // example: 1 90 | Total int `json:"total"` 91 | } 92 | 93 | // swagger:model StatisticRetentions 94 | type StatisticRetentions struct { 95 | Data struct { 96 | Results []struct { 97 | Series []struct { 98 | // example: ["name", "duration", "shardGroupDuration", "replicaN", "default"] 99 | Columns []string `json:"columns"` 100 | // example: [["autogen", "0s", "168h0m0s", 1, true]] 101 | Values [][]interface{} `json:"values"` 102 | } `json:"Series"` 103 | Messages interface{} `json:"Messages"` 104 | } `json:"Results"` 105 | } `json:"data"` 106 | // example: ok 107 | Status string `json:"status"` 108 | // example: 1 109 | Total int `json:"total"` 110 | } 111 | -------------------------------------------------------------------------------- /utils/sipparser/internal/machine.go: -------------------------------------------------------------------------------- 1 | 2 | //line machine.rl:1 3 | package internal 4 | 5 | import ( 6 | "strings" 7 | ) 8 | 9 | // ragel -G2 -Z machine.rl 10 | 11 | 12 | //line machine.go:13 13 | const metric_start int = 2 14 | const metric_first_final int = 2 15 | const metric_error int = 0 16 | 17 | const metric_en_main int = 2 18 | 19 | 20 | //line machine.rl:12 21 | 22 | 23 | func ExtractSIPParam(param, data string) (s string) { 24 | if numPos := strings.Index(data, param); numPos >= 0 { 25 | numPos += len(param) 26 | data = data[numPos:] 27 | } else { 28 | return s 29 | } 30 | 31 | cs, p, pe, eof := 0, 0, len(data), len(data) 32 | mark := 0 33 | 34 | 35 | //line machine.go:36 36 | { 37 | cs = metric_start 38 | } 39 | 40 | //line machine.go:41 41 | { 42 | if p == pe { 43 | goto _test_eof 44 | } 45 | switch cs { 46 | case 2: 47 | goto st_case_2 48 | case 3: 49 | goto st_case_3 50 | case 4: 51 | goto st_case_4 52 | case 0: 53 | goto st_case_0 54 | case 1: 55 | goto st_case_1 56 | } 57 | goto st_out 58 | st_case_2: 59 | switch data[p] { 60 | case 13: 61 | goto tr3 62 | case 32: 63 | goto tr3 64 | case 34: 65 | goto tr4 66 | case 59: 67 | goto tr3 68 | } 69 | if 9 <= data[p] && data[p] <= 10 { 70 | goto tr3 71 | } 72 | goto tr2 73 | tr2: 74 | //line machine.rl:26 75 | 76 | mark = p 77 | 78 | goto st3 79 | st3: 80 | if p++; p == pe { 81 | goto _test_eof3 82 | } 83 | st_case_3: 84 | //line machine.go:85 85 | switch data[p] { 86 | case 13: 87 | goto tr6 88 | case 32: 89 | goto tr6 90 | case 34: 91 | goto tr7 92 | case 59: 93 | goto tr6 94 | } 95 | if 9 <= data[p] && data[p] <= 10 { 96 | goto tr6 97 | } 98 | goto st3 99 | tr0: 100 | //line machine.rl:37 101 | return s 102 | goto st4 103 | tr3: 104 | //line machine.rl:26 105 | 106 | mark = p 107 | 108 | //line machine.rl:30 109 | 110 | s = data[mark:p] 111 | 112 | //line machine.rl:37 113 | return s 114 | goto st4 115 | tr6: 116 | //line machine.rl:30 117 | 118 | s = data[mark:p] 119 | 120 | //line machine.rl:37 121 | return s 122 | goto st4 123 | st4: 124 | if p++; p == pe { 125 | goto _test_eof4 126 | } 127 | st_case_4: 128 | //line machine.go:129 129 | goto st0 130 | st_case_0: 131 | st0: 132 | cs = 0 133 | goto _out 134 | tr4: 135 | //line machine.rl:26 136 | 137 | mark = p 138 | 139 | //line machine.rl:30 140 | 141 | s = data[mark:p] 142 | 143 | goto st1 144 | tr7: 145 | //line machine.rl:30 146 | 147 | s = data[mark:p] 148 | 149 | goto st1 150 | st1: 151 | if p++; p == pe { 152 | goto _test_eof1 153 | } 154 | st_case_1: 155 | //line machine.go:156 156 | if data[p] == 44 { 157 | goto tr0 158 | } 159 | goto st0 160 | st_out: 161 | _test_eof3: cs = 3; goto _test_eof 162 | _test_eof4: cs = 4; goto _test_eof 163 | _test_eof1: cs = 1; goto _test_eof 164 | 165 | _test_eof: {} 166 | if p == eof { 167 | switch cs { 168 | case 3: 169 | //line machine.rl:30 170 | 171 | s = data[mark:p] 172 | 173 | case 2: 174 | //line machine.rl:26 175 | 176 | mark = p 177 | 178 | //line machine.rl:30 179 | 180 | s = data[mark:p] 181 | 182 | //line machine.go:183 183 | } 184 | } 185 | 186 | _out: {} 187 | } 188 | 189 | //line machine.rl:41 190 | 191 | 192 | return s 193 | } -------------------------------------------------------------------------------- /auth/middleware.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/golang-jwt/jwt/v5" 7 | "github.com/labstack/echo/v4" 8 | "github.com/sipcapture/homer-app/model" 9 | "github.com/sipcapture/homer-app/utils/logger" 10 | ) 11 | 12 | func MiddlewareRes(next echo.HandlerFunc) echo.HandlerFunc { 13 | return func(c echo.Context) error { 14 | 15 | if c.Get("user") != nil { 16 | user := c.Get("user").(*jwt.Token) 17 | claims := user.Claims.(*JwtUserClaim) 18 | logger.Debug("Claims") 19 | logger.Debug(claims) 20 | 21 | appContext := model.AppContext{ 22 | Context: c, 23 | UserName: claims.UserName, 24 | Admin: claims.UserAdmin, 25 | UserGroup: claims.UserGroup, 26 | ExternalAuth: claims.ExternalAuth, 27 | } 28 | if err := next(appContext); err != nil { 29 | c.Error(err) 30 | } 31 | 32 | return nil 33 | } 34 | 35 | if c.Get("authtoken") != nil { 36 | 37 | tokenKey := c.Get("authtoken").(model.KeyContext) 38 | 39 | logger.Debug("Authkey: ", tokenKey.AuthKey) 40 | logger.Debug(tokenKey) 41 | 42 | if err := next(tokenKey); err != nil { 43 | c.Error(err) 44 | } 45 | return nil 46 | } 47 | 48 | return nil 49 | } 50 | } 51 | 52 | func IsAdmin(next echo.HandlerFunc) echo.HandlerFunc { 53 | return func(c echo.Context) error { 54 | if c.Get("user") != nil { 55 | user := c.Get("user").(*jwt.Token) 56 | claims := user.Claims.(*JwtUserClaim) 57 | isAdmin := claims.UserAdmin 58 | if !isAdmin { 59 | return echo.NewHTTPError(403, "This API requires admin access.") 60 | } 61 | return next(c) 62 | } else if c.Get("authtoken") != nil { 63 | 64 | tokenKey := c.Get("authtoken").(model.KeyContext) 65 | isAdmin := tokenKey.UserAdmin 66 | 67 | if !isAdmin { 68 | return echo.NewHTTPError(403, "This API requires admin access. The AuthToken in use!") 69 | } 70 | return next(c) 71 | } else { 72 | //return httpresponse.CreateSuccessResponseWithJson(&c, http.StatusOK, []byte(reply)) 73 | return echo.NewHTTPError(403, "This API requires admin access!") 74 | } 75 | } 76 | } 77 | 78 | /* check if it's admin */ 79 | func IsRequestAdmin(c echo.Context) (string, bool) { 80 | if c.Get("user") != nil { 81 | user := c.Get("user").(*jwt.Token) 82 | claims := user.Claims.(*JwtUserClaim) 83 | isAdmin := claims.UserAdmin 84 | return claims.UserName, isAdmin 85 | } else if c.Get("authtoken") != nil { 86 | tokenKey := c.Get("authtoken").(model.KeyContext) 87 | isAdmin := tokenKey.UserAdmin 88 | return tokenKey.UserName, isAdmin 89 | } else { 90 | return "default", false 91 | } 92 | } 93 | 94 | /* get user group */ 95 | func GetUserGroup(c echo.Context) string { 96 | 97 | if c.Get("user") != nil { 98 | user := c.Get("user").(*jwt.Token) 99 | claims := user.Claims.(*JwtUserClaim) 100 | return claims.UserGroup 101 | } else if c.Get("authtoken") != nil { 102 | tokenKey := c.Get("authtoken").(model.KeyContext) 103 | return tokenKey.UserGroup 104 | } else { 105 | return "guest" 106 | } 107 | } 108 | 109 | /* get user group */ 110 | func GetUserProfile(c echo.Context) (*JwtUserClaim, error) { 111 | 112 | if c.Get("user") != nil { 113 | user := c.Get("user").(*jwt.Token) 114 | if user != nil { 115 | claims := user.Claims.(*JwtUserClaim) 116 | return claims, nil 117 | } 118 | } 119 | 120 | return nil, fmt.Errorf("no user in token") 121 | } 122 | -------------------------------------------------------------------------------- /model/mappingSchema.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | func (TableMappingSchema) TableName() string { 9 | return "mapping_schema" 10 | } 11 | 12 | // swagger:model MappingSchema 13 | type TableMappingSchema struct { 14 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT" json:"-"` 15 | // example:a006a250-c261-4052-b1c2-6cb85ed580c2 16 | GUID string `gorm:"column:guid;type:uuid" json:"guid"` 17 | // example: call 18 | Profile string `gorm:"column:profile;type:varchar(100);not null" json:"profile" validate:"required"` 19 | // example: 1 20 | Hepid int `gorm:"column:hepid;type:int;not null" json:"hepid" validate:"required"` 21 | // example: SIP 22 | HepAlias string `gorm:"column:hep_alias;type:varchar(100)" json:"hep_alias"` 23 | // example: 10 24 | PartID int `gorm:"column:partid;type:int;int:10;not null" json:"partid" validate:"required"` 25 | // example: 1 26 | Version int `gorm:"column:version;type:int;not null" json:"version" validate:"required"` 27 | // example: 10 28 | Retention int `gorm:"column:retention;type:int;not null" json:"retention" validate:"required"` 29 | // example: 10 30 | PartitionStep int `gorm:"column:partition_step;int;not null" json:"partition_step" validate:"required"` 31 | CreateIndex json.RawMessage `gorm:"column:create_index;default:'{}';type:json" json:"create_index"` 32 | // example: CREATE TABLE test(id integer, data text); 33 | CreateTable string `gorm:"column:create_table;default:'CREATE TABLE';type:text" json:"create_table"` 34 | CorrelationMapping json.RawMessage `gorm:"column:correlation_mapping;default:'{}';type:json" json:"correlation_mapping"` 35 | FieldsMapping json.RawMessage `gorm:"column:fields_mapping;default:'{}';type:json" json:"fields_mapping"` 36 | MappingSettings json.RawMessage `gorm:"column:mapping_settings;default:'{}';type:json" json:"fields_settings"` 37 | SchemaMapping json.RawMessage `gorm:"column:schema_mapping;default:'{}';type:json" json:"schema_mapping"` 38 | SchemaSettings json.RawMessage `gorm:"column:schema_settings;default:'{}';type:json" json:"schema_settings"` 39 | CreateDate time.Time `gorm:"column:create_date;default:current_timestamp;not null" json:"-"` 40 | } 41 | 42 | // swagger:model MappingSchemaList 43 | type MappingSchemaList struct { 44 | // example: 1 45 | Count int `json:"count"` 46 | Data []TableMappingSchema `json:"data"` 47 | } 48 | 49 | // swagger:model MappingUpdateSuccessResponse 50 | type MappingUpdateSuccessResponse struct { 51 | // example: 4b855914-ca3d-4562-8563-f2b660fe2636 52 | Data string `json:"data"` 53 | // example: successfully updated mapping settings 54 | Message string `json:"message"` 55 | } 56 | 57 | // swagger:model MappingCreateSuccessResponse 58 | type MappingCreateSuccessResponse struct { 59 | // example: 4b855914-ca3d-4562-8563-f2b660fe2636 60 | Data string `json:"data"` 61 | // example: successfully created mapping settings 62 | Message string `json:"message"` 63 | } 64 | 65 | // swagger:model MappingDeleteSuccessResponse 66 | type MappingDeleteSuccessResponse struct { 67 | // example: 4b855914-ca3d-4562-8563-f2b660fe2636 68 | Data string `json:"data"` 69 | // example: successfully deleted mapping settings 70 | Message string `json:"message"` 71 | } 72 | 73 | // swagger:model SmartSearchField 74 | type SmartSearchField struct { 75 | // example: sip 76 | Category string `json:"category"` 77 | // example: callid 78 | Name string `json:"name"` 79 | // example: sip.callid 80 | Value string `json:"value"` 81 | } 82 | 83 | // swagger:model SmartSearchFieldList 84 | type SmartSearchFieldList struct { 85 | Data struct { 86 | Data []SmartSearchField `json:"data"` 87 | } `json:"data"` 88 | } 89 | 90 | type MappingSmart struct { 91 | Value string 92 | Type string 93 | } 94 | -------------------------------------------------------------------------------- /controller/v1/profile.go: -------------------------------------------------------------------------------- 1 | package controllerv1 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/Jeffail/gabs/v2" 7 | "github.com/labstack/echo/v4" 8 | "github.com/sipcapture/homer-app/config" 9 | "github.com/sipcapture/homer-app/data/service" 10 | httpresponse "github.com/sipcapture/homer-app/network/response" 11 | "github.com/sipcapture/homer-app/system/webmessages" 12 | ) 13 | 14 | type ProfileController struct { 15 | Controller 16 | ProfileService *service.ProfileService 17 | } 18 | 19 | func (pc *ProfileController) GetHepsub(c echo.Context) error { 20 | 21 | reply, err := pc.ProfileService.GetProfile() 22 | if err != nil { 23 | return httpresponse.CreateBadResponse(&c, http.StatusBadRequest, webmessages.MappingHepSubFailed) 24 | } 25 | return httpresponse.CreateSuccessResponseWithJson(&c, http.StatusOK, []byte(reply)) 26 | 27 | } 28 | 29 | // swagger:route GET /admin/profiles Admin ListProfiles 30 | // 31 | // Returns data from server 32 | // --- 33 | // consumes: 34 | // - application/json 35 | // produces: 36 | // - application/json 37 | // securityDefinitions: 38 | // 39 | // bearer: 40 | // type: apiKey 41 | // in: header 42 | // name: Authorization 43 | // 44 | // security: 45 | // - bearer: [] 46 | // 47 | // responses: 48 | // 49 | // 201: body:HepsubSchema 50 | // 400: body:FailureResponse 51 | func (pc *ProfileController) GetDashboardList(c echo.Context) error { 52 | 53 | reply, err := pc.ProfileService.GetProfile() 54 | if err != nil { 55 | return httpresponse.CreateBadResponse(&c, http.StatusBadRequest, webmessages.GetDashboardFailed) 56 | } 57 | return httpresponse.CreateSuccessResponseWithJson(&c, http.StatusOK, []byte(reply)) 58 | 59 | } 60 | 61 | // swagger:route GET /database/node/list profile profileGetDBNodeList 62 | // 63 | // Get list of DB nodes 64 | // --- 65 | // consumes: 66 | // - application/json 67 | // produces: 68 | // - application/json 69 | // Security: 70 | // - bearer: [] 71 | // 72 | // SecurityDefinitions: 73 | // bearer: 74 | // 75 | // type: apiKey 76 | // name: Authorization 77 | // in: header 78 | // 79 | // responses: 80 | // 81 | // 201: body:NodeList 82 | // 400: body:FailureResponse 83 | func (pc *ProfileController) GetDBNodeList(c echo.Context) error { 84 | 85 | reply, err := pc.ProfileService.GetDBNodeList() 86 | if err != nil { 87 | return httpresponse.CreateBadResponse(&c, http.StatusBadRequest, webmessages.GetDBNodeListFailed) 88 | } 89 | 90 | return httpresponse.CreateSuccessResponseWithJson(&c, http.StatusOK, []byte(reply)) 91 | 92 | } 93 | 94 | // swagger:route GET /modules/status Status ListMapping 95 | // 96 | // Returns data from server 97 | // --- 98 | // consumes: 99 | // - application/json 100 | // produces: 101 | // - application/json 102 | // Security: 103 | // - JWT 104 | // - ApiKeyAuth 105 | // 106 | // SecurityDefinitions: 107 | // JWT: 108 | // 109 | // type: apiKey 110 | // name: Authorization 111 | // in: header 112 | // 113 | // ApiKeyAuth: 114 | // 115 | // type: apiKey 116 | // in: header 117 | // name: Auth-Token 118 | // 119 | // Responses: 120 | // 121 | // 201: body:SuccessResponse 122 | // 400: body:FailureResponse 123 | func (pc *ProfileController) GetModulesStatus(c echo.Context) error { 124 | 125 | moduleLoki := gabs.New() 126 | moduleLoki.Set(config.Setting.LOKI_CONFIG.Enable, "enable") 127 | moduleLoki.Set(config.Setting.LOKI_CONFIG.Template, "template") 128 | moduleLoki.Set(config.Setting.LOKI_CONFIG.ExternalUrl, "external_url") 129 | 130 | modulesResponse := gabs.New() 131 | modulesResponse.Set(moduleLoki.Data(), "loki") 132 | 133 | reply := gabs.New() 134 | reply.Set("Modules status", "message") 135 | reply.Set(modulesResponse.Data(), "data") 136 | 137 | return httpresponse.CreateSuccessResponseWithJson(&c, http.StatusOK, []byte(reply.String())) 138 | 139 | } 140 | -------------------------------------------------------------------------------- /utils/sipparser/startline.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011, Shelby Ramsey. All rights reserved. 2 | // Copyright 2018, Eugen Biegler. All rights reserved. 3 | // Use of this code is governed by a BSD license that can be 4 | // found in the LICENSE.txt file. 5 | 6 | package sipparser 7 | 8 | // Imports from the go standard library 9 | import ( 10 | "errors" 11 | "fmt" 12 | "strings" 13 | ) 14 | 15 | /* 16 | var ( 17 | SIP_METHODS = []string{ 18 | SIP_METHOD_INVITE, 19 | SIP_METHOD_ACK, 20 | SIP_METHOD_OPTIONS, 21 | SIP_METHOD_BYE, 22 | SIP_METHOD_CANCEL, 23 | SIP_METHOD_REGISTER, 24 | SIP_METHOD_INFO, 25 | SIP_METHOD_PRACK, 26 | SIP_METHOD_SUBSCRIBE, 27 | SIP_METHOD_NOTIFY, 28 | SIP_METHOD_UPDATE, 29 | SIP_METHOD_MESSAGE, 30 | SIP_METHOD_REFER, 31 | SIP_METHOD_PUBLISH, 32 | } 33 | ) 34 | */ 35 | 36 | type parseStartLineStateFn func(s *StartLine) parseStartLineStateFn 37 | 38 | type StartLine struct { 39 | Error error //internal error condition 40 | Val string //StartLine as a String 41 | Type string //one of SIP_REQUEST or SIP_RESPONSE 42 | Method string //INV, ACK, BYE etc 43 | URI *URI //Request URI; sip:alice@chicago.com 44 | Resp string //Response code: e.g. 200, 400 45 | RespText string //Response String e.g. "Trying" 46 | Proto string //e.g. "SIP" in the string "SIP/2.0" 47 | Version string //e.g. "2.0" in the string "SIP/2.0" 48 | } 49 | 50 | func (s *StartLine) run() { 51 | for state := parseStartLine; state != nil; { 52 | state = state(s) 53 | } 54 | } 55 | 56 | func parseStartLine(s *StartLine) parseStartLineStateFn { 57 | if s.Error != nil { 58 | return nil 59 | } 60 | if len(s.Val) < 3 { 61 | s.Error = errors.New("parseStartLine err: length of s.Val is less than 3. Invalid start line") 62 | return nil 63 | } 64 | if s.Val[0:3] == "SIP" { 65 | s.Type = SIP_RESPONSE 66 | return parseStartLineResponse 67 | } 68 | s.Type = SIP_REQUEST 69 | return parseStartLineRequest 70 | } 71 | 72 | func parseStartLineResponse(s *StartLine) parseStartLineStateFn { 73 | parts := strings.SplitN(s.Val, " ", 3) 74 | if len(parts) != 3 { 75 | s.Error = errors.New("parseStartLineRespone err: err getting parts from LWS") 76 | return nil 77 | } 78 | charPos := strings.IndexRune(parts[0], '/') 79 | if charPos == -1 { 80 | s.Error = errors.New("parseStartLineRespone err: err getting proto char") 81 | return nil 82 | } 83 | s.Proto = parts[0][0:charPos] 84 | if len(parts[0])-1 < charPos+1 { 85 | s.Error = errors.New("parseStartLineResponse err: proto char appears to be at end of proto") 86 | return nil 87 | } 88 | s.Version = parts[0][charPos+1:] 89 | s.Resp = parts[1] 90 | s.RespText = parts[2] 91 | return nil 92 | } 93 | 94 | func parseStartLineRequest(s *StartLine) parseStartLineStateFn { 95 | parts := strings.SplitN(s.Val, " ", 3) 96 | if len(parts) != 3 { 97 | s.Error = errors.New("parseStartLineRequest err: request line did not split on LWS correctly") 98 | return nil 99 | } else if len(parts[1]) == 0 { 100 | s.Error = errors.New("parseStartLineRequest err: empty request uri part") 101 | return nil 102 | } 103 | s.Method = parts[0] 104 | s.URI = ParseURI(parts[1]) 105 | if s.URI.Error != nil { 106 | s.Error = fmt.Errorf("parseStartLineRequest err: err in URI: %v", s.URI.Error) 107 | return nil 108 | } 109 | charPos := strings.IndexRune(parts[2], '/') 110 | if charPos == -1 { 111 | s.Error = errors.New("parseStartLineRequest err: could not get \"/\" pos in parts[2]") 112 | return nil 113 | } 114 | if len(parts[2])-1 < charPos+1 { 115 | s.Error = errors.New("parseStartLineRequest err: \"/\" char appears to be at end of line") 116 | return nil 117 | } 118 | s.Proto = parts[2][0:charPos] 119 | s.Version = parts[2][charPos+1:] 120 | return nil 121 | } 122 | 123 | func ParseStartLine(str string) *StartLine { 124 | s := &StartLine{Val: str} 125 | s.run() 126 | return s 127 | } 128 | -------------------------------------------------------------------------------- /data/service/hepsub.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "sort" 8 | 9 | "github.com/sipcapture/homer-app/model" 10 | "github.com/sipcapture/homer-app/utils/logger" 11 | ) 12 | 13 | type HepsubService struct { 14 | ServiceConfig 15 | } 16 | 17 | // this method gets all users from database 18 | func (hs *HepsubService) GetHepSubAgainstGUID(guid string) (string, error) { 19 | var hepsubObject []model.TableHepsubSchema 20 | var count int 21 | if err := hs.Session.Debug().Table("hepsub_mapping_schema"). 22 | Where("guid = ?", guid). 23 | Find(&hepsubObject).Count(&count).Error; err != nil { 24 | return "", err 25 | } 26 | if len(hepsubObject) == 0 { 27 | return "", fmt.Errorf("no hepsub object found for guid [%s]", guid) 28 | } 29 | sort.Slice(hepsubObject[:], func(i, j int) bool { 30 | return hepsubObject[i].GUID < hepsubObject[j].GUID 31 | }) 32 | 33 | data, _ := json.Marshal(hepsubObject) 34 | responseMap := map[string]interface{}{ 35 | "count": count, 36 | "data": json.RawMessage(data), 37 | } 38 | response, _ := json.Marshal(responseMap) 39 | return string(response), nil 40 | } 41 | 42 | // this method gets all users from database 43 | func (hs *HepsubService) GetHepSub() (string, error) { 44 | var hepsubObject []model.TableHepsubSchema 45 | var count int 46 | if err := hs.Session.Debug().Table("hepsub_mapping_schema"). 47 | Find(&hepsubObject).Count(&count).Error; err != nil { 48 | return "", err 49 | } 50 | sort.Slice(hepsubObject[:], func(i, j int) bool { 51 | return hepsubObject[i].GUID < hepsubObject[j].GUID 52 | }) 53 | 54 | data, _ := json.Marshal(hepsubObject) 55 | responseMap := map[string]interface{}{ 56 | "count": count, 57 | "data": json.RawMessage(data), 58 | } 59 | response, _ := json.Marshal(responseMap) 60 | return string(response), nil 61 | } 62 | 63 | // this method gets all users from database 64 | func (hs *HepsubService) GetHepSubFields(id, transaction string) (string, error) { 65 | var hepsubObject []model.TableHepsubSchema 66 | var count int 67 | if err := hs.Session.Debug().Table("hepsub_mapping_schema"). 68 | Where("hepid = ? and profile = ?", id, transaction). 69 | Find(&hepsubObject).Count(&count).Error; err != nil { 70 | return "", err 71 | } 72 | data, _ := json.Marshal(hepsubObject) 73 | responseMap := map[string]interface{}{ 74 | "count": count, 75 | "data": json.RawMessage(data), 76 | } 77 | response, _ := json.Marshal(responseMap) 78 | return string(response), nil 79 | } 80 | 81 | // this method gets all users from database 82 | func (hs *HepsubService) AddHepSub(data model.TableHepsubSchema) (string, error) { 83 | if err := hs.Session.Debug().Table("hepsub_mapping_schema"). 84 | Create(&data).Error; err != nil { 85 | return "", err 86 | } 87 | response := fmt.Sprintf("{\"message\":\"successfully created hepsub settings\",\"data\":\"%s\"}", data.GUID) 88 | return response, nil 89 | } 90 | 91 | // this method gets all users from database 92 | func (hs *HepsubService) UpdateHepSubAgainstGUID(guid string, data model.TableHepsubSchema) (string, error) { 93 | if err := hs.Session.Debug().Table("hepsub_mapping_schema"). 94 | Where("guid = ?", guid). 95 | Update(&data).Error; err != nil { 96 | return "", err 97 | } 98 | response := fmt.Sprintf("{\"message\":\"successfully updated hepsub settings\",\"data\":\"%s\"}", guid) 99 | return response, nil 100 | } 101 | 102 | // this method gets all users from database 103 | func (hs *HepsubService) DeleteHepSubAgainstGUID(guid string) (string, error) { 104 | var hepsubObject []model.TableHepsubSchema 105 | if err := hs.Session.Debug().Table("hepsub_mapping_schema"). 106 | Where("guid = ?", guid). 107 | Delete(&hepsubObject).Error; err != nil { 108 | logger.Debug(err.Error()) 109 | return "", err 110 | } 111 | response := fmt.Sprintf("{\"message\":\"successfully deleted hepsub settings\",\"data\":\"%s\"}", guid) 112 | return response, nil 113 | } 114 | -------------------------------------------------------------------------------- /controller/v1/remote.go: -------------------------------------------------------------------------------- 1 | package controllerv1 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/labstack/echo/v4" 7 | "github.com/sipcapture/homer-app/data/service" 8 | "github.com/sipcapture/homer-app/model" 9 | httpresponse "github.com/sipcapture/homer-app/network/response" 10 | "github.com/sipcapture/homer-app/system/webmessages" 11 | "github.com/sipcapture/homer-app/utils/logger" 12 | ) 13 | 14 | type RemoteController struct { 15 | Controller 16 | RemoteService *service.RemoteService 17 | } 18 | 19 | // swagger:route GET /search/remote/label remote remoteRemoteLabel 20 | // 21 | // Returns data based upon filtered json 22 | // --- 23 | // produces: 24 | // - application/json 25 | // Security: 26 | // - bearer: [] 27 | // 28 | // SecurityDefinitions: 29 | // bearer: 30 | // type: apiKey 31 | // name: Authorization 32 | // in: header 33 | // parameters: 34 | // + name: server 35 | // in: query 36 | // description: url 37 | // required: true 38 | // type: string 39 | // responses: 40 | // 200: body:RemoteLabels 41 | // 400: body:FailureResponse 42 | func (pc *RemoteController) RemoteLabel(c echo.Context) error { 43 | 44 | if !pc.RemoteService.Active { 45 | logger.Error("Loki service is not enabled") 46 | return httpresponse.CreateBadResponse(&c, http.StatusBadRequest, "Loki service is not enabled") 47 | } 48 | 49 | serverName := c.QueryParam("server") 50 | 51 | responseData, err := pc.RemoteService.RemoteLabels(serverName) 52 | if err != nil { 53 | logger.Debug(responseData) 54 | } 55 | return httpresponse.CreateSuccessResponse(&c, http.StatusCreated, responseData) 56 | } 57 | 58 | // swagger:route GET /search/remote/values remote remoteRemoteValues 59 | // 60 | // Returns data based upon filtered json 61 | // --- 62 | // produces: 63 | // - application/json 64 | // Security: 65 | // - bearer: [] 66 | // 67 | // SecurityDefinitions: 68 | // bearer: 69 | // type: apiKey 70 | // name: Authorization 71 | // in: header 72 | // parameters: 73 | // + name: server 74 | // in: query 75 | // description: url 76 | // required: true 77 | // type: string 78 | // + name: label 79 | // in: query 80 | // description: label 81 | // required: true 82 | // type: string 83 | // responses: 84 | // 200: body:RemoteValues 85 | // 400: body:FailureResponse 86 | func (pc *RemoteController) RemoteValues(c echo.Context) error { 87 | 88 | if !pc.RemoteService.Active { 89 | logger.Error("Loki service is not enabled") 90 | return httpresponse.CreateBadResponse(&c, http.StatusBadRequest, "Loki service is not enabled") 91 | } 92 | 93 | serverName := c.QueryParam("server") 94 | label := c.QueryParam("label") 95 | 96 | responseData, err := pc.RemoteService.RemoteValues(serverName, label) 97 | if err != nil { 98 | logger.Debug(responseData) 99 | } 100 | return httpresponse.CreateSuccessResponse(&c, http.StatusCreated, responseData) 101 | } 102 | 103 | // swagger:route POST /search/remote/data remote remoteRemoteData 104 | // 105 | // Returns data based upon filtered json 106 | // --- 107 | // produces: 108 | // - application/json 109 | // parameters: 110 | // + name: RemoteObject 111 | // in: body 112 | // description: RemoteObject parameters 113 | // schema: 114 | // type: RemoteObject 115 | // required: true 116 | // Security: 117 | // - bearer: [] 118 | // 119 | // SecurityDefinitions: 120 | // bearer: 121 | // type: apiKey 122 | // name: Authorization 123 | // in: header 124 | // responses: 125 | // 200: body:RemoteResponseData 126 | // 400: body:FailureResponse 127 | func (pc *RemoteController) RemoteData(c echo.Context) error { 128 | 129 | if !pc.RemoteService.Active { 130 | logger.Error("Loki service is not enabled") 131 | return httpresponse.CreateBadResponse(&c, http.StatusBadRequest, "Loki service is not enabled") 132 | } 133 | 134 | remoteObject := model.RemoteObject{} 135 | 136 | if err := c.Bind(&remoteObject); err != nil { 137 | logger.Error(err.Error()) 138 | return httpresponse.CreateBadResponse(&c, http.StatusBadRequest, webmessages.UserRequestFormatIncorrect) 139 | } 140 | 141 | responseData, err := pc.RemoteService.RemoteData(&remoteObject) 142 | if err != nil { 143 | logger.Debug(responseData) 144 | } 145 | return httpresponse.CreateSuccessResponse(&c, http.StatusCreated, responseData) 146 | } 147 | -------------------------------------------------------------------------------- /model/authToken.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | func (TableAuthToken) TableName() string { 9 | return "auth_token" 10 | } 11 | 12 | // swagger:model AuthToken 13 | type TableAuthToken struct { 14 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT" json:"-"` 15 | GUID string `gorm:"column:guid;type:uuid" json:"guid"` 16 | UserGUID string `gorm:"column:creator_guid;type:uuid" json:"creator_guid"` 17 | Name string `gorm:"column:name;type:varchar(100)" json:"name" validate:"required"` 18 | Token string `gorm:"column:token;type:varchar(250)" json:"-"` 19 | UserObject json.RawMessage `gorm:"column:user_object;type:json" json:"user_object"` 20 | IPAddress string `gorm:"column:ip_address;type:inet" json:"ip_address"` 21 | CreateDate time.Time `gorm:"column:create_date;default:current_timestamp;not null" json:"create_date"` 22 | LastUsageDate time.Time `gorm:"column:lastusage_date;not null" json:"lastusage_date"` 23 | ExpireDate time.Time `gorm:"column:expire_date;not null" json:"expire_date" validate:"required"` 24 | UsageCalls int `gorm:"column:usage_calls;type:int;default:1" json:"usage_calls"` 25 | LimitCalls int `gorm:"column:limit_calls;type:int;default:1000" json:"limit_calls"` 26 | Active *bool `gorm:"column:active;type:bool" json:"active" validate:"required"` 27 | } 28 | 29 | // swagger:model AuthToken 30 | type AuthToken struct { 31 | Count int `json:"count"` 32 | Data []struct { 33 | // example: 9e4afbd5-7453-415d-b2d4-181057afbfe5" 34 | GUID string `json:"guid"` 35 | // example: ced47b64-ae93-417b-9e1b-8dd1b1410807 36 | CreatorGUID string `json:"creator_guid"` 37 | // example: JGoolsby-Api 38 | Name string `json:"name"` 39 | UserObject struct { 40 | // example: JGoolsby 41 | Username string `json:"username"` 42 | // example: James 43 | Firstname string `json:"firstname"` 44 | // example:Goolsby 45 | Lastname string `json:"lastname"` 46 | // example: JGoolsby@example.com 47 | Email string `json:"email"` 48 | // example: admin 49 | Usergroup string `json:"usergroup"` 50 | // example: 1000 51 | ID int `json:"id"` 52 | // example: 10 53 | Partid int `json:"partid"` 54 | } `json:"user_object"` 55 | // example: 56 | IPAddress string `json:"ip_address"` 57 | // example: 2021-10-01T17:36:21.80063Z 58 | CreateDate string `json:"create_date"` 59 | // example: 2021-10-08T17:36:21.80063Z 60 | LastusageDate string `json:"lastusage_date"` 61 | // example: 2021-10-09T17:36:21.80063Z 62 | ExpireDate string `json:"expire_date"` 63 | // example: 1 64 | UsageCalls int `json:"usage_calls"` 65 | // example: 1000 66 | LimitCalls int `json:"limit_calls"` 67 | // example: true 68 | Active bool `json:"active"` 69 | } `json:"data"` 70 | } 71 | 72 | // swagger:model AuthTokenList 73 | type AuthTokenList struct { 74 | Data []AuthToken `json:"data"` 75 | // example: 2 76 | Count int `json:"count"` 77 | } 78 | 79 | // swagger:model AuthTokenCreateSuccessfulResponse 80 | type AuthTokenCreateSuccessfulResponse struct { 81 | Data struct { 82 | // example: xeCbhpWTrjvJTsTvhTdDXHyXnKTFNecRndhXTiXvDCtdmpeFyvWGxsufJzwyyEXrxuqdAZlTaVmbufdG 83 | Token string `json:"token"` 84 | } `json:"data"` 85 | // example: successfully created auth token 86 | Message string `json:"message"` 87 | } 88 | 89 | // swagger:model AuthTokenDeleteSuccessfulResponse 90 | type AuthTokenDeleteSuccessfulResponse struct { 91 | // example: f4e2953e-ab42-40df-a7de-9ceb7faca396 92 | Data string `json:"data"` 93 | // example: successfully deleted authtoken 94 | Message string `json:"message"` 95 | } 96 | 97 | // swagger:model AuthTokenUpdateSuccessfulResponse 98 | type AuthTokenUpdateSuccessfulResponse struct { 99 | // example: f4e2953e-ab42-40df-a7de-9ceb7faca396 100 | Data string `json:"data"` 101 | // example: successfully updated authtoken 102 | Message string `json:"message"` 103 | } 104 | 105 | // swagger:model UserObjectToken 106 | type UserObjectToken struct { 107 | UserName string `default:"test" json:"username"` 108 | FirstName string `default:"Tester" json:"firstname"` 109 | Lastname string `default:"Tester" json:"lastname"` 110 | Email string `default:"tester@test.com" json:"email"` 111 | Usergroup string `default:"user" json:"usergroup"` 112 | ID uint32 `default:"1000" json:"id"` 113 | PartID uint32 `default:"10" json:"partid"` 114 | } 115 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: App Builder 2 | 3 | on: 4 | release: 5 | type: [published] 6 | push: 7 | branches: [ master, main ] 8 | paths-ignore: 9 | - '**.md' 10 | - '**.yml' 11 | pull_request: 12 | branches: [ master, main ] 13 | paths-ignore: 14 | - '**.md' 15 | 16 | env: 17 | REGISTRY: ghcr.io 18 | 19 | jobs: 20 | build: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v3 24 | 25 | - name: Set up Go 26 | uses: actions/setup-go@v3 27 | with: 28 | go-version: 1.24.5 29 | 30 | - name: Build Frontend 31 | if: github.event_name != 'pull_request' 32 | run: echo y | ./scripts/build_frontend.sh 33 | 34 | - name: Build Binary 35 | run: echo y |./scripts/build_binary.sh 36 | 37 | - name: Build Packages 38 | if: github.event_name != 'pull_request' 39 | run: | 40 | sed -i 's|goreleaser/nfpm|goreleaser/nfpm|g' scripts/build_package.sh 41 | RELEASE=$(./homer-app -version | egrep -o '[0-9].[0-9].[0-9]+') 42 | echo "TAG_NAME=$RELEASE" >> $GITHUB_ENV 43 | VERSION=$RELEASE make package 44 | 45 | - name: Archive Frontend 46 | if: github.event_name != 'pull_request' 47 | run: tar cvfz homer-ui-${{env.TAG_NAME}}.tgz dist 48 | 49 | - name: Upload release 50 | if: github.event_name != 'pull_request' 51 | uses: boxpositron/upload-multiple-releases@1.0.7 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | with: 55 | release_config: | 56 | homer-app 57 | homer-app-${{ env.TAG_NAME }}-amd64.deb 58 | homer-app-${{ env.TAG_NAME }}-amd64.rpm 59 | homer-ui-${{ env.TAG_NAME }}.tgz 60 | tag_name: ${{ env.TAG_NAME }} 61 | release_name: homer-app-${{ env.TAG_NAME }} 62 | draft: false 63 | prerelease: false 64 | overwrite: true 65 | 66 | - name: Check PACKAGECLOUD secret presence 67 | id: checkpackagecloud 68 | shell: bash 69 | run: | 70 | if [ "$SECRET" == "" ] || [ "$USERNAME" == "" ]; then 71 | echo "secretspresent=false" >> $GITHUB_OUTPUT 72 | else 73 | echo "secretspresent=true" >> $GITHUB_OUTPUT 74 | fi 75 | env: 76 | SECRET: ${{ secrets.PACKAGECLOUD_TOKEN }} 77 | USERNAME: ${{ secrets.PACKAGECLOUD_USERNAME }} 78 | 79 | - name: upload deb packagecloud 80 | if: ${{ steps.checkpackagecloud.outputs.secretspresent == 'true' }} 81 | uses: danielmundi/upload-packagecloud@v1 82 | with: 83 | PACKAGE-NAME: homer-app-${{ env.TAG_NAME }}-amd64.deb 84 | PACKAGECLOUD-REPO: sipcapture 85 | PACKAGECLOUD-DISTRIB: any/any 86 | PACKAGECLOUD-USERNAME: ${{ secrets.PACKAGECLOUD_USERNAME }} 87 | PACKAGECLOUD-TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }} 88 | continue-on-error: true 89 | 90 | - name: upload rpm packagecloud 91 | if: ${{ steps.checkpackagecloud.outputs.secretspresent == 'true' }} 92 | uses: danielmundi/upload-packagecloud@v1 93 | with: 94 | PACKAGE-NAME: homer-app-${{ env.TAG_NAME }}-amd64.rpm 95 | PACKAGECLOUD-REPO: sipcapture 96 | PACKAGECLOUD-DISTRIB: rpm_any/rpm_any 97 | PACKAGECLOUD-USERNAME: ${{ secrets.PACKAGECLOUD_USERNAME }} 98 | PACKAGECLOUD-TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }} 99 | continue-on-error: true 100 | 101 | docker-push: 102 | if: github.event_name != 'pull_request' 103 | runs-on: ubuntu-latest 104 | needs: build 105 | permissions: 106 | packages: write 107 | contents: read 108 | 109 | steps: 110 | - uses: actions/checkout@v3 111 | - id: tag_bump 112 | name: Bump version and push tag 113 | uses: anothrNick/github-tag-action@1.39.0 114 | env: 115 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 116 | DEFAULT_BUMP: none 117 | BRANCH: master 118 | 119 | - name: Log in to the Container registry 120 | uses: docker/login-action@v2.1.0 121 | with: 122 | registry: ${{ env.REGISTRY }} 123 | username: ${{ github.actor }} 124 | password: ${{ secrets.GITHUB_TOKEN }} 125 | 126 | - name: Docker meta 127 | id: meta 128 | uses: docker/metadata-action@v4.1.1 129 | with: 130 | images: | 131 | ghcr.io/${{ github.repository }} 132 | tags: | 133 | latest 134 | ${{ steps.tag_bump.outputs.tag }} 135 | 136 | - name: Build and push 137 | uses: docker/build-push-action@v3.2.0 138 | with: 139 | context: . 140 | file: ./Dockerfile 141 | push: ${{ github.event_name != 'pull_request' }} 142 | tags: ${{ steps.meta.outputs.tags }} 143 | labels: ${{ steps.meta.outputs.labels }} 144 | -------------------------------------------------------------------------------- /data/service/alias.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "net" 5 | "net/netip" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/Jeffail/gabs/v2" 10 | "github.com/gaissmai/cidrtree" 11 | "github.com/sipcapture/homer-app/model" 12 | "github.com/sipcapture/homer-app/utils/heputils" 13 | "github.com/sipcapture/homer-app/utils/logger" 14 | ) 15 | 16 | type AliasService struct { 17 | ServiceConfig 18 | } 19 | 20 | // this method create new user in the database 21 | // it doesn't check internally whether all the validation are applied or not 22 | func (as *AliasService) GetAll() ([]model.TableAlias, error) { 23 | 24 | alias := []model.TableAlias{} 25 | if err := as.Session.Debug(). 26 | Table("alias"). 27 | Find(&alias).Error; err != nil { 28 | return alias, err 29 | } 30 | return alias, nil 31 | } 32 | 33 | // this method create new user in the database 34 | // it doesn't check internally whether all the validation are applied or not 35 | func (as *AliasService) GetAllActive() ([]model.TableAlias, error) { 36 | 37 | alias := []model.TableAlias{} 38 | if err := as.Session.Debug(). 39 | Table("alias"). 40 | Where("status = true"). 41 | Find(&alias).Error; err != nil { 42 | return alias, err 43 | } 44 | return alias, nil 45 | } 46 | 47 | func (as *AliasService) GetAllActiveAsMap() (*AliasMap, error) { 48 | rows, err := as.GetAllActive() 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | return NewAliasMap(rows), nil 54 | } 55 | 56 | // this method create new user in the database 57 | // it doesn't check internally whether all the validation are applied or not 58 | func (as *AliasService) Add(alias *model.TableAlias) (string, error) { 59 | alias.GUID = heputils.GenereateNewUUID() 60 | alias.CreateDate = time.Now() 61 | if err := as.Session.Debug(). 62 | Table("alias"). 63 | Create(&alias).Error; err != nil { 64 | return "", err 65 | } 66 | reply := gabs.New() 67 | reply.Set(alias.GUID, "data") 68 | reply.Set("successfully created alias", "message") 69 | return reply.String(), nil 70 | } 71 | 72 | // this method create new user in the database 73 | // it doesn't check internally whether all the validation are applied or not 74 | func (as *AliasService) Get(alias *model.TableAlias) (model.TableAlias, error) { 75 | data := model.TableAlias{} 76 | if err := as.Session.Debug(). 77 | Table("alias"). 78 | Where("guid = ?", alias.GUID).Find(&data).Error; err != nil { 79 | return data, err 80 | } 81 | return data, nil 82 | } 83 | 84 | // this method create new user in the database 85 | // it doesn't check internally whether all the validation are applied or not 86 | func (as *AliasService) Delete(alias *model.TableAlias) error { 87 | if err := as.Session.Debug(). 88 | Table("alias"). 89 | Where("guid = ?", alias.GUID).Delete(model.TableAlias{}).Error; err != nil { 90 | return err 91 | } 92 | return nil 93 | } 94 | 95 | // this method create new user in the database 96 | // it doesn't check internally whether all the validation are applied or not 97 | func (as *AliasService) Update(alias *model.TableAlias) error { 98 | if err := as.Session.Debug(). 99 | Table("alias"). 100 | Debug(). 101 | Model(&model.TableAlias{}). 102 | Where("guid = ?", alias.GUID).Update(alias).Error; err != nil { 103 | return err 104 | } 105 | return nil 106 | } 107 | 108 | type Alias struct { 109 | Name string 110 | Port int 111 | CaptureID string 112 | } 113 | 114 | type AliasMap struct { 115 | table *cidrtree.Table[*[]Alias] 116 | } 117 | 118 | func NewAliasMap(rows []model.TableAlias) *AliasMap { 119 | var table cidrtree.Table[*[]Alias] 120 | m := make(map[netip.Prefix]*[]Alias) 121 | for _, row := range rows { 122 | cidr := row.IP + "/" + strconv.Itoa(*row.Mask) 123 | prefix, err := netip.ParsePrefix(cidr) 124 | if err != nil { 125 | logger.Error("ParsePrefix alias CIDR: ["+cidr+"] error: ", err.Error()) 126 | continue 127 | } 128 | 129 | alias := Alias{ 130 | Name: row.Alias, 131 | Port: *row.Port, 132 | CaptureID: row.CaptureID, 133 | } 134 | 135 | if as, ok := m[prefix]; ok { 136 | *as = append(*as, alias) 137 | } else { 138 | as = &[]Alias{alias} 139 | m[prefix] = as 140 | table.Insert(prefix, as) 141 | } 142 | } 143 | 144 | return &AliasMap{table: &table} 145 | } 146 | 147 | // Find finds an alias in the map based on the IP, port and capture ID provided. 148 | // Performs a longest prefix match based on the IP address. 149 | func (m *AliasMap) Find(ip net.IP, port int, captureID *string) (string, bool) { 150 | addr, ok := netip.AddrFromSlice(ip) 151 | if !ok { 152 | return "", false 153 | } 154 | 155 | _, as, ok := m.table.Lookup(addr.Unmap()) 156 | if !ok { 157 | return "", false 158 | } 159 | 160 | for _, a := range *as { 161 | if a.Port == port && (captureID == nil || a.CaptureID == *captureID) { 162 | return a.Name, true 163 | } 164 | } 165 | 166 | return "", false 167 | } 168 | -------------------------------------------------------------------------------- /utils/logger/echologrus/echologrus.go: -------------------------------------------------------------------------------- 1 | package echologrus 2 | 3 | import ( 4 | "io" 5 | "strconv" 6 | "time" 7 | 8 | "github.com/labstack/echo/v4" 9 | "github.com/labstack/gommon/log" 10 | "github.com/sirupsen/logrus" 11 | ) 12 | 13 | // Logrus : implement Logger 14 | type Logrus struct { 15 | *logrus.Logger 16 | } 17 | 18 | // Logger ... 19 | var Logger *logrus.Logger 20 | 21 | // GetEchoLogger for e.Logger 22 | func GetEchoLogger() Logrus { 23 | return Logrus{Logger} 24 | } 25 | 26 | // Level returns logger level 27 | func (l Logrus) Level() log.Lvl { 28 | switch l.Logger.Level { 29 | case logrus.DebugLevel: 30 | return log.DEBUG 31 | case logrus.WarnLevel: 32 | return log.WARN 33 | case logrus.ErrorLevel: 34 | return log.ERROR 35 | case logrus.InfoLevel: 36 | return log.INFO 37 | default: 38 | l.Panic("Invalid level") 39 | } 40 | 41 | return log.OFF 42 | } 43 | 44 | // SetHeader is a stub to satisfy interface 45 | // It's controlled by Logger 46 | func (l Logrus) SetHeader(_ string) {} 47 | 48 | // SetPrefix It's controlled by Logger 49 | func (l Logrus) SetPrefix(s string) {} 50 | 51 | // Prefix It's controlled by Logger 52 | func (l Logrus) Prefix() string { 53 | return "" 54 | } 55 | 56 | // SetLevel set level to logger from given log.Lvl 57 | func (l Logrus) SetLevel(lvl log.Lvl) { 58 | switch lvl { 59 | case log.DEBUG: 60 | Logger.SetLevel(logrus.DebugLevel) 61 | case log.WARN: 62 | Logger.SetLevel(logrus.WarnLevel) 63 | case log.ERROR: 64 | Logger.SetLevel(logrus.ErrorLevel) 65 | case log.INFO: 66 | Logger.SetLevel(logrus.InfoLevel) 67 | default: 68 | l.Panic("Invalid level") 69 | } 70 | } 71 | 72 | // Output logger output func 73 | func (l Logrus) Output() io.Writer { 74 | return l.Out 75 | } 76 | 77 | // SetOutput change output, default os.Stdout 78 | func (l Logrus) SetOutput(w io.Writer) { 79 | Logger.SetOutput(w) 80 | } 81 | 82 | // Printj print json log 83 | func (l Logrus) Printj(j log.JSON) { 84 | Logger.WithFields(logrus.Fields(j)).Print() 85 | } 86 | 87 | // Debugj debug json log 88 | func (l Logrus) Debugj(j log.JSON) { 89 | Logger.WithFields(logrus.Fields(j)).Debug() 90 | } 91 | 92 | // Infoj info json log 93 | func (l Logrus) Infoj(j log.JSON) { 94 | Logger.WithFields(logrus.Fields(j)).Info() 95 | } 96 | 97 | // Warnj warning json log 98 | func (l Logrus) Warnj(j log.JSON) { 99 | Logger.WithFields(logrus.Fields(j)).Warn() 100 | } 101 | 102 | // Errorj error json log 103 | func (l Logrus) Errorj(j log.JSON) { 104 | Logger.WithFields(logrus.Fields(j)).Error() 105 | } 106 | 107 | // Fatalj fatal json log 108 | func (l Logrus) Fatalj(j log.JSON) { 109 | Logger.WithFields(logrus.Fields(j)).Fatal() 110 | } 111 | 112 | // Panicj panic json log 113 | func (l Logrus) Panicj(j log.JSON) { 114 | Logger.WithFields(logrus.Fields(j)).Panic() 115 | } 116 | 117 | // Print string log 118 | func (l Logrus) Print(i ...interface{}) { 119 | Logger.Print(i[0].(string)) 120 | } 121 | 122 | // Debug string log 123 | func (l Logrus) Debug(i ...interface{}) { 124 | Logger.Debug(i[0].(string)) 125 | } 126 | 127 | // Info string log 128 | func (l Logrus) Info(i ...interface{}) { 129 | Logger.Info(i[0].(string)) 130 | } 131 | 132 | // Warn string log 133 | func (l Logrus) Warn(i ...interface{}) { 134 | Logger.Warn(i[0].(string)) 135 | } 136 | 137 | // Error string log 138 | func (l Logrus) Error(i ...interface{}) { 139 | Logger.Error(i[0].(string)) 140 | } 141 | 142 | // Fatal string log 143 | func (l Logrus) Fatal(i ...interface{}) { 144 | Logger.Fatal(i[0].(string)) 145 | } 146 | 147 | // Panic string log 148 | func (l Logrus) Panic(i ...interface{}) { 149 | Logger.Panic(i[0].(string)) 150 | } 151 | 152 | func logrusMiddlewareHandler(c echo.Context, next echo.HandlerFunc) error { 153 | req := c.Request() 154 | res := c.Response() 155 | start := time.Now() 156 | if err := next(c); err != nil { 157 | c.Error(err) 158 | } 159 | stop := time.Now() 160 | 161 | p := req.URL.Path 162 | 163 | bytesIn := req.Header.Get(echo.HeaderContentLength) 164 | 165 | Logger.WithFields(map[string]interface{}{ 166 | "time_rfc3339": time.Now().Format(time.RFC3339), 167 | "remote_ip": c.RealIP(), 168 | "host": req.Host, 169 | "uri": req.RequestURI, 170 | "method": req.Method, 171 | "path": p, 172 | "referer": req.Referer(), 173 | "user_agent": req.UserAgent(), 174 | "status": res.Status, 175 | "latency": strconv.FormatInt(stop.Sub(start).Nanoseconds()/1000, 10), 176 | "latency_human": stop.Sub(start).String(), 177 | "bytes_in": bytesIn, 178 | "bytes_out": strconv.FormatInt(res.Size, 10), 179 | }).Info("Handled request") 180 | 181 | return nil 182 | } 183 | 184 | func logger(next echo.HandlerFunc) echo.HandlerFunc { 185 | return func(c echo.Context) error { 186 | return logrusMiddlewareHandler(c, next) 187 | } 188 | } 189 | 190 | // Hook is a function to process middleware. 191 | func Hook() echo.MiddlewareFunc { 192 | return logger 193 | } 194 | -------------------------------------------------------------------------------- /etc/dashboard_home.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "alias": "home", 3 | "config": { 4 | "columns": 28, 5 | "draggable": { "handle": ".box-header" }, 6 | "gridType": "scrollVertical", 7 | "ignoreMinSize": "warning", 8 | "margins": [10, 10], 9 | "maxrows": 28, 10 | "pushing": false, 11 | "resizable": { 12 | "enabled": true, 13 | "handles": ["n", "e", "s", "w", "ne", "se", "sw", "nw"] 14 | } 15 | }, 16 | "dashboardId": "home", 17 | "id": "home", 18 | "isLocked": false, 19 | "name": "Home", 20 | "param": "home", 21 | "selectedItem": "", 22 | "shared": false, 23 | "title": "Home", 24 | "type": 3, 25 | "weight": 10, 26 | "widgets": [ 27 | { 28 | "activeTab": true, 29 | "cols": 6, 30 | "config": { 31 | "config": { 32 | "title": "CALL SIP SEARCH", 33 | "searchbutton": true, 34 | "protocol_id": { "name": "SIP", "value": 1 }, 35 | "protocol_profile": { "name": "call", "value": "call" } 36 | }, 37 | "countFieldColumns": 1, 38 | "fields": [ 39 | { 40 | "field_name": "data_header.from_user", 41 | "hepid": 1, 42 | "name": "1:call:data_header.from_user", 43 | "selection": "SIP From user", 44 | "type": "string" 45 | }, 46 | { 47 | "field_name": "data_header.to_user", 48 | "hepid": 1, 49 | "name": "1:call:data_header.to_user", 50 | "selection": "SIP To user", 51 | "type": "string" 52 | }, 53 | { 54 | "field_name": "data_header.method", 55 | "hepid": 1, 56 | "name": "1:call:data_header.method", 57 | "selection": "SIP Method", 58 | "type": "string" 59 | }, 60 | { 61 | "field_name": "data_header.callid", 62 | "hepid": 1, 63 | "name": "1:call:data_header.callid", 64 | "selection": "SIP Callid", 65 | "type": "string" 66 | }, 67 | { 68 | "field_name": "limit", 69 | "hepid": 1, 70 | "name": "1:call:limit", 71 | "selection": "Query Limit", 72 | "type": "string" 73 | }, 74 | { 75 | "field_name": "targetResultsContainer", 76 | "hepid": 1, 77 | "name": "1:call:targetResultsContainer", 78 | "selection": "Results Container", 79 | "type": "string" 80 | } 81 | ], 82 | "title": "CALL SIP SEARCH" 83 | }, 84 | "id": "display-results-1", 85 | "isWarning": true, 86 | "layerIndex": 2, 87 | "minItemCols": 1, 88 | "minItemRows": 1, 89 | "name": "display-results", 90 | "output": {}, 91 | "rows": 9, 92 | "strongIndex": "ProtosearchWidgetComponent", 93 | "tabGroup": "tabIndex-298361", 94 | "title": "TDR Call Search", 95 | "x": 0, 96 | "y": 0 97 | }, 98 | { 99 | "cols": 22, 100 | "config": { "title": "Widget Result" }, 101 | "id": "RESULT-1", 102 | "isWarning": true, 103 | "layerIndex": 2, 104 | "minHeight": 400, 105 | "minItemCols": 1, 106 | "minItemRows": 1, 107 | "minWidth": 650, 108 | "name": "RESULT", 109 | "output": {}, 110 | "rows": 12, 111 | "strongIndex": "ResultWidgetComponent", 112 | "tabGroup": "tabIndex-522642", 113 | "title": "RESULT-1", 114 | "x": 6, 115 | "y": 0 116 | }, 117 | { 118 | "cols": 6, 119 | "config": { 120 | "datePattern": "YYYY-MM-DD", 121 | "fontSizeClock": 20, 122 | "fontSizeDate": 20, 123 | "id": "clock-1", 124 | "location": { 125 | "desc": "Europe/Amsterdam", 126 | "name": "Europe/Amsterdam", 127 | "offset": "+2" 128 | }, 129 | "radius": 177.46875, 130 | "showAnalog": "Digital", 131 | "showDate": true, 132 | "showseconds": false, 133 | "timePattern": "HH:mm:ss", 134 | "title": "Home Clock" 135 | }, 136 | "id": "clock-1", 137 | "layerIndex": 2, 138 | "name": "clock", 139 | "output": {}, 140 | "rows": 3, 141 | "strongIndex": "ClockWidgetComponent", 142 | "tabGroup": "tabIndex-306538", 143 | "title": "World Clock", 144 | "x": 0, 145 | "y": 9 146 | } 147 | ] 148 | } 149 | -------------------------------------------------------------------------------- /utils/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "log" 5 | "log/syslog" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "time" 10 | 11 | rotatelogs "github.com/lestrrat-go/file-rotatelogs" 12 | globalConfig "github.com/sipcapture/homer-app/config" 13 | "github.com/sirupsen/logrus" 14 | ) 15 | 16 | type LogInfo logrus.Fields 17 | 18 | var RLogs *rotatelogs.RotateLogs 19 | var Logger = logrus.New() 20 | 21 | type GormLogger struct{} 22 | 23 | // Code Action 24 | //LOG_INFO 25 | const ( 26 | SYSLOG_LOG_EMERG = "LOG_EMERG" 27 | SYSLOG_LOG_ALERT = "LOG_ALERT" 28 | SYSLOG_LOG_CRIT = "LOG_CRIT" 29 | SYSLOG_LOG_ERR = "LOG_ERR" 30 | SYSLOG_LOG_WARNING = "LOG_WARNING" 31 | SYSLOG_LOG_NOTICE = "LOG_NOTICE" 32 | SYSLOG_LOG_INFO = "LOG_INFO" 33 | SYSLOG_LOG_DEBUG = "LOG_DEBUG" 34 | ) 35 | 36 | /* gorm logger for logrus */ 37 | func (*GormLogger) Print(v ...interface{}) { 38 | if v[0] == "sql" { 39 | logrus.WithFields(logrus.Fields{"module": "gorm", "type": "sql"}).Print(v[3]) 40 | } 41 | if v[0] == "log" { 42 | logrus.WithFields(logrus.Fields{"module": "gorm", "type": "log"}).Print(v[2]) 43 | } 44 | } 45 | 46 | // initLogger function 47 | func InitLogger() { 48 | 49 | //env := os.Getenv("environment") 50 | //isLocalHost := env == "local" 51 | if globalConfig.Setting.LOG_SETTINGS.Json { 52 | // Log as JSON instead of the default ASCII formatter. 53 | Logger.SetFormatter(&logrus.JSONFormatter{}) 54 | } else { 55 | Logger.Formatter.(*logrus.TextFormatter).DisableTimestamp = false 56 | Logger.Formatter.(*logrus.TextFormatter).DisableColors = true 57 | } 58 | // Output to stdout instead of the default stderr 59 | // Can be any io.Writer, see below for File example 60 | if globalConfig.Setting.LOG_SETTINGS.Stdout { 61 | Logger.SetOutput(os.Stdout) 62 | log.SetOutput(os.Stdout) 63 | } 64 | 65 | /* log level default */ 66 | if globalConfig.Setting.LOG_SETTINGS.Level == "" { 67 | globalConfig.Setting.LOG_SETTINGS.Level = "error" 68 | } 69 | 70 | if logLevel, ok := logrus.ParseLevel(globalConfig.Setting.LOG_SETTINGS.Level); ok == nil { 71 | // Only log the warning severity or above. 72 | Logger.SetLevel(logLevel) 73 | } else { 74 | Logger.Error("Couldn't parse loglevel", globalConfig.Setting.LOG_SETTINGS.Level) 75 | Logger.SetLevel(logrus.ErrorLevel) 76 | } 77 | 78 | Logger.Info("init logging system") 79 | 80 | if !globalConfig.Setting.LOG_SETTINGS.Stdout && !globalConfig.Setting.LOG_SETTINGS.SysLog { 81 | // configure file system hook 82 | configureLocalFileSystemHook() 83 | } else if !globalConfig.Setting.LOG_SETTINGS.Stdout { 84 | configureSyslogHook() 85 | } 86 | } 87 | 88 | // SetLoggerLevel function 89 | func SetLoggerLevel(loglevelString string) { 90 | 91 | if logLevel, ok := logrus.ParseLevel(loglevelString); ok == nil { 92 | // Only log the warning severity or above. 93 | Logger.SetLevel(logLevel) 94 | } else { 95 | Logger.Error("Couldn't parse loglevel", loglevelString) 96 | Logger.SetLevel(logrus.ErrorLevel) 97 | } 98 | } 99 | 100 | func configureLocalFileSystemHook() { 101 | 102 | logPath := globalConfig.Setting.LOG_SETTINGS.Path 103 | logName := globalConfig.Setting.LOG_SETTINGS.Name 104 | var err error 105 | 106 | if configPath := os.Getenv("WEBAPPLOGPATH"); configPath != "" { 107 | logPath = configPath 108 | } 109 | 110 | if configName := os.Getenv("WEBAPPLOGNAME"); configName != "" { 111 | logName = configName 112 | } 113 | 114 | fileLogExtension := filepath.Ext(logName) 115 | fileLogBase := strings.TrimSuffix(logName, fileLogExtension) 116 | 117 | pathAllLog := logPath + "/" + fileLogBase + "_%Y%m%d%H%M" + fileLogExtension 118 | pathLog := logPath + "/" + logName 119 | 120 | RLogs, err = rotatelogs.New( 121 | pathAllLog, 122 | rotatelogs.WithLinkName(pathLog), 123 | rotatelogs.WithMaxAge(time.Duration(globalConfig.Setting.LOG_SETTINGS.MaxAgeDays)*time.Hour), 124 | rotatelogs.WithRotationTime(time.Duration(globalConfig.Setting.LOG_SETTINGS.RotationHours)*time.Hour), 125 | ) 126 | 127 | if err != nil { 128 | Logger.Println("Local file system hook initialize fail") 129 | return 130 | } 131 | 132 | Logger.SetOutput(RLogs) 133 | log.SetOutput(RLogs) 134 | } 135 | func configureSyslogHook() { 136 | 137 | var err error 138 | 139 | Logger.Println("Init syslog...") 140 | 141 | sevceritySyslog := getSevirtyByName(globalConfig.Setting.LOG_SETTINGS.SysLogLevel) 142 | 143 | syslogger, err := syslog.New(sevceritySyslog, "homer-app-server") 144 | 145 | if err != nil { 146 | Logger.Println("Unable to connect to syslog:", err) 147 | } 148 | 149 | Logger.SetOutput(syslogger) 150 | log.SetOutput(syslogger) 151 | 152 | } 153 | 154 | func Info(args ...interface{}) { 155 | Logger.Info(args...) 156 | } 157 | 158 | func Error(args ...interface{}) { 159 | Logger.Error(args...) 160 | } 161 | 162 | func Debug(args ...interface{}) { 163 | Logger.Debug(args...) 164 | } 165 | 166 | func getSevirtyByName(sevirity string) syslog.Priority { 167 | 168 | switch sevirity { 169 | case SYSLOG_LOG_EMERG: 170 | return syslog.LOG_EMERG 171 | case SYSLOG_LOG_ALERT: 172 | return syslog.LOG_ALERT 173 | case SYSLOG_LOG_CRIT: 174 | return syslog.LOG_CRIT 175 | case SYSLOG_LOG_ERR: 176 | return syslog.LOG_ERR 177 | case SYSLOG_LOG_WARNING: 178 | return syslog.LOG_WARNING 179 | case SYSLOG_LOG_NOTICE: 180 | return syslog.LOG_NOTICE 181 | case SYSLOG_LOG_INFO: 182 | return syslog.LOG_INFO 183 | case SYSLOG_LOG_DEBUG: 184 | return syslog.LOG_DEBUG 185 | default: 186 | return syslog.LOG_INFO 187 | 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/sipcapture/homer-app/model" 7 | "golang.org/x/oauth2" 8 | ) 9 | 10 | var Setting HomerSettingServer 11 | 12 | var OAuth2TokenMap map[string]model.OAuth2MapToken 13 | 14 | type HomerSettingServer struct { 15 | MAIN_SETTINGS struct { 16 | IsolateQuery string `default:""` 17 | IsolateGroup string `default:""` 18 | UseCaptureIDInAlias bool `default:"false"` 19 | DefaultAuth string `default:"internal"` 20 | EnableGravatar bool `default:"false"` 21 | GravatarUrl string `default:"https://www.gravatar.com/avatar/%s.jpg"` 22 | OAuth2Config oauth2.Config 23 | GlobalToken *oauth2.Token 24 | UserGroups []string `default:"[admin,user,support]"` 25 | SubscribeHttpClient *http.Client 26 | TimeoutHttpClient uint32 `default:"10"` 27 | APIPrefix string `default:""` 28 | ApplyPrefixIndexHtml bool `default:"false"` 29 | RootPath string `default:"/usr/local/homer/dist"` 30 | } 31 | 32 | GRAFANA_SETTINGS struct { 33 | URL string `default:"http://grafana:3000/"` 34 | AuthKey string `default:""` 35 | User string `default:""` 36 | Password string `default:""` 37 | ProxyControl bool `default:"false"` 38 | Path string `default:"grafana"` 39 | ProxyCheck string `default:"simple"` 40 | Enable bool `default:"false"` 41 | } 42 | 43 | TRANSACTION_SETTINGS struct { 44 | DedupModel string `default:"message-ip-pair"` 45 | GlobalDeduplicate bool `default:"false"` 46 | } 47 | 48 | DASHBOARD_SETTINGS struct { 49 | ExternalHomeDashboard string `default:""` 50 | } 51 | 52 | AUTH_SETTINGS struct { 53 | JwtSecret string `default:""` 54 | AuthTokenHeader string `default:"Auth-Token"` 55 | AuthTokenExpire uint32 `default:"1200"` 56 | } 57 | 58 | API_SETTINGS struct { 59 | EnableTokenAccess bool `default:"false"` 60 | } 61 | 62 | OAUTH2_SETTINGS struct { 63 | Enable bool `default:"false"` 64 | ClientID string `default:"1234565"` 65 | ClientSecret string `default:"FAKE"` 66 | ProjectID string `default:"Homer Oauth2"` 67 | AuthUri string `default:"https://accounts.google.com/o/oauth2/auth"` 68 | TokenUri string `default:"https://oauth2.googleapis.com/token"` 69 | AuthProviderCert string `default:"https://www.googleapis.com/oauth2/v1/certs"` 70 | RedirectUri string `default:"http://localhost:80/api/v3/oauth2/auth"` 71 | ProfileURL string `default:"https://www.googleapis.com/oauth2/v1/userinfo"` 72 | Method string `default:"GET"` 73 | ResponseType string `default:"code"` 74 | GrantType string `default:"authorization_code"` 75 | UsePkce bool `default:"true"` 76 | UserToken string `default:"randommin43characterstringisneededasusertoken"` 77 | ServiceProviderName string `default:"google"` 78 | ServiceProviderImage string `default:""` 79 | StateValue string `default:"jkwh027yasj"` 80 | UrlToServiceRedirect string `default:"/api/v3/oauth2/redirect"` 81 | UrlToService string `default:"/"` 82 | Scope []string `default:"[email,openid,profile]"` 83 | EnableGravatar bool `default:"false"` 84 | EnableAutoRedirect bool `default:"false"` 85 | AuthStyle int `default:"0"` 86 | GravatarUrl string `default:"https://www.gravatar.com/avatar/%s.jpg"` 87 | ExpireSSOToken uint32 `default:"5"` 88 | } 89 | 90 | LOG_SETTINGS struct { 91 | Enable bool `default:"true"` 92 | MaxAgeDays uint32 `default:"7"` 93 | RotationHours uint32 `default:"24"` 94 | Path string `default:"/usr/local/homer/log"` 95 | Level string `default:"error"` 96 | Name string `default:"homer-app.log"` 97 | Stdout bool `default:"false"` 98 | Json bool `default:"true"` 99 | SysLogLevel string `default:"LOG_INFO"` 100 | SysLog bool `default:"false"` 101 | SyslogUri string `default:""` 102 | } 103 | 104 | SWAGGER struct { 105 | Enable bool `default:"true"` 106 | ApiJson string `default:"/usr/local/homer/etc/swagger.json"` 107 | ApiHost string `default:"127.0.0.1:9080"` 108 | } 109 | 110 | DECODER_SHARK struct { 111 | Bin string `default:"/usr/bin/tshark"` 112 | Param string `default:""` 113 | Protocols []string `default:""` 114 | UID uint32 `default:"0"` 115 | GID uint32 `default:"0"` 116 | ImportNode string `default:""` 117 | Enable bool `default:"false"` 118 | } 119 | //Loki 120 | LOKI_CONFIG struct { 121 | User string `json:"user" mapstructure:"user" default:"admin"` 122 | Pass string `json:"pass" mapstructure:"pass" default:""` 123 | OldStylePass string `json:"password" mapstructure:"password" default:""` 124 | ParamQuery string `json:"param_query" mapstructure:"param_query" default:"query_range"` 125 | Regexp bool `json:"regexp" mapstructure:"regexp" default:"false"` 126 | Host string `json:"host" mapstructure:"host" default:"http:/127.0.0.1:3100"` 127 | Template string `json:"template" mapstructure:"template" default:"{job=\"heplify-server\"}"` 128 | ExternalUrl string `json:"external_url" mapstructure:"external_url" default:""` 129 | Api string `json:"api" mapstructure:"api" default:"api/v1"` 130 | Enable bool `json:"enable" mapstructure:"enable" default:"false"` 131 | } `json:"loki_config" mapstructure:"loki_config"` 132 | } 133 | --------------------------------------------------------------------------------