diff --git a/NOTES.md b/NOTES.md index 4ef9374b1949fecde9c9b0f8ff1aa4d8c061a6c2..2f659b47aa42988161dd84a33f85e094d2988096 100644 --- a/NOTES.md +++ b/NOTES.md @@ -19,6 +19,8 @@ Look up how to run migrations on the db from embedded files. Figure out readline completion. Case sensitive - issue? Building from repl.commands? +sqlite trigger tracing: `.trace stdout --row --profile --stmt --expanded --plain --close` + ## Things to do * Implement a better dclish parser. diff --git a/accounts/accounts.go b/accounts/accounts.go index e69a617b817dfa6a7a501f12884cea1fa46b47d0..186a1c9f08947cb6dd81c809edac64c1bc8205e0 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -2,15 +2,11 @@ package accounts import ( - "database/sql" "errors" "fmt" - "os" - "path" "strings" "git.lyda.ie/kevin/bulletin/folders" - "github.com/adrg/xdg" _ "modernc.org/sqlite" // Loads sqlite driver. ) @@ -19,7 +15,6 @@ import ( type UserData struct { Account string FullName string - pref *sql.DB Folders *folders.Store CurrentFolder string CurrentMessage int @@ -49,18 +44,7 @@ func Open(acc string) error { Account: acc, } - prefdir := path.Join(xdg.ConfigHome, "BULLETIN") - err = os.MkdirAll(prefdir, 0700) - if err != nil { - return errors.New("account preference directory problem") - } - User.pref, err = sql.Open("sqlite", path.Join(prefdir, fmt.Sprintf("%s.%s", acc, ".db"))) - if err != nil { - return errors.New("account preference database problem") - } - // TODO: run prefs migration - move this to a storage module. - - User.Folders, err = folders.Open() + User.Folders, err = folders.Open(acc) if err != nil { return err } @@ -70,7 +54,6 @@ func Open(acc string) error { // Close closes the resources open for the account. func (u *UserData) Close() { - u.pref.Close() u.Folders.Close() } diff --git a/decus/vax91b/bulletin/bulletin6.for b/decus/vax91b/bulletin/bulletin6.for index 7dc073d2b387b5cfc00ef4b9e3b788fcd5d421c0..55abcadcb941c99332f2c11194a742e6f4338023 100644 --- a/decus/vax91b/bulletin/bulletin6.for +++ b/decus/vax91b/bulletin/bulletin6.for @@ -18,122 +18,122 @@ C C FUNCTION: To close out the bulletin files and enable CTRL-C & -Y C DATA LUN /0/ - + ENTRY CLOSE_BULLINF LUN = LUN + 1 ! Unit = 9 - + ENTRY CLOSE_SYSUAF LUN = LUN + 1 ! Unit = 8 - + ENTRY CLOSE_BULLNEWS ENTRY CLOSE_BULLFOLDER LUN = LUN + 3 ! Unit = 7 - + ENTRY CLOSE_BULLUSER LUN = LUN + 2 ! Unit = 4 - + ENTRY CLOSE_BULLDIR LUN = LUN + 1 ! Unit = 2 - + ENTRY CLOSE_BULLFIL LUN = LUN + 1 ! Unit = 1 - + CALL ENABLE_CTRL - + CLOSE (UNIT=LUN) - + LUN = 0 - + RETURN END - - + + SUBROUTINE CLOSE_FILE_DELETE - + IMPLICIT INTEGER (A-Z) - + DATA LUN /0/ - + ENTRY CLOSE_BULLDIR_DELETE LUN = LUN + 1 ! Unit = 2 - + ENTRY CLOSE_BULLFIL_DELETE LUN = LUN + 1 ! Unit = 1 - + CALL ENABLE_CTRL - + CLOSE (UNIT=LUN,STATUS='DELETE') - + LUN = 0 - + RETURN END - - + + SUBROUTINE OPEN_FILE(UNIT) - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLFILES.INC' - + INCLUDE 'BULLFOLDER.INC' - + INCLUDE 'BULLUSER.INC' - + INCLUDE 'BULLDIR.INC' - + INCLUDE '($FORIOSDEF)' - + INCLUDE '($PRVDEF)' - + COMMON /REMOTE_FOLDER/ REMOTE_SET,REMOTE_UNIT - + COMMON /DIR_POSITION/ DIR_NUM - + COMMON /NEWS_OPEN/ NEWS_OPEN - + DATA LUN /0/ - + LUN = UNIT - 14 ! 14 gets added to LUN - + ENTRY OPEN_BULLNEWS LUN = LUN + 5 ! Unit = 14 - + ENTRY OPEN_BULLINF LUN = LUN + 1 ! Unit = 9 - + ENTRY OPEN_SYSUAF LUN = LUN + 1 ! Unit = 8 - + ENTRY OPEN_BULLFOLDER LUN = LUN + 3 ! Unit = 7 - + ENTRY OPEN_BULLUSER LUN = LUN + 2 ! Unit = 4 - + ENTRY OPEN_BULLDIR LUN = LUN + 1 ! Unit = 2 - + ENTRY OPEN_BULLFIL LUN = LUN + 1 ! Unit = 1 - + IER = 0 - + NTRIES = 0 - + CALL SET_PROTECTION - + CALL DISABLE_CTRL ! No breaks while file is open - + IF (LUN.EQ.2.AND..NOT.REMOTE_SET) THEN DO WHILE (FILE_LOCK(IER,IER1)) - + OPEN (UNIT=2,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLDIR',STATUS='OLD',FORM='UNFORMATTED', & RECORDTYPE='FIXED',RECORDSIZE=DIR_RECORD_LENGTH/4, & ORGANIZATION='INDEXED',IOSTAT=IER, & KEY=(9:12:INTEGER,1:8:CHARACTER),ACCESS='KEYED') - + IF (IER.EQ.FOR$IOS_FILNOTFOU) THEN OPEN (UNIT=2,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLDIR',STATUS='NEW',FORM='UNFORMATTED', @@ -158,7 +158,7 @@ C END DO DIR_NUM = -1 END IF - + IF (LUN.EQ.1.AND..NOT.REMOTE_SET) THEN DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=1,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) @@ -174,7 +174,7 @@ C IF (NTRIES.GT.30) CALL TIMER_ERR(LUN) END DO END IF - + IF (LUN.EQ.4) THEN DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=4,FILE=BULLUSER_FILE,STATUS='OLD', @@ -200,7 +200,7 @@ C IF (NTRIES.GT.30) CALL TIMER_ERR(LUN) END DO END IF - + IF (LUN.EQ.7) THEN DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=7,FILE=BULLFOLDER_FILE,STATUS='OLD', @@ -232,7 +232,7 @@ C END DO IF (IER.EQ.0) NEWS_OPEN = .FALSE. END IF - + IF (LUN.EQ.14) THEN DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=7,FILE=BULLNEWS_FILE,STATUS='OLD', @@ -253,7 +253,7 @@ C END DO IF (IER.EQ.0) NEWS_OPEN = .TRUE. END IF - + IF (LUN.EQ.9) THEN DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=9,FILE=BULLINF_FILE,STATUS='UNKNOWN', @@ -269,7 +269,7 @@ C IF (NTRIES.GT.30) CALL TIMER_ERR(LUN) END DO END IF - + IF (IER.NE.0) THEN WRITE (6,'( & '' Cannot open file in OPEN_FILE, unit = '',I)') LUN @@ -281,108 +281,108 @@ C END IF CALL ENABLE_CTRL_EXIT ! Enable CTRL-Y & -C & EXIT END IF - + LUN = 0 - + CALL RESET_PROTECTION - + RETURN END - - - + + + SUBROUTINE TIMER_ERR(UNIT) - + IMPLICIT INTEGER (A-Z) - + CHARACTER*14 NAMES(6) DATA NAMES/'directory','message','BULLUSER.DAT','BULLFOLDER.DAT', & 'BULLINF.DAT','BULLNEWS.DAT'/ INTEGER NAME(14) DATA NAME/1,2,0,3,0,0,4,0,5,0,0,0,0,6/ - + IF (TEST_BULLCP().NE.2) THEN ! If BULLCP process, don't log error WRITE(6,'('' ERROR: Unable to open '',A, & '' file after 30 secs.'')') & NAMES(NAME(UNIT))(:TRIM(NAMES(NAME(UNIT)))) WRITE (6,'('' Please try again later.'')') END IF - + CALL ENABLE_CTRL_EXIT ! No breaks while file is open END - - - + + + SUBROUTINE OPEN_FILE_SHARED - + IMPLICIT INTEGER (A-Z) - + INCLUDE '($FORIOSDEF)' - + INCLUDE 'BULLFILES.INC' - + INCLUDE 'BULLFOLDER.INC' - + INCLUDE 'BULLUSER.INC' - + INCLUDE 'BULLDIR.INC' - + COMMON /POINT/ BULL_POINT - + COMMON /REMOTE_FOLDER/ REMOTE_SET,REMOTE_UNIT - + COMMON /DIR_POSITION/ DIR_NUM - + COMMON /NEWS_OPEN/ NEWS_OPEN - + EXTERNAL LNM_MODE_EXEC,ENABLE_CTRL_EXIT C C The following 2 files were used prior to V1.1. C CHARACTER*80 BULLDIR_FILE /'BULL_DIR:BULLDIR.DAT'/ CHARACTER*80 BULLETIN_FILE /'BULL_DIR:BULLETIN.DAT'/ - + CHARACTER*25 SAVE_FOLDER DATA SAVE_BLOCK/-1/ - + CHARACTER*14 NAMES(6) DATA NAMES/'directory','message','BULLUSER.DAT','BULLFOLDER.DAT', & 'BULLINF.DAT','BULLNEWS.DAT'/ INTEGER NAME(14) DATA NAME/1,2,0,3,0,0,4,0,5,0,0,0,0,6/ - + DATA LUN /0/ - + ENTRY OPEN_BULLNEWS_SHARED LUN = LUN + 5 ! Unit = 14 - + ENTRY OPEN_BULLINF_SHARED LUN = LUN + 1 ! Unit = 9 - + ENTRY OPEN_SYSUAF_SHARED LUN = LUN + 1 ! Unit = 8 - + ENTRY OPEN_BULLFOLDER_SHARED LUN = LUN + 3 ! Unit = 7 - + ENTRY OPEN_BULLUSER_SHARED LUN = LUN + 2 ! Unit = 4 - + ENTRY OPEN_BULLDIR_SHARED LUN = LUN + 1 ! Unit = 2 - + ENTRY OPEN_BULLFIL_SHARED LUN = LUN + 1 ! Unit = 1 - + IER = 0 - + NTRIES = 0 - + CALL DISABLE_CTRL - + IF (LUN.EQ.2.AND..NOT.REMOTE_SET) THEN DO WHILE (FILE_LOCK(IER,IER1)) - + OPEN (UNIT=2,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLDIR',STATUS='OLD',FORM='UNFORMATTED', & RECORDTYPE='FIXED',RECORDSIZE=DIR_RECORD_LENGTH/4, @@ -411,7 +411,7 @@ C END DO DIR_NUM = -1 END IF - + IF (LUN.EQ.1.AND.REMOTE_SET.AND.(SAVE_BLOCK.NE.BLOCK.OR. & SAVE_FOLDER.NE.FOLDER)) THEN CALL REMOTE_READ_MESSAGE(BULL_POINT,IER) @@ -439,7 +439,7 @@ C IF (NTRIES.GT.30) CALL ENABLE_CTRL_EXIT END DO END IF - + IF (LUN.EQ.4) THEN DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=4,FILE=BULLUSER_FILE,STATUS='OLD', @@ -455,14 +455,14 @@ C IF (NTRIES.GT.30) CALL ENABLE_CTRL_EXIT END DO END IF - + IF (LUN.EQ.7) THEN DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=7,FILE=BULLFOLDER_FILE,STATUS='OLD', & ACCESS='KEYED',RECORDTYPE='FIXED', & IOSTAT=IER,ORGANIZATION='INDEXED',SHARED, & KEY=(1:25:CHARACTER,26:29:INTEGER)) - + IF (IER.EQ.0) THEN INQUIRE(UNIT=7,RECORDSIZE=ASK_SIZE) IF (ASK_SIZE.NE.FOLDER_RECORD/4) THEN @@ -477,20 +477,20 @@ C END DO IF (IER.EQ.0) NEWS_OPEN = .FALSE. END IF - + IF (LUN.EQ.14) THEN DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=7,FILE=BULLNEWS_FILE,STATUS='OLD', & ACCESS='KEYED',RECORDTYPE='FIXED', & IOSTAT=IER,ORGANIZATION='INDEXED',SHARED, & KEY=(1:25:CHARACTER,26:29:INTEGER)) - + NTRIES = NTRIES + 1 IF (NTRIES.GT.30) CALL ENABLE_CTRL_EXIT END DO IF (IER.EQ.0) NEWS_OPEN = .TRUE. END IF - + IF (LUN.EQ.8) THEN DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=8,FILE='SYSUAF',DEFAULTFILE='SYS$SYSTEM:SYSUAF.DAT', @@ -499,7 +499,7 @@ C & USEROPEN=LNM_MODE_EXEC) END DO END IF - + IF (LUN.EQ.9) THEN DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=9,FILE=BULLINF_FILE,STATUS='OLD', @@ -516,7 +516,7 @@ C IF (NTRIES.GT.30) CALL ENABLE_CTRL_EXIT END DO END IF - + IF (IER.EQ.FOR$IOS_FILNOTFOU.AND.LUN.NE.8) THEN CALL OPEN_FILE(LUN) ELSE IF (IER.NE.0) THEN @@ -530,46 +530,46 @@ C END IF CALL ENABLE_CTRL_EXIT END IF - + LUN = 0 - + RETURN END - - + + SUBROUTINE RESET_PROTECTION - + IMPLICIT INTEGER (A-Z) - + DATA PROT_LEVEL /0/ - + PROT_LEVEL = PROT_LEVEL - 1 IF (PROT_LEVEL.GT.0) RETURN - + CALL SYS$SETDFPROT(CUR_DEF_PROT,) ! Reset default protection - + RETURN - + ENTRY SET_PROTECTION - + PROT_LEVEL = PROT_LEVEL + 1 IF (PROT_LEVEL.GT.1) RETURN - + CALL SYS$SETDFPROT('FF00'X,CUR_DEF_PROT) ! Set protection to (SYSTEM:RWED,OWNER:RWED,WORLD,GROUP) - + RETURN END - - - - + + + + SUBROUTINE FOLDER_TO_NEWS - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLFOLDER.INC' - + NEWS_FOLDER = FOLDER NEWS_FOLDER_NUMBER = FOLDER_NUMBER NEWS_FOLDER_DESCRIP = FOLDER_DESCRIP( @@ -578,11 +578,11 @@ C NEWS_F_NBULL = F_NBULL NEWS_F_NEWEST_BTIM(1) = F_NEWEST_BTIM(1) NEWS_F_NEWEST_BTIM(2) = F_NEWEST_BTIM(2) - + RETURN - + ENTRY FOLDER1_TO_NEWS - + NEWS_FOLDER1 = FOLDER1 NEWS_FOLDER1_NUMBER = FOLDER1_NUMBER NEWS_FOLDER1_DESCRIP = FOLDER1_DESCRIP( @@ -591,11 +591,11 @@ C NEWS_F1_NBULL = F1_NBULL NEWS_F1_NEWEST_BTIM(1) = F1_NEWEST_BTIM(1) NEWS_F1_NEWEST_BTIM(2) = F1_NEWEST_BTIM(2) - + RETURN - + ENTRY NEWS_TO_FOLDER - + FOLDER = NEWS_FOLDER FOLDER_NUMBER = NEWS_FOLDER_NUMBER FOLDER_DESCRIP = NEWS_FOLDER(:TRIM(NEWS_FOLDER)) @@ -605,11 +605,11 @@ C F_NEWEST_BTIM(1) = NEWS_F_NEWEST_BTIM(1) F_NEWEST_BTIM(2) = NEWS_F_NEWEST_BTIM(2) FOLDER_FLAG = 0 - + RETURN - + ENTRY NEWS_TO_FOLDER1 - + FOLDER1 = NEWS_FOLDER1 FOLDER1_NUMBER = NEWS_FOLDER1_NUMBER FOLDER1_DESCRIP = NEWS_FOLDER1(:TRIM(NEWS_FOLDER1)) @@ -619,49 +619,49 @@ C F1_NEWEST_BTIM(1) = NEWS_F1_NEWEST_BTIM(1) F1_NEWEST_BTIM(2) = NEWS_F1_NEWEST_BTIM(2) FOLDER1_FLAG = 0 - + RETURN - + END - - - - + + + + SUBROUTINE CONVERT_BULLDIRS - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLDIR.INC' - + INCLUDE 'BULLFOLDER.INC' - + INCLUDE 'BULLFILES.INC' - + CHARACTER BUFFER*115 - + WRITE (6,'('' Converting data files to new format. Please wait.'')') - + CALL SET_PROTECTION - + OPEN (UNIT=2,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLDIR',STATUS='OLD',FORM='UNFORMATTED', & RECORDTYPE='FIXED',ACCESS='DIRECT', & ORGANIZATION='RELATIVE',DISPOSE='KEEP', & IOSTAT=IER) - + IF (IER.NE.0) GO TO 900 ! No BULLDIR file found. - + READ (2'1,IOSTAT=IER1) BUFFER - + CALL LIB$MOVC3(4,%REF(BUFFER(39:)),NBULL) - + OPEN (UNIT=9,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLDIR',STATUS='NEW',FORM='UNFORMATTED', & RECORDTYPE='FIXED',RECORDSIZE=DIR_RECORD_LENGTH/4, & ORGANIZATION='INDEXED',IOSTAT=IER,DISPOSE='DELETE', & KEY=(9:12:INTEGER,1:8:CHARACTER),ACCESS='KEYED', & INITIALSIZE=(((NBULL+1)*DIR_RECORD_LENGTH)/512)+5 ) - + IF (IER.NE.0) THEN OPEN (UNIT=9,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLDIR',STATUS='NEW',FORM='UNFORMATTED', @@ -669,16 +669,16 @@ C & ORGANIZATION='INDEXED',IOSTAT=IER,DISPOSE='DELETE', & KEY=(9:12:INTEGER,1:8:CHARACTER),ACCESS='KEYED') END IF - + IF (IER1.NE.0) GO TO 800 - + CALL SYS_BINTIM(BUFFER(1:11)//' '//BUFFER(12:19),NEWEST_EXBTIM) CALL SYS_BINTIM(BUFFER(20:30)//' '//BUFFER(31:38),NEWEST_MSGBTIM) BULLDIR_HEADER(29:40) = BUFFER(39:) CALL SYS_BINTIM(BUFFER(51:61)//' '//BUFFER(62:69),SHUTDOWN_BTIM) BULLDIR_HEADER(49:52) = BUFFER(70:) IF (IER.EQ.0) WRITE (9,IOSTAT=IER) BULLDIR_HEADER - + ICOUNT = 2 DO WHILE (IER.EQ.0) READ (2'ICOUNT,IOSTAT=IER) BUFFER @@ -695,18 +695,18 @@ C ICOUNT = ICOUNT + 1 END IF END DO - + 800 CLOSE (UNIT=9,DISPOSE='KEEP') CLOSE (UNIT=2) - + 900 CALL RESET_PROTECTION - + RETURN - + END - - - + + + SUBROUTINE CONVERT_BULLFILES C C SUBROUTINE CONVERT_BULLFILES @@ -716,54 +716,54 @@ C Add expiration time to directory file, add extra byte to bulletin C file to show where each bulletin starts (for redunancy sake in C case crash occurs). C - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLDIR.INC' - + INCLUDE 'BULLFOLDER.INC' - + INCLUDE 'BULLFILES.INC' - + CHARACTER*81 BUFFER - + WRITE (6,'('' Converting data files to new format. Please wait.'')') - + OPEN (UNIT=9,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLDIR',STATUS='OLD', & RECORDTYPE='FIXED',RECORDSIZE=107,ACCESS='DIRECT', & ORGANIZATION='RELATIVE',DISPOSE='KEEP',FORM='FORMATTED', & SHARED,READONLY,IOSTAT=IER) - + IF (IER.NE.0) CALL ERROR_AND_EXIT ! Error. Why? - + OPEN (UNIT=10,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLFIL',STATUS='OLD', & RECORDTYPE='FIXED',RECORDSIZE=80, & FORM='FORMATTED',IOSTAT=IER,SHARED,READONLY) - + IF (IER.NE.0) CALL ERROR_AND_EXIT ! Error. Why? - + CALL SET_PROTECTION - + OPEN (UNIT=1,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLFIL',STATUS='NEW',IOSTAT=IER, & ACCESS='DIRECT',RECORDTYPE='FIXED',RECORDSIZE=81, & FORM='FORMATTED') - + OPEN (UNIT=2,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLDIR',STATUS='NEW',FORM='UNFORMATTED', & RECORDTYPE='FIXED',RECORDSIZE=DIR_RECORD_LENGTH/4, & ORGANIZATION='INDEXED',IOSTAT=IER,DISPOSE='KEEP', & KEY=(9:12:INTEGER,1:8:CHARACTER),ACCESS='KEYED') - + NEWEST_EXTIME = '00:00:00.00' - READ (9'1,1000,IOSTAT=IER) + READ (9'1,1000,IOSTAT=IER) & NEWEST_EXDATE,NEWEST_DATE,NEWEST_TIME(:8), & NBULL,NBLOCK,SHUTDOWN,SHUTDOWN_DATE,SHUTDOWN_TIME(:8) NEMPTY = 0 IF (IER.EQ.0) CALL WRITEDIR(0,IER1) - + EXTIME = '00:00:00.00' ICOUNT = 2 DO WHILE (IER.EQ.0) @@ -780,20 +780,20 @@ C ICOUNT = ICOUNT + 1 END IF END DO - + CLOSE (UNIT=9) CLOSE (UNIT=2) CLOSE (UNIT=10) CLOSE (UNIT=1) - + CALL RESET_PROTECTION RETURN - + 1000 FORMAT(A11,A11,A8,A4,A4,A4,A11,A8) 1010 FORMAT(A53,A12,A11,A8,A4,A11,A4,A4) - + END - + SUBROUTINE CONVERT_BULLFILE C C SUBROUTINE CONVERT_BULLFILE @@ -803,29 +803,29 @@ C C NOTE: CONVERT_BULLFILES converts from 80 to 81 byte length. C This converts from 81 byte length to 128 compressed format. C - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLDIR.INC' - + INCLUDE 'BULLFOLDER.INC' - + INCLUDE 'BULLFILES.INC' - + CHARACTER*80 BUFFER,NEW_FILE - + WRITE (6,'('' Converting data files to new format. Please wait.'')') - + CALL CLOSE_BULLDIR - + CALL SET_PROTECTION - + CALL OPEN_BULLFOLDER - + 100 READ (7,FMT=FOLDER_FMT,ERR=200) & FOLDER,FOLDER_NUMBER,FOLDER_OWNER,FOLDER_DESCRIP & ,FOLDER_BBOARD,FOLDER_BBEXPIRE,USERB,GROUPB,ACCOUNTB - + FOLDER_FILE = FOLDER_DIRECTORY(:TRIM(FOLDER_DIRECTORY)) & //FOLDER(:TRIM(FOLDER)) NEW_FILE = FOLDER_FILE(:TRIM(FOLDER_FILE))//'.BULLFILOLD' @@ -833,20 +833,20 @@ C & ,STATUS='OLD', & RECORDTYPE='FIXED',RECORDSIZE=81,ACCESS='DIRECT', & FORM='FORMATTED',IOSTAT=IER,SHARED,READONLY) - + IF (IER.NE.0) CALL ERROR_AND_EXIT ! Error. Why? - + OPEN (UNIT=1,FILE=FOLDER_FILE(1:TRIM(FOLDER_FILE)) & //'.BULLFIL',STATUS='NEW',IOSTAT=IER, & ACCESS='DIRECT',RECORDTYPE='FIXED',RECORDSIZE=32, & FORM='UNFORMATTED') IER = LIB$RENAME_FILE(FOLDER_FILE(:TRIM(FOLDER_FILE)) & //'.BULLFIL;-1',NEW_FILE) - + CALL OPEN_BULLDIR - + CALL READDIR(0,IER) - + IF (IER.EQ.1) THEN NBLOCK = 0 DO I=1,NBULL @@ -864,27 +864,27 @@ C BLOCK = SBLOCK CALL WRITEDIR(I,IER) END DO - + NEMPTY = 0 CALL WRITEDIR(0,IER) END IF - + CLOSE (UNIT=10) CLOSE (UNIT=1) - + CALL CLOSE_BULLDIR GOTO 100 - + 200 CALL OPEN_BULLDIR_SHARED - + CALL RESET_PROTECTION - + RETURN - + END - - - + + + SUBROUTINE CONVERT_BULLFOLDER(FILENAME,ASK_SIZE) C C SUBROUTINE CONVERT_BULLFOLDER @@ -892,46 +892,46 @@ C C FUNCTION: Converts bulletin folder file to new format. C IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLDIR.INC' - + INCLUDE 'BULLFOLDER.INC' - + INCLUDE 'BULLFILES.INC' - + INCLUDE '($SSDEF)' - + INCLUDE '($FORIOSDEF)' - + CHARACTER*(*) FILENAME - + CHARACTER*80 NEW_FILE - + WRITE (6,'('' Converting data files to new format. Please wait.'')') - + CALL SET_PROTECTION - + EODIR = MAX(INDEX(FILENAME,':'),INDEX(FILENAME,']')) SUFFIX = INDEX(FILENAME(EODIR:),'.') + EODIR - 1 NEW_FILE = FILENAME(:SUFFIX)//'OLD' - + DO WHILE (FILE_LOCK(IER,IER1)) OPEN (UNIT=7,FILE=FILENAME,STATUS='OLD', & ACCESS='KEYED',RECORDTYPE='FIXED', & FORM='FORMATTED',ORGANIZATION='INDEXED',IOSTAT=IER, & KEY=(1:25:CHARACTER,26:29:INTEGER)) END DO - + IF (IER.NE.0) CALL ERROR_AND_EXIT ! Error. Why? - + OPEN (UNIT=19,FILE=NEW_FILE,STATUS='NEW', & ACCESS='KEYED',RECORDTYPE='FIXED', & RECORDSIZE=FOLDER_RECORD, & FORM='FORMATTED',ORGANIZATION='INDEXED',IOSTAT=IER, & KEY=(1:25:CHARACTER,26:29:INTEGER),DISPOSE='DELETE') - + IF (IER.NE.0) CALL ERROR_AND_EXIT ! Error. Why? - + IF (ASK_SIZE.EQ.173/4) THEN F_NUMBER = 0 DO WHILE (IER.EQ.0) @@ -1001,52 +1001,52 @@ C END IF END DO END IF - + CLOSE (UNIT=7) CLOSE (UNIT=19,STATUS='SAVE') - + IER = LIB$RENAME_FILE(NEW_FILE,FILENAME) IER = LIB$RENAME_FILE(BULLFOLDER_FILE//';-1',NEW_FILE) - + CALL RESET_PROTECTION - + IER = LIB$DELETE_FILE(BBOARD_DIRECTORY(:TRIM(BBOARD_DIRECTORY)) & //'BOARD.COM;*') ! BULLETIN$ is referenced in old file - + RETURN END - + SUBROUTINE CONVERT_USERFILE C C SUBROUTINE CONVERT_USERFILE C C FUNCTION: Converts user file to new format which has 8 bytes added. C - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLFILES.INC' - + INCLUDE 'BULLUSER.INC' - + CHARACTER BUFFER*74,NEW_FILE*80 - + CHARACTER*11 LOGIN_DATE,READ_DATE CHARACTER*8 LOGIN_TIME,READ_TIME - + WRITE (6,'('' Converting data files to new format. Please wait.'')') - + EODIR = MAX(INDEX(BULLUSER_FILE,':'),INDEX(BULLUSER_FILE,']')) SUFFIX = INDEX(BULLUSER_FILE(EODIR:),'.') + EODIR - 1 NEW_FILE = BULLUSER_FILE(:SUFFIX)//'OLD' IER = LIB$RENAME_FILE(BULLUSER_FILE,NEW_FILE) - + OPEN (UNIT=9,FILE=NEW_FILE,STATUS='OLD', & ACCESS='KEYED',RECORDTYPE='FIXED', & FORM='FORMATTED',ORGANIZATION='INDEXED',IOSTAT=IER, & KEY=(1:12:CHARACTER)) INQUIRE (UNIT=9,RECORDSIZE=RECL) - + IF ((RECL-28)/16.GT.FLONG) THEN WRITE (6,'('' ERROR: Old data files have more folders'', & '' than was specified with BULLUSER.INC.'')') @@ -1059,7 +1059,7 @@ C CALL EXIT END IF END IF - + IF (IER.EQ.0) THEN CALL SET_PROTECTION OPEN (UNIT=4,FILE=BULLUSER_FILE,STATUS='NEW', @@ -1067,7 +1067,7 @@ C & FORM='FORMATTED',ORGANIZATION='INDEXED',IOSTAT=IER, & KEY=(1:12:CHARACTER)) END IF - + IF (IER.NE.0) THEN WRITE (6,'('' Cannot convert user file.'')') IF (IER1.EQ.0) CALL ERRSNS(IDUMMY,IER1) @@ -1075,14 +1075,14 @@ C CALL RESET_PROTECTION CALL ENABLE_CTRL_EXIT END IF - + DO I=1,FLONG NEW_FLAG(I) = 'FFFFFFFF'X NOTIFY_FLAG(I) = 0 BRIEF_FLAG(I) = 0 SET_FLAG(I) = 0 END DO - + IF (RECL.EQ.42.OR.RECL.EQ.50.OR.RECL.EQ.58.OR.RECL.EQ.66.OR. & RECL.EQ.74) THEN ! Old format IF (RECL.LE.58) RECL = 50 @@ -1118,7 +1118,7 @@ C ELSE ! Folder maxmimum increase OFLONG = (RECL - 28) / 16 ! Old #longwords/flag DO WHILE (IER.EQ.0) - READ (9,FMT='(A12,<4+OFLONG*4>A4)',IOSTAT=IER) + READ (9,FMT='(A12,<4+OFLONG*4>A4)',IOSTAT=IER) & TEMP_USER,LOGIN_BTIM,READ_BTIM, & (NEW_FLAG(I),I=1,OFLONG),(SET_FLAG(I),I=1,OFLONG), & (BRIEF_FLAG(I),I=1,OFLONG),(NOTIFY_FLAG(I),I=1,OFLONG) @@ -1128,18 +1128,18 @@ C END IF END DO END IF - + IER = 0 - + CLOSE (UNIT=9) CLOSE (UNIT=4) - + CALL RESET_PROTECTION - + RETURN END - - + + SUBROUTINE READDIR(BULLETIN_NUM,ICOUNT) C C SUBROUTINE READDIR @@ -1154,24 +1154,24 @@ C number of blocks in bulletin file, etc. C OUTPUTS: C ICOUNT - The last record read by this routine. C - + IMPLICIT INTEGER (A - Z) - + INCLUDE 'BULLDIR.INC' - + INCLUDE 'BULLFOLDER.INC' - + COMMON /PROMPT/ COMMAND_PROMPT CHARACTER*39 COMMAND_PROMPT - + COMMON /REMOTE_FOLDER/ REMOTE_SET,REMOTE_UNIT - + COMMON /DIR_POSITION/ DIR_NUM - + CHARACTER*3 CFOLDER_NUMBER - + ICOUNT = BULLETIN_NUM - + IF (ICOUNT.EQ.0) THEN IF (.NOT.REMOTE_SET) THEN DO WHILE (REC_LOCK(IER)) @@ -1230,19 +1230,19 @@ C RETURN END IF END IF - + IF (IER.EQ.0) ICOUNT = ICOUNT + 1 - + UNLOCK 2 - + RETURN - + END - - - - - + + + + + SUBROUTINE READDIR_KEYGE(IER) C C SUBROUTINE READDIR_KEYGE @@ -1255,15 +1255,15 @@ C MSG_KEY - Message key (passed via BULLDIR.INC common block). C OUTPUTS: C IER - If not 0, no entry found. Else contains message number. C - + IMPLICIT INTEGER (A - Z) - + INCLUDE 'BULLDIR.INC' - + COMMON /REMOTE_FOLDER/ REMOTE_SET,REMOTE_UNIT - + COMMON /DIR_POSITION/ DIR_NUM - + IF (.NOT.REMOTE_SET) THEN DO WHILE (REC_LOCK(IER)) READ(2,KEYID=1,KEYGT=MSG_KEY,IOSTAT=IER) BULLDIR_ENTRY @@ -1281,66 +1281,66 @@ C ELSE CALL REMOTE_GET_HEADER(DUMMY,-1,IER) END IF - + RETURN - + END - - - + + + SUBROUTINE CONVERT_HEADER_FROMBIN - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLDIR.INC' - + CHARACTER*23 DATETIME - + CALL SYS$ASCTIM(,DATETIME,NEWEST_EXBTIM,) - + NEWEST_EXDATE = DATETIME NEWEST_EXTIME = DATETIME(13:) - + CALL SYS$ASCTIM(,DATETIME,NEWEST_MSGBTIM,) - + NEWEST_DATE = DATETIME NEWEST_TIME = DATETIME(13:) - + CALL SYS$ASCTIM(,DATETIME,SHUTDOWN_BTIM,) - + SHUTDOWN_DATE = DATETIME SHUTDOWN_TIME = DATETIME(13:) - + RETURN END - - - + + + SUBROUTINE CONVERT_ENTRY_FROMBIN - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLDIR.INC' - + CHARACTER*23 DATETIME - + CALL SYS$ASCTIM(,DATETIME,EX_BTIM,) - + EXDATE = DATETIME EXTIME = DATETIME(13:) - + CALL SYS$ASCTIM(,DATETIME,MSG_BTIM,) - + DATE = DATETIME TIME = DATETIME(13:) - + RETURN END - - - - - + + + + + SUBROUTINE WRITEDIR(BULLETIN_NUM,IER) C C SUBROUTINE WRITEDIR @@ -1354,23 +1354,23 @@ C If 0, write the header of the directory file. C OUTPUTS: C IER - Error status from WRITE. C - + IMPLICIT INTEGER (A - Z) - + COMMON /REMOTE_FOLDER/ REMOTE_SET,REMOTE_UNIT - + COMMON /DIR_POSITION/ DIR_NUM - + INCLUDE 'BULLDIR.INC' - + CONV = .TRUE. - + GO TO 10 - + ENTRY WRITEDIR_NOCONV(BULLETIN_NUM,IER) - + CONV = .FALSE. - + 10 IF (BULLETIN_NUM.EQ.0) THEN IF (CONV) CALL CONVERT_HEADER_TOBIN IF (REMOTE_SET) THEN @@ -1410,52 +1410,52 @@ C END IF END IF END IF - + IF (REMOTE_SET.AND.IER.GT.0) CALL ERROR_AND_EXIT - + DIR_NUM = -1 - + RETURN - + END - - - + + + SUBROUTINE CONVERT_HEADER_TOBIN - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLDIR.INC' - + CALL SYS_BINTIM(NEWEST_EXDATE//' '//NEWEST_EXTIME,NEWEST_EXBTIM) - + CALL SYS_BINTIM(NEWEST_DATE//' '//NEWEST_TIME,NEWEST_MSGBTIM) - + CALL SYS_BINTIM(SHUTDOWN_DATE//' '//SHUTDOWN_TIME,SHUTDOWN_BTIM) - + RETURN END - - - + + + SUBROUTINE CONVERT_ENTRY_TOBIN - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLDIR.INC' - + CALL SYS_BINTIM(EXDATE//' '//EXTIME,EX_BTIM) - + CALL SYS_BINTIM(DATE//' '//TIME,MSG_BTIM) - + CALL GET_MSGKEY(MSG_BTIM,MSG_KEY) - + RETURN END - - - - + + + + SUBROUTINE READACL(FILENAME,ACLENT,ACLLENGTH) C C SUBROUTINE READACL @@ -1467,26 +1467,26 @@ C FILENAME - Name of file to check. C ACLENT - String which will be large enough to hold ACL information. C IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLFILES.INC' - + INCLUDE '($ACLDEF)' - + CHARACTER ACLENT*(*),OUTPUT*80,ACLSTR*255,FILENAME*(*) - + CALL INIT_ITMLST ! Initialize item list CALL ADD_2_ITMLST(ACLLENGTH,ACL$C_READACL,%LOC(ACLENT)) CALL END_ITMLST(ACL_ITMLST) ! Get address of itemlist - + IER = SYS$CHANGE_ACL(,ACL$C_FILE,FILENAME,%VAL(ACL_ITMLST),,,) - + BIG = .NOT.IER IF (BIG) THEN IER = SYS$PARSE_ACL('(ID=*,ACCESS=NONE)',ACLENT,,) ACLLENGTH = ACL$S_ADDACLENT CTXT = 0 END IF - + DO ACC_TYPE=1,2 POINT = 1 OUTLEN = 0 @@ -1554,7 +1554,7 @@ C OUTPUT = ACLSTR(START_ID:END_ID)//',' OUTLEN = IDLEN + 2 ELSE IF (OUTLEN+IDLEN-1.EQ.80) THEN - WRITE (6,'(1X,A)') + WRITE (6,'(1X,A)') & OUTPUT(:OUTLEN-1)//ACLSTR(START_ID:END_ID) OUTLEN = 1 ELSE @@ -1566,28 +1566,28 @@ C END DO IF (OUTLEN.GT.1) WRITE (6,'(1X,A)') OUTPUT(:OUTLEN-2) END DO - + RETURN END - - - - + + + + SUBROUTINE CONVERT_INFFILE - + IMPLICIT INTEGER (A-Z) - + INCLUDE 'BULLUSER.INC' - + INCLUDE 'BULLFILES.INC' - + OPEN (UNIT=10,FILE=BULLINF_FILE,STATUS='OLD', & ACCESS='KEYED',RECORDTYPE='FIXED', & IOSTAT=IER,ORGANIZATION='INDEXED', & KEY=(1:12:CHARACTER)) - + INQUIRE (UNIT=10,RECORDSIZE=RECL) - + IF ((RECL-28)/16.GT.FLONG) THEN WRITE (6,'('' ERROR: Old data files have more folders'', & '' than was specified with BULLUSER.INC.'')') @@ -1599,42 +1599,42 @@ C CALL EXIT END IF END IF - + RECL = RECL/8 - + OPEN (UNIT=9,FILE=BULLINF_FILE,STATUS='NEW', & ACCESS='KEYED',RECORDTYPE='FIXED',RECORDSIZE=FOLDER_MAX*2+3, & IOSTAT=IER,ORGANIZATION='INDEXED', & KEY=(1:12:CHARACTER)) - + DO WHILE (IER.EQ.0) READ (10,IOSTAT=IER) TEMP_USER,((LAST_READ_BTIM(J,I),J=1,2),I=1,RECL) IF (IER.EQ.0) WRITE (9) TEMP_USER, & ((LAST_READ_BTIM(J,I),J=1,2),I=1,FOLDER_MAX) END DO - + CLOSE (UNIT=10,STATUS='DELETE') - + CLOSE (UNIT=9) - + RETURN END - - + + SUBROUTINE ERROR_AND_EXIT - + IMPLICIT INTEGER (A-Z) - + CALL ERRSNS(IDUMMY,IER) CALL SYS_GETMSG(IER) CALL ENABLE_CTRL_EXIT - + RETURN END - - - - + + + + SUBROUTINE COPY_ACL(INFILE,OUTFILE) C C SUBROUTINE COPY_ACL @@ -1643,29 +1643,29 @@ C FUNCTION: C Copy ACLs from one file to another file C IMPLICIT INTEGER (A-Z) - + INCLUDE '($ACLDEF)' - + CHARACTER*(*) INFILE,OUTFILE - + CALL INIT_ITMLST ! Initialize item list CALL ADD_2_ITMLST(4,ACL$C_ACLLENGTH,%LOC(ACLLENGTH)) ! Get length needed to store acl output CALL END_ITMLST(ACL_ITMLST) ! Get address of itemlist - + IER = SYS$CHANGE_ACL(,ACL$C_FILE,INFILE,%VAL(ACL_ITMLST),,,,,) - + CALL LIB$GET_VM(ACLLENGTH+8,ACLSTR) ! Create character string to CALL MAKE_CHAR(%VAL(ACLSTR),ACLLENGTH,ACLLENGTH) ! store acl - + CALL COPY_ACL1(INFILE,OUTFILE,%VAL(ACLSTR),ACLLENGTH) ! Pass location of string CALL LIB$FREE_VM(ACLLENGTH+8,ACLSTR) - + RETURN END - - + + SUBROUTINE COPY_ACL1(INFILE,OUTFILE,ACLENT,ACLLENGTH) C C SUBROUTINE COPY_ACL1 @@ -1674,17 +1674,17 @@ C FUNCTION: Called by COPY_ACL to actually do the copy. Need 2 routines C since must convert location of string into a character string. C IMPLICIT INTEGER (A-Z) - + INCLUDE '($ACLDEF)' - + CHARACTER ACLENT*(*),INFILE*(*),OUTFILE*(*) - + CALL INIT_ITMLST ! Initialize item list CALL ADD_2_ITMLST(ACLLENGTH,ACL$C_READACL,%LOC(ACLENT)) CALL END_ITMLST(ACL_ITMLST) ! Get address of itemlist IER = SYS$CHANGE_ACL(,ACL$C_FILE,INFILE,%VAL(ACL_ITMLST),,,,,) ! Read input file acl - + IF (.NOT.IER) THEN IER = SYS$PARSE_ACL('(ID=*,ACCESS=NONE)',ACLENT,,) IF (.NOT.IER) RETURN @@ -1697,7 +1697,7 @@ C CALL END_ITMLST(ACL1_ITMLST) ! Get address of itemlist IER = SYS$CHANGE_ACL & (,ACL$C_FILE,OUTFILE,%VAL(ACL1_ITMLST),,,) - + CALL INIT_ITMLST ! Initialize item list CALL ADD_2_ITMLST(ACLLENGTH,ACL$C_FNDACETYP,%LOC(ACLENT)) CALL END_ITMLST(ACL_ITMLST) ! Get address of itemlist @@ -1709,18 +1709,18 @@ C END DO RETURN END IF - + CALL INIT_ITMLST ! Initialize item list - + POINT = 1 DO WHILE (POINT.LT.ACLLENGTH) ! Transfer all acls to output file CALL ADD_2_ITMLST(ICHAR(ACLENT(POINT:POINT)),ACL$C_ADDACLENT, & %LOC(ACLENT(POINT:))) POINT = POINT + ICHAR(ACLENT(POINT:POINT)) END DO - + CALL END_ITMLST(ACL_ITMLST) ! Get address of itemlist IER = SYS$CHANGE_ACL(,ACL$C_FILE,OUTFILE,%VAL(ACL_ITMLST),,,) - + RETURN END diff --git a/folders/folders.go b/folders/folders.go index 9f4c8fa4303e78c65412b1efedbb3e67f425191c..2fd334bf4aad947db4f3fdce6adaa8cba8a31e51 100644 --- a/folders/folders.go +++ b/folders/folders.go @@ -5,7 +5,6 @@ import ( "database/sql" "embed" "errors" - "log" "os" "path" @@ -23,11 +22,12 @@ var fs embed.FS // Store is the store for folders. type Store struct { - db *sql.DB + user string + db *sql.DB } // Open opens the folders database. -func Open() (*Store, error) { +func Open(user string) (*Store, error) { fdir := path.Join(xdg.DataHome, "BULLETIN") err := os.MkdirAll(fdir, 0700) if err != nil { @@ -39,18 +39,18 @@ func Open() (*Store, error) { if err != nil { return nil, err } - m, err := migrate.NewWithSourceInstance("iofs", sqldir, "sqlite://"+fdb) + m, err := migrate.NewWithSourceInstance("iofs", sqldir, "sqlite://"+fdb+"?_pragma=foreign_keys(1)") if err != nil { - log.Fatal(err) + return nil, err } err = m.Up() - if err != nil { + if err != nil && err != migrate.ErrNoChange { return nil, err } m.Close() - store := &Store{} - store.db, err = sql.Open("sqlite", fdb) + store := &Store{user: user} + store.db, err = sql.Open("sqlite", "file://"+fdb+"?_pragma=foreign_keys(1)") if err != nil { return nil, errors.New("bulletin database problem") } diff --git a/folders/sql/1_create_table.down.sql b/folders/sql/1_create_table.down.sql index df3837bed721d8c7b22e2eaf5ded9a401202c71f..4070921fe631b62d42878b713da994824eacb233 100644 --- a/folders/sql/1_create_table.down.sql +++ b/folders/sql/1_create_table.down.sql @@ -1 +1,14 @@ +--- Dropped in reverse order to deal with foreign keys. +DROP TRIGGER co_owners_after_update_update_at; +DROP TABLE co_owners; + +DROP TRIGGER folders_before_delete_protect; +DROP TRIGGER folders_after_update_update_at; +DROP TRIGGER folders_before_update_validate; +DROP TRIGGER folders_before_insert_validate; DROP TABLE folders; + +DROP TRIGGER users_before_delete_protect; +DROP TRIGGER users_before_update_protect; +DROP TRIGGER users_after_update_update_at; +DROP TABLE users; diff --git a/folders/sql/1_create_table.up.sql b/folders/sql/1_create_table.up.sql index 232fd99f8a09a6d1edd860f6aecd5e56ec7d6d55..6094a1cdb7c4f74ebb93615ae3e20e45ec1e146e 100644 --- a/folders/sql/1_create_table.up.sql +++ b/folders/sql/1_create_table.up.sql @@ -1,18 +1,101 @@ +CREATE TABLE users ( + login VARCHAR(25) NOT NULL PRIMARY KEY, + name VARCHAR(53) NOT NULL, + admin INT DEFAULT 0, + create_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL +) WITHOUT ROWID; + +CREATE TRIGGER users_after_update_update_at + AFTER UPDATE ON users FOR EACH ROW + WHEN NEW.update_at = OLD.update_at --- avoid infinite loop +BEGIN + UPDATE users SET update_at=CURRENT_TIMESTAMP WHERE login=NEW.login; +END; + +INSERT INTO users (login, name, admin) + VALUES ('SYSTEM', 'System User', 1); + +CREATE TRIGGER users_before_update_protect + AFTER UPDATE ON users FOR EACH ROW + WHEN OLD.login = 'SYSTEM' AND (NEW.login != OLD.login OR NEW.admin != 1) +BEGIN + SELECT RAISE (ABORT, 'SYSTEM user is protected'); +END; + +CREATE TRIGGER users_before_delete_protect + BEFORE DELETE on users FOR EACH ROW + WHEN OLD.login = 'SYSTEM' +BEGIN + SELECT RAISE (ABORT, 'SYSTEM user is protected'); +END; + CREATE TABLE folders ( - name VARCHAR(25) NOT NULL UNIQUE, + name VARCHAR(25) NOT NULL PRIMARY KEY, always INT DEFAULT 0, brief INT DEFAULT 0, - description VARCHAR(53), - co_owners TEXT, + description VARCHAR(53) NOT NULL, notify INT DEFAULT 0, - owner TEXT, + owner VARCHAR(25) REFERENCES users(login) ON UPDATE CASCADE, readnew INT DEFAULT 0, shownew INT DEFAULT 0, system INT DEFAULT 0, expire INT DEFAULT 14, - visibility TEXT DEFAULT "public" -); + visibility TEXT DEFAULT 'public', + create_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL +) WITHOUT ROWID; + +CREATE TRIGGER folders_before_insert_validate + BEFORE INSERT on folders +BEGIN + SELECT + CASE + WHEN NEW.name != UPPER(NEW.name) OR NEW.name GLOB '*[^A-Z0-9_-]*' THEN + RAISE (ABORT, 'Invalid folder name') + END; +END; + +CREATE TRIGGER folders_before_update_validate + BEFORE UPDATE on folders +BEGIN + SELECT + CASE + WHEN NEW.name != UPPER(NEW.name) OR NEW.name GLOB '*[^A-Z0-9_-]*' THEN + RAISE (ABORT, 'Invalid folder name') + WHEN OLD.name = 'GENERAL' AND OLD.name != NEW.name THEN + RAISE (ABORT, 'GENERAL folder is protected') + END; +END; + +CREATE TRIGGER folders_after_update_update_at + AFTER UPDATE ON folders FOR EACH ROW + WHEN NEW.update_at = OLD.update_at --- avoid infinite loop +BEGIN + UPDATE folders SET update_at=CURRENT_TIMESTAMP WHERE name=NEW.name; +END; + +CREATE TRIGGER folders_before_delete_protect + BEFORE DELETE on folders FOR EACH ROW + WHEN OLD.name = 'GENERAL' +BEGIN + SELECT RAISE (ABORT, 'GENERAL folder is protected'); +END; INSERT INTO folders (name, description, system, shownew, owner) - VALUES ("GENERAL", "Default general bulletin folder.", 1, 1, "SYSTEM"); + VALUES ('GENERAL', 'Default general bulletin folder.', 1, 1, 'SYSTEM'); + +CREATE TABLE co_owners ( + folder VARCHAR(25) REFERENCES folders(name) ON UPDATE CASCADE, + owner VARCHAR(25) REFERENCES users(login) ON UPDATE CASCADE, + create_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY (folder, owner) +) WITHOUT ROWID; +CREATE TRIGGER co_owners_after_update_update_at + AFTER UPDATE ON co_owners FOR EACH ROW + WHEN NEW.update_at = OLD.update_at --- avoid infinite loop +BEGIN + UPDATE co_owners SET update_at=CURRENT_TIMESTAMP WHERE folder=NEW.folder AND owner=NEW.owner; +END; diff --git a/repl/command.go b/repl/command.go index 378d46a922cf2ea6a93dd799b97ff66bb3f67696..77f5d1bf9a2d8ea90ad86092bd3a1de5b084fe1d 100644 --- a/repl/command.go +++ b/repl/command.go @@ -9,7 +9,7 @@ var commands = dclish.Commands{ contains the message. Otherwise, BULLETIN will prompt for the text. BULLETIN will ask for an expiration date and a header to contain the topic of the message. - + Format: ADD [file-name]`, MaxArgs: 1, @@ -20,8 +20,8 @@ with the /BROADCAST qualifier. If specified, all terminals are sent the message. Otherwise, only users are sent the message.`, }, "/BELL": { - Description: `This option is restricted to privileged users. It is used in conjunction -with the /BROADCAST qualifier. If specified, the bell is rung on the + Description: `This option is restricted to privileged users. It is used in conjunction +with the /BROADCAST qualifier. If specified, the bell is rung on the terminals when the message is broadcasted.`, }, "/BROADCAST": { @@ -31,12 +31,12 @@ in at the time. If the folder is remote, a message will be broadcast on all nodes which are connected to that folder, unless /LOCAL is specified. A node which does not have BULLCP running cannot have a message broadcasted to it, (even though it is able to create a remote folder). - + See also /ALL and /BELL.`, }, "/CLUSTER": { Description: `/[NO]CLUSTER - + This option specifies that broadcasted messages should be sent to all nodes in the cluster. /CLUSTER is the default.`, }, @@ -48,7 +48,7 @@ BULLETIN command line.`, }, "/EXPIRATION": { Description: `/EXPIRATION=time - + Specifies the time at which the message is to expire. Either absolute time: [dd-mmm-yyyy] hh:mm:ss, or delta time: dddd [hh:mm:ss] can be used.`, @@ -62,7 +62,7 @@ suppressed with /NOINDENT.`, }, "/FOLDER": { Description: `/FOLDER=(foldername,[...]) - + Specifies the foldername into which the message is to be added. Does not change the current selected folder. Folders can be either local or remote folders. Thus, a nodename can precede the foldername (this @@ -74,11 +74,11 @@ Specifying remote nodes is only possible if that remote node is running a special BULLCP process. If it isn't, the only way to add messages to that remote node is via the /NODE command. However, /FOLDER is a much quicker method, and much more versatile. - + You can specify logical names which translate to one or more folder names. I.e. $ DEFINE ALL_FOLDERS "VAX1,VAX2,VAX3", and then specify ALL_FOLDERS after /FOLDER=. Note that the quotation marks are required. - + When using /FOLDER for remote nodes, proxy logins are used to determine if privileged options are allowed. If they are not allowed, the message will still be added, but without the privileged settings.`, @@ -89,7 +89,7 @@ message is broadcasted ONLY on the local node.`, }, "/NODES": { Description: `/NODES=(nodes[,...]) - + Specifies to send the message to the listed DECNET nodes. The BULLETIN utility must be installed properly on the other nodes. (See installation notes). You can specify a different username to use at the @@ -97,11 +97,11 @@ other nodes by either using the USERNAME qualifier, or by specifying the nodename with 2 semi-colons followed by the username, i.e. nodename::username. If you specify a username, you will be prompted for the password of the account on the other nodes. - + Additionally, you can specify logical names which translate to one or more node names. I.e. $ DEFINE ALL_NODES "VAX1,VAX2,VAX3", and then specify /NODES=ALL_NODES. Note that the quotation marks are required. - + NOTE: It is preferable to use /FOLDER instead of /NODE if possible, since adding messages via /FOLDER is much quicker.`, }, @@ -120,7 +120,7 @@ user has privileges.`, }, "/SUBJECT": { Description: `/SUBJECT=description - + Specifies the subject of the message to be added.`, }, "/SHUTDOWN": { @@ -128,12 +128,12 @@ Specifies the subject of the message to be added.`, This option is restricted to privileged users. If specified, message will be automatically deleted after a computer shutdown has occurred. This option is restricted to SYSTEM folders. - + If the bulletin files are shared between cluster nodes, the message will be deleted after the node on which the message was submitted from is rebooted. If you wish the message to be deleted after a different node reboots, you have the option of specifying that node name. - + NOTE: If the folder is a remote folder, the message will be deleted after the remote node reboots, not the node from which the message was added. The nodename cannot be specified with a remote folder.`, @@ -160,7 +160,7 @@ useful for scanning a long message.`, }, "/HEADER": { Description: `/[NO]HEADER - + Specifies that if a message header exists, the header will be shown. If /HEADER or /NOHEADER is specified, the setting will apply for all further reads in the selected folder. The default is /HEADER.`, @@ -185,7 +185,7 @@ are to be changed. If the text of the message is to be changed, a file can be specified which contains the text. If the editor is used for changing the text, the old message text will be extracted. This can be suppressed by the qualifier /NEW. - + Format: CHANGE [file-name]`, MaxArgs: 1, @@ -203,7 +203,7 @@ added /EDIT to your BULLETIN command line.`, }, "/EXPIRATION": { Description: `/EXPIRATION[=time] - + Specifies the time at which the message is to expire. Either absolute time: [dd-mmm-yyyy] hh:mm:ss, or delta time: dddd [hh:mm:ss] can be used. If no time is specified, you will be prompted for the time.`, @@ -223,12 +223,12 @@ new text is to be read in.`, }, "/NUMBER": { Description: `/NUMBER=message_number[-message_number1] - -Specifies the message or messages to be replaced. If this qualifier is + +Specifies the message or messages to be replaced. If this qualifier is omitted, the message that is presently being read will be replaced. A range of messages can be specified, i.e. /NUMBER=1-5. Only the expiration date and message headers can be changed if a range is specified. - + The key words CURRENT and LAST can also be specified in the range, in place of an actual number, i.e. CURRENT-LAST, 1-CURRENT, etc.`, }, @@ -241,7 +241,7 @@ shutdown. This option is restricted to SYSTEM folders.`, }, "/SUBJECT": { Description: `/SUBJECT=description - + Specifies the subject of the message to be added.`, }, "/SYSTEM": { @@ -256,15 +256,15 @@ privileged command and is restricted to SYSTEM folders.`, "COPY": { Description: `Copies a message to another folder without deleting it from the current folder. - + Format: - + COPY folder-name [message_number][-message_number1] - + The folder-name is the name of the folder to which the message is to be copied to. Optionally, a range of messages which are to be copied can be specified following the folder name, i.e. COPY NEWFOLDER 2-5. - + The key words CURRENT and LAST can also be specified in the range, in place of an actual number, i.e. CURRENT-LAST, 1-CURRENT, etc.`, MinArgs: 1, @@ -275,14 +275,14 @@ in place of an actual number, i.e. CURRENT-LAST, 1-CURRENT, etc.`, }, "/HEADER": { Description: `/[NO]HEADER - + Valid only if destination folder is a news group. Specifies that header of message is to be included with the text when the text is copied. The default is /NOHEADER.`, }, "/MERGE": { Description: `Specifies that the original date and time of the copied messages are -saved and that the messages are placed in correct chronological order +saved and that the messages are placed in correct chronological order in the new folder. This operation is lengthy if the new folder is large.`, }, "/ORIGINAL": { @@ -300,14 +300,14 @@ to specified users. Once created, that message is automatically selected (see information on SELECT command). The commands that can be used to modify the folder's characteristics are: MODIFY, REMOVE, SET ACCESS, SET BBOARD, SET NODE, and SET SYSTEM. - + Format: CREATE folder-name - + The folder-name is limited to 25 letters and must not include spaces or characters that are also invalid in filenames (this is because the folder is stored in a file name created with the folder name). - + NOTE: Creation of folders may be a restricted command if the installer has elected to install it as such. This is done by modifying BULLCOM.CLD.`, @@ -332,26 +332,26 @@ more information.)`, }, "/DESCRIPTION": { Description: `/DESCRIPTION=description - + Specifies the description of the folder, which is displayed using the SHOW FOLDER command. If omitted, you are prompted for a description. - + If this folder is to receive messages from a network mailing list via the BBOARD feature, and you wish to use the POST and RESPOND/LIST commands, the address of the mailing list should be included in the description. This is done by enclosing the address using <> and placing it at the end of the description, i.e. - + INFOVAX MAILING LIST <INFO-VAX@KL.SRI.COM> - + If a mailer protocol is needs to be added to the network address in order for it to be sent by VMS MAIL, i.e. protocol%"address", the appropriate protocol can be specified by either hardcoding it into the file BULLNEWS.INC before compiling BULLETIN, or by defining the system logical name BULL_NEWS_MAILER (it is the same protocol used by the NEWS feature in order to respond to NEWS messages). The default protocol is -IN%. If desired, you can specify the protocol with the address, i.e. - +IN%. If desired, you can specify the protocol with the address, i.e. + INFOVAX MAILING LIST <IN%"INFO-VAX@KL.SRI.COM">`, }, "/ID": { @@ -360,12 +360,12 @@ identifier. The creator's process must have the identifier presently assigned to it. Any process which has that identifier assigned to it will be able to control the folder as if it were the folder's owner. This is used to allow more than one use to control a folder. - + Note: This feature will not work during remote access to the folder.`, }, "/NODE": { Description: `/NODE=node - + Specifies that the folder is a remote folder at the specified node. A remote folder is a folder in which the messages are actually stored on a folder at a remote DECNET node. The specified node is checked to @@ -379,7 +379,7 @@ between more than one node. This capability is only present if the BULLCP process is running on the remote node via the BULL/STARTUP command. If the remote folder name is different from the local folder name, the remote folder name is specified using the /REMOTENAME qualifier. - + NOTE: If a message is added to a remote node, the message is stored immediately. However, a user logging into another node might not be immediately alerted that the message is present. That information is @@ -433,7 +433,7 @@ respect to adding or modifying messages. All users can read the folder.`, Description: `Specifies that the folder is a SYSTEM folder. A SYSTEM folder is allowed to have SYSTEM and SHUTDOWN messages added to it. By default, the GENERAL folder is a SYSTEM folder. This is a privileged command. - + If this is a remote folder, /SYSTEM cannot be specified unless the folder at the other node is also a SYSTEM folder.`, }, @@ -443,9 +443,9 @@ folder at the other node is also a SYSTEM folder.`, Description: `Displays the beginning of the message you are currently reading. If you are reading a long message and want to display the first part of the message again, you can enter the CURRENT command. - + Format: - + CURRENT`, Flags: dclish.Flags{ "/EDIT": { @@ -454,7 +454,7 @@ useful for scanning a long message.`, }, "/HEADER": { Description: `/[NO]HEADER - + Specifies that if a message header exists, the header will be shown. If /HEADER or /NOHEADER is specified, the setting will apply for all further reads in the selected folder. The default is /HEADER.`, @@ -468,15 +468,15 @@ delete a message. Note that the message is not deleted immediately, but its expiration is set 15 minutes in the future. This is to allow a user to recover the message using the UNDELETE command. If you want the message deleted immediately, use the /IMMEDIATE qualifier. - + Format: DELETE [message_number][-message_number1] - + The message's relative number is found by the DIRECTORY command. It is possible to delete a range of messages by specifying two numbers separated by a dash, i.e. DELETE 1-5. However, a range cannot be specified if the folder is remote. - + The key words CURRENT and LAST can also be specified in the range, in place of an actual number, i.e. CURRENT-LAST, 1-CURRENT, etc.`, MaxArgs: 1, @@ -491,7 +491,7 @@ remote folder at a time.`, }, "/NODES": { Description: `/NODES=(nodes[,...]) - + Specifies to delete the message at the listed DECNET nodes. The BULLETIN utility must be installed properly on the other nodes. You can specify a different username to use at the other nodes by either using the @@ -500,14 +500,14 @@ followed by the username, i.e. nodename::username. If you specify a username, you will be prompted for the password of the account on the other nodes. The /SUBJECT must be specified to identify the specific message that is to be deleted. - + Additionally, you can specify logical names which translate to one or more node names. I.e. $ DEFINE ALL_NODES "VAX1,VAX2,VAX3", and then specify /NODES=ALL_NODES. Note that the quotation marks are required.`, }, "/SUBJECT": { Description: `/SUBJECT=subject - + Specifies the subject of the bulletin to be deleted at a remote DECNET node. The DECNET node must be specified with the /NODE qualifier. The specified subject need not be the exact subject of the message. @@ -524,11 +524,11 @@ on other DECNET nodes via the /NODE qualifier.`, "DIRECTORY": { Description: `Lists a summary of the messages. The message number, submitter's name, date, and subject of each message is displayed. - + Format: - + DIRECTORY [folder] - + If a folder is specified, that folder is selected before the directory is listed. Unless otherwise specified, listing starts with the first newest message. If there are no new messages, listing will start at the @@ -550,7 +550,7 @@ folder.`, }, "/END": { Description: `/END=message_number - + Indicates the last message number you want to display.`, }, "/FOLDERS": { @@ -572,7 +572,7 @@ to be read. To see all messages, use either /ALL, or reselect the folder.`, }, "/SEEN": { - Description: `Lists messages that have been seen (indicated by a greater than sign). + Description: `Lists messages that have been seen (indicated by a greater than sign). Using /SEEN is equivalent to selecting the folder with /SEEN, i.e. only seen messages will be shown and be able to be read. To see all messages, use either /ALL, or reselect the folder.`, @@ -600,27 +600,27 @@ are to be displayed. This cannot be used in conjunction with /MARKED.`, }, "/SEARCH": { Description: `/SEARCH=[string] - + Specifies that only messages which contain the specified string are to be displayed. This cannot be used in conjunction with /MARKED. If no string is specified, the previously specified string is used.`, }, "/SINCE": { Description: `/SINCE=date - + Displays a listing of all the messages created on or after the specified date. If no date is specified, the default is TODAY.`, }, "/START": { Description: `/START=message_number - + Indicates the first message number you want to display. For example, to display all the messages beginning with number three, enter the command line DIRECTORY/START=3. Not valid with /FOLDER.`, }, "/SUBJECT": { Description: `/SUBJECT=[string] - + Specifies that only messages which contain the specified string in it's subject header are to be displayed. This cannot be used in conjunction with /MARKED. If no string is specified, the previously specified string @@ -639,13 +639,13 @@ is used.`, Description: `Copies the current message to the named file. The file-name parameter is required. If the file exists, the message is appended to the file, unless the /NEW qualifier is specified. - + Format: FILE filename [message_number][-message_number1],[...] - + A range of messages to be copied can optionally be specified, i.e. FILE 2-5. - + The key words CURRENT and LAST can also be specified in the range, in place of an actual number, i.e. CURRENT-LAST, 1-CURRENT, etc.`, MinArgs: 1, @@ -659,8 +659,8 @@ in place of an actual number, i.e. CURRENT-LAST, 1-CURRENT, etc.`, }, "/HEADER": { Description: `/[NO]HEADER - -Controls whether a header containing the owner, subject, and date of the + +Controls whether a header containing the owner, subject, and date of the message is written in the file. The default is to write the header.`, }, "/NEW": { @@ -677,8 +677,10 @@ file exists, the file would be appended to that file.`, }, "HELP": { Description: `To obtain help on any topic, type: - - HELP topic`, + + HELP topic + +Type "HELP Topics" to get a list of topics.`, MaxArgs: 1, Action: ActionHelp, }, @@ -688,7 +690,7 @@ INDEX command is re-entered while the listing is in progress, the listing will skip to the next folder. This is useful for skipping a particular folder. It also can be used to continue the listing from where one left off after one has read a message. - + Format: INDEX`, Action: ActionIndex, @@ -705,7 +707,7 @@ with /UNMARKED, i.e. only unmarked messages will be shown and be able to be read.`, }, "/SEEN": { - Description: `Lists messages that have been seen (indicated by a greater than sign). + Description: `Lists messages that have been seen (indicated by a greater than sign). Using /SEEN is equivalent to selecting the folder with /SEEN, i.e. only seen messages will be shown and be able to be read.`, }, @@ -755,7 +757,7 @@ first folder.`, }, "LAST": { Description: `Displays the last message in the current folder. - + Format: LAST`, Flags: dclish.Flags{ @@ -765,7 +767,7 @@ useful for scanning a long message.`, }, "/HEADER": { Description: `/[NO]HEADER - + Specifies that if a message header exists, the header will be shown. If /HEADER or /NOHEADER is specified, the setting will apply for all further reads in the selected folder. The default is /HEADER.`, @@ -775,11 +777,11 @@ further reads in the selected folder. The default is /HEADER.`, "MAIL": { Description: `Invokes the VAX/VMS Personal Mail Utility (MAIL) to send the message which you are reading to the specified recipients. - + Format: - + MAIL recipient-name[s] - + The input for the recipient name is exactly the same format as used by the MAIL command at DCL level. Note that this means when specifying an address that has quotes, in order to pass the quotes you must specify @@ -794,16 +796,16 @@ mailing it.`, }, "/HEADER": { Description: `/[NO]HEADER - -Controls whether a header containing the owner, subject, and date of the + +Controls whether a header containing the owner, subject, and date of the message is written in the mail. The default is to write the header.`, }, "/SUBJECT": { Description: `/SUBJECT=text - + Specifies the subject of the mail message. If the text consists of more than one word, enclose the text in quotation marks ("). - + If you omit this qualifier, the description of the message will be used as the subject.`, }, @@ -815,12 +817,12 @@ displayed with an asterisk in the left hand column of the directory listing. A marked message can serve as a reminder of important information. The UNMARK command sets the current or message-id message as unmarked. - + Format: - + MARK [message-number or numbers] UNMARK [message-number or numbers] - + NOTE: The list of marked messages are stored in a file username.BULLMARK. The files are created in the directory pointed to by the logical name BULL_MARK. If BULL_MARK is not defined, SYS$LOGIN will be used.`, @@ -832,12 +834,12 @@ displayed with an asterisk in the left hand column of the directory listing. A marked message can serve as a reminder of important information. The UNMARK command sets the current or message-id message as unmarked. - + Format: - + MARK [message-number or numbers] UNMARK [message-number or numbers] - + NOTE: The list of marked messages are stored in a file username.BULLMARK. The files are created in the directory pointed to by the logical name BULL_MARK. If BULL_MARK is not defined, SYS$LOGIN will be used.`, @@ -845,23 +847,23 @@ BULL_MARK. If BULL_MARK is not defined, SYS$LOGIN will be used.`, }, "MODIFY": { Description: `Modifies the database information for the current folder. Only the -owner of the folder or a user with privileges can use this command. - +owner of the folder or a user with privileges can use this command. + Format: - + MODIFY`, Action: ActionModify, Flags: dclish.Flags{ "/DESCRIPTION": { Description: `Specifies a new description for the folder. You will be prompted for the text of the description. - + NOTE: If this folder is to receive messages from a network mailing list via the BBOARD feature, and you wish to use the POST and RESPOND/LIST commands, the address of the mailing list should be included in the description. This is done by enclosing the address using <> and placing it at the end of the description, i.e. - + INFOVAX MAILING LIST <IN%"INFO-VAX@KL.SRI.COM">`, }, "/ID": { @@ -870,17 +872,17 @@ identifier. The creator's process must have the identifier presently assigned to it. Any process which has that identifier assigned to it will be able to control the folder as if it were the folder's owner. This is used to allow more than one use to control a folder. - + Note: This feature will not work during remote access to the folder.`, }, "/NAME": { Description: `/NAME=foldername - + Specifies a new name for the folder.`, }, "/OWNER": { Description: `/OWNER=username - + Specifies a new owner for the folder. If the owner does not have privileges, BULLETIN will prompt for the password of the new owner account in order to okay the modification. See also /ID.`, @@ -890,17 +892,17 @@ account in order to okay the modification. See also /ID.`, "MOVE": { Description: `Moves a message to another folder and deletes it from the current folder. - + Format: - + MOVE folder-name [message_number][-message_number1] - + The folder-name is the name of the folder to which the message is to be be moved to. Optionally, a range of messages which are to be moved can be specified following the folder name, i.e. COPY NEWFOLDER 2-5. However, if the old folder is remote, they will be copied but not deleted, as only one message can be delted from a remote folder at a time. - + The key words CURRENT and LAST can also be specified in the range, in place of an actual number, i.e. CURRENT-LAST, 1-CURRENT, etc.`, MinArgs: 1, @@ -913,7 +915,7 @@ message can be deleted from a remote folder at a time.`, }, "/MERGE": { Description: `Specifies that the original date and time of the moved messages are -saved and that the messages are placed in correct chronological order +saved and that the messages are placed in correct chronological order in the new folder. This operation is lengthy if the new folder is large.`, }, "/ORIGINAL": { @@ -934,7 +936,7 @@ useful for scanning a long message.`, }, "/HEADER": { Description: `/[NO]HEADER - + Specifies that if a message header exists, the header will be shown. If /HEADER or /NOHEADER is specified, the setting will apply for all further reads in the selected folder. The default is /HEADER.`, @@ -942,22 +944,22 @@ further reads in the selected folder. The default is /HEADER.`, }, }, "PRINT": { - Description: `Queues a copy of the message you are currently reading (or have + Description: `Queues a copy of the message you are currently reading (or have just read) for printing. The file created by the PRINT command is not released to the print queue until you exit, unless you add the qualifier /NOW or change one of the print job's qualifiers. Multiple messages are concatenated into one print job. - + Format: - + PRINT [message_number][-message_number1],[...] - + A range of messages to be printed can optionally be specified, i.e. PRINT 2-5. - + The key words CURRENT and LAST can also be specified in the range, in place of an actual number, i.e. CURRENT-LAST, 1-CURRENT, etc. - + NOTE: The qualifier /PRINT is present on the DIRECTORY command. This provides more flexibility than is present with the PRINT command. For example, if you want to print all messages with a particular string in @@ -983,13 +985,13 @@ queue specifying the new form type as the mounted form.)`, }, "/HEADER": { Description: `/[NO]HEADER - -Controls whether a header containing the owner, subject, and date of the + +Controls whether a header containing the owner, subject, and date of the message is printed at the beginning. The default is to write the header.`, }, "/NOTIFY": { Description: `/[NO]NOTIFY - + Indicates that you will be notified by a broadcast message when the file or files have been printed. If /NONOTIFY is specified, there is no notification. The default is /NOTIFY.`, @@ -1000,7 +1002,7 @@ command during this session to the printer.`, }, "/QUEUE": { Description: `/QUEUE=queue_name - + The name of the queue to which a message is to be sent. If the /QUEUE qualifier is not specified, the message is queued to SYS$PRINT.`, }, @@ -1012,18 +1014,18 @@ the first time you enter the command, the first message in the folder will be displayed. However, if there are new messages, the first new message will be displayed. Each time you enter the command, the next page, or if there are no more pages, the next message will be displayed. - + Format: READ [message-number] - + The message's relative number is found by the DIRECTORY command. If you specify a number greater than the number of messages in the folder, the last message in the folder will be displayed. - + NOTE: The READ command can be abbreviated by omitting the READ command, i.e. typing the command "2" is equivalent to "READ 2", and simply -hitting the <RETURN> key is equivalent to "READ". - +hitting the <RETURN> key is equivalent to "READ". + BULLETIN normally stores only the latest message that has been read per folder. It can optionally store and display which messages have been read in a folder on a per message basis. For information on this, see @@ -1040,7 +1042,7 @@ useful for scanning a long message.`, }, "/HEADER": { Description: `/[NO]HEADER - + Specifies that if a message header exists, the header will be shown. If /HEADER or /NOHEADER is specified, the setting will apply for all further reads in the selected folder. The default is /HEADER.`, @@ -1077,7 +1079,7 @@ reselect the folder.`, }, "/PAGE": { Description: `/[NO]PAGE - + Specifies that the display of the message will pause when it reaches the end of the page. If /NOPAGE is specified, the whole message will be displayed. This is useful for terminals that can store more than one @@ -1086,7 +1088,7 @@ the contents of the terminal's memory.`, }, "/SINCE": { Description: `/SINCE=date - + Specifies to read the first message created on or after the specified date. If no date is specified, the default is TODAY.`, }, @@ -1095,7 +1097,7 @@ date. If no date is specified, the default is TODAY.`, "REMOVE": { Description: `Removes a folder. Only the owner of a folder or a privileged user can remove the folder. - + Format: REMOVE folder-name`, MinArgs: 1, @@ -1105,7 +1107,7 @@ remove the folder. Description: `Adds message with subject of message being the subject of the currently read message with "RE:" preceeding it. Format and qualifiers is exactly the same as the ADD command except for /NOINDENT and /EXTRACT. - + Format: REPLY [file-name]`, MaxArgs: 1, @@ -1124,10 +1126,10 @@ This can be suppressed with /NOINDENT.`, "RESPOND": { Description: `Invokes the VAX/VMS Personal Mail Utility (MAIL) to send a reply mail message to the owner of the currently read message. - + Format: RESPOND [file-name] - + If you wish to use another method for sending the mail, define BULL_MAILER to point to a command procedure. This procedure will then be executed in place of MAIL, and the parameters passed to it are the username and subject @@ -1164,10 +1166,10 @@ See the help topic POST Signature_file for signature information.`, }, "/SUBJECT": { Description: `/SUBJECT=text - + Specifies the subject of the mail message. If the text consists of more than one word, enclose the text in quotation marks ("). - + If you omit this qualifier, the description of the message will be used as the subject preceeded by "RE: ".`, }, @@ -1180,11 +1182,11 @@ as the subject preceeded by "RE: ".`, "SEARCH": { Description: `Searches the currently selected folder for the message containing the first occurrence of the specified text string. - + Format: - + SEARCH [search-string] - + The search starts from the first message in the current folder. The search includes both the text of the message, and the description header. If a "search-string" is not specified, a search is made using the @@ -1199,7 +1201,7 @@ search can be aborted by typing a CTRL-C.`, }, "/FOLDER": { Description: `/FOLDER=(folder,[...]) - + Specifies a list of folders to be searched. The search will start by selecting the first folder in the list and searching the messages for a match. If, during a search, no more matches or messages are found, @@ -1219,7 +1221,7 @@ message.`, }, "/START": { Description: `/START=message_number - + Specifies the message number to start the search at.`, }, "/SUBJECT": { @@ -1233,17 +1235,17 @@ track of messages on a per message basis. Seen messages are displayed with a greater than sign in the left hand column of the directory listing. Once you have used the SEEN command once, messages will be automatically be set as being SEEN when they are read. The UNSEEN -command sets the current or message-id message as unseen. - +command sets the current or message-id message as unseen. + Format: - + SEEN [message-number or numbers] UNSEEN [message-number or numbers] - + If you have used the SEEN command and wish to disable the automatic marking of messages in regular folders as SEEN when they are read, type the command SEEN/NOREAD. To reenable, simply use the SEEN command again. - + NOTE: The list of SEEN messages are stored in a file username.BULLMARK. NEWSMARK. The files are created in the directory pointed to by the logical name BULL_MARK. If BULL_MARK is not defined, @@ -1257,17 +1259,17 @@ track of messages on a per message basis. Seen messages are displayed with a greater than sign in the left hand column of the directory listing. Once you have used the SEEN command once, messages will be automatically be set as being SEEN when they are read. The UNSEEN -command sets the current or message-id message as unseen. - +command sets the current or message-id message as unseen. + Format: - + SEEN [message-number or numbers] UNSEEN [message-number or numbers] - + If you have used the SEEN command and wish to disable the automatic marking of messages in regular folders as SEEN when they are read, type the command SEEN/NOREAD. To reenable, simply use the SEEN command again. - + NOTE: The list of SEEN messages are stored in a file username.BULLMARK. NEWSMARK. The files are created in the directory pointed to by the logical name BULL_MARK. If BULL_MARK is not defined, @@ -1281,19 +1283,19 @@ folder. Once a folder has been selected, all commands, i.e. DIRECTORY, READ, etc. will apply only to those messages. Use the CREATE command to create a folder. Use the DIRECTORY/FOLDER command to see the list of folders that have been created. - + Format: - + SELECT [node-name::][folder-name] - + The complete folder name need not be specified. BULLETIN will try to find the closest matching name. I.e. INFOV can be used for INFOVAX. - + Omitting the folder name will select the default general messages. - + The node name can be specified only if the remote node has the special BULLCP process running (invoked by BULLETIN/STARTUP command.) - + After selecting a folder, the user will notified of the number of unread messages, and the message pointer will be placed at the first unread message.`, @@ -1310,20 +1312,20 @@ to be reselected.`, "SET": { Description: `The SET command is used with other commands to define or change characteristics of the BULLETIN Utility. - + Format: - + SET option`, Flags: dclish.Flags{ "ACCESS": { Description: `Controls access to a private folder. A private folder can only be selected by users who have been granted access. Only the owner of that folder is allowed to grant access. - + Format: - + SET [NO]ACCESS id-name [folder-name] - + The id-name can be one or more ids from the system Rights Database for which access is being modified. It can also be a file name which contains a list of ids. For more information concerning usage of @@ -1331,7 +1333,7 @@ private folders, see HELP CREATE /PRIVATE. NOTE: Access is created via ACLs. If a user's process privileges are set to override ACLs, that user will be able to access the folder even if access has not been granted. - + It is suggested that if you plan on granting access to many users, that you create an id using the AUTHORIZE utility and then use the SET ACCESS command to grant access to that id. Then, you can use the GRANT/ID @@ -1341,7 +1343,7 @@ running into system quota when checking for acls on a file with a large amount of acls. It is also means that you don't have to remember to remove the access for that user from a folder if that user is removed from the system. - + A user with BULLETIN privileges (see HELP SET PRIV) will be able to select a protected folder regardless of the access settings. However, a user without explicit access will not receive login notifications of new @@ -1357,7 +1359,7 @@ comma must be enclosed in quotes. UICs can contain wildcards, i.e. id SYS$NODE_nodename, where nodename is the decnet nodename. Thus, by specifing this id, a folder can be restricted to a specific node, which is useful when the folder is shared among nodes in a cluster. - + Alternatively, the id-name can be a filename which contains a list of ids. The filename should be preceeded by a "@". If the suffix is not specified, it will be assumed that the suffix is ".DIS" . @@ -1367,9 +1369,9 @@ is not specified, the folder will no longer be private. If /READ is specified, all users will have read access, but only privileged users will have write access (of course non-privileged users can gain access via a later SET ACCESS command.) - + Format: - + SET ACCESS /ALL [folder-name] 3 /READ Specifies that access to the folder will be limited to being able to @@ -1394,9 +1396,9 @@ than just once. Non-SYSTEM message will also be displayed every time the user actually reads that message (or a later one). This feature is meant for messages which are very important, and thus you want to make sure they are read. - + Format: - + SET [NO]ALWAYS`, }, "BBOARD": { @@ -1413,19 +1415,19 @@ which is spawned to read the mail. The parameters and the suggested values are: PQL_DPGFLQUOTA = 10000, PQL_DWSQUOTA = 500, and PQL_DFILLM = 30. If you are not using the BULLCP process, the subprocess limit for users must be at least 2. - + Format: - + SET BBOARD [username] - + BBOARD cannot be set for remote folders. See also the command SET DIGEST for options on formatting BBOARD messages. - + If BULLCP is running, BBOARD is updated every 15 minutes. If you want to length this period, define BULL_BBOARD_UPDATE to be the number of minutes, between updates. I.e. DEFINE/SYSTEM BULL_BBOARD_UPDATE "30" will cause the updates to be don every 30 minutes. - + NOTE: If you want to control the expiration date on a per message basis, you can do so by adding a special header line to the message. The form is Expires: or X-Expires: followed by the date in the form DD MMM YYYY. @@ -1433,12 +1435,12 @@ The time will always be 00:00, even if the time is specified on the line. 3 /EXPIRATION /EXPIRATION=days /NOEXPIRATION - + Specifies the number of days the message created by the BBOARD is to be retained. The default is 14 days. The highest limit that can be specified is 30 days. This can be overridden by a user with privileges. If /NOEXPIRATION is specified, messages will become permanent. - + NOTE: This value is the same value as specified by SET DEFAULT_EXPIRE. If one is changed, the other will change also. 3 /SPECIAL @@ -1461,37 +1463,37 @@ This problem is solved by placing the word LISTSERV in the folder description line. Then, messages sent to the mailing list by the POST command will be sent from the BBOARD account rather than from the user's account. For example, the folder description might be: - + FAKE MAILING LIST <FAKELIST@FAKENODE.BITNET> LISTSERV. - + If you have PMDF or MX installed, the corresponding logical name PMDF_REPLY_TO or MX_REPLY_TO will be temporarily defined in order to add a REPLY-TO: line to the message header to display the real user's address. - + Users who use the method described in HELP SET BBOARD MORE_INFORMATION should note the following: When using this LISTSERV feature, the BBOARD account must be a real account, not simply a VMS MAIL forwarding entry. Mail can only be sent from a real account. However, if mail forwarding is set for that the account, the account does not need a real directory or a unique uic, since it will not need space to store mail. - + In order to be able to send LISTSERV commands from the BBOARD account without having to actually login to the BBOARD account, there is a utility included with BULLETIN called SETUSER. This requires privileges to use. After compiling it, use the link command: - + LINK SETUSER,SYS$SYSTEM:SYS.STB/SELECT - + When you run it, it will prompt for a username. After verifying that the given username is a valid account, it will then change your process's username. You can then send mail from that account. - + If you are using PMDF or MX, and wish to use this feature, you can still do so by setting BBOARD. As long as the BBOARD account is not a real account, it will work properly, even though the mail feed is not really coming from the BBOARD account. - + In order to find out if the LISTSERV mailing list will accept posts only from subscribed users, send the command 'REV listname'. This will retrieve the file listname.LIST. It begins with a list of keywords. If @@ -1501,16 +1503,16 @@ of the keywords and the meaning of their settings, send any LISTSERV the command 'INFO KEY'. Note that the 'listname.LIST' files include a list of owners and subscribers. If 'send' is set to 'owners', then neither the public nor the subscribers can post to the list. - + 3 More_information If more than one folder is to have a BBOARD setting, only one of the BBOARD names need be a real account. All other names could be names whose mail is forwarded to the real account. BULLETIN will then determine from the mail header which folder the mail is to be sent to. Forwarding can be enabled for any name within MAIL by the command: - + MAIL> SET FORWARD/USER=from_name to_name - + Any mail sent to FROM_NAME will be forwarded to TO_NAME. Thus, only TO_NAME need be a real account. For example, if you have INFOVAX and LASER-LOVERS folders, you need create only a INFOVAX account, and then @@ -1523,7 +1525,7 @@ the /SPECIAL set on their BBOARD accounts cannot have their mail forwarded to BBOARD accounts that don't have /SPECIAL set. Folders of the same type, i.e. that use the same /SPECIAL command procedure, must be grouped separately. - + The BBOARD account must match the mailing list name. If you prefer not to have them match, then you must include the actual address of the mailing list in the folder description in the format described under @@ -1537,9 +1539,9 @@ BULLETIN. Note the difference between BRIEF and READNEW. The latter causes a listing of the description of the new messages to be displayed and prompts the user to read the messages. Setting BRIEF will clear a READNEW setting (and visa versa). - + Format: - + SET [NO]BRIEF 3 /ALL Specifies that the SET [NO]BRIEF option is the default for all users for @@ -1550,12 +1552,12 @@ folder. This is a privileged qualifier. It will only affect brand new users (or those that have never logged in). Use /ALL to modify all users. 3 /FOLDER /FOLDER=foldername - + Specifies the folder for which the option is to modified. If not specified, the selected folder is modified. Valid only with NOBRIEF. 3 /PERMANENT /[NO]PERMANENT - + Specifies that BRIEF is a permanent flag and cannot be changed by the individual, except if changing to SHOWNEW or READNEW. This is a privileged qualifier.`, @@ -1566,11 +1568,11 @@ the notification message "there are new messages" will be displayed every time when logging in, until the new messages are read. Normally, the BRIEF setting causes notification only at the first time that new messages are detected. - + Format: - + SET [NO]CONTINUOUS_BRIEF - + NOTE: Both SET GENERIC and SET CONTINUOUS_BRIEF cannot be set for the same user.`, }, @@ -1579,21 +1581,21 @@ same user.`, PMDF path) is to be retained. The default is 14 days. The highest limit that can be specified is 30 days. This can be overridden by a user with privileges. - + This also specifies the default expiration date when adding a message. If no expiration date is entered when prompted for a date, or if prompting has been disabled via SET NOPROMPT_EXPIRE, this value will be used. - + Format: - + SET DEFAULT_EXPIRE days - + If -1 is specified, messages will become permanent. If 0 is specified, no default expiration date will be present. The latter should never be specified for a folder with a BBOARD, or else the messages will disappear. - + NOTE: This value is the same value that SET BBOARD/EXPIRATION specifies. If one is changed, the other will change also.`, }, @@ -1603,11 +1605,11 @@ written directly from a network mailing program (i.e. PMDF). Several mailing lists use digest format to send their messages, i.e. the messages are concatenated into one long message. If DIGEST is set, the messages will be separated into individual BULLETIN messages. - + Format: - + SET [NO]DIGEST - + The command SHOW FOLDER/FULL will show if DIGEST has been set. `, }, @@ -1615,11 +1617,11 @@ The command SHOW FOLDER/FULL will show if DIGEST has been set. Description: `Specifies that messages deleted from the selected folder are written into a dump (or log) file. The name of the log file is foldername.LOG, and it is located in the folder directory. - + Format: - + SET [NO]DUMP - + The command SHOW FOLDER/FULL will show if dump has been set. (NOTE: SHOW FOLDER/FULL is a privileged command.)`, }, @@ -1627,18 +1629,18 @@ SHOW FOLDER/FULL is a privileged command.)`, Description: `Specifies expiration limit that is allowed for messages. Non-privileged users cannot specify an expiration that exceeds the number of days specified. Privileged users can exceed the limit. - + SET [NO]EXPIRE_LIMIT [days] - + The command SHOW FOLDER/FULL will show the expiration limit, if one exists. (NOTE: SHOW FOLDER/FULL is a privileged command.)`, }, "FOLDER": { Description: `Select a folder of messages. Identical to the SELECT command. See help on that command for more information. - + Format: - + SET FOLDER [node-name::][folder-name] 3 /MARKED Selects messages that have been marked (indicated by an asterisk). @@ -1652,16 +1654,16 @@ many different people. If an account is specified as GENERIC, new messages placed in the GENERAL folder will be displayed upon logging in for a specific number of days, rather than only once. The default period is 7 days. This command is a privileged command. - + Format: - + SET [NO]GENERIC username - + NOTE: Both SET GENERIC and SET CONTINUOUS_BRIEF cannot be set for the same user. 3 /DAYS /DAYS=number_of_days - + Specifies the number days that new GENERAL messages will be displayed for upon logging in. `, @@ -1671,9 +1673,9 @@ for upon logging in. keypad correspond to command definitions. These definitions can be seen via the SHOW KEYPAD command. The default is NOKEYPAD unless the /KEYPAD qualifier has been added to the BULLETIN command line. - + Format: - + SET [NO]KEYPAD `, }, @@ -1686,9 +1688,9 @@ occur if DISMAIL is set for an old account. Additionally, removing the DISMAIL flag will not automatically enable LOGIN. (The reason for the above was to avoid extra overhead for constant checking for the DISMAIL flag.) This command is a privileged command. - + Format: - + SET [NO]LOGIN username `, }, @@ -1702,18 +1704,18 @@ the local folder. When the command is executed, the selected folder will then point to the remote folder. If there were messages in the local folder, they will be deleted. This feature is present only if the BULLCP process is running on the remote node. - + Format: SET NODE nodename [remotename] SET NONODE - + NOTE: If one node adds a message to a remote node, other nodes connected to the same folder will not immediately be aware of the new message. This info is updated every 15 minutes, or if a user accesses that folder. 3 /FOLDER /FOLDER=foldername - + Specifies the folder for which the node information is to modified. If not specified, the selected folder is modified. `, @@ -1721,11 +1723,11 @@ If not specified, the selected folder is modified. "NOTIFY": { Description: `Specifies whether you will be notified via a broadcast message when a message is added to the selected folder. - + Format: - + SET [NO]NOTIFY - + In a cluster, if the logical name MAIL$SYSTEM_FLAGS is defined so that bit 1 is set, users will be notified no matter which node they are logged in to. If you wish to disable this, you should define BULL_SYSTEM_FLAGS @@ -1739,12 +1741,12 @@ folder. This is a privileged qualifier. It will only affect brand new users (or those that have never logged in). Use /ALL to modify all users. 3 /FOLDER /FOLDER=foldername - + Specifies the folder for which the option is to modified. If not specified, the selected folder is modified. Valid only with NONOTIFY. 3 /PERMANENT /[NO]PERMANENT - + Specifies that NOTIFY is a permanent flag and cannot be changed by the individual. /DEFAULT must be specified. This is a privileged qualifier. `, @@ -1756,9 +1758,9 @@ useful for terminals that can store more than one screenful at a time, and that have a remote printer that can then print the contents of the terminal's memory. The default is PAGE, unless the default was changed by specifying /NOPAGE on the command line to invoke BULLETIN. - + Format: - + SET [NO]PAGE `, }, @@ -1766,17 +1768,17 @@ by specifying /NOPAGE on the command line to invoke BULLETIN. Description: `Specifies either process privileges or rights identifiers that are necessary to use privileged commands. Use the SHOW PRIVILEGES command to see what is presently set. This is a privileged command. - + Format: - + SET PRIVILEGES parameters - + The parameters are one or more privileges separated by commas. To remove a privilege, specify the privilege preceeded by "NO". If /ID is specified, the parameters are rights identifiers. 3 /ID /[NO]ID - + If specified, then the rights identifier which is specified as the parameter will allow users holding that rights identifier to execute privileged commands. If /NOID is specified, the identifier is removed. @@ -1791,9 +1793,9 @@ greater than the expiration limit, and the user does not have privileges, then the expiration limit will be used as the default expiration. (If there is no expiration limit, and the user doesn't have privileges, then an error will result.) PROMPT_EXPIRE is the default. - + Format: - + SET [NO]PROMPT_EXPIRE `, }, @@ -1803,14 +1805,14 @@ read new non-system or folder messages (if any exist). A new message is defined as one that has been added since the last login, or since accessing BULLETIN. The default setting for READNEW is dependent on how the folder was created by the owner. - + In order to apply this to a specific folder, first select the folder (using the SELECT command), and then enter the SET READNEW command. - + Format: - + SET [NO]READNEW - + NOTE: If you have several folders with READNEW enabled, each folder's messages will be displayed separately. However, if you EXIT the READNEW mode before all the folders have been displayed, you will not be alerted @@ -1831,12 +1833,12 @@ folder. This is a privileged qualifier. It will only affect brand new users (or those that have never logged in). Use /ALL to modify all users. 3 /FOLDER /FOLDER=foldername - + Specifies the folder for which the option is to modified. If not specified, the selected folder is modified. Valid only with NOREADNEW. 3 /PERMANENT /[NO]PERMANENT - + Specifies that READNEW is a permanent flag and cannot be changed by the individual. This is a privileged qualifier. `, @@ -1848,12 +1850,12 @@ except you will not be prompted to read the messages. The default is dependent on how the folder was created by the owner. A new message is defined as one that has been added since the last login, or since accessing BULLETIN. - + In order to apply this to a specific folder, first select the folder (using the SELECT command), and then enter the SET SHOWNEW command. - + Format: - + SET [NO]SHOWNEW 3 /ALL Specifies that the SET [NO]SHOWNEW option is the default for all users for @@ -1866,12 +1868,12 @@ folder. This is a privileged qualifier. It will only affect brand new users (or those that have never logged in). Use /ALL to modify all users. 3 /FOLDER /FOLDER=foldername - + Specifies the folder for which the option is to modified. If not specified, the selected folder is modified. Valid only with NOSHOWNEW. 3 /PERMANENT /[NO]PERMANENT - + Specifies that SHOWNEW is a permanent flag and cannot be changed by the individual, except if changing to READNEW. This is a privileged qualifier. `, @@ -1880,14 +1882,14 @@ individual, except if changing to READNEW. This is a privileged qualifier. Description: `Specifies that the selected folder is a SYSTEM folder. A SYSTEM folder is allowed to have SYSTEM and SHUTDOWN messages added to it. This is a privileged command. - + Format: - + SET [NO]SYSTEM - + By default, the GENERAL folder is a SYSTEM folder, and the setting for that folder cannot be removed. - + If the selected folder is remote, /SYSTEM cannot be specified unless the folder at the other node is also a SYSTEM folder. `, @@ -1907,9 +1909,9 @@ currently selected folder. Description: `Shows information about a folder of messages. Owner and description are shown. If the folder name is omitted, and a folder has been selected via the SELECT command, information about that folder is shown. - + Format: - + SHOW FOLDER [folder-name] 3 /FULL Control whether all information of the folder is displayed. This @@ -1920,10 +1922,10 @@ have access to that folder. }, "KEYPAD": { Description: `Displays the keypad command definitions. If the keypad has been enabled -by either the SET KEYPAD COMMAND, or /KEYPAD is specified on the command +by either the SET KEYPAD COMMAND, or /KEYPAD is specified on the command line, the keypad keys will be defined as commands. SHOW KEYPAD is the equivalent of HELP KEYPAD. - + NOTE: If the keypad is not enabled, PF2 is defined to be SET KEYPAD. 3 /PRINT Prints the keypad definitions on the default printer (SYS$PRINT). @@ -1948,13 +1950,13 @@ the latest message which a user has read in the folder. If NOLOGIN is set for a user, this information will be displayed. This is a privileged command. Non-privileged users will only be able to display the information for their own account. - + Format: SHOW USER [username] - + The username is optional. If omitted, the process's username is used. The username should not be included if /ALL or /[NO]LOGIN is specified. - + NOTE: The last logged in time displayed is that which is stored when the BULLETIN/LOGIN command is executed, not that which VMS stores. Some sites make BULLETIN/LOGIN an optional command for users to store in @@ -1965,27 +1967,27 @@ Specifies that information for all users is to be displayed. This is a privileged command. 3 /LOGIN /[NO]LOGIN - + Specifies that only those users which do not have NOLOGIN set are to be displayed. If negated, only those users with NOLOGIN set are displayed. This is a privileged command. The qualifier /ALL need not be specified. 3 /FOLDER /FOLDER=[foldername] - + Specifies to display the latest message that was read by the user(s) for the specified foldername. A newsgroup can be specified, but the info can only be shown if the user has subscribed to the newsgroup. If the foldername is not specified, the selected folder will be used. 3 /SINCE /SINCE=[date] - + Specifies to display only those users whose latest read message date is the same date or later than the specified date. If no date is specified, the date of the current message is used. Only valid for folders or with /LOGIN. Use /START for newsgroups. 3 /START /START=[number] - + Specifies to display only those users whose latest read message number is equal to or greather than the specified number. If no number is specified, the message number of the current message is used. Only @@ -2006,7 +2008,7 @@ their expiration date set to 15 minutes in the future and are deleted then. Undeleting the message will reset the expiration date back to its original value. Deleted messages will be indicated as such by the string (DELETED) when either reading or doing a directory listing. - + Format: UNDELETE [message-number] `,