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-12-20 03:52:08 +01:00
const HistoryCols = "historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd, incognito"
2022-08-30 04:18:02 +02:00
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
2022-12-20 03:52:08 +01:00
( historyid , ts , userid , sessionid , screenid , windowid , lineid , cmdid , haderror , cmdstr , remoteownerid , remoteid , remotename , ismetacmd , incognito ) VALUES
( : historyid , : ts , : userid , : sessionid , : screenid , : windowid , : lineid , : cmdid , : haderror , : cmdstr , : remoteownerid , : remoteid , : remotename , : ismetacmd , : incognito ) `
2022-10-11 02:30:48 +02:00
tx . NamedExecWrap ( query , hitem . ToMap ( ) )
return nil
} )
return txErr
2022-08-11 21:07:41 +02:00
}
2022-12-20 03:52:08 +01:00
func IsIncognitoScreen ( ctx context . Context , sessionId string , screenId string ) ( bool , error ) {
var rtn bool
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT incognito FROM screen WHERE sessionid = ? AND screenid = ? `
tx . GetWrap ( & rtn , query , sessionId , screenId )
return nil
} )
return rtn , txErr
}
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-12-25 22:03:11 +01:00
// includes archived sessions
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-12-27 01:09:21 +01:00
query := ` SELECT * FROM session ORDER BY archived, sessionidx, archivedts `
2022-08-09 01:21:46 +02:00
tx . SelectWrap ( & rtn , query )
return nil
} )
if err != nil {
return nil , err
}
return rtn , nil
}
2022-12-27 01:09:21 +01:00
// does not include archived, finds lowest sessionidx (for resetting active session)
func GetFirstSessionId ( ctx context . Context ) ( string , error ) {
2022-08-27 01:21:19 +02:00
var rtn [ ] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-25 22:03:11 +01:00
query := ` SELECT sessionid from session WHERE NOT archived ORDER by sessionidx `
2022-08-27 01:21:19 +02:00
rtn = tx . SelectStrings ( query )
return nil
} )
if txErr != nil {
2022-12-27 01:09:21 +01:00
return "" , txErr
2022-08-27 01:21:19 +02:00
}
2022-12-27 01:09:21 +01:00
if len ( rtn ) == 0 {
return "" , nil
}
return rtn [ 0 ] , nil
2022-08-27 01:21:19 +02:00
}
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-12-27 01:09:21 +01:00
query := ` SELECT * FROM session ORDER BY archived, sessionidx, archivedts `
2022-07-12 22:50:44 +02:00
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
2022-12-25 22:21:48 +01:00
query = ` SELECT * FROM screen ORDER BY archived, screenidx, archivedts `
2022-07-13 06:51:17 +02:00
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-12-26 21:38:47 +01:00
// includes archived screens (does not include screen windows)
func GetBareSessionScreens ( ctx context . Context , sessionId string ) ( [ ] * ScreenType , error ) {
2022-07-15 03:39:40 +02:00
var rtn [ ] * ScreenType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-25 22:21:48 +01:00
query := ` SELECT * FROM screen WHERE sessionid = ? ORDER BY archived, screenidx, archivedts `
2022-12-24 00:56:29 +01:00
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-12-20 03:52:08 +01:00
newSessionId := scbase . GenPromptUUID ( )
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-12-25 22:21:48 +01:00
query := ` INSERT INTO session ( sessionid , name , activescreenid , sessionidx , notifynum , archived , archivedts , ownerid , sharemode , accesskey )
VALUES ( ? , ? , ' ' , ? , ? , 0 , 0 , ' ' , ' 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 {
2022-12-27 01:09:21 +01:00
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
2022-08-30 01:31:06 +02:00
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-12-24 00:56:29 +01:00
func GetActiveSessionId ( ctx context . Context ) ( string , error ) {
var rtnId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT activesessionid FROM client `
rtnId = tx . GetString ( query )
return nil
} )
return rtnId , 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-12-25 22:03:11 +01:00
query := ` SELECT sessionid FROM session WHERE sessionid = ? AND NOT archived `
2022-07-13 06:51:17 +02:00
if ! tx . Exists ( query , sessionId ) {
2022-12-27 01:09:21 +01:00
return fmt . Errorf ( "cannot create screen, no session found (or session archived)" )
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-12-25 22:03:11 +01:00
maxScreenIdx := tx . GetInt ( ` SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? AND NOT archived ` , sessionId )
2022-12-26 21:18:13 +01:00
var screenName string
if origScreenName == "" {
screenNames := tx . SelectStrings ( ` SELECT name FROM screen WHERE sessionid = ? AND NOT archived ` , sessionId )
screenName = fmtUniqueName ( "" , "s%d" , maxScreenIdx + 1 , screenNames )
} else {
screenName = origScreenName
}
2022-12-20 03:52:08 +01:00
newScreenId = scbase . GenPromptUUID ( )
2022-12-25 22:21:48 +01:00
query = ` INSERT INTO screen (sessionid, screenid, name, activewindowid, screenidx, screenopts, ownerid, sharemode, incognito, archived, archivedts) VALUES (?, ?, ?, ?, ?, ?, '', 'local', 0, 0, 0) `
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-12-27 01:09:21 +01:00
if txErr != nil {
return nil , txErr
}
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
}
2022-12-27 01:09:21 +01:00
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , err
2022-07-15 10:57:45 +02:00
}
2022-12-27 01:09:21 +01:00
bareSession . Screens = append ( bareSession . Screens , newScreen )
return ModelUpdate { Sessions : [ ] * SessionType { bareSession } } , nil
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-12-20 03:52:08 +01:00
WindowId : scbase . GenPromptUUID ( ) ,
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
2022-12-06 07:59:00 +01:00
query := ` SELECT lineid FROM line WHERE sessionid = ? AND windowid = ? AND lineid = ? `
2022-09-21 02:37:49 +02:00
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-12-28 08:12:27 +01:00
query = ` INSERT INTO line ( sessionid , windowid , userid , lineid , ts , linenum , linenumtemp , linelocal , linetype , text , cmdid , ephemeral , contentheight , star , archived )
VALUES ( : sessionid , : windowid , : userid , : lineid , : ts , : linenum , : linenumtemp , : linelocal , : linetype , : text , : cmdid , : ephemeral , : contentheight , : star , : archived ) `
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-11-29 03:03:02 +01:00
INSERT INTO cmd ( sessionid , cmdid , remoteownerid , remoteid , remotename , cmdstr , festate , statebasehash , statediffhasharr , termopts , origtermopts , status , startpk , doneinfo , rtnstate , runout , rtnbasehash , rtndiffhasharr )
VALUES ( : sessionid , : cmdid , : remoteownerid , : remoteid , : remotename , : cmdstr , : festate , : statebasehash , : statediffhasharr , : termopts , : origtermopts , : status , : startpk , : doneinfo , : rtnstate , : runout , : rtnbasehash , : rtndiffhasharr )
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-29 03:03:02 +01:00
func HasDoneInfo ( ctx context . Context , ck base . CommandKey ) ( bool , error ) {
2022-11-27 23:12:15 +01:00
var found bool
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-11-29 03:03:02 +01:00
found = tx . Exists ( ` SELECT sessionid FROM cmd WHERE sessionid = ? AND cmdid = ? AND doneinfo is NOT NULL ` , ck . GetSessionId ( ) , ck . GetCmdId ( ) )
2022-11-27 23:12:15 +01:00
return nil
} )
if txErr != nil {
return false , txErr
}
return found , nil
}
2022-11-29 03:03:02 +01:00
func UpdateCmdDoneInfo ( ctx context . Context , ck base . CommandKey , doneInfo * CmdDoneInfo ) ( * ModelUpdate , error ) {
if doneInfo == nil {
return nil , fmt . Errorf ( "invalid cmddone packet" )
}
if ck . IsEmpty ( ) {
return nil , fmt . Errorf ( "cannot update cmddoneinfo, empty 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-11-29 03:03:02 +01:00
query := ` UPDATE cmd SET status = ?, doneinfo = ? WHERE sessionid = ? AND cmdid = ? `
tx . ExecWrap ( query , CmdStatusDone , quickJson ( doneInfo ) , ck . GetSessionId ( ) , ck . GetCmdId ( ) )
2022-08-20 02:14:53 +02:00
var err error
2022-11-29 03:03:02 +01:00
rtnCmd , err = GetCmdById ( tx . Context ( ) , ck . GetSessionId ( ) , ck . GetCmdId ( ) )
2022-08-20 02:14:53 +02:00
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 {
2022-11-29 03:03:02 +01:00
return nil , fmt . Errorf ( "cmd data not found for ck[%s]" , ck )
2022-08-20 02:14:53 +02:00
}
2022-10-12 08:11:43 +02:00
return & ModelUpdate { Cmd : rtnCmd } , nil
2022-07-08 01:29:14 +02:00
}
2022-11-29 03:03:02 +01:00
func UpdateCmdRtnState ( ctx context . Context , ck base . CommandKey , statePtr ShellStatePtr ) error {
if ck . IsEmpty ( ) {
return fmt . Errorf ( "cannot update cmdrtnstate, empty ck" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE cmd SET rtnbasehash = ?, rtndiffhasharr = ? WHERE sessionid = ? AND cmdid = ? `
tx . ExecWrap ( query , statePtr . BaseHash , quickJsonArr ( statePtr . DiffHashArr ) , ck . GetSessionId ( ) , ck . GetCmdId ( ) )
return nil
} )
if txErr != nil {
return txErr
}
return 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
} )
}
2023-01-12 05:53:46 +01:00
func ReInitFocus ( ctx context . Context ) error {
return WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE screen_window SET focustype = 'input' `
tx . ExecWrap ( query )
return nil
} )
}
2022-07-08 01:29:14 +02:00
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 {
2022-12-26 21:18:13 +01:00
query := ` SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? `
2022-07-15 03:39:40 +02:00
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-12-27 01:09:21 +01:00
if txErr != nil {
return nil , txErr
}
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , err
}
return ModelUpdate { Sessions : [ ] * SessionType { bareSession } } , nil
2022-07-15 03:39:40 +02:00
}
2022-07-15 10:57:45 +02:00
2023-01-02 21:09:01 +01:00
func cleanSessionCmds ( ctx context . Context , sessionId string ) error {
2023-01-25 23:29:12 +01:00
var removedCmds [ ] string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-01-02 21:09:01 +01:00
query := ` SELECT cmdid FROM cmd WHERE sessionid = ? AND cmdid NOT IN (SELECT cmdid FROM line WHERE sessionid = ?) `
2023-01-25 23:29:12 +01:00
removedCmds = tx . SelectStrings ( query , sessionId , sessionId )
2023-01-02 21:09:01 +01:00
query = ` DELETE FROM cmd WHERE sessionid = ? AND cmdid NOT IN (SELECT cmdid FROM line WHERE sessionid = ?) `
tx . ExecWrap ( query , sessionId , sessionId )
return nil
} )
if txErr != nil {
return txErr
}
2023-01-25 23:29:12 +01:00
for _ , cmdId := range removedCmds {
DeletePtyOutFile ( ctx , sessionId , cmdId )
}
2023-01-02 21:09:01 +01:00
return nil
}
2022-12-25 22:03:11 +01:00
func CleanWindows ( sessionId string ) {
2023-01-25 23:29:12 +01:00
// NOTE: context.Background() here! (this could take a long time, and is async)
2022-12-25 22:03:11 +01:00
txErr := WithTx ( context . Background ( ) , func ( tx * TxWrap ) error {
query := ` SELECT windowid FROM window WHERE sessionid = ? AND windowid NOT IN (SELECT windowid FROM screen_window WHERE sessionid = ?) `
removedWindowIds := tx . SelectStrings ( query , sessionId , sessionId )
if len ( removedWindowIds ) == 0 {
return nil
}
for _ , windowId := range removedWindowIds {
query = ` DELETE FROM window WHERE sessionid = ? AND windowid = ? `
tx . ExecWrap ( query , sessionId , windowId )
query = ` DELETE FROM history WHERE sessionid = ? AND windowid = ? `
tx . ExecWrap ( query , sessionId , windowId )
2022-12-25 22:21:48 +01:00
query = ` DELETE FROM line WHERE sessionid = ? AND windowid = ? `
2022-12-25 22:03:11 +01:00
tx . ExecWrap ( query , sessionId , windowId )
}
2023-01-02 21:09:01 +01:00
return cleanSessionCmds ( tx . Context ( ) , sessionId )
2022-12-25 22:03:11 +01:00
} )
if txErr != nil {
fmt . Printf ( "ERROR cleaning windows sessionid:%s: %v\n" , sessionId , txErr )
}
2022-07-15 10:57:45 +02:00
}
2022-12-25 22:21:48 +01:00
func ArchiveScreen ( ctx context . Context , sessionId string , screenId string ) ( UpdatePacket , error ) {
2022-12-24 00:56:29 +01: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 close screen (not found)" )
}
2022-12-25 22:03:11 +01:00
query = ` SELECT archived FROM screen WHERE sessionid = ? AND screenid = ? `
2022-12-24 00:56:29 +01:00
closeVal := tx . GetBool ( query , sessionId , screenId )
if closeVal {
return nil
}
2022-12-25 22:03:11 +01:00
query = ` SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived `
2022-12-24 00:56:29 +01:00
numScreens := tx . GetInt ( query , sessionId )
if numScreens <= 1 {
2022-12-26 21:18:13 +01:00
return fmt . Errorf ( "cannot archive the last screen in a session" )
2022-12-24 00:56:29 +01:00
}
2022-12-25 22:21:48 +01:00
query = ` UPDATE screen SET archived = 1, archivedts = ?, screenidx = 0 WHERE sessionid = ? AND screenid = ? `
tx . ExecWrap ( query , time . Now ( ) . UnixMilli ( ) , sessionId , screenId )
2022-12-24 00:56:29 +01:00
isActive := tx . Exists ( ` SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ? ` , sessionId , screenId )
if isActive {
2022-12-25 22:03:11 +01:00
screenIds := tx . SelectStrings ( ` SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx ` , sessionId )
2022-12-24 00:56:29 +01:00
nextId := getNextId ( screenIds , screenId )
tx . ExecWrap ( ` UPDATE session SET activescreenid = ? WHERE sessionid = ? ` , nextId , sessionId )
}
return nil
} )
if txErr != nil {
return nil , txErr
}
2022-12-27 01:09:21 +01:00
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , txErr
}
update := ModelUpdate { Sessions : [ ] * SessionType { bareSession } }
2022-12-26 21:38:47 +01:00
newScreen , _ := GetScreenById ( ctx , sessionId , screenId )
if newScreen != nil {
2022-12-27 01:09:21 +01:00
bareSession . Screens = append ( bareSession . Screens , newScreen )
2022-12-26 21:38:47 +01:00
}
2022-12-24 00:56:29 +01:00
return update , nil
}
2022-12-25 22:21:48 +01:00
func UnArchiveScreen ( ctx context . Context , sessionId string , screenId string ) error {
2022-12-24 00:56:29 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-25 22:03:11 +01:00
query := ` SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? AND archived `
2022-12-24 00:56:29 +01:00
if ! tx . Exists ( query , sessionId , screenId ) {
2022-12-25 22:03:11 +01:00
return fmt . Errorf ( "cannot re-open screen (not found or not archived)" )
2022-12-24 00:56:29 +01:00
}
2022-12-25 22:03:11 +01:00
maxScreenIdx := tx . GetInt ( ` SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? AND NOT archived ` , sessionId )
2022-12-26 21:18:13 +01:00
query = ` UPDATE screen SET archived = 0, screenidx = ? WHERE sessionid = ? AND screenid = ? `
tx . ExecWrap ( query , maxScreenIdx + 1 , sessionId , screenId )
2022-12-24 00:56:29 +01:00
return nil
} )
return txErr
}
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
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-25 22:03:11 +01:00
query := ` SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ? `
if ! tx . Exists ( query , sessionId , screenId ) {
return fmt . Errorf ( "cannot purge screen (not found)" )
}
query = ` SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived `
numScreens := tx . GetInt ( query , sessionId )
if numScreens <= 1 {
return fmt . Errorf ( "cannot purge the last screen in a session" )
}
2022-07-15 10:57:45 +02:00
isActive := tx . Exists ( ` SELECT sessionid FROM session WHERE sessionid = ? AND activescreenid = ? ` , sessionId , screenId )
if isActive {
2022-12-25 22:03:11 +01:00
screenIds := tx . SelectStrings ( ` SELECT screenid FROM screen WHERE sessionid = ? AND NOT archived ORDER BY screenidx ` , sessionId )
2022-07-15 10:57:45 +02:00
nextId := getNextId ( screenIds , screenId )
tx . ExecWrap ( ` UPDATE session SET activescreenid = ? WHERE sessionid = ? ` , nextId , sessionId )
}
2022-12-25 22:03:11 +01:00
query = ` DELETE FROM screen_window WHERE sessionid = ? AND screenid = ? `
2022-07-15 10:57:45 +02:00
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
}
2022-12-25 22:03:11 +01:00
go CleanWindows ( sessionId )
2022-12-27 01:09:21 +01:00
bareSession , err := GetBareSessionById ( ctx , sessionId )
if err != nil {
return nil , err
}
bareSession . Screens = append ( bareSession . Screens , & ScreenType { SessionId : sessionId , ScreenId : screenId , Remove : true } )
2022-12-28 22:56:19 +01:00
update := ModelUpdate { Sessions : [ ] * SessionType { bareSession } }
return update , nil
2022-07-15 10:57:45 +02:00
}
2022-07-16 02:37:32 +02:00
2022-11-29 03:03:02 +01:00
func GetRemoteState ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) ( * packet . ShellState , * ShellStatePtr , error ) {
ssptr , err := GetRemoteStatePtr ( ctx , sessionId , windowId , remotePtr )
if err != nil {
return nil , nil , err
}
if ssptr == nil {
return nil , nil , nil
}
state , err := GetFullState ( ctx , * ssptr )
if err != nil {
return nil , nil , err
}
return state , ssptr , err
}
func GetRemoteStatePtr ( ctx context . Context , sessionId string , windowId string , remotePtr RemotePtrType ) ( * ShellStatePtr , error ) {
var ssptr * ShellStatePtr
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-29 03:03:02 +01:00
ssptr = & ShellStatePtr { ri . StateBaseHash , ri . StateDiffHashArr }
2022-07-16 02:37:32 +02:00
return nil
} )
2022-11-28 09:13:00 +01:00
if txErr != nil {
return nil , txErr
}
2022-11-29 03:03:02 +01:00
return ssptr , 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 )
2022-11-29 03:03:02 +01:00
ri . StateDiffHashArr = nil
2022-11-28 09:13:00 +01:00
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
}
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-12-20 03:52:08 +01:00
RIId : scbase . GenPromptUUID ( ) ,
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 {
2022-11-29 03:03:02 +01:00
query = ` UPDATE remote_instance SET festate = ?, statebasehash = ?, statediffhasharr = ? WHERE riid = ? `
2022-11-28 09:13:00 +01:00
ri . FeState = feState
err = updateRIWithState ( tx . Context ( ) , ri , stateBase , stateDiff )
if err != nil {
return err
}
2022-11-29 03:03:02 +01:00
tx . ExecWrap ( query , quickJson ( ri . FeState ) , ri . StateBaseHash , quickJsonArr ( ri . StateDiffHashArr ) , ri . RIId )
2022-11-28 09:13:00 +01:00
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-12-27 01:09:21 +01:00
query := ` SELECT sessionid FROM session WHERE NOT archived 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-12-27 01:09:21 +01:00
query = ` SELECT archived FROM session WHERE sessionid = ? `
isArchived := tx . GetBool ( query , sessionId )
if ! isArchived {
query = ` SELECT sessionid FROM session WHERE name = ? AND NOT archived `
dupSessionId := tx . GetString ( query , name )
if dupSessionId == sessionId {
return nil
}
if dupSessionId != "" {
return fmt . Errorf ( "invalid duplicate session name '%s'" , name )
}
2022-08-27 02:17:33 +02:00
}
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 = ` 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
2023-01-02 21:09:01 +01:00
func ArchiveWindowLines ( ctx context . Context , sessionId string , windowId string ) ( * ModelUpdate , error ) {
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 = ` UPDATE line SET archived = 1 WHERE sessionid = ? AND windowid = ? `
tx . ExecWrap ( query , sessionId , windowId )
return nil
} )
if txErr != nil {
return nil , txErr
}
win , err := GetWindowById ( ctx , sessionId , windowId )
if err != nil {
return nil , err
}
return & ModelUpdate { Window : win } , nil
}
func PurgeWindowLines ( ctx context . Context , sessionId string , windowId string ) ( * ModelUpdate , error ) {
2022-08-27 07:01:29 +02:00
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 )
2023-01-02 21:09:01 +01:00
query = ` DELETE FROM history 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
}
2023-01-02 21:09:01 +01:00
go cleanSessionCmds ( context . Background ( ) , sessionId )
2022-08-27 07:01:29 +02:00
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
2022-12-27 01:09:21 +01:00
func DeleteSession ( ctx context . Context , sessionId string ) ( UpdatePacket , error ) {
var newActiveSessionId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "session does not exist" )
}
query = ` DELETE FROM session WHERE sessionid = ? `
tx . ExecWrap ( query , sessionId )
query = ` DELETE FROM screen WHERE sessionid = ? `
tx . ExecWrap ( query , sessionId )
query = ` DELETE FROM screen_window WHERE sessionid = ? `
tx . ExecWrap ( query , sessionId )
query = ` DELETE FROM window WHERE sessionid = ? `
tx . ExecWrap ( query , sessionId )
query = ` DELETE FROM history WHERE sessionid = ? `
tx . ExecWrap ( query , sessionId )
query = ` DELETE FROM line WHERE sessionid = ? `
tx . ExecWrap ( query , sessionId )
query = ` DELETE FROM cmd WHERE sessionid = ? `
tx . ExecWrap ( query , sessionId )
newActiveSessionId , _ = fixActiveSessionId ( tx . Context ( ) )
return nil
} )
if txErr != nil {
return nil , txErr
}
delErr := DeleteSessionDir ( ctx , sessionId )
update := ModelUpdate { }
if newActiveSessionId != "" {
update . ActiveSessionId = newActiveSessionId
}
if delErr != nil {
update . Info = & InfoMsgType {
InfoMsg : fmt . Sprintf ( "error removing session files: %v" , delErr ) ,
}
}
update . Sessions = append ( update . Sessions , & SessionType { SessionId : sessionId , Remove : true } )
return update , nil
}
func fixActiveSessionId ( ctx context . Context ) ( string , error ) {
var newActiveSessionId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
curActiveSessionId := tx . GetString ( "SELECT activesessionid FROM client" )
query := ` SELECT sessionid FROM session WHERE sessionid = ? AND NOT archived `
if tx . Exists ( query , curActiveSessionId ) {
return nil
}
var err error
newActiveSessionId , err = GetFirstSessionId ( tx . Context ( ) )
if err != nil {
return err
}
tx . ExecWrap ( "UPDATE client SET activesessionid = ?" , newActiveSessionId )
return nil
} )
if txErr != nil {
return "" , txErr
}
return newActiveSessionId , nil
}
2022-12-27 03:42:55 +01:00
func ArchiveSession ( ctx context . Context , sessionId string ) ( * ModelUpdate , error ) {
2022-12-27 01:09:21 +01:00
if sessionId == "" {
return nil , fmt . Errorf ( "invalid blank sessionid" )
}
var newActiveSessionId string
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "session does not exist" )
}
query = ` SELECT archived FROM session WHERE sessionid = ? `
isArchived := tx . GetBool ( query , sessionId )
if isArchived {
return nil
}
query = ` UPDATE session SET archived = 1, archivedts = ? WHERE sessionid = ? `
tx . ExecWrap ( query , time . Now ( ) . UnixMilli ( ) , sessionId )
newActiveSessionId , _ = fixActiveSessionId ( tx . Context ( ) )
return nil
} )
if txErr != nil {
return nil , txErr
}
bareSession , _ := GetBareSessionById ( ctx , sessionId )
2022-12-27 03:42:55 +01:00
update := & ModelUpdate { }
2022-12-27 01:09:21 +01:00
if bareSession != nil {
update . Sessions = append ( update . Sessions , bareSession )
}
if newActiveSessionId != "" {
update . ActiveSessionId = newActiveSessionId
}
return update , nil
}
2022-12-27 03:42:55 +01:00
func UnArchiveSession ( ctx context . Context , sessionId string , activate bool ) ( * ModelUpdate , error ) {
if sessionId == "" {
return nil , fmt . Errorf ( "invalid blank sessionid" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT sessionid FROM session WHERE sessionid = ? `
if ! tx . Exists ( query , sessionId ) {
return fmt . Errorf ( "session does not exist" )
}
query = ` SELECT archived FROM session WHERE sessionid = ? `
isArchived := tx . GetBool ( query , sessionId )
if ! isArchived {
return nil
}
query = ` UPDATE session SET archived = 0, archivedts = 0 WHERE sessionid = ? `
tx . ExecWrap ( query , sessionId )
if activate {
query = ` UPDATE client SET activesessionid = ? `
tx . ExecWrap ( query , sessionId )
}
return nil
} )
if txErr != nil {
return nil , txErr
}
bareSession , _ := GetBareSessionById ( ctx , sessionId )
update := & ModelUpdate { }
if bareSession != nil {
update . Sessions = append ( update . Sessions , bareSession )
}
if activate {
update . ActiveSessionId = sessionId
}
return update , nil
2022-09-13 21:06:12 +02:00
}
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" )
}
2022-12-25 22:03:11 +01:00
query = ` SELECT count(*) FROM screen WHERE sessionid = ? AND NOT archived `
2022-09-20 23:15:20 +02:00
rtn . NumScreens = tx . GetInt ( query , sessionId )
2022-12-25 22:03:11 +01:00
query = ` SELECT count(*) FROM screen WHERE sessionid = ? AND archived `
rtn . NumArchivedScreens = tx . GetInt ( query , sessionId )
2022-09-20 23:15:20 +02:00
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
2022-11-29 03:03:02 +01:00
func GetFullState ( ctx context . Context , ssPtr ShellStatePtr ) ( * packet . ShellState , error ) {
2022-11-28 09:13:00 +01:00
var state * packet . ShellState
2022-11-29 03:03:02 +01:00
if ssPtr . BaseHash == "" {
2022-11-28 09:13:00 +01:00
return nil , fmt . Errorf ( "invalid empty basehash" )
}
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var stateBase StateBase
query := ` SELECT * FROM state_base WHERE basehash = ? `
2022-11-29 03:03:02 +01:00
found := tx . GetWrap ( & stateBase , query , ssPtr . BaseHash )
2022-11-28 09:13:00 +01:00
if ! found {
2022-11-29 03:03:02 +01:00
return fmt . Errorf ( "ShellState %s not found" , ssPtr . BaseHash )
2022-11-28 09:13:00 +01:00
}
state = & packet . ShellState { }
err := state . DecodeShellState ( stateBase . Data )
if err != nil {
return err
}
2022-11-29 03:03:02 +01:00
for idx , diffHash := range ssPtr . DiffHashArr {
2022-11-28 09:13:00 +01:00
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
}
2022-12-06 07:59:00 +01:00
func UpdateLineStar ( ctx context . Context , lineId string , starVal int ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE line SET star = ? WHERE lineid = ? `
tx . ExecWrap ( query , starVal , lineId )
return nil
} )
if txErr != nil {
return txErr
}
return nil
}
// can return nil, nil if line is not found
func GetLineById ( ctx context . Context , lineId string ) ( * LineType , error ) {
var rtn * LineType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
var line LineType
query := ` SELECT * FROM line WHERE lineid = ? `
found := tx . GetWrap ( & line , query , lineId )
if found {
rtn = & line
}
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2022-12-22 02:45:40 +01:00
2022-12-28 08:12:27 +01:00
func SetLineArchivedById ( ctx context . Context , lineId string , archived bool ) error {
2022-12-22 02:45:40 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2022-12-28 08:12:27 +01:00
query := ` UPDATE line SET archived = ? WHERE lineid = ? `
tx . ExecWrap ( query , archived , lineId )
2022-12-22 02:45:40 +01:00
return nil
} )
return txErr
}
func purgeCmdById ( ctx context . Context , sessionId string , cmdId string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` DELETE FROM cmd WHERE sessionid = ? AND cmdid = ? `
tx . ExecWrap ( query , sessionId , cmdId )
return DeletePtyOutFile ( tx . Context ( ) , sessionId , cmdId )
} )
return txErr
}
func PurgeLineById ( ctx context . Context , sessionId string , lineId string ) error {
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT cmdid FROM line WHERE sessionid = ? AND lineid = ? `
cmdId := tx . GetString ( query , sessionId , lineId )
query = ` DELETE FROM line WHERE sessionid = ? AND lineid = ? `
tx . ExecWrap ( query , sessionId , lineId )
2023-01-02 21:09:01 +01:00
query = ` DELETE FROM history WHERE sessionid = ? AND lineid = ? `
tx . ExecWrap ( query , sessionId , lineId )
2022-12-22 02:45:40 +01:00
if cmdId != "" {
query = ` SELECT count(*) FROM line WHERE sessionid = ? AND cmdid = ? `
cmdRefCount := tx . GetInt ( query , sessionId , cmdId )
if cmdRefCount == 0 {
err := purgeCmdById ( tx . Context ( ) , sessionId , cmdId )
if err != nil {
return err
}
}
}
return nil
} )
return txErr
}
2022-12-31 02:01:17 +01:00
func GetRIsForWindow ( ctx context . Context , sessionId string , windowId string ) ( [ ] * RemoteInstance , error ) {
var rtn [ ] * RemoteInstance
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM remote_instance WHERE sessionid = ? AND (windowid = '' OR windowid = ?) `
riMaps := tx . SelectMaps ( query , sessionId , windowId )
for _ , m := range riMaps {
ri := RIFromMap ( m )
if ri != nil {
rtn = append ( rtn , ri )
}
}
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2023-01-17 08:36:52 +01:00
2023-01-23 21:54:32 +01:00
func GetCurDayStr ( ) string {
2023-01-17 08:36:52 +01:00
now := time . Now ( )
dayStr := now . Format ( "2006-01-02" )
2023-01-23 21:54:32 +01:00
return dayStr
}
func UpdateCurrentActivity ( ctx context . Context , update ActivityUpdate ) error {
now := time . Now ( )
dayStr := GetCurDayStr ( )
2023-01-17 08:36:52 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
2023-01-23 21:54:32 +01:00
var tdata TelemetryData
query := ` SELECT tdata FROM activity WHERE day = ? `
found := tx . GetWrap ( & tdata , query , dayStr )
if ! found {
query = ` INSERT INTO activity ( day , uploaded , tdata , tzname , tzoffset , clientversion , clientarch )
VALUES ( ? , 0 , ? , ? , ? , ? , ? ) `
2023-01-17 08:36:52 +01:00
tzName , tzOffset := now . Zone ( )
if len ( tzName ) > MaxTzNameLen {
tzName = tzName [ 0 : MaxTzNameLen ]
}
2023-01-23 21:54:32 +01:00
tx . ExecWrap ( query , dayStr , tdata , tzName , tzOffset , scbase . PromptVersion , scbase . ClientArch ( ) )
2023-01-17 08:36:52 +01:00
}
2023-01-23 21:54:32 +01:00
tdata . NumCommands += update . NumCommands
tdata . FgMinutes += update . FgMinutes
tdata . ActiveMinutes += update . ActiveMinutes
tdata . OpenMinutes += update . OpenMinutes
2023-01-18 01:02:44 +01:00
query = ` UPDATE activity
2023-01-23 21:54:32 +01:00
SET tdata = ? ,
2023-01-18 01:02:44 +01:00
clientversion = ?
WHERE day = ? `
2023-01-23 21:54:32 +01:00
tx . ExecWrap ( query , tdata , scbase . PromptVersion , dayStr )
2023-01-17 08:36:52 +01:00
return nil
} )
if txErr != nil {
return txErr
}
return nil
}
func GetNonUploadedActivity ( ctx context . Context ) ( [ ] * ActivityType , error ) {
var rtn [ ] * ActivityType
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` SELECT * FROM activity WHERE uploaded = 0 ORDER BY day DESC LIMIT 30 `
tx . SelectWrap ( & rtn , query )
return nil
} )
if txErr != nil {
return nil , txErr
}
return rtn , nil
}
2023-01-23 21:54:32 +01:00
// note, will not mark the current day as uploaded
2023-01-17 08:36:52 +01:00
func MarkActivityAsUploaded ( ctx context . Context , activityArr [ ] * ActivityType ) error {
2023-01-23 21:54:32 +01:00
dayStr := GetCurDayStr ( )
2023-01-17 08:36:52 +01:00
txErr := WithTx ( ctx , func ( tx * TxWrap ) error {
query := ` UPDATE activity SET uploaded = 1 WHERE day = ? `
for _ , activity := range activityArr {
2023-01-23 21:54:32 +01:00
if activity . Day == dayStr {
continue
}
2023-01-17 08:36:52 +01:00
tx . ExecWrap ( query , activity . Day )
}
return nil
} )
return txErr
}