Rename root, api and sdk module paths (imports, go.mod, go_package, ldflags, build scripts, buf configs) and regenerate protobuf/gateway code with the project's pinned toolchain. Wire identifiers unchanged. api gets a local replace for sdk; root/api/sdk require v0.1.0. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
95 lines
3.1 KiB
Go
95 lines
3.1 KiB
Go
// Copyright IBM Corp. 2020, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package convert
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/bsr"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/bsr/internal/is"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/bsr/ssh"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/storage"
|
|
)
|
|
|
|
// ToAsciicast accepts a bsr.Session and will convert the underlying BSR connection or channel file to an asciinema file.
|
|
// The tempFs will be used to write the asciinema file to disk
|
|
// It returns an io.Reader to the converted asciinema file.
|
|
// This supports the following options:
|
|
// - WithChannelId to indicate this conversion should occur on a channel on a multiplexed session
|
|
// - WithMinWidth to set a minimum width for the asciicast
|
|
// - WithMinHeigh to set a minimum height for the asciicast
|
|
func ToAsciicast(ctx context.Context, session *bsr.Session, tmp storage.TempFile, connectionId string, options ...Option) (io.ReadCloser, error) {
|
|
const op = "convert.ToAsciicast"
|
|
|
|
switch {
|
|
case is.Nil(session):
|
|
return nil, fmt.Errorf("%s: missing session: %w", op, bsr.ErrInvalidParameter)
|
|
case is.Nil(session.Meta):
|
|
return nil, fmt.Errorf("%s: missing session meta: %w", op, bsr.ErrInvalidParameter)
|
|
case is.Nil(tmp):
|
|
return nil, fmt.Errorf("%s: missing temp file: %w", op, bsr.ErrInvalidParameter)
|
|
case connectionId == "":
|
|
return nil, fmt.Errorf("%s: missing connection id: %w", op, bsr.ErrInvalidParameter)
|
|
}
|
|
|
|
opts := getOpts(options...)
|
|
|
|
switch session.Meta.Protocol {
|
|
case ssh.Protocol:
|
|
chanId := opts.withChannelId
|
|
switch {
|
|
case chanId == "":
|
|
return nil, fmt.Errorf("%s: protocol %q requires channel id to convert: %w", op, ssh.Protocol, bsr.ErrInvalidParameter)
|
|
}
|
|
|
|
conn, err := session.OpenConnection(ctx, connectionId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
defer conn.Close(ctx)
|
|
|
|
ch, err := conn.OpenChannel(ctx, chanId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
defer ch.Close(ctx)
|
|
|
|
switch chs := ch.Summary.(type) {
|
|
case *ssh.ChannelSummary:
|
|
switch chs.SessionProgram {
|
|
case ssh.Shell, ssh.Exec:
|
|
reqScanner, err := ch.OpenRequestScanner(ctx, bsr.Inbound)
|
|
if err != nil {
|
|
if !is.Nil(reqScanner) {
|
|
reqScanner.Close()
|
|
}
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
defer reqScanner.Close()
|
|
|
|
msgScanner, err := ch.OpenMessageScanner(ctx, bsr.Outbound)
|
|
if err != nil {
|
|
if !is.Nil(msgScanner) {
|
|
msgScanner.Close()
|
|
}
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
defer msgScanner.Close()
|
|
return sshChannelToAsciicast(ctx, reqScanner, msgScanner, tmp, options...)
|
|
case "":
|
|
return nil, fmt.Errorf("%s: session program not set for asciicast conversion", op)
|
|
default:
|
|
return nil, fmt.Errorf("%s: unsupported %q session program for asciicast conversion", op, chs.SessionProgram)
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("%s: unexpected error occurred with channel summary. possibly a malformed Boundary Session Recording", op)
|
|
}
|
|
|
|
default:
|
|
return nil, fmt.Errorf("%s: %w", op, ErrUnsupportedProtocol)
|
|
}
|
|
}
|