// Package repl implements the main event loop. package repl import ( "errors" "fmt" "strconv" "strings" "time" "git.lyda.ie/kevin/bulletin/ask" "git.lyda.ie/kevin/bulletin/dclish" "git.lyda.ie/kevin/bulletin/editor" "git.lyda.ie/kevin/bulletin/folders" "git.lyda.ie/kevin/bulletin/pager" "git.lyda.ie/kevin/bulletin/this" ) // ActionDirectory handles the `DIRECTORY` command. This lists all the // messages in the current folder. func ActionDirectory(cmd *dclish.Command) error { // TODO: flag parsing. showExpiration := false if cmd.Flags["/EXPIRATION"].Value == "true" { showExpiration = true } if len(cmd.Args) == 1 { folder, err := folders.ValidFolder(cmd.Args[0]) if err != nil { return err } this.Folder = folder } msgs, err := folders.ListMessages(this.Folder.Name) if err != nil { return err } if len(msgs) == 0 { fmt.Println("There are no messages present.") return nil } fmt.Printf("%4s %-43s %-12s %-10s\n", "#", "Subject", "From", "Date") for _, msg := range msgs { fmt.Printf("%s\n", msg.OneLine(showExpiration)) } return nil } // ActionAdd handles the `ADD` command. This adds a message to a folder. func ActionAdd(cmd *dclish.Command) error { optAll := 0 optBell := 0 optBroadcast := 0 optEdit := 0 optExpiration := &time.Time{} optExtract := 0 optFolder := []string{} optIndent := 0 optPermanent := 0 optShutdown := 0 optSignature := 0 optSubject := "" optSystem := 0 if cmd.Flags["/ALL"].Value == "true" { optAll = 1 } if cmd.Flags["/BELL"].Value == "true" { optBell = 1 } if cmd.Flags["/BROADCAST"].Value == "true" { optBroadcast = 1 } if cmd.Flags["/EDIT"].Value == "true" { optEdit = 1 } if cmd.Flags["/EXPIRATION"].Value != "" { // dd-mmm-yyyy, or delta time: dddd exp, err := time.Parse("2006-01-02", cmd.Flags["/EXPIRATION"].Value) if err != nil { days, err := strconv.Atoi(cmd.Flags["/EXPIRATION"].Value) if err != nil { optExpiration = nil } exp := time.Now().AddDate(0, 0, days) optExpiration = &exp } else { optExpiration = &exp } } if cmd.Flags["/EXTRACT"].Value == "true" { optExtract = 1 } if cmd.Flags["/FOLDER"].Value != "" { fmt.Printf("/FOLDER = %s\n", cmd.Flags["/FOLDER"].Value) optFolder = strings.Split(cmd.Flags["/FOLDER"].Value, ",") } if cmd.Flags["/INDENT"].Value == "true" { optIndent = 1 } if cmd.Flags["/PERMANENT"].Value == "true" { optPermanent = 1 } if cmd.Flags["/SHUTDOWN"].Value == "true" { optShutdown = 1 } if cmd.Flags["/SIGNATURE"].Value == "true" { optSignature = 1 } if cmd.Flags["/SUBJECT"].Value != "" { optSubject = cmd.Flags["/SUBJECT"].Value } if cmd.Flags["/SYSTEM"].Value == "true" { optSystem = 1 } fmt.Printf("TODO: optAll is not yet implemented - you set it to %d\n", optAll) fmt.Printf("TODO: optBell is not yet implemented - you set it to %d\n", optBell) fmt.Printf("TODO: optBroadcast is not yet implemented - you set it to %d\n", optBroadcast) fmt.Printf("TODO: optEdit is not yet implemented - you set it to %d\n", optEdit) fmt.Printf("TODO: optExtract is not yet implemented - you set it to %d\n", optExtract) fmt.Printf("TODO: optIndent is not yet implemented - you set it to %d\n", optIndent) fmt.Printf("TODO: optSignature is not yet implemented - you set it to %d\n", optSignature) fmt.Printf("TODO: optSystem is not yet implemented - you set it to %d\n", optSystem) if len(optFolder) == 0 { optFolder = []string{this.Folder.Name} } // TODO: check if folders exist. if optSubject == "" { optSubject, _ = ask.GetLine("Enter subject of message: ") if optSubject == "" { return errors.New("Must enter a subject") } } // TODO: check we have permission for shutdown and permanent message, err := editor.Editor(fmt.Sprintf("Enter message for '%s'...", optSubject), "Edit message") if err != nil { return err } for i := range optFolder { err = folders.CreateMessage(this.User.Login, optSubject, message, optFolder[i], optPermanent, optShutdown, optExpiration) } return nil } // ActionCurrent handles the `CURRENT` command. func ActionCurrent(_ *dclish.Command) error { // TODO: handle flags. msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, this.MsgID) if err != nil { return err } lines := strings.Split(msg.String(), "\n") if len(lines) > 10 { lines = lines[:10] } fmt.Printf("%s\n", strings.Join(lines, "\n")) return nil } // ActionBack handles the `BACK` command. func ActionBack(_ *dclish.Command) error { // TODO: handle flags. msgid := folders.PrevMsgid(this.User.Login, this.Folder.Name, this.MsgID) if msgid == 0 { fmt.Println("No previous messages") return nil } msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid) 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()) this.MsgID = msgid return nil } // ActionChange handles the `CHANGE` command. func ActionChange(cmd *dclish.Command) error { fmt.Printf("TODO: unimplemented...\n%s\n", cmd.Description) return nil } // ActionFirst handles the `FIRST` command. func ActionFirst(_ *dclish.Command) error { msgid := folders.FirstMessage(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. msgid := folders.NextMsgid(this.User.Login, this.Folder.Name, this.MsgID) if msgid == 0 { fmt.Println("No next messages") return nil } msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid) 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()) this.MsgID = msgid return nil } // ActionRead handles the `READ` command. func ActionRead(cmd *dclish.Command) error { // TODO: handle flags. msgid := this.MsgID if len(cmd.Args) == 1 { var err error msgid, err = strconv.ParseInt(cmd.Args[0], 10, 64) if err != nil { return err } } msg, err := folders.ReadMessage(this.User.Login, this.Folder.Name, msgid) 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()) msgid = folders.NextMsgid(this.User.Login, this.Folder.Name, msgid) this.MsgID = max(this.MsgID, msgid) return nil } // ActionReply handles the `REPLY` command. func ActionReply(cmd *dclish.Command) error { fmt.Printf("TODO: unimplemented...\n%s\n", cmd.Description) return nil } // ActionForward handles the `FORWARD` command. func ActionForward(cmd *dclish.Command) error { fmt.Printf("TODO: unimplemented...\n%s\n", cmd.Description) return nil } // ActionSeen handles the `SEEN` command. func ActionSeen(cmd *dclish.Command) error { var err error msgids := []int64{this.MsgID} if len(cmd.Args) == 1 { msgids, err = ParseNumberList(cmd.Args[0]) if err != nil { return err } } err = folders.MarkSeen(msgids) if err != nil { fmt.Printf("ERROR: %s.\n", err) } return nil } // ActionUnseen handles the `UNSEEN` command. func ActionUnseen(cmd *dclish.Command) error { var err error msgids := []int64{this.MsgID} if len(cmd.Args) == 1 { msgids, err = ParseNumberList(cmd.Args[0]) if err != nil { return err } } err = folders.MarkUnseen(msgids) if err != nil { fmt.Printf("ERROR: %s.\n", err) } return nil } // ActionDelete handles the `DELETE` command. func ActionDelete(cmd *dclish.Command) error { // TODO: Follow permissions. var err error all := false if cmd.Flags["/ALL"].Value == "true" { all = true } if all { if len(cmd.Args) == 1 { fmt.Println("ERROR: Can't specify both message numbers and /ALL flag.") return nil } err := folders.DeleteAllMessages() if err != nil { fmt.Printf("ERROR: %s.\n", err) } return nil } msgids := []int64{this.MsgID} if len(cmd.Args) == 1 { msgids, err = ParseNumberList(cmd.Args[0]) if err != nil { fmt.Printf("ERROR: %s.\n", err) return nil } } err = folders.DeleteMessages(msgids) if err != nil { fmt.Printf("ERROR: %s.\n", err) } return nil }