2022-07-01 23:07:13 +02:00
package sstore
import (
"context"
"fmt"
2022-09-21 02:37:49 +02:00
"strconv"
2022-07-13 06:51:17 +02:00
"strings"
2022-11-28 09:13:00 +01:00
"time"
2022-07-01 23:07:13 +02:00
"github.com/google/uuid"
2022-11-27 23:12:15 +01:00
"github.com/scripthaus-dev/mshell/pkg/base"
2022-07-08 01:29:14 +02:00
"github.com/scripthaus-dev/mshell/pkg/packet"
2022-11-28 09:13:00 +01:00
"github.com/scripthaus-dev/mshell/pkg/shexec"
2022-09-21 02:37:49 +02:00
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
2022-07-01 23:07:13 +02:00
)
2022-08-30 04:18:02 +02:00
const HistoryCols = "historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd"
const DefaultMaxHistoryItems = 1000
2022-07-01 23:07:13 +02:00
func NumSessions ( ctx context . Context ) ( int , error ) {
2022-10-11 02:30:48 +02:00
var numSessions int
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := "SELECT count(*) FROM session"
numSessions = tx . GetInt ( query )
return nil
} )
return numSessions , txErr
2022-07-01 23:07:13 +02:00
}
2022-07-02 02:38:36 +02:00
func GetAllRemotes ( ctx context . Context ) ( [ ] * RemoteType , error ) {
2022-07-07 22:26:46 +02:00
var rtn [ ] * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-09-14 21:06:55 +02:00
query := ` SELECT * FROM remote ORDER BY remoteidx `
2022-07-07 22:26:46 +02:00
marr := tx . SelectMaps ( query )
for _ , m := range marr {
rtn = append ( rtn , RemoteFromMap ( m ) )
}
return nil
} )
2022-07-02 02:38:36 +02:00
if err != nil {
return nil , err
}
2022-07-07 22:26:46 +02:00
return rtn , nil
2022-07-02 02:38:36 +02:00
}
2022-08-17 21:24:09 +02:00
func GetRemoteByAlias ( ctx context . Context , alias string ) ( * RemoteType , error ) {
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM remote WHERE remotealias = ? `
m := tx . GetMap ( query , alias )
remote = RemoteFromMap ( m )
return nil
} )
if err != nil {
return nil , err
}
return remote , nil
}
2022-08-17 00:08:28 +02:00
func GetRemoteById ( ctx context . Context , remoteId string ) ( * RemoteType , error ) {
2022-07-07 22:26:46 +02:00
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-17 00:08:28 +02:00
query := ` SELECT * FROM remote WHERE remoteid = ? `
m := tx . GetMap ( query , remoteId )
2022-07-07 22:26:46 +02:00
remote = RemoteFromMap ( m )
return nil
} )
2022-07-01 23:07:13 +02:00
if err != nil {
return nil , err
}
2022-07-07 22:26:46 +02:00
return remote , nil
2022-07-01 23:07:13 +02:00
}
2022-10-04 20:45:24 +02:00
func GetLocalRemote ( ctx context . Context ) ( * RemoteType , error ) {
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM remote WHERE local `
m := tx . GetMap ( query )
remote = RemoteFromMap ( m )
return nil
} )
if err != nil {
return nil , err
}
return remote , nil
}
2022-09-14 02:11:36 +02:00
func GetRemoteByCanonicalName ( ctx context . Context , cname string ) ( * RemoteType , error ) {
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM remote WHERE remotecanonicalname = ? `
m := tx . GetMap ( query , cname )
remote = RemoteFromMap ( m )
return nil
} )
if err != nil {
return nil , err
}
return remote , nil
}
2022-08-17 00:08:28 +02:00
func GetRemoteByPhysicalId ( ctx context . Context , physicalId string ) ( * RemoteType , error ) {
2022-07-07 22:26:46 +02:00
var remote * RemoteType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-17 00:08:28 +02:00
query := ` SELECT * FROM remote WHERE physicalid = ? `
m := tx . GetMap ( query , physicalId )
2022-07-07 22:26:46 +02:00
remote = RemoteFromMap ( m )
return nil
} )
2022-07-01 23:07:13 +02:00
if err != nil {
return nil , err
}
2022-07-07 22:26:46 +02:00
return remote , nil
2022-07-01 23:07:13 +02:00
}
2022-09-14 02:11:36 +02:00
func UpsertRemote ( ctx context . Context , r * RemoteType ) error {
if r == nil {
2022-07-01 23:07:13 +02:00
return fmt . Errorf ( "cannot insert nil remote" )
}
2022-09-14 02:11:36 +02:00
if r . RemoteId == "" {
2022-09-01 21:47:10 +02:00
return fmt . Errorf ( "cannot insert remote without id" )
2022-07-01 23:07:13 +02:00
}
2022-09-14 02:11:36 +02:00
if r . RemoteCanonicalName == "" {
2022-09-01 21:47:10 +02:00
return fmt . Errorf ( "cannot insert remote with canonicalname" )
}
2022-09-14 02:11:36 +02:00
if r . RemoteType == "" {
2022-09-01 21:47:10 +02:00
return fmt . Errorf ( "cannot insert remote without type" )
2022-07-01 23:07:13 +02:00
}
2022-09-01 21:47:10 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT remoteid FROM remote WHERE remoteid = ? `
2022-09-14 02:11:36 +02:00
if tx . Exists ( query , r . RemoteId ) {
tx . ExecWrap ( ` DELETE FROM remote WHERE remoteid = ? ` , r . RemoteId )
2022-09-01 21:47:10 +02:00
}
2022-09-14 02:11:36 +02:00
query = ` SELECT remoteid FROM remote WHERE remotecanonicalname = ? `
if tx . Exists ( query , r . RemoteCanonicalName ) {
return fmt . Errorf ( "remote has duplicate canonicalname '%s', cannot create" , r . RemoteCanonicalName )
2022-09-01 21:47:10 +02:00
}
2022-10-01 01:23:40 +02:00
query = ` SELECT remoteid FROM remote WHERE remotealias = ? `
2022-10-01 01:05:48 +02:00
if r . RemoteAlias != "" && tx . Exists ( query , r . RemoteAlias ) {
return fmt . Errorf ( "remote has duplicate alias '%s', cannot create" , r . RemoteAlias )
}
2022-09-20 23:23:53 +02:00
query = ` SELECT COALESCE(max(remoteidx), 0) FROM remote `
2022-09-14 21:06:55 +02:00
maxRemoteIdx := tx . GetInt ( query )
r . RemoteIdx = int64 ( maxRemoteIdx + 1 )
2022-09-01 21:47:10 +02:00
query = ` INSERT INTO remote
2022-11-28 09:13:00 +01:00
( remoteid , physicalid , remotetype , remotealias , remotecanonicalname , remotesudo , remoteuser , remotehost , connectmode , autoinstall , sshopts , remoteopts , lastconnectts , archived , remoteidx , local ) VALUES
( : remoteid , : physicalid , : remotetype , : remotealias , : remotecanonicalname , : remotesudo , : remoteuser , : remotehost , : connectmode , : autoinstall , : sshopts , : remoteopts , : lastconnectts , : archived , : remoteidx , : local ) `
2022-09-14 02:11:36 +02:00
tx . NamedExecWrap ( query , r . ToMap ( ) )
2022-09-13 21:06:12 +02:00
return nil
} )
return txErr
}
2022-08-11 21:07:41 +02:00
func InsertHistoryItem ( ctx context . Context , hitem * HistoryItemType ) error {
if hitem == nil {
return fmt . Errorf ( "cannot insert nil history item" )
}
2022-10-11 02:30:48 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` INSERT INTO history
( historyid , ts , userid , sessionid , screenid , windowid , lineid , cmdid , haderror , cmdstr , remoteownerid , remoteid , remotename , ismetacmd ) VALUES
( : historyid , : ts , : userid , : sessionid , : screenid , : windowid , : lineid , : cmdid , : haderror , : cmdstr , : remoteownerid , : remoteid , : remotename , : ismetacmd ) `
tx . NamedExecWrap ( query , hitem . ToMap ( ) )
return nil
} )
return txErr
2022-08-11 21:07:41 +02:00
}
2022-08-30 04:18:02 +02:00
func runHistoryQuery ( tx * TxWrap , sessionId string , windowId string , opts HistoryQueryOpts ) ( [ ] * HistoryItemType , error ) {
// check sessionid/windowid format because we are directly inserting them into the SQL
if sessionId != "" {
_ , err := uuid . Parse ( sessionId )
if err != nil {
return nil , fmt . Errorf ( "malformed sessionid" )
}
}
if windowId != "" {
_ , err := uuid . Parse ( windowId )
if err != nil {
return nil , fmt . Errorf ( "malformed windowid" )
}
}
hnumStr := ""
whereClause := ""
if sessionId != "" && windowId != "" {
whereClause = fmt . Sprintf ( "WHERE sessionid = '%s' AND windowid = '%s'" , sessionId , windowId )
hnumStr = "w"
} else if sessionId != "" {
whereClause = fmt . Sprintf ( "WHERE sessionid = '%s'" , sessionId )
hnumStr = "s"
} else {
hnumStr = "g"
}
maxItems := opts . MaxItems
if maxItems == 0 {
maxItems = DefaultMaxHistoryItems
}
query := fmt . Sprintf ( "SELECT %s, '%s' || row_number() OVER win AS historynum FROM history %s WINDOW win AS (ORDER BY ts, historyid) ORDER BY ts DESC, historyid DESC LIMIT %d" , HistoryCols , hnumStr , whereClause , maxItems )
if opts . FromTs > 0 {
query = fmt . Sprintf ( "SELECT * FROM (%s) WHERE ts >= %d" , query , opts . FromTs )
}
marr := tx . SelectMaps ( query )
rtn := make ( [ ] * HistoryItemType , len ( marr ) )
for idx , m := range marr {
hitem := HistoryItemFromMap ( m )
rtn [ idx ] = hitem
}
return rtn , nil
}
func GetHistoryItems ( ctx context . Context , sessionId string , windowId string , opts HistoryQueryOpts ) ( [ ] * HistoryItemType , error ) {
2022-08-12 08:45:15 +02:00
var rtn [ ] * HistoryItemType
2022-08-30 04:18:02 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var err error
rtn , err = runHistoryQuery ( tx , sessionId , windowId , opts )
if err != nil {
return err
2022-08-28 23:24:05 +02:00
}
2022-08-12 08:45:15 +02:00
return nil
} )
2022-08-30 04:18:02 +02:00
if txErr != nil {
return nil , txErr
2022-08-12 08:45:15 +02:00
}
return rtn , nil
}
2022-08-09 01:21:46 +02:00
func GetBareSessions ( ctx context . Context ) ( [ ] * SessionType , error ) {
var rtn [ ] * SessionType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-27 01:21:19 +02:00
query := ` SELECT * FROM session ORDER BY sessionidx `
2022-08-09 01:21:46 +02:00
tx . SelectWrap ( & rtn , query )
return nil
} )
if err != nil {
return nil , err
}
return rtn , nil
}
2022-08-27 01:21:19 +02:00
func GetAllSessionIds ( ctx context . Context ) ( [ ] string , error ) {
var rtn [ ] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid from session ORDER by sessionidx `
rtn = tx . SelectStrings ( query )
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-08-26 22:12:17 +02:00
func GetBareSessionById ( ctx context . Context , sessionId string ) ( * SessionType , error ) {
var rtn SessionType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM session WHERE sessionid = ? `
tx . GetWrap ( & rtn , query , sessionId )
return nil
} )
if txErr != nil {
return nil , txErr
}
if rtn . SessionId == "" {
return nil , nil
}
return & rtn , nil
}
2022-08-27 01:21:19 +02:00
func GetAllSessions ( ctx context . Context ) ( * ModelUpdate , error ) {
2022-07-08 22:23:45 +02:00
var rtn [ ] * SessionType
2022-08-27 01:21:19 +02:00
var activeSessionId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-07-12 22:50:44 +02:00
query := ` SELECT * FROM session `
tx . SelectWrap ( & rtn , query )
2022-08-11 03:33:32 +02:00
sessionMap := make ( map [ string ] * SessionType )
2022-08-09 01:21:46 +02:00
for _ , session := range rtn {
2022-08-11 03:33:32 +02:00
sessionMap [ session . SessionId ] = session
2022-08-09 01:21:46 +02:00
session . Full = true
}
2022-07-13 06:51:17 +02:00
var screens [ ] * ScreenType
query = ` SELECT * FROM screen ORDER BY screenidx `
tx . SelectWrap ( & screens , query )
screenMap := make ( map [ string ] [ ] * ScreenType )
for _ , screen := range screens {
screenArr := screenMap [ screen . SessionId ]
screenArr = append ( screenArr , screen )
screenMap [ screen . SessionId ] = screenArr
}
for _ , session := range rtn {
session . Screens = screenMap [ session . SessionId ]
}
var sws [ ] * ScreenWindowType
query = ` SELECT * FROM screen_window `
tx . SelectWrap ( & sws , query )
screenIdMap := make ( map [ string ] * ScreenType )
for _ , screen := range screens {
screenIdMap [ screen . SessionId + screen . ScreenId ] = screen
}
for _ , sw := range sws {
screen := screenIdMap [ sw . SessionId + sw . ScreenId ]
if screen == nil {
continue
}
screen . Windows = append ( screen . Windows , sw )
}
2022-08-24 11:14:16 +02:00
query = ` SELECT * FROM remote_instance `
2022-10-17 08:51:04 +02:00
riMaps := tx . SelectMaps ( query )
for _ , m := range riMaps {
ri := RIFromMap ( m )
2022-08-11 03:33:32 +02:00
s := sessionMap [ ri . SessionId ]
if s != nil {
s . Remotes = append ( s . Remotes , ri )
}
}
2022-08-27 01:21:19 +02:00
query = ` SELECT activesessionid FROM client `
activeSessionId = tx . GetString ( query )
2022-07-12 22:50:44 +02:00
return nil
} )
2022-08-27 01:21:19 +02:00
if txErr != nil {
return nil , txErr
}
return & ModelUpdate { Sessions : rtn , ActiveSessionId : activeSessionId } , nil
2022-07-12 22:50:44 +02:00
}
func GetWindowById ( ctx context . Context , sessionId string , windowId string ) ( * WindowType , error ) {
var rtnWindow * WindowType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM window WHERE sessionid = ? AND windowid = ? `
2022-08-24 11:14:16 +02:00
m := tx . GetMap ( query , sessionId , windowId )
if m == nil {
2022-07-12 22:50:44 +02:00
return nil
}
2022-08-24 11:14:16 +02:00
rtnWindow = WindowFromMap ( m )
2022-10-07 08:58:38 +02:00
query = ` SELECT * FROM line WHERE sessionid = ? AND windowid = ? ORDER BY linenum `
2022-08-24 11:14:16 +02:00
tx . SelectWrap ( & rtnWindow . Lines , query , sessionId , windowId )
2022-07-12 22:50:44 +02:00
query = ` SELECT * FROM cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE sessionid = ? AND windowid = ?) `
cmdMaps := tx . SelectMaps ( query , sessionId , windowId )
for _ , m := range cmdMaps {
2022-08-24 11:14:16 +02:00
rtnWindow . Cmds = append ( rtnWindow . Cmds , CmdFromMap ( m ) )
2022-07-12 22:50:44 +02:00
}
return nil
} )
return rtnWindow , err
2022-07-08 22:23:45 +02:00
}
2022-07-15 03:39:40 +02:00
func GetSessionScreens ( ctx context . Context , sessionId string ) ( [ ] * ScreenType , error ) {
var rtn [ ] * ScreenType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM screen WHERE sessionid = ? ORDER BY screenidx `
tx . SelectWrap ( & rtn , query , sessionId )
return nil
} )
return rtn , txErr
}
2022-07-01 23:07:13 +02:00
func GetSessionById ( ctx context . Context , id string ) ( * SessionType , error ) {
2022-08-27 01:21:19 +02:00
allSessionsUpdate , err := GetAllSessions ( ctx )
2022-07-01 23:07:13 +02:00
if err != nil {
return nil , err
}
2022-08-27 01:21:19 +02:00
allSessions := allSessionsUpdate . Sessions
2022-08-09 01:21:46 +02:00
for _ , session := range allSessions {
if session . SessionId == id {
return session , nil
}
}
return nil , nil
2022-07-01 23:07:13 +02:00
}
2022-07-05 07:18:01 +02:00
func GetSessionByName ( ctx context . Context , name string ) ( * SessionType , error ) {
2022-10-11 02:30:48 +02:00
var session * SessionType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid FROM session WHERE name = ? `
sessionId := tx . GetString ( query , name )
if sessionId == "" {
return nil
2022-07-05 07:18:01 +02:00
}
2022-10-11 02:30:48 +02:00
var err error
session , err = GetSessionById ( tx . Context ( ) , sessionId )
if err != nil {
return err
}
return nil
} )
if txErr != nil {
return nil , txErr
2022-07-05 07:18:01 +02:00
}
2022-10-11 02:30:48 +02:00
return session , nil
2022-07-05 07:18:01 +02:00
}
2022-07-08 22:23:45 +02:00
// also creates default window, returns sessionId
// if sessionName == "", it will be generated
2022-08-24 11:14:16 +02:00
func InsertSessionWithName ( ctx context . Context , sessionName string , activate bool ) ( UpdatePacket , error ) {
2022-09-21 02:37:49 +02:00
newSessionId := scbase . GenSCUUID ( )
2022-07-08 22:23:45 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-07-13 06:51:17 +02:00
names := tx . SelectStrings ( ` SELECT name FROM session ` )
sessionName = fmtUniqueName ( sessionName , "session-%d" , len ( names ) + 1 , names )
maxSessionIdx := tx . GetInt ( ` SELECT COALESCE(max(sessionidx), 0) FROM session ` )
2022-08-24 22:21:54 +02:00
query := ` INSERT INTO session (sessionid, name, activescreenid, sessionidx, notifynum, ownerid, sharemode, accesskey) VALUES (?, ?, '', ?, ?, '', 'local', '') `
2022-07-13 06:51:17 +02:00
tx . ExecWrap ( query , newSessionId , sessionName , maxSessionIdx + 1 , 0 )
2022-07-15 03:39:40 +02:00
_ , err := InsertScreen ( tx . Context ( ) , newSessionId , "" , true )
2022-07-13 06:51:17 +02:00
if err != nil {
return err
2022-07-08 22:23:45 +02:00
}
2022-08-27 01:21:19 +02:00
if activate {
query = ` UPDATE client SET activesessionid = ? `
tx . ExecWrap ( query , newSessionId )
}
2022-07-01 23:07:13 +02:00
return nil
} )
2022-07-15 03:39:40 +02:00
if txErr != nil {
2022-08-09 01:21:46 +02:00
return nil , txErr
}
session , err := GetSessionById ( ctx , newSessionId )
if err != nil {
return nil , err
}
2022-08-24 11:14:16 +02:00
update := ModelUpdate {
2022-08-09 01:21:46 +02:00
Sessions : [ ] * SessionType { session } ,
}
if activate {
update . ActiveSessionId = newSessionId
2022-07-15 03:39:40 +02:00
}
2022-08-09 01:21:46 +02:00
return update , nil
2022-07-08 22:23:45 +02:00
}
2022-08-30 01:31:06 +02:00
func SetActiveSessionId ( ctx context . Context , sessionId string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "cannot switch to session, not found" )
}
query = ` UPDATE client SET activesessionid = ? `
tx . ExecWrap ( query , sessionId )
return nil
} )
return txErr
}
2022-09-25 09:26:33 +02:00
func SetWinSize ( ctx context . Context , winSize ClientWinSizeType ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE client SET winsize = ? `
tx . ExecWrap ( query , quickJson ( winSize ) )
return nil
} )
return txErr
}
2022-07-08 22:23:45 +02:00
func containsStr ( strs [ ] string , testStr string ) bool {
for _ , s := range strs {
if s == testStr {
return true
}
}
return false
}
2022-07-13 06:51:17 +02:00
func fmtUniqueName ( name string , defaultFmtStr string , startIdx int , strs [ ] string ) string {
var fmtStr string
if name != "" {
if ! containsStr ( strs , name ) {
return name
}
fmtStr = name + "-%d"
startIdx = 2
} else {
fmtStr = defaultFmtStr
}
if strings . Index ( fmtStr , "%d" ) == - 1 {
panic ( "invalid fmtStr: " + fmtStr )
}
for {
testName := fmt . Sprintf ( fmtStr , startIdx )
if containsStr ( strs , testName ) {
startIdx ++
continue
}
return testName
}
2022-07-13 01:10:46 +02:00
}
2022-08-27 02:17:33 +02:00
func InsertScreen ( ctx context . Context , sessionId string , origScreenName string , activate bool ) ( UpdatePacket , error ) {
2022-07-13 06:51:17 +02:00
var newScreenId string
2022-07-08 22:23:45 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-07-13 06:51:17 +02:00
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "cannot create screen, no session found" )
2022-07-08 22:23:45 +02:00
}
2022-08-24 11:14:16 +02:00
remoteId := tx . GetString ( ` SELECT remoteid FROM remote WHERE remotealias = ? ` , LocalRemoteAlias )
if remoteId == "" {
return fmt . Errorf ( "cannot create screen, no local remote found" )
}
newWindowId := txCreateWindow ( tx , sessionId , RemotePtrType { RemoteId : remoteId } )
2022-07-13 06:51:17 +02:00
maxScreenIdx := tx . GetInt ( ` SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? ` , sessionId )
screenNames := tx . SelectStrings ( ` SELECT name FROM screen WHERE sessionid = ? ` , sessionId )
2022-08-27 02:17:33 +02:00
screenName := fmtUniqueName ( origScreenName , "s%d" , maxScreenIdx + 1 , screenNames )
2022-09-21 02:37:49 +02:00
newScreenId = scbase . GenSCUUID ( )
2022-08-24 22:21:54 +02:00
query = ` INSERT INTO screen (sessionid, screenid, name, activewindowid, screenidx, screenopts, ownerid, sharemode) VALUES (?, ?, ?, ?, ?, ?, '', 'local') `
2022-07-13 06:51:17 +02:00
tx . ExecWrap ( query , sessionId , newScreenId , screenName , newWindowId , maxScreenIdx + 1 , ScreenOptsType { } )
layout := LayoutType { Type : LayoutFull }
2022-10-11 10:11:04 +02:00
query = ` INSERT INTO screen_window (sessionid, screenid, windowid, name, layout, selectedline, anchor, focustype) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `
2022-11-22 22:52:31 +01:00
tx . ExecWrap ( query , sessionId , newScreenId , newWindowId , DefaultScreenWindowName , layout , 0 , SWAnchorType { } , "input" )
2022-07-15 03:39:40 +02:00
if activate {
query = ` UPDATE session SET activescreenid = ? WHERE sessionid = ? `
tx . ExecWrap ( query , newScreenId , sessionId )
}
2022-07-08 22:23:45 +02:00
return nil
} )
2022-07-15 10:57:45 +02:00
newScreen , err := GetScreenById ( ctx , sessionId , newScreenId )
if err != nil {
2022-07-16 02:53:23 +02:00
return nil , err
2022-07-15 10:57:45 +02:00
}
update , session := MakeSingleSessionUpdate ( sessionId )
if activate {
session . ActiveScreenId = newScreenId
}
session . Screens = append ( session . Screens , newScreen )
2022-07-16 02:53:23 +02:00
return update , txErr
2022-07-01 23:07:13 +02:00
}
2022-07-02 22:31:56 +02:00
2022-07-15 03:39:40 +02:00
func GetScreenById ( ctx context . Context , sessionId string , screenId string ) ( * ScreenType , error ) {
var rtnScreen * ScreenType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM screen WHERE sessionid = ? AND screenid = ? `
var screen ScreenType
found := tx . GetWrap ( & screen , query , sessionId , screenId )
if ! found {
return nil
}
rtnScreen = & screen
query = ` SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ? `
tx . SelectWrap ( & screen . Windows , query , sessionId , screenId )
2022-07-15 10:57:45 +02:00
screen . Full = true
2022-07-15 03:39:40 +02:00
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtnScreen , nil
}
2022-08-24 11:14:16 +02:00
func txCreateWindow ( tx * TxWrap , sessionId string , curRemote RemotePtrType ) string {
w := & WindowType {
2022-09-21 02:01:25 +02:00
SessionId : sessionId ,
2022-09-21 02:37:49 +02:00
WindowId : scbase . GenSCUUID ( ) ,
2022-09-21 02:01:25 +02:00
CurRemote : curRemote ,
NextLineNum : 1 ,
WinOpts : WindowOptsType { } ,
ShareMode : ShareModeLocal ,
ShareOpts : WindowShareOptsType { } ,
2022-08-24 11:14:16 +02:00
}
wmap := w . ToMap ( )
2022-09-21 02:01:25 +02:00
query := ` INSERT INTO window ( sessionid , windowid , curremoteownerid , curremoteid , curremotename , nextlinenum , winopts , ownerid , sharemode , shareopts )
VALUES ( : sessionid , : windowid , : curremoteownerid , : curremoteid , : curremotename , : nextlinenum , : winopts , : ownerid , : sharemode , : shareopts ) `
2022-08-24 11:14:16 +02:00
tx . NamedExecWrap ( query , wmap )
return w . WindowId
2022-07-12 22:50:44 +02:00
}
2022-09-21 02:37:49 +02:00
func FindLineIdByArg ( ctx context . Context , sessionId string , windowId string , lineArg string ) ( string , error ) {
var lineId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
lineNum , err := strconv . Atoi ( lineArg )
if err == nil {
// valid linenum
query := ` SELECT lineid FROM line WHERE sessionid = ? AND windowid = ? AND linenum = ? `
lineId = tx . GetString ( query , sessionId , windowId , lineNum )
} else if len ( lineArg ) == 8 {
// prefix id string match
query := ` SELECT lineid FROM line WHERE sessionid = ? AND windowid = ? AND substr(lineid, 1, 8) = ? `
lineId = tx . GetString ( query , sessionId , windowId , lineArg )
} else {
// id match
query := ` SELECT * FROM line WHERE sessionid = ? AND windowid = ? AND lineid = ? `
lineId = tx . GetString ( query , sessionId , windowId , lineArg )
}
return nil
} )
if txErr != nil {
return "" , txErr
}
return lineId , nil
}
2022-10-28 07:00:10 +02:00
func GetLineCmdByLineId ( ctx context . Context , sessionId string , windowId string , lineId string ) ( * LineType , * CmdType , error ) {
2022-09-21 02:37:49 +02:00
var lineRtn * LineType
var cmdRtn * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT windowid FROM window WHERE sessionid = ? AND windowid = ? `
if ! tx . Exists ( query , sessionId , windowId ) {
return fmt . Errorf ( "window not found" )
}
var lineVal LineType
query = ` SELECT * FROM line WHERE sessionid = ? AND windowid = ? AND lineid = ? `
found := tx . GetWrap ( & lineVal , query , sessionId , windowId , lineId )
if ! found {
return nil
}
lineRtn = & lineVal
if lineVal . CmdId != "" {
query = ` SELECT * FROM cmd WHERE sessionid = ? AND cmdid = ? `
m := tx . GetMap ( query , sessionId , lineVal . CmdId )
cmdRtn = CmdFromMap ( m )
}
return nil
} )
if txErr != nil {
return nil , nil , txErr
}
return lineRtn , cmdRtn , nil
}
2022-10-28 07:00:10 +02:00
func GetLineCmdByCmdId ( ctx context . Context , sessionId string , windowId string , cmdId string ) ( * LineType , * CmdType , error ) {
var lineRtn * LineType
var cmdRtn * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT windowid FROM window WHERE sessionid = ? AND windowid = ? `
if ! tx . Exists ( query , sessionId , windowId ) {
return fmt . Errorf ( "window not found" )
}
var lineVal LineType
query = ` SELECT * FROM line WHERE sessionid = ? AND windowid = ? AND cmdid = ? `
found := tx . GetWrap ( & lineVal , query , sessionId , windowId , cmdId )
if ! found {
return nil
}
lineRtn = & lineVal
query = ` SELECT * FROM cmd WHERE sessionid = ? AND cmdid = ? `
m := tx . GetMap ( query , sessionId , cmdId )
cmdRtn = CmdFromMap ( m )
return nil
} )
if txErr != nil {
return nil , nil , txErr
}
return lineRtn , cmdRtn , nil
}
2022-07-08 06:39:25 +02:00
func InsertLine ( ctx context . Context , line * LineType , cmd * CmdType ) error {
2022-07-02 22:31:56 +02:00
if line == nil {
return fmt . Errorf ( "line cannot be nil" )
}
2022-08-16 21:08:26 +02:00
if line . LineId == "" {
return fmt . Errorf ( "line must have lineid set" )
2022-07-02 22:31:56 +02:00
}
2022-09-21 02:01:25 +02:00
if line . LineNum != 0 {
return fmt . Errorf ( "line should not hage linenum set" )
}
2022-07-02 22:31:56 +02:00
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT windowid FROM window WHERE sessionid = ? AND windowid = ? `
2022-09-21 02:37:49 +02:00
if ! tx . Exists ( query , line . SessionId , line . WindowId ) {
2022-07-02 22:31:56 +02:00
return fmt . Errorf ( "window not found, cannot insert line[%s/%s]" , line . SessionId , line . WindowId )
}
2022-09-21 02:01:25 +02:00
query = ` SELECT nextlinenum FROM window WHERE sessionid = ? AND windowid = ? `
nextLineNum := tx . GetInt ( query , line . SessionId , line . WindowId )
line . LineNum = int64 ( nextLineNum )
2022-10-27 09:33:50 +02:00
query = ` INSERT INTO line ( sessionid , windowid , userid , lineid , ts , linenum , linenumtemp , linelocal , linetype , text , cmdid , ephemeral , contentheight )
VALUES ( : sessionid , : windowid , : userid , : lineid , : ts , : linenum , : linenumtemp , : linelocal , : linetype , : text , : cmdid , : ephemeral , : contentheight ) `
2022-07-02 22:31:56 +02:00
tx . NamedExecWrap ( query , line )
2022-09-21 02:01:25 +02:00
query = ` UPDATE window SET nextlinenum = ? WHERE sessionid = ? AND windowid = ? `
tx . ExecWrap ( query , nextLineNum + 1 , line . SessionId , line . WindowId )
2022-07-08 06:39:25 +02:00
if cmd != nil {
2022-10-07 03:33:54 +02:00
cmd . OrigTermOpts = cmd . TermOpts
2022-07-08 06:39:25 +02:00
cmdMap := cmd . ToMap ( )
query = `
2022-10-27 09:33:50 +02:00
INSERT INTO cmd ( sessionid , cmdid , remoteownerid , remoteid , remotename , cmdstr , remotestate , termopts , origtermopts , status , startpk , donepk , rtnstate , runout )
VALUES ( : sessionid , : cmdid , : remoteownerid , : remoteid , : remotename , : cmdstr , : remotestate , : termopts , : origtermopts , : status , : startpk , : donepk , : rtnstate , : runout )
2022-07-07 09:10:37 +02:00
`
2022-07-08 06:39:25 +02:00
tx . NamedExecWrap ( query , cmdMap )
}
2022-07-07 09:10:37 +02:00
return nil
} )
}
2022-07-07 22:26:46 +02:00
func GetCmdById ( ctx context . Context , sessionId string , cmdId string ) ( * CmdType , error ) {
var cmd * CmdType
err := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM cmd WHERE sessionid = ? AND cmdid = ? `
m := tx . GetMap ( query , sessionId , cmdId )
cmd = CmdFromMap ( m )
return nil
} )
2022-07-07 09:10:37 +02:00
if err != nil {
return nil , err
}
2022-07-07 22:26:46 +02:00
return cmd , nil
2022-07-07 09:10:37 +02:00
}
2022-07-08 01:29:14 +02:00
2022-11-27 23:12:15 +01:00
func HasDonePk ( ctx context . Context , ck base . CommandKey ) ( bool , error ) {
var found bool
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
found = tx . Exists ( ` SELECT sessionid FROM cmd WHERE sessionid = ? AND cmdid = ? AND donepk is NOT NULL ` , ck . GetSessionId ( ) , ck . GetCmdId ( ) )
return nil
} )
if txErr != nil {
return false , txErr
}
return found , nil
}
2022-10-12 08:11:43 +02:00
func UpdateCmdDonePk ( ctx context . Context , donePk * packet . CmdDonePacketType ) ( * ModelUpdate , error ) {
2022-07-08 01:29:14 +02:00
if donePk == nil || donePk . CK . IsEmpty ( ) {
2022-08-20 02:14:53 +02:00
return nil , fmt . Errorf ( "invalid cmddone packet (no ck)" )
2022-07-08 01:29:14 +02:00
}
2022-08-20 02:14:53 +02:00
var rtnCmd * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-07-08 01:29:14 +02:00
query := ` UPDATE cmd SET status = ?, donepk = ? WHERE sessionid = ? AND cmdid = ? `
tx . ExecWrap ( query , CmdStatusDone , quickJson ( donePk ) , donePk . CK . GetSessionId ( ) , donePk . CK . GetCmdId ( ) )
2022-08-20 02:14:53 +02:00
var err error
rtnCmd , err = GetCmdById ( tx . Context ( ) , donePk . CK . GetSessionId ( ) , donePk . CK . GetCmdId ( ) )
if err != nil {
return err
}
2022-07-08 01:29:14 +02:00
return nil
} )
2022-08-20 02:14:53 +02:00
if txErr != nil {
return nil , txErr
}
if rtnCmd == nil {
return nil , fmt . Errorf ( "cmd data not found for ck[%s]" , donePk . CK )
}
2022-10-12 08:11:43 +02:00
return & ModelUpdate { Cmd : rtnCmd } , nil
2022-07-08 01:29:14 +02:00
}
func AppendCmdErrorPk ( ctx context . Context , errPk * packet . CmdErrorPacketType ) error {
if errPk == nil || errPk . CK . IsEmpty ( ) {
return fmt . Errorf ( "invalid cmderror packet (no ck)" )
}
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE cmd SET runout = json_insert(runout, '$[#]', ?) WHERE sessionid = ? AND cmdid = ? `
tx . ExecWrap ( query , quickJson ( errPk ) , errPk . CK . GetSessionId ( ) , errPk . CK . GetCmdId ( ) )
return nil
} )
}
func HangupAllRunningCmds ( ctx context . Context ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE cmd SET status = ? WHERE status = ? `
tx . ExecWrap ( query , CmdStatusHangup , CmdStatusRunning )
return nil
} )
}
func HangupRunningCmdsByRemoteId ( ctx context . Context , remoteId string ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE cmd SET status = ? WHERE status = ? AND remoteid = ? `
tx . ExecWrap ( query , CmdStatusHangup , CmdStatusRunning , remoteId )
return nil
} )
}
2022-07-15 03:39:40 +02:00
2022-11-27 23:12:15 +01:00
func HangupCmd ( ctx context . Context , ck base . CommandKey ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE cmd SET status = ? WHERE sessionid = ? AND cmdid = ? `
tx . ExecWrap ( query , CmdStatusHangup , ck . GetSessionId ( ) , ck . GetCmdId ( ) )
return nil
} )
}
2022-07-15 10:57:45 +02:00
func getNextId ( ids [ ] string , delId string ) string {
if len ( ids ) == 0 {
return ""
}
if len ( ids ) == 1 {
if ids [ 0 ] == delId {
return ""
}
return ids [ 0 ]
}
for idx := 0 ; idx < len ( ids ) ; idx ++ {
if ids [ idx ] == delId {
var rtnIdx int
if idx == len ( ids ) - 1 {
rtnIdx = idx - 1
} else {
rtnIdx = idx + 1
}
return ids [ rtnIdx ]
}
}
return ids [ 0 ]
}
2022-07-16 02:53:23 +02:00
func SwitchScreenById ( ctx context . Context , sessionId string , screenId string ) ( UpdatePacket , error ) {
2022-07-15 03:39:40 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? `
if ! tx . Exists ( query , sessionId , screenId ) {
return fmt . Errorf ( "cannot switch to screen, screen=%s does not exist in session=%s" , screenId , sessionId )
}
query = ` UPDATE session SET activescreenid = ? WHERE sessionid = ? `
tx . ExecWrap ( query , screenId , sessionId )
return nil
} )
2022-07-15 10:57:45 +02:00
update , session := MakeSingleSessionUpdate ( sessionId )
session . ActiveScreenId = screenId
2022-07-16 02:53:23 +02:00
return update , txErr
2022-07-15 03:39:40 +02:00
}
2022-07-15 10:57:45 +02:00
func CleanWindows ( ) {
}
2022-07-16 02:53:23 +02:00
func DeleteScreen ( ctx context . Context , sessionId string , screenId string ) ( UpdatePacket , error ) {
2022-07-15 10:57:45 +02:00
var newActiveScreenId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
isActive := tx . Exists ( ` SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ? ` , sessionId , screenId )
if isActive {
screenIds := tx . SelectStrings ( ` SELECT screenid FROM screen WHERE sessionid = ? ORDER BY screenidx ` , sessionId )
nextId := getNextId ( screenIds , screenId )
tx . ExecWrap ( ` UPDATE session SET activescreenid = ? WHERE sessionid = ? ` , nextId , sessionId )
newActiveScreenId = nextId
}
query := ` DELETE FROM screen_window WHERE sessionid = ? AND screenid = ? `
tx . ExecWrap ( query , sessionId , screenId )
query = ` DELETE FROM screen WHERE sessionid = ? AND screenid = ? `
tx . ExecWrap ( query , sessionId , screenId )
return nil
} )
if txErr != nil {
2022-07-16 02:53:23 +02:00
return nil , txErr
2022-07-15 10:57:45 +02:00
}
go CleanWindows ( )
update , session := MakeSingleSessionUpdate ( sessionId )
session . ActiveScreenId = newActiveScreenId
session . Screens = append ( session . Screens , & ScreenType { SessionId : sessionId , ScreenId : screenId , Remove : true } )
2022-07-16 02:53:23 +02:00
return update , nil
2022-07-15 10:57:45 +02:00
}
2022-07-16 02:37:32 +02:00
2022-10-17 08:51:04 +02:00
func GetRemoteState ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) ( * packet . ShellState , error ) {
2022-11-28 09:13:00 +01:00
var state * packet . ShellState
2022-07-16 02:37:32 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-11-28 09:13:00 +01:00
ri , err := GetRemoteInstance ( tx . Context ( ) , sessionId , windowId , remotePtr )
if err != nil {
return err
}
if ri == nil {
2022-07-16 02:37:32 +02:00
return nil
}
2022-11-28 09:13:00 +01:00
state , err = GetFullState ( tx . Context ( ) , ri . StateBaseHash , ri . StateDiffHashArr )
if err != nil {
return err
}
2022-07-16 02:37:32 +02:00
return nil
} )
2022-11-28 09:13:00 +01:00
if txErr != nil {
return nil , txErr
}
return state , nil
2022-07-16 02:37:32 +02:00
}
2022-08-09 23:24:57 +02:00
2022-08-24 11:14:16 +02:00
func validateSessionWindow ( tx * TxWrap , sessionId string , windowId string ) error {
if windowId == "" {
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "no session found" )
}
return nil
} else {
2022-08-09 23:24:57 +02:00
query := ` SELECT windowid FROM window WHERE sessionid = ? AND windowid = ? `
if ! tx . Exists ( query , sessionId , windowId ) {
2022-08-24 11:14:16 +02:00
return fmt . Errorf ( "no window found" )
}
return nil
}
}
2022-11-28 09:13:00 +01:00
func GetRemoteInstance ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) ( * RemoteInstance , error ) {
if remotePtr . IsSessionScope ( ) {
windowId = ""
}
var ri * RemoteInstance
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM remote_instance WHERE sessionid = ? AND windowid = ? AND remoteownerid = ? AND remoteid = ? AND name = ? `
m := tx . GetMap ( query , sessionId , windowId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
ri = RIFromMap ( m )
return nil
} )
if txErr != nil {
return nil , txErr
}
return ri , nil
}
// internal function for UpdateRemoteState
func updateRIWithState ( ctx context . Context , ri * RemoteInstance , stateBase * packet . ShellState , stateDiff * packet . ShellStateDiff ) error {
if stateBase != nil {
ri . StateBaseHash = stateBase . GetHashVal ( false )
err := StoreStateBase ( ctx , stateBase )
if err != nil {
return err
}
} else if stateDiff != nil {
ri . StateBaseHash = stateDiff . BaseHash
ri . StateDiffHashArr = append ( stateDiff . DiffHashArr , stateDiff . GetHashVal ( false ) )
err := StoreStateDiff ( ctx , stateDiff )
if err != nil {
return err
}
}
return nil
}
// TODO - statediff
func UpdateRemoteState ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType , feState FeStateType , stateBase * packet . ShellState , stateDiff * packet . ShellStateDiff ) ( * RemoteInstance , error ) {
if stateBase == nil && stateDiff == nil {
return nil , fmt . Errorf ( "UpdateRemoteState, must set state or diff" )
}
if stateBase != nil && stateDiff != nil {
return nil , fmt . Errorf ( "UpdateRemoteState, cannot set state and diff" )
}
2022-08-24 11:14:16 +02:00
if remotePtr . IsSessionScope ( ) {
windowId = ""
}
2022-10-17 08:51:04 +02:00
var ri * RemoteInstance
2022-08-24 11:14:16 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
err := validateSessionWindow ( tx , sessionId , windowId )
if err != nil {
2022-11-28 09:13:00 +01:00
return fmt . Errorf ( "cannot update remote instance state: %w" , err )
2022-08-09 23:24:57 +02:00
}
2022-08-24 22:21:54 +02:00
query := ` SELECT * FROM remote_instance WHERE sessionid = ? AND windowid = ? AND remoteownerid = ? AND remoteid = ? AND name = ? `
2022-10-17 08:51:04 +02:00
m := tx . GetMap ( query , sessionId , windowId , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name )
ri = RIFromMap ( m )
if ri == nil {
ri = & RemoteInstance {
2022-09-21 02:37:49 +02:00
RIId : scbase . GenSCUUID ( ) ,
2022-08-24 22:21:54 +02:00
Name : remotePtr . Name ,
SessionId : sessionId ,
WindowId : windowId ,
RemoteOwnerId : remotePtr . OwnerId ,
RemoteId : remotePtr . RemoteId ,
2022-11-28 09:13:00 +01:00
FeState : feState ,
}
err = updateRIWithState ( tx . Context ( ) , ri , stateBase , stateDiff )
if err != nil {
return err
2022-08-09 23:24:57 +02:00
}
2022-11-28 09:13:00 +01:00
query = ` INSERT INTO remote_instance ( riid , name , sessionid , windowid , remoteownerid , remoteid , festate , statebasehash , statediffhasharr )
VALUES ( : riid , : name , : sessionid , : windowid , : remoteownerid , : remoteid , : festate , : statebasehash , : statediffhasharr ) `
2022-10-17 08:51:04 +02:00
tx . NamedExecWrap ( query , ri . ToMap ( ) )
2022-08-09 23:24:57 +02:00
return nil
2022-11-28 09:13:00 +01:00
} else {
query = ` UPDATE remote_instance SET festate = ? WHERE riid = ? `
ri . FeState = feState
err = updateRIWithState ( tx . Context ( ) , ri , stateBase , stateDiff )
if err != nil {
return err
}
tx . ExecWrap ( query , quickJson ( ri . FeState ) , ri . RIId )
return nil
2022-08-09 23:24:57 +02:00
}
} )
2022-10-17 08:51:04 +02:00
return ri , txErr
2022-08-09 23:24:57 +02:00
}
2022-08-17 21:24:09 +02:00
2022-08-24 11:14:16 +02:00
func UpdateCurRemote ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) error {
2022-08-17 21:24:09 +02:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT windowid FROM window WHERE sessionid = ? AND windowid = ? `
if ! tx . Exists ( query , sessionId , windowId ) {
2022-08-24 11:14:16 +02:00
return fmt . Errorf ( "cannot update curremote: no window found" )
2022-08-17 21:24:09 +02:00
}
2022-08-24 22:21:54 +02:00
query = ` UPDATE window SET curremoteownerid = ?, curremoteid = ?, curremotename = ? WHERE sessionid = ? AND windowid = ? `
tx . ExecWrap ( query , remotePtr . OwnerId , remotePtr . RemoteId , remotePtr . Name , sessionId , windowId )
2022-08-17 21:24:09 +02:00
return nil
} )
return txErr
}
2022-08-26 22:12:17 +02:00
func reorderStrings ( strs [ ] string , toMove string , newIndex int ) [ ] string {
if toMove == "" {
return strs
}
var newStrs [ ] string
if newIndex < 0 {
newStrs = append ( newStrs , toMove )
}
for _ , sval := range strs {
if len ( newStrs ) == newIndex {
newStrs = append ( newStrs , toMove )
}
if sval != toMove {
newStrs = append ( newStrs , sval )
}
}
if newIndex >= len ( newStrs ) {
newStrs = append ( newStrs , toMove )
}
return newStrs
}
func ReIndexSessions ( ctx context . Context , sessionId string , newIndex int ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-27 01:21:19 +02:00
query := ` SELECT sessionid FROM session ORDER BY sessionidx, name, sessionid `
2022-08-26 22:12:17 +02:00
ids := tx . SelectStrings ( query )
if sessionId != "" {
ids = reorderStrings ( ids , sessionId , newIndex )
}
2022-08-27 01:21:19 +02:00
query = ` UPDATE session SET sessionid = ? WHERE sessionid = ? `
2022-08-26 22:12:17 +02:00
for idx , id := range ids {
tx . ExecWrap ( query , id , idx + 1 )
}
return nil
} )
return txErr
}
func SetSessionName ( ctx context . Context , sessionId string , name string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-08-27 01:21:19 +02:00
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
2022-08-26 22:12:17 +02:00
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "session does not exist" )
}
2022-08-27 02:17:33 +02:00
query = ` SELECT sessionid FROM session WHERE name = ? `
dupSessionId := tx . GetString ( query , name )
if dupSessionId == sessionId {
return nil
}
2022-08-27 02:51:28 +02:00
if dupSessionId != "" {
2022-08-27 02:17:33 +02:00
return fmt . Errorf ( "invalid duplicate session name '%s'" , name )
}
2022-08-27 01:21:19 +02:00
query = ` UPDATE session SET name = ? WHERE sessionid = ? `
2022-08-26 22:12:17 +02:00
tx . ExecWrap ( query , name , sessionId )
return nil
} )
return txErr
}
2022-08-27 02:51:28 +02:00
func SetScreenName ( ctx context . Context , sessionId string , screenId string , name string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? `
if ! tx . Exists ( query , sessionId , screenId ) {
return fmt . Errorf ( "screen does not exist" )
}
query = ` SELECT screenid FROM screen WHERE sessionid = ? AND name = ? `
dupScreenId := tx . GetString ( query , sessionId , name )
if dupScreenId == screenId {
return nil
}
if dupScreenId != "" {
return fmt . Errorf ( "invalid duplicate screen name '%s'" , name )
}
query = ` UPDATE screen SET name = ? WHERE sessionid = ? AND screenid = ? `
tx . ExecWrap ( query , name , sessionId , screenId )
return nil
} )
return txErr
}
2022-08-27 06:44:18 +02:00
func SetScreenOpts ( ctx context . Context , sessionId string , screenId string , opts * ScreenOptsType ) error {
if opts == nil {
return fmt . Errorf ( "invalid screen opts cannot be nil" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? `
if ! tx . Exists ( query , sessionId , screenId ) {
return fmt . Errorf ( "screen does not exist" )
}
query = ` UPDATE screen SET screenopts = ? WHERE sessionid = ? AND screenid = ? `
tx . ExecWrap ( query , opts , sessionId , screenId )
return nil
} )
return txErr
}
2022-08-27 07:01:29 +02:00
func ClearWindow ( ctx context . Context , sessionId string , windowId string ) ( * ModelUpdate , error ) {
var lineIds [ ] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT windowid FROM window WHERE sessionid = ? AND windowid = ? `
if ! tx . Exists ( query , sessionId , windowId ) {
return fmt . Errorf ( "window does not exist" )
}
query = ` SELECT lineid FROM line WHERE sessionid = ? AND windowid = ? `
lineIds = tx . SelectStrings ( query , sessionId , windowId )
query = ` DELETE FROM line WHERE sessionid = ? AND windowid = ? `
tx . ExecWrap ( query , sessionId , windowId )
2022-09-21 02:01:25 +02:00
query = ` UPDATE window SET nextlinenum = 1 WHERE sessionid = ? AND windowid = ? `
tx . ExecWrap ( query , sessionId , windowId )
2022-08-27 07:01:29 +02:00
return nil
} )
if txErr != nil {
return nil , txErr
}
win , err := GetWindowById ( ctx , sessionId , windowId )
if err != nil {
return nil , err
}
for _ , lineId := range lineIds {
line := & LineType {
SessionId : sessionId ,
WindowId : windowId ,
LineId : lineId ,
Remove : true ,
}
win . Lines = append ( win . Lines , line )
}
return & ModelUpdate { Window : win } , nil
}
2022-09-06 05:08:59 +02:00
func GetRunningWindowCmds ( ctx context . Context , sessionId string , windowId string ) ( [ ] * CmdType , error ) {
var rtn [ ] * CmdType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * from cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE sessionid = ? AND windowid = ?) AND status = ? `
cmdMaps := tx . SelectMaps ( query , sessionId , windowId , CmdStatusRunning )
for _ , m := range cmdMaps {
rtn = append ( rtn , CmdFromMap ( m ) )
}
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
func UpdateCmdTermOpts ( ctx context . Context , sessionId string , cmdId string , termOpts TermOpts ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE cmd SET termopts = ? WHERE sessionid = ? AND cmdid = ? `
tx . ExecWrap ( query , termOpts , sessionId , cmdId )
return nil
} )
return txErr
}
2022-09-13 21:06:12 +02:00
func DeleteSession ( ctx context . Context , sessionId string ) error {
return nil
}
2022-09-20 23:15:20 +02:00
func GetSessionStats ( ctx context . Context , sessionId string ) ( * SessionStatsType , error ) {
rtn := & SessionStatsType { SessionId : sessionId }
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "not found" )
}
query = ` SELECT count(*) FROM screen WHERE sessionid = ? `
rtn . NumScreens = tx . GetInt ( query , sessionId )
query = ` SELECT count(*) FROM window WHERE sessionid = ? `
rtn . NumWindows = tx . GetInt ( query , sessionId )
query = ` SELECT count(*) FROM line WHERE sessionid = ? `
rtn . NumLines = tx . GetInt ( query , sessionId )
query = ` SELECT count(*) FROM cmd WHERE sessionid = ? `
rtn . NumCmds = tx . GetInt ( query , sessionId )
return nil
} )
if txErr != nil {
return nil , txErr
}
diskSize , err := SessionDiskSize ( sessionId )
if err != nil {
return nil , err
}
rtn . DiskStats = diskSize
return rtn , nil
}
2022-10-03 03:52:55 +02:00
const (
RemoteField_Alias = "alias" // string
RemoteField_ConnectMode = "connectmode" // string
RemoteField_AutoInstall = "autoinstall" // bool
RemoteField_SSHKey = "sshkey" // string
RemoteField_SSHPassword = "sshpassword" // string
RemoteField_Color = "color" // string
)
// editMap: alias, connectmode, autoinstall, sshkey, color, sshpassword (from constants)
func UpdateRemote ( ctx context . Context , remoteId string , editMap map [ string ] interface { } ) ( * RemoteType , error ) {
var rtn * RemoteType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT remoteid FROM remote WHERE remoteid = ? `
if ! tx . Exists ( query , remoteId ) {
return fmt . Errorf ( "remote not found" )
}
if alias , found := editMap [ RemoteField_Alias ] ; found {
query = ` SELECT remoteid FROM remote WHERE remotealias = ? AND remoteid <> ? `
2022-10-04 04:04:48 +02:00
if alias != "" && tx . Exists ( query , alias , remoteId ) {
2022-10-03 03:52:55 +02:00
return fmt . Errorf ( "remote has duplicate alias, cannot update" )
}
query = ` UPDATE remote SET remotealias = ? WHERE remoteid = ? `
tx . ExecWrap ( query , alias , remoteId )
}
if mode , found := editMap [ RemoteField_ConnectMode ] ; found {
query = ` UPDATE remote SET connectmode = ? WHERE remoteid = ? `
tx . ExecWrap ( query , mode , remoteId )
}
if autoInstall , found := editMap [ RemoteField_AutoInstall ] ; found {
query = ` UPDATE remote SET autoinstall = ? WHERE remoteid = ? `
tx . ExecWrap ( query , autoInstall , remoteId )
}
if sshKey , found := editMap [ RemoteField_SSHKey ] ; found {
query = ` UPDATE remote SET sshopts = json_set(sshopts, '$.sshidentity', ?) WHERE remoteid = ? `
tx . ExecWrap ( query , sshKey , remoteId )
}
if sshPassword , found := editMap [ RemoteField_SSHPassword ] ; found {
query = ` UPDATE remote SET sshopts = json_set(sshopts, '$.sshpassword', ?) WHERE remoteid = ? `
tx . ExecWrap ( query , sshPassword , remoteId )
}
if color , found := editMap [ RemoteField_Color ] ; found {
query = ` UPDATE remote SET remoteopts = json_set(remoteopts, '$.color', ?) WHERE remoteid = ? `
tx . ExecWrap ( query , color , remoteId )
}
var err error
rtn , err = GetRemoteById ( tx . Context ( ) , remoteId )
if err != nil {
return err
}
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-10-07 03:33:54 +02:00
const (
2022-10-11 10:11:04 +02:00
SWField_AnchorLine = "anchorline" // int
SWField_AnchorOffset = "anchoroffset" // int
2022-10-07 10:08:03 +02:00
SWField_SelectedLine = "selectedline" // int
2022-10-11 10:11:04 +02:00
SWField_Focus = "focustype" // string
2022-10-07 03:33:54 +02:00
)
func UpdateScreenWindow ( ctx context . Context , sessionId string , screenId string , windowId string , editMap map [ string ] interface { } ) ( * ScreenWindowType , error ) {
var rtn * ScreenWindowType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid FROM screen_window WHERE sessionid = ? AND screenid = ? AND windowid = ? `
if ! tx . Exists ( query , sessionId , screenId , windowId ) {
return fmt . Errorf ( "screen-window not found" )
}
2022-10-11 10:11:04 +02:00
if anchorLine , found := editMap [ SWField_AnchorLine ] ; found {
query = ` UPDATE screen_window SET anchor = json_set(anchor, '$.anchorline', ?) WHERE sessionid = ? AND screenid = ? AND windowid = ? `
tx . ExecWrap ( query , anchorLine , sessionId , screenId , windowId )
}
if anchorOffset , found := editMap [ SWField_AnchorOffset ] ; found {
query = ` UPDATE screen_window SET anchor = json_set(anchor, '$.anchoroffset', ?) WHERE sessionid = ? AND screenid = ? AND windowid = ? `
tx . ExecWrap ( query , anchorOffset , sessionId , screenId , windowId )
2022-10-07 03:33:54 +02:00
}
2022-10-07 10:08:03 +02:00
if sline , found := editMap [ SWField_SelectedLine ] ; found {
query = ` UPDATE screen_window SET selectedline = ? WHERE sessionid = ? AND screenid = ? AND windowid = ? `
tx . ExecWrap ( query , sline , sessionId , screenId , windowId )
}
2022-10-11 10:11:04 +02:00
if focusType , found := editMap [ SWField_Focus ] ; found {
query = ` UPDATE screen_window SET focustype = ? WHERE sessionid = ? AND screenid = ? AND windowid = ? `
tx . ExecWrap ( query , focusType , sessionId , screenId , windowId )
}
2022-10-07 03:33:54 +02:00
var sw ScreenWindowType
query = ` SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ? AND windowid = ? `
found := tx . GetWrap ( & sw , query , sessionId , screenId , windowId )
if found {
rtn = & sw
}
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-10-07 08:58:38 +02:00
2022-10-07 10:08:03 +02:00
func GetScreenWindowByIds ( ctx context . Context , sessionId string , screenId string , windowId string ) ( * ScreenWindowType , error ) {
var rtn * ScreenWindowType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var sw ScreenWindowType
query := ` SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ? AND windowid = ? `
found := tx . GetWrap ( & sw , query , sessionId , screenId , windowId )
if found {
rtn = & sw
}
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-10-07 08:58:38 +02:00
func GetLineResolveItems ( ctx context . Context , sessionId string , windowId string ) ( [ ] ResolveItem , error ) {
var rtn [ ] ResolveItem
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT lineid as id, linenum as num FROM line WHERE sessionid = ? AND windowid = ? ORDER BY linenum `
tx . SelectWrap ( & rtn , query , sessionId , windowId )
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-10-12 08:11:43 +02:00
func UpdateSWsWithCmdFg ( ctx context . Context , sessionId string , cmdId string ) ( [ ] * ScreenWindowType , error ) {
var rtn [ ] * ScreenWindowType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid , screenid , windowid
FROM screen_window sw
WHERE
sessionid = ?
AND focustype = ' cmd - fg '
AND selectedline IN ( SELECT linenum
FROM line l
WHERE l . sessionid = sw . sessionid
AND l . windowid = sw . windowid
AND l . cmdid = ?
) `
var swKeys [ ] SWKey
tx . SelectWrap ( & swKeys , query , sessionId , cmdId )
if len ( swKeys ) == 0 {
return nil
}
for _ , key := range swKeys {
editMap := make ( map [ string ] interface { } )
editMap [ SWField_Focus ] = SWFocusInput
sw , err := UpdateScreenWindow ( tx . Context ( ) , key . SessionId , key . ScreenId , key . WindowId , editMap )
if err != nil {
return err
}
rtn = append ( rtn , sw )
}
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-11-28 09:13:00 +01:00
func StoreStateBase ( ctx context . Context , state * packet . ShellState ) error {
stateBase := & StateBase {
Version : state . Version ,
Ts : time . Now ( ) . UnixMilli ( ) ,
}
stateBase . BaseHash , stateBase . Data = state . EncodeAndHash ( )
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT basehash FROM state_base WHERE basehash = ? `
if tx . Exists ( query , stateBase . BaseHash ) {
return nil
}
query = ` INSERT INTO state_base (basehash, ts, version, data) VALUES (:basehash,:ts,:version,:data) `
tx . NamedExecWrap ( query , stateBase )
return nil
} )
if txErr != nil {
return txErr
}
return nil
}
func StoreStateDiff ( ctx context . Context , diff * packet . ShellStateDiff ) error {
stateDiff := & StateDiff {
BaseHash : diff . BaseHash ,
Ts : time . Now ( ) . UnixMilli ( ) ,
DiffHashArr : diff . DiffHashArr ,
}
stateDiff . DiffHash , stateDiff . Data = diff . EncodeAndHash ( )
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT basehash FROM state_base WHERE basehash = ? `
if stateDiff . BaseHash == "" || ! tx . Exists ( query , stateDiff . BaseHash ) {
return fmt . Errorf ( "cannot store statediff, basehash:%s does not exist" , stateDiff . BaseHash )
}
query = ` SELECT diffhash FROM state_diff WHERE diffhash = ? `
for idx , diffHash := range stateDiff . DiffHashArr {
if ! tx . Exists ( query , diffHash ) {
return fmt . Errorf ( "cannot store statediff, diffhash[%d]:%s does not exist" , idx , diffHash )
}
}
if tx . Exists ( query , stateDiff . DiffHash ) {
return nil
}
query = ` INSERT INTO state_diff (diffhash, ts, basehash, diffhasharr, data) VALUES (:diffhash,:ts,:basehash,:diffhasharr,:data) `
tx . NamedExecWrap ( query , stateDiff . ToMap ( ) )
return nil
} )
if txErr != nil {
return txErr
}
return nil
}
// returns error when not found
func GetFullState ( ctx context . Context , baseHash string , diffHashArr [ ] string ) ( * packet . ShellState , error ) {
var state * packet . ShellState
if baseHash == "" {
return nil , fmt . Errorf ( "invalid empty basehash" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var stateBase StateBase
query := ` SELECT * FROM state_base WHERE basehash = ? `
found := tx . GetWrap ( & stateBase , query , baseHash )
if ! found {
return fmt . Errorf ( "ShellState %s not found" , baseHash )
}
state = & packet . ShellState { }
err := state . DecodeShellState ( stateBase . Data )
if err != nil {
return err
}
for idx , diffHash := range diffHashArr {
query = ` SELECT * FROM state_diff WHERE diffhash = ? `
m := tx . GetMap ( query , diffHash )
stateDiff := StateDiffFromMap ( m )
if stateDiff == nil {
return fmt . Errorf ( "ShellStateDiff %s not found" , diffHash )
}
var ssDiff packet . ShellStateDiff
err = ssDiff . DecodeShellStateDiff ( stateDiff . Data )
if err != nil {
return err
}
newState , err := shexec . ApplyShellStateDiff ( * state , ssDiff )
if err != nil {
return fmt . Errorf ( "GetFullState, diff[%d]:%s: %v" , idx , diffHash , err )
}
state = & newState
}
return nil
} )
if txErr != nil {
return nil , txErr
}
if state == nil {
return nil , fmt . Errorf ( "ShellState not found" )
}
return state , nil
}