Initial commit
This commit is contained in:
commit
b7bea8cf09
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# LSF - LSF S... F... rekusives akronym!!!111elf
|
||||
|
||||
## TODO
|
||||
|
||||
- documentation
|
||||
- tests
|
||||
- webapi
|
137
cmd/lsf/main.go
Normal file
137
cmd/lsf/main.go
Normal file
@ -0,0 +1,137 @@
|
||||
// Command lsf provides functions to use the lsf with the commandline.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"git.marceltransier.de/lsf"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
var (
|
||||
sessionCache string
|
||||
username string
|
||||
)
|
||||
|
||||
func init() {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defaultCacheDir := path.Join(homeDir, ".cache/go-lsf/sessions")
|
||||
flag.StringVar(&sessionCache, "session-cache", defaultCacheDir, "path where the session tokens are located")
|
||||
flag.StringVar(&username, "username", "", "username to login with in lsf")
|
||||
flag.Parse()
|
||||
|
||||
err = os.MkdirAll(sessionCache, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
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)
|
||||
}
|
||||
|
||||
fmt.Printf("s: %+v\n", s)
|
||||
}
|
||||
|
||||
func readUsername() (string, error) {
|
||||
var username string
|
||||
fmt.Print("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.Print("Password: ")
|
||||
b, err := terminal.ReadPassword(int(syscall.Stdin))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not read password")
|
||||
}
|
||||
fmt.Print("\n")
|
||||
password = string(b)
|
||||
return password, nil
|
||||
}
|
||||
|
||||
func session(username string) (*lsf.Session, error) {
|
||||
sessionPath := path.Join(sessionCache, 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 := &lsf.Session{
|
||||
SID: sid,
|
||||
}
|
||||
valid, err := s.Valid()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not check session")
|
||||
}
|
||||
if !valid {
|
||||
fmt.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)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func login(username string) (*lsf.Session, error) {
|
||||
password, err := readPassword()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not login")
|
||||
}
|
||||
s, err := lsf.Login(username, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sessionPath := path.Join(sessionCache, 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
|
||||
}
|
9
go.mod
Normal file
9
go.mod
Normal file
@ -0,0 +1,9 @@
|
||||
module git.marceltransier.de/lsf
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.5.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc
|
||||
)
|
18
go.sum
Normal file
18
go.sum
Normal file
@ -0,0 +1,18 @@
|
||||
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
|
||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
|
||||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
10
noten.go
Normal file
10
noten.go
Normal file
@ -0,0 +1,10 @@
|
||||
package lsf
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
//"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
func (s *Session) Noten() (map[Modul]float32, error) {
|
||||
return nil, errors.New("noten not implemented yet")
|
||||
}
|
75
session.go
Normal file
75
session.go
Normal file
@ -0,0 +1,75 @@
|
||||
package lsf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
SID string
|
||||
}
|
||||
|
||||
func (s *Session) Valid() (bool, error) {
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", "https://lsf.hs-worms.de/qisserver/rds?state=user&type=8&topitem=functions&breadCrumbSource=portal", nil)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "could not prepare the request")
|
||||
}
|
||||
req.Header.Add("Cookie", fmt.Sprintf("JSESSIONID=%s", s.SID))
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "could not do the request")
|
||||
}
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "could not read the response body")
|
||||
}
|
||||
|
||||
if strings.Contains(string(b), "<u>L</u>ogout") {
|
||||
return true, nil
|
||||
}
|
||||
if strings.Contains(string(b), "<u>L</u>ogin") {
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.New("unexpected response body")
|
||||
}
|
||||
|
||||
func Login(username, password string) (*Session, error) {
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error { // don't follow redirects
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
form := url.Values{}
|
||||
form.Add("asdf", username) // who wrote this backend? lol
|
||||
form.Add("fdsa", password)
|
||||
form.Add("submit", "Login")
|
||||
req, err := http.NewRequest("POST", "https://lsf.hs-worms.de/qisserver/rds?state=user&type=1&category=auth.login&startpage=portal.vm&breadCrumbSource=portal", strings.NewReader(form.Encode()))
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not prepare the login request")
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not do the login request")
|
||||
}
|
||||
if resp.StatusCode == 302 {
|
||||
for _, c := range resp.Cookies() {
|
||||
if c.Name == "JSESSIONID" {
|
||||
return &Session{
|
||||
SID: c.Value,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("no session cookie found")
|
||||
}
|
||||
if resp.StatusCode == 200 {
|
||||
return nil, errors.New("wrong credentials") // TODO or other errors
|
||||
}
|
||||
return nil, errors.New("unexpected response status code")
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user