Loading batch/batch.go +4 −4 Original line number Diff line number Diff line Loading @@ -79,8 +79,7 @@ the following files: * ~/.ssh/authorized_keys - key lines to let users connect to BULLETIN To complete the installation, please enter the login, name and ssh key for the first user. `) the first user.`) login, err := ask.GetLine("Enter login of initial user: ") login = strings.ToUpper(login) ask.CheckErr(err) Loading Loading @@ -109,6 +108,7 @@ the first user. _, err = q.AddUser(ctx, storage.AddUserParams{ Login: seedMsgs[i].Login, Name: seedMsgs[i].Name, Disabled: 1, }) ask.CheckErr(err) userCreated[seedMsgs[i].Login] = true Loading dclish/dclish.go +8 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ type Flag struct { Value string Default string Description string Set bool } // Flags is the list of flags. Loading Loading @@ -177,6 +178,7 @@ func (c Commands) run(words []string) error { } for flg := range cmd.Flags { cmd.Flags[flg].Value = cmd.Flags[flg].Default cmd.Flags[flg].Set = false } cmd.Args = []string{} Loading @@ -196,7 +198,7 @@ func (c Commands) run(words []string) error { if assigned { wordup = strings.ToUpper(flag) } else { wordup = args[i] wordup = strings.ToUpper(args[i]) } toggleValue := "true" flg, ok := cmd.Flags[wordup] Loading @@ -213,6 +215,11 @@ func (c Commands) run(words []string) error { fmt.Printf("ERROR: Flag '%s' is a toggle.\n", args[i]) return nil } if flg.Set { fmt.Printf("ERROR: Flag '%s' is already set.\n", args[i]) return nil } flg.Set = true if flg.OptArg { if assigned { flg.Value = strings.Trim(val, "\"'") Loading repl/command.go +1 −77 Original line number Diff line number Diff line Loading @@ -1205,83 +1205,6 @@ characteristics of the BULLETIN Utility. SET option`, Action: ActionSet, Commands: dclish.Commands{ "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 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 command in AUTHORIZE to grant the id to users, and this will give those users access to the folder. This is preferred because of problems with 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 messages, and thus will not be able to set any login flags. (NOTE: If such a user selects such a folder and then uses SET ACCESS to grant him or herself access, the user must reselect the folder in order for the new access to take affect in order to be able to set login flags.) The id-name can be one or more ids contained in the system Rights Database. This includes usernames and UICs. A UIC that contains a comma must be enclosed in quotes. UICs can contain wildcards, i.e. "[130,*]". Note that by default, a process is given the process rights 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" . Warning If a user logs in after a private folder has been created but before being given access, and then is given access, any defaults that the folder has, i.e. /BRIEF, /READNEW, & /NOTIFY, will not be set for that user. This is because if the id is not a username, it becomes an extremely lengthy operation to check each user to see if have that id assigned to them. The alternative is to set the defaults for all users after every SET ACCESS, but that might cause problems with users who have manually reset those defaults. The correct solution requires a large programming modification, which will be done in a later version.`, MinArgs: 1, MaxArgs: 1, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies that access to the folder is granted to all users. If /READ 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]`, OptArg: true, }, "/READ": { Description: ` Specifies that access to the folder will be limited to being able to read the messages.`, }, }, }, "ALWAYS": { Description: `Specifies that the selected folder has the ALWAYS attribute. This causes messages in the folder to be displayed differently when logging Loading Loading @@ -1665,6 +1588,7 @@ have done this.`, Specifies to display the latest message that was read by the user(s) for the specified foldername. If the foldername is not specified, the selected folder will be used.`, OptArg: true, }, "/SINCE": { Description: `/SINCE=[date] Loading repl/show.go +114 −18 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ package repl import ( "fmt" "strings" "github.com/carlmjohnson/versioninfo" Loading Loading @@ -127,33 +128,128 @@ func ActionShowPrivileges(_ *dclish.Command) error { // ActionShowUser handles the `SHOW USER` command. func ActionShowUser(cmd *dclish.Command) error { showAll := false if cmd.Flags["/ALL"].Value == "true" { showAll = true // TODO: Check permissions. fmt.Println("ERROR: No privs to use command.") // Parse the options. login := this.User.Login if len(cmd.Args) == 1 { if this.User.Admin == 0 { fmt.Println("ERROR: You are not an admin.") return nil } login = strings.ToUpper(cmd.Args[0]) } if cmd.Flags["/ALL"].Set { if this.User.Admin == 0 { fmt.Println("ERROR: You are not an admin.") return nil } if len(cmd.Args) == 1 { fmt.Println("ERROR: You can't specify a user with /ALL.") return nil } showLogin := false } var showLogin int64 if cmd.Flags["/LOGIN"].Value == "true" { // TODO: Check permissions. showLogin = true if this.User.Admin == 0 { fmt.Println("ERROR: You are not an admin.") return nil } showLogin = 1 } folder := this.Folder if cmd.Flags["/FOLDER"].Set { fmt.Printf("%v\n", cmd.Flags["/FOLDER"].Value) if cmd.Flags["/FOLDER"].Value != "" { // TODO: Check permissions. folder = folders.FindFolder(cmd.Flags["/FOLDER"].Value) if folder.Name == "" { fmt.Println("ERROR: Folder does not exist.") return nil } if cmd.Flags["/FOLDER"].Value != "" { since, err := ParseDate(cmd.Flags["/FOLDER"].Value) } } /* TODO: need to add this. var since time.Time var err error if cmd.Flags["/SINCE"].Value != "" { if cmd.Flags["/LOGIN"].Set || cmd.Flags["/FOLDER"].Set { fmt.Println("ERROR: Must set /[NO]LOGIN or /FOLDER.") return nil } since, err = ParseDate(cmd.Flags["/SINCE"].Value) if err != nil { fmt.Println("ERROR: Invalid date specified.") return nil } fmt.Printf("TODO: select messages since %s.\n", since.Format("2006-05-04")) } fmt.Println("TODO: implement ActionShowUser.") fmt.Printf("TODO: %t %t %s.\n", showAll, showLogin, folder.Name) */ // Actually do the thing. ctx := storage.Context() if cmd.Flags["/LOGIN"].Set && cmd.Flags["/FOLDER"].Set { rows, err := this.Q.GetLastReadByEnabled(ctx, storage.GetLastReadByEnabledParams{ Folder: folder.Name, Disabled: showLogin, }) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Folder Message #") for _, r := range rows { fmt.Printf("%-12s %-25s % 4d\n", r.Author, folder.Name, r.ID) } } else if cmd.Flags["/ALL"].Set && cmd.Flags["/FOLDER"].Set { rows, err := this.Q.GetLastRead(ctx, folder.Name) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Folder Message #") for _, r := range rows { fmt.Printf("%-12s %-25s % 4d\n", r.Author, folder.Name, r.ID) } } else if cmd.Flags["/FOLDER"].Set { r, err := this.Q.GetLastReadByUser(ctx, storage.GetLastReadByUserParams{ Folder: folder.Name, Author: login, }) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Folder Message #") fmt.Printf("%-12s %-25s % 4d\n", r.Author, folder.Name, r.ID) } else if cmd.Flags["/LOGIN"].Set { rows, err := this.Q.GetLastLoginByEnabled(ctx, showLogin) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Last Login") for _, r := range rows { fmt.Printf("%-12s %s\n", r.Login, r.LastLogin.Format("2006-01-02 15:04:05")) } } else if cmd.Flags["/ALL"].Set { rows, err := this.Q.GetLastLogin(ctx) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Last Login") for _, r := range rows { fmt.Printf("%-12s %s\n", r.Login, r.LastLogin.Format("2006-01-02 15:04:05")) } } else { r, err := this.Q.GetLastLoginByLogin(ctx, login) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Last Login") fmt.Printf("%-12s %s\n", r.Login, r.LastLogin.Format("2006-01-02 15:04:05")) } return nil } Loading storage/display.go +1 −1 Original line number Diff line number Diff line Loading @@ -43,7 +43,7 @@ func (u User) String() string { u.Moderator, u.Alert, u.Disabled, u.LastLogin.Format("06-05-04 15:02:01")) u.LastLogin.Format("06-01-02 15:04:05")) } // String displays a folder (mainly used for debugging). Loading Loading
batch/batch.go +4 −4 Original line number Diff line number Diff line Loading @@ -79,8 +79,7 @@ the following files: * ~/.ssh/authorized_keys - key lines to let users connect to BULLETIN To complete the installation, please enter the login, name and ssh key for the first user. `) the first user.`) login, err := ask.GetLine("Enter login of initial user: ") login = strings.ToUpper(login) ask.CheckErr(err) Loading Loading @@ -109,6 +108,7 @@ the first user. _, err = q.AddUser(ctx, storage.AddUserParams{ Login: seedMsgs[i].Login, Name: seedMsgs[i].Name, Disabled: 1, }) ask.CheckErr(err) userCreated[seedMsgs[i].Login] = true Loading
dclish/dclish.go +8 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ type Flag struct { Value string Default string Description string Set bool } // Flags is the list of flags. Loading Loading @@ -177,6 +178,7 @@ func (c Commands) run(words []string) error { } for flg := range cmd.Flags { cmd.Flags[flg].Value = cmd.Flags[flg].Default cmd.Flags[flg].Set = false } cmd.Args = []string{} Loading @@ -196,7 +198,7 @@ func (c Commands) run(words []string) error { if assigned { wordup = strings.ToUpper(flag) } else { wordup = args[i] wordup = strings.ToUpper(args[i]) } toggleValue := "true" flg, ok := cmd.Flags[wordup] Loading @@ -213,6 +215,11 @@ func (c Commands) run(words []string) error { fmt.Printf("ERROR: Flag '%s' is a toggle.\n", args[i]) return nil } if flg.Set { fmt.Printf("ERROR: Flag '%s' is already set.\n", args[i]) return nil } flg.Set = true if flg.OptArg { if assigned { flg.Value = strings.Trim(val, "\"'") Loading
repl/command.go +1 −77 Original line number Diff line number Diff line Loading @@ -1205,83 +1205,6 @@ characteristics of the BULLETIN Utility. SET option`, Action: ActionSet, Commands: dclish.Commands{ "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 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 command in AUTHORIZE to grant the id to users, and this will give those users access to the folder. This is preferred because of problems with 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 messages, and thus will not be able to set any login flags. (NOTE: If such a user selects such a folder and then uses SET ACCESS to grant him or herself access, the user must reselect the folder in order for the new access to take affect in order to be able to set login flags.) The id-name can be one or more ids contained in the system Rights Database. This includes usernames and UICs. A UIC that contains a comma must be enclosed in quotes. UICs can contain wildcards, i.e. "[130,*]". Note that by default, a process is given the process rights 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" . Warning If a user logs in after a private folder has been created but before being given access, and then is given access, any defaults that the folder has, i.e. /BRIEF, /READNEW, & /NOTIFY, will not be set for that user. This is because if the id is not a username, it becomes an extremely lengthy operation to check each user to see if have that id assigned to them. The alternative is to set the defaults for all users after every SET ACCESS, but that might cause problems with users who have manually reset those defaults. The correct solution requires a large programming modification, which will be done in a later version.`, MinArgs: 1, MaxArgs: 1, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies that access to the folder is granted to all users. If /READ 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]`, OptArg: true, }, "/READ": { Description: ` Specifies that access to the folder will be limited to being able to read the messages.`, }, }, }, "ALWAYS": { Description: `Specifies that the selected folder has the ALWAYS attribute. This causes messages in the folder to be displayed differently when logging Loading Loading @@ -1665,6 +1588,7 @@ have done this.`, Specifies to display the latest message that was read by the user(s) for the specified foldername. If the foldername is not specified, the selected folder will be used.`, OptArg: true, }, "/SINCE": { Description: `/SINCE=[date] Loading
repl/show.go +114 −18 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ package repl import ( "fmt" "strings" "github.com/carlmjohnson/versioninfo" Loading Loading @@ -127,33 +128,128 @@ func ActionShowPrivileges(_ *dclish.Command) error { // ActionShowUser handles the `SHOW USER` command. func ActionShowUser(cmd *dclish.Command) error { showAll := false if cmd.Flags["/ALL"].Value == "true" { showAll = true // TODO: Check permissions. fmt.Println("ERROR: No privs to use command.") // Parse the options. login := this.User.Login if len(cmd.Args) == 1 { if this.User.Admin == 0 { fmt.Println("ERROR: You are not an admin.") return nil } login = strings.ToUpper(cmd.Args[0]) } if cmd.Flags["/ALL"].Set { if this.User.Admin == 0 { fmt.Println("ERROR: You are not an admin.") return nil } if len(cmd.Args) == 1 { fmt.Println("ERROR: You can't specify a user with /ALL.") return nil } showLogin := false } var showLogin int64 if cmd.Flags["/LOGIN"].Value == "true" { // TODO: Check permissions. showLogin = true if this.User.Admin == 0 { fmt.Println("ERROR: You are not an admin.") return nil } showLogin = 1 } folder := this.Folder if cmd.Flags["/FOLDER"].Set { fmt.Printf("%v\n", cmd.Flags["/FOLDER"].Value) if cmd.Flags["/FOLDER"].Value != "" { // TODO: Check permissions. folder = folders.FindFolder(cmd.Flags["/FOLDER"].Value) if folder.Name == "" { fmt.Println("ERROR: Folder does not exist.") return nil } if cmd.Flags["/FOLDER"].Value != "" { since, err := ParseDate(cmd.Flags["/FOLDER"].Value) } } /* TODO: need to add this. var since time.Time var err error if cmd.Flags["/SINCE"].Value != "" { if cmd.Flags["/LOGIN"].Set || cmd.Flags["/FOLDER"].Set { fmt.Println("ERROR: Must set /[NO]LOGIN or /FOLDER.") return nil } since, err = ParseDate(cmd.Flags["/SINCE"].Value) if err != nil { fmt.Println("ERROR: Invalid date specified.") return nil } fmt.Printf("TODO: select messages since %s.\n", since.Format("2006-05-04")) } fmt.Println("TODO: implement ActionShowUser.") fmt.Printf("TODO: %t %t %s.\n", showAll, showLogin, folder.Name) */ // Actually do the thing. ctx := storage.Context() if cmd.Flags["/LOGIN"].Set && cmd.Flags["/FOLDER"].Set { rows, err := this.Q.GetLastReadByEnabled(ctx, storage.GetLastReadByEnabledParams{ Folder: folder.Name, Disabled: showLogin, }) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Folder Message #") for _, r := range rows { fmt.Printf("%-12s %-25s % 4d\n", r.Author, folder.Name, r.ID) } } else if cmd.Flags["/ALL"].Set && cmd.Flags["/FOLDER"].Set { rows, err := this.Q.GetLastRead(ctx, folder.Name) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Folder Message #") for _, r := range rows { fmt.Printf("%-12s %-25s % 4d\n", r.Author, folder.Name, r.ID) } } else if cmd.Flags["/FOLDER"].Set { r, err := this.Q.GetLastReadByUser(ctx, storage.GetLastReadByUserParams{ Folder: folder.Name, Author: login, }) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Folder Message #") fmt.Printf("%-12s %-25s % 4d\n", r.Author, folder.Name, r.ID) } else if cmd.Flags["/LOGIN"].Set { rows, err := this.Q.GetLastLoginByEnabled(ctx, showLogin) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Last Login") for _, r := range rows { fmt.Printf("%-12s %s\n", r.Login, r.LastLogin.Format("2006-01-02 15:04:05")) } } else if cmd.Flags["/ALL"].Set { rows, err := this.Q.GetLastLogin(ctx) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Last Login") for _, r := range rows { fmt.Printf("%-12s %s\n", r.Login, r.LastLogin.Format("2006-01-02 15:04:05")) } } else { r, err := this.Q.GetLastLoginByLogin(ctx, login) if err != nil { fmt.Printf("ERROR: Failed to get list (%s).\n", err) return nil } fmt.Println("User Last Login") fmt.Printf("%-12s %s\n", r.Login, r.LastLogin.Format("2006-01-02 15:04:05")) } return nil } Loading
storage/display.go +1 −1 Original line number Diff line number Diff line Loading @@ -43,7 +43,7 @@ func (u User) String() string { u.Moderator, u.Alert, u.Disabled, u.LastLogin.Format("06-05-04 15:02:01")) u.LastLogin.Format("06-01-02 15:04:05")) } // String displays a folder (mainly used for debugging). Loading