From e92a00338dc4b88d2b0b72b335495b02e2c25021 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Nov 2026 14:09:01 +0000 Subject: [PATCH] modemmenu: pass the caller's SIP address (DMODEM_PEER) on to the BBS app_dmodem's DMODEM_PEER (the address a call came from by SIP), from the environment or the session header, is now {peer} and MODEM_PEER: in the selection's environment and session file, and in the header written to a BBS behind the menu, which places a caller without a caller ID by it (D-Modem/bbs#45, D-Modem/D-Modem#108). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01B82eRSVXNfvCFNooYkqeMp --- cmd/modemmenu/e2e_test.go | 4 ++-- cmd/modemmenu/server.go | 1 + cmd/modemmenu/server_test.go | 7 ++++--- cmd/modemmenu/session.go | 28 +++++++++++++++++----------- docs/configuration.md | 1 + docs/running.md | 8 +++++--- samples/modemmenu.conf.sample | 2 ++ 7 files changed, 32 insertions(+), 19 deletions(-) diff --git a/cmd/modemmenu/e2e_test.go b/cmd/modemmenu/e2e_test.go index ae651e8..a84f8d8 100644 --- a/cmd/modemmenu/e2e_test.go +++ b/cmd/modemmenu/e2e_test.go @@ -190,7 +190,7 @@ command = env > %s; cat "$MODEM_SESSION_FILE" >> %s; echo args: {user} {baud} {d cmd := exec.Command(bin, "-config", lineMenu(conf)) cmd.Stdin, cmd.Stdout, cmd.Stderr = s, s, s cmd.Env = append(os.Environ(), "DMODEM_RATE=33600/31200", "DMODEM_CALLERID=5559999", - "DMODEM_DIALED=5551000", "DMODEM_CHANNEL=Local/s@test", "TMPDIR="+dir) + "DMODEM_DIALED=5551000", "DMODEM_CHANNEL=Local/s@test", "DMODEM_PEER=203.0.113.5:5060", "TMPDIR="+dir) cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true, Setctty: true} if err := cmd.Start(); err != nil { t.Fatal(err) @@ -254,7 +254,7 @@ command = env > %s; cat "$MODEM_SESSION_FILE" >> %s; echo args: {user} {baud} {d env, _ := os.ReadFile(envOut) for _, w := range []string{"MODEM_USER=alice", "MODEM_PASSWORD=secret", "MODEM_BAUD=33600", "MODEM_DIALED=5551000", "MODEM_CALLERID=5559999", "MODEM_SELECTION=info", - "MODEM_AUTH=file", "MODEM_CHANNEL=Local/s@test", "MODEM_DATETIME=", `"user": "alice"`, + "MODEM_AUTH=file", "MODEM_CHANNEL=Local/s@test", "MODEM_PEER=203.0.113.5:5060", "MODEM_DATETIME=", `"user": "alice"`, `"password": "secret"`, `"dialed": "5551000"`} { if !bytes.Contains(env, []byte(w)) { t.Errorf("no %s in the environment and session file:\n%s", w, env) diff --git a/cmd/modemmenu/server.go b/cmd/modemmenu/server.go index 1ec2218..ca70873 100644 --- a/cmd/modemmenu/server.go +++ b/cmd/modemmenu/server.go @@ -99,6 +99,7 @@ func headerEnv(h map[string]string, trust bool) []string { "DMODEM_CHANNEL": "MODEM_CHANNEL", "DMODEM_PROFILE": "MODEM_PROFILE", "DMODEM_RATE": "MODEM_RATE", "DMODEM_CALLERID": "MODEM_CALLERID", "DMODEM_DIALED": "MODEM_DIALED", "DMODEM_CONNECTED": "MODEM_EPOCH", + "DMODEM_PEER": "MODEM_PEER", } var env []string for d, m := range call { diff --git a/cmd/modemmenu/server_test.go b/cmd/modemmenu/server_test.go index 8da48ad..6b6cbe5 100644 --- a/cmd/modemmenu/server_test.go +++ b/cmd/modemmenu/server_test.go @@ -21,7 +21,7 @@ import ( func TestHeader(t *testing.T) { a, b := net.Pipe() s := newSession("33600/31200", "555\n1000", "", time.Unix(1791666309, 0)) - s.User, s.Password = "alice", "secret" + s.User, s.Password, s.Peer = "alice", "secret", "203.0.113.5:5060" go func() { writeHeader(a, s, false) a.Write([]byte("data")) @@ -30,7 +30,8 @@ func TestHeader(t *testing.T) { if err != nil { t.Fatal(err) } - if h["MODEM_USER"] != "alice" || h["MODEM_DIALED"] != "555 1000" || h["MODEM_PASSWORD"] != "" { + if h["MODEM_USER"] != "alice" || h["MODEM_DIALED"] != "555 1000" || h["MODEM_PASSWORD"] != "" || + h["MODEM_PEER"] != "203.0.113.5:5060" { t.Errorf("header %v", h) } rest := make([]byte, 4) @@ -40,7 +41,7 @@ func TestHeader(t *testing.T) { } env := strings.Join(headerEnv(h, false), "\n") if !strings.Contains(env, "DMODEM_RATE=33600/31200") || !strings.Contains(env, "DMODEM_CONNECTED=1791666309") || - strings.Contains(env, "UPSTREAM") { + !strings.Contains(env, "DMODEM_PEER=203.0.113.5:5060") || strings.Contains(env, "UPSTREAM") { t.Errorf("env %s", env) } if env := strings.Join(headerEnv(h, true), "\n"); !strings.Contains(env, "MODEMMENU_UPSTREAM_USER=alice") { diff --git a/cmd/modemmenu/session.go b/cmd/modemmenu/session.go index f079620..0cb85b0 100644 --- a/cmd/modemmenu/session.go +++ b/cmd/modemmenu/session.go @@ -16,16 +16,19 @@ import ( // session is what is known of the call and the caller, handed to the // selection in its environment, its command line and its session file type session struct { - User string `json:"user"` - Password string `json:"password,omitempty"` - Auth string `json:"auth"` - Dialed string `json:"dialed"` - CallerID string `json:"callerid"` - Baud string `json:"baud"` - Rate string `json:"rate"` - TxRate string `json:"tx_rate"` - RxRate string `json:"rx_rate"` - Channel string `json:"channel"` + User string `json:"user"` + Password string `json:"password,omitempty"` + Auth string `json:"auth"` + Dialed string `json:"dialed"` + CallerID string `json:"callerid"` + Baud string `json:"baud"` + Rate string `json:"rate"` + TxRate string `json:"tx_rate"` + RxRate string `json:"rx_rate"` + Channel string `json:"channel"` + // the address the call came from by VoIP (the SIP peer's: app_dmodem's + // DMODEM_PEER), "" if not told + Peer string `json:"peer"` Profile string `json:"profile"` Connected time.Time `json:"connected"` Selection string `json:"selection"` @@ -51,13 +54,15 @@ type session struct { } // newSession from app_dmodem's environment (DMODEM_RATE "tx/rx", -// DMODEM_CALLERID, DMODEM_DIALED, DMODEM_CHANNEL, DMODEM_PROFILE), what the +// DMODEM_CALLERID, DMODEM_DIALED, DMODEM_CHANNEL, DMODEM_PROFILE, +// DMODEM_PEER), what the // command line overrides of it func newSession(rate, dialed, callerID string, now time.Time) *session { s := &session{ Dialed: dialed, CallerID: callerID, Channel: os.Getenv("DMODEM_CHANNEL"), + Peer: os.Getenv("DMODEM_PEER"), Profile: os.Getenv("DMODEM_PROFILE"), Connected: now, } @@ -111,6 +116,7 @@ func (s *session) vars(withPassword bool) map[string]string { "rx_rate": s.RxRate, "speed": s.Speed(), "channel": s.Channel, + "peer": s.Peer, "profile": s.Profile, "selection": s.Selection, "time_left": s.TimeLeft, diff --git a/docs/configuration.md b/docs/configuration.md index 507467c..d8cd4e4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -416,6 +416,7 @@ cooked terminal (`tty = sane`), and return to the menu. | `{date}`, `{time}` | `MODEM_DATE`, `MODEM_TIME` | the connection's date and time | | `{datetime}`, `{epoch}` | `MODEM_DATETIME`, `MODEM_EPOCH` | the same, RFC 3339 and Unix time | | `{channel}`, `{profile}` | `MODEM_CHANNEL`, `MODEM_PROFILE` | app_dmodem's channel and profile | +| `{peer}` | `MODEM_PEER` | the address the call came from by SIP (app_dmodem's `DMODEM_PEER`), empty if not known; the BBS places a caller without a caller ID by it | | `{selection}` | `MODEM_SELECTION` | the item chosen | | `{time_left}` | `MODEM_TIME_LEFT` | whole minutes left, empty without a limit | | `{seconds_left}` | `MODEM_SECONDS_LEFT` | the same in seconds | diff --git a/docs/running.md b/docs/running.md index bd7f053..cbc706f 100644 --- a/docs/running.md +++ b/docs/running.md @@ -25,7 +25,8 @@ exten => 5551000,1,DModem(menu) modemmenu reads the call's details from app_dmodem's environment: `DMODEM_RATE` (tx/rx bit/s), `DMODEM_CALLERID`, `DMODEM_DIALED`, -`DMODEM_CHANNEL` and `DMODEM_PROFILE`. `DMODEM_DIALED` needs an +`DMODEM_CHANNEL`, `DMODEM_PROFILE` and `DMODEM_PEER` (the address the +call came from by SIP). `DMODEM_DIALED` needs an app_dmodem that sets it. The `-rate`, `-callerid` and `-dialed` options set these instead, e.g. under a getty on another modem. @@ -53,8 +54,9 @@ socket_group = asterisk A socket has no environment, so app_dmodem's `header = yes` sends the call's details first: `DMODEM_CHANNEL`, `DMODEM_PROFILE`, `DMODEM_RATE`, -`DMODEM_CALLERID`, `DMODEM_DIALED` and `DMODEM_CONNECTED` (Unix time), -one `NAME=value` per line, then an empty line, then the caller's bytes. +`DMODEM_CALLERID`, `DMODEM_DIALED`, `DMODEM_CONNECTED` (Unix time) and +`DMODEM_PEER`, one `NAME=value` per line, then an empty line, then the +caller's bytes. `[server] header = yes` (the default) reads it; with `header = no` the caller's bytes come from the start and the details are unknown. diff --git a/samples/modemmenu.conf.sample b/samples/modemmenu.conf.sample index 4075e11..68591b9 100644 --- a/samples/modemmenu.conf.sample +++ b/samples/modemmenu.conf.sample @@ -40,6 +40,8 @@ ; {epoch} MODEM_EPOCH seconds since 1970 ; {channel} MODEM_CHANNEL the Asterisk channel ; {profile} MODEM_PROFILE the dmodem.conf profile +; {peer} MODEM_PEER the address the call came from by SIP +; (app_dmodem's DMODEM_PEER; "" unknown) ; {selection} MODEM_SELECTION the [item ...] chosen ; ; The rate, dialed number and caller ID come from app_dmodem's DMODEM_RATE,