Loading folders/messages.go +12 −5 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ package folders import ( "errors" "fmt" "time" "git.lyda.ie/kevin/bulletin/storage" Loading Loading @@ -40,6 +41,7 @@ func CreateMessage(author, subject, message, folder string, permanent, shutdown // ReadMessage reads a message for a user. func ReadMessage(login, folder string, msgid int64) (*storage.Message, error) { ctx := storage.Context() fmt.Printf("TODO: Make sure %s can read this message.\n", login) msg, err := this.Q.ReadMessage(ctx, storage.ReadMessageParams{ Folder: folder, ID: msgid, Loading @@ -51,11 +53,6 @@ func ReadMessage(login, folder string, msgid int64) (*storage.Message, error) { if msg.ID != int64(msgid) || msgid == 0 { return nil, errors.New("Specified message was not found") } err = this.Q.SetMessageSeen(ctx, storage.SetMessageSeenParams{ Login: login, Folder: folder, Msgid: int64(msgid), }) return &msg, nil } Loading Loading @@ -130,6 +127,16 @@ func FirstMessage(folder string) int64 { return first } // LastMessage gets the last message in a folder. func LastMessage(folder string) int64 { ctx := storage.Context() last, err := this.Q.LastMsgidIgnoringSeen(ctx, folder) if err != nil { return 0 } return last } // ListMessages lists messages. func ListMessages(folder string) ([]storage.Message, error) { ctx := storage.Context() Loading repl/command.go +22 −76 Original line number Diff line number Diff line Loading @@ -127,13 +127,6 @@ topic of the message. Specifies that the editor is to be used to read the message. This is 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.`, }, }, }, "CHANGE": { Loading Loading @@ -176,10 +169,6 @@ This can be suppressed by the qualifier /NEW. "/GENERAL": { Description: ` Specifies that the message is to be converted from a SYSTEM message to a GENERAL message. This only applies to the GENERAL folder.`, }, "/HEADER": { Description: ` Specifies that the message header is to be replaced. You will be prompted for the new message description.`, }, "/NEW": { Description: ` If the editor is to be used for replacing the text of the message, NEW Loading Loading @@ -236,17 +225,11 @@ 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, MaxArgs: 2, Action: ActionCopy, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies to copy all the messages in the old folder.`, }, "/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 Loading Loading @@ -382,13 +365,6 @@ of the message again, you can enter the CURRENT command. Description: ` Specifies that the editor is to be used to read the message. This is 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.`, }, }, }, "DELETE": { Loading Loading @@ -560,13 +536,6 @@ place of an actual number, i.e. CURRENT-LAST, 1-CURRENT, etc.`, "/FF": { Description: ` Specifies that a form feed is placed between messages in the file.`, }, "/HEADER": { Description: `/[NO]HEADER 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": { Description: ` Specifies that a new file is to be created. Otherwise, if the specified file exists, the file would be appended to that file.`, Loading Loading @@ -631,10 +600,6 @@ where one left off after one has read a message. Description: ` If specified, causes the listing to be reinitialized and start from the first folder.`, }, "/SUBSCRIBE": { Description: ` If specified, lists only those news folders which have been subscribed to.`, }, }, }, "LAST": { Loading @@ -642,18 +607,12 @@ where one left off after one has read a message. Format: LAST`, Action: ActionLast, Flags: dclish.Flags{ "/EDIT": { Description: ` Specifies that the editor is to be used to read the message. This is 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.`, }, }, }, "MAIL": { Loading @@ -674,13 +633,6 @@ specified as xxx%"""address""".`, "/EDIT": { Description: ` Specifies that the editor is to be used to edit the message before mailing it.`, }, "/HEADER": { Description: `/[NO]HEADER 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 Loading Loading @@ -768,6 +720,7 @@ 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, MaxArgs: 2, Action: ActionMove, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies to move all the messages from the old folder. Note: If the Loading Loading @@ -797,13 +750,6 @@ you would like to skip over.`, Description: ` Specifies that the editor is to be used to read the message. This is 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.`, }, }, }, "PRINT": { Loading Loading @@ -844,12 +790,6 @@ it's subject line, DIRECTORY/PRINT/SUBJ would allow you do it.`, to have your job print, the system manager should stop the queue, physically change the paper stock on the output device, and restart the 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 message is printed at the beginning. The default is to write the header.`, }, "/NOTIFY": { Description: `/[NO]NOTIFY Loading Loading @@ -904,13 +844,6 @@ the help on the SEEN command.`, "/EDIT": { Description: ` Specifies that the editor is to be used to read the message. This is 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.`, }, "/MARKED": { Description: ` Specifies to read only messages that have been marked (marked messages Loading Loading @@ -1263,12 +1196,14 @@ sure they are read. Format: SET ALWAYS`, Action: ActionSetAlways, }, "NOALWAYS": { Description: `Removes ALWAYS attribute from the selected folder. Format: SET NOALWAYS`, Action: ActionSetAlways, }, "BRIEF": { Description: `Controls whether you will be alerted upon logging that there are new Loading @@ -1281,6 +1216,7 @@ READNEW setting (and visa versa). Format: SET BRIEF`, Action: ActionSetBrief, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies that the SET BRIEF option is the default for all users for Loading @@ -1306,6 +1242,7 @@ privileged qualifier.`, Format: SET NOBRIEF`, Action: ActionSetBrief, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies that the SET NOBRIEF option is the default for all users for Loading Loading @@ -1385,6 +1322,7 @@ 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 so that bit 1 is cleared.`, Action: ActionSetNotify, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies that the SET NOTIFY option is the default for all users for Loading @@ -1410,6 +1348,7 @@ so that bit 1 is cleared.`, Format: SET NONOTIFY [folder-name]`, Action: ActionSetNonotify, MaxArgs: 1, }, "PRIVILEGES": { Loading Loading @@ -1560,14 +1499,21 @@ is allowed to have SYSTEM and SHUTDOWN messages added to it. This is a privileged command. Format: SET [NO]SYSTEM SET SYSTEM By default, the GENERAL folder is a SYSTEM folder, and the setting for that folder cannot be removed. that folder cannot be removed.`, Action: ActionSetSystem, }, "NOSYSTEM": { Description: `Specifies that the selected folder is not a SYSTEM folder. If the selected folder is remote, /SYSTEM cannot be specified unless the folder at the other node is also a SYSTEM folder. `, Format: SET NOSYSTEM By default, the GENERAL folder is a SYSTEM folder, and the setting for that folder cannot be removed.`, Action: ActionSetNosystem, }, }, }, Loading Loading @@ -1654,7 +1600,7 @@ have done this.`, 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.`, folders or with /LOGIN.`, }, }, }, Loading repl/messages.go +41 −5 Original line number Diff line number Diff line Loading @@ -200,6 +200,17 @@ func ActionFirst(_ *dclish.Command) error { return nil } // ActionLast handles the `LAST` command. func ActionLast(_ *dclish.Command) error { msgid := folders.LastMessage(this.Folder.Name) if msgid == 0 { fmt.Println("No messages in folder") return nil } this.MsgID = msgid return nil } // ActionNext handles the `NEXT` command. func ActionNext(_ *dclish.Command) error { // TODO: handle flags. Loading @@ -219,6 +230,31 @@ func ActionNext(_ *dclish.Command) error { return nil } // ActionPrint handles the `PRINT` command. func ActionPrint(cmd *dclish.Command) error { // TODO: handle flags. msgids := []int64{this.MsgID} if len(cmd.Args) == 1 { var err error msgids, err = ParseNumberList(cmd.Args[0]) if err != nil { return err } } print("\033[5i") for _, msgid := range msgids { msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid) if err != nil { fmt.Printf("Message %d not found.\n") } else { fmt.Print(msg.String()) } print("\n\v") } print("\033[4i") return nil } // ActionRead handles the `READ` command. func ActionRead(cmd *dclish.Command) error { // TODO: handle flags. Loading @@ -234,11 +270,11 @@ func ActionRead(cmd *dclish.Command) error { if err != nil { return err } // TODO: pager needs to report if the whole message was read // and only increment if not. pager.Pager(msg.String()) if pager.Pager(msg.String()) { folders.MarkSeen([]int64{msgid}) msgid = folders.NextMsgid(this.User.Login, this.Folder.Name, msgid) this.MsgID = max(this.MsgID, msgid) } return nil } Loading repl/msg-text.go 0 → 100644 +8 −0 Original line number Diff line number Diff line package repl import "strings" func quote(content string) string { lines := strings.Split(content, "\n") return "> " + strings.Join(lines, "\n> ") } repl/repl.go +4 −1 Original line number Diff line number Diff line Loading @@ -34,8 +34,10 @@ func Loop() error { // TODO: Remove once commands are implemented. unimplemented := 0 total := len(commands) fmt.Print("Missing") for c := range commands { if commands[c].Action == nil { fmt.Printf(" [%s]", c) unimplemented++ } if len(commands[c].Commands) > 0 { Loading @@ -43,13 +45,14 @@ func Loop() error { unimplemented-- for subc := range commands[c].Commands { if commands[c].Commands[subc].Action == nil { fmt.Printf(" [%s %s]", c, subc) unimplemented++ } } total += len(commands[c].Commands) } } fmt.Printf("TODO: %d out of %d commands still to be implemented.\n", fmt.Printf("\nTODO: %d out of %d commands still to be implemented.\n", unimplemented, total) // TODO: END Loading Loading
folders/messages.go +12 −5 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ package folders import ( "errors" "fmt" "time" "git.lyda.ie/kevin/bulletin/storage" Loading Loading @@ -40,6 +41,7 @@ func CreateMessage(author, subject, message, folder string, permanent, shutdown // ReadMessage reads a message for a user. func ReadMessage(login, folder string, msgid int64) (*storage.Message, error) { ctx := storage.Context() fmt.Printf("TODO: Make sure %s can read this message.\n", login) msg, err := this.Q.ReadMessage(ctx, storage.ReadMessageParams{ Folder: folder, ID: msgid, Loading @@ -51,11 +53,6 @@ func ReadMessage(login, folder string, msgid int64) (*storage.Message, error) { if msg.ID != int64(msgid) || msgid == 0 { return nil, errors.New("Specified message was not found") } err = this.Q.SetMessageSeen(ctx, storage.SetMessageSeenParams{ Login: login, Folder: folder, Msgid: int64(msgid), }) return &msg, nil } Loading Loading @@ -130,6 +127,16 @@ func FirstMessage(folder string) int64 { return first } // LastMessage gets the last message in a folder. func LastMessage(folder string) int64 { ctx := storage.Context() last, err := this.Q.LastMsgidIgnoringSeen(ctx, folder) if err != nil { return 0 } return last } // ListMessages lists messages. func ListMessages(folder string) ([]storage.Message, error) { ctx := storage.Context() Loading
repl/command.go +22 −76 Original line number Diff line number Diff line Loading @@ -127,13 +127,6 @@ topic of the message. Specifies that the editor is to be used to read the message. This is 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.`, }, }, }, "CHANGE": { Loading Loading @@ -176,10 +169,6 @@ This can be suppressed by the qualifier /NEW. "/GENERAL": { Description: ` Specifies that the message is to be converted from a SYSTEM message to a GENERAL message. This only applies to the GENERAL folder.`, }, "/HEADER": { Description: ` Specifies that the message header is to be replaced. You will be prompted for the new message description.`, }, "/NEW": { Description: ` If the editor is to be used for replacing the text of the message, NEW Loading Loading @@ -236,17 +225,11 @@ 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, MaxArgs: 2, Action: ActionCopy, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies to copy all the messages in the old folder.`, }, "/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 Loading Loading @@ -382,13 +365,6 @@ of the message again, you can enter the CURRENT command. Description: ` Specifies that the editor is to be used to read the message. This is 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.`, }, }, }, "DELETE": { Loading Loading @@ -560,13 +536,6 @@ place of an actual number, i.e. CURRENT-LAST, 1-CURRENT, etc.`, "/FF": { Description: ` Specifies that a form feed is placed between messages in the file.`, }, "/HEADER": { Description: `/[NO]HEADER 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": { Description: ` Specifies that a new file is to be created. Otherwise, if the specified file exists, the file would be appended to that file.`, Loading Loading @@ -631,10 +600,6 @@ where one left off after one has read a message. Description: ` If specified, causes the listing to be reinitialized and start from the first folder.`, }, "/SUBSCRIBE": { Description: ` If specified, lists only those news folders which have been subscribed to.`, }, }, }, "LAST": { Loading @@ -642,18 +607,12 @@ where one left off after one has read a message. Format: LAST`, Action: ActionLast, Flags: dclish.Flags{ "/EDIT": { Description: ` Specifies that the editor is to be used to read the message. This is 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.`, }, }, }, "MAIL": { Loading @@ -674,13 +633,6 @@ specified as xxx%"""address""".`, "/EDIT": { Description: ` Specifies that the editor is to be used to edit the message before mailing it.`, }, "/HEADER": { Description: `/[NO]HEADER 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 Loading Loading @@ -768,6 +720,7 @@ 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, MaxArgs: 2, Action: ActionMove, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies to move all the messages from the old folder. Note: If the Loading Loading @@ -797,13 +750,6 @@ you would like to skip over.`, Description: ` Specifies that the editor is to be used to read the message. This is 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.`, }, }, }, "PRINT": { Loading Loading @@ -844,12 +790,6 @@ it's subject line, DIRECTORY/PRINT/SUBJ would allow you do it.`, to have your job print, the system manager should stop the queue, physically change the paper stock on the output device, and restart the 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 message is printed at the beginning. The default is to write the header.`, }, "/NOTIFY": { Description: `/[NO]NOTIFY Loading Loading @@ -904,13 +844,6 @@ the help on the SEEN command.`, "/EDIT": { Description: ` Specifies that the editor is to be used to read the message. This is 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.`, }, "/MARKED": { Description: ` Specifies to read only messages that have been marked (marked messages Loading Loading @@ -1263,12 +1196,14 @@ sure they are read. Format: SET ALWAYS`, Action: ActionSetAlways, }, "NOALWAYS": { Description: `Removes ALWAYS attribute from the selected folder. Format: SET NOALWAYS`, Action: ActionSetAlways, }, "BRIEF": { Description: `Controls whether you will be alerted upon logging that there are new Loading @@ -1281,6 +1216,7 @@ READNEW setting (and visa versa). Format: SET BRIEF`, Action: ActionSetBrief, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies that the SET BRIEF option is the default for all users for Loading @@ -1306,6 +1242,7 @@ privileged qualifier.`, Format: SET NOBRIEF`, Action: ActionSetBrief, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies that the SET NOBRIEF option is the default for all users for Loading Loading @@ -1385,6 +1322,7 @@ 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 so that bit 1 is cleared.`, Action: ActionSetNotify, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies that the SET NOTIFY option is the default for all users for Loading @@ -1410,6 +1348,7 @@ so that bit 1 is cleared.`, Format: SET NONOTIFY [folder-name]`, Action: ActionSetNonotify, MaxArgs: 1, }, "PRIVILEGES": { Loading Loading @@ -1560,14 +1499,21 @@ is allowed to have SYSTEM and SHUTDOWN messages added to it. This is a privileged command. Format: SET [NO]SYSTEM SET SYSTEM By default, the GENERAL folder is a SYSTEM folder, and the setting for that folder cannot be removed. that folder cannot be removed.`, Action: ActionSetSystem, }, "NOSYSTEM": { Description: `Specifies that the selected folder is not a SYSTEM folder. If the selected folder is remote, /SYSTEM cannot be specified unless the folder at the other node is also a SYSTEM folder. `, Format: SET NOSYSTEM By default, the GENERAL folder is a SYSTEM folder, and the setting for that folder cannot be removed.`, Action: ActionSetNosystem, }, }, }, Loading Loading @@ -1654,7 +1600,7 @@ have done this.`, 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.`, folders or with /LOGIN.`, }, }, }, Loading
repl/messages.go +41 −5 Original line number Diff line number Diff line Loading @@ -200,6 +200,17 @@ func ActionFirst(_ *dclish.Command) error { return nil } // ActionLast handles the `LAST` command. func ActionLast(_ *dclish.Command) error { msgid := folders.LastMessage(this.Folder.Name) if msgid == 0 { fmt.Println("No messages in folder") return nil } this.MsgID = msgid return nil } // ActionNext handles the `NEXT` command. func ActionNext(_ *dclish.Command) error { // TODO: handle flags. Loading @@ -219,6 +230,31 @@ func ActionNext(_ *dclish.Command) error { return nil } // ActionPrint handles the `PRINT` command. func ActionPrint(cmd *dclish.Command) error { // TODO: handle flags. msgids := []int64{this.MsgID} if len(cmd.Args) == 1 { var err error msgids, err = ParseNumberList(cmd.Args[0]) if err != nil { return err } } print("\033[5i") for _, msgid := range msgids { msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid) if err != nil { fmt.Printf("Message %d not found.\n") } else { fmt.Print(msg.String()) } print("\n\v") } print("\033[4i") return nil } // ActionRead handles the `READ` command. func ActionRead(cmd *dclish.Command) error { // TODO: handle flags. Loading @@ -234,11 +270,11 @@ func ActionRead(cmd *dclish.Command) error { if err != nil { return err } // TODO: pager needs to report if the whole message was read // and only increment if not. pager.Pager(msg.String()) if pager.Pager(msg.String()) { folders.MarkSeen([]int64{msgid}) msgid = folders.NextMsgid(this.User.Login, this.Folder.Name, msgid) this.MsgID = max(this.MsgID, msgid) } return nil } Loading
repl/msg-text.go 0 → 100644 +8 −0 Original line number Diff line number Diff line package repl import "strings" func quote(content string) string { lines := strings.Split(content, "\n") return "> " + strings.Join(lines, "\n> ") }
repl/repl.go +4 −1 Original line number Diff line number Diff line Loading @@ -34,8 +34,10 @@ func Loop() error { // TODO: Remove once commands are implemented. unimplemented := 0 total := len(commands) fmt.Print("Missing") for c := range commands { if commands[c].Action == nil { fmt.Printf(" [%s]", c) unimplemented++ } if len(commands[c].Commands) > 0 { Loading @@ -43,13 +45,14 @@ func Loop() error { unimplemented-- for subc := range commands[c].Commands { if commands[c].Commands[subc].Action == nil { fmt.Printf(" [%s %s]", c, subc) unimplemented++ } } total += len(commands[c].Commands) } } fmt.Printf("TODO: %d out of %d commands still to be implemented.\n", fmt.Printf("\nTODO: %d out of %d commands still to be implemented.\n", unimplemented, total) // TODO: END Loading