diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt
index 0b2ceefc..0f8c0d35 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt
@@ -18,6 +18,7 @@ import pl.szczodrzynski.edziennik.api.v2.events.*
import pl.szczodrzynski.edziennik.api.v2.events.requests.*
import pl.szczodrzynski.edziennik.api.v2.events.task.ErrorReportTask
import pl.szczodrzynski.edziennik.api.v2.events.task.NotifyTask
+import pl.szczodrzynski.edziennik.api.v2.idziennik.Idziennik
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.Librus
@@ -219,6 +220,7 @@ class ApiService : Service() {
LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback)
LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback)
+ LOGIN_TYPE_IDZIENNIK -> Idziennik(app, profile, loginStore, taskCallback)
LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback)
else -> null
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt
index 371e3c58..90aa98ec 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt
@@ -45,7 +45,18 @@ const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/to
const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module"
const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action="
-
+const val IDZIENNIK_USER_AGENT = SYNERGIA_USER_AGENT
+const val IDZIENNIK_WEB_URL = "https://iuczniowie.progman.pl/idziennik"
+const val IDZIENNIK_WEB_LOGIN = "login.aspx"
+const val IDZIENNIK_WEB_SETTINGS = "mod_panelRodzica/Ustawienia.aspx"
+const val IDZIENNIK_WEB_TIMETABLE = "mod_panelRodzica/plan/WS_Plan.asmx/pobierzPlanZajec"
+const val IDZIENNIK_WEB_GRADES = "mod_panelRodzica/oceny/WS_ocenyUcznia.asmx/pobierzOcenyUcznia"
+const val IDZIENNIK_WEB_MISSING_GRADES = "mod_panelRodzica/brak_ocen/WS_BrakOcenUcznia.asmx/pobierzBrakujaceOcenyUcznia"
+const val IDZIENNIK_WEB_EXAMS = "mod_panelRodzica/sprawdziany/mod_sprawdzianyPanel.asmx/pobierzListe"
+const val IDZIENNIK_WEB_NOTICES = "mod_panelRodzica/uwagi/WS_uwagiUcznia.asmx/pobierzUwagiUcznia"
+const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcznia.asmx/pobierzObecnosciUcznia"
+const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia"
+const val IDZIENNIK_WEB_MESSAGES_LIST = "mod_komunikator/WS_wiadomosci.asmx/PobierzListeWiadomosci"
val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt
index a16c265e..6dd2ffb9 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt
@@ -4,9 +4,11 @@
package pl.szczodrzynski.edziennik.api.v2
-import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal
+import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLoginApi
+import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLoginWeb
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginMessages
+import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLoginWeb
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
@@ -118,6 +120,16 @@ val vulcanLoginMethods = listOf(
}
)
+val idziennikLoginMethods = listOf(
+ LoginMethod(LOGIN_TYPE_IDZIENNIK, LOGIN_METHOD_IDZIENNIK_WEB, IdziennikLoginWeb::class.java)
+ .withIsPossible { _, _ -> true }
+ .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
+
+ LoginMethod(LOGIN_TYPE_IDZIENNIK, LOGIN_METHOD_IDZIENNIK_API, IdziennikLoginApi::class.java)
+ .withIsPossible { _, _ -> true }
+ .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_IDZIENNIK_WEB }
+)
+
val templateLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java)
.withIsPossible { _, _ -> true }
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt
index ab0f4260..a35d95ba 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt
@@ -36,4 +36,28 @@ object Regexes {
val MOBIDZIENNIK_CLASS_CALENDAR by lazy {
"""events: (.+),$""".toRegex(RegexOption.MULTILINE)
}
+
+
+
+ val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
+ "".toRegex(RegexOption.DOT_MATCHES_ALL)
+ }
+ val IDZIENNIK_LOGIN_ERROR by lazy {
+ "id=\"spanErrorMessage\">(.*?)".toRegex(RegexOption.DOT_MATCHES_ALL)
+ }
+ val IDZIENNIK_LOGIN_FIRST_ACCOUNT_NAME by lazy {
+ "Imię i nazwisko:.+?\">(.+?)".toRegex(RegexOption.DOT_MATCHES_ALL)
+ }
+ val IDZIENNIK_LOGIN_FIRST_IS_PARENT by lazy {
+ "id=\"ctl00_CzyRodzic\" value=\"([01])\" />".toRegex()
+ }
+ val IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR by lazy {
+ "name=\"ctl00\\\$dxComboRokSzkolny\".+?selected=\"selected\".*?value=\"([0-9]+)\">([0-9/]+)<".toRegex(RegexOption.DOT_MATCHES_ALL)
+ }
+ val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy {
+ "".toRegex(RegexOption.DOT_MATCHES_ALL)
+ }
+ val IDZIENNIK_LOGIN_FIRST_STUDENT by lazy {
+ "(.+?)\\s(.+?)\\s*\\((.+?),\\s*(.+?)\\)".toRegex(RegexOption.DOT_MATCHES_ALL)
+ }
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt
new file mode 100644
index 00000000..faa2f517
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2019-10-25.
+ */
+
+package pl.szczodrzynski.edziennik.api.v2.idziennik
+
+import okhttp3.Cookie
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_API
+import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_WEB
+import pl.szczodrzynski.edziennik.api.v2.models.Data
+import pl.szczodrzynski.edziennik.currentTimeUnix
+import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
+import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
+import pl.szczodrzynski.edziennik.isNotNullNorEmpty
+
+class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
+
+ fun isWebLoginValid() = loginExpiryTime-30 > currentTimeUnix() && webSessionId.isNotNullNorEmpty() && webAuth.isNotNullNorEmpty()
+ fun isApiLoginValid() = loginExpiryTime-30 > currentTimeUnix() && apiBearer.isNotNullNorEmpty()
+
+ override fun satisfyLoginMethods() {
+ loginMethods.clear()
+ if (isWebLoginValid()) {
+ loginMethods += LOGIN_METHOD_IDZIENNIK_WEB
+ app.cookieJar.saveFromResponse(null, listOf(
+ Cookie.Builder()
+ .name("ASP.NET_SessionId_iDziennik")
+ .value(webSessionId!!)
+ .domain("iuczniowie.progman.pl")
+ .secure().httpOnly().build(),
+ Cookie.Builder()
+ .name(".ASPXAUTH")
+ .value(webAuth!!)
+ .domain("iuczniowie.progman.pl")
+ .secure().httpOnly().build()
+ ))
+ }
+ if (isApiLoginValid())
+ loginMethods += LOGIN_METHOD_IDZIENNIK_API
+ }
+
+ private var mLoginExpiryTime: Long? = null
+ var loginExpiryTime: Long
+ get() { mLoginExpiryTime = mLoginExpiryTime ?: loginStore.getLoginData("loginExpiryTime", 0L); return mLoginExpiryTime ?: 0L }
+ set(value) { loginStore.putLoginData("loginExpiryTime", value); mLoginExpiryTime = value }
+
+ /* __ __ _
+ \ \ / / | |
+ \ \ /\ / /__| |__
+ \ \/ \/ / _ \ '_ \
+ \ /\ / __/ |_) |
+ \/ \/ \___|_._*/
+ private var mWebSchoolName: String? = null
+ var webSchoolName: String?
+ get() { mWebSchoolName = mWebSchoolName ?: loginStore.getLoginData("schoolName", null); return mWebSchoolName }
+ set(value) { loginStore.putLoginData("schoolName", value); mWebSchoolName = value }
+ private var mWebUsername: String? = null
+ var webUsername: String?
+ get() { mWebUsername = mWebUsername ?: loginStore.getLoginData("username", null); return mWebUsername }
+ set(value) { loginStore.putLoginData("username", value); mWebUsername = value }
+ private var mWebPassword: String? = null
+ var webPassword: String?
+ get() { mWebPassword = mWebPassword ?: loginStore.getLoginData("password", null); return mWebPassword }
+ set(value) { loginStore.putLoginData("password", value); mWebPassword = value }
+
+ private var mWebSessionId: String? = null
+ var webSessionId: String?
+ get() { mWebSessionId = mWebSessionId ?: loginStore.getLoginData("webSessionId", null); return mWebSessionId }
+ set(value) { loginStore.putLoginData("webSessionId", value); mWebSessionId = value }
+ private var mWebAuth: String? = null
+ var webAuth: String?
+ get() { mWebAuth = mWebAuth ?: loginStore.getLoginData("webAuth", null); return mWebAuth }
+ set(value) { loginStore.putLoginData("webAuth", value); mWebAuth = value }
+
+ /* _
+ /\ (_)
+ / \ _ __ _
+ / /\ \ | '_ \| |
+ / ____ \| |_) | |
+ /_/ \_\ .__/|_|
+ | |
+ |*/
+ private var mApiBearer: String? = null
+ var apiBearer: String?
+ get() { mApiBearer = mApiBearer ?: loginStore.getLoginData("apiBearer", null); return mApiBearer }
+ set(value) { loginStore.putLoginData("apiBearer", value); mApiBearer = value }
+
+ /* ____ _ _
+ / __ \| | | |
+ | | | | |_| |__ ___ _ __
+ | | | | __| '_ \ / _ \ '__|
+ | |__| | |_| | | | __/ |
+ \____/ \__|_| |_|\___|*/
+ private var mStudentId: String? = null
+ var studentId: String?
+ get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", null); return mStudentId }
+ set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value }
+
+ private var mRegisterId: Int? = null
+ var registerId: Int
+ get() { mRegisterId = mRegisterId ?: profile?.getStudentData("registerId", 0); return mRegisterId ?: 0 }
+ set(value) { profile?.putStudentData("registerId", value) ?: return; mRegisterId = value }
+
+ private var mSchoolYearId: Int? = null
+ var schoolYearId: Int
+ get() { mSchoolYearId = mSchoolYearId ?: profile?.getStudentData("schoolYearId", 0); return mSchoolYearId ?: 0 }
+ set(value) { profile?.putStudentData("schoolYearId", value) ?: return; mSchoolYearId = value }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt
new file mode 100644
index 00000000..2a09756f
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2019-10-25.
+ */
+
+package pl.szczodrzynski.edziennik.api.v2.idziennik
+
+import android.util.Log
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
+import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikData
+import pl.szczodrzynski.edziennik.api.v2.idziennik.firstlogin.IdziennikFirstLogin
+import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLogin
+import pl.szczodrzynski.edziennik.api.v2.idziennikLoginMethods
+import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
+import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
+import pl.szczodrzynski.edziennik.api.v2.models.ApiError
+import pl.szczodrzynski.edziennik.api.v2.prepare
+import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
+import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
+import pl.szczodrzynski.edziennik.utils.Utils
+
+class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
+ companion object {
+ private const val TAG = "Idziennik"
+ }
+
+ val internalErrorList = mutableListOf()
+ val data: DataIdziennik
+
+ init {
+ data = DataIdziennik(app, profile, loginStore).apply {
+ callback = wrapCallback(this@Idziennik.callback)
+ satisfyLoginMethods()
+ }
+ }
+
+ private fun completed() {
+ data.saveData()
+ data.notifyAndSyncEvents {
+ callback.onCompleted()
+ }
+ }
+
+ /* _______ _ _ _ _ _
+ |__ __| | /\ | | (_) | | |
+ | | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___
+ | | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \
+ | | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | |
+ |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
+ __/ |
+ |__*/
+ override fun sync(featureIds: List, viewId: Int?) {
+ data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId)
+ Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
+ Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
+ IdziennikLogin(data) {
+ IdziennikData(data) {
+ completed()
+ }
+ }
+ }
+
+ override fun getMessage(messageId: Int) {
+
+ }
+
+ override fun markAllAnnouncementsAsRead() {
+
+ }
+
+ override fun firstLogin() {
+ IdziennikFirstLogin(data) {
+ completed()
+ }
+ }
+
+ override fun cancel() {
+ Utils.d(TAG, "Cancelled")
+ data.cancel()
+ }
+
+ private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
+ return object : EdziennikCallback {
+ override fun onCompleted() {
+ callback.onCompleted()
+ }
+
+ override fun onProgress(step: Int) {
+ callback.onProgress(step)
+ }
+
+ override fun onStartProgress(stringRes: Int) {
+ callback.onStartProgress(stringRes)
+ }
+
+ override fun onError(apiError: ApiError) {
+ when (apiError.errorCode) {
+ in internalErrorList -> {
+ // finish immediately if the same error occurs twice during the same sync
+ callback.onError(apiError)
+ }
+ CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
+ internalErrorList.add(apiError.errorCode)
+ loginStore.removeLoginData("refreshToken") // force a clean login
+ //loginLibrus()
+ }
+ else -> callback.onError(apiError)
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt
new file mode 100644
index 00000000..c3a01ea1
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2019-10-25.
+ */
+
+package pl.szczodrzynski.edziennik.api.v2.idziennik
+
+import pl.szczodrzynski.edziennik.api.v2.*
+import pl.szczodrzynski.edziennik.api.v2.models.Feature
+
+const val ENDPOINT_IDZIENNIK_WEB_SAMPLE = 9991
+const val ENDPOINT_IDZIENNIK_WEB_SAMPLE_2 = 9992
+const val ENDPOINT_IDZIENNIK_API_SAMPLE = 9993
+
+val IdziennikFeatures = listOf(
+ Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_STUDENT_INFO, listOf(
+ ENDPOINT_IDZIENNIK_WEB_SAMPLE to LOGIN_METHOD_IDZIENNIK_WEB
+ ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),
+ Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_SCHOOL_INFO, listOf(
+ ENDPOINT_IDZIENNIK_WEB_SAMPLE_2 to LOGIN_METHOD_IDZIENNIK_WEB
+ ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),
+ Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_GRADES, listOf(
+ ENDPOINT_IDZIENNIK_API_SAMPLE to LOGIN_METHOD_IDZIENNIK_API
+ ), listOf(LOGIN_METHOD_IDZIENNIK_API))
+)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt
new file mode 100644
index 00000000..4873a275
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2019-10-25.
+ */
+
+package pl.szczodrzynski.edziennik.api.v2.idziennik.data
+
+import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
+import pl.szczodrzynski.edziennik.utils.Utils
+
+class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
+ companion object {
+ private const val TAG = "IdziennikData"
+ }
+
+ private var cancelled = false
+
+ init {
+ nextEndpoint(onSuccess)
+ }
+
+ private fun nextEndpoint(onSuccess: () -> Unit) {
+ if (data.targetEndpointIds.isEmpty()) {
+ onSuccess()
+ return
+ }
+ if (cancelled) {
+ onSuccess()
+ return
+ }
+ useEndpoint(data.targetEndpointIds.removeAt(0)) {
+ nextEndpoint(onSuccess)
+ }
+ }
+
+ private fun useEndpoint(endpointId: Int, onSuccess: () -> Unit) {
+ Utils.d(TAG, "Using endpoint $endpointId")
+ when (endpointId) {
+ /*ENDPOINT_IDZIENNIK_WEB_SAMPLE -> {
+ data.startProgress(R.string.edziennik_progress_endpoint_student_info)
+ IdziennikWebSample(data) { onSuccess() }
+ }*/
+ else -> onSuccess()
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt
new file mode 100644
index 00000000..f1241c0c
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2019-10-25.
+ */
+
+package pl.szczodrzynski.edziennik.api.v2.idziennik.data
+
+import com.google.gson.JsonObject
+import im.wangchao.mhttp.Request
+import im.wangchao.mhttp.Response
+import im.wangchao.mhttp.callback.JsonCallbackHandler
+import im.wangchao.mhttp.callback.TextCallbackHandler
+import pl.szczodrzynski.edziennik.api.v2.*
+import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
+import pl.szczodrzynski.edziennik.api.v2.models.ApiError
+import pl.szczodrzynski.edziennik.utils.Utils.d
+import java.net.HttpURLConnection.HTTP_INTERNAL_ERROR
+import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
+
+open class IdziennikWeb(open val data: DataIdziennik) {
+ companion object {
+ const val TAG = "IdziennikWeb"
+ }
+
+ val profileId
+ get() = data.profile?.id ?: -1
+
+ val profile
+ get() = data.profile
+
+ fun webApiGet(tag: String, endpoint: String, parameters: Map = emptyMap(), onSuccess: (json: JsonObject) -> Unit) {
+ d(tag, "Request: Idziennik/Web/API - $IDZIENNIK_WEB_URL/$endpoint")
+
+ val callback = object : JsonCallbackHandler() {
+ override fun onSuccess(json: JsonObject?, response: Response?) {
+ if (json == null && response?.parserErrorBody == null) {
+ data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
+ .withResponse(response))
+ return
+ }
+
+ when {
+ response?.code() == HTTP_UNAUTHORIZED -> ERROR_IDZIENNIK_WEB_ACCESS_DENIED
+ response?.code() == HTTP_INTERNAL_ERROR -> ERROR_IDZIENNIK_WEB_SERVER_ERROR
+ response?.parserErrorBody != null -> when {
+ response.parserErrorBody.contains("Identyfikator zgłoszenia") -> ERROR_IDZIENNIK_WEB_SERVER_ERROR
+ response.parserErrorBody.contains("Hasło dostępu do systemu wygasło") -> ERROR_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED
+ response.parserErrorBody.contains("Trwają prace konserwacyjne") -> ERROR_IDZIENNIK_WEB_MAINTENANCE
+ else -> ERROR_IDZIENNIK_WEB_OTHER
+ }
+ else -> null
+ }?.let { errorCode ->
+ data.error(ApiError(TAG, errorCode)
+ .withApiResponse(json?.toString() ?: response?.parserErrorBody)
+ .withResponse(response))
+ return
+ }
+
+ if (json == null) {
+ data.error(ApiError(tag, ERROR_RESPONSE_EMPTY)
+ .withResponse(response))
+ return
+ }
+
+ try {
+ onSuccess(json)
+ } catch (e: Exception) {
+ data.error(ApiError(tag, EXCEPTION_IDZIENNIK_WEB_API_REQUEST)
+ .withResponse(response)
+ .withThrowable(e)
+ .withApiResponse(json))
+ }
+ }
+
+ override fun onFailure(response: Response?, throwable: Throwable?) {
+ data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
+ .withResponse(response)
+ .withThrowable(throwable))
+ }
+ }
+
+ Request.builder()
+ .url("$IDZIENNIK_WEB_URL/$endpoint")
+ .userAgent(IDZIENNIK_USER_AGENT)
+ .postJson()
+ .apply {
+ parameters.map { (name, value) ->
+ addParameter(name, value)
+ }
+ }
+ .allowErrorCode(HTTP_UNAUTHORIZED)
+ .allowErrorCode(HTTP_INTERNAL_ERROR)
+ .callback(callback)
+ .build()
+ .enqueue()
+ }
+
+ fun webGet(tag: String, endpoint: String, onSuccess: (text: String) -> Unit) {
+ d(tag, "Request: Idziennik/Web - $IDZIENNIK_WEB_URL/$endpoint")
+
+ val callback = object : TextCallbackHandler() {
+ override fun onSuccess(text: String?, response: Response?) {
+ if (text == null) {
+ data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
+ .withResponse(response))
+ return
+ }
+
+ if (!text.contains("czyWyswietlicDostepMobilny")) {
+ when {
+ text.contains("Identyfikator zgłoszenia") -> ERROR_IDZIENNIK_WEB_SERVER_ERROR
+ text.contains("Hasło dostępu do systemu wygasło") -> ERROR_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED
+ text.contains("Trwają prace konserwacyjne") -> ERROR_IDZIENNIK_WEB_MAINTENANCE
+ else -> ERROR_IDZIENNIK_WEB_OTHER
+ }.let { errorCode ->
+ data.error(ApiError(TAG, errorCode)
+ .withApiResponse(text)
+ .withResponse(response))
+ return
+ }
+ }
+
+ try {
+ onSuccess(text)
+ } catch (e: Exception) {
+ data.error(ApiError(tag, EXCEPTION_IDZIENNIK_WEB_REQUEST)
+ .withResponse(response)
+ .withThrowable(e)
+ .withApiResponse(text))
+ }
+ }
+
+ override fun onFailure(response: Response?, throwable: Throwable?) {
+ data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
+ .withResponse(response)
+ .withThrowable(throwable))
+ }
+ }
+
+ Request.builder()
+ .url("$IDZIENNIK_WEB_URL/$endpoint")
+ .userAgent(IDZIENNIK_USER_AGENT)
+ .get()
+ .callback(callback)
+ .build()
+ .enqueue()
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/firstlogin/IdziennikFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/firstlogin/IdziennikFirstLogin.kt
new file mode 100644
index 00000000..b282643b
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/firstlogin/IdziennikFirstLogin.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2019-10-27.
+ */
+
+package pl.szczodrzynski.edziennik.api.v2.idziennik.firstlogin
+
+import org.greenrobot.eventbus.EventBus
+import pl.szczodrzynski.edziennik.api.v2.ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR
+import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_SETTINGS
+import pl.szczodrzynski.edziennik.api.v2.Regexes
+import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent
+import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
+import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
+import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLoginWeb
+import pl.szczodrzynski.edziennik.api.v2.models.ApiError
+import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
+import pl.szczodrzynski.edziennik.fixName
+import pl.szczodrzynski.edziennik.get
+import pl.szczodrzynski.edziennik.swapFirstLastName
+
+class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
+ companion object {
+ private const val TAG = "IdziennikFirstLogin"
+ }
+
+ private val web = IdziennikWeb(data)
+ private val profileList = mutableListOf()
+
+ init {
+ IdziennikLoginWeb(data) {
+ web.webGet(TAG, IDZIENNIK_WEB_SETTINGS) { text ->
+ //val accounts = json.getJsonArray("accounts")
+
+ val isParent = Regexes.IDZIENNIK_LOGIN_FIRST_IS_PARENT.find(text)?.get(1) != "0"
+ val accountNameLong = if (isParent)
+ Regexes.IDZIENNIK_LOGIN_FIRST_ACCOUNT_NAME.find(text)?.get(1)?.swapFirstLastName()?.fixName()
+ else
+ null
+
+ var schoolYearName: String? = null
+ val schoolYear = Regexes.IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR.find(text)?.let {
+ schoolYearName = it[2]
+ it[1].toIntOrNull()
+ } ?: run {
+ data.error(ApiError(TAG, ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR)
+ .withApiResponse(text))
+ return@webGet
+ }
+
+ Regexes.IDZIENNIK_LOGIN_FIRST_STUDENT.findAll(text)
+ .toMutableList()
+ .reversed()
+ .forEach { match ->
+ val registerId = match[1].toIntOrNull() ?: return@forEach
+ val studentId = match[2]
+ val firstName = match[3]
+ val lastName = match[4]
+ val className = match[5] + " " + match[6]
+
+ val profile = Profile()
+ profile.studentNameLong = "$firstName $lastName".fixName()
+ profile.studentNameShort = "$firstName ${lastName[0]}.".fixName()
+ profile.accountNameLong = accountNameLong
+ profile.studentClassName = className
+ profile.studentSchoolYear = schoolYearName
+ profile.name = profile.studentNameLong
+ profile.subname = data.webUsername
+ profile.empty = true
+ profile.putStudentData("studentId", studentId)
+ profile.putStudentData("registerId", registerId)
+ profile.putStudentData("schoolYearId", schoolYear)
+ profileList.add(profile)
+ }
+
+ EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
+ onSuccess()
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt
new file mode 100644
index 00000000..be79e3e3
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2019-10-25.
+ */
+
+package pl.szczodrzynski.edziennik.api.v2.idziennik.login
+
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_API
+import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_WEB
+import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
+import pl.szczodrzynski.edziennik.utils.Utils
+
+class IdziennikLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
+ companion object {
+ private const val TAG = "IdziennikLogin"
+ }
+
+ private var cancelled = false
+
+ init {
+ nextLoginMethod(onSuccess)
+ }
+
+ private fun nextLoginMethod(onSuccess: () -> Unit) {
+ if (data.targetLoginMethodIds.isEmpty()) {
+ onSuccess()
+ return
+ }
+ if (cancelled) {
+ onSuccess()
+ return
+ }
+ useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId ->
+ if (usedMethodId != -1)
+ data.loginMethods.add(usedMethodId)
+ nextLoginMethod(onSuccess)
+ }
+ }
+
+ private fun useLoginMethod(loginMethodId: Int, onSuccess: (usedMethodId: Int) -> Unit) {
+ // this should never be true
+ if (data.loginMethods.contains(loginMethodId)) {
+ onSuccess(-1)
+ return
+ }
+ Utils.d(TAG, "Using login method $loginMethodId")
+ when (loginMethodId) {
+ LOGIN_METHOD_IDZIENNIK_WEB -> {
+ data.startProgress(R.string.edziennik_progress_login_idziennik_web)
+ IdziennikLoginWeb(data) { onSuccess(loginMethodId) }
+ }
+ LOGIN_METHOD_IDZIENNIK_API -> {
+ data.startProgress(R.string.edziennik_progress_login_idziennik_api)
+ IdziennikLoginApi(data) { onSuccess(loginMethodId) }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginApi.kt
new file mode 100644
index 00000000..4782b980
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginApi.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2019-10-27.
+ */
+
+package pl.szczodrzynski.edziennik.api.v2.idziennik.login
+
+import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
+
+class IdziennikLoginApi(val data: DataIdziennik, val onSuccess: () -> Unit) {
+ companion object {
+ private const val TAG = "IdziennikLoginApi"
+ }
+
+ init { run {
+ if (data.isApiLoginValid()) {
+ onSuccess()
+ }
+ else {
+ onSuccess()
+ }
+ }}
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt
new file mode 100644
index 00000000..d03c254e
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2019-10-26.
+ */
+
+package pl.szczodrzynski.edziennik.api.v2.idziennik.login
+
+import im.wangchao.mhttp.Request
+import im.wangchao.mhttp.Response
+import im.wangchao.mhttp.callback.TextCallbackHandler
+import okhttp3.Cookie
+import pl.szczodrzynski.edziennik.api.v2.*
+import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
+import pl.szczodrzynski.edziennik.api.v2.models.ApiError
+import pl.szczodrzynski.edziennik.get
+import pl.szczodrzynski.edziennik.getUnixDate
+import pl.szczodrzynski.edziennik.utils.Utils
+
+class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
+ companion object {
+ private const val TAG = "IdziennikLoginWeb"
+ }
+
+ init { run {
+ if (data.isWebLoginValid()) {
+ data.app.cookieJar.saveFromResponse(null, listOf(
+ Cookie.Builder()
+ .name("ASP.NET_SessionId_iDziennik")
+ .value(data.webSessionId!!)
+ .domain("iuczniowie.progman.pl")
+ .secure().httpOnly().build(),
+ Cookie.Builder()
+ .name(".ASPXAUTH")
+ .value(data.webAuth!!)
+ .domain("iuczniowie.progman.pl")
+ .secure().httpOnly().build()
+ ))
+ onSuccess()
+ }
+ else {
+ data.app.cookieJar.clearForDomain("iuczniowie.progman.pl")
+ if (data.webSchoolName != null && data.webUsername != null && data.webPassword != null) {
+ loginWithCredentials()
+ }
+ else {
+ data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING))
+ }
+ }
+ }}
+
+ private fun loginWithCredentials() {
+ Utils.d(TAG, "Request: Idziennik/Login/Web - $IDZIENNIK_WEB_URL/$IDZIENNIK_WEB_LOGIN")
+
+ val loginCallback = object : TextCallbackHandler() {
+ override fun onSuccess(text: String?, response: Response?) {
+ if (text.isNullOrEmpty()) {
+ data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
+ .withResponse(response))
+ return
+ }
+
+ // login succeeded: there is a start page
+ if (text.contains("czyWyswietlicDostepMobilny")) {
+ val cookies = data.app.cookieJar.getForDomain("iuczniowie.progman.pl")
+ run {
+ data.webSessionId = cookies.singleOrNull { it.name() == "ASP.NET_SessionId_iDziennik" }?.name() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION
+ data.webAuth = cookies.singleOrNull { it.name() == ".ASPXAUTH" }?.name() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH
+ data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.name() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER
+ data.loginExpiryTime = response.getUnixDate() + 45 * 60 /* 45 min */
+ return@run null
+ }?.let { errorCode ->
+ data.error(ApiError(TAG, errorCode)
+ .withApiResponse(text)
+ .withResponse(response))
+ return
+ }
+
+ onSuccess()
+ return
+ }
+
+ val errorText = Regexes.IDZIENNIK_LOGIN_ERROR.find(text)?.get(1)
+ when {
+ errorText?.contains("nieprawidłową nazwę szkoły") == true -> ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME
+ errorText?.contains("nieprawidłowy login lub hasło") == true -> ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN
+ text.contains("Identyfikator zgłoszenia") -> ERROR_LOGIN_IDZIENNIK_WEB_SERVER_ERROR
+ text.contains("Hasło dostępu do systemu wygasło") -> ERROR_LOGIN_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED
+ text.contains("Trwają prace konserwacyjne") -> ERROR_LOGIN_IDZIENNIK_WEB_MAINTENANCE
+ else -> ERROR_LOGIN_IDZIENNIK_WEB_OTHER
+ }.let { errorCode ->
+ data.error(ApiError(TAG, errorCode)
+ .withApiResponse(text)
+ .withResponse(response))
+ return
+ }
+ }
+
+ override fun onFailure(response: Response?, throwable: Throwable?) {
+ data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
+ .withResponse(response)
+ .withThrowable(throwable))
+ }
+ }
+
+ val getCallback = object : TextCallbackHandler() {
+ override fun onSuccess(text: String?, response: Response?) {
+ Request.builder()
+ .url("$IDZIENNIK_WEB_URL/$IDZIENNIK_WEB_LOGIN")
+ .userAgent(IDZIENNIK_USER_AGENT)
+ .addHeader("Origin", "https://iuczniowie.progman.pl")
+ .addHeader("Referer", "$IDZIENNIK_WEB_URL/$IDZIENNIK_WEB_LOGIN")
+ .apply {
+ Regexes.IDZIENNIK_LOGIN_HIDDEN_FIELDS.findAll(text ?: return@apply).forEach {
+ addParameter(it[1], it[2])
+ }
+ }
+ .addParameter("ctl00\$ContentPlaceHolder\$nazwaPrzegladarki", IDZIENNIK_USER_AGENT)
+ .addParameter("ctl00\$ContentPlaceHolder\$NazwaSzkoly", data.webSchoolName)
+ .addParameter("ctl00\$ContentPlaceHolder\$UserName", data.webUsername)
+ .addParameter("ctl00\$ContentPlaceHolder\$Password", data.webPassword)
+ .addParameter("ctl00\$ContentPlaceHolder\$captcha", "")
+ .addParameter("ctl00\$ContentPlaceHolder\$Logowanie", "Zaloguj")
+ .post()
+ .allowErrorCode(502)
+ .callback(loginCallback)
+ .build()
+ .enqueue()
+ }
+
+ override fun onFailure(response: Response?, throwable: Throwable?) {
+ data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
+ .withResponse(response)
+ .withThrowable(throwable))
+ }
+ }
+
+ Request.builder()
+ .url("$IDZIENNIK_WEB_URL/$IDZIENNIK_WEB_LOGIN")
+ .userAgent(IDZIENNIK_USER_AGENT)
+ .get()
+ .allowErrorCode(502)
+ .callback(getCallback)
+ .build()
+ .enqueue()
+ }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 26202e3b..f73a26fd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -963,6 +963,8 @@
Pobieranie kategorii wydarzeń...
Pobieranie kategorii uwag...
Pobieranie zebrań z rodzicami...
+ Logowanie do iDziennika...
+ Logowanie do iDziennika...
Pobieranie wiadomości wysłanych...
(rodzic)
(uczeń)