diff --git a/dualisauth.py b/dualisauth.py deleted file mode 100644 index feebdc5..0000000 --- a/dualisauth.py +++ /dev/null @@ -1,62 +0,0 @@ -import requests -import urllib.parse -import time -from bs4 import BeautifulSoup - -headers = { - 'Cookie': 'cnsc=0', - 'Content-Type': 'application/x-www-form-urlencoded', - 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36' -} - -url = "https://dualis.dhbw.de/scripts/mgrqispi.dll" - - -def checkUser(email: str, password: str): - s = requests.Session() - fpw = urllib.parse.quote(password, safe='', encoding=None, errors=None) - fmail = urllib.parse.quote(email, safe='', encoding=None, errors=None) - payload = 'usrname=' + fmail + '&pass=' + fpw + '&ARGUMENTS=clino%2Cusrname%2Cpass%2Cmenuno%2Cmenu_type%2Cbrowser%2Cplatform&APPNAME=CampusNet&PRGNAME=LOGINCHECK' - response = s.post(url, headers=headers, data=payload) - header = response.headers - try: - refresh = header["REFRESH"] - arg = refresh.find("=-N") + 3 - komma = refresh[arg:].find(",") - except KeyError: - return -2, s - token = refresh[arg:komma + arg] - return token, s - - -def getKurs(token: int, cookie: str): - try: - headers["Cookie"] = "cnsc=" + cookie - token = str(token) - response = requests.request("GET", - url + "?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N" + token + ",-N000307,", - headers=headers, data={}) - html = BeautifulSoup(response.text, 'lxml') - link = html.body.find('a', attrs={'id': "Popup_details0001"})['href'] - response = requests.request("GET", url + link[21:], headers=headers, data={}) - html = BeautifulSoup(response.text, 'lxml') - content = html.body.find('td', attrs={'class': 'level02'}).text - start = content.find(" ") + 4 - end = start + (content[start:].find(" ")) - kurs = content[start:end] - except: - kurs = 0 - return kurs - - -def logOut(token: int, cookie: str): - headers["Cookie"] = "cnsc=" + cookie - response = requests.request("GET", url + "?APPNAME=CampusNet&PRGNAME=LOGOUT&ARGUMENTS=-N" + str(token) + ", -N001", - headers=headers, data={}) - - -def checkLifetime(timecode: float): - if time.time() - timecode > 1800: - return False - else: - return True diff --git a/fetchDUALIS.py b/fetchDUALIS.py new file mode 100644 index 0000000..819137e --- /dev/null +++ b/fetchDUALIS.py @@ -0,0 +1,117 @@ +import requests +import urllib.parse +import time +from bs4 import BeautifulSoup + +headers = { + 'Cookie': 'cnsc=0', + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/58.0.3029.110 Safari/537.36', + 'Accept-Encoding': 'utf-8' +} + +url = "https://dualis.dhbw.de/scripts/mgrqispi.dll" + + +def checkUser(email: str, password: str): + s = requests.Session() + fpw = urllib.parse.quote(password, safe='', encoding=None, errors=None) + fmail = urllib.parse.quote(email, safe='', encoding=None, errors=None) + payload = 'usrname=' + fmail + '&pass=' + fpw + ('&ARGUMENTS=clino%2Cusrname%2Cpass%2Cmenuno%2Cmenu_type%2Cbrowser' + '%2Cplatform&APPNAME=CampusNet&PRGNAME=LOGINCHECK') + response = s.post(url, headers=headers, data=payload) + header = response.headers + try: + refresh = header["REFRESH"] + arg = refresh.find("=-N") + 3 + komma = refresh[arg:].find(",") + except KeyError: + return -2, s + token = refresh[arg:komma + arg] + return token, s + + +def getKurs(token: int, cookie: str): + try: + headers["Cookie"] = "cnsc=" + cookie + token = str(token) + response = requests.request("GET", url + + "?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N" + token + ",-N000307,", + headers=headers, data={}) + html = BeautifulSoup(response.text, 'lxml') + link = html.body.find('a', attrs={'id': "Popup_details0001"})['href'] + response = requests.request("GET", url + link[21:], headers=headers, data={}) + html = BeautifulSoup(response.text, 'lxml') + content = html.body.find('td', attrs={'class': 'level02'}).text + start = content.find(" ") + 4 + end = start + (content[start:].find(" ")) + kurs = content[start:end] + except: + kurs = 0 + return kurs + + +def logOut(token: int, cookie: str): + headers["Cookie"] = "cnsc=" + cookie + requests.request("GET", url + "?APPNAME=CampusNet&PRGNAME=LOGOUT&ARGUMENTS=-N" + str(token) + + ", -N001", headers=headers, data={}) + + +def getSem(token: int, cookie: str): + headers["Cookie"] = "cnsc=" + cookie + token = str(token) + response = requests.request("GET", url + + "?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N" + token + ",-N000307,", + headers=headers, data={}) + html = BeautifulSoup(response.text, 'lxml') + select = html.find('select') + select = select.find_all(value=True) + optlist = [] + for i in select: + t = i.text.replace("Wi", "Winter").replace("So", "Sommer") + t = t.replace("Se", "semester") + optlist += [[t, i['value']]] + return optlist + + +def getResults(token, cookie: str, resl: str): + headers["Cookie"] = "cnsc=" + cookie + response = requests.request("GET", url + "?APPNAME=CampusNet&PRGNAME=COURSERESULTS&ARGUMENTS=-N" + token + + ",-N000307," + ",-N" + resl, headers=headers, data={}) + html = BeautifulSoup(response.content.decode("utf-8"), 'lxml') + table = html.find('table', attrs={"class": "nb list"}) + body = table.find("tbody") + vorl = body.find_all("tr") + vorlist = [] + for row in vorl: + cols = row.find_all("td") + col = [e.text.strip() for e in cols] + if len(col) != 0: + if len(col[4]) == 0: + col[2] = getPruefung(row.find("a")["href"]) + vorlist += [col[1:4]] + return vorlist + + +def getPruefung(url): + response = requests.request("GET", "https://dualis.dhbw.de" + url, headers=headers, data={}) + html = BeautifulSoup(response.content.decode("utf-8"), 'lxml') + table = html.find('table') + pruefung = table.find_all("tr") + ret = " " + for row in pruefung: + cols = row.find_all("td") + col = [e.text.strip() for e in cols] + if len(col) == 6: + ret = col[3][:3] + if ret[1] != ",": + ret = "noch nicht gesetzt" + return ret + + +def checkLifetime(timecode: float): + if time.time() - timecode > 1800: + return False + else: + return True diff --git a/init-sql.sh b/init-sql.sh index 5fe0edb..0467c21 100755 --- a/init-sql.sh +++ b/init-sql.sh @@ -1,5 +1,5 @@ #!/bin/sh mysql -e "USE paulmrtn_DUALHUB; CREATE TABLE user ( id int NOT NULL, email VARCHAR(255), password VARCHAR(255), name VARCHAR(255), kurs VARCHAR (15), PRIMARY KEY (ID), UNIQUE (ID, EMAIL) );" -mysql -e "USE paulmrtn_DUALHUB; CREATE TABLE dualis ( uid int NOT NULL, token VARCHAR(255), result_lists VARCHAR(255), token_created INT, PRIMARY KEY (uid));" +mysql -e "USE paulmrtn_DUALHUB; CREATE TABLE dualis ( uid int NOT NULL, token VARCHAR(255), result_list VARCHAR(15), token_created INT, PRIMARY KEY (uid));" mysql -e "USE paulmrtn_DUALHUB; CREATE TABLE meals ( id int NOT NULL, date date, name VARCHAR(200), vegetarian tinyint(1), vegan tinyint(1), schwein tinyint(1), PRIMARY KEY (id));" diff --git a/init.py b/init.py index c6d0fca..f799146 100644 --- a/init.py +++ b/init.py @@ -45,7 +45,7 @@ class Dualis(db.Model): token = db.Column(db.String(255), unique=True) uid = db.Column(db.Integer, primary_key=True) token_created = db.Column(db.Integer) - result_lists = db.Column(db.String(255)) + result_list = db.Column(db.String(15)) class Meals(db.Model): diff --git a/app.py b/routing.py similarity index 75% rename from app.py rename to routing.py index 1413827..29396db 100644 --- a/app.py +++ b/routing.py @@ -1,221 +1,260 @@ -#!/usr/bin/env python3.6 -from flask import Flask, make_response -from flask import render_template, url_for, send_from_directory, redirect, request, send_file -from flask_login import login_user, login_required, current_user, LoginManager, UserMixin, logout_user, login_manager -from flask_sqlalchemy import SQLAlchemy -from werkzeug.security import generate_password_hash, check_password_hash -from talisman import Talisman -import hashlib -import datetime -import time - -import dualisauth -import fetchRAPLA -import requesthelpers -from fetchRAPLA import * -from get_mysql import get_mysql -from calendar_generation import getWeek -from init import * - - -@app.route("/") -def index(): - return redirect(url_for("login")) - - -@app.route("/welcome") -@login_required -def welcome(): - kurs = current_user.kurs - name = current_user.name - return render_template('index.html', headermessage='DualHub', message="Hallo, " - + name + " (" + kurs + ")") - - -@app.route("/backendpoc/set-up") -@login_required -def getKurs(): - d = Dualis.query.filter_by(uid=current_user.id).first() - if d: - e = False - if not current_user.kurs: - cookie = request.cookies.get("cnsc") - kurs = dualisauth.getKurs(d.token, cookie) - if kurs != 0: - current_user.kurs = kurs - db.session.commit() - else: - e = True - else: - kurs = current_user.kurs - current_user.kurs = kurs - db.session.commit() - else: - e = True - kurs = "" - return render_template('kurs.html', detected=(kurs, e)) - - - -@app.route("/backendpoc/error") -def error(ecode): - if ecode == 900: - msg = "Ungültige RAPLA-URL! Sicher, dass der Link zum DHBW-Rapla führt?" - elif ecode == 899: - msg = "Der Kalender wurde nicht gefunden! Sicher, dass der Link korrekt ist?" - else: - msg = "Unbekannter Fehler!" - return render_template('index.html', message=msg, headermessage="DualHub") - - -@app.route("/backendpoc/rapla") -@login_required -def chooseRaplas(): - r = getRaplas() - return render_template("rapla.html", raplas=r) - - -@login_required -@app.route("/backendpoc/rapla", methods=["POST"]) -def getRapla(): - file = str(request.form.get("file")) - url = str(request.form.get("url")) - if file == url == "None": - return redirect(url_for("chooseRaplas")) - if file != "None": - User.query.filter_by(id=current_user.id).first().kurs = file[5:-5] - db.session.commit() - #return send_file("calendars/" + file) - elif url != "None": - file = getNewRapla(url) - if type(file) is not int: - User.query.filter_by(id=current_user.id).first().kurs = file[5:-5] - db.session.commit() - #return send_file("calendars/" + file) - else: - return redirect(url_for("error", ecode=file + 900)) - return redirect(url_for("welcome")) - - -@app.route("/backendpoc/plan", methods=["GET"]) -@login_required -def displayRapla(): - week = request.args.get("week") - if week: - week = datetime.datetime.strptime(week, "%Y-%m-%d") - else: - week = "today" - samstag = request.args.get("samstag") - if not samstag: - samstag = False - events = getWeek(week, fetchRAPLA.getIcal(current_user.kurs), samstag) - return render_template("plan.html", events=events[0], eventdays=events[1]) - - -@app.route("/backendpoc/plan/") -def displayPlan(kurs): - week = request.args.get("week") - if week: - week = datetime.datetime.strptime(week, "%Y-%m-%d") - else: - week = "today" - try: - if current_user.kurs == kurs.upper(): - return redirect(url_for("displayRapla")) - except AttributeError: - pass - plan = fetchRAPLA.getIcal(kurs.upper()) - if plan: - samstag = request.args.get("samstag") - if not samstag: - samstag = False - events = getWeek(week, plan, samstag) - return render_template("plan-anon.html", events=events[0], eventdays=events[1]) - else: - return redirect(url_for("login")) - -@app.route("/backendpoc/log-in") -def login(code: int = None): - if code: - print(code) - return render_template("login.html") - - -@app.route("/backendpoc/log-in", methods=["POST"]) -def login_post(): - email = request.form.get("email") - password = request.form.get("password") - n = request.args.get("next") - if n: - success = make_response(redirect(n)) - else: - success = make_response(redirect(url_for("getKurs"))) - - user = User.query.filter_by(email=email).first() - newcookie = "" - if user: - dualis = Dualis.query.filter_by(uid=user.id).first() - if check_password_hash(user.password, password): - if not dualis.token or not dualisauth.checkLifetime(dualis.token_created): - new_token = dualisauth.checkUser(email, password) - dualis.token = new_token[0] - newcookie = requesthelpers.getCookie(new_token[1].cookies) - dualis.token_created = time.time() - db.session.commit() - else: - t = dualisauth.checkUser(email, password) - if t[0] == -2: - return redirect(url_for("login", code=-2)) - else: - user.password = generate_password_hash(password, method="pbkdf2:sha256") - dualis.token = t[0] - newcookie = requesthelpers.getCookie(t[1].cookies) - dualis.token_created = time.time() - db.session.commit() - login_user(user) - if user.kurs: - success = make_response(redirect(url_for("welcome"))) - success.set_cookie("cnsc", value=newcookie, httponly=True, secure=True) - return success - - t = dualisauth.checkUser(email, password) - if t[0] == -2: - return redirect(url_for("login", code=-2)) - - hashid = int(hashlib.sha1(email.encode("utf-8")).hexdigest(), 16) % (10 ** 8) - hashpw = generate_password_hash(password, method="pbkdf2:sha256") - pname = email.find(".") + 1 - ename = min(email[pname:].find("."), email[pname:].find("@")) - name = email[pname:pname + ename].capitalize() - - new_user = User(email=email, password=hashpw, name=name, id=hashid) - db.session.add(new_user) - - cookie = requesthelpers.getCookie(t[1].cookies) - - new_dualis = Dualis(uid=hashid, token=t[0], token_created=int(time.time())) - db.session.add(new_dualis) - db.session.commit() - login_user(new_user) - newcookie = cookie - success.set_cookie("cnsc", value=newcookie, httponly=True, secure=True) - return success - - -@app.route("/backendpoc/log-out") -@login_required -def logout(): - cookie = request.cookies.get("cnsc") - dualis = Dualis.query.filter_by(uid=current_user.id).first() - dualisauth.logOut(dualis.token, cookie) - dualis.token = None - db.session.commit() - logout_user() - red = make_response(redirect(url_for("login", code=1))) - red.set_cookie("cnsc", value="Logged out! Your temporary token " - "on our server and the cookie on your device have been deleted.", httponly=True, secure=True) - return red - - -if __name__ == "__main__": - app.run(host='0.0.0.0', port=2024, debug=True) +#!/usr/bin/env python3.6 +from flask import make_response +from flask import render_template, url_for, redirect, request +from flask_login import login_user, login_required, current_user, logout_user +from werkzeug.exceptions import HTTPException +from werkzeug.security import generate_password_hash, check_password_hash +import hashlib +import datetime +import time + +import fetchDUALIS +import fetchRAPLA +import requesthelpers +from fetchRAPLA import * +from calendar_generation import getWeek +from init import * + + +@app.route("/") +def index(): + return redirect(url_for("login")) + + +@app.route("/welcome") +@login_required +def welcome(): + kurs = current_user.kurs + name = current_user.name + return render_template('index.html', headermessage='DualHub', message="Hallo, " + + name + " (" + kurs + ")") + + +@app.route("/backendpoc/noten") +@login_required +def displayNoten(): + d = Dualis.query.filter_by(uid=current_user.id).first() + t = d.token + sem = d.result_list + c = request.cookies.get("cnsc") + res = fetchDUALIS.getResults(t, c, sem) + return render_template("noten.html", noten=res, semester=fetchDUALIS.getSem(t, c)) + + +@app.route("/backendpoc/plan", methods=["GET"]) +@login_required +def displayRapla(): + week = request.args.get("week") + if week: + week = datetime.datetime.strptime(week, "%Y-%m-%d") + else: + week = "today" + samstag = request.args.get("samstag") + if not samstag: + samstag = False + events = getWeek(week, fetchRAPLA.getIcal(current_user.kurs), samstag) + return render_template("plan.html", events=events[0], eventdays=events[1]) + + +@app.route("/backendpoc/plan/") +def displayPlan(kurs): + week = request.args.get("week") + if week: + week = datetime.datetime.strptime(week, "%Y-%m-%d") + else: + week = "today" + try: + if current_user.kurs == kurs.upper(): + return redirect(url_for("displayRapla")) + except AttributeError: + pass + plan = fetchRAPLA.getIcal(kurs.upper()) + if plan: + samstag = request.args.get("samstag") + if not samstag: + samstag = False + events = getWeek(week, plan, samstag) + return render_template("plan-anon.html", events=events[0], eventdays=events[1]) + else: + return redirect(url_for("login")) + + +@app.route("/backendpoc/set-up") +def redKurs(): + return redirect(url_for("getKurs")) + + +@app.route("/backendpoc/set-up/kurs") +@login_required +def getKurs(): + d = Dualis.query.filter_by(uid=current_user.id).first() + if d: + e = False + if not current_user.kurs: + cookie = request.cookies.get("cnsc") + kurs = fetchDUALIS.getKurs(d.token, cookie) + if kurs != 0: + current_user.kurs = kurs + db.session.commit() + else: + e = True + else: + kurs = current_user.kurs + current_user.kurs = kurs + db.session.commit() + else: + e = True + kurs = "" + return render_template('kurs.html', detected=(kurs, e)) + + +@app.route("/backendpoc/set-up/semester") +@login_required +def getSemester(): + t = Dualis.query.filter_by(uid=current_user.id).first().token + c = request.cookies.get("cnsc") + + return render_template("semester.html", semester=fetchDUALIS.getSem(t, c)) + + +@app.route("/backendpoc/set-up/semester", methods=["POST"]) +@login_required +def setSemester(): + n = request.args.get("next") + if not n: + n = url_for("welcome") + d = Dualis.query.filter_by(uid=current_user.id).first() + d.result_list = request.form.get("sem") + db.session.commit() + return redirect(n) + + +@app.route("/backendpoc/set-up/rapla") +@login_required +def chooseRaplas(): + r = getRaplas() + return render_template("rapla.html", raplas=r) + + +@login_required +@app.route("/backendpoc/set-up/rapla", methods=["POST"]) +def getRapla(): + file = str(request.form.get("file")) + url = str(request.form.get("url")) + if file == url == "None": + return redirect(url_for("chooseRaplas")) + if file != "None": + User.query.filter_by(id=current_user.id).first().kurs = file[5:-5] + db.session.commit() + elif url != "None": + file = getNewRapla(url) + if type(file) is not int: + User.query.filter_by(id=current_user.id).first().kurs = file[5:-5] + db.session.commit() + else: + return redirect(url_for("error", ecode=file + 900)) + return redirect(url_for("welcome")) + + +@app.route("/backendpoc/log-in") +def login(code: int = None): + if code: + print(code) + return render_template("login.html") + + +@app.route("/backendpoc/log-in", methods=["POST"]) +def login_post(): + email = request.form.get("email") + password = request.form.get("password") + n = request.args.get("next") + if n: + success = make_response(redirect(n)) + else: + success = make_response(redirect(url_for("getKurs"))) + + user = User.query.filter_by(email=email).first() + newcookie = "" + if user: + dualis = Dualis.query.filter_by(uid=user.id).first() + if check_password_hash(user.password, password): + if not dualis.token or not fetchDUALIS.checkLifetime(dualis.token_created): + new_token = fetchDUALIS.checkUser(email, password) + dualis.token = new_token[0] + newcookie = requesthelpers.getCookie(new_token[1].cookies) + dualis.token_created = time.time() + db.session.commit() + else: + t = fetchDUALIS.checkUser(email, password) + if t[0] == -2: + return redirect(url_for("login", code=-2)) + else: + user.password = generate_password_hash(password, method="pbkdf2:sha256") + dualis.token = t[0] + newcookie = requesthelpers.getCookie(t[1].cookies) + dualis.token_created = time.time() + db.session.commit() + login_user(user) + if user.kurs: + if not n: + success = make_response(redirect(url_for("welcome"))) + success.set_cookie("cnsc", value=newcookie, httponly=True, secure=True) + return success + + t = fetchDUALIS.checkUser(email, password) + if t[0] == -2: + return redirect(url_for("login", code=-2)) + + hashid = int(hashlib.sha1(email.encode("utf-8")).hexdigest(), 16) % (10 ** 8) + hashpw = generate_password_hash(password, method="pbkdf2:sha256") + pname = email.find(".") + 1 + ename = min(email[pname:].find("."), email[pname:].find("@")) + name = email[pname:pname + ename].capitalize() + new_user = User(email=email, password=hashpw, name=name, id=hashid) + db.session.add(new_user) + + cookie = requesthelpers.getCookie(t[1].cookies) + + new_dualis = Dualis(uid=hashid, token=t[0], token_created=int(time.time())) + db.session.add(new_dualis) + db.session.commit() + login_user(new_user) + newcookie = cookie + success.set_cookie("cnsc", value=newcookie, httponly=True, secure=True) + return success + + +@app.route("/backendpoc/log-out") +@login_required +def logout(): + cookie = request.cookies.get("cnsc") + dualis = Dualis.query.filter_by(uid=current_user.id).first() + fetchDUALIS.logOut(dualis.token, cookie) + dualis.token = None + db.session.commit() + logout_user() + red = make_response(redirect(url_for("login", code=1))) + red.set_cookie("cnsc", value="Logged out! Your temporary token " + "on our server and the cookie on your device have been deleted.", httponly=True, + secure=True) + return red + + +@app.route("/backendpoc/error") +def error(ecode): + if ecode == 900: + msg = "Ungültige RAPLA-URL! Sicher, dass der Link zum DHBW-Rapla führt?" + elif ecode == 899: + msg = "Der Kalender wurde nicht gefunden! Sicher, dass der Link korrekt ist?" + else: + msg = "Unbekannter Fehler!" + return render_template('index.html', message=msg, headermessage="DualHub") + + +@app.errorhandler(HTTPException) +def handle(e): + return render_template('index.html', message=e, headermessage="DualHub") + + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=2024, debug=True) diff --git a/static/style.css b/static/style.css index 50bc523..3ca4f70 100644 --- a/static/style.css +++ b/static/style.css @@ -19,7 +19,7 @@ input[type=url], input[type=email], input[type=password] { } select { - width: 150px; + width: 200px; height: 50px } diff --git a/templates/kurs.html b/templates/kurs.html index ab95eeb..826533d 100644 --- a/templates/kurs.html +++ b/templates/kurs.html @@ -9,7 +9,7 @@ {% if not detected[1] %}

Wir haben {{ detected[0] }} als deinen Kurs ermittelt. Falls er nicht stimmt, kannst du ihn unten auswählen.

-
+
{% else %} diff --git a/templates/noten.html b/templates/noten.html new file mode 100644 index 0000000..c0c95c7 --- /dev/null +++ b/templates/noten.html @@ -0,0 +1,24 @@ + + + + + Noten + + + + + {% for i in noten %} +

{{ i[0] }}: {{ i[1].capitalize() }} (Credits: {{ i[2] }})

+ {% endfor %} +
+ + + +
+ + \ No newline at end of file diff --git a/templates/rapla.html b/templates/rapla.html index f47a7b0..0185479 100644 --- a/templates/rapla.html +++ b/templates/rapla.html @@ -7,19 +7,17 @@

Verfügbare Raplas

- {% block content %} -
- + + - - -
- {% endblock %} + + +

Eigenen Rapla hinzufügen

diff --git a/templates/semester.html b/templates/semester.html new file mode 100644 index 0000000..b785a3d --- /dev/null +++ b/templates/semester.html @@ -0,0 +1,22 @@ + + + + + Semester wählen + + + + +

Bitte wähle aus der unten stehenden Liste das Semester, dessen Noten du sehen möchtest.

+ + + + +
+ + \ No newline at end of file