Add freeswitch container
Some checks failed
/ build-freeswitch (push) Has been cancelled

This commit is contained in:
Finn 2024-11-03 21:55:46 -08:00
parent e2f58b8aaf
commit a011ba5dca
12 changed files with 458 additions and 0 deletions

View file

@ -0,0 +1,22 @@
on:
push:
paths:
- containers/freeswitch/**
- .forgejo/workflows/build-freeswitch.yaml
jobs:
build-freeswitch:
runs-on: docker
container:
image: library/docker:dind
steps:
- run: apk add --no-cache nodejs git
- name: login to container registry
run: echo "${{ secrets.DEPLOY_TOKEN }}" | docker login --username ${{ secrets.DEPLOY_USER }} --password-stdin git.janky.solutions
- name: build container image
uses: docker/build-push-action@v6
with:
file: Containerfile
context: "{{defaultContext}}:containers/freeswitch"
tags: git.janky.solutions/jankysolutions/infra/freeswitch:latest
platforms: linux/amd64
push: true

1
containers/freeswitch/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
sources/

View file

@ -0,0 +1,84 @@
FROM docker.io/library/debian:latest AS build
RUN apt-get update && apt-get install -y \
# build
build-essential cmake automake autoconf 'libtool-bin|libtool' pkg-config \
# general
libssl-dev zlib1g-dev libdb-dev unixodbc-dev libncurses5-dev libexpat1-dev libgdbm-dev bison erlang-dev libtpl-dev libtiff5-dev uuid-dev \
# core
libpcre3-dev libedit-dev libsqlite3-dev libcurl4-openssl-dev nasm \
# core codecs
libogg-dev libspeex-dev libspeexdsp-dev \
# mod_enum
libldns-dev \
# mod_python3
python3-dev \
# mod_av
libavformat-dev libswscale-dev \
# mod_lua
liblua5.2-dev \
# mod_opus
libopus-dev \
# mod_pgsql
libpq-dev \
# mod_sndfile
libsndfile1-dev libflac-dev libogg-dev libvorbis-dev \
# mod_shout
libshout3-dev libmpg123-dev libmp3lame-dev \
# mod_memcached
libmemcached-dev
# without this git am will fail
RUN git config --global user.name nobody && git config --global user.email nobody@localhost
ADD patches /patches
# sofia-sip
RUN git clone https://github.com/freeswitch/sofia-sip.git /usr/src/sofia-sip && \
cd /usr/src/sofia-sip && \
git checkout v1.13.17 && \
./bootstrap.sh && \
./configure CFLAGS="-g -ggdb" --with-pic --with-glib=no --without-doxygen && \
make && \
make install
# libks
RUN git clone https://github.com/signalwire/libks.git /usr/src/libks && \
cd /usr/src/libks && \
git checkout v2.0.3 && \
cmake . -DWITH_LIBBACKTRACE=1 -DCMAKE_INSTALL_PREFIX:PATH=/usr/local && \
make && \
make install
# spandsp
RUN git clone https://github.com/freeswitch/spandsp.git /usr/src/spandsp && \
cd /usr/src/spandsp && \
./bootstrap.sh && \
./configure && \
make && \
make install
# freeswitch
RUN git clone https://github.com/signalwire/freeswitch.git /usr/src/freeswitch && \
cd /usr/src/freeswitch && \
git checkout v1.10.11 && \
git am /patches/freeswitch/* && \
./bootstrap.sh -j && \
./configure --enable-core-pgsql-support --disable-fhs && \
make && \
make install
RUN rm -rf /usr/local/freeswitch/conf
FROM library/golang:1.21 AS dynamic-xmlconfig
ADD dynamic-xmlconfig /go/dynamic-xmlconfig
WORKDIR /go/dynamic-xmlconfig
RUN go build -o /dynamic-xmlconfig .
FROM debian:bookworm-slim
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y libcurl4 libsqlite3-0 libpcre3 libspeexdsp1 libspeex1 libedit2 libtpl0 libodbc2 libtiff6 liblua5.2-0 libopus0 libavformat59 libopus0 libsndfile1 libswscale6 libldns3 libpq5 && apt-get clean && rm -rf /var/lib/apt/lists/*
COPY --from=build /usr/local/lib /usr/local/lib
COPY --from=build /usr/local/freeswitch /usr/local/freeswitch
COPY --from=dynamic-xmlconfig /dynamic-xmlconfig /usr/local/bin/dynamic-xmlconfig
ADD freeswitch.xml /usr/local/freeswitch/conf/freeswitch.xml
RUN ldconfig
CMD ["/usr/local/freeswitch/bin/freeswitch", "-nf"]

View file

@ -0,0 +1,7 @@
# FreeSWITCH Container
This is an attempt to package a minimal FreeSWITCH container.
* State is stored in an ODBC database. Must set environment variable `DSN`
* Configuration can be retreived from an HTTP server by setting environment variable `CONFIG_URL`. See [mod_curl_xml](https://developer.signalwire.com/freeswitch/FreeSWITCH-Explained/Modules/mod_xml_curl_1049001/) for details about the HTTP response format.
*

View file

@ -0,0 +1,6 @@
<configuration name="modules.conf" description="Modules">
<modules>
<load module="mod_console"/>
{{ $_, $module := range .Modules }}<load module="{{ $module }}" />{{ end }}
</modules>
</configuration>

View file

@ -0,0 +1,8 @@
<configuration name="xml_curl.conf" description="cURL XML Gateway">
<bindings>
<binding name="all configs">
<param name="gateway-url" value="{{ .ConfigURL }}"
bindings="configuration|dialplan|directory|phrases" />
</binding>
</bindings>
</configuration>

View file

@ -0,0 +1,5 @@
module codeberg.org/thefinn93/freeswitch-container
go 1.21.5
require github.com/kelseyhightower/envconfig v1.4.0

View file

@ -0,0 +1,2 @@
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=

View file

@ -0,0 +1,165 @@
package main
import (
"embed"
"fmt"
"net/url"
"os"
"text/template"
"github.com/kelseyhightower/envconfig"
)
type Options struct {
DSN string `envconfig:"DSN"`
ConfigURL string `envconfig:"CONFIG_URL"` // URL called for configuration, see https://developer.signalwire.com/freeswitch/FreeSWITCH-Explained/Modules/mod_xml_curl_1049001/ (dialplan|directory|phrases will be fetched from to this URL)
CaptureServer string `envconfig:"CAPTURE_SERVER"` // if set, this server gets a copy of all SIP messages. Example: udp:homer.domain.com:5060;hep=3;capture_id=100
LogLevel string `envconfig:"LOG_LEVEL" default:"info"`
Gateway Gateway `envconfig:"GATEWAY"`
Modules []string `envconfig:"MODULES" default:"mod_event_socket,mod_sofia,mod_db,mod_dialplan_xml,mod_g723_1,mod_g729,mod_amr,mod_b64,mod_opus,mod_av,mod_sndfile,mod_native_file,mod_png,mod_local_stream,mod_tone_stream,mod_lua,mod_say_en"`
}
type Gateway struct {
Name string
Username string
Password string
Extra map[string]string
}
var (
//go:embed autoload_configs
autoloadConfigsFS embed.FS
autoloadConfigsTemplates = template.Must(template.New("autoload_configs").ParseFS(autoloadConfigsFS, "autoload_configs/*"))
options Options
vars = map[string]string{ // this is used to fill in what is normally in vars.xml
"sound_prefix": "$${sounds_dir}/en/us/callie",
"domain": "$${local_ip_v4}",
"domain_name": "$${domain}",
"hold_music": "local_stream://moh",
"use_profile": "external",
"rtp_sdes_suites": "AEAD_AES_256_GCM_8|AEAD_AES_128_GCM_8|AES_CM_256_HMAC_SHA1_80|AES_CM_192_HMAC_SHA1_80|AES_CM_128_HMAC_SHA1_80|AES_CM_256_HMAC_SHA1_32|AES_CM_192_HMAC_SHA1_32|AES_CM_128_HMAC_SHA1_32|AES_CM_128_NULL_AUTH",
"global_codec_prefs": "OPUS,G722,PCMU,PCMA,H264,VP8",
"outbound_codec_prefs": "OPUS,G722,PCMU,PCMA,H264,VP8",
"xmpp_client_profile": "xmppc",
"xmpp_server_profile": "xmpps",
"bind_server_ip": "auto",
"unroll_loops": "true",
"outbound_caller_name": "FreeSWITCH",
"outbound_caller_id": "0000000000",
"call_debug": "false",
"console_loglevel": "info",
"default_areacode": "918",
"default_country": "US",
"presence_privacy": "false",
"au-ring": "%(400,200,383,417);%(400,2000,383,417)",
"be-ring": "%(1000,3000,425)",
"ca-ring": "%(2000,4000,440,480)",
"cn-ring": "%(1000,4000,450)",
"cy-ring": "%(1500,3000,425)",
"cz-ring": "%(1000,4000,425)",
"de-ring": "%(1000,4000,425)",
"dk-ring": "%(1000,4000,425)",
"dz-ring": "%(1500,3500,425)",
"eg-ring": "%(2000,1000,475,375)",
"es-ring": "%(1500,3000,425)",
"fi-ring": "%(1000,4000,425)",
"fr-ring": "%(1500,3500,440)",
"hk-ring": "%(400,200,440,480);%(400,3000,440,480)",
"hu-ring": "%(1250,3750,425)",
"il-ring": "%(1000,3000,400)",
"in-ring": "%(400,200,425,375);%(400,2000,425,375)",
"jp-ring": "%(1000,2000,420,380)",
"ko-ring": "%(1000,2000,440,480)",
"pk-ring": "%(1000,2000,400)",
"pl-ring": "%(1000,4000,425)",
"ro-ring": "%(1850,4150,475,425)",
"rs-ring": "%(1000,4000,425)",
"ru-ring": "%(800,3200,425)",
"sa-ring": "%(1200,4600,425)",
"tr-ring": "%(2000,4000,450)",
"uk-ring": "%(400,200,400,450);%(400,2000,400,450)",
"us-ring": "%(2000,4000,440,480)",
"bong-ring": "v",
"beep": "%(1000,0,640)",
"sit": "%(274,0,913.8);%(274,0,1370.6);%(380,0,1776.7)",
"df_us_ssn": "(?!219099999|078051120)(?!666|000|9d{2})d{3}(?!00)d{2}(?!0{4})d{4}",
"df_luhn": "?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35d{3})d{11}",
"default_provider": "example.com",
"default_provider_username": "joeuser",
"default_provider_password": "password",
"default_provider_from_domain": "example.com",
"default_provider_register": "false",
"default_provider_contact": "5000",
"sip_tls_version": "tlsv1,tlsv1.1,tlsv1.2",
"sip_tls_ciphers": "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH",
"internal_auth_calls": "true",
"internal_sip_port": "5060",
"internal_tls_port": "5061",
"internal_ssl_enable": "false",
"external_auth_calls": "false",
"external_sip_port": "5080",
"external_tls_port": "5081",
"external_ssl_enable": "false",
"rtp_video_max_bandwidth_in": "3mb",
"rtp_video_max_bandwidth_out": "3mb",
"suppress_cng": "true",
"rtp_liberal_dtmf": "true",
"video_mute_png": "$${images_dir}/default-mute.png",
"video_no_avatar_png": "$${images_dir}/default-avatar.png",
}
)
func main() {
if err := envconfig.Process("", &options); err != nil {
panic(err)
}
if options.ConfigURL != "" {
options.Modules = append(options.Modules, "mod_xml_curl")
}
// autoload_configs
for k, v := range vars {
fmt.Printf("<X-PRE-PROCESS cmd=\"set\" data=\"%s=%s\"/>\n", k, v)
}
fmt.Println("<section name=\"configuration\" description=\"Various Configuration\">")
configs, err := autoloadConfigsFS.ReadDir("autoload_configs")
if err != nil {
panic(err)
}
for _, file := range configs {
if file.IsDir() {
fmt.Println("skipping dir: ", file.Name())
continue
}
if err := autoloadConfigsTemplates.ExecuteTemplate(os.Stdout, file.Name(), options); err != nil {
panic(err)
}
}
fmt.Println("</section>")
}
func (g *Gateway) Decode(value string) error {
u, err := url.Parse(value)
if err != nil {
return err
}
g.Name = u.Hostname()
g.Username = u.User.Username()
if password, ok := u.User.Password(); ok {
g.Password = password
}
g.Extra = make(map[string]string)
for k, v := range u.Query() {
if len(v) > 0 {
g.Extra[k] = v[0]
}
}
return nil
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0"?>
<document type="freeswitch/xml">
<!--
these two stun-sets are from the stock vars.xml and I cant quickly find documentation on them,
so I'm leaving them for now. Everyting else is in that file is just "set"
-->
<X-PRE-PROCESS cmd="stun-set" data="external_rtp_ip=stun:stun.freeswitch.org"/>
<X-PRE-PROCESS cmd="stun-set" data="external_sip_ip=stun:stun.freeswitch.org"/>
<X-PRE-PROCESS cmd="exec" data="/usr/local/bin/dynamic-xmlconfig" />
</document>

View file

@ -0,0 +1,61 @@
From 9d91452d5a0a4a44884ff74fa2e49c51aaaa32dd Mon Sep 17 00:00:00 2001
From: nobody <nobody@localhost>
Date: Wed, 24 Jan 2024 21:19:50 -0800
Subject: [PATCH 1/2] Fix mod_spandsp build
based on https://github.com/xrobau/freeswitch-ubuntubuilder/blob/master/patches/freeswitch/005-spandsp-updates.patch
---
src/mod/applications/mod_spandsp/mod_spandsp_dsp.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c b/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c
index 836808a48d..9558f42169 100644
--- a/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c
+++ b/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c
@@ -156,13 +156,13 @@ static int get_v18_mode(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
const char *var;
- int r = V18_MODE_5BIT_4545;
+ int r = V18_MODE_WEITBRECHT_5BIT_4545;
if ((var = switch_channel_get_variable(channel, "v18_mode"))) {
if (!strcasecmp(var, "5BIT_45") || !strcasecmp(var, "baudot")) {
- r = V18_MODE_5BIT_4545;
+ r = V18_MODE_WEITBRECHT_5BIT_4545;
} else if (!strcasecmp(var, "5BIT_50")) {
- r = V18_MODE_5BIT_50;
+ r = V18_MODE_WEITBRECHT_5BIT_50;
} else if (!strcasecmp(var, "DTMF")) {
r = V18_MODE_DTMF;
} else if (!strcasecmp(var, "EDT")) {
@@ -213,7 +213,7 @@ switch_status_t spandsp_tdd_send_session(switch_core_session_t *session, const c
return SWITCH_STATUS_FALSE;
}
- tdd_state = v18_init(NULL, TRUE, get_v18_mode(session), V18_AUTOMODING_GLOBAL, put_text_msg, NULL);
+ tdd_state = v18_init(NULL, TRUE, get_v18_mode(session), V18_AUTOMODING_GLOBAL, put_text_msg, NULL, NULL, NULL);
v18_put(tdd_state, text, -1);
@@ -260,7 +260,7 @@ switch_status_t spandsp_tdd_encode_session(switch_core_session_t *session, const
}
pvt->session = session;
- pvt->tdd_state = v18_init(NULL, TRUE, get_v18_mode(session), V18_AUTOMODING_GLOBAL, put_text_msg, NULL);
+ pvt->tdd_state = v18_init(NULL, TRUE, get_v18_mode(session), V18_AUTOMODING_GLOBAL, put_text_msg, NULL, NULL, NULL);
pvt->head_lead = TDD_LEAD;
v18_put(pvt->tdd_state, text, -1);
@@ -338,7 +338,7 @@ switch_status_t spandsp_tdd_decode_session(switch_core_session_t *session)
}
pvt->session = session;
- pvt->tdd_state = v18_init(NULL, FALSE, get_v18_mode(session), V18_AUTOMODING_GLOBAL, put_text_msg, pvt);
+ pvt->tdd_state = v18_init(NULL, FALSE, get_v18_mode(session), V18_AUTOMODING_GLOBAL, put_text_msg, pvt, NULL, NULL);
if ((status = switch_core_media_bug_add(session, "spandsp_tdd_decode", NULL,
tdd_decode_callback, pvt, 0, SMBF_READ_REPLACE | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) {
--
2.39.2

View file

@ -0,0 +1,87 @@
From 28b7f07ae806a975074e8d0087263f6965f24ae3 Mon Sep 17 00:00:00 2001
From: nobody <nobody@localhost>
Date: Wed, 24 Jan 2024 21:20:07 -0800
Subject: [PATCH] disable some cloud bullshit, enable some other bullshit
---
build/modules.conf.in | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/build/modules.conf.in b/build/modules.conf.in
index 7bf59e2acc..22ddc03845 100644
--- a/build/modules.conf.in
+++ b/build/modules.conf.in
@@ -3,12 +3,12 @@ applications/mod_av
#applications/mod_avmd
#applications/mod_bert
#applications/mod_blacklist
-#applications/mod_callcenter
-#applications/mod_cidlookup
+applications/mod_callcenter
+applications/mod_cidlookup
#applications/mod_cluechoo
applications/mod_commands
applications/mod_conference
-#applications/mod_curl
+applications/mod_curl
#applications/mod_cv
applications/mod_db
#applications/mod_directory
@@ -17,7 +17,7 @@ applications/mod_dptools
#applications/mod_easyroute
applications/mod_enum
applications/mod_esf
-#applications/mod_esl
+applications/mod_esl
applications/mod_expr
applications/mod_fifo
#applications/mod_fsk
@@ -28,7 +28,7 @@ applications/mod_httapi
#applications/mod_http_cache
#applications/mod_ladspa
#applications/mod_lcr
-#applications/mod_memcache
+applications/mod_memcache
#applications/mod_mongo
#applications/mod_mp4
#applications/mod_mp4v2
@@ -39,7 +39,6 @@ applications/mod_httapi
#applications/mod_rad_auth
#applications/mod_redis
#applications/mod_rss
-applications/mod_signalwire
applications/mod_sms
#applications/mod_sms_flowroute
#applications/mod_snapshot
@@ -121,7 +120,7 @@ formats/mod_native_file
formats/mod_png
#formats/mod_portaudio_stream
#formats/mod_shell_stream
-#formats/mod_shout
+formats/mod_shout
formats/mod_sndfile
#formats/mod_ssml
formats/mod_tone_stream
@@ -138,8 +137,8 @@ languages/mod_lua
#languages/mod_yaml
loggers/mod_console
#loggers/mod_graylog2
-loggers/mod_logfile
-loggers/mod_syslog
+#loggers/mod_logfile
+#loggers/mod_syslog
#loggers/mod_raven
#say/mod_say_de
say/mod_say_en
@@ -162,7 +161,7 @@ say/mod_say_en
#timers/mod_posix_timer
#timers/mod_timerfd
xml_int/mod_xml_cdr
-#xml_int/mod_xml_curl
+xml_int/mod_xml_curl
#xml_int/mod_xml_ldap
#xml_int/mod_xml_radius
xml_int/mod_xml_rpc
--
2.39.2