2020-08-13 21:01:21 +02:00
package org.mosad.seil0.projectlaogai.controller
import android.content.Context
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.jsoup.Jsoup
2020-08-14 15:08:42 +02:00
import org.jsoup.nodes.Element
2020-08-13 21:01:21 +02:00
import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.controller.preferences.EncryptedPreferences
2020-08-14 15:08:42 +02:00
import org.mosad.seil0.projectlaogai.util.gradeSubject
2020-08-13 21:01:21 +02:00
import java.security.KeyStore
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManagerFactory
2020-08-14 15:08:42 +02:00
/ * *
* TODO context in constructor ?
* /
2020-08-13 21:01:21 +02:00
class QISPOSParser {
private val className = this . javaClass . name
private val baseURL = " https://notenverwaltung.hs-offenburg.de "
private val loginPath = " /qispos/rds?state=user&type=1&category=auth.login&startpage=portal.vm&breadCrumbSource=portal "
2020-08-14 15:08:42 +02:00
//private val rootPath = "/qispos/rds?state=change&type=1&moduleParameter=studyPOSMenu&nextdir=change&next=menu.vm&subdir=applications&xml=menu&purge=y&navigationPosition=functions%2CstudyPOSMenu&breadcrumb=studyPOSMenu&topitem=functions&subitem=studyPOSMenu"
2020-08-13 21:01:21 +02:00
2020-08-14 15:08:42 +02:00
fun parseGrades ( context : Context ) {
val gradesMap = HashMap < String , ArrayList < gradeSubject > > ( )
val gradesListHtml = readGrades ( context )
gradesListHtml ?. select ( " table > tbody > tr " ) ?. forEach {
val row = it . select ( " td.qis_konto " )
//println("-----------------------------------------------------------")
//println(it.select("td.qis_konto"))
if ( row . size >= 6 ) {
val subject = gradeSubject (
id = row [ 0 ] . text ( ) ,
name = row [ 1 ] . text ( ) ,
semester = row [ 2 ] . text ( ) ,
grade = row [ 3 ] . text ( ) ,
credits = row [ 5 ] . text ( )
)
if ( gradesMap . containsKey ( subject . semester ) ) {
gradesMap [ subject . semester ] !! . add ( subject )
println ( subject . name )
} else {
gradesMap [ subject . semester ] = arrayListOf ( subject )
println ( subject . name )
}
println ( " ID: ${row[0].text()} " )
println ( " Subject Name: ${row[1].text()} " )
println ( " Semester: ${row[2].text()} " )
println ( " Grade: ${row[3].text()} " )
println ( " Credits: ${row[5].text()} " )
println ( " ------------------------------------------------------ " )
}
}
println ( gradesMap )
gradesMap . forEach {
println ( " ${it.key} : ${it.value} " )
}
// TODO sort
println ( " finished parsing! " )
}
/ * *
* read the grades html from qispos
* @return the grades list as html element or null
* /
private fun readGrades ( context : Context ) : Element ? {
2020-08-13 21:01:21 +02:00
val credentials = EncryptedPreferences . readCredentials ( context )
val username = credentials . first . substringBefore ( " @ " )
val password = credentials . second
2020-08-14 15:08:42 +02:00
return runBlocking {
2020-08-13 21:01:21 +02:00
withContext ( Dispatchers . IO ) {
try {
println ( " querying qispos ... " )
println ( " using $username with ******** " )
val socketFactory = createSSLSocketFactory ( context )
// login, asdf = username, fdsa = password, wtf
val list = mapOf (
Pair ( " asdf " , username ) ,
Pair ( " fdsa " , password ) ,
Pair ( " submit " , " Anmelden " )
)
// login and get session cookies
val res = Jsoup . connect ( baseURL + loginPath )
. sslSocketFactory ( socketFactory )
. followRedirects ( true )
. referrer ( " https://notenverwaltung.hs-offenburg.de/qispos/rds?state=user&type=0 " )
. data ( list )
. postDataCharset ( " UTF-8 " )
. execute ( )
val loginCookies = res . cookies ( )
println ( " cookies: $loginCookies " )
println ( " status is: ${res.statusMessage()} : ${res.statusCode()} " )
2020-08-14 15:08:42 +02:00
// grades root document and url
val rootHtml = Jsoup . parse ( res . body ( ) )
val gradesRootLink = rootHtml . select ( " li.menueListStyle > a.auflistung " ) . last ( ) . attr ( " abs:href " )
println ( gradesRootLink )
// parse grades url
val gradesHtml = Jsoup . connect ( gradesRootLink )
. sslSocketFactory ( socketFactory )
. followRedirects ( true )
. cookies ( loginCookies )
. referrer ( " https://notenverwaltung.hs-offenburg.de/qispos/rds?state=user&type=0 " )
. get ( )
val gradesNextLink = gradesHtml . select ( " li.treelist > a.regular " ) . attr ( " abs:href " )
println ( gradesNextLink )
val gradesNextHtml = Jsoup . connect ( gradesNextLink )
. sslSocketFactory ( socketFactory )
. followRedirects ( true )
. cookies ( loginCookies )
. referrer ( " https://notenverwaltung.hs-offenburg.de/qispos/rds?state=user&type=0 " )
. get ( )
val gradesListLink = gradesNextHtml . selectFirst ( " li.treelist > ul > li " ) . selectFirst ( " a " ) . attr ( " abs:href " )
println ( gradesListLink )
// get the grades list
val gradesListHtml = Jsoup . connect ( gradesListLink )
. sslSocketFactory ( socketFactory )
. followRedirects ( true )
. cookies ( loginCookies )
. referrer ( " https://notenverwaltung.hs-offenburg.de/qispos/rds?state=user&type=0 " )
. get ( )
2020-08-13 21:01:21 +02:00
2020-08-14 15:08:42 +02:00
//println(gradesListHtml.body())
gradesListHtml . body ( )
2020-08-13 21:01:21 +02:00
} catch ( ex : Exception ) {
Log . e ( className , " could not load qispos " , ex )
2020-08-14 15:08:42 +02:00
null
2020-08-13 21:01:21 +02:00
}
}
}
}
private fun createSSLSocketFactory ( context : Context ) : SSLSocketFactory {
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
val cf : CertificateFactory = CertificateFactory . getInstance ( " X.509 " )
val caInput = context . resources . openRawResource ( R . raw . notenverwaltung _hs _offenburg _de )
val ca = caInput . use {
cf . generateCertificate ( it ) as X509Certificate
}
println ( " ca= " + ca . subjectDN )
// Create a KeyStore containing our trusted CAs
val keyStoreType = KeyStore . getDefaultType ( )
val keyStore = KeyStore . getInstance ( keyStoreType ) . apply {
load ( null , null )
setCertificateEntry ( " ca " , ca )
}
// Create a TrustManager that trusts the CAs inputStream our KeyStore
val tmfAlgorithm = TrustManagerFactory . getDefaultAlgorithm ( )
val tmf = TrustManagerFactory . getInstance ( tmfAlgorithm ) . apply {
init ( keyStore )
}
// Create an SSLContext that uses our TrustManager
val sslContext = SSLContext . getInstance ( " TLS " ) . apply {
init ( null , tmf . trustManagers , null )
}
return sslContext . socketFactory
}
}