LSF/cmd/lsf/main.go
2023-06-20 16:19:20 +02:00

181 lines
4.1 KiB
Go

// Command lsf provides functions to use the lsf with the commandline.
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"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
configDir := filepath.Join(os.Getenv("HOME"), ".config", "lsfshutthefuckup")
configPath := filepath.Join(configDir, "credentials")
// Check if the credentials file exists
if _, err := os.Stat(configPath); err == nil {
// Read the password from the file
content, err := ioutil.ReadFile(configPath)
if err != nil {
return "", err
}
// Extract the password value from the file content
password := extractPassword(content)
return password, nil
}
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 extractPassword(content []byte) string {
lines := strings.Split(string(content), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "password=") {
return strings.TrimPrefix(line, "password=")
}
}
return ""
}
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
}