// Command lsf provides functions to use the lsf with the commandline. package main import ( "fmt" "io/ioutil" "log" "os" "path" "strings" "syscall" "git.marceltransier.de/lsf" cli "github.com/jawher/mow.cli" "github.com/pkg/errors" "golang.org/x/crypto/ssh/terminal" ) var ( sessionCacheDir *string username *string ) var s *lsf.Session func main() { cacheDir, err := os.UserCacheDir() if err != nil { log.Fatal(err) } defaultSessionCacheDir := path.Join(cacheDir, "go-lsf/sessions") app := cli.App("lsf", "LSF Shutthe Fuckup") sessionCacheDir = app.StringOpt("c session-cache-dir", defaultSessionCacheDir, "path to where the session tokens are cached") username = app.StringOpt("u username", "", "username to login with") app.Command("grades", "view your grades", cmdGrades) //app.Command("events", "show your personalized events", cmdEvents) //app.Command("reports", "download reports", cmdReports) //app.Command("courses", "show courses", cmdCourses) app.Run(os.Args) } func cmdGrades(cmd *cli.Cmd) { cmd.Before = sessionNeeded cmd.Action = func() { grades, err := s.Grades() if err != nil { log.Fatal(err) } grades.Print() } } func sessionNeeded() { err := os.MkdirAll(*sessionCacheDir, os.ModePerm) if err != nil { log.Fatal(err) } if len(*username) == 0 { var err error *username, err = readUsername() if err != nil { log.Fatal(err) } } s, err = session(*username) if err != nil { log.Fatal(err) } } func readUsername() (string, error) { var username string fmt.Fprint(os.Stderr, "Username: ") _, err := fmt.Scanf("%s", &username) if err != nil { return "", errors.Wrap(err, "could not read username") } return username, nil } func readPassword() (string, error) { var password string fmt.Fprint(os.Stderr, "Password: ") b, err := terminal.ReadPassword(int(syscall.Stdin)) if err != nil { return "", err } fmt.Fprint(os.Stderr, "\n") password = string(b) return password, nil } func session(username string) (*lsf.Session, error) { sessionPath := path.Join(*sessionCacheDir, username) sessionFile, err := os.Open(sessionPath) if err != nil { pErr, ok := err.(*os.PathError) if !ok { return nil, err } errno, ok := pErr.Unwrap().(syscall.Errno) if !ok { return nil, err } if errno != syscall.ENOENT { log.Fatal(err) } log.Printf("no session for %s cached\n", username) return login(username) } defer sessionFile.Close() b, err := ioutil.ReadAll(sessionFile) if err != nil { return nil, errors.Wrapf(err, "could not read session file %s", sessionPath) } sid := strings.TrimSuffix(string(b), "\n") s, err := lsf.NewSessionBySID(sid) if err == lsf.ErrInvalSID { log.Println("session is not valid") err := os.Remove(sessionPath) if err != nil { log.Println(errors.Wrap(err, "could not remove invalid session file")) } return login(username) } else if err != nil { return nil, errors.Wrap(err, "could not get session by existing sid") } return s, nil } func login(username string) (*lsf.Session, error) { password, err := readPassword() if err != nil { return nil, errors.Wrap(err, "could not read password") } s, err := lsf.Login(username, password) if err != nil { return nil, err } sessionPath := path.Join(*sessionCacheDir, username) err = ioutil.WriteFile(sessionPath, []byte(s.SID), 0666) if err != nil { log.Println(errors.Wrap(err, "could not cache session id")) } return s, nil }